diff --git a/.all-contributorsrc b/.all-contributorsrc new file mode 100644 index 00000000..a625ed92 --- /dev/null +++ b/.all-contributorsrc @@ -0,0 +1,342 @@ +{ + "files": [ + "README.md" + ], + "imageSize": 100, + "commit": false, + "commitConvention": "angular", + "contributors": [ + { + "login": "refraction-ray", + "name": "Shixin Zhang", + "avatar_url": "https://avatars.githubusercontent.com/u/35157286?v=4", + "profile": "https://re-ra.xyz", + "contributions": [ + "code", + "doc", + "example", + "ideas", + "infra", + "maintenance", + "research", + "review", + "translation", + "test", + "tutorial", + "talk", + "question" + ] + }, + { + "login": "yutuer21", + "name": "Yuqin Chen", + "avatar_url": "https://avatars.githubusercontent.com/u/83822724?v=4", + "profile": "https://github.com/yutuer21", + "contributions": [ + "code", + "doc", + "example", + "ideas", + "research", + "test", + "tutorial", + "talk" + ] + }, + { + "login": "xptree", + "name": "Jiezhong Qiu", + "avatar_url": "https://avatars.githubusercontent.com/u/3853009?v=4", + "profile": "http://jiezhongqiu.com", + "contributions": [ + "code", + "example", + "ideas", + "research" + ] + }, + { + "login": "liwt31", + "name": "Weitang Li", + "avatar_url": "https://avatars.githubusercontent.com/u/22628546?v=4", + "profile": "http://liwt31.github.io", + "contributions": [ + "code", + "doc", + "ideas", + "research", + "test", + "talk" + ] + }, + { + "login": "SUSYUSTC", + "name": "Jiace Sun", + "avatar_url": "https://avatars.githubusercontent.com/u/30529122?v=4", + "profile": "https://github.com/SUSYUSTC", + "contributions": [ + "code", + "doc", + "example", + "ideas", + "research", + "test" + ] + }, + { + "login": "Zhouquan-Wan", + "name": "Zhouquan Wan", + "avatar_url": "https://avatars.githubusercontent.com/u/54523490?v=4", + "profile": "https://github.com/Zhouquan-Wan", + "contributions": [ + "code", + "doc", + "example", + "ideas", + "research", + "test", + "tutorial" + ] + }, + { + "login": "ls-iastu", + "name": "Shuo Liu", + "avatar_url": "https://avatars.githubusercontent.com/u/70554346?v=4", + "profile": "https://github.com/ls-iastu", + "contributions": [ + "example", + "research", + "tutorial" + ] + }, + { + "login": "YHPeter", + "name": "Hao Yu", + "avatar_url": "https://avatars.githubusercontent.com/u/44126839?v=4", + "profile": "https://github.com/YHPeter", + "contributions": [ + "code", + "doc", + "infra", + "test", + "tutorial" + ] + }, + { + "login": "SexyCarrots", + "name": "Xinghan Yang", + "avatar_url": "https://avatars.githubusercontent.com/u/63588721?v=4", + "profile": "https://github.com/SexyCarrots", + "contributions": [ + "doc", + "translation", + "tutorial" + ] + }, + { + "login": "JachyMeow", + "name": "JachyMeow", + "avatar_url": "https://avatars.githubusercontent.com/u/114171061?v=4", + "profile": "https://github.com/JachyMeow", + "contributions": [ + "tutorial", + "translation" + ] + }, + { + "login": "Mzye21", + "name": "Zhaofeng Ye", + "avatar_url": "https://avatars.githubusercontent.com/u/86239031?v=4", + "profile": "https://github.com/Mzye21", + "contributions": [ + "design" + ] + }, + { + "login": "erertertet", + "name": "erertertet", + "avatar_url": "https://avatars.githubusercontent.com/u/41342153?v=4", + "profile": "https://github.com/erertertet", + "contributions": [ + "code", + "doc", + "test" + ] + }, + { + "login": "yicongzheng", + "name": "Yicong Zheng", + "avatar_url": "https://avatars.githubusercontent.com/u/107173985?v=4", + "profile": "https://github.com/yicongzheng", + "contributions": [ + "tutorial" + ] + }, + { + "login": "MarkSong535", + "name": "Zixuan Song", + "avatar_url": "https://avatars.githubusercontent.com/u/78847784?v=4", + "profile": "https://marksong.tech", + "contributions": [ + "doc", + "translation", + "code", + "test" + ] + }, + { + "login": "buwantaiji", + "name": "Hao Xie", + "avatar_url": "https://avatars.githubusercontent.com/u/25216189?v=4", + "profile": "https://github.com/buwantaiji", + "contributions": [ + "doc" + ] + }, + { + "login": "pramitsingh0", + "name": "Pramit Singh", + "avatar_url": "https://avatars.githubusercontent.com/u/52959209?v=4", + "profile": "https://github.com/pramitsingh0", + "contributions": [ + "test" + ] + }, + { + "login": "JAllcock", + "name": "Jonathan Allcock", + "avatar_url": "https://avatars.githubusercontent.com/u/26302022?v=4", + "profile": "https://github.com/JAllcock", + "contributions": [ + "doc", + "ideas", + "talk" + ] + }, + { + "login": "nealchen2003", + "name": "nealchen2003", + "avatar_url": "https://avatars.githubusercontent.com/u/45502551?v=4", + "profile": "https://github.com/nealchen2003", + "contributions": [ + "doc" + ] + }, + { + "login": "eurethia", + "name": "隐公观鱼", + "avatar_url": "https://avatars.githubusercontent.com/u/84611606?v=4", + "profile": "https://github.com/eurethia", + "contributions": [ + "code", + "test" + ] + }, + { + "login": "WiuYuan", + "name": "WiuYuan", + "avatar_url": "https://avatars.githubusercontent.com/u/108848998?v=4", + "profile": "https://github.com/WiuYuan", + "contributions": [ + "example" + ] + }, + { + "login": "FelixXu35", + "name": "Felix Xu", + "avatar_url": "https://avatars.githubusercontent.com/u/61252303?v=4", + "profile": "https://www.linkedin.com/in/felix-xu-16a153196/", + "contributions": [ + "tutorial", + "code", + "test" + ] + }, + { + "login": "hongyehu", + "name": "Hong-Ye Hu", + "avatar_url": "https://avatars.githubusercontent.com/u/50563225?v=4", + "profile": "https://scholar.harvard.edu/hongyehu/home", + "contributions": [ + "doc" + ] + }, + { + "login": "PeilinZHENG", + "name": "peilin", + "avatar_url": "https://avatars.githubusercontent.com/u/45784888?v=4", + "profile": "https://github.com/PeilinZHENG", + "contributions": [ + "tutorial", + "code", + "test", + "doc" + ] + }, + { + "login": "EmilianoG-byte", + "name": "Cristian Emiliano Godinez Ramirez", + "avatar_url": "https://avatars.githubusercontent.com/u/57567043?v=4", + "profile": "https://emilianog-byte.github.io", + "contributions": [ + "code", + "test" + ] + }, + { + "login": "ztzhu1", + "name": "ztzhu", + "avatar_url": "https://avatars.githubusercontent.com/u/111620128?v=4", + "profile": "https://github.com/ztzhu1", + "contributions": [ + "code" + ] + }, + { + "login": "royess", + "name": "Rabqubit", + "avatar_url": "https://avatars.githubusercontent.com/u/31059422?v=4", + "profile": "https://github.com/royess", + "contributions": [ + "example" + ] + }, + { + "login": "king-p3nguin", + "name": "Kazuki Tsuoka", + "avatar_url": "https://avatars.githubusercontent.com/u/103920010?v=4", + "profile": "https://github.com/king-p3nguin", + "contributions": [ + "code", + "test", + "doc", + "example" + ] + }, + { + "login": "Gopal-Dahale", + "name": "Gopal Ramesh Dahale", + "avatar_url": "https://avatars.githubusercontent.com/u/49199003?v=4", + "profile": "https://gopal-dahale.github.io/", + "contributions": [ + "example" + ] + }, + { + "login": "AbdullahKazi500", + "name": "Chanandellar Bong", + "avatar_url": "https://avatars.githubusercontent.com/u/75779966?v=4", + "profile": "https://github.com/AbdullahKazi500", + "contributions": [ + "example" + ] + } + ], + "contributorsPerLine": 6, + "skipCi": true, + "repoType": "github", + "repoHost": "https://github.com", + "projectName": "tensorcircuit", + "projectOwner": "tencent-quantum-lab", + "commitType": "docs" +} diff --git a/.code.yml b/.code.yml deleted file mode 100644 index 1e158629..00000000 --- a/.code.yml +++ /dev/null @@ -1,8 +0,0 @@ -source: - # internal usage to filter code scan - test_source: - filepath_regex: [".*/examples/.*", ".*/benchmarks/scripts/.*"] - auto_generate_source: - filepath_regex: [".*/setup.py"] - third_party_source: - filepath_regex: [".*/tensorcircuit/applications/.*", ".*/docs/.*"] diff --git a/.devcontainer/Dockerfile b/.devcontainer/Dockerfile new file mode 100644 index 00000000..f0216aae --- /dev/null +++ b/.devcontainer/Dockerfile @@ -0,0 +1,25 @@ +# See here for image contents: https://github.com/microsoft/vscode-dev-containers/tree/v0.140.1/containers/python-3/.devcontainer/base.Dockerfile + +# [Choice] Python version: 3, 3.8, 3.7, 3.6 +ARG VARIANT="3" +FROM mcr.microsoft.com/vscode/devcontainers/python:0-${VARIANT} + +# [Option] Install Node.js +ARG INSTALL_NODE="true" +ARG NODE_VERSION="lts/*" +RUN if [ "${INSTALL_NODE}" = "true" ]; then su vscode -c "source /usr/local/share/nvm/nvm.sh && nvm install ${NODE_VERSION} 2>&1"; fi + +# [Optional] If your pip requirements rarely change, uncomment this section to add them to the image. +COPY requirements/requirements.txt /tmp/pip-tmp/ +COPY requirements/requirements-extra.txt /tmp/pip-tmp/ + +RUN pip3 --disable-pip-version-check --no-cache-dir install -r /tmp/pip-tmp/requirements.txt \ + && pip3 --disable-pip-version-check --no-cache-dir install -r /tmp/pip-tmp/requirements-extra.txt \ + && rm -rf /tmp/pip-tmp + +# [Optional] Uncomment this section to install additional OS packages. +# RUN apt-get update && export DEBIAN_FRONTEND=noninteractive \ +# && apt-get -y install --no-install-recommends + +# [Optional] Uncomment this line to install global node packages. +# RUN su vscode -c "source /usr/local/share/nvm/nvm.sh && npm install -g " 2>&1 \ No newline at end of file diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json new file mode 100644 index 00000000..f3c6b074 --- /dev/null +++ b/.devcontainer/devcontainer.json @@ -0,0 +1,45 @@ +// For format details, see https://aka.ms/devcontainer.json. For config options, see the README at: +// https://github.com/microsoft/vscode-dev-containers/tree/v0.140.1/containers/python-3 +{ + "name": "TensorCircuit User", + "build": { + "dockerfile": "Dockerfile", + "context": "..", + "args": { + // Update 'VARIANT' to pick a Python version: 3, 3.6, 3.7, 3.8 + "VARIANT": "3.8", + // Options + "INSTALL_NODE": "true", + "NODE_VERSION": "lts/*" + } + }, + + // Set *default* container specific settings.json values on container create. + "settings": { + "terminal.integrated.shell.linux": "/bin/bash", + "python.pythonPath": "/usr/local/bin/python", + "python.linting.enabled": true, + "python.linting.pylintEnabled": true, + "python.formatting.autopep8Path": "/usr/local/py-utils/bin/autopep8", + "python.formatting.blackPath": "/usr/local/py-utils/bin/black", + "python.formatting.yapfPath": "/usr/local/py-utils/bin/yapf", + "python.linting.banditPath": "/usr/local/py-utils/bin/bandit", + "python.linting.flake8Path": "/usr/local/py-utils/bin/flake8", + "python.linting.mypyPath": "/usr/local/py-utils/bin/mypy", + "python.linting.pycodestylePath": "/usr/local/py-utils/bin/pycodestyle", + "python.linting.pydocstylePath": "/usr/local/py-utils/bin/pydocstyle", + "python.linting.pylintPath": "/usr/local/py-utils/bin/pylint" + }, + + // Add the IDs of extensions you want installed when the container is created. + "extensions": ["ms-python.python", "ms-toolsai.jupyter"], + + // Use 'forwardPorts' to make a list of ports inside the container available locally. + // "forwardPorts": [], + + // Use 'postCreateCommand' to run commands after the container is created. + "postCreateCommand": "sudo python3 setup.py develop" + + // Uncomment to connect as a non-root user. See https://aka.ms/vscode-remote/containers/non-root. + // "remoteUser": "vscode" +} diff --git a/.dockerignore b/.dockerignore index b470a6f7..be657f84 100644 --- a/.dockerignore +++ b/.dockerignore @@ -12,4 +12,6 @@ **/*.outdated **/*.result **/*.results -**/*.data \ No newline at end of file +**/*.data +**/*.egg-info +**/*examples-ng \ No newline at end of file diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 00000000..176a458f --- /dev/null +++ b/.gitattributes @@ -0,0 +1 @@ +* text=auto diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md index 03e7c193..187c97ad 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.md +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -25,3 +25,5 @@ assignees: "" ## Environment Context <--! Please report your OS version, Python environment and version, TensorCircuit version and necessary dependent package (NumPy, TensorFlow, Jax, Jaxlib, PyTorch) version here. --> + +Output of `tc.about()` and `tc.__version__`. diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 5a3e23ea..964f9d67 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -7,20 +7,22 @@ jobs: strategy: matrix: os: [ubuntu-20.04, macos-latest] # macos-latest disabled to save quota - python-version: [3.8] + python-version: ["3.10"] fail-fast: false steps: - uses: actions/checkout@v2 - name: Set up Python ${{ matrix.python-version }} - uses: actions/setup-python@v1 + uses: actions/setup-python@v4 with: python-version: ${{ matrix.python-version }} + cache: "pip" - name: install dependencies run: | python -m pip install --upgrade pip pip install --no-cache-dir -r requirements/requirements.txt pip install --no-cache-dir -r requirements/requirements-extra.txt pip install --no-cache-dir -r requirements/requirements-dev.txt + pip install --no-cache-dir -r requirements/requirements-types.txt - name: black linter run: | black . --check @@ -34,7 +36,7 @@ jobs: run: | pytest --cov=tensorcircuit --cov-report=xml -svv --benchmark-skip - name: Upload coverage to Codecov - if: matrix.os == 'ubuntu-18.04' + if: matrix.os == 'ubuntu-20.04' uses: codecov/codecov-action@v2 with: verbose: true @@ -47,6 +49,8 @@ jobs: python mcnoise_boost.py python quantumng.py python universal_lr.py + python parameter_shift.py + python mpsvsexact.py - name: setup build run: | python3 setup.py build diff --git a/.github/workflows/nightly_release.yml b/.github/workflows/nightly_release.yml index a0d351c7..8a1dbda8 100644 --- a/.github/workflows/nightly_release.yml +++ b/.github/workflows/nightly_release.yml @@ -16,15 +16,17 @@ jobs: with: ref: beta - name: Set up Python - uses: actions/setup-python@v1 + uses: actions/setup-python@v4 with: - python-version: 3.8 + python-version: "3.10" + cache: "pip" - name: install dependencies run: | python -m pip install --upgrade pip pip install --no-cache-dir -r requirements/requirements.txt pip install --no-cache-dir -r requirements/requirements-extra.txt pip install --no-cache-dir -r requirements/requirements-dev.txt + pip install --no-cache-dir -r requirements/requirements-types.txt pip install requests - name: black linter run: | diff --git a/.gitignore b/.gitignore index 32fa5a10..c21b5efb 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ +data .prettierignore .idea/ dataset @@ -5,6 +6,7 @@ dataset *.disable *.mo develop +examples-ng .coverage* tutorials.po whitepaper.po @@ -26,3 +28,4 @@ examples/Unified AD model.ipynb docs/source/locale/zh/LC_MESSAGES/textbook.po docs/source/locale/zh/LC_MESSAGES/whitepapertoc_cn.po docs/source/locale/zh/LC_MESSAGES/textbooktoc.po +test.qasm diff --git a/.readthedocs.yaml b/.readthedocs.yaml new file mode 100644 index 00000000..2acb1049 --- /dev/null +++ b/.readthedocs.yaml @@ -0,0 +1,24 @@ +# .readthedocs.yaml +# Read the Docs configuration file +# See https://docs.readthedocs.io/en/stable/config-file/v2.html for details + +# Required +version: 2 + +formats: + - pdf + +# Set the version of Python and other tools you might need +build: + os: ubuntu-20.04 + tools: + python: "3.8" + +# Build documentation in the docs/ directory with Sphinx +sphinx: + configuration: docs/source/conf.py +# We recommend specifying your dependencies to enable reproducible builds: +# https://docs.readthedocs.io/en/stable/guides/reproducible-builds.html +python: + install: + - requirements: requirements/requirements-rtd.txt diff --git a/CHANGELOG.md b/CHANGELOG.md index 7323f4af..3f072c9a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,323 @@ ## Unreleased +### Added + +- Add support for parameter expression in qiskit translation + +## 0.12.0 + +### Added + +- Add translation of r gate from qiskit + +- Add `det` method at backends + +- Add fermion Gaussian state simulator in `fgs.py` + +- Add `partial_transpose` and `entanglement_negativity` method in `quantum.py` + +- Add `reduced_wavefunction` method in `quantum.py` to get reduced pure state + +### Changed + +- move ensemble module to applications/ai (breaking changes) + +- tc2qiskit now record qiskit measure with incremental clbit from 0 + +### Fixed + +- Support degenerate eigenvalue for jax backend `eigh` method when using AD + +- Fixed `cu` gate translation from qiskit to avoid qiskit bug + +- Fixed jax refactoring (0.4.24) where SVD and QR return a namedtuple instead of a tuple + +- Fix qiskit<1.0 and tf<2.16 + +## 0.11.0 + +### Added + +- Add multiple GPU VQE examples using jax pmap + +- Add `with_prob` option to `general_kraus` so that the probability of each option can be returned together + +- Add benchmark example showcasing new way of implementing matrix product using vmap + +- Add keras3 example showcasing integration with tc + +- Add circuit copy method that avoid shallow copy issue `Circuit.copy()` + +- Add end to end infrastructures and methods for classical shadow in `shadows.py` + +- Add classical shadow tutorial + +- Add NN-VQE tutorial + +### Fixed + +- improve the `adaptive_vmap` to support internal jit and pytree output + +- fix `pauli_gates` dtype unchange issue when set new dtype (not recommend to use this attr anymore) + +- fix rem `apply_correction` bug when non-numpy backend is set + +- fix tf warning for `cast` with higher version of tf + +### Changed + +- The static method `BaseCircuit.copy` is renamed as `BaseCircuit.copy_nodes` (breaking changes) + +## 0.10.0 + +### Added + +- `c.measure_instruction(*qubits)` now supports multiple ints specified at the same time + +- `c.expectation_ps()` now also supports `ps` argument directly (pauli structures) + +- Add tc version print in `tc.about()` method + +- tc now supports fancy batch indexing for gates, e.g. `c.rxx([0, 1, 2], [1, 2, 3], theta=K.ones([3]))` + +- Task management via group tag (when `submit_task` and `list_tasks`) + +- `batch_expectation_ps` now supports local device without topology and thus unify the interface for numerical exact simulation, numerical simulation with measurement shots and QPU experiments + +- introduce two stage compiling for `batch_expectation_ps` to save some compiling overhead + +- Add experimental support for ODE backend pulse level control simulation/analog quantum computing + +- make the pulse level control support differentiating the end time + +- Add new qem module with qem methods: zne, dd and rc + +### Fixed + +- `tc.results.counts.plot_histogram` now can dispatch kws to corresponding qiskit method + +- New implementation for `c.inverse()` to partially avoid unrecognized gate name issue + +- Fixed bug for `batch_expectation_ps` for jax backend + +- Partially fix the SVD numerical stability bug on tf backend when using `MPSCircuit` + +- List syntax for gate now supports range + +## 0.9.1 + +### Added + +- Add `tc.TorchHardwarLayer` for shortcut layer construction of quantum hardware experiments + +- Add cotengra contractor setup shortcut + +- Add simplecompiler module to assite qiskit compile for better performance when targeting rz native basis + +### Changed + +- Add compiler and cloud namespace to the global tensorcircuit namespace + +- Refactor composed compiler pipeline interface to include simple_compiler, using `DefaultCompiler` for now (breaking) + +- Refactor `batch_submit_template` wrapper to make it a standard abstraction layer between tc cloud infras and `batch_expectation_ps` abstraction, providing another way to adpot other cloud providers with only `batch_submit_template` implemented + +### Fixed + +- `submit_task` return (list of dict vs dict) follows the data type of provided circuit instead of the number of circuits + +- Fix qubit mapping related bug when using `batch_expectation_ps` or `simple_compile` + +## 0.9.0 + +### Added + +- Cloud module for Tencent QCloud is now merged into the master branch and ready to release + +- Add `tc.about()` to print related software versions and configs + +- Torch support is upgraded to 2.0, and now support native vmap and native functional grad, and thus `vvag`. Still jit support is conflict with these functional transformations and be turned off by default + +- Add `torch_interfaces_kws` that support static keyword arguments when wrapping with the interface + +- Add `gpu_memory_share` function and enable it by default + +- Add `scan` methods for backends + +- Add example demontrating how jax compiling time can be accelerated by `jax.lax.scan` + +### Fixed + +- Add tests and fixed some missing methods for cupy backend, cupy backend is now ready to use (though still not guaranteed) + +- Fix adjoint gate numpy conversion for fixed gate case + +- Sometime, tf just return IndexedSlice instead of tensor from gradient API, partially fix this in tc backend methods + +### Changed + +- Upgraded black and mypy==1.2.0 (breaking change for developers) + +## 0.8.0 + +### Added + +- Add `initial_mapping` circuit method to return a new circuit with given `logical_physical_mapping` + +- Add `get_positional_logical_mapping` circuit method to return the mapping when only part of the qubits are measured + +- `results.rem.ReadoutMit` class now support three layers of abstraction on qubits: positional, logical, and physical + +- Add an example script demonstrating how tc can use external contraction path finder wirtten in Julia + +- Add `cals_from_api` method for `ReadoutMit` class which can acquire the readout error information from the api + +- Add experimental compiler module + +- Make the compiler infra more ready for a pipeline compling + +- When translating to qiskit, multicontrol gate is manipulated specifically instead of a general unitary + +- Add qft blocks in template module + +- Add Tensorcircuit MacOS (univerisal) installation guide + +- Add KerasLayer without jit (quantum hardware compatible) + +- Add regularizer support for KerasLayer + +- Add methods in quantum module for translating ps list and xyz argument dict + +- Add `templates.ensemble.bagging` module for bagging ensemble method + +- The speed of Pauli string sum Hamiltonian generation is improved by a divide-and-conquer sum + +### Fixed + +- Circuit nosify in noise model now support all circuit attributes apart from qubit number + +- Some string warnings are fixed by using r-string + +- Fix bug in `tc.quantum.quimb2qop` when mps is the input + +- Fix bug in translation.py when qiskit is not installed + +- Rem results after `apply_correction` is now sorted + +- Fix `KerasLayer` so that it supports null weights + +- Fix tf optimizer bug and optimizer compatibility issue with tf2.11 + +## 0.7.0 + +### Added + +- Add `c.probability()` method to return probability amplitude + +- Add results module including funtionalities on count dict manipulation and readout error mitigation (local/global calibriation, scalable counts and expectation mitigation from research papers) + +- Add `_extra_qir` to store information on hardware level measurement and reset + +- Add `enable_instruction` option in `to_qiskit` method that enables measurements in qiskit export + +- Add circuit method `measure_instruction`, `barrier_instruction` and `reset_instruction` for hardware level + instruction flags + +- Auto unroll composite qiskit instructions when translating to tc circuit + +- Add `binding_parameters` argument for translating parameterized qiskit circuit to tc circuit + +- Add `keep_measure_order` bool option to `from_openqasm` methods so that the measure instruction order is kept by qiskit + +- Add Chinese translation for doc Sharpbit + +- Add `circuit_constructor` argument for `qiskit2tc` method, so that we can support more circuit class than circuit and dmcircuit + +### Fixed + +- Fix adjoint possible bug with agnostic backend + +- Fix `sigmoid` bug on pytorch backend + +- Fix `relu` bug on pytorch backend + +- Ignore ComplexWarning for `cast` method on numpy and jax backend + +- Fix `vjp` method bug on tensorflow backend, where none is replaced with zeros + +## 0.6.0 + +### Added + +- Add native support for `rxx`, `ryy` and `rzz` gates for translation from qiskit + +- Add `from_openqasm` and `from_openqasm_file` methods for `Circuit` + +- Add `circuit_params` argument for translation from qiskit to make the interface universal and consistent with other `from_` methods + +- Add `shifts` tuple parameter for `experimental.parameter_shift_grad` API so that we can also customize finite difference gradient from this method + +- Add `std` method for backends + +- Add `NoiseModel` class to programmably configure the global error model when simulating the quantum circuit + +- Add `tc.channels.composedkraus` to compose the different Kraus operators as a new one + +- Add direct support for noise model via `sample_expectation_ps` and `expectation` methods, both Monte Carlo trajectory and density matrix evolution approaches are supported + +### Changed + +- Improve the efficiency of `sample_expectation_ps` method by using cached state + +### Fixed + +- Fixed `unitary_kraus` of Circuit class support for multi-qubit kraus channels, previous implementation fails to reshape the multi-qubit kraus tensor as matrix + +- Fixed `kraus_to_super_gate` bug when multi-qubit kraus channels are presented on tensorflow backend + +## 0.5.0 + +### Added + +- Finished quantum noise modeling and simulation development stage 1. Add more quantum channels and the differentiable transformation between different channel forms. Add readout error support for sample and sample_expectation_ps methods. + +- Add new parameter shift gradient API that supports finite measurement shots and the corresponding example scripts + +- Add openqasm format transformation method `c.to_openqasm()` + +- Add native support for `phase` and `cphase` gates when transforming to qiskit + +- Add native support for `rxx`, `ryy`, `rzz` and `u`, `cu` gates when transforming to qiskit + +- Add native support for `u` gate when transforming from qiskit + +- Add circuit `from_qsim_file` method to load Google random circuit structure + +- Add `searchsorted` method for backend + +- Add `probability_sample` method for backend as an alternative for `random_choice` since it supports `status` as external randomness format + +- Add `status` support for `sample` and `sample_expection_ps` methods + +### Changed + +- The inner mechanism for `sample_expectation_ps` is changed to sample representation from count representation for a fast speed + +### Fixed + +- Fixed the breaking change introduced in jax 0.3.18, `jax._src` is no longer imported into the from the public jax namespace. + +- `tc.quantum.correlation_from_samples` now fix the sign error with odd number of spins + +- Updated to the latest version of mypy and get rid of lots of type: ignored + +- Fix the dtype bug when float is pass to u gate or phase gate + +- Fix to qiskit bug when parameterized gate has default nonset parameters + +- Fix `iswap` gate translation to qiskit with support for parameters + ## 0.4.1 ### Added diff --git a/CITATION.cff b/CITATION.cff new file mode 100644 index 00000000..f4fa6cc3 --- /dev/null +++ b/CITATION.cff @@ -0,0 +1,53 @@ +cff-version: 1.2.0 +message: "If you find this software helpful in your research, please cite it as below." +authors: +- family-names: "Zhang" + given-names: "Shi-Xin" +- family-names: "Chen" + given-names: "Yu-Qin" +title: "TensorCircuit" +version: 0.7.0 +date-released: 2020-04-19 +url: "https://github.com/tencent-quantum-lab/tensorcircuit" +preferred-citation: + type: article + authors: + - family-names: "Zhang" + given-names: "Shi-Xin" + - family-names: "Allcock" + given-names: "Jonathan" + - family-names: "Wan" + given-names: "Zhou-Quan" + - family-names: "Liu" + given-names: "Shuo" + - family-names: "Sun" + given-names: "Jiace" + - family-names: "Yu" + given-names: "Hao" + - family-names: "Yang" + given-names: "Xing-Han" + - family-names: "Qiu" + given-names: "Jiezhong" + - family-names: "Ye" + given-names: "Zhaofeng" + - family-names: "Chen" + given-names: "Yu-Qin" + - family-names: "Lee" + given-names: "Chee-Kong" + - family-names: "Zheng" + given-names: "Yi-Cong" + - family-names: "Jian" + given-names: "Shao-Kai" + - family-names: "Yao" + given-names: "Hong" + - family-names: "Hsieh" + given-names: "Chang-Yu" + - family-names: "Zhang" + given-names: "Shengyu" + doi: "10.22331/q-2023-02-02-912" + journal: "Quantum" + month: 2 + pages: 912 + title: "TensorCircuit: a Quantum Software Framework for the NISQ Era" + volume: 7 + year: 2023 \ No newline at end of file diff --git a/HISTORY.md b/HISTORY.md index 64f28080..77f61e28 100644 --- a/HISTORY.md +++ b/HISTORY.md @@ -1 +1 @@ -TensorCircuit is initially a personal project by @refraction-ray (Shi-Xin Zhang). He began this project in April 2020, inspired by the MPS quantum simulator [mpsim](https://github.com/grmlarose/mpsim) and the introduction of the Google [TensorNetwork](https://github.com/google/TensorNetwork) package. This project is further developed by him during 2020 and the first half of 2021 when he was a Ph.D. candidate at Tsinghua University, with multiple new features and applications added for his research purpose. The original TensorCircuit project is archived now on [GitHub](https://github.com/refraction-ray/tensorcircuit/). He decided to make this project an official open-source product after he joined Tencent in July 2021. And he has extensively refactored and optimized the codebase since then. As the lead author for this project, he thanks all the contributors who have made TensorCircuit and the ecosystem better. +TensorCircuit is initially a personal project created by @refraction-ray (Shi-Xin Zhang). He began this project in April 2020, inspired by the MPS quantum simulator [mpsim](https://github.com/grmlarose/mpsim) and the introduction of the Google [TensorNetwork](https://github.com/google/TensorNetwork) package. This project is further developed by him during 2020 and the first half of 2021 when he was a Ph.D. candidate at Tsinghua University, with multiple new features and applications added for his research purpose. The original TensorCircuit project is archived now on [GitHub](https://github.com/refraction-ray/tensorcircuit/). He decided to make this project a more universal open-source framework after he joined Tencent in July 2021. And he has extensively refactored and optimized the codebase since then. As the creator and the lead author of TensorCircuit, he thanks all the [contributors](https://github.com/tencent-quantum-lab/tensorcircuit#contributors) who have made TensorCircuit and the ecosystem better. diff --git a/README.md b/README.md index 8b5ff34f..c46b54c7 100644 --- a/README.md +++ b/README.md @@ -7,7 +7,7 @@

- + @@ -29,15 +29,15 @@

English | 简体中文

-TensorCircuit is the next generation of quantum circuit simulators with support for automatic differentiation, just-in-time compiling, hardware acceleration, and vectorized parallelism. +TensorCircuit is the next generation of quantum software framework with support for automatic differentiation, just-in-time compiling, hardware acceleration, and vectorized parallelism. -TensorCircuit is built on top of modern machine learning frameworks and is machine learning backend agnostic. It is specifically suitable for highly efficient simulations of quantum-classical hybrid paradigm and variational quantum algorithms. +TensorCircuit is built on top of modern machine learning frameworks: Jax, TensorFlow, and PyTorch. It is specifically suitable for highly efficient simulations of quantum-classical hybrid paradigm and variational quantum algorithms in ideal, noisy and approximate cases. It also supports real quantum hardware access and provides CPU/GPU/QPU hybrid deployment solutions since v0.9. ## Getting Started -Please begin with [Quick Start](/docs/source/quickstart.rst). +Please begin with [Quick Start](/docs/source/quickstart.rst) in the [full documentation](https://tensorcircuit.readthedocs.io/). -For more information and introductions, please refer to helpful [example scripts](/examples) and [full documentation](https://tensorcircuit.readthedocs.io/). API docstrings and test cases in [tests](/tests) are also informative. +For more information on software usage, sota algorithm implementation and engineer paradigm demonstration, please refer to 70+ [example scripts](/examples) and 30+ [tutorial notebooks](https://tensorcircuit.readthedocs.io/en/latest/#tutorials). API docstrings and test cases in [tests](/tests) are also informative. The following are some minimal demos. @@ -51,7 +51,7 @@ c.CNOT(0,1) c.rx(1, theta=0.2) print(c.wavefunction()) print(c.expectation_ps(z=[0, 1])) -print(c.sample()) +print(c.sample(allow_state=True, batch=1024, format="count_dict_bin")) ``` - Runtime behavior customization: @@ -76,78 +76,330 @@ theta = tc.array_to_tensor(1.0) print(g(theta)) ``` -## Install +
+ More highlight features for TensorCircuit (click for details) -The package is purely written in Python and can be obtained via pip as: +- Sparse Hamiltonian generation and expectation evaluation: ```python -pip install tensorcircuit +n = 6 +pauli_structures = [] +weights = [] +for i in range(n): + pauli_structures.append(tc.quantum.xyz2ps({"z": [i, (i + 1) % n]}, n=n)) + weights.append(1.0) +for i in range(n): + pauli_structures.append(tc.quantum.xyz2ps({"x": [i]}, n=n)) + weights.append(-1.0) +h = tc.quantum.PauliStringSum2COO(pauli_structures, weights) +print(h) +# BCOO(complex64[64, 64], nse=448) +c = tc.Circuit(n) +c.h(range(n)) +energy = tc.templates.measurements.operator_expectation(c, h) +# -6 ``` -And we recommend you install this package with tensorflow also installed as: +- Large-scale simulation with tensor network engine ```python -pip install tensorcircuit[tensorflow] +# tc.set_contractor("cotengra-30-10") +n=500 +c = tc.Circuit(n) +c.h(0) +c.cx(range(n-1), range(1, n)) +c.expectation_ps(z=[0, n-1], reuse=False) ``` -Other optional dependencies include `[torch]`, `[jax]` and `[qiskit]`. +- Density matrix simulator and quantum info quantities + +```python +c = tc.DMCircuit(2) +c.h(0) +c.cx(0, 1) +c.depolarizing(1, px=0.1, py=0.1, pz=0.1) +dm = c.state() +print(tc.quantum.entropy(dm)) +print(tc.quantum.entanglement_entropy(dm, [0])) +print(tc.quantum.entanglement_negativity(dm, [0])) +print(tc.quantum.log_negativity(dm, [0])) +``` + +
+ +## Install + +The package is written in pure Python and can be obtained via pip as: + +```python +pip install tensorcircuit +``` -For nightly build of tensorcircuit with new features, try: +We recommend you install this package with tensorflow also installed as: ```python -pip uninstall tensorcircuit -pip install tensorcircuit-nightly +pip install tensorcircuit[tensorflow] ``` +Other optional dependencies include `[torch]`, `[jax]`, `[qiskit]` and `[cloud]`. + We also have [Docker support](/docker). ## Advantages - Tensor network simulation engine based -- JIT, AD, vectorized parallelism compatible, GPU support +- JIT, AD, vectorized parallelism compatible + +- GPU support, quantum device access support, hybrid deployment support - Efficiency - - Time: 10 to 10^6 times acceleration compared to tfq or qiskit + - Time: 10 to 10^6+ times acceleration compared to TensorFlow Quantum, Pennylane or Qiskit - Space: 600+ qubits 1D VQE workflow (converged energy inaccuracy: < 1%) - Elegance - - Flexibility: customized contraction, multiple ML backend/interface choices, multiple dtype precisions + - Flexibility: customized contraction, multiple ML backend/interface choices, multiple dtype precisions, multiple QPU providers - API design: quantum for humans, less code, more power -## Citing TensorCircuit +- Batteries included + +
+ Tons of amazing features and built in tools for research (click for details) + + - Support **super large circuit simulation** using tensor network engine. + + - Support **noisy simulation** with both Monte Carlo and density matrix (tensor network powered) modes. + + - Support **approximate simulation** with MPS-TEBD modes. + + - Support **analog/digital hybrid simulation** (time dependent Hamiltonian evolution, **pulse** level simulation) with neural ode modes. -This project is released by [Tencent Quantum Lab](https://quantum.tencent.com/) and is currently maintained by [Shi-Xin Zhang](https://github.com/refraction-ray) with contributions from the lab and open source community. + - Support **Fermion Gaussian state** simulation with expectation, entanglement, measurement, ground state, real and imaginary time evolution. -If this project helps in your research, please cite our software whitepaper: + - Support **qudits simulation**. -[TensorCircuit: a Quantum Software Framework for the NISQ Era](https://arxiv.org/abs/2205.10091) + - Support **parallel** quantum circuit evaluation across **multiple GPUs**. -which is also a good introduction for the software. + - Highly customizable **noise model** with gate error and scalable readout error. + + - Support for **non-unitary** gate and post-selection simulation. + + - Support **real quantum devices access** from different providers. + + - **Scalable readout error mitigation** native to both bitstring and expectation level with automatic qubit mapping consideration. + + - **Advanced quantum error mitigation methods** and pipelines such as ZNE, DD, RC, etc. + + - Support **MPS/MPO** as representations for input states, quantum gates and observables to be measured. + + - Support **vectorized parallelism** on circuit inputs, circuit parameters, circuit structures, circuit measurements and these vectorization can be nested. + + - Gradients can be obtained with both **automatic differenation** and parameter shift (vmap accelerated) modes. + + - **Machine learning interface/layer/model** abstraction in both TensorFlow and PyTorch for both numerical simulation and real QPU experiments. + + - Circuit sampling supports both final state sampling and perfect sampling from tensor networks. + + - Light cone reduction support for local expectation calculation. + + - Highly customizable tensor network contraction path finder with opteinsum interface. + + - Observables are supported in measurement, sparse matrix, dense matrix and MPO format. + + - Super fast weighted sum Pauli string Hamiltonian matrix generation. + + - Reusable common circuit/measurement/problem templates and patterns. + + - Jittable classical shadow infrastructures. + + - SOTA quantum algorithm and model implementations. + + - Support hybrid workflows and pipelines with CPU/GPU/QPU hardware from local/cloud/hpc resources using tf/torch/jax/cupy/numpy frameworks all at the same time. + +
## Contributing +### Status + +This project is created and maintained by [Shi-Xin Zhang](https://github.com/refraction-ray) with current core authors [Shi-Xin Zhang](https://github.com/refraction-ray) and [Yu-Qin Chen](https://github.com/yutuer21). We also thank [contributions](https://github.com/tencent-quantum-lab/tensorcircuit/graphs/contributors) from the open source community. + +### Citation + +If this project helps in your research, please cite our software whitepaper to acknowledge the work put into the development of TensorCircuit. + +[TensorCircuit: a Quantum Software Framework for the NISQ Era](https://quantum-journal.org/papers/q-2023-02-02-912/) (published in Quantum) + +which is also a good introduction to the software. + +Research works citing TensorCircuit can be highlighted in [Research and Applications section](https://github.com/tencent-quantum-lab/tensorcircuit#research-and-applications). + +### Guidelines + For contribution guidelines and notes, see [CONTRIBUTING](/CONTRIBUTING.md). We welcome [issues](https://github.com/tencent-quantum-lab/tensorcircuit/issues), [PRs](https://github.com/tencent-quantum-lab/tensorcircuit/pulls), and [discussions](https://github.com/tencent-quantum-lab/tensorcircuit/discussions) from everyone, and these are all hosted on GitHub. -## Researches and Applications +### License + +TensorCircuit is open source, released under the Apache License, Version 2.0. + +### Contributors + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Shixin Zhang
Shixin Zhang

💻 📖 💡 🤔 🚇 🚧 🔬 👀 🌍 ⚠️ 📢 💬
Yuqin Chen
Yuqin Chen

💻 📖 💡 🤔 🔬 ⚠️ 📢
Jiezhong Qiu
Jiezhong Qiu

💻 💡 🤔 🔬
Weitang Li
Weitang Li

💻 📖 🤔 🔬 ⚠️ 📢
Jiace Sun
Jiace Sun

💻 📖 💡 🤔 🔬 ⚠️
Zhouquan Wan
Zhouquan Wan

💻 📖 💡 🤔 🔬 ⚠️
Shuo Liu
Shuo Liu

💡 🔬
Hao Yu
Hao Yu

💻 📖 🚇 ⚠️
Xinghan Yang
Xinghan Yang

📖 🌍
JachyMeow
JachyMeow

🌍
Zhaofeng Ye
Zhaofeng Ye

🎨
erertertet
erertertet

💻 📖 ⚠️
Yicong Zheng
Yicong Zheng

Zixuan Song
Zixuan Song

📖 🌍 💻 ⚠️
Hao Xie
Hao Xie

📖
Pramit Singh
Pramit Singh

⚠️
Jonathan Allcock
Jonathan Allcock

📖 🤔 📢
nealchen2003
nealchen2003

📖
隐公观鱼
隐公观鱼

💻 ⚠️
WiuYuan
WiuYuan

💡
Felix Xu
Felix Xu

💻 ⚠️
Hong-Ye Hu
Hong-Ye Hu

📖
peilin
peilin

💻 ⚠️ 📖
Cristian Emiliano Godinez Ramirez
Cristian Emiliano Godinez Ramirez

💻 ⚠️
ztzhu
ztzhu

💻
Rabqubit
Rabqubit

💡
Kazuki Tsuoka
Kazuki Tsuoka

💻 ⚠️ 📖 💡
Gopal Ramesh Dahale
Gopal Ramesh Dahale

💡
Chanandellar Bong
Chanandellar Bong

💡
+ + + + + + + + + + + + + +## Research and Applications ### DQAS For the application of Differentiable Quantum Architecture Search, see [applications](/tensorcircuit/applications). -Reference paper: https://arxiv.org/pdf/2010.08561.pdf. + +Reference paper: https://arxiv.org/abs/2010.08561 (published in QST). ### VQNHE For the application of Variational Quantum-Neural Hybrid Eigensolver, see [applications](/tensorcircuit/applications). -Reference paper: https://arxiv.org/pdf/2106.05105.pdf and https://arxiv.org/pdf/2112.10380.pdf. -### VQEX - MBL +Reference paper: https://arxiv.org/abs/2106.05105 (published in PRL) and https://arxiv.org/abs/2112.10380 (published in AQT). + +### VQEX-MBL For the application of VQEX on MBL phase identification, see the [tutorial](/docs/source/tutorials/vqex_mbl.ipynb). -Reference paper: https://arxiv.org/pdf/2111.13719.pdf. + +Reference paper: https://arxiv.org/abs/2111.13719 (published in PRB). + +### Stark-DTC + +For the numerical demosntration of discrete time crystal enabled by Stark many-body localization, see the Floquet simulation [demo](/examples/timeevolution_trotter.py). + +Reference paper: https://arxiv.org/abs/2208.02866 (published in PRL). + +### RA-Training + +For the numerical simulation of variational quantum algorithm training using random gate activation strategy by us, see the [project repo](https://github.com/ls-iastu/RAtraining). + +Reference paper: https://arxiv.org/abs/2303.08154 (published in PRR as a Letter). + +### TenCirChem + +[TenCirChem](https://github.com/tencent-quantum-lab/TenCirChem) is an efficient and versatile quantum computation package for molecular properties. TenCirChem is based on TensorCircuit and is optimized for chemistry applications. + +Reference paper: https://arxiv.org/abs/2303.10825 (published in JCTC). + +### EMQAOA-DARBO + +For the numerical simulation and hardware experiments with error mitigation on QAOA, see the [project repo](https://github.com/sherrylixuecheng/EMQAOA-DARBO). + +Reference paper: https://arxiv.org/abs/2303.14877 (published in Communications Physics). + +### NN-VQA + +For the setup and simulation code of neural network encoded variational quantum eigensolver, see the [demo](/docs/source/tutorials/nnvqe.ipynb). + +Reference paper: https://arxiv.org/abs/2308.01068 (published in PRApplied). + +### More works + +
+ More research works and code projects using TensorCircuit (click for details) + +- Neural Predictor based Quantum Architecture Search: https://arxiv.org/abs/2103.06524 (published in Machine Learning: Science and Technology). + +- Quantum imaginary-time control for accelerating the ground-state preparation: https://arxiv.org/abs/2112.11782 (published in PRR). + +- Efficient Quantum Simulation of Electron-Phonon Systems by Variational Basis State Encoder: https://arxiv.org/abs/2301.01442 (published in PRR). + +- Variational Quantum Simulations of Finite-Temperature Dynamical Properties via Thermofield Dynamics: https://arxiv.org/abs/2206.05571. + +- Understanding quantum machine learning also requires rethinking generalization: https://arxiv.org/abs/2306.13461 (published in Nature Communications). + +- Decentralized Quantum Federated Learning for Metaverse: Analysis, Design and Implementation: https://arxiv.org/abs/2306.11297. Code: https://github.com/s222416822/BQFL. + +- Non-IID quantum federated learning with one-shot communication complexity: https://arxiv.org/abs/2209.00768 (published in Quantum Machine Intelligence). Code: https://github.com/JasonZHM/quantum-fed-infer. + +- Quantum generative adversarial imitation learning: https://doi.org/10.1088/1367-2630/acc605 (published in New Journal of Physics). + +- GSQAS: Graph Self-supervised Quantum Architecture Search: https://arxiv.org/abs/2303.12381 (published in Physica A: Statistical Mechanics and its Applications). + +- Practical advantage of quantum machine learning in ghost imaging: https://www.nature.com/articles/s42005-023-01290-1 (published in Communications Physics). + +- Zero and Finite Temperature Quantum Simulations Powered by Quantum Magic: https://arxiv.org/abs/2308.11616. + +- Comparison of Quantum Simulators for Variational Quantum Search: A Benchmark Study: https://arxiv.org/abs/2309.05924. + +- Statistical analysis of quantum state learning process in quantum neural networks: https://arxiv.org/abs/2309.14980 (published in NeurIPS). + +- Generative quantum machine learning via denoising diffusion probabilistic models: https://arxiv.org/abs/2310.05866 (published in PRL). + +- Quantum imaginary time evolution and quantum annealing meet topological sector optimization: https://arxiv.org/abs/2310.04291. + +- Google Summer of Code 2023 Projects (QML4HEP): https://github.com/ML4SCI/QMLHEP, https://github.com/Gopal-Dahale/qgnn-hep, https://github.com/salcc/QuantumTransformers. + +- Absence of barren plateaus in finite local-depth circuits with long-range entanglement: https://arxiv.org/abs/2311.01393 (published in PRL). + +- Non-Markovianity benefits quantum dynamics simulation: https://arxiv.org/abs/2311.17622. + +
+ +If you want to highlight your research work or projects here, feel free to add by opening PR. diff --git a/README_cn.md b/README_cn.md index 9b19223b..8137eb85 100644 --- a/README_cn.md +++ b/README_cn.md @@ -3,7 +3,7 @@

- + @@ -25,15 +25,17 @@

English | 简体中文

-TensorCircuit 是下一代量子电路模拟器,支持自动微分、即时编译、硬件加速和向量并行化。 +TensorCircuit 是下一代量子软件框架,完美支持自动微分、即时编译、硬件加速和向量并行化。 -TensorCircuit 建立在现代机器学习框架之上,并且与机器学习后端无关。 它特别适用于量子经典混合范式和变分量子算法的高效模拟。 +TensorCircuit 建立在现代机器学习框架 Jax, TensorFlow, PyTorch 之上,支持机器学习后端无关的统一界面。 其特别适用于理想情况、含噪声情况及可控近似情况下,大规模量子经典混合范式和变分量子算法的高效模拟。 + +TensorCircuit 现在支持真实量子硬件连接和实验,并提供优雅的 CPU/GPU/QPU 混合部署训练方案(v0.9+)。 ## 入门 -请从 [快速上手](/docs/source/quickstart.rst) 和 [Jupyter 教程](/docs/source/tutorials) 开始。 +请从 [完整文档](https://tensorcircuit.readthedocs.io/zh/latest/) 中的 [快速上手](/docs/source/quickstart.rst) 开始。 -有关更多信息和介绍,请参阅有用的 [示例脚本](/examples) 和 [完整文档](https://tensorcircuit.readthedocs.io/zh/latest/)。 [测试](/tests) 中的 API docstring 和测试用例也提供了丰富的信息。 +有关软件用法,算法实现和工程范式演示的更多信息和介绍,请参阅 70+ [示例脚本](/examples) 和 30+ [案例教程](https://tensorcircuit.readthedocs.io/zh/latest/#tutorials)。 [测试](/tests) 用例和 API docstring 也提供了丰富的使用信息。 以下是一些最简易的演示。 @@ -47,10 +49,10 @@ c.CNOT(0,1) c.rx(1, theta=0.2) print(c.wavefunction()) print(c.expectation_ps(z=[0, 1])) -print(c.sample()) +print(c.sample(allow_state=True, batch=1024, format="count_dict_bin")) ``` -- 运行时特性定制: +- 运行时特性设置: ```python tc.set_backend("tensorflow") @@ -86,7 +88,7 @@ pip install tensorcircuit pip install tensorcircuit[tensorflow] ``` -其他安装选项包括: `[torch]`, `[jax]` and `[qiskit]`。 +其他安装选项包括: `[torch]`, `[jax]`, `[qiskit]` 和 `[cloud]`。 此外我们有每日发布的最新版本 pip package,可以尝鲜开发的最新功能,请通过以下方式安装: @@ -101,51 +103,90 @@ pip install tensorcircuit-nightly - 基于张量网络模拟引擎 -- 即时编译、自动微分、向量并行化兼容,GPU 支持 +- 即时编译、自动微分、向量并行化兼容 + +- GPU 支持、量子硬件支持、混合部署方案支持 - 效率 - - 时间:与 TFQ 或 Qiskit 相比,加速 10 到 10^6 倍 + - 时间:与 TFQ, Pennylane, 或 Qiskit 相比,加速 10 到 10^6+ 倍 - 空间:600+ qubits 1D VQE 工作流(收敛能量误差:< 1%) - 优雅 - - 灵活性:自定义张量收缩、多种 ML 后端/接口选择、多种数值精度 + - 灵活性:自定义张量收缩、多种 ML 后端/接口选择、多种数值精度、多种量子硬件 - API 设计:人类可理解的量子,更少的代码,更多的可能 -## 引用 +## 贡献 -该项目由[腾讯量子实验室](https://quantum.tencent.com/)发布,现阶段由 [Shi-Xin Zhang](https://github.com/refraction-ray) 维护。 +### 现况 -如果该软件对您的研究有帮助, 请引用我们的白皮书文章: +该项目由 [Shi-Xin Zhang](https://github.com/refraction-ray) 创造并维护。当前核心作者包括 [Shi-Xin Zhang](https://github.com/refraction-ray) 和 [Yu-Qin Chen](https://github.com/yutuer21)。我们也感谢来自开源社区的[贡献](https://github.com/tencent-quantum-lab/tensorcircuit/graphs/contributors)。 -[TensorCircuit: a Quantum Software Framework for the NISQ Era](https://arxiv.org/abs/2205.10091). +### 引用 -## 贡献 +如果该软件对您的研究有帮助, 请引用我们发表在 Quantum 期刊的白皮书文章来支持我们的研发付出。 + +[TensorCircuit: a Quantum Software Framework for the NISQ Era](https://quantum-journal.org/papers/q-2023-02-02-912/). + +### 说明 有关贡献指南和说明,请参阅 [贡献](/CONTRIBUTING.md)。 我们欢迎大家提出 [issues](https://github.com/tencent-quantum-lab/tensorcircuit/issues), [PR](https://github.com/tencent-quantum-lab/tensorcircuit/pulls), 和 [讨论](https://github.com/tencent-quantum-lab/tensorcircuit/discussions),这些都托管在 GitHub 上。 +### 协议 + +TensorCircuit 是基于 Apache License 2.0 的开源软件。 + ## 研究和应用 ### DQAS 可微量子架构搜索的应用见 [应用](/tensorcircuit/applications)。 -参考论文:https://arxiv.org/pdf/2010.08561.pdf + +参考论文:https://arxiv.org/abs/2010.08561 (QST)。 ### VQNHE 关于变分量子神经混合本征求解器的应用,请参见 [应用](tensorcircuit/applications)。 -参考论文:https://arxiv.org/pdf/2106.05105.pdf 和 https://arxiv.org/pdf/2112.10380.pdf 。 -### VQEX - MBL +参考论文:https://arxiv.org/abs/2106.05105 (PRL) 和 https://arxiv.org/abs/2112.10380 。 + +### VQEX-MBL VQEX 在 MBL 相位识别上的应用见 [教程](/docs/source/tutorials/vqex_mbl.ipynb)。 -参考论文: https://arxiv.org/pdf/2111.13719.pdf 。 -``` +参考论文: https://arxiv.org/abs/2111.13719 (PRB)。 -``` +### Stark-DTC + +数值验证 Stark 多体局域化稳定的离散时间晶体,类似的 Floquet 系统模拟请参考 [例子](/examples/timeevolution_trotter.py)。 + +参考论文: https://arxiv.org/abs/2208.02866 (PRL)。 + +### RA-Training + +利用我们提出的随机量子门激活策略训练优化变分量子算法的实现请参考 [项目](https://github.com/ls-iastu/RAtraining). + +参考论文: https://arxiv.org/abs/2303.08154。 + +### TenCirChem + +[TenCirChem](https://github.com/tencent-quantum-lab/TenCirChem) 是高效的,专注于处理和计算分子性质的量子计算软件。其基于 TensorCircuit 并为量子化学任务进行了专门的优化。 + +参考论文: https://arxiv.org/abs/2303.10825 (JCTC)。 + +### EMQAOA-DARBO + +数值模拟和带错误消除的真实量子硬件实验验证 QAOA 优化的代码请参考 [项目](https://github.com/sherrylixuecheng/EMQAOA-DARBO)。 + +参考论文: https://arxiv.org/abs/2303.14877。 + +### NN-VQA + +关于神经网络编码的变分量子算法的实现和工作流, 见 [教程](/docs/source/tutorials/nnvqe.ipynb)。 + +参考论文: https://arxiv.org/abs/2308.01068。 diff --git a/benchmarks/README.md b/benchmarks/README.md index 8167d832..668b7a10 100644 --- a/benchmarks/README.md +++ b/benchmarks/README.md @@ -2,7 +2,7 @@ `cd scripts` -`python benchmark.py -n [# of Qubits] -nlayer [# of QC layers] -nitrs [# of max iterations] -t [time limitation] -gpu [0 for no gpu and 1 for gpu enabled] -tcbackend [jax or tensorflow]` +`python benchmark.py -n [# of Qubits] -nlayer [# of QC layers] -nitrs [# of max iterations] -nbatch [# of batch for QML task] -t [time limitation] -gpu [0 for no gpu and 1 for gpu enabled] -tcbackend [jax or tensorflow]` then a `.json` file will be created in data folder which contains the information of benchmarking parameters and results. diff --git a/benchmarks/scripts/qml_pennylane.py b/benchmarks/scripts/qml_pennylane.py index 68c3db0c..0068ee1d 100644 --- a/benchmarks/scripts/qml_pennylane.py +++ b/benchmarks/scripts/qml_pennylane.py @@ -10,8 +10,6 @@ import tensorcircuit as tc import utils -tc.set_backend("tensorflow") - def pennylane_benchmark( uuid, @@ -60,6 +58,52 @@ def pennylane_benchmark( ) meta["Results"] = {} + dev = qml.device("lightning.qubit", wires=nwires) + + K = tc.get_backend("tensorflow") + + @qml.qnode(dev, diff_method="adjoint", interface="tf") + def lt_expval(img, params): + for i in range(nwires - 1): + qml.RX(img[i] * np.pi, wires=i) + for j in range(nlayer): + for i in range(nwires - 1): + qml.IsingZZ(params[i + j * 2 * nwires], wires=[i, nwires - 1]) + for i in range(nwires): + qml.RX(params[nwires + i + j * 2 * nwires], wires=i) + return qml.expval(qml.Hamiltonian([1.0], [qml.PauliZ(nwires - 1)], True)) + + def loss(imgs, lbls, param): + params = K.stack([param for _ in range(lbls.shape[0])]) + params = K.transpose(params, [1, 0]) + return K.mean( + (lbls - K.cast(lt_expval(imgs, params), "float32") * 0.5 - 0.5) ** 2 + ) + + vag = K.value_and_grad(loss, argnums=2) + param = K.convert_to_tensor((np.random.normal(size=[nlayer * 2 * nwires]))) + + def f(train_imgs, train_lbls): + e, grad = vag( + K.transpose( + K.convert_to_tensor(np.array(train_imgs).astype(np.float32)), (1, 0) + ), + K.reshape( + K.convert_to_tensor(np.array(train_lbls).astype(np.float32)), (-1) + ), + param, + ) + return e + + ct, it, Nitrs = utils.qml_timing(f, nbatch, nitrs, timeLimit) + meta["Results"]["lightning"] = { + "Construction time": ct, + "Iteration time": it, + "# of actual iterations": Nitrs, + } + + print(meta["Results"]["lightning"]) + dev = qml.device("default.qubit.jax", wires=nwires) @qml.qnode(dev, interface="jax") @@ -196,7 +240,7 @@ def tf_loss(img, lbl, params): return loss tf_vvag = tf.function( - tc.backend.vvag(tf_loss, vectorized_argnums=(0, 1), argnums=2) + tc.get_backend("tensorflow").vvag(tf_loss, vectorized_argnums=(0, 1), argnums=2) ) def f(train_imgs, train_lbls): diff --git a/benchmarks/scripts/utils.py b/benchmarks/scripts/utils.py index 4ca014a1..82b2a01d 100644 --- a/benchmarks/scripts/utils.py +++ b/benchmarks/scripts/utils.py @@ -6,6 +6,8 @@ import numpy as np import tensorflow as tf from pathlib import Path +import optax +import tensorcircuit as tc qml_data = {} @@ -126,6 +128,7 @@ def mnist_data_preprocessing(PCA_components=10): if qml_data == {}: if Path("mnist.npz").exists(): print("load local dataset") + # from https://www.kaggle.com/vikramtiwari/mnist-numpy def load_data(path): with np.load(path) as f: @@ -218,18 +221,16 @@ def save(data, _uuid, path): def timing(f, nitrs, timeLimit): t0 = time.time() - print(f()) + a = f() t1 = time.time() Nitrs = 1e-8 for i in range(nitrs): a = f() - print(a) # if a != None: # print(a) + Nitrs += 1 if time.time() - t1 > timeLimit: break - else: - Nitrs += 1 t2 = time.time() return t1 - t0, (t2 - t1) / Nitrs, int(Nitrs) @@ -254,24 +255,38 @@ def qml_timing(f, nbatch, nitrs, timeLimit, tfq=False): ) if a is not None: print(a) + Nitrs += 1 if time.time() - t1 > timeLimit: break - else: - Nitrs += 1 t2 = time.time() return t1 - t0, (t2 - t1) / Nitrs, int(Nitrs) class Opt: - def __init__(self, f, params, lr=0.01, tuning=True): + def __init__(self, f, params, lr=0.002, tuning=True, backend="tensorflow"): self.f = f self.params = params - self.adam = tf.keras.optimizers.Adam(lr) + if backend == "tensorflow": + self.adam = tc.backend.optimizer(tf.keras.optimizers.Adam(lr)) + elif backend == "jax": + self.adam = tc.backend.optimizer(optax.adam(lr)) + elif backend == "numpy": + self.adam = tc.get_backend("tensorflow").optimizer( + tf.keras.optimizers.Adam(lr) + ) self.tuning = tuning + self.backend = backend def step(self): - e, grad = self.f(*self.params) + if getattr(self.params, "shape", False): + e, grad = self.f(self.params) + else: + e, grad = self.f(*self.params) if self.tuning: - grad = [tf.convert_to_tensor(g) for g in grad] - self.adam.apply_gradients(zip(grad, self.params)) - return e[()] + if self.backend == "numpy": + self.params = tf.constant(self.params) + self.params = self.adam.update(grad, self.params) + if self.backend == "numpy": + self.params = self.params.numpy() + print(e) + return e diff --git a/benchmarks/scripts/vqe_pennylane.py b/benchmarks/scripts/vqe_pennylane.py index 6b62611b..6ab0ede3 100644 --- a/benchmarks/scripts/vqe_pennylane.py +++ b/benchmarks/scripts/vqe_pennylane.py @@ -129,6 +129,32 @@ def f(): "# of actual iterations": Nitrs, } + print("begin testing lightning") + dev = qml.device("lightning.qubit", wires=nwires) + + @qml.qnode(dev, diff_method="adjoint") + def lt_expval(params): + for i in range(nwires): + qml.Hadamard(wires=i) + for j in range(nlayer): + for i in range(nwires - minus): + qml.IsingZZ(params[i + j * 2 * nwires], wires=[i, (i + 1) % nwires]) + for i in range(nwires): + qml.RX(params[nwires + i + j * 2 * nwires], wires=i) + return qml.expval(Htfim) + + vag = qml.grad(lt_expval, argnum=0) + params = np.random.normal(size=[nlayer * 2 * nwires]) + + def f(): + return vag(params) + + ct, it, Nitrs = utils.timing(f, nitrs, timeLimit) + meta["Results"]["lightning.cpu"] = { + "Construction time": ct, + "Iteration time": it, + "# of actual iterations": Nitrs, + } # using tf interface and expvalcost # params.assign(np.random.normal(size=[nlayer * 2 * nwires])) # cost_fn_tf = qml.ExpvalCost(circuit, Htfim, dev, interface="tf") diff --git a/benchmarks/scripts/vqe_qibo.py b/benchmarks/scripts/vqe_qibo.py new file mode 100644 index 00000000..731c470a --- /dev/null +++ b/benchmarks/scripts/vqe_qibo.py @@ -0,0 +1,109 @@ +import datetime +import sys +import uuid +import cpuinfo + +import tensorcircuit as tc + +tc.set_backend("tensorflow") +import tensorflow as tf +from functools import reduce +from operator import mul +import qibo + +qibo.set_backend("tensorflow") +from qibo import gates, models, hamiltonians +from qibo.symbols import I, X, Z +import utils + + +def qibo_benchmark(uuid, n, nlayer, nitrs, timeLimit, minus=1): + @tf.function + def geth(s, n): + ctc = tc.Circuit(n, inputs=s) + loss = 0.0 + for i in range(n - 1): + loss += ctc.expectation_ps(z=[i, i + 1]) + for i in range(n): + loss -= ctc.expectation_ps(x=[i]) + return loss + + @tf.function # jit will raise error + def optimize(params): + with tf.GradientTape() as tape: + c = models.Circuit(n) + for i in range(n): + c.add(gates.H(i)) + for j in range(nlayer): + for i in range(n - minus): + c.add(gates.CNOT(i, i + 1)) + c.add(gates.RZ(i + 1, theta=params[j, i, 0])) + c.add(gates.CNOT(i, i + 1)) + for i in range(n): + c.add(gates.RX(0, theta=params[j, i, 1])) + s = c().state() + # h = hamiltonians.TFIM(n, h=-1) + # loss = h.expectation(s) + # failed in jit + # qibo lacks the API for local expectation, using tc as assistance + # we must ensure the observable is computed term by term + # instead of the expectation for the Hamiltonian as a whole + # to ensure the comparison is fair + + loss = geth(s, n) + + grads = tape.gradient(loss, params) + return loss, grads + + meta = {} + meta["Software"] = "qibo" + meta["minus"] = minus + meta["Cpuinfo"] = cpuinfo.get_cpu_info()["brand_raw"] + meta["Version"] = { + "sys": sys.version, + "qibo": qibo.__version__, + } + meta["VQE test parameters"] = { + "nQubits": n, + "nlayer": nlayer, + "nitrs": nitrs, + "timeLimit": timeLimit, + } + meta["UUID"] = uuid + meta["Benchmark Time"] = ( + datetime.datetime.now().astimezone().strftime("%Y-%m-%d %H:%M %Z") + ) + meta["Results"] = {} + params = tf.Variable(tf.random.normal((nlayer, n, 2), dtype=tf.float64)) + opt = utils.Opt(optimize, params, tuning=True, backend="tensorflow") + ct, it, Nitrs = utils.timing(opt.step, nitrs, timeLimit) + meta["Results"]["with jit"] = { + "Construction time": ct, + "Iteration time": it, + "# of actual iterations": Nitrs, + } + print(meta) + return meta + + +if __name__ == "__main__": + _uuid = str(uuid.uuid4()) + ( + n, + nlayer, + nitrs, + timeLimit, + isgpu, + minus, + path, + ) = utils.arg() + + r = qibo_benchmark( + _uuid, + n, + nlayer, + nitrs, + timeLimit, + minus=minus, + ) + utils.save(r, _uuid, path) diff --git a/benchmarks/scripts/vqe_qiskit.py b/benchmarks/scripts/vqe_qiskit.py new file mode 100644 index 00000000..5b94ae23 --- /dev/null +++ b/benchmarks/scripts/vqe_qiskit.py @@ -0,0 +1,108 @@ +import sys +import datetime +from functools import reduce +from operator import xor, add +import uuid +import numpy as np +import cpuinfo + +import qiskit +from qiskit.opflow import I, X, Z, StateFn +from qiskit.circuit import QuantumCircuit, ParameterVector +from qiskit.opflow.gradients import Gradient + +import utils + + +def qiskit_benchmark(uuid, n, nlayer, nitrs, timeLimit, minus=1): + def tfim_hamiltonian(n, j=1.0, h=-1.0): + hams = [] + for i in range(n): + xi = [I for _ in range(n)] + xi[i] = X + hams.append(h * reduce(xor, xi)) + for i in range(n - 1): + zzi = [I for _ in range(n)] + zzi[i] = Z + zzi[i + 1] = Z + hams.append(j * reduce(xor, zzi)) + return reduce(add, hams) + + def gradf(values): + hamiltonian = tfim_hamiltonian(n) + c = QuantumCircuit(n) + params = ParameterVector("theta", length=2 * n * nlayer) + for i in range(n): + c.h(i) + for j in range(nlayer): + for i in range(n - minus): + c.rzz( + params[j * n * 2 + i * 2], + i, + (i + 1) % n, + ) + for i in range(n): + c.rx(params[j * n * 2 + i * 2 + 1], i) + + # Define the expectation value corresponding to the energy + op = ~StateFn(hamiltonian) @ StateFn(c) + grad = Gradient().convert(operator=op, params=params) + + value_dict = {params: values} + exp_result = op.assign_parameters(value_dict).eval() + grad_result = grad.assign_parameters(value_dict).eval() + return np.real(exp_result), np.real(np.array(grad_result)) + + meta = {} + meta["Software"] = "qiskit" + meta["minus"] = minus + meta["Cpuinfo"] = cpuinfo.get_cpu_info()["brand_raw"] + meta["Version"] = { + "sys": sys.version, + "numpy": np.__version__, + "qiskit": qiskit.version.VERSION, + } + meta["VQE test parameters"] = { + "nQubits": n, + "nlayer": nlayer, + "nitrs": nitrs, + "timeLimit": timeLimit, + } + meta["UUID"] = uuid + meta["Benchmark Time"] = ( + datetime.datetime.now().astimezone().strftime("%Y-%m-%d %H:%M %Z") + ) + meta["Results"] = {} + param = np.random.normal(scale=0.1, size=[2 * n * nlayer]) + opt = utils.Opt(gradf, param, tuning=True, backend="numpy") + ct, it, Nitrs = utils.timing(opt.step, nitrs, timeLimit) + meta["Results"]["with jit"] = { + "Construction time": ct, + "Iteration time": it, + "# of actual iterations": Nitrs, + } + print(meta) + return meta + + +if __name__ == "__main__": + _uuid = str(uuid.uuid4()) + ( + n, + nlayer, + nitrs, + timeLimit, + isgpu, + minus, + path, + ) = utils.arg() + + r = qiskit_benchmark( + _uuid, + n, + nlayer, + nitrs, + timeLimit, + minus=minus, + ) + utils.save(r, _uuid, path) diff --git a/benchmarks/scripts/vqe_tc.py b/benchmarks/scripts/vqe_tc.py index 515afc1e..b626c1b4 100644 --- a/benchmarks/scripts/vqe_tc.py +++ b/benchmarks/scripts/vqe_tc.py @@ -109,7 +109,7 @@ def energy_raw(paramx, paramzz): c = tc.Circuit(n) else: c = tc.MPSCircuit(n) - c.set_truncation_rule(max_singular_values=mpsd) + c.set_split_rules({"max_singular_values": mpsd}) paramx = tc.backend.cast(paramx, dtype) paramzz = tc.backend.cast(paramzz, dtype) @@ -132,8 +132,8 @@ def energy_raw(paramx, paramzz): e = tfi_energy(c, n) fd = c._fidelity # tensorflow only works for complex case, while jax only works for real case, don't know how to solve it - if tcbackend != "tensorflow": - e = tc.backend.real(e) + # if tcbackend != "tensorflow": + e = tc.backend.real(e) return e, fd @@ -144,12 +144,13 @@ def energy(paramx, paramzz): # paramx = tc.backend.convert_to_tensor(paramx) # paramzz = tc.backend.convert_to_tensor(paramzz) (value, f), grads = energy_raw(paramx, paramzz) - print(tc.backend.numpy(f), tc.backend.numpy(value)) + print("fidelity: ", f, value) + # print(tc.backend.numpy(f), tc.backend.numpy(value)) # value = tc.backend.numpy(tc.backend.real(value)) # grads = [tc.backend.numpy(tc.backend.real(g)) for g in grads] return value, grads - opt = utils.Opt(energy, [paramx, paramzz], tuning=False) + opt = utils.Opt(energy, [paramx, paramzz], tuning=True, backend=tcbackend) ct, it, Nitrs = utils.timing(opt.step, nitrs, timeLimit) meta["Results"]["with jit"] = { "Construction time": ct, diff --git a/check_all.sh b/check_all.sh old mode 100755 new mode 100644 index 3ef52ecd..84e839ef --- a/check_all.sh +++ b/check_all.sh @@ -8,6 +8,8 @@ echo "pylint check" pylint tensorcircuit tests examples/*.py echo "pytest check" pytest -n auto --cov=tensorcircuit -vv -W ignore::DeprecationWarning +# for test on gpu machine, please set `export TF_FORCE_GPU_ALLOW_GROWTH=true` for tf +# and `export XLA_PYTHON_CLIENT_PREALLOCATE=false` for jax to avoid OOM in testing echo "sphinx check" cd docs && sphinx-build source build/html && sphinx-build source -D language="zh" build/html_cn echo "all checks passed, congratulation! 💐" diff --git a/codecov.yml b/codecov.yml index 3cad6aa6..1708389f 100644 --- a/codecov.yml +++ b/codecov.yml @@ -1,2 +1,2 @@ ignore: - - "applications" + - "tensorcircuit/applications" diff --git a/docker/Dockerfile b/docker/Dockerfile index aba5d7df..718fbc2c 100644 --- a/docker/Dockerfile +++ b/docker/Dockerfile @@ -33,7 +33,7 @@ COPY requirements/requirements-docker.txt /app/requirements-docker.txt RUN pip install -r /app/requirements-docker.txt -RUN pip install jaxlib==0.3.2+cuda11.cudnn805 -f https://storage.googleapis.com/jax-releases/jax_releases.html +RUN pip install jaxlib==0.3.2+cuda11.cudnn805 -f https://storage.googleapis.com/jax-releases/jax_cuda_releases.html RUN pip install -U git+https://github.com/jcmgray/cotengra.git diff --git a/docker/Dockerfile_v2 b/docker/Dockerfile_v2 new file mode 100644 index 00000000..9f2f4523 --- /dev/null +++ b/docker/Dockerfile_v2 @@ -0,0 +1,37 @@ +FROM nvidia/cuda:11.7.1-cudnn8-devel-ubuntu20.04 +# nvidia/cuda:11.6.0-cudnn8-devel-ubuntu20.04 + +RUN apt update && DEBIAN_FRONTEND=noninteractive apt install -y \ + wget \ + git \ + vim \ + pandoc + +RUN wget -q -P /tmp \ + https://repo.anaconda.com/miniconda/Miniconda3-latest-Linux-x86_64.sh \ + && bash /tmp/Miniconda3-latest-Linux-x86_64.sh -b -p /opt/conda \ + && rm /tmp/Miniconda3-latest-Linux-x86_64.sh + +ENV PATH="/opt/conda/bin:$PATH" + +RUN conda install -y \ + pip \ + python=3.10 + +COPY requirements/requirements-docker-v2.txt /requirements-docker-v2.txt + +# RUN pip install -r /requirements-docker-v2.txt -f https://storage.googleapis.com/jax-releases/jax_cuda_releases.html +RUN pip install -i https://pypi.tuna.tsinghua.edu.cn/simple -r /requirements-docker-v2.txt -f https://storage.googleapis.com/jax-releases/jax_cuda_releases.html + +# RUN pip install nvidia-cudnn-cu11==8.6.0.163 ray +RUN pip install -i https://pypi.tuna.tsinghua.edu.cn/simple nvidia-cudnn-cu11==8.6.0.163 ray + +RUN pip install tensorcircuit + +# requirements conflict for ray +# jax must have cudnn>8.6 otherwise fail when init array on gpu, +# while torch insists cudnn 8.5 in setup but 8.6 can also work for torch + +RUN echo export TF_CPP_MIN_LOG_LEVEL=3 >> ~/.bashrc + +CMD ["/bin/bash"] \ No newline at end of file diff --git a/docker/README.md b/docker/README.md index 4b011c1b..1c5aea0d 100644 --- a/docker/README.md +++ b/docker/README.md @@ -4,22 +4,26 @@ Run the following command to build the docker for tensorcircuit at parent path: sudo docker build . -f docker/Dockerfile -t tensorcircuit ``` -One can also pull the [official image](https://hub.docker.com/repository/docker/tensorcircuit/tensorcircuit) from DockerHub as +Since v0.10 we introduce new docker env based on ubuntu20.04+cuda11.7+py3.10 (+ pip installed tensorcircuit package), build the new docker use ```bash -sudo docker pull tensorcircuit/tensorcircuit +sudo docker build . -f docker/Dockerfile_v2 -t tensorcircuit ``` +One can also pull the [official image](https://hub.docker.com/repository/docker/tensorcircuit/tensorcircuit) from DockerHub as + +```bash +sudo docker pull tensorcircuit/tensorcircuit +``` Run the docker container by the following command: ```bash sudo docker run -it --network host --gpus all tensorcircuit -# if one also wants mount local source code, also add args `-v "$(pwd)":/app` +# if one also wants to mount local source code, also add args `-v "$(pwd)":/root` -# for old dockerfile with no runtime env setting -# sudo docker run -it --network host -e LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/usr/local/cuda-11.0/targets/x86_64-linux/lib -e PYTHONPATH=/app -v "$(pwd)":/app --gpus all tensorcircuit +# using tensorcircuit/tensorcircuit:latest to run the prebuild docker image from dockerhub ``` -`export TF_CPP_MIN_LOG_LEVEL=3` maybe necessary since jax suprisingly frequently complain about ptxas version problem. And `export CUDA_VISIBLE_DEVICES=-1` if you want to test only on CPU. +`export CUDA_VISIBLE_DEVICES=-1` if you want to test only on CPU. diff --git a/docs/source/advance.rst b/docs/source/advance.rst index 4b041646..4539326e 100644 --- a/docs/source/advance.rst +++ b/docs/source/advance.rst @@ -7,6 +7,16 @@ MPS Simulator (Still experimental support) +Very simple, we provide the same set of API for ``MPSCircuit`` as ``Circuit``, +the only new line is to set the bond dimension for the new simulator. + +.. code-block:: python + + c = tc.MPSCircuit(n) + c.set_split_rules({"max_singular_values": 50}) + +The larger bond dimension we set, the better approximation ratio (of course the more computational cost we pay) + Split Two-qubit Gates ------------------------- diff --git a/docs/source/api/about.rst b/docs/source/api/about.rst new file mode 100644 index 00000000..e065f1eb --- /dev/null +++ b/docs/source/api/about.rst @@ -0,0 +1,7 @@ +tensorcircuit.about +================================================================================ +.. automodule:: tensorcircuit.about + :members: + :undoc-members: + :show-inheritance: + :inherited-members: \ No newline at end of file diff --git a/docs/source/api/abstractcircuit.rst b/docs/source/api/abstractcircuit.rst index 2caf0af1..3d67a499 100644 --- a/docs/source/api/abstractcircuit.rst +++ b/docs/source/api/abstractcircuit.rst @@ -1,5 +1,5 @@ tensorcircuit.abstractcircuit -================================================== +================================================================================ .. automodule:: tensorcircuit.abstractcircuit :members: :undoc-members: diff --git a/docs/source/api/applications.rst b/docs/source/api/applications.rst index ad329ccf..85c31126 100644 --- a/docs/source/api/applications.rst +++ b/docs/source/api/applications.rst @@ -1,9 +1,13 @@ tensorcircuit.applications -================================================== +================================================================================ .. toctree:: + applications/ai.rst applications/dqas.rst + applications/finance.rst applications/graphdata.rst applications/layers.rst + applications/optimization.rst + applications/physics.rst applications/utils.rst applications/vags.rst applications/van.rst diff --git a/docs/source/api/applications/ai.rst b/docs/source/api/applications/ai.rst new file mode 100644 index 00000000..96a22cdb --- /dev/null +++ b/docs/source/api/applications/ai.rst @@ -0,0 +1,4 @@ +tensorcircuit.applications.ai +================================================================================ +.. toctree:: + ai/ensemble.rst \ No newline at end of file diff --git a/docs/source/api/applications/ai/ensemble.rst b/docs/source/api/applications/ai/ensemble.rst new file mode 100644 index 00000000..0173ac00 --- /dev/null +++ b/docs/source/api/applications/ai/ensemble.rst @@ -0,0 +1,7 @@ +tensorcircuit.applications.ai.ensemble +================================================================================ +.. automodule:: tensorcircuit.applications.ai.ensemble + :members: + :undoc-members: + :show-inheritance: + :inherited-members: \ No newline at end of file diff --git a/docs/source/api/applications/dqas.rst b/docs/source/api/applications/dqas.rst index 32457e1f..73cacd43 100644 --- a/docs/source/api/applications/dqas.rst +++ b/docs/source/api/applications/dqas.rst @@ -1,5 +1,5 @@ tensorcircuit.applications.dqas -================================================== +================================================================================ .. automodule:: tensorcircuit.applications.dqas :members: :undoc-members: diff --git a/docs/source/api/applications/finance.rst b/docs/source/api/applications/finance.rst new file mode 100644 index 00000000..d3302b31 --- /dev/null +++ b/docs/source/api/applications/finance.rst @@ -0,0 +1,4 @@ +tensorcircuit.applications.finance +================================================================================ +.. toctree:: + finance/portfolio.rst \ No newline at end of file diff --git a/docs/source/api/applications/finance/portfolio.rst b/docs/source/api/applications/finance/portfolio.rst new file mode 100644 index 00000000..993b5754 --- /dev/null +++ b/docs/source/api/applications/finance/portfolio.rst @@ -0,0 +1,7 @@ +tensorcircuit.applications.finance.portfolio +================================================================================ +.. automodule:: tensorcircuit.applications.finance.portfolio + :members: + :undoc-members: + :show-inheritance: + :inherited-members: \ No newline at end of file diff --git a/docs/source/api/applications/graphdata.rst b/docs/source/api/applications/graphdata.rst index 22e1af13..44851513 100644 --- a/docs/source/api/applications/graphdata.rst +++ b/docs/source/api/applications/graphdata.rst @@ -1,5 +1,5 @@ tensorcircuit.applications.graphdata -================================================== +================================================================================ .. automodule:: tensorcircuit.applications.graphdata :members: :undoc-members: diff --git a/docs/source/api/applications/layers.rst b/docs/source/api/applications/layers.rst index 69303e98..d4f49e81 100644 --- a/docs/source/api/applications/layers.rst +++ b/docs/source/api/applications/layers.rst @@ -1,5 +1,5 @@ tensorcircuit.applications.layers -================================================== +================================================================================ .. automodule:: tensorcircuit.applications.layers :members: :undoc-members: diff --git a/docs/source/api/applications/optimization.rst b/docs/source/api/applications/optimization.rst new file mode 100644 index 00000000..87a0ffbb --- /dev/null +++ b/docs/source/api/applications/optimization.rst @@ -0,0 +1,7 @@ +tensorcircuit.applications.optimization +================================================================================ +.. automodule:: tensorcircuit.applications.optimization + :members: + :undoc-members: + :show-inheritance: + :inherited-members: \ No newline at end of file diff --git a/docs/source/api/applications/physics.rst b/docs/source/api/applications/physics.rst new file mode 100644 index 00000000..98d1a2ed --- /dev/null +++ b/docs/source/api/applications/physics.rst @@ -0,0 +1,5 @@ +tensorcircuit.applications.physics +================================================================================ +.. toctree:: + physics/baseline.rst + physics/fss.rst \ No newline at end of file diff --git a/docs/source/api/applications/physics/baseline.rst b/docs/source/api/applications/physics/baseline.rst new file mode 100644 index 00000000..2ac581ba --- /dev/null +++ b/docs/source/api/applications/physics/baseline.rst @@ -0,0 +1,7 @@ +tensorcircuit.applications.physics.baseline +================================================================================ +.. automodule:: tensorcircuit.applications.physics.baseline + :members: + :undoc-members: + :show-inheritance: + :inherited-members: \ No newline at end of file diff --git a/docs/source/api/applications/physics/fss.rst b/docs/source/api/applications/physics/fss.rst new file mode 100644 index 00000000..d65cd6c1 --- /dev/null +++ b/docs/source/api/applications/physics/fss.rst @@ -0,0 +1,7 @@ +tensorcircuit.applications.physics.fss +================================================================================ +.. automodule:: tensorcircuit.applications.physics.fss + :members: + :undoc-members: + :show-inheritance: + :inherited-members: \ No newline at end of file diff --git a/docs/source/api/applications/utils.rst b/docs/source/api/applications/utils.rst index d4549700..4114e7d8 100644 --- a/docs/source/api/applications/utils.rst +++ b/docs/source/api/applications/utils.rst @@ -1,5 +1,5 @@ tensorcircuit.applications.utils -================================================== +================================================================================ .. automodule:: tensorcircuit.applications.utils :members: :undoc-members: diff --git a/docs/source/api/applications/vags.rst b/docs/source/api/applications/vags.rst index 5b951bd3..af0f451f 100644 --- a/docs/source/api/applications/vags.rst +++ b/docs/source/api/applications/vags.rst @@ -1,5 +1,5 @@ tensorcircuit.applications.vags -================================================== +================================================================================ .. automodule:: tensorcircuit.applications.vags :members: :undoc-members: diff --git a/docs/source/api/applications/van.rst b/docs/source/api/applications/van.rst index 463e44d2..5c90f2e5 100644 --- a/docs/source/api/applications/van.rst +++ b/docs/source/api/applications/van.rst @@ -1,5 +1,5 @@ tensorcircuit.applications.van -================================================== +================================================================================ .. automodule:: tensorcircuit.applications.van :members: :undoc-members: diff --git a/docs/source/api/applications/vqes.rst b/docs/source/api/applications/vqes.rst index e3c775e5..d868c634 100644 --- a/docs/source/api/applications/vqes.rst +++ b/docs/source/api/applications/vqes.rst @@ -1,5 +1,5 @@ tensorcircuit.applications.vqes -================================================== +================================================================================ .. automodule:: tensorcircuit.applications.vqes :members: :undoc-members: diff --git a/docs/source/api/backends.rst b/docs/source/api/backends.rst index 0186313f..cfc63bec 100644 --- a/docs/source/api/backends.rst +++ b/docs/source/api/backends.rst @@ -1,7 +1,8 @@ tensorcircuit.backends -================================================== +================================================================================ .. toctree:: backends/backend_factory.rst + backends/cupy_backend.rst backends/jax_backend.rst backends/numpy_backend.rst backends/pytorch_backend.rst diff --git a/docs/source/api/backends/backend_factory.rst b/docs/source/api/backends/backend_factory.rst index 8864abfe..6df6374e 100644 --- a/docs/source/api/backends/backend_factory.rst +++ b/docs/source/api/backends/backend_factory.rst @@ -1,5 +1,5 @@ tensorcircuit.backends.backend_factory -================================================== +================================================================================ .. automodule:: tensorcircuit.backends.backend_factory :members: :undoc-members: diff --git a/docs/source/api/backends/cupy_backend.rst b/docs/source/api/backends/cupy_backend.rst new file mode 100644 index 00000000..1e2421eb --- /dev/null +++ b/docs/source/api/backends/cupy_backend.rst @@ -0,0 +1,7 @@ +tensorcircuit.backends.cupy_backend +================================================================================ +.. automodule:: tensorcircuit.backends.cupy_backend + :members: + :undoc-members: + :show-inheritance: + :inherited-members: \ No newline at end of file diff --git a/docs/source/api/backends/jax_backend.rst b/docs/source/api/backends/jax_backend.rst index e0dfe7c3..209409bc 100644 --- a/docs/source/api/backends/jax_backend.rst +++ b/docs/source/api/backends/jax_backend.rst @@ -1,5 +1,5 @@ tensorcircuit.backends.jax_backend -================================================== +================================================================================ .. automodule:: tensorcircuit.backends.jax_backend :members: :undoc-members: diff --git a/docs/source/api/backends/numpy_backend.rst b/docs/source/api/backends/numpy_backend.rst index af19d26b..735f969f 100644 --- a/docs/source/api/backends/numpy_backend.rst +++ b/docs/source/api/backends/numpy_backend.rst @@ -1,5 +1,5 @@ tensorcircuit.backends.numpy_backend -================================================== +================================================================================ .. automodule:: tensorcircuit.backends.numpy_backend :members: :undoc-members: diff --git a/docs/source/api/backends/pytorch_backend.rst b/docs/source/api/backends/pytorch_backend.rst index df2712c6..0d10f664 100644 --- a/docs/source/api/backends/pytorch_backend.rst +++ b/docs/source/api/backends/pytorch_backend.rst @@ -1,5 +1,5 @@ tensorcircuit.backends.pytorch_backend -================================================== +================================================================================ .. automodule:: tensorcircuit.backends.pytorch_backend :members: :undoc-members: diff --git a/docs/source/api/backends/tensorflow_backend.rst b/docs/source/api/backends/tensorflow_backend.rst index 52663b1a..a595418e 100644 --- a/docs/source/api/backends/tensorflow_backend.rst +++ b/docs/source/api/backends/tensorflow_backend.rst @@ -1,5 +1,5 @@ tensorcircuit.backends.tensorflow_backend -================================================== +================================================================================ .. automodule:: tensorcircuit.backends.tensorflow_backend :members: :undoc-members: diff --git a/docs/source/api/basecircuit.rst b/docs/source/api/basecircuit.rst index 6b014bb1..79c2636e 100644 --- a/docs/source/api/basecircuit.rst +++ b/docs/source/api/basecircuit.rst @@ -1,5 +1,5 @@ tensorcircuit.basecircuit -================================================== +================================================================================ .. automodule:: tensorcircuit.basecircuit :members: :undoc-members: diff --git a/docs/source/api/channels.rst b/docs/source/api/channels.rst index 3a7cf3af..d9d6fd00 100644 --- a/docs/source/api/channels.rst +++ b/docs/source/api/channels.rst @@ -1,5 +1,5 @@ tensorcircuit.channels -================================================== +================================================================================ .. automodule:: tensorcircuit.channels :members: :undoc-members: diff --git a/docs/source/api/circuit.rst b/docs/source/api/circuit.rst index 910c5ef6..59c76ddd 100644 --- a/docs/source/api/circuit.rst +++ b/docs/source/api/circuit.rst @@ -1,5 +1,5 @@ tensorcircuit.circuit -================================================== +================================================================================ .. automodule:: tensorcircuit.circuit :members: :undoc-members: diff --git a/docs/source/api/cloud.rst b/docs/source/api/cloud.rst new file mode 100644 index 00000000..be2faf7d --- /dev/null +++ b/docs/source/api/cloud.rst @@ -0,0 +1,11 @@ +tensorcircuit.cloud +================================================================================ +.. toctree:: + cloud/abstraction.rst + cloud/apis.rst + cloud/config.rst + cloud/local.rst + cloud/quafu_provider.rst + cloud/tencent.rst + cloud/utils.rst + cloud/wrapper.rst \ No newline at end of file diff --git a/docs/source/api/cloud/abstraction.rst b/docs/source/api/cloud/abstraction.rst new file mode 100644 index 00000000..3f00247c --- /dev/null +++ b/docs/source/api/cloud/abstraction.rst @@ -0,0 +1,7 @@ +tensorcircuit.cloud.abstraction +================================================================================ +.. automodule:: tensorcircuit.cloud.abstraction + :members: + :undoc-members: + :show-inheritance: + :inherited-members: \ No newline at end of file diff --git a/docs/source/api/cloud/apis.rst b/docs/source/api/cloud/apis.rst new file mode 100644 index 00000000..fe623eec --- /dev/null +++ b/docs/source/api/cloud/apis.rst @@ -0,0 +1,7 @@ +tensorcircuit.cloud.apis +================================================================================ +.. automodule:: tensorcircuit.cloud.apis + :members: + :undoc-members: + :show-inheritance: + :inherited-members: \ No newline at end of file diff --git a/docs/source/api/cloud/config.rst b/docs/source/api/cloud/config.rst new file mode 100644 index 00000000..8f6282a0 --- /dev/null +++ b/docs/source/api/cloud/config.rst @@ -0,0 +1,7 @@ +tensorcircuit.cloud.config +================================================================================ +.. automodule:: tensorcircuit.cloud.config + :members: + :undoc-members: + :show-inheritance: + :inherited-members: \ No newline at end of file diff --git a/docs/source/api/cloud/local.rst b/docs/source/api/cloud/local.rst new file mode 100644 index 00000000..649f66d6 --- /dev/null +++ b/docs/source/api/cloud/local.rst @@ -0,0 +1,7 @@ +tensorcircuit.cloud.local +================================================================================ +.. automodule:: tensorcircuit.cloud.local + :members: + :undoc-members: + :show-inheritance: + :inherited-members: \ No newline at end of file diff --git a/docs/source/api/cloud/quafu_provider.rst b/docs/source/api/cloud/quafu_provider.rst new file mode 100644 index 00000000..06d15eee --- /dev/null +++ b/docs/source/api/cloud/quafu_provider.rst @@ -0,0 +1,7 @@ +tensorcircuit.cloud.quafu_provider +================================================================================ +.. automodule:: tensorcircuit.cloud.quafu_provider + :members: + :undoc-members: + :show-inheritance: + :inherited-members: \ No newline at end of file diff --git a/docs/source/api/cloud/tencent.rst b/docs/source/api/cloud/tencent.rst new file mode 100644 index 00000000..431c3294 --- /dev/null +++ b/docs/source/api/cloud/tencent.rst @@ -0,0 +1,7 @@ +tensorcircuit.cloud.tencent +================================================================================ +.. automodule:: tensorcircuit.cloud.tencent + :members: + :undoc-members: + :show-inheritance: + :inherited-members: \ No newline at end of file diff --git a/docs/source/api/cloud/utils.rst b/docs/source/api/cloud/utils.rst new file mode 100644 index 00000000..a7e33fe4 --- /dev/null +++ b/docs/source/api/cloud/utils.rst @@ -0,0 +1,7 @@ +tensorcircuit.cloud.utils +================================================================================ +.. automodule:: tensorcircuit.cloud.utils + :members: + :undoc-members: + :show-inheritance: + :inherited-members: \ No newline at end of file diff --git a/docs/source/api/cloud/wrapper.rst b/docs/source/api/cloud/wrapper.rst new file mode 100644 index 00000000..d65d3c07 --- /dev/null +++ b/docs/source/api/cloud/wrapper.rst @@ -0,0 +1,7 @@ +tensorcircuit.cloud.wrapper +================================================================================ +.. automodule:: tensorcircuit.cloud.wrapper + :members: + :undoc-members: + :show-inheritance: + :inherited-members: \ No newline at end of file diff --git a/docs/source/api/compiler.rst b/docs/source/api/compiler.rst new file mode 100644 index 00000000..cb47419f --- /dev/null +++ b/docs/source/api/compiler.rst @@ -0,0 +1,6 @@ +tensorcircuit.compiler +================================================================================ +.. toctree:: + compiler/composed_compiler.rst + compiler/qiskit_compiler.rst + compiler/simple_compiler.rst \ No newline at end of file diff --git a/docs/source/api/compiler/composed_compiler.rst b/docs/source/api/compiler/composed_compiler.rst new file mode 100644 index 00000000..07f7f23e --- /dev/null +++ b/docs/source/api/compiler/composed_compiler.rst @@ -0,0 +1,7 @@ +tensorcircuit.compiler.composed_compiler +================================================================================ +.. automodule:: tensorcircuit.compiler.composed_compiler + :members: + :undoc-members: + :show-inheritance: + :inherited-members: \ No newline at end of file diff --git a/docs/source/api/compiler/qiskit_compiler.rst b/docs/source/api/compiler/qiskit_compiler.rst new file mode 100644 index 00000000..b46ae8dc --- /dev/null +++ b/docs/source/api/compiler/qiskit_compiler.rst @@ -0,0 +1,7 @@ +tensorcircuit.compiler.qiskit_compiler +================================================================================ +.. automodule:: tensorcircuit.compiler.qiskit_compiler + :members: + :undoc-members: + :show-inheritance: + :inherited-members: \ No newline at end of file diff --git a/docs/source/api/compiler/simple_compiler.rst b/docs/source/api/compiler/simple_compiler.rst new file mode 100644 index 00000000..941efba5 --- /dev/null +++ b/docs/source/api/compiler/simple_compiler.rst @@ -0,0 +1,7 @@ +tensorcircuit.compiler.simple_compiler +================================================================================ +.. automodule:: tensorcircuit.compiler.simple_compiler + :members: + :undoc-members: + :show-inheritance: + :inherited-members: \ No newline at end of file diff --git a/docs/source/api/cons.rst b/docs/source/api/cons.rst index 6e077058..d4f48ab6 100644 --- a/docs/source/api/cons.rst +++ b/docs/source/api/cons.rst @@ -1,5 +1,5 @@ tensorcircuit.cons -================================================== +================================================================================ .. automodule:: tensorcircuit.cons :members: :undoc-members: diff --git a/docs/source/api/densitymatrix.rst b/docs/source/api/densitymatrix.rst index 571647d2..274dc323 100644 --- a/docs/source/api/densitymatrix.rst +++ b/docs/source/api/densitymatrix.rst @@ -1,5 +1,5 @@ tensorcircuit.densitymatrix -================================================== +================================================================================ .. automodule:: tensorcircuit.densitymatrix :members: :undoc-members: diff --git a/docs/source/api/experimental.rst b/docs/source/api/experimental.rst index 16761d4c..dbdfa068 100644 --- a/docs/source/api/experimental.rst +++ b/docs/source/api/experimental.rst @@ -1,5 +1,5 @@ tensorcircuit.experimental -================================================== +================================================================================ .. automodule:: tensorcircuit.experimental :members: :undoc-members: diff --git a/docs/source/api/fgs.rst b/docs/source/api/fgs.rst new file mode 100644 index 00000000..f00001b4 --- /dev/null +++ b/docs/source/api/fgs.rst @@ -0,0 +1,7 @@ +tensorcircuit.fgs +================================================================================ +.. automodule:: tensorcircuit.fgs + :members: + :undoc-members: + :show-inheritance: + :inherited-members: \ No newline at end of file diff --git a/docs/source/api/gates.rst b/docs/source/api/gates.rst index 8f72fbcc..71428553 100644 --- a/docs/source/api/gates.rst +++ b/docs/source/api/gates.rst @@ -1,5 +1,5 @@ tensorcircuit.gates -================================================== +================================================================================ .. automodule:: tensorcircuit.gates :members: :undoc-members: diff --git a/docs/source/api/interfaces.rst b/docs/source/api/interfaces.rst index 6371d824..5b234d0f 100644 --- a/docs/source/api/interfaces.rst +++ b/docs/source/api/interfaces.rst @@ -1,5 +1,5 @@ tensorcircuit.interfaces -================================================== +================================================================================ .. toctree:: interfaces/numpy.rst interfaces/scipy.rst diff --git a/docs/source/api/interfaces/numpy.rst b/docs/source/api/interfaces/numpy.rst index 5df8b0bb..5271b873 100644 --- a/docs/source/api/interfaces/numpy.rst +++ b/docs/source/api/interfaces/numpy.rst @@ -1,5 +1,5 @@ tensorcircuit.interfaces.numpy -================================================== +================================================================================ .. automodule:: tensorcircuit.interfaces.numpy :members: :undoc-members: diff --git a/docs/source/api/interfaces/scipy.rst b/docs/source/api/interfaces/scipy.rst index c263bd93..284dcbe9 100644 --- a/docs/source/api/interfaces/scipy.rst +++ b/docs/source/api/interfaces/scipy.rst @@ -1,5 +1,5 @@ tensorcircuit.interfaces.scipy -================================================== +================================================================================ .. automodule:: tensorcircuit.interfaces.scipy :members: :undoc-members: diff --git a/docs/source/api/interfaces/tensorflow.rst b/docs/source/api/interfaces/tensorflow.rst index e02981b9..8ac1a344 100644 --- a/docs/source/api/interfaces/tensorflow.rst +++ b/docs/source/api/interfaces/tensorflow.rst @@ -1,5 +1,5 @@ tensorcircuit.interfaces.tensorflow -================================================== +================================================================================ .. automodule:: tensorcircuit.interfaces.tensorflow :members: :undoc-members: diff --git a/docs/source/api/interfaces/tensortrans.rst b/docs/source/api/interfaces/tensortrans.rst index b666e177..a92b166d 100644 --- a/docs/source/api/interfaces/tensortrans.rst +++ b/docs/source/api/interfaces/tensortrans.rst @@ -1,5 +1,5 @@ tensorcircuit.interfaces.tensortrans -================================================== +================================================================================ .. automodule:: tensorcircuit.interfaces.tensortrans :members: :undoc-members: diff --git a/docs/source/api/interfaces/torch.rst b/docs/source/api/interfaces/torch.rst index 28090f54..5f7e3dea 100644 --- a/docs/source/api/interfaces/torch.rst +++ b/docs/source/api/interfaces/torch.rst @@ -1,5 +1,5 @@ tensorcircuit.interfaces.torch -================================================== +================================================================================ .. automodule:: tensorcircuit.interfaces.torch :members: :undoc-members: diff --git a/docs/source/api/keras.rst b/docs/source/api/keras.rst index 5ed313b2..9f2e4860 100644 --- a/docs/source/api/keras.rst +++ b/docs/source/api/keras.rst @@ -1,5 +1,5 @@ tensorcircuit.keras -================================================== +================================================================================ .. automodule:: tensorcircuit.keras :members: :undoc-members: diff --git a/docs/source/api/mps_base.rst b/docs/source/api/mps_base.rst index caf11b36..039da259 100644 --- a/docs/source/api/mps_base.rst +++ b/docs/source/api/mps_base.rst @@ -1,5 +1,5 @@ tensorcircuit.mps_base -================================================== +================================================================================ .. automodule:: tensorcircuit.mps_base :members: :undoc-members: diff --git a/docs/source/api/mpscircuit.rst b/docs/source/api/mpscircuit.rst index a4de8119..58a68f56 100644 --- a/docs/source/api/mpscircuit.rst +++ b/docs/source/api/mpscircuit.rst @@ -1,5 +1,5 @@ tensorcircuit.mpscircuit -================================================== +================================================================================ .. automodule:: tensorcircuit.mpscircuit :members: :undoc-members: diff --git a/docs/source/api/noisemodel.rst b/docs/source/api/noisemodel.rst new file mode 100644 index 00000000..4930d8f0 --- /dev/null +++ b/docs/source/api/noisemodel.rst @@ -0,0 +1,7 @@ +tensorcircuit.noisemodel +================================================================================ +.. automodule:: tensorcircuit.noisemodel + :members: + :undoc-members: + :show-inheritance: + :inherited-members: \ No newline at end of file diff --git a/docs/source/api/quantum.rst b/docs/source/api/quantum.rst index c9d13b6b..f25c8a5d 100644 --- a/docs/source/api/quantum.rst +++ b/docs/source/api/quantum.rst @@ -1,5 +1,5 @@ tensorcircuit.quantum -================================================== +================================================================================ .. automodule:: tensorcircuit.quantum :members: :undoc-members: diff --git a/docs/source/api/results.rst b/docs/source/api/results.rst new file mode 100644 index 00000000..2e60327c --- /dev/null +++ b/docs/source/api/results.rst @@ -0,0 +1,6 @@ +tensorcircuit.results +================================================================================ +.. toctree:: + results/counts.rst + results/qem.rst + results/readout_mitigation.rst \ No newline at end of file diff --git a/docs/source/api/results/counts.rst b/docs/source/api/results/counts.rst new file mode 100644 index 00000000..7f145206 --- /dev/null +++ b/docs/source/api/results/counts.rst @@ -0,0 +1,7 @@ +tensorcircuit.results.counts +================================================================================ +.. automodule:: tensorcircuit.results.counts + :members: + :undoc-members: + :show-inheritance: + :inherited-members: \ No newline at end of file diff --git a/docs/source/api/results/qem.rst b/docs/source/api/results/qem.rst new file mode 100644 index 00000000..160098f7 --- /dev/null +++ b/docs/source/api/results/qem.rst @@ -0,0 +1,5 @@ +tensorcircuit.results.qem +================================================================================ +.. toctree:: + qem/benchmark_circuits.rst + qem/qem_methods.rst \ No newline at end of file diff --git a/docs/source/api/results/qem/benchmark_circuits.rst b/docs/source/api/results/qem/benchmark_circuits.rst new file mode 100644 index 00000000..3c339884 --- /dev/null +++ b/docs/source/api/results/qem/benchmark_circuits.rst @@ -0,0 +1,7 @@ +tensorcircuit.results.qem.benchmark_circuits +================================================================================ +.. automodule:: tensorcircuit.results.qem.benchmark_circuits + :members: + :undoc-members: + :show-inheritance: + :inherited-members: \ No newline at end of file diff --git a/docs/source/api/results/qem/qem_methods.rst b/docs/source/api/results/qem/qem_methods.rst new file mode 100644 index 00000000..a95bdf95 --- /dev/null +++ b/docs/source/api/results/qem/qem_methods.rst @@ -0,0 +1,7 @@ +tensorcircuit.results.qem.qem_methods +================================================================================ +.. automodule:: tensorcircuit.results.qem.qem_methods + :members: + :undoc-members: + :show-inheritance: + :inherited-members: \ No newline at end of file diff --git a/docs/source/api/results/readout_mitigation.rst b/docs/source/api/results/readout_mitigation.rst new file mode 100644 index 00000000..325fe21a --- /dev/null +++ b/docs/source/api/results/readout_mitigation.rst @@ -0,0 +1,7 @@ +tensorcircuit.results.readout_mitigation +================================================================================ +.. automodule:: tensorcircuit.results.readout_mitigation + :members: + :undoc-members: + :show-inheritance: + :inherited-members: \ No newline at end of file diff --git a/docs/source/api/shadows.rst b/docs/source/api/shadows.rst new file mode 100644 index 00000000..7aea082e --- /dev/null +++ b/docs/source/api/shadows.rst @@ -0,0 +1,7 @@ +tensorcircuit.shadows +================================================================================ +.. automodule:: tensorcircuit.shadows + :members: + :undoc-members: + :show-inheritance: + :inherited-members: \ No newline at end of file diff --git a/docs/source/api/simplify.rst b/docs/source/api/simplify.rst index c1816c31..22833f9f 100644 --- a/docs/source/api/simplify.rst +++ b/docs/source/api/simplify.rst @@ -1,5 +1,5 @@ tensorcircuit.simplify -================================================== +================================================================================ .. automodule:: tensorcircuit.simplify :members: :undoc-members: diff --git a/docs/source/api/templates.rst b/docs/source/api/templates.rst index 586d74cc..202b049d 100644 --- a/docs/source/api/templates.rst +++ b/docs/source/api/templates.rst @@ -1,8 +1,10 @@ tensorcircuit.templates -================================================== +================================================================================ .. toctree:: + templates/ansatz.rst templates/blocks.rst templates/chems.rst + templates/conversions.rst templates/dataset.rst templates/graphs.rst templates/measurements.rst \ No newline at end of file diff --git a/docs/source/api/templates/ansatz.rst b/docs/source/api/templates/ansatz.rst new file mode 100644 index 00000000..15f19650 --- /dev/null +++ b/docs/source/api/templates/ansatz.rst @@ -0,0 +1,7 @@ +tensorcircuit.templates.ansatz +================================================================================ +.. automodule:: tensorcircuit.templates.ansatz + :members: + :undoc-members: + :show-inheritance: + :inherited-members: \ No newline at end of file diff --git a/docs/source/api/templates/blocks.rst b/docs/source/api/templates/blocks.rst index 0c88f3d9..b7a0945a 100644 --- a/docs/source/api/templates/blocks.rst +++ b/docs/source/api/templates/blocks.rst @@ -1,5 +1,5 @@ tensorcircuit.templates.blocks -================================================== +================================================================================ .. automodule:: tensorcircuit.templates.blocks :members: :undoc-members: diff --git a/docs/source/api/templates/chems.rst b/docs/source/api/templates/chems.rst index 8a31f9d3..d06d9e39 100644 --- a/docs/source/api/templates/chems.rst +++ b/docs/source/api/templates/chems.rst @@ -1,5 +1,5 @@ tensorcircuit.templates.chems -================================================== +================================================================================ .. automodule:: tensorcircuit.templates.chems :members: :undoc-members: diff --git a/docs/source/api/templates/conversions.rst b/docs/source/api/templates/conversions.rst new file mode 100644 index 00000000..38cbe47f --- /dev/null +++ b/docs/source/api/templates/conversions.rst @@ -0,0 +1,7 @@ +tensorcircuit.templates.conversions +================================================================================ +.. automodule:: tensorcircuit.templates.conversions + :members: + :undoc-members: + :show-inheritance: + :inherited-members: \ No newline at end of file diff --git a/docs/source/api/templates/dataset.rst b/docs/source/api/templates/dataset.rst index 36b9e510..aa6cdfa7 100644 --- a/docs/source/api/templates/dataset.rst +++ b/docs/source/api/templates/dataset.rst @@ -1,5 +1,5 @@ tensorcircuit.templates.dataset -================================================== +================================================================================ .. automodule:: tensorcircuit.templates.dataset :members: :undoc-members: diff --git a/docs/source/api/templates/graphs.rst b/docs/source/api/templates/graphs.rst index 0a2141f0..b86ab51e 100644 --- a/docs/source/api/templates/graphs.rst +++ b/docs/source/api/templates/graphs.rst @@ -1,5 +1,5 @@ tensorcircuit.templates.graphs -================================================== +================================================================================ .. automodule:: tensorcircuit.templates.graphs :members: :undoc-members: diff --git a/docs/source/api/templates/measurements.rst b/docs/source/api/templates/measurements.rst index 2113f03b..7e05673c 100644 --- a/docs/source/api/templates/measurements.rst +++ b/docs/source/api/templates/measurements.rst @@ -1,5 +1,5 @@ tensorcircuit.templates.measurements -================================================== +================================================================================ .. automodule:: tensorcircuit.templates.measurements :members: :undoc-members: diff --git a/docs/source/api/torchnn.rst b/docs/source/api/torchnn.rst index 5a5b2775..9f9c6598 100644 --- a/docs/source/api/torchnn.rst +++ b/docs/source/api/torchnn.rst @@ -1,5 +1,5 @@ tensorcircuit.torchnn -================================================== +================================================================================ .. automodule:: tensorcircuit.torchnn :members: :undoc-members: diff --git a/docs/source/api/translation.rst b/docs/source/api/translation.rst index a33667f7..f320c909 100644 --- a/docs/source/api/translation.rst +++ b/docs/source/api/translation.rst @@ -1,5 +1,5 @@ tensorcircuit.translation -================================================== +================================================================================ .. automodule:: tensorcircuit.translation :members: :undoc-members: diff --git a/docs/source/api/utils.rst b/docs/source/api/utils.rst index 3fa45319..93ee9496 100644 --- a/docs/source/api/utils.rst +++ b/docs/source/api/utils.rst @@ -1,5 +1,5 @@ tensorcircuit.utils -================================================== +================================================================================ .. automodule:: tensorcircuit.utils :members: :undoc-members: diff --git a/docs/source/api/vis.rst b/docs/source/api/vis.rst index f27680f1..2cdc89e2 100644 --- a/docs/source/api/vis.rst +++ b/docs/source/api/vis.rst @@ -1,5 +1,5 @@ tensorcircuit.vis -================================================== +================================================================================ .. automodule:: tensorcircuit.vis :members: :undoc-members: diff --git a/docs/source/cnconf.py b/docs/source/cnconf.py index 5c861ba1..ceecf794 100644 --- a/docs/source/cnconf.py +++ b/docs/source/cnconf.py @@ -48,6 +48,9 @@ "sphinx_copybutton", "nbsphinx", "toctree_filter", + "sphinx.ext.napoleon", + "myst_parser", + "sphinx_design", ] autosectionlabel_prefix_document = True diff --git a/docs/source/conf.py b/docs/source/conf.py index a52a2176..9d8147d9 100644 --- a/docs/source/conf.py +++ b/docs/source/conf.py @@ -21,7 +21,7 @@ # -- Project information ----------------------------------------------------- project = "tensorcircuit" -copyright = "2020, The TensorCircuit Authors" +copyright = "2020, TensorCircuit Development Team. Created by Shi-Xin Zhang" author = "refraction-ray" # The short X.Y version @@ -49,8 +49,12 @@ "nbsphinx", "toctree_filter", "sphinx.ext.napoleon", + "myst_parser", + "sphinx_design", ] +nbsphinx_allow_errors = True + autosectionlabel_prefix_document = True # Add any paths that contain templates here, relative to this directory. @@ -143,6 +147,7 @@ # Output file base name for HTML help builder. htmlhelp_basename = "tensorcircuitdoc" +html_title = "TensorCircuit Documentation" # -- Options for LaTeX output ------------------------------------------------ diff --git a/docs/source/contribs/development_Mac.md b/docs/source/contribs/development_Mac.md new file mode 100644 index 00000000..b2682f32 --- /dev/null +++ b/docs/source/contribs/development_Mac.md @@ -0,0 +1,113 @@ +# Tensorcircuit Installation Guide on MacOS + +Contributed by [_Mark (Zixuan) Song_](https://marksong.tech) + +Apple has updated Tensorflow (for MacOS) so that installation on M-series (until M2) and Intel-series Mac can follow the exact same procedure. + +## Starting From Scratch + +For completely new Macos or Macos without Xcode installed. + +If you have Xcode installed, skip to Install TC backends. + +### Install Xcode Command Line Tools + +Need graphical access to the machine. + +Run `xcode-select --install` to install if on optimal internet. + +Or Download it from [Apple](https://developer.apple.com/download/more/) Command Line Tools installation image then install it if the internet connection is weak. + +## Install TC Backends + +There are four backends to choose from, Numpy, Tensorflow, Jax, and Torch. + +### Install Jax, Pytorch (Optional) + +```bash +pip install [Package Name] +``` +### Install Tensorflow (Optional - Recommended) + +#### Install Miniconda (Optional - Recommended) + +If you wish to install Tensorflow optimized for MacOS (`tensorflow-macos`) or Tensorflow GPU optimized (`tensorflow-metal`) please install miniconda. + +If you wish to install Vanilla Tensorflow developed by Google (`tensorflow`) please skip this step. + +```bash +curl -o ~/miniconda.sh https://repo.anaconda.com/miniconda/Miniconda3-latest-MacOSX-arm64.sh +bash ~/miniconda.sh -b -p $HOME/miniconda +source ~/miniconda/bin/activate +conda install -c apple tensorflow-deps +``` + +#### Installation + +```bash +pip install tensorflow +``` + +If you wish to use tensorflow-metal PluggableDevice, then continue install (not recommended): + +```bash +pip install tensorflow-metal +``` + +#### Verify Tensorflow Installation + +```python +import tensorflow as tf + +cifar = tf.keras.datasets.cifar100 +(x_train, y_train), (x_test, y_test) = cifar.load_data() +model = tf.keras.applications.ResNet50( + include_top=True, + weights=None, + input_shape=(32, 32, 3), + classes=100,) + +loss_fn = tf.keras.losses.SparseCategoricalCrossentropy(from_logits=True) +model.compile(optimizer="adam", loss=loss_fn, metrics=["accuracy"]) +model.fit(x_train, y_train, epochs=5, batch_size=64) +``` + +## Install Tensorcircuit + +```bash +pip install tensorcircuit +``` + +## Benchmarking + +This data is collected by running `benchmarks/scripts/vqe_tc.py` 10 times and average results. + + + + + + + + + + + + + + + + + + + + + + + + + + +
Vanilla TensorflowApple TensorflowApple Tensorflow with Metal Plugin
Construction Time11.49241641s11.31878941s11.6103961s
Iteration time0.002313011s0.002333004s0.046412581s
Total time11.72371747s11.55208979s16.25165417s
+ + +Until July 2023, this has been tested on Intel Macs running Ventura, M1 Macs running Ventura, M2 Macs running Ventura, and M2 Macs running Sonoma beta. \ No newline at end of file diff --git a/docs/source/contribs/development_MacARM.md b/docs/source/contribs/development_MacARM.md new file mode 100644 index 00000000..73c63948 --- /dev/null +++ b/docs/source/contribs/development_MacARM.md @@ -0,0 +1,123 @@ +# Tensorcircuit Installation Guide on MacOS + +Contributed by Mark (Zixuan) Song + +.. warning:: + This page is deprecated. Please visit `the update tutorial `_ for the latest information. + +## Starting From Scratch + +For completely new macos or macos without xcode and brew + +### Install Xcode Command Line Tools + +Need graphical access to the machine. + +Run `xcode-select --install` to install if on optimal internet. + +Or Download from [Apple](https://developer.apple.com/download/more/) Command Line Tools installation image then install if internet connection is weak. + +## Install Miniconda + +Due to the limitation of MacOS and packages, the lastest version of python does not always function as desired, thus miniconda installation is advised to solve the issues. + +``` +curl -o ~/miniconda.sh https://repo.anaconda.com/miniconda/Miniconda3-latest-MacOSX-arm64.sh +bash ~/miniconda.sh -b -p $HOME/miniconda +source ~/miniconda/bin/activate +conda install -c apple tensorflow-deps +``` + +## Install TC Prerequisites + +``` +pip install numpy scipy tensornetwork networkx +``` + +## Install TC Backends + +There are four backends to choose from, Numpy, Tensorflow, Jax, Torch. + +### Install Jax, Pytorch, Qiskit, Cirq (Optional) + +``` +pip install [Package Name] +``` + +### Install Tensorflow (Optional) + +#### Install Tensorflow without MacOS optimization + +``` +conda config --add channels conda-forge +conda config --set channel_priority strict +conda create -n tc_venv python tensorflow=2.7 +``` + +#### Verify Tensorflow Installation + +``` +import tensorflow as tf + +cifar = tf.keras.datasets.cifar100 +(x_train, y_train), (x_test, y_test) = cifar.load_data() +model = tf.keras.applications.ResNet50( + include_top=True, + weights=None, + input_shape=(32, 32, 3), + classes=100,) + +loss_fn = tf.keras.losses.SparseCategoricalCrossentropy(from_logits=True) +model.compile(optimizer="adam", loss=loss_fn, metrics=["accuracy"]) +model.fit(x_train, y_train, epochs=5, batch_size=64) +``` + +#### Install Tensorflow with MacOS optimization (Recommended) + +For tensorflow version 2.13 or later: +``` +pip install tensorflow +pip install tensorflow-metal +``` + +For tensorflow version 2.12 or earlier: +``` +pip install tensorflow-macos +pip install tensorflow-metal +``` + +#### Verify Tensorflow Installation + +``` +import tensorflow as tf + +cifar = tf.keras.datasets.cifar100 +(x_train, y_train), (x_test, y_test) = cifar.load_data() +model = tf.keras.applications.ResNet50( + include_top=True, + weights=None, + input_shape=(32, 32, 3), + classes=100,) + +loss_fn = tf.keras.losses.SparseCategoricalCrossentropy(from_logits=True) +model.compile(optimizer="adam", loss=loss_fn, metrics=["accuracy"]) +model.fit(x_train, y_train, epochs=5, batch_size=64) +``` + +## Install Tensorcircuit + +``` +pip install tensorcircuit +``` + +Testing Platform (Tested Jun 2023) + +- Platform 1: + - MacOS Ventura 13.1 (Build version 22C65) + - M1 Ultra +- Platform 2: + - MacOS Ventura 13.2 (Build version 22D49) + - M1 Ultra (Virtual) +- Platform 4: + - MacOS Sonoma 14.0 Beta 2 (Build version 23A5276g) + - M2 Max \ No newline at end of file diff --git a/docs/source/contribs/development_MacM1.rst b/docs/source/contribs/development_MacM1.rst index e7a54454..8ce9f058 100644 --- a/docs/source/contribs/development_MacM1.rst +++ b/docs/source/contribs/development_MacM1.rst @@ -2,6 +2,11 @@ Run TensorCircuit on TensorlowBackend with Apple M1 ======================================================== Contributed by (Yuqin Chen) + +.. warning:: + This page is deprecated. Please visit `the update tutorial `_ for the latest information. + + Why We Can't Run TensorCircuit on TensorlowBackend with Apple M1 ----------------------------------------------------------------------- TensorCircuit requires Tensorflow to support TensorflowBackend. However for Apple M1, Tensorflow package cannot be properly installed by a usual method like "pip install tensorflow". As well, the TensorCircuit package cannot be properly installed by a usual method "pip install tensorcircuit" diff --git a/docs/source/contribs/development_MacM2.md b/docs/source/contribs/development_MacM2.md new file mode 100644 index 00000000..b3daf5fb --- /dev/null +++ b/docs/source/contribs/development_MacM2.md @@ -0,0 +1,53 @@ +# Tensorcircuit Installation Guide on MacOS + +Contributed by [Hong-Ye Hu](https://github.com/hongyehu) + +.. warning:: + This page is deprecated. Please visit `the update tutorial `_ for the latest information. + +The key issue addressed in this document is **how to install both TensorFlow and Jax on a M2 chip MacOS without conflict**. + +## Starting From Scratch + +### Install Xcode Command Line Tools + +Need graphical access to the machine. + +Run `xcode-select --install` to install if on optimal internet. + +Or Download from [Apple](https://developer.apple.com/download/more/) Command Line Tools installation image then install if internet connection is weak. + +## Install Miniconda + +Due to the limitation of MacOS and packages, the lastest version of python does not always function as desired, thus miniconda installation is advised to solve the issues. And use anaconda virtual environment is always a good habit. + +``` +curl -o ~/miniconda.sh https://repo.anaconda.com/miniconda/Miniconda3-latest-MacOSX-arm64.sh +bash ~/miniconda.sh -b -p $HOME/miniconda +source ~/miniconda/bin/activate +``` + +## Install Packages +First, create a virtual environment, and make sure the python version is 3.8.5 by +``` +conda create --name NewEnv python==3.8.5 +conda activate NewEnv +``` +Then, install the TensorFlow from `.whl` file (file can be downloaded from this [URL](https://drive.google.com/drive/folders/1oSipZLnoeQB0Awz8U68KYeCPsULy_dQ7)). This will install TensorFlow version 2.4.1 +``` +pip install ~/Downloads/tensorflow-2.4.1-py3-none-any.whl +``` +Next, one need to install **Jax** and **Optax** by +``` +conda install jax==0.3.0 +conda install optax==0.1.4 +``` +Now, hopefully, you should be able to use both Jax and TensorFlow in this environment. But sometimes, it may give you an error "ERROR: package Chardet not found.". +If that is the case, you can install it by `conda install chardet`. +Lastly, install tensorcircuit +``` +pip install tensorcircuit +``` +This is the solution that seems to work for M2-chip MacOS. Please let me know if there is a better solution! + + diff --git a/docs/source/contribs/development_Mac_cn.md b/docs/source/contribs/development_Mac_cn.md new file mode 100644 index 00000000..f23fd01f --- /dev/null +++ b/docs/source/contribs/development_Mac_cn.md @@ -0,0 +1,114 @@ +# MacOS Tensorcircuit 安装教程 + +[_Mark (Zixuan) Song_](https://marksong.tech) 撰写 + +由于苹果更新了Tensorflow,因此M系列(直到M2)和英特尔系列Mac上的安装可以遵循完全相同的过程。 + +## 从头开始 + +对于全新的Macos或未安装Xcode的Macos。 + +若您已安装Xcode,请跳转到安装TC后端。 + +### 安装Xcode命令行工具 + +需要对机器的图形访问 + +如果网络良好,请运行`xcode-select --install`进行安装。 + +或者,如果网络连接不理想,请从[苹果](https://developer.apple.com/download/more/)下载命令行工具安装映像,然后进行安装。 + +## 安装TC后端 + +有四个后端可供选择,Numpy,Tensorflow,Jax和Torch。 + +### 安装Jax、Pytorch(可选) + +```bash +pip install [Package Name] +``` + +### 安装Tensorflow(可选 - 推荐) + +#### 安装miniconda(可选 - 推荐) + +若您希望使用苹果为MacOS优化的Tensorflow(`tensorflow-macos`)或使用Tensorflow GPU优化(`tensorflow-metal`)请安装mimiconda。 + +若您希望使Google开发的原版Tensorflow(`tensorflow`)请跳过此步骤。 + +```bash +curl -o ~/miniconda.sh https://repo.anaconda.com/miniconda/Miniconda3-latest-MacOSX-arm64.sh +bash ~/miniconda.sh -b -p $HOME/miniconda +source ~/miniconda/bin/activate +conda install -c apple tensorflow-deps +``` + +#### 安装步骤 + +```bash +pip install tensorflow +``` + +若您希望使用苹果为Tensorflow优化的Metal后端,请继续运行(不建议): + +```bash +pip install tensorflow-metal +``` + +#### 验证Tensorflow安装 + +```python +import tensorflow as tf + +cifar = tf.keras.datasets.cifar100 +(x_train, y_train), (x_test, y_test) = cifar.load_data() +model = tf.keras.applications.ResNet50( + include_top=True, + weights=None, + input_shape=(32, 32, 3), + classes=100,) + +loss_fn = tf.keras.losses.SparseCategoricalCrossentropy(from_logits=True) +model.compile(optimizer="adam", loss=loss_fn, metrics=["accuracy"]) +model.fit(x_train, y_train, epochs=5, batch_size=64) +``` + +## 安装Tensorcircuit + +```bash +pip install tensorcircuit +``` + +## 测试与比较 + +以下数据由运行`benchmarks/scripts/vqe_tc.py` 10次并取平均值得到。 + + + + + + + + + + + + + + + + + + + + + + + + + + +
原版Tensorflow苹果优化版Tensorflow苹果优化版Tensorflow并安装Tensorflow Metal插件
构建时间11.49241641s11.31878941s11.6103961s
迭代时间0.002313011s0.002333004s0.046412581s
从时间11.72371747s11.55208979s16.25165417s
+ + +直到2023年7月,这已在运行Ventura的英特尔i9 Mac、运行Ventura的M1 Mac、运行Ventura的M2 Mac、运行Sonoma测试版的M2 Mac上进行了测试。 \ No newline at end of file diff --git a/docs/source/contribution.rst b/docs/source/contribution.rst index 5e8d7385..d8dbd493 100644 --- a/docs/source/contribution.rst +++ b/docs/source/contribution.rst @@ -146,7 +146,59 @@ We use `sphinx `__ to manage the document The source files for docs are .rst file in docs/source. -For English docs, ``sphinx-build source build/html`` in docs dir is enough. The html version of the docs are in docs/build/html. +For English docs, ``sphinx-build source build/html`` and ``make latexpdf LATEXMKOPTS="-silent"`` in docs dir are enough. +The html and pdf version of the docs are in docs/build/html and docs/build/latex, respectively. + +**Formula Environment Attention** + +It should be noted that the formula environment ``$$CONTENT$$`` in markdown is equivalent to the ``equation`` environment in latex. +Therefore, in the jupyter notebook documents, do not nest the formula environment in ``$$CONTENT$$`` that is incompatible with +``equation`` in latex, such as ``eqnarray``, which will cause errors in the pdf file built by ``nbsphinx``. +However, compatible formula environments can be used. For example, this legal code in markdown + +.. code-block:: markdown + + $$ + \begin{split} + X&=Y\\ + &=Z + \end{split} + $$ + +will be convert to + +.. code-block:: latex + + \begin{equation} + \begin{split} + X&=Y\\ + &=Z + \end{split} + \end{equation} + +in latex automatically by ``nbsphinx``, which is a legal latex code. However, this legal code in markdown + +.. code-block:: markdown + + $$ + \begin{eqnarray} + X&=&Y\\ + &=&Z + \end{eqnarray} + $$ + +will be convert to + +.. code-block:: latex + + \begin{equation} + \begin{eqnarray} + X&=&Y\\ + &=&Z + \end{eqnarray} + \end{equation} + +in latex, which is an illegal latex code. **Auto Generation of API Docs:** diff --git a/docs/source/generate_rst.py b/docs/source/generate_rst.py index a0226511..9b112c46 100644 --- a/docs/source/generate_rst.py +++ b/docs/source/generate_rst.py @@ -5,8 +5,7 @@ class RSTGenerator: - - title_line = "=" * 50 + title_line = "=" * 80 toctree = ".. toctree::\n {}" automodule = ".. automodule:: {}\n :members:\n :undoc-members:\n :show-inheritance:\n :inherited-members:" @@ -22,11 +21,14 @@ def __init__( def cleanup(self): if os.path.exists("modules.rst"): os.remove("modules.rst") - shutil.rmtree(self.dfolder) + try: + shutil.rmtree(self.dfolder) + except FileNotFoundError: + pass os.makedirs(self.dfolder) def write(self, path, content): - if type(content) == type([]): + if isinstance(content, list): content = "\n".join(content) with open(path, "w") as f: @@ -34,71 +36,68 @@ def write(self, path, content): print(f"Finish writing {path}") - def single_file_module(self): - """Process the module in the self.pfolder/*.py""" - - for module_name in glob.glob(pj(self.pfolder, "*.py")): - + def _file_generate(self, package_parents): + file_list = [] + for module_name in glob.glob(pj(self.pfolder, *package_parents, "*.py")): module_name = os.path.basename(module_name)[:-3] if module_name in self.ingnored_modules: continue - rst_file = pj(self.dfolder, f"{module_name}.rst") + rst_file = pj(self.dfolder, *package_parents, f"{module_name}.rst") + name = f"{self.name}" + for n in package_parents: + name += f".{n}" + name += f".{module_name}" content = [ - f"{self.name}.{module_name}", + name, self.title_line, - self.automodule.format(f"{self.name}.{module_name}"), + self.automodule.format(name), ] - self.write(rst_file, content) - self.tree[rst_file] = [] - - def subdir_files_module(self): - """Write the rst files for modules with subdir or files""" - for subdir in glob.glob(pj(self.pfolder, "*/")): + if not package_parents: + upper = self.dfolder + else: + upper = package_parents[-1] + file_list.append(upper + f"/{module_name}.rst") + for subdir in glob.glob(pj(self.pfolder, *package_parents, "*/")): if "_" in subdir: continue - subdir = os.path.basename(os.path.normpath(subdir)) - os.makedirs(pj(self.dfolder, subdir), exist_ok=True) - rst_file = pj(self.dfolder, f"{subdir}.rst") - self.tree[rst_file] = [] - - for module_name in glob.glob(pj(self.pfolder, subdir, f"*.py")): - module_name = os.path.basename(module_name)[:-3] - if module_name in self.ingnored_modules: - continue - - content = [ - f"{self.name}.{subdir}.{module_name}", - self.title_line, - self.automodule.format(f"{self.name}.{subdir}.{module_name}"), - ] - - self.write(pj(self.dfolder, subdir, f"{module_name}.rst"), content) - self.tree[rst_file].append(f"{subdir}/{module_name}.rst") - + os.makedirs(pj(self.dfolder, *package_parents, subdir), exist_ok=True) + rst_file = pj(self.dfolder, *package_parents, f"{subdir}.rst") + subdir_filelist = self._file_generate(package_parents + [subdir]) + + name = f"{self.name}" + for n in package_parents: + name += f".{n}" + name += f".{subdir}" content = [ - f"{self.name}.{subdir}", + name, self.title_line, - self.toctree.format("\n ".join(sorted(self.tree[rst_file]))), + self.toctree.format("\n ".join(sorted(subdir_filelist))), ] self.write(rst_file, content) - def modules_file(self): + if not package_parents: + upper = self.dfolder + else: + upper = package_parents[-1] + file_list.append(upper + f"/{subdir}.rst") + return file_list + + def modules_file(self, file_list): """Write the modules.rst""" content = [ self.name, self.title_line, - self.toctree.format("\n ".join(sorted(self.tree.keys()))), + self.toctree.format("\n ".join(sorted(file_list))), ] self.write("modules.rst", content) def start(self): self.cleanup() - self.single_file_module() - self.subdir_files_module() - self.modules_file() + file_list = self._file_generate([]) + self.modules_file(file_list) if __name__ == "__main__": diff --git a/docs/source/index.rst b/docs/source/index.rst index e8121a26..bd055a60 100644 --- a/docs/source/index.rst +++ b/docs/source/index.rst @@ -1,37 +1,196 @@ -Guide to TensorCircuit -================================== +TensorCircuit Documentation +=========================================================== .. image:: https://github.com/tencent-quantum-lab/tensorcircuit/blob/master/docs/source/statics/logov2.jpg?raw=true :target: https://github.com/tencent-quantum-lab/tensorcircuit -TensorCircuit is an open source quantum circuit and algorithm simulation framework. -* It is built for human beings. 👽 +**Welcome and congratulations! You have found TensorCircuit.** 👏 + +Introduction +--------------- + +TensorCircuit is an open-source high-performance quantum computing software framework in Python. + +* It is built for humans. 👽 * It is designed for speed, flexibility and elegance. 🚀 * It is empowered by advanced tensor network simulator engine. 🔋 +* It is ready for quantum hardware access with CPU/GPU/QPU (local/cloud) hybrid solutions. 🖥 + * It is implemented with industry-standard machine learning frameworks: TensorFlow, JAX, and PyTorch. 🤖 * It is compatible with machine learning engineering paradigms: automatic differentiation, just-in-time compilation, vectorized parallelism and GPU acceleration. 🛠 -Links ----------- +With the help of TensorCircuit, now get ready to efficiently and elegantly solve interesting and challenging quantum computing problems: from academic research prototype to industry application deployment. + + + + +Relevant Links +-------------------- + +TensorCircuit is created and maintained by `Shi-Xin Zhang `_ and this version is released by `Tencent Quantum Lab `_. + +The current core authors of TensorCircuit are `Shi-Xin Zhang `_ and `Yu-Qin Chen `_. +We also thank `contributions `_ from the open source community. + +If you have any further questions or collaboration ideas, please use the issue tracker or forum below, or send email to shixinzhang#tencent.com. + + +.. card-carousel:: 2 + + .. card:: Source code + :link: https://github.com/tencent-quantum-lab/tensorcircuit + :shadow: md + + GitHub + + + .. card:: Documentation + :link: https://tensorcircuit.readthedocs.io + :shadow: md + + Readthedocs + + + .. card:: Whitepaper + :link: https://quantum-journal.org/papers/q-2023-02-02-912/ + :shadow: md + + *Quantum* journal + + + .. card:: Issue Tracker + :link: https://github.com/tencent-quantum-lab/tensorcircuit/issues + :shadow: md + + GitHub Issues + + + .. card:: Forum + :link: https://github.com/tencent-quantum-lab/tensorcircuit/discussions + :shadow: md + + GitHub Discussions + + + .. card:: PyPI + :link: https://pypi.org/project/tensorcircuit + :shadow: md + + ``pip install`` + + + .. card:: DockerHub + :link: https://hub.docker.com/repository/docker/tensorcircuit/tensorcircuit + :shadow: md + + ``docker pull`` + + + .. card:: Application + :link: https://github.com/tencent-quantum-lab/tensorcircuit#research-and-applications + :shadow: md + + Research using TC + + + .. card:: Cloud + :link: https://quantum.tencent.com/cloud + + Tencent Quantum Cloud + + + + +.. + * Source code: https://github.com/tencent-quantum-lab/tensorcircuit + + * Documentation: https://tensorcircuit.readthedocs.io + + * Software Whitepaper (published in Quantum): https://quantum-journal.org/papers/q-2023-02-02-912/ + + * Issue Tracker: https://github.com/tencent-quantum-lab/tensorcircuit/issues + + * Forum: https://github.com/tencent-quantum-lab/tensorcircuit/discussions + + * PyPI page: https://pypi.org/project/tensorcircuit + + * DockerHub page: https://hub.docker.com/repository/docker/tensorcircuit/tensorcircuit + + * Research and projects based on TensorCircuit: https://github.com/tencent-quantum-lab/tensorcircuit#research-and-applications + + * Tencent Quantum Cloud Service: https://quantum.tencent.com/cloud/ + + + +Unified Quantum Programming +------------------------------ + +TensorCircuit is unifying infrastructures and interfaces for quantum computing. + +.. grid:: 1 2 4 4 + :margin: 0 + :padding: 0 + :gutter: 2 + + .. grid-item-card:: Unified Backends + :columns: 12 6 3 3 + :shadow: md + + Jax/TensorFlow/PyTorch/Numpy/Cupy + + .. grid-item-card:: Unified Devices + :columns: 12 6 3 3 + :shadow: md + + CPU/GPU/TPU + + .. grid-item-card:: Unified Providers + :columns: 12 6 3 3 + :shadow: md + + QPUs from different vendors + + .. grid-item-card:: Unified Resources + :columns: 12 6 3 3 + :shadow: md + + local/cloud/HPC + + +.. grid:: 1 2 4 4 + :margin: 0 + :padding: 0 + :gutter: 2 + + .. grid-item-card:: Unified Interfaces + :columns: 12 6 3 3 + :shadow: md + + numerical sim/hardware exp -* Source code: https://github.com/tencent-quantum-lab/tensorcircuit + .. grid-item-card:: Unified Engines + :columns: 12 6 3 3 + :shadow: md -* Issue Tracker: https://github.com/tencent-quantum-lab/tensorcircuit/issues + ideal/noisy/approximate simulation -* Forum: https://github.com/tencent-quantum-lab/tensorcircuit/discussions + .. grid-item-card:: Unified Representations + :columns: 12 6 3 3 + :shadow: md -* Documentation: https://tensorcircuit.readthedocs.io + from/to_IR/qiskit/openqasm/json -* Whitepaper: https://arxiv.org/abs/2205.10091 + .. grid-item-card:: Unified Pipelines + :columns: 12 6 3 3 + :shadow: md -* PyPI page: https://pypi.org/project/tensorcircuit + stateless functional programming/stateful ML models -* DockerHub page: https://hub.docker.com/repository/docker/tensorcircuit/tensorcircuit diff --git a/docs/source/infras.rst b/docs/source/infras.rst index 1727b3cf..576de2a9 100644 --- a/docs/source/infras.rst +++ b/docs/source/infras.rst @@ -28,9 +28,11 @@ Overview of Modules - :py:mod:`tensorcircuit.densitymatrix`: Referenced and highly efficient implementation of ``tc.DMCircuit`` class, with similar set API of ``tc.Circuit`` while simulating the noise in the full form of the density matrix. +- :py:mod:`tensorcircuit.noisemodel`: The global noise configuration and circuit noisy method APIs + **ML Interfaces Related Modules:** -- :py:mod:`tensorcircuit.interfaces`: Provide interfaces when quantum simulation backend is different from neural libraries. Currently include PyTorch and scipy optimizer interfaces. +- :py:mod:`tensorcircuit.interfaces`: Provide interfaces when quantum simulation backend is different from neural libraries. Currently include PyTorch, TensorFlow, NumPy and SciPy optimizer interfaces. - :py:mod:`tensorcircuit.keras`: Provide TensorFlow Keras layers, as well as wrappers of jitted function, save/load from tf side. @@ -58,6 +60,16 @@ Overview of Modules - :py:mod:`tensorcircuit.translation`: Translate circuit object to circuit object in other quantum packages. +**Processing and error mitigation on sample results:** + +- :py:mod:`tensorcircuit.results`: Provide tools to process count dict and to apply error mitigation. + +**Cloud quantum hardware access module:** + +- :py:mod:`tensorcircuit.cloud`: Provide quantum cloud SDK that can access and program the real quantum hardware. + +- :py:mod:`tensorcircuit.compiler`: Provide compiler chains to compile and transform quantum circuits. + **Shortcuts and Templates for Circuit Manipulation:** - :py:mod:`tensorcircuit.templates`: provide handy shortcuts functions for expectation or circuit building patterns. @@ -143,4 +155,29 @@ Also, note how ``^`` is overloaded as ``tn.connect`` to connect edges between di The convention to define the ``QuOperator`` is firstly giving ``out_edges`` (left index or row index of the matrix) and then giving ``in_edges`` (right index or column index of the matrix). The edges list contains edge objects from the TensorNetwork library. Such QuOperator/QuVector abstraction support various calculations only possible on matrix/vectors, such as matmul (``@``), adjoint (``.adjoint()``), scalar multiplication (``*``), tensor product (``|``), and partial trace (``.partial_trace(subsystems_to_trace_out)``). -To extract the matrix information of these objects, we can use ``.eval()`` or ``.eval_matrix()``, the former keeps the shape information of the tensor network while the latter gives the matrix representation with shape rank 2. \ No newline at end of file +To extract the matrix information of these objects, we can use ``.eval()`` or ``.eval_matrix()``, the former keeps the shape information of the tensor network while the latter gives the matrix representation with shape rank 2. + + +Quantum Cloud SDK: Layerwise API design +----------------------------------------------------- + +From lower level to higher level, a view of API layers invoking QPU calls + +- Vendor specific implementation of functional API in, e.g., :py:mod:`tensorcircuit.cloud.tencent` + +- Provider agnostic functional lower level API for task/device management in :py:mod:`tensorcircuit.cloud.apis` + +- Object oriented abstraction for Provider/Device/Task in :py:mod:`tensorcircuit.cloud.abstraction` + +- Unified batch submission interface as standarized in :py:meth:`tensorcircuit.cloud.wrapper.batch_submit_template` + +- Numerical and experimental unified all-in-one interface as :py:meth:`tensorcircuit.cloud.wrapper.batch_expectation_ps` + +- Application level code with QPU calls built directly on ``batch_expectation_ps`` or more fancy algorithms can be built on ``batch_submit_func`` so that these algorithms can be reused as long as one function ``batch_submit_func`` is defined for a given vendor (cheaper than defining a new provider from lower level). + + +.. Note:: + + For compiler, error mitigation and results post-processing parts, they can be carefully designed to decouple with the QPU calls, + so they are separately implemented in :py:mod:`tensorcircuit.compiler` and :py:mod:`tensorcircuit.results`, + and they can be independently useful even without tc's cloud access. diff --git a/docs/source/locale/zh/LC_MESSAGES/advance.po b/docs/source/locale/zh/LC_MESSAGES/advance.po index 16e19463..4e4b7fca 100644 --- a/docs/source/locale/zh/LC_MESSAGES/advance.po +++ b/docs/source/locale/zh/LC_MESSAGES/advance.po @@ -6,9 +6,9 @@ # msgid "" msgstr "" -"Project-Id-Version: tensorcircuit\n" +"Project-Id-Version: tensorcircuit\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2022-04-10 17:39+0800\n" +"POT-Creation-Date: 2023-01-13 11:04+0800\n" "PO-Revision-Date: 2022-04-11 07:50+0800\n" "Last-Translator: Xinghan Yang\n" "Language: cn\n" @@ -17,7 +17,6 @@ msgstr "" "Content-Type: text/plain; charset=utf-8\n" "Content-Transfer-Encoding: 8bit\n" "Generated-By: Babel 2.9.1\n" -"X-Generator: Poedit 1.6.11\n" #: ../../source/advance.rst:3 msgid "Advanced Usage" @@ -31,155 +30,174 @@ msgstr "MPS 模拟器" msgid "(Still experimental support)" msgstr "施工中" -#: ../../source/advance.rst:11 +#: ../../source/advance.rst:10 +msgid "" +"Very simple, we provide the same set of API for ``MPSCircuit`` as " +"``Circuit``, the only new line is to set the bond dimension for the new " +"simulator." +msgstr "" + +#: ../../source/advance.rst:18 +msgid "" +"The larger bond dimension we set, the better approximation ratio (of " +"course the more computational cost we pay)" +msgstr "" + +#: ../../source/advance.rst:21 msgid "Split Two-qubit Gates" msgstr "分解双量子比特门" -#: ../../source/advance.rst:13 +#: ../../source/advance.rst:23 msgid "" -"The two-qubit gates applied on the circuit can be decomposed via SVD, which " -"may further improve the optimality of the contraction pathfinding." -msgstr "" -"应用在电路上的双量子比特门可以通过 SVD 进行分解,这可以进一步提高收缩路径查找" -"的最优性。" +"The two-qubit gates applied on the circuit can be decomposed via SVD, " +"which may further improve the optimality of the contraction pathfinding." +msgstr "应用在电路上的双量子比特门可以通过 SVD 进行分解,这可以进一步提高收缩路径查找的最优性。" -#: ../../source/advance.rst:15 +#: ../../source/advance.rst:25 msgid "`split` configuration can be set at circuit-level or gate-level." msgstr "`split` 配置可以在电路级或门级设置。" -#: ../../source/advance.rst:36 +#: ../../source/advance.rst:46 msgid "" -"Note ``max_singular_values`` must be specified to make the whole procedure " -"static and thus jittable." -msgstr "" -"请注意 ``max_singular_values`` 必须被指定明以使整个过程成为静态的,因此是可即" -"时编译的。" +"Note ``max_singular_values`` must be specified to make the whole " +"procedure static and thus jittable." +msgstr "请注意 ``max_singular_values`` 必须被指定明以使整个过程成为静态的,因此是可即时编译的。" -#: ../../source/advance.rst:40 +#: ../../source/advance.rst:50 msgid "Jitted Function Save/Load" msgstr "即时编译函的保存/加载" -#: ../../source/advance.rst:42 +#: ../../source/advance.rst:52 msgid "" -"To reuse the jitted function, we can save it on the disk via support from " -"the TensorFlow `SavedModel `_. " -"That is to say, only jitted quantum function on the TensorFlow backend can " -"be saved on the disk." +"To reuse the jitted function, we can save it on the disk via support from" +" the TensorFlow `SavedModel " +"`_. That is to say, only " +"jitted quantum function on the TensorFlow backend can be saved on the " +"disk." msgstr "" -"要重新使用可即时编译函数,我们可以通过 TensorFlow `SavedModel `_. 的帮助将其保存在磁盘上。也就是说,只有 " +"要重新使用可即时编译函数,我们可以通过 TensorFlow `SavedModel " +"`_. 的帮助将其保存在磁盘上。也就是说,只有 " "TensorFlow 后端的可即时编译量子函数才能保存在磁盘上。" -#: ../../source/advance.rst:44 +#: ../../source/advance.rst:54 msgid "" -"For the JAX-backend quantum function, one can first transform them into the " -"tf-backend function via JAX experimental support: `jax2tf `_." +"For the JAX-backend quantum function, one can first transform them into " +"the tf-backend function via JAX experimental support: `jax2tf " +"`_." msgstr "" -"对于 jax-backend 量子函数,可以先通过 jax 实验支持将它们转换为 tf-backend 函" -"数: `jax2tf `_。" +"对于 jax-backend 量子函数,可以先通过 jax 实验支持将它们转换为 tf-backend 函数: `jax2tf " +"`_。" -#: ../../source/advance.rst:46 +#: ../../source/advance.rst:56 msgid "" -"We wrap the tf-backend `SavedModel` as very easy-to-use function :py:meth:" -"`tensorcircuit.keras.save_func` and :py:meth:`tensorcircuit.keras.load_func`." +"We wrap the tf-backend `SavedModel` as very easy-to-use function " +":py:meth:`tensorcircuit.keras.save_func` and " +":py:meth:`tensorcircuit.keras.load_func`." msgstr "" -"我们将 tf-backend `SavedModel` 包装为非常易于使用的函数 :py:meth:" -"`tensorcircuit.keras.save_func` 和 :py:meth:`tensorcircuit.keras.load_func`。" +"我们将 tf-backend `SavedModel` 包装为非常易于使用的函数 " +":py:meth:`tensorcircuit.keras.save_func` 和 " +":py:meth:`tensorcircuit.keras.load_func`。" -#: ../../source/advance.rst:49 +#: ../../source/advance.rst:59 msgid "Parameterized Measurements" msgstr "参数化测量" -#: ../../source/advance.rst:51 +#: ../../source/advance.rst:61 msgid "" -"For plain measurements API on a ``tc.Circuit``, eg. `c = tc.Circuit(n=3)`, " -"if we want to evaluate the expectation :math:``, we need to call the " -"API as ``c.expectation((tc.gates.z(), [1]), (tc.gates.z(), [2]))``." +"For plain measurements API on a ``tc.Circuit``, eg. `c = " +"tc.Circuit(n=3)`, if we want to evaluate the expectation " +":math:``, we need to call the API as " +"``c.expectation((tc.gates.z(), [1]), (tc.gates.z(), [2]))``." msgstr "" -"对于 ``tc.Circuit`` 上的普通测量 API, 例如 `c = tc.Circuit(n=3)`, 如果我们要" -"评估期望 :math:``, 我们需要调用API为 ``c.expectation((tc.gates.z(), " -"[1]), (tc.gates.z(), [2]))``。" +"对于 ``tc.Circuit`` 上的普通测量 API, 例如 `c = tc.Circuit(n=3)`, 如果我们要评估期望 " +":math:``, 我们需要调用API为 ``c.expectation((tc.gates.z(), [1]), " +"(tc.gates.z(), [2]))``。" -#: ../../source/advance.rst:53 +#: ../../source/advance.rst:63 msgid "" "In some cases, we may want to tell the software what to measure but in a " -"tensor fashion. For example, if we want to get the above expectation, we can " -"use the following API: :py:meth:`tensorcircuit.templates.measurements." -"parameterized_measurements`." +"tensor fashion. For example, if we want to get the above expectation, we " +"can use the following API: " +":py:meth:`tensorcircuit.templates.measurements.parameterized_measurements`." msgstr "" -"在某些情况下,我们可能希望以张量形式告诉软件要测量什么。例如,如果我们想获得" -"上述期望,我们可以使用以下 API : :py:meth:`tensorcircuit.templates." -"measurements.parameterized_measurements`。" +"在某些情况下,我们可能希望以张量形式告诉软件要测量什么。例如,如果我们想获得上述期望,我们可以使用以下 API : " +":py:meth:`tensorcircuit.templates.measurements.parameterized_measurements`。" -#: ../../source/advance.rst:60 +#: ../../source/advance.rst:70 msgid "" -"This API corresponds to measure :math:`I_0Z_1Z_2I_3` where 0, 1, 2, 3 are " -"for local I, X, Y, and Z operators respectively." -msgstr "" -"此 API 对应于测量 :math:`I_0Z_1Z_2I_3`, 其中 0、1、2、3 分别用于 I、X、Y、Z " -"局部运算符。" +"This API corresponds to measure :math:`I_0Z_1Z_2I_3` where 0, 1, 2, 3 are" +" for local I, X, Y, and Z operators respectively." +msgstr "此 API 对应于测量 :math:`I_0Z_1Z_2I_3`, 其中 0、1、2、3 分别用于 I、X、Y、Z 局部运算符。" -#: ../../source/advance.rst:63 +#: ../../source/advance.rst:73 msgid "Sparse Matrix" msgstr "稀疏矩阵" -#: ../../source/advance.rst:65 +#: ../../source/advance.rst:75 msgid "" "We support COO format sparse matrix as most backends only support this " -"format, and some common backend methods for sparse matrices are listed below:" -msgstr "" -"我们只支持 COO 格式的稀疏矩阵,因为大多数后端只支持这种格式,下面列出了一些常" -"用的稀疏矩阵后端方法:" +"format, and some common backend methods for sparse matrices are listed " +"below:" +msgstr "我们只支持 COO 格式的稀疏矩阵,因为大多数后端只支持这种格式,下面列出了一些常用的稀疏矩阵后端方法:" -#: ../../source/advance.rst:80 +#: ../../source/advance.rst:90 msgid "" -"The sparse matrix is specifically useful to evaluate Hamiltonian expectation " -"on the circuit, where sparse matrix representation has a good tradeoff " -"between space and time. Please refer to :py:meth:`tensorcircuit.templates." -"measurements.sparse_expectation` for more detail." +"The sparse matrix is specifically useful to evaluate Hamiltonian " +"expectation on the circuit, where sparse matrix representation has a good" +" tradeoff between space and time. Please refer to " +":py:meth:`tensorcircuit.templates.measurements.sparse_expectation` for " +"more detail." msgstr "" -"稀疏矩阵对于评估电路上的哈密顿期望特别有用,其中稀疏矩阵表示在空间和时间之间" -"具有良好的效率。请参阅 :py:meth:`tensorcircuit.templates.measurements." -"sparse_expectation` 了解更多详细信息。" +"稀疏矩阵对于评估电路上的哈密顿期望特别有用,其中稀疏矩阵表示在空间和时间之间具有良好的效率。请参阅 " +":py:meth:`tensorcircuit.templates.measurements.sparse_expectation` " +"了解更多详细信息。" -#: ../../source/advance.rst:83 +#: ../../source/advance.rst:93 msgid "" "For different representations to evaluate Hamiltonian expectation in " "tensorcircuit, please refer to :doc:`tutorials/tfim_vqe_diffreph`." -msgstr "" -"对于在张量电路中评估哈密顿期望的不同表示,请参阅 :doc:`tutorials/" -"tfim_vqe_diffreph` 。" +msgstr "对于在张量电路中评估哈密顿期望的不同表示,请参阅 :doc:`tutorials/tfim_vqe_diffreph` 。" -#: ../../source/advance.rst:86 +#: ../../source/advance.rst:96 msgid "Randoms, Jit, Backend Agnostic, and Their Interplay" msgstr "随机数,即时编译,后端无关特性,和他们的相互作用" -#: ../../source/advance.rst:129 +#: ../../source/advance.rst:139 msgid "" -"Therefore, a unified jittable random infrastructure with backend agnostic " -"can be formulated as" +"Therefore, a unified jittable random infrastructure with backend agnostic" +" can be formulated as" msgstr "因此,一个与后端无关并且统一可即时编译的随机基础设施可以表述为" -#: ../../source/advance.rst:157 +#: ../../source/advance.rst:167 msgid "And a more neat approach to achieve this is as follows:" msgstr "实现这一目标的更简洁的方法如下:" -#: ../../source/advance.rst:172 +#: ../../source/advance.rst:182 msgid "" -"It is worth noting that since ``Circuit.unitary_kraus`` and ``Circuit." -"general_kraus`` call ``implicit_rand*`` API, the correct usage of these APIs " -"is the same as above." +"It is worth noting that since ``Circuit.unitary_kraus`` and " +"``Circuit.general_kraus`` call ``implicit_rand*`` API, the correct usage " +"of these APIs is the same as above." msgstr "" -"值得注意的是,由于 ``Circuit.unitary_kraus`` 和 ``Circuit.general_kraus`` 调" -"用 ``implicit_rand*`` API,这些 API 的正确用法与上面相同。" +"值得注意的是,由于 ``Circuit.unitary_kraus`` 和 ``Circuit.general_kraus`` 调用 " +"``implicit_rand*`` API,这些 API 的正确用法与上面相同。" -#: ../../source/advance.rst:174 +#: ../../source/advance.rst:184 msgid "" "One may wonder why random numbers are dealt in such a complicated way, " -"please refer to the `Jax design note `_ for some hints." +"please refer to the `Jax design note " +"`_ for" +" some hints." msgstr "" "有人可能想知道为什么以如此复杂的方式处理随机数,请参阅 `Jax 设计说明 " -"`_ 提示。" +"`_ " +"提示。" + +#: ../../source/advance.rst:186 +msgid "" +"If vmap is also involved apart from jit, I currently find no way to " +"maintain the backend agnosticity as TensorFlow seems to have no support " +"of vmap over random keys (ping me on GitHub if you think you have a way " +"to do this). I strongly recommend the users using Jax backend in the " +"vmap+random setup." +msgstr "" + diff --git a/docs/source/locale/zh/LC_MESSAGES/api.po b/docs/source/locale/zh/LC_MESSAGES/api.po index da199805..b7958e1e 100644 --- a/docs/source/locale/zh/LC_MESSAGES/api.po +++ b/docs/source/locale/zh/LC_MESSAGES/api.po @@ -8,7 +8,7 @@ msgid "" msgstr "" "Project-Id-Version: tensorcircuit\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2022-06-27 20:10+0800\n" +"POT-Creation-Date: 2023-07-14 15:43+0800\n" "PO-Revision-Date: 2022-04-13 14:58+0800\n" "Last-Translator: Xinghan Yang\n" "Language: cn\n" @@ -16,25 +16,124 @@ msgstr "" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=utf-8\n" "Content-Transfer-Encoding: 8bit\n" -"Generated-By: Babel 2.9.1\n" +"Generated-By: Babel 2.12.1\n" -#: ../../source/api/applications.rst:2 -msgid "tensorcircuit.applications" +#: ../../source/api/about.rst:2 +msgid "tensorcircuit.about" msgstr "" -#: ../../source/api/applications/dqas.rst:2 -msgid "tensorcircuit.applications.dqas" +#: of tensorcircuit.about:1 tensorcircuit.about.about:1 +msgid "Prints the information for tensorcircuit installation and environment." msgstr "" -#: of tensorcircuit.applications.dqas:1 -msgid "Modules for DQAS framework" +#: ../../source/api/abstractcircuit.rst:2 +msgid "tensorcircuit.abstractcircuit" msgstr "" -#: of tensorcircuit.applications.dqas.DQAS_search:1 -msgid "DQAS framework entrypoint" +#: of tensorcircuit.abstractcircuit:1 +msgid "Methods for abstract circuits independent of nodes, edges and contractions" +msgstr "" + +#: of tensorcircuit.abstractcircuit.AbstractCircuit:1 +#: tensorcircuit.applications.utils.FakeModule:1 +#: tensorcircuit.applications.vqes.VQNHE:1 +#: tensorcircuit.backends.jax_backend.optax_optimizer:1 +#: tensorcircuit.backends.pytorch_backend.torch_jit_func:1 +#: tensorcircuit.backends.pytorch_backend.torch_optimizer:1 +#: tensorcircuit.backends.tensorflow_backend.keras_optimizer:1 +#: tensorcircuit.cloud.abstraction.Device:1 +#: tensorcircuit.cloud.abstraction.Provider:1 +#: tensorcircuit.cloud.abstraction.Task:1 +#: tensorcircuit.compiler.composed_compiler.Compiler:1 +#: tensorcircuit.gates.GateF:1 tensorcircuit.noisemodel.NoiseConf:1 +#: tensorcircuit.quantum.QuOperator:1 +#: tensorcircuit.results.readout_mitigation.ReadoutMit:1 +#: tensorcircuit.templates.ensemble.bagging:1 +#: tensorcircuit.templates.graphs.Grid2DCoord:1 +msgid "Bases: :py:class:`object`" +msgstr "" + +#: of tensorcircuit.abstractcircuit.AbstractCircuit.append:1 +msgid "append circuit ``c`` before" +msgstr "" + +#: of tensorcircuit.abstractcircuit.AbstractCircuit.append +#: tensorcircuit.templates.measurements.any_local_measurements +#: tensorcircuit.templates.measurements.any_measurements +#: tensorcircuit.templates.measurements.heisenberg_measurements +#: tensorcircuit.templates.measurements.spin_glass_measurements +msgid "example" msgstr "" -#: of tensorcircuit.applications.dqas.DQAS_search +#: keras.src.engine.base_layer.Layer.add_loss +#: keras.src.engine.base_layer.Layer.add_metric +#: keras.src.engine.base_layer.Layer.add_update +#: keras.src.engine.base_layer.Layer.add_weight +#: keras.src.engine.base_layer.Layer.build +#: keras.src.engine.base_layer.Layer.build_from_config +#: keras.src.engine.base_layer.Layer.compute_mask +#: keras.src.engine.base_layer.Layer.compute_output_shape +#: keras.src.engine.base_layer.Layer.compute_output_signature +#: keras.src.engine.base_layer.Layer.from_config +#: keras.src.engine.base_layer.Layer.get_input_at +#: keras.src.engine.base_layer.Layer.get_input_mask_at +#: keras.src.engine.base_layer.Layer.get_input_shape_at +#: keras.src.engine.base_layer.Layer.get_output_at +#: keras.src.engine.base_layer.Layer.get_output_mask_at +#: keras.src.engine.base_layer.Layer.get_output_shape_at +#: keras.src.engine.base_layer.Layer.load_own_variables +#: keras.src.engine.base_layer.Layer.save_own_variables +#: keras.src.engine.base_layer.Layer.set_weights +#: keras.src.engine.training.Model.build +#: keras.src.engine.training.Model.compile +#: keras.src.engine.training.Model.compile_from_config +#: keras.src.engine.training.Model.compute_loss +#: keras.src.engine.training.Model.compute_metrics +#: keras.src.engine.training.Model.evaluate +#: keras.src.engine.training.Model.export keras.src.engine.training.Model.fit +#: keras.src.engine.training.Model.from_config +#: keras.src.engine.training.Model.get_layer +#: keras.src.engine.training.Model.load_weights +#: keras.src.engine.training.Model.make_predict_function +#: keras.src.engine.training.Model.make_test_function +#: keras.src.engine.training.Model.make_train_function +#: keras.src.engine.training.Model.predict +#: keras.src.engine.training.Model.predict_on_batch +#: keras.src.engine.training.Model.predict_step +#: keras.src.engine.training.Model.save +#: keras.src.engine.training.Model.save_weights +#: keras.src.engine.training.Model.summary +#: keras.src.engine.training.Model.test_on_batch +#: keras.src.engine.training.Model.test_step +#: keras.src.engine.training.Model.to_json +#: keras.src.engine.training.Model.to_yaml +#: keras.src.engine.training.Model.train_on_batch +#: keras.src.engine.training.Model.train_step +#: keras.src.optimizers.schedules.learning_rate_schedule.LearningRateSchedule.from_config +#: of tensorcircuit.abstractcircuit.AbstractCircuit.append +#: tensorcircuit.abstractcircuit.AbstractCircuit.append_from_qir +#: tensorcircuit.abstractcircuit.AbstractCircuit.apply_general_gate_delayed..apply_list +#: tensorcircuit.abstractcircuit.AbstractCircuit.apply_general_variable_gate_delayed..apply_list +#: tensorcircuit.abstractcircuit.AbstractCircuit.barrier_instruction +#: tensorcircuit.abstractcircuit.AbstractCircuit.cond_measurement +#: tensorcircuit.abstractcircuit.AbstractCircuit.expectation_ps +#: tensorcircuit.abstractcircuit.AbstractCircuit.from_json +#: tensorcircuit.abstractcircuit.AbstractCircuit.from_json_file +#: tensorcircuit.abstractcircuit.AbstractCircuit.from_qir +#: tensorcircuit.abstractcircuit.AbstractCircuit.from_qiskit +#: tensorcircuit.abstractcircuit.AbstractCircuit.gate_count +#: tensorcircuit.abstractcircuit.AbstractCircuit.gate_count_by_condition +#: tensorcircuit.abstractcircuit.AbstractCircuit.initial_mapping +#: tensorcircuit.abstractcircuit.AbstractCircuit.inverse +#: tensorcircuit.abstractcircuit.AbstractCircuit.measure_instruction +#: tensorcircuit.abstractcircuit.AbstractCircuit.prepend +#: tensorcircuit.abstractcircuit.AbstractCircuit.reset_instruction +#: tensorcircuit.abstractcircuit.AbstractCircuit.select_gate +#: tensorcircuit.abstractcircuit.AbstractCircuit.standardize_gate +#: tensorcircuit.abstractcircuit.AbstractCircuit.to_cirq +#: tensorcircuit.abstractcircuit.AbstractCircuit.to_json +#: tensorcircuit.abstractcircuit.AbstractCircuit.to_qiskit +#: tensorcircuit.applications.dqas.DQAS_search #: tensorcircuit.applications.dqas.DQAS_search_pmb #: tensorcircuit.applications.dqas.get_var #: tensorcircuit.applications.dqas.get_weights @@ -67,9 +166,116 @@ msgstr "" #: tensorcircuit.applications.vags.tfim_measurements #: tensorcircuit.applications.vags.unitary_design #: tensorcircuit.applications.vags.unitary_design_block +#: tensorcircuit.applications.van.MADE.call +#: tensorcircuit.applications.van.MaskedConv2D.build +#: tensorcircuit.applications.van.MaskedConv2D.call +#: tensorcircuit.applications.van.MaskedLinear.call +#: tensorcircuit.applications.van.NMF.call +#: tensorcircuit.applications.van.PixelCNN.call +#: tensorcircuit.applications.van.ResidualBlock.call +#: tensorcircuit.applications.vqes.Linear.call #: tensorcircuit.applications.vqes.VQNHE.evaluation #: tensorcircuit.applications.vqes.VQNHE.plain_evaluation +#: tensorcircuit.backends.abstract_backend.ExtendedBackend.adjoint +#: tensorcircuit.backends.abstract_backend.ExtendedBackend.coo_sparse_matrix +#: tensorcircuit.backends.abstract_backend.ExtendedBackend.coo_sparse_matrix_from_numpy +#: tensorcircuit.backends.abstract_backend.ExtendedBackend.from_dlpack +#: tensorcircuit.backends.abstract_backend.ExtendedBackend.gather1d +#: tensorcircuit.backends.abstract_backend.ExtendedBackend.get_random_state +#: tensorcircuit.backends.abstract_backend.ExtendedBackend.implicit_randc +#: tensorcircuit.backends.abstract_backend.ExtendedBackend.implicit_randn +#: tensorcircuit.backends.abstract_backend.ExtendedBackend.implicit_randu +#: tensorcircuit.backends.abstract_backend.ExtendedBackend.is_sparse +#: tensorcircuit.backends.abstract_backend.ExtendedBackend.jacfwd +#: tensorcircuit.backends.abstract_backend.ExtendedBackend.jacrev +#: tensorcircuit.backends.abstract_backend.ExtendedBackend.probability_sample +#: tensorcircuit.backends.abstract_backend.ExtendedBackend.random_split +#: tensorcircuit.backends.abstract_backend.ExtendedBackend.reshape2 +#: tensorcircuit.backends.abstract_backend.ExtendedBackend.reshapem +#: tensorcircuit.backends.abstract_backend.ExtendedBackend.reverse +#: tensorcircuit.backends.abstract_backend.ExtendedBackend.scan +#: tensorcircuit.backends.abstract_backend.ExtendedBackend.scatter +#: tensorcircuit.backends.abstract_backend.ExtendedBackend.set_random_state +#: tensorcircuit.backends.abstract_backend.ExtendedBackend.sizen +#: tensorcircuit.backends.abstract_backend.ExtendedBackend.sparse_dense_matmul +#: tensorcircuit.backends.abstract_backend.ExtendedBackend.sqrtmh +#: tensorcircuit.backends.abstract_backend.ExtendedBackend.stateful_randc +#: tensorcircuit.backends.abstract_backend.ExtendedBackend.stateful_randn +#: tensorcircuit.backends.abstract_backend.ExtendedBackend.stateful_randu +#: tensorcircuit.backends.abstract_backend.ExtendedBackend.to_dense +#: tensorcircuit.backends.abstract_backend.ExtendedBackend.to_dlpack +#: tensorcircuit.backends.abstract_backend.ExtendedBackend.tree_flatten +#: tensorcircuit.backends.abstract_backend.ExtendedBackend.tree_map +#: tensorcircuit.backends.abstract_backend.ExtendedBackend.tree_unflatten #: tensorcircuit.backends.backend_factory.get_backend +#: tensorcircuit.backends.cupy_backend.CuPyBackend.acos +#: tensorcircuit.backends.cupy_backend.CuPyBackend.acosh +#: tensorcircuit.backends.cupy_backend.CuPyBackend.arange +#: tensorcircuit.backends.cupy_backend.CuPyBackend.argmax +#: tensorcircuit.backends.cupy_backend.CuPyBackend.argmin +#: tensorcircuit.backends.cupy_backend.CuPyBackend.asin +#: tensorcircuit.backends.cupy_backend.CuPyBackend.asinh +#: tensorcircuit.backends.cupy_backend.CuPyBackend.atan +#: tensorcircuit.backends.cupy_backend.CuPyBackend.atan2 +#: tensorcircuit.backends.cupy_backend.CuPyBackend.atanh +#: tensorcircuit.backends.cupy_backend.CuPyBackend.cast +#: tensorcircuit.backends.cupy_backend.CuPyBackend.concat +#: tensorcircuit.backends.cupy_backend.CuPyBackend.cond +#: tensorcircuit.backends.cupy_backend.CuPyBackend.coo_sparse_matrix +#: tensorcircuit.backends.cupy_backend.CuPyBackend.copy +#: tensorcircuit.backends.cupy_backend.CuPyBackend.cosh +#: tensorcircuit.backends.cupy_backend.CuPyBackend.cumsum +#: tensorcircuit.backends.cupy_backend.CuPyBackend.device +#: tensorcircuit.backends.cupy_backend.CuPyBackend.device_move +#: tensorcircuit.backends.cupy_backend.CuPyBackend.dtype +#: tensorcircuit.backends.cupy_backend.CuPyBackend.eigvalsh +#: tensorcircuit.backends.cupy_backend.CuPyBackend.eye +#: tensorcircuit.backends.cupy_backend.CuPyBackend.grad +#: tensorcircuit.backends.cupy_backend.CuPyBackend.i +#: tensorcircuit.backends.cupy_backend.CuPyBackend.imag +#: tensorcircuit.backends.cupy_backend.CuPyBackend.is_sparse +#: tensorcircuit.backends.cupy_backend.CuPyBackend.is_tensor +#: tensorcircuit.backends.cupy_backend.CuPyBackend.jvp +#: tensorcircuit.backends.cupy_backend.CuPyBackend.kron +#: tensorcircuit.backends.cupy_backend.CuPyBackend.left_shift +#: tensorcircuit.backends.cupy_backend.CuPyBackend.max +#: tensorcircuit.backends.cupy_backend.CuPyBackend.mean +#: tensorcircuit.backends.cupy_backend.CuPyBackend.min +#: tensorcircuit.backends.cupy_backend.CuPyBackend.mod +#: tensorcircuit.backends.cupy_backend.CuPyBackend.numpy +#: tensorcircuit.backends.cupy_backend.CuPyBackend.onehot +#: tensorcircuit.backends.cupy_backend.CuPyBackend.real +#: tensorcircuit.backends.cupy_backend.CuPyBackend.relu +#: tensorcircuit.backends.cupy_backend.CuPyBackend.reshape +#: tensorcircuit.backends.cupy_backend.CuPyBackend.right_shift +#: tensorcircuit.backends.cupy_backend.CuPyBackend.scatter +#: tensorcircuit.backends.cupy_backend.CuPyBackend.searchsorted +#: tensorcircuit.backends.cupy_backend.CuPyBackend.set_random_state +#: tensorcircuit.backends.cupy_backend.CuPyBackend.shape_tuple +#: tensorcircuit.backends.cupy_backend.CuPyBackend.sigmoid +#: tensorcircuit.backends.cupy_backend.CuPyBackend.sign +#: tensorcircuit.backends.cupy_backend.CuPyBackend.sinh +#: tensorcircuit.backends.cupy_backend.CuPyBackend.size +#: tensorcircuit.backends.cupy_backend.CuPyBackend.softmax +#: tensorcircuit.backends.cupy_backend.CuPyBackend.solve +#: tensorcircuit.backends.cupy_backend.CuPyBackend.sparse_dense_matmul +#: tensorcircuit.backends.cupy_backend.CuPyBackend.stack +#: tensorcircuit.backends.cupy_backend.CuPyBackend.stateful_randc +#: tensorcircuit.backends.cupy_backend.CuPyBackend.stateful_randn +#: tensorcircuit.backends.cupy_backend.CuPyBackend.stateful_randu +#: tensorcircuit.backends.cupy_backend.CuPyBackend.std +#: tensorcircuit.backends.cupy_backend.CuPyBackend.stop_gradient +#: tensorcircuit.backends.cupy_backend.CuPyBackend.switch +#: tensorcircuit.backends.cupy_backend.CuPyBackend.tan +#: tensorcircuit.backends.cupy_backend.CuPyBackend.tanh +#: tensorcircuit.backends.cupy_backend.CuPyBackend.tensordot +#: tensorcircuit.backends.cupy_backend.CuPyBackend.tile +#: tensorcircuit.backends.cupy_backend.CuPyBackend.to_dense +#: tensorcircuit.backends.cupy_backend.CuPyBackend.unique_with_counts +#: tensorcircuit.backends.cupy_backend.CuPyBackend.value_and_grad +#: tensorcircuit.backends.cupy_backend.CuPyBackend.vectorized_value_and_grad +#: tensorcircuit.backends.cupy_backend.CuPyBackend.vjp +#: tensorcircuit.backends.cupy_backend.CuPyBackend.vmap #: tensorcircuit.backends.jax_backend.JaxBackend.acos #: tensorcircuit.backends.jax_backend.JaxBackend.acosh #: tensorcircuit.backends.jax_backend.JaxBackend.arange @@ -79,16 +285,20 @@ msgstr "" #: tensorcircuit.backends.jax_backend.JaxBackend.asinh #: tensorcircuit.backends.jax_backend.JaxBackend.atan #: tensorcircuit.backends.jax_backend.JaxBackend.atan2 +#: tensorcircuit.backends.jax_backend.JaxBackend.atanh #: tensorcircuit.backends.jax_backend.JaxBackend.cast #: tensorcircuit.backends.jax_backend.JaxBackend.concat #: tensorcircuit.backends.jax_backend.JaxBackend.cond #: tensorcircuit.backends.jax_backend.JaxBackend.coo_sparse_matrix #: tensorcircuit.backends.jax_backend.JaxBackend.copy -#: tensorcircuit.backends.jax_backend.JaxBackend.cos #: tensorcircuit.backends.jax_backend.JaxBackend.cosh #: tensorcircuit.backends.jax_backend.JaxBackend.cumsum +#: tensorcircuit.backends.jax_backend.JaxBackend.device +#: tensorcircuit.backends.jax_backend.JaxBackend.device_move +#: tensorcircuit.backends.jax_backend.JaxBackend.dtype #: tensorcircuit.backends.jax_backend.JaxBackend.eigvalsh -#: tensorcircuit.backends.jax_backend.JaxBackend.expm +#: tensorcircuit.backends.jax_backend.JaxBackend.eye +#: tensorcircuit.backends.jax_backend.JaxBackend.from_dlpack #: tensorcircuit.backends.jax_backend.JaxBackend.grad #: tensorcircuit.backends.jax_backend.JaxBackend.i #: tensorcircuit.backends.jax_backend.JaxBackend.imag @@ -97,7 +307,6 @@ msgstr "" #: tensorcircuit.backends.jax_backend.JaxBackend.implicit_randu #: tensorcircuit.backends.jax_backend.JaxBackend.is_sparse #: tensorcircuit.backends.jax_backend.JaxBackend.is_tensor -#: tensorcircuit.backends.jax_backend.JaxBackend.jit #: tensorcircuit.backends.jax_backend.JaxBackend.jvp #: tensorcircuit.backends.jax_backend.JaxBackend.kron #: tensorcircuit.backends.jax_backend.JaxBackend.left_shift @@ -111,10 +320,11 @@ msgstr "" #: tensorcircuit.backends.jax_backend.JaxBackend.real #: tensorcircuit.backends.jax_backend.JaxBackend.relu #: tensorcircuit.backends.jax_backend.JaxBackend.right_shift +#: tensorcircuit.backends.jax_backend.JaxBackend.scan #: tensorcircuit.backends.jax_backend.JaxBackend.scatter +#: tensorcircuit.backends.jax_backend.JaxBackend.searchsorted #: tensorcircuit.backends.jax_backend.JaxBackend.set_random_state #: tensorcircuit.backends.jax_backend.JaxBackend.sigmoid -#: tensorcircuit.backends.jax_backend.JaxBackend.sin #: tensorcircuit.backends.jax_backend.JaxBackend.sinh #: tensorcircuit.backends.jax_backend.JaxBackend.size #: tensorcircuit.backends.jax_backend.JaxBackend.softmax @@ -124,12 +334,14 @@ msgstr "" #: tensorcircuit.backends.jax_backend.JaxBackend.stateful_randc #: tensorcircuit.backends.jax_backend.JaxBackend.stateful_randn #: tensorcircuit.backends.jax_backend.JaxBackend.stateful_randu +#: tensorcircuit.backends.jax_backend.JaxBackend.std #: tensorcircuit.backends.jax_backend.JaxBackend.stop_gradient #: tensorcircuit.backends.jax_backend.JaxBackend.switch #: tensorcircuit.backends.jax_backend.JaxBackend.tan #: tensorcircuit.backends.jax_backend.JaxBackend.tanh #: tensorcircuit.backends.jax_backend.JaxBackend.tile #: tensorcircuit.backends.jax_backend.JaxBackend.to_dense +#: tensorcircuit.backends.jax_backend.JaxBackend.to_dlpack #: tensorcircuit.backends.jax_backend.JaxBackend.tree_flatten #: tensorcircuit.backends.jax_backend.JaxBackend.tree_map #: tensorcircuit.backends.jax_backend.JaxBackend.tree_unflatten @@ -138,6 +350,7 @@ msgstr "" #: tensorcircuit.backends.jax_backend.JaxBackend.vectorized_value_and_grad #: tensorcircuit.backends.jax_backend.JaxBackend.vjp #: tensorcircuit.backends.jax_backend.JaxBackend.vmap +#: tensorcircuit.backends.jax_backend._svd_jax #: tensorcircuit.backends.numpy_backend.NumpyBackend.acos #: tensorcircuit.backends.numpy_backend.NumpyBackend.acosh #: tensorcircuit.backends.numpy_backend.NumpyBackend.arange @@ -147,22 +360,24 @@ msgstr "" #: tensorcircuit.backends.numpy_backend.NumpyBackend.asinh #: tensorcircuit.backends.numpy_backend.NumpyBackend.atan #: tensorcircuit.backends.numpy_backend.NumpyBackend.atan2 +#: tensorcircuit.backends.numpy_backend.NumpyBackend.atanh #: tensorcircuit.backends.numpy_backend.NumpyBackend.cast #: tensorcircuit.backends.numpy_backend.NumpyBackend.concat #: tensorcircuit.backends.numpy_backend.NumpyBackend.cond #: tensorcircuit.backends.numpy_backend.NumpyBackend.coo_sparse_matrix #: tensorcircuit.backends.numpy_backend.NumpyBackend.copy -#: tensorcircuit.backends.numpy_backend.NumpyBackend.cos #: tensorcircuit.backends.numpy_backend.NumpyBackend.cosh #: tensorcircuit.backends.numpy_backend.NumpyBackend.cumsum +#: tensorcircuit.backends.numpy_backend.NumpyBackend.device +#: tensorcircuit.backends.numpy_backend.NumpyBackend.device_move +#: tensorcircuit.backends.numpy_backend.NumpyBackend.dtype #: tensorcircuit.backends.numpy_backend.NumpyBackend.eigvalsh -#: tensorcircuit.backends.numpy_backend.NumpyBackend.expm +#: tensorcircuit.backends.numpy_backend.NumpyBackend.eye #: tensorcircuit.backends.numpy_backend.NumpyBackend.grad #: tensorcircuit.backends.numpy_backend.NumpyBackend.i #: tensorcircuit.backends.numpy_backend.NumpyBackend.imag #: tensorcircuit.backends.numpy_backend.NumpyBackend.is_sparse #: tensorcircuit.backends.numpy_backend.NumpyBackend.is_tensor -#: tensorcircuit.backends.numpy_backend.NumpyBackend.jit #: tensorcircuit.backends.numpy_backend.NumpyBackend.jvp #: tensorcircuit.backends.numpy_backend.NumpyBackend.kron #: tensorcircuit.backends.numpy_backend.NumpyBackend.left_shift @@ -176,9 +391,9 @@ msgstr "" #: tensorcircuit.backends.numpy_backend.NumpyBackend.relu #: tensorcircuit.backends.numpy_backend.NumpyBackend.right_shift #: tensorcircuit.backends.numpy_backend.NumpyBackend.scatter +#: tensorcircuit.backends.numpy_backend.NumpyBackend.searchsorted #: tensorcircuit.backends.numpy_backend.NumpyBackend.set_random_state #: tensorcircuit.backends.numpy_backend.NumpyBackend.sigmoid -#: tensorcircuit.backends.numpy_backend.NumpyBackend.sin #: tensorcircuit.backends.numpy_backend.NumpyBackend.sinh #: tensorcircuit.backends.numpy_backend.NumpyBackend.size #: tensorcircuit.backends.numpy_backend.NumpyBackend.softmax @@ -188,6 +403,7 @@ msgstr "" #: tensorcircuit.backends.numpy_backend.NumpyBackend.stateful_randc #: tensorcircuit.backends.numpy_backend.NumpyBackend.stateful_randn #: tensorcircuit.backends.numpy_backend.NumpyBackend.stateful_randu +#: tensorcircuit.backends.numpy_backend.NumpyBackend.std #: tensorcircuit.backends.numpy_backend.NumpyBackend.stop_gradient #: tensorcircuit.backends.numpy_backend.NumpyBackend.switch #: tensorcircuit.backends.numpy_backend.NumpyBackend.tan @@ -208,20 +424,23 @@ msgstr "" #: tensorcircuit.backends.pytorch_backend.PyTorchBackend.asinh #: tensorcircuit.backends.pytorch_backend.PyTorchBackend.atan #: tensorcircuit.backends.pytorch_backend.PyTorchBackend.atan2 +#: tensorcircuit.backends.pytorch_backend.PyTorchBackend.atanh #: tensorcircuit.backends.pytorch_backend.PyTorchBackend.cast #: tensorcircuit.backends.pytorch_backend.PyTorchBackend.concat #: tensorcircuit.backends.pytorch_backend.PyTorchBackend.cond #: tensorcircuit.backends.pytorch_backend.PyTorchBackend.copy -#: tensorcircuit.backends.pytorch_backend.PyTorchBackend.cos #: tensorcircuit.backends.pytorch_backend.PyTorchBackend.cosh #: tensorcircuit.backends.pytorch_backend.PyTorchBackend.cumsum +#: tensorcircuit.backends.pytorch_backend.PyTorchBackend.device +#: tensorcircuit.backends.pytorch_backend.PyTorchBackend.device_move +#: tensorcircuit.backends.pytorch_backend.PyTorchBackend.dtype #: tensorcircuit.backends.pytorch_backend.PyTorchBackend.eigvalsh -#: tensorcircuit.backends.pytorch_backend.PyTorchBackend.expm +#: tensorcircuit.backends.pytorch_backend.PyTorchBackend.eye +#: tensorcircuit.backends.pytorch_backend.PyTorchBackend.from_dlpack #: tensorcircuit.backends.pytorch_backend.PyTorchBackend.grad #: tensorcircuit.backends.pytorch_backend.PyTorchBackend.i #: tensorcircuit.backends.pytorch_backend.PyTorchBackend.imag #: tensorcircuit.backends.pytorch_backend.PyTorchBackend.is_tensor -#: tensorcircuit.backends.pytorch_backend.PyTorchBackend.jit #: tensorcircuit.backends.pytorch_backend.PyTorchBackend.jvp #: tensorcircuit.backends.pytorch_backend.PyTorchBackend.kron #: tensorcircuit.backends.pytorch_backend.PyTorchBackend.left_shift @@ -235,18 +454,20 @@ msgstr "" #: tensorcircuit.backends.pytorch_backend.PyTorchBackend.relu #: tensorcircuit.backends.pytorch_backend.PyTorchBackend.reverse #: tensorcircuit.backends.pytorch_backend.PyTorchBackend.right_shift +#: tensorcircuit.backends.pytorch_backend.PyTorchBackend.searchsorted #: tensorcircuit.backends.pytorch_backend.PyTorchBackend.sigmoid -#: tensorcircuit.backends.pytorch_backend.PyTorchBackend.sin #: tensorcircuit.backends.pytorch_backend.PyTorchBackend.sinh #: tensorcircuit.backends.pytorch_backend.PyTorchBackend.size #: tensorcircuit.backends.pytorch_backend.PyTorchBackend.softmax #: tensorcircuit.backends.pytorch_backend.PyTorchBackend.solve #: tensorcircuit.backends.pytorch_backend.PyTorchBackend.stack +#: tensorcircuit.backends.pytorch_backend.PyTorchBackend.std #: tensorcircuit.backends.pytorch_backend.PyTorchBackend.stop_gradient #: tensorcircuit.backends.pytorch_backend.PyTorchBackend.switch #: tensorcircuit.backends.pytorch_backend.PyTorchBackend.tan #: tensorcircuit.backends.pytorch_backend.PyTorchBackend.tanh #: tensorcircuit.backends.pytorch_backend.PyTorchBackend.tile +#: tensorcircuit.backends.pytorch_backend.PyTorchBackend.to_dlpack #: tensorcircuit.backends.pytorch_backend.PyTorchBackend.tree_flatten #: tensorcircuit.backends.pytorch_backend.PyTorchBackend.tree_map #: tensorcircuit.backends.pytorch_backend.PyTorchBackend.tree_unflatten @@ -255,6 +476,8 @@ msgstr "" #: tensorcircuit.backends.pytorch_backend.PyTorchBackend.vectorized_value_and_grad #: tensorcircuit.backends.pytorch_backend.PyTorchBackend.vjp #: tensorcircuit.backends.pytorch_backend.PyTorchBackend.vmap +#: tensorcircuit.backends.pytorch_backend._qr_torch +#: tensorcircuit.backends.pytorch_backend._rq_torch #: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.acos #: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.acosh #: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.arange @@ -264,23 +487,26 @@ msgstr "" #: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.asinh #: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.atan #: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.atan2 +#: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.atanh #: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.cast #: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.concat #: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.cond #: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.coo_sparse_matrix #: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.copy -#: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.cos #: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.cosh #: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.cumsum +#: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.device +#: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.device_move +#: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.dtype #: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.eigvalsh -#: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.expm +#: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.eye +#: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.from_dlpack #: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.gather1d #: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.grad #: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.i #: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.imag #: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.is_sparse #: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.is_tensor -#: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.jit #: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.jvp #: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.kron #: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.left_shift @@ -293,10 +519,11 @@ msgstr "" #: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.real #: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.relu #: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.right_shift +#: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.scan #: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.scatter +#: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.searchsorted #: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.set_random_state #: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.sigmoid -#: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.sin #: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.sinh #: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.size #: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.softmax @@ -306,71 +533,109 @@ msgstr "" #: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.stateful_randc #: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.stateful_randn #: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.stateful_randu +#: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.std #: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.stop_gradient #: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.switch #: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.tan #: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.tanh #: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.tile #: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.to_dense +#: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.to_dlpack #: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.unique_with_counts #: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.value_and_grad #: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.vectorized_value_and_grad #: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.vjp #: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.vmap +#: tensorcircuit.backends.tensorflow_backend._qr_tf +#: tensorcircuit.backends.tensorflow_backend._rq_tf +#: tensorcircuit.backends.tensorflow_backend._tensordot_tf +#: tensorcircuit.basecircuit.BaseCircuit.amplitude +#: tensorcircuit.basecircuit.BaseCircuit.cond_measurement +#: tensorcircuit.basecircuit.BaseCircuit.expectation_before +#: tensorcircuit.basecircuit.BaseCircuit.measure_jit +#: tensorcircuit.basecircuit.BaseCircuit.perfect_sampling +#: tensorcircuit.basecircuit.BaseCircuit.readouterror_bs +#: tensorcircuit.basecircuit.BaseCircuit.replace_inputs +#: tensorcircuit.basecircuit.BaseCircuit.sample +#: tensorcircuit.basecircuit.BaseCircuit.sample_expectation_ps #: tensorcircuit.channels.amplitudedampingchannel -#: tensorcircuit.channels.depolarizingchannel +#: tensorcircuit.channels.check_rep_transformation +#: tensorcircuit.channels.choi_to_kraus tensorcircuit.channels.choi_to_super +#: tensorcircuit.channels.composedkraus +#: tensorcircuit.channels.depolarizingchannel tensorcircuit.channels.evol_kraus +#: tensorcircuit.channels.evol_superop +#: tensorcircuit.channels.generaldepolarizingchannel +#: tensorcircuit.channels.is_hermitian_matrix +#: tensorcircuit.channels.isotropicdepolarizingchannel +#: tensorcircuit.channels.kraus_identity_check +#: tensorcircuit.channels.kraus_to_choi tensorcircuit.channels.kraus_to_super #: tensorcircuit.channels.kraus_to_super_gate -#: tensorcircuit.channels.phasedampingchannel -#: tensorcircuit.channels.single_qubit_kraus_identity_check +#: tensorcircuit.channels.krausgate_to_krausmatrix +#: tensorcircuit.channels.krausmatrix_to_krausgate +#: tensorcircuit.channels.phasedampingchannel tensorcircuit.channels.reshuffle +#: tensorcircuit.channels.super_to_choi tensorcircuit.channels.super_to_kraus +#: tensorcircuit.channels.thermalrelaxationchannel #: tensorcircuit.circuit.Circuit.__init__ -#: tensorcircuit.circuit.Circuit.amplitude -#: tensorcircuit.circuit.Circuit.append_from_qir -#: tensorcircuit.circuit.Circuit.apply_double_gate -#: tensorcircuit.circuit.Circuit.apply_general_gate_delayed..apply -#: tensorcircuit.circuit.Circuit.apply_general_variable_gate_delayed..apply -#: tensorcircuit.circuit.Circuit.apply_single_gate -#: tensorcircuit.circuit.Circuit.cond_measurement -#: tensorcircuit.circuit.Circuit.depolarizing +#: tensorcircuit.circuit.Circuit.apply_general_kraus_delayed..apply +#: tensorcircuit.circuit.Circuit.depolarizing_reference #: tensorcircuit.circuit.Circuit.expectation -#: tensorcircuit.circuit.Circuit.expectation_before -#: tensorcircuit.circuit.Circuit.from_qir -#: tensorcircuit.circuit.Circuit.from_qiskit #: tensorcircuit.circuit.Circuit.general_kraus -#: tensorcircuit.circuit.Circuit.measure_jit #: tensorcircuit.circuit.Circuit.measure_reference #: tensorcircuit.circuit.Circuit.mid_measurement -#: tensorcircuit.circuit.Circuit.replace_inputs #: tensorcircuit.circuit.Circuit.replace_mps_inputs -#: tensorcircuit.circuit.Circuit.sample -#: tensorcircuit.circuit.Circuit.select_gate #: tensorcircuit.circuit.Circuit.unitary_kraus -#: tensorcircuit.circuit.Circuit.wavefunction -#: tensorcircuit.circuit._expectation_ps tensorcircuit.circuit.expectation +#: tensorcircuit.circuit.Circuit.wavefunction tensorcircuit.circuit.expectation +#: tensorcircuit.cloud.abstraction.Device.topology_graph +#: tensorcircuit.cloud.abstraction.Task.details +#: tensorcircuit.cloud.abstraction.Task.results +#: tensorcircuit.cloud.apis.get_device tensorcircuit.cloud.apis.get_provider +#: tensorcircuit.cloud.apis.get_task tensorcircuit.cloud.apis.get_task_details +#: tensorcircuit.cloud.apis.get_token tensorcircuit.cloud.apis.list_devices +#: tensorcircuit.cloud.apis.list_properties tensorcircuit.cloud.apis.list_tasks +#: tensorcircuit.cloud.apis.resubmit_task tensorcircuit.cloud.apis.set_device +#: tensorcircuit.cloud.apis.set_provider tensorcircuit.cloud.apis.set_token +#: tensorcircuit.cloud.apis.submit_task tensorcircuit.cloud.tencent.submit_task +#: tensorcircuit.cloud.utils.set_proxy +#: tensorcircuit.cloud.wrapper.batch_expectation_ps +#: tensorcircuit.compiler.composed_compiler.DefaultCompiler.__init__ +#: tensorcircuit.compiler.qiskit_compiler.qiskit_compile #: tensorcircuit.cons.get_contractor tensorcircuit.cons.get_dtype #: tensorcircuit.cons.plain_contractor tensorcircuit.cons.runtime_backend #: tensorcircuit.cons.runtime_dtype tensorcircuit.cons.set_contractor #: tensorcircuit.cons.set_dtype tensorcircuit.cons.set_function_backend #: tensorcircuit.cons.set_function_dtype -#: tensorcircuit.cons.set_tensornetwork_backend +#: tensorcircuit.cons.set_tensornetwork_backend tensorcircuit.cons.split_rules #: tensorcircuit.densitymatrix.DMCircuit.__init__ -#: tensorcircuit.densitymatrix.DMCircuit.apply_general_gate_delayed..apply #: tensorcircuit.densitymatrix.DMCircuit.apply_general_kraus_delayed..apply -#: tensorcircuit.densitymatrix.DMCircuit.apply_general_variable_gate_delayed..apply #: tensorcircuit.densitymatrix.DMCircuit.densitymatrix #: tensorcircuit.densitymatrix.DMCircuit.expectation -#: tensorcircuit.densitymatrix.DMCircuit.measure_jit -#: tensorcircuit.densitymatrix2.DMCircuit2.apply_general_kraus_delayed..apply +#: tensorcircuit.densitymatrix.DMCircuit.to_circuit +#: tensorcircuit.densitymatrix.DMCircuit2.apply_general_kraus_delayed..apply +#: tensorcircuit.experimental.evol_global tensorcircuit.experimental.evol_local +#: tensorcircuit.experimental.hamiltonian_evol +#: tensorcircuit.experimental.parameter_shift_grad +#: tensorcircuit.experimental.parameter_shift_grad_v2 #: tensorcircuit.gates.any_gate tensorcircuit.gates.bmatrix #: tensorcircuit.gates.cr_gate tensorcircuit.gates.exponential_gate -#: tensorcircuit.gates.exponential_gate_unity tensorcircuit.gates.iswap_gate -#: tensorcircuit.gates.matrix_for_gate tensorcircuit.gates.num_to_tensor +#: tensorcircuit.gates.exponential_gate_unity +#: tensorcircuit.gates.get_u_parameter tensorcircuit.gates.iswap_gate +#: tensorcircuit.gates.matrix_for_gate tensorcircuit.gates.multicontrol_gate +#: tensorcircuit.gates.num_to_tensor tensorcircuit.gates.phase_gate #: tensorcircuit.gates.r_gate tensorcircuit.gates.rgate_theoretical #: tensorcircuit.gates.rx_gate tensorcircuit.gates.rxx_gate #: tensorcircuit.gates.ry_gate tensorcircuit.gates.ryy_gate #: tensorcircuit.gates.rz_gate tensorcircuit.gates.rzz_gate -#: tensorcircuit.interfaces.scipy_optimize_interface -#: tensorcircuit.interfaces.torch_interface -#: tensorcircuit.keras.QuantumLayer.__init__ tensorcircuit.keras.load_func +#: tensorcircuit.gates.u_gate tensorcircuit.interfaces.numpy.numpy_interface +#: tensorcircuit.interfaces.scipy.scipy_optimize_interface +#: tensorcircuit.interfaces.tensorflow.tensorflow_interface +#: tensorcircuit.interfaces.tensortrans.args_to_tensor +#: tensorcircuit.interfaces.tensortrans.general_args_to_numpy +#: tensorcircuit.interfaces.tensortrans.numpy_args_to_backend +#: tensorcircuit.interfaces.tensortrans.which_backend +#: tensorcircuit.interfaces.torch.torch_interface +#: tensorcircuit.interfaces.torch.torch_interface_kws +#: tensorcircuit.keras.QuantumLayer.__init__ +#: tensorcircuit.keras.QuantumLayer.build tensorcircuit.keras.load_func #: tensorcircuit.keras.output_asis_loss tensorcircuit.keras.save_func #: tensorcircuit.mps_base.FiniteMPS.apply_two_site_gate #: tensorcircuit.mps_base.FiniteMPS.measure_local_operator @@ -379,21 +644,24 @@ msgstr "" #: tensorcircuit.mpscircuit.MPSCircuit.apply_adjacent_double_gate #: tensorcircuit.mpscircuit.MPSCircuit.apply_double_gate #: tensorcircuit.mpscircuit.MPSCircuit.apply_general_gate -#: tensorcircuit.mpscircuit.MPSCircuit.apply_general_gate_delayed..apply -#: tensorcircuit.mpscircuit.MPSCircuit.apply_general_variable_gate_delayed..apply #: tensorcircuit.mpscircuit.MPSCircuit.apply_single_gate -#: tensorcircuit.mpscircuit.MPSCircuit.expectation_double_gates -#: tensorcircuit.mpscircuit.MPSCircuit.expectation_single_gate -#: tensorcircuit.mpscircuit.MPSCircuit.expectation_two_gates_product -#: tensorcircuit.mpscircuit.MPSCircuit.from_wavefunction -#: tensorcircuit.mpscircuit.MPSCircuit.general_expectation +#: tensorcircuit.mpscircuit.MPSCircuit.expectation #: tensorcircuit.mpscircuit.MPSCircuit.measure #: tensorcircuit.mpscircuit.MPSCircuit.mid_measurement #: tensorcircuit.mpscircuit.MPSCircuit.position #: tensorcircuit.mpscircuit.MPSCircuit.proj_with_mps -#: tensorcircuit.mpscircuit.MPSCircuit.set_truncation_rule +#: tensorcircuit.mpscircuit.MPSCircuit.set_split_rules #: tensorcircuit.mpscircuit.MPSCircuit.wavefunction -#: tensorcircuit.mpscircuit.split_tensor tensorcircuit.quantum.PauliString2COO +#: tensorcircuit.mpscircuit.MPSCircuit.wavefunction_to_tensors +#: tensorcircuit.mpscircuit.split_tensor +#: tensorcircuit.noisemodel.NoiseConf.add_noise +#: tensorcircuit.noisemodel.NoiseConf.add_noise_by_condition +#: tensorcircuit.noisemodel.NoiseConf.channel_count +#: tensorcircuit.noisemodel.apply_qir_with_noise +#: tensorcircuit.noisemodel.circuit_with_noise +#: tensorcircuit.noisemodel.expectation_noisfy +#: tensorcircuit.noisemodel.sample_expectation_ps_noisfy +#: tensorcircuit.quantum.PauliString2COO #: tensorcircuit.quantum.PauliStringSum2COO #: tensorcircuit.quantum.PauliStringSum2COO_numpy #: tensorcircuit.quantum.PauliStringSum2COO_tf @@ -415,150 +683,269 @@ msgstr "" #: tensorcircuit.quantum.QuVector.reduced_density #: tensorcircuit.quantum.check_spaces #: tensorcircuit.quantum.correlation_from_counts -#: tensorcircuit.quantum.double_state +#: tensorcircuit.quantum.correlation_from_samples +#: tensorcircuit.quantum.count_d2s tensorcircuit.quantum.count_s2d +#: tensorcircuit.quantum.count_tuple2dict +#: tensorcircuit.quantum.count_vector2dict tensorcircuit.quantum.double_state #: tensorcircuit.quantum.eliminate_identities tensorcircuit.quantum.entropy #: tensorcircuit.quantum.fidelity tensorcircuit.quantum.free_energy #: tensorcircuit.quantum.generate_local_hamiltonian #: tensorcircuit.quantum.gibbs_state #: tensorcircuit.quantum.heisenberg_hamiltonian tensorcircuit.quantum.identity #: tensorcircuit.quantum.measurement_counts -#: tensorcircuit.quantum.mutual_information +#: tensorcircuit.quantum.mutual_information tensorcircuit.quantum.ps2xyz #: tensorcircuit.quantum.quantum_constructor tensorcircuit.quantum.quimb2qop #: tensorcircuit.quantum.reduced_density_matrix #: tensorcircuit.quantum.renyi_entropy tensorcircuit.quantum.renyi_free_energy +#: tensorcircuit.quantum.sample2all tensorcircuit.quantum.sample2count +#: tensorcircuit.quantum.sample_bin2int tensorcircuit.quantum.sample_int2bin #: tensorcircuit.quantum.spin_by_basis tensorcircuit.quantum.taylorlnm #: tensorcircuit.quantum.tn2qop tensorcircuit.quantum.trace_distance -#: tensorcircuit.quantum.truncated_free_energy +#: tensorcircuit.quantum.truncated_free_energy tensorcircuit.quantum.xyz2ps +#: tensorcircuit.results.counts.expectation +#: tensorcircuit.results.counts.plot_histogram +#: tensorcircuit.results.qem.qem_methods.add_dd +#: tensorcircuit.results.qem.qem_methods.apply_dd +#: tensorcircuit.results.qem.qem_methods.apply_rc +#: tensorcircuit.results.qem.qem_methods.apply_zne +#: tensorcircuit.results.qem.qem_methods.prune_ddcircuit +#: tensorcircuit.results.qem.qem_methods.used_qubits +#: tensorcircuit.results.readout_mitigation.ReadoutMit.__init__ +#: tensorcircuit.results.readout_mitigation.ReadoutMit.apply_correction +#: tensorcircuit.results.readout_mitigation.ReadoutMit.apply_readout_mitigation +#: tensorcircuit.results.readout_mitigation.ReadoutMit.cals_from_api +#: tensorcircuit.results.readout_mitigation.ReadoutMit.cals_from_system +#: tensorcircuit.results.readout_mitigation.ReadoutMit.expectation +#: tensorcircuit.results.readout_mitigation.ReadoutMit.get_matrix +#: tensorcircuit.results.readout_mitigation.ReadoutMit.mapping_preprocess +#: tensorcircuit.results.readout_mitigation.ReadoutMit.mitigate_probability +#: tensorcircuit.results.readout_mitigation.ReadoutMit.newrange +#: tensorcircuit.results.readout_mitigation.ReadoutMit.ubs #: tensorcircuit.simplify.infer_new_shape #: tensorcircuit.simplify.pseudo_contract_between #: tensorcircuit.templates.blocks.Bell_pair_block #: tensorcircuit.templates.blocks.example_block +#: tensorcircuit.templates.blocks.qft #: tensorcircuit.templates.blocks.state_centric -#: tensorcircuit.templates.chems.get_ps #: tensorcircuit.templates.graphs.Grid2DCoord.__init__ #: tensorcircuit.templates.graphs.Grid2DCoord.all_cols #: tensorcircuit.templates.graphs.Grid2DCoord.all_rows #: tensorcircuit.templates.graphs.Grid2DCoord.lattice_graph #: tensorcircuit.templates.graphs.Line1D +#: tensorcircuit.templates.measurements.any_local_measurements #: tensorcircuit.templates.measurements.any_measurements #: tensorcircuit.templates.measurements.heisenberg_measurements #: tensorcircuit.templates.measurements.mpo_expectation #: tensorcircuit.templates.measurements.operator_expectation #: tensorcircuit.templates.measurements.sparse_expectation #: tensorcircuit.templates.measurements.spin_glass_measurements -#: tensorcircuit.torchnn.QuantumNet.__init__ -#: tensorcircuit.translation.perm_matrix tensorcircuit.translation.qir2qiskit -#: tensorcircuit.translation.qiskit2tc tensorcircuit.utils.append +#: tensorcircuit.torchnn.HardwareNet.__init__ +#: tensorcircuit.torchnn.QuantumNet.__init__ tensorcircuit.translation.eqasm2tc +#: tensorcircuit.translation.perm_matrix tensorcircuit.translation.qir2cirq +#: tensorcircuit.translation.qir2json tensorcircuit.translation.qir2qiskit +#: tensorcircuit.translation.qiskit2tc +#: tensorcircuit.translation.qiskit_from_qasm_str_ordered_measure +#: tensorcircuit.utils.append tensorcircuit.utils.arg_alias #: tensorcircuit.utils.benchmark tensorcircuit.utils.return_partial #: tensorcircuit.vis.gate_name_trans tensorcircuit.vis.qir2tex #: tensorcircuit.vis.render_pdf +#: tensorflow.python.module.module.Module.with_name_scope +#: tensornetwork.backends.abstract_backend.AbstractBackend.deserialize_tensor +#: tensornetwork.backends.abstract_backend.AbstractBackend.diagonal +#: tensornetwork.backends.abstract_backend.AbstractBackend.eigh +#: tensornetwork.backends.abstract_backend.AbstractBackend.eigs +#: tensornetwork.backends.abstract_backend.AbstractBackend.eigsh +#: tensornetwork.backends.abstract_backend.AbstractBackend.eigsh_lanczos +#: tensornetwork.backends.abstract_backend.AbstractBackend.eps +#: tensornetwork.backends.abstract_backend.AbstractBackend.gmres +#: tensornetwork.backends.abstract_backend.AbstractBackend.index_update +#: tensornetwork.backends.abstract_backend.AbstractBackend.inv +#: tensornetwork.backends.abstract_backend.AbstractBackend.item +#: tensornetwork.backends.abstract_backend.AbstractBackend.pivot +#: tensornetwork.backends.abstract_backend.AbstractBackend.power +#: tensornetwork.backends.abstract_backend.AbstractBackend.serialize_tensor +#: tensornetwork.backends.abstract_backend.AbstractBackend.shape_tensor +#: tensornetwork.backends.abstract_backend.AbstractBackend.slice +#: tensornetwork.backends.abstract_backend.AbstractBackend.svd +#: tensornetwork.backends.abstract_backend.AbstractBackend.trace +#: tensornetwork.backends.jax.jax_backend.JaxBackend.diagonal +#: tensornetwork.backends.jax.jax_backend.JaxBackend.eigh +#: tensornetwork.backends.jax.jax_backend.JaxBackend.eigs +#: tensornetwork.backends.jax.jax_backend.JaxBackend.eigsh +#: tensornetwork.backends.jax.jax_backend.JaxBackend.eigsh_lanczos +#: tensornetwork.backends.jax.jax_backend.JaxBackend.eps +#: tensornetwork.backends.jax.jax_backend.JaxBackend.index_update +#: tensornetwork.backends.jax.jax_backend.JaxBackend.inv +#: tensornetwork.backends.jax.jax_backend.JaxBackend.item +#: tensornetwork.backends.jax.jax_backend.JaxBackend.power +#: tensornetwork.backends.jax.jax_backend.JaxBackend.reshape +#: tensornetwork.backends.jax.jax_backend.JaxBackend.shape_tensor +#: tensornetwork.backends.jax.jax_backend.JaxBackend.shape_tuple +#: tensornetwork.backends.jax.jax_backend.JaxBackend.sign +#: tensornetwork.backends.jax.jax_backend.JaxBackend.slice +#: tensornetwork.backends.jax.jax_backend.JaxBackend.tensordot +#: tensornetwork.backends.jax.jax_backend.JaxBackend.trace +#: tensornetwork.backends.numpy.numpy_backend.NumPyBackend.deserialize_tensor +#: tensornetwork.backends.numpy.numpy_backend.NumPyBackend.diagonal +#: tensornetwork.backends.numpy.numpy_backend.NumPyBackend.eigh +#: tensornetwork.backends.numpy.numpy_backend.NumPyBackend.eigs +#: tensornetwork.backends.numpy.numpy_backend.NumPyBackend.eigsh +#: tensornetwork.backends.numpy.numpy_backend.NumPyBackend.eigsh_lanczos +#: tensornetwork.backends.numpy.numpy_backend.NumPyBackend.eps +#: tensornetwork.backends.numpy.numpy_backend.NumPyBackend.index_update +#: tensornetwork.backends.numpy.numpy_backend.NumPyBackend.inv +#: tensornetwork.backends.numpy.numpy_backend.NumPyBackend.item +#: tensornetwork.backends.numpy.numpy_backend.NumPyBackend.power +#: tensornetwork.backends.numpy.numpy_backend.NumPyBackend.reshape +#: tensornetwork.backends.numpy.numpy_backend.NumPyBackend.serialize_tensor +#: tensornetwork.backends.numpy.numpy_backend.NumPyBackend.shape_tensor +#: tensornetwork.backends.numpy.numpy_backend.NumPyBackend.shape_tuple +#: tensornetwork.backends.numpy.numpy_backend.NumPyBackend.sign +#: tensornetwork.backends.numpy.numpy_backend.NumPyBackend.slice +#: tensornetwork.backends.numpy.numpy_backend.NumPyBackend.svd +#: tensornetwork.backends.numpy.numpy_backend.NumPyBackend.tensordot +#: tensornetwork.backends.numpy.numpy_backend.NumPyBackend.trace +#: tensornetwork.backends.pytorch.pytorch_backend.PyTorchBackend.diagonal +#: tensornetwork.backends.pytorch.pytorch_backend.PyTorchBackend.eigh +#: tensornetwork.backends.pytorch.pytorch_backend.PyTorchBackend.eigsh_lanczos +#: tensornetwork.backends.pytorch.pytorch_backend.PyTorchBackend.eps +#: tensornetwork.backends.pytorch.pytorch_backend.PyTorchBackend.index_update +#: tensornetwork.backends.pytorch.pytorch_backend.PyTorchBackend.inv +#: tensornetwork.backends.pytorch.pytorch_backend.PyTorchBackend.item +#: tensornetwork.backends.pytorch.pytorch_backend.PyTorchBackend.reshape +#: tensornetwork.backends.pytorch.pytorch_backend.PyTorchBackend.shape_tensor +#: tensornetwork.backends.pytorch.pytorch_backend.PyTorchBackend.shape_tuple +#: tensornetwork.backends.pytorch.pytorch_backend.PyTorchBackend.sign +#: tensornetwork.backends.pytorch.pytorch_backend.PyTorchBackend.slice +#: tensornetwork.backends.pytorch.pytorch_backend.PyTorchBackend.svd +#: tensornetwork.backends.pytorch.pytorch_backend.PyTorchBackend.tensordot +#: tensornetwork.backends.pytorch.pytorch_backend.PyTorchBackend.trace +#: tensornetwork.backends.tensorflow.tensorflow_backend.TensorFlowBackend.diagonal +#: tensornetwork.backends.tensorflow.tensorflow_backend.TensorFlowBackend.eigh +#: tensornetwork.backends.tensorflow.tensorflow_backend.TensorFlowBackend.eps +#: tensornetwork.backends.tensorflow.tensorflow_backend.TensorFlowBackend.index_update +#: tensornetwork.backends.tensorflow.tensorflow_backend.TensorFlowBackend.inv +#: tensornetwork.backends.tensorflow.tensorflow_backend.TensorFlowBackend.item +#: tensornetwork.backends.tensorflow.tensorflow_backend.TensorFlowBackend.power +#: tensornetwork.backends.tensorflow.tensorflow_backend.TensorFlowBackend.reshape +#: tensornetwork.backends.tensorflow.tensorflow_backend.TensorFlowBackend.shape_tensor +#: tensornetwork.backends.tensorflow.tensorflow_backend.TensorFlowBackend.shape_tuple +#: tensornetwork.backends.tensorflow.tensorflow_backend.TensorFlowBackend.sign +#: tensornetwork.backends.tensorflow.tensorflow_backend.TensorFlowBackend.slice +#: tensornetwork.backends.tensorflow.tensorflow_backend.TensorFlowBackend.trace +#: tensornetwork.matrixproductstates.base_mps.BaseMPS.apply_transfer_operator +#: tensornetwork.matrixproductstates.base_mps.BaseMPS.check_orthonormality +#: tensornetwork.matrixproductstates.base_mps.BaseMPS.get_tensor +#: tensornetwork.matrixproductstates.base_mps.BaseMPS.position +#: tensornetwork.matrixproductstates.finite_mps.FiniteMPS.__init__ +#: tensornetwork.matrixproductstates.finite_mps.FiniteMPS.canonicalize +#: tensornetwork.matrixproductstates.finite_mps.FiniteMPS.left_envs +#: tensornetwork.matrixproductstates.finite_mps.FiniteMPS.random +#: tensornetwork.matrixproductstates.finite_mps.FiniteMPS.right_envs +#: tensornetwork.network_components.AbstractNode.add_axis_names +#: tensornetwork.network_components.AbstractNode.add_edge +#: tensornetwork.network_components.AbstractNode.get_dimension +#: tensornetwork.network_components.AbstractNode.reorder_axes +#: tensornetwork.network_components.AbstractNode.reorder_edges +#: tensornetwork.network_components.Node.__init__ +#: tensornetwork.network_components.Node.from_serial_dict +#: torch.nn.modules.module.Module.add_module +#: torch.nn.modules.module.Module.apply torch.nn.modules.module.Module.buffers +#: torch.nn.modules.module.Module.cuda +#: torch.nn.modules.module.Module.get_buffer +#: torch.nn.modules.module.Module.get_parameter +#: torch.nn.modules.module.Module.get_submodule +#: torch.nn.modules.module.Module.ipu +#: torch.nn.modules.module.Module.load_state_dict +#: torch.nn.modules.module.Module.named_buffers +#: torch.nn.modules.module.Module.named_modules +#: torch.nn.modules.module.Module.named_parameters +#: torch.nn.modules.module.Module.parameters +#: torch.nn.modules.module.Module.register_buffer +#: torch.nn.modules.module.Module.register_forward_hook +#: torch.nn.modules.module.Module.register_forward_pre_hook +#: torch.nn.modules.module.Module.register_full_backward_hook +#: torch.nn.modules.module.Module.register_full_backward_pre_hook +#: torch.nn.modules.module.Module.register_parameter +#: torch.nn.modules.module.Module.requires_grad_ +#: torch.nn.modules.module.Module.set_extra_state +#: torch.nn.modules.module.Module.state_dict torch.nn.modules.module.Module.to +#: torch.nn.modules.module.Module.to_empty torch.nn.modules.module.Module.train +#: torch.nn.modules.module.Module.type torch.nn.modules.module.Module.xpu +#: torch.nn.modules.module.Module.zero_grad msgid "Parameters" msgstr "" -#: of tensorcircuit.applications.dqas.DQAS_search:3 -msgid "" -"function with input of data instance, circuit parameters theta and " -"structural paramter k, return tuple of objective value and gradient with " -"respect to theta" -msgstr "" - -#: of tensorcircuit.applications.dqas.DQAS_search:5 -msgid "data generator as dataset" -msgstr "" - -#: of tensorcircuit.applications.dqas.DQAS_search:6 -msgid "list of operations as primitive operator pool" -msgstr "" - -#: of tensorcircuit.applications.dqas.DQAS_search:7 -msgid "the default layer number of the circuit ansatz" -msgstr "" - -#: of tensorcircuit.applications.dqas.DQAS_search:8 -msgid "" -"shape of circuit parameter pool, in general p_stp*l, where l is the max " -"number of circuit parameters for op in the operator pool" -msgstr "" - -#: of tensorcircuit.applications.dqas.DQAS_search:10 -msgid "the same as p in the most times" -msgstr "" - -#: of tensorcircuit.applications.dqas.DQAS_search:11 -msgid "batch size of one epoch" -msgstr "" - -#: of tensorcircuit.applications.dqas.DQAS_search:12 -msgid "prethermal update times" -msgstr "" - -#: of tensorcircuit.applications.dqas.DQAS_search:13 -msgid "training epochs" -msgstr "" - -#: of tensorcircuit.applications.dqas.DQAS_search:14 -msgid "parallel thread number, 0 to disable multiprocessing model by default" -msgstr "" - -#: of tensorcircuit.applications.dqas.DQAS_search:15 -msgid "set verbose log to print" -msgstr "" - -#: of tensorcircuit.applications.dqas.DQAS_search:16 -msgid "function to output verbose information" -msgstr "" - -#: of tensorcircuit.applications.dqas.DQAS_search:17 -msgid "function return intermiediate result for final history list" -msgstr "" - -#: of tensorcircuit.applications.dqas.DQAS_search:18 -msgid "cutoff probability to avoid peak distribution" -msgstr "" - -#: of tensorcircuit.applications.dqas.DQAS_search:19 -msgid "" -"function accepting list of objective values and return the baseline value" -" used in the next round" -msgstr "" - -#: of tensorcircuit.applications.dqas.DQAS_search:21 -msgid "return noise with the same shape as circuit parameter pool" -msgstr "" - -#: of tensorcircuit.applications.dqas.DQAS_search:22 -msgid "initial values for circuit parameter pool" -msgstr "" - -#: of tensorcircuit.applications.dqas.DQAS_search:23 -msgid "initial values for probabilistic model parameters" -msgstr "" - -#: of tensorcircuit.applications.dqas.DQAS_search:24 -msgid "optimizer for circuit parameters theta" -msgstr "" - -#: of tensorcircuit.applications.dqas.DQAS_search:25 -msgid "optimizer for model parameters alpha" -msgstr "" - -#: of tensorcircuit.applications.dqas.DQAS_search:26 -msgid "optimizer for circuit parameters in prethermal stage" -msgstr "" - -#: of tensorcircuit.applications.dqas.DQAS_search:27 -msgid "fixed structural parameters for prethermal training" -msgstr "" - -#: of tensorcircuit.applications.dqas.DQAS_search:28 -msgid "regularization function for model parameters alpha" -msgstr "" - -#: of tensorcircuit.applications.dqas.DQAS_search:29 -msgid "regularization function for circuit parameters theta" -msgstr "" - -#: of tensorcircuit.applications.dqas.DQAS_search +#: of tensorcircuit.abstractcircuit.AbstractCircuit.append:19 +msgid "The other circuit to be appended" +msgstr "" + +#: of tensorcircuit.abstractcircuit.AbstractCircuit.append:21 +msgid "" +"the qubit indices to which ``c`` is appended on. Defaults to None, which " +"means plain concatenation." +msgstr "" + +#: keras.src.engine.base_layer.Layer.add_weight +#: keras.src.engine.base_layer.Layer.compute_mask +#: keras.src.engine.base_layer.Layer.compute_output_shape +#: keras.src.engine.base_layer.Layer.compute_output_signature +#: keras.src.engine.base_layer.Layer.count_params +#: keras.src.engine.base_layer.Layer.from_config +#: keras.src.engine.base_layer.Layer.get_build_config +#: keras.src.engine.base_layer.Layer.get_config +#: keras.src.engine.base_layer.Layer.get_input_at +#: keras.src.engine.base_layer.Layer.get_input_mask_at +#: keras.src.engine.base_layer.Layer.get_input_shape_at +#: keras.src.engine.base_layer.Layer.get_output_at +#: keras.src.engine.base_layer.Layer.get_output_mask_at +#: keras.src.engine.base_layer.Layer.get_output_shape_at +#: keras.src.engine.base_layer.Layer.get_weights +#: keras.src.engine.training.Model.compute_loss +#: keras.src.engine.training.Model.compute_metrics +#: keras.src.engine.training.Model.evaluate keras.src.engine.training.Model.fit +#: keras.src.engine.training.Model.from_config +#: keras.src.engine.training.Model.get_compile_config +#: keras.src.engine.training.Model.get_config +#: keras.src.engine.training.Model.get_layer +#: keras.src.engine.training.Model.get_metrics_result +#: keras.src.engine.training.Model.get_weight_paths +#: keras.src.engine.training.Model.get_weights +#: keras.src.engine.training.Model.make_predict_function +#: keras.src.engine.training.Model.make_test_function +#: keras.src.engine.training.Model.make_train_function +#: keras.src.engine.training.Model.predict +#: keras.src.engine.training.Model.predict_on_batch +#: keras.src.engine.training.Model.predict_step +#: keras.src.engine.training.Model.test_on_batch +#: keras.src.engine.training.Model.test_step +#: keras.src.engine.training.Model.to_json +#: keras.src.engine.training.Model.to_yaml +#: keras.src.engine.training.Model.train_on_batch +#: keras.src.engine.training.Model.train_step +#: keras.src.optimizers.schedules.learning_rate_schedule.LearningRateSchedule.from_config +#: of tensorcircuit.abstractcircuit.AbstractCircuit.append +#: tensorcircuit.abstractcircuit.AbstractCircuit.cond_measurement +#: tensorcircuit.abstractcircuit.AbstractCircuit.expectation_ps +#: tensorcircuit.abstractcircuit.AbstractCircuit.from_json +#: tensorcircuit.abstractcircuit.AbstractCircuit.from_json_file +#: tensorcircuit.abstractcircuit.AbstractCircuit.from_qir +#: tensorcircuit.abstractcircuit.AbstractCircuit.from_qiskit +#: tensorcircuit.abstractcircuit.AbstractCircuit.gate_count +#: tensorcircuit.abstractcircuit.AbstractCircuit.gate_count_by_condition +#: tensorcircuit.abstractcircuit.AbstractCircuit.gate_summary +#: tensorcircuit.abstractcircuit.AbstractCircuit.get_positional_logical_mapping +#: tensorcircuit.abstractcircuit.AbstractCircuit.initial_mapping +#: tensorcircuit.abstractcircuit.AbstractCircuit.inverse +#: tensorcircuit.abstractcircuit.AbstractCircuit.prepend +#: tensorcircuit.abstractcircuit.AbstractCircuit.standardize_gate +#: tensorcircuit.abstractcircuit.AbstractCircuit.to_cirq +#: tensorcircuit.abstractcircuit.AbstractCircuit.to_json +#: tensorcircuit.abstractcircuit.AbstractCircuit.to_openqasm +#: tensorcircuit.abstractcircuit.AbstractCircuit.to_qir +#: tensorcircuit.abstractcircuit.AbstractCircuit.to_qiskit +#: tensorcircuit.abstractcircuit.AbstractCircuit.vis_tex +#: tensorcircuit.applications.dqas.DQAS_search #: tensorcircuit.applications.dqas.DQAS_search_pmb #: tensorcircuit.applications.dqas.get_var #: tensorcircuit.applications.dqas.get_weights @@ -591,9 +978,240 @@ msgstr "" #: tensorcircuit.applications.vags.tfim_measurements #: tensorcircuit.applications.vags.unitary_design #: tensorcircuit.applications.vags.unitary_design_block +#: tensorcircuit.applications.van.MADE.call +#: tensorcircuit.applications.van.MADE.compute_dtype +#: tensorcircuit.applications.van.MADE.input +#: tensorcircuit.applications.van.MADE.input_mask +#: tensorcircuit.applications.van.MADE.input_shape +#: tensorcircuit.applications.van.MADE.input_spec +#: tensorcircuit.applications.van.MADE.losses +#: tensorcircuit.applications.van.MADE.non_trainable_variables +#: tensorcircuit.applications.van.MADE.non_trainable_weights +#: tensorcircuit.applications.van.MADE.output +#: tensorcircuit.applications.van.MADE.output_mask +#: tensorcircuit.applications.van.MADE.output_shape +#: tensorcircuit.applications.van.MADE.run_eagerly +#: tensorcircuit.applications.van.MADE.state_updates +#: tensorcircuit.applications.van.MADE.submodules +#: tensorcircuit.applications.van.MADE.trainable_variables +#: tensorcircuit.applications.van.MADE.trainable_weights +#: tensorcircuit.applications.van.MADE.variables +#: tensorcircuit.applications.van.MADE.weights +#: tensorcircuit.applications.van.MaskedConv2D.call +#: tensorcircuit.applications.van.MaskedConv2D.compute_dtype +#: tensorcircuit.applications.van.MaskedConv2D.input +#: tensorcircuit.applications.van.MaskedConv2D.input_mask +#: tensorcircuit.applications.van.MaskedConv2D.input_shape +#: tensorcircuit.applications.van.MaskedConv2D.input_spec +#: tensorcircuit.applications.van.MaskedConv2D.losses +#: tensorcircuit.applications.van.MaskedConv2D.metrics +#: tensorcircuit.applications.van.MaskedConv2D.non_trainable_variables +#: tensorcircuit.applications.van.MaskedConv2D.non_trainable_weights +#: tensorcircuit.applications.van.MaskedConv2D.output +#: tensorcircuit.applications.van.MaskedConv2D.output_mask +#: tensorcircuit.applications.van.MaskedConv2D.output_shape +#: tensorcircuit.applications.van.MaskedConv2D.submodules +#: tensorcircuit.applications.van.MaskedConv2D.trainable_variables +#: tensorcircuit.applications.van.MaskedConv2D.trainable_weights +#: tensorcircuit.applications.van.MaskedConv2D.variables +#: tensorcircuit.applications.van.MaskedConv2D.weights +#: tensorcircuit.applications.van.MaskedLinear.call +#: tensorcircuit.applications.van.MaskedLinear.compute_dtype +#: tensorcircuit.applications.van.MaskedLinear.input +#: tensorcircuit.applications.van.MaskedLinear.input_mask +#: tensorcircuit.applications.van.MaskedLinear.input_shape +#: tensorcircuit.applications.van.MaskedLinear.input_spec +#: tensorcircuit.applications.van.MaskedLinear.losses +#: tensorcircuit.applications.van.MaskedLinear.metrics +#: tensorcircuit.applications.van.MaskedLinear.non_trainable_variables +#: tensorcircuit.applications.van.MaskedLinear.non_trainable_weights +#: tensorcircuit.applications.van.MaskedLinear.output +#: tensorcircuit.applications.van.MaskedLinear.output_mask +#: tensorcircuit.applications.van.MaskedLinear.output_shape +#: tensorcircuit.applications.van.MaskedLinear.submodules +#: tensorcircuit.applications.van.MaskedLinear.trainable_variables +#: tensorcircuit.applications.van.MaskedLinear.trainable_weights +#: tensorcircuit.applications.van.MaskedLinear.variables +#: tensorcircuit.applications.van.MaskedLinear.weights +#: tensorcircuit.applications.van.NMF.call +#: tensorcircuit.applications.van.NMF.compute_dtype +#: tensorcircuit.applications.van.NMF.input +#: tensorcircuit.applications.van.NMF.input_mask +#: tensorcircuit.applications.van.NMF.input_shape +#: tensorcircuit.applications.van.NMF.input_spec +#: tensorcircuit.applications.van.NMF.losses +#: tensorcircuit.applications.van.NMF.non_trainable_variables +#: tensorcircuit.applications.van.NMF.non_trainable_weights +#: tensorcircuit.applications.van.NMF.output +#: tensorcircuit.applications.van.NMF.output_mask +#: tensorcircuit.applications.van.NMF.output_shape +#: tensorcircuit.applications.van.NMF.run_eagerly +#: tensorcircuit.applications.van.NMF.state_updates +#: tensorcircuit.applications.van.NMF.submodules +#: tensorcircuit.applications.van.NMF.trainable_variables +#: tensorcircuit.applications.van.NMF.trainable_weights +#: tensorcircuit.applications.van.NMF.variables +#: tensorcircuit.applications.van.NMF.weights +#: tensorcircuit.applications.van.PixelCNN.call +#: tensorcircuit.applications.van.PixelCNN.compute_dtype +#: tensorcircuit.applications.van.PixelCNN.input +#: tensorcircuit.applications.van.PixelCNN.input_mask +#: tensorcircuit.applications.van.PixelCNN.input_shape +#: tensorcircuit.applications.van.PixelCNN.input_spec +#: tensorcircuit.applications.van.PixelCNN.losses +#: tensorcircuit.applications.van.PixelCNN.non_trainable_variables +#: tensorcircuit.applications.van.PixelCNN.non_trainable_weights +#: tensorcircuit.applications.van.PixelCNN.output +#: tensorcircuit.applications.van.PixelCNN.output_mask +#: tensorcircuit.applications.van.PixelCNN.output_shape +#: tensorcircuit.applications.van.PixelCNN.run_eagerly +#: tensorcircuit.applications.van.PixelCNN.state_updates +#: tensorcircuit.applications.van.PixelCNN.submodules +#: tensorcircuit.applications.van.PixelCNN.trainable_variables +#: tensorcircuit.applications.van.PixelCNN.trainable_weights +#: tensorcircuit.applications.van.PixelCNN.variables +#: tensorcircuit.applications.van.PixelCNN.weights +#: tensorcircuit.applications.van.ResidualBlock.call +#: tensorcircuit.applications.van.ResidualBlock.compute_dtype +#: tensorcircuit.applications.van.ResidualBlock.input +#: tensorcircuit.applications.van.ResidualBlock.input_mask +#: tensorcircuit.applications.van.ResidualBlock.input_shape +#: tensorcircuit.applications.van.ResidualBlock.input_spec +#: tensorcircuit.applications.van.ResidualBlock.losses +#: tensorcircuit.applications.van.ResidualBlock.metrics +#: tensorcircuit.applications.van.ResidualBlock.non_trainable_variables +#: tensorcircuit.applications.van.ResidualBlock.non_trainable_weights +#: tensorcircuit.applications.van.ResidualBlock.output +#: tensorcircuit.applications.van.ResidualBlock.output_mask +#: tensorcircuit.applications.van.ResidualBlock.output_shape +#: tensorcircuit.applications.van.ResidualBlock.submodules +#: tensorcircuit.applications.van.ResidualBlock.trainable_variables +#: tensorcircuit.applications.van.ResidualBlock.trainable_weights +#: tensorcircuit.applications.van.ResidualBlock.variables +#: tensorcircuit.applications.van.ResidualBlock.weights +#: tensorcircuit.applications.vqes.Linear.call +#: tensorcircuit.applications.vqes.Linear.compute_dtype +#: tensorcircuit.applications.vqes.Linear.input +#: tensorcircuit.applications.vqes.Linear.input_mask +#: tensorcircuit.applications.vqes.Linear.input_shape +#: tensorcircuit.applications.vqes.Linear.input_spec +#: tensorcircuit.applications.vqes.Linear.losses +#: tensorcircuit.applications.vqes.Linear.metrics +#: tensorcircuit.applications.vqes.Linear.non_trainable_variables +#: tensorcircuit.applications.vqes.Linear.non_trainable_weights +#: tensorcircuit.applications.vqes.Linear.output +#: tensorcircuit.applications.vqes.Linear.output_mask +#: tensorcircuit.applications.vqes.Linear.output_shape +#: tensorcircuit.applications.vqes.Linear.submodules +#: tensorcircuit.applications.vqes.Linear.trainable_variables +#: tensorcircuit.applications.vqes.Linear.trainable_weights +#: tensorcircuit.applications.vqes.Linear.variables +#: tensorcircuit.applications.vqes.Linear.weights #: tensorcircuit.applications.vqes.VQNHE.evaluation #: tensorcircuit.applications.vqes.VQNHE.plain_evaluation +#: tensorcircuit.backends.abstract_backend.ExtendedBackend.adjoint +#: tensorcircuit.backends.abstract_backend.ExtendedBackend.coo_sparse_matrix +#: tensorcircuit.backends.abstract_backend.ExtendedBackend.coo_sparse_matrix_from_numpy +#: tensorcircuit.backends.abstract_backend.ExtendedBackend.from_dlpack +#: tensorcircuit.backends.abstract_backend.ExtendedBackend.gather1d +#: tensorcircuit.backends.abstract_backend.ExtendedBackend.implicit_randc +#: tensorcircuit.backends.abstract_backend.ExtendedBackend.implicit_randn +#: tensorcircuit.backends.abstract_backend.ExtendedBackend.implicit_randu +#: tensorcircuit.backends.abstract_backend.ExtendedBackend.is_sparse +#: tensorcircuit.backends.abstract_backend.ExtendedBackend.jacfwd +#: tensorcircuit.backends.abstract_backend.ExtendedBackend.jacrev +#: tensorcircuit.backends.abstract_backend.ExtendedBackend.probability_sample +#: tensorcircuit.backends.abstract_backend.ExtendedBackend.random_split +#: tensorcircuit.backends.abstract_backend.ExtendedBackend.reshape2 +#: tensorcircuit.backends.abstract_backend.ExtendedBackend.reshapem +#: tensorcircuit.backends.abstract_backend.ExtendedBackend.reverse +#: tensorcircuit.backends.abstract_backend.ExtendedBackend.scan +#: tensorcircuit.backends.abstract_backend.ExtendedBackend.scatter +#: tensorcircuit.backends.abstract_backend.ExtendedBackend.sizen +#: tensorcircuit.backends.abstract_backend.ExtendedBackend.sparse_dense_matmul +#: tensorcircuit.backends.abstract_backend.ExtendedBackend.sqrtmh +#: tensorcircuit.backends.abstract_backend.ExtendedBackend.stateful_randc +#: tensorcircuit.backends.abstract_backend.ExtendedBackend.stateful_randn +#: tensorcircuit.backends.abstract_backend.ExtendedBackend.stateful_randu +#: tensorcircuit.backends.abstract_backend.ExtendedBackend.to_dense +#: tensorcircuit.backends.abstract_backend.ExtendedBackend.to_dlpack +#: tensorcircuit.backends.abstract_backend.ExtendedBackend.tree_flatten +#: tensorcircuit.backends.abstract_backend.ExtendedBackend.tree_map +#: tensorcircuit.backends.abstract_backend.ExtendedBackend.tree_unflatten #: tensorcircuit.backends.backend_factory.get_backend +#: tensorcircuit.backends.cupy_backend.CuPyBackend.abs +#: tensorcircuit.backends.cupy_backend.CuPyBackend.acos +#: tensorcircuit.backends.cupy_backend.CuPyBackend.acosh +#: tensorcircuit.backends.cupy_backend.CuPyBackend.arange +#: tensorcircuit.backends.cupy_backend.CuPyBackend.argmax +#: tensorcircuit.backends.cupy_backend.CuPyBackend.argmin +#: tensorcircuit.backends.cupy_backend.CuPyBackend.asin +#: tensorcircuit.backends.cupy_backend.CuPyBackend.asinh +#: tensorcircuit.backends.cupy_backend.CuPyBackend.atan +#: tensorcircuit.backends.cupy_backend.CuPyBackend.atan2 +#: tensorcircuit.backends.cupy_backend.CuPyBackend.atanh +#: tensorcircuit.backends.cupy_backend.CuPyBackend.cast +#: tensorcircuit.backends.cupy_backend.CuPyBackend.cond +#: tensorcircuit.backends.cupy_backend.CuPyBackend.conj +#: tensorcircuit.backends.cupy_backend.CuPyBackend.coo_sparse_matrix +#: tensorcircuit.backends.cupy_backend.CuPyBackend.copy +#: tensorcircuit.backends.cupy_backend.CuPyBackend.cos +#: tensorcircuit.backends.cupy_backend.CuPyBackend.cosh +#: tensorcircuit.backends.cupy_backend.CuPyBackend.cumsum +#: tensorcircuit.backends.cupy_backend.CuPyBackend.device +#: tensorcircuit.backends.cupy_backend.CuPyBackend.device_move +#: tensorcircuit.backends.cupy_backend.CuPyBackend.dtype +#: tensorcircuit.backends.cupy_backend.CuPyBackend.eigvalsh +#: tensorcircuit.backends.cupy_backend.CuPyBackend.expm +#: tensorcircuit.backends.cupy_backend.CuPyBackend.grad +#: tensorcircuit.backends.cupy_backend.CuPyBackend.i +#: tensorcircuit.backends.cupy_backend.CuPyBackend.imag +#: tensorcircuit.backends.cupy_backend.CuPyBackend.is_sparse +#: tensorcircuit.backends.cupy_backend.CuPyBackend.is_tensor +#: tensorcircuit.backends.cupy_backend.CuPyBackend.jit +#: tensorcircuit.backends.cupy_backend.CuPyBackend.jvp +#: tensorcircuit.backends.cupy_backend.CuPyBackend.kron +#: tensorcircuit.backends.cupy_backend.CuPyBackend.left_shift +#: tensorcircuit.backends.cupy_backend.CuPyBackend.max +#: tensorcircuit.backends.cupy_backend.CuPyBackend.mean +#: tensorcircuit.backends.cupy_backend.CuPyBackend.min +#: tensorcircuit.backends.cupy_backend.CuPyBackend.mod +#: tensorcircuit.backends.cupy_backend.CuPyBackend.multiply +#: tensorcircuit.backends.cupy_backend.CuPyBackend.numpy +#: tensorcircuit.backends.cupy_backend.CuPyBackend.onehot +#: tensorcircuit.backends.cupy_backend.CuPyBackend.real +#: tensorcircuit.backends.cupy_backend.CuPyBackend.relu +#: tensorcircuit.backends.cupy_backend.CuPyBackend.reshape +#: tensorcircuit.backends.cupy_backend.CuPyBackend.right_shift +#: tensorcircuit.backends.cupy_backend.CuPyBackend.scatter +#: tensorcircuit.backends.cupy_backend.CuPyBackend.searchsorted +#: tensorcircuit.backends.cupy_backend.CuPyBackend.shape_tuple +#: tensorcircuit.backends.cupy_backend.CuPyBackend.sigmoid +#: tensorcircuit.backends.cupy_backend.CuPyBackend.sin +#: tensorcircuit.backends.cupy_backend.CuPyBackend.sinh +#: tensorcircuit.backends.cupy_backend.CuPyBackend.size +#: tensorcircuit.backends.cupy_backend.CuPyBackend.softmax +#: tensorcircuit.backends.cupy_backend.CuPyBackend.solve +#: tensorcircuit.backends.cupy_backend.CuPyBackend.sparse_dense_matmul +#: tensorcircuit.backends.cupy_backend.CuPyBackend.stack +#: tensorcircuit.backends.cupy_backend.CuPyBackend.stateful_randc +#: tensorcircuit.backends.cupy_backend.CuPyBackend.stateful_randn +#: tensorcircuit.backends.cupy_backend.CuPyBackend.stateful_randu +#: tensorcircuit.backends.cupy_backend.CuPyBackend.std +#: tensorcircuit.backends.cupy_backend.CuPyBackend.stop_gradient +#: tensorcircuit.backends.cupy_backend.CuPyBackend.sum +#: tensorcircuit.backends.cupy_backend.CuPyBackend.switch +#: tensorcircuit.backends.cupy_backend.CuPyBackend.tan +#: tensorcircuit.backends.cupy_backend.CuPyBackend.tanh +#: tensorcircuit.backends.cupy_backend.CuPyBackend.tile +#: tensorcircuit.backends.cupy_backend.CuPyBackend.to_dense +#: tensorcircuit.backends.cupy_backend.CuPyBackend.transpose +#: tensorcircuit.backends.cupy_backend.CuPyBackend.unique_with_counts +#: tensorcircuit.backends.cupy_backend.CuPyBackend.value_and_grad +#: tensorcircuit.backends.cupy_backend.CuPyBackend.vectorized_value_and_grad +#: tensorcircuit.backends.cupy_backend.CuPyBackend.vjp +#: tensorcircuit.backends.cupy_backend.CuPyBackend.vmap +#: tensorcircuit.backends.jax_backend.JaxBackend.abs #: tensorcircuit.backends.jax_backend.JaxBackend.acos #: tensorcircuit.backends.jax_backend.JaxBackend.acosh #: tensorcircuit.backends.jax_backend.JaxBackend.arange @@ -603,6 +1221,7 @@ msgstr "" #: tensorcircuit.backends.jax_backend.JaxBackend.asinh #: tensorcircuit.backends.jax_backend.JaxBackend.atan #: tensorcircuit.backends.jax_backend.JaxBackend.atan2 +#: tensorcircuit.backends.jax_backend.JaxBackend.atanh #: tensorcircuit.backends.jax_backend.JaxBackend.cast #: tensorcircuit.backends.jax_backend.JaxBackend.cond #: tensorcircuit.backends.jax_backend.JaxBackend.coo_sparse_matrix @@ -610,8 +1229,12 @@ msgstr "" #: tensorcircuit.backends.jax_backend.JaxBackend.cos #: tensorcircuit.backends.jax_backend.JaxBackend.cosh #: tensorcircuit.backends.jax_backend.JaxBackend.cumsum +#: tensorcircuit.backends.jax_backend.JaxBackend.device +#: tensorcircuit.backends.jax_backend.JaxBackend.device_move +#: tensorcircuit.backends.jax_backend.JaxBackend.dtype #: tensorcircuit.backends.jax_backend.JaxBackend.eigvalsh #: tensorcircuit.backends.jax_backend.JaxBackend.expm +#: tensorcircuit.backends.jax_backend.JaxBackend.from_dlpack #: tensorcircuit.backends.jax_backend.JaxBackend.grad #: tensorcircuit.backends.jax_backend.JaxBackend.i #: tensorcircuit.backends.jax_backend.JaxBackend.imag @@ -634,7 +1257,9 @@ msgstr "" #: tensorcircuit.backends.jax_backend.JaxBackend.real #: tensorcircuit.backends.jax_backend.JaxBackend.relu #: tensorcircuit.backends.jax_backend.JaxBackend.right_shift +#: tensorcircuit.backends.jax_backend.JaxBackend.scan #: tensorcircuit.backends.jax_backend.JaxBackend.scatter +#: tensorcircuit.backends.jax_backend.JaxBackend.searchsorted #: tensorcircuit.backends.jax_backend.JaxBackend.sigmoid #: tensorcircuit.backends.jax_backend.JaxBackend.sin #: tensorcircuit.backends.jax_backend.JaxBackend.sinh @@ -646,12 +1271,14 @@ msgstr "" #: tensorcircuit.backends.jax_backend.JaxBackend.stateful_randc #: tensorcircuit.backends.jax_backend.JaxBackend.stateful_randn #: tensorcircuit.backends.jax_backend.JaxBackend.stateful_randu +#: tensorcircuit.backends.jax_backend.JaxBackend.std #: tensorcircuit.backends.jax_backend.JaxBackend.stop_gradient #: tensorcircuit.backends.jax_backend.JaxBackend.switch #: tensorcircuit.backends.jax_backend.JaxBackend.tan #: tensorcircuit.backends.jax_backend.JaxBackend.tanh #: tensorcircuit.backends.jax_backend.JaxBackend.tile #: tensorcircuit.backends.jax_backend.JaxBackend.to_dense +#: tensorcircuit.backends.jax_backend.JaxBackend.to_dlpack #: tensorcircuit.backends.jax_backend.JaxBackend.tree_flatten #: tensorcircuit.backends.jax_backend.JaxBackend.tree_map #: tensorcircuit.backends.jax_backend.JaxBackend.tree_unflatten @@ -660,6 +1287,8 @@ msgstr "" #: tensorcircuit.backends.jax_backend.JaxBackend.vectorized_value_and_grad #: tensorcircuit.backends.jax_backend.JaxBackend.vjp #: tensorcircuit.backends.jax_backend.JaxBackend.vmap +#: tensorcircuit.backends.jax_backend._svd_jax +#: tensorcircuit.backends.numpy_backend.NumpyBackend.abs #: tensorcircuit.backends.numpy_backend.NumpyBackend.acos #: tensorcircuit.backends.numpy_backend.NumpyBackend.acosh #: tensorcircuit.backends.numpy_backend.NumpyBackend.arange @@ -669,6 +1298,7 @@ msgstr "" #: tensorcircuit.backends.numpy_backend.NumpyBackend.asinh #: tensorcircuit.backends.numpy_backend.NumpyBackend.atan #: tensorcircuit.backends.numpy_backend.NumpyBackend.atan2 +#: tensorcircuit.backends.numpy_backend.NumpyBackend.atanh #: tensorcircuit.backends.numpy_backend.NumpyBackend.cast #: tensorcircuit.backends.numpy_backend.NumpyBackend.cond #: tensorcircuit.backends.numpy_backend.NumpyBackend.coo_sparse_matrix @@ -676,6 +1306,9 @@ msgstr "" #: tensorcircuit.backends.numpy_backend.NumpyBackend.cos #: tensorcircuit.backends.numpy_backend.NumpyBackend.cosh #: tensorcircuit.backends.numpy_backend.NumpyBackend.cumsum +#: tensorcircuit.backends.numpy_backend.NumpyBackend.device +#: tensorcircuit.backends.numpy_backend.NumpyBackend.device_move +#: tensorcircuit.backends.numpy_backend.NumpyBackend.dtype #: tensorcircuit.backends.numpy_backend.NumpyBackend.eigvalsh #: tensorcircuit.backends.numpy_backend.NumpyBackend.expm #: tensorcircuit.backends.numpy_backend.NumpyBackend.grad @@ -697,6 +1330,7 @@ msgstr "" #: tensorcircuit.backends.numpy_backend.NumpyBackend.relu #: tensorcircuit.backends.numpy_backend.NumpyBackend.right_shift #: tensorcircuit.backends.numpy_backend.NumpyBackend.scatter +#: tensorcircuit.backends.numpy_backend.NumpyBackend.searchsorted #: tensorcircuit.backends.numpy_backend.NumpyBackend.sigmoid #: tensorcircuit.backends.numpy_backend.NumpyBackend.sin #: tensorcircuit.backends.numpy_backend.NumpyBackend.sinh @@ -708,6 +1342,7 @@ msgstr "" #: tensorcircuit.backends.numpy_backend.NumpyBackend.stateful_randc #: tensorcircuit.backends.numpy_backend.NumpyBackend.stateful_randn #: tensorcircuit.backends.numpy_backend.NumpyBackend.stateful_randu +#: tensorcircuit.backends.numpy_backend.NumpyBackend.std #: tensorcircuit.backends.numpy_backend.NumpyBackend.stop_gradient #: tensorcircuit.backends.numpy_backend.NumpyBackend.switch #: tensorcircuit.backends.numpy_backend.NumpyBackend.tan @@ -719,6 +1354,7 @@ msgstr "" #: tensorcircuit.backends.numpy_backend.NumpyBackend.vectorized_value_and_grad #: tensorcircuit.backends.numpy_backend.NumpyBackend.vjp #: tensorcircuit.backends.numpy_backend.NumpyBackend.vmap +#: tensorcircuit.backends.numpy_backend._sum_numpy #: tensorcircuit.backends.pytorch_backend.PyTorchBackend.acos #: tensorcircuit.backends.pytorch_backend.PyTorchBackend.acosh #: tensorcircuit.backends.pytorch_backend.PyTorchBackend.arange @@ -728,14 +1364,19 @@ msgstr "" #: tensorcircuit.backends.pytorch_backend.PyTorchBackend.asinh #: tensorcircuit.backends.pytorch_backend.PyTorchBackend.atan #: tensorcircuit.backends.pytorch_backend.PyTorchBackend.atan2 +#: tensorcircuit.backends.pytorch_backend.PyTorchBackend.atanh #: tensorcircuit.backends.pytorch_backend.PyTorchBackend.cast #: tensorcircuit.backends.pytorch_backend.PyTorchBackend.cond #: tensorcircuit.backends.pytorch_backend.PyTorchBackend.copy #: tensorcircuit.backends.pytorch_backend.PyTorchBackend.cos #: tensorcircuit.backends.pytorch_backend.PyTorchBackend.cosh #: tensorcircuit.backends.pytorch_backend.PyTorchBackend.cumsum +#: tensorcircuit.backends.pytorch_backend.PyTorchBackend.device +#: tensorcircuit.backends.pytorch_backend.PyTorchBackend.device_move +#: tensorcircuit.backends.pytorch_backend.PyTorchBackend.dtype #: tensorcircuit.backends.pytorch_backend.PyTorchBackend.eigvalsh #: tensorcircuit.backends.pytorch_backend.PyTorchBackend.expm +#: tensorcircuit.backends.pytorch_backend.PyTorchBackend.from_dlpack #: tensorcircuit.backends.pytorch_backend.PyTorchBackend.grad #: tensorcircuit.backends.pytorch_backend.PyTorchBackend.i #: tensorcircuit.backends.pytorch_backend.PyTorchBackend.imag @@ -754,6 +1395,7 @@ msgstr "" #: tensorcircuit.backends.pytorch_backend.PyTorchBackend.relu #: tensorcircuit.backends.pytorch_backend.PyTorchBackend.reverse #: tensorcircuit.backends.pytorch_backend.PyTorchBackend.right_shift +#: tensorcircuit.backends.pytorch_backend.PyTorchBackend.searchsorted #: tensorcircuit.backends.pytorch_backend.PyTorchBackend.sigmoid #: tensorcircuit.backends.pytorch_backend.PyTorchBackend.sin #: tensorcircuit.backends.pytorch_backend.PyTorchBackend.sinh @@ -761,11 +1403,13 @@ msgstr "" #: tensorcircuit.backends.pytorch_backend.PyTorchBackend.softmax #: tensorcircuit.backends.pytorch_backend.PyTorchBackend.solve #: tensorcircuit.backends.pytorch_backend.PyTorchBackend.stack +#: tensorcircuit.backends.pytorch_backend.PyTorchBackend.std #: tensorcircuit.backends.pytorch_backend.PyTorchBackend.stop_gradient #: tensorcircuit.backends.pytorch_backend.PyTorchBackend.switch #: tensorcircuit.backends.pytorch_backend.PyTorchBackend.tan #: tensorcircuit.backends.pytorch_backend.PyTorchBackend.tanh #: tensorcircuit.backends.pytorch_backend.PyTorchBackend.tile +#: tensorcircuit.backends.pytorch_backend.PyTorchBackend.to_dlpack #: tensorcircuit.backends.pytorch_backend.PyTorchBackend.tree_flatten #: tensorcircuit.backends.pytorch_backend.PyTorchBackend.tree_map #: tensorcircuit.backends.pytorch_backend.PyTorchBackend.tree_unflatten @@ -774,6 +1418,11 @@ msgstr "" #: tensorcircuit.backends.pytorch_backend.PyTorchBackend.vectorized_value_and_grad #: tensorcircuit.backends.pytorch_backend.PyTorchBackend.vjp #: tensorcircuit.backends.pytorch_backend.PyTorchBackend.vmap +#: tensorcircuit.backends.pytorch_backend._conj_torch +#: tensorcircuit.backends.pytorch_backend._qr_torch +#: tensorcircuit.backends.pytorch_backend._rq_torch +#: tensorcircuit.backends.pytorch_backend._sum_torch +#: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.abs #: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.acos #: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.acosh #: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.arange @@ -783,6 +1432,7 @@ msgstr "" #: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.asinh #: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.atan #: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.atan2 +#: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.atanh #: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.cast #: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.cond #: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.coo_sparse_matrix @@ -790,8 +1440,12 @@ msgstr "" #: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.cos #: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.cosh #: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.cumsum +#: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.device +#: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.device_move +#: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.dtype #: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.eigvalsh #: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.expm +#: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.from_dlpack #: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.gather1d #: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.grad #: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.i @@ -811,7 +1465,9 @@ msgstr "" #: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.real #: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.relu #: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.right_shift +#: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.scan #: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.scatter +#: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.searchsorted #: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.sigmoid #: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.sin #: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.sinh @@ -823,40 +1479,76 @@ msgstr "" #: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.stateful_randc #: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.stateful_randn #: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.stateful_randu +#: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.std #: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.stop_gradient #: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.switch #: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.tan #: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.tanh #: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.tile #: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.to_dense +#: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.to_dlpack #: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.unique_with_counts #: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.value_and_grad #: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.vectorized_value_and_grad #: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.vjp #: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.vmap +#: tensorcircuit.backends.tensorflow_backend._matmul_tf +#: tensorcircuit.backends.tensorflow_backend._qr_tf +#: tensorcircuit.backends.tensorflow_backend._rq_tf +#: tensorcircuit.basecircuit.BaseCircuit.amplitude +#: tensorcircuit.basecircuit.BaseCircuit.cond_measurement +#: tensorcircuit.basecircuit.BaseCircuit.copy +#: tensorcircuit.basecircuit.BaseCircuit.expectation_before +#: tensorcircuit.basecircuit.BaseCircuit.get_quvector +#: tensorcircuit.basecircuit.BaseCircuit.measure_jit +#: tensorcircuit.basecircuit.BaseCircuit.perfect_sampling +#: tensorcircuit.basecircuit.BaseCircuit.probability +#: tensorcircuit.basecircuit.BaseCircuit.sample +#: tensorcircuit.basecircuit.BaseCircuit.sample_expectation_ps +#: tensorcircuit.basecircuit.BaseCircuit.to_qir #: tensorcircuit.channels.amplitudedampingchannel -#: tensorcircuit.channels.depolarizingchannel +#: tensorcircuit.channels.choi_to_kraus tensorcircuit.channels.choi_to_super +#: tensorcircuit.channels.composedkraus +#: tensorcircuit.channels.depolarizingchannel tensorcircuit.channels.evol_kraus +#: tensorcircuit.channels.evol_superop +#: tensorcircuit.channels.generaldepolarizingchannel +#: tensorcircuit.channels.is_hermitian_matrix +#: tensorcircuit.channels.isotropicdepolarizingchannel +#: tensorcircuit.channels.kraus_to_choi tensorcircuit.channels.kraus_to_super #: tensorcircuit.channels.kraus_to_super_gate +#: tensorcircuit.channels.krausgate_to_krausmatrix +#: tensorcircuit.channels.krausmatrix_to_krausgate #: tensorcircuit.channels.phasedampingchannel -#: tensorcircuit.channels.resetchannel tensorcircuit.circuit.Circuit.amplitude -#: tensorcircuit.circuit.Circuit.cond_measurement -#: tensorcircuit.circuit.Circuit.depolarizing +#: tensorcircuit.channels.resetchannel tensorcircuit.channels.reshuffle +#: tensorcircuit.channels.super_to_choi tensorcircuit.channels.super_to_kraus +#: tensorcircuit.channels.thermalrelaxationchannel +#: tensorcircuit.circuit.Circuit.depolarizing_reference #: tensorcircuit.circuit.Circuit.expectation -#: tensorcircuit.circuit.Circuit.expectation_before -#: tensorcircuit.circuit.Circuit.from_qir -#: tensorcircuit.circuit.Circuit.from_qiskit #: tensorcircuit.circuit.Circuit.get_quoperator -#: tensorcircuit.circuit.Circuit.get_quvector #: tensorcircuit.circuit.Circuit.is_valid tensorcircuit.circuit.Circuit.matrix -#: tensorcircuit.circuit.Circuit.measure_jit #: tensorcircuit.circuit.Circuit.measure_reference -#: tensorcircuit.circuit.Circuit.perfect_sampling -#: tensorcircuit.circuit.Circuit.sample tensorcircuit.circuit.Circuit.to_qir -#: tensorcircuit.circuit.Circuit.to_qiskit #: tensorcircuit.circuit.Circuit.unitary_kraus -#: tensorcircuit.circuit.Circuit.vis_tex -#: tensorcircuit.circuit.Circuit.wavefunction -#: tensorcircuit.circuit._expectation_ps tensorcircuit.circuit.expectation +#: tensorcircuit.circuit.Circuit.wavefunction tensorcircuit.circuit.expectation +#: tensorcircuit.cloud.abstraction.Device.list_properties +#: tensorcircuit.cloud.abstraction.Device.native_gates +#: tensorcircuit.cloud.abstraction.Device.topology +#: tensorcircuit.cloud.abstraction.Device.topology_graph +#: tensorcircuit.cloud.abstraction.Task.details +#: tensorcircuit.cloud.abstraction.Task.get_device +#: tensorcircuit.cloud.abstraction.Task.resubmit +#: tensorcircuit.cloud.abstraction.Task.results +#: tensorcircuit.cloud.abstraction.Task.state +#: tensorcircuit.cloud.apis.get_device tensorcircuit.cloud.apis.get_provider +#: tensorcircuit.cloud.apis.get_task tensorcircuit.cloud.apis.get_task_details +#: tensorcircuit.cloud.apis.get_token tensorcircuit.cloud.apis.list_devices +#: tensorcircuit.cloud.apis.list_properties +#: tensorcircuit.cloud.apis.list_providers tensorcircuit.cloud.apis.list_tasks +#: tensorcircuit.cloud.apis.resubmit_task tensorcircuit.cloud.apis.set_device +#: tensorcircuit.cloud.apis.set_provider tensorcircuit.cloud.apis.set_token +#: tensorcircuit.cloud.apis.submit_task tensorcircuit.cloud.tencent.submit_task +#: tensorcircuit.cloud.utils.set_proxy +#: tensorcircuit.cloud.wrapper.batch_expectation_ps +#: tensorcircuit.compiler.qiskit_compiler.qiskit_compile #: tensorcircuit.cons.get_contractor tensorcircuit.cons.get_dtype #: tensorcircuit.cons.plain_contractor tensorcircuit.cons.set_contractor #: tensorcircuit.cons.set_dtype tensorcircuit.cons.set_function_backend @@ -865,20 +1557,68 @@ msgstr "" #: tensorcircuit.cons.set_tensornetwork_backend #: tensorcircuit.densitymatrix.DMCircuit.densitymatrix #: tensorcircuit.densitymatrix.DMCircuit.expectation -#: tensorcircuit.densitymatrix.DMCircuit.measure_jit -#: tensorcircuit.densitymatrix.DMCircuit.perfect_sampling +#: tensorcircuit.densitymatrix.DMCircuit.get_dm_as_quoperator +#: tensorcircuit.densitymatrix.DMCircuit.to_circuit +#: tensorcircuit.densitymatrix.DMCircuit.wavefunction +#: tensorcircuit.experimental.evol_global tensorcircuit.experimental.evol_local +#: tensorcircuit.experimental.hamiltonian_evol +#: tensorcircuit.experimental.parameter_shift_grad +#: tensorcircuit.experimental.parameter_shift_grad_v2 #: tensorcircuit.gates.any_gate tensorcircuit.gates.bmatrix #: tensorcircuit.gates.cr_gate tensorcircuit.gates.exponential_gate -#: tensorcircuit.gates.exponential_gate_unity tensorcircuit.gates.iswap_gate -#: tensorcircuit.gates.matrix_for_gate tensorcircuit.gates.num_to_tensor +#: tensorcircuit.gates.exponential_gate_unity +#: tensorcircuit.gates.get_u_parameter tensorcircuit.gates.iswap_gate +#: tensorcircuit.gates.matrix_for_gate tensorcircuit.gates.multicontrol_gate +#: tensorcircuit.gates.num_to_tensor tensorcircuit.gates.phase_gate #: tensorcircuit.gates.r_gate tensorcircuit.gates.random_single_qubit_gate #: tensorcircuit.gates.random_two_qubit_gate #: tensorcircuit.gates.rgate_theoretical tensorcircuit.gates.rx_gate #: tensorcircuit.gates.rxx_gate tensorcircuit.gates.ry_gate #: tensorcircuit.gates.ryy_gate tensorcircuit.gates.rz_gate -#: tensorcircuit.gates.rzz_gate -#: tensorcircuit.interfaces.scipy_optimize_interface -#: tensorcircuit.interfaces.torch_interface tensorcircuit.keras.load_func +#: tensorcircuit.gates.rzz_gate tensorcircuit.gates.u_gate +#: tensorcircuit.interfaces.numpy.numpy_interface +#: tensorcircuit.interfaces.scipy.scipy_optimize_interface +#: tensorcircuit.interfaces.tensorflow.tensorflow_interface +#: tensorcircuit.interfaces.tensortrans.args_to_tensor +#: tensorcircuit.interfaces.tensortrans.general_args_to_numpy +#: tensorcircuit.interfaces.tensortrans.numpy_args_to_backend +#: tensorcircuit.interfaces.tensortrans.which_backend +#: tensorcircuit.interfaces.torch.torch_interface +#: tensorcircuit.interfaces.torch.torch_interface_kws +#: tensorcircuit.keras.HardwareLayer.compute_dtype +#: tensorcircuit.keras.HardwareLayer.input +#: tensorcircuit.keras.HardwareLayer.input_mask +#: tensorcircuit.keras.HardwareLayer.input_shape +#: tensorcircuit.keras.HardwareLayer.input_spec +#: tensorcircuit.keras.HardwareLayer.losses +#: tensorcircuit.keras.HardwareLayer.metrics +#: tensorcircuit.keras.HardwareLayer.non_trainable_variables +#: tensorcircuit.keras.HardwareLayer.non_trainable_weights +#: tensorcircuit.keras.HardwareLayer.output +#: tensorcircuit.keras.HardwareLayer.output_mask +#: tensorcircuit.keras.HardwareLayer.output_shape +#: tensorcircuit.keras.HardwareLayer.submodules +#: tensorcircuit.keras.HardwareLayer.trainable_variables +#: tensorcircuit.keras.HardwareLayer.trainable_weights +#: tensorcircuit.keras.HardwareLayer.variables +#: tensorcircuit.keras.HardwareLayer.weights +#: tensorcircuit.keras.QuantumLayer.compute_dtype +#: tensorcircuit.keras.QuantumLayer.input +#: tensorcircuit.keras.QuantumLayer.input_mask +#: tensorcircuit.keras.QuantumLayer.input_shape +#: tensorcircuit.keras.QuantumLayer.input_spec +#: tensorcircuit.keras.QuantumLayer.losses +#: tensorcircuit.keras.QuantumLayer.metrics +#: tensorcircuit.keras.QuantumLayer.non_trainable_variables +#: tensorcircuit.keras.QuantumLayer.non_trainable_weights +#: tensorcircuit.keras.QuantumLayer.output +#: tensorcircuit.keras.QuantumLayer.output_mask +#: tensorcircuit.keras.QuantumLayer.output_shape +#: tensorcircuit.keras.QuantumLayer.submodules +#: tensorcircuit.keras.QuantumLayer.trainable_variables +#: tensorcircuit.keras.QuantumLayer.trainable_weights +#: tensorcircuit.keras.QuantumLayer.variables +#: tensorcircuit.keras.QuantumLayer.weights tensorcircuit.keras.load_func #: tensorcircuit.keras.output_asis_loss #: tensorcircuit.mps_base.FiniteMPS.apply_two_site_gate #: tensorcircuit.mps_base.FiniteMPS.measure_local_operator @@ -886,16 +1626,24 @@ msgstr "" #: tensorcircuit.mpscircuit.MPSCircuit.conj #: tensorcircuit.mpscircuit.MPSCircuit.copy #: tensorcircuit.mpscircuit.MPSCircuit.copy_without_tensor -#: tensorcircuit.mpscircuit.MPSCircuit.expectation_single_gate -#: tensorcircuit.mpscircuit.MPSCircuit.expectation_two_gates_product -#: tensorcircuit.mpscircuit.MPSCircuit.from_wavefunction -#: tensorcircuit.mpscircuit.MPSCircuit.general_expectation +#: tensorcircuit.mpscircuit.MPSCircuit.expectation +#: tensorcircuit.mpscircuit.MPSCircuit.get_bond_dimensions +#: tensorcircuit.mpscircuit.MPSCircuit.get_center_position #: tensorcircuit.mpscircuit.MPSCircuit.get_norm +#: tensorcircuit.mpscircuit.MPSCircuit.get_quvector +#: tensorcircuit.mpscircuit.MPSCircuit.get_tensors #: tensorcircuit.mpscircuit.MPSCircuit.is_valid #: tensorcircuit.mpscircuit.MPSCircuit.measure #: tensorcircuit.mpscircuit.MPSCircuit.proj_with_mps #: tensorcircuit.mpscircuit.MPSCircuit.wavefunction -#: tensorcircuit.mpscircuit.split_tensor tensorcircuit.quantum.PauliString2COO +#: tensorcircuit.mpscircuit.MPSCircuit.wavefunction_to_tensors +#: tensorcircuit.mpscircuit.split_tensor +#: tensorcircuit.noisemodel.NoiseConf.channel_count +#: tensorcircuit.noisemodel.apply_qir_with_noise +#: tensorcircuit.noisemodel.circuit_with_noise +#: tensorcircuit.noisemodel.expectation_noisfy +#: tensorcircuit.noisemodel.sample_expectation_ps_noisfy +#: tensorcircuit.quantum.PauliString2COO #: tensorcircuit.quantum.PauliStringSum2COO #: tensorcircuit.quantum.PauliStringSum2COO_numpy #: tensorcircuit.quantum.PauliStringSum2COO_tf @@ -915,108 +1663,356 @@ msgstr "" #: tensorcircuit.quantum.QuVector.projector #: tensorcircuit.quantum.QuVector.reduced_density #: tensorcircuit.quantum.correlation_from_counts -#: tensorcircuit.quantum.double_state +#: tensorcircuit.quantum.correlation_from_samples +#: tensorcircuit.quantum.count_d2s tensorcircuit.quantum.count_s2d +#: tensorcircuit.quantum.count_tuple2dict +#: tensorcircuit.quantum.count_vector2dict tensorcircuit.quantum.double_state #: tensorcircuit.quantum.eliminate_identities tensorcircuit.quantum.entropy #: tensorcircuit.quantum.fidelity tensorcircuit.quantum.free_energy #: tensorcircuit.quantum.generate_local_hamiltonian #: tensorcircuit.quantum.gibbs_state #: tensorcircuit.quantum.heisenberg_hamiltonian tensorcircuit.quantum.identity #: tensorcircuit.quantum.measurement_counts -#: tensorcircuit.quantum.mutual_information +#: tensorcircuit.quantum.mutual_information tensorcircuit.quantum.ps2xyz #: tensorcircuit.quantum.quantum_constructor tensorcircuit.quantum.quimb2qop #: tensorcircuit.quantum.reduced_density_matrix #: tensorcircuit.quantum.renyi_entropy tensorcircuit.quantum.renyi_free_energy +#: tensorcircuit.quantum.sample2all tensorcircuit.quantum.sample2count +#: tensorcircuit.quantum.sample_bin2int tensorcircuit.quantum.sample_int2bin #: tensorcircuit.quantum.spin_by_basis tensorcircuit.quantum.taylorlnm #: tensorcircuit.quantum.tn2qop tensorcircuit.quantum.trace_distance #: tensorcircuit.quantum.trace_product -#: tensorcircuit.quantum.truncated_free_energy +#: tensorcircuit.quantum.truncated_free_energy tensorcircuit.quantum.xyz2ps +#: tensorcircuit.results.counts.expectation +#: tensorcircuit.results.counts.plot_histogram +#: tensorcircuit.results.qem.qem_methods.add_dd +#: tensorcircuit.results.qem.qem_methods.apply_dd +#: tensorcircuit.results.qem.qem_methods.apply_rc +#: tensorcircuit.results.qem.qem_methods.apply_zne +#: tensorcircuit.results.qem.qem_methods.prune_ddcircuit +#: tensorcircuit.results.qem.qem_methods.used_qubits +#: tensorcircuit.results.readout_mitigation.ReadoutMit.apply_readout_mitigation +#: tensorcircuit.results.readout_mitigation.ReadoutMit.expectation +#: tensorcircuit.results.readout_mitigation.ReadoutMit.get_matrix +#: tensorcircuit.results.readout_mitigation.ReadoutMit.global_miti_readout_circ +#: tensorcircuit.results.readout_mitigation.ReadoutMit.local_miti_readout_circ +#: tensorcircuit.results.readout_mitigation.ReadoutMit.mapping_preprocess +#: tensorcircuit.results.readout_mitigation.ReadoutMit.mitigate_probability +#: tensorcircuit.results.readout_mitigation.ReadoutMit.newrange +#: tensorcircuit.results.readout_mitigation.ReadoutMit.ubs #: tensorcircuit.simplify.infer_new_shape #: tensorcircuit.simplify.pseudo_contract_between #: tensorcircuit.templates.blocks.Bell_pair_block #: tensorcircuit.templates.blocks.example_block +#: tensorcircuit.templates.blocks.qft #: tensorcircuit.templates.blocks.state_centric -#: tensorcircuit.templates.chems.get_ps #: tensorcircuit.templates.graphs.Grid2DCoord.all_cols #: tensorcircuit.templates.graphs.Grid2DCoord.all_rows #: tensorcircuit.templates.graphs.Grid2DCoord.lattice_graph #: tensorcircuit.templates.graphs.Line1D +#: tensorcircuit.templates.measurements.any_local_measurements #: tensorcircuit.templates.measurements.any_measurements #: tensorcircuit.templates.measurements.heisenberg_measurements #: tensorcircuit.templates.measurements.mpo_expectation #: tensorcircuit.templates.measurements.operator_expectation #: tensorcircuit.templates.measurements.sparse_expectation #: tensorcircuit.templates.measurements.spin_glass_measurements -#: tensorcircuit.translation.perm_matrix tensorcircuit.translation.qir2qiskit -#: tensorcircuit.translation.qiskit2tc tensorcircuit.utils.append +#: tensorcircuit.translation.eqasm2tc tensorcircuit.translation.perm_matrix +#: tensorcircuit.translation.qir2cirq tensorcircuit.translation.qir2json +#: tensorcircuit.translation.qir2qiskit tensorcircuit.translation.qiskit2tc +#: tensorcircuit.translation.qiskit_from_qasm_str_ordered_measure +#: tensorcircuit.utils.append tensorcircuit.utils.arg_alias #: tensorcircuit.utils.benchmark tensorcircuit.utils.is_m1mac #: tensorcircuit.utils.return_partial tensorcircuit.vis.gate_name_trans -#: tensorcircuit.vis.render_pdf +#: tensorcircuit.vis.qir2tex tensorcircuit.vis.render_pdf +#: tensorflow.python.module.module.Module.with_name_scope +#: tensornetwork.backends.abstract_backend.AbstractBackend.addition +#: tensornetwork.backends.abstract_backend.AbstractBackend.broadcast_left_multiplication +#: tensornetwork.backends.abstract_backend.AbstractBackend.broadcast_right_multiplication +#: tensornetwork.backends.abstract_backend.AbstractBackend.deserialize_tensor +#: tensornetwork.backends.abstract_backend.AbstractBackend.diagflat +#: tensornetwork.backends.abstract_backend.AbstractBackend.diagonal +#: tensornetwork.backends.abstract_backend.AbstractBackend.divide +#: tensornetwork.backends.abstract_backend.AbstractBackend.eigh +#: tensornetwork.backends.abstract_backend.AbstractBackend.eigs +#: tensornetwork.backends.abstract_backend.AbstractBackend.eigsh +#: tensornetwork.backends.abstract_backend.AbstractBackend.eigsh_lanczos +#: tensornetwork.backends.abstract_backend.AbstractBackend.eps +#: tensornetwork.backends.abstract_backend.AbstractBackend.exp +#: tensornetwork.backends.abstract_backend.AbstractBackend.gmres +#: tensornetwork.backends.abstract_backend.AbstractBackend.inv +#: tensornetwork.backends.abstract_backend.AbstractBackend.item +#: tensornetwork.backends.abstract_backend.AbstractBackend.log +#: tensornetwork.backends.abstract_backend.AbstractBackend.matmul +#: tensornetwork.backends.abstract_backend.AbstractBackend.pivot +#: tensornetwork.backends.abstract_backend.AbstractBackend.power +#: tensornetwork.backends.abstract_backend.AbstractBackend.random_uniform +#: tensornetwork.backends.abstract_backend.AbstractBackend.serialize_tensor +#: tensornetwork.backends.abstract_backend.AbstractBackend.shape_tensor +#: tensornetwork.backends.abstract_backend.AbstractBackend.subtraction +#: tensornetwork.backends.abstract_backend.AbstractBackend.svd +#: tensornetwork.backends.abstract_backend.AbstractBackend.trace +#: tensornetwork.backends.jax.jax_backend.JaxBackend.addition +#: tensornetwork.backends.jax.jax_backend.JaxBackend.broadcast_left_multiplication +#: tensornetwork.backends.jax.jax_backend.JaxBackend.broadcast_right_multiplication +#: tensornetwork.backends.jax.jax_backend.JaxBackend.conj +#: tensornetwork.backends.jax.jax_backend.JaxBackend.diagflat +#: tensornetwork.backends.jax.jax_backend.JaxBackend.diagonal +#: tensornetwork.backends.jax.jax_backend.JaxBackend.divide +#: tensornetwork.backends.jax.jax_backend.JaxBackend.eigh +#: tensornetwork.backends.jax.jax_backend.JaxBackend.eigs +#: tensornetwork.backends.jax.jax_backend.JaxBackend.eigsh +#: tensornetwork.backends.jax.jax_backend.JaxBackend.eigsh_lanczos +#: tensornetwork.backends.jax.jax_backend.JaxBackend.eps +#: tensornetwork.backends.jax.jax_backend.JaxBackend.exp +#: tensornetwork.backends.jax.jax_backend.JaxBackend.inv +#: tensornetwork.backends.jax.jax_backend.JaxBackend.item +#: tensornetwork.backends.jax.jax_backend.JaxBackend.log +#: tensornetwork.backends.jax.jax_backend.JaxBackend.matmul +#: tensornetwork.backends.jax.jax_backend.JaxBackend.multiply +#: tensornetwork.backends.jax.jax_backend.JaxBackend.random_uniform +#: tensornetwork.backends.jax.jax_backend.JaxBackend.reshape +#: tensornetwork.backends.jax.jax_backend.JaxBackend.shape_tensor +#: tensornetwork.backends.jax.jax_backend.JaxBackend.shape_tuple +#: tensornetwork.backends.jax.jax_backend.JaxBackend.subtraction +#: tensornetwork.backends.jax.jax_backend.JaxBackend.sum +#: tensornetwork.backends.jax.jax_backend.JaxBackend.trace +#: tensornetwork.backends.jax.jax_backend.JaxBackend.transpose +#: tensornetwork.backends.numpy.numpy_backend.NumPyBackend.addition +#: tensornetwork.backends.numpy.numpy_backend.NumPyBackend.broadcast_left_multiplication +#: tensornetwork.backends.numpy.numpy_backend.NumPyBackend.broadcast_right_multiplication +#: tensornetwork.backends.numpy.numpy_backend.NumPyBackend.conj +#: tensornetwork.backends.numpy.numpy_backend.NumPyBackend.deserialize_tensor +#: tensornetwork.backends.numpy.numpy_backend.NumPyBackend.diagflat +#: tensornetwork.backends.numpy.numpy_backend.NumPyBackend.diagonal +#: tensornetwork.backends.numpy.numpy_backend.NumPyBackend.divide +#: tensornetwork.backends.numpy.numpy_backend.NumPyBackend.eigh +#: tensornetwork.backends.numpy.numpy_backend.NumPyBackend.eigs +#: tensornetwork.backends.numpy.numpy_backend.NumPyBackend.eigsh +#: tensornetwork.backends.numpy.numpy_backend.NumPyBackend.eigsh_lanczos +#: tensornetwork.backends.numpy.numpy_backend.NumPyBackend.eps +#: tensornetwork.backends.numpy.numpy_backend.NumPyBackend.exp +#: tensornetwork.backends.numpy.numpy_backend.NumPyBackend.inv +#: tensornetwork.backends.numpy.numpy_backend.NumPyBackend.item +#: tensornetwork.backends.numpy.numpy_backend.NumPyBackend.log +#: tensornetwork.backends.numpy.numpy_backend.NumPyBackend.matmul +#: tensornetwork.backends.numpy.numpy_backend.NumPyBackend.multiply +#: tensornetwork.backends.numpy.numpy_backend.NumPyBackend.power +#: tensornetwork.backends.numpy.numpy_backend.NumPyBackend.random_uniform +#: tensornetwork.backends.numpy.numpy_backend.NumPyBackend.reshape +#: tensornetwork.backends.numpy.numpy_backend.NumPyBackend.serialize_tensor +#: tensornetwork.backends.numpy.numpy_backend.NumPyBackend.shape_tensor +#: tensornetwork.backends.numpy.numpy_backend.NumPyBackend.shape_tuple +#: tensornetwork.backends.numpy.numpy_backend.NumPyBackend.subtraction +#: tensornetwork.backends.numpy.numpy_backend.NumPyBackend.svd +#: tensornetwork.backends.numpy.numpy_backend.NumPyBackend.trace +#: tensornetwork.backends.numpy.numpy_backend.NumPyBackend.transpose +#: tensornetwork.backends.pytorch.pytorch_backend.PyTorchBackend.abs +#: tensornetwork.backends.pytorch.pytorch_backend.PyTorchBackend.addition +#: tensornetwork.backends.pytorch.pytorch_backend.PyTorchBackend.broadcast_left_multiplication +#: tensornetwork.backends.pytorch.pytorch_backend.PyTorchBackend.broadcast_right_multiplication +#: tensornetwork.backends.pytorch.pytorch_backend.PyTorchBackend.diagflat +#: tensornetwork.backends.pytorch.pytorch_backend.PyTorchBackend.diagonal +#: tensornetwork.backends.pytorch.pytorch_backend.PyTorchBackend.divide +#: tensornetwork.backends.pytorch.pytorch_backend.PyTorchBackend.eigh +#: tensornetwork.backends.pytorch.pytorch_backend.PyTorchBackend.eigsh_lanczos +#: tensornetwork.backends.pytorch.pytorch_backend.PyTorchBackend.eps +#: tensornetwork.backends.pytorch.pytorch_backend.PyTorchBackend.inv +#: tensornetwork.backends.pytorch.pytorch_backend.PyTorchBackend.item +#: tensornetwork.backends.pytorch.pytorch_backend.PyTorchBackend.matmul +#: tensornetwork.backends.pytorch.pytorch_backend.PyTorchBackend.multiply +#: tensornetwork.backends.pytorch.pytorch_backend.PyTorchBackend.random_uniform +#: tensornetwork.backends.pytorch.pytorch_backend.PyTorchBackend.reshape +#: tensornetwork.backends.pytorch.pytorch_backend.PyTorchBackend.shape_tensor +#: tensornetwork.backends.pytorch.pytorch_backend.PyTorchBackend.shape_tuple +#: tensornetwork.backends.pytorch.pytorch_backend.PyTorchBackend.subtraction +#: tensornetwork.backends.pytorch.pytorch_backend.PyTorchBackend.svd +#: tensornetwork.backends.pytorch.pytorch_backend.PyTorchBackend.trace +#: tensornetwork.backends.pytorch.pytorch_backend.PyTorchBackend.transpose +#: tensornetwork.backends.tensorflow.tensorflow_backend.TensorFlowBackend.addition +#: tensornetwork.backends.tensorflow.tensorflow_backend.TensorFlowBackend.broadcast_left_multiplication +#: tensornetwork.backends.tensorflow.tensorflow_backend.TensorFlowBackend.broadcast_right_multiplication +#: tensornetwork.backends.tensorflow.tensorflow_backend.TensorFlowBackend.conj +#: tensornetwork.backends.tensorflow.tensorflow_backend.TensorFlowBackend.diagflat +#: tensornetwork.backends.tensorflow.tensorflow_backend.TensorFlowBackend.diagonal +#: tensornetwork.backends.tensorflow.tensorflow_backend.TensorFlowBackend.divide +#: tensornetwork.backends.tensorflow.tensorflow_backend.TensorFlowBackend.eigh +#: tensornetwork.backends.tensorflow.tensorflow_backend.TensorFlowBackend.eps +#: tensornetwork.backends.tensorflow.tensorflow_backend.TensorFlowBackend.exp +#: tensornetwork.backends.tensorflow.tensorflow_backend.TensorFlowBackend.inv +#: tensornetwork.backends.tensorflow.tensorflow_backend.TensorFlowBackend.item +#: tensornetwork.backends.tensorflow.tensorflow_backend.TensorFlowBackend.log +#: tensornetwork.backends.tensorflow.tensorflow_backend.TensorFlowBackend.multiply +#: tensornetwork.backends.tensorflow.tensorflow_backend.TensorFlowBackend.power +#: tensornetwork.backends.tensorflow.tensorflow_backend.TensorFlowBackend.random_uniform +#: tensornetwork.backends.tensorflow.tensorflow_backend.TensorFlowBackend.reshape +#: tensornetwork.backends.tensorflow.tensorflow_backend.TensorFlowBackend.shape_tensor +#: tensornetwork.backends.tensorflow.tensorflow_backend.TensorFlowBackend.shape_tuple +#: tensornetwork.backends.tensorflow.tensorflow_backend.TensorFlowBackend.subtraction +#: tensornetwork.backends.tensorflow.tensorflow_backend.TensorFlowBackend.sum +#: tensornetwork.backends.tensorflow.tensorflow_backend.TensorFlowBackend.trace +#: tensornetwork.backends.tensorflow.tensorflow_backend.TensorFlowBackend.transpose +#: tensornetwork.matrixproductstates.base_mps.BaseMPS.apply_transfer_operator +#: tensornetwork.matrixproductstates.base_mps.BaseMPS.check_orthonormality +#: tensornetwork.matrixproductstates.base_mps.BaseMPS.get_tensor +#: tensornetwork.matrixproductstates.base_mps.BaseMPS.position +#: tensornetwork.matrixproductstates.finite_mps.FiniteMPS.canonicalize +#: tensornetwork.matrixproductstates.finite_mps.FiniteMPS.check_canonical +#: tensornetwork.matrixproductstates.finite_mps.FiniteMPS.left_envs +#: tensornetwork.matrixproductstates.finite_mps.FiniteMPS.random +#: tensornetwork.matrixproductstates.finite_mps.FiniteMPS.right_envs +#: tensornetwork.network_components.AbstractNode.get_dimension +#: tensornetwork.network_components.AbstractNode.reorder_axes +#: tensornetwork.network_components.AbstractNode.reorder_edges +#: tensornetwork.network_components.Node.from_serial_dict +#: torch.nn.modules.module.Module.apply torch.nn.modules.module.Module.bfloat16 +#: torch.nn.modules.module.Module.cpu torch.nn.modules.module.Module.cuda +#: torch.nn.modules.module.Module.double torch.nn.modules.module.Module.eval +#: torch.nn.modules.module.Module.float +#: torch.nn.modules.module.Module.get_buffer +#: torch.nn.modules.module.Module.get_extra_state +#: torch.nn.modules.module.Module.get_parameter +#: torch.nn.modules.module.Module.get_submodule +#: torch.nn.modules.module.Module.half torch.nn.modules.module.Module.ipu +#: torch.nn.modules.module.Module.load_state_dict +#: torch.nn.modules.module.Module.register_backward_hook +#: torch.nn.modules.module.Module.register_forward_hook +#: torch.nn.modules.module.Module.register_forward_pre_hook +#: torch.nn.modules.module.Module.register_full_backward_hook +#: torch.nn.modules.module.Module.register_full_backward_pre_hook +#: torch.nn.modules.module.Module.register_load_state_dict_post_hook +#: torch.nn.modules.module.Module.requires_grad_ +#: torch.nn.modules.module.Module.state_dict torch.nn.modules.module.Module.to +#: torch.nn.modules.module.Module.to_empty torch.nn.modules.module.Module.train +#: torch.nn.modules.module.Module.type torch.nn.modules.module.Module.xpu msgid "Returns" msgstr "" -#: of tensorcircuit.applications.dqas.DQAS_search_pmb:1 -msgid "" -"The probabilistic model based DQAS, can use extensively for DQAS case for" -" ``NMF`` probabilistic model." -msgstr "" - -#: of tensorcircuit.applications.dqas.DQAS_search_pmb:3 -msgid "vag func, return loss and nabla lnp" -msgstr "" - -#: of tensorcircuit.applications.dqas.DQAS_search_pmb:4 -msgid "keras model" -msgstr "" - -#: of tensorcircuit.applications.dqas.DQAS_search_pmb:5 -msgid "sample func of logic with keras model input" -msgstr "" - -#: of tensorcircuit.applications.dqas.DQAS_search_pmb:6 -msgid "input data pipeline generator" -msgstr "" - -#: of tensorcircuit.applications.dqas.DQAS_search_pmb:7 -msgid "operation pool" -msgstr "" - -#: of tensorcircuit.applications.dqas.DQAS_search_pmb:8 -msgid "depth for DQAS" -msgstr "" - -#: of tensorcircuit.applications.dqas.DQAS_search_pmb:12 -msgid "parallel kernels" -msgstr "" - -#: of tensorcircuit.applications.dqas.DQAS_search_pmb:23 -msgid "final loss function in terms of average of sub loss for each circuit" -msgstr "" - -#: of tensorcircuit.applications.dqas.DQAS_search_pmb:24 -msgid "derivative function for ``loss_func``" -msgstr "" - -#: of tensorcircuit.applications.dqas.get_var:1 -msgid "" -"Call in customized functions and grab variables within DQAS framework " -"function by var name str." -msgstr "" - -#: of tensorcircuit.applications.dqas.get_var:3 -msgid "The DQAS framework function" -msgstr "" - -#: of tensorcircuit.applications.dqas.get_var:5 -msgid "Variables within the DQAS framework" -msgstr "" - -#: of tensorcircuit.applications.dqas.get_var +#: of tensorcircuit.abstractcircuit.AbstractCircuit.append:24 +#: tensorcircuit.abstractcircuit.AbstractCircuit.prepend:5 +msgid "The composed circuit" +msgstr "" + +#: of tensorcircuit.abstractcircuit.AbstractCircuit.append +#: tensorcircuit.abstractcircuit.AbstractCircuit.cond_measurement +#: tensorcircuit.abstractcircuit.AbstractCircuit.expectation_ps +#: tensorcircuit.abstractcircuit.AbstractCircuit.from_json +#: tensorcircuit.abstractcircuit.AbstractCircuit.from_json_file +#: tensorcircuit.abstractcircuit.AbstractCircuit.from_qir +#: tensorcircuit.abstractcircuit.AbstractCircuit.from_qiskit +#: tensorcircuit.abstractcircuit.AbstractCircuit.gate_count +#: tensorcircuit.abstractcircuit.AbstractCircuit.gate_count_by_condition +#: tensorcircuit.abstractcircuit.AbstractCircuit.gate_summary +#: tensorcircuit.abstractcircuit.AbstractCircuit.get_positional_logical_mapping +#: tensorcircuit.abstractcircuit.AbstractCircuit.initial_mapping +#: tensorcircuit.abstractcircuit.AbstractCircuit.inverse +#: tensorcircuit.abstractcircuit.AbstractCircuit.prepend +#: tensorcircuit.abstractcircuit.AbstractCircuit.standardize_gate +#: tensorcircuit.abstractcircuit.AbstractCircuit.to_json +#: tensorcircuit.abstractcircuit.AbstractCircuit.to_openqasm +#: tensorcircuit.abstractcircuit.AbstractCircuit.to_qir +#: tensorcircuit.abstractcircuit.AbstractCircuit.vis_tex +#: tensorcircuit.applications.dqas.get_var #: tensorcircuit.applications.graphdata.graph1D #: tensorcircuit.applications.graphdata.reduced_ansatz #: tensorcircuit.applications.graphdata.split_ansatz #: tensorcircuit.applications.vqes.VQNHE.evaluation #: tensorcircuit.applications.vqes.VQNHE.plain_evaluation +#: tensorcircuit.backends.abstract_backend.ExtendedBackend.adjoint +#: tensorcircuit.backends.abstract_backend.ExtendedBackend.coo_sparse_matrix +#: tensorcircuit.backends.abstract_backend.ExtendedBackend.coo_sparse_matrix_from_numpy +#: tensorcircuit.backends.abstract_backend.ExtendedBackend.from_dlpack +#: tensorcircuit.backends.abstract_backend.ExtendedBackend.gather1d +#: tensorcircuit.backends.abstract_backend.ExtendedBackend.implicit_randc +#: tensorcircuit.backends.abstract_backend.ExtendedBackend.implicit_randn +#: tensorcircuit.backends.abstract_backend.ExtendedBackend.implicit_randu +#: tensorcircuit.backends.abstract_backend.ExtendedBackend.is_sparse +#: tensorcircuit.backends.abstract_backend.ExtendedBackend.jacfwd +#: tensorcircuit.backends.abstract_backend.ExtendedBackend.jacrev +#: tensorcircuit.backends.abstract_backend.ExtendedBackend.probability_sample +#: tensorcircuit.backends.abstract_backend.ExtendedBackend.random_split +#: tensorcircuit.backends.abstract_backend.ExtendedBackend.reshape2 +#: tensorcircuit.backends.abstract_backend.ExtendedBackend.reshapem +#: tensorcircuit.backends.abstract_backend.ExtendedBackend.reverse +#: tensorcircuit.backends.abstract_backend.ExtendedBackend.scan +#: tensorcircuit.backends.abstract_backend.ExtendedBackend.scatter +#: tensorcircuit.backends.abstract_backend.ExtendedBackend.sizen +#: tensorcircuit.backends.abstract_backend.ExtendedBackend.sparse_dense_matmul +#: tensorcircuit.backends.abstract_backend.ExtendedBackend.sqrtmh +#: tensorcircuit.backends.abstract_backend.ExtendedBackend.stateful_randc +#: tensorcircuit.backends.abstract_backend.ExtendedBackend.stateful_randn +#: tensorcircuit.backends.abstract_backend.ExtendedBackend.stateful_randu +#: tensorcircuit.backends.abstract_backend.ExtendedBackend.to_dense +#: tensorcircuit.backends.abstract_backend.ExtendedBackend.to_dlpack +#: tensorcircuit.backends.abstract_backend.ExtendedBackend.tree_flatten +#: tensorcircuit.backends.abstract_backend.ExtendedBackend.tree_map +#: tensorcircuit.backends.abstract_backend.ExtendedBackend.tree_unflatten #: tensorcircuit.backends.backend_factory.get_backend +#: tensorcircuit.backends.cupy_backend.CuPyBackend.abs +#: tensorcircuit.backends.cupy_backend.CuPyBackend.acos +#: tensorcircuit.backends.cupy_backend.CuPyBackend.acosh +#: tensorcircuit.backends.cupy_backend.CuPyBackend.arange +#: tensorcircuit.backends.cupy_backend.CuPyBackend.argmax +#: tensorcircuit.backends.cupy_backend.CuPyBackend.argmin +#: tensorcircuit.backends.cupy_backend.CuPyBackend.asin +#: tensorcircuit.backends.cupy_backend.CuPyBackend.asinh +#: tensorcircuit.backends.cupy_backend.CuPyBackend.atan +#: tensorcircuit.backends.cupy_backend.CuPyBackend.atan2 +#: tensorcircuit.backends.cupy_backend.CuPyBackend.atanh +#: tensorcircuit.backends.cupy_backend.CuPyBackend.cast +#: tensorcircuit.backends.cupy_backend.CuPyBackend.cond +#: tensorcircuit.backends.cupy_backend.CuPyBackend.coo_sparse_matrix +#: tensorcircuit.backends.cupy_backend.CuPyBackend.copy +#: tensorcircuit.backends.cupy_backend.CuPyBackend.cosh +#: tensorcircuit.backends.cupy_backend.CuPyBackend.cumsum +#: tensorcircuit.backends.cupy_backend.CuPyBackend.device +#: tensorcircuit.backends.cupy_backend.CuPyBackend.device_move +#: tensorcircuit.backends.cupy_backend.CuPyBackend.dtype +#: tensorcircuit.backends.cupy_backend.CuPyBackend.eigvalsh +#: tensorcircuit.backends.cupy_backend.CuPyBackend.grad +#: tensorcircuit.backends.cupy_backend.CuPyBackend.i +#: tensorcircuit.backends.cupy_backend.CuPyBackend.imag +#: tensorcircuit.backends.cupy_backend.CuPyBackend.is_sparse +#: tensorcircuit.backends.cupy_backend.CuPyBackend.is_tensor +#: tensorcircuit.backends.cupy_backend.CuPyBackend.jit +#: tensorcircuit.backends.cupy_backend.CuPyBackend.jvp +#: tensorcircuit.backends.cupy_backend.CuPyBackend.kron +#: tensorcircuit.backends.cupy_backend.CuPyBackend.left_shift +#: tensorcircuit.backends.cupy_backend.CuPyBackend.max +#: tensorcircuit.backends.cupy_backend.CuPyBackend.mean +#: tensorcircuit.backends.cupy_backend.CuPyBackend.min +#: tensorcircuit.backends.cupy_backend.CuPyBackend.mod +#: tensorcircuit.backends.cupy_backend.CuPyBackend.numpy +#: tensorcircuit.backends.cupy_backend.CuPyBackend.onehot +#: tensorcircuit.backends.cupy_backend.CuPyBackend.real +#: tensorcircuit.backends.cupy_backend.CuPyBackend.relu +#: tensorcircuit.backends.cupy_backend.CuPyBackend.right_shift +#: tensorcircuit.backends.cupy_backend.CuPyBackend.scatter +#: tensorcircuit.backends.cupy_backend.CuPyBackend.searchsorted +#: tensorcircuit.backends.cupy_backend.CuPyBackend.sigmoid +#: tensorcircuit.backends.cupy_backend.CuPyBackend.sinh +#: tensorcircuit.backends.cupy_backend.CuPyBackend.size +#: tensorcircuit.backends.cupy_backend.CuPyBackend.softmax +#: tensorcircuit.backends.cupy_backend.CuPyBackend.solve +#: tensorcircuit.backends.cupy_backend.CuPyBackend.sparse_dense_matmul +#: tensorcircuit.backends.cupy_backend.CuPyBackend.stack +#: tensorcircuit.backends.cupy_backend.CuPyBackend.stateful_randc +#: tensorcircuit.backends.cupy_backend.CuPyBackend.stateful_randn +#: tensorcircuit.backends.cupy_backend.CuPyBackend.stateful_randu +#: tensorcircuit.backends.cupy_backend.CuPyBackend.std +#: tensorcircuit.backends.cupy_backend.CuPyBackend.stop_gradient +#: tensorcircuit.backends.cupy_backend.CuPyBackend.sum +#: tensorcircuit.backends.cupy_backend.CuPyBackend.switch +#: tensorcircuit.backends.cupy_backend.CuPyBackend.tan +#: tensorcircuit.backends.cupy_backend.CuPyBackend.tanh +#: tensorcircuit.backends.cupy_backend.CuPyBackend.tile +#: tensorcircuit.backends.cupy_backend.CuPyBackend.to_dense +#: tensorcircuit.backends.cupy_backend.CuPyBackend.unique_with_counts +#: tensorcircuit.backends.cupy_backend.CuPyBackend.value_and_grad +#: tensorcircuit.backends.cupy_backend.CuPyBackend.vectorized_value_and_grad +#: tensorcircuit.backends.cupy_backend.CuPyBackend.vjp +#: tensorcircuit.backends.cupy_backend.CuPyBackend.vmap +#: tensorcircuit.backends.jax_backend.JaxBackend.abs #: tensorcircuit.backends.jax_backend.JaxBackend.acos #: tensorcircuit.backends.jax_backend.JaxBackend.acosh #: tensorcircuit.backends.jax_backend.JaxBackend.arange @@ -1026,15 +2022,18 @@ msgstr "" #: tensorcircuit.backends.jax_backend.JaxBackend.asinh #: tensorcircuit.backends.jax_backend.JaxBackend.atan #: tensorcircuit.backends.jax_backend.JaxBackend.atan2 +#: tensorcircuit.backends.jax_backend.JaxBackend.atanh #: tensorcircuit.backends.jax_backend.JaxBackend.cast #: tensorcircuit.backends.jax_backend.JaxBackend.cond #: tensorcircuit.backends.jax_backend.JaxBackend.coo_sparse_matrix #: tensorcircuit.backends.jax_backend.JaxBackend.copy -#: tensorcircuit.backends.jax_backend.JaxBackend.cos #: tensorcircuit.backends.jax_backend.JaxBackend.cosh #: tensorcircuit.backends.jax_backend.JaxBackend.cumsum +#: tensorcircuit.backends.jax_backend.JaxBackend.device +#: tensorcircuit.backends.jax_backend.JaxBackend.device_move +#: tensorcircuit.backends.jax_backend.JaxBackend.dtype #: tensorcircuit.backends.jax_backend.JaxBackend.eigvalsh -#: tensorcircuit.backends.jax_backend.JaxBackend.expm +#: tensorcircuit.backends.jax_backend.JaxBackend.from_dlpack #: tensorcircuit.backends.jax_backend.JaxBackend.grad #: tensorcircuit.backends.jax_backend.JaxBackend.i #: tensorcircuit.backends.jax_backend.JaxBackend.imag @@ -1057,9 +2056,10 @@ msgstr "" #: tensorcircuit.backends.jax_backend.JaxBackend.real #: tensorcircuit.backends.jax_backend.JaxBackend.relu #: tensorcircuit.backends.jax_backend.JaxBackend.right_shift +#: tensorcircuit.backends.jax_backend.JaxBackend.scan #: tensorcircuit.backends.jax_backend.JaxBackend.scatter +#: tensorcircuit.backends.jax_backend.JaxBackend.searchsorted #: tensorcircuit.backends.jax_backend.JaxBackend.sigmoid -#: tensorcircuit.backends.jax_backend.JaxBackend.sin #: tensorcircuit.backends.jax_backend.JaxBackend.sinh #: tensorcircuit.backends.jax_backend.JaxBackend.size #: tensorcircuit.backends.jax_backend.JaxBackend.softmax @@ -1069,12 +2069,14 @@ msgstr "" #: tensorcircuit.backends.jax_backend.JaxBackend.stateful_randc #: tensorcircuit.backends.jax_backend.JaxBackend.stateful_randn #: tensorcircuit.backends.jax_backend.JaxBackend.stateful_randu +#: tensorcircuit.backends.jax_backend.JaxBackend.std #: tensorcircuit.backends.jax_backend.JaxBackend.stop_gradient #: tensorcircuit.backends.jax_backend.JaxBackend.switch #: tensorcircuit.backends.jax_backend.JaxBackend.tan #: tensorcircuit.backends.jax_backend.JaxBackend.tanh #: tensorcircuit.backends.jax_backend.JaxBackend.tile #: tensorcircuit.backends.jax_backend.JaxBackend.to_dense +#: tensorcircuit.backends.jax_backend.JaxBackend.to_dlpack #: tensorcircuit.backends.jax_backend.JaxBackend.tree_flatten #: tensorcircuit.backends.jax_backend.JaxBackend.tree_map #: tensorcircuit.backends.jax_backend.JaxBackend.tree_unflatten @@ -1083,6 +2085,8 @@ msgstr "" #: tensorcircuit.backends.jax_backend.JaxBackend.vectorized_value_and_grad #: tensorcircuit.backends.jax_backend.JaxBackend.vjp #: tensorcircuit.backends.jax_backend.JaxBackend.vmap +#: tensorcircuit.backends.jax_backend._svd_jax +#: tensorcircuit.backends.numpy_backend.NumpyBackend.abs #: tensorcircuit.backends.numpy_backend.NumpyBackend.acos #: tensorcircuit.backends.numpy_backend.NumpyBackend.acosh #: tensorcircuit.backends.numpy_backend.NumpyBackend.arange @@ -1092,15 +2096,17 @@ msgstr "" #: tensorcircuit.backends.numpy_backend.NumpyBackend.asinh #: tensorcircuit.backends.numpy_backend.NumpyBackend.atan #: tensorcircuit.backends.numpy_backend.NumpyBackend.atan2 +#: tensorcircuit.backends.numpy_backend.NumpyBackend.atanh #: tensorcircuit.backends.numpy_backend.NumpyBackend.cast #: tensorcircuit.backends.numpy_backend.NumpyBackend.cond #: tensorcircuit.backends.numpy_backend.NumpyBackend.coo_sparse_matrix #: tensorcircuit.backends.numpy_backend.NumpyBackend.copy -#: tensorcircuit.backends.numpy_backend.NumpyBackend.cos #: tensorcircuit.backends.numpy_backend.NumpyBackend.cosh #: tensorcircuit.backends.numpy_backend.NumpyBackend.cumsum +#: tensorcircuit.backends.numpy_backend.NumpyBackend.device +#: tensorcircuit.backends.numpy_backend.NumpyBackend.device_move +#: tensorcircuit.backends.numpy_backend.NumpyBackend.dtype #: tensorcircuit.backends.numpy_backend.NumpyBackend.eigvalsh -#: tensorcircuit.backends.numpy_backend.NumpyBackend.expm #: tensorcircuit.backends.numpy_backend.NumpyBackend.grad #: tensorcircuit.backends.numpy_backend.NumpyBackend.i #: tensorcircuit.backends.numpy_backend.NumpyBackend.imag @@ -1120,8 +2126,8 @@ msgstr "" #: tensorcircuit.backends.numpy_backend.NumpyBackend.relu #: tensorcircuit.backends.numpy_backend.NumpyBackend.right_shift #: tensorcircuit.backends.numpy_backend.NumpyBackend.scatter +#: tensorcircuit.backends.numpy_backend.NumpyBackend.searchsorted #: tensorcircuit.backends.numpy_backend.NumpyBackend.sigmoid -#: tensorcircuit.backends.numpy_backend.NumpyBackend.sin #: tensorcircuit.backends.numpy_backend.NumpyBackend.sinh #: tensorcircuit.backends.numpy_backend.NumpyBackend.size #: tensorcircuit.backends.numpy_backend.NumpyBackend.softmax @@ -1131,6 +2137,7 @@ msgstr "" #: tensorcircuit.backends.numpy_backend.NumpyBackend.stateful_randc #: tensorcircuit.backends.numpy_backend.NumpyBackend.stateful_randn #: tensorcircuit.backends.numpy_backend.NumpyBackend.stateful_randu +#: tensorcircuit.backends.numpy_backend.NumpyBackend.std #: tensorcircuit.backends.numpy_backend.NumpyBackend.stop_gradient #: tensorcircuit.backends.numpy_backend.NumpyBackend.switch #: tensorcircuit.backends.numpy_backend.NumpyBackend.tan @@ -1142,6 +2149,7 @@ msgstr "" #: tensorcircuit.backends.numpy_backend.NumpyBackend.vectorized_value_and_grad #: tensorcircuit.backends.numpy_backend.NumpyBackend.vjp #: tensorcircuit.backends.numpy_backend.NumpyBackend.vmap +#: tensorcircuit.backends.numpy_backend._sum_numpy #: tensorcircuit.backends.pytorch_backend.PyTorchBackend.acos #: tensorcircuit.backends.pytorch_backend.PyTorchBackend.acosh #: tensorcircuit.backends.pytorch_backend.PyTorchBackend.arange @@ -1151,14 +2159,17 @@ msgstr "" #: tensorcircuit.backends.pytorch_backend.PyTorchBackend.asinh #: tensorcircuit.backends.pytorch_backend.PyTorchBackend.atan #: tensorcircuit.backends.pytorch_backend.PyTorchBackend.atan2 +#: tensorcircuit.backends.pytorch_backend.PyTorchBackend.atanh #: tensorcircuit.backends.pytorch_backend.PyTorchBackend.cast #: tensorcircuit.backends.pytorch_backend.PyTorchBackend.cond #: tensorcircuit.backends.pytorch_backend.PyTorchBackend.copy -#: tensorcircuit.backends.pytorch_backend.PyTorchBackend.cos #: tensorcircuit.backends.pytorch_backend.PyTorchBackend.cosh #: tensorcircuit.backends.pytorch_backend.PyTorchBackend.cumsum +#: tensorcircuit.backends.pytorch_backend.PyTorchBackend.device +#: tensorcircuit.backends.pytorch_backend.PyTorchBackend.device_move +#: tensorcircuit.backends.pytorch_backend.PyTorchBackend.dtype #: tensorcircuit.backends.pytorch_backend.PyTorchBackend.eigvalsh -#: tensorcircuit.backends.pytorch_backend.PyTorchBackend.expm +#: tensorcircuit.backends.pytorch_backend.PyTorchBackend.from_dlpack #: tensorcircuit.backends.pytorch_backend.PyTorchBackend.grad #: tensorcircuit.backends.pytorch_backend.PyTorchBackend.i #: tensorcircuit.backends.pytorch_backend.PyTorchBackend.imag @@ -1177,18 +2188,20 @@ msgstr "" #: tensorcircuit.backends.pytorch_backend.PyTorchBackend.relu #: tensorcircuit.backends.pytorch_backend.PyTorchBackend.reverse #: tensorcircuit.backends.pytorch_backend.PyTorchBackend.right_shift +#: tensorcircuit.backends.pytorch_backend.PyTorchBackend.searchsorted #: tensorcircuit.backends.pytorch_backend.PyTorchBackend.sigmoid -#: tensorcircuit.backends.pytorch_backend.PyTorchBackend.sin #: tensorcircuit.backends.pytorch_backend.PyTorchBackend.sinh #: tensorcircuit.backends.pytorch_backend.PyTorchBackend.size #: tensorcircuit.backends.pytorch_backend.PyTorchBackend.softmax #: tensorcircuit.backends.pytorch_backend.PyTorchBackend.solve #: tensorcircuit.backends.pytorch_backend.PyTorchBackend.stack +#: tensorcircuit.backends.pytorch_backend.PyTorchBackend.std #: tensorcircuit.backends.pytorch_backend.PyTorchBackend.stop_gradient #: tensorcircuit.backends.pytorch_backend.PyTorchBackend.switch #: tensorcircuit.backends.pytorch_backend.PyTorchBackend.tan #: tensorcircuit.backends.pytorch_backend.PyTorchBackend.tanh #: tensorcircuit.backends.pytorch_backend.PyTorchBackend.tile +#: tensorcircuit.backends.pytorch_backend.PyTorchBackend.to_dlpack #: tensorcircuit.backends.pytorch_backend.PyTorchBackend.tree_flatten #: tensorcircuit.backends.pytorch_backend.PyTorchBackend.tree_map #: tensorcircuit.backends.pytorch_backend.PyTorchBackend.tree_unflatten @@ -1197,6 +2210,10 @@ msgstr "" #: tensorcircuit.backends.pytorch_backend.PyTorchBackend.vectorized_value_and_grad #: tensorcircuit.backends.pytorch_backend.PyTorchBackend.vjp #: tensorcircuit.backends.pytorch_backend.PyTorchBackend.vmap +#: tensorcircuit.backends.pytorch_backend._qr_torch +#: tensorcircuit.backends.pytorch_backend._rq_torch +#: tensorcircuit.backends.pytorch_backend._sum_torch +#: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.abs #: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.acos #: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.acosh #: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.arange @@ -1206,15 +2223,18 @@ msgstr "" #: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.asinh #: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.atan #: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.atan2 +#: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.atanh #: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.cast #: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.cond #: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.coo_sparse_matrix #: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.copy -#: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.cos #: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.cosh #: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.cumsum +#: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.device +#: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.device_move +#: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.dtype #: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.eigvalsh -#: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.expm +#: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.from_dlpack #: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.gather1d #: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.grad #: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.i @@ -1234,9 +2254,10 @@ msgstr "" #: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.real #: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.relu #: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.right_shift +#: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.scan #: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.scatter +#: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.searchsorted #: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.sigmoid -#: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.sin #: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.sinh #: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.size #: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.softmax @@ -1246,39 +2267,75 @@ msgstr "" #: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.stateful_randc #: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.stateful_randn #: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.stateful_randu +#: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.std #: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.stop_gradient #: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.switch #: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.tan #: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.tanh #: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.tile #: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.to_dense +#: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.to_dlpack #: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.unique_with_counts #: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.value_and_grad #: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.vectorized_value_and_grad #: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.vjp #: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.vmap +#: tensorcircuit.backends.tensorflow_backend._matmul_tf +#: tensorcircuit.backends.tensorflow_backend._qr_tf +#: tensorcircuit.backends.tensorflow_backend._rq_tf +#: tensorcircuit.basecircuit.BaseCircuit.amplitude +#: tensorcircuit.basecircuit.BaseCircuit.cond_measurement +#: tensorcircuit.basecircuit.BaseCircuit.expectation_before +#: tensorcircuit.basecircuit.BaseCircuit.get_quvector +#: tensorcircuit.basecircuit.BaseCircuit.measure_jit +#: tensorcircuit.basecircuit.BaseCircuit.perfect_sampling +#: tensorcircuit.basecircuit.BaseCircuit.probability +#: tensorcircuit.basecircuit.BaseCircuit.readouterror_bs +#: tensorcircuit.basecircuit.BaseCircuit.sample +#: tensorcircuit.basecircuit.BaseCircuit.sample_expectation_ps +#: tensorcircuit.basecircuit.BaseCircuit.to_qir #: tensorcircuit.channels.amplitudedampingchannel -#: tensorcircuit.channels.depolarizingchannel +#: tensorcircuit.channels.choi_to_kraus tensorcircuit.channels.choi_to_super +#: tensorcircuit.channels.composedkraus +#: tensorcircuit.channels.depolarizingchannel tensorcircuit.channels.evol_kraus +#: tensorcircuit.channels.evol_superop +#: tensorcircuit.channels.generaldepolarizingchannel +#: tensorcircuit.channels.is_hermitian_matrix +#: tensorcircuit.channels.isotropicdepolarizingchannel +#: tensorcircuit.channels.kraus_to_choi tensorcircuit.channels.kraus_to_super #: tensorcircuit.channels.kraus_to_super_gate +#: tensorcircuit.channels.krausgate_to_krausmatrix +#: tensorcircuit.channels.krausmatrix_to_krausgate #: tensorcircuit.channels.phasedampingchannel -#: tensorcircuit.channels.resetchannel tensorcircuit.circuit.Circuit.amplitude -#: tensorcircuit.circuit.Circuit.cond_measurement -#: tensorcircuit.circuit.Circuit.depolarizing +#: tensorcircuit.channels.resetchannel tensorcircuit.channels.reshuffle +#: tensorcircuit.channels.super_to_choi tensorcircuit.channels.super_to_kraus +#: tensorcircuit.channels.thermalrelaxationchannel +#: tensorcircuit.circuit.Circuit.depolarizing_reference #: tensorcircuit.circuit.Circuit.expectation -#: tensorcircuit.circuit.Circuit.expectation_before -#: tensorcircuit.circuit.Circuit.from_qir -#: tensorcircuit.circuit.Circuit.from_qiskit #: tensorcircuit.circuit.Circuit.get_quoperator -#: tensorcircuit.circuit.Circuit.get_quvector #: tensorcircuit.circuit.Circuit.is_valid tensorcircuit.circuit.Circuit.matrix -#: tensorcircuit.circuit.Circuit.measure_jit #: tensorcircuit.circuit.Circuit.measure_reference -#: tensorcircuit.circuit.Circuit.perfect_sampling -#: tensorcircuit.circuit.Circuit.sample tensorcircuit.circuit.Circuit.to_qir #: tensorcircuit.circuit.Circuit.unitary_kraus -#: tensorcircuit.circuit.Circuit.vis_tex -#: tensorcircuit.circuit.Circuit.wavefunction -#: tensorcircuit.circuit._expectation_ps tensorcircuit.circuit.expectation +#: tensorcircuit.circuit.Circuit.wavefunction tensorcircuit.circuit.expectation +#: tensorcircuit.cloud.abstraction.Device.list_properties +#: tensorcircuit.cloud.abstraction.Device.native_gates +#: tensorcircuit.cloud.abstraction.Device.topology +#: tensorcircuit.cloud.abstraction.Device.topology_graph +#: tensorcircuit.cloud.abstraction.Task.details +#: tensorcircuit.cloud.abstraction.Task.get_device +#: tensorcircuit.cloud.abstraction.Task.resubmit +#: tensorcircuit.cloud.abstraction.Task.results +#: tensorcircuit.cloud.abstraction.Task.state +#: tensorcircuit.cloud.apis.get_device tensorcircuit.cloud.apis.get_provider +#: tensorcircuit.cloud.apis.get_task tensorcircuit.cloud.apis.get_task_details +#: tensorcircuit.cloud.apis.get_token tensorcircuit.cloud.apis.list_devices +#: tensorcircuit.cloud.apis.list_properties +#: tensorcircuit.cloud.apis.list_providers tensorcircuit.cloud.apis.list_tasks +#: tensorcircuit.cloud.apis.resubmit_task tensorcircuit.cloud.apis.set_device +#: tensorcircuit.cloud.apis.set_provider tensorcircuit.cloud.apis.set_token +#: tensorcircuit.cloud.apis.submit_task tensorcircuit.cloud.tencent.submit_task +#: tensorcircuit.cloud.wrapper.batch_expectation_ps +#: tensorcircuit.compiler.qiskit_compiler.qiskit_compile #: tensorcircuit.cons.get_contractor tensorcircuit.cons.get_dtype #: tensorcircuit.cons.plain_contractor tensorcircuit.cons.runtime_backend #: tensorcircuit.cons.runtime_contractor tensorcircuit.cons.runtime_dtype @@ -1289,36 +2346,59 @@ msgstr "" #: tensorcircuit.cons.set_tensornetwork_backend #: tensorcircuit.densitymatrix.DMCircuit.densitymatrix #: tensorcircuit.densitymatrix.DMCircuit.expectation -#: tensorcircuit.densitymatrix.DMCircuit.measure_jit -#: tensorcircuit.densitymatrix.DMCircuit.perfect_sampling +#: tensorcircuit.densitymatrix.DMCircuit.get_dm_as_quoperator +#: tensorcircuit.densitymatrix.DMCircuit.to_circuit +#: tensorcircuit.densitymatrix.DMCircuit.wavefunction +#: tensorcircuit.experimental.evol_global tensorcircuit.experimental.evol_local +#: tensorcircuit.experimental.hamiltonian_evol +#: tensorcircuit.experimental.parameter_shift_grad +#: tensorcircuit.experimental.parameter_shift_grad_v2 #: tensorcircuit.gates.any_gate tensorcircuit.gates.bmatrix #: tensorcircuit.gates.cr_gate tensorcircuit.gates.exponential_gate -#: tensorcircuit.gates.exponential_gate_unity tensorcircuit.gates.iswap_gate -#: tensorcircuit.gates.matrix_for_gate tensorcircuit.gates.num_to_tensor +#: tensorcircuit.gates.exponential_gate_unity +#: tensorcircuit.gates.get_u_parameter tensorcircuit.gates.iswap_gate +#: tensorcircuit.gates.matrix_for_gate tensorcircuit.gates.multicontrol_gate +#: tensorcircuit.gates.num_to_tensor tensorcircuit.gates.phase_gate #: tensorcircuit.gates.r_gate tensorcircuit.gates.random_single_qubit_gate #: tensorcircuit.gates.random_two_qubit_gate #: tensorcircuit.gates.rgate_theoretical tensorcircuit.gates.rx_gate #: tensorcircuit.gates.rxx_gate tensorcircuit.gates.ry_gate #: tensorcircuit.gates.ryy_gate tensorcircuit.gates.rz_gate -#: tensorcircuit.gates.rzz_gate -#: tensorcircuit.interfaces.scipy_optimize_interface -#: tensorcircuit.interfaces.torch_interface tensorcircuit.keras.load_func -#: tensorcircuit.keras.output_asis_loss +#: tensorcircuit.gates.rzz_gate tensorcircuit.gates.u_gate +#: tensorcircuit.interfaces.numpy.numpy_interface +#: tensorcircuit.interfaces.scipy.scipy_optimize_interface +#: tensorcircuit.interfaces.tensorflow.tensorflow_interface +#: tensorcircuit.interfaces.tensortrans.args_to_tensor +#: tensorcircuit.interfaces.tensortrans.general_args_to_numpy +#: tensorcircuit.interfaces.tensortrans.numpy_args_to_backend +#: tensorcircuit.interfaces.tensortrans.which_backend +#: tensorcircuit.interfaces.torch.torch_interface +#: tensorcircuit.interfaces.torch.torch_interface_kws +#: tensorcircuit.keras.load_func tensorcircuit.keras.output_asis_loss #: tensorcircuit.mps_base.FiniteMPS.apply_two_site_gate #: tensorcircuit.mps_base.FiniteMPS.measure_local_operator #: tensorcircuit.mps_base.FiniteMPS.measure_two_body_correlator #: tensorcircuit.mpscircuit.MPSCircuit.conj #: tensorcircuit.mpscircuit.MPSCircuit.copy #: tensorcircuit.mpscircuit.MPSCircuit.copy_without_tensor -#: tensorcircuit.mpscircuit.MPSCircuit.expectation_single_gate -#: tensorcircuit.mpscircuit.MPSCircuit.expectation_two_gates_product -#: tensorcircuit.mpscircuit.MPSCircuit.from_wavefunction -#: tensorcircuit.mpscircuit.MPSCircuit.general_expectation +#: tensorcircuit.mpscircuit.MPSCircuit.expectation +#: tensorcircuit.mpscircuit.MPSCircuit.get_bond_dimensions +#: tensorcircuit.mpscircuit.MPSCircuit.get_center_position #: tensorcircuit.mpscircuit.MPSCircuit.get_norm +#: tensorcircuit.mpscircuit.MPSCircuit.get_quvector +#: tensorcircuit.mpscircuit.MPSCircuit.get_tensors #: tensorcircuit.mpscircuit.MPSCircuit.is_valid +#: tensorcircuit.mpscircuit.MPSCircuit.measure #: tensorcircuit.mpscircuit.MPSCircuit.proj_with_mps #: tensorcircuit.mpscircuit.MPSCircuit.wavefunction -#: tensorcircuit.mpscircuit.split_tensor tensorcircuit.quantum.PauliString2COO +#: tensorcircuit.mpscircuit.MPSCircuit.wavefunction_to_tensors +#: tensorcircuit.mpscircuit.split_tensor +#: tensorcircuit.noisemodel.NoiseConf.channel_count +#: tensorcircuit.noisemodel.apply_qir_with_noise +#: tensorcircuit.noisemodel.circuit_with_noise +#: tensorcircuit.noisemodel.expectation_noisfy +#: tensorcircuit.noisemodel.sample_expectation_ps_noisfy +#: tensorcircuit.quantum.PauliString2COO #: tensorcircuit.quantum.PauliStringSum2COO #: tensorcircuit.quantum.PauliStringSum2COO_numpy #: tensorcircuit.quantum.PauliStringSum2COO_tf @@ -1338,11147 +2418,23479 @@ msgstr "" #: tensorcircuit.quantum.QuVector.projector #: tensorcircuit.quantum.QuVector.reduced_density #: tensorcircuit.quantum.correlation_from_counts -#: tensorcircuit.quantum.double_state +#: tensorcircuit.quantum.correlation_from_samples +#: tensorcircuit.quantum.count_d2s tensorcircuit.quantum.count_s2d +#: tensorcircuit.quantum.count_tuple2dict +#: tensorcircuit.quantum.count_vector2dict tensorcircuit.quantum.double_state #: tensorcircuit.quantum.eliminate_identities tensorcircuit.quantum.entropy #: tensorcircuit.quantum.fidelity tensorcircuit.quantum.free_energy #: tensorcircuit.quantum.generate_local_hamiltonian #: tensorcircuit.quantum.gibbs_state #: tensorcircuit.quantum.heisenberg_hamiltonian tensorcircuit.quantum.identity #: tensorcircuit.quantum.measurement_counts -#: tensorcircuit.quantum.mutual_information +#: tensorcircuit.quantum.mutual_information tensorcircuit.quantum.ps2xyz #: tensorcircuit.quantum.quantum_constructor tensorcircuit.quantum.quimb2qop #: tensorcircuit.quantum.reduced_density_matrix #: tensorcircuit.quantum.renyi_entropy tensorcircuit.quantum.renyi_free_energy +#: tensorcircuit.quantum.sample2all tensorcircuit.quantum.sample2count +#: tensorcircuit.quantum.sample_bin2int tensorcircuit.quantum.sample_int2bin #: tensorcircuit.quantum.spin_by_basis tensorcircuit.quantum.taylorlnm #: tensorcircuit.quantum.tn2qop tensorcircuit.quantum.trace_distance #: tensorcircuit.quantum.trace_product -#: tensorcircuit.quantum.truncated_free_energy +#: tensorcircuit.quantum.truncated_free_energy tensorcircuit.quantum.xyz2ps +#: tensorcircuit.results.counts.expectation +#: tensorcircuit.results.counts.plot_histogram +#: tensorcircuit.results.qem.qem_methods.add_dd +#: tensorcircuit.results.qem.qem_methods.apply_dd +#: tensorcircuit.results.qem.qem_methods.apply_rc +#: tensorcircuit.results.qem.qem_methods.apply_zne +#: tensorcircuit.results.qem.qem_methods.prune_ddcircuit +#: tensorcircuit.results.qem.qem_methods.used_qubits +#: tensorcircuit.results.readout_mitigation.ReadoutMit.apply_readout_mitigation +#: tensorcircuit.results.readout_mitigation.ReadoutMit.expectation +#: tensorcircuit.results.readout_mitigation.ReadoutMit.get_matrix +#: tensorcircuit.results.readout_mitigation.ReadoutMit.global_miti_readout_circ +#: tensorcircuit.results.readout_mitigation.ReadoutMit.local_miti_readout_circ +#: tensorcircuit.results.readout_mitigation.ReadoutMit.mapping_preprocess +#: tensorcircuit.results.readout_mitigation.ReadoutMit.mitigate_probability +#: tensorcircuit.results.readout_mitigation.ReadoutMit.newrange +#: tensorcircuit.results.readout_mitigation.ReadoutMit.ubs #: tensorcircuit.simplify.infer_new_shape #: tensorcircuit.simplify.pseudo_contract_between #: tensorcircuit.templates.blocks.Bell_pair_block #: tensorcircuit.templates.blocks.example_block +#: tensorcircuit.templates.blocks.qft #: tensorcircuit.templates.blocks.state_centric -#: tensorcircuit.templates.chems.get_ps #: tensorcircuit.templates.graphs.Grid2DCoord.all_cols #: tensorcircuit.templates.graphs.Grid2DCoord.all_rows #: tensorcircuit.templates.graphs.Grid2DCoord.lattice_graph #: tensorcircuit.templates.graphs.Line1D +#: tensorcircuit.templates.measurements.any_local_measurements #: tensorcircuit.templates.measurements.any_measurements #: tensorcircuit.templates.measurements.heisenberg_measurements #: tensorcircuit.templates.measurements.mpo_expectation #: tensorcircuit.templates.measurements.operator_expectation #: tensorcircuit.templates.measurements.sparse_expectation #: tensorcircuit.templates.measurements.spin_glass_measurements -#: tensorcircuit.translation.perm_matrix tensorcircuit.translation.qir2qiskit -#: tensorcircuit.translation.qiskit2tc tensorcircuit.utils.append +#: tensorcircuit.translation.eqasm2tc tensorcircuit.translation.perm_matrix +#: tensorcircuit.translation.qir2cirq tensorcircuit.translation.qir2json +#: tensorcircuit.translation.qir2qiskit tensorcircuit.translation.qiskit2tc +#: tensorcircuit.translation.qiskit_from_qasm_str_ordered_measure +#: tensorcircuit.utils.append tensorcircuit.utils.arg_alias #: tensorcircuit.utils.benchmark tensorcircuit.utils.is_m1mac #: tensorcircuit.utils.return_partial tensorcircuit.vis.gate_name_trans -#: tensorcircuit.vis.render_pdf +#: tensorcircuit.vis.qir2tex tensorcircuit.vis.render_pdf +#: tensornetwork.backends.abstract_backend.AbstractBackend.broadcast_left_multiplication +#: tensornetwork.backends.abstract_backend.AbstractBackend.broadcast_right_multiplication +#: tensornetwork.backends.abstract_backend.AbstractBackend.diagflat +#: tensornetwork.backends.abstract_backend.AbstractBackend.diagonal +#: tensornetwork.backends.abstract_backend.AbstractBackend.eigh +#: tensornetwork.backends.abstract_backend.AbstractBackend.eigs +#: tensornetwork.backends.abstract_backend.AbstractBackend.eigsh +#: tensornetwork.backends.abstract_backend.AbstractBackend.eps +#: tensornetwork.backends.abstract_backend.AbstractBackend.gmres +#: tensornetwork.backends.abstract_backend.AbstractBackend.inv +#: tensornetwork.backends.abstract_backend.AbstractBackend.matmul +#: tensornetwork.backends.abstract_backend.AbstractBackend.random_uniform +#: tensornetwork.backends.abstract_backend.AbstractBackend.svd +#: tensornetwork.backends.abstract_backend.AbstractBackend.trace +#: tensornetwork.backends.jax.jax_backend.JaxBackend.broadcast_left_multiplication +#: tensornetwork.backends.jax.jax_backend.JaxBackend.broadcast_right_multiplication +#: tensornetwork.backends.jax.jax_backend.JaxBackend.diagflat +#: tensornetwork.backends.jax.jax_backend.JaxBackend.diagonal +#: tensornetwork.backends.jax.jax_backend.JaxBackend.eigh +#: tensornetwork.backends.jax.jax_backend.JaxBackend.eps +#: tensornetwork.backends.jax.jax_backend.JaxBackend.inv +#: tensornetwork.backends.jax.jax_backend.JaxBackend.matmul +#: tensornetwork.backends.jax.jax_backend.JaxBackend.random_uniform +#: tensornetwork.backends.jax.jax_backend.JaxBackend.sum +#: tensornetwork.backends.jax.jax_backend.JaxBackend.trace +#: tensornetwork.backends.numpy.numpy_backend.NumPyBackend.broadcast_left_multiplication +#: tensornetwork.backends.numpy.numpy_backend.NumPyBackend.broadcast_right_multiplication +#: tensornetwork.backends.numpy.numpy_backend.NumPyBackend.diagflat +#: tensornetwork.backends.numpy.numpy_backend.NumPyBackend.diagonal +#: tensornetwork.backends.numpy.numpy_backend.NumPyBackend.eigh +#: tensornetwork.backends.numpy.numpy_backend.NumPyBackend.eigs +#: tensornetwork.backends.numpy.numpy_backend.NumPyBackend.eigsh +#: tensornetwork.backends.numpy.numpy_backend.NumPyBackend.eps +#: tensornetwork.backends.numpy.numpy_backend.NumPyBackend.inv +#: tensornetwork.backends.numpy.numpy_backend.NumPyBackend.matmul +#: tensornetwork.backends.numpy.numpy_backend.NumPyBackend.random_uniform +#: tensornetwork.backends.numpy.numpy_backend.NumPyBackend.svd +#: tensornetwork.backends.numpy.numpy_backend.NumPyBackend.trace +#: tensornetwork.backends.pytorch.pytorch_backend.PyTorchBackend.abs +#: tensornetwork.backends.pytorch.pytorch_backend.PyTorchBackend.broadcast_left_multiplication +#: tensornetwork.backends.pytorch.pytorch_backend.PyTorchBackend.broadcast_right_multiplication +#: tensornetwork.backends.pytorch.pytorch_backend.PyTorchBackend.diagflat +#: tensornetwork.backends.pytorch.pytorch_backend.PyTorchBackend.diagonal +#: tensornetwork.backends.pytorch.pytorch_backend.PyTorchBackend.eigh +#: tensornetwork.backends.pytorch.pytorch_backend.PyTorchBackend.eps +#: tensornetwork.backends.pytorch.pytorch_backend.PyTorchBackend.inv +#: tensornetwork.backends.pytorch.pytorch_backend.PyTorchBackend.matmul +#: tensornetwork.backends.pytorch.pytorch_backend.PyTorchBackend.random_uniform +#: tensornetwork.backends.pytorch.pytorch_backend.PyTorchBackend.svd +#: tensornetwork.backends.pytorch.pytorch_backend.PyTorchBackend.trace +#: tensornetwork.backends.tensorflow.tensorflow_backend.TensorFlowBackend.broadcast_left_multiplication +#: tensornetwork.backends.tensorflow.tensorflow_backend.TensorFlowBackend.broadcast_right_multiplication +#: tensornetwork.backends.tensorflow.tensorflow_backend.TensorFlowBackend.diagflat +#: tensornetwork.backends.tensorflow.tensorflow_backend.TensorFlowBackend.diagonal +#: tensornetwork.backends.tensorflow.tensorflow_backend.TensorFlowBackend.eigh +#: tensornetwork.backends.tensorflow.tensorflow_backend.TensorFlowBackend.eps +#: tensornetwork.backends.tensorflow.tensorflow_backend.TensorFlowBackend.inv +#: tensornetwork.backends.tensorflow.tensorflow_backend.TensorFlowBackend.random_uniform +#: tensornetwork.backends.tensorflow.tensorflow_backend.TensorFlowBackend.sum +#: tensornetwork.backends.tensorflow.tensorflow_backend.TensorFlowBackend.trace +#: tensornetwork.matrixproductstates.base_mps.BaseMPS.apply_transfer_operator +#: tensornetwork.matrixproductstates.base_mps.BaseMPS.check_orthonormality +#: tensornetwork.matrixproductstates.base_mps.BaseMPS.get_tensor +#: tensornetwork.matrixproductstates.base_mps.BaseMPS.position +#: tensornetwork.matrixproductstates.finite_mps.FiniteMPS.canonicalize +#: tensornetwork.matrixproductstates.finite_mps.FiniteMPS.left_envs +#: tensornetwork.matrixproductstates.finite_mps.FiniteMPS.right_envs +#: torch.nn.modules.module.Module.apply torch.nn.modules.module.Module.bfloat16 +#: torch.nn.modules.module.Module.cpu torch.nn.modules.module.Module.cuda +#: torch.nn.modules.module.Module.double torch.nn.modules.module.Module.eval +#: torch.nn.modules.module.Module.float +#: torch.nn.modules.module.Module.get_buffer +#: torch.nn.modules.module.Module.get_extra_state +#: torch.nn.modules.module.Module.get_parameter +#: torch.nn.modules.module.Module.get_submodule +#: torch.nn.modules.module.Module.half torch.nn.modules.module.Module.ipu +#: torch.nn.modules.module.Module.load_state_dict +#: torch.nn.modules.module.Module.register_backward_hook +#: torch.nn.modules.module.Module.register_forward_hook +#: torch.nn.modules.module.Module.register_forward_pre_hook +#: torch.nn.modules.module.Module.register_full_backward_hook +#: torch.nn.modules.module.Module.register_full_backward_pre_hook +#: torch.nn.modules.module.Module.register_load_state_dict_post_hook +#: torch.nn.modules.module.Module.requires_grad_ +#: torch.nn.modules.module.Module.state_dict torch.nn.modules.module.Module.to +#: torch.nn.modules.module.Module.to_empty torch.nn.modules.module.Module.train +#: torch.nn.modules.module.Module.type torch.nn.modules.module.Module.xpu msgid "Return type" msgstr "" -#: of tensorcircuit.applications.dqas.get_weights:1 -msgid "" -"This function works only when nnp has the same shape as stp, i.e. one " -"parameter for each op." -msgstr "" - -#: of tensorcircuit.applications.dqas.parallel_kernel:1 -msgid "The kernel for multiprocess to run parallel in DQAS function/" -msgstr "" - -#: of tensorcircuit.applications.dqas.parallel_qaoa_train:1 +#: of tensorcircuit.abstractcircuit.AbstractCircuit.append_from_qir:1 msgid "" -"parallel variational parameter training and search to avoid local minimum" -" not limited to qaoa setup as the function name indicates, as long as you" -" provided suitable `vag_func`" -msgstr "" - -#: of tensorcircuit.applications.dqas.parallel_qaoa_train:6 -msgid "data input generator for vag_func" -msgstr "" - -#: of tensorcircuit.applications.dqas.parallel_qaoa_train:7 -msgid "vag_kernel" -msgstr "" - -#: of tensorcircuit.applications.dqas.parallel_qaoa_train:10 -msgid "number of tries" +"Apply the ciurict in form of quantum intermediate representation after " +"the current cirucit." msgstr "" -#: of tensorcircuit.applications.dqas.parallel_qaoa_train:11 -msgid "for optimization problem the input is in general fixed so batch is often 1" +#: of tensorcircuit.abstractcircuit.AbstractCircuit.append_from_qir +#: tensorcircuit.abstractcircuit.AbstractCircuit.cond_measurement +#: tensorcircuit.abstractcircuit.AbstractCircuit.draw +#: tensorcircuit.abstractcircuit.AbstractCircuit.expectation_ps +#: tensorcircuit.abstractcircuit.AbstractCircuit.from_qir +#: tensorcircuit.abstractcircuit.AbstractCircuit.from_qiskit +#: tensorcircuit.abstractcircuit.AbstractCircuit.gate_count +#: tensorcircuit.abstractcircuit.AbstractCircuit.gate_count_by_condition +#: tensorcircuit.abstractcircuit.AbstractCircuit.to_qir +#: tensorcircuit.backends.cupy_backend.CuPyBackend.grad +#: tensorcircuit.backends.cupy_backend.CuPyBackend.value_and_grad +#: tensorcircuit.backends.jax_backend.JaxBackend.grad +#: tensorcircuit.backends.jax_backend.JaxBackend.value_and_grad +#: tensorcircuit.backends.numpy_backend.NumpyBackend.grad +#: tensorcircuit.backends.numpy_backend.NumpyBackend.value_and_grad +#: tensorcircuit.backends.pytorch_backend.PyTorchBackend.grad +#: tensorcircuit.backends.pytorch_backend.PyTorchBackend.value_and_grad +#: tensorcircuit.backends.pytorch_backend._qr_torch +#: tensorcircuit.backends.pytorch_backend._rq_torch +#: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.grad +#: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.value_and_grad +#: tensorcircuit.backends.tensorflow_backend._qr_tf +#: tensorcircuit.backends.tensorflow_backend._rq_tf +#: tensorcircuit.basecircuit.BaseCircuit.amplitude +#: tensorcircuit.basecircuit.BaseCircuit.cond_measurement +#: tensorcircuit.basecircuit.BaseCircuit.readouterror_bs +#: tensorcircuit.basecircuit.BaseCircuit.sample_expectation_ps +#: tensorcircuit.basecircuit.BaseCircuit.to_qir +#: tensorcircuit.channels.amplitudedampingchannel +#: tensorcircuit.channels.depolarizingchannel +#: tensorcircuit.channels.generaldepolarizingchannel +#: tensorcircuit.channels.isotropicdepolarizingchannel +#: tensorcircuit.channels.phasedampingchannel +#: tensorcircuit.channels.resetchannel +#: tensorcircuit.channels.thermalrelaxationchannel +#: tensorcircuit.circuit.Circuit.expectation +#: tensorcircuit.circuit.Circuit.measure_reference +#: tensorcircuit.circuit.Circuit.replace_mps_inputs +#: tensorcircuit.cloud.wrapper.batch_expectation_ps +#: tensorcircuit.cons.set_tensornetwork_backend tensorcircuit.gates.bmatrix +#: tensorcircuit.gates.matrix_for_gate tensorcircuit.gates.num_to_tensor +#: tensorcircuit.interfaces.numpy.numpy_interface +#: tensorcircuit.interfaces.scipy.scipy_optimize_interface +#: tensorcircuit.interfaces.tensorflow.tensorflow_interface +#: tensorcircuit.interfaces.tensortrans.args_to_tensor +#: tensorcircuit.interfaces.torch.torch_interface +#: tensorcircuit.interfaces.torch.torch_interface_kws +#: tensorcircuit.keras.load_func tensorcircuit.keras.save_func +#: tensorcircuit.quantum.QuAdjointVector.from_tensor +#: tensorcircuit.quantum.QuOperator.from_tensor +#: tensorcircuit.quantum.QuOperator.tensor_product +#: tensorcircuit.quantum.QuScalar.from_tensor +#: tensorcircuit.quantum.QuVector.from_tensor +#: tensorcircuit.quantum.correlation_from_counts +#: tensorcircuit.quantum.count_d2s tensorcircuit.quantum.entropy +#: tensorcircuit.quantum.free_energy +#: tensorcircuit.quantum.heisenberg_hamiltonian tensorcircuit.quantum.identity +#: tensorcircuit.quantum.measurement_counts +#: tensorcircuit.quantum.quantum_constructor +#: tensorcircuit.quantum.renyi_free_energy tensorcircuit.quantum.spin_by_basis +#: tensorcircuit.quantum.trace_product tensorcircuit.simplify.infer_new_shape +#: tensorcircuit.torchnn.HardwareNet.__init__ +#: tensorcircuit.torchnn.QuantumNet.__init__ tensorcircuit.translation.qir2cirq +#: tensorcircuit.translation.qir2qiskit tensorcircuit.translation.qiskit2tc +#: tensorcircuit.utils.append tensorcircuit.utils.return_partial +#: tensorcircuit.vis.gate_name_trans tensorcircuit.vis.qir2tex +#: tensorcircuit.vis.render_pdf +msgid "Example" msgstr "" -#: of tensorcircuit.applications.dqas.parallel_qaoa_train:12 -msgid "number of parallel jobs" +#: of tensorcircuit.abstractcircuit.AbstractCircuit.append_from_qir:18 +msgid "The quantum intermediate representation." msgstr "" -#: of tensorcircuit.applications.dqas.parallel_qaoa_train:13 -msgid "mean value of normal distribution for nnp" +#: of tensorcircuit.abstractcircuit.AbstractCircuit.apply_general_gate:1 +#: tensorcircuit.basecircuit.BaseCircuit.apply_general_gate:1 +msgid "" +"An implementation of this method should also append gate directionary to " +"self._qir" msgstr "" -#: of tensorcircuit.applications.dqas.parallel_qaoa_train:14 -msgid "std deviation of normal distribution for nnp" +#: of tensorcircuit.abstractcircuit.AbstractCircuit.barrier_instruction:1 +msgid "add a barrier instruction flag, no effect on numerical simulation" msgstr "" -#: of tensorcircuit.applications.dqas.verbose_output:1 -msgid "Doesn't support prob model DQAS search." +#: of tensorcircuit.abstractcircuit.AbstractCircuit.barrier_instruction:3 +#: tensorcircuit.abstractcircuit.AbstractCircuit.measure_instruction:3 +#: tensorcircuit.abstractcircuit.AbstractCircuit.reset_instruction:3 +msgid "the corresponding qubits" msgstr "" -#: ../../source/api/applications/graphdata.rst:2 -msgid "tensorcircuit.applications.graphdata" +#: of tensorcircuit.abstractcircuit.AbstractCircuit.cond_measurement:1 +#: tensorcircuit.basecircuit.BaseCircuit.cond_measurement:1 +msgid "" +"Measurement on z basis at ``index`` qubit based on quantum amplitude (not" +" post-selection). The highlight is that this method can return the " +"measured result as a int Tensor and thus maintained a jittable pipeline." msgstr "" -#: of tensorcircuit.applications.graphdata:1 -msgid "Modules for graph instance data and more" +#: of tensorcircuit.abstractcircuit.AbstractCircuit.cond_measurement:16 +#: tensorcircuit.basecircuit.BaseCircuit.cond_measurement:16 +msgid "" +"In terms of ``DMCircuit``, this method returns nothing and the density " +"matrix after this method is kept in mixed state without knowing the " +"measuremet resuslts" msgstr "" -#: of tensorcircuit.applications.graphdata.dict2graph:1 -msgid "```python d = nx.to_dict_of_dicts(g) ```" +#: of tensorcircuit.abstractcircuit.AbstractCircuit.cond_measurement:22 +#: tensorcircuit.basecircuit.BaseCircuit.cond_measurement:22 +msgid "the qubit for the z-basis measurement" msgstr "" -#: of tensorcircuit.applications.graphdata.graph1D:1 -msgid "1D PBC chain with n sites." +#: of tensorcircuit.abstractcircuit.AbstractCircuit.cond_measurement:24 +#: tensorcircuit.basecircuit.BaseCircuit.cond_measurement:24 +msgid "0 or 1 for z measurement on up and down freedom" msgstr "" -#: of tensorcircuit.applications.graphdata.graph1D:3 -msgid "The number of nodes" +#: of tensorcircuit.abstractcircuit.AbstractCircuit.select_gate:1 +msgid "Apply ``which``-th gate from ``kraus`` list, i.e. apply kraus[which]" msgstr "" -#: of tensorcircuit.applications.graphdata.graph1D:5 -msgid "The resulted graph g" +#: of tensorcircuit.abstractcircuit.AbstractCircuit.select_gate:3 +msgid "Tensor of shape [] and dtype int" msgstr "" -#: of tensorcircuit.applications.graphdata.reduce_edges:3 -msgid "all graphs with m edge out from g" +#: of tensorcircuit.abstractcircuit.AbstractCircuit.select_gate:5 +msgid "A list of gate in the form of ``tc.gate`` or Tensor" msgstr "" -#: of tensorcircuit.applications.graphdata.reduced_ansatz:1 +#: of tensorcircuit.abstractcircuit.AbstractCircuit.select_gate:7 +msgid "the qubit lines the gate applied on" +msgstr "" + +#: of tensorcircuit.abstractcircuit.AbstractCircuit.draw:1 msgid "" -"Generate a reduced graph with given ratio of edges compared to the " -"original graph g." +"Visualise the circuit. This method recevies the keywords as same as " +"qiskit.circuit.QuantumCircuit.draw. More details can be found here: " +"https://qiskit.org/documentation/stubs/qiskit.circuit.QuantumCircuit.draw.html." +" Interesting kws options include: ``idle_wires``(bool)" msgstr "" -#: of tensorcircuit.applications.graphdata.reduced_ansatz:3 -msgid "The base graph" +#: of tensorcircuit.abstractcircuit.AbstractCircuit.expectation_ps:1 +msgid "" +"Shortcut for Pauli string expectation. x, y, z list are for X, Y, Z " +"positions" msgstr "" -#: of tensorcircuit.applications.graphdata.reduced_ansatz:5 -msgid "number of edges kept, default half of the edges" +#: of tensorcircuit.abstractcircuit.AbstractCircuit.expectation_ps:26 +#: tensorcircuit.noisemodel.sample_expectation_ps_noisfy:5 +msgid "sites to apply X gate, defaults to None" msgstr "" -#: of tensorcircuit.applications.graphdata.reduced_ansatz:6 -msgid "The resulted reduced graph" +#: of tensorcircuit.abstractcircuit.AbstractCircuit.expectation_ps:28 +#: tensorcircuit.noisemodel.sample_expectation_ps_noisfy:7 +msgid "sites to apply Y gate, defaults to None" msgstr "" -#: of tensorcircuit.applications.graphdata.split_ansatz:1 -msgid "Split the graph in exactly ``split`` piece evenly." +#: of tensorcircuit.abstractcircuit.AbstractCircuit.expectation_ps:30 +#: tensorcircuit.noisemodel.sample_expectation_ps_noisfy:9 +msgid "sites to apply Z gate, defaults to None" msgstr "" -#: of tensorcircuit.applications.graphdata.split_ansatz:3 -msgid "The mother graph" +#: of tensorcircuit.abstractcircuit.AbstractCircuit.expectation_ps:32 +msgid "" +"or one can apply a ps structures instead of ``x``, ``y``, ``z``, e.g. [0," +" 1, 3, 0, 2, 2] for X_1Z_2Y_4Y_5 defaults to None, ``ps`` can overwrite " +"``x``, ``y`` and ``z``" msgstr "" -#: of tensorcircuit.applications.graphdata.split_ansatz:5 -msgid "The number of the graph we want to divide into, defaults to 2" +#: of tensorcircuit.abstractcircuit.AbstractCircuit.expectation_ps:36 +msgid "whether to cache and reuse the wavefunction, defaults to True" msgstr "" -#: of tensorcircuit.applications.graphdata.split_ansatz:7 -msgid "List of graph instance of size ``split``" +#: of tensorcircuit.abstractcircuit.AbstractCircuit.expectation_ps:38 +#: tensorcircuit.basecircuit.BaseCircuit.sample_expectation_ps:46 +#: tensorcircuit.circuit.Circuit.expectation:32 +#: tensorcircuit.densitymatrix.DMCircuit.expectation:8 +#: tensorcircuit.noisemodel.expectation_noisfy:5 +#: tensorcircuit.noisemodel.sample_expectation_ps_noisfy:11 +msgid "Noise Configuration, defaults to None" msgstr "" -#: ../../source/api/applications/layers.rst:2 -msgid "tensorcircuit.applications.layers" +#: of tensorcircuit.abstractcircuit.AbstractCircuit.expectation_ps:40 +#: tensorcircuit.basecircuit.BaseCircuit.sample_expectation_ps:48 +#: tensorcircuit.circuit.Circuit.expectation:34 +#: tensorcircuit.noisemodel.expectation_noisfy:7 +msgid "" +"repetition time for Monte Carlo sampling for noisfy calculation, defaults" +" to 1000" msgstr "" -#: of tensorcircuit.applications.layers:1 -msgid "Module for functions adding layers of circuits" +#: of tensorcircuit.abstractcircuit.AbstractCircuit.expectation_ps:42 +#: tensorcircuit.basecircuit.BaseCircuit.sample_expectation_ps:50 +#: tensorcircuit.circuit.Circuit.expectation:36 +#: tensorcircuit.densitymatrix.DMCircuit.expectation:10 +#: tensorcircuit.noisemodel.expectation_noisfy:9 +#: tensorcircuit.noisemodel.sample_expectation_ps_noisfy:17 +msgid "" +"external randomness given by tensor uniformly from [0, 1], defaults to " +"None, used for noisfy circuit sampling" msgstr "" -#: of tensorcircuit.applications.layers.generate_cirq_gate_layer..f:1 -msgid "Hlayer" +#: of tensorcircuit.abstractcircuit.AbstractCircuit.expectation_ps:45 +msgid "Expectation value" msgstr "" -#: of -#: tensorcircuit.applications.layers.generate_cirq_any_gate_layer..f:1 -msgid "anyrxlayer" +#: of tensorcircuit.abstractcircuit.AbstractCircuit.from_json:1 +msgid "load json str as a Circuit" +msgstr "" + +#: of tensorcircuit.abstractcircuit.AbstractCircuit.from_json:3 +#: tensorcircuit.abstractcircuit.AbstractCircuit.from_json:8 +#: tensorcircuit.abstractcircuit.AbstractCircuit.from_json_file:7 +#: tensorcircuit.abstractcircuit.AbstractCircuit.initial_mapping:9 +#: tensorcircuit.backends.abstract_backend.ExtendedBackend.from_dlpack:5 +#: tensorcircuit.backends.abstract_backend.ExtendedBackend.scan:4 +#: tensorcircuit.backends.abstract_backend.ExtendedBackend.scan:6 +#: tensorcircuit.backends.abstract_backend.ExtendedBackend.scan:8 +#: tensorcircuit.backends.abstract_backend.ExtendedBackend.scan:10 +#: tensorcircuit.backends.abstract_backend.ExtendedBackend.to_dlpack:3 +#: tensorcircuit.backends.abstract_backend.ExtendedBackend.to_dlpack:5 +#: tensorcircuit.backends.cupy_backend.CuPyBackend.arange:9 +#: tensorcircuit.backends.cupy_backend.CuPyBackend.mean:9 +#: tensorcircuit.backends.cupy_backend.CuPyBackend.std:3 +#: tensorcircuit.backends.cupy_backend.CuPyBackend.std:12 +#: tensorcircuit.backends.jax_backend.JaxBackend.arange:9 +#: tensorcircuit.backends.jax_backend.JaxBackend.from_dlpack:5 +#: tensorcircuit.backends.jax_backend.JaxBackend.mean:9 +#: tensorcircuit.backends.jax_backend.JaxBackend.scan:4 +#: tensorcircuit.backends.jax_backend.JaxBackend.scan:6 +#: tensorcircuit.backends.jax_backend.JaxBackend.scan:8 +#: tensorcircuit.backends.jax_backend.JaxBackend.scan:10 +#: tensorcircuit.backends.jax_backend.JaxBackend.std:3 +#: tensorcircuit.backends.jax_backend.JaxBackend.std:12 +#: tensorcircuit.backends.jax_backend.JaxBackend.to_dlpack:3 +#: tensorcircuit.backends.jax_backend.JaxBackend.to_dlpack:5 +#: tensorcircuit.backends.numpy_backend.NumpyBackend.arange:9 +#: tensorcircuit.backends.numpy_backend.NumpyBackend.mean:9 +#: tensorcircuit.backends.numpy_backend.NumpyBackend.std:3 +#: tensorcircuit.backends.numpy_backend.NumpyBackend.std:12 +#: tensorcircuit.backends.pytorch_backend.PyTorchBackend.arange:9 +#: tensorcircuit.backends.pytorch_backend.PyTorchBackend.from_dlpack:5 +#: tensorcircuit.backends.pytorch_backend.PyTorchBackend.mean:9 +#: tensorcircuit.backends.pytorch_backend.PyTorchBackend.std:3 +#: tensorcircuit.backends.pytorch_backend.PyTorchBackend.std:12 +#: tensorcircuit.backends.pytorch_backend.PyTorchBackend.to_dlpack:3 +#: tensorcircuit.backends.pytorch_backend.PyTorchBackend.to_dlpack:5 +#: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.arange:9 +#: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.from_dlpack:5 +#: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.mean:9 +#: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.scan:4 +#: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.scan:6 +#: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.scan:8 +#: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.scan:10 +#: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.std:3 +#: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.std:12 +#: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.to_dlpack:3 +#: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.to_dlpack:5 +#: tensorcircuit.basecircuit.BaseCircuit.expectation_before:6 +#: tensorcircuit.basecircuit.BaseCircuit.expectation_before:7 +#: tensorcircuit.channels.is_hermitian_matrix:9 +#: tensorcircuit.cloud.abstraction.Device.native_gates:3 +#: tensorcircuit.cloud.abstraction.Task.details:8 +#: tensorcircuit.cloud.abstraction.Task.get_device:3 +#: tensorcircuit.cloud.abstraction.Task.state:3 +#: tensorcircuit.cloud.apis.get_device:10 +#: tensorcircuit.cloud.apis.get_provider:8 tensorcircuit.cloud.apis.get_task:3 +#: tensorcircuit.cloud.apis.get_task:9 +#: tensorcircuit.cloud.apis.get_task_details:3 +#: tensorcircuit.cloud.apis.get_task_details:10 +#: tensorcircuit.cloud.apis.get_token:8 tensorcircuit.cloud.apis.list_devices:7 +#: tensorcircuit.cloud.apis.list_providers:3 +#: tensorcircuit.cloud.apis.resubmit_task:3 +#: tensorcircuit.cloud.apis.resubmit_task:7 +#: tensorcircuit.cloud.apis.set_device:10 +#: tensorcircuit.cloud.apis.set_provider:8 +#: tensorcircuit.cloud.apis.set_token:13 +#: tensorcircuit.cons.runtime_contractor:3 +#: tensorcircuit.cons.set_function_contractor:3 +#: tensorcircuit.experimental.evol_global:4 +#: tensorcircuit.experimental.evol_global:9 +#: tensorcircuit.experimental.evol_global:11 +#: tensorcircuit.experimental.evol_local:4 +#: tensorcircuit.experimental.evol_local:6 +#: tensorcircuit.experimental.evol_local:13 +#: tensorcircuit.experimental.hamiltonian_evol:4 +#: tensorcircuit.experimental.hamiltonian_evol:6 +#: tensorcircuit.experimental.hamiltonian_evol:8 tensorcircuit.gates.u_gate:16 +#: tensorcircuit.interfaces.torch.torch_interface_kws:22 +#: tensorcircuit.interfaces.torch.torch_interface_kws:28 +#: tensorcircuit.quantum.count_vector2dict:9 tensorcircuit.quantum.ps2xyz:5 +#: tensorcircuit.quantum.ps2xyz:7 tensorcircuit.quantum.sample2count:3 +#: tensorcircuit.quantum.sample2count:5 tensorcircuit.quantum.sample2count:9 +#: tensorcircuit.quantum.xyz2ps:3 tensorcircuit.quantum.xyz2ps:7 +#: tensorcircuit.results.counts.plot_histogram:6 +#: tensorcircuit.results.counts.plot_histogram:8 +#: tensorcircuit.templates.graphs.Grid2DCoord.lattice_graph:6 +#: tensorcircuit.translation.eqasm2tc:3 tensorcircuit.translation.eqasm2tc:9 +#: tensorcircuit.translation.qir2json:3 tensorcircuit.translation.qir2json:8 +#: tensorcircuit.utils.arg_alias:3 tensorcircuit.utils.arg_alias:5 +#: tensorcircuit.utils.benchmark:3 tensorcircuit.utils.benchmark:9 +msgid "_description_" msgstr "" -#: of -#: tensorcircuit.applications.layers.generate_cirq_any_gate_layer..f:1 -msgid "anyrylayer" +#: of tensorcircuit.abstractcircuit.AbstractCircuit.from_json:5 +msgid "Extra circuit parameters in the format of ``__init__``, defaults to None" msgstr "" -#: of -#: tensorcircuit.applications.layers.generate_cirq_any_gate_layer..f:1 -msgid "anyrzlayer" +#: of tensorcircuit.abstractcircuit.AbstractCircuit.from_json_file:1 +msgid "load json file and convert it to a circuit" msgstr "" -#: of -#: tensorcircuit.applications.layers.generate_cirq_any_double_gate_layer..f:1 -msgid "anyswaplayer" +#: of tensorcircuit.abstractcircuit.AbstractCircuit.from_json_file:3 +msgid "filename" msgstr "" -#: of -#: tensorcircuit.applications.layers.generate_cirq_any_double_gate_layer..f:1 -msgid "anyxxlayer" +#: of tensorcircuit.abstractcircuit.AbstractCircuit.from_json_file:5 +#: tensorcircuit.abstractcircuit.AbstractCircuit.initial_mapping:7 +#: tensorcircuit.cloud.apis.get_provider:3 tensorcircuit.cloud.apis.get_task:5 +#: tensorcircuit.cloud.apis.get_task:7 +#: tensorcircuit.cloud.apis.get_task_details:5 +#: tensorcircuit.cloud.apis.get_token:4 tensorcircuit.cloud.apis.get_token:6 +#: tensorcircuit.cloud.apis.list_devices:3 +#: tensorcircuit.cloud.apis.list_devices:5 +#: tensorcircuit.cloud.apis.list_properties:3 +#: tensorcircuit.cloud.apis.list_properties:5 +#: tensorcircuit.cloud.apis.list_properties:7 +#: tensorcircuit.cloud.apis.list_tasks:3 tensorcircuit.cloud.apis.list_tasks:5 +#: tensorcircuit.cloud.apis.list_tasks:7 +#: tensorcircuit.cloud.apis.resubmit_task:5 +#: tensorcircuit.cloud.apis.set_provider:3 tensorcircuit.cloud.apis.set_token:5 +#: tensorcircuit.cloud.apis.set_token:7 tensorcircuit.cloud.apis.submit_task:7 +#: tensorcircuit.cloud.apis.submit_task:9 +#: tensorcircuit.cloud.apis.submit_task:11 +#: tensorcircuit.experimental.hamiltonian_evol:10 +#: tensorcircuit.quantum.xyz2ps:5 tensorcircuit.translation.eqasm2tc:5 +msgid "_description_, defaults to None" msgstr "" -#: of -#: tensorcircuit.applications.layers.generate_cirq_any_double_gate_layer..f:1 -msgid "anyxylayer" +#: of tensorcircuit.abstractcircuit.AbstractCircuit.from_qir:1 +msgid "Restore the circuit from the quantum intermediate representation." msgstr "" -#: of -#: tensorcircuit.applications.layers.generate_cirq_any_double_gate_layer..f:1 -msgid "anyxzlayer" +#: of tensorcircuit.abstractcircuit.AbstractCircuit.from_qir:21 +#: tensorcircuit.translation.qir2cirq:13 +#: tensorcircuit.translation.qir2qiskit:14 +msgid "The quantum intermediate representation of a circuit." msgstr "" -#: of -#: tensorcircuit.applications.layers.generate_cirq_any_double_gate_layer..f:1 -msgid "anyyxlayer" +#: of tensorcircuit.abstractcircuit.AbstractCircuit.from_qir:23 +msgid "Extra circuit parameters." msgstr "" -#: of -#: tensorcircuit.applications.layers.generate_cirq_any_double_gate_layer..f:1 -msgid "anyyylayer" +#: of tensorcircuit.abstractcircuit.AbstractCircuit.from_qir:25 +msgid "The circuit have same gates in the qir." msgstr "" -#: of -#: tensorcircuit.applications.layers.generate_cirq_any_double_gate_layer..f:1 -msgid "anyyzlayer" +#: of tensorcircuit.abstractcircuit.AbstractCircuit.from_qiskit:1 +msgid "Import Qiskit QuantumCircuit object as a ``tc.Circuit`` object." msgstr "" -#: of -#: tensorcircuit.applications.layers.generate_cirq_any_double_gate_layer..f:1 -msgid "anyzxlayer" +#: of tensorcircuit.abstractcircuit.AbstractCircuit.from_qiskit:12 +msgid "Qiskit Circuit object" msgstr "" -#: of -#: tensorcircuit.applications.layers.generate_cirq_any_double_gate_layer..f:1 -msgid "anyzylayer" +#: of tensorcircuit.abstractcircuit.AbstractCircuit.from_qiskit:14 +msgid "The number of qubits for the circuit" msgstr "" -#: of -#: tensorcircuit.applications.layers.generate_cirq_any_double_gate_layer..f:1 -msgid "anyzzlayer" +#: of tensorcircuit.abstractcircuit.AbstractCircuit.from_qiskit:16 +msgid "possible input wavefunction for ``tc.Circuit``, defaults to None" msgstr "" -#: of -#: tensorcircuit.applications.layers.generate_cirq_double_gate_layer..f:1 -msgid "cnotlayer" +#: of tensorcircuit.abstractcircuit.AbstractCircuit.from_qiskit:18 +#: tensorcircuit.translation.qiskit2tc:20 +msgid "kwargs given in Circuit.__init__ construction function, default to None." msgstr "" -#: of tensorcircuit.applications.layers.generate_cirq_gate_layer..f:1 -msgid "rxlayer" +#: of tensorcircuit.abstractcircuit.AbstractCircuit.from_qiskit:20 +#: tensorcircuit.translation.qiskit2tc:22 +msgid "" +"(variational) parameters for the circuit. Could be either a sequence or " +"dictionary depending on the type of parameters in the Qiskit circuit. For" +" ``ParameterVectorElement`` use sequence. For ``Parameter`` use " +"dictionary" msgstr "" -#: of tensorcircuit.applications.layers.generate_cirq_gate_layer..f:1 -msgid "rylayer" +#: of tensorcircuit.abstractcircuit.AbstractCircuit.from_qiskit:24 +msgid "The same circuit but as tensorcircuit object" msgstr "" -#: of tensorcircuit.applications.layers.generate_cirq_gate_layer..f:1 -msgid "rzlayer" +#: of tensorcircuit.abstractcircuit.AbstractCircuit.gate_count:1 +msgid "count the gate number of the circuit" msgstr "" -#: of -#: tensorcircuit.applications.layers.generate_cirq_double_gate_layer..f:1 -msgid "swaplayer" +#: of tensorcircuit.abstractcircuit.AbstractCircuit.gate_count:14 +msgid "" +"gate name or gate name list to be counted, defaults to None (counting all" +" gates)" msgstr "" -#: of tensorcircuit.applications.layers.generate_cirq_double_gate..f:1 -msgid "xxgate" +#: of tensorcircuit.abstractcircuit.AbstractCircuit.gate_count:16 +msgid "the total number of all gates or gates in the ``gate_list``" msgstr "" -#: of -#: tensorcircuit.applications.layers.generate_cirq_double_gate_layer..f:1 -msgid "xxlayer" +#: of tensorcircuit.abstractcircuit.AbstractCircuit.gate_count_by_condition:1 +msgid "count the number of gates that satisfy certain condition" msgstr "" -#: of tensorcircuit.applications.layers.generate_cirq_double_gate..f:1 -msgid "xygate" +#: of tensorcircuit.abstractcircuit.AbstractCircuit.gate_count_by_condition:14 +msgid "the condition for counting the gate" msgstr "" -#: of -#: tensorcircuit.applications.layers.generate_cirq_double_gate_layer..f:1 -msgid "xylayer" +#: of tensorcircuit.abstractcircuit.AbstractCircuit.gate_count_by_condition:16 +msgid "the total number of all gates which satisfy the ``condition``" msgstr "" -#: of tensorcircuit.applications.layers.generate_cirq_double_gate..f:1 -msgid "xzgate" +#: of tensorcircuit.abstractcircuit.AbstractCircuit.gate_summary:1 +msgid "return the summary dictionary on gate type - gate count pair" msgstr "" -#: of -#: tensorcircuit.applications.layers.generate_cirq_double_gate_layer..f:1 -msgid "xzlayer" +#: of tensorcircuit.abstractcircuit.AbstractCircuit.gate_summary:3 +msgid "the gate count dict by gate type" msgstr "" -#: of tensorcircuit.applications.layers.generate_cirq_double_gate..f:1 -msgid "yxgate" +#: of +#: tensorcircuit.abstractcircuit.AbstractCircuit.get_positional_logical_mapping:1 +msgid "" +"Get positional logical mapping dict based on measure instruction. This " +"function is useful when we only measure part of the qubits in the " +"circuit, to process the count result from partial measurement, we must be" +" aware of the mapping, i.e. for each position in the count bitstring, " +"what is the corresponding qubits (logical) defined on the circuit" msgstr "" #: of -#: tensorcircuit.applications.layers.generate_cirq_double_gate_layer..f:1 -msgid "yxlayer" +#: tensorcircuit.abstractcircuit.AbstractCircuit.get_positional_logical_mapping:7 +msgid "``positional_logical_mapping``" msgstr "" -#: of tensorcircuit.applications.layers.generate_cirq_double_gate..f:1 -msgid "yygate" +#: of tensorcircuit.abstractcircuit.AbstractCircuit.initial_mapping:1 +msgid "" +"generate a new circuit with the qubit mapping given by " +"``logical_physical_mapping``" msgstr "" -#: of -#: tensorcircuit.applications.layers.generate_cirq_double_gate_layer..f:1 -msgid "yylayer" +#: of tensorcircuit.abstractcircuit.AbstractCircuit.initial_mapping:3 +msgid "how to map logical qubits to the physical qubits on the new circuit" msgstr "" -#: of tensorcircuit.applications.layers.generate_cirq_double_gate..f:1 -msgid "yzgate" +#: of tensorcircuit.abstractcircuit.AbstractCircuit.initial_mapping:5 +msgid "" +"number of qubit of the new circuit, can be different from the original " +"one, defaults to None" msgstr "" -#: of -#: tensorcircuit.applications.layers.generate_cirq_double_gate_layer..f:1 -msgid "yzlayer" +#: of tensorcircuit.abstractcircuit.AbstractCircuit.inverse:1 +msgid "inverse the circuit, return a new inversed circuit" msgstr "" -#: of tensorcircuit.applications.layers.generate_cirq_double_gate..f:1 -msgid "zxgate" +#: of tensorcircuit.abstractcircuit.AbstractCircuit.inverse +msgid "EXAMPLE" msgstr "" -#: of -#: tensorcircuit.applications.layers.generate_cirq_double_gate_layer..f:1 -msgid "zxlayer" +#: of tensorcircuit.abstractcircuit.AbstractCircuit.inverse:10 +msgid "keywords dict for initialization the new circuit, defaults to None" msgstr "" -#: of tensorcircuit.applications.layers.generate_cirq_double_gate..f:1 -msgid "zygate" +#: of tensorcircuit.abstractcircuit.AbstractCircuit.inverse:12 +msgid "the inversed circuit" msgstr "" -#: of -#: tensorcircuit.applications.layers.generate_cirq_double_gate_layer..f:1 -msgid "zylayer" +#: of tensorcircuit.abstractcircuit.AbstractCircuit.measure_instruction:1 +msgid "add a measurement instruction flag, no effect on numerical simulation" msgstr "" -#: of tensorcircuit.applications.layers.generate_cirq_double_gate..f:1 -msgid "zzgate" +#: of tensorcircuit.abstractcircuit.AbstractCircuit.prepend:1 +msgid "prepend circuit ``c`` before" msgstr "" -#: of -#: tensorcircuit.applications.layers.generate_cirq_double_gate_layer..f:1 -msgid "zzlayer" +#: of tensorcircuit.abstractcircuit.AbstractCircuit.prepend:3 +msgid "The other circuit to be prepended" msgstr "" -#: of tensorcircuit.applications.layers.generate_any_gate_layer:1 -msgid "$$e^{-i heta_i \\sigma}$$" +#: of tensorcircuit.abstractcircuit.AbstractCircuit.reset_instruction:1 +msgid "add a reset instruction flag, no effect on numerical simulation" msgstr "" -#: of tensorcircuit.applications.layers.generate_cirq_any_double_gate_layer:1 -msgid "" -"The following function should be used to generate layers with special " -"case. As its soundness depends on the nature of the task or problem, it " -"doesn't always make sense." +#: of tensorcircuit.abstractcircuit.AbstractCircuit.standardize_gate:1 +msgid "standardize the gate name to tc common gate sets" msgstr "" -#: of tensorcircuit.applications.layers.generate_cirq_any_gate_layer:1 -#: tensorcircuit.applications.layers.generate_cirq_gate_layer:1 -msgid "$$e^{-i heta \\sigma}$$" +#: of tensorcircuit.abstractcircuit.AbstractCircuit.standardize_gate:3 +msgid "non-standard gate name" msgstr "" -#: of tensorcircuit.applications.layers.generate_gate_layer:1 -msgid "$$e^{-i heta \\sigma}$$" +#: of tensorcircuit.abstractcircuit.AbstractCircuit.standardize_gate:5 +msgid "the standard gate name" msgstr "" -#: ../../source/api/applications/utils.rst:2 -msgid "tensorcircuit.applications.utils" +#: of tensorcircuit.abstractcircuit.AbstractCircuit.vis_tex:1 +msgid "Generate latex string based on quantikz latex package" msgstr "" -#: of tensorcircuit.applications.utils:1 -msgid "" -"A collection of useful function snippets that irrelevant with the main " -"modules or await for further refactor" +#: of tensorcircuit.abstractcircuit.AbstractCircuit.vis_tex:3 +msgid "Latex string that can be directly compiled via, e.g. latexit" msgstr "" -#: of tensorcircuit.applications.utils.FakeModule:1 -#: tensorcircuit.applications.vqes.VQNHE:1 -#: tensorcircuit.backends.jax_backend.optax_optimizer:1 -#: tensorcircuit.backends.pytorch_backend.torch_optimizer:1 -#: tensorcircuit.backends.tensorflow_backend.keras_optimizer:1 -#: tensorcircuit.circuit.Circuit:1 tensorcircuit.densitymatrix.DMCircuit:1 -#: tensorcircuit.gates.GateF:1 tensorcircuit.mpscircuit.MPSCircuit:1 -#: tensorcircuit.quantum.QuOperator:1 -#: tensorcircuit.templates.graphs.Grid2DCoord:1 -msgid "Bases: :py:class:`object`" +#: of tensorcircuit.abstractcircuit.AbstractCircuit.to_cirq:1 +msgid "Translate ``tc.Circuit`` to a cirq circuit object." msgstr "" -#: of tensorcircuit.applications.utils.color_svg:1 -msgid "color cirq circuit SVG for given gates, a small tool to hack the cirq SVG" +#: of tensorcircuit.abstractcircuit.AbstractCircuit.to_cirq:3 +#: tensorcircuit.abstractcircuit.AbstractCircuit.to_qiskit:3 +msgid "whether also export measurement and reset instructions" msgstr "" -#: of tensorcircuit.applications.utils.color_svg:5 -msgid "integer coordinate which gate is colored" +#: of tensorcircuit.abstractcircuit.AbstractCircuit.to_cirq:5 +msgid "A cirq circuit of this circuit." msgstr "" -#: of tensorcircuit.applications.utils.repr2array:1 -msgid "transform repr form of an array to real numpy array" +#: of tensorcircuit.abstractcircuit.AbstractCircuit.to_json:1 +msgid "circuit dumps to json" msgstr "" -#: ../../source/api/applications/vags.rst:2 -msgid "tensorcircuit.applications.vags" +#: of tensorcircuit.abstractcircuit.AbstractCircuit.to_json:3 +msgid "file str to dump the json to, defaults to None, return the json str" msgstr "" -#: of tensorcircuit.applications.vags:1 -msgid "DQAS application kernels as vag functions" +#: of tensorcircuit.abstractcircuit.AbstractCircuit.to_json:5 +#: tensorcircuit.translation.qir2json:5 +msgid "" +"If False, keep all info for each gate, defaults to be False. If True, " +"suitable for IO since less information is required" msgstr "" -#: of tensorcircuit.applications.vags.ave_func:1 -msgid "1D array for full wavefunction, the basis is in lexcical order" +#: of tensorcircuit.abstractcircuit.AbstractCircuit.to_json:8 +msgid "None if dumps to file otherwise the json str" msgstr "" -#: of tensorcircuit.applications.vags.ave_func:2 -#: tensorcircuit.applications.vags.energy:5 -msgid "nx.Graph" +#: of tensorcircuit.abstractcircuit.AbstractCircuit.to_openqasm:1 +msgid "" +"transform circuit to openqasm via qiskit circuit, see " +"https://qiskit.org/documentation/stubs/qiskit.circuit.QuantumCircuit.qasm.html" +" for usage on possible options for ``kws``" msgstr "" -#: of tensorcircuit.applications.vags.ave_func:3 -msgid "transformation functions before averaged" +#: of tensorcircuit.abstractcircuit.AbstractCircuit.to_openqasm:5 +msgid "circuit representation in openqasm format" msgstr "" -#: of tensorcircuit.applications.vags.cvar:1 -msgid "as f3" +#: of tensorcircuit.abstractcircuit.AbstractCircuit.to_qir:1 +#: tensorcircuit.basecircuit.BaseCircuit.to_qir:1 +msgid "Return the quantum intermediate representation of the circuit." msgstr "" -#: of tensorcircuit.applications.vags.energy:1 -msgid "maxcut energy for n qubit wavefunction i-th basis" +#: of tensorcircuit.abstractcircuit.AbstractCircuit.to_qir:32 +#: tensorcircuit.basecircuit.BaseCircuit.to_qir:32 +msgid "The quantum intermediate representation of the circuit." msgstr "" -#: of tensorcircuit.applications.vags.energy:3 -msgid "ranged from 0 to 2**n-1" +#: of tensorcircuit.abstractcircuit.AbstractCircuit.to_qiskit:1 +msgid "Translate ``tc.Circuit`` to a qiskit QuantumCircuit object." msgstr "" -#: of tensorcircuit.applications.vags.energy:4 -#: tensorcircuit.applications.vags.unitary_design:4 -msgid "number of qubits" +#: of tensorcircuit.abstractcircuit.AbstractCircuit.to_qiskit:5 +msgid "whether also export the inputs" msgstr "" -#: of tensorcircuit.applications.vags.entanglement_entropy:1 -msgid "" -"deprecated as non tf and non flexible, use the combination of " -"``reduced_density_matrix`` and ``entropy`` instead." +#: of tensorcircuit.abstractcircuit.AbstractCircuit.to_qiskit:7 +msgid "A qiskit object of this circuit." msgstr "" -#: of tensorcircuit.applications.vags.entropy:1 -#: tensorcircuit.applications.vags.reduced_density_matrix:1 -msgid "deprecated, current version in tc.quantum" +#: ../../source/api/applications.rst:2 +msgid "tensorcircuit.applications" msgstr "" -#: of tensorcircuit.applications.vags.evaluate_vag:1 -msgid "" -"value and gradient, currently only tensorflow backend is supported jax " -"and numpy seems to be slow in circuit simulation anyhow. *deprecated*" +#: ../../source/api/applications/dqas.rst:2 +msgid "tensorcircuit.applications.dqas" msgstr "" -#: of tensorcircuit.applications.vags.evaluate_vag:8 -msgid "if lbd=0, take energy as objective" +#: of tensorcircuit.applications.dqas:1 +msgid "Modules for DQAS framework" msgstr "" -#: of tensorcircuit.applications.vags.evaluate_vag:9 -msgid "if as default 0, overlap will not compute in the process" +#: of tensorcircuit.applications.dqas.DQAS_search:1 +msgid "DQAS framework entrypoint" msgstr "" -#: of tensorcircuit.applications.vags.gapfilling:1 -msgid "Fill single qubit gates according to placeholder on circuit" +#: of tensorcircuit.applications.dqas.DQAS_search:3 +msgid "" +"function with input of data instance, circuit parameters theta and " +"structural paramter k, return tuple of objective value and gradient with " +"respect to theta" msgstr "" -#: of tensorcircuit.applications.vags.heisenberg_measurements:1 -msgid "Hamiltonian measurements for Heisenberg model on graph lattice g" +#: of tensorcircuit.applications.dqas.DQAS_search:5 +msgid "data generator as dataset" msgstr "" -#: of tensorcircuit.applications.vags.q:1 -msgid "short cut for ``cirq.LineQubit(i)``" +#: of tensorcircuit.applications.dqas.DQAS_search:6 +msgid "list of operations as primitive operator pool" msgstr "" -#: of tensorcircuit.applications.vags.qaoa_block_vag:1 -#: tensorcircuit.applications.vags.qaoa_block_vag_energy:1 -msgid "QAOA block encoding kernel, support 2 params in one op" +#: of tensorcircuit.applications.dqas.DQAS_search:7 +msgid "the default layer number of the circuit ansatz" msgstr "" -#: of tensorcircuit.applications.vags.qaoa_train:1 +#: of tensorcircuit.applications.dqas.DQAS_search:8 msgid "" -"training QAOA with only optimizing circuit parameters, can be well " -"replaced with more general function `DQAS_search`" +"shape of circuit parameter pool, in general p_stp*l, where l is the max " +"number of circuit parameters for op in the operator pool" msgstr "" -#: of tensorcircuit.applications.vags.quantum_mp_qaoa_vag:1 -msgid "multi parameter for one layer" +#: of tensorcircuit.applications.dqas.DQAS_search:10 +msgid "the same as p in the most times" msgstr "" -#: of tensorcircuit.applications.vags.quantum_mp_qaoa_vag:7 -#: tensorcircuit.applications.vags.quantum_qaoa_vag:7 -msgid "kw arguments for measurements_func" +#: of tensorcircuit.applications.dqas.DQAS_search:11 +msgid "batch size of one epoch" msgstr "" -#: of tensorcircuit.applications.vags.quantum_mp_qaoa_vag:8 -msgid "loss function, gradient of nnp" +#: of tensorcircuit.applications.dqas.DQAS_search:12 +msgid "prethermal update times" msgstr "" -#: of tensorcircuit.applications.vags.quantum_qaoa_vag:1 -msgid "" -"tensorflow quantum backend compare to qaoa_vag which is tensorcircuit " -"backend" +#: of tensorcircuit.applications.dqas.DQAS_search:13 +msgid "training epochs" msgstr "" -#: of tensorcircuit.applications.vags.tfim_measurements:1 -msgid "Hamiltonian for tfim on lattice defined by graph g" +#: of tensorcircuit.applications.dqas.DQAS_search:14 +msgid "parallel thread number, 0 to disable multiprocessing model by default" msgstr "" -#: of tensorcircuit.applications.vags.tfim_measurements:8 -msgid "cirq.PauliSum as operators for tfq expectation layer" +#: of tensorcircuit.applications.dqas.DQAS_search:15 +msgid "set verbose log to print" msgstr "" -#: of tensorcircuit.applications.vags.unitary_design:1 -msgid "" -"generate random wavefunction from approximately Haar measure, reference:" -" https://doi.org/10.1063/1.4983266" +#: of tensorcircuit.applications.dqas.DQAS_search:16 +msgid "function to output verbose information" msgstr "" -#: of tensorcircuit.applications.vags.unitary_design:5 -msgid "repetition of the blocks" +#: of tensorcircuit.applications.dqas.DQAS_search:17 +msgid "function return intermiediate result for final history list" msgstr "" -#: of tensorcircuit.applications.vags.unitary_design_block:1 -msgid "random Haar measure approximation" +#: of tensorcircuit.applications.dqas.DQAS_search:18 +msgid "cutoff probability to avoid peak distribution" msgstr "" -#: of tensorcircuit.applications.vags.unitary_design_block:3 -msgid "cirq.Circuit, empty circuit" +#: of tensorcircuit.applications.dqas.DQAS_search:19 +msgid "" +"function accepting list of objective values and return the baseline value" +" used in the next round" msgstr "" -#: of tensorcircuit.applications.vags.unitary_design_block:4 -msgid "# of qubit" +#: of tensorcircuit.applications.dqas.DQAS_search:21 +msgid "return noise with the same shape as circuit parameter pool" msgstr "" -#: ../../source/api/applications/van.rst:2 -msgid "tensorcircuit.applications.van" +#: of tensorcircuit.applications.dqas.DQAS_search:22 +msgid "initial values for circuit parameter pool" msgstr "" -#: of tensorcircuit.applications.van:1 -msgid "" -"One-hot variational autoregressive models for multiple categorical " -"choices beyond binary" +#: of tensorcircuit.applications.dqas.DQAS_search:23 +msgid "initial values for probabilistic model parameters" msgstr "" -#: of tensorcircuit.applications.van.MADE:1 -#: tensorcircuit.applications.van.NMF:1 -#: tensorcircuit.applications.van.PixelCNN:1 -msgid "Bases: :py:class:`keras.engine.training.Model`" +#: of tensorcircuit.applications.dqas.DQAS_search:24 +msgid "optimizer for circuit parameters theta" msgstr "" -#: of tensorcircuit.applications.van.MADE.call:1 -#: tensorcircuit.applications.van.NMF.call:1 -#: tensorcircuit.applications.van.PixelCNN.call:1 -msgid "Calls the model on new inputs and returns the outputs as tensors." +#: of tensorcircuit.applications.dqas.DQAS_search:25 +msgid "optimizer for model parameters alpha" msgstr "" -#: of tensorcircuit.applications.van.MADE.call:3 -#: tensorcircuit.applications.van.NMF.call:3 -#: tensorcircuit.applications.van.PixelCNN.call:3 -msgid "" -"In this case `call()` just reapplies all ops in the graph to the new " -"inputs (e.g. build a new computational graph from the provided inputs)." +#: of tensorcircuit.applications.dqas.DQAS_search:26 +msgid "optimizer for circuit parameters in prethermal stage" msgstr "" -#: of tensorcircuit.applications.van.MADE.call:7 -#: tensorcircuit.applications.van.NMF.call:7 -#: tensorcircuit.applications.van.PixelCNN.call:7 -msgid "" -"Note: This method should not be called directly. It is only meant to be " -"overridden when subclassing `tf.keras.Model`. To call a model on an " -"input, always use the `__call__()` method, i.e. `model(inputs)`, which " -"relies on the underlying `call()` method." +#: of tensorcircuit.applications.dqas.DQAS_search:27 +msgid "fixed structural parameters for prethermal training" msgstr "" -#: of tensorcircuit.applications.van.MADE.call:18 -#: tensorcircuit.applications.van.MaskedConv2D.build:11 -#: tensorcircuit.applications.van.MaskedConv2D.call:37 -#: tensorcircuit.applications.van.MaskedLinear.call:37 -#: tensorcircuit.applications.van.NMF.call:18 -#: tensorcircuit.applications.van.PixelCNN.call:18 -#: tensorcircuit.applications.van.ResidualBlock.call:37 -#: tensorcircuit.applications.vqes.Linear.call:37 -#: tensorcircuit.backends.jax_backend.JaxBackend.eye:8 -#: tensorcircuit.backends.numpy_backend.NumpyBackend.eye:8 -#: tensorcircuit.backends.pytorch_backend.PyTorchBackend.eye:8 -#: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.eye:8 -#: tensorcircuit.keras.QuantumLayer.build:11 -msgid "Args:" +#: of tensorcircuit.applications.dqas.DQAS_search:28 +msgid "regularization function for model parameters alpha" msgstr "" -#: of tensorcircuit.applications.van.MADE.call:13 -#: tensorcircuit.applications.van.NMF.call:13 -#: tensorcircuit.applications.van.PixelCNN.call:13 +#: of tensorcircuit.applications.dqas.DQAS_search:29 +msgid "regularization function for circuit parameters theta" +msgstr "" + +#: of tensorcircuit.applications.dqas.DQAS_search_pmb:1 msgid "" -"inputs: Input tensor, or dict/list/tuple of input tensors. training: " -"Boolean or boolean scalar tensor, indicating whether to run" +"The probabilistic model based DQAS, can use extensively for DQAS case for" +" ``NMF`` probabilistic model." msgstr "" -#: of tensorcircuit.applications.van.MADE.call:15 -#: tensorcircuit.applications.van.NMF.call:15 -#: tensorcircuit.applications.van.PixelCNN.call:15 -msgid "the `Network` in training mode or inference mode." +#: of tensorcircuit.applications.dqas.DQAS_search_pmb:3 +msgid "vag func, return loss and nabla lnp" +msgstr "" + +#: of tensorcircuit.applications.dqas.DQAS_search_pmb:4 +msgid "keras model" msgstr "" -#: of tensorcircuit.applications.van.MADE.call:18 -#: tensorcircuit.applications.van.NMF.call:18 -#: tensorcircuit.applications.van.PixelCNN.call:18 -msgid "mask: A mask or list of masks. A mask can be either a boolean tensor or" +#: of tensorcircuit.applications.dqas.DQAS_search_pmb:5 +msgid "sample func of logic with keras model input" msgstr "" -#: of tensorcircuit.applications.van.MADE.call:18 -#: tensorcircuit.applications.van.NMF.call:18 -#: tensorcircuit.applications.van.PixelCNN.call:18 -msgid "None (no mask). For more details, check the guide" +#: of tensorcircuit.applications.dqas.DQAS_search_pmb:6 +msgid "input data pipeline generator" msgstr "" -#: of tensorcircuit.applications.van.MADE.call:18 -#: tensorcircuit.applications.van.NMF.call:18 -#: tensorcircuit.applications.van.PixelCNN.call:18 -msgid "[here](https://www.tensorflow.org/guide/keras/masking_and_padding)." +#: of tensorcircuit.applications.dqas.DQAS_search_pmb:7 +msgid "operation pool" msgstr "" -#: of tensorcircuit.applications.van.MADE.call:21 -#: tensorcircuit.applications.van.MaskedConv2D.call:39 -#: tensorcircuit.applications.van.MaskedLinear.call:39 -#: tensorcircuit.applications.van.NMF.call:21 -#: tensorcircuit.applications.van.PixelCNN.call:21 -#: tensorcircuit.applications.van.ResidualBlock.call:39 -#: tensorcircuit.applications.vqes.Linear.call:39 -#: tensorcircuit.backends.jax_backend.JaxBackend.abs:4 -#: tensorcircuit.backends.numpy_backend.NumpyBackend.abs:4 -#: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.abs:4 -msgid "Returns:" +#: of tensorcircuit.applications.dqas.DQAS_search_pmb:8 +msgid "depth for DQAS" msgstr "" -#: of tensorcircuit.applications.van.MADE.call:21 -#: tensorcircuit.applications.van.NMF.call:21 -#: tensorcircuit.applications.van.PixelCNN.call:21 -msgid "" -"A tensor if there is a single output, or a list of tensors if there are " -"more than one outputs." +#: of tensorcircuit.applications.dqas.DQAS_search_pmb:12 +msgid "parallel kernels" msgstr "" -#: of tensorcircuit.applications.van.MaskedConv2D:1 -#: tensorcircuit.applications.van.MaskedLinear:1 -#: tensorcircuit.applications.van.ResidualBlock:1 -#: tensorcircuit.applications.vqes.Linear:1 tensorcircuit.keras.QuantumLayer:1 -msgid "Bases: :py:class:`keras.engine.base_layer.Layer`" +#: of tensorcircuit.applications.dqas.DQAS_search_pmb:23 +msgid "final loss function in terms of average of sub loss for each circuit" msgstr "" -#: of tensorcircuit.applications.van.MaskedConv2D.build:1 -#: tensorcircuit.keras.QuantumLayer.build:1 -msgid "Creates the variables of the layer (optional, for subclass implementers)." +#: of tensorcircuit.applications.dqas.DQAS_search_pmb:24 +msgid "derivative function for ``loss_func``" msgstr "" -#: of tensorcircuit.applications.van.MaskedConv2D.build:3 -#: tensorcircuit.keras.QuantumLayer.build:3 +#: of tensorcircuit.applications.dqas.get_var:1 msgid "" -"This is a method that implementers of subclasses of `Layer` or `Model` " -"can override if they need a state-creation step in-between layer " -"instantiation and layer call." +"Call in customized functions and grab variables within DQAS framework " +"function by var name str." msgstr "" -#: of tensorcircuit.applications.van.MaskedConv2D.build:7 -#: tensorcircuit.keras.QuantumLayer.build:7 -msgid "This is typically used to create the weights of `Layer` subclasses." +#: of tensorcircuit.applications.dqas.get_var:3 +msgid "The DQAS framework function" msgstr "" -#: of tensorcircuit.applications.van.MaskedConv2D.build:11 -#: tensorcircuit.keras.QuantumLayer.build:11 -msgid "input_shape: Instance of `TensorShape`, or list of instances of" +#: of tensorcircuit.applications.dqas.get_var:5 +msgid "Variables within the DQAS framework" msgstr "" -#: of tensorcircuit.applications.van.MaskedConv2D.build:11 -#: tensorcircuit.keras.QuantumLayer.build:11 +#: of tensorcircuit.applications.dqas.get_weights:1 msgid "" -"`TensorShape` if the layer expects a list of inputs (one instance per " -"input)." +"This function works only when nnp has the same shape as stp, i.e. one " +"parameter for each op." msgstr "" -#: of tensorcircuit.applications.van.MaskedConv2D.call:1 -#: tensorcircuit.applications.van.MaskedLinear.call:1 -#: tensorcircuit.applications.van.ResidualBlock.call:1 -#: tensorcircuit.applications.vqes.Linear.call:1 -msgid "This is where the layer's logic lives." +#: of tensorcircuit.applications.dqas.parallel_kernel:1 +msgid "The kernel for multiprocess to run parallel in DQAS function/" msgstr "" -#: of tensorcircuit.applications.van.MaskedConv2D.call:3 -#: tensorcircuit.applications.van.MaskedLinear.call:3 -#: tensorcircuit.applications.van.ResidualBlock.call:3 -#: tensorcircuit.applications.vqes.Linear.call:3 +#: of tensorcircuit.applications.dqas.parallel_qaoa_train:1 msgid "" -"Note here that `call()` method in `tf.keras` is little bit different from" -" `keras` API. In `keras` API, you can pass support masking for layers as " -"additional arguments. Whereas `tf.keras` has `compute_mask()` method to " -"support masking." +"parallel variational parameter training and search to avoid local minimum" +" not limited to qaoa setup as the function name indicates, as long as you" +" provided suitable `vag_func`" msgstr "" -#: of tensorcircuit.applications.van.MaskedConv2D.call:24 -#: tensorcircuit.applications.van.MaskedLinear.call:24 -#: tensorcircuit.applications.van.ResidualBlock.call:24 -#: tensorcircuit.applications.vqes.Linear.call:24 -msgid "inputs: Input tensor, or dict/list/tuple of input tensors." +#: of tensorcircuit.applications.dqas.parallel_qaoa_train:6 +msgid "data input generator for vag_func" msgstr "" -#: of tensorcircuit.applications.van.MaskedConv2D.call:10 -#: tensorcircuit.applications.van.MaskedLinear.call:10 -#: tensorcircuit.applications.van.ResidualBlock.call:10 -#: tensorcircuit.applications.vqes.Linear.call:10 -msgid "" -"The first positional `inputs` argument is subject to special rules: - " -"`inputs` must be explicitly passed. A layer cannot have zero" +#: of tensorcircuit.applications.dqas.parallel_qaoa_train:7 +msgid "vag_kernel" msgstr "" -#: of tensorcircuit.applications.van.MaskedConv2D.call:12 -#: tensorcircuit.applications.van.MaskedLinear.call:12 -#: tensorcircuit.applications.van.ResidualBlock.call:12 -#: tensorcircuit.applications.vqes.Linear.call:12 -msgid "" -"arguments, and `inputs` cannot be provided via the default value of a " -"keyword argument." +#: of tensorcircuit.applications.dqas.parallel_qaoa_train:10 +msgid "number of tries" msgstr "" -#: of tensorcircuit.applications.van.MaskedConv2D.call:14 -#: tensorcircuit.applications.van.MaskedLinear.call:14 -#: tensorcircuit.applications.van.ResidualBlock.call:14 -#: tensorcircuit.applications.vqes.Linear.call:14 -msgid "NumPy array or Python scalar values in `inputs` get cast as tensors." +#: of tensorcircuit.applications.dqas.parallel_qaoa_train:11 +msgid "for optimization problem the input is in general fixed so batch is often 1" msgstr "" -#: of tensorcircuit.applications.van.MaskedConv2D.call:15 -#: tensorcircuit.applications.van.MaskedLinear.call:15 -#: tensorcircuit.applications.van.ResidualBlock.call:15 -#: tensorcircuit.applications.vqes.Linear.call:15 -msgid "Keras mask metadata is only collected from `inputs`." +#: of tensorcircuit.applications.dqas.parallel_qaoa_train:12 +msgid "number of parallel jobs" msgstr "" -#: of tensorcircuit.applications.van.MaskedConv2D.call:16 -#: tensorcircuit.applications.van.MaskedLinear.call:16 -#: tensorcircuit.applications.van.ResidualBlock.call:16 -#: tensorcircuit.applications.vqes.Linear.call:16 -msgid "" -"Layers are built (`build(input_shape)` method) using shape info from " -"`inputs` only." +#: of tensorcircuit.applications.dqas.parallel_qaoa_train:13 +msgid "mean value of normal distribution for nnp" msgstr "" -#: of tensorcircuit.applications.van.MaskedConv2D.call:18 -#: tensorcircuit.applications.van.MaskedLinear.call:18 -#: tensorcircuit.applications.van.ResidualBlock.call:18 -#: tensorcircuit.applications.vqes.Linear.call:18 -msgid "`input_spec` compatibility is only checked against `inputs`." +#: of tensorcircuit.applications.dqas.parallel_qaoa_train:14 +msgid "std deviation of normal distribution for nnp" msgstr "" -#: of tensorcircuit.applications.van.MaskedConv2D.call:19 -#: tensorcircuit.applications.van.MaskedLinear.call:19 -#: tensorcircuit.applications.van.ResidualBlock.call:19 -#: tensorcircuit.applications.vqes.Linear.call:19 -msgid "" -"Mixed precision input casting is only applied to `inputs`. If a layer has" -" tensor arguments in `*args` or `**kwargs`, their casting behavior in " -"mixed precision should be handled manually." +#: of tensorcircuit.applications.dqas.verbose_output:1 +msgid "Doesn't support prob model DQAS search." msgstr "" -#: of tensorcircuit.applications.van.MaskedConv2D.call:22 -#: tensorcircuit.applications.van.MaskedLinear.call:22 -#: tensorcircuit.applications.van.ResidualBlock.call:22 -#: tensorcircuit.applications.vqes.Linear.call:22 -msgid "The SavedModel input specification is generated using `inputs` only." +#: ../../source/api/applications/graphdata.rst:2 +msgid "tensorcircuit.applications.graphdata" msgstr "" -#: of tensorcircuit.applications.van.MaskedConv2D.call:23 -#: tensorcircuit.applications.van.MaskedLinear.call:23 -#: tensorcircuit.applications.van.ResidualBlock.call:23 -#: tensorcircuit.applications.vqes.Linear.call:23 -msgid "" -"Integration with various ecosystem packages like TFMOT, TFLite, TF.js, " -"etc is only supported for `inputs` and not for tensors in positional and " -"keyword arguments." +#: of tensorcircuit.applications.graphdata:1 +msgid "Modules for graph instance data and more" msgstr "" -#: of tensorcircuit.applications.van.MaskedConv2D.call:26 -#: tensorcircuit.applications.van.MaskedLinear.call:26 -#: tensorcircuit.applications.van.ResidualBlock.call:26 -#: tensorcircuit.applications.vqes.Linear.call:26 -msgid "*args: Additional positional arguments. May contain tensors, although" +#: of tensorcircuit.applications.graphdata.dict2graph:1 +msgid "```python d = nx.to_dict_of_dicts(g) ```" msgstr "" -#: of tensorcircuit.applications.van.MaskedConv2D.call:27 -#: tensorcircuit.applications.van.MaskedLinear.call:27 -#: tensorcircuit.applications.van.ResidualBlock.call:27 -#: tensorcircuit.applications.vqes.Linear.call:27 -msgid "this is not recommended, for the reasons above." +#: of tensorcircuit.applications.graphdata.graph1D:1 +msgid "1D PBC chain with n sites." msgstr "" -#: of tensorcircuit.applications.van.MaskedConv2D.call:37 -#: tensorcircuit.applications.van.MaskedLinear.call:37 -#: tensorcircuit.applications.van.ResidualBlock.call:37 -#: tensorcircuit.applications.vqes.Linear.call:37 -msgid "**kwargs: Additional keyword arguments. May contain tensors, although" +#: of tensorcircuit.applications.graphdata.graph1D:3 +msgid "The number of nodes" msgstr "" -#: of tensorcircuit.applications.van.MaskedConv2D.call:29 -#: tensorcircuit.applications.van.MaskedLinear.call:29 -#: tensorcircuit.applications.van.ResidualBlock.call:29 -#: tensorcircuit.applications.vqes.Linear.call:29 -msgid "" -"this is not recommended, for the reasons above. The following optional " -"keyword arguments are reserved: - `training`: Boolean scalar tensor of " -"Python boolean indicating" +#: of tensorcircuit.applications.graphdata.graph1D:5 +msgid "The resulted graph g" msgstr "" -#: of tensorcircuit.applications.van.MaskedConv2D.call:32 -#: tensorcircuit.applications.van.MaskedLinear.call:32 -#: tensorcircuit.applications.van.ResidualBlock.call:32 -#: tensorcircuit.applications.vqes.Linear.call:32 -msgid "whether the `call` is meant for training or inference." +#: of tensorcircuit.applications.graphdata.reduce_edges:3 +msgid "all graphs with m edge out from g" msgstr "" -#: of tensorcircuit.applications.van.MaskedConv2D.call:33 -#: tensorcircuit.applications.van.MaskedLinear.call:33 -#: tensorcircuit.applications.van.ResidualBlock.call:33 -#: tensorcircuit.applications.vqes.Linear.call:33 +#: of tensorcircuit.applications.graphdata.reduced_ansatz:1 msgid "" -"`mask`: Boolean input mask. If the layer's `call()` method takes a `mask`" -" argument, its default value will be set to the mask generated for " -"`inputs` by the previous layer (if `input` did come from a layer that " -"generated a corresponding mask, i.e. if it came from a Keras layer with " -"masking support)." +"Generate a reduced graph with given ratio of edges compared to the " +"original graph g." msgstr "" -#: of tensorcircuit.applications.van.MaskedConv2D.call:40 -#: tensorcircuit.applications.van.MaskedLinear.call:40 -#: tensorcircuit.applications.van.ResidualBlock.call:40 -#: tensorcircuit.applications.vqes.Linear.call:40 -msgid "A tensor or list/tuple of tensors." +#: of tensorcircuit.applications.graphdata.reduced_ansatz:3 +msgid "The base graph" msgstr "" -#: ../../source/api/applications/vqes.rst:2 -msgid "tensorcircuit.applications.vqes" +#: of tensorcircuit.applications.graphdata.reduced_ansatz:5 +msgid "number of edges kept, default half of the edges" msgstr "" -#: of tensorcircuit.applications.vqes:1 -msgid "VQNHE application" +#: of tensorcircuit.applications.graphdata.reduced_ansatz:6 +msgid "The resulted reduced graph" msgstr "" -#: of tensorcircuit.applications.vqes.JointSchedule:1 -msgid "" -"Bases: " -":py:class:`keras.optimizer_v2.learning_rate_schedule.LearningRateSchedule`" +#: of tensorcircuit.applications.graphdata.split_ansatz:1 +msgid "Split the graph in exactly ``split`` piece evenly." msgstr "" -#: of tensorcircuit.applications.vqes.Linear:1 -msgid "Dense layer but with complex weights, used for building complex RBM" +#: of tensorcircuit.applications.graphdata.split_ansatz:3 +msgid "The mother graph" msgstr "" -#: of tensorcircuit.applications.vqes.VQNHE.evaluation:1 -msgid "VQNHE" +#: of tensorcircuit.applications.graphdata.split_ansatz:5 +msgid "The number of the graph we want to divide into, defaults to 2" msgstr "" -#: of tensorcircuit.applications.vqes.VQNHE.evaluation:3 -#: tensorcircuit.applications.vqes.VQNHE.evaluation:5 -#: tensorcircuit.applications.vqes.VQNHE.plain_evaluation:3 -#: tensorcircuit.applications.vqes.VQNHE.plain_evaluation:5 -#: tensorcircuit.backends.jax_backend.JaxBackend.argmax:3 -#: tensorcircuit.backends.jax_backend.JaxBackend.argmax:7 -#: tensorcircuit.backends.jax_backend.JaxBackend.argmin:3 -#: tensorcircuit.backends.jax_backend.JaxBackend.argmin:7 -#: tensorcircuit.backends.jax_backend.JaxBackend.concat:3 -#: tensorcircuit.backends.jax_backend.JaxBackend.cond:3 -#: tensorcircuit.backends.jax_backend.JaxBackend.cond:5 -#: tensorcircuit.backends.jax_backend.JaxBackend.cond:7 -#: tensorcircuit.backends.jax_backend.JaxBackend.cond:9 -#: tensorcircuit.backends.jax_backend.JaxBackend.coo_sparse_matrix:10 -#: tensorcircuit.backends.jax_backend.JaxBackend.cumsum:3 -#: tensorcircuit.backends.jax_backend.JaxBackend.cumsum:8 -#: tensorcircuit.backends.jax_backend.JaxBackend.implicit_randc:3 -#: tensorcircuit.backends.jax_backend.JaxBackend.implicit_randc:11 -#: tensorcircuit.backends.jax_backend.JaxBackend.implicit_randn:11 -#: tensorcircuit.backends.jax_backend.JaxBackend.implicit_randu:11 -#: tensorcircuit.backends.jax_backend.JaxBackend.max:3 -#: tensorcircuit.backends.jax_backend.JaxBackend.max:7 -#: tensorcircuit.backends.jax_backend.JaxBackend.min:3 -#: tensorcircuit.backends.jax_backend.JaxBackend.min:7 -#: tensorcircuit.backends.jax_backend.JaxBackend.random_split:6 -#: tensorcircuit.backends.jax_backend.JaxBackend.random_split:8 -#: tensorcircuit.backends.jax_backend.JaxBackend.scatter:3 -#: tensorcircuit.backends.jax_backend.JaxBackend.scatter:5 -#: tensorcircuit.backends.jax_backend.JaxBackend.scatter:7 -#: tensorcircuit.backends.jax_backend.JaxBackend.scatter:9 -#: tensorcircuit.backends.jax_backend.JaxBackend.sigmoid:3 -#: tensorcircuit.backends.jax_backend.JaxBackend.sigmoid:5 -#: tensorcircuit.backends.jax_backend.JaxBackend.stateful_randc:3 -#: tensorcircuit.backends.jax_backend.JaxBackend.stateful_randc:11 -#: tensorcircuit.backends.jax_backend.JaxBackend.stateful_randn:3 -#: tensorcircuit.backends.jax_backend.JaxBackend.stateful_randn:15 -#: tensorcircuit.backends.jax_backend.JaxBackend.stateful_randu:13 -#: tensorcircuit.backends.jax_backend.JaxBackend.stop_gradient:3 -#: tensorcircuit.backends.jax_backend.JaxBackend.stop_gradient:5 -#: tensorcircuit.backends.jax_backend.JaxBackend.switch:3 -#: tensorcircuit.backends.jax_backend.JaxBackend.switch:5 -#: tensorcircuit.backends.jax_backend.JaxBackend.switch:7 -#: tensorcircuit.backends.jax_backend.JaxBackend.tile:3 -#: tensorcircuit.backends.jax_backend.JaxBackend.tile:7 -#: tensorcircuit.backends.jax_backend.JaxBackend.unique_with_counts:3 -#: tensorcircuit.backends.jax_backend.JaxBackend.vectorized_value_and_grad:29 -#: tensorcircuit.backends.jax_backend.JaxBackend.vectorized_value_and_grad:36 -#: tensorcircuit.backends.numpy_backend.NumpyBackend.argmax:3 -#: tensorcircuit.backends.numpy_backend.NumpyBackend.argmax:7 -#: tensorcircuit.backends.numpy_backend.NumpyBackend.argmin:3 -#: tensorcircuit.backends.numpy_backend.NumpyBackend.argmin:7 -#: tensorcircuit.backends.numpy_backend.NumpyBackend.concat:3 -#: tensorcircuit.backends.numpy_backend.NumpyBackend.cond:3 -#: tensorcircuit.backends.numpy_backend.NumpyBackend.cond:5 -#: tensorcircuit.backends.numpy_backend.NumpyBackend.cond:7 -#: tensorcircuit.backends.numpy_backend.NumpyBackend.cond:9 -#: tensorcircuit.backends.numpy_backend.NumpyBackend.coo_sparse_matrix:10 -#: tensorcircuit.backends.numpy_backend.NumpyBackend.cumsum:3 -#: tensorcircuit.backends.numpy_backend.NumpyBackend.cumsum:8 -#: tensorcircuit.backends.numpy_backend.NumpyBackend.max:3 -#: tensorcircuit.backends.numpy_backend.NumpyBackend.max:7 -#: tensorcircuit.backends.numpy_backend.NumpyBackend.min:3 -#: tensorcircuit.backends.numpy_backend.NumpyBackend.min:7 -#: tensorcircuit.backends.numpy_backend.NumpyBackend.scatter:3 -#: tensorcircuit.backends.numpy_backend.NumpyBackend.scatter:5 -#: tensorcircuit.backends.numpy_backend.NumpyBackend.scatter:7 -#: tensorcircuit.backends.numpy_backend.NumpyBackend.scatter:9 -#: tensorcircuit.backends.numpy_backend.NumpyBackend.sigmoid:3 -#: tensorcircuit.backends.numpy_backend.NumpyBackend.sigmoid:5 -#: tensorcircuit.backends.numpy_backend.NumpyBackend.stateful_randc:3 -#: tensorcircuit.backends.numpy_backend.NumpyBackend.stateful_randc:11 -#: tensorcircuit.backends.numpy_backend.NumpyBackend.stateful_randn:3 -#: tensorcircuit.backends.numpy_backend.NumpyBackend.stateful_randn:15 -#: tensorcircuit.backends.numpy_backend.NumpyBackend.stateful_randu:13 -#: tensorcircuit.backends.numpy_backend.NumpyBackend.stop_gradient:3 -#: tensorcircuit.backends.numpy_backend.NumpyBackend.stop_gradient:5 -#: tensorcircuit.backends.numpy_backend.NumpyBackend.switch:3 -#: tensorcircuit.backends.numpy_backend.NumpyBackend.switch:5 -#: tensorcircuit.backends.numpy_backend.NumpyBackend.switch:7 -#: tensorcircuit.backends.numpy_backend.NumpyBackend.tile:3 -#: tensorcircuit.backends.numpy_backend.NumpyBackend.tile:7 -#: tensorcircuit.backends.numpy_backend.NumpyBackend.unique_with_counts:3 -#: tensorcircuit.backends.numpy_backend.NumpyBackend.vectorized_value_and_grad:29 -#: tensorcircuit.backends.numpy_backend.NumpyBackend.vectorized_value_and_grad:36 -#: tensorcircuit.backends.pytorch_backend.PyTorchBackend.argmax:3 -#: tensorcircuit.backends.pytorch_backend.PyTorchBackend.argmax:7 -#: tensorcircuit.backends.pytorch_backend.PyTorchBackend.argmin:3 -#: tensorcircuit.backends.pytorch_backend.PyTorchBackend.argmin:7 -#: tensorcircuit.backends.pytorch_backend.PyTorchBackend.concat:3 -#: tensorcircuit.backends.pytorch_backend.PyTorchBackend.cond:3 -#: tensorcircuit.backends.pytorch_backend.PyTorchBackend.cond:5 -#: tensorcircuit.backends.pytorch_backend.PyTorchBackend.cond:7 -#: tensorcircuit.backends.pytorch_backend.PyTorchBackend.cond:9 -#: tensorcircuit.backends.pytorch_backend.PyTorchBackend.cumsum:3 -#: tensorcircuit.backends.pytorch_backend.PyTorchBackend.cumsum:8 -#: tensorcircuit.backends.pytorch_backend.PyTorchBackend.max:3 -#: tensorcircuit.backends.pytorch_backend.PyTorchBackend.max:7 -#: tensorcircuit.backends.pytorch_backend.PyTorchBackend.min:3 -#: tensorcircuit.backends.pytorch_backend.PyTorchBackend.min:7 -#: tensorcircuit.backends.pytorch_backend.PyTorchBackend.sigmoid:3 -#: tensorcircuit.backends.pytorch_backend.PyTorchBackend.sigmoid:5 -#: tensorcircuit.backends.pytorch_backend.PyTorchBackend.stop_gradient:3 -#: tensorcircuit.backends.pytorch_backend.PyTorchBackend.stop_gradient:5 -#: tensorcircuit.backends.pytorch_backend.PyTorchBackend.switch:3 -#: tensorcircuit.backends.pytorch_backend.PyTorchBackend.switch:5 -#: tensorcircuit.backends.pytorch_backend.PyTorchBackend.switch:7 -#: tensorcircuit.backends.pytorch_backend.PyTorchBackend.tile:3 -#: tensorcircuit.backends.pytorch_backend.PyTorchBackend.tile:7 -#: tensorcircuit.backends.pytorch_backend.PyTorchBackend.unique_with_counts:3 -#: tensorcircuit.backends.pytorch_backend.PyTorchBackend.vectorized_value_and_grad:29 -#: tensorcircuit.backends.pytorch_backend.PyTorchBackend.vectorized_value_and_grad:36 -#: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.argmax:3 -#: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.argmax:7 -#: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.argmin:3 -#: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.argmin:7 -#: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.concat:3 -#: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.cond:3 -#: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.cond:5 -#: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.cond:7 -#: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.cond:9 -#: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.coo_sparse_matrix:10 -#: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.cumsum:3 -#: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.cumsum:8 -#: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.max:3 -#: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.max:7 -#: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.min:3 -#: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.min:7 -#: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.scatter:3 -#: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.scatter:5 -#: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.scatter:7 -#: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.scatter:9 -#: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.sigmoid:3 -#: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.sigmoid:5 -#: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.stateful_randc:3 -#: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.stateful_randc:11 -#: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.stateful_randn:3 -#: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.stateful_randn:15 -#: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.stateful_randu:13 -#: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.stop_gradient:3 -#: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.stop_gradient:5 -#: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.switch:3 -#: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.switch:5 -#: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.switch:7 -#: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.tile:3 -#: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.tile:7 -#: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.unique_with_counts:3 -#: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.vectorized_value_and_grad:29 -#: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.vectorized_value_and_grad:36 -#: tensorcircuit.simplify.pseudo_contract_between:3 -#: tensorcircuit.simplify.pseudo_contract_between:5 -#: tensorcircuit.simplify.pseudo_contract_between:7 -#: tensorcircuit.templates.graphs.Line1D:3 -#: tensorcircuit.templates.graphs.Line1D:7 -msgid "[description]" +#: of tensorcircuit.applications.graphdata.split_ansatz:7 +msgid "List of graph instance of size ``split``" msgstr "" -#: of tensorcircuit.applications.vqes.VQNHE.plain_evaluation:1 -msgid "VQE" +#: ../../source/api/applications/layers.rst:2 +msgid "tensorcircuit.applications.layers" msgstr "" -#: ../../source/api/backends.rst:2 -msgid "tensorcircuit.backends" +#: of tensorcircuit.applications.layers:1 +msgid "Module for functions adding layers of circuits" msgstr "" -#: ../../source/api/backends/backend_factory.rst:2 -msgid "tensorcircuit.backends.backend_factory" +#: of tensorcircuit.applications.layers.generate_cirq_gate_layer..f:1 +msgid "Hlayer" msgstr "" -#: of tensorcircuit.backends.backend_factory:1 -msgid "Backend register" +#: of +#: tensorcircuit.applications.layers.generate_cirq_any_gate_layer..f:1 +msgid "anyrxlayer" msgstr "" -#: of tensorcircuit.backends.backend_factory.get_backend:1 -msgid "Get the `tc.backend` object." +#: of +#: tensorcircuit.applications.layers.generate_cirq_any_gate_layer..f:1 +msgid "anyrylayer" msgstr "" -#: of tensorcircuit.backends.backend_factory.get_backend:3 -msgid "\"numpy\", \"tensorflow\", \"jax\", \"pytorch\"" +#: of +#: tensorcircuit.applications.layers.generate_cirq_any_gate_layer..f:1 +msgid "anyrzlayer" msgstr "" -#: of tensorcircuit.backends.backend_factory.get_backend -#: tensorcircuit.backends.jax_backend.JaxBackend.tree_map -#: tensorcircuit.backends.pytorch_backend.PyTorchBackend.tree_map -#: tensorcircuit.circuit.Circuit.expectation tensorcircuit.circuit.expectation -#: tensorcircuit.cons.get_contractor tensorcircuit.cons.set_contractor -#: tensorcircuit.gates.bmatrix tensorcircuit.keras.load_func -#: tensorcircuit.mps_base.FiniteMPS.apply_two_site_gate -#: tensorcircuit.mpscircuit.MPSCircuit.apply_general_gate -#: tensorcircuit.quantum.QuOperator.__init__ -#: tensorcircuit.quantum.QuOperator.eval -#: tensorcircuit.quantum.QuOperator.eval_matrix -#: tensorcircuit.quantum.check_spaces -msgid "Raises" +#: of +#: tensorcircuit.applications.layers.generate_cirq_any_double_gate_layer..f:1 +msgid "anyswaplayer" msgstr "" -#: of tensorcircuit.backends.backend_factory.get_backend:5 -msgid "Backend doesn't exist for `backend` argument." +#: of +#: tensorcircuit.applications.layers.generate_cirq_any_double_gate_layer..f:1 +msgid "anyxxlayer" msgstr "" -#: of tensorcircuit.backends.backend_factory.get_backend:6 -#: tensorcircuit.cons.set_tensornetwork_backend:32 -msgid "The `tc.backend` object that with all registered universal functions." +#: of +#: tensorcircuit.applications.layers.generate_cirq_any_double_gate_layer..f:1 +msgid "anyxylayer" msgstr "" -#: ../../source/api/backends/jax_backend.rst:2 -msgid "tensorcircuit.backends.jax_backend" +#: of +#: tensorcircuit.applications.layers.generate_cirq_any_double_gate_layer..f:1 +msgid "anyxzlayer" msgstr "" -#: of tensorcircuit.backends.jax_backend:1 -msgid "Backend magic inherited from tensornetwork: jax backend" +#: of +#: tensorcircuit.applications.layers.generate_cirq_any_double_gate_layer..f:1 +msgid "anyyxlayer" msgstr "" -#: of tensorcircuit.backends.jax_backend.JaxBackend:1 -msgid "Bases: :py:class:`tensornetwork.backends.jax.jax_backend.JaxBackend`" +#: of +#: tensorcircuit.applications.layers.generate_cirq_any_double_gate_layer..f:1 +msgid "anyyylayer" msgstr "" -#: of tensorcircuit.backends.jax_backend.JaxBackend:1 -msgid "" -"See the original backend API at `jax backend " -"`_" +#: of +#: tensorcircuit.applications.layers.generate_cirq_any_double_gate_layer..f:1 +msgid "anyyzlayer" msgstr "" -#: of tensorcircuit.backends.jax_backend.JaxBackend.abs:1 -#: tensorcircuit.backends.numpy_backend.NumpyBackend.abs:1 -#: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.abs:1 -msgid "Returns the elementwise absolute value of tensor. Args:" +#: of +#: tensorcircuit.applications.layers.generate_cirq_any_double_gate_layer..f:1 +msgid "anyzxlayer" msgstr "" -#: of tensorcircuit.backends.jax_backend.JaxBackend.abs:3 -#: tensorcircuit.backends.numpy_backend.NumpyBackend.abs:3 -#: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.abs:3 -msgid "tensor: An input tensor." +#: of +#: tensorcircuit.applications.layers.generate_cirq_any_double_gate_layer..f:1 +msgid "anyzylayer" msgstr "" -#: of tensorcircuit.backends.jax_backend.JaxBackend.abs:5 -#: tensorcircuit.backends.numpy_backend.NumpyBackend.abs:5 -#: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.abs:5 -msgid "tensor: Its elementwise absolute value." +#: of +#: tensorcircuit.applications.layers.generate_cirq_any_double_gate_layer..f:1 +msgid "anyzzlayer" msgstr "" -#: of tensorcircuit.backends.jax_backend.JaxBackend.acos:1 -#: tensorcircuit.backends.jax_backend.JaxBackend.asin:1 -#: tensorcircuit.backends.numpy_backend.NumpyBackend.acos:1 -#: tensorcircuit.backends.numpy_backend.NumpyBackend.asin:1 -#: tensorcircuit.backends.pytorch_backend.PyTorchBackend.acos:1 -#: tensorcircuit.backends.pytorch_backend.PyTorchBackend.asin:1 -#: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.acos:1 -#: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.asin:1 -msgid "Return the acos of a tensor ``a``." +#: of +#: tensorcircuit.applications.layers.generate_cirq_double_gate_layer..f:1 +msgid "cnotlayer" msgstr "" -#: of tensorcircuit.backends.jax_backend.JaxBackend.acos:3 -#: tensorcircuit.backends.jax_backend.JaxBackend.acosh:3 -#: tensorcircuit.backends.jax_backend.JaxBackend.asin:3 -#: tensorcircuit.backends.jax_backend.JaxBackend.asinh:3 -#: tensorcircuit.backends.jax_backend.JaxBackend.atan:3 -#: tensorcircuit.backends.jax_backend.JaxBackend.atan2:3 -#: tensorcircuit.backends.jax_backend.JaxBackend.copy:3 -#: tensorcircuit.backends.jax_backend.JaxBackend.cos:3 -#: tensorcircuit.backends.jax_backend.JaxBackend.cosh:3 -#: tensorcircuit.backends.jax_backend.JaxBackend.eigvalsh:3 -#: tensorcircuit.backends.jax_backend.JaxBackend.expm:3 -#: tensorcircuit.backends.jax_backend.JaxBackend.kron:3 -#: tensorcircuit.backends.jax_backend.JaxBackend.kron:5 -#: tensorcircuit.backends.jax_backend.JaxBackend.numpy:3 -#: tensorcircuit.backends.jax_backend.JaxBackend.sin:3 -#: tensorcircuit.backends.jax_backend.JaxBackend.sinh:3 -#: tensorcircuit.backends.jax_backend.JaxBackend.tan:3 -#: tensorcircuit.backends.jax_backend.JaxBackend.tanh:3 -#: tensorcircuit.backends.numpy_backend.NumpyBackend.acos:3 -#: tensorcircuit.backends.numpy_backend.NumpyBackend.acosh:3 -#: tensorcircuit.backends.numpy_backend.NumpyBackend.asin:3 -#: tensorcircuit.backends.numpy_backend.NumpyBackend.asinh:3 -#: tensorcircuit.backends.numpy_backend.NumpyBackend.atan:3 -#: tensorcircuit.backends.numpy_backend.NumpyBackend.atan2:3 -#: tensorcircuit.backends.numpy_backend.NumpyBackend.copy:3 -#: tensorcircuit.backends.numpy_backend.NumpyBackend.cos:3 -#: tensorcircuit.backends.numpy_backend.NumpyBackend.cosh:3 -#: tensorcircuit.backends.numpy_backend.NumpyBackend.eigvalsh:3 -#: tensorcircuit.backends.numpy_backend.NumpyBackend.expm:3 -#: tensorcircuit.backends.numpy_backend.NumpyBackend.kron:3 -#: tensorcircuit.backends.numpy_backend.NumpyBackend.kron:5 -#: tensorcircuit.backends.numpy_backend.NumpyBackend.numpy:3 -#: tensorcircuit.backends.numpy_backend.NumpyBackend.sin:3 -#: tensorcircuit.backends.numpy_backend.NumpyBackend.sinh:3 -#: tensorcircuit.backends.numpy_backend.NumpyBackend.tan:3 -#: tensorcircuit.backends.numpy_backend.NumpyBackend.tanh:3 -#: tensorcircuit.backends.pytorch_backend.PyTorchBackend.acos:3 -#: tensorcircuit.backends.pytorch_backend.PyTorchBackend.acosh:3 -#: tensorcircuit.backends.pytorch_backend.PyTorchBackend.asin:3 -#: tensorcircuit.backends.pytorch_backend.PyTorchBackend.asinh:3 -#: tensorcircuit.backends.pytorch_backend.PyTorchBackend.atan:3 -#: tensorcircuit.backends.pytorch_backend.PyTorchBackend.atan2:3 -#: tensorcircuit.backends.pytorch_backend.PyTorchBackend.copy:3 -#: tensorcircuit.backends.pytorch_backend.PyTorchBackend.cos:3 -#: tensorcircuit.backends.pytorch_backend.PyTorchBackend.cosh:3 -#: tensorcircuit.backends.pytorch_backend.PyTorchBackend.eigvalsh:3 -#: tensorcircuit.backends.pytorch_backend.PyTorchBackend.expm:3 -#: tensorcircuit.backends.pytorch_backend.PyTorchBackend.kron:3 -#: tensorcircuit.backends.pytorch_backend.PyTorchBackend.kron:5 -#: tensorcircuit.backends.pytorch_backend.PyTorchBackend.numpy:3 -#: tensorcircuit.backends.pytorch_backend.PyTorchBackend.sin:3 -#: tensorcircuit.backends.pytorch_backend.PyTorchBackend.sinh:3 -#: tensorcircuit.backends.pytorch_backend.PyTorchBackend.tan:3 -#: tensorcircuit.backends.pytorch_backend.PyTorchBackend.tanh:3 -#: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.acos:3 -#: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.acosh:3 -#: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.asin:3 -#: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.asinh:3 -#: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.atan:3 -#: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.atan2:3 -#: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.copy:3 -#: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.cos:3 -#: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.cosh:3 -#: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.eigvalsh:3 -#: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.expm:3 -#: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.kron:3 -#: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.kron:5 -#: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.numpy:3 -#: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.sin:3 -#: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.sinh:3 -#: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.tan:3 -#: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.tanh:3 -msgid "tensor in matrix form" +#: of tensorcircuit.applications.layers.generate_cirq_gate_layer..f:1 +msgid "rxlayer" msgstr "" -#: of tensorcircuit.backends.jax_backend.JaxBackend.acos:5 -#: tensorcircuit.backends.numpy_backend.NumpyBackend.acos:5 -#: tensorcircuit.backends.pytorch_backend.PyTorchBackend.acos:5 -#: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.acos:5 -msgid "acos of ``a``" +#: of tensorcircuit.applications.layers.generate_cirq_gate_layer..f:1 +msgid "rylayer" msgstr "" -#: of tensorcircuit.backends.jax_backend.JaxBackend.acosh:1 -#: tensorcircuit.backends.numpy_backend.NumpyBackend.acosh:1 -#: tensorcircuit.backends.pytorch_backend.PyTorchBackend.acosh:1 -#: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.acosh:1 -msgid "Return the acosh of a tensor ``a``." +#: of tensorcircuit.applications.layers.generate_cirq_gate_layer..f:1 +msgid "rzlayer" msgstr "" -#: of tensorcircuit.backends.jax_backend.JaxBackend.acosh:5 -#: tensorcircuit.backends.numpy_backend.NumpyBackend.acosh:5 -#: tensorcircuit.backends.pytorch_backend.PyTorchBackend.acosh:5 -#: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.acosh:5 -msgid "acosh of ``a``" +#: of +#: tensorcircuit.applications.layers.generate_cirq_double_gate_layer..f:1 +msgid "swaplayer" msgstr "" -#: of tensorcircuit.backends.jax_backend.JaxBackend.arange:1 -#: tensorcircuit.backends.numpy_backend.NumpyBackend.arange:1 -#: tensorcircuit.backends.pytorch_backend.PyTorchBackend.arange:1 -#: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.arange:1 -msgid "Values are generated within the half-open interval [start, stop)" +#: of tensorcircuit.applications.layers.generate_cirq_double_gate..f:1 +msgid "xxgate" msgstr "" -#: of tensorcircuit.backends.jax_backend.JaxBackend.arange:3 -#: tensorcircuit.backends.numpy_backend.NumpyBackend.arange:3 -#: tensorcircuit.backends.pytorch_backend.PyTorchBackend.arange:3 -#: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.arange:3 -msgid "start index" +#: of +#: tensorcircuit.applications.layers.generate_cirq_double_gate_layer..f:1 +msgid "xxlayer" msgstr "" -#: of tensorcircuit.backends.jax_backend.JaxBackend.arange:5 -#: tensorcircuit.backends.numpy_backend.NumpyBackend.arange:5 -#: tensorcircuit.backends.pytorch_backend.PyTorchBackend.arange:5 -#: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.arange:5 -msgid "end index, defaults to None" +#: of tensorcircuit.applications.layers.generate_cirq_double_gate..f:1 +msgid "xygate" msgstr "" -#: of tensorcircuit.backends.jax_backend.JaxBackend.arange:7 -#: tensorcircuit.backends.numpy_backend.NumpyBackend.arange:7 -#: tensorcircuit.backends.pytorch_backend.PyTorchBackend.arange:7 -#: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.arange:7 -msgid "steps, defaults to 1" +#: of +#: tensorcircuit.applications.layers.generate_cirq_double_gate_layer..f:1 +msgid "xylayer" msgstr "" -#: of tensorcircuit.backends.jax_backend.JaxBackend.arange:9 -#: tensorcircuit.backends.jax_backend.JaxBackend.mean:9 -#: tensorcircuit.backends.numpy_backend.NumpyBackend.arange:9 -#: tensorcircuit.backends.numpy_backend.NumpyBackend.mean:9 -#: tensorcircuit.backends.pytorch_backend.PyTorchBackend.arange:9 -#: tensorcircuit.backends.pytorch_backend.PyTorchBackend.mean:9 -#: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.arange:9 -#: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.mean:9 -#: tensorcircuit.cons.runtime_contractor:3 -#: tensorcircuit.cons.set_function_contractor:3 -#: tensorcircuit.templates.graphs.Grid2DCoord.lattice_graph:6 -#: tensorcircuit.utils.benchmark:3 tensorcircuit.utils.benchmark:9 -msgid "_description_" +#: of tensorcircuit.applications.layers.generate_cirq_double_gate..f:1 +msgid "xzgate" msgstr "" -#: of tensorcircuit.backends.jax_backend.JaxBackend.argmax:1 -#: tensorcircuit.backends.numpy_backend.NumpyBackend.argmax:1 -#: tensorcircuit.backends.pytorch_backend.PyTorchBackend.argmax:1 -#: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.argmax:1 -msgid "Return the index of maximum of an array an axis." +#: of +#: tensorcircuit.applications.layers.generate_cirq_double_gate_layer..f:1 +msgid "xzlayer" msgstr "" -#: of tensorcircuit.backends.jax_backend.JaxBackend.argmax:5 -#: tensorcircuit.backends.jax_backend.JaxBackend.argmin:5 -#: tensorcircuit.backends.numpy_backend.NumpyBackend.argmax:5 -#: tensorcircuit.backends.numpy_backend.NumpyBackend.argmin:5 -#: tensorcircuit.backends.pytorch_backend.PyTorchBackend.argmax:5 -#: tensorcircuit.backends.pytorch_backend.PyTorchBackend.argmin:5 -#: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.argmax:5 -#: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.argmin:5 -msgid "[description], defaults to 0, different behavior from numpy defaults!" +#: of tensorcircuit.applications.layers.generate_cirq_double_gate..f:1 +msgid "yxgate" msgstr "" -#: of tensorcircuit.backends.jax_backend.JaxBackend.argmin:1 -#: tensorcircuit.backends.numpy_backend.NumpyBackend.argmin:1 -#: tensorcircuit.backends.pytorch_backend.PyTorchBackend.argmin:1 -#: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.argmin:1 -msgid "Return the index of minimum of an array an axis." +#: of +#: tensorcircuit.applications.layers.generate_cirq_double_gate_layer..f:1 +msgid "yxlayer" msgstr "" -#: of tensorcircuit.backends.jax_backend.JaxBackend.asin:5 -#: tensorcircuit.backends.numpy_backend.NumpyBackend.asin:5 -#: tensorcircuit.backends.pytorch_backend.PyTorchBackend.asin:5 -#: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.asin:5 -msgid "asin of ``a``" +#: of tensorcircuit.applications.layers.generate_cirq_double_gate..f:1 +msgid "yygate" msgstr "" -#: of tensorcircuit.backends.jax_backend.JaxBackend.asinh:1 -#: tensorcircuit.backends.numpy_backend.NumpyBackend.asinh:1 -#: tensorcircuit.backends.pytorch_backend.PyTorchBackend.asinh:1 -#: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.asinh:1 -msgid "Return the asinh of a tensor ``a``." +#: of +#: tensorcircuit.applications.layers.generate_cirq_double_gate_layer..f:1 +msgid "yylayer" msgstr "" -#: of tensorcircuit.backends.jax_backend.JaxBackend.asinh:5 -#: tensorcircuit.backends.numpy_backend.NumpyBackend.asinh:5 -#: tensorcircuit.backends.pytorch_backend.PyTorchBackend.asinh:5 -#: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.asinh:5 -msgid "asinh of ``a``" +#: of tensorcircuit.applications.layers.generate_cirq_double_gate..f:1 +msgid "yzgate" msgstr "" -#: of tensorcircuit.backends.jax_backend.JaxBackend.atan:1 -#: tensorcircuit.backends.numpy_backend.NumpyBackend.atan:1 -#: tensorcircuit.backends.pytorch_backend.PyTorchBackend.atan:1 -#: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.atan:1 -msgid "Return the atan of a tensor ``a``." +#: of +#: tensorcircuit.applications.layers.generate_cirq_double_gate_layer..f:1 +msgid "yzlayer" msgstr "" -#: of tensorcircuit.backends.jax_backend.JaxBackend.atan:5 -#: tensorcircuit.backends.numpy_backend.NumpyBackend.atan:5 -#: tensorcircuit.backends.pytorch_backend.PyTorchBackend.atan:5 -#: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.atan:5 -msgid "atan of ``a``" +#: of tensorcircuit.applications.layers.generate_cirq_double_gate..f:1 +msgid "zxgate" msgstr "" -#: of tensorcircuit.backends.jax_backend.JaxBackend.atan2:1 -#: tensorcircuit.backends.numpy_backend.NumpyBackend.atan2:1 -#: tensorcircuit.backends.pytorch_backend.PyTorchBackend.atan2:1 -#: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.atan2:1 -msgid "Return the atan of a tensor ``y``/``x``." +#: of +#: tensorcircuit.applications.layers.generate_cirq_double_gate_layer..f:1 +msgid "zxlayer" msgstr "" -#: of tensorcircuit.backends.jax_backend.JaxBackend.atan2:5 -#: tensorcircuit.backends.numpy_backend.NumpyBackend.atan2:5 -#: tensorcircuit.backends.pytorch_backend.PyTorchBackend.atan2:5 -#: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.atan2:5 -msgid "atan2 of ``a``" +#: of tensorcircuit.applications.layers.generate_cirq_double_gate..f:1 +msgid "zygate" msgstr "" -#: of tensorcircuit.backends.jax_backend.JaxBackend.cast:1 -#: tensorcircuit.backends.numpy_backend.NumpyBackend.cast:1 -#: tensorcircuit.backends.pytorch_backend.PyTorchBackend.cast:1 -#: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.cast:1 -msgid "Cast the tensor dtype of a ``a``." +#: of +#: tensorcircuit.applications.layers.generate_cirq_double_gate_layer..f:1 +msgid "zylayer" msgstr "" -#: of tensorcircuit.backends.jax_backend.JaxBackend.cast:3 -#: tensorcircuit.backends.jax_backend.JaxBackend.imag:3 -#: tensorcircuit.backends.jax_backend.JaxBackend.real:3 -#: tensorcircuit.backends.jax_backend.JaxBackend.size:3 -#: tensorcircuit.backends.numpy_backend.NumpyBackend.cast:3 -#: tensorcircuit.backends.numpy_backend.NumpyBackend.imag:3 -#: tensorcircuit.backends.numpy_backend.NumpyBackend.real:3 -#: tensorcircuit.backends.numpy_backend.NumpyBackend.size:3 -#: tensorcircuit.backends.pytorch_backend.PyTorchBackend.cast:3 -#: tensorcircuit.backends.pytorch_backend.PyTorchBackend.imag:3 -#: tensorcircuit.backends.pytorch_backend.PyTorchBackend.real:3 -#: tensorcircuit.backends.pytorch_backend.PyTorchBackend.size:3 -#: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.cast:3 -#: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.imag:3 -#: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.real:3 -#: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.size:3 -msgid "tensor" +#: of tensorcircuit.applications.layers.generate_cirq_double_gate..f:1 +msgid "zzgate" msgstr "" -#: of tensorcircuit.backends.jax_backend.JaxBackend.cast:5 -#: tensorcircuit.backends.numpy_backend.NumpyBackend.cast:5 -#: tensorcircuit.backends.pytorch_backend.PyTorchBackend.cast:5 -#: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.cast:5 -msgid "\"float32\", \"float64\", \"complex64\", \"complex128\"" +#: of +#: tensorcircuit.applications.layers.generate_cirq_double_gate_layer..f:1 +msgid "zzlayer" msgstr "" -#: of tensorcircuit.backends.jax_backend.JaxBackend.cast:7 -#: tensorcircuit.backends.numpy_backend.NumpyBackend.cast:7 -#: tensorcircuit.backends.pytorch_backend.PyTorchBackend.cast:7 -#: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.cast:7 -msgid "``a`` of new dtype" +#: of tensorcircuit.applications.layers.generate_any_gate_layer:1 +msgid "$$e^{-i\\theta_i \\sigma}$$" msgstr "" -#: of tensorcircuit.backends.jax_backend.JaxBackend.concat:1 -#: tensorcircuit.backends.numpy_backend.NumpyBackend.concat:1 -#: tensorcircuit.backends.pytorch_backend.PyTorchBackend.concat:1 -#: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.concat:1 -msgid "Join a sequence of arrays along an existing axis." +#: of tensorcircuit.applications.layers.generate_cirq_any_double_gate_layer:1 +msgid "" +"The following function should be used to generate layers with special " +"case. As its soundness depends on the nature of the task or problem, it " +"doesn't always make sense." msgstr "" -#: of tensorcircuit.backends.jax_backend.JaxBackend.concat:5 -#: tensorcircuit.backends.jax_backend.JaxBackend.implicit_randn:5 -#: tensorcircuit.backends.jax_backend.JaxBackend.implicit_randu:5 -#: tensorcircuit.backends.jax_backend.JaxBackend.stateful_randn:9 -#: tensorcircuit.backends.jax_backend.JaxBackend.stateful_randu:7 -#: tensorcircuit.backends.jax_backend.JaxBackend.vectorized_value_and_grad:31 -#: tensorcircuit.backends.numpy_backend.NumpyBackend.concat:5 -#: tensorcircuit.backends.numpy_backend.NumpyBackend.stateful_randn:9 -#: tensorcircuit.backends.numpy_backend.NumpyBackend.stateful_randu:7 -#: tensorcircuit.backends.numpy_backend.NumpyBackend.vectorized_value_and_grad:31 -#: tensorcircuit.backends.pytorch_backend.PyTorchBackend.concat:5 -#: tensorcircuit.backends.pytorch_backend.PyTorchBackend.vectorized_value_and_grad:31 -#: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.concat:5 -#: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.stateful_randn:9 -#: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.stateful_randu:7 -#: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.vectorized_value_and_grad:31 -msgid "[description], defaults to 0" +#: of tensorcircuit.applications.layers.generate_cirq_any_gate_layer:1 +#: tensorcircuit.applications.layers.generate_cirq_gate_layer:1 +#: tensorcircuit.applications.layers.generate_gate_layer:1 +msgid "$$e^{-i\\theta \\sigma}$$" msgstr "" -#: of tensorcircuit.backends.jax_backend.JaxBackend.cond:1 -#: tensorcircuit.backends.numpy_backend.NumpyBackend.cond:1 -#: tensorcircuit.backends.pytorch_backend.PyTorchBackend.cond:1 -#: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.cond:1 +#: ../../source/api/applications/utils.rst:2 +msgid "tensorcircuit.applications.utils" +msgstr "" + +#: of tensorcircuit.applications.utils:1 msgid "" -"The native cond for XLA compiling, wrapper for ``tf.cond`` and limited " -"functionality of ``jax.lax.cond``." +"A collection of useful function snippets that irrelevant with the main " +"modules or await for further refactor" msgstr "" -#: of tensorcircuit.backends.jax_backend.JaxBackend.convert_to_tensor:1 -msgid "Convert a np.array or a tensor to a tensor type for the backend." +#: of tensorcircuit.applications.utils.color_svg:1 +msgid "color cirq circuit SVG for given gates, a small tool to hack the cirq SVG" msgstr "" -#: of tensorcircuit.backends.jax_backend.JaxBackend.coo_sparse_matrix:1 -#: tensorcircuit.backends.numpy_backend.NumpyBackend.coo_sparse_matrix:1 -#: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.coo_sparse_matrix:1 -msgid "" -"Generate the coo format sparse matrix from indices and values, which is " -"the only sparse format supported in different ML backends." +#: of tensorcircuit.applications.utils.color_svg:5 +msgid "integer coordinate which gate is colored" msgstr "" -#: of tensorcircuit.backends.jax_backend.JaxBackend.coo_sparse_matrix:4 -#: tensorcircuit.backends.numpy_backend.NumpyBackend.coo_sparse_matrix:4 -#: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.coo_sparse_matrix:4 -msgid "shape [n, 2] for n non zero values in the returned matrix" +#: of tensorcircuit.applications.utils.repr2array:1 +msgid "transform repr form of an array to real numpy array" msgstr "" -#: of tensorcircuit.backends.jax_backend.JaxBackend.coo_sparse_matrix:6 -#: tensorcircuit.backends.numpy_backend.NumpyBackend.coo_sparse_matrix:6 -#: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.coo_sparse_matrix:6 -msgid "shape [n]" +#: ../../source/api/applications/vags.rst:2 +msgid "tensorcircuit.applications.vags" msgstr "" -#: of tensorcircuit.backends.jax_backend.JaxBackend.coo_sparse_matrix:8 -#: tensorcircuit.backends.numpy_backend.NumpyBackend.coo_sparse_matrix:8 -#: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.coo_sparse_matrix:8 -msgid "Tuple[int, ...]" +#: of tensorcircuit.applications.vags:1 +msgid "DQAS application kernels as vag functions" msgstr "" -#: of tensorcircuit.backends.jax_backend.JaxBackend.copy:1 -#: tensorcircuit.backends.numpy_backend.NumpyBackend.copy:1 -#: tensorcircuit.backends.pytorch_backend.PyTorchBackend.copy:1 -#: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.copy:1 -msgid "Return the expm of ``a``, matrix exponential." +#: of tensorcircuit.applications.vags.ave_func:1 +msgid "1D array for full wavefunction, the basis is in lexcical order" msgstr "" -#: of tensorcircuit.backends.jax_backend.JaxBackend.copy:5 -#: tensorcircuit.backends.jax_backend.JaxBackend.expm:5 -#: tensorcircuit.backends.numpy_backend.NumpyBackend.copy:5 -#: tensorcircuit.backends.numpy_backend.NumpyBackend.expm:5 -#: tensorcircuit.backends.pytorch_backend.PyTorchBackend.copy:5 -#: tensorcircuit.backends.pytorch_backend.PyTorchBackend.expm:5 -#: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.copy:5 -#: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.expm:5 -msgid "matrix exponential of matrix ``a``" +#: of tensorcircuit.applications.vags.ave_func:2 +#: tensorcircuit.applications.vags.energy:5 +msgid "nx.Graph" msgstr "" -#: of tensorcircuit.backends.jax_backend.JaxBackend.cos:1 -#: tensorcircuit.backends.numpy_backend.NumpyBackend.cos:1 -#: tensorcircuit.backends.pytorch_backend.PyTorchBackend.cos:1 -#: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.cos:1 -msgid "Return the cosine of a tensor ``a``." +#: of tensorcircuit.applications.vags.ave_func:3 +msgid "transformation functions before averaged" msgstr "" -#: of tensorcircuit.backends.jax_backend.JaxBackend.cos:5 -#: tensorcircuit.backends.numpy_backend.NumpyBackend.cos:5 -#: tensorcircuit.backends.pytorch_backend.PyTorchBackend.cos:5 -#: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.cos:5 -msgid "cosine of ``a``" +#: of tensorcircuit.applications.vags.cvar:1 +msgid "as f3" msgstr "" -#: of tensorcircuit.backends.jax_backend.JaxBackend.cosh:1 -#: tensorcircuit.backends.numpy_backend.NumpyBackend.cosh:1 -#: tensorcircuit.backends.pytorch_backend.PyTorchBackend.cosh:1 -#: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.cosh:1 -msgid "Return the cosh of a tensor ``a``." +#: of tensorcircuit.applications.vags.energy:1 +msgid "maxcut energy for n qubit wavefunction i-th basis" msgstr "" -#: of tensorcircuit.backends.jax_backend.JaxBackend.cosh:5 -#: tensorcircuit.backends.numpy_backend.NumpyBackend.cosh:5 -#: tensorcircuit.backends.pytorch_backend.PyTorchBackend.cosh:5 -#: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.cosh:5 -msgid "cosh of ``a``" +#: of tensorcircuit.applications.vags.energy:3 +msgid "ranged from 0 to 2**n-1" msgstr "" -#: of tensorcircuit.backends.jax_backend.JaxBackend.cumsum:1 -#: tensorcircuit.backends.numpy_backend.NumpyBackend.cumsum:1 -#: tensorcircuit.backends.pytorch_backend.PyTorchBackend.cumsum:1 -#: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.cumsum:1 -msgid "Return the cumulative sum of the elements along a given axis." +#: of tensorcircuit.applications.vags.energy:4 +#: tensorcircuit.applications.vags.unitary_design:4 +#: tensorcircuit.quantum.correlation_from_samples:8 +#: tensorcircuit.quantum.count_s2d:6 tensorcircuit.quantum.count_tuple2dict:5 +#: tensorcircuit.quantum.count_vector2dict:5 tensorcircuit.quantum.sample2all:5 +#: tensorcircuit.quantum.sample_bin2int:5 +#: tensorcircuit.quantum.sample_int2bin:5 +msgid "number of qubits" msgstr "" -#: of tensorcircuit.backends.jax_backend.JaxBackend.cumsum:5 -#: tensorcircuit.backends.numpy_backend.NumpyBackend.cumsum:5 -#: tensorcircuit.backends.pytorch_backend.PyTorchBackend.cumsum:5 -#: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.cumsum:5 +#: of tensorcircuit.applications.vags.entanglement_entropy:1 msgid "" -"The default behavior is the same as numpy, different from tf/torch as " -"cumsum of the flatten 1D array, defaults to None" +"deprecated as non tf and non flexible, use the combination of " +"``reduced_density_matrix`` and ``entropy`` instead." msgstr "" -#: of tensorcircuit.backends.jax_backend.JaxBackend.eigvalsh:1 -#: tensorcircuit.backends.numpy_backend.NumpyBackend.eigvalsh:1 -#: tensorcircuit.backends.pytorch_backend.PyTorchBackend.eigvalsh:1 -#: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.eigvalsh:1 -msgid "Get the eigenvalues of matrix ``a``." +#: of tensorcircuit.applications.vags.entropy:1 +#: tensorcircuit.applications.vags.reduced_density_matrix:1 +msgid "deprecated, current version in tc.quantum" msgstr "" -#: of tensorcircuit.backends.jax_backend.JaxBackend.eigvalsh:5 -#: tensorcircuit.backends.numpy_backend.NumpyBackend.eigvalsh:5 -#: tensorcircuit.backends.pytorch_backend.PyTorchBackend.eigvalsh:5 -#: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.eigvalsh:5 -msgid "eigenvalues of ``a``" +#: of tensorcircuit.applications.vags.evaluate_vag:1 +msgid "" +"value and gradient, currently only tensorflow backend is supported jax " +"and numpy seems to be slow in circuit simulation anyhow. *deprecated*" msgstr "" -#: of tensorcircuit.backends.jax_backend.JaxBackend.expm:1 -#: tensorcircuit.backends.numpy_backend.NumpyBackend.expm:1 -#: tensorcircuit.backends.pytorch_backend.PyTorchBackend.expm:1 -#: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.expm:1 -msgid "Return the copy of tensor ''a''." +#: of tensorcircuit.applications.vags.evaluate_vag:8 +msgid "if lbd=0, take energy as objective" msgstr "" -#: of tensorcircuit.backends.jax_backend.JaxBackend.eye:4 -#: tensorcircuit.backends.numpy_backend.NumpyBackend.eye:4 -#: tensorcircuit.backends.pytorch_backend.PyTorchBackend.eye:4 -#: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.eye:4 -msgid "Return an identity matrix of dimension `dim`" +#: of tensorcircuit.applications.vags.evaluate_vag:9 +msgid "if as default 0, overlap will not compute in the process" msgstr "" -#: of tensorcircuit.backends.jax_backend.JaxBackend.eye:2 -#: tensorcircuit.backends.numpy_backend.NumpyBackend.eye:2 -#: tensorcircuit.backends.pytorch_backend.PyTorchBackend.eye:2 -#: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.eye:2 -msgid "" -"Depending on specific backends, `dim` has to be either an int (numpy, " -"torch, tensorflow) or a `ShapeType` object (for block-sparse backends). " -"Block-sparse behavior is currently not supported" +#: of tensorcircuit.applications.vags.gapfilling:1 +msgid "Fill single qubit gates according to placeholder on circuit" msgstr "" -#: of tensorcircuit.backends.jax_backend.JaxBackend.eye:7 -#: tensorcircuit.backends.numpy_backend.NumpyBackend.eye:7 -#: tensorcircuit.backends.pytorch_backend.PyTorchBackend.eye:7 -#: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.eye:7 -msgid "" -"N (int): The dimension of the returned matrix. dtype: The dtype of the " -"returned matrix. M (int): The dimension of the returned matrix." +#: of tensorcircuit.applications.vags.heisenberg_measurements:1 +msgid "Hamiltonian measurements for Heisenberg model on graph lattice g" msgstr "" -#: of tensorcircuit.backends.jax_backend.JaxBackend.grad:1 -#: tensorcircuit.backends.numpy_backend.NumpyBackend.grad:1 -#: tensorcircuit.backends.pytorch_backend.PyTorchBackend.grad:1 -#: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.grad:1 -msgid "Return the function which is the grad function of input ``f``." +#: of tensorcircuit.applications.vags.q:1 +msgid "short cut for ``cirq.LineQubit(i)``" msgstr "" -#: of tensorcircuit.backends.jax_backend.JaxBackend.grad -#: tensorcircuit.backends.jax_backend.JaxBackend.value_and_grad -#: tensorcircuit.backends.numpy_backend.NumpyBackend.grad -#: tensorcircuit.backends.numpy_backend.NumpyBackend.value_and_grad -#: tensorcircuit.backends.pytorch_backend.PyTorchBackend.grad -#: tensorcircuit.backends.pytorch_backend.PyTorchBackend.value_and_grad -#: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.grad -#: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.value_and_grad -#: tensorcircuit.channels.amplitudedampingchannel -#: tensorcircuit.channels.depolarizingchannel -#: tensorcircuit.channels.phasedampingchannel -#: tensorcircuit.channels.resetchannel tensorcircuit.circuit.Circuit.amplitude -#: tensorcircuit.circuit.Circuit.append_from_qir -#: tensorcircuit.circuit.Circuit.apply_double_gate -#: tensorcircuit.circuit.Circuit.apply_single_gate -#: tensorcircuit.circuit.Circuit.cond_measurement -#: tensorcircuit.circuit.Circuit.draw tensorcircuit.circuit.Circuit.expectation -#: tensorcircuit.circuit.Circuit.from_qir -#: tensorcircuit.circuit.Circuit.from_qiskit -#: tensorcircuit.circuit.Circuit.measure_reference -#: tensorcircuit.circuit.Circuit.replace_mps_inputs -#: tensorcircuit.circuit.Circuit.to_qir tensorcircuit.circuit._expectation_ps -#: tensorcircuit.cons.set_tensornetwork_backend tensorcircuit.gates.bmatrix -#: tensorcircuit.gates.matrix_for_gate tensorcircuit.gates.num_to_tensor -#: tensorcircuit.interfaces.scipy_optimize_interface -#: tensorcircuit.interfaces.torch_interface tensorcircuit.keras.load_func -#: tensorcircuit.keras.save_func -#: tensorcircuit.quantum.QuAdjointVector.from_tensor -#: tensorcircuit.quantum.QuOperator.from_tensor -#: tensorcircuit.quantum.QuOperator.tensor_product -#: tensorcircuit.quantum.QuScalar.from_tensor -#: tensorcircuit.quantum.QuVector.from_tensor -#: tensorcircuit.quantum.correlation_from_counts tensorcircuit.quantum.entropy -#: tensorcircuit.quantum.free_energy -#: tensorcircuit.quantum.heisenberg_hamiltonian tensorcircuit.quantum.identity -#: tensorcircuit.quantum.measurement_counts -#: tensorcircuit.quantum.quantum_constructor -#: tensorcircuit.quantum.renyi_free_energy tensorcircuit.quantum.spin_by_basis -#: tensorcircuit.quantum.trace_product tensorcircuit.simplify.infer_new_shape -#: tensorcircuit.torchnn.QuantumNet.__init__ -#: tensorcircuit.translation.qir2qiskit tensorcircuit.translation.qiskit2tc -#: tensorcircuit.utils.append tensorcircuit.utils.return_partial -#: tensorcircuit.vis.gate_name_trans tensorcircuit.vis.qir2tex -#: tensorcircuit.vis.render_pdf -msgid "Example" +#: of tensorcircuit.applications.vags.qaoa_block_vag:1 +#: tensorcircuit.applications.vags.qaoa_block_vag_energy:1 +msgid "QAOA block encoding kernel, support 2 params in one op" msgstr "" -#: of tensorcircuit.backends.jax_backend.JaxBackend.grad:13 -#: tensorcircuit.backends.jax_backend.JaxBackend.value_and_grad:13 -#: tensorcircuit.backends.numpy_backend.NumpyBackend.grad:13 -#: tensorcircuit.backends.numpy_backend.NumpyBackend.value_and_grad:13 -#: tensorcircuit.backends.pytorch_backend.PyTorchBackend.grad:13 -#: tensorcircuit.backends.pytorch_backend.PyTorchBackend.value_and_grad:13 -#: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.grad:13 -#: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.value_and_grad:13 -msgid "the function to be differentiated" +#: of tensorcircuit.applications.vags.qaoa_train:1 +msgid "" +"training QAOA with only optimizing circuit parameters, can be well " +"replaced with more general function `DQAS_search`" msgstr "" -#: of tensorcircuit.backends.jax_backend.JaxBackend.grad:15 -#: tensorcircuit.backends.jax_backend.JaxBackend.value_and_grad:15 -#: tensorcircuit.backends.numpy_backend.NumpyBackend.grad:15 -#: tensorcircuit.backends.numpy_backend.NumpyBackend.value_and_grad:15 -#: tensorcircuit.backends.pytorch_backend.PyTorchBackend.grad:15 -#: tensorcircuit.backends.pytorch_backend.PyTorchBackend.value_and_grad:15 -#: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.grad:15 -#: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.value_and_grad:15 -msgid "" -"the position of args in ``f`` that are to be differentiated, defaults to " -"be 0" +#: of tensorcircuit.applications.vags.quantum_mp_qaoa_vag:1 +msgid "multi parameter for one layer" msgstr "" -#: of tensorcircuit.backends.jax_backend.JaxBackend.grad:17 -#: tensorcircuit.backends.numpy_backend.NumpyBackend.grad:17 -#: tensorcircuit.backends.pytorch_backend.PyTorchBackend.grad:17 -#: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.grad:17 -msgid "the grad function of ``f`` with the same set of arguments as ``f``" +#: of tensorcircuit.applications.vags.quantum_mp_qaoa_vag:7 +#: tensorcircuit.applications.vags.quantum_qaoa_vag:7 +msgid "kw arguments for measurements_func" msgstr "" -#: of tensorcircuit.backends.jax_backend.JaxBackend.i:1 -#: tensorcircuit.backends.numpy_backend.NumpyBackend.i:1 -#: tensorcircuit.backends.pytorch_backend.PyTorchBackend.i:1 -#: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.i:1 -msgid "Return 1.j in as a tensor compatible with the backend." +#: of tensorcircuit.applications.vags.quantum_mp_qaoa_vag:8 +msgid "loss function, gradient of nnp" msgstr "" -#: of tensorcircuit.backends.jax_backend.JaxBackend.i:3 -#: tensorcircuit.backends.numpy_backend.NumpyBackend.i:3 -#: tensorcircuit.backends.pytorch_backend.PyTorchBackend.i:3 -#: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.i:3 -msgid "\"complex64\" or \"complex128\"" +#: of tensorcircuit.applications.vags.quantum_qaoa_vag:1 +msgid "" +"tensorflow quantum backend compare to qaoa_vag which is tensorcircuit " +"backend" msgstr "" -#: of tensorcircuit.backends.jax_backend.JaxBackend.i:5 -#: tensorcircuit.backends.numpy_backend.NumpyBackend.i:5 -#: tensorcircuit.backends.pytorch_backend.PyTorchBackend.i:5 -#: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.i:5 -msgid "1.j tensor" +#: of tensorcircuit.applications.vags.tfim_measurements:1 +msgid "Hamiltonian for tfim on lattice defined by graph g" msgstr "" -#: of tensorcircuit.backends.jax_backend.JaxBackend.imag:1 -#: tensorcircuit.backends.numpy_backend.NumpyBackend.imag:1 -#: tensorcircuit.backends.pytorch_backend.PyTorchBackend.imag:1 -#: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.imag:1 -msgid "Return the elementwise imaginary value of a tensor ``a``." +#: of tensorcircuit.applications.vags.tfim_measurements:8 +msgid "cirq.PauliSum as operators for tfq expectation layer" msgstr "" -#: of tensorcircuit.backends.jax_backend.JaxBackend.imag:5 -#: tensorcircuit.backends.numpy_backend.NumpyBackend.imag:5 -#: tensorcircuit.backends.pytorch_backend.PyTorchBackend.imag:5 -#: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.imag:5 -msgid "imaginary value of ``a``" +#: of tensorcircuit.applications.vags.unitary_design:1 +msgid "" +"generate random wavefunction from approximately Haar measure, reference:" +" https://doi.org/10.1063/1.4983266" msgstr "" -#: of tensorcircuit.backends.jax_backend.JaxBackend.implicit_randc:1 -#: tensorcircuit.backends.jax_backend.JaxBackend.stateful_randc:1 -#: tensorcircuit.backends.jax_backend.JaxBackend.stateful_randn:1 -#: tensorcircuit.backends.numpy_backend.NumpyBackend.stateful_randc:1 -#: tensorcircuit.backends.numpy_backend.NumpyBackend.stateful_randn:1 -#: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.stateful_randc:1 -#: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.stateful_randn:1 -msgid "[summary]" +#: of tensorcircuit.applications.vags.unitary_design:5 +msgid "repetition of the blocks" msgstr "" -#: of tensorcircuit.backends.jax_backend.JaxBackend.implicit_randc:5 -#: tensorcircuit.backends.jax_backend.JaxBackend.stateful_randc:5 -#: tensorcircuit.backends.numpy_backend.NumpyBackend.stateful_randc:5 -#: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.stateful_randc:5 -msgid "The possible options" +#: of tensorcircuit.applications.vags.unitary_design_block:1 +msgid "random Haar measure approximation" msgstr "" -#: of tensorcircuit.backends.jax_backend.JaxBackend.implicit_randc:7 -#: tensorcircuit.backends.jax_backend.JaxBackend.stateful_randc:7 -#: tensorcircuit.backends.numpy_backend.NumpyBackend.stateful_randc:7 -#: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.stateful_randc:7 -msgid "Sampling output shape" +#: of tensorcircuit.applications.vags.unitary_design_block:3 +msgid "cirq.Circuit, empty circuit" msgstr "" -#: of tensorcircuit.backends.jax_backend.JaxBackend.implicit_randc:9 -#: tensorcircuit.backends.jax_backend.JaxBackend.stateful_randc:9 -#: tensorcircuit.backends.numpy_backend.NumpyBackend.stateful_randc:9 -#: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.stateful_randc:9 -msgid "" -"probability for each option in a, defaults to None, as equal probability " -"distribution" +#: of tensorcircuit.applications.vags.unitary_design_block:4 +msgid "# of qubit" msgstr "" -#: of tensorcircuit.backends.jax_backend.JaxBackend.implicit_randn:1 -#: tensorcircuit.backends.jax_backend.JaxBackend.implicit_randu:1 +#: ../../source/api/applications/van.rst:2 +msgid "tensorcircuit.applications.van" +msgstr "" + +#: of tensorcircuit.applications.van:1 msgid "" -"Call the random normal function with the random state management behind " -"the scene." +"One-hot variational autoregressive models for multiple categorical " +"choices beyond binary" msgstr "" -#: of tensorcircuit.backends.jax_backend.JaxBackend.implicit_randn:3 -#: tensorcircuit.backends.jax_backend.JaxBackend.implicit_randn:7 -#: tensorcircuit.backends.jax_backend.JaxBackend.implicit_randu:3 -#: tensorcircuit.backends.jax_backend.JaxBackend.implicit_randu:7 -#: tensorcircuit.backends.jax_backend.JaxBackend.stateful_randn:11 -#: tensorcircuit.backends.jax_backend.JaxBackend.stateful_randu:9 -#: tensorcircuit.backends.numpy_backend.NumpyBackend.stateful_randn:11 -#: tensorcircuit.backends.numpy_backend.NumpyBackend.stateful_randu:9 -#: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.stateful_randn:11 -#: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.stateful_randu:9 -msgid "[description], defaults to 1" +#: of tensorcircuit.applications.van.MADE:1 +#: tensorcircuit.applications.van.NMF:1 +#: tensorcircuit.applications.van.PixelCNN:1 +msgid "Bases: :py:class:`~keras.src.engine.training.Model`" msgstr "" -#: of tensorcircuit.backends.jax_backend.JaxBackend.implicit_randn:9 -#: tensorcircuit.backends.jax_backend.JaxBackend.implicit_randu:9 -msgid "[description], defaults to \"32\"" +#: of tensorcircuit.applications.van.MADE.activity_regularizer:1 +#: tensorcircuit.applications.van.MaskedConv2D.activity_regularizer:1 +#: tensorcircuit.applications.van.MaskedLinear.activity_regularizer:1 +#: tensorcircuit.applications.van.NMF.activity_regularizer:1 +#: tensorcircuit.applications.van.PixelCNN.activity_regularizer:1 +#: tensorcircuit.applications.van.ResidualBlock.activity_regularizer:1 +#: tensorcircuit.applications.vqes.Linear.activity_regularizer:1 +#: tensorcircuit.keras.HardwareLayer.activity_regularizer:1 +#: tensorcircuit.keras.QuantumLayer.activity_regularizer:1 +msgid "Optional regularizer function for the output of this layer." msgstr "" -#: of tensorcircuit.backends.jax_backend.JaxBackend.is_sparse:1 -#: tensorcircuit.backends.numpy_backend.NumpyBackend.is_sparse:1 -#: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.is_sparse:1 -msgid "Determine whether the type of input ``a`` is ``sparse``." +#: keras.src.engine.base_layer.Layer.add_loss:1 of +msgid "Add loss tensor(s), potentially dependent on layer inputs." msgstr "" -#: of tensorcircuit.backends.jax_backend.JaxBackend.is_sparse:3 -#: tensorcircuit.backends.numpy_backend.NumpyBackend.is_sparse:3 -#: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.is_sparse:3 -msgid "input matrix ``a``" +#: keras.src.engine.base_layer.Layer.add_loss:3 of +msgid "" +"Some losses (for instance, activity regularization losses) may be " +"dependent on the inputs passed when calling a layer. Hence, when reusing " +"the same layer on different inputs `a` and `b`, some entries in " +"`layer.losses` may be dependent on `a` and some on `b`. This method " +"automatically keeps track of dependencies." msgstr "" -#: of tensorcircuit.backends.jax_backend.JaxBackend.is_sparse:5 -#: tensorcircuit.backends.numpy_backend.NumpyBackend.is_sparse:5 -#: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.is_sparse:5 -msgid "a bool indicating whether the matrix ``a`` is sparse" +#: keras.src.engine.base_layer.Layer.add_loss:9 of +msgid "" +"This method can be used inside a subclassed layer or model's `call` " +"function, in which case `losses` should be a Tensor or list of Tensors." msgstr "" -#: of tensorcircuit.backends.jax_backend.JaxBackend.is_tensor:1 -#: tensorcircuit.backends.numpy_backend.NumpyBackend.is_tensor:1 -#: tensorcircuit.backends.pytorch_backend.PyTorchBackend.is_tensor:1 -#: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.is_tensor:1 -msgid "Return a boolean on whether ``a`` is a tensor in backend package." +#: keras.src.engine.base_layer.Layer.add_loss:12 +#: keras.src.engine.base_layer.Layer.add_loss:32 +#: keras.src.engine.base_layer.Layer.add_loss:48 +#: keras.src.engine.training.Model.compile:3 +#: keras.src.engine.training.Model.export:15 +#: keras.src.engine.training.Model.get_weight_paths:18 +#: keras.src.engine.training.Model.save:38 of +#: tensorcircuit.applications.van.MaskedConv2D.metrics:3 +#: tensorcircuit.applications.van.MaskedLinear.metrics:3 +#: tensorcircuit.applications.van.ResidualBlock.metrics:3 +#: tensorcircuit.applications.vqes.Linear.metrics:3 +#: tensorcircuit.keras.HardwareLayer.metrics:3 +#: tensorcircuit.keras.QuantumLayer.metrics:3 +msgid "Example:" msgstr "" -#: of tensorcircuit.backends.jax_backend.JaxBackend.is_tensor:3 -#: tensorcircuit.backends.numpy_backend.NumpyBackend.is_tensor:3 -#: tensorcircuit.backends.pytorch_backend.PyTorchBackend.is_tensor:3 -#: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.is_tensor:3 -msgid "a tensor to be determined" +#: keras.src.engine.base_layer.Layer.add_loss:14 of +msgid "```python class MyLayer(tf.keras.layers.Layer):" msgstr "" -#: of tensorcircuit.backends.jax_backend.JaxBackend.is_tensor:5 -#: tensorcircuit.backends.numpy_backend.NumpyBackend.is_tensor:5 -#: tensorcircuit.backends.pytorch_backend.PyTorchBackend.is_tensor:5 -#: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.is_tensor:5 -msgid "whether ``a`` is a tensor" +#: keras.src.engine.base_layer.Layer.add_loss:17 +#: keras.src.engine.base_layer.Layer.add_metric:14 +#: keras.src.engine.training.Model.get_weight_paths:30 of +msgid "def call(self, inputs):" msgstr "" -#: of tensorcircuit.backends.jax_backend.JaxBackend.jit:1 -#: tensorcircuit.backends.numpy_backend.NumpyBackend.jit:1 -#: tensorcircuit.backends.pytorch_backend.PyTorchBackend.jit:1 -#: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.jit:1 -msgid "Return the jitted version of function ``f``." +#: keras.src.engine.base_layer.Layer.add_loss:17 of +msgid "self.add_loss(tf.abs(tf.reduce_mean(inputs))) return inputs" msgstr "" -#: of tensorcircuit.backends.jax_backend.JaxBackend.jit:3 -#: tensorcircuit.backends.numpy_backend.NumpyBackend.jit:3 -#: tensorcircuit.backends.pytorch_backend.PyTorchBackend.jit:3 -#: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.jit:3 -msgid "function to be jitted" +#: keras.src.engine.base_layer.Layer.add_loss:19 +#: keras.src.engine.base_layer.Layer.add_metric:16 +#: keras.src.engine.training.Model.compile:10 +#: keras.src.engine.training.Model.compute_metrics:21 of +#: tensornetwork.backends.jax.jax_backend.JaxBackend.eigs:21 +#: tensornetwork.backends.jax.jax_backend.JaxBackend.eigs:35 +#: tensornetwork.backends.jax.jax_backend.JaxBackend.eigsh:21 +#: tensornetwork.backends.jax.jax_backend.JaxBackend.eigsh:35 +#: tensornetwork.backends.jax.jax_backend.JaxBackend.eigsh_lanczos:19 +#: tensornetwork.backends.jax.jax_backend.JaxBackend.eigsh_lanczos:33 +msgid "```" msgstr "" -#: of tensorcircuit.backends.jax_backend.JaxBackend.jit:5 -#: tensorcircuit.backends.numpy_backend.NumpyBackend.jit:5 -#: tensorcircuit.backends.pytorch_backend.PyTorchBackend.jit:5 -#: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.jit:5 -msgid "index of args that doesn't regarded as tensor, only work for jax backend" +#: keras.src.engine.base_layer.Layer.add_loss:21 of +msgid "" +"The same code works in distributed training: the input to `add_loss()` is" +" treated like a regularization loss and averaged across replicas by the " +"training loop (both built-in `Model.fit()` and compliant custom training " +"loops)." msgstr "" -#: of tensorcircuit.backends.jax_backend.JaxBackend.jit:8 -#: tensorcircuit.backends.numpy_backend.NumpyBackend.jit:8 -#: tensorcircuit.backends.pytorch_backend.PyTorchBackend.jit:8 -#: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.jit:8 +#: keras.src.engine.base_layer.Layer.add_loss:26 of msgid "" -"whether open XLA compilation, only works for tensorflow backend, defaults" -" False since several ops has no XLA correspondence" +"The `add_loss` method can also be called directly on a Functional Model " +"during construction. In this case, any loss Tensors passed to this Model " +"must be symbolic and be able to be traced back to the model's `Input`s. " +"These losses become part of the model's topology and are tracked in " +"`get_config`." msgstr "" -#: of tensorcircuit.backends.jax_backend.JaxBackend.jit:11 -#: tensorcircuit.backends.numpy_backend.NumpyBackend.jit:11 -#: tensorcircuit.backends.pytorch_backend.PyTorchBackend.jit:11 -#: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.jit:11 -msgid "jitted version of ``f``" +#: keras.src.engine.base_layer.Layer.add_loss:34 of +msgid "" +"```python inputs = tf.keras.Input(shape=(10,)) x = " +"tf.keras.layers.Dense(10)(inputs) outputs = tf.keras.layers.Dense(1)(x) " +"model = tf.keras.Model(inputs, outputs) # Activity regularization. " +"model.add_loss(tf.abs(tf.reduce_mean(x))) ```" msgstr "" -#: of tensorcircuit.backends.jax_backend.JaxBackend.jvp:1 -#: tensorcircuit.backends.numpy_backend.NumpyBackend.jvp:1 -#: tensorcircuit.backends.pytorch_backend.PyTorchBackend.jvp:1 -#: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.jvp:1 +#: keras.src.engine.base_layer.Layer.add_loss:43 of msgid "" -"Function that computes a (forward-mode) Jacobian-vector product of ``f``." -" Strictly speaking, this function is value_and_jvp." +"If this is not the case for your loss (if, for example, your loss " +"references a `Variable` of one of the model's layers), you can wrap your " +"loss in a zero-argument lambda. These losses are not tracked as part of " +"the model's topology since they can't be serialized." msgstr "" -#: of tensorcircuit.backends.jax_backend.JaxBackend.jvp:4 -#: tensorcircuit.backends.numpy_backend.NumpyBackend.jvp:4 -#: tensorcircuit.backends.pytorch_backend.PyTorchBackend.jvp:4 -#: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.jvp:4 -msgid "The function to compute jvp" +#: keras.src.engine.base_layer.Layer.add_loss:50 of +msgid "" +"```python inputs = tf.keras.Input(shape=(10,)) d = " +"tf.keras.layers.Dense(10) x = d(inputs) outputs = " +"tf.keras.layers.Dense(1)(x) model = tf.keras.Model(inputs, outputs) # " +"Weight regularization. model.add_loss(lambda: tf.reduce_mean(d.kernel)) " +"```" msgstr "" -#: of tensorcircuit.backends.jax_backend.JaxBackend.jvp:6 -#: tensorcircuit.backends.jax_backend.JaxBackend.vjp:7 -#: tensorcircuit.backends.numpy_backend.NumpyBackend.jvp:6 -#: tensorcircuit.backends.numpy_backend.NumpyBackend.vjp:7 -#: tensorcircuit.backends.pytorch_backend.PyTorchBackend.jvp:6 -#: tensorcircuit.backends.pytorch_backend.PyTorchBackend.vjp:7 -#: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.jvp:6 -#: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.vjp:7 -msgid "input for ``f``" +#: keras.src.engine.base_layer.Layer.add_loss:60 of +msgid "" +"Loss tensor, or list/tuple of tensors. Rather than tensors, losses may " +"also be zero-argument callables which create a loss tensor." msgstr "" -#: of tensorcircuit.backends.jax_backend.JaxBackend.jvp:8 -#: tensorcircuit.backends.numpy_backend.NumpyBackend.jvp:8 -#: tensorcircuit.backends.pytorch_backend.PyTorchBackend.jvp:8 -#: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.jvp:8 -msgid "tangents" +#: keras.src.engine.base_layer.Layer.add_loss:63 of +msgid "Used for backwards compatibility only." msgstr "" -#: of tensorcircuit.backends.jax_backend.JaxBackend.jvp:10 -#: tensorcircuit.backends.numpy_backend.NumpyBackend.jvp:10 -#: tensorcircuit.backends.pytorch_backend.PyTorchBackend.jvp:10 -#: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.jvp:10 -msgid "" -"(``f(*inputs)``, jvp_tensor), where jvp_tensor is the same shape as the " -"output of ``f``" +#: keras.src.engine.base_layer.Layer.add_metric:1 of +msgid "Adds metric tensor to the layer." msgstr "" -#: of tensorcircuit.backends.jax_backend.JaxBackend.kron:1 -#: tensorcircuit.backends.numpy_backend.NumpyBackend.kron:1 -#: tensorcircuit.backends.pytorch_backend.PyTorchBackend.kron:1 -#: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.kron:1 -msgid "Return the kronecker product of two matrices ``a`` and ``b``." +#: keras.src.engine.base_layer.Layer.add_metric:3 of +msgid "" +"This method can be used inside the `call()` method of a subclassed layer " +"or model." msgstr "" -#: of tensorcircuit.backends.jax_backend.JaxBackend.kron:7 -#: tensorcircuit.backends.numpy_backend.NumpyBackend.kron:7 -#: tensorcircuit.backends.pytorch_backend.PyTorchBackend.kron:7 -#: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.kron:7 -msgid "kronecker product of ``a`` and ``b``" +#: keras.src.engine.base_layer.Layer.add_metric:6 of +msgid "```python class MyMetricLayer(tf.keras.layers.Layer):" msgstr "" -#: of tensorcircuit.backends.jax_backend.JaxBackend.left_shift:1 -#: tensorcircuit.backends.numpy_backend.NumpyBackend.left_shift:1 -#: tensorcircuit.backends.pytorch_backend.PyTorchBackend.left_shift:1 -#: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.left_shift:1 -msgid "Shift the bits of an integer x to the left y bits." +#: keras.src.engine.base_layer.Layer.add_metric:10 of +msgid "def __init__(self):" msgstr "" -#: of tensorcircuit.backends.jax_backend.JaxBackend.left_shift:3 -#: tensorcircuit.backends.jax_backend.JaxBackend.mod:3 -#: tensorcircuit.backends.jax_backend.JaxBackend.right_shift:3 -#: tensorcircuit.backends.numpy_backend.NumpyBackend.left_shift:3 -#: tensorcircuit.backends.numpy_backend.NumpyBackend.mod:3 -#: tensorcircuit.backends.numpy_backend.NumpyBackend.right_shift:3 -#: tensorcircuit.backends.pytorch_backend.PyTorchBackend.left_shift:3 -#: tensorcircuit.backends.pytorch_backend.PyTorchBackend.mod:3 -#: tensorcircuit.backends.pytorch_backend.PyTorchBackend.right_shift:3 -#: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.left_shift:3 -#: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.mod:3 -#: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.right_shift:3 -msgid "input values" +#: keras.src.engine.base_layer.Layer.add_metric:9 of +msgid "" +"super(MyMetricLayer, self).__init__(name='my_metric_layer') self.mean = " +"tf.keras.metrics.Mean(name='metric_1')" msgstr "" -#: of tensorcircuit.backends.jax_backend.JaxBackend.left_shift:5 -#: tensorcircuit.backends.jax_backend.JaxBackend.right_shift:5 -#: tensorcircuit.backends.numpy_backend.NumpyBackend.left_shift:5 -#: tensorcircuit.backends.numpy_backend.NumpyBackend.right_shift:5 -#: tensorcircuit.backends.pytorch_backend.PyTorchBackend.left_shift:5 -#: tensorcircuit.backends.pytorch_backend.PyTorchBackend.right_shift:5 -#: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.left_shift:5 -#: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.right_shift:5 -msgid "Number of bits shift to ``x``" +#: keras.src.engine.base_layer.Layer.add_metric:13 of +msgid "" +"self.add_metric(self.mean(inputs)) self.add_metric(tf.reduce_sum(inputs)," +" name='metric_2') return inputs" msgstr "" -#: of tensorcircuit.backends.jax_backend.JaxBackend.left_shift:7 -#: tensorcircuit.backends.jax_backend.JaxBackend.right_shift:7 -#: tensorcircuit.backends.numpy_backend.NumpyBackend.left_shift:7 -#: tensorcircuit.backends.numpy_backend.NumpyBackend.right_shift:7 -#: tensorcircuit.backends.pytorch_backend.PyTorchBackend.left_shift:7 -#: tensorcircuit.backends.pytorch_backend.PyTorchBackend.right_shift:7 -#: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.left_shift:7 -#: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.right_shift:7 -msgid "result with the same shape as ``x``" +#: keras.src.engine.base_layer.Layer.add_metric:18 of +msgid "" +"This method can also be called directly on a Functional Model during " +"construction. In this case, any tensor passed to this Model must be " +"symbolic and be able to be traced back to the model's `Input`s. These " +"metrics become part of the model's topology and are tracked when you save" +" the model via `save()`." msgstr "" -#: of tensorcircuit.backends.jax_backend.JaxBackend.max:1 -#: tensorcircuit.backends.numpy_backend.NumpyBackend.max:1 -#: tensorcircuit.backends.pytorch_backend.PyTorchBackend.max:1 -#: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.max:1 -msgid "Return the maximum of an array or maximum along an axis." +#: keras.src.engine.base_layer.Layer.add_metric:24 of +msgid "" +"```python inputs = tf.keras.Input(shape=(10,)) x = " +"tf.keras.layers.Dense(10)(inputs) outputs = tf.keras.layers.Dense(1)(x) " +"model = tf.keras.Model(inputs, outputs) " +"model.add_metric(math_ops.reduce_sum(x), name='metric_1') ```" msgstr "" -#: of tensorcircuit.backends.jax_backend.JaxBackend.max:5 -#: tensorcircuit.backends.jax_backend.JaxBackend.min:5 -#: tensorcircuit.backends.numpy_backend.NumpyBackend.max:5 -#: tensorcircuit.backends.numpy_backend.NumpyBackend.min:5 -#: tensorcircuit.backends.pytorch_backend.PyTorchBackend.max:5 -#: tensorcircuit.backends.pytorch_backend.PyTorchBackend.min:5 -#: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.max:5 -#: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.min:5 -#: tensorcircuit.keras.QuantumLayer.__init__:10 -msgid "[description], defaults to None" +#: keras.src.engine.base_layer.Layer.add_metric:32 of +msgid "" +"Note: Calling `add_metric()` with the result of a metric object on a " +"Functional Model, as shown in the example below, is not supported. This " +"is because we cannot trace the metric result tensor back to the model's " +"inputs." msgstr "" -#: of tensorcircuit.backends.jax_backend.JaxBackend.mean:1 -#: tensorcircuit.backends.numpy_backend.NumpyBackend.mean:1 -#: tensorcircuit.backends.pytorch_backend.PyTorchBackend.mean:1 -#: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.mean:1 -msgid "Compute the arithmetic mean for ``a`` along the specified ``axis``." +#: keras.src.engine.base_layer.Layer.add_metric:37 of +msgid "" +"```python inputs = tf.keras.Input(shape=(10,)) x = " +"tf.keras.layers.Dense(10)(inputs) outputs = tf.keras.layers.Dense(1)(x) " +"model = tf.keras.Model(inputs, outputs) " +"model.add_metric(tf.keras.metrics.Mean()(x), name='metric_1') ```" msgstr "" -#: of tensorcircuit.backends.jax_backend.JaxBackend.mean:3 -#: tensorcircuit.backends.numpy_backend.NumpyBackend.mean:3 -#: tensorcircuit.backends.pytorch_backend.PyTorchBackend.mean:3 -#: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.mean:3 -msgid "tensor to take average" +#: keras.src.engine.base_layer.Layer.add_metric:45 of +msgid "Metric tensor." msgstr "" -#: of tensorcircuit.backends.jax_backend.JaxBackend.mean:5 -#: tensorcircuit.backends.numpy_backend.NumpyBackend.mean:5 -#: tensorcircuit.backends.pytorch_backend.PyTorchBackend.mean:5 -#: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.mean:5 -msgid "the axis to take mean, defaults to None indicating sum over flatten array" +#: keras.src.engine.base_layer.Layer.add_metric:46 of +msgid "String metric name." msgstr "" -#: of tensorcircuit.backends.jax_backend.JaxBackend.mean:7 -#: tensorcircuit.backends.numpy_backend.NumpyBackend.mean:7 -#: tensorcircuit.backends.pytorch_backend.PyTorchBackend.mean:7 -#: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.mean:7 -msgid "_description_, defaults to False" +#: keras.src.engine.base_layer.Layer.add_metric:47 of +msgid "" +"Additional keyword arguments for backward compatibility. Accepted values:" +" `aggregation` - When the `value` tensor provided is not the result of " +"calling a `keras.Metric` instance, it will be aggregated by default using" +" a `keras.Metric.Mean`." msgstr "" -#: of tensorcircuit.backends.jax_backend.JaxBackend.min:1 -#: tensorcircuit.backends.numpy_backend.NumpyBackend.min:1 -#: tensorcircuit.backends.pytorch_backend.PyTorchBackend.min:1 -#: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.min:1 -msgid "Return the minimum of an array or minimum along an axis." +#: keras.src.engine.base_layer.Layer.add_update:1 of +msgid "Add update op(s), potentially dependent on layer inputs." msgstr "" -#: of tensorcircuit.backends.jax_backend.JaxBackend.mod:1 -#: tensorcircuit.backends.numpy_backend.NumpyBackend.mod:1 -#: tensorcircuit.backends.pytorch_backend.PyTorchBackend.mod:1 -#: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.mod:1 +#: keras.src.engine.base_layer.Layer.add_update:3 of msgid "" -"Compute y-mod of x (negative number behavior is not guaranteed to be " -"consistent)" +"Weight updates (for instance, the updates of the moving mean and variance" +" in a BatchNormalization layer) may be dependent on the inputs passed " +"when calling a layer. Hence, when reusing the same layer on different " +"inputs `a` and `b`, some entries in `layer.updates` may be dependent on " +"`a` and some on `b`. This method automatically keeps track of " +"dependencies." msgstr "" -#: of tensorcircuit.backends.jax_backend.JaxBackend.mod:5 -#: tensorcircuit.backends.numpy_backend.NumpyBackend.mod:5 -#: tensorcircuit.backends.pytorch_backend.PyTorchBackend.mod:5 -#: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.mod:5 -msgid "mod ``y``" +#: keras.src.engine.base_layer.Layer.add_update:10 of +msgid "" +"This call is ignored when eager execution is enabled (in that case, " +"variable updates are run on the fly and thus do not need to be tracked " +"for later execution)." msgstr "" -#: of tensorcircuit.backends.jax_backend.JaxBackend.mod:7 -#: tensorcircuit.backends.numpy_backend.NumpyBackend.mod:7 -#: tensorcircuit.backends.pytorch_backend.PyTorchBackend.mod:7 -#: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.mod:7 -msgid "results" +#: keras.src.engine.base_layer.Layer.add_update:14 of +msgid "" +"Update op, or list/tuple of update ops, or zero-arg callable that returns" +" an update op. A zero-arg callable should be passed in order to disable " +"running the updates by setting `trainable=False` on this Layer, when " +"executing in Eager mode." msgstr "" -#: of tensorcircuit.backends.jax_backend.JaxBackend.numpy:1 -#: tensorcircuit.backends.numpy_backend.NumpyBackend.numpy:1 -#: tensorcircuit.backends.pytorch_backend.PyTorchBackend.numpy:1 -#: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.numpy:1 -msgid "" -"Return the numpy array of a tensor ``a``, but may not work in a jitted " -"function." +#: keras.src.engine.base_layer.Layer.add_variable:1 of +msgid "Deprecated, do NOT use! Alias for `add_weight`." msgstr "" -#: of tensorcircuit.backends.jax_backend.JaxBackend.numpy:5 -#: tensorcircuit.backends.numpy_backend.NumpyBackend.numpy:5 -#: tensorcircuit.backends.pytorch_backend.PyTorchBackend.numpy:5 -#: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.numpy:5 -msgid "numpy array of ``a``" +#: keras.src.engine.base_layer.Layer.add_weight:1 of +msgid "Adds a new variable to the layer." msgstr "" -#: of tensorcircuit.backends.jax_backend.JaxBackend.onehot:1 -#: tensorcircuit.backends.numpy_backend.NumpyBackend.onehot:1 -#: tensorcircuit.backends.pytorch_backend.PyTorchBackend.onehot:1 -#: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.onehot:1 -msgid "" -"One-hot encodes the given ``a``. Each index in the input ``a`` is encoded" -" as a vector of zeros of length ``num`` with the element at index set to " -"one:" +#: keras.src.engine.base_layer.Layer.add_weight:3 of +msgid "Variable name." msgstr "" -#: of tensorcircuit.backends.jax_backend.JaxBackend.onehot:5 -#: tensorcircuit.backends.numpy_backend.NumpyBackend.onehot:5 -#: tensorcircuit.backends.pytorch_backend.PyTorchBackend.onehot:5 -#: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.onehot:5 -msgid "input tensor" +#: keras.src.engine.base_layer.Layer.add_weight:4 of +msgid "Variable shape. Defaults to scalar if unspecified." msgstr "" -#: of tensorcircuit.backends.jax_backend.JaxBackend.onehot:7 -#: tensorcircuit.backends.numpy_backend.NumpyBackend.onehot:7 -#: tensorcircuit.backends.pytorch_backend.PyTorchBackend.onehot:7 -#: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.onehot:7 -msgid "number of features in onehot dimension" +#: keras.src.engine.base_layer.Layer.add_weight:5 of +msgid "The type of the variable. Defaults to `self.dtype`." msgstr "" -#: of tensorcircuit.backends.jax_backend.JaxBackend.onehot:9 -#: tensorcircuit.backends.numpy_backend.NumpyBackend.onehot:9 -#: tensorcircuit.backends.pytorch_backend.PyTorchBackend.onehot:9 -#: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.onehot:9 -msgid "onehot tensor with the last extra dimension" +#: keras.src.engine.base_layer.Layer.add_weight:6 of +msgid "Initializer instance (callable)." msgstr "" -#: of tensorcircuit.backends.jax_backend.JaxBackend.ones:1 -#: tensorcircuit.backends.numpy_backend.NumpyBackend.ones:1 -#: tensorcircuit.backends.pytorch_backend.PyTorchBackend.ones:1 -#: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.ones:1 -msgid "" -"Return an ones-matrix of dimension `dim` Depending on specific backends, " -"`dim` has to be either an int (numpy, torch, tensorflow) or a `ShapeType`" -" object (for block-sparse backends). Block-sparse behavior is currently " -"not supported Args:" +#: keras.src.engine.base_layer.Layer.add_weight:7 of +msgid "Regularizer instance (callable)." msgstr "" -#: of tensorcircuit.backends.jax_backend.JaxBackend.ones:7 -#: tensorcircuit.backends.jax_backend.JaxBackend.zeros:8 -#: tensorcircuit.backends.numpy_backend.NumpyBackend.ones:7 -#: tensorcircuit.backends.numpy_backend.NumpyBackend.zeros:8 -#: tensorcircuit.backends.pytorch_backend.PyTorchBackend.ones:7 -#: tensorcircuit.backends.pytorch_backend.PyTorchBackend.zeros:8 -#: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.ones:7 -#: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.zeros:8 +#: keras.src.engine.base_layer.Layer.add_weight:8 of msgid "" -"shape (int): The dimension of the returned matrix. dtype: The dtype of " -"the returned matrix." +"Boolean, whether the variable should be part of the layer's " +"\"trainable_variables\" (e.g. variables, biases) or " +"\"non_trainable_variables\" (e.g. BatchNorm mean and variance). Note that" +" `trainable` cannot be `True` if `synchronization` is set to `ON_READ`." msgstr "" -#: of tensorcircuit.backends.jax_backend.JaxBackend.random_split:1 +#: keras.src.engine.base_layer.Layer.add_weight:13 of +msgid "Constraint instance (callable)." +msgstr "" + +#: keras.src.engine.base_layer.Layer.add_weight:14 of msgid "" -"A jax like split API, but it doesn't split the key generator for other " -"backends. It is just for a consistent interface of random code; make sure" -" you know what the function actually does. This function is mainly a " -"utility to write backend agnostic code instead of doing magic things." +"Whether to use a `ResourceVariable` or not. See [this guide]( " +"https://www.tensorflow.org/guide/migrate/tf1_vs_tf2#resourcevariables_instead_of_referencevariables)" +" for more information." msgstr "" -#: of tensorcircuit.backends.jax_backend.JaxBackend.real:1 -#: tensorcircuit.backends.numpy_backend.NumpyBackend.real:1 -#: tensorcircuit.backends.pytorch_backend.PyTorchBackend.real:1 -#: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.real:1 -msgid "Return the elementwise real value of a tensor ``a``." +#: keras.src.engine.base_layer.Layer.add_weight:14 of +msgid "" +"Whether to use a `ResourceVariable` or not. See [this guide]( " +"https://www.tensorflow.org/guide/migrate/tf1_vs_tf2#resourcevariables_instead_of_referencevariables)" msgstr "" -#: of tensorcircuit.backends.jax_backend.JaxBackend.real:5 -#: tensorcircuit.backends.numpy_backend.NumpyBackend.real:5 -#: tensorcircuit.backends.pytorch_backend.PyTorchBackend.real:5 -#: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.real:5 -msgid "real value of ``a``" +#: keras.src.engine.base_layer.Layer.add_weight:17 of +msgid "for more information." msgstr "" -#: of tensorcircuit.backends.jax_backend.JaxBackend.relu:1 -#: tensorcircuit.backends.numpy_backend.NumpyBackend.relu:1 -#: tensorcircuit.backends.pytorch_backend.PyTorchBackend.relu:1 -#: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.relu:1 +#: keras.src.engine.base_layer.Layer.add_weight:18 of msgid "" -"Rectified linear unit activation function. Computes the element-wise " -"function:" +"Indicates when a distributed a variable will be aggregated. Accepted " +"values are constants defined in the class `tf.VariableSynchronization`. " +"By default the synchronization is set to `AUTO` and the current " +"`DistributionStrategy` chooses when to synchronize. If `synchronization` " +"is set to `ON_READ`, `trainable` must not be set to `True`." msgstr "" -#: of tensorcircuit.backends.jax_backend.JaxBackend.relu:4 -#: tensorcircuit.backends.numpy_backend.NumpyBackend.relu:4 -#: tensorcircuit.backends.pytorch_backend.PyTorchBackend.relu:4 -#: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.relu:4 -msgid "\\mathrm{relu}(x)=\\max(x,0)" +#: keras.src.engine.base_layer.Layer.add_weight:24 of +msgid "" +"Indicates how a distributed variable will be aggregated. Accepted values " +"are constants defined in the class `tf.VariableAggregation`." msgstr "" -#: of tensorcircuit.backends.jax_backend.JaxBackend.relu:9 -#: tensorcircuit.backends.numpy_backend.NumpyBackend.relu:9 -#: tensorcircuit.backends.pytorch_backend.PyTorchBackend.relu:9 -#: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.relu:9 -msgid "Input tensor" +#: keras.src.engine.base_layer.Layer.add_weight:27 of +msgid "" +"Additional keyword arguments. Accepted values are `getter`, " +"`collections`, `experimental_autocast` and `caching_device`." msgstr "" -#: of tensorcircuit.backends.jax_backend.JaxBackend.relu:11 -#: tensorcircuit.backends.numpy_backend.NumpyBackend.relu:11 -#: tensorcircuit.backends.pytorch_backend.PyTorchBackend.relu:11 -#: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.relu:11 -msgid "Tensor after relu" +#: keras.src.engine.base_layer.Layer.add_weight:30 of +msgid "The variable created." msgstr "" -#: of tensorcircuit.backends.jax_backend.JaxBackend.right_shift:1 -#: tensorcircuit.backends.numpy_backend.NumpyBackend.right_shift:1 -#: tensorcircuit.backends.pytorch_backend.PyTorchBackend.right_shift:1 -#: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.right_shift:1 -msgid "Shift the bits of an integer x to the right y bits." +#: keras.src.engine.base_layer.Layer.add_weight +#: keras.src.engine.base_layer.Layer.compute_output_signature +#: keras.src.engine.base_layer.Layer.count_params +#: keras.src.engine.base_layer.Layer.get_input_at +#: keras.src.engine.base_layer.Layer.get_input_shape_at +#: keras.src.engine.base_layer.Layer.get_output_at +#: keras.src.engine.base_layer.Layer.get_output_shape_at +#: keras.src.engine.base_layer.Layer.set_weights +#: keras.src.engine.training.Model.build +#: keras.src.engine.training.Model.evaluate keras.src.engine.training.Model.fit +#: keras.src.engine.training.Model.predict +#: keras.src.engine.training.Model.predict_on_batch +#: keras.src.engine.training.Model.save_weights +#: keras.src.engine.training.Model.summary +#: keras.src.engine.training.Model.test_on_batch +#: keras.src.engine.training.Model.to_yaml +#: keras.src.engine.training.Model.train_on_batch of +#: tensorcircuit.applications.van.MADE.input +#: tensorcircuit.applications.van.MADE.input_mask +#: tensorcircuit.applications.van.MADE.input_shape +#: tensorcircuit.applications.van.MADE.output +#: tensorcircuit.applications.van.MADE.output_mask +#: tensorcircuit.applications.van.MADE.output_shape +#: tensorcircuit.applications.van.MaskedConv2D.input +#: tensorcircuit.applications.van.MaskedConv2D.input_mask +#: tensorcircuit.applications.van.MaskedConv2D.input_shape +#: tensorcircuit.applications.van.MaskedConv2D.output +#: tensorcircuit.applications.van.MaskedConv2D.output_mask +#: tensorcircuit.applications.van.MaskedConv2D.output_shape +#: tensorcircuit.applications.van.MaskedLinear.input +#: tensorcircuit.applications.van.MaskedLinear.input_mask +#: tensorcircuit.applications.van.MaskedLinear.input_shape +#: tensorcircuit.applications.van.MaskedLinear.output +#: tensorcircuit.applications.van.MaskedLinear.output_mask +#: tensorcircuit.applications.van.MaskedLinear.output_shape +#: tensorcircuit.applications.van.NMF.input +#: tensorcircuit.applications.van.NMF.input_mask +#: tensorcircuit.applications.van.NMF.input_shape +#: tensorcircuit.applications.van.NMF.output +#: tensorcircuit.applications.van.NMF.output_mask +#: tensorcircuit.applications.van.NMF.output_shape +#: tensorcircuit.applications.van.PixelCNN.input +#: tensorcircuit.applications.van.PixelCNN.input_mask +#: tensorcircuit.applications.van.PixelCNN.input_shape +#: tensorcircuit.applications.van.PixelCNN.output +#: tensorcircuit.applications.van.PixelCNN.output_mask +#: tensorcircuit.applications.van.PixelCNN.output_shape +#: tensorcircuit.applications.van.ResidualBlock.input +#: tensorcircuit.applications.van.ResidualBlock.input_mask +#: tensorcircuit.applications.van.ResidualBlock.input_shape +#: tensorcircuit.applications.van.ResidualBlock.output +#: tensorcircuit.applications.van.ResidualBlock.output_mask +#: tensorcircuit.applications.van.ResidualBlock.output_shape +#: tensorcircuit.applications.vqes.Linear.input +#: tensorcircuit.applications.vqes.Linear.input_mask +#: tensorcircuit.applications.vqes.Linear.input_shape +#: tensorcircuit.applications.vqes.Linear.output +#: tensorcircuit.applications.vqes.Linear.output_mask +#: tensorcircuit.applications.vqes.Linear.output_shape +#: tensorcircuit.backends.abstract_backend.ExtendedBackend.tree_map +#: tensorcircuit.backends.backend_factory.get_backend +#: tensorcircuit.backends.jax_backend.JaxBackend.tree_map +#: tensorcircuit.backends.pytorch_backend.PyTorchBackend.tree_map +#: tensorcircuit.basecircuit.BaseCircuit.expectation_before +#: tensorcircuit.circuit.Circuit.expectation tensorcircuit.circuit.expectation +#: tensorcircuit.cons.get_contractor tensorcircuit.cons.set_contractor +#: tensorcircuit.gates.bmatrix tensorcircuit.keras.HardwareLayer.input +#: tensorcircuit.keras.HardwareLayer.input_mask +#: tensorcircuit.keras.HardwareLayer.input_shape +#: tensorcircuit.keras.HardwareLayer.output +#: tensorcircuit.keras.HardwareLayer.output_mask +#: tensorcircuit.keras.HardwareLayer.output_shape +#: tensorcircuit.keras.QuantumLayer.input +#: tensorcircuit.keras.QuantumLayer.input_mask +#: tensorcircuit.keras.QuantumLayer.input_shape +#: tensorcircuit.keras.QuantumLayer.output +#: tensorcircuit.keras.QuantumLayer.output_mask +#: tensorcircuit.keras.QuantumLayer.output_shape tensorcircuit.keras.load_func +#: tensorcircuit.mps_base.FiniteMPS.apply_two_site_gate +#: tensorcircuit.mpscircuit.MPSCircuit.apply_general_gate +#: tensorcircuit.quantum.QuOperator.__init__ +#: tensorcircuit.quantum.QuOperator.eval +#: tensorcircuit.quantum.QuOperator.eval_matrix +#: tensorcircuit.quantum.check_spaces +#: tensornetwork.backends.abstract_backend.AbstractBackend.gmres +#: tensornetwork.matrixproductstates.base_mps.BaseMPS.check_orthonormality +#: tensornetwork.matrixproductstates.base_mps.BaseMPS.position +#: tensornetwork.network_components.AbstractNode.add_axis_names +#: tensornetwork.network_components.AbstractNode.add_edge +#: tensornetwork.network_components.AbstractNode.get_dimension +#: tensornetwork.network_components.AbstractNode.reorder_axes +#: tensornetwork.network_components.AbstractNode.reorder_edges +#: tensornetwork.network_components.Node.__init__ +#: torch.nn.modules.module.Module.get_buffer +#: torch.nn.modules.module.Module.get_parameter +#: torch.nn.modules.module.Module.get_submodule +msgid "Raises" msgstr "" -#: of tensorcircuit.backends.jax_backend.JaxBackend.scatter:1 -#: tensorcircuit.backends.numpy_backend.NumpyBackend.scatter:1 -#: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.scatter:1 +#: keras.src.engine.base_layer.Layer.add_weight:32 of msgid "" -"Roughly equivalent to operand[indices] = updates, indices only support " -"shape with rank 2 for now." +"When giving unsupported dtype and no initializer or when trainable " +"has been set to True with synchronization set as `ON_READ`." msgstr "" -#: of tensorcircuit.backends.jax_backend.JaxBackend.set_random_state:1 -#: tensorcircuit.backends.numpy_backend.NumpyBackend.set_random_state:1 -#: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.set_random_state:1 -msgid "Set the random state attached to the backend." +#: keras.src.engine.training.Model.build:1 of +msgid "Builds the model based on input shapes received." msgstr "" -#: of tensorcircuit.backends.jax_backend.JaxBackend.set_random_state:3 -#: tensorcircuit.backends.numpy_backend.NumpyBackend.set_random_state:3 -#: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.set_random_state:3 -msgid "the random seed, defaults to be None" +#: keras.src.engine.training.Model.build:3 of +msgid "" +"This is to be used for subclassed models, which do not know at " +"instantiation time what their inputs look like." msgstr "" -#: of tensorcircuit.backends.jax_backend.JaxBackend.set_random_state:5 -#: tensorcircuit.backends.numpy_backend.NumpyBackend.set_random_state:5 -#: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.set_random_state:5 +#: keras.src.engine.training.Model.build:6 of msgid "" -"If set to be true, only get the random state in return instead of setting" -" the state on the backend" +"This method only exists for users who want to call `model.build()` in a " +"standalone way (as a substitute for calling the model on real data to " +"build it). It will never be called by the framework (and thus it will " +"never throw unexpected errors in an unrelated workflow)." msgstr "" -#: of tensorcircuit.backends.jax_backend.JaxBackend.sigmoid:1 -#: tensorcircuit.backends.numpy_backend.NumpyBackend.sigmoid:1 -#: tensorcircuit.backends.pytorch_backend.PyTorchBackend.sigmoid:1 -#: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.sigmoid:1 -msgid "Compute sigmoid of input ``a``" +#: keras.src.engine.training.Model.build:11 of +msgid "" +"Single tuple, `TensorShape` instance, or list/dict of shapes, where " +"shapes are tuples, integers, or `TensorShape` instances." msgstr "" -#: of tensorcircuit.backends.jax_backend.JaxBackend.sin:1 -#: tensorcircuit.backends.numpy_backend.NumpyBackend.sin:1 -#: tensorcircuit.backends.pytorch_backend.PyTorchBackend.sin:1 -#: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.sin:1 -msgid "Return the elementwise sine of a tensor ``a``." +#: keras.src.engine.training.Model.build:15 of +msgid "" +"1. In case of invalid user-provided data (not of type tuple, list," +" `TensorShape`, or dict). 2. If the model requires call arguments " +"that are agnostic to the input shapes (positional or keyword arg " +"in call signature). 3. If not all layers were properly built." +" 4. If float type inputs are not supported within the layers." msgstr "" -#: of tensorcircuit.backends.jax_backend.JaxBackend.sin:5 -#: tensorcircuit.backends.numpy_backend.NumpyBackend.sin:5 -#: tensorcircuit.backends.pytorch_backend.PyTorchBackend.sin:5 -#: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.sin:5 -msgid "sine of ``a``" +#: keras.src.engine.training.Model.build:15 of +msgid "" +"In case of invalid user-provided data (not of type tuple, list, " +"`TensorShape`, or dict). 2. If the model requires call arguments that" +" are agnostic to the input shapes (positional or keyword arg in " +"call signature). 3. If not all layers were properly built." +" 4. If float type inputs are not supported within the layers." msgstr "" -#: of tensorcircuit.backends.jax_backend.JaxBackend.sinh:1 -#: tensorcircuit.backends.numpy_backend.NumpyBackend.sinh:1 -#: tensorcircuit.backends.pytorch_backend.PyTorchBackend.sinh:1 -#: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.sinh:1 -msgid "Return the sinh of a tensor ``a``." +#: keras.src.engine.base_layer.Layer.build_from_config:1 of +msgid "Builds the layer's states with the supplied config dict." msgstr "" -#: of tensorcircuit.backends.jax_backend.JaxBackend.sinh:5 -#: tensorcircuit.backends.numpy_backend.NumpyBackend.sinh:5 -#: tensorcircuit.backends.pytorch_backend.PyTorchBackend.sinh:5 -#: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.sinh:5 -msgid "sinh of ``a``" +#: keras.src.engine.base_layer.Layer.build_from_config:3 of +msgid "" +"By default, this method calls the `build(config[\"input_shape\"])` " +"method, which creates weights based on the layer's input shape in the " +"supplied config. If your config contains other information needed to load" +" the layer's state, you should override this method." msgstr "" -#: of tensorcircuit.backends.jax_backend.JaxBackend.size:1 -#: tensorcircuit.backends.numpy_backend.NumpyBackend.size:1 -#: tensorcircuit.backends.pytorch_backend.PyTorchBackend.size:1 -#: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.size:1 -msgid "Return the total number of elements in ``a`` in tensor form." +#: keras.src.engine.base_layer.Layer.build_from_config:8 of +msgid "Dict containing the input shape associated with this layer." msgstr "" -#: of tensorcircuit.backends.jax_backend.JaxBackend.size:5 -#: tensorcircuit.backends.numpy_backend.NumpyBackend.size:5 -#: tensorcircuit.backends.pytorch_backend.PyTorchBackend.size:5 -#: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.size:5 -msgid "the total number of elements in ``a``" +#: of tensorcircuit.applications.van.MADE.call:1 +#: tensorcircuit.applications.van.NMF.call:1 +#: tensorcircuit.applications.van.PixelCNN.call:1 +msgid "Calls the model on new inputs and returns the outputs as tensors." msgstr "" -#: of tensorcircuit.backends.jax_backend.JaxBackend.softmax:1 -#: tensorcircuit.backends.numpy_backend.NumpyBackend.softmax:1 -#: tensorcircuit.backends.pytorch_backend.PyTorchBackend.softmax:1 -#: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.softmax:1 +#: of tensorcircuit.applications.van.MADE.call:3 +#: tensorcircuit.applications.van.NMF.call:3 +#: tensorcircuit.applications.van.PixelCNN.call:3 msgid "" -"Softmax function. Computes the function which rescales elements to the " -"range [0,1] such that the elements along axis sum to 1." +"In this case `call()` just reapplies all ops in the graph to the new " +"inputs (e.g. build a new computational graph from the provided inputs)." msgstr "" -#: of tensorcircuit.backends.jax_backend.JaxBackend.softmax:4 -#: tensorcircuit.backends.numpy_backend.NumpyBackend.softmax:4 -#: tensorcircuit.backends.pytorch_backend.PyTorchBackend.softmax:4 -#: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.softmax:4 -msgid "\\mathrm{softmax}(x) = \\frac{\\exp(x_i)}{\\sum_j \\exp(x_j)}" +#: of tensorcircuit.applications.van.MADE.call:7 +#: tensorcircuit.applications.van.NMF.call:7 +#: tensorcircuit.applications.van.PixelCNN.call:7 +msgid "" +"Note: This method should not be called directly. It is only meant to be " +"overridden when subclassing `tf.keras.Model`. To call a model on an " +"input, always use the `__call__()` method, i.e. `model(inputs)`, which " +"relies on the underlying `call()` method." msgstr "" -#: of tensorcircuit.backends.jax_backend.JaxBackend.softmax:9 -#: tensorcircuit.backends.numpy_backend.NumpyBackend.softmax:9 -#: tensorcircuit.backends.pytorch_backend.PyTorchBackend.softmax:9 -#: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.softmax:9 -msgid "Tensor" +#: of tensorcircuit.applications.van.MADE.call:12 +#: tensorcircuit.applications.van.NMF.call:12 +#: tensorcircuit.applications.van.PixelCNN.call:12 +msgid "Input tensor, or dict/list/tuple of input tensors." msgstr "" -#: of tensorcircuit.backends.jax_backend.JaxBackend.softmax:11 -#: tensorcircuit.backends.numpy_backend.NumpyBackend.softmax:11 -#: tensorcircuit.backends.pytorch_backend.PyTorchBackend.softmax:11 -#: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.softmax:11 +#: of tensorcircuit.applications.van.MADE.call:13 +#: tensorcircuit.applications.van.NMF.call:13 +#: tensorcircuit.applications.van.PixelCNN.call:13 msgid "" -"A dimension along which Softmax will be computed , defaults to None for " -"all axis sum." +"Boolean or boolean scalar tensor, indicating whether to run the `Network`" +" in training mode or inference mode." msgstr "" -#: of tensorcircuit.backends.jax_backend.JaxBackend.softmax:13 -#: tensorcircuit.backends.jax_backend.JaxBackend.stack:7 -#: tensorcircuit.backends.numpy_backend.NumpyBackend.softmax:13 -#: tensorcircuit.backends.numpy_backend.NumpyBackend.stack:7 -#: tensorcircuit.backends.pytorch_backend.PyTorchBackend.softmax:13 -#: tensorcircuit.backends.pytorch_backend.PyTorchBackend.stack:7 -#: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.softmax:13 -#: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.stack:7 -msgid "concatenated tensor" +#: of tensorcircuit.applications.van.MADE.call:15 +#: tensorcircuit.applications.van.NMF.call:15 +#: tensorcircuit.applications.van.PixelCNN.call:15 +msgid "" +"A mask or list of masks. A mask can be either a boolean tensor or None " +"(no mask). For more details, check the guide " +"[here](https://www.tensorflow.org/guide/keras/masking_and_padding)." msgstr "" -#: of tensorcircuit.backends.jax_backend.JaxBackend.solve:1 -#: tensorcircuit.backends.numpy_backend.NumpyBackend.solve:1 -#: tensorcircuit.backends.pytorch_backend.PyTorchBackend.solve:1 -#: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.solve:1 -msgid "Solve the linear system Ax=b and return the solution x." +#: of tensorcircuit.applications.van.MADE.call:19 +#: tensorcircuit.applications.van.NMF.call:19 +#: tensorcircuit.applications.van.PixelCNN.call:19 +msgid "" +"A tensor if there is a single output, or a list of tensors if there are " +"more than one outputs." msgstr "" -#: of tensorcircuit.backends.jax_backend.JaxBackend.solve:3 -#: tensorcircuit.backends.numpy_backend.NumpyBackend.solve:3 -#: tensorcircuit.backends.pytorch_backend.PyTorchBackend.solve:3 -#: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.solve:3 -msgid "The multiplied matrix." +#: keras.src.engine.training.Model.compile:1 of +msgid "Configures the model for training." msgstr "" -#: of tensorcircuit.backends.jax_backend.JaxBackend.solve:5 -#: tensorcircuit.backends.numpy_backend.NumpyBackend.solve:5 -#: tensorcircuit.backends.pytorch_backend.PyTorchBackend.solve:5 -#: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.solve:5 -msgid "The resulted matrix." +#: keras.src.engine.training.Model.compile:5 of +msgid "" +"```python " +"model.compile(optimizer=tf.keras.optimizers.Adam(learning_rate=1e-3)," msgstr "" -#: of tensorcircuit.backends.jax_backend.JaxBackend.solve:7 -#: tensorcircuit.backends.numpy_backend.NumpyBackend.solve:7 -#: tensorcircuit.backends.pytorch_backend.PyTorchBackend.solve:7 -#: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.solve:7 -msgid "The solution of the linear system." +#: keras.src.engine.training.Model.compile:7 of +msgid "" +"loss=tf.keras.losses.BinaryCrossentropy(), " +"metrics=[tf.keras.metrics.BinaryAccuracy()," msgstr "" -#: of tensorcircuit.backends.jax_backend.JaxBackend.sparse_dense_matmul:1 -#: tensorcircuit.backends.numpy_backend.NumpyBackend.sparse_dense_matmul:1 -#: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.sparse_dense_matmul:1 -msgid "A sparse matrix multiplies a dense matrix." +#: keras.src.engine.training.Model.compile:9 of +msgid "tf.keras.metrics.FalseNegatives()])" msgstr "" -#: of tensorcircuit.backends.jax_backend.JaxBackend.sparse_dense_matmul:3 -#: tensorcircuit.backends.jax_backend.JaxBackend.to_dense:3 -#: tensorcircuit.backends.numpy_backend.NumpyBackend.sparse_dense_matmul:3 -#: tensorcircuit.backends.numpy_backend.NumpyBackend.to_dense:3 -#: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.sparse_dense_matmul:3 -#: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.to_dense:3 -msgid "a sparse matrix" +#: keras.src.engine.training.Model.compile:12 of +msgid "" +"String (name of optimizer) or optimizer instance. See " +"`tf.keras.optimizers`." msgstr "" -#: of tensorcircuit.backends.jax_backend.JaxBackend.sparse_dense_matmul:5 -#: tensorcircuit.backends.numpy_backend.NumpyBackend.sparse_dense_matmul:5 -#: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.sparse_dense_matmul:5 -msgid "a dense matrix" +#: keras.src.engine.training.Model.compile:14 of +msgid "" +"Loss function. May be a string (name of loss function), or a " +"`tf.keras.losses.Loss` instance. See `tf.keras.losses`. A loss function " +"is any callable with the signature `loss = fn(y_true, y_pred)`, where " +"`y_true` are the ground truth values, and `y_pred` are the model's " +"predictions. `y_true` should have shape `(batch_size, d0, .. dN)` (except" +" in the case of sparse loss functions such as sparse categorical " +"crossentropy which expects integer arrays of shape `(batch_size, d0, .. " +"dN-1)`). `y_pred` should have shape `(batch_size, d0, .. dN)`. The loss " +"function should return a float tensor. If a custom `Loss` instance is " +"used and reduction is set to `None`, return value has shape `(batch_size," +" d0, .. dN-1)` i.e. per-sample or per-timestep loss values; otherwise, it" +" is a scalar. If the model has multiple outputs, you can use a different " +"loss on each output by passing a dictionary or a list of losses. The loss" +" value that will be minimized by the model will then be the sum of all " +"individual losses, unless `loss_weights` is specified." msgstr "" -#: of tensorcircuit.backends.jax_backend.JaxBackend.sparse_dense_matmul:7 -#: tensorcircuit.backends.numpy_backend.NumpyBackend.sparse_dense_matmul:7 -#: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.sparse_dense_matmul:7 -msgid "dense matrix" +#: keras.src.engine.training.Model.compile:34 of +msgid "" +"List of metrics to be evaluated by the model during training and testing." +" Each of this can be a string (name of a built-in function), function or " +"a `tf.keras.metrics.Metric` instance. See `tf.keras.metrics`. Typically " +"you will use `metrics=['accuracy']`. A function is any callable with the " +"signature `result = fn(y_true, y_pred)`. To specify different metrics for" +" different outputs of a multi-output model, you could also pass a " +"dictionary, such as `metrics={'output_a':'accuracy', " +"'output_b':['accuracy', 'mse']}`. You can also pass a list to specify a " +"metric or a list of metrics for each output, such as " +"`metrics=[['accuracy'], ['accuracy', 'mse']]` or `metrics=['accuracy', " +"['accuracy', 'mse']]`. When you pass the strings 'accuracy' or 'acc', we " +"convert this to one of `tf.keras.metrics.BinaryAccuracy`, " +"`tf.keras.metrics.CategoricalAccuracy`, " +"`tf.keras.metrics.SparseCategoricalAccuracy` based on the shapes of the " +"targets and of the model output. We do a similar conversion for the " +"strings 'crossentropy' and 'ce' as well. The metrics passed here are " +"evaluated without sample weighting; if you would like sample weighting to" +" apply, you can specify your metrics via the `weighted_metrics` argument " +"instead." msgstr "" -#: of tensorcircuit.backends.jax_backend.JaxBackend.stack:1 -#: tensorcircuit.backends.numpy_backend.NumpyBackend.stack:1 -#: tensorcircuit.backends.pytorch_backend.PyTorchBackend.stack:1 -#: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.stack:1 -msgid "Concatenates a sequence of tensors ``a`` along a new dimension ``axis``." +#: keras.src.engine.training.Model.compile:56 of +msgid "" +"Optional list or dictionary specifying scalar coefficients (Python " +"floats) to weight the loss contributions of different model outputs. The " +"loss value that will be minimized by the model will then be the *weighted" +" sum* of all individual losses, weighted by the `loss_weights` " +"coefficients. If a list, it is expected to have a 1:1 mapping to the " +"model's outputs. If a dict, it is expected to map output names (strings) " +"to scalar coefficients." msgstr "" -#: of tensorcircuit.backends.jax_backend.JaxBackend.stack:3 -#: tensorcircuit.backends.numpy_backend.NumpyBackend.stack:3 -#: tensorcircuit.backends.pytorch_backend.PyTorchBackend.stack:3 -#: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.stack:3 -msgid "List of tensors in the same shape" +#: keras.src.engine.training.Model.compile:64 of +msgid "" +"List of metrics to be evaluated and weighted by `sample_weight` or " +"`class_weight` during training and testing." msgstr "" -#: of tensorcircuit.backends.jax_backend.JaxBackend.stack:5 -#: tensorcircuit.backends.numpy_backend.NumpyBackend.stack:5 -#: tensorcircuit.backends.pytorch_backend.PyTorchBackend.stack:5 -#: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.stack:5 -msgid "the stack axis, defaults to 0" +#: keras.src.engine.training.Model.compile:66 of +msgid "" +"Bool. If `True`, this `Model`'s logic will not be wrapped in a " +"`tf.function`. Recommended to leave this as `None` unless your `Model` " +"cannot be run inside a `tf.function`. `run_eagerly=True` is not supported" +" when using `tf.distribute.experimental.ParameterServerStrategy`. " +"Defaults to `False`." msgstr "" -#: of tensorcircuit.backends.jax_backend.JaxBackend.stateful_randn:5 -#: tensorcircuit.backends.jax_backend.JaxBackend.stateful_randu:3 -#: tensorcircuit.backends.numpy_backend.NumpyBackend.stateful_randn:5 -#: tensorcircuit.backends.numpy_backend.NumpyBackend.stateful_randu:3 -#: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.stateful_randn:5 -#: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.stateful_randu:3 -msgid "stateful register for each package" +#: keras.src.engine.training.Model.compile:66 of +msgid "" +"Bool. If `True`, this `Model`'s logic will not be wrapped in a " +"`tf.function`. Recommended to leave this as `None` unless your `Model` " +"cannot be run inside a `tf.function`. `run_eagerly=True` is not supported" +" when using `tf.distribute.experimental.ParameterServerStrategy`. " +"Defaults to" msgstr "" -#: of tensorcircuit.backends.jax_backend.JaxBackend.stateful_randn:7 -#: tensorcircuit.backends.numpy_backend.NumpyBackend.stateful_randn:7 -#: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.stateful_randn:7 -msgid "shape of output sampling tensor" +#: keras.src.engine.training.Model.compile:71 of +msgid "`False`." msgstr "" -#: of tensorcircuit.backends.jax_backend.JaxBackend.stateful_randn:13 -#: tensorcircuit.backends.jax_backend.JaxBackend.stateful_randu:11 -#: tensorcircuit.backends.numpy_backend.NumpyBackend.stateful_randn:13 -#: tensorcircuit.backends.numpy_backend.NumpyBackend.stateful_randu:11 -#: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.stateful_randn:13 -#: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.stateful_randu:11 -msgid "only real data type is supported, \"32\" or \"64\", defaults to \"32\"" +#: keras.src.engine.training.Model.compile:72 of +msgid "" +"Int. The number of batches to run during each `tf.function` call. Running" +" multiple batches inside a single `tf.function` call can greatly improve " +"performance on TPUs or small models with a large Python overhead. At " +"most, one full epoch will be run each execution. If a number larger than " +"the size of the epoch is passed, the execution will be truncated to the " +"size of the epoch. Note that if `steps_per_execution` is set to `N`, " +"`Callback.on_batch_begin` and `Callback.on_batch_end` methods will only " +"be called every `N` batches (i.e. before/after each `tf.function` " +"execution). Defaults to `1`." msgstr "" -#: of tensorcircuit.backends.jax_backend.JaxBackend.stateful_randu:1 -#: tensorcircuit.backends.numpy_backend.NumpyBackend.stateful_randu:1 -#: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.stateful_randu:1 -msgid "Uniform random sampler from ``low`` to ``high``." +#: keras.src.engine.training.Model.compile:82 of +msgid "" +"If `True`, compile the model training step with XLA. " +"[XLA](https://www.tensorflow.org/xla) is an optimizing compiler for " +"machine learning. `jit_compile` is not enabled for by default. Note that " +"`jit_compile=True` may not necessarily work for all models. For more " +"information on supported operations please refer to the [XLA " +"documentation](https://www.tensorflow.org/xla). Also refer to [known XLA " +"issues](https://www.tensorflow.org/xla/known_issues) for more details." +msgstr "" + +#: keras.src.engine.training.Model.compile:93 of +msgid "" +"Integer or 'auto'. Used for `tf.distribute.ParameterServerStrategy` " +"training only. This arg sets the number of shards to split the dataset " +"into, to enable an exact visitation guarantee for evaluation, meaning the" +" model will be applied to each dataset element exactly once, even if " +"workers fail. The dataset must be sharded to ensure separate workers do " +"not process the same data. The number of shards should be at least the " +"number of workers for good performance. A value of 'auto' turns on exact " +"evaluation and uses a heuristic for the number of shards based on the " +"number of workers. 0, meaning no visitation guarantee is provided. NOTE: " +"Custom implementations of `Model.test_step` will be ignored when doing " +"exact evaluation. Defaults to `0`." msgstr "" -#: of tensorcircuit.backends.jax_backend.JaxBackend.stateful_randu:5 -#: tensorcircuit.backends.numpy_backend.NumpyBackend.stateful_randu:5 -#: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.stateful_randu:5 -msgid "shape of output sampling tensor, defaults to 1" +#: keras.src.engine.training.Model.compile:106 of +msgid "Arguments supported for backwards compatibility only." msgstr "" -#: of tensorcircuit.backends.jax_backend.JaxBackend.stop_gradient:1 -#: tensorcircuit.backends.numpy_backend.NumpyBackend.stop_gradient:1 -#: tensorcircuit.backends.pytorch_backend.PyTorchBackend.stop_gradient:1 -#: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.stop_gradient:1 -msgid "Stop backpropagation from ``a``." +#: keras.src.engine.training.Model.compile_from_config:1 of +msgid "Compiles the model with the information given in config." msgstr "" -#: of tensorcircuit.backends.jax_backend.JaxBackend.switch:1 -#: tensorcircuit.backends.numpy_backend.NumpyBackend.switch:1 -#: tensorcircuit.backends.pytorch_backend.PyTorchBackend.switch:1 -#: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.switch:1 -msgid "``branches[index]()``" +#: keras.src.engine.training.Model.compile_from_config:3 of +msgid "" +"This method uses the information in the config (optimizer, loss, metrics," +" etc.) to compile the model." msgstr "" -#: of tensorcircuit.backends.jax_backend.JaxBackend.tan:1 -#: tensorcircuit.backends.numpy_backend.NumpyBackend.tan:1 -#: tensorcircuit.backends.pytorch_backend.PyTorchBackend.tan:1 -#: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.tan:1 -msgid "Return the tan of a tensor ``a``." +#: keras.src.engine.training.Model.compile_from_config:6 of +msgid "Dict containing information for compiling the model." msgstr "" -#: of tensorcircuit.backends.jax_backend.JaxBackend.tan:5 -#: tensorcircuit.backends.numpy_backend.NumpyBackend.tan:5 -#: tensorcircuit.backends.pytorch_backend.PyTorchBackend.tan:5 -#: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.tan:5 -msgid "tan of ``a``" +#: of tensorcircuit.applications.van.MADE.compute_dtype:1 +#: tensorcircuit.applications.van.MaskedConv2D.compute_dtype:1 +#: tensorcircuit.applications.van.MaskedLinear.compute_dtype:1 +#: tensorcircuit.applications.van.NMF.compute_dtype:1 +#: tensorcircuit.applications.van.PixelCNN.compute_dtype:1 +#: tensorcircuit.applications.van.ResidualBlock.compute_dtype:1 +#: tensorcircuit.applications.vqes.Linear.compute_dtype:1 +#: tensorcircuit.keras.HardwareLayer.compute_dtype:1 +#: tensorcircuit.keras.QuantumLayer.compute_dtype:1 +msgid "The dtype of the layer's computations." msgstr "" -#: of tensorcircuit.backends.jax_backend.JaxBackend.tanh:1 -#: tensorcircuit.backends.numpy_backend.NumpyBackend.tanh:1 -#: tensorcircuit.backends.pytorch_backend.PyTorchBackend.tanh:1 -#: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.tanh:1 -msgid "Return the tanh of a tensor ``a``." +#: of tensorcircuit.applications.van.MADE.compute_dtype:3 +#: tensorcircuit.applications.van.MaskedConv2D.compute_dtype:3 +#: tensorcircuit.applications.van.MaskedLinear.compute_dtype:3 +#: tensorcircuit.applications.van.NMF.compute_dtype:3 +#: tensorcircuit.applications.van.PixelCNN.compute_dtype:3 +#: tensorcircuit.applications.van.ResidualBlock.compute_dtype:3 +#: tensorcircuit.applications.vqes.Linear.compute_dtype:3 +#: tensorcircuit.keras.HardwareLayer.compute_dtype:3 +#: tensorcircuit.keras.QuantumLayer.compute_dtype:3 +msgid "" +"This is equivalent to `Layer.dtype_policy.compute_dtype`. Unless mixed " +"precision is used, this is the same as `Layer.dtype`, the dtype of the " +"weights." msgstr "" -#: of tensorcircuit.backends.jax_backend.JaxBackend.tanh:5 -#: tensorcircuit.backends.numpy_backend.NumpyBackend.tanh:5 -#: tensorcircuit.backends.pytorch_backend.PyTorchBackend.tanh:5 -#: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.tanh:5 -msgid "tanh of ``a``" +#: of tensorcircuit.applications.van.MADE.compute_dtype:7 +#: tensorcircuit.applications.van.MaskedConv2D.compute_dtype:7 +#: tensorcircuit.applications.van.MaskedLinear.compute_dtype:7 +#: tensorcircuit.applications.van.NMF.compute_dtype:7 +#: tensorcircuit.applications.van.PixelCNN.compute_dtype:7 +#: tensorcircuit.applications.van.ResidualBlock.compute_dtype:7 +#: tensorcircuit.applications.vqes.Linear.compute_dtype:7 +#: tensorcircuit.keras.HardwareLayer.compute_dtype:7 +#: tensorcircuit.keras.QuantumLayer.compute_dtype:7 +msgid "" +"Layers automatically cast their inputs to the compute dtype, which causes" +" computations and the output to be in the compute dtype as well. This is " +"done by the base Layer class in `Layer.__call__`, so you do not have to " +"insert these casts if implementing your own layer." msgstr "" -#: of tensorcircuit.backends.jax_backend.JaxBackend.tile:1 -#: tensorcircuit.backends.numpy_backend.NumpyBackend.tile:1 -#: tensorcircuit.backends.pytorch_backend.PyTorchBackend.tile:1 -#: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.tile:1 -msgid "Constructs a tensor by tiling a given tensor." +#: of tensorcircuit.applications.van.MADE.compute_dtype:12 +#: tensorcircuit.applications.van.MaskedConv2D.compute_dtype:12 +#: tensorcircuit.applications.van.MaskedLinear.compute_dtype:12 +#: tensorcircuit.applications.van.NMF.compute_dtype:12 +#: tensorcircuit.applications.van.PixelCNN.compute_dtype:12 +#: tensorcircuit.applications.van.ResidualBlock.compute_dtype:12 +#: tensorcircuit.applications.vqes.Linear.compute_dtype:12 +#: tensorcircuit.keras.HardwareLayer.compute_dtype:12 +#: tensorcircuit.keras.QuantumLayer.compute_dtype:12 +msgid "" +"Layers often perform certain internal computations in higher precision " +"when `compute_dtype` is float16 or bfloat16 for numeric stability. The " +"output will still typically be float16 or bfloat16 in such cases." msgstr "" -#: of tensorcircuit.backends.jax_backend.JaxBackend.tile:5 -#: tensorcircuit.backends.numpy_backend.NumpyBackend.tile:5 -#: tensorcircuit.backends.pytorch_backend.PyTorchBackend.tile:5 -#: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.tile:5 -msgid "1d tensor with length the same as the rank of ``a``" +#: of tensorcircuit.applications.van.MADE.compute_dtype:16 +#: tensorcircuit.applications.van.MaskedConv2D.compute_dtype:16 +#: tensorcircuit.applications.van.MaskedLinear.compute_dtype:16 +#: tensorcircuit.applications.van.NMF.compute_dtype:16 +#: tensorcircuit.applications.van.PixelCNN.compute_dtype:16 +#: tensorcircuit.applications.van.ResidualBlock.compute_dtype:16 +#: tensorcircuit.applications.vqes.Linear.compute_dtype:16 +#: tensorcircuit.keras.HardwareLayer.compute_dtype:16 +#: tensorcircuit.keras.QuantumLayer.compute_dtype:16 +msgid "The layer's compute dtype." msgstr "" -#: of tensorcircuit.backends.jax_backend.JaxBackend.to_dense:1 -#: tensorcircuit.backends.numpy_backend.NumpyBackend.to_dense:1 -#: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.to_dense:1 -msgid "Convert a sparse matrix to dense tensor." +#: keras.src.engine.training.Model.compute_loss:1 of +msgid "Compute the total loss, validate it, and return it." msgstr "" -#: of tensorcircuit.backends.jax_backend.JaxBackend.to_dense:5 -#: tensorcircuit.backends.numpy_backend.NumpyBackend.to_dense:5 -#: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.to_dense:5 -msgid "the resulted dense matrix" +#: keras.src.engine.training.Model.compute_loss:3 of +msgid "" +"Subclasses can optionally override this method to provide custom loss " +"computation logic." msgstr "" -#: of tensorcircuit.backends.jax_backend.JaxBackend.tree_flatten:1 -#: tensorcircuit.backends.pytorch_backend.PyTorchBackend.tree_flatten:1 -msgid "Flatten python structure to 1D list" +#: keras.src.engine.training.Model.compute_loss:6 of +msgid "Example: ```python class MyModel(tf.keras.Model):" msgstr "" -#: of tensorcircuit.backends.jax_backend.JaxBackend.tree_flatten:3 -#: tensorcircuit.backends.pytorch_backend.PyTorchBackend.tree_flatten:3 -msgid "python structure to be flattened" +#: keras.src.engine.training.Model.compute_loss:12 of +msgid "def __init__(self, *args, **kwargs):" msgstr "" -#: of tensorcircuit.backends.jax_backend.JaxBackend.tree_flatten:5 -#: tensorcircuit.backends.pytorch_backend.PyTorchBackend.tree_flatten:5 +#: keras.src.engine.training.Model.compute_loss:11 of msgid "" -"The 1D list of flattened structure and treedef which can be used for " -"later unflatten" -msgstr "" - -#: of tensorcircuit.backends.jax_backend.JaxBackend.tree_map:1 -#: tensorcircuit.backends.pytorch_backend.PyTorchBackend.tree_map:1 -msgid "Return the new tree map with multiple arg function ``f`` through pytrees." +"super(MyModel, self).__init__(*args, **kwargs) self.loss_tracker = " +"tf.keras.metrics.Mean(name='loss')" msgstr "" -#: of tensorcircuit.backends.jax_backend.JaxBackend.tree_map:3 -#: tensorcircuit.backends.pytorch_backend.PyTorchBackend.tree_map:3 -msgid "The function" +#: keras.src.engine.training.Model.compute_loss:18 of +msgid "def compute_loss(self, x, y, y_pred, sample_weight):" msgstr "" -#: of tensorcircuit.backends.jax_backend.JaxBackend.tree_map:5 -#: tensorcircuit.backends.pytorch_backend.PyTorchBackend.tree_map:5 -msgid "inputs as any python structure" +#: keras.src.engine.training.Model.compute_loss:15 of +msgid "" +"loss = tf.reduce_mean(tf.math.squared_difference(y_pred, y)) loss += " +"tf.add_n(self.losses) self.loss_tracker.update_state(loss) return loss" msgstr "" -#: of tensorcircuit.backends.jax_backend.JaxBackend.tree_map:7 -#: tensorcircuit.backends.pytorch_backend.PyTorchBackend.tree_map:7 -msgid "raise when neither tensorflow or jax is installed." +#: keras.src.engine.training.Model.compute_loss:21 of +msgid "def reset_metrics(self):" msgstr "" -#: of tensorcircuit.backends.jax_backend.JaxBackend.tree_map:8 -#: tensorcircuit.backends.pytorch_backend.PyTorchBackend.tree_map:8 -msgid "The new tree map with the same structure but different values." +#: keras.src.engine.training.Model.compute_loss:21 of +msgid "self.loss_tracker.reset_states()" msgstr "" -#: of tensorcircuit.backends.jax_backend.JaxBackend.tree_unflatten:1 -#: tensorcircuit.backends.pytorch_backend.PyTorchBackend.tree_unflatten:1 -msgid "Pack 1D list to pytree defined via ``treedef``" +#: keras.src.engine.training.Model.compute_loss:23 of +msgid "@property def metrics(self):" msgstr "" -#: of tensorcircuit.backends.jax_backend.JaxBackend.tree_unflatten:3 -#: tensorcircuit.backends.pytorch_backend.PyTorchBackend.tree_unflatten:3 -msgid "Def of pytree structure, the second return from ``tree_flatten``" +#: keras.src.engine.training.Model.compute_loss:25 of +msgid "return [self.loss_tracker]" msgstr "" -#: of tensorcircuit.backends.jax_backend.JaxBackend.tree_unflatten:5 -#: tensorcircuit.backends.pytorch_backend.PyTorchBackend.tree_unflatten:5 -msgid "the 1D list of flattened data structure" +#: keras.src.engine.training.Model.compute_loss:27 of +msgid "" +"tensors = tf.random.uniform((10, 10)), tf.random.uniform((10,)) dataset =" +" tf.data.Dataset.from_tensor_slices(tensors).repeat().batch(1)" msgstr "" -#: of tensorcircuit.backends.jax_backend.JaxBackend.tree_unflatten:7 -#: tensorcircuit.backends.pytorch_backend.PyTorchBackend.tree_unflatten:7 -msgid "Packed pytree" +#: keras.src.engine.training.Model.compute_loss:30 of +msgid "" +"inputs = tf.keras.layers.Input(shape=(10,), name='my_input') outputs = " +"tf.keras.layers.Dense(10)(inputs) model = MyModel(inputs, outputs) " +"model.add_loss(tf.reduce_sum(outputs))" msgstr "" -#: of tensorcircuit.backends.jax_backend.JaxBackend.unique_with_counts:1 -#: tensorcircuit.backends.numpy_backend.NumpyBackend.unique_with_counts:1 -#: tensorcircuit.backends.pytorch_backend.PyTorchBackend.unique_with_counts:1 -#: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.unique_with_counts:1 +#: keras.src.engine.training.Model.compute_loss:35 of msgid "" -"Find the unique elements and their corresponding counts of the given " -"tensor ``a``." +"optimizer = tf.keras.optimizers.SGD() model.compile(optimizer, " +"loss='mse', steps_per_execution=10) model.fit(dataset, epochs=2, " +"steps_per_epoch=10) print('My custom loss: ', " +"model.loss_tracker.result().numpy()) ```" msgstr "" -#: of tensorcircuit.backends.jax_backend.JaxBackend.unique_with_counts:5 -#: tensorcircuit.backends.numpy_backend.NumpyBackend.unique_with_counts:5 -#: tensorcircuit.backends.pytorch_backend.PyTorchBackend.unique_with_counts:5 -#: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.unique_with_counts:5 -msgid "Unique elements, corresponding counts" +#: keras.src.engine.training.Model.compute_loss:41 +#: keras.src.engine.training.Model.compute_metrics:23 of +msgid "Input data." msgstr "" -#: of tensorcircuit.backends.jax_backend.JaxBackend.value_and_grad:1 -#: tensorcircuit.backends.numpy_backend.NumpyBackend.value_and_grad:1 -#: tensorcircuit.backends.pytorch_backend.PyTorchBackend.value_and_grad:1 -#: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.value_and_grad:1 -msgid "Return the function which returns the value and grad of ``f``." +#: keras.src.engine.training.Model.compute_loss:42 +#: keras.src.engine.training.Model.compute_metrics:24 of +msgid "Target data." msgstr "" -#: of tensorcircuit.backends.jax_backend.JaxBackend.value_and_grad:17 -#: tensorcircuit.backends.numpy_backend.NumpyBackend.value_and_grad:17 -#: tensorcircuit.backends.pytorch_backend.PyTorchBackend.value_and_grad:17 -#: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.value_and_grad:17 -msgid "" -"the value and grad function of ``f`` with the same set of arguments as " -"``f``" +#: keras.src.engine.training.Model.compute_loss:43 of +msgid "Predictions returned by the model (output of `model(x)`)" msgstr "" -#: of tensorcircuit.backends.jax_backend.JaxBackend.vectorized_value_and_grad:1 -#: tensorcircuit.backends.numpy_backend.NumpyBackend.vectorized_value_and_grad:1 -#: tensorcircuit.backends.pytorch_backend.PyTorchBackend.vectorized_value_and_grad:1 -#: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.vectorized_value_and_grad:1 -msgid "" -"Return the VVAG function of ``f``. The inputs for ``f`` is (args[0], " -"args[1], args[2], ...), and the output of ``f`` is a scalar. Suppose " -"VVAG(f) is a function with inputs in the form (vargs[0], args[1], " -"args[2], ...), where vagrs[0] has one extra dimension than args[0] in the" -" first axis and consistent with args[0] in shape for remaining " -"dimensions, i.e. shape(vargs[0]) = [batch] + shape(args[0]). (We only " -"cover cases where ``vectorized_argnums`` defaults to 0 here for " -"demonstration). VVAG(f) returns a tuple as a value tensor with shape " -"[batch, 1] and a gradient tuple with shape: ([batch]+shape(args[argnum]) " -"for argnum in argnums). The gradient for argnums=k is defined as" +#: keras.src.engine.training.Model.compute_loss:44 +#: keras.src.engine.training.Model.compute_metrics:26 of +msgid "Sample weights for weighting the loss function." msgstr "" -#: of tensorcircuit.backends.jax_backend.JaxBackend.vectorized_value_and_grad:9 -#: tensorcircuit.backends.numpy_backend.NumpyBackend.vectorized_value_and_grad:9 -#: tensorcircuit.backends.pytorch_backend.PyTorchBackend.vectorized_value_and_grad:9 -#: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.vectorized_value_and_grad:9 +#: keras.src.engine.training.Model.compute_loss:46 of msgid "" -"g^k = \\frac{\\partial \\sum_{i\\in batch} f(vargs[0][i], args[1], " -"...)}{\\partial args[k]}" +"The total loss as a `tf.Tensor`, or `None` if no loss results (which is " +"the case when called by `Model.test_step`)." msgstr "" -#: of -#: tensorcircuit.backends.jax_backend.JaxBackend.vectorized_value_and_grad:13 -#: tensorcircuit.backends.numpy_backend.NumpyBackend.vectorized_value_and_grad:13 -#: tensorcircuit.backends.pytorch_backend.PyTorchBackend.vectorized_value_and_grad:13 -#: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.vectorized_value_and_grad:13 -msgid "Therefore, if argnums=0, the gradient is reduced to" +#: keras.src.engine.base_layer.Layer.compute_mask:1 of +msgid "Computes an output mask tensor." msgstr "" -#: of -#: tensorcircuit.backends.jax_backend.JaxBackend.vectorized_value_and_grad:15 -#: tensorcircuit.backends.numpy_backend.NumpyBackend.vectorized_value_and_grad:15 -#: tensorcircuit.backends.pytorch_backend.PyTorchBackend.vectorized_value_and_grad:15 -#: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.vectorized_value_and_grad:15 -msgid "g^0_i = \\frac{\\partial f(vargs[0][i])}{\\partial vargs[0][i]}" +#: keras.src.engine.base_layer.Layer.compute_mask:3 +#: keras.src.engine.base_layer.Layer.compute_mask:4 of +msgid "Tensor or list of tensors." msgstr "" -#: of -#: tensorcircuit.backends.jax_backend.JaxBackend.vectorized_value_and_grad:19 -#: tensorcircuit.backends.numpy_backend.NumpyBackend.vectorized_value_and_grad:19 -#: tensorcircuit.backends.pytorch_backend.PyTorchBackend.vectorized_value_and_grad:19 -#: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.vectorized_value_and_grad:19 +#: keras.src.engine.base_layer.Layer.compute_mask:6 of msgid "" -", which is specifically suitable for batched VQE optimization, where " -"args[0] is the circuit parameters." +"None or a tensor (or list of tensors, one per output tensor of the " +"layer)." msgstr "" -#: of -#: tensorcircuit.backends.jax_backend.JaxBackend.vectorized_value_and_grad:21 -#: tensorcircuit.backends.numpy_backend.NumpyBackend.vectorized_value_and_grad:21 -#: tensorcircuit.backends.pytorch_backend.PyTorchBackend.vectorized_value_and_grad:21 -#: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.vectorized_value_and_grad:21 -msgid "And if argnums=1, the gradient is like" +#: keras.src.engine.base_layer.Layer.compute_mask:8 of +msgid "None or a tensor (or list of tensors," msgstr "" -#: of -#: tensorcircuit.backends.jax_backend.JaxBackend.vectorized_value_and_grad:23 -#: tensorcircuit.backends.numpy_backend.NumpyBackend.vectorized_value_and_grad:23 -#: tensorcircuit.backends.pytorch_backend.PyTorchBackend.vectorized_value_and_grad:23 -#: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.vectorized_value_and_grad:23 -msgid "" -"g^1_i = \\frac{\\partial \\sum_j f(vargs[0][j], args[1])}{\\partial " -"args[1][i]}\n" -"\n" +#: keras.src.engine.base_layer.Layer.compute_mask:9 of +msgid "one per output tensor of the layer)." msgstr "" -#: of -#: tensorcircuit.backends.jax_backend.JaxBackend.vectorized_value_and_grad:26 -#: tensorcircuit.backends.numpy_backend.NumpyBackend.vectorized_value_and_grad:26 -#: tensorcircuit.backends.pytorch_backend.PyTorchBackend.vectorized_value_and_grad:26 -#: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.vectorized_value_and_grad:26 -msgid "" -", which is suitable for quantum machine learning scenarios, where ``f`` " -"is the loss function, args[0] corresponds to the input data and args[1] " -"corresponds to the weights in the QML model." +#: keras.src.engine.training.Model.compute_metrics:1 of +msgid "Update metric states and collect all metrics to be returned." msgstr "" -#: of -#: tensorcircuit.backends.jax_backend.JaxBackend.vectorized_value_and_grad:33 -#: tensorcircuit.backends.jax_backend.JaxBackend.vmap:6 -#: tensorcircuit.backends.numpy_backend.NumpyBackend.vectorized_value_and_grad:33 -#: tensorcircuit.backends.numpy_backend.NumpyBackend.vmap:6 -#: tensorcircuit.backends.pytorch_backend.PyTorchBackend.vectorized_value_and_grad:33 -#: tensorcircuit.backends.pytorch_backend.PyTorchBackend.vmap:6 -#: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.vectorized_value_and_grad:33 -#: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.vmap:6 +#: keras.src.engine.training.Model.compute_metrics:3 of msgid "" -"the args to be vectorized, these arguments should share the same batch " -"shape in the fist dimension" +"Subclasses can optionally override this method to provide custom metric " +"updating and collection logic." msgstr "" -#: of tensorcircuit.backends.jax_backend.JaxBackend.vjp:1 -#: tensorcircuit.backends.numpy_backend.NumpyBackend.vjp:1 -#: tensorcircuit.backends.pytorch_backend.PyTorchBackend.vjp:1 -#: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.vjp:1 -msgid "" -"Function that computes the dot product between a vector v and the " -"Jacobian of the given function at the point given by the inputs. (reverse" -" mode AD relevant) Strictly speaking, this function is value_and_vjp." +#: keras.src.engine.training.Model.compute_metrics:6 of +msgid "Example: ```python class MyModel(tf.keras.Sequential):" msgstr "" -#: of tensorcircuit.backends.jax_backend.JaxBackend.vjp:5 -#: tensorcircuit.backends.numpy_backend.NumpyBackend.vjp:5 -#: tensorcircuit.backends.pytorch_backend.PyTorchBackend.vjp:5 -#: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.vjp:5 -msgid "the function to carry out vjp calculation" +#: keras.src.engine.training.Model.compute_metrics:10 of +msgid "def compute_metrics(self, x, y, y_pred, sample_weight):" msgstr "" -#: of tensorcircuit.backends.jax_backend.JaxBackend.vjp:9 -#: tensorcircuit.backends.numpy_backend.NumpyBackend.vjp:9 -#: tensorcircuit.backends.pytorch_backend.PyTorchBackend.vjp:9 -#: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.vjp:9 +#: keras.src.engine.training.Model.compute_metrics:12 of msgid "" -"value vector or gradient from downstream in reverse mode AD the same " -"shape as return of function ``f``" +"# This super call updates `self.compiled_metrics` and returns # results " +"for all metrics listed in `self.metrics`. metric_results = super(MyModel," +" self).compute_metrics(" msgstr "" -#: of tensorcircuit.backends.jax_backend.JaxBackend.vjp:12 -#: tensorcircuit.backends.numpy_backend.NumpyBackend.vjp:12 -#: tensorcircuit.backends.pytorch_backend.PyTorchBackend.vjp:12 -#: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.vjp:12 -msgid "(``f(*inputs)``, vjp_tensor), where vjp_tensor is the same shape as inputs" +#: keras.src.engine.training.Model.compute_metrics:15 of +msgid "x, y, y_pred, sample_weight)" msgstr "" -#: of tensorcircuit.backends.jax_backend.JaxBackend.vmap:1 -#: tensorcircuit.backends.numpy_backend.NumpyBackend.vmap:1 -#: tensorcircuit.backends.pytorch_backend.PyTorchBackend.vmap:1 -#: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.vmap:1 +#: keras.src.engine.training.Model.compute_metrics:17 of msgid "" -"Return the vectorized map or batched version of ``f`` on the first extra " -"axis. The general interface supports ``f`` with multiple arguments and " -"broadcast in the fist dimension." -msgstr "" - -#: of tensorcircuit.backends.jax_backend.JaxBackend.vmap:4 -#: tensorcircuit.backends.numpy_backend.NumpyBackend.vmap:4 -#: tensorcircuit.backends.pytorch_backend.PyTorchBackend.vmap:4 -#: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.vmap:4 -msgid "function to be broadcasted." +"# Note that `self.custom_metric` is not listed in `self.metrics`. " +"self.custom_metric.update_state(x, y, y_pred, sample_weight) " +"metric_results['custom_metric_name'] = self.custom_metric.result() return" +" metric_results" msgstr "" -#: of tensorcircuit.backends.jax_backend.JaxBackend.vmap:9 -#: tensorcircuit.backends.numpy_backend.NumpyBackend.vmap:9 -#: tensorcircuit.backends.pytorch_backend.PyTorchBackend.vmap:9 -#: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.vmap:9 -msgid "vmap version of ``f``" +#: keras.src.engine.training.Model.compute_metrics:25 of +msgid "Predictions returned by the model (output of `model.call(x)`)" msgstr "" -#: of tensorcircuit.backends.jax_backend.JaxBackend.zeros:1 -#: tensorcircuit.backends.numpy_backend.NumpyBackend.zeros:1 -#: tensorcircuit.backends.pytorch_backend.PyTorchBackend.zeros:1 -#: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.zeros:1 +#: keras.src.engine.training.Model.compute_metrics:28 of msgid "" -"Return a zeros-matrix of dimension `dim` Depending on specific backends, " -"`dim` has to be either an int (numpy, torch, tensorflow) or a `ShapeType`" -" object (for block-sparse backends)." -msgstr "" - -#: of tensorcircuit.backends.jax_backend.JaxBackend.zeros:5 -#: tensorcircuit.backends.numpy_backend.NumpyBackend.zeros:5 -#: tensorcircuit.backends.pytorch_backend.PyTorchBackend.zeros:5 -#: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.zeros:5 -msgid "Block-sparse behavior is currently not supported Args:" +"A `dict` containing values that will be passed to " +"`tf.keras.callbacks.CallbackList.on_train_batch_end()`. Typically, the " +"values of the metrics listed in `self.metrics` are returned. Example: " +"`{'loss': 0.2, 'accuracy': 0.7}`." msgstr "" -#: ../../source/api/backends/numpy_backend.rst:2 -msgid "tensorcircuit.backends.numpy_backend" -msgstr "" - -#: of tensorcircuit.backends.numpy_backend:1 -msgid "Backend magic inherited from tensornetwork: numpy backend" +#: keras.src.engine.base_layer.Layer.compute_output_shape:1 of +msgid "Computes the output shape of the layer." msgstr "" -#: of tensorcircuit.backends.numpy_backend.NumpyBackend:1 -msgid "Bases: :py:class:`tensornetwork.backends.numpy.numpy_backend.NumPyBackend`" +#: keras.src.engine.base_layer.Layer.compute_output_shape:3 of +msgid "" +"This method will cause the layer's state to be built, if that has not " +"happened before. This requires that the layer will later be used with " +"inputs that match the input shape provided here." msgstr "" -#: of tensorcircuit.backends.numpy_backend.NumpyBackend:1 +#: keras.src.engine.base_layer.Layer.compute_output_shape:7 of msgid "" -"see the original backend API at `numpy backend " -"`_" +"Shape tuple (tuple of integers) or `tf.TensorShape`, or structure of " +"shape tuples / `tf.TensorShape` instances (one per output tensor of the " +"layer). Shape tuples can include None for free dimensions, instead of an " +"integer." msgstr "" -#: ../../source/api/backends/pytorch_backend.rst:2 -msgid "tensorcircuit.backends.pytorch_backend" +#: keras.src.engine.base_layer.Layer.compute_output_shape:13 of +msgid "A `tf.TensorShape` instance or structure of `tf.TensorShape` instances." msgstr "" -#: of tensorcircuit.backends.pytorch_backend:1 -msgid "Backend magic inherited from tensornetwork: pytorch backend" +#: keras.src.engine.base_layer.Layer.compute_output_signature:1 of +msgid "Compute the output tensor signature of the layer based on the inputs." msgstr "" -#: of tensorcircuit.backends.pytorch_backend.PyTorchBackend:1 +#: keras.src.engine.base_layer.Layer.compute_output_signature:3 of msgid "" -"Bases: " -":py:class:`tensornetwork.backends.pytorch.pytorch_backend.PyTorchBackend`" +"Unlike a TensorShape object, a TensorSpec object contains both shape and " +"dtype information for a tensor. This method allows layers to provide " +"output dtype information if it is different from the input dtype. For any" +" layer that doesn't implement this function, the framework will fall back" +" to use `compute_output_shape`, and will assume that the output dtype " +"matches the input dtype." msgstr "" -#: of tensorcircuit.backends.pytorch_backend.PyTorchBackend:1 +#: keras.src.engine.base_layer.Layer.compute_output_signature:10 of msgid "" -"See the original backend API at `pytorch backend " -"`_" +"Single TensorSpec or nested structure of TensorSpec objects, describing a" +" candidate input for the layer." msgstr "" -#: of tensorcircuit.backends.pytorch_backend.PyTorchBackend:4 +#: keras.src.engine.base_layer.Layer.compute_output_signature:13 of msgid "" -"Note the functionality provided by pytorch backend is incomplete, it " -"currenly lacks native efficicent jit and vmap support." +"Single TensorSpec or nested structure of TensorSpec objects, describing" +" how the layer would transform the provided input." msgstr "" -#: of tensorcircuit.backends.pytorch_backend.PyTorchBackend.reverse:1 -msgid "return ``a[::-1]``, only 1D tensor is guaranteed for consistent behavior" +#: keras.src.engine.base_layer.Layer.compute_output_signature:16 of +msgid "Single TensorSpec or nested structure of TensorSpec objects," msgstr "" -#: of tensorcircuit.backends.pytorch_backend.PyTorchBackend.reverse:3 -msgid "1D tensor" +#: keras.src.engine.base_layer.Layer.compute_output_signature:16 of +msgid "describing how the layer would transform the provided input." msgstr "" -#: of tensorcircuit.backends.pytorch_backend.PyTorchBackend.reverse:5 -msgid "1D tensor in reverse order" +#: keras.src.engine.base_layer.Layer.compute_output_signature:18 of +msgid "If input_signature contains a non-TensorSpec object." msgstr "" -#: ../../source/api/backends/tensorflow_backend.rst:2 -msgid "tensorcircuit.backends.tensorflow_backend" +#: keras.src.engine.base_layer.Layer.count_params:1 of +msgid "Count the total number of scalars composing the weights." msgstr "" -#: of tensorcircuit.backends.tensorflow_backend:1 -msgid "Backend magic inherited from tensornetwork: tensorflow backend" +#: keras.src.engine.base_layer.Layer.count_params:3 of +msgid "An integer count." msgstr "" -#: of tensorcircuit.backends.tensorflow_backend.TensorFlowBackend:1 +#: keras.src.engine.base_layer.Layer.count_params:5 of msgid "" -"Bases: " -":py:class:`tensornetwork.backends.tensorflow.tensorflow_backend.TensorFlowBackend`" +"if the layer isn't yet built (in which case its weights aren't yet " +"defined)." msgstr "" -#: of tensorcircuit.backends.tensorflow_backend.TensorFlowBackend:1 +#: of tensorcircuit.applications.van.MADE.distribute_reduction_method:1 +#: tensorcircuit.applications.van.NMF.distribute_reduction_method:1 +#: tensorcircuit.applications.van.PixelCNN.distribute_reduction_method:1 +msgid "The method employed to reduce per-replica values during training." +msgstr "" + +#: of tensorcircuit.applications.van.MADE.distribute_reduction_method:3 +#: tensorcircuit.applications.van.NMF.distribute_reduction_method:3 +#: tensorcircuit.applications.van.PixelCNN.distribute_reduction_method:3 msgid "" -"See the original backend API at `tensorflow backend " -"`_" +"Unless specified, the value \"auto\" will be assumed, indicating that the" +" reduction strategy should be chosen based on the current running " +"environment. See `reduce_per_replica` function for more details." +msgstr "" + +#: of tensorcircuit.applications.van.MADE.distribute_strategy:1 +#: tensorcircuit.applications.van.NMF.distribute_strategy:1 +#: tensorcircuit.applications.van.PixelCNN.distribute_strategy:1 +msgid "The `tf.distribute.Strategy` this model was created under." +msgstr "" + +#: of tensorcircuit.applications.van.MADE.dtype:1 +#: tensorcircuit.applications.van.MaskedConv2D.dtype:1 +#: tensorcircuit.applications.van.MaskedLinear.dtype:1 +#: tensorcircuit.applications.van.NMF.dtype:1 +#: tensorcircuit.applications.van.PixelCNN.dtype:1 +#: tensorcircuit.applications.van.ResidualBlock.dtype:1 +#: tensorcircuit.applications.vqes.Linear.dtype:1 +#: tensorcircuit.keras.HardwareLayer.dtype:1 +#: tensorcircuit.keras.QuantumLayer.dtype:1 +msgid "The dtype of the layer weights." msgstr "" -#: of tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.gather1d:1 +#: of tensorcircuit.applications.van.MADE.dtype:3 +#: tensorcircuit.applications.van.MaskedConv2D.dtype:3 +#: tensorcircuit.applications.van.MaskedLinear.dtype:3 +#: tensorcircuit.applications.van.NMF.dtype:3 +#: tensorcircuit.applications.van.PixelCNN.dtype:3 +#: tensorcircuit.applications.van.ResidualBlock.dtype:3 +#: tensorcircuit.applications.vqes.Linear.dtype:3 +#: tensorcircuit.keras.HardwareLayer.dtype:3 +#: tensorcircuit.keras.QuantumLayer.dtype:3 msgid "" -"Return ``operand[indices]``, both ``operand`` and ``indices`` are rank-1 " -"tensor." +"This is equivalent to `Layer.dtype_policy.variable_dtype`. Unless mixed " +"precision is used, this is the same as `Layer.compute_dtype`, the dtype " +"of the layer's computations." msgstr "" -#: of tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.gather1d:3 -msgid "rank-1 tensor" +#: of tensorcircuit.applications.van.MADE.dtype_policy:1 +#: tensorcircuit.applications.van.MaskedConv2D.dtype_policy:1 +#: tensorcircuit.applications.van.MaskedLinear.dtype_policy:1 +#: tensorcircuit.applications.van.NMF.dtype_policy:1 +#: tensorcircuit.applications.van.PixelCNN.dtype_policy:1 +#: tensorcircuit.applications.van.ResidualBlock.dtype_policy:1 +#: tensorcircuit.applications.vqes.Linear.dtype_policy:1 +#: tensorcircuit.keras.HardwareLayer.dtype_policy:1 +#: tensorcircuit.keras.QuantumLayer.dtype_policy:1 +msgid "The dtype policy associated with this layer." msgstr "" -#: of tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.gather1d:5 -msgid "rank-1 tensor with int dtype" +#: of tensorcircuit.applications.van.MADE.dtype_policy:3 +#: tensorcircuit.applications.van.MaskedConv2D.dtype_policy:3 +#: tensorcircuit.applications.van.MaskedLinear.dtype_policy:3 +#: tensorcircuit.applications.van.NMF.dtype_policy:3 +#: tensorcircuit.applications.van.PixelCNN.dtype_policy:3 +#: tensorcircuit.applications.van.ResidualBlock.dtype_policy:3 +#: tensorcircuit.applications.vqes.Linear.dtype_policy:3 +#: tensorcircuit.keras.HardwareLayer.dtype_policy:3 +#: tensorcircuit.keras.QuantumLayer.dtype_policy:3 +msgid "This is an instance of a `tf.keras.mixed_precision.Policy`." msgstr "" -#: of tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.gather1d:7 -msgid "``operand[indices]``" +#: of tensorcircuit.applications.van.MADE.dynamic:1 +#: tensorcircuit.applications.van.MaskedConv2D.dynamic:1 +#: tensorcircuit.applications.van.MaskedLinear.dynamic:1 +#: tensorcircuit.applications.van.NMF.dynamic:1 +#: tensorcircuit.applications.van.PixelCNN.dynamic:1 +#: tensorcircuit.applications.van.ResidualBlock.dynamic:1 +#: tensorcircuit.applications.vqes.Linear.dynamic:1 +#: tensorcircuit.keras.HardwareLayer.dynamic:1 +#: tensorcircuit.keras.QuantumLayer.dynamic:1 +msgid "Whether the layer is dynamic (eager-only); set in the constructor." msgstr "" -#: ../../source/api/channels.rst:2 -msgid "tensorcircuit.channels" +#: keras.src.engine.training.Model.evaluate:1 of +msgid "Returns the loss value & metrics values for the model in test mode." msgstr "" -#: of tensorcircuit.channels:1 -msgid "Some common noise quantum channels." +#: keras.src.engine.training.Model.evaluate:3 of +msgid "Computation is done in batches (see the `batch_size` arg.)" msgstr "" -#: of tensorcircuit.channels.amplitudedampingchannel:1 +#: keras.src.engine.training.Model.evaluate:5 of msgid "" -"Return an amplitude damping channel. Notice: Amplitude damping " -"corrspondings to p = 1." +"Input data. It could be: - A Numpy array (or array-like), or a list of " +"arrays (in case the model has multiple inputs). - A TensorFlow tensor, " +"or a list of tensors (in case the model has multiple inputs). - A dict " +"mapping input names to the corresponding array/tensors, if the model " +"has named inputs. - A `tf.data` dataset. Should return a tuple of " +"either `(inputs, targets)` or `(inputs, targets, sample_weights)`. - A " +"generator or `keras.utils.Sequence` returning `(inputs, targets)` or " +"`(inputs, targets, sample_weights)`. A more detailed description of " +"unpacking behavior for iterator types (Dataset, generator, Sequence) is " +"given in the `Unpacking behavior for iterator-like inputs` section of " +"`Model.fit`." msgstr "" -#: of tensorcircuit.channels.amplitudedampingchannel:4 +#: keras.src.engine.training.Model.evaluate:5 +#: keras.src.engine.training.Model.fit:3 +#: keras.src.engine.training.Model.train_on_batch:3 of msgid "" -"\\sqrt{p}\n" -"\\begin{bmatrix}\n" -" 1 & 0\\\\\n" -" 0 & \\sqrt{1-\\gamma}\\\\\n" -"\\end{bmatrix}\\qquad\n" -"\\sqrt{p}\n" -"\\begin{bmatrix}\n" -" 0 & \\sqrt{\\gamma}\\\\\n" -" 0 & 0\\\\\n" -"\\end{bmatrix}\\qquad\n" -"\\sqrt{1-p}\n" -"\\begin{bmatrix}\n" -" \\sqrt{1-\\gamma} & 0\\\\\n" -" 0 & 1\\\\\n" -"\\end{bmatrix}\\qquad\n" -"\\sqrt{1-p}\n" -"\\begin{bmatrix}\n" -" 0 & 0\\\\\n" -" \\sqrt{\\gamma} & 0\\\\\n" -"\\end{bmatrix}\n" -"\n" +"Input data. It could be: - A Numpy array (or array-like), or a list of " +"arrays" msgstr "" -#: of tensorcircuit.channels.amplitudedampingchannel:31 -msgid "the damping parameter of amplitude (:math:`\\gamma`)" +#: keras.src.engine.training.Model.evaluate:7 +#: keras.src.engine.training.Model.fit:5 +#: keras.src.engine.training.Model.predict:28 +#: keras.src.engine.training.Model.train_on_batch:5 +#: keras.src.engine.training.Model.train_on_batch:7 of +msgid "(in case the model has multiple inputs)." msgstr "" -#: of tensorcircuit.channels.amplitudedampingchannel:33 -msgid ":math:`p`" +#: keras.src.engine.training.Model.evaluate:8 +#: keras.src.engine.training.Model.fit:6 +#: keras.src.engine.training.Model.predict:29 of +msgid "" +"A TensorFlow tensor, or a list of tensors (in case the model has multiple" +" inputs)." msgstr "" -#: of tensorcircuit.channels.amplitudedampingchannel:35 -msgid "An amplitude damping channel with given :math:`\\gamma` and :math:`p`" +#: keras.src.engine.training.Model.evaluate:10 +#: keras.src.engine.training.Model.fit:8 of +msgid "" +"A dict mapping input names to the corresponding array/tensors, if the " +"model has named inputs." msgstr "" -#: of tensorcircuit.channels.depolarizingchannel:1 -msgid "Return a Depolarizing Channel" +#: keras.src.engine.training.Model.evaluate:12 +#: keras.src.engine.training.Model.fit:10 of +msgid "" +"A `tf.data` dataset. Should return a tuple of either `(inputs, targets)` " +"or `(inputs, targets, sample_weights)`." msgstr "" -#: of tensorcircuit.channels.depolarizingchannel:3 +#: keras.src.engine.training.Model.evaluate:15 +#: keras.src.engine.training.Model.fit:13 of msgid "" -"\\sqrt{1-p_x-p_y-p_z}\n" -"\\begin{bmatrix}\n" -" 1 & 0\\\\\n" -" 0 & 1\\\\\n" -"\\end{bmatrix}\\qquad\n" -"\\sqrt{p_x}\n" -"\\begin{bmatrix}\n" -" 0 & 1\\\\\n" -" 1 & 0\\\\\n" -"\\end{bmatrix}\\qquad\n" -"\\sqrt{p_y}\n" -"\\begin{bmatrix}\n" -" 0 & -1j\\\\\n" -" 1j & 0\\\\\n" -"\\end{bmatrix}\\qquad\n" -"\\sqrt{p_z}\n" -"\\begin{bmatrix}\n" -" 1 & 0\\\\\n" -" 0 & -1\\\\\n" -"\\end{bmatrix}\n" -"\n" +"A generator or `keras.utils.Sequence` returning `(inputs, targets)` or " +"`(inputs, targets, sample_weights)`." msgstr "" -#: of tensorcircuit.channels.depolarizingchannel:30 -msgid ":math:`p_x`" +#: keras.src.engine.training.Model.evaluate:17 +#: keras.src.engine.training.Model.predict:33 of +msgid "" +"A more detailed description of unpacking behavior for iterator types " +"(Dataset, generator, Sequence) is given in the `Unpacking behavior for " +"iterator-like inputs` section of `Model.fit`." msgstr "" -#: of tensorcircuit.channels.depolarizingchannel:32 -msgid ":math:`p_y`" +#: keras.src.engine.training.Model.evaluate:20 of +msgid "" +"Target data. Like the input data `x`, it could be either Numpy array(s) " +"or TensorFlow tensor(s). It should be consistent with `x` (you cannot " +"have Numpy inputs and tensor targets, or inversely). If `x` is a dataset," +" generator or `keras.utils.Sequence` instance, `y` should not be " +"specified (since targets will be obtained from the iterator/dataset)." msgstr "" -#: of tensorcircuit.channels.depolarizingchannel:34 -msgid ":math:`p_z`" +#: keras.src.engine.training.Model.evaluate:26 of +msgid "" +"Integer or `None`. Number of samples per batch of computation. If " +"unspecified, `batch_size` will default to 32. Do not specify the " +"`batch_size` if your data is in the form of a dataset, generators, or " +"`keras.utils.Sequence` instances (since they generate batches)." msgstr "" -#: of tensorcircuit.channels.depolarizingchannel:36 -msgid "Sequences of Gates" +#: keras.src.engine.training.Model.evaluate:31 +#: keras.src.engine.training.Model.predict:42 of +msgid "" +"`\"auto\"`, 0, 1, or 2. Verbosity mode. 0 = silent, 1 = progress bar, 2 =" +" single line. `\"auto\"` becomes 1 for most cases, and to 2 when used " +"with `ParameterServerStrategy`. Note that the progress bar is not " +"particularly useful when logged to a file, so `verbose=2` is recommended " +"when not running interactively (e.g. in a production environment). " +"Defaults to 'auto'." msgstr "" -#: of tensorcircuit.channels.kraus_to_super_gate:1 -msgid "Convert Kraus operators to one Tensor (as one Super Gate)." +#: keras.src.engine.training.Model.evaluate:38 of +msgid "" +"Optional Numpy array of weights for the test samples, used for weighting " +"the loss function. You can either pass a flat (1D) Numpy array with the " +"same length as the input samples (1:1 mapping between weights and " +"samples), or in the case of temporal data, you can pass a 2D array " +"with shape `(samples, sequence_length)`, to apply a different weight " +"to every timestep of every sample. This argument is not supported " +"when `x` is a dataset, instead pass sample weights as the third " +"element of `x`." msgstr "" -#: of tensorcircuit.channels.kraus_to_super_gate:3 +#: keras.src.engine.training.Model.evaluate:38 of msgid "" -"\\sum_{k}^{} K_k \\otimes K_k^{\\dagger}\n" -"\n" +"Optional Numpy array of weights for the test samples, used for weighting " +"the loss function. You can either pass a flat (1D) Numpy array with the " +"same length as the input samples" msgstr "" -#: of tensorcircuit.channels.kraus_to_super_gate:6 -msgid "A sequence of Gate" +#: keras.src.engine.training.Model.evaluate:45 of +msgid "(1:1 mapping between weights and samples), or in the case of" msgstr "" -#: of tensorcircuit.channels.kraus_to_super_gate:8 -msgid "The corresponding Tensor of the list of Kraus operators" +#: keras.src.engine.training.Model.evaluate:42 of +msgid "" +"temporal data, you can pass a 2D array with shape `(samples, " +"sequence_length)`, to apply a different weight to every timestep of every" +" sample. This argument is not supported when `x` is a dataset, instead " +"pass sample weights as the third element of `x`." msgstr "" -#: of tensorcircuit.channels.phasedampingchannel:1 -msgid "Return a phase damping channel with given :math:`\\gamma`" +#: keras.src.engine.training.Model.evaluate:47 of +msgid "" +"Integer or `None`. Total number of steps (batches of samples) before " +"declaring the evaluation round finished. Ignored with the default value " +"of `None`. If x is a `tf.data` dataset and `steps` is None, 'evaluate' " +"will run until the dataset is exhausted. This argument is not supported " +"with array inputs." msgstr "" -#: of tensorcircuit.channels.phasedampingchannel:3 +#: keras.src.engine.training.Model.evaluate:52 of msgid "" -"\\begin{bmatrix}\n" -" 1 & 0\\\\\n" -" 0 & \\sqrt{1-\\gamma}\\\\\n" -"\\end{bmatrix}\\qquad\n" -"\\begin{bmatrix}\n" -" 0 & 0\\\\\n" -" 0 & \\sqrt{\\gamma}\\\\\n" -"\\end{bmatrix}\n" -"\n" +"List of `keras.callbacks.Callback` instances. List of callbacks to apply " +"during evaluation. See " +"[callbacks](https://www.tensorflow.org/api_docs/python/tf/keras/callbacks)." msgstr "" -#: of tensorcircuit.channels.phasedampingchannel:18 -msgid "The damping parameter of phase (:math:`\\gamma`)" +#: keras.src.engine.training.Model.evaluate:55 +#: keras.src.engine.training.Model.predict:58 of +msgid "" +"Integer. Used for generator or `keras.utils.Sequence` input only. Maximum" +" size for the generator queue. If unspecified, `max_queue_size` will " +"default to 10." msgstr "" -#: of tensorcircuit.channels.phasedampingchannel:20 -msgid "A phase damping channel with given :math:`\\gamma`" +#: keras.src.engine.training.Model.evaluate:58 +#: keras.src.engine.training.Model.fit:178 +#: keras.src.engine.training.Model.predict:62 of +msgid "" +"Integer. Used for generator or `keras.utils.Sequence` input only. Maximum" +" number of processes to spin up when using process-based threading. If " +"unspecified, `workers` will default to 1." msgstr "" -#: of tensorcircuit.channels.resetchannel:1 -#: tensorcircuit.channels.resetchannel:18 -msgid "Reset channel" +#: keras.src.engine.training.Model.evaluate:62 +#: keras.src.engine.training.Model.fit:182 +#: keras.src.engine.training.Model.predict:66 of +msgid "" +"Boolean. Used for generator or `keras.utils.Sequence` input only. If " +"`True`, use process-based threading. If unspecified, " +"`use_multiprocessing` will default to `False`. Note that because this " +"implementation relies on multiprocessing, you should not pass non-" +"picklable arguments to the generator as they can't be passed easily to " +"children processes." msgstr "" -#: of tensorcircuit.channels.resetchannel:3 +#: keras.src.engine.training.Model.evaluate:69 +#: keras.src.engine.training.Model.test_on_batch:21 +#: keras.src.engine.training.Model.train_on_batch:27 of msgid "" -"\\begin{bmatrix}\n" -" 1 & 0\\\\\n" -" 0 & 0\\\\\n" -"\\end{bmatrix}\\qquad\n" -"\\begin{bmatrix}\n" -" 0 & 1\\\\\n" -" 0 & 0\\\\\n" -"\\end{bmatrix}\n" -"\n" +"If `True`, loss and metric results are returned as a dict, with each key " +"being the name of the metric. If `False`, they are returned as a list." msgstr "" -#: of tensorcircuit.channels.single_qubit_kraus_identity_check:1 -msgid "Check identity of a single qubit Kraus operators." +#: keras.src.engine.training.Model.evaluate:72 of +msgid "Unused at this time." msgstr "" -#: of tensorcircuit.channels.single_qubit_kraus_identity_check -msgid "Examples" +#: keras.src.engine.training.Model.evaluate:74 of +msgid "" +"See the discussion of `Unpacking behavior for iterator-like inputs` for " +"`Model.fit`." msgstr "" -#: of tensorcircuit.channels.single_qubit_kraus_identity_check:8 +#: keras.src.engine.training.Model.evaluate:77 +#: keras.src.engine.training.Model.test_on_batch:25 of msgid "" -"\\sum_{k}^{} K_k^{\\dagger} K_k = I\n" -"\n" +"Scalar test loss (if the model has a single output and no metrics) or " +"list of scalars (if the model has multiple outputs and/or metrics). The " +"attribute `model.metrics_names` will give you the display labels for the " +"scalar outputs." msgstr "" -#: of tensorcircuit.channels.single_qubit_kraus_identity_check:11 -msgid "List of Kraus operators." +#: keras.src.engine.training.Model.evaluate:82 of +msgid "If `model.evaluate` is wrapped in a `tf.function`." msgstr "" -#: ../../source/api/circuit.rst:2 -msgid "tensorcircuit.circuit" +#: keras.src.engine.training.Model.evaluate_generator:1 of +msgid "Evaluates the model on a data generator." msgstr "" -#: of tensorcircuit.circuit:1 -msgid "Quantum circuit: the state simulator" +#: keras.src.engine.training.Model.evaluate_generator:4 +#: keras.src.engine.training.Model.fit_generator:4 +#: keras.src.engine.training.Model.predict_generator:4 of +msgid "DEPRECATED:" msgstr "" -#: of tensorcircuit.circuit.Circuit:1 -msgid "``Circuit`` class. Simple usage demo below." +#: keras.src.engine.training.Model.evaluate_generator:4 of +msgid "" +"`Model.evaluate` now supports generators, so there is no longer any need " +"to use this endpoint." msgstr "" -#: of -#: tensorcircuit.circuit.Circuit.apply_general_variable_gate_delayed..apply:1 -#: tensorcircuit.densitymatrix.DMCircuit.apply_general_variable_gate_delayed..apply:1 +#: keras.src.engine.training.Model.export:1 of +msgid "Create a SavedModel artifact for inference (e.g. via TF-Serving)." +msgstr "" + +#: keras.src.engine.training.Model.export:3 of msgid "" -"Apply **ANY** gate with parameters on the circuit. See " -":py:meth:`tensorcircuit.gates.any_gate`." +"This method lets you export a model to a lightweight SavedModel artifact " +"that contains the model's forward pass only (its `call()` method) and can" +" be served via e.g. TF-Serving. The forward pass is registered under the " +"name `serve()` (see example below)." msgstr "" -#: of -#: tensorcircuit.circuit.Circuit.apply_general_variable_gate_delayed..apply:3 -#: tensorcircuit.circuit.Circuit.apply_general_variable_gate_delayed..apply:5 -#: tensorcircuit.densitymatrix.DMCircuit.apply_general_kraus_delayed..apply:4 -#: tensorcircuit.densitymatrix.DMCircuit.apply_general_variable_gate_delayed..apply:5 -#: tensorcircuit.densitymatrix2.DMCircuit2.apply_general_kraus_delayed..apply:4 -#: tensorcircuit.mpscircuit.MPSCircuit.apply_general_variable_gate_delayed..apply:3 -msgid "Qubit number that the gate applies on." +#: keras.src.engine.training.Model.export:8 of +msgid "" +"The original code of the model (including any custom layers you may have " +"used) is *no longer* necessary to reload the artifact -- it is entirely " +"standalone." msgstr "" -#: of -#: tensorcircuit.circuit.Circuit.apply_general_variable_gate_delayed..apply:5 -#: tensorcircuit.circuit.Circuit.apply_general_variable_gate_delayed..apply:7 -#: tensorcircuit.densitymatrix.DMCircuit.apply_general_variable_gate_delayed..apply:7 -msgid "Parameters for the gate." +#: keras.src.engine.training.Model.export:12 of +msgid "`str` or `pathlib.Path` object. Path where to save the artifact." +msgstr "" + +#: keras.src.engine.training.Model.export:17 of +msgid "```python # Create the artifact model.export(\"path/to/location\")" msgstr "" -#: of tensorcircuit.circuit.Circuit.apply_general_gate_delayed..apply:1 -#: tensorcircuit.densitymatrix.DMCircuit.apply_general_gate_delayed..apply:1 +#: keras.src.engine.training.Model.export:21 of msgid "" -"Apply **CNOT** gate on the circuit. See " -":py:meth:`tensorcircuit.gates.cnot_gate`." +"# Later, in a different process / environment... reloaded_artifact = " +"tf.saved_model.load(\"path/to/location\") predictions = " +"reloaded_artifact.serve(input_data) ```" msgstr "" -#: of tensorcircuit.circuit.Circuit.apply_general_gate_delayed..apply:5 -#: tensorcircuit.densitymatrix.DMCircuit.apply_general_gate_delayed..apply:5 -#: tensorcircuit.mpscircuit.MPSCircuit.apply_general_gate_delayed..apply:3 +#: keras.src.engine.training.Model.export:26 of msgid "" -"Qubit number that the gate applies on. The matrix for the gate is .. " -"math:: \\begin{bmatrix} 1.+0.j & 0.+0.j & 0.+0.j & 0.+0.j\\\\" -" 0.+0.j & 1.+0.j & 0.+0.j & 0.+0.j\\\\ 0.+0.j & 0.+0.j & 0.+0.j & " -"1.+0.j\\\\ 0.+0.j & 0.+0.j & 1.+0.j & 0.+0.j \\end{bmatrix}" +"If you would like to customize your serving endpoints, you can use the " +"lower-level `keras.export.ExportArchive` class. The `export()` method " +"relies on `ExportArchive` internally." msgstr "" -#: of tensorcircuit.circuit.Circuit.apply_general_gate_delayed..apply:5 -#: tensorcircuit.densitymatrix.DMCircuit.apply_general_gate_delayed..apply:5 -#: tensorcircuit.mpscircuit.MPSCircuit.apply_general_gate_delayed..apply:3 -msgid "Qubit number that the gate applies on. The matrix for the gate is" +#: keras.src.engine.base_layer.Layer.finalize_state:1 of +msgid "Finalizes the layers state after updating layer weights." msgstr "" -#: of tensorcircuit.circuit.Circuit.apply_general_gate_delayed..apply:8 -#: tensorcircuit.densitymatrix.DMCircuit.apply_general_gate_delayed..apply:8 -#: tensorcircuit.mpscircuit.MPSCircuit.apply_general_gate_delayed..apply:6 +#: keras.src.engine.base_layer.Layer.finalize_state:3 of msgid "" -"\\begin{bmatrix} 1.+0.j & 0.+0.j & 0.+0.j & 0.+0.j\\\\ 0.+0.j & " -"1.+0.j & 0.+0.j & 0.+0.j\\\\ 0.+0.j & 0.+0.j & 0.+0.j & 1.+0.j\\\\ " -"0.+0.j & 0.+0.j & 1.+0.j & 0.+0.j \\end{bmatrix}" +"This function can be subclassed in a layer and will be called after " +"updating a layer weights. It can be overridden to finalize any additional" +" layer state after a weight update." msgstr "" -#: of -#: tensorcircuit.circuit.Circuit.apply_general_variable_gate_delayed..apply:1 -#: tensorcircuit.densitymatrix.DMCircuit.apply_general_variable_gate_delayed..apply:1 +#: keras.src.engine.base_layer.Layer.finalize_state:7 of msgid "" -"Apply **CR** gate with parameters on the circuit. See " -":py:meth:`tensorcircuit.gates.cr_gate`." +"This function will be called after weights of a layer have been restored " +"from a loaded model." msgstr "" -#: of -#: tensorcircuit.circuit.Circuit.apply_general_variable_gate_delayed..apply:1 -#: tensorcircuit.densitymatrix.DMCircuit.apply_general_variable_gate_delayed..apply:1 -msgid "" -"Apply **CRX** gate with parameters on the circuit. See " -":py:meth:`tensorcircuit.gates.crx_gate`." +#: keras.src.engine.training.Model.fit:1 of +msgid "Trains the model for a fixed number of epochs (dataset iterations)." msgstr "" -#: of -#: tensorcircuit.circuit.Circuit.apply_general_variable_gate_delayed..apply:1 -#: tensorcircuit.densitymatrix.DMCircuit.apply_general_variable_gate_delayed..apply:1 +#: keras.src.engine.training.Model.fit:3 of msgid "" -"Apply **CRY** gate with parameters on the circuit. See " -":py:meth:`tensorcircuit.gates.cry_gate`." +"Input data. It could be: - A Numpy array (or array-like), or a list of " +"arrays (in case the model has multiple inputs). - A TensorFlow tensor, " +"or a list of tensors (in case the model has multiple inputs). - A dict " +"mapping input names to the corresponding array/tensors, if the model " +"has named inputs. - A `tf.data` dataset. Should return a tuple of " +"either `(inputs, targets)` or `(inputs, targets, sample_weights)`. - A " +"generator or `keras.utils.Sequence` returning `(inputs, targets)` or " +"`(inputs, targets, sample_weights)`. - A " +"`tf.keras.utils.experimental.DatasetCreator`, which wraps a callable " +"that takes a single argument of type `tf.distribute.InputContext`, and " +"returns a `tf.data.Dataset`. `DatasetCreator` should be used when users" +" prefer to specify the per-replica batching and sharding logic for the " +"`Dataset`. See `tf.keras.utils.experimental.DatasetCreator` doc for " +"more information. A more detailed description of unpacking behavior for" +" iterator types (Dataset, generator, Sequence) is given below. If these " +"include `sample_weights` as a third component, note that sample weighting" +" applies to the `weighted_metrics` argument but not the `metrics` " +"argument in `compile()`. If using " +"`tf.distribute.experimental.ParameterServerStrategy`, only " +"`DatasetCreator` type is supported for `x`." msgstr "" -#: of -#: tensorcircuit.circuit.Circuit.apply_general_variable_gate_delayed..apply:1 -#: tensorcircuit.densitymatrix.DMCircuit.apply_general_variable_gate_delayed..apply:1 +#: keras.src.engine.training.Model.fit:15 of msgid "" -"Apply **CRZ** gate with parameters on the circuit. See " -":py:meth:`tensorcircuit.gates.crz_gate`." +"A `tf.keras.utils.experimental.DatasetCreator`, which wraps a callable " +"that takes a single argument of type `tf.distribute.InputContext`, and " +"returns a `tf.data.Dataset`. `DatasetCreator` should be used when users " +"prefer to specify the per-replica batching and sharding logic for the " +"`Dataset`. See `tf.keras.utils.experimental.DatasetCreator` doc for more " +"information." msgstr "" -#: of tensorcircuit.circuit.Circuit.apply_general_gate_delayed..apply:1 -#: tensorcircuit.densitymatrix.DMCircuit.apply_general_gate_delayed..apply:1 +#: keras.src.engine.training.Model.fit:22 of msgid "" -"Apply **CY** gate on the circuit. See " -":py:meth:`tensorcircuit.gates.cy_gate`." +"A more detailed description of unpacking behavior for iterator types " +"(Dataset, generator, Sequence) is given below. If these include " +"`sample_weights` as a third component, note that sample weighting applies" +" to the `weighted_metrics` argument but not the `metrics` argument in " +"`compile()`. If using " +"`tf.distribute.experimental.ParameterServerStrategy`, only " +"`DatasetCreator` type is supported for `x`." msgstr "" -#: of tensorcircuit.circuit.Circuit.apply_general_gate_delayed..apply:5 -#: tensorcircuit.densitymatrix.DMCircuit.apply_general_gate_delayed..apply:5 -#: tensorcircuit.mpscircuit.MPSCircuit.apply_general_gate_delayed..apply:3 +#: keras.src.engine.training.Model.fit:29 of msgid "" -"Qubit number that the gate applies on. The matrix for the gate is .. " -"math:: \\begin{bmatrix} 1.+0.j & 0.+0.j & 0.+0.j & 0.+0.j\\\\" -" 0.+0.j & 1.+0.j & 0.+0.j & 0.+0.j\\\\ 0.+0.j & 0.+0.j & 0.+0.j & " -"0.-1.j\\\\ 0.+0.j & 0.+0.j & 0.+1.j & 0.+0.j \\end{bmatrix}" +"Target data. Like the input data `x`, it could be either Numpy array(s) " +"or TensorFlow tensor(s). It should be consistent with `x` (you cannot " +"have Numpy inputs and tensor targets, or inversely). If `x` is a dataset," +" generator, or `keras.utils.Sequence` instance, `y` should not be " +"specified (since targets will be obtained from `x`)." msgstr "" -#: of tensorcircuit.circuit.Circuit.apply_general_gate_delayed..apply:8 -#: tensorcircuit.densitymatrix.DMCircuit.apply_general_gate_delayed..apply:8 -#: tensorcircuit.mpscircuit.MPSCircuit.apply_general_gate_delayed..apply:6 +#: keras.src.engine.training.Model.fit:35 of msgid "" -"\\begin{bmatrix} 1.+0.j & 0.+0.j & 0.+0.j & 0.+0.j\\\\ 0.+0.j & " -"1.+0.j & 0.+0.j & 0.+0.j\\\\ 0.+0.j & 0.+0.j & 0.+0.j & 0.-1.j\\\\ " -"0.+0.j & 0.+0.j & 0.+1.j & 0.+0.j \\end{bmatrix}" +"Integer or `None`. Number of samples per gradient update. If unspecified," +" `batch_size` will default to 32. Do not specify the `batch_size` if your" +" data is in the form of datasets, generators, or `keras.utils.Sequence` " +"instances (since they generate batches)." msgstr "" -#: of tensorcircuit.circuit.Circuit.apply_general_gate_delayed..apply:1 -#: tensorcircuit.densitymatrix.DMCircuit.apply_general_gate_delayed..apply:1 +#: keras.src.engine.training.Model.fit:41 of msgid "" -"Apply **CZ** gate on the circuit. See " -":py:meth:`tensorcircuit.gates.cz_gate`." +"Integer. Number of epochs to train the model. An epoch is an iteration " +"over the entire `x` and `y` data provided (unless the `steps_per_epoch` " +"flag is set to something other than None). Note that in conjunction with " +"`initial_epoch`, `epochs` is to be understood as \"final epoch\". The " +"model is not trained for a number of iterations given by `epochs`, but " +"merely until the epoch of index `epochs` is reached." msgstr "" -#: of tensorcircuit.circuit.Circuit.apply_general_gate_delayed..apply:5 -#: tensorcircuit.densitymatrix.DMCircuit.apply_general_gate_delayed..apply:5 -#: tensorcircuit.mpscircuit.MPSCircuit.apply_general_gate_delayed..apply:3 +#: keras.src.engine.training.Model.fit:51 of msgid "" -"Qubit number that the gate applies on. The matrix for the gate is .. " -"math:: \\begin{bmatrix} 1.+0.j & 0.+0.j & 0.+0.j & 0.+0.j\\\\" -" 0.+0.j & 1.+0.j & 0.+0.j & 0.+0.j\\\\ 0.+0.j & 0.+0.j & 1.+0.j & " -"0.+0.j\\\\ 0.+0.j & 0.+0.j & 0.+0.j & -1.+0.j \\end{bmatrix}" +"'auto', 0, 1, or 2. Verbosity mode. 0 = silent, 1 = progress bar, 2 = one" +" line per epoch. 'auto' becomes 1 for most cases, but 2 when used with " +"`ParameterServerStrategy`. Note that the progress bar is not particularly" +" useful when logged to a file, so verbose=2 is recommended when not " +"running interactively (eg, in a production environment). Defaults to " +"'auto'." msgstr "" -#: of tensorcircuit.circuit.Circuit.apply_general_gate_delayed..apply:8 -#: tensorcircuit.densitymatrix.DMCircuit.apply_general_gate_delayed..apply:8 -#: tensorcircuit.mpscircuit.MPSCircuit.apply_general_gate_delayed..apply:6 +#: keras.src.engine.training.Model.fit:58 of msgid "" -"\\begin{bmatrix} 1.+0.j & 0.+0.j & 0.+0.j & 0.+0.j\\\\ 0.+0.j & " -"1.+0.j & 0.+0.j & 0.+0.j\\\\ 0.+0.j & 0.+0.j & 1.+0.j & 0.+0.j\\\\ " -"0.+0.j & 0.+0.j & 0.+0.j & -1.+0.j \\end{bmatrix}" +"List of `keras.callbacks.Callback` instances. List of callbacks to apply " +"during training. See `tf.keras.callbacks`. Note " +"`tf.keras.callbacks.ProgbarLogger` and `tf.keras.callbacks.History` " +"callbacks are created automatically and need not be passed into " +"`model.fit`. `tf.keras.callbacks.ProgbarLogger` is created or not based " +"on `verbose` argument to `model.fit`. Callbacks with batch-level calls " +"are currently unsupported with " +"`tf.distribute.experimental.ParameterServerStrategy`, and users are " +"advised to implement epoch-level calls instead with an appropriate " +"`steps_per_epoch` value." msgstr "" -#: of -#: tensorcircuit.circuit.Circuit.apply_general_variable_gate_delayed..apply:1 -#: tensorcircuit.densitymatrix.DMCircuit.apply_general_variable_gate_delayed..apply:1 +#: keras.src.engine.training.Model.fit:70 of msgid "" -"Apply **EXP** gate with parameters on the circuit. See " -":py:meth:`tensorcircuit.gates.exp_gate`." +"Float between 0 and 1. Fraction of the training data to be used as " +"validation data. The model will set apart this fraction of the training " +"data, will not train on it, and will evaluate the loss and any model " +"metrics on this data at the end of each epoch. The validation data is " +"selected from the last samples in the `x` and `y` data provided, before " +"shuffling. This argument is not supported when `x` is a dataset, " +"generator or `keras.utils.Sequence` instance. If both `validation_data` " +"and `validation_split` are provided, `validation_data` will override " +"`validation_split`. `validation_split` is not yet supported with " +"`tf.distribute.experimental.ParameterServerStrategy`." msgstr "" -#: of -#: tensorcircuit.circuit.Circuit.apply_general_variable_gate_delayed..apply:1 -#: tensorcircuit.densitymatrix.DMCircuit.apply_general_variable_gate_delayed..apply:1 +#: keras.src.engine.training.Model.fit:84 of msgid "" -"Apply **EXP1** gate with parameters on the circuit. See " -":py:meth:`tensorcircuit.gates.exp1_gate`." +"Data on which to evaluate the loss and any model metrics at the end of " +"each epoch. The model will not be trained on this data. Thus, note the " +"fact that the validation loss of data provided using `validation_split` " +"or `validation_data` is not affected by regularization layers like noise " +"and dropout. `validation_data` will override `validation_split`. " +"`validation_data` could be: - A tuple `(x_val, y_val)` of Numpy arrays " +"or tensors. - A tuple `(x_val, y_val, val_sample_weights)` of NumPy" +" arrays. - A `tf.data.Dataset`. - A Python generator or " +"`keras.utils.Sequence` returning `(inputs, targets)` or `(inputs, " +"targets, sample_weights)`. `validation_data` is not yet supported with " +"`tf.distribute.experimental.ParameterServerStrategy`." msgstr "" -#: of tensorcircuit.circuit.Circuit.apply_general_gate_delayed..apply:1 -#: tensorcircuit.densitymatrix.DMCircuit.apply_general_gate_delayed..apply:1 +#: keras.src.engine.training.Model.fit:84 of msgid "" -"Apply **FREDKIN** gate on the circuit. See " -":py:meth:`tensorcircuit.gates.fredkin_gate`." +"Data on which to evaluate the loss and any model metrics at the end of " +"each epoch. The model will not be trained on this data. Thus, note the " +"fact that the validation loss of data provided using `validation_split` " +"or `validation_data` is not affected by regularization layers like noise " +"and dropout. `validation_data` will override `validation_split`. " +"`validation_data` could be:" msgstr "" -#: of tensorcircuit.circuit.Circuit.apply_general_gate_delayed..apply:5 -#: tensorcircuit.densitymatrix.DMCircuit.apply_general_gate_delayed..apply:5 -msgid "" -"Qubit number that the gate applies on. The matrix for the gate is .. " -"math:: \\begin{bmatrix} 1.+0.j & 0.+0.j & 0.+0.j & 0.+0.j & " -"0.+0.j & 0.+0.j & 0.+0.j & 0.+0.j\\\\ 0.+0.j & 1.+0.j & 0.+0.j & " -"0.+0.j & 0.+0.j & 0.+0.j & 0.+0.j & 0.+0.j\\\\ 0.+0.j & 0.+0.j & " -"1.+0.j & 0.+0.j & 0.+0.j & 0.+0.j & 0.+0.j & 0.+0.j\\\\ 0.+0.j & " -"0.+0.j & 0.+0.j & 1.+0.j & 0.+0.j & 0.+0.j & 0.+0.j & 0.+0.j\\\\ " -"0.+0.j & 0.+0.j & 0.+0.j & 0.+0.j & 1.+0.j & 0.+0.j & 0.+0.j & 0.+0.j\\\\" -" 0.+0.j & 0.+0.j & 0.+0.j & 0.+0.j & 0.+0.j & 0.+0.j & 1.+0.j & " -"0.+0.j\\\\ 0.+0.j & 0.+0.j & 0.+0.j & 0.+0.j & 0.+0.j & 1.+0.j & " -"0.+0.j & 0.+0.j\\\\ 0.+0.j & 0.+0.j & 0.+0.j & 0.+0.j & 0.+0.j & " -"0.+0.j & 0.+0.j & 1.+0.j \\end{bmatrix}" +#: keras.src.engine.training.Model.fit:92 of +msgid "A tuple `(x_val, y_val)` of Numpy arrays or tensors." msgstr "" -#: of tensorcircuit.circuit.Circuit.apply_general_gate_delayed..apply:8 -#: tensorcircuit.densitymatrix.DMCircuit.apply_general_gate_delayed..apply:8 -msgid "" -"\\begin{bmatrix} 1.+0.j & 0.+0.j & 0.+0.j & 0.+0.j & 0.+0.j & 0.+0.j &" -" 0.+0.j & 0.+0.j\\\\ 0.+0.j & 1.+0.j & 0.+0.j & 0.+0.j & 0.+0.j & " -"0.+0.j & 0.+0.j & 0.+0.j\\\\ 0.+0.j & 0.+0.j & 1.+0.j & 0.+0.j & " -"0.+0.j & 0.+0.j & 0.+0.j & 0.+0.j\\\\ 0.+0.j & 0.+0.j & 0.+0.j & " -"1.+0.j & 0.+0.j & 0.+0.j & 0.+0.j & 0.+0.j\\\\ 0.+0.j & 0.+0.j & " -"0.+0.j & 0.+0.j & 1.+0.j & 0.+0.j & 0.+0.j & 0.+0.j\\\\ 0.+0.j & " -"0.+0.j & 0.+0.j & 0.+0.j & 0.+0.j & 0.+0.j & 1.+0.j & 0.+0.j\\\\ " -"0.+0.j & 0.+0.j & 0.+0.j & 0.+0.j & 0.+0.j & 1.+0.j & 0.+0.j & 0.+0.j\\\\" -" 0.+0.j & 0.+0.j & 0.+0.j & 0.+0.j & 0.+0.j & 0.+0.j & 0.+0.j & 1.+0.j" -" \\end{bmatrix}" +#: keras.src.engine.training.Model.fit:93 of +msgid "A tuple `(x_val, y_val, val_sample_weights)` of NumPy arrays." msgstr "" -#: of tensorcircuit.circuit.Circuit.apply_general_gate_delayed..apply:1 -#: tensorcircuit.densitymatrix.DMCircuit.apply_general_gate_delayed..apply:1 -msgid "" -"Apply **H** gate on the circuit. See " -":py:meth:`tensorcircuit.gates.h_gate`." +#: keras.src.engine.training.Model.fit:95 of +msgid "A `tf.data.Dataset`." msgstr "" -#: of tensorcircuit.circuit.Circuit.apply_general_gate_delayed..apply:5 -#: tensorcircuit.densitymatrix.DMCircuit.apply_general_gate_delayed..apply:5 -#: tensorcircuit.mpscircuit.MPSCircuit.apply_general_gate_delayed..apply:3 -msgid "" -"Qubit number that the gate applies on. The matrix for the gate is .. " -"math:: \\begin{bmatrix} 0.70710677+0.j & 0.70710677+0.j\\\\ " -"0.70710677+0.j & -0.70710677+0.j \\end{bmatrix}" +#: keras.src.engine.training.Model.fit:96 of +msgid "A Python generator or `keras.utils.Sequence` returning" msgstr "" -#: of tensorcircuit.circuit.Circuit.apply_general_gate_delayed..apply:8 -#: tensorcircuit.densitymatrix.DMCircuit.apply_general_gate_delayed..apply:8 -#: tensorcircuit.mpscircuit.MPSCircuit.apply_general_gate_delayed..apply:6 -msgid "" -"\\begin{bmatrix} 0.70710677+0.j & 0.70710677+0.j\\\\ 0.70710677+0.j" -" & -0.70710677+0.j \\end{bmatrix}" +#: keras.src.engine.training.Model.fit:97 of +msgid "`(inputs, targets)` or `(inputs, targets, sample_weights)`." msgstr "" -#: of tensorcircuit.circuit.Circuit.apply_general_gate_delayed..apply:1 -#: tensorcircuit.densitymatrix.DMCircuit.apply_general_gate_delayed..apply:1 +#: keras.src.engine.training.Model.fit:98 of msgid "" -"Apply **I** gate on the circuit. See " -":py:meth:`tensorcircuit.gates.i_gate`." +"`validation_data` is not yet supported with " +"`tf.distribute.experimental.ParameterServerStrategy`." msgstr "" -#: of tensorcircuit.circuit.Circuit.apply_general_gate_delayed..apply:5 -#: tensorcircuit.densitymatrix.DMCircuit.apply_general_gate_delayed..apply:5 -#: tensorcircuit.mpscircuit.MPSCircuit.apply_general_gate_delayed..apply:3 +#: keras.src.engine.training.Model.fit:100 of msgid "" -"Qubit number that the gate applies on. The matrix for the gate is .. " -"math:: \\begin{bmatrix} 1.+0.j & 0.+0.j\\\\ 0.+0.j & 1.+0.j " -"\\end{bmatrix}" +"Boolean (whether to shuffle the training data before each epoch) or str " +"(for 'batch'). This argument is ignored when `x` is a generator or an " +"object of tf.data.Dataset. 'batch' is a special option for dealing with " +"the limitations of HDF5 data; it shuffles in batch-sized chunks. Has no " +"effect when `steps_per_epoch` is not `None`." msgstr "" -#: of tensorcircuit.circuit.Circuit.apply_general_gate_delayed..apply:8 -#: tensorcircuit.densitymatrix.DMCircuit.apply_general_gate_delayed..apply:8 -#: tensorcircuit.mpscircuit.MPSCircuit.apply_general_gate_delayed..apply:6 -msgid "\\begin{bmatrix} 1.+0.j & 0.+0.j\\\\ 0.+0.j & 1.+0.j \\end{bmatrix}" +#: keras.src.engine.training.Model.fit:106 of +msgid "" +"Optional dictionary mapping class indices (integers) to a weight (float) " +"value, used for weighting the loss function (during training only). This " +"can be useful to tell the model to \"pay more attention\" to samples from" +" an under-represented class. When `class_weight` is specified and targets" +" have a rank of 2 or greater, either `y` must be one-hot encoded, or an " +"explicit final dimension of `1` must be included for sparse class labels." msgstr "" -#: of tensorcircuit.circuit.Circuit.apply_general_gate_delayed..apply:1 -#: tensorcircuit.densitymatrix.DMCircuit.apply_general_gate_delayed..apply:1 +#: keras.src.engine.training.Model.fit:115 of msgid "" -"Apply **ISWAP** gate on the circuit. See " -":py:meth:`tensorcircuit.gates.iswap_gate`." +"Optional Numpy array of weights for the training samples, used for " +"weighting the loss function (during training only). You can either pass a" +" flat (1D) Numpy array with the same length as the input samples (1:1 " +"mapping between weights and samples), or in the case of temporal data, " +"you can pass a 2D array with shape `(samples, sequence_length)`, to apply" +" a different weight to every timestep of every sample. This argument is " +"not supported when `x` is a dataset, generator, or `keras.utils.Sequence`" +" instance, instead provide the sample_weights as the third element of " +"`x`. Note that sample weighting does not apply to metrics specified via " +"the `metrics` argument in `compile()`. To apply sample weighting to your " +"metrics, you can specify them via the `weighted_metrics` in `compile()` " +"instead." msgstr "" -#: of tensorcircuit.circuit.Circuit.apply_general_gate_delayed..apply:5 -#: tensorcircuit.densitymatrix.DMCircuit.apply_general_gate_delayed..apply:5 +#: keras.src.engine.training.Model.fit:131 of msgid "" -"Qubit number that the gate applies on. The matrix for the gate is .. " -"math:: \\begin{bmatrix} 1.+0.j & 0.+0.j & 0.+0.j & 0.+0.j\\\\" -" 0.+0.j & 0.+0.j & 0.+1.j & 0.+0.j\\\\ 0.+0.j & 0.+1.j & 0.+0.j & " -"0.+0.j\\\\ 0.+0.j & 0.+0.j & 0.+0.j & 1.+0.j \\end{bmatrix}" +"Integer. Epoch at which to start training (useful for resuming a previous" +" training run)." msgstr "" -#: of tensorcircuit.circuit.Circuit.apply_general_gate_delayed..apply:8 -#: tensorcircuit.densitymatrix.DMCircuit.apply_general_gate_delayed..apply:8 +#: keras.src.engine.training.Model.fit:134 of msgid "" -"\\begin{bmatrix} 1.+0.j & 0.+0.j & 0.+0.j & 0.+0.j\\\\ 0.+0.j & " -"0.+0.j & 0.+1.j & 0.+0.j\\\\ 0.+0.j & 0.+1.j & 0.+0.j & 0.+0.j\\\\ " -"0.+0.j & 0.+0.j & 0.+0.j & 1.+0.j \\end{bmatrix}" +"Integer or `None`. Total number of steps (batches of samples) before " +"declaring one epoch finished and starting the next epoch. When training " +"with input tensors such as TensorFlow data tensors, the default `None` is" +" equal to the number of samples in your dataset divided by the batch " +"size, or 1 if that cannot be determined. If x is a `tf.data` dataset, and" +" 'steps_per_epoch' is None, the epoch will run until the input dataset is" +" exhausted. When passing an infinitely repeating dataset, you must " +"specify the `steps_per_epoch` argument. If `steps_per_epoch=-1` the " +"training will run indefinitely with an infinitely repeating dataset. " +"This argument is not supported with array inputs. When using " +"`tf.distribute.experimental.ParameterServerStrategy`: * " +"`steps_per_epoch=None` is not supported." msgstr "" -#: of -#: tensorcircuit.circuit.Circuit.apply_general_variable_gate_delayed..apply:1 -msgid "Apply mpo gate in MPO format on the circuit." +#: keras.src.engine.training.Model.fit:134 of +msgid "" +"Integer or `None`. Total number of steps (batches of samples) before " +"declaring one epoch finished and starting the next epoch. When training " +"with input tensors such as TensorFlow data tensors, the default `None` is" +" equal to the number of samples in your dataset divided by the batch " +"size, or 1 if that cannot be determined. If x is a `tf.data` dataset, and" +" 'steps_per_epoch' is None, the epoch will run until the input dataset is" +" exhausted. When passing an infinitely repeating dataset, you must " +"specify the `steps_per_epoch` argument. If `steps_per_epoch=-1` the " +"training will run indefinitely with an infinitely repeating dataset. " +"This argument is not supported with array inputs. When using " +"`tf.distribute.experimental.ParameterServerStrategy`:" msgstr "" -#: of -#: tensorcircuit.circuit.Circuit.apply_general_variable_gate_delayed..apply:1 -msgid "Apply multicontrol gate in MPO format on the circuit." +#: keras.src.engine.training.Model.fit:149 of +msgid "`steps_per_epoch=None` is not supported." msgstr "" -#: of -#: tensorcircuit.circuit.Circuit.apply_general_variable_gate_delayed..apply:1 -#: tensorcircuit.densitymatrix.DMCircuit.apply_general_variable_gate_delayed..apply:1 +#: keras.src.engine.training.Model.fit:150 of msgid "" -"Apply **ORX** gate with parameters on the circuit. See " -":py:meth:`tensorcircuit.gates.orx_gate`." +"Only relevant if `validation_data` is provided and is a `tf.data` " +"dataset. Total number of steps (batches of samples) to draw before " +"stopping when performing validation at the end of every epoch. If " +"'validation_steps' is None, validation will run until the " +"`validation_data` dataset is exhausted. In the case of an infinitely " +"repeated dataset, it will run into an infinite loop. If " +"'validation_steps' is specified and only part of the dataset will be " +"consumed, the evaluation will start from the beginning of the dataset at " +"each epoch. This ensures that the same validation samples are used every " +"time." msgstr "" -#: of -#: tensorcircuit.circuit.Circuit.apply_general_variable_gate_delayed..apply:1 -#: tensorcircuit.densitymatrix.DMCircuit.apply_general_variable_gate_delayed..apply:1 +#: keras.src.engine.training.Model.fit:161 of msgid "" -"Apply **ORY** gate with parameters on the circuit. See " -":py:meth:`tensorcircuit.gates.ory_gate`." +"Integer or `None`. Number of samples per validation batch. If " +"unspecified, will default to `batch_size`. Do not specify the " +"`validation_batch_size` if your data is in the form of datasets, " +"generators, or `keras.utils.Sequence` instances (since they generate " +"batches)." msgstr "" -#: of -#: tensorcircuit.circuit.Circuit.apply_general_variable_gate_delayed..apply:1 -#: tensorcircuit.densitymatrix.DMCircuit.apply_general_variable_gate_delayed..apply:1 +#: keras.src.engine.training.Model.fit:167 of msgid "" -"Apply **ORZ** gate with parameters on the circuit. See " -":py:meth:`tensorcircuit.gates.orz_gate`." +"Only relevant if validation data is provided. Integer or " +"`collections.abc.Container` instance (e.g. list, tuple, etc.). If an " +"integer, specifies how many training epochs to run before a new " +"validation run is performed, e.g. `validation_freq=2` runs validation " +"every 2 epochs. If a Container, specifies the epochs on which to run " +"validation, e.g. `validation_freq=[1, 2, 10]` runs validation at the end " +"of the 1st, 2nd, and 10th epochs." msgstr "" -#: of tensorcircuit.circuit.Circuit.apply_general_gate_delayed..apply:1 -#: tensorcircuit.densitymatrix.DMCircuit.apply_general_gate_delayed..apply:1 +#: keras.src.engine.training.Model.fit:175 of msgid "" -"Apply **OX** gate on the circuit. See " -":py:meth:`tensorcircuit.gates.ox_gate`." +"Integer. Used for generator or `keras.utils.Sequence` input only. Maximum" +" size for the generator queue. If unspecified, `max_queue_size` will " +"default to 10." msgstr "" -#: of tensorcircuit.circuit.Circuit.apply_general_gate_delayed..apply:5 -#: tensorcircuit.densitymatrix.DMCircuit.apply_general_gate_delayed..apply:5 -msgid "" -"Qubit number that the gate applies on. The matrix for the gate is .. " -"math:: \\begin{bmatrix} 0.+0.j & 1.+0.j & 0.+0.j & 0.+0.j\\\\" -" 1.+0.j & 0.+0.j & 0.+0.j & 0.+0.j\\\\ 0.+0.j & 0.+0.j & 1.+0.j & " -"0.+0.j\\\\ 0.+0.j & 0.+0.j & 0.+0.j & 1.+0.j \\end{bmatrix}" +#: keras.src.engine.training.Model.fit:214 of +msgid "Unpacking behavior for iterator-like inputs:" msgstr "" -#: of tensorcircuit.circuit.Circuit.apply_general_gate_delayed..apply:8 -#: tensorcircuit.densitymatrix.DMCircuit.apply_general_gate_delayed..apply:8 -msgid "" -"\\begin{bmatrix} 0.+0.j & 1.+0.j & 0.+0.j & 0.+0.j\\\\ 1.+0.j & " -"0.+0.j & 0.+0.j & 0.+0.j\\\\ 0.+0.j & 0.+0.j & 1.+0.j & 0.+0.j\\\\ " -"0.+0.j & 0.+0.j & 0.+0.j & 1.+0.j \\end{bmatrix}" +#: keras.src.engine.training.Model.fit:191 of +msgid "A common pattern is to pass a tf.data.Dataset, generator, or" msgstr "" -#: of tensorcircuit.circuit.Circuit.apply_general_gate_delayed..apply:1 -#: tensorcircuit.densitymatrix.DMCircuit.apply_general_gate_delayed..apply:1 +#: keras.src.engine.training.Model.fit:192 of msgid "" -"Apply **OY** gate on the circuit. See " -":py:meth:`tensorcircuit.gates.oy_gate`." +"tf.keras.utils.Sequence to the `x` argument of fit, which will in fact " +"yield not only features (x) but optionally targets (y) and sample " +"weights. Keras requires that the output of such iterator-likes be " +"unambiguous. The iterator should return a tuple of length 1, 2, or 3, " +"where the optional second and third elements will be used for y and " +"sample_weight respectively. Any other type provided will be wrapped in a " +"length one tuple, effectively treating everything as 'x'. When yielding " +"dicts, they should still adhere to the top-level tuple structure. e.g. " +"`({\"x0\": x0, \"x1\": x1}, y)`. Keras will not attempt to separate " +"features, targets, and weights from the keys of a single dict." msgstr "" -#: of tensorcircuit.circuit.Circuit.apply_general_gate_delayed..apply:5 -#: tensorcircuit.densitymatrix.DMCircuit.apply_general_gate_delayed..apply:5 -msgid "" -"Qubit number that the gate applies on. The matrix for the gate is .. " -"math:: \\begin{bmatrix} 0.+0.j & 0.-1.j & 0.+0.j & 0.+0.j\\\\" -" 0.+1.j & 0.+0.j & 0.+0.j & 0.+0.j\\\\ 0.+0.j & 0.+0.j & 1.+0.j & " -"0.+0.j\\\\ 0.+0.j & 0.+0.j & 0.+0.j & 1.+0.j \\end{bmatrix}" +#: keras.src.engine.training.Model.fit:203 of +msgid "A notable unsupported data type is the namedtuple. The reason is" msgstr "" -#: of tensorcircuit.circuit.Circuit.apply_general_gate_delayed..apply:8 -#: tensorcircuit.densitymatrix.DMCircuit.apply_general_gate_delayed..apply:8 +#: keras.src.engine.training.Model.fit:204 of msgid "" -"\\begin{bmatrix} 0.+0.j & 0.-1.j & 0.+0.j & 0.+0.j\\\\ 0.+1.j & " -"0.+0.j & 0.+0.j & 0.+0.j\\\\ 0.+0.j & 0.+0.j & 1.+0.j & 0.+0.j\\\\ " -"0.+0.j & 0.+0.j & 0.+0.j & 1.+0.j \\end{bmatrix}" +"that it behaves like both an ordered datatype (tuple) and a mapping " +"datatype (dict). So given a namedtuple of the form:" msgstr "" -#: of tensorcircuit.circuit.Circuit.apply_general_gate_delayed..apply:1 -#: tensorcircuit.densitymatrix.DMCircuit.apply_general_gate_delayed..apply:1 -msgid "" -"Apply **OZ** gate on the circuit. See " -":py:meth:`tensorcircuit.gates.oz_gate`." +#: keras.src.engine.training.Model.fit:206 of +msgid "`namedtuple(\"example_tuple\", [\"y\", \"x\"])`" msgstr "" -#: of tensorcircuit.circuit.Circuit.apply_general_gate_delayed..apply:5 -#: tensorcircuit.densitymatrix.DMCircuit.apply_general_gate_delayed..apply:5 +#: keras.src.engine.training.Model.fit:207 of msgid "" -"Qubit number that the gate applies on. The matrix for the gate is .. " -"math:: \\begin{bmatrix} 1.+0.j & 0.+0.j & 0.+0.j & 0.+0.j\\\\" -" 0.+0.j & -1.+0.j & 0.+0.j & 0.+0.j\\\\ 0.+0.j & 0.+0.j & 1.+0.j & " -"0.+0.j\\\\ 0.+0.j & 0.+0.j & 0.+0.j & 1.+0.j \\end{bmatrix}" +"it is ambiguous whether to reverse the order of the elements when " +"interpreting the value. Even worse is a tuple of the form:" msgstr "" -#: of tensorcircuit.circuit.Circuit.apply_general_gate_delayed..apply:8 -#: tensorcircuit.densitymatrix.DMCircuit.apply_general_gate_delayed..apply:8 -msgid "" -"\\begin{bmatrix} 1.+0.j & 0.+0.j & 0.+0.j & 0.+0.j\\\\ 0.+0.j & " -"-1.+0.j & 0.+0.j & 0.+0.j\\\\ 0.+0.j & 0.+0.j & 1.+0.j & 0.+0.j\\\\" -" 0.+0.j & 0.+0.j & 0.+0.j & 1.+0.j \\end{bmatrix}" +#: keras.src.engine.training.Model.fit:209 of +msgid "`namedtuple(\"other_tuple\", [\"x\", \"y\", \"z\"])`" msgstr "" -#: of -#: tensorcircuit.circuit.Circuit.apply_general_variable_gate_delayed..apply:1 -#: tensorcircuit.densitymatrix.DMCircuit.apply_general_variable_gate_delayed..apply:1 +#: keras.src.engine.training.Model.fit:210 of msgid "" -"Apply **R** gate with parameters on the circuit. See " -":py:meth:`tensorcircuit.gates.r_gate`." +"where it is unclear if the tuple was intended to be unpacked into x, y, " +"and sample_weight or passed through as a single element to `x`. As a " +"result the data processing code will simply raise a ValueError if it " +"encounters a namedtuple. (Along with instructions to remedy the issue.)" msgstr "" -#: of -#: tensorcircuit.circuit.Circuit.apply_general_variable_gate_delayed..apply:1 -#: tensorcircuit.densitymatrix.DMCircuit.apply_general_variable_gate_delayed..apply:1 +#: keras.src.engine.training.Model.fit:216 of msgid "" -"Apply **RX** gate with parameters on the circuit. See " -":py:meth:`tensorcircuit.gates.rx_gate`." +"A `History` object. Its `History.history` attribute is a record of " +"training loss values and metrics values at successive epochs, as well as " +"validation loss values and validation metrics values (if applicable)." msgstr "" -#: of -#: tensorcircuit.circuit.Circuit.apply_general_variable_gate_delayed..apply:1 -#: tensorcircuit.densitymatrix.DMCircuit.apply_general_variable_gate_delayed..apply:1 -msgid "" -"Apply **RXX** gate with parameters on the circuit. See " -":py:meth:`tensorcircuit.gates.rxx_gate`." +#: keras.src.engine.training.Model.fit:221 of +msgid "1. If the model was never compiled or," msgstr "" -#: of -#: tensorcircuit.circuit.Circuit.apply_general_variable_gate_delayed..apply:1 -#: tensorcircuit.densitymatrix.DMCircuit.apply_general_variable_gate_delayed..apply:1 -msgid "" -"Apply **RY** gate with parameters on the circuit. See " -":py:meth:`tensorcircuit.gates.ry_gate`." +#: keras.src.engine.training.Model.fit:221 of +msgid "If the model was never compiled or," msgstr "" -#: of -#: tensorcircuit.circuit.Circuit.apply_general_variable_gate_delayed..apply:1 -#: tensorcircuit.densitymatrix.DMCircuit.apply_general_variable_gate_delayed..apply:1 +#: keras.src.engine.training.Model.fit:223 of msgid "" -"Apply **RYY** gate with parameters on the circuit. See " -":py:meth:`tensorcircuit.gates.ryy_gate`." +"In case of mismatch between the provided input data and what the " +"model expects or when the input data is empty." msgstr "" -#: of -#: tensorcircuit.circuit.Circuit.apply_general_variable_gate_delayed..apply:1 -#: tensorcircuit.densitymatrix.DMCircuit.apply_general_variable_gate_delayed..apply:1 -msgid "" -"Apply **RZ** gate with parameters on the circuit. See " -":py:meth:`tensorcircuit.gates.rz_gate`." +#: keras.src.engine.training.Model.fit_generator:1 of +msgid "Fits the model on data yielded batch-by-batch by a Python generator." msgstr "" -#: of -#: tensorcircuit.circuit.Circuit.apply_general_variable_gate_delayed..apply:1 -#: tensorcircuit.densitymatrix.DMCircuit.apply_general_variable_gate_delayed..apply:1 +#: keras.src.engine.training.Model.fit_generator:4 of msgid "" -"Apply **RZZ** gate with parameters on the circuit. See " -":py:meth:`tensorcircuit.gates.rzz_gate`." +"`Model.fit` now supports generators, so there is no longer any need to " +"use this endpoint." msgstr "" -#: of tensorcircuit.circuit.Circuit.apply_general_gate_delayed..apply:1 -#: tensorcircuit.densitymatrix.DMCircuit.apply_general_gate_delayed..apply:1 -msgid "" -"Apply **S** gate on the circuit. See " -":py:meth:`tensorcircuit.gates.s_gate`." +#: keras.src.engine.base_layer.Layer.from_config:1 +#: keras.src.engine.training.Model.from_config:1 of +msgid "Creates a layer from its config." msgstr "" -#: of tensorcircuit.circuit.Circuit.apply_general_gate_delayed..apply:5 -#: tensorcircuit.densitymatrix.DMCircuit.apply_general_gate_delayed..apply:5 -#: tensorcircuit.mpscircuit.MPSCircuit.apply_general_gate_delayed..apply:3 +#: keras.src.engine.base_layer.Layer.from_config:3 +#: keras.src.engine.training.Model.from_config:3 of msgid "" -"Qubit number that the gate applies on. The matrix for the gate is .. " -"math:: \\begin{bmatrix} 1.+0.j & 0.+0.j\\\\ 0.+0.j & 0.+1.j " -"\\end{bmatrix}" +"This method is the reverse of `get_config`, capable of instantiating the " +"same layer from the config dictionary. It does not handle layer " +"connectivity (handled by Network), nor weights (handled by " +"`set_weights`)." msgstr "" -#: of tensorcircuit.circuit.Circuit.apply_general_gate_delayed..apply:8 -#: tensorcircuit.densitymatrix.DMCircuit.apply_general_gate_delayed..apply:8 -#: tensorcircuit.mpscircuit.MPSCircuit.apply_general_gate_delayed..apply:6 -msgid "\\begin{bmatrix} 1.+0.j & 0.+0.j\\\\ 0.+0.j & 0.+1.j \\end{bmatrix}" -msgstr "" - -#: of tensorcircuit.circuit.Circuit.apply_general_gate_delayed..apply:1 -#: tensorcircuit.densitymatrix.DMCircuit.apply_general_gate_delayed..apply:1 -msgid "" -"Apply **SD** gate on the circuit. See " -":py:meth:`tensorcircuit.gates.sd_gate`." +#: keras.src.engine.base_layer.Layer.from_config:8 +#: keras.src.engine.training.Model.from_config:8 of +msgid "A Python dictionary, typically the output of get_config." msgstr "" -#: of tensorcircuit.circuit.Circuit.apply_general_gate_delayed..apply:5 -#: tensorcircuit.densitymatrix.DMCircuit.apply_general_gate_delayed..apply:5 -msgid "" -"Qubit number that the gate applies on. The matrix for the gate is .. " -"math:: \\begin{bmatrix} 1.+0.j & 0.+0.j\\\\ 0.+0.j & 0.-1.j " -"\\end{bmatrix}" +#: keras.src.engine.base_layer.Layer.from_config:11 +#: keras.src.engine.training.Model.from_config:11 +#: keras.src.engine.training.Model.get_layer:9 of +msgid "A layer instance." msgstr "" -#: of tensorcircuit.circuit.Circuit.apply_general_gate_delayed..apply:8 -#: tensorcircuit.densitymatrix.DMCircuit.apply_general_gate_delayed..apply:8 -msgid "\\begin{bmatrix} 1.+0.j & 0.+0.j\\\\ 0.+0.j & 0.-1.j \\end{bmatrix}" +#: keras.src.engine.base_layer.Layer.get_build_config:1 of +msgid "Returns a dictionary with the layer's input shape." msgstr "" -#: of tensorcircuit.circuit.Circuit.apply_general_gate_delayed..apply:1 -#: tensorcircuit.densitymatrix.DMCircuit.apply_general_gate_delayed..apply:1 +#: keras.src.engine.base_layer.Layer.get_build_config:3 of msgid "" -"Apply **SWAP** gate on the circuit. See " -":py:meth:`tensorcircuit.gates.swap_gate`." +"This method returns a config dict that can be used by " +"`build_from_config(config)` to create all states (e.g. Variables and " +"Lookup tables) needed by the layer." msgstr "" -#: of tensorcircuit.circuit.Circuit.apply_general_gate_delayed..apply:5 -#: tensorcircuit.densitymatrix.DMCircuit.apply_general_gate_delayed..apply:5 -#: tensorcircuit.mpscircuit.MPSCircuit.apply_general_gate_delayed..apply:3 +#: keras.src.engine.base_layer.Layer.get_build_config:7 of msgid "" -"Qubit number that the gate applies on. The matrix for the gate is .. " -"math:: \\begin{bmatrix} 1.+0.j & 0.+0.j & 0.+0.j & 0.+0.j\\\\" -" 0.+0.j & 0.+0.j & 1.+0.j & 0.+0.j\\\\ 0.+0.j & 1.+0.j & 0.+0.j & " -"0.+0.j\\\\ 0.+0.j & 0.+0.j & 0.+0.j & 1.+0.j \\end{bmatrix}" +"By default, the config only contains the input shape that the layer was " +"built with. If you're writing a custom layer that creates state in an " +"unusual way, you should override this method to make sure this state is " +"already created when Keras attempts to load its value upon model loading." msgstr "" -#: of tensorcircuit.circuit.Circuit.apply_general_gate_delayed..apply:8 -#: tensorcircuit.densitymatrix.DMCircuit.apply_general_gate_delayed..apply:8 -#: tensorcircuit.mpscircuit.MPSCircuit.apply_general_gate_delayed..apply:6 -msgid "" -"\\begin{bmatrix} 1.+0.j & 0.+0.j & 0.+0.j & 0.+0.j\\\\ 0.+0.j & " -"0.+0.j & 1.+0.j & 0.+0.j\\\\ 0.+0.j & 1.+0.j & 0.+0.j & 0.+0.j\\\\ " -"0.+0.j & 0.+0.j & 0.+0.j & 1.+0.j \\end{bmatrix}" +#: keras.src.engine.base_layer.Layer.get_build_config:13 of +msgid "A dict containing the input shape associated with the layer." msgstr "" -#: of tensorcircuit.circuit.Circuit.apply_general_gate_delayed..apply:1 -#: tensorcircuit.densitymatrix.DMCircuit.apply_general_gate_delayed..apply:1 -msgid "" -"Apply **T** gate on the circuit. See " -":py:meth:`tensorcircuit.gates.t_gate`." +#: keras.src.engine.training.Model.get_compile_config:1 of +msgid "Returns a serialized config with information for compiling the model." msgstr "" -#: of tensorcircuit.circuit.Circuit.apply_general_gate_delayed..apply:5 -#: tensorcircuit.densitymatrix.DMCircuit.apply_general_gate_delayed..apply:5 -#: tensorcircuit.mpscircuit.MPSCircuit.apply_general_gate_delayed..apply:3 +#: keras.src.engine.training.Model.get_compile_config:3 of msgid "" -"Qubit number that the gate applies on. The matrix for the gate is .. " -"math:: \\begin{bmatrix} 1. & +0.j & 0. & +0.j\\\\ 0. & +0.j " -"& 0.70710677+0.70710677j \\end{bmatrix}" +"This method returns a config dictionary containing all the information " +"(optimizer, loss, metrics, etc.) with which the model was compiled." msgstr "" -#: of tensorcircuit.circuit.Circuit.apply_general_gate_delayed..apply:8 -#: tensorcircuit.densitymatrix.DMCircuit.apply_general_gate_delayed..apply:8 -#: tensorcircuit.mpscircuit.MPSCircuit.apply_general_gate_delayed..apply:6 -msgid "" -"\\begin{bmatrix} 1. & +0.j & 0. & +0.j\\\\ 0. & +0.j & " -"0.70710677+0.70710677j \\end{bmatrix}" +#: keras.src.engine.training.Model.get_compile_config:6 of +msgid "A dict containing information for compiling the model." msgstr "" -#: of tensorcircuit.circuit.Circuit.apply_general_gate_delayed..apply:1 -#: tensorcircuit.densitymatrix.DMCircuit.apply_general_gate_delayed..apply:1 -msgid "" -"Apply **TD** gate on the circuit. See " -":py:meth:`tensorcircuit.gates.td_gate`." +#: keras.src.engine.training.Model.get_config:1 of +msgid "Returns the config of the `Model`." msgstr "" -#: of tensorcircuit.circuit.Circuit.apply_general_gate_delayed..apply:5 -#: tensorcircuit.densitymatrix.DMCircuit.apply_general_gate_delayed..apply:5 +#: keras.src.engine.training.Model.get_config:3 of msgid "" -"Qubit number that the gate applies on. The matrix for the gate is .. " -"math:: \\begin{bmatrix} 1. & +0.j & 0. & +0.j\\\\ 0. & +0.j " -"& 0.70710677-0.70710677j \\end{bmatrix}" +"Config is a Python dictionary (serializable) containing the configuration" +" of an object, which in this case is a `Model`. This allows the `Model` " +"to be be reinstantiated later (without its trained weights) from this " +"configuration." msgstr "" -#: of tensorcircuit.circuit.Circuit.apply_general_gate_delayed..apply:8 -#: tensorcircuit.densitymatrix.DMCircuit.apply_general_gate_delayed..apply:8 +#: keras.src.engine.base_layer.Layer.get_config:12 +#: keras.src.engine.training.Model.get_config:8 of msgid "" -"\\begin{bmatrix} 1. & +0.j & 0. & +0.j\\\\ 0. & +0.j & " -"0.70710677-0.70710677j \\end{bmatrix}" +"Note that `get_config()` does not guarantee to return a fresh copy of " +"dict every time it is called. The callers should make a copy of the " +"returned dict if they want to modify it." msgstr "" -#: of tensorcircuit.circuit.Circuit.apply_general_gate_delayed..apply:1 -#: tensorcircuit.densitymatrix.DMCircuit.apply_general_gate_delayed..apply:1 +#: keras.src.engine.training.Model.get_config:12 of msgid "" -"Apply **TOFFOLI** gate on the circuit. See " -":py:meth:`tensorcircuit.gates.toffoli_gate`." +"Developers of subclassed `Model` are advised to override this method, and" +" continue to update the dict from `super(MyModel, self).get_config()` to " +"provide the proper configuration of this `Model`. The default config will" +" return config dict for init parameters if they are basic types. Raises " +"`NotImplementedError` when in cases where a custom `get_config()` " +"implementation is required for the subclassed model." msgstr "" -#: of tensorcircuit.circuit.Circuit.apply_general_gate_delayed..apply:5 -#: tensorcircuit.densitymatrix.DMCircuit.apply_general_gate_delayed..apply:5 -msgid "" -"Qubit number that the gate applies on. The matrix for the gate is .. " -"math:: \\begin{bmatrix} 1.+0.j & 0.+0.j & 0.+0.j & 0.+0.j & " -"0.+0.j & 0.+0.j & 0.+0.j & 0.+0.j\\\\ 0.+0.j & 1.+0.j & 0.+0.j & " -"0.+0.j & 0.+0.j & 0.+0.j & 0.+0.j & 0.+0.j\\\\ 0.+0.j & 0.+0.j & " -"1.+0.j & 0.+0.j & 0.+0.j & 0.+0.j & 0.+0.j & 0.+0.j\\\\ 0.+0.j & " -"0.+0.j & 0.+0.j & 1.+0.j & 0.+0.j & 0.+0.j & 0.+0.j & 0.+0.j\\\\ " -"0.+0.j & 0.+0.j & 0.+0.j & 0.+0.j & 1.+0.j & 0.+0.j & 0.+0.j & 0.+0.j\\\\" -" 0.+0.j & 0.+0.j & 0.+0.j & 0.+0.j & 0.+0.j & 1.+0.j & 0.+0.j & " -"0.+0.j\\\\ 0.+0.j & 0.+0.j & 0.+0.j & 0.+0.j & 0.+0.j & 0.+0.j & " -"0.+0.j & 1.+0.j\\\\ 0.+0.j & 0.+0.j & 0.+0.j & 0.+0.j & 0.+0.j & " -"0.+0.j & 1.+0.j & 0.+0.j \\end{bmatrix}" +#: keras.src.engine.training.Model.get_config:19 of +msgid "Python dictionary containing the configuration of this `Model`." msgstr "" -#: of tensorcircuit.circuit.Circuit.apply_general_gate_delayed..apply:8 -#: tensorcircuit.densitymatrix.DMCircuit.apply_general_gate_delayed..apply:8 -msgid "" -"\\begin{bmatrix} 1.+0.j & 0.+0.j & 0.+0.j & 0.+0.j & 0.+0.j & 0.+0.j &" -" 0.+0.j & 0.+0.j\\\\ 0.+0.j & 1.+0.j & 0.+0.j & 0.+0.j & 0.+0.j & " -"0.+0.j & 0.+0.j & 0.+0.j\\\\ 0.+0.j & 0.+0.j & 1.+0.j & 0.+0.j & " -"0.+0.j & 0.+0.j & 0.+0.j & 0.+0.j\\\\ 0.+0.j & 0.+0.j & 0.+0.j & " -"1.+0.j & 0.+0.j & 0.+0.j & 0.+0.j & 0.+0.j\\\\ 0.+0.j & 0.+0.j & " -"0.+0.j & 0.+0.j & 1.+0.j & 0.+0.j & 0.+0.j & 0.+0.j\\\\ 0.+0.j & " -"0.+0.j & 0.+0.j & 0.+0.j & 0.+0.j & 1.+0.j & 0.+0.j & 0.+0.j\\\\ " -"0.+0.j & 0.+0.j & 0.+0.j & 0.+0.j & 0.+0.j & 0.+0.j & 0.+0.j & 1.+0.j\\\\" -" 0.+0.j & 0.+0.j & 0.+0.j & 0.+0.j & 0.+0.j & 0.+0.j & 1.+0.j & 0.+0.j" -" \\end{bmatrix}" +#: keras.src.engine.base_layer.Layer.get_input_at:1 of +msgid "Retrieves the input tensor(s) of a layer at a given node." msgstr "" -#: of tensorcircuit.circuit.Circuit.apply_general_gate_delayed..apply:1 -#: tensorcircuit.densitymatrix.DMCircuit.apply_general_gate_delayed..apply:1 +#: keras.src.engine.base_layer.Layer.get_input_at:3 of msgid "" -"Apply **WROOT** gate on the circuit. See " -":py:meth:`tensorcircuit.gates.wroot_gate`." +"Integer, index of the node from which to retrieve the attribute. E.g. " +"`node_index=0` will correspond to the first input node of the layer." msgstr "" -#: of tensorcircuit.circuit.Circuit.apply_general_gate_delayed..apply:5 -#: tensorcircuit.densitymatrix.DMCircuit.apply_general_gate_delayed..apply:5 -#: tensorcircuit.mpscircuit.MPSCircuit.apply_general_gate_delayed..apply:3 -msgid "" -"Qubit number that the gate applies on. The matrix for the gate is .. " -"math:: \\begin{bmatrix} 0.70710677+0.j & -0.5 & -0.5j\\\\ " -"0.5 & -0.5j & 0.70710677+0.j \\end{bmatrix}" +#: keras.src.engine.base_layer.Layer.get_input_at:8 of +msgid "A tensor (or list of tensors if the layer has multiple inputs)." msgstr "" -#: of tensorcircuit.circuit.Circuit.apply_general_gate_delayed..apply:8 -#: tensorcircuit.densitymatrix.DMCircuit.apply_general_gate_delayed..apply:8 -#: tensorcircuit.mpscircuit.MPSCircuit.apply_general_gate_delayed..apply:6 -msgid "" -"\\begin{bmatrix} 0.70710677+0.j & -0.5 & -0.5j\\\\ 0.5 & -0.5j & " -"0.70710677+0.j \\end{bmatrix}" +#: keras.src.engine.base_layer.Layer.get_input_at:10 +#: keras.src.engine.base_layer.Layer.get_input_shape_at:11 +#: keras.src.engine.base_layer.Layer.get_output_at:10 +#: keras.src.engine.base_layer.Layer.get_output_shape_at:11 of +#: tensorcircuit.applications.van.MADE.input:8 +#: tensorcircuit.applications.van.MaskedConv2D.input:8 +#: tensorcircuit.applications.van.MaskedLinear.input:8 +#: tensorcircuit.applications.van.NMF.input:8 +#: tensorcircuit.applications.van.PixelCNN.input:8 +#: tensorcircuit.applications.van.ResidualBlock.input:8 +#: tensorcircuit.applications.vqes.Linear.input:8 +#: tensorcircuit.keras.HardwareLayer.input:8 +#: tensorcircuit.keras.QuantumLayer.input:8 +msgid "If called in Eager mode." msgstr "" -#: of tensorcircuit.circuit.Circuit.apply_general_gate_delayed..apply:1 -#: tensorcircuit.densitymatrix.DMCircuit.apply_general_gate_delayed..apply:1 -msgid "" -"Apply **X** gate on the circuit. See " -":py:meth:`tensorcircuit.gates.x_gate`." +#: keras.src.engine.base_layer.Layer.get_input_mask_at:1 of +msgid "Retrieves the input mask tensor(s) of a layer at a given node." msgstr "" -#: of tensorcircuit.circuit.Circuit.apply_general_gate_delayed..apply:5 -#: tensorcircuit.densitymatrix.DMCircuit.apply_general_gate_delayed..apply:5 -#: tensorcircuit.mpscircuit.MPSCircuit.apply_general_gate_delayed..apply:3 +#: keras.src.engine.base_layer.Layer.get_input_mask_at:3 +#: keras.src.engine.base_layer.Layer.get_input_shape_at:3 +#: keras.src.engine.base_layer.Layer.get_output_mask_at:3 +#: keras.src.engine.base_layer.Layer.get_output_shape_at:3 of msgid "" -"Qubit number that the gate applies on. The matrix for the gate is .. " -"math:: \\begin{bmatrix} 0.+0.j & 1.+0.j\\\\ 1.+0.j & 0.+0.j " -"\\end{bmatrix}" +"Integer, index of the node from which to retrieve the attribute. E.g. " +"`node_index=0` will correspond to the first time the layer was called." msgstr "" -#: of tensorcircuit.circuit.Circuit.apply_general_gate_delayed..apply:8 -#: tensorcircuit.densitymatrix.DMCircuit.apply_general_gate_delayed..apply:8 -#: tensorcircuit.mpscircuit.MPSCircuit.apply_general_gate_delayed..apply:6 -msgid "\\begin{bmatrix} 0.+0.j & 1.+0.j\\\\ 1.+0.j & 0.+0.j \\end{bmatrix}" +#: keras.src.engine.base_layer.Layer.get_input_mask_at:8 of +msgid "A mask tensor (or list of tensors if the layer has multiple inputs)." msgstr "" -#: of tensorcircuit.circuit.Circuit.apply_general_gate_delayed..apply:1 -#: tensorcircuit.densitymatrix.DMCircuit.apply_general_gate_delayed..apply:1 -msgid "" -"Apply **Y** gate on the circuit. See " -":py:meth:`tensorcircuit.gates.y_gate`." +#: keras.src.engine.base_layer.Layer.get_input_shape_at:1 of +msgid "Retrieves the input shape(s) of a layer at a given node." msgstr "" -#: of tensorcircuit.circuit.Circuit.apply_general_gate_delayed..apply:5 -#: tensorcircuit.densitymatrix.DMCircuit.apply_general_gate_delayed..apply:5 -#: tensorcircuit.mpscircuit.MPSCircuit.apply_general_gate_delayed..apply:3 -msgid "" -"Qubit number that the gate applies on. The matrix for the gate is .. " -"math:: \\begin{bmatrix} 0.+0.j & 0.-1.j\\\\ 0.+1.j & 0.+0.j " -"\\end{bmatrix}" +#: keras.src.engine.base_layer.Layer.get_input_shape_at:8 of +msgid "A shape tuple (or list of shape tuples if the layer has multiple inputs)." msgstr "" -#: of tensorcircuit.circuit.Circuit.apply_general_gate_delayed..apply:8 -#: tensorcircuit.densitymatrix.DMCircuit.apply_general_gate_delayed..apply:8 -#: tensorcircuit.mpscircuit.MPSCircuit.apply_general_gate_delayed..apply:6 -msgid "\\begin{bmatrix} 0.+0.j & 0.-1.j\\\\ 0.+1.j & 0.+0.j \\end{bmatrix}" +#: keras.src.engine.training.Model.get_layer:1 of +msgid "Retrieves a layer based on either its name (unique) or index." msgstr "" -#: of tensorcircuit.circuit.Circuit.apply_general_gate_delayed..apply:1 -#: tensorcircuit.densitymatrix.DMCircuit.apply_general_gate_delayed..apply:1 +#: keras.src.engine.training.Model.get_layer:3 of msgid "" -"Apply **Z** gate on the circuit. See " -":py:meth:`tensorcircuit.gates.z_gate`." +"If `name` and `index` are both provided, `index` will take precedence. " +"Indices are based on order of horizontal graph traversal (bottom-up)." msgstr "" -#: of tensorcircuit.circuit.Circuit.apply_general_gate_delayed..apply:5 -#: tensorcircuit.densitymatrix.DMCircuit.apply_general_gate_delayed..apply:5 -#: tensorcircuit.mpscircuit.MPSCircuit.apply_general_gate_delayed..apply:3 -msgid "" -"Qubit number that the gate applies on. The matrix for the gate is .. " -"math:: \\begin{bmatrix} 1.+0.j & 0.+0.j\\\\ 0.+0.j & -1.+0.j" -" \\end{bmatrix}" +#: keras.src.engine.training.Model.get_layer:6 of +msgid "String, name of layer." msgstr "" -#: of tensorcircuit.circuit.Circuit.apply_general_gate_delayed..apply:8 -#: tensorcircuit.densitymatrix.DMCircuit.apply_general_gate_delayed..apply:8 -#: tensorcircuit.mpscircuit.MPSCircuit.apply_general_gate_delayed..apply:6 -msgid "\\begin{bmatrix} 1.+0.j & 0.+0.j\\\\ 0.+0.j & -1.+0.j \\end{bmatrix}" +#: keras.src.engine.training.Model.get_layer:7 of +msgid "Integer, index of layer." msgstr "" -#: of tensorcircuit.circuit.Circuit.__init__:1 -msgid "Circuit object based on state simulator." +#: keras.src.engine.training.Model.get_metrics_result:1 of +msgid "Returns the model's metrics values as a dict." msgstr "" -#: of tensorcircuit.circuit.Circuit.__init__:3 -#: tensorcircuit.mpscircuit.MPSCircuit.__init__:3 -msgid "The number of qubits in the circuit." +#: keras.src.engine.training.Model.get_metrics_result:3 of +msgid "" +"If any of the metric result is a dict (containing multiple metrics), each" +" of them gets added to the top level returned dict of this method." msgstr "" -#: of tensorcircuit.circuit.Circuit.__init__:5 +#: keras.src.engine.training.Model.get_metrics_result:6 of msgid "" -"If not None, the initial state of the circuit is taken as ``inputs`` " -"instead of :math:`\\vert 0\\rangle^n` qubits, defaults to None." +"A `dict` containing values of the metrics listed in `self.metrics`. " +"Example: `{'loss': 0.2, 'accuracy': 0.7}`." msgstr "" -#: of tensorcircuit.circuit.Circuit.__init__:8 -#: tensorcircuit.circuit.Circuit.replace_mps_inputs:18 -msgid "(Nodes, dangling Edges) for a MPS like initial wavefunction." +#: keras.src.engine.base_layer.Layer.get_output_at:1 of +msgid "Retrieves the output tensor(s) of a layer at a given node." msgstr "" -#: of tensorcircuit.circuit.Circuit.__init__:10 +#: keras.src.engine.base_layer.Layer.get_output_at:3 of msgid "" -"dict if two qubit gate is ready for split, including parameters for at " -"least one of ``max_singular_values`` and ``max_truncation_err``." +"Integer, index of the node from which to retrieve the attribute. E.g. " +"`node_index=0` will correspond to the first output node of the layer." msgstr "" -#: of tensorcircuit.circuit.Circuit.amplitude:1 -msgid "Returns the amplitude of the circuit given the bitstring l." +#: keras.src.engine.base_layer.Layer.get_output_at:8 of +msgid "A tensor (or list of tensors if the layer has multiple outputs)." msgstr "" -#: of tensorcircuit.circuit.Circuit.amplitude:13 -msgid "The bitstring of 0 and 1s." +#: keras.src.engine.base_layer.Layer.get_output_mask_at:1 of +msgid "Retrieves the output mask tensor(s) of a layer at a given node." msgstr "" -#: of tensorcircuit.circuit.Circuit.amplitude:15 -msgid "The amplitude of the circuit." +#: keras.src.engine.base_layer.Layer.get_output_mask_at:8 of +msgid "A mask tensor (or list of tensors if the layer has multiple outputs)." msgstr "" -#: of tensorcircuit.circuit.Circuit.append_from_qir:1 -msgid "" -"Apply the ciurict in form of quantum intermediate representation after " -"the current cirucit." +#: keras.src.engine.base_layer.Layer.get_output_shape_at:1 of +msgid "Retrieves the output shape(s) of a layer at a given node." msgstr "" -#: of tensorcircuit.circuit.Circuit.append_from_qir:18 -msgid "The quantum intermediate representation." +#: keras.src.engine.base_layer.Layer.get_output_shape_at:8 of +msgid "A shape tuple (or list of shape tuples if the layer has multiple outputs)." msgstr "" -#: of tensorcircuit.circuit.Circuit.apply_double_gate:1 -msgid "Apply the gate to two bits with given indexes." +#: keras.src.engine.training.Model.get_weight_paths:1 of +msgid "Retrieve all the variables and their paths for the model." msgstr "" -#: of tensorcircuit.circuit.Circuit.apply_double_gate:11 -msgid "The Gate applied on bits." +#: keras.src.engine.training.Model.get_weight_paths:3 of +msgid "" +"The variable path (string) is a stable key to identify a `tf.Variable` " +"instance owned by the model. It can be used to specify variable-specific " +"configurations (e.g. DTensor, quantization) from a global view." msgstr "" -#: of tensorcircuit.circuit.Circuit.apply_double_gate:13 -#: tensorcircuit.circuit.Circuit.apply_double_gate:15 -#: tensorcircuit.circuit.Circuit.apply_single_gate:13 -msgid "The index of the bit to apply the Gate." +#: keras.src.engine.training.Model.get_weight_paths:7 of +msgid "" +"This method returns a dict with weight object paths as keys and the " +"corresponding `tf.Variable` instances as values." msgstr "" -#: of tensorcircuit.circuit.Circuit.general_kraus:1 +#: keras.src.engine.training.Model.get_weight_paths:10 of msgid "" -"Monte Carlo trajectory simulation of general Kraus channel whose Kraus " -"operators cannot be amplified to unitary operators. For unitary operators" -" composed Kraus channel, :py:meth:`unitary_kraus` is much faster." +"Note that if the model is a subclassed model and the weights haven't been" +" initialized, an empty dict will be returned." msgstr "" -#: of tensorcircuit.circuit.Circuit.general_kraus:5 +#: keras.src.engine.training.Model.get_weight_paths:13 of msgid "" -"This function is jittable in theory. But only jax+GPU combination is " -"recommended for jit since the graph building time is too long for other " -"backend options; though the running time of the function is very fast for" -" every case." +"A dict where keys are variable paths and values are `tf.Variable` " +"instances." msgstr "" -#: of tensorcircuit.circuit.Circuit.general_kraus:9 -msgid "A list of ``tn.Node`` for Kraus operators." +#: keras.src.engine.training.Model.get_weight_paths:16 of +msgid "A dict where keys are variable paths and values are `tf.Variable`" msgstr "" -#: of tensorcircuit.circuit.Circuit.general_kraus:11 -msgid "The qubits index that Kraus channel is applied on." +#: keras.src.engine.training.Model.get_weight_paths:16 of +msgid "instances." msgstr "" -#: of tensorcircuit.circuit.Circuit.general_kraus:13 -msgid "" -"Random tensor uniformly between 0 or 1, defaults to be None, when the " -"random number will be generated automatically" +#: keras.src.engine.training.Model.get_weight_paths:20 of +msgid "```python class SubclassModel(tf.keras.Model):" msgstr "" -#: of tensorcircuit.circuit.Circuit.apply_single_gate:1 -msgid "Apply the gate to the bit with the given index." +#: keras.src.engine.training.Model.get_weight_paths:26 of +msgid "def __init__(self, name=None):" msgstr "" -#: of tensorcircuit.circuit.Circuit.apply_single_gate:11 -msgid "The Gate applied on the bit." +#: keras.src.engine.training.Model.get_weight_paths:24 of +msgid "" +"super().__init__(name=name) self.d1 = tf.keras.layers.Dense(10) self.d2 =" +" tf.keras.layers.Dense(20)" msgstr "" -#: of tensorcircuit.circuit.Circuit.cond_measurement:1 -msgid "" -"Measurement on z basis at ``index`` qubit based on quantum amplitude (not" -" post-selection). The highlight is that this method can return the " -"measured result as a int Tensor and thus maintained a jittable pipeline." +#: keras.src.engine.training.Model.get_weight_paths:29 of +msgid "x = self.d1(inputs) return self.d2(x)" msgstr "" -#: of tensorcircuit.circuit.Circuit.cond_measurement:14 -msgid "the qubit for the z-basis measurement" +#: keras.src.engine.training.Model.get_weight_paths:32 of +msgid "" +"model = SubclassModel() model(tf.zeros((10, 10))) weight_paths = " +"model.get_weight_paths() # weight_paths: # { # 'd1.kernel': " +"model.d1.kernel, # 'd1.bias': model.d1.bias, # 'd2.kernel': " +"model.d2.kernel, # 'd2.bias': model.d2.bias, # }" msgstr "" -#: of tensorcircuit.circuit.Circuit.cond_measurement:16 -msgid "0 or 1 for z measurement on up and down freedom" +#: keras.src.engine.training.Model.get_weight_paths:43 of +msgid "" +"# Functional model inputs = tf.keras.Input((10,), batch_size=10) x = " +"tf.keras.layers.Dense(20, name='d1')(inputs) output = " +"tf.keras.layers.Dense(30, name='d2')(x) model = tf.keras.Model(inputs, " +"output) d1 = model.layers[1] d2 = model.layers[2] weight_paths = " +"model.get_weight_paths() # weight_paths: # { # 'd1.kernel': d1.kernel," +" # 'd1.bias': d1.bias, # 'd2.kernel': d2.kernel, # 'd2.bias': " +"d2.bias, # } ```" msgstr "" -#: of tensorcircuit.circuit.Circuit.select_gate:1 -msgid "Apply ``which``-th gate from ``kraus`` list, i.e. apply kraus[which]" +#: keras.src.engine.training.Model.get_weights:1 of +msgid "Retrieves the weights of the model." msgstr "" -#: of tensorcircuit.circuit.Circuit.select_gate:3 -msgid "Tensor of shape [] and dtype int" +#: keras.src.engine.training.Model.get_weights:3 of +msgid "A flat list of Numpy arrays." msgstr "" -#: of tensorcircuit.circuit.Circuit.select_gate:5 -msgid "A list of gate in the form of ``tc.gate`` or Tensor" +#: of tensorcircuit.applications.van.MADE.inbound_nodes:1 +#: tensorcircuit.applications.van.MaskedConv2D.inbound_nodes:1 +#: tensorcircuit.applications.van.MaskedLinear.inbound_nodes:1 +#: tensorcircuit.applications.van.NMF.inbound_nodes:1 +#: tensorcircuit.applications.van.PixelCNN.inbound_nodes:1 +#: tensorcircuit.applications.van.ResidualBlock.inbound_nodes:1 +#: tensorcircuit.applications.vqes.Linear.inbound_nodes:1 +#: tensorcircuit.keras.HardwareLayer.inbound_nodes:1 +#: tensorcircuit.keras.QuantumLayer.inbound_nodes:1 +msgid "Return Functional API nodes upstream of this layer." msgstr "" -#: of tensorcircuit.circuit.Circuit.select_gate:7 -msgid "the qubit lines the gate applied on" +#: of tensorcircuit.applications.van.MADE.input:1 +#: tensorcircuit.applications.van.MaskedConv2D.input:1 +#: tensorcircuit.applications.van.MaskedLinear.input:1 +#: tensorcircuit.applications.van.NMF.input:1 +#: tensorcircuit.applications.van.PixelCNN.input:1 +#: tensorcircuit.applications.van.ResidualBlock.input:1 +#: tensorcircuit.applications.vqes.Linear.input:1 +#: tensorcircuit.keras.HardwareLayer.input:1 +#: tensorcircuit.keras.QuantumLayer.input:1 +msgid "Retrieves the input tensor(s) of a layer." msgstr "" -#: of tensorcircuit.circuit.Circuit.depolarizing:1 +#: of tensorcircuit.applications.van.MADE.input:3 +#: tensorcircuit.applications.van.MaskedConv2D.input:3 +#: tensorcircuit.applications.van.MaskedLinear.input:3 +#: tensorcircuit.applications.van.NMF.input:3 +#: tensorcircuit.applications.van.PixelCNN.input:3 +#: tensorcircuit.applications.van.ResidualBlock.input:3 +#: tensorcircuit.applications.vqes.Linear.input:3 +#: tensorcircuit.keras.HardwareLayer.input:3 +#: tensorcircuit.keras.QuantumLayer.input:3 msgid "" -"Apply depolarizing channel in a Monte Carlo way, i.e. for each call of " -"this method, one of gates from X, Y, Z, I are applied on the circuit " -"based on the probability indicated by ``px``, ``py``, ``pz``." +"Only applicable if the layer has exactly one input, i.e. if it is " +"connected to one incoming layer." +msgstr "" + +#: of tensorcircuit.applications.van.MADE.input:6 +#: tensorcircuit.applications.van.MaskedConv2D.input:6 +#: tensorcircuit.applications.van.MaskedLinear.input:6 +#: tensorcircuit.applications.van.NMF.input:6 +#: tensorcircuit.applications.van.PixelCNN.input:6 +#: tensorcircuit.applications.van.ResidualBlock.input:6 +#: tensorcircuit.applications.vqes.Linear.input:6 +#: tensorcircuit.keras.HardwareLayer.input:6 +#: tensorcircuit.keras.QuantumLayer.input:6 +msgid "Input tensor or list of input tensors." +msgstr "" + +#: of tensorcircuit.applications.van.MADE.input:9 +#: tensorcircuit.applications.van.MaskedConv2D.input:9 +#: tensorcircuit.applications.van.MaskedLinear.input:9 +#: tensorcircuit.applications.van.NMF.input:9 +#: tensorcircuit.applications.van.PixelCNN.input:9 +#: tensorcircuit.applications.van.ResidualBlock.input:9 +#: tensorcircuit.applications.vqes.Linear.input:9 +#: tensorcircuit.keras.HardwareLayer.input:9 +#: tensorcircuit.keras.QuantumLayer.input:9 +msgid "If no inbound nodes are found." +msgstr "" + +#: of tensorcircuit.applications.van.MADE.input_mask:1 +#: tensorcircuit.applications.van.MaskedConv2D.input_mask:1 +#: tensorcircuit.applications.van.MaskedLinear.input_mask:1 +#: tensorcircuit.applications.van.NMF.input_mask:1 +#: tensorcircuit.applications.van.PixelCNN.input_mask:1 +#: tensorcircuit.applications.van.ResidualBlock.input_mask:1 +#: tensorcircuit.applications.vqes.Linear.input_mask:1 +#: tensorcircuit.keras.HardwareLayer.input_mask:1 +#: tensorcircuit.keras.QuantumLayer.input_mask:1 +msgid "Retrieves the input mask tensor(s) of a layer." +msgstr "" + +#: of tensorcircuit.applications.van.MADE.input_mask:3 +#: tensorcircuit.applications.van.MADE.output_mask:3 +#: tensorcircuit.applications.van.MaskedConv2D.input_mask:3 +#: tensorcircuit.applications.van.MaskedConv2D.output_mask:3 +#: tensorcircuit.applications.van.MaskedLinear.input_mask:3 +#: tensorcircuit.applications.van.MaskedLinear.output_mask:3 +#: tensorcircuit.applications.van.NMF.input_mask:3 +#: tensorcircuit.applications.van.NMF.output_mask:3 +#: tensorcircuit.applications.van.PixelCNN.input_mask:3 +#: tensorcircuit.applications.van.PixelCNN.output_mask:3 +#: tensorcircuit.applications.van.ResidualBlock.input_mask:3 +#: tensorcircuit.applications.van.ResidualBlock.output_mask:3 +#: tensorcircuit.applications.vqes.Linear.input_mask:3 +#: tensorcircuit.applications.vqes.Linear.output_mask:3 +#: tensorcircuit.keras.HardwareLayer.input_mask:3 +#: tensorcircuit.keras.HardwareLayer.output_mask:3 +#: tensorcircuit.keras.QuantumLayer.input_mask:3 +#: tensorcircuit.keras.QuantumLayer.output_mask:3 +msgid "" +"Only applicable if the layer has exactly one inbound node, i.e. if it is " +"connected to one incoming layer." +msgstr "" + +#: of tensorcircuit.applications.van.MADE.input_mask:6 +#: tensorcircuit.applications.van.MaskedConv2D.input_mask:6 +#: tensorcircuit.applications.van.MaskedLinear.input_mask:6 +#: tensorcircuit.applications.van.NMF.input_mask:6 +#: tensorcircuit.applications.van.PixelCNN.input_mask:6 +#: tensorcircuit.applications.van.ResidualBlock.input_mask:6 +#: tensorcircuit.applications.vqes.Linear.input_mask:6 +#: tensorcircuit.keras.HardwareLayer.input_mask:6 +#: tensorcircuit.keras.QuantumLayer.input_mask:6 +msgid "Input mask tensor (potentially None) or list of input mask tensors." +msgstr "" + +#: of tensorcircuit.applications.van.MADE.input_mask:9 +#: tensorcircuit.applications.van.MADE.output_mask:9 +#: tensorcircuit.applications.van.MaskedConv2D.input_mask:9 +#: tensorcircuit.applications.van.MaskedConv2D.output_mask:9 +#: tensorcircuit.applications.van.MaskedLinear.input_mask:9 +#: tensorcircuit.applications.van.MaskedLinear.output_mask:9 +#: tensorcircuit.applications.van.NMF.input_mask:9 +#: tensorcircuit.applications.van.NMF.output_mask:9 +#: tensorcircuit.applications.van.PixelCNN.input_mask:9 +#: tensorcircuit.applications.van.PixelCNN.output_mask:9 +#: tensorcircuit.applications.van.ResidualBlock.input_mask:9 +#: tensorcircuit.applications.van.ResidualBlock.output_mask:9 +#: tensorcircuit.applications.vqes.Linear.input_mask:9 +#: tensorcircuit.applications.vqes.Linear.output_mask:9 +#: tensorcircuit.keras.HardwareLayer.input_mask:9 +#: tensorcircuit.keras.HardwareLayer.output_mask:9 +#: tensorcircuit.keras.QuantumLayer.input_mask:9 +#: tensorcircuit.keras.QuantumLayer.output_mask:9 +msgid "if the layer is connected to" +msgstr "" + +#: of tensorcircuit.applications.van.MADE.input_shape:1 +#: tensorcircuit.applications.van.MaskedConv2D.input_shape:1 +#: tensorcircuit.applications.van.MaskedLinear.input_shape:1 +#: tensorcircuit.applications.van.NMF.input_shape:1 +#: tensorcircuit.applications.van.PixelCNN.input_shape:1 +#: tensorcircuit.applications.van.ResidualBlock.input_shape:1 +#: tensorcircuit.applications.vqes.Linear.input_shape:1 +#: tensorcircuit.keras.HardwareLayer.input_shape:1 +#: tensorcircuit.keras.QuantumLayer.input_shape:1 +msgid "Retrieves the input shape(s) of a layer." +msgstr "" + +#: of tensorcircuit.applications.van.MADE.input_shape:3 +#: tensorcircuit.applications.van.MaskedConv2D.input_shape:3 +#: tensorcircuit.applications.van.MaskedLinear.input_shape:3 +#: tensorcircuit.applications.van.NMF.input_shape:3 +#: tensorcircuit.applications.van.PixelCNN.input_shape:3 +#: tensorcircuit.applications.van.ResidualBlock.input_shape:3 +#: tensorcircuit.applications.vqes.Linear.input_shape:3 +#: tensorcircuit.keras.HardwareLayer.input_shape:3 +#: tensorcircuit.keras.QuantumLayer.input_shape:3 +msgid "" +"Only applicable if the layer has exactly one input, i.e. if it is " +"connected to one incoming layer, or if all inputs have the same shape." +msgstr "" + +#: of tensorcircuit.applications.van.MADE.input_shape:7 +#: tensorcircuit.applications.van.MaskedConv2D.input_shape:7 +#: tensorcircuit.applications.van.MaskedLinear.input_shape:7 +#: tensorcircuit.applications.van.NMF.input_shape:7 +#: tensorcircuit.applications.van.PixelCNN.input_shape:7 +#: tensorcircuit.applications.van.ResidualBlock.input_shape:7 +#: tensorcircuit.applications.vqes.Linear.input_shape:7 +#: tensorcircuit.keras.HardwareLayer.input_shape:7 +#: tensorcircuit.keras.QuantumLayer.input_shape:7 +msgid "" +"Input shape, as an integer shape tuple (or list of shape tuples, one " +"tuple per input tensor)." +msgstr "" + +#: of tensorcircuit.applications.van.MADE.input_shape:10 +#: tensorcircuit.applications.van.MaskedConv2D.input_shape:10 +#: tensorcircuit.applications.van.MaskedLinear.input_shape:10 +#: tensorcircuit.applications.van.NMF.input_shape:10 +#: tensorcircuit.applications.van.PixelCNN.input_shape:10 +#: tensorcircuit.applications.van.ResidualBlock.input_shape:10 +#: tensorcircuit.applications.vqes.Linear.input_shape:10 +#: tensorcircuit.keras.HardwareLayer.input_shape:10 +#: tensorcircuit.keras.QuantumLayer.input_shape:10 +msgid "if the layer has no defined input_shape." +msgstr "" + +#: of tensorcircuit.applications.van.MADE.input_shape:11 +#: tensorcircuit.applications.van.MADE.output:9 +#: tensorcircuit.applications.van.MADE.output_shape:10 +#: tensorcircuit.applications.van.MaskedConv2D.input_shape:11 +#: tensorcircuit.applications.van.MaskedConv2D.output:9 +#: tensorcircuit.applications.van.MaskedConv2D.output_shape:10 +#: tensorcircuit.applications.van.MaskedLinear.input_shape:11 +#: tensorcircuit.applications.van.MaskedLinear.output:9 +#: tensorcircuit.applications.van.MaskedLinear.output_shape:10 +#: tensorcircuit.applications.van.NMF.input_shape:11 +#: tensorcircuit.applications.van.NMF.output:9 +#: tensorcircuit.applications.van.NMF.output_shape:10 +#: tensorcircuit.applications.van.PixelCNN.input_shape:11 +#: tensorcircuit.applications.van.PixelCNN.output:9 +#: tensorcircuit.applications.van.PixelCNN.output_shape:10 +#: tensorcircuit.applications.van.ResidualBlock.input_shape:11 +#: tensorcircuit.applications.van.ResidualBlock.output:9 +#: tensorcircuit.applications.van.ResidualBlock.output_shape:10 +#: tensorcircuit.applications.vqes.Linear.input_shape:11 +#: tensorcircuit.applications.vqes.Linear.output:9 +#: tensorcircuit.applications.vqes.Linear.output_shape:10 +#: tensorcircuit.keras.HardwareLayer.input_shape:11 +#: tensorcircuit.keras.HardwareLayer.output:9 +#: tensorcircuit.keras.HardwareLayer.output_shape:10 +#: tensorcircuit.keras.QuantumLayer.input_shape:11 +#: tensorcircuit.keras.QuantumLayer.output:9 +#: tensorcircuit.keras.QuantumLayer.output_shape:10 +msgid "if called in Eager mode." +msgstr "" + +#: of tensorcircuit.applications.van.MADE.input_spec:1 +#: tensorcircuit.applications.van.MaskedConv2D.input_spec:1 +#: tensorcircuit.applications.van.MaskedLinear.input_spec:1 +#: tensorcircuit.applications.van.NMF.input_spec:1 +#: tensorcircuit.applications.van.PixelCNN.input_spec:1 +#: tensorcircuit.applications.van.ResidualBlock.input_spec:1 +#: tensorcircuit.applications.vqes.Linear.input_spec:1 +#: tensorcircuit.keras.HardwareLayer.input_spec:1 +#: tensorcircuit.keras.QuantumLayer.input_spec:1 +msgid "`InputSpec` instance(s) describing the input format for this layer." +msgstr "" + +#: of tensorcircuit.applications.van.MADE.input_spec:3 +#: tensorcircuit.applications.van.MaskedConv2D.input_spec:3 +#: tensorcircuit.applications.van.MaskedLinear.input_spec:3 +#: tensorcircuit.applications.van.NMF.input_spec:3 +#: tensorcircuit.applications.van.PixelCNN.input_spec:3 +#: tensorcircuit.applications.van.ResidualBlock.input_spec:3 +#: tensorcircuit.applications.vqes.Linear.input_spec:3 +#: tensorcircuit.keras.HardwareLayer.input_spec:3 +#: tensorcircuit.keras.QuantumLayer.input_spec:3 +msgid "" +"When you create a layer subclass, you can set `self.input_spec` to enable" +" the layer to run input compatibility checks when it is called. Consider " +"a `Conv2D` layer: it can only be called on a single input tensor of rank " +"4. As such, you can set, in `__init__()`:" +msgstr "" + +#: of tensorcircuit.applications.van.MADE.input_spec:8 +#: tensorcircuit.applications.van.MaskedConv2D.input_spec:8 +#: tensorcircuit.applications.van.MaskedLinear.input_spec:8 +#: tensorcircuit.applications.van.NMF.input_spec:8 +#: tensorcircuit.applications.van.PixelCNN.input_spec:8 +#: tensorcircuit.applications.van.ResidualBlock.input_spec:8 +#: tensorcircuit.applications.vqes.Linear.input_spec:8 +#: tensorcircuit.keras.HardwareLayer.input_spec:8 +#: tensorcircuit.keras.QuantumLayer.input_spec:8 +msgid "```python self.input_spec = tf.keras.layers.InputSpec(ndim=4) ```" +msgstr "" + +#: of tensorcircuit.applications.van.MADE.input_spec:12 +#: tensorcircuit.applications.van.MaskedConv2D.input_spec:12 +#: tensorcircuit.applications.van.MaskedLinear.input_spec:12 +#: tensorcircuit.applications.van.NMF.input_spec:12 +#: tensorcircuit.applications.van.PixelCNN.input_spec:12 +#: tensorcircuit.applications.van.ResidualBlock.input_spec:12 +#: tensorcircuit.applications.vqes.Linear.input_spec:12 +#: tensorcircuit.keras.HardwareLayer.input_spec:12 +#: tensorcircuit.keras.QuantumLayer.input_spec:12 +msgid "" +"Now, if you try to call the layer on an input that isn't rank 4 (for " +"instance, an input of shape `(2,)`, it will raise a nicely-formatted " +"error:" +msgstr "" + +#: of tensorcircuit.applications.van.MADE.input_spec:16 +#: tensorcircuit.applications.van.MaskedConv2D.input_spec:16 +#: tensorcircuit.applications.van.MaskedLinear.input_spec:16 +#: tensorcircuit.applications.van.NMF.input_spec:16 +#: tensorcircuit.applications.van.PixelCNN.input_spec:16 +#: tensorcircuit.applications.van.ResidualBlock.input_spec:16 +#: tensorcircuit.applications.vqes.Linear.input_spec:16 +#: tensorcircuit.keras.HardwareLayer.input_spec:16 +#: tensorcircuit.keras.QuantumLayer.input_spec:16 +msgid "" +"``` ValueError: Input 0 of layer conv2d is incompatible with the layer: " +"expected ndim=4, found ndim=1. Full shape received: [2] ```" +msgstr "" + +#: of tensorcircuit.applications.van.MADE.input_spec:21 +#: tensorcircuit.applications.van.MaskedConv2D.input_spec:21 +#: tensorcircuit.applications.van.MaskedLinear.input_spec:21 +#: tensorcircuit.applications.van.NMF.input_spec:21 +#: tensorcircuit.applications.van.PixelCNN.input_spec:21 +#: tensorcircuit.applications.van.ResidualBlock.input_spec:21 +#: tensorcircuit.applications.vqes.Linear.input_spec:21 +#: tensorcircuit.keras.HardwareLayer.input_spec:21 +#: tensorcircuit.keras.QuantumLayer.input_spec:21 +msgid "" +"Input checks that can be specified via `input_spec` include: - Structure " +"(e.g. a single input, a list of 2 inputs, etc) - Shape - Rank (ndim) - " +"Dtype" msgstr "" -#: of tensorcircuit.circuit.Circuit.depolarizing:6 -msgid "The qubit that depolarizing channel is on" +#: of tensorcircuit.applications.van.MADE.input_spec:27 +#: tensorcircuit.applications.van.MaskedConv2D.input_spec:27 +#: tensorcircuit.applications.van.MaskedLinear.input_spec:27 +#: tensorcircuit.applications.van.NMF.input_spec:27 +#: tensorcircuit.applications.van.PixelCNN.input_spec:27 +#: tensorcircuit.applications.van.ResidualBlock.input_spec:27 +#: tensorcircuit.applications.vqes.Linear.input_spec:27 +#: tensorcircuit.keras.HardwareLayer.input_spec:27 +#: tensorcircuit.keras.QuantumLayer.input_spec:27 +msgid "For more information, see `tf.keras.layers.InputSpec`." msgstr "" -#: of tensorcircuit.circuit.Circuit.depolarizing:8 -msgid "probability for X noise" +#: of tensorcircuit.applications.van.MADE.input_spec:29 +#: tensorcircuit.applications.van.MaskedConv2D.input_spec:29 +#: tensorcircuit.applications.van.MaskedLinear.input_spec:29 +#: tensorcircuit.applications.van.NMF.input_spec:29 +#: tensorcircuit.applications.van.PixelCNN.input_spec:29 +#: tensorcircuit.applications.van.ResidualBlock.input_spec:29 +#: tensorcircuit.applications.vqes.Linear.input_spec:29 +#: tensorcircuit.keras.HardwareLayer.input_spec:29 +#: tensorcircuit.keras.QuantumLayer.input_spec:29 +msgid "A `tf.keras.layers.InputSpec` instance, or nested structure thereof." msgstr "" -#: of tensorcircuit.circuit.Circuit.depolarizing:10 -msgid "probability for Y noise" +#: of tensorcircuit.applications.van.MADE.jit_compile:1 +#: tensorcircuit.applications.van.NMF.jit_compile:1 +#: tensorcircuit.applications.van.PixelCNN.jit_compile:1 +msgid "Specify whether to compile the model with XLA." msgstr "" -#: of tensorcircuit.circuit.Circuit.depolarizing:12 -msgid "probability for Z noise" +#: of tensorcircuit.applications.van.MADE.jit_compile:3 +#: tensorcircuit.applications.van.NMF.jit_compile:3 +#: tensorcircuit.applications.van.PixelCNN.jit_compile:3 +msgid "" +"[XLA](https://www.tensorflow.org/xla) is an optimizing compiler for " +"machine learning. `jit_compile` is not enabled by default. Note that " +"`jit_compile=True` may not necessarily work for all models." msgstr "" -#: of tensorcircuit.circuit.Circuit.depolarizing:14 -msgid "random seed uniformly from 0 to 1, defaults to None (generated implicitly)" +#: of tensorcircuit.applications.van.MADE.jit_compile:7 +#: tensorcircuit.applications.van.NMF.jit_compile:7 +#: tensorcircuit.applications.van.PixelCNN.jit_compile:7 +msgid "" +"For more information on supported operations please refer to the [XLA " +"documentation](https://www.tensorflow.org/xla). Also refer to [known XLA " +"issues](https://www.tensorflow.org/xla/known_issues) for more details." msgstr "" -#: of tensorcircuit.circuit.Circuit.depolarizing:16 -msgid "int Tensor, the element lookup: [0: x, 1: y, 2: z, 3: I]" +#: keras.src.engine.base_layer.Layer.load_own_variables:1 of +msgid "Loads the state of the layer." msgstr "" -#: of tensorcircuit.circuit.Circuit.draw:1 +#: keras.src.engine.base_layer.Layer.load_own_variables:3 of msgid "" -"Visualise the circuit. This method recevies the keywords as same as " -"qiskit.circuit.QuantumCircuit.draw. More details can be found here: " -"https://qiskit.org/documentation/stubs/qiskit.circuit.QuantumCircuit.draw.html." +"You can override this method to take full control of how the state of the" +" layer is loaded upon calling `keras.models.load_model()`." msgstr "" -#: of tensorcircuit.circuit.Circuit.expectation:1 -#: tensorcircuit.densitymatrix.DMCircuit.expectation:1 -msgid "Compute the expectation of corresponding operators." +#: keras.src.engine.base_layer.Layer.load_own_variables:6 of +msgid "Dict from which the state of the model will be loaded." msgstr "" -#: of tensorcircuit.circuit.Circuit.expectation:10 -#: tensorcircuit.densitymatrix.DMCircuit.expectation:3 +#: keras.src.engine.training.Model.load_weights:1 of +msgid "Loads all layer weights from a saved files." +msgstr "" + +#: keras.src.engine.training.Model.load_weights:3 of msgid "" -"Operator and its position on the circuit, eg. ``(tc.gates.z(), [1, ]), " -"(tc.gates.x(), [2, ])`` is for operator :math:`Z_1X_2`." +"The saved file could be a SavedModel file, a `.keras` file (v3 saving " +"format), or a file created via `model.save_weights()`." msgstr "" -#: of tensorcircuit.circuit.Circuit.expectation:13 +#: keras.src.engine.training.Model.load_weights:6 of msgid "" -"If True, then the wavefunction tensor is cached for further expectation " -"evaluation, defaults to be true." +"By default, weights are loaded based on the network's topology. This " +"means the architecture should be the same as when the weights were saved." +" Note that layers that don't have weights are not taken into account in " +"the topological ordering, so adding or removing layers is fine as long as" +" they don't have weights." msgstr "" -#: of tensorcircuit.circuit.Circuit.expectation:16 -msgid "whether enable light cone simplification, defaults to False" +#: keras.src.engine.training.Model.load_weights:12 of +msgid "**Partial weight loading**" msgstr "" -#: of tensorcircuit.circuit.Circuit.expectation:18 -#: tensorcircuit.circuit.expectation:50 -msgid "\"Cannot measure two operators in one index\"" +#: keras.src.engine.training.Model.load_weights:14 of +msgid "" +"If you have modified your model, for instance by adding a new layer (with" +" weights) or by changing the shape of the weights of a layer, you can " +"choose to ignore errors and continue loading by setting " +"`skip_mismatch=True`. In this case any layer with mismatching weights " +"will be skipped. A warning will be displayed for each skipped layer." msgstr "" -#: of tensorcircuit.circuit.Circuit.expectation:19 -#: tensorcircuit.densitymatrix.DMCircuit.expectation:6 -msgid "Tensor with one element" +#: keras.src.engine.training.Model.load_weights:21 of +msgid "**Weight loading by name**" msgstr "" -#: of tensorcircuit.circuit.Circuit.expectation_before:1 +#: keras.src.engine.training.Model.load_weights:23 of msgid "" -"Return the list of nodes that consititues the expectation value just " -"before the contraction." +"If your weights are saved as a `.h5` file created via " +"`model.save_weights()`, you can use the argument `by_name=True`." msgstr "" -#: of tensorcircuit.circuit.Circuit.expectation_before:3 -msgid "whether contract the output state firstly, defaults to True" +#: keras.src.engine.training.Model.load_weights:26 of +msgid "" +"In this case, weights are loaded into layers only if they share the same " +"name. This is useful for fine-tuning or transfer-learning models where " +"some of the layers have changed." msgstr "" -#: of tensorcircuit.circuit.Circuit.expectation_before:5 -msgid "The tensor network for the expectation" +#: keras.src.engine.training.Model.load_weights:30 of +msgid "" +"Note that only topological loading (`by_name=False`) is supported when " +"loading weights from the `.keras` v3 format or from the TensorFlow " +"SavedModel format." msgstr "" -#: of tensorcircuit.circuit._expectation_ps:1 +#: keras.src.engine.training.Model.load_weights:34 of msgid "" -"Shortcut for Pauli string expectation. x, y, z list are for X, Y, Z " -"positions" +"String, path to the weights file to load. For weight files in TensorFlow " +"format, this is the file prefix (the same as was passed to " +"`save_weights()`). This can also be a path to a SavedModel or a `.keras` " +"file (v3 saving format) saved via `model.save()`." msgstr "" -#: of tensorcircuit.circuit._expectation_ps:12 -#: tensorcircuit.circuit._expectation_ps:14 -#: tensorcircuit.circuit._expectation_ps:16 -msgid "_description_, defaults to None" +#: keras.src.engine.training.Model.load_weights:39 of +msgid "" +"Boolean, whether to skip loading of layers where there is a mismatch in " +"the number of weights, or a mismatch in the shape of the weights." msgstr "" -#: of tensorcircuit.circuit._expectation_ps:18 -msgid "whether to cache and reuse the wavefunction, defaults to True" +#: keras.src.engine.training.Model.load_weights:42 of +msgid "" +"Boolean, whether to load weights by name or by topological order. Only " +"topological loading is supported for weight files in the `.keras` v3 " +"format or in the TensorFlow SavedModel format." msgstr "" -#: of tensorcircuit.circuit._expectation_ps:20 -msgid "Expectation value" +#: keras.src.engine.training.Model.load_weights:45 of +msgid "" +"Optional `tf.train.CheckpointOptions` object that specifies options for " +"loading weights (only valid for a SavedModel file)." msgstr "" -#: of tensorcircuit.circuit.Circuit.from_qir:1 -msgid "Restore the circuit from the quantum intermediate representation." +#: of tensorcircuit.applications.van.MADE.losses:1 +#: tensorcircuit.applications.van.MaskedConv2D.losses:1 +#: tensorcircuit.applications.van.MaskedLinear.losses:1 +#: tensorcircuit.applications.van.NMF.losses:1 +#: tensorcircuit.applications.van.PixelCNN.losses:1 +#: tensorcircuit.applications.van.ResidualBlock.losses:1 +#: tensorcircuit.applications.vqes.Linear.losses:1 +#: tensorcircuit.keras.HardwareLayer.losses:1 +#: tensorcircuit.keras.QuantumLayer.losses:1 +msgid "List of losses added using the `add_loss()` API." msgstr "" -#: of tensorcircuit.circuit.Circuit.from_qir:21 -#: tensorcircuit.translation.qir2qiskit:14 -msgid "The quantum intermediate representation of a circuit." +#: of tensorcircuit.applications.van.MADE.losses:3 +#: tensorcircuit.applications.van.MaskedConv2D.losses:3 +#: tensorcircuit.applications.van.MaskedLinear.losses:3 +#: tensorcircuit.applications.van.NMF.losses:3 +#: tensorcircuit.applications.van.PixelCNN.losses:3 +#: tensorcircuit.applications.van.ResidualBlock.losses:3 +#: tensorcircuit.applications.vqes.Linear.losses:3 +#: tensorcircuit.keras.HardwareLayer.losses:3 +#: tensorcircuit.keras.QuantumLayer.losses:3 +msgid "" +"Variable regularization tensors are created when this property is " +"accessed, so it is eager safe: accessing `losses` under a " +"`tf.GradientTape` will propagate gradients back to the corresponding " +"variables." msgstr "" -#: of tensorcircuit.circuit.Circuit.from_qir:23 -msgid "Extra circuit parameters." +#: keras.src.engine.training.Model.reset_metrics:3 of +#: tensorcircuit.applications.van.MADE.losses:8 +#: tensorcircuit.applications.van.MADE.metrics:6 +#: tensorcircuit.applications.van.MADE.metrics_names:6 +#: tensorcircuit.applications.van.MaskedConv2D.losses:8 +#: tensorcircuit.applications.van.MaskedLinear.losses:8 +#: tensorcircuit.applications.van.NMF.losses:8 +#: tensorcircuit.applications.van.NMF.metrics:6 +#: tensorcircuit.applications.van.NMF.metrics_names:6 +#: tensorcircuit.applications.van.PixelCNN.losses:8 +#: tensorcircuit.applications.van.PixelCNN.metrics:6 +#: tensorcircuit.applications.van.PixelCNN.metrics_names:6 +#: tensorcircuit.applications.van.ResidualBlock.losses:8 +#: tensorcircuit.applications.vqes.Linear.losses:8 +#: tensorcircuit.keras.HardwareLayer.losses:8 +#: tensorcircuit.keras.QuantumLayer.losses:8 +msgid "Examples:" msgstr "" -#: of tensorcircuit.circuit.Circuit.from_qir:25 -msgid "The circuit have same gates in the qir." +#: of tensorcircuit.applications.van.MADE.losses:40 +#: tensorcircuit.applications.van.MaskedConv2D.losses:40 +#: tensorcircuit.applications.van.MaskedLinear.losses:40 +#: tensorcircuit.applications.van.NMF.losses:40 +#: tensorcircuit.applications.van.PixelCNN.losses:40 +#: tensorcircuit.applications.van.ResidualBlock.losses:40 +#: tensorcircuit.applications.vqes.Linear.losses:40 +#: tensorcircuit.keras.HardwareLayer.losses:40 +#: tensorcircuit.keras.QuantumLayer.losses:40 +msgid "A list of tensors." msgstr "" -#: of tensorcircuit.circuit.Circuit.from_qiskit:1 -msgid "Import Qiskit QuantumCircuit object as a ``tc.Circuit`` object." +#: keras.src.engine.training.Model.make_predict_function:1 of +msgid "Creates a function that executes one step of inference." msgstr "" -#: of tensorcircuit.circuit.Circuit.from_qiskit:12 -msgid "Qiskit Circuit object" +#: keras.src.engine.training.Model.make_predict_function:3 of +msgid "" +"This method can be overridden to support custom inference logic. This " +"method is called by `Model.predict` and `Model.predict_on_batch`." msgstr "" -#: of tensorcircuit.circuit.Circuit.from_qiskit:14 -msgid "The number of qubits for the circuit" +#: keras.src.engine.training.Model.make_predict_function:6 of +msgid "" +"Typically, this method directly controls `tf.function` and " +"`tf.distribute.Strategy` settings, and delegates the actual evaluation " +"logic to `Model.predict_step`." msgstr "" -#: of tensorcircuit.circuit.Circuit.from_qiskit:16 -msgid "possible input wavefunction for ``tc.Circuit``, defaults to None" +#: keras.src.engine.training.Model.make_predict_function:10 of +msgid "" +"This function is cached the first time `Model.predict` or " +"`Model.predict_on_batch` is called. The cache is cleared whenever " +"`Model.compile` is called. You can skip the cache and generate again the " +"function with `force=True`." msgstr "" -#: of tensorcircuit.circuit.Circuit.from_qiskit:18 -msgid "The same circuit but as tensorcircuit object" +#: keras.src.engine.training.Model.make_predict_function:15 of +msgid "" +"Whether to regenerate the predict function and skip the cached function " +"if available." msgstr "" -#: of tensorcircuit.circuit.Circuit.get_quoperator:1 +#: keras.src.engine.training.Model.make_predict_function:18 of msgid "" -"Get the ``QuOperator`` MPO like representation of the circuit unitary " -"without contraction." +"Function. The function created by this method should accept a " +"`tf.data.Iterator`, and return the outputs of the `Model`." msgstr "" -#: of tensorcircuit.circuit.Circuit.get_quoperator:3 +#: keras.src.engine.training.Model.make_test_function:1 of +msgid "Creates a function that executes one step of evaluation." +msgstr "" + +#: keras.src.engine.training.Model.make_test_function:3 of msgid "" -"``QuOperator`` object for the circuit unitary (open indices for the input" -" state)" +"This method can be overridden to support custom evaluation logic. This " +"method is called by `Model.evaluate` and `Model.test_on_batch`." msgstr "" -#: of tensorcircuit.circuit.Circuit.get_quvector:1 +#: keras.src.engine.training.Model.make_test_function:6 of msgid "" -"Get the representation of the output state in the form of ``QuVector`` " -"while maintaining the circuit uncomputed" +"Typically, this method directly controls `tf.function` and " +"`tf.distribute.Strategy` settings, and delegates the actual evaluation " +"logic to `Model.test_step`." msgstr "" -#: of tensorcircuit.circuit.Circuit.get_quvector:4 -msgid "``QuVector`` representation of the output state from the circuit" +#: keras.src.engine.training.Model.make_test_function:10 of +msgid "" +"This function is cached the first time `Model.evaluate` or " +"`Model.test_on_batch` is called. The cache is cleared whenever " +"`Model.compile` is called. You can skip the cache and generate again the " +"function with `force=True`." msgstr "" -#: of tensorcircuit.circuit.Circuit.is_valid:1 -msgid "[WIP], check whether the circuit is legal." +#: keras.src.engine.training.Model.make_test_function:15 of +msgid "" +"Whether to regenerate the test function and skip the cached function if " +"available." msgstr "" -#: of tensorcircuit.circuit.Circuit.is_valid:3 -msgid "The bool indicating whether the circuit is legal" +#: keras.src.engine.training.Model.make_test_function:18 of +msgid "" +"Function. The function created by this method should accept a " +"`tf.data.Iterator`, and return a `dict` containing values that will be " +"passed to `tf.keras.Callbacks.on_test_batch_end`." msgstr "" -#: of tensorcircuit.circuit.Circuit.matrix:1 +#: keras.src.engine.training.Model.make_train_function:1 of +msgid "Creates a function that executes one step of training." +msgstr "" + +#: keras.src.engine.training.Model.make_train_function:3 of msgid "" -"Get the unitary matrix for the circuit irrespective with the circuit " -"input state." +"This method can be overridden to support custom training logic. This " +"method is called by `Model.fit` and `Model.train_on_batch`." msgstr "" -#: of tensorcircuit.circuit.Circuit.matrix:3 -msgid "The circuit unitary matrix" +#: keras.src.engine.training.Model.make_train_function:6 of +msgid "" +"Typically, this method directly controls `tf.function` and " +"`tf.distribute.Strategy` settings, and delegates the actual training " +"logic to `Model.train_step`." msgstr "" -#: of tensorcircuit.circuit.Circuit.measure_jit:1 +#: keras.src.engine.training.Model.make_train_function:10 of msgid "" -"Take measurement to the given quantum lines. This method is jittable is " -"and about 100 times faster than unjit version!" +"This function is cached the first time `Model.fit` or " +"`Model.train_on_batch` is called. The cache is cleared whenever " +"`Model.compile` is called. You can skip the cache and generate again the " +"function with `force=True`." msgstr "" -#: of tensorcircuit.circuit.Circuit.measure_jit:4 -#: tensorcircuit.circuit.Circuit.measure_reference:16 -#: tensorcircuit.densitymatrix.DMCircuit.measure_jit:3 -msgid "Measure on which quantum line." +#: keras.src.engine.training.Model.make_train_function:15 of +msgid "" +"Whether to regenerate the train function and skip the cached function if " +"available." msgstr "" -#: of tensorcircuit.circuit.Circuit.measure_jit:6 -#: tensorcircuit.circuit.Circuit.measure_reference:17 -#: tensorcircuit.densitymatrix.DMCircuit.measure_jit:5 -msgid "If true, theoretical probability is also returned." +#: keras.src.engine.training.Model.make_train_function:18 of +msgid "" +"Function. The function created by this method should accept a " +"`tf.data.Iterator`, and return a `dict` containing values that will be " +"passed to `tf.keras.Callbacks.on_train_batch_end`, such as `{'loss': 0.2," +" 'accuracy': 0.7}`." msgstr "" -#: of tensorcircuit.circuit.Circuit.measure_jit:8 -#: tensorcircuit.circuit.Circuit.measure_reference:18 -#: tensorcircuit.densitymatrix.DMCircuit.measure_jit:7 -msgid "The sample output and probability (optional) of the quantum line." +#: of tensorcircuit.applications.van.MADE.metrics:1 +#: tensorcircuit.applications.van.NMF.metrics:1 +#: tensorcircuit.applications.van.PixelCNN.metrics:1 +msgid "Return metrics added using `compile()` or `add_metric()`." msgstr "" -#: of tensorcircuit.circuit.Circuit.measure_reference:1 -msgid "Take measurement on the given quantum lines by ``index``." +#: of tensorcircuit.applications.van.MADE.metrics:3 +#: tensorcircuit.applications.van.NMF.metrics:3 +#: tensorcircuit.applications.van.PixelCNN.metrics:3 +msgid "" +"Note: Metrics passed to `compile()` are available only after a " +"`keras.Model` has been trained/evaluated on actual data." msgstr "" -#: of tensorcircuit.circuit.Circuit.mid_measurement:1 +#: of tensorcircuit.applications.van.MADE.metrics_names:1 +#: tensorcircuit.applications.van.NMF.metrics_names:1 +#: tensorcircuit.applications.van.PixelCNN.metrics_names:1 +msgid "Returns the model's display labels for all outputs." +msgstr "" + +#: of tensorcircuit.applications.van.MADE.metrics_names:3 +#: tensorcircuit.applications.van.NMF.metrics_names:3 +#: tensorcircuit.applications.van.PixelCNN.metrics_names:3 msgid "" -"Middle measurement in z-basis on the circuit, note the wavefunction " -"output is not normalized with ``mid_measurement`` involved, one should " -"normalize the state manually if needed. This is a post-selection method " -"as keep is provided as a prior." +"Note: `metrics_names` are available only after a `keras.Model` has been " +"trained/evaluated on actual data." msgstr "" -#: of tensorcircuit.circuit.Circuit.mid_measurement:5 -msgid "The index of qubit that the Z direction postselection applied on." +#: of tensorcircuit.applications.van.MADE.name:1 +#: tensorcircuit.applications.van.MaskedConv2D.name:1 +#: tensorcircuit.applications.van.MaskedLinear.name:1 +#: tensorcircuit.applications.van.NMF.name:1 +#: tensorcircuit.applications.van.PixelCNN.name:1 +#: tensorcircuit.applications.van.ResidualBlock.name:1 +#: tensorcircuit.applications.vqes.Linear.name:1 +#: tensorcircuit.keras.HardwareLayer.name:1 +#: tensorcircuit.keras.QuantumLayer.name:1 +msgid "Name of the layer (string), set in the constructor." msgstr "" -#: of tensorcircuit.circuit.Circuit.mid_measurement:7 -msgid "0 for spin up, 1 for spin down, defaults to be 0." +#: of tensorcircuit.applications.van.MADE.name_scope:1 +#: tensorcircuit.applications.van.MaskedConv2D.name_scope:1 +#: tensorcircuit.applications.van.MaskedLinear.name_scope:1 +#: tensorcircuit.applications.van.NMF.name_scope:1 +#: tensorcircuit.applications.van.PixelCNN.name_scope:1 +#: tensorcircuit.applications.van.ResidualBlock.name_scope:1 +#: tensorcircuit.applications.vqes.Linear.name_scope:1 +#: tensorcircuit.keras.HardwareLayer.name_scope:1 +#: tensorcircuit.keras.QuantumLayer.name_scope:1 +msgid "Returns a `tf.name_scope` instance for this class." msgstr "" -#: of tensorcircuit.circuit.Circuit.perfect_sampling:1 +#: of tensorcircuit.applications.van.MADE.non_trainable_variables:1 +#: tensorcircuit.applications.van.MaskedConv2D.non_trainable_variables:1 +#: tensorcircuit.applications.van.MaskedLinear.non_trainable_variables:1 +#: tensorcircuit.applications.van.NMF.non_trainable_variables:1 +#: tensorcircuit.applications.van.PixelCNN.non_trainable_variables:1 +#: tensorcircuit.applications.van.ResidualBlock.non_trainable_variables:1 +#: tensorcircuit.applications.vqes.Linear.non_trainable_variables:1 +#: tensorcircuit.keras.HardwareLayer.non_trainable_variables:1 +#: tensorcircuit.keras.QuantumLayer.non_trainable_variables:1 +msgid "" +"Sequence of non-trainable variables owned by this module and its " +"submodules." +msgstr "" + +#: of tensorcircuit.applications.van.MADE.non_trainable_variables:3 +#: tensorcircuit.applications.van.MADE.trainable_variables:3 +#: tensorcircuit.applications.van.MaskedConv2D.non_trainable_variables:3 +#: tensorcircuit.applications.van.MaskedConv2D.trainable_variables:3 +#: tensorcircuit.applications.van.MaskedLinear.non_trainable_variables:3 +#: tensorcircuit.applications.van.MaskedLinear.trainable_variables:3 +#: tensorcircuit.applications.van.NMF.non_trainable_variables:3 +#: tensorcircuit.applications.van.NMF.trainable_variables:3 +#: tensorcircuit.applications.van.PixelCNN.non_trainable_variables:3 +#: tensorcircuit.applications.van.PixelCNN.trainable_variables:3 +#: tensorcircuit.applications.van.ResidualBlock.non_trainable_variables:3 +#: tensorcircuit.applications.van.ResidualBlock.trainable_variables:3 +#: tensorcircuit.applications.vqes.Linear.non_trainable_variables:3 +#: tensorcircuit.applications.vqes.Linear.trainable_variables:3 +#: tensorcircuit.keras.HardwareLayer.non_trainable_variables:3 +#: tensorcircuit.keras.HardwareLayer.trainable_variables:3 +#: tensorcircuit.keras.QuantumLayer.non_trainable_variables:3 +#: tensorcircuit.keras.QuantumLayer.trainable_variables:3 +msgid "" +"Note: this method uses reflection to find variables on the current " +"instance and submodules. For performance reasons you may wish to cache " +"the result of calling this method if you don't expect the return value to" +" change." +msgstr "" + +#: of tensorcircuit.applications.van.MADE.non_trainable_variables:7 +#: tensorcircuit.applications.van.MADE.trainable_variables:7 +#: tensorcircuit.applications.van.MaskedConv2D.non_trainable_variables:7 +#: tensorcircuit.applications.van.MaskedConv2D.trainable_variables:7 +#: tensorcircuit.applications.van.MaskedLinear.non_trainable_variables:7 +#: tensorcircuit.applications.van.MaskedLinear.trainable_variables:7 +#: tensorcircuit.applications.van.NMF.non_trainable_variables:7 +#: tensorcircuit.applications.van.NMF.trainable_variables:7 +#: tensorcircuit.applications.van.PixelCNN.non_trainable_variables:7 +#: tensorcircuit.applications.van.PixelCNN.trainable_variables:7 +#: tensorcircuit.applications.van.ResidualBlock.non_trainable_variables:7 +#: tensorcircuit.applications.van.ResidualBlock.trainable_variables:7 +#: tensorcircuit.applications.vqes.Linear.non_trainable_variables:7 +#: tensorcircuit.applications.vqes.Linear.trainable_variables:7 +#: tensorcircuit.keras.HardwareLayer.non_trainable_variables:7 +#: tensorcircuit.keras.HardwareLayer.trainable_variables:7 +#: tensorcircuit.keras.QuantumLayer.non_trainable_variables:7 +#: tensorcircuit.keras.QuantumLayer.trainable_variables:7 +msgid "" +"A sequence of variables for the current module (sorted by attribute name)" +" followed by variables from all submodules recursively (breadth first)." +msgstr "" + +#: of tensorcircuit.applications.van.MADE.non_trainable_weights:1 +#: tensorcircuit.applications.van.MaskedConv2D.non_trainable_weights:1 +#: tensorcircuit.applications.van.MaskedLinear.non_trainable_weights:1 +#: tensorcircuit.applications.van.NMF.non_trainable_weights:1 +#: tensorcircuit.applications.van.PixelCNN.non_trainable_weights:1 +#: tensorcircuit.applications.van.ResidualBlock.non_trainable_weights:1 +#: tensorcircuit.applications.vqes.Linear.non_trainable_weights:1 +#: tensorcircuit.keras.HardwareLayer.non_trainable_weights:1 +#: tensorcircuit.keras.QuantumLayer.non_trainable_weights:1 +msgid "List of all non-trainable weights tracked by this layer." +msgstr "" + +#: of tensorcircuit.applications.van.MADE.non_trainable_weights:3 +#: tensorcircuit.applications.van.MaskedConv2D.non_trainable_weights:3 +#: tensorcircuit.applications.van.MaskedLinear.non_trainable_weights:3 +#: tensorcircuit.applications.van.NMF.non_trainable_weights:3 +#: tensorcircuit.applications.van.PixelCNN.non_trainable_weights:3 +#: tensorcircuit.applications.van.ResidualBlock.non_trainable_weights:3 +#: tensorcircuit.applications.vqes.Linear.non_trainable_weights:3 +#: tensorcircuit.keras.HardwareLayer.non_trainable_weights:3 +#: tensorcircuit.keras.QuantumLayer.non_trainable_weights:3 +msgid "" +"Non-trainable weights are *not* updated during training. They are " +"expected to be updated manually in `call()`." +msgstr "" + +#: of tensorcircuit.applications.van.MADE.non_trainable_weights:6 +#: tensorcircuit.applications.van.MaskedConv2D.non_trainable_weights:6 +#: tensorcircuit.applications.van.MaskedLinear.non_trainable_weights:6 +#: tensorcircuit.applications.van.NMF.non_trainable_weights:6 +#: tensorcircuit.applications.van.PixelCNN.non_trainable_weights:6 +#: tensorcircuit.applications.van.ResidualBlock.non_trainable_weights:6 +#: tensorcircuit.applications.vqes.Linear.non_trainable_weights:6 +#: tensorcircuit.keras.HardwareLayer.non_trainable_weights:6 +#: tensorcircuit.keras.QuantumLayer.non_trainable_weights:6 +msgid "A list of non-trainable variables." +msgstr "" + +#: of tensorcircuit.applications.van.MADE.outbound_nodes:1 +#: tensorcircuit.applications.van.MaskedConv2D.outbound_nodes:1 +#: tensorcircuit.applications.van.MaskedLinear.outbound_nodes:1 +#: tensorcircuit.applications.van.NMF.outbound_nodes:1 +#: tensorcircuit.applications.van.PixelCNN.outbound_nodes:1 +#: tensorcircuit.applications.van.ResidualBlock.outbound_nodes:1 +#: tensorcircuit.applications.vqes.Linear.outbound_nodes:1 +#: tensorcircuit.keras.HardwareLayer.outbound_nodes:1 +#: tensorcircuit.keras.QuantumLayer.outbound_nodes:1 +msgid "Return Functional API nodes downstream of this layer." +msgstr "" + +#: of tensorcircuit.applications.van.MADE.output:1 +#: tensorcircuit.applications.van.MaskedConv2D.output:1 +#: tensorcircuit.applications.van.MaskedLinear.output:1 +#: tensorcircuit.applications.van.NMF.output:1 +#: tensorcircuit.applications.van.PixelCNN.output:1 +#: tensorcircuit.applications.van.ResidualBlock.output:1 +#: tensorcircuit.applications.vqes.Linear.output:1 +#: tensorcircuit.keras.HardwareLayer.output:1 +#: tensorcircuit.keras.QuantumLayer.output:1 +msgid "Retrieves the output tensor(s) of a layer." +msgstr "" + +#: of tensorcircuit.applications.van.MADE.output:3 +#: tensorcircuit.applications.van.MaskedConv2D.output:3 +#: tensorcircuit.applications.van.MaskedLinear.output:3 +#: tensorcircuit.applications.van.NMF.output:3 +#: tensorcircuit.applications.van.PixelCNN.output:3 +#: tensorcircuit.applications.van.ResidualBlock.output:3 +#: tensorcircuit.applications.vqes.Linear.output:3 +#: tensorcircuit.keras.HardwareLayer.output:3 +#: tensorcircuit.keras.QuantumLayer.output:3 +msgid "" +"Only applicable if the layer has exactly one output, i.e. if it is " +"connected to one incoming layer." +msgstr "" + +#: of tensorcircuit.applications.van.MADE.output:6 +#: tensorcircuit.applications.van.MaskedConv2D.output:6 +#: tensorcircuit.applications.van.MaskedLinear.output:6 +#: tensorcircuit.applications.van.NMF.output:6 +#: tensorcircuit.applications.van.PixelCNN.output:6 +#: tensorcircuit.applications.van.ResidualBlock.output:6 +#: tensorcircuit.applications.vqes.Linear.output:6 +#: tensorcircuit.keras.HardwareLayer.output:6 +#: tensorcircuit.keras.QuantumLayer.output:6 +msgid "Output tensor or list of output tensors." +msgstr "" + +#: of tensorcircuit.applications.van.MADE.output:8 +#: tensorcircuit.applications.van.MaskedConv2D.output:8 +#: tensorcircuit.applications.van.MaskedLinear.output:8 +#: tensorcircuit.applications.van.NMF.output:8 +#: tensorcircuit.applications.van.PixelCNN.output:8 +#: tensorcircuit.applications.van.ResidualBlock.output:8 +#: tensorcircuit.applications.vqes.Linear.output:8 +#: tensorcircuit.keras.HardwareLayer.output:8 +#: tensorcircuit.keras.QuantumLayer.output:8 +msgid "if the layer is connected to more than one incoming layers." +msgstr "" + +#: of tensorcircuit.applications.van.MADE.output_mask:1 +#: tensorcircuit.applications.van.MaskedConv2D.output_mask:1 +#: tensorcircuit.applications.van.MaskedLinear.output_mask:1 +#: tensorcircuit.applications.van.NMF.output_mask:1 +#: tensorcircuit.applications.van.PixelCNN.output_mask:1 +#: tensorcircuit.applications.van.ResidualBlock.output_mask:1 +#: tensorcircuit.applications.vqes.Linear.output_mask:1 +#: tensorcircuit.keras.HardwareLayer.output_mask:1 +#: tensorcircuit.keras.QuantumLayer.output_mask:1 +msgid "Retrieves the output mask tensor(s) of a layer." +msgstr "" + +#: of tensorcircuit.applications.van.MADE.output_mask:6 +#: tensorcircuit.applications.van.MaskedConv2D.output_mask:6 +#: tensorcircuit.applications.van.MaskedLinear.output_mask:6 +#: tensorcircuit.applications.van.NMF.output_mask:6 +#: tensorcircuit.applications.van.PixelCNN.output_mask:6 +#: tensorcircuit.applications.van.ResidualBlock.output_mask:6 +#: tensorcircuit.applications.vqes.Linear.output_mask:6 +#: tensorcircuit.keras.HardwareLayer.output_mask:6 +#: tensorcircuit.keras.QuantumLayer.output_mask:6 +msgid "Output mask tensor (potentially None) or list of output mask tensors." +msgstr "" + +#: of tensorcircuit.applications.van.MADE.output_shape:1 +#: tensorcircuit.applications.van.MaskedConv2D.output_shape:1 +#: tensorcircuit.applications.van.MaskedLinear.output_shape:1 +#: tensorcircuit.applications.van.NMF.output_shape:1 +#: tensorcircuit.applications.van.PixelCNN.output_shape:1 +#: tensorcircuit.applications.van.ResidualBlock.output_shape:1 +#: tensorcircuit.applications.vqes.Linear.output_shape:1 +#: tensorcircuit.keras.HardwareLayer.output_shape:1 +#: tensorcircuit.keras.QuantumLayer.output_shape:1 +msgid "Retrieves the output shape(s) of a layer." +msgstr "" + +#: of tensorcircuit.applications.van.MADE.output_shape:3 +#: tensorcircuit.applications.van.MaskedConv2D.output_shape:3 +#: tensorcircuit.applications.van.MaskedLinear.output_shape:3 +#: tensorcircuit.applications.van.NMF.output_shape:3 +#: tensorcircuit.applications.van.PixelCNN.output_shape:3 +#: tensorcircuit.applications.van.ResidualBlock.output_shape:3 +#: tensorcircuit.applications.vqes.Linear.output_shape:3 +#: tensorcircuit.keras.HardwareLayer.output_shape:3 +#: tensorcircuit.keras.QuantumLayer.output_shape:3 msgid "" -"Sampling bistrings from the circuit output based on quantum amplitudes. " -"Reference: arXiv:1201.3974." +"Only applicable if the layer has one output, or if all outputs have the " +"same shape." msgstr "" -#: of tensorcircuit.circuit.Circuit.perfect_sampling:4 -#: tensorcircuit.densitymatrix.DMCircuit.perfect_sampling:3 -msgid "Sampled bit string and the corresponding theoretical probability." +#: of tensorcircuit.applications.van.MADE.output_shape:6 +#: tensorcircuit.applications.van.MaskedConv2D.output_shape:6 +#: tensorcircuit.applications.van.MaskedLinear.output_shape:6 +#: tensorcircuit.applications.van.NMF.output_shape:6 +#: tensorcircuit.applications.van.PixelCNN.output_shape:6 +#: tensorcircuit.applications.van.ResidualBlock.output_shape:6 +#: tensorcircuit.applications.vqes.Linear.output_shape:6 +#: tensorcircuit.keras.HardwareLayer.output_shape:6 +#: tensorcircuit.keras.QuantumLayer.output_shape:6 +msgid "" +"Output shape, as an integer shape tuple (or list of shape tuples, one " +"tuple per output tensor)." msgstr "" -#: of tensorcircuit.circuit.Circuit.replace_inputs:1 -msgid "Replace the input state with the circuit structure unchanged." +#: of tensorcircuit.applications.van.MADE.output_shape:9 +#: tensorcircuit.applications.van.MaskedConv2D.output_shape:9 +#: tensorcircuit.applications.van.MaskedLinear.output_shape:9 +#: tensorcircuit.applications.van.NMF.output_shape:9 +#: tensorcircuit.applications.van.PixelCNN.output_shape:9 +#: tensorcircuit.applications.van.ResidualBlock.output_shape:9 +#: tensorcircuit.applications.vqes.Linear.output_shape:9 +#: tensorcircuit.keras.HardwareLayer.output_shape:9 +#: tensorcircuit.keras.QuantumLayer.output_shape:9 +msgid "if the layer has no defined output shape." msgstr "" -#: of tensorcircuit.circuit.Circuit.replace_inputs:3 -msgid "Input wavefunction." +#: keras.src.engine.training.Model.predict:1 of +msgid "Generates output predictions for the input samples." msgstr "" -#: of tensorcircuit.circuit.Circuit.replace_mps_inputs:1 +#: keras.src.engine.training.Model.predict:3 of msgid "" -"Replace the input state in MPS representation while keep the circuit " -"structure unchanged." +"Computation is done in batches. This method is designed for batch " +"processing of large numbers of inputs. It is not intended for use inside " +"of loops that iterate over your data and process small numbers of inputs " +"at a time." msgstr "" -#: of tensorcircuit.circuit.Circuit.sample:1 -msgid "batched sampling from state or circuit tensor network directly" +#: keras.src.engine.training.Model.predict:8 of +msgid "" +"For small numbers of inputs that fit in one batch, directly use " +"`__call__()` for faster execution, e.g., `model(x)`, or `model(x, " +"training=False)` if you have layers such as " +"`tf.keras.layers.BatchNormalization` that behave differently during " +"inference. You may pair the individual model call with a `tf.function` " +"for additional performance inside your inner loop. If you need access to " +"numpy array values instead of tensors after your model call, you can use " +"`tensor.numpy()` to get the numpy array value of an eager tensor." msgstr "" -#: of tensorcircuit.circuit.Circuit.sample:3 -msgid "number of samples, defaults to None" +#: keras.src.engine.training.Model.predict:18 of +msgid "" +"Also, note the fact that test loss is not affected by regularization " +"layers like noise and dropout." msgstr "" -#: of tensorcircuit.circuit.Circuit.sample:5 +#: keras.src.engine.training.Model.predict:21 of msgid "" -"if true, we sample from the final state if memory allsows, True is " -"prefered, defaults to False" +"Note: See [this FAQ entry]( https://keras.io/getting_started/faq/#whats-" +"the-difference-between-model-methods-predict-and-call) for more details " +"about the difference between `Model` methods `predict()` and " +"`__call__()`." msgstr "" -#: of tensorcircuit.circuit.Circuit.sample:8 -msgid "random generator, defaults to None" +#: keras.src.engine.training.Model.predict:26 of +msgid "" +"Input samples. It could be: - A Numpy array (or array-like), or a list of" +" arrays (in case the model has multiple inputs). - A TensorFlow tensor," +" or a list of tensors (in case the model has multiple inputs). - A " +"`tf.data` dataset. - A generator or `keras.utils.Sequence` instance. A " +"more detailed description of unpacking behavior for iterator types " +"(Dataset, generator, Sequence) is given in the `Unpacking behavior for " +"iterator-like inputs` section of `Model.fit`." msgstr "" -#: of tensorcircuit.circuit.Circuit.sample:10 +#: keras.src.engine.training.Model.predict:26 of msgid "" -"List (if batch) of tuple (binary configuration tensor and correponding " -"probability)" +"Input samples. It could be: - A Numpy array (or array-like), or a list of" +" arrays" msgstr "" -#: of tensorcircuit.circuit.Circuit.wavefunction:1 -#: tensorcircuit.mpscircuit.MPSCircuit.wavefunction:1 -msgid "Compute the output wavefunction from the circuit." +#: keras.src.engine.training.Model.predict:31 of +msgid "A `tf.data` dataset." msgstr "" -#: of tensorcircuit.circuit.Circuit.wavefunction:3 +#: keras.src.engine.training.Model.predict:32 of +msgid "A generator or `keras.utils.Sequence` instance." +msgstr "" + +#: keras.src.engine.training.Model.predict:36 of msgid "" -"The str indicating the form of the output wavefunction. \"default\": " -"[-1], \"ket\": [-1, 1], \"bra\": [1, -1]" +"Integer or `None`. Number of samples per batch. If unspecified, " +"`batch_size` will default to 32. Do not specify the `batch_size` if your " +"data is in the form of dataset, generators, or `keras.utils.Sequence` " +"instances (since they generate batches)." msgstr "" -#: of tensorcircuit.circuit.Circuit.wavefunction:6 -msgid "Tensor with the corresponding shape." +#: keras.src.engine.training.Model.predict:49 of +msgid "" +"Total number of steps (batches of samples) before declaring the " +"prediction round finished. Ignored with the default value of `None`. If x" +" is a `tf.data` dataset and `steps` is None, `predict()` will run until " +"the input dataset is exhausted." msgstr "" -#: of tensorcircuit.circuit.Circuit.vis_tex:1 -msgid "Generate latex string based on quantikz latex package" +#: keras.src.engine.training.Model.predict:54 of +msgid "" +"List of `keras.callbacks.Callback` instances. List of callbacks to apply " +"during prediction. See [callbacks]( " +"https://www.tensorflow.org/api_docs/python/tf/keras/callbacks)." msgstr "" -#: of tensorcircuit.circuit.Circuit.vis_tex:3 -msgid "Latex string that can be directly compiled via, e.g. latexit" +#: keras.src.engine.training.Model.predict:74 of +msgid "" +"See the discussion of `Unpacking behavior for iterator-like inputs` for " +"`Model.fit`. Note that Model.predict uses the same interpretation rules " +"as `Model.fit` and `Model.evaluate`, so inputs must be unambiguous for " +"all three methods." msgstr "" -#: of tensorcircuit.circuit.Circuit.to_qir:1 -msgid "Return the quantum intermediate representation of the circuit." +#: keras.src.engine.training.Model.predict:79 +#: keras.src.engine.training.Model.predict_on_batch:9 of +msgid "Numpy array(s) of predictions." msgstr "" -#: of tensorcircuit.circuit.Circuit.to_qir:32 -msgid "The quantum intermediate representation of the circuit." +#: keras.src.engine.training.Model.predict:81 of +msgid "If `model.predict` is wrapped in a `tf.function`." msgstr "" -#: of tensorcircuit.circuit.Circuit.to_qiskit:1 -msgid "Translate ``tc.Circuit`` to a qiskit QuantumCircuit object." +#: keras.src.engine.training.Model.predict:82 of +msgid "" +"In case of mismatch between the provided input data and the model's " +"expectations, or in case a stateful model receives a number of " +"samples that is not a multiple of the batch size." msgstr "" -#: of tensorcircuit.circuit.Circuit.to_qiskit:3 -msgid "A qiskit object of this circuit." +#: keras.src.engine.training.Model.predict_generator:1 of +msgid "Generates predictions for the input samples from a data generator." msgstr "" -#: of tensorcircuit.circuit.Circuit.unitary_kraus:1 +#: keras.src.engine.training.Model.predict_generator:4 of msgid "" -"Apply unitary gates in ``kraus`` randomly based on corresponding " -"``prob``. If ``prob`` is ``None``, this is reduced to kraus channel " -"language." +"`Model.predict` now supports generators, so there is no longer any need " +"to use this endpoint." msgstr "" -#: of tensorcircuit.circuit.Circuit.unitary_kraus:4 -msgid "List of ``tc.gates.Gate`` or just Tensors" +#: keras.src.engine.training.Model.predict_on_batch:1 of +msgid "Returns predictions for a single batch of samples." msgstr "" -#: of tensorcircuit.circuit.Circuit.unitary_kraus:6 -msgid "prob list with the same size as ``kraus``, defaults to None" +#: keras.src.engine.training.Model.predict_on_batch:3 of +msgid "" +"Input data. It could be: - A Numpy array (or array-like), or a list of " +"arrays (in case the model has multiple inputs). - A TensorFlow " +"tensor, or a list of tensors (in case the model has multiple inputs)." msgstr "" -#: of tensorcircuit.circuit.Circuit.unitary_kraus:8 -msgid "random seed between 0 to 1, defaults to None" +#: keras.src.engine.training.Model.predict_on_batch:3 +#: keras.src.engine.training.Model.test_on_batch:3 of +msgid "" +"Input data. It could be: - A Numpy array (or array-like), or a list of " +"arrays (in case the" msgstr "" -#: of tensorcircuit.circuit.Circuit.unitary_kraus:10 -msgid "shape [] int dtype tensor indicates which kraus gate is actually applied" +#: keras.src.engine.training.Model.predict_on_batch:5 +#: keras.src.engine.training.Model.test_on_batch:5 of +msgid "model has multiple inputs)." msgstr "" -#: of tensorcircuit.circuit.expectation:1 -msgid "Compute :math:`\\langle bra\\vert ops \\vert ket\\rangle`." +#: keras.src.engine.training.Model.predict_on_batch:7 +#: keras.src.engine.training.Model.test_on_batch:6 of +msgid "A TensorFlow tensor, or a list of tensors (in case the model has" msgstr "" -#: of tensorcircuit.circuit.expectation:3 -msgid "Example 1 (:math:`bra` is same as :math:`ket`)" +#: keras.src.engine.training.Model.predict_on_batch:7 +#: keras.src.engine.training.Model.test_on_batch:7 of +msgid "multiple inputs)." msgstr "" -#: of tensorcircuit.circuit.expectation:24 -msgid "Example 2 (:math:`bra` is different from :math:`ket`)" +#: keras.src.engine.training.Model.predict_on_batch:11 of +msgid "If `model.predict_on_batch` is wrapped in a `tf.function`." msgstr "" -#: of tensorcircuit.circuit.expectation:42 -msgid ":math:`ket`. The state in tensor or ``QuVector`` format" +#: keras.src.engine.training.Model.predict_step:1 of +msgid "The logic for one inference step." msgstr "" -#: of tensorcircuit.circuit.expectation:44 -msgid ":math:`bra`, defaults to None, which is the same as ``ket``." +#: keras.src.engine.training.Model.predict_step:3 of +msgid "" +"This method can be overridden to support custom inference logic. This " +"method is called by `Model.make_predict_function`." msgstr "" -#: of tensorcircuit.circuit.expectation:46 +#: keras.src.engine.training.Model.predict_step:6 of msgid "" -":math:`bra` changes to the adjoint matrix of :math:`bra`, defaults to " -"True." +"This method should contain the mathematical logic for one step of " +"inference. This typically includes the forward pass." msgstr "" -#: of tensorcircuit.circuit.expectation:48 -msgid "Normalize the :math:`ket` and :math:`bra`, defaults to False." +#: keras.src.engine.training.Model.predict_step:9 of +msgid "" +"Configuration details for *how* this logic is run (e.g. `tf.function` and" +" `tf.distribute.Strategy` settings), should be left to " +"`Model.make_predict_function`, which can also be overridden." msgstr "" -#: of tensorcircuit.circuit.expectation:51 -msgid "The result of :math:`\\langle bra\\vert ops \\vert ket\\rangle`." +#: keras.src.engine.training.Model.predict_step:13 +#: keras.src.engine.training.Model.test_step:15 +#: keras.src.engine.training.Model.train_step:17 of +msgid "A nested structure of `Tensor`s." msgstr "" -#: of tensorcircuit.circuit.to_graphviz:1 +#: keras.src.engine.training.Model.predict_step:15 of msgid "" -"Not an ideal visualization for quantum circuit, but reserve here as a " -"general approach to show the tensornetwork [Deprecated, use " -"``Circuit.vis_tex`` or ``Circuit.draw`` instead]" +"The result of one inference step, typically the output of calling the " +"`Model` on data." msgstr "" -#: ../../source/api/cons.rst:2 -msgid "tensorcircuit.cons" +#: keras.src.engine.training.Model.reset_metrics:1 of +msgid "Resets the state of all the metrics in the model." msgstr "" -#: of tensorcircuit.cons:1 -msgid "Constants and setups" +#: of tensorcircuit.applications.van.MADE.run_eagerly:1 +#: tensorcircuit.applications.van.NMF.run_eagerly:1 +#: tensorcircuit.applications.van.PixelCNN.run_eagerly:1 +msgid "Settable attribute indicating whether the model should run eagerly." msgstr "" -#: of tensorcircuit.cons.get_contractor:1 tensorcircuit.cons.set_contractor:1 +#: of tensorcircuit.applications.van.MADE.run_eagerly:3 +#: tensorcircuit.applications.van.NMF.run_eagerly:3 +#: tensorcircuit.applications.van.PixelCNN.run_eagerly:3 msgid "" -"To set runtime contractor of the tensornetwork for a better contraction " -"path. For more information on the usage of contractor, please refer to " -"independent tutorial." +"Running eagerly means that your model will be run step by step, like " +"Python code. Your model might run slower, but it should become easier for" +" you to debug it by stepping into individual layer calls." msgstr "" -#: of tensorcircuit.cons.get_contractor:4 tensorcircuit.cons.set_contractor:4 +#: of tensorcircuit.applications.van.MADE.run_eagerly:7 +#: tensorcircuit.applications.van.NMF.run_eagerly:7 +#: tensorcircuit.applications.van.PixelCNN.run_eagerly:7 msgid "" -"\"auto\", \"greedy\", \"branch\", \"plain\", \"tng\", \"custom\", " -"\"custom_stateful\". defaults to None (\"auto\")" +"By default, we will attempt to compile your model to a static graph to " +"deliver the best execution performance." msgstr "" -#: of tensorcircuit.cons.get_contractor:6 tensorcircuit.cons.set_contractor:6 -msgid "Valid for \"custom\" or \"custom_stateful\" as method, defaults to None" +#: of tensorcircuit.applications.van.MADE.run_eagerly:10 +#: tensorcircuit.applications.van.NMF.run_eagerly:10 +#: tensorcircuit.applications.van.PixelCNN.run_eagerly:10 +msgid "Boolean, whether the model should run eagerly." msgstr "" -#: of tensorcircuit.cons.get_contractor:8 tensorcircuit.cons.set_contractor:8 -msgid "" -"It is not very useful, as ``memory_limit`` leads to ``branch`` " -"contraction instead of ``greedy`` which is rather slow, defaults to None" +#: keras.src.engine.training.Model.save:1 of +msgid "Saves a model as a TensorFlow SavedModel or HDF5 file." msgstr "" -#: of tensorcircuit.cons.get_contractor:11 tensorcircuit.cons.set_contractor:11 -msgid "Tensornetwork version is too low to support some of the contractors." +#: keras.src.engine.training.Model.save:4 of +msgid "See the [Serialization and Saving guide](" msgstr "" -#: of tensorcircuit.cons.get_contractor:12 tensorcircuit.cons.set_contractor:12 -msgid "Unknown method options." +#: keras.src.engine.training.Model.save:4 of +msgid "https://keras.io/guides/serialization_and_saving/) for details." msgstr "" -#: of tensorcircuit.cons.get_contractor:13 tensorcircuit.cons.set_contractor:13 -msgid "The new tensornetwork with its contractor set." +#: keras.src.engine.training.Model.save:6 of +msgid "Keras model instance to be saved." msgstr "" -#: of tensorcircuit.cons.get_dtype:1 tensorcircuit.cons.set_dtype:1 -msgid "Set the global runtime numerical dtype of tensors." +#: keras.src.engine.training.Model.save:7 of +msgid "`str` or `pathlib.Path` object. Path where to save the model." msgstr "" -#: of tensorcircuit.cons.get_dtype:3 tensorcircuit.cons.set_dtype:3 +#: keras.src.engine.training.Model.save:9 of msgid "" -"\"complex64\" or \"complex128\", defaults to None, which is equivalent to" -" \"complex64\"." +"Whether we should overwrite any existing model at the target location, or" +" instead ask the user via an interactive prompt." msgstr "" -#: of tensorcircuit.cons.get_dtype:5 tensorcircuit.cons.set_dtype:5 -msgid "complex dtype str and the corresponding real dtype str" +#: keras.src.engine.training.Model.save:12 of +msgid "" +"Either `\"keras\"`, `\"tf\"`, `\"h5\"`, indicating whether to save the " +"model in the native Keras format (`.keras`), in the TensorFlow SavedModel" +" format (referred to as \"SavedModel\" below), or in the legacy HDF5 " +"format (`.h5`). Defaults to `\"tf\"` in TF 2.X, and `\"h5\"` in TF 1.X." msgstr "" -#: of tensorcircuit.cons.plain_contractor:1 -msgid "The naive state-vector simulator contraction path." +#: keras.src.engine.training.Model.save:36 of +msgid "SavedModel format arguments:" msgstr "" -#: of tensorcircuit.cons.plain_contractor:3 -msgid "The list of ``tn.Node``." +#: keras.src.engine.training.Model.save:22 of +msgid "include_optimizer: Only applied to SavedModel and legacy HDF5" msgstr "" -#: of tensorcircuit.cons.plain_contractor:5 -msgid "The list of dangling node edges, defaults to be None." +#: keras.src.engine.training.Model.save:22 of +msgid "formats. If False, do not save the optimizer state. Defaults to `True`." msgstr "" -#: of tensorcircuit.cons.plain_contractor:7 -msgid "The ``tn.Node`` after contraction" +#: keras.src.engine.training.Model.save:25 of +msgid "signatures: Only applies to SavedModel format. Signatures to save" msgstr "" -#: of tensorcircuit.cons.runtime_backend:1 -msgid "Context manager to set with-level runtime backend" +#: keras.src.engine.training.Model.save:25 of +msgid "" +"with the SavedModel. See the `signatures` argument in " +"`tf.saved_model.save` for details." msgstr "" -#: of tensorcircuit.cons.runtime_backend:3 -#: tensorcircuit.cons.set_function_backend:3 -msgid "\"numpy\", \"tensorflow\", \"jax\", \"pytorch\", defaults to None" +#: keras.src.engine.training.Model.save:28 of +msgid "options: Only applies to SavedModel format." msgstr "" -#: of tensorcircuit.cons.runtime_backend tensorcircuit.cons.runtime_contractor -#: tensorcircuit.cons.runtime_dtype -msgid "yield" +#: keras.src.engine.training.Model.save:28 of +msgid "" +"`tf.saved_model.SaveOptions` object that specifies SavedModel saving " +"options." msgstr "" -#: of tensorcircuit.cons.runtime_backend:5 -msgid "the backend object" +#: keras.src.engine.training.Model.save:36 of +msgid "save_traces: Only applies to SavedModel format. When enabled, the" msgstr "" -#: of tensorcircuit.cons.runtime_contractor:1 -msgid "Context manager to change with-levek contractor" +#: keras.src.engine.training.Model.save:31 of +msgid "" +"SavedModel will store the function traces for each layer. This can be " +"disabled, so that only the configs of each layer are stored. Defaults to " +"`True`. Disabling this will decrease serialization time and reduce file " +"size, but it requires that all custom layers/models implement a " +"`get_config()` method." msgstr "" -#: of tensorcircuit.cons.runtime_dtype:1 -msgid "Context manager to set with-level runtime dtype" +#: keras.src.engine.training.Model.save:40 of +msgid "```python model = tf.keras.Sequential([" msgstr "" -#: of tensorcircuit.cons.runtime_dtype:3 -msgid "\"complex64\" or \"complex128\", defaults to None (\"complex64\")" +#: keras.src.engine.training.Model.save:42 of +msgid "tf.keras.layers.Dense(5, input_shape=(3,)), tf.keras.layers.Softmax()])" msgstr "" -#: of tensorcircuit.cons.runtime_dtype:5 -msgid "complex dtype str and real dtype str" +#: keras.src.engine.training.Model.save:44 of +msgid "" +"model.save(\"model.keras\") loaded_model = " +"tf.keras.models.load_model(\"model.keras\") x = tf.random.uniform((10, " +"3)) assert np.allclose(model.predict(x), loaded_model.predict(x)) ```" msgstr "" -#: of tensorcircuit.cons.set_tensornetwork_backend:1 -msgid "To set the runtime backend of tensorcircuit." +#: keras.src.engine.training.Model.save:50 of +msgid "Note that `model.save()` is an alias for `tf.keras.models.save_model()`." msgstr "" -#: of tensorcircuit.cons.set_tensornetwork_backend:3 -msgid "" -"Note: ``tc.set_backend`` and ``tc.cons.set_tensornetwork_backend`` are " -"the same." +#: keras.src.engine.base_layer.Layer.save_own_variables:1 of +msgid "Saves the state of the layer." msgstr "" -#: of tensorcircuit.cons.set_tensornetwork_backend:27 +#: keras.src.engine.base_layer.Layer.save_own_variables:3 of msgid "" -"\"numpy\", \"tensorflow\", \"jax\", \"pytorch\". defaults to None, which " -"gives the same behavior as " -"``tensornetwork.backend_contextmanager.get_default_backend()``." +"You can override this method to take full control of how the state of the" +" layer is saved upon calling `model.save()`." msgstr "" -#: of tensorcircuit.cons.set_tensornetwork_backend:30 -msgid "Whether the object should be set as global." +#: keras.src.engine.base_layer.Layer.save_own_variables:6 of +msgid "Dict where the state of the model will be saved." msgstr "" -#: of tensorcircuit.cons.set_function_backend:1 -msgid "Function decorator to set function-level runtime backend" +#: keras.src.engine.training.Model.save_spec:1 of +msgid "Returns the `tf.TensorSpec` of call args as a tuple `(args, kwargs)`." msgstr "" -#: of tensorcircuit.cons.set_function_backend:5 -msgid "Decorated function" +#: keras.src.engine.training.Model.save_spec:3 of +msgid "" +"This value is automatically defined after calling the model for the first" +" time. Afterwards, you can use it when exporting the model for serving:" msgstr "" -#: of tensorcircuit.cons.set_function_contractor:1 -msgid "Function decorate to change function-level contractor" +#: keras.src.engine.training.Model.save_spec:7 of +msgid "```python model = tf.keras.Model(...)" msgstr "" -#: of tensorcircuit.cons.set_function_dtype:1 -msgid "Function decorator to set function-level numerical dtype" +#: keras.src.engine.training.Model.save_spec:10 of +msgid "@tf.function def serve(*args, **kwargs):" msgstr "" -#: of tensorcircuit.cons.set_function_dtype:3 -msgid "\"complex64\" or \"complex128\", defaults to None" +#: keras.src.engine.training.Model.save_spec:12 of +msgid "" +"outputs = model(*args, **kwargs) # Apply postprocessing steps, or add " +"additional outputs. ... return outputs" msgstr "" -#: of tensorcircuit.cons.set_function_dtype:5 -msgid "The decorated function" +#: keras.src.engine.training.Model.save_spec:17 of +msgid "" +"# arg_specs is `[tf.TensorSpec(...), ...]`. kwarg_specs, in this # " +"example, is an empty dict since functional models do not use keyword # " +"arguments. arg_specs, kwarg_specs = model.save_spec()" msgstr "" -#: ../../source/api/densitymatrix.rst:2 -msgid "tensorcircuit.densitymatrix" +#: keras.src.engine.training.Model.save_spec:23 of +msgid "model.save(path, signatures={" msgstr "" -#: of tensorcircuit.densitymatrix:1 -msgid "Quantum circuit class but with density matrix simulator" +#: keras.src.engine.training.Model.save_spec:23 of +msgid "'serving_default': serve.get_concrete_function(*arg_specs," msgstr "" -#: of tensorcircuit.densitymatrix.DMCircuit.__init__:1 -msgid "The density matrix simulator based on tensornetwork engine." +#: keras.src.engine.training.Model.save_spec:24 of +msgid "**kwarg_specs)" msgstr "" -#: of tensorcircuit.densitymatrix.DMCircuit.__init__:3 -msgid "Number of qubits" +#: keras.src.engine.training.Model.save_spec:26 of +msgid "})" msgstr "" -#: of tensorcircuit.densitymatrix.DMCircuit.__init__:5 -msgid "if True, nothing initialized, only for internal use, defaults to False" +#: keras.src.engine.training.Model.save_spec of +msgid "param dynamic_batch" msgstr "" -#: of tensorcircuit.densitymatrix.DMCircuit.__init__:7 -msgid "the state input for the circuit, defaults to None" +#: keras.src.engine.training.Model.save_spec:28 of +msgid "" +"Whether to set the batch sizes of all the returned `tf.TensorSpec` to " +"`None`. (Note that when defining functional or Sequential models with " +"`tf.keras.Input([...], batch_size=X)`, the batch size will always be " +"preserved). Defaults to `True`." msgstr "" -#: of tensorcircuit.densitymatrix.DMCircuit.__init__:9 -msgid "the density matrix input for the circuit, defaults to None" +#: keras.src.engine.training.Model.save_spec of +msgid "returns" msgstr "" -#: of -#: tensorcircuit.densitymatrix.DMCircuit.apply_general_kraus_delayed..apply:1 -#: tensorcircuit.densitymatrix2.DMCircuit2.apply_general_kraus_delayed..apply:1 +#: keras.src.engine.training.Model.save_spec:33 of msgid "" -"Apply amplitudedamping quantum channel on the circuit. See " -":py:meth:`tensorcircuit.channels.amplitudedampingchannel`" +"If the model inputs are defined, returns a tuple `(args, kwargs)`. All " +"elements in `args` and `kwargs` are `tf.TensorSpec`. If the model inputs " +"are not defined, returns `None`. The model inputs are automatically set " +"when calling the model, `model.fit`, `model.evaluate` or `model.predict`." msgstr "" -#: of -#: tensorcircuit.densitymatrix.DMCircuit.apply_general_kraus_delayed..apply:6 -#: tensorcircuit.densitymatrix2.DMCircuit2.apply_general_kraus_delayed..apply:6 -msgid "Parameters for the channel." +#: keras.src.engine.training.Model.save_weights:1 of +msgid "Saves all layer weights." msgstr "" -#: of tensorcircuit.densitymatrix.DMCircuit.densitymatrix:1 -msgid "Return the output density matrix of the circuit." +#: keras.src.engine.training.Model.save_weights:3 of +msgid "" +"Either saves in HDF5 or in TensorFlow format based on the `save_format` " +"argument." msgstr "" -#: of tensorcircuit.densitymatrix.DMCircuit.densitymatrix:3 -msgid "" -"check whether the final return is a legal density matrix, defaults to " -"False" +#: keras.src.engine.training.Model.save_weights:14 of +msgid "When saving in HDF5 format, the weight file has:" msgstr "" -#: of tensorcircuit.densitymatrix.DMCircuit.densitymatrix:5 -msgid "whether to reuse previous results, defaults to True" +#: keras.src.engine.training.Model.save_weights:7 of +msgid "`layer_names` (attribute), a list of strings" msgstr "" -#: of tensorcircuit.densitymatrix.DMCircuit.densitymatrix:7 -msgid "The output densitymatrix in 2D shape tensor form" +#: keras.src.engine.training.Model.save_weights:8 of +msgid "(ordered names of model layers)." msgstr "" -#: of -#: tensorcircuit.densitymatrix.DMCircuit.apply_general_kraus_delayed..apply:1 -#: tensorcircuit.densitymatrix2.DMCircuit2.apply_general_kraus_delayed..apply:1 -msgid "" -"Apply depolarizing quantum channel on the circuit. See " -":py:meth:`tensorcircuit.channels.depolarizingchannel`" -msgstr "" - -#: of tensorcircuit.densitymatrix.DMCircuit.measure_jit:1 -msgid "Take measurement to the given quantum lines." +#: keras.src.engine.training.Model.save_weights:14 of +msgid "For every layer, a `group` named `layer.name`" msgstr "" -#: of tensorcircuit.densitymatrix.DMCircuit.perfect_sampling:1 -msgid "Sampling bistrings from the circuit output based on quantum amplitudes." +#: keras.src.engine.training.Model.save_weights:11 of +msgid "For every such layer group, a group attribute `weight_names`," msgstr "" -#: of -#: tensorcircuit.densitymatrix.DMCircuit.apply_general_kraus_delayed..apply:1 -#: tensorcircuit.densitymatrix2.DMCircuit2.apply_general_kraus_delayed..apply:1 -msgid "" -"Apply phasedamping quantum channel on the circuit. See " -":py:meth:`tensorcircuit.channels.phasedampingchannel`" +#: keras.src.engine.training.Model.save_weights:11 of +msgid "a list of strings (ordered names of weights tensor of the layer)." msgstr "" -#: of -#: tensorcircuit.densitymatrix.DMCircuit.apply_general_kraus_delayed..apply:1 -#: tensorcircuit.densitymatrix2.DMCircuit2.apply_general_kraus_delayed..apply:1 -msgid "" -"Apply reset quantum channel on the circuit. See " -":py:meth:`tensorcircuit.channels.resetchannel`" +#: keras.src.engine.training.Model.save_weights:14 of +msgid "For every weight in the layer, a dataset" msgstr "" -#: ../../source/api/densitymatrix2.rst:2 -msgid "tensorcircuit.densitymatrix2" +#: keras.src.engine.training.Model.save_weights:14 of +msgid "storing the weight value, named after the weight tensor." msgstr "" -#: of tensorcircuit.densitymatrix2:1 -msgid "Quantum circuit class but with density matrix simulator: v2" +#: keras.src.engine.training.Model.save_weights:16 of +msgid "" +"When saving in TensorFlow format, all objects referenced by the network " +"are saved in the same format as `tf.train.Checkpoint`, including any " +"`Layer` instances or `Optimizer` instances assigned to object attributes." +" For networks constructed from inputs and outputs using " +"`tf.keras.Model(inputs, outputs)`, `Layer` instances used by the network " +"are tracked/saved automatically. For user-defined classes which inherit " +"from `tf.keras.Model`, `Layer` instances must be assigned to object " +"attributes, typically in the constructor. See the documentation of " +"`tf.train.Checkpoint` and `tf.keras.Model` for details." msgstr "" -#: of tensorcircuit.densitymatrix2.DMCircuit2:1 -msgid "Bases: :py:class:`tensorcircuit.densitymatrix.DMCircuit`" +#: keras.src.engine.training.Model.save_weights:26 of +msgid "" +"While the formats are the same, do not mix `save_weights` and " +"`tf.train.Checkpoint`. Checkpoints saved by `Model.save_weights` should " +"be loaded using `Model.load_weights`. Checkpoints saved using " +"`tf.train.Checkpoint.save` should be restored using the corresponding " +"`tf.train.Checkpoint.restore`. Prefer `tf.train.Checkpoint` over " +"`save_weights` for training checkpoints." msgstr "" -#: ../../source/api/experimental.rst:2 -msgid "tensorcircuit.experimental" +#: keras.src.engine.training.Model.save_weights:33 of +msgid "" +"The TensorFlow format matches objects and variables by starting at a root" +" object, `self` for `save_weights`, and greedily matching attribute " +"names. For `Model.save` this is the `Model`, and for `Checkpoint.save` " +"this is the `Checkpoint` even if the `Checkpoint` has a model attached. " +"This means saving a `tf.keras.Model` using `save_weights` and loading " +"into a `tf.train.Checkpoint` with a `Model` attached (or vice versa) will" +" not match the `Model`'s variables. See the [guide to training " +"checkpoints]( https://www.tensorflow.org/guide/checkpoint) for details on" +" the TensorFlow format." msgstr "" -#: of tensorcircuit.experimental:1 -msgid "Experimental features" +#: keras.src.engine.training.Model.save_weights:44 of +msgid "" +"String or PathLike, path to the file to save the weights to. When saving " +"in TensorFlow format, this is the prefix used for checkpoint files " +"(multiple files are generated). Note that the '.h5' suffix causes weights" +" to be saved in HDF5 format." msgstr "" -#: ../../source/api/gates.rst:2 -msgid "tensorcircuit.gates" +#: keras.src.engine.training.Model.save_weights:48 of +msgid "" +"Whether to silently overwrite any existing file at the target location, " +"or provide the user with a manual prompt." msgstr "" -#: of tensorcircuit.gates:1 +#: keras.src.engine.training.Model.save_weights:50 of msgid "" -"Declarations of single-qubit and two-qubit gates and their corresponding " -"matrix." +"Either 'tf' or 'h5'. A `filepath` ending in '.h5' or '.keras' will " +"default to HDF5 if `save_format` is `None`. Otherwise, `None` becomes " +"'tf'. Defaults to `None`." msgstr "" -#: of tensorcircuit.gates.Gate:1 -msgid "Bases: :py:class:`tensornetwork.network_components.Node`" +#: keras.src.engine.training.Model.save_weights:53 of +msgid "" +"Optional `tf.train.CheckpointOptions` object that specifies options for " +"saving weights." msgstr "" -#: of tensorcircuit.gates.Gate:1 -msgid "Wrapper of tn.Node, quantum gate" +#: keras.src.engine.training.Model.save_weights:56 of +msgid "If `h5py` is not available when attempting to save in HDF5 format." msgstr "" -#: of tensorcircuit.gates.GateVF:1 -msgid "Bases: :py:class:`tensorcircuit.gates.GateF`" +#: keras.src.engine.base_layer.Layer.set_weights:1 of +msgid "Sets the weights of the layer, from NumPy arrays." msgstr "" -#: of tensorcircuit.gates.any_gate:1 -msgid "Note one should provide the gate with properly reshaped." +#: keras.src.engine.base_layer.Layer.set_weights:3 of +msgid "" +"The weights of a layer represent the state of the layer. This function " +"sets the weight values from numpy arrays. The weight values should be " +"passed in the order they are created by the layer. Note that the layer's " +"weights must be instantiated before calling this function, by calling the" +" layer." msgstr "" -#: of tensorcircuit.gates.any_gate:3 -msgid "corresponding gate" +#: keras.src.engine.base_layer.Layer.get_weights:8 +#: keras.src.engine.base_layer.Layer.set_weights:9 of +msgid "" +"For example, a `Dense` layer returns a list of two values: the kernel " +"matrix and the bias vector. These can be used to set the weights of " +"another `Dense` layer:" msgstr "" -#: of tensorcircuit.gates.any_gate:5 -msgid "The name of the gate." +#: keras.src.engine.base_layer.Layer.set_weights:33 of +msgid "" +"a list of NumPy arrays. The number of arrays and their shape must match " +"number of the dimensions of the weights of the layer (i.e. it should " +"match the output of `get_weights`)." msgstr "" -#: of tensorcircuit.gates.any_gate:7 -msgid "the resulted gate" +#: keras.src.engine.base_layer.Layer.set_weights:39 of +msgid "" +"If the provided weights list does not match the layer's " +"specifications." msgstr "" -#: of tensorcircuit.gates.num_to_tensor:1 -msgid "Convert the inputs to Tensor with specified dtype." +#: of tensorcircuit.applications.van.MADE.state_updates:1 +#: tensorcircuit.applications.van.NMF.state_updates:1 +#: tensorcircuit.applications.van.PixelCNN.state_updates:1 +msgid "Deprecated, do NOT use!" msgstr "" -#: of tensorcircuit.gates.num_to_tensor:35 -msgid "inputs" +#: of tensorcircuit.applications.van.MADE.state_updates:3 +#: tensorcircuit.applications.van.NMF.state_updates:3 +#: tensorcircuit.applications.van.PixelCNN.state_updates:3 +msgid "Returns the `updates` from all layers that are stateful." msgstr "" -#: of tensorcircuit.gates.num_to_tensor:37 -msgid "dtype of the output Tensors" +#: of tensorcircuit.applications.van.MADE.state_updates:5 +#: tensorcircuit.applications.van.NMF.state_updates:5 +#: tensorcircuit.applications.van.PixelCNN.state_updates:5 +msgid "" +"This is useful for separating training updates and state updates, e.g. " +"when we need to update a layer's internal state during prediction." msgstr "" -#: of tensorcircuit.gates.num_to_tensor:39 -msgid "List of Tensors" +#: of tensorcircuit.applications.van.MADE.state_updates:9 +#: tensorcircuit.applications.van.NMF.state_updates:9 +#: tensorcircuit.applications.van.PixelCNN.state_updates:9 +msgid "A list of update ops." msgstr "" -#: of tensorcircuit.gates.bmatrix:1 -msgid "Returns a :math:`\\LaTeX` bmatrix." +#: of tensorcircuit.applications.van.MADE.submodules:1 +#: tensorcircuit.applications.van.MaskedConv2D.submodules:1 +#: tensorcircuit.applications.van.MaskedLinear.submodules:1 +#: tensorcircuit.applications.van.NMF.submodules:1 +#: tensorcircuit.applications.van.PixelCNN.submodules:1 +#: tensorcircuit.applications.van.ResidualBlock.submodules:1 +#: tensorcircuit.applications.vqes.Linear.submodules:1 +#: tensorcircuit.keras.HardwareLayer.submodules:1 +#: tensorcircuit.keras.QuantumLayer.submodules:1 +msgid "Sequence of all sub-modules." msgstr "" -#: of tensorcircuit.gates.bmatrix:13 -msgid "Formatted Display:" +#: of tensorcircuit.applications.van.MADE.submodules:3 +#: tensorcircuit.applications.van.MaskedConv2D.submodules:3 +#: tensorcircuit.applications.van.MaskedLinear.submodules:3 +#: tensorcircuit.applications.van.NMF.submodules:3 +#: tensorcircuit.applications.van.PixelCNN.submodules:3 +#: tensorcircuit.applications.van.ResidualBlock.submodules:3 +#: tensorcircuit.applications.vqes.Linear.submodules:3 +#: tensorcircuit.keras.HardwareLayer.submodules:3 +#: tensorcircuit.keras.QuantumLayer.submodules:3 +msgid "" +"Submodules are modules which are properties of this module, or found as " +"properties of modules which are properties of this module (and so on)." msgstr "" -#: of tensorcircuit.gates.bmatrix:15 -msgid "" -"\\begin{bmatrix} 1.+0.j & 0.+0.j\\\\ 0.+0.j & 1.+0.j \\end{bmatrix}" -"\n" -"\n" +#: of tensorcircuit.applications.van.MADE.submodules:18 +#: tensorcircuit.applications.van.MaskedConv2D.submodules:18 +#: tensorcircuit.applications.van.MaskedLinear.submodules:18 +#: tensorcircuit.applications.van.NMF.submodules:18 +#: tensorcircuit.applications.van.PixelCNN.submodules:18 +#: tensorcircuit.applications.van.ResidualBlock.submodules:18 +#: tensorcircuit.applications.vqes.Linear.submodules:18 +#: tensorcircuit.keras.HardwareLayer.submodules:18 +#: tensorcircuit.keras.QuantumLayer.submodules:18 +msgid "A sequence of all submodules." msgstr "" -#: of tensorcircuit.gates.bmatrix:18 -msgid "2D numpy array" +#: keras.src.engine.training.Model.summary:1 of +msgid "Prints a string summary of the network." msgstr "" -#: of tensorcircuit.gates.bmatrix:20 -msgid "ValueError(\"bmatrix can at most display two dimensions\")" +#: keras.src.engine.training.Model.summary:3 of +msgid "" +"Total length of printed lines (e.g. set this to adapt the display to " +"different terminal window sizes)." msgstr "" -#: of tensorcircuit.gates.bmatrix:21 -msgid ":math:`\\LaTeX`-formatted string for bmatrix of the array a" +#: keras.src.engine.training.Model.summary:6 of +msgid "" +"Relative or absolute positions of log elements in each line. If not " +"provided, becomes `[0.3, 0.6, 0.70, 1.]`. Defaults to `None`." msgstr "" -#: of tensorcircuit.gates.cr_gate:1 +#: keras.src.engine.training.Model.summary:9 of msgid "" -"Controlled rotation gate. When the control qubit is 1, `rgate` is applied" -" to the target qubit." +"Print function to use. By default, prints to `stdout`. If `stdout` " +"doesn't work in your environment, change to `print`. It will be called on" +" each line of the summary. You can set it to a custom function in order " +"to capture the string summary." msgstr "" -#: of tensorcircuit.gates.cr_gate:3 tensorcircuit.gates.cr_gate:5 -#: tensorcircuit.gates.cr_gate:7 tensorcircuit.gates.exponential_gate:8 -#: tensorcircuit.gates.exponential_gate_unity:9 -#: tensorcircuit.gates.iswap_gate:12 tensorcircuit.gates.r_gate:9 -#: tensorcircuit.gates.r_gate:11 tensorcircuit.gates.r_gate:13 -#: tensorcircuit.gates.rgate_theoretical:8 -#: tensorcircuit.gates.rgate_theoretical:10 -#: tensorcircuit.gates.rgate_theoretical:12 tensorcircuit.gates.rx_gate:6 -#: tensorcircuit.gates.rxx_gate:9 tensorcircuit.gates.ry_gate:6 -#: tensorcircuit.gates.ryy_gate:9 tensorcircuit.gates.rz_gate:6 -#: tensorcircuit.gates.rzz_gate:9 -msgid "angle in radians" +#: keras.src.engine.training.Model.summary:14 of +msgid "Whether to expand the nested models. Defaults to `False`." msgstr "" -#: of tensorcircuit.gates.cr_gate:10 -msgid "CR Gate" +#: keras.src.engine.training.Model.summary:16 of +msgid "Whether to show if a layer is trainable. Defaults to `False`." msgstr "" -#: of tensorcircuit.gates.exponential_gate_unity:1 -#: tensorcircuit.gates.rxx_gate:1 tensorcircuit.gates.ryy_gate:1 -#: tensorcircuit.gates.rzz_gate:1 +#: keras.src.engine.training.Model.summary:18 of msgid "" -"Faster exponential gate directly implemented based on RHS. Only works " -"when :math:`U^2 = I` is an identity matrix." +"a list or tuple of 2 strings, which is the starting layer name and ending" +" layer name (both inclusive) indicating the range of layers to be printed" +" in summary. It also accepts regex patterns instead of exact name. In " +"such case, start predicate will be the first element it matches to " +"`layer_range[0]` and the end predicate will be the last element it " +"matches to `layer_range[1]`. By default `None` which considers all layers" +" of model." msgstr "" -#: of tensorcircuit.gates.exponential_gate_unity:3 -#: tensorcircuit.gates.rxx_gate:3 tensorcircuit.gates.ryy_gate:3 -#: tensorcircuit.gates.rzz_gate:3 -msgid "" -"\\textrm{exp}(U) &= e^{-j \\theta U} \\\\\n" -" &= \\cos(\\theta) I - j \\sin(\\theta) U \\\\\n" -"\n" +#: keras.src.engine.training.Model.summary:27 of +msgid "if `summary()` is called before the model is built." msgstr "" -#: of tensorcircuit.gates.exponential_gate:6 -#: tensorcircuit.gates.exponential_gate_unity:7 tensorcircuit.gates.rxx_gate:7 -#: tensorcircuit.gates.ryy_gate:7 tensorcircuit.gates.rzz_gate:7 -msgid "input unitary :math:`U`" +#: of tensorcircuit.applications.van.MADE.supports_masking:1 +#: tensorcircuit.applications.van.MaskedConv2D.supports_masking:1 +#: tensorcircuit.applications.van.MaskedLinear.supports_masking:1 +#: tensorcircuit.applications.van.NMF.supports_masking:1 +#: tensorcircuit.applications.van.PixelCNN.supports_masking:1 +#: tensorcircuit.applications.van.ResidualBlock.supports_masking:1 +#: tensorcircuit.applications.vqes.Linear.supports_masking:1 +#: tensorcircuit.keras.HardwareLayer.supports_masking:1 +#: tensorcircuit.keras.QuantumLayer.supports_masking:1 +msgid "Whether this layer supports computing a mask using `compute_mask`." msgstr "" -#: of tensorcircuit.gates.exponential_gate:10 -#: tensorcircuit.gates.exponential_gate_unity:11 -#: tensorcircuit.gates.rxx_gate:11 tensorcircuit.gates.ryy_gate:11 -#: tensorcircuit.gates.rzz_gate:11 -msgid "suffix of Gate name" +#: keras.src.engine.training.Model.test_on_batch:1 of +msgid "Test the model on a single batch of samples." msgstr "" -#: of tensorcircuit.gates.exponential_gate:11 -#: tensorcircuit.gates.exponential_gate_unity:13 -#: tensorcircuit.gates.rxx_gate:13 tensorcircuit.gates.ryy_gate:13 -#: tensorcircuit.gates.rzz_gate:13 -msgid "Exponential Gate" +#: keras.src.engine.training.Model.test_on_batch:3 of +msgid "" +"Input data. It could be: - A Numpy array (or array-like), or a list of " +"arrays (in case the model has multiple inputs). - A TensorFlow " +"tensor, or a list of tensors (in case the model has multiple inputs)." +" - A dict mapping input names to the corresponding array/tensors, if " +"the model has named inputs." msgstr "" -#: of tensorcircuit.gates.exponential_gate:1 -msgid "Exponential gate." +#: keras.src.engine.training.Model.test_on_batch:8 +#: keras.src.engine.training.Model.train_on_batch:8 of +msgid "A dict mapping input names to the corresponding array/tensors," msgstr "" -#: of tensorcircuit.gates.exponential_gate:3 -msgid "" -"\\textrm{exp}(U) = e^{-j \\theta U}\n" -"\n" +#: keras.src.engine.training.Model.test_on_batch:9 +#: keras.src.engine.training.Model.train_on_batch:9 of +msgid "if the model has named inputs." msgstr "" -#: of tensorcircuit.gates.iswap_gate:1 -msgid "iSwap gate." +#: keras.src.engine.training.Model.test_on_batch:10 of +msgid "" +"Target data. Like the input data `x`, it could be either Numpy array(s) " +"or TensorFlow tensor(s). It should be consistent with `x` (you cannot " +"have Numpy inputs and tensor targets, or inversely)." msgstr "" -#: of tensorcircuit.gates.iswap_gate:3 +#: keras.src.engine.training.Model.test_on_batch:13 +#: keras.src.engine.training.Model.train_on_batch:12 of msgid "" -"\\textrm{iSwap}(\\theta) =\n" -"\\begin{pmatrix}\n" -" 1 & 0 & 0 & 0\\\\\n" -" 0 & \\cos(\\frac{\\pi}{2} \\theta ) & j \\sin(\\frac{\\pi}{2} \\theta" -" ) & 0\\\\\n" -" 0 & j \\sin(\\frac{\\pi}{2} \\theta ) & \\cos(\\frac{\\pi}{2} \\theta" -" ) & 0\\\\\n" -" 0 & 0 & 0 & 1\\\\\n" -"\\end{pmatrix}\n" -"\n" +"Optional array of the same length as x, containing weights to apply to " +"the model's loss for each sample. In the case of temporal data, you can " +"pass a 2D array with shape (samples, sequence_length), to apply a " +"different weight to every timestep of every sample." msgstr "" -#: of tensorcircuit.gates.iswap_gate:14 -msgid "iSwap Gate" +#: keras.src.engine.training.Model.test_on_batch:18 +#: keras.src.engine.training.Model.train_on_batch:24 of +msgid "" +"If `True`, the metrics returned will be only for this batch. If `False`, " +"the metrics will be statefully accumulated across batches." msgstr "" -#: of tensorcircuit.gates.matrix_for_gate:1 -msgid "Convert Gate to numpy array." +#: keras.src.engine.training.Model.test_on_batch:30 of +msgid "If `model.test_on_batch` is wrapped in a `tf.function`." msgstr "" -#: of tensorcircuit.gates.matrix_for_gate:10 -msgid "input Gate" +#: keras.src.engine.training.Model.test_step:1 of +msgid "The logic for one evaluation step." msgstr "" -#: of tensorcircuit.gates.matrix_for_gate:12 -msgid "Corresponding Tensor" +#: keras.src.engine.training.Model.test_step:3 of +msgid "" +"This method can be overridden to support custom evaluation logic. This " +"method is called by `Model.make_test_function`." msgstr "" -#: of tensorcircuit.gates.meta_gate:1 +#: keras.src.engine.training.Model.test_step:6 of msgid "" -"Inner helper function to generate gate functions, such as ``z()`` from " -"``_z_matrix``" +"This function should contain the mathematical logic for one step of " +"evaluation. This typically includes the forward pass, loss calculation, " +"and metrics updates." msgstr "" -#: of tensorcircuit.gates.r_gate:1 -msgid "General single qubit rotation gate" +#: keras.src.engine.training.Model.test_step:11 of +msgid "" +"Configuration details for *how* this logic is run (e.g. `tf.function` and" +" `tf.distribute.Strategy` settings), should be left to " +"`Model.make_test_function`, which can also be overridden." msgstr "" -#: of tensorcircuit.gates.r_gate:3 +#: keras.src.engine.training.Model.test_step:17 of msgid "" -"R(\\theta, \\alpha, \\phi) = j \\cos(\\theta) I\n" -"- j \\cos(\\phi) \\sin(\\alpha) \\sin(\\theta) X\n" -"- j \\sin(\\phi) \\sin(\\alpha) \\sin(\\theta) Y\n" -"- j \\sin(\\theta) \\cos(\\alpha) Z\n" -"\n" +"A `dict` containing values that will be passed to " +"`tf.keras.callbacks.CallbackList.on_train_batch_end`. Typically, the " +"values of the `Model`'s metrics are returned." msgstr "" -#: of tensorcircuit.gates.r_gate:16 -msgid "R Gate" +#: keras.src.engine.training.Model.to_json:1 of +msgid "Returns a JSON string containing the network configuration." msgstr "" -#: of tensorcircuit.gates.random_single_qubit_gate:1 -msgid "Random single qubit gate described in https://arxiv.org/abs/2002.07730." +#: keras.src.engine.training.Model.to_json:3 of +msgid "" +"To load a network from a JSON save file, use " +"`keras.models.model_from_json(json_string, custom_objects={})`." msgstr "" -#: of tensorcircuit.gates.random_single_qubit_gate:3 -msgid "A random single-qubit gate" +#: keras.src.engine.training.Model.to_json:6 of +msgid "Additional keyword arguments to be passed to *`json.dumps()`." msgstr "" -#: of tensorcircuit.gates.random_two_qubit_gate:1 -msgid "Returns a random two-qubit gate." +#: keras.src.engine.training.Model.to_json:9 of +msgid "A JSON string." msgstr "" -#: of tensorcircuit.gates.random_two_qubit_gate:3 -msgid "A random two-qubit gate" +#: keras.src.engine.training.Model.to_yaml:1 of +msgid "Returns a yaml string containing the network configuration." msgstr "" -#: of tensorcircuit.gates.rgate_theoretical:1 +#: keras.src.engine.training.Model.to_yaml:3 of msgid "" -"Rotation gate implemented by matrix exponential. The output is the same " -"as `rgate`." +"Note: Since TF 2.6, this method is no longer supported and will raise a " +"RuntimeError." msgstr "" -#: of tensorcircuit.gates.rgate_theoretical:3 +#: keras.src.engine.training.Model.to_yaml:6 of msgid "" -"R(\\theta, \\alpha, \\phi) = e^{-j \\theta \\left[\\sin(\\alpha) " -"\\cos(\\phi) X\n" -" + \\sin(\\alpha) \\sin(\\phi) " -"Y\n" -" + \\cos(\\alpha) Z\\right]}\n" -"\n" +"To load a network from a yaml save file, use " +"`keras.models.model_from_yaml(yaml_string, custom_objects={})`." msgstr "" -#: of tensorcircuit.gates.rgate_theoretical:14 -msgid "Rotation Gate" +#: keras.src.engine.training.Model.to_yaml:9 of +msgid "" +"`custom_objects` should be a dictionary mapping the names of custom " +"losses / layers / etc to the corresponding functions / classes." msgstr "" -#: of tensorcircuit.gates.rx_gate:1 -msgid "Rotation gate along :math:`x` axis." +#: keras.src.engine.training.Model.to_yaml:13 of +msgid "Additional keyword arguments to be passed to `yaml.dump()`." msgstr "" -#: of tensorcircuit.gates.rx_gate:3 -msgid "" -"RX(\\theta) = e^{-j\\frac{\\theta}{2}X}\n" -"\n" +#: keras.src.engine.training.Model.to_yaml:16 of +msgid "A YAML string." msgstr "" -#: of tensorcircuit.gates.rx_gate:8 -msgid "RX Gate" +#: keras.src.engine.training.Model.to_yaml:18 of +msgid "announces that the method poses a security risk" msgstr "" -#: of tensorcircuit.gates.ry_gate:1 -msgid "Rotation gate along :math:`y` axis." +#: keras.src.engine.training.Model.train_on_batch:1 of +msgid "Runs a single gradient update on a single batch of data." msgstr "" -#: of tensorcircuit.gates.ry_gate:3 +#: keras.src.engine.training.Model.train_on_batch:3 of msgid "" -"RY(\\theta) = e^{-j\\frac{\\theta}{2}Y}\n" -"\n" -msgstr "" - -#: of tensorcircuit.gates.ry_gate:8 -msgid "RY Gate" +"Input data. It could be: - A Numpy array (or array-like), or a list of " +"arrays (in case the model has multiple inputs). - A TensorFlow " +"tensor, or a list of tensors (in case the model has multiple inputs)." +" - A dict mapping input names to the corresponding array/tensors, if " +"the model has named inputs." msgstr "" -#: of tensorcircuit.gates.rz_gate:1 -msgid "Rotation gate along :math:`z` axis." +#: keras.src.engine.training.Model.train_on_batch:6 of +msgid "A TensorFlow tensor, or a list of tensors" msgstr "" -#: of tensorcircuit.gates.rz_gate:3 +#: keras.src.engine.training.Model.train_on_batch:10 of msgid "" -"RZ(\\theta) = e^{-j\\frac{\\theta}{2}Z}\n" -"\n" +"Target data. Like the input data `x`, it could be either Numpy array(s) " +"or TensorFlow tensor(s)." msgstr "" -#: of tensorcircuit.gates.rz_gate:8 -msgid "RZ Gate" +#: keras.src.engine.training.Model.train_on_batch:17 of +msgid "" +"Optional dictionary mapping class indices (integers) to a weight (float) " +"to apply to the model's loss for the samples from this class during " +"training. This can be useful to tell the model to \"pay more attention\" " +"to samples from an under-represented class. When `class_weight` is " +"specified and targets have a rank of 2 or greater, either `y` must be " +"one-hot encoded, or an explicit final dimension of `1` must be included " +"for sparse class labels." msgstr "" -#: ../../source/api/interfaces.rst:2 -msgid "tensorcircuit.interfaces" +#: keras.src.engine.training.Model.train_on_batch:31 of +msgid "" +"Scalar training loss (if the model has a single output and no metrics) or" +" list of scalars (if the model has multiple outputs and/or metrics). The " +"attribute `model.metrics_names` will give you the display labels for the " +"scalar outputs." msgstr "" -#: of tensorcircuit.interfaces:1 -msgid "Interfaces bridging different backends" +#: keras.src.engine.training.Model.train_on_batch:37 of +msgid "If `model.train_on_batch` is wrapped in a `tf.function`." msgstr "" -#: of tensorcircuit.interfaces.scipy_optimize_interface:1 -msgid "Convert ``fun`` into a scipy optimize interface compatible version" +#: keras.src.engine.training.Model.train_step:1 of +msgid "The logic for one training step." msgstr "" -#: of tensorcircuit.interfaces.scipy_optimize_interface:35 -msgid "The quantum function with scalar out that to be optimized" +#: keras.src.engine.training.Model.train_step:3 of +msgid "" +"This method can be overridden to support custom training logic. For " +"concrete examples of how to override this method see [Customizing what " +"happens in fit]( " +"https://www.tensorflow.org/guide/keras/customizing_what_happens_in_fit). " +"This method is called by `Model.make_train_function`." msgstr "" -#: of tensorcircuit.interfaces.scipy_optimize_interface:37 -msgid "the shape of parameters that ``fun`` accepts, defaults to None" +#: keras.src.engine.training.Model.train_step:9 of +msgid "" +"This method should contain the mathematical logic for one step of " +"training. This typically includes the forward pass, loss calculation, " +"backpropagation, and metric updates." msgstr "" -#: of tensorcircuit.interfaces.scipy_optimize_interface:39 -msgid "whether to jit ``fun``, defaults to True" +#: keras.src.engine.training.Model.train_step:13 of +msgid "" +"Configuration details for *how* this logic is run (e.g. `tf.function` and" +" `tf.distribute.Strategy` settings), should be left to " +"`Model.make_train_function`, which can also be overridden." msgstr "" -#: of tensorcircuit.interfaces.scipy_optimize_interface:41 +#: keras.src.engine.training.Model.train_step:19 of msgid "" -"whether using gradient-based or gradient free scipy optimize interface, " -"defaults to True" +"A `dict` containing values that will be passed to " +"`tf.keras.callbacks.CallbackList.on_train_batch_end`. Typically, the " +"values of the `Model`'s metrics are returned. Example: `{'loss': 0.2, " +"'accuracy': 0.7}`." msgstr "" -#: of tensorcircuit.interfaces.scipy_optimize_interface:44 -msgid "The scipy interface compatible version of ``fun``" +#: of tensorcircuit.applications.van.MADE.trainable_variables:1 +#: tensorcircuit.applications.van.MaskedConv2D.trainable_variables:1 +#: tensorcircuit.applications.van.MaskedLinear.trainable_variables:1 +#: tensorcircuit.applications.van.NMF.trainable_variables:1 +#: tensorcircuit.applications.van.PixelCNN.trainable_variables:1 +#: tensorcircuit.applications.van.ResidualBlock.trainable_variables:1 +#: tensorcircuit.applications.vqes.Linear.trainable_variables:1 +#: tensorcircuit.keras.HardwareLayer.trainable_variables:1 +#: tensorcircuit.keras.QuantumLayer.trainable_variables:1 +msgid "Sequence of trainable variables owned by this module and its submodules." msgstr "" -#: of tensorcircuit.interfaces.torch_interface:1 -msgid "Wrap a quantum function on different ML backend with a pytorch interface." +#: of tensorcircuit.applications.van.MADE.trainable_weights:1 +#: tensorcircuit.applications.van.MaskedConv2D.trainable_weights:1 +#: tensorcircuit.applications.van.MaskedLinear.trainable_weights:1 +#: tensorcircuit.applications.van.NMF.trainable_weights:1 +#: tensorcircuit.applications.van.PixelCNN.trainable_weights:1 +#: tensorcircuit.applications.van.ResidualBlock.trainable_weights:1 +#: tensorcircuit.applications.vqes.Linear.trainable_weights:1 +#: tensorcircuit.keras.HardwareLayer.trainable_weights:1 +#: tensorcircuit.keras.QuantumLayer.trainable_weights:1 +msgid "List of all trainable weights tracked by this layer." +msgstr "" + +#: of tensorcircuit.applications.van.MADE.trainable_weights:3 +#: tensorcircuit.applications.van.MaskedConv2D.trainable_weights:3 +#: tensorcircuit.applications.van.MaskedLinear.trainable_weights:3 +#: tensorcircuit.applications.van.NMF.trainable_weights:3 +#: tensorcircuit.applications.van.PixelCNN.trainable_weights:3 +#: tensorcircuit.applications.van.ResidualBlock.trainable_weights:3 +#: tensorcircuit.applications.vqes.Linear.trainable_weights:3 +#: tensorcircuit.keras.HardwareLayer.trainable_weights:3 +#: tensorcircuit.keras.QuantumLayer.trainable_weights:3 +msgid "Trainable weights are updated via gradient descent during training." +msgstr "" + +#: of tensorcircuit.applications.van.MADE.trainable_weights:5 +#: tensorcircuit.applications.van.MaskedConv2D.trainable_weights:5 +#: tensorcircuit.applications.van.MaskedLinear.trainable_weights:5 +#: tensorcircuit.applications.van.NMF.trainable_weights:5 +#: tensorcircuit.applications.van.PixelCNN.trainable_weights:5 +#: tensorcircuit.applications.van.ResidualBlock.trainable_weights:5 +#: tensorcircuit.applications.vqes.Linear.trainable_weights:5 +#: tensorcircuit.keras.HardwareLayer.trainable_weights:5 +#: tensorcircuit.keras.QuantumLayer.trainable_weights:5 +msgid "A list of trainable variables." +msgstr "" + +#: of tensorcircuit.applications.van.MADE.variable_dtype:1 +#: tensorcircuit.applications.van.MaskedConv2D.variable_dtype:1 +#: tensorcircuit.applications.van.MaskedLinear.variable_dtype:1 +#: tensorcircuit.applications.van.NMF.variable_dtype:1 +#: tensorcircuit.applications.van.PixelCNN.variable_dtype:1 +#: tensorcircuit.applications.van.ResidualBlock.variable_dtype:1 +#: tensorcircuit.applications.vqes.Linear.variable_dtype:1 +#: tensorcircuit.keras.HardwareLayer.variable_dtype:1 +#: tensorcircuit.keras.QuantumLayer.variable_dtype:1 +msgid "Alias of `Layer.dtype`, the dtype of the weights." +msgstr "" + +#: of tensorcircuit.applications.van.MADE.variables:1 +#: tensorcircuit.applications.van.MADE.weights:1 +#: tensorcircuit.applications.van.MaskedConv2D.variables:1 +#: tensorcircuit.applications.van.MaskedConv2D.weights:1 +#: tensorcircuit.applications.van.MaskedLinear.variables:1 +#: tensorcircuit.applications.van.MaskedLinear.weights:1 +#: tensorcircuit.applications.van.NMF.variables:1 +#: tensorcircuit.applications.van.NMF.weights:1 +#: tensorcircuit.applications.van.PixelCNN.variables:1 +#: tensorcircuit.applications.van.PixelCNN.weights:1 +#: tensorcircuit.applications.van.ResidualBlock.variables:1 +#: tensorcircuit.applications.van.ResidualBlock.weights:1 +#: tensorcircuit.applications.vqes.Linear.variables:1 +#: tensorcircuit.applications.vqes.Linear.weights:1 +#: tensorcircuit.keras.HardwareLayer.variables:1 +#: tensorcircuit.keras.HardwareLayer.weights:1 +#: tensorcircuit.keras.QuantumLayer.variables:1 +#: tensorcircuit.keras.QuantumLayer.weights:1 +msgid "Returns the list of all layer variables/weights." +msgstr "" + +#: of tensorcircuit.applications.van.MADE.variables:3 +#: tensorcircuit.applications.van.MaskedConv2D.variables:3 +#: tensorcircuit.applications.van.MaskedLinear.variables:3 +#: tensorcircuit.applications.van.NMF.variables:3 +#: tensorcircuit.applications.van.PixelCNN.variables:3 +#: tensorcircuit.applications.van.ResidualBlock.variables:3 +#: tensorcircuit.applications.vqes.Linear.variables:3 +#: tensorcircuit.keras.HardwareLayer.variables:3 +#: tensorcircuit.keras.QuantumLayer.variables:3 +msgid "Alias of `self.weights`." +msgstr "" + +#: of tensorcircuit.applications.van.MADE.variables:5 +#: tensorcircuit.applications.van.MADE.weights:3 +#: tensorcircuit.applications.van.MaskedConv2D.variables:5 +#: tensorcircuit.applications.van.MaskedLinear.variables:5 +#: tensorcircuit.applications.van.NMF.variables:5 +#: tensorcircuit.applications.van.NMF.weights:3 +#: tensorcircuit.applications.van.PixelCNN.variables:5 +#: tensorcircuit.applications.van.PixelCNN.weights:3 +#: tensorcircuit.applications.van.ResidualBlock.variables:5 +#: tensorcircuit.applications.vqes.Linear.variables:5 +#: tensorcircuit.keras.HardwareLayer.variables:5 +#: tensorcircuit.keras.QuantumLayer.variables:5 +msgid "" +"Note: This will not track the weights of nested `tf.Modules` that are not" +" themselves Keras layers." +msgstr "" + +#: of tensorcircuit.applications.van.MADE.variables:8 +#: tensorcircuit.applications.van.MADE.weights:6 +#: tensorcircuit.applications.van.MaskedConv2D.variables:8 +#: tensorcircuit.applications.van.MaskedConv2D.weights:3 +#: tensorcircuit.applications.van.MaskedLinear.variables:8 +#: tensorcircuit.applications.van.MaskedLinear.weights:3 +#: tensorcircuit.applications.van.NMF.variables:8 +#: tensorcircuit.applications.van.NMF.weights:6 +#: tensorcircuit.applications.van.PixelCNN.variables:8 +#: tensorcircuit.applications.van.PixelCNN.weights:6 +#: tensorcircuit.applications.van.ResidualBlock.variables:8 +#: tensorcircuit.applications.van.ResidualBlock.weights:3 +#: tensorcircuit.applications.vqes.Linear.variables:8 +#: tensorcircuit.applications.vqes.Linear.weights:3 +#: tensorcircuit.keras.HardwareLayer.variables:8 +#: tensorcircuit.keras.HardwareLayer.weights:3 +#: tensorcircuit.keras.QuantumLayer.variables:8 +#: tensorcircuit.keras.QuantumLayer.weights:3 +msgid "A list of variables." +msgstr "" + +#: of tensorflow.python.module.module.Module.with_name_scope:1 +msgid "Decorator to automatically enter the module name scope." +msgstr "" + +#: of tensorflow.python.module.module.Module.with_name_scope:10 +msgid "" +"Using the above module would produce `tf.Variable`s and `tf.Tensor`s " +"whose names included the module name:" +msgstr "" + +#: of tensorflow.python.module.module.Module.with_name_scope:20 +msgid "The method to wrap." +msgstr "" + +#: of tensorflow.python.module.module.Module.with_name_scope:22 +msgid "The original method wrapped such that it enters the module's name scope." msgstr "" -#: of tensorcircuit.interfaces.torch_interface:28 -msgid "The quantum function with tensor in and tensor out" +#: of tensorcircuit.applications.van.MaskedConv2D:1 +#: tensorcircuit.applications.van.MaskedLinear:1 +#: tensorcircuit.applications.van.ResidualBlock:1 +#: tensorcircuit.applications.vqes.Linear:1 tensorcircuit.keras.QuantumLayer:1 +msgid "Bases: :py:class:`~keras.src.engine.base_layer.Layer`" msgstr "" -#: of tensorcircuit.interfaces.torch_interface:30 -msgid "whether to jit ``fun``, defaults to False" +#: keras.src.engine.base_layer.Layer.build:1 of +#: tensorcircuit.applications.van.MaskedConv2D.build:1 +#: tensorcircuit.keras.QuantumLayer.build:1 +msgid "Creates the variables of the layer (for subclass implementers)." msgstr "" -#: of tensorcircuit.interfaces.torch_interface:32 +#: keras.src.engine.base_layer.Layer.build:3 of +#: tensorcircuit.applications.van.MaskedConv2D.build:3 +#: tensorcircuit.keras.QuantumLayer.build:3 msgid "" -"The same quantum function but now with torch tensor in and torch tensor " -"out while AD is also supported" +"This is a method that implementers of subclasses of `Layer` or `Model` " +"can override if they need a state-creation step in-between layer " +"instantiation and layer call. It is invoked automatically before the " +"first execution of `call()`." msgstr "" -#: ../../source/api/keras.rst:2 -msgid "tensorcircuit.keras" +#: keras.src.engine.base_layer.Layer.build:8 of +#: tensorcircuit.applications.van.MaskedConv2D.build:8 +#: tensorcircuit.keras.QuantumLayer.build:8 +msgid "" +"This is typically used to create the weights of `Layer` subclasses (at " +"the discretion of the subclass implementer)." msgstr "" -#: of tensorcircuit.keras:1 -msgid "Keras layer for tc quantum function" +#: keras.src.engine.base_layer.Layer.build:11 of +#: tensorcircuit.applications.van.MaskedConv2D.build:11 +#: tensorcircuit.keras.QuantumLayer.build:11 +msgid "" +"Instance of `TensorShape`, or list of instances of `TensorShape` if the " +"layer expects a list of inputs (one instance per input)." msgstr "" -#: of tensorcircuit.keras.QuantumLayer.__init__:1 -msgid "" -"`QuantumLayer` wraps the quantum function `f` as a `keras.Layer` so that " -"tensorcircuit is better integrated with tensorflow." +#: of tensorcircuit.applications.van.MaskedConv2D.call:1 +#: tensorcircuit.applications.van.MaskedLinear.call:1 +#: tensorcircuit.applications.van.ResidualBlock.call:1 +#: tensorcircuit.applications.vqes.Linear.call:1 +msgid "This is where the layer's logic lives." msgstr "" -#: of tensorcircuit.keras.QuantumLayer.__init__:4 -msgid "Callabel function." +#: of tensorcircuit.applications.van.MaskedConv2D.call:3 +#: tensorcircuit.applications.van.MaskedLinear.call:3 +#: tensorcircuit.applications.van.ResidualBlock.call:3 +#: tensorcircuit.applications.vqes.Linear.call:3 +msgid "" +"The `call()` method may not create state (except in its first invocation," +" wrapping the creation of variables or other resources in " +"`tf.init_scope()`). It is recommended to create state, including " +"`tf.Variable` instances and nested `Layer` instances," msgstr "" -#: of tensorcircuit.keras.QuantumLayer.__init__:6 -msgid "The shape of the weights." +#: of tensorcircuit.applications.van.MaskedConv2D.call:7 +#: tensorcircuit.applications.van.MaskedLinear.call:7 +#: tensorcircuit.applications.van.ResidualBlock.call:7 +#: tensorcircuit.applications.vqes.Linear.call:7 +msgid "in `__init__()`, or in the `build()` method that is" msgstr "" -#: of tensorcircuit.keras.QuantumLayer.__init__:8 -msgid "The initializer of the weights, defaults to \"glorot_uniform\"" +#: of tensorcircuit.applications.van.MaskedConv2D.call:8 +#: tensorcircuit.applications.van.MaskedLinear.call:8 +#: tensorcircuit.applications.van.ResidualBlock.call:8 +#: tensorcircuit.applications.vqes.Linear.call:8 +msgid "called automatically before `call()` executes for the first time." msgstr "" -#: of tensorcircuit.keras.load_func:1 +#: of tensorcircuit.applications.van.MaskedConv2D.call:10 +#: tensorcircuit.applications.van.MaskedLinear.call:10 +#: tensorcircuit.applications.van.ResidualBlock.call:10 +#: tensorcircuit.applications.vqes.Linear.call:10 msgid "" -"Load function from the files in the ``tf.savedmodel`` format. We can load" -" several functions at the same time, as they can be the same function of " -"different input shapes." +"Input tensor, or dict/list/tuple of input tensors. The first positional " +"`inputs` argument is subject to special rules: - `inputs` must be " +"explicitly passed. A layer cannot have zero arguments, and `inputs` " +"cannot be provided via the default value of a keyword argument. - NumPy" +" array or Python scalar values in `inputs` get cast as tensors. - Keras" +" mask metadata is only collected from `inputs`. - Layers are built " +"(`build(input_shape)` method) using shape info from `inputs` only. - " +"`input_spec` compatibility is only checked against `inputs`. - Mixed " +"precision input casting is only applied to `inputs`. If a layer has " +"tensor arguments in `*args` or `**kwargs`, their casting behavior in " +"mixed precision should be handled manually. - The SavedModel input " +"specification is generated using `inputs` only. - Integration with " +"various ecosystem packages like TFMOT, TFLite, TF.js, etc is only " +"supported for `inputs` and not for tensors in positional and keyword " +"arguments." msgstr "" -#: of tensorcircuit.keras.load_func:24 -msgid "" -"The fallback function when all functions loaded are failed, defaults to " -"None" -msgstr "" - -#: of tensorcircuit.keras.load_func:26 +#: of tensorcircuit.applications.van.MaskedConv2D.call:10 +#: tensorcircuit.applications.van.MaskedLinear.call:10 +#: tensorcircuit.applications.van.ResidualBlock.call:10 +#: tensorcircuit.applications.vqes.Linear.call:10 msgid "" -"When there is not legal loaded function of the input shape and no " -"fallback callable." +"Input tensor, or dict/list/tuple of input tensors. The first positional " +"`inputs` argument is subject to special rules: - `inputs` must be " +"explicitly passed. A layer cannot have zero" msgstr "" -#: of tensorcircuit.keras.load_func:27 +#: of tensorcircuit.applications.van.MaskedConv2D.call:13 +#: tensorcircuit.applications.van.MaskedLinear.call:13 +#: tensorcircuit.applications.van.ResidualBlock.call:13 +#: tensorcircuit.applications.vqes.Linear.call:13 msgid "" -"A function that tries all loaded function against the input until the " -"first success one." -msgstr "" - -#: of tensorcircuit.keras.output_asis_loss:1 -msgid "The keras loss function that directly taking the model output as the loss." -msgstr "" - -#: of tensorcircuit.keras.output_asis_loss:3 -msgid "Ignoring this parameter." -msgstr "" - -#: of tensorcircuit.keras.output_asis_loss:5 -msgid "Model output." -msgstr "" - -#: of tensorcircuit.keras.output_asis_loss:7 -msgid "Model output, which is y_pred." -msgstr "" - -#: of tensorcircuit.keras.save_func:1 -msgid "Save tf function in the file (``tf.savedmodel`` format)." -msgstr "" - -#: of tensorcircuit.keras.save_func:30 -msgid "``tf.function`` ed function with graph building" -msgstr "" - -#: of tensorcircuit.keras.save_func:32 -msgid "the dir path to save the function" -msgstr "" - -#: ../../source/api/mps_base.rst:2 -msgid "tensorcircuit.mps_base" +"arguments, and `inputs` cannot be provided via the default value of a " +"keyword argument." msgstr "" -#: of tensorcircuit.mps_base:1 -msgid "FiniteMPS from tensornetwork with bug fixed" +#: of tensorcircuit.applications.van.MaskedConv2D.call:15 +#: tensorcircuit.applications.van.MaskedLinear.call:15 +#: tensorcircuit.applications.van.ResidualBlock.call:15 +#: tensorcircuit.applications.vqes.Linear.call:15 +msgid "NumPy array or Python scalar values in `inputs` get cast as tensors." msgstr "" -#: of tensorcircuit.mps_base.FiniteMPS:1 -msgid "Bases: :py:class:`tensornetwork.matrixproductstates.finite_mps.FiniteMPS`" +#: of tensorcircuit.applications.van.MaskedConv2D.call:17 +#: tensorcircuit.applications.van.MaskedLinear.call:17 +#: tensorcircuit.applications.van.ResidualBlock.call:17 +#: tensorcircuit.applications.vqes.Linear.call:17 +msgid "Keras mask metadata is only collected from `inputs`." msgstr "" -#: of tensorcircuit.mps_base.FiniteMPS.apply_two_site_gate:1 +#: of tensorcircuit.applications.van.MaskedConv2D.call:18 +#: tensorcircuit.applications.van.MaskedLinear.call:18 +#: tensorcircuit.applications.van.ResidualBlock.call:18 +#: tensorcircuit.applications.vqes.Linear.call:18 msgid "" -"Apply a two-site gate to an MPS. This routine will in general destroy any" -" canonical form of the state. If a canonical form is needed, the user can" -" restore it using `FiniteMPS.position`." -msgstr "" - -#: of tensorcircuit.mps_base.FiniteMPS.apply_two_site_gate:5 -msgid "A two-body gate." -msgstr "" - -#: of tensorcircuit.mps_base.FiniteMPS.apply_two_site_gate:7 -msgid "The first site where the gate acts." -msgstr "" - -#: of tensorcircuit.mps_base.FiniteMPS.apply_two_site_gate:9 -msgid "The second site where the gate acts." -msgstr "" - -#: of tensorcircuit.mps_base.FiniteMPS.apply_two_site_gate:11 -#: tensorcircuit.mpscircuit.MPSCircuit.from_wavefunction:5 -#: tensorcircuit.mpscircuit.MPSCircuit.set_truncation_rule:5 -#: tensorcircuit.mpscircuit.split_tensor:7 -msgid "The maximum number of singular values to keep." +"Layers are built (`build(input_shape)` method) using shape info from " +"`inputs` only." msgstr "" -#: of tensorcircuit.mps_base.FiniteMPS.apply_two_site_gate:13 -#: tensorcircuit.mpscircuit.MPSCircuit.from_wavefunction:7 -#: tensorcircuit.mpscircuit.MPSCircuit.set_truncation_rule:7 -#: tensorcircuit.mpscircuit.split_tensor:9 -msgid "The maximum allowed truncation error." +#: of tensorcircuit.applications.van.MaskedConv2D.call:20 +#: tensorcircuit.applications.van.MaskedLinear.call:20 +#: tensorcircuit.applications.van.ResidualBlock.call:20 +#: tensorcircuit.applications.vqes.Linear.call:20 +msgid "`input_spec` compatibility is only checked against `inputs`." msgstr "" -#: of tensorcircuit.mps_base.FiniteMPS.apply_two_site_gate:15 +#: of tensorcircuit.applications.van.MaskedConv2D.call:21 +#: tensorcircuit.applications.van.MaskedLinear.call:21 +#: tensorcircuit.applications.van.ResidualBlock.call:21 +#: tensorcircuit.applications.vqes.Linear.call:21 msgid "" -"An optional value to choose the MPS tensor at `center_position` to be " -"isometric after the application of the gate. Defaults to `site1`. If the " -"MPS is canonical (i.e.`BaseMPS.center_position != None`), and if the " -"orthogonality center coincides with either `site1` or `site2`, the " -"orthogonality center will be shifted to `center_position` (`site1` by " -"default). If the orthogonality center does not coincide with `(site1, " -"site2)` then `MPS.center_position` is set to `None`." +"Mixed precision input casting is only applied to `inputs`. If a layer has" +" tensor arguments in `*args` or `**kwargs`, their casting behavior in " +"mixed precision should be handled manually." msgstr "" -#: of tensorcircuit.mps_base.FiniteMPS.apply_two_site_gate:24 -#: tensorcircuit.mpscircuit.MPSCircuit.from_wavefunction:9 -#: tensorcircuit.mpscircuit.MPSCircuit.set_truncation_rule:9 -#: tensorcircuit.mpscircuit.split_tensor:11 -msgid "Multiply `max_truncation_err` with the largest singular value." +#: of tensorcircuit.applications.van.MaskedConv2D.call:24 +#: tensorcircuit.applications.van.MaskedLinear.call:24 +#: tensorcircuit.applications.van.ResidualBlock.call:24 +#: tensorcircuit.applications.vqes.Linear.call:24 +msgid "The SavedModel input specification is generated using `inputs` only." msgstr "" -#: of tensorcircuit.mps_base.FiniteMPS.apply_two_site_gate:26 +#: of tensorcircuit.applications.van.MaskedConv2D.call:26 +#: tensorcircuit.applications.van.MaskedLinear.call:26 +#: tensorcircuit.applications.van.ResidualBlock.call:26 +#: tensorcircuit.applications.vqes.Linear.call:26 msgid "" -"\"rank of gate is {} but has to be 4\", \"site1 = {} is not between 0 <= " -"site < N - 1 = {}\", \"site2 = {} is not between 1 <= site < N = " -"{}\",\"Found site2 ={}, site1={}. Only nearest neighbor gates are " -"currently supported\", \"f center_position = {center_position} not f in " -"{(site1, site2)} \", or \"center_position = {}, but gate is applied at " -"sites {}, {}. Truncation should only be done if the gate is applied at " -"the center position of the MPS.\"" -msgstr "" - -#: of tensorcircuit.mps_base.FiniteMPS.apply_two_site_gate:32 -msgid "A scalar tensor containing the truncated weight of the truncation." -msgstr "" - -#: of tensorcircuit.mps_base.FiniteMPS.measure_local_operator:1 -msgid "Measure the expectation value of local operators `ops` site `sites`." -msgstr "" - -#: of tensorcircuit.mps_base.FiniteMPS.measure_local_operator:3 -msgid "A list Tensors of rank 2; the local operators to be measured." -msgstr "" - -#: of tensorcircuit.mps_base.FiniteMPS.measure_local_operator:5 -msgid "Sites where `ops` act." -msgstr "" - -#: of tensorcircuit.mps_base.FiniteMPS.measure_local_operator:7 -msgid "measurements :math:`\\langle` `ops[n]`:math:`\\rangle` for n in `sites`" +"Integration with various ecosystem packages like TFMOT, TFLite, TF.js, " +"etc is only supported for `inputs` and not for tensors in positional and " +"keyword arguments." msgstr "" -#: of tensorcircuit.mps_base.FiniteMPS.measure_two_body_correlator:1 +#: of tensorcircuit.applications.van.MaskedConv2D.call:29 +#: tensorcircuit.applications.van.MaskedLinear.call:29 +#: tensorcircuit.applications.van.ResidualBlock.call:29 +#: tensorcircuit.applications.vqes.Linear.call:29 msgid "" -"Compute the correlator :math:`\\langle` `op1[site1], " -"op2[s]`:math:`\\rangle` between `site1` and all sites `s` in `sites2`. If" -" `s == site1`, `op2[s]` will be applied first." -msgstr "" - -#: of tensorcircuit.mps_base.FiniteMPS.measure_two_body_correlator:6 -msgid "Tensor of rank 2; the local operator at `site1`." -msgstr "" - -#: of tensorcircuit.mps_base.FiniteMPS.measure_two_body_correlator:8 -msgid "Tensor of rank 2; the local operator at `sites2`." -msgstr "" - -#: of tensorcircuit.mps_base.FiniteMPS.measure_two_body_correlator:10 -msgid "The site where `op1` acts" -msgstr "" - -#: of tensorcircuit.mps_base.FiniteMPS.measure_two_body_correlator:12 -msgid "Sites where operator `op2` acts." +"Additional positional arguments. May contain tensors, although this is " +"not recommended, for the reasons above." msgstr "" -#: of tensorcircuit.mps_base.FiniteMPS.measure_two_body_correlator:14 +#: of tensorcircuit.applications.van.MaskedConv2D.call:31 +#: tensorcircuit.applications.van.MaskedLinear.call:31 +#: tensorcircuit.applications.van.ResidualBlock.call:31 +#: tensorcircuit.applications.vqes.Linear.call:31 msgid "" -"Correlator :math:`\\langle` `op1[site1], op2[s]`:math:`\\rangle` for `s` " -":math:`\\in` `sites2`." -msgstr "" - -#: ../../source/api/mpscircuit.rst:2 -msgid "tensorcircuit.mpscircuit" -msgstr "" - -#: of tensorcircuit.mpscircuit:1 -msgid "Quantum circuit: MPS state simulator" -msgstr "" - -#: of tensorcircuit.mpscircuit.MPSCircuit:1 -msgid "``MPSCircuit`` class. Simple usage demo below." +"Additional keyword arguments. May contain tensors, although this is not " +"recommended, for the reasons above. The following optional keyword " +"arguments are reserved: - `training`: Boolean scalar tensor of Python " +"boolean indicating whether the `call` is meant for training or " +"inference. - `mask`: Boolean input mask. If the layer's `call()` method " +"takes a `mask` argument, its default value will be set to the mask " +"generated for `inputs` by the previous layer (if `input` did come from " +"a layer that generated a corresponding mask, i.e. if it came from a " +"Keras layer with masking support)." msgstr "" -#: of -#: tensorcircuit.mpscircuit.MPSCircuit.apply_general_variable_gate_delayed..apply:1 -msgid "Apply any gate with parameters on the circuit." -msgstr "" - -#: of -#: tensorcircuit.mpscircuit.MPSCircuit.apply_general_variable_gate_delayed..apply:5 -msgid "Parameters for the gate" -msgstr "" - -#: of -#: tensorcircuit.mpscircuit.MPSCircuit.apply_general_gate_delayed..apply:1 -msgid "Apply **CNOT** gate on the circuit." +#: of tensorcircuit.applications.van.MaskedConv2D.call:31 +#: tensorcircuit.applications.van.MaskedLinear.call:31 +#: tensorcircuit.applications.van.ResidualBlock.call:31 +#: tensorcircuit.applications.vqes.Linear.call:31 +msgid "" +"Additional keyword arguments. May contain tensors, although this is not " +"recommended, for the reasons above. The following optional keyword " +"arguments are reserved: - `training`: Boolean scalar tensor of Python " +"boolean indicating" msgstr "" -#: of -#: tensorcircuit.mpscircuit.MPSCircuit.apply_general_variable_gate_delayed..apply:1 -msgid "Apply cr gate with parameters on the circuit." +#: of tensorcircuit.applications.van.MaskedConv2D.call:35 +#: tensorcircuit.applications.van.MaskedLinear.call:35 +#: tensorcircuit.applications.van.ResidualBlock.call:35 +#: tensorcircuit.applications.vqes.Linear.call:35 +msgid "whether the `call` is meant for training or inference." msgstr "" -#: of -#: tensorcircuit.mpscircuit.MPSCircuit.apply_general_gate_delayed..apply:1 -msgid "Apply **CY** gate on the circuit." +#: of tensorcircuit.applications.van.MaskedConv2D.call:36 +#: tensorcircuit.applications.van.MaskedLinear.call:36 +#: tensorcircuit.applications.van.ResidualBlock.call:36 +#: tensorcircuit.applications.vqes.Linear.call:36 +msgid "" +"`mask`: Boolean input mask. If the layer's `call()` method takes a `mask`" +" argument, its default value will be set to the mask generated for " +"`inputs` by the previous layer (if `input` did come from a layer that " +"generated a corresponding mask, i.e. if it came from a Keras layer with " +"masking support)." msgstr "" -#: of -#: tensorcircuit.mpscircuit.MPSCircuit.apply_general_gate_delayed..apply:1 -msgid "Apply **CZ** gate on the circuit." +#: of tensorcircuit.applications.van.MaskedConv2D.call:42 +#: tensorcircuit.applications.van.MaskedLinear.call:42 +#: tensorcircuit.applications.van.ResidualBlock.call:42 +#: tensorcircuit.applications.vqes.Linear.call:42 +msgid "A tensor or list/tuple of tensors." msgstr "" -#: of -#: tensorcircuit.mpscircuit.MPSCircuit.apply_general_variable_gate_delayed..apply:1 -msgid "Apply exp gate with parameters on the circuit." +#: keras.src.engine.base_layer.Layer.get_config:1 of +msgid "Returns the config of the layer." msgstr "" -#: of -#: tensorcircuit.mpscircuit.MPSCircuit.apply_general_variable_gate_delayed..apply:1 -msgid "Apply exp1 gate with parameters on the circuit." +#: keras.src.engine.base_layer.Layer.get_config:3 of +msgid "" +"A layer config is a Python dictionary (serializable) containing the " +"configuration of a layer. The same layer can be reinstantiated later " +"(without its trained weights) from this configuration." msgstr "" -#: of -#: tensorcircuit.mpscircuit.MPSCircuit.apply_general_gate_delayed..apply:1 -msgid "Apply **H** gate on the circuit." +#: keras.src.engine.base_layer.Layer.get_config:8 of +msgid "" +"The config of a layer does not include connectivity information, nor the " +"layer class name. These are handled by `Network` (one layer of " +"abstraction above)." msgstr "" -#: of -#: tensorcircuit.mpscircuit.MPSCircuit.apply_general_gate_delayed..apply:1 -msgid "Apply **I** gate on the circuit." +#: keras.src.engine.base_layer.Layer.get_config:16 of +msgid "Python dictionary." msgstr "" -#: of -#: tensorcircuit.mpscircuit.MPSCircuit.apply_general_variable_gate_delayed..apply:1 -msgid "Apply r gate with parameters on the circuit." +#: keras.src.engine.base_layer.Layer.get_weights:1 of +msgid "Returns the current weights of the layer, as NumPy arrays." msgstr "" -#: of -#: tensorcircuit.mpscircuit.MPSCircuit.apply_general_variable_gate_delayed..apply:1 -msgid "Apply rx gate with parameters on the circuit." +#: keras.src.engine.base_layer.Layer.get_weights:3 of +msgid "" +"The weights of a layer represent the state of the layer. This function " +"returns both trainable and non-trainable weight values associated with " +"this layer as a list of NumPy arrays, which can in turn be used to load " +"state into similarly parameterized layers." msgstr "" -#: of -#: tensorcircuit.mpscircuit.MPSCircuit.apply_general_variable_gate_delayed..apply:1 -msgid "Apply ry gate with parameters on the circuit." +#: keras.src.engine.base_layer.Layer.get_weights:32 of +msgid "Weights values as a list of NumPy arrays." msgstr "" -#: of -#: tensorcircuit.mpscircuit.MPSCircuit.apply_general_variable_gate_delayed..apply:1 -msgid "Apply rz gate with parameters on the circuit." +#: of tensorcircuit.applications.van.MaskedConv2D.metrics:1 +#: tensorcircuit.applications.van.MaskedLinear.metrics:1 +#: tensorcircuit.applications.van.ResidualBlock.metrics:1 +#: tensorcircuit.applications.vqes.Linear.metrics:1 +#: tensorcircuit.keras.HardwareLayer.metrics:1 +#: tensorcircuit.keras.QuantumLayer.metrics:1 +msgid "List of metrics added using the `add_metric()` API." msgstr "" -#: of -#: tensorcircuit.mpscircuit.MPSCircuit.apply_general_gate_delayed..apply:1 -msgid "Apply **S** gate on the circuit." +#: of tensorcircuit.applications.van.MaskedConv2D.metrics:13 +#: tensorcircuit.applications.van.MaskedLinear.metrics:13 +#: tensorcircuit.applications.van.ResidualBlock.metrics:13 +#: tensorcircuit.applications.vqes.Linear.metrics:13 +#: tensorcircuit.keras.HardwareLayer.metrics:13 +#: tensorcircuit.keras.QuantumLayer.metrics:13 +msgid "A list of `Metric` objects." msgstr "" -#: of -#: tensorcircuit.mpscircuit.MPSCircuit.apply_general_gate_delayed..apply:1 -msgid "Apply **SWAP** gate on the circuit." +#: ../../source/api/applications/vqes.rst:2 +msgid "tensorcircuit.applications.vqes" msgstr "" -#: of -#: tensorcircuit.mpscircuit.MPSCircuit.apply_general_gate_delayed..apply:1 -msgid "Apply **T** gate on the circuit." +#: of tensorcircuit.applications.vqes:1 +msgid "VQNHE application" msgstr "" -#: of -#: tensorcircuit.mpscircuit.MPSCircuit.apply_general_gate_delayed..apply:1 -msgid "Apply **WROOT** gate on the circuit." +#: of tensorcircuit.applications.vqes.JointSchedule:1 +msgid "" +"Bases: " +":py:class:`~keras.src.optimizers.schedules.learning_rate_schedule.LearningRateSchedule`" msgstr "" +#: keras.src.optimizers.schedules.learning_rate_schedule.LearningRateSchedule.from_config:1 #: of -#: tensorcircuit.mpscircuit.MPSCircuit.apply_general_gate_delayed..apply:1 -msgid "Apply **X** gate on the circuit." +msgid "Instantiates a `LearningRateSchedule` from its config." msgstr "" +#: keras.src.optimizers.schedules.learning_rate_schedule.LearningRateSchedule.from_config:3 #: of -#: tensorcircuit.mpscircuit.MPSCircuit.apply_general_gate_delayed..apply:1 -msgid "Apply **Y** gate on the circuit." +msgid "Output of `get_config()`." msgstr "" +#: keras.src.optimizers.schedules.learning_rate_schedule.LearningRateSchedule.from_config:5 #: of -#: tensorcircuit.mpscircuit.MPSCircuit.apply_general_gate_delayed..apply:1 -msgid "Apply **Z** gate on the circuit." -msgstr "" - -#: of tensorcircuit.mpscircuit.MPSCircuit.__init__:1 -msgid "MPSCircuit object based on state simulator." -msgstr "" - -#: of tensorcircuit.mpscircuit.MPSCircuit.__init__:5 -msgid "" -"If not None, the initial state of the circuit is taken as ``tensors`` " -"instead of :math:`\\vert 0\\rangle^n` qubits, defaults to None" -msgstr "" - -#: of tensorcircuit.mpscircuit.MPSCircuit.__init__:8 -msgid "The center position of MPS, default to 0" -msgstr "" - -#: of tensorcircuit.mpscircuit.MPSCircuit.apply_general_gate:1 -msgid "Apply a general qubit gate on MPS." -msgstr "" - -#: of tensorcircuit.mpscircuit.MPSCircuit.apply_adjacent_double_gate:4 -#: tensorcircuit.mpscircuit.MPSCircuit.apply_double_gate:3 -#: tensorcircuit.mpscircuit.MPSCircuit.apply_general_gate:3 -msgid "The Gate to be applied" -msgstr "" - -#: of tensorcircuit.mpscircuit.MPSCircuit.apply_general_gate:6 -msgid "Qubit indices of the gate" -msgstr "" - -#: of tensorcircuit.mpscircuit.MPSCircuit.apply_general_gate:5 -msgid "\"MPS does not support application of gate on > 2 qubits.\"" +msgid "A `LearningRateSchedule` instance." msgstr "" -#: of tensorcircuit.mpscircuit.MPSCircuit.apply_adjacent_double_gate:1 -msgid "" -"Apply a double qubit gate on adjacent qubits of Matrix Product States " -"(MPS). Truncation rule is specified by `set_truncation_rule`." +#: of tensorcircuit.applications.vqes.Linear:1 +msgid "Dense layer but with complex weights, used for building complex RBM" msgstr "" -#: of tensorcircuit.mpscircuit.MPSCircuit.apply_adjacent_double_gate:6 -#: tensorcircuit.mpscircuit.MPSCircuit.apply_double_gate:5 -msgid "The first qubit index of the gate" +#: of tensorcircuit.applications.vqes.VQNHE.evaluation:1 +msgid "VQNHE" msgstr "" -#: of tensorcircuit.mpscircuit.MPSCircuit.apply_adjacent_double_gate:8 -#: tensorcircuit.mpscircuit.MPSCircuit.apply_double_gate:7 +#: of tensorcircuit.applications.vqes.VQNHE.evaluation:3 +#: tensorcircuit.applications.vqes.VQNHE.evaluation:5 +#: tensorcircuit.applications.vqes.VQNHE.plain_evaluation:3 +#: tensorcircuit.applications.vqes.VQNHE.plain_evaluation:5 +#: tensorcircuit.backends.abstract_backend.ExtendedBackend.coo_sparse_matrix:10 +#: tensorcircuit.backends.abstract_backend.ExtendedBackend.implicit_randc:3 +#: tensorcircuit.backends.abstract_backend.ExtendedBackend.implicit_randc:11 +#: tensorcircuit.backends.abstract_backend.ExtendedBackend.implicit_randn:11 +#: tensorcircuit.backends.abstract_backend.ExtendedBackend.implicit_randu:11 +#: tensorcircuit.backends.abstract_backend.ExtendedBackend.random_split:6 +#: tensorcircuit.backends.abstract_backend.ExtendedBackend.random_split:8 +#: tensorcircuit.backends.abstract_backend.ExtendedBackend.scatter:3 +#: tensorcircuit.backends.abstract_backend.ExtendedBackend.scatter:5 +#: tensorcircuit.backends.abstract_backend.ExtendedBackend.scatter:7 +#: tensorcircuit.backends.abstract_backend.ExtendedBackend.scatter:9 +#: tensorcircuit.backends.abstract_backend.ExtendedBackend.stateful_randc:3 +#: tensorcircuit.backends.abstract_backend.ExtendedBackend.stateful_randc:11 +#: tensorcircuit.backends.abstract_backend.ExtendedBackend.stateful_randn:3 +#: tensorcircuit.backends.abstract_backend.ExtendedBackend.stateful_randn:15 +#: tensorcircuit.backends.abstract_backend.ExtendedBackend.stateful_randu:13 +#: tensorcircuit.backends.cupy_backend.CuPyBackend.argmax:3 +#: tensorcircuit.backends.cupy_backend.CuPyBackend.argmax:7 +#: tensorcircuit.backends.cupy_backend.CuPyBackend.argmin:3 +#: tensorcircuit.backends.cupy_backend.CuPyBackend.argmin:7 +#: tensorcircuit.backends.cupy_backend.CuPyBackend.concat:3 +#: tensorcircuit.backends.cupy_backend.CuPyBackend.cond:3 +#: tensorcircuit.backends.cupy_backend.CuPyBackend.cond:5 +#: tensorcircuit.backends.cupy_backend.CuPyBackend.cond:7 +#: tensorcircuit.backends.cupy_backend.CuPyBackend.cond:9 +#: tensorcircuit.backends.cupy_backend.CuPyBackend.coo_sparse_matrix:10 +#: tensorcircuit.backends.cupy_backend.CuPyBackend.cumsum:3 +#: tensorcircuit.backends.cupy_backend.CuPyBackend.cumsum:8 +#: tensorcircuit.backends.cupy_backend.CuPyBackend.max:3 +#: tensorcircuit.backends.cupy_backend.CuPyBackend.max:7 +#: tensorcircuit.backends.cupy_backend.CuPyBackend.min:3 +#: tensorcircuit.backends.cupy_backend.CuPyBackend.min:7 +#: tensorcircuit.backends.cupy_backend.CuPyBackend.scatter:3 +#: tensorcircuit.backends.cupy_backend.CuPyBackend.scatter:5 +#: tensorcircuit.backends.cupy_backend.CuPyBackend.scatter:7 +#: tensorcircuit.backends.cupy_backend.CuPyBackend.scatter:9 +#: tensorcircuit.backends.cupy_backend.CuPyBackend.sigmoid:3 +#: tensorcircuit.backends.cupy_backend.CuPyBackend.sigmoid:5 +#: tensorcircuit.backends.cupy_backend.CuPyBackend.stateful_randc:3 +#: tensorcircuit.backends.cupy_backend.CuPyBackend.stateful_randc:11 +#: tensorcircuit.backends.cupy_backend.CuPyBackend.stateful_randn:3 +#: tensorcircuit.backends.cupy_backend.CuPyBackend.stateful_randn:15 +#: tensorcircuit.backends.cupy_backend.CuPyBackend.stateful_randu:13 +#: tensorcircuit.backends.cupy_backend.CuPyBackend.stop_gradient:3 +#: tensorcircuit.backends.cupy_backend.CuPyBackend.stop_gradient:5 +#: tensorcircuit.backends.cupy_backend.CuPyBackend.switch:3 +#: tensorcircuit.backends.cupy_backend.CuPyBackend.switch:5 +#: tensorcircuit.backends.cupy_backend.CuPyBackend.switch:7 +#: tensorcircuit.backends.cupy_backend.CuPyBackend.tile:3 +#: tensorcircuit.backends.cupy_backend.CuPyBackend.tile:7 +#: tensorcircuit.backends.cupy_backend.CuPyBackend.unique_with_counts:3 +#: tensorcircuit.backends.cupy_backend.CuPyBackend.vectorized_value_and_grad:29 +#: tensorcircuit.backends.cupy_backend.CuPyBackend.vectorized_value_and_grad:36 +#: tensorcircuit.backends.jax_backend.JaxBackend.argmax:3 +#: tensorcircuit.backends.jax_backend.JaxBackend.argmax:7 +#: tensorcircuit.backends.jax_backend.JaxBackend.argmin:3 +#: tensorcircuit.backends.jax_backend.JaxBackend.argmin:7 +#: tensorcircuit.backends.jax_backend.JaxBackend.concat:3 +#: tensorcircuit.backends.jax_backend.JaxBackend.cond:3 +#: tensorcircuit.backends.jax_backend.JaxBackend.cond:5 +#: tensorcircuit.backends.jax_backend.JaxBackend.cond:7 +#: tensorcircuit.backends.jax_backend.JaxBackend.cond:9 +#: tensorcircuit.backends.jax_backend.JaxBackend.coo_sparse_matrix:10 +#: tensorcircuit.backends.jax_backend.JaxBackend.cumsum:3 +#: tensorcircuit.backends.jax_backend.JaxBackend.cumsum:8 +#: tensorcircuit.backends.jax_backend.JaxBackend.implicit_randc:3 +#: tensorcircuit.backends.jax_backend.JaxBackend.implicit_randc:11 +#: tensorcircuit.backends.jax_backend.JaxBackend.implicit_randn:11 +#: tensorcircuit.backends.jax_backend.JaxBackend.implicit_randu:11 +#: tensorcircuit.backends.jax_backend.JaxBackend.max:3 +#: tensorcircuit.backends.jax_backend.JaxBackend.max:7 +#: tensorcircuit.backends.jax_backend.JaxBackend.min:3 +#: tensorcircuit.backends.jax_backend.JaxBackend.min:7 +#: tensorcircuit.backends.jax_backend.JaxBackend.random_split:6 +#: tensorcircuit.backends.jax_backend.JaxBackend.random_split:8 +#: tensorcircuit.backends.jax_backend.JaxBackend.scatter:3 +#: tensorcircuit.backends.jax_backend.JaxBackend.scatter:5 +#: tensorcircuit.backends.jax_backend.JaxBackend.scatter:7 +#: tensorcircuit.backends.jax_backend.JaxBackend.scatter:9 +#: tensorcircuit.backends.jax_backend.JaxBackend.sigmoid:3 +#: tensorcircuit.backends.jax_backend.JaxBackend.sigmoid:5 +#: tensorcircuit.backends.jax_backend.JaxBackend.stateful_randc:3 +#: tensorcircuit.backends.jax_backend.JaxBackend.stateful_randc:11 +#: tensorcircuit.backends.jax_backend.JaxBackend.stateful_randn:3 +#: tensorcircuit.backends.jax_backend.JaxBackend.stateful_randn:15 +#: tensorcircuit.backends.jax_backend.JaxBackend.stateful_randu:13 +#: tensorcircuit.backends.jax_backend.JaxBackend.stop_gradient:3 +#: tensorcircuit.backends.jax_backend.JaxBackend.stop_gradient:5 +#: tensorcircuit.backends.jax_backend.JaxBackend.switch:3 +#: tensorcircuit.backends.jax_backend.JaxBackend.switch:5 +#: tensorcircuit.backends.jax_backend.JaxBackend.switch:7 +#: tensorcircuit.backends.jax_backend.JaxBackend.tile:3 +#: tensorcircuit.backends.jax_backend.JaxBackend.tile:7 +#: tensorcircuit.backends.jax_backend.JaxBackend.unique_with_counts:3 +#: tensorcircuit.backends.jax_backend.JaxBackend.vectorized_value_and_grad:29 +#: tensorcircuit.backends.jax_backend.JaxBackend.vectorized_value_and_grad:36 +#: tensorcircuit.backends.numpy_backend.NumpyBackend.argmax:3 +#: tensorcircuit.backends.numpy_backend.NumpyBackend.argmax:7 +#: tensorcircuit.backends.numpy_backend.NumpyBackend.argmin:3 +#: tensorcircuit.backends.numpy_backend.NumpyBackend.argmin:7 +#: tensorcircuit.backends.numpy_backend.NumpyBackend.concat:3 +#: tensorcircuit.backends.numpy_backend.NumpyBackend.cond:3 +#: tensorcircuit.backends.numpy_backend.NumpyBackend.cond:5 +#: tensorcircuit.backends.numpy_backend.NumpyBackend.cond:7 +#: tensorcircuit.backends.numpy_backend.NumpyBackend.cond:9 +#: tensorcircuit.backends.numpy_backend.NumpyBackend.coo_sparse_matrix:10 +#: tensorcircuit.backends.numpy_backend.NumpyBackend.cumsum:3 +#: tensorcircuit.backends.numpy_backend.NumpyBackend.cumsum:8 +#: tensorcircuit.backends.numpy_backend.NumpyBackend.max:3 +#: tensorcircuit.backends.numpy_backend.NumpyBackend.max:7 +#: tensorcircuit.backends.numpy_backend.NumpyBackend.min:3 +#: tensorcircuit.backends.numpy_backend.NumpyBackend.min:7 +#: tensorcircuit.backends.numpy_backend.NumpyBackend.scatter:3 +#: tensorcircuit.backends.numpy_backend.NumpyBackend.scatter:5 +#: tensorcircuit.backends.numpy_backend.NumpyBackend.scatter:7 +#: tensorcircuit.backends.numpy_backend.NumpyBackend.scatter:9 +#: tensorcircuit.backends.numpy_backend.NumpyBackend.sigmoid:3 +#: tensorcircuit.backends.numpy_backend.NumpyBackend.sigmoid:5 +#: tensorcircuit.backends.numpy_backend.NumpyBackend.stateful_randc:3 +#: tensorcircuit.backends.numpy_backend.NumpyBackend.stateful_randc:11 +#: tensorcircuit.backends.numpy_backend.NumpyBackend.stateful_randn:3 +#: tensorcircuit.backends.numpy_backend.NumpyBackend.stateful_randn:15 +#: tensorcircuit.backends.numpy_backend.NumpyBackend.stateful_randu:13 +#: tensorcircuit.backends.numpy_backend.NumpyBackend.stop_gradient:3 +#: tensorcircuit.backends.numpy_backend.NumpyBackend.stop_gradient:5 +#: tensorcircuit.backends.numpy_backend.NumpyBackend.switch:3 +#: tensorcircuit.backends.numpy_backend.NumpyBackend.switch:5 +#: tensorcircuit.backends.numpy_backend.NumpyBackend.switch:7 +#: tensorcircuit.backends.numpy_backend.NumpyBackend.tile:3 +#: tensorcircuit.backends.numpy_backend.NumpyBackend.tile:7 +#: tensorcircuit.backends.numpy_backend.NumpyBackend.unique_with_counts:3 +#: tensorcircuit.backends.numpy_backend.NumpyBackend.vectorized_value_and_grad:29 +#: tensorcircuit.backends.numpy_backend.NumpyBackend.vectorized_value_and_grad:36 +#: tensorcircuit.backends.pytorch_backend.PyTorchBackend.argmax:3 +#: tensorcircuit.backends.pytorch_backend.PyTorchBackend.argmax:7 +#: tensorcircuit.backends.pytorch_backend.PyTorchBackend.argmin:3 +#: tensorcircuit.backends.pytorch_backend.PyTorchBackend.argmin:7 +#: tensorcircuit.backends.pytorch_backend.PyTorchBackend.concat:3 +#: tensorcircuit.backends.pytorch_backend.PyTorchBackend.cond:3 +#: tensorcircuit.backends.pytorch_backend.PyTorchBackend.cond:5 +#: tensorcircuit.backends.pytorch_backend.PyTorchBackend.cond:7 +#: tensorcircuit.backends.pytorch_backend.PyTorchBackend.cond:9 +#: tensorcircuit.backends.pytorch_backend.PyTorchBackend.cumsum:3 +#: tensorcircuit.backends.pytorch_backend.PyTorchBackend.cumsum:8 +#: tensorcircuit.backends.pytorch_backend.PyTorchBackend.max:3 +#: tensorcircuit.backends.pytorch_backend.PyTorchBackend.max:7 +#: tensorcircuit.backends.pytorch_backend.PyTorchBackend.min:3 +#: tensorcircuit.backends.pytorch_backend.PyTorchBackend.min:7 +#: tensorcircuit.backends.pytorch_backend.PyTorchBackend.sigmoid:3 +#: tensorcircuit.backends.pytorch_backend.PyTorchBackend.sigmoid:5 +#: tensorcircuit.backends.pytorch_backend.PyTorchBackend.stop_gradient:3 +#: tensorcircuit.backends.pytorch_backend.PyTorchBackend.stop_gradient:5 +#: tensorcircuit.backends.pytorch_backend.PyTorchBackend.switch:3 +#: tensorcircuit.backends.pytorch_backend.PyTorchBackend.switch:5 +#: tensorcircuit.backends.pytorch_backend.PyTorchBackend.switch:7 +#: tensorcircuit.backends.pytorch_backend.PyTorchBackend.tile:3 +#: tensorcircuit.backends.pytorch_backend.PyTorchBackend.tile:7 +#: tensorcircuit.backends.pytorch_backend.PyTorchBackend.unique_with_counts:3 +#: tensorcircuit.backends.pytorch_backend.PyTorchBackend.vectorized_value_and_grad:29 +#: tensorcircuit.backends.pytorch_backend.PyTorchBackend.vectorized_value_and_grad:36 +#: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.argmax:3 +#: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.argmax:7 +#: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.argmin:3 +#: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.argmin:7 +#: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.concat:3 +#: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.cond:3 +#: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.cond:5 +#: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.cond:7 +#: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.cond:9 +#: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.coo_sparse_matrix:10 +#: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.cumsum:3 +#: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.cumsum:8 +#: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.max:3 +#: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.max:7 +#: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.min:3 +#: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.min:7 +#: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.scatter:3 +#: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.scatter:5 +#: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.scatter:7 +#: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.scatter:9 +#: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.sigmoid:3 +#: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.sigmoid:5 +#: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.stateful_randc:3 +#: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.stateful_randc:11 +#: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.stateful_randn:3 +#: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.stateful_randn:15 +#: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.stateful_randu:13 +#: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.stop_gradient:3 +#: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.stop_gradient:5 +#: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.switch:3 +#: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.switch:5 +#: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.switch:7 +#: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.tile:3 +#: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.tile:7 +#: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.unique_with_counts:3 +#: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.vectorized_value_and_grad:29 +#: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.vectorized_value_and_grad:36 +#: tensorcircuit.basecircuit.BaseCircuit.sample_expectation_ps:53 +#: tensorcircuit.cloud.abstraction.Device.list_properties:3 +#: tensorcircuit.cloud.abstraction.Device.topology:3 +#: tensorcircuit.cloud.abstraction.Device.topology_graph:5 +#: tensorcircuit.cloud.tencent.submit_task:6 +#: tensorcircuit.cloud.tencent.submit_task:8 tensorcircuit.quantum.count_d2s:10 +#: tensorcircuit.quantum.count_d2s:14 tensorcircuit.quantum.count_s2d:4 +#: tensorcircuit.quantum.count_s2d:8 +#: tensorcircuit.simplify.pseudo_contract_between:3 +#: tensorcircuit.simplify.pseudo_contract_between:5 +#: tensorcircuit.simplify.pseudo_contract_between:7 +#: tensorcircuit.templates.graphs.Line1D:3 +#: tensorcircuit.templates.graphs.Line1D:7 +msgid "[description]" +msgstr "" + +#: of tensorcircuit.applications.vqes.VQNHE.plain_evaluation:1 +msgid "VQE" +msgstr "" + +#: ../../source/api/backends.rst:2 +msgid "tensorcircuit.backends" +msgstr "" + +#: ../../source/api/backends/backend_factory.rst:2 +msgid "tensorcircuit.backends.backend_factory" +msgstr "" + +#: of tensorcircuit.backends.backend_factory:1 +msgid "Backend register" +msgstr "" + +#: of tensorcircuit.backends.backend_factory.get_backend:1 +msgid "Get the `tc.backend` object." +msgstr "" + +#: of tensorcircuit.backends.backend_factory.get_backend:3 +msgid "\"numpy\", \"tensorflow\", \"jax\", \"pytorch\"" +msgstr "" + +#: of tensorcircuit.backends.backend_factory.get_backend:5 +msgid "Backend doesn't exist for `backend` argument." +msgstr "" + +#: of tensorcircuit.backends.backend_factory.get_backend:6 +#: tensorcircuit.cons.set_tensornetwork_backend:32 +msgid "The `tc.backend` object that with all registered universal functions." +msgstr "" + +#: ../../source/api/backends/cupy_backend.rst:2 +msgid "tensorcircuit.backends.cupy_backend" +msgstr "" + +#: of tensorcircuit.backends.cupy_backend:1 +msgid "CuPy backend. Not in the tensornetwork package and highly experimental." +msgstr "" + +#: of tensorcircuit.backends.cupy_backend.CuPyBackend:1 +msgid "" +"Bases: " +":py:class:`~tensornetwork.backends.abstract_backend.AbstractBackend`, " +":py:class:`~tensorcircuit.backends.abstract_backend.ExtendedBackend`" +msgstr "" + +#: of tensorcircuit.backends.cupy_backend.CuPyBackend.abs:1 +#: tensorcircuit.backends.jax_backend.JaxBackend.abs:1 +#: tensorcircuit.backends.numpy_backend.NumpyBackend.abs:1 +#: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.abs:1 +#: tensornetwork.backends.pytorch.pytorch_backend.PyTorchBackend.abs:1 +msgid "" +"Returns the elementwise absolute value of tensor. :param tensor: An input" +" tensor." +msgstr "" + +#: of tensorcircuit.backends.cupy_backend.CuPyBackend.abs:4 +#: tensorcircuit.backends.jax_backend.JaxBackend.abs:4 +#: tensorcircuit.backends.numpy_backend.NumpyBackend.abs:4 +#: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.abs:4 +#: tensornetwork.backends.pytorch.pytorch_backend.PyTorchBackend.abs:4 +msgid "Its elementwise absolute value." +msgstr "" + +#: of tensorcircuit.backends.cupy_backend.CuPyBackend.acos:1 +#: tensorcircuit.backends.cupy_backend.CuPyBackend.asin:1 +#: tensorcircuit.backends.jax_backend.JaxBackend.acos:1 +#: tensorcircuit.backends.jax_backend.JaxBackend.asin:1 +#: tensorcircuit.backends.numpy_backend.NumpyBackend.acos:1 +#: tensorcircuit.backends.numpy_backend.NumpyBackend.asin:1 +#: tensorcircuit.backends.pytorch_backend.PyTorchBackend.acos:1 +#: tensorcircuit.backends.pytorch_backend.PyTorchBackend.asin:1 +#: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.acos:1 +#: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.asin:1 +msgid "Return the acos of a tensor ``a``." +msgstr "" + +#: of tensorcircuit.backends.abstract_backend.ExtendedBackend.sqrtmh:3 +#: tensorcircuit.backends.cupy_backend.CuPyBackend.acos:3 +#: tensorcircuit.backends.cupy_backend.CuPyBackend.acosh:3 +#: tensorcircuit.backends.cupy_backend.CuPyBackend.asin:3 +#: tensorcircuit.backends.cupy_backend.CuPyBackend.asinh:3 +#: tensorcircuit.backends.cupy_backend.CuPyBackend.atan:3 +#: tensorcircuit.backends.cupy_backend.CuPyBackend.atan2:3 +#: tensorcircuit.backends.cupy_backend.CuPyBackend.atanh:3 +#: tensorcircuit.backends.cupy_backend.CuPyBackend.copy:3 +#: tensorcircuit.backends.cupy_backend.CuPyBackend.cosh:3 +#: tensorcircuit.backends.cupy_backend.CuPyBackend.eigvalsh:3 +#: tensorcircuit.backends.cupy_backend.CuPyBackend.kron:3 +#: tensorcircuit.backends.cupy_backend.CuPyBackend.kron:5 +#: tensorcircuit.backends.cupy_backend.CuPyBackend.numpy:3 +#: tensorcircuit.backends.cupy_backend.CuPyBackend.sinh:3 +#: tensorcircuit.backends.cupy_backend.CuPyBackend.tan:3 +#: tensorcircuit.backends.cupy_backend.CuPyBackend.tanh:3 +#: tensorcircuit.backends.jax_backend.JaxBackend.acos:3 +#: tensorcircuit.backends.jax_backend.JaxBackend.acosh:3 +#: tensorcircuit.backends.jax_backend.JaxBackend.asin:3 +#: tensorcircuit.backends.jax_backend.JaxBackend.asinh:3 +#: tensorcircuit.backends.jax_backend.JaxBackend.atan:3 +#: tensorcircuit.backends.jax_backend.JaxBackend.atan2:3 +#: tensorcircuit.backends.jax_backend.JaxBackend.atanh:3 +#: tensorcircuit.backends.jax_backend.JaxBackend.copy:3 +#: tensorcircuit.backends.jax_backend.JaxBackend.cosh:3 +#: tensorcircuit.backends.jax_backend.JaxBackend.eigvalsh:3 +#: tensorcircuit.backends.jax_backend.JaxBackend.kron:3 +#: tensorcircuit.backends.jax_backend.JaxBackend.kron:5 +#: tensorcircuit.backends.jax_backend.JaxBackend.numpy:3 +#: tensorcircuit.backends.jax_backend.JaxBackend.sinh:3 +#: tensorcircuit.backends.jax_backend.JaxBackend.tan:3 +#: tensorcircuit.backends.jax_backend.JaxBackend.tanh:3 +#: tensorcircuit.backends.numpy_backend.NumpyBackend.acos:3 +#: tensorcircuit.backends.numpy_backend.NumpyBackend.acosh:3 +#: tensorcircuit.backends.numpy_backend.NumpyBackend.asin:3 +#: tensorcircuit.backends.numpy_backend.NumpyBackend.asinh:3 +#: tensorcircuit.backends.numpy_backend.NumpyBackend.atan:3 +#: tensorcircuit.backends.numpy_backend.NumpyBackend.atan2:3 +#: tensorcircuit.backends.numpy_backend.NumpyBackend.atanh:3 +#: tensorcircuit.backends.numpy_backend.NumpyBackend.copy:3 +#: tensorcircuit.backends.numpy_backend.NumpyBackend.cosh:3 +#: tensorcircuit.backends.numpy_backend.NumpyBackend.eigvalsh:3 +#: tensorcircuit.backends.numpy_backend.NumpyBackend.kron:3 +#: tensorcircuit.backends.numpy_backend.NumpyBackend.kron:5 +#: tensorcircuit.backends.numpy_backend.NumpyBackend.numpy:3 +#: tensorcircuit.backends.numpy_backend.NumpyBackend.sinh:3 +#: tensorcircuit.backends.numpy_backend.NumpyBackend.tan:3 +#: tensorcircuit.backends.numpy_backend.NumpyBackend.tanh:3 +#: tensorcircuit.backends.pytorch_backend.PyTorchBackend.acos:3 +#: tensorcircuit.backends.pytorch_backend.PyTorchBackend.acosh:3 +#: tensorcircuit.backends.pytorch_backend.PyTorchBackend.asin:3 +#: tensorcircuit.backends.pytorch_backend.PyTorchBackend.asinh:3 +#: tensorcircuit.backends.pytorch_backend.PyTorchBackend.atan:3 +#: tensorcircuit.backends.pytorch_backend.PyTorchBackend.atan2:3 +#: tensorcircuit.backends.pytorch_backend.PyTorchBackend.atanh:3 +#: tensorcircuit.backends.pytorch_backend.PyTorchBackend.copy:3 +#: tensorcircuit.backends.pytorch_backend.PyTorchBackend.cosh:3 +#: tensorcircuit.backends.pytorch_backend.PyTorchBackend.eigvalsh:3 +#: tensorcircuit.backends.pytorch_backend.PyTorchBackend.kron:3 +#: tensorcircuit.backends.pytorch_backend.PyTorchBackend.kron:5 +#: tensorcircuit.backends.pytorch_backend.PyTorchBackend.numpy:3 +#: tensorcircuit.backends.pytorch_backend.PyTorchBackend.sinh:3 +#: tensorcircuit.backends.pytorch_backend.PyTorchBackend.tan:3 +#: tensorcircuit.backends.pytorch_backend.PyTorchBackend.tanh:3 +#: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.acos:3 +#: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.acosh:3 +#: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.asin:3 +#: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.asinh:3 +#: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.atan:3 +#: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.atan2:3 +#: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.atanh:3 +#: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.copy:3 +#: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.cosh:3 +#: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.eigvalsh:3 +#: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.kron:3 +#: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.kron:5 +#: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.numpy:3 +#: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.sinh:3 +#: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.tan:3 +#: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.tanh:3 +msgid "tensor in matrix form" +msgstr "" + +#: of tensorcircuit.backends.cupy_backend.CuPyBackend.acos:5 +#: tensorcircuit.backends.jax_backend.JaxBackend.acos:5 +#: tensorcircuit.backends.numpy_backend.NumpyBackend.acos:5 +#: tensorcircuit.backends.pytorch_backend.PyTorchBackend.acos:5 +#: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.acos:5 +msgid "acos of ``a``" +msgstr "" + +#: of tensorcircuit.backends.cupy_backend.CuPyBackend.acosh:1 +#: tensorcircuit.backends.jax_backend.JaxBackend.acosh:1 +#: tensorcircuit.backends.numpy_backend.NumpyBackend.acosh:1 +#: tensorcircuit.backends.pytorch_backend.PyTorchBackend.acosh:1 +#: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.acosh:1 +msgid "Return the acosh of a tensor ``a``." +msgstr "" + +#: of tensorcircuit.backends.cupy_backend.CuPyBackend.acosh:5 +#: tensorcircuit.backends.jax_backend.JaxBackend.acosh:5 +#: tensorcircuit.backends.numpy_backend.NumpyBackend.acosh:5 +#: tensorcircuit.backends.pytorch_backend.PyTorchBackend.acosh:5 +#: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.acosh:5 +msgid "acosh of ``a``" +msgstr "" + +#: of tensornetwork.backends.abstract_backend.AbstractBackend.addition:1 +#: tensornetwork.backends.jax.jax_backend.JaxBackend.addition:1 +#: tensornetwork.backends.numpy.numpy_backend.NumPyBackend.addition:1 +#: tensornetwork.backends.pytorch.pytorch_backend.PyTorchBackend.addition:1 +#: tensornetwork.backends.tensorflow.tensorflow_backend.TensorFlowBackend.addition:1 +msgid "" +"Return the default addition of `tensor`. A backend can override such " +"implementation. :param tensor1: A tensor. :param tensor2: A tensor." +msgstr "" + +#: of tensorcircuit.backends.cupy_backend.CuPyBackend.conj:4 +#: tensorcircuit.backends.cupy_backend.CuPyBackend.cos:4 +#: tensorcircuit.backends.cupy_backend.CuPyBackend.expm:4 +#: tensorcircuit.backends.cupy_backend.CuPyBackend.multiply:7 +#: tensorcircuit.backends.cupy_backend.CuPyBackend.sin:4 +#: tensorcircuit.backends.cupy_backend.CuPyBackend.softmax:9 +#: tensorcircuit.backends.jax_backend.JaxBackend.cos:4 +#: tensorcircuit.backends.jax_backend.JaxBackend.expm:4 +#: tensorcircuit.backends.jax_backend.JaxBackend.sin:4 +#: tensorcircuit.backends.jax_backend.JaxBackend.softmax:9 +#: tensorcircuit.backends.numpy_backend.NumpyBackend.cos:4 +#: tensorcircuit.backends.numpy_backend.NumpyBackend.expm:4 +#: tensorcircuit.backends.numpy_backend.NumpyBackend.sin:4 +#: tensorcircuit.backends.numpy_backend.NumpyBackend.softmax:9 +#: tensorcircuit.backends.pytorch_backend.PyTorchBackend.cos:4 +#: tensorcircuit.backends.pytorch_backend.PyTorchBackend.expm:4 +#: tensorcircuit.backends.pytorch_backend.PyTorchBackend.sin:4 +#: tensorcircuit.backends.pytorch_backend.PyTorchBackend.softmax:9 +#: tensorcircuit.backends.pytorch_backend._conj_torch:4 +#: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.cos:4 +#: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.expm:4 +#: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.sin:4 +#: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.softmax:9 +#: tensorcircuit.experimental.hamiltonian_evol:12 +#: tensornetwork.backends.abstract_backend.AbstractBackend.addition:6 +#: tensornetwork.backends.abstract_backend.AbstractBackend.divide:6 +#: tensornetwork.backends.abstract_backend.AbstractBackend.exp:4 +#: tensornetwork.backends.abstract_backend.AbstractBackend.log:4 +#: tensornetwork.backends.abstract_backend.AbstractBackend.subtraction:6 +#: tensornetwork.backends.jax.jax_backend.JaxBackend.addition:6 +#: tensornetwork.backends.jax.jax_backend.JaxBackend.conj:4 +#: tensornetwork.backends.jax.jax_backend.JaxBackend.divide:6 +#: tensornetwork.backends.jax.jax_backend.JaxBackend.exp:4 +#: tensornetwork.backends.jax.jax_backend.JaxBackend.log:4 +#: tensornetwork.backends.jax.jax_backend.JaxBackend.multiply:7 +#: tensornetwork.backends.jax.jax_backend.JaxBackend.subtraction:6 +#: tensornetwork.backends.numpy.numpy_backend.NumPyBackend.addition:6 +#: tensornetwork.backends.numpy.numpy_backend.NumPyBackend.conj:4 +#: tensornetwork.backends.numpy.numpy_backend.NumPyBackend.divide:6 +#: tensornetwork.backends.numpy.numpy_backend.NumPyBackend.exp:4 +#: tensornetwork.backends.numpy.numpy_backend.NumPyBackend.log:4 +#: tensornetwork.backends.numpy.numpy_backend.NumPyBackend.multiply:7 +#: tensornetwork.backends.numpy.numpy_backend.NumPyBackend.subtraction:6 +#: tensornetwork.backends.pytorch.pytorch_backend.PyTorchBackend.addition:6 +#: tensornetwork.backends.pytorch.pytorch_backend.PyTorchBackend.divide:6 +#: tensornetwork.backends.pytorch.pytorch_backend.PyTorchBackend.multiply:7 +#: tensornetwork.backends.pytorch.pytorch_backend.PyTorchBackend.subtraction:6 +#: tensornetwork.backends.tensorflow.tensorflow_backend.TensorFlowBackend.addition:6 +#: tensornetwork.backends.tensorflow.tensorflow_backend.TensorFlowBackend.conj:4 +#: tensornetwork.backends.tensorflow.tensorflow_backend.TensorFlowBackend.divide:6 +#: tensornetwork.backends.tensorflow.tensorflow_backend.TensorFlowBackend.exp:4 +#: tensornetwork.backends.tensorflow.tensorflow_backend.TensorFlowBackend.log:4 +#: tensornetwork.backends.tensorflow.tensorflow_backend.TensorFlowBackend.multiply:7 +#: tensornetwork.backends.tensorflow.tensorflow_backend.TensorFlowBackend.subtraction:6 +msgid "Tensor" +msgstr "" + +#: of tensorcircuit.backends.abstract_backend.ExtendedBackend.adjoint:1 +msgid "Return the conjugate and transpose of a tensor ``a``" +msgstr "" + +#: of tensorcircuit.backends.abstract_backend.ExtendedBackend.adjoint:3 +#: tensorcircuit.backends.abstract_backend.ExtendedBackend.reshape2:3 +#: tensorcircuit.backends.abstract_backend.ExtendedBackend.reshapem:3 +#: tensorcircuit.backends.cupy_backend.CuPyBackend.relu:9 +#: tensorcircuit.backends.jax_backend.JaxBackend.relu:9 +#: tensorcircuit.backends.numpy_backend.NumpyBackend.relu:9 +#: tensorcircuit.backends.pytorch_backend.PyTorchBackend.relu:9 +#: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.relu:9 +msgid "Input tensor" +msgstr "" + +#: of tensorcircuit.backends.abstract_backend.ExtendedBackend.adjoint:5 +msgid "adjoint tensor of ``a``" +msgstr "" + +#: of tensorcircuit.backends.cupy_backend.CuPyBackend.arange:1 +#: tensorcircuit.backends.jax_backend.JaxBackend.arange:1 +#: tensorcircuit.backends.numpy_backend.NumpyBackend.arange:1 +#: tensorcircuit.backends.pytorch_backend.PyTorchBackend.arange:1 +#: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.arange:1 +msgid "Values are generated within the half-open interval [start, stop)" +msgstr "" + +#: of tensorcircuit.backends.cupy_backend.CuPyBackend.arange:3 +#: tensorcircuit.backends.jax_backend.JaxBackend.arange:3 +#: tensorcircuit.backends.numpy_backend.NumpyBackend.arange:3 +#: tensorcircuit.backends.pytorch_backend.PyTorchBackend.arange:3 +#: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.arange:3 +msgid "start index" +msgstr "" + +#: of tensorcircuit.backends.cupy_backend.CuPyBackend.arange:5 +#: tensorcircuit.backends.jax_backend.JaxBackend.arange:5 +#: tensorcircuit.backends.numpy_backend.NumpyBackend.arange:5 +#: tensorcircuit.backends.pytorch_backend.PyTorchBackend.arange:5 +#: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.arange:5 +msgid "end index, defaults to None" +msgstr "" + +#: of tensorcircuit.backends.cupy_backend.CuPyBackend.arange:7 +#: tensorcircuit.backends.jax_backend.JaxBackend.arange:7 +#: tensorcircuit.backends.numpy_backend.NumpyBackend.arange:7 +#: tensorcircuit.backends.pytorch_backend.PyTorchBackend.arange:7 +#: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.arange:7 +msgid "steps, defaults to 1" +msgstr "" + +#: of tensorcircuit.backends.cupy_backend.CuPyBackend.argmax:1 +#: tensorcircuit.backends.jax_backend.JaxBackend.argmax:1 +#: tensorcircuit.backends.numpy_backend.NumpyBackend.argmax:1 +#: tensorcircuit.backends.pytorch_backend.PyTorchBackend.argmax:1 +#: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.argmax:1 +msgid "Return the index of maximum of an array an axis." +msgstr "" + +#: of tensorcircuit.backends.cupy_backend.CuPyBackend.argmax:5 +#: tensorcircuit.backends.cupy_backend.CuPyBackend.argmin:5 +#: tensorcircuit.backends.jax_backend.JaxBackend.argmax:5 +#: tensorcircuit.backends.jax_backend.JaxBackend.argmin:5 +#: tensorcircuit.backends.numpy_backend.NumpyBackend.argmax:5 +#: tensorcircuit.backends.numpy_backend.NumpyBackend.argmin:5 +#: tensorcircuit.backends.pytorch_backend.PyTorchBackend.argmax:5 +#: tensorcircuit.backends.pytorch_backend.PyTorchBackend.argmin:5 +#: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.argmax:5 +#: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.argmin:5 +msgid "[description], defaults to 0, different behavior from numpy defaults!" +msgstr "" + +#: of tensorcircuit.backends.cupy_backend.CuPyBackend.argmin:1 +#: tensorcircuit.backends.jax_backend.JaxBackend.argmin:1 +#: tensorcircuit.backends.numpy_backend.NumpyBackend.argmin:1 +#: tensorcircuit.backends.pytorch_backend.PyTorchBackend.argmin:1 +#: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.argmin:1 +msgid "Return the index of minimum of an array an axis." +msgstr "" + +#: of tensorcircuit.backends.cupy_backend.CuPyBackend.asin:5 +#: tensorcircuit.backends.jax_backend.JaxBackend.asin:5 +#: tensorcircuit.backends.numpy_backend.NumpyBackend.asin:5 +#: tensorcircuit.backends.pytorch_backend.PyTorchBackend.asin:5 +#: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.asin:5 +msgid "asin of ``a``" +msgstr "" + +#: of tensorcircuit.backends.cupy_backend.CuPyBackend.asinh:1 +#: tensorcircuit.backends.jax_backend.JaxBackend.asinh:1 +#: tensorcircuit.backends.numpy_backend.NumpyBackend.asinh:1 +#: tensorcircuit.backends.pytorch_backend.PyTorchBackend.asinh:1 +#: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.asinh:1 +msgid "Return the asinh of a tensor ``a``." +msgstr "" + +#: of tensorcircuit.backends.cupy_backend.CuPyBackend.asinh:5 +#: tensorcircuit.backends.jax_backend.JaxBackend.asinh:5 +#: tensorcircuit.backends.numpy_backend.NumpyBackend.asinh:5 +#: tensorcircuit.backends.pytorch_backend.PyTorchBackend.asinh:5 +#: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.asinh:5 +msgid "asinh of ``a``" +msgstr "" + +#: of tensorcircuit.backends.cupy_backend.CuPyBackend.atan:1 +#: tensorcircuit.backends.jax_backend.JaxBackend.atan:1 +#: tensorcircuit.backends.numpy_backend.NumpyBackend.atan:1 +#: tensorcircuit.backends.pytorch_backend.PyTorchBackend.atan:1 +#: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.atan:1 +msgid "Return the atan of a tensor ``a``." +msgstr "" + +#: of tensorcircuit.backends.cupy_backend.CuPyBackend.atan:5 +#: tensorcircuit.backends.jax_backend.JaxBackend.atan:5 +#: tensorcircuit.backends.numpy_backend.NumpyBackend.atan:5 +#: tensorcircuit.backends.pytorch_backend.PyTorchBackend.atan:5 +#: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.atan:5 +msgid "atan of ``a``" +msgstr "" + +#: of tensorcircuit.backends.cupy_backend.CuPyBackend.atan2:1 +#: tensorcircuit.backends.jax_backend.JaxBackend.atan2:1 +#: tensorcircuit.backends.numpy_backend.NumpyBackend.atan2:1 +#: tensorcircuit.backends.pytorch_backend.PyTorchBackend.atan2:1 +#: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.atan2:1 +msgid "Return the atan of a tensor ``y``/``x``." +msgstr "" + +#: of tensorcircuit.backends.cupy_backend.CuPyBackend.atan2:5 +#: tensorcircuit.backends.jax_backend.JaxBackend.atan2:5 +#: tensorcircuit.backends.numpy_backend.NumpyBackend.atan2:5 +#: tensorcircuit.backends.pytorch_backend.PyTorchBackend.atan2:5 +#: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.atan2:5 +msgid "atan2 of ``a``" +msgstr "" + +#: of tensorcircuit.backends.cupy_backend.CuPyBackend.atanh:1 +#: tensorcircuit.backends.jax_backend.JaxBackend.atanh:1 +#: tensorcircuit.backends.numpy_backend.NumpyBackend.atanh:1 +#: tensorcircuit.backends.pytorch_backend.PyTorchBackend.atanh:1 +#: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.atanh:1 +msgid "Return the atanh of a tensor ``a``." +msgstr "" + +#: of tensorcircuit.backends.cupy_backend.CuPyBackend.atanh:5 +#: tensorcircuit.backends.jax_backend.JaxBackend.atanh:5 +#: tensorcircuit.backends.numpy_backend.NumpyBackend.atanh:5 +#: tensorcircuit.backends.pytorch_backend.PyTorchBackend.atanh:5 +#: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.atanh:5 +msgid "atanh of ``a``" +msgstr "" + +#: of +#: tensornetwork.backends.abstract_backend.AbstractBackend.broadcast_left_multiplication:1 +#: tensornetwork.backends.jax.jax_backend.JaxBackend.broadcast_left_multiplication:1 +#: tensornetwork.backends.numpy.numpy_backend.NumPyBackend.broadcast_left_multiplication:1 +#: tensornetwork.backends.pytorch.pytorch_backend.PyTorchBackend.broadcast_left_multiplication:1 +#: tensornetwork.backends.tensorflow.tensorflow_backend.TensorFlowBackend.broadcast_left_multiplication:1 +msgid "" +"Perform broadcasting for multiplication of `tensor1` onto `tensor2`, i.e." +" `tensor1` * tensor2`, where `tensor2` is an arbitrary tensor and " +"`tensor1` is a one-dimensional tensor. The broadcasting is applied to the" +" first index of `tensor2`. :param tensor1: A tensor. :param tensor2: A " +"tensor." +msgstr "" + +#: of +#: tensornetwork.backends.abstract_backend.AbstractBackend.broadcast_left_multiplication:8 +#: tensornetwork.backends.abstract_backend.AbstractBackend.broadcast_right_multiplication:8 +#: tensornetwork.backends.jax.jax_backend.JaxBackend.broadcast_left_multiplication:8 +#: tensornetwork.backends.jax.jax_backend.JaxBackend.broadcast_right_multiplication:8 +#: tensornetwork.backends.numpy.numpy_backend.NumPyBackend.broadcast_left_multiplication:8 +#: tensornetwork.backends.numpy.numpy_backend.NumPyBackend.broadcast_right_multiplication:8 +#: tensornetwork.backends.pytorch.pytorch_backend.PyTorchBackend.broadcast_left_multiplication:8 +#: tensornetwork.backends.pytorch.pytorch_backend.PyTorchBackend.broadcast_right_multiplication:8 +#: tensornetwork.backends.tensorflow.tensorflow_backend.TensorFlowBackend.broadcast_left_multiplication:8 +#: tensornetwork.backends.tensorflow.tensorflow_backend.TensorFlowBackend.broadcast_right_multiplication:8 +msgid "The result of multiplying `tensor1` onto `tensor2`." +msgstr "" + +#: of +#: tensornetwork.backends.abstract_backend.AbstractBackend.broadcast_right_multiplication:1 +#: tensornetwork.backends.jax.jax_backend.JaxBackend.broadcast_right_multiplication:1 +#: tensornetwork.backends.numpy.numpy_backend.NumPyBackend.broadcast_right_multiplication:1 +#: tensornetwork.backends.pytorch.pytorch_backend.PyTorchBackend.broadcast_right_multiplication:1 +#: tensornetwork.backends.tensorflow.tensorflow_backend.TensorFlowBackend.broadcast_right_multiplication:1 +msgid "" +"Perform broadcasting for multiplication of `tensor2` onto `tensor1`, i.e." +" `tensor1` * tensor2`, where `tensor1` is an arbitrary tensor and " +"`tensor2` is a one-dimensional tensor. The broadcasting is applied to the" +" last index of `tensor1`. :param tensor1: A tensor. :param tensor2: A " +"tensor." +msgstr "" + +#: of tensorcircuit.backends.cupy_backend.CuPyBackend.cast:1 +#: tensorcircuit.backends.jax_backend.JaxBackend.cast:1 +#: tensorcircuit.backends.numpy_backend.NumpyBackend.cast:1 +#: tensorcircuit.backends.pytorch_backend.PyTorchBackend.cast:1 +#: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.cast:1 +msgid "Cast the tensor dtype of a ``a``." +msgstr "" + +#: of tensorcircuit.backends.abstract_backend.ExtendedBackend.sizen:3 +#: tensorcircuit.backends.cupy_backend.CuPyBackend.cast:3 +#: tensorcircuit.backends.cupy_backend.CuPyBackend.imag:3 +#: tensorcircuit.backends.cupy_backend.CuPyBackend.real:3 +#: tensorcircuit.backends.cupy_backend.CuPyBackend.size:3 +#: tensorcircuit.backends.jax_backend.JaxBackend.cast:3 +#: tensorcircuit.backends.jax_backend.JaxBackend.imag:3 +#: tensorcircuit.backends.jax_backend.JaxBackend.real:3 +#: tensorcircuit.backends.jax_backend.JaxBackend.size:3 +#: tensorcircuit.backends.numpy_backend.NumpyBackend.cast:3 +#: tensorcircuit.backends.numpy_backend.NumpyBackend.imag:3 +#: tensorcircuit.backends.numpy_backend.NumpyBackend.real:3 +#: tensorcircuit.backends.numpy_backend.NumpyBackend.size:3 +#: tensorcircuit.backends.pytorch_backend.PyTorchBackend.cast:3 +#: tensorcircuit.backends.pytorch_backend.PyTorchBackend.imag:3 +#: tensorcircuit.backends.pytorch_backend.PyTorchBackend.real:3 +#: tensorcircuit.backends.pytorch_backend.PyTorchBackend.size:3 +#: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.cast:3 +#: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.imag:3 +#: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.real:3 +#: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.size:3 +msgid "tensor" +msgstr "" + +#: of tensorcircuit.backends.cupy_backend.CuPyBackend.cast:5 +#: tensorcircuit.backends.jax_backend.JaxBackend.cast:5 +#: tensorcircuit.backends.numpy_backend.NumpyBackend.cast:5 +#: tensorcircuit.backends.pytorch_backend.PyTorchBackend.cast:5 +#: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.cast:5 +msgid "\"float32\", \"float64\", \"complex64\", \"complex128\"" +msgstr "" + +#: of tensorcircuit.backends.cupy_backend.CuPyBackend.cast:7 +#: tensorcircuit.backends.jax_backend.JaxBackend.cast:7 +#: tensorcircuit.backends.numpy_backend.NumpyBackend.cast:7 +#: tensorcircuit.backends.pytorch_backend.PyTorchBackend.cast:7 +#: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.cast:7 +msgid "``a`` of new dtype" +msgstr "" + +#: of tensorcircuit.backends.cupy_backend.CuPyBackend.concat:1 +#: tensorcircuit.backends.jax_backend.JaxBackend.concat:1 +#: tensorcircuit.backends.numpy_backend.NumpyBackend.concat:1 +#: tensorcircuit.backends.pytorch_backend.PyTorchBackend.concat:1 +#: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.concat:1 +msgid "Join a sequence of arrays along an existing axis." +msgstr "" + +#: of tensorcircuit.backends.abstract_backend.ExtendedBackend.implicit_randn:5 +#: tensorcircuit.backends.abstract_backend.ExtendedBackend.implicit_randu:5 +#: tensorcircuit.backends.abstract_backend.ExtendedBackend.stateful_randn:9 +#: tensorcircuit.backends.abstract_backend.ExtendedBackend.stateful_randu:7 +#: tensorcircuit.backends.cupy_backend.CuPyBackend.concat:5 +#: tensorcircuit.backends.cupy_backend.CuPyBackend.stateful_randn:9 +#: tensorcircuit.backends.cupy_backend.CuPyBackend.stateful_randu:7 +#: tensorcircuit.backends.cupy_backend.CuPyBackend.vectorized_value_and_grad:31 +#: tensorcircuit.backends.jax_backend.JaxBackend.concat:5 +#: tensorcircuit.backends.jax_backend.JaxBackend.implicit_randn:5 +#: tensorcircuit.backends.jax_backend.JaxBackend.implicit_randu:5 +#: tensorcircuit.backends.jax_backend.JaxBackend.stateful_randn:9 +#: tensorcircuit.backends.jax_backend.JaxBackend.stateful_randu:7 +#: tensorcircuit.backends.jax_backend.JaxBackend.vectorized_value_and_grad:31 +#: tensorcircuit.backends.numpy_backend.NumpyBackend.concat:5 +#: tensorcircuit.backends.numpy_backend.NumpyBackend.stateful_randn:9 +#: tensorcircuit.backends.numpy_backend.NumpyBackend.stateful_randu:7 +#: tensorcircuit.backends.numpy_backend.NumpyBackend.vectorized_value_and_grad:31 +#: tensorcircuit.backends.pytorch_backend.PyTorchBackend.concat:5 +#: tensorcircuit.backends.pytorch_backend.PyTorchBackend.vectorized_value_and_grad:31 +#: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.concat:5 +#: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.stateful_randn:9 +#: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.stateful_randu:7 +#: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.vectorized_value_and_grad:31 +msgid "[description], defaults to 0" +msgstr "" + +#: of tensorcircuit.backends.cupy_backend.CuPyBackend.cond:1 +#: tensorcircuit.backends.jax_backend.JaxBackend.cond:1 +#: tensorcircuit.backends.numpy_backend.NumpyBackend.cond:1 +#: tensorcircuit.backends.pytorch_backend.PyTorchBackend.cond:1 +#: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.cond:1 +msgid "" +"The native cond for XLA compiling, wrapper for ``tf.cond`` and limited " +"functionality of ``jax.lax.cond``." +msgstr "" + +#: of tensorcircuit.backends.cupy_backend.CuPyBackend.conj:1 +#: tensorcircuit.backends.pytorch_backend._conj_torch:1 +#: tensornetwork.backends.jax.jax_backend.JaxBackend.conj:1 +#: tensornetwork.backends.numpy.numpy_backend.NumPyBackend.conj:1 +#: tensornetwork.backends.tensorflow.tensorflow_backend.TensorFlowBackend.conj:1 +msgid "Return the complex conjugate of `tensor` :param tensor: A tensor." +msgstr "" + +#: of tensorcircuit.backends.cupy_backend.CuPyBackend.convert_to_tensor:1 +#: tensorcircuit.backends.jax_backend.JaxBackend.convert_to_tensor:1 +#: tensorcircuit.backends.numpy_backend._convert_to_tensor_numpy:1 +#: tensornetwork.backends.pytorch.pytorch_backend.PyTorchBackend.convert_to_tensor:1 +#: tensornetwork.backends.tensorflow.tensorflow_backend.TensorFlowBackend.convert_to_tensor:1 +msgid "Convert a np.array or a tensor to a tensor type for the backend." +msgstr "" + +#: of +#: tensorcircuit.backends.abstract_backend.ExtendedBackend.coo_sparse_matrix:1 +#: tensorcircuit.backends.cupy_backend.CuPyBackend.coo_sparse_matrix:1 +#: tensorcircuit.backends.jax_backend.JaxBackend.coo_sparse_matrix:1 +#: tensorcircuit.backends.numpy_backend.NumpyBackend.coo_sparse_matrix:1 +#: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.coo_sparse_matrix:1 +msgid "" +"Generate the coo format sparse matrix from indices and values, which is " +"the only sparse format supported in different ML backends." +msgstr "" + +#: of +#: tensorcircuit.backends.abstract_backend.ExtendedBackend.coo_sparse_matrix:4 +#: tensorcircuit.backends.cupy_backend.CuPyBackend.coo_sparse_matrix:4 +#: tensorcircuit.backends.jax_backend.JaxBackend.coo_sparse_matrix:4 +#: tensorcircuit.backends.numpy_backend.NumpyBackend.coo_sparse_matrix:4 +#: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.coo_sparse_matrix:4 +msgid "shape [n, 2] for n non zero values in the returned matrix" +msgstr "" + +#: of +#: tensorcircuit.backends.abstract_backend.ExtendedBackend.coo_sparse_matrix:6 +#: tensorcircuit.backends.cupy_backend.CuPyBackend.coo_sparse_matrix:6 +#: tensorcircuit.backends.jax_backend.JaxBackend.coo_sparse_matrix:6 +#: tensorcircuit.backends.numpy_backend.NumpyBackend.coo_sparse_matrix:6 +#: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.coo_sparse_matrix:6 +msgid "shape [n]" +msgstr "" + +#: of +#: tensorcircuit.backends.abstract_backend.ExtendedBackend.coo_sparse_matrix:8 +#: tensorcircuit.backends.cupy_backend.CuPyBackend.coo_sparse_matrix:8 +#: tensorcircuit.backends.jax_backend.JaxBackend.coo_sparse_matrix:8 +#: tensorcircuit.backends.numpy_backend.NumpyBackend.coo_sparse_matrix:8 +#: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.coo_sparse_matrix:8 +msgid "Tuple[int, ...]" +msgstr "" + +#: of +#: tensorcircuit.backends.abstract_backend.ExtendedBackend.coo_sparse_matrix_from_numpy:1 +msgid "Generate the coo format sparse matrix from scipy coo sparse matrix." +msgstr "" + +#: of +#: tensorcircuit.backends.abstract_backend.ExtendedBackend.coo_sparse_matrix_from_numpy:3 +msgid "Scipy coo format sparse matrix" +msgstr "" + +#: of +#: tensorcircuit.backends.abstract_backend.ExtendedBackend.coo_sparse_matrix_from_numpy:5 +msgid "SparseTensor in backend format" +msgstr "" + +#: of tensorcircuit.backends.cupy_backend.CuPyBackend.copy:1 +#: tensorcircuit.backends.jax_backend.JaxBackend.copy:1 +#: tensorcircuit.backends.numpy_backend.NumpyBackend.copy:1 +#: tensorcircuit.backends.pytorch_backend.PyTorchBackend.copy:1 +#: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.copy:1 +msgid "Return the copy of ``a``, matrix exponential." +msgstr "" + +#: of tensorcircuit.backends.cupy_backend.CuPyBackend.copy:5 +#: tensorcircuit.backends.jax_backend.JaxBackend.copy:5 +#: tensorcircuit.backends.numpy_backend.NumpyBackend.copy:5 +#: tensorcircuit.backends.pytorch_backend.PyTorchBackend.copy:5 +#: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.copy:5 +msgid "matrix exponential of matrix ``a``" +msgstr "" + +#: of tensorcircuit.backends.cupy_backend.CuPyBackend.cos:1 +#: tensorcircuit.backends.jax_backend.JaxBackend.cos:1 +#: tensorcircuit.backends.numpy_backend.NumpyBackend.cos:1 +#: tensorcircuit.backends.pytorch_backend.PyTorchBackend.cos:1 +#: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.cos:1 +msgid "Return cos of `tensor`. :param tensor: A tensor." +msgstr "" + +#: of tensorcircuit.backends.cupy_backend.CuPyBackend.cosh:1 +#: tensorcircuit.backends.jax_backend.JaxBackend.cosh:1 +#: tensorcircuit.backends.numpy_backend.NumpyBackend.cosh:1 +#: tensorcircuit.backends.pytorch_backend.PyTorchBackend.cosh:1 +#: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.cosh:1 +msgid "Return the cosh of a tensor ``a``." +msgstr "" + +#: of tensorcircuit.backends.cupy_backend.CuPyBackend.cosh:5 +#: tensorcircuit.backends.jax_backend.JaxBackend.cosh:5 +#: tensorcircuit.backends.numpy_backend.NumpyBackend.cosh:5 +#: tensorcircuit.backends.pytorch_backend.PyTorchBackend.cosh:5 +#: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.cosh:5 +msgid "cosh of ``a``" +msgstr "" + +#: of tensorcircuit.backends.cupy_backend.CuPyBackend.cumsum:1 +#: tensorcircuit.backends.jax_backend.JaxBackend.cumsum:1 +#: tensorcircuit.backends.numpy_backend.NumpyBackend.cumsum:1 +#: tensorcircuit.backends.pytorch_backend.PyTorchBackend.cumsum:1 +#: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.cumsum:1 +msgid "Return the cumulative sum of the elements along a given axis." +msgstr "" + +#: of tensorcircuit.backends.cupy_backend.CuPyBackend.cumsum:5 +#: tensorcircuit.backends.jax_backend.JaxBackend.cumsum:5 +#: tensorcircuit.backends.numpy_backend.NumpyBackend.cumsum:5 +#: tensorcircuit.backends.pytorch_backend.PyTorchBackend.cumsum:5 +#: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.cumsum:5 +msgid "" +"The default behavior is the same as numpy, different from tf/torch as " +"cumsum of the flatten 1D array, defaults to None" +msgstr "" + +#: of +#: tensornetwork.backends.abstract_backend.AbstractBackend.deserialize_tensor:1 +#: tensornetwork.backends.numpy.numpy_backend.NumPyBackend.deserialize_tensor:1 +msgid "Return a tensor given a serialized tensor string." +msgstr "" + +#: of +#: tensornetwork.backends.abstract_backend.AbstractBackend.deserialize_tensor:3 +#: tensornetwork.backends.numpy.numpy_backend.NumPyBackend.deserialize_tensor:3 +msgid "The input string representing a serialized tensor." +msgstr "" + +#: of +#: tensornetwork.backends.abstract_backend.AbstractBackend.deserialize_tensor:5 +#: tensornetwork.backends.numpy.numpy_backend.NumPyBackend.deserialize_tensor:5 +msgid "The tensor object represented by the string." +msgstr "" + +#: of tensorcircuit.backends.cupy_backend.CuPyBackend.device:1 +#: tensorcircuit.backends.jax_backend.JaxBackend.device:1 +#: tensorcircuit.backends.numpy_backend.NumpyBackend.device:1 +#: tensorcircuit.backends.pytorch_backend.PyTorchBackend.device:1 +#: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.device:1 +msgid "get the universal device str for the tensor, in the format of tf" +msgstr "" + +#: of tensorcircuit.backends.cupy_backend.CuPyBackend.device:3 +#: tensorcircuit.backends.cupy_backend.CuPyBackend.device_move:3 +#: tensorcircuit.backends.jax_backend.JaxBackend.device:3 +#: tensorcircuit.backends.jax_backend.JaxBackend.device_move:3 +#: tensorcircuit.backends.numpy_backend.NumpyBackend.device:3 +#: tensorcircuit.backends.numpy_backend.NumpyBackend.device_move:3 +#: tensorcircuit.backends.pytorch_backend.PyTorchBackend.device:3 +#: tensorcircuit.backends.pytorch_backend.PyTorchBackend.device_move:3 +#: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.device:3 +#: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.device_move:3 +#: tensorcircuit.interfaces.tensortrans.which_backend:3 +msgid "the tensor" +msgstr "" + +#: of tensorcircuit.backends.cupy_backend.CuPyBackend.device:5 +#: tensorcircuit.backends.jax_backend.JaxBackend.device:5 +#: tensorcircuit.backends.numpy_backend.NumpyBackend.device:5 +#: tensorcircuit.backends.pytorch_backend.PyTorchBackend.device:5 +#: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.device:5 +msgid "device str where the tensor lives on" +msgstr "" + +#: of tensorcircuit.backends.cupy_backend.CuPyBackend.device_move:1 +#: tensorcircuit.backends.jax_backend.JaxBackend.device_move:1 +#: tensorcircuit.backends.numpy_backend.NumpyBackend.device_move:1 +#: tensorcircuit.backends.pytorch_backend.PyTorchBackend.device_move:1 +#: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.device_move:1 +msgid "move tensor ``a`` to device ``dev``" +msgstr "" + +#: of tensorcircuit.backends.cupy_backend.CuPyBackend.device_move:5 +#: tensorcircuit.backends.jax_backend.JaxBackend.device_move:5 +#: tensorcircuit.backends.numpy_backend.NumpyBackend.device_move:5 +#: tensorcircuit.backends.pytorch_backend.PyTorchBackend.device_move:5 +#: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.device_move:5 +msgid "device str or device obj in corresponding backend" +msgstr "" + +#: of tensorcircuit.backends.cupy_backend.CuPyBackend.device_move:7 +#: tensorcircuit.backends.jax_backend.JaxBackend.device_move:7 +#: tensorcircuit.backends.numpy_backend.NumpyBackend.device_move:7 +#: tensorcircuit.backends.pytorch_backend.PyTorchBackend.device_move:7 +#: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.device_move:7 +msgid "the tensor on new device" +msgstr "" + +#: of tensornetwork.backends.abstract_backend.AbstractBackend.diagflat:1 +#: tensornetwork.backends.jax.jax_backend.JaxBackend.diagflat:1 +#: tensornetwork.backends.numpy.numpy_backend.NumPyBackend.diagflat:1 +#: tensornetwork.backends.pytorch.pytorch_backend.PyTorchBackend.diagflat:1 +#: tensornetwork.backends.tensorflow.tensorflow_backend.TensorFlowBackend.diagflat:1 +msgid "" +"Flattens tensor and creates a new matrix of zeros with its elements on " +"the k'th diagonal. :param tensor: A tensor. :param k: The diagonal upon " +"which to place its elements." +msgstr "" + +#: of tensornetwork.backends.abstract_backend.AbstractBackend.diagflat:6 +#: tensornetwork.backends.jax.jax_backend.JaxBackend.diagflat:6 +#: tensornetwork.backends.numpy.numpy_backend.NumPyBackend.diagflat:6 +#: tensornetwork.backends.pytorch.pytorch_backend.PyTorchBackend.diagflat:6 +#: tensornetwork.backends.tensorflow.tensorflow_backend.TensorFlowBackend.diagflat:6 +msgid "A new tensor with all zeros save the specified diagonal." +msgstr "" + +#: of tensornetwork.backends.abstract_backend.AbstractBackend.diagonal:1 +#: tensornetwork.backends.jax.jax_backend.JaxBackend.diagonal:1 +#: tensornetwork.backends.numpy.numpy_backend.NumPyBackend.diagonal:1 +#: tensornetwork.backends.pytorch.pytorch_backend.PyTorchBackend.diagonal:1 +#: tensornetwork.backends.tensorflow.tensorflow_backend.TensorFlowBackend.diagonal:1 +msgid "Return specified diagonals." +msgstr "" + +#: of tensornetwork.backends.abstract_backend.AbstractBackend.diagonal:3 +#: tensornetwork.backends.jax.jax_backend.JaxBackend.diagonal:3 +#: tensornetwork.backends.numpy.numpy_backend.NumPyBackend.diagonal:3 +#: tensornetwork.backends.pytorch.pytorch_backend.PyTorchBackend.diagonal:3 +#: tensornetwork.backends.tensorflow.tensorflow_backend.TensorFlowBackend.diagonal:3 +msgid "" +"If tensor is 2-D, returns the diagonal of tensor with the given offset, " +"i.e., the collection of elements of the form a[i, i+offset]. If a has " +"more than two dimensions, then the axes specified by axis1 and axis2 are " +"used to determine the 2-D sub-array whose diagonal is returned. The shape" +" of the resulting array can be determined by removing axis1 and axis2 and" +" appending an index to the right equal to the size of the resulting " +"diagonals." +msgstr "" + +#: of tensornetwork.backends.abstract_backend.AbstractBackend.diagonal:11 +#: tensornetwork.backends.jax.jax_backend.JaxBackend.diagonal:11 +#: tensornetwork.backends.numpy.numpy_backend.NumPyBackend.diagonal:11 +#: tensornetwork.backends.pytorch.pytorch_backend.PyTorchBackend.diagonal:11 +#: tensornetwork.backends.tensorflow.tensorflow_backend.TensorFlowBackend.diagonal:11 +msgid "" +"This function only extracts diagonals. If you wish to create diagonal " +"matrices from vectors, use diagflat." +msgstr "" + +#: of tensorcircuit.backends.cupy_backend.CuPyBackend.reshape:3 +#: tensorcircuit.backends.cupy_backend.CuPyBackend.shape_tuple:3 +#: tensorcircuit.backends.cupy_backend.CuPyBackend.tensordot:3 +#: tensorcircuit.backends.tensorflow_backend._tensordot_tf:3 +#: tensornetwork.backends.abstract_backend.AbstractBackend.diagonal:14 +#: tensornetwork.backends.abstract_backend.AbstractBackend.shape_tensor:3 +#: tensornetwork.backends.abstract_backend.AbstractBackend.slice:3 +#: tensornetwork.backends.abstract_backend.AbstractBackend.trace:10 +#: tensornetwork.backends.jax.jax_backend.JaxBackend.diagonal:14 +#: tensornetwork.backends.jax.jax_backend.JaxBackend.reshape:3 +#: tensornetwork.backends.jax.jax_backend.JaxBackend.shape_tensor:3 +#: tensornetwork.backends.jax.jax_backend.JaxBackend.shape_tuple:3 +#: tensornetwork.backends.jax.jax_backend.JaxBackend.slice:3 +#: tensornetwork.backends.jax.jax_backend.JaxBackend.tensordot:3 +#: tensornetwork.backends.jax.jax_backend.JaxBackend.trace:10 +#: tensornetwork.backends.numpy.numpy_backend.NumPyBackend.diagonal:14 +#: tensornetwork.backends.numpy.numpy_backend.NumPyBackend.reshape:3 +#: tensornetwork.backends.numpy.numpy_backend.NumPyBackend.shape_tensor:3 +#: tensornetwork.backends.numpy.numpy_backend.NumPyBackend.shape_tuple:3 +#: tensornetwork.backends.numpy.numpy_backend.NumPyBackend.slice:3 +#: tensornetwork.backends.numpy.numpy_backend.NumPyBackend.tensordot:3 +#: tensornetwork.backends.numpy.numpy_backend.NumPyBackend.trace:10 +#: tensornetwork.backends.pytorch.pytorch_backend.PyTorchBackend.diagonal:14 +#: tensornetwork.backends.pytorch.pytorch_backend.PyTorchBackend.reshape:3 +#: tensornetwork.backends.pytorch.pytorch_backend.PyTorchBackend.shape_tensor:3 +#: tensornetwork.backends.pytorch.pytorch_backend.PyTorchBackend.shape_tuple:3 +#: tensornetwork.backends.pytorch.pytorch_backend.PyTorchBackend.slice:3 +#: tensornetwork.backends.pytorch.pytorch_backend.PyTorchBackend.tensordot:3 +#: tensornetwork.backends.pytorch.pytorch_backend.PyTorchBackend.trace:13 +#: tensornetwork.backends.tensorflow.tensorflow_backend.TensorFlowBackend.diagonal:14 +#: tensornetwork.backends.tensorflow.tensorflow_backend.TensorFlowBackend.reshape:3 +#: tensornetwork.backends.tensorflow.tensorflow_backend.TensorFlowBackend.shape_tensor:3 +#: tensornetwork.backends.tensorflow.tensorflow_backend.TensorFlowBackend.shape_tuple:3 +#: tensornetwork.backends.tensorflow.tensorflow_backend.TensorFlowBackend.slice:3 +#: tensornetwork.backends.tensorflow.tensorflow_backend.TensorFlowBackend.trace:10 +msgid "A tensor." +msgstr "" + +#: of tensornetwork.backends.abstract_backend.AbstractBackend.diagonal:15 +#: tensornetwork.backends.abstract_backend.AbstractBackend.trace:11 +#: tensornetwork.backends.jax.jax_backend.JaxBackend.diagonal:15 +#: tensornetwork.backends.jax.jax_backend.JaxBackend.trace:11 +#: tensornetwork.backends.numpy.numpy_backend.NumPyBackend.diagonal:15 +#: tensornetwork.backends.numpy.numpy_backend.NumPyBackend.trace:11 +#: tensornetwork.backends.pytorch.pytorch_backend.PyTorchBackend.diagonal:15 +#: tensornetwork.backends.tensorflow.tensorflow_backend.TensorFlowBackend.diagonal:15 +msgid "Offset of the diagonal from the main diagonal." +msgstr "" + +#: of tensornetwork.backends.abstract_backend.AbstractBackend.diagonal:16 +#: tensornetwork.backends.abstract_backend.AbstractBackend.diagonal:19 +#: tensornetwork.backends.abstract_backend.AbstractBackend.trace:12 +#: tensornetwork.backends.abstract_backend.AbstractBackend.trace:15 +#: tensornetwork.backends.numpy.numpy_backend.NumPyBackend.diagonal:16 +#: tensornetwork.backends.numpy.numpy_backend.NumPyBackend.diagonal:19 +#: tensornetwork.backends.numpy.numpy_backend.NumPyBackend.trace:12 +#: tensornetwork.backends.numpy.numpy_backend.NumPyBackend.trace:15 +msgid "" +"Axis to be used as the first/second axis of the 2D sub-arrays from which " +"the diagonals should be taken. Defaults to second-last/last axis." +msgstr "" + +#: of tensornetwork.backends.abstract_backend.AbstractBackend.diagonal:23 +#: tensornetwork.backends.jax.jax_backend.JaxBackend.diagonal:23 +#: tensornetwork.backends.numpy.numpy_backend.NumPyBackend.diagonal:23 +#: tensornetwork.backends.pytorch.pytorch_backend.PyTorchBackend.diagonal:25 +#: tensornetwork.backends.tensorflow.tensorflow_backend.TensorFlowBackend.diagonal:33 +msgid "" +"A dim = min(1, tensor.ndim - 2) tensor storing the " +"batched diagonals." +msgstr "" + +#: of tensornetwork.backends.abstract_backend.AbstractBackend.diagonal:25 +#: tensornetwork.backends.jax.jax_backend.JaxBackend.diagonal:25 +#: tensornetwork.backends.numpy.numpy_backend.NumPyBackend.diagonal:25 +#: tensornetwork.backends.pytorch.pytorch_backend.PyTorchBackend.diagonal:27 +#: tensornetwork.backends.tensorflow.tensorflow_backend.TensorFlowBackend.diagonal:35 +msgid "A dim = min(1, tensor.ndim - 2) tensor storing" +msgstr "" + +#: of tensornetwork.backends.abstract_backend.AbstractBackend.diagonal:26 +#: tensornetwork.backends.jax.jax_backend.JaxBackend.diagonal:26 +#: tensornetwork.backends.numpy.numpy_backend.NumPyBackend.diagonal:26 +#: tensornetwork.backends.pytorch.pytorch_backend.PyTorchBackend.diagonal:28 +#: tensornetwork.backends.tensorflow.tensorflow_backend.TensorFlowBackend.diagonal:36 +msgid "the batched diagonals." +msgstr "" + +#: of tensornetwork.backends.abstract_backend.AbstractBackend.divide:1 +#: tensornetwork.backends.jax.jax_backend.JaxBackend.divide:1 +#: tensornetwork.backends.numpy.numpy_backend.NumPyBackend.divide:1 +#: tensornetwork.backends.pytorch.pytorch_backend.PyTorchBackend.divide:1 +#: tensornetwork.backends.tensorflow.tensorflow_backend.TensorFlowBackend.divide:1 +msgid "" +"Return the default divide of `tensor`. A backend can override such " +"implementation. :param tensor1: A tensor. :param tensor2: A tensor." +msgstr "" + +#: of tensorcircuit.backends.cupy_backend.CuPyBackend.dtype:1 +#: tensorcircuit.backends.jax_backend.JaxBackend.dtype:1 +#: tensorcircuit.backends.numpy_backend.NumpyBackend.dtype:1 +#: tensorcircuit.backends.pytorch_backend.PyTorchBackend.dtype:1 +#: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.dtype:1 +msgid "Obtain dtype string for tensor ``a``" +msgstr "" + +#: of tensorcircuit.backends.cupy_backend.CuPyBackend.dtype:3 +#: tensorcircuit.backends.jax_backend.JaxBackend.dtype:3 +#: tensorcircuit.backends.numpy_backend.NumpyBackend.dtype:3 +#: tensorcircuit.backends.pytorch_backend.PyTorchBackend.dtype:3 +#: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.dtype:3 +msgid "The tensor" +msgstr "" + +#: of tensorcircuit.backends.cupy_backend.CuPyBackend.dtype:5 +#: tensorcircuit.backends.jax_backend.JaxBackend.dtype:5 +#: tensorcircuit.backends.numpy_backend.NumpyBackend.dtype:5 +#: tensorcircuit.backends.pytorch_backend.PyTorchBackend.dtype:5 +#: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.dtype:5 +msgid "dtype str, such as \"complex64\"" +msgstr "" + +#: of tensornetwork.backends.abstract_backend.AbstractBackend.eigh:1 +#: tensornetwork.backends.jax.jax_backend.JaxBackend.eigh:1 +#: tensornetwork.backends.numpy.numpy_backend.NumPyBackend.eigh:1 +#: tensornetwork.backends.pytorch.pytorch_backend.PyTorchBackend.eigh:1 +#: tensornetwork.backends.tensorflow.tensorflow_backend.TensorFlowBackend.eigh:1 +msgid "Compute eigenvectors and eigenvalues of a hermitian matrix." +msgstr "" + +#: of tensornetwork.backends.abstract_backend.AbstractBackend.eigh:3 +#: tensornetwork.backends.jax.jax_backend.JaxBackend.eigh:3 +#: tensornetwork.backends.numpy.numpy_backend.NumPyBackend.eigh:3 +#: tensornetwork.backends.pytorch.pytorch_backend.PyTorchBackend.eigh:3 +#: tensornetwork.backends.tensorflow.tensorflow_backend.TensorFlowBackend.eigh:3 +msgid "A symetric matrix." +msgstr "" + +#: of tensornetwork.backends.abstract_backend.AbstractBackend.eigh:5 +#: tensornetwork.backends.jax.jax_backend.JaxBackend.eigh:5 +#: tensornetwork.backends.numpy.numpy_backend.NumPyBackend.eigh:5 +#: tensornetwork.backends.pytorch.pytorch_backend.PyTorchBackend.eigh:5 +#: tensornetwork.backends.tensorflow.tensorflow_backend.TensorFlowBackend.eigh:5 +msgid "The eigenvalues in ascending order. Tensor: The eigenvectors." +msgstr "" + +#: of tensornetwork.backends.abstract_backend.AbstractBackend.eigs:1 +msgid "" +"Arnoldi method for finding the lowest eigenvector-eigenvalue pairs of a " +"linear operator `A`. `A` is a callable implementing the matrix-vector " +"product. If no `initial_state` is provided then `shape` and `dtype` have " +"to be passed so that a suitable initial state can be randomly generated." +" :param A: A (sparse) implementation of a linear operator :param arsg: A " +"list of arguments to `A`. `A` will be called as" +msgstr "" + +#: of tensornetwork.backends.abstract_backend.AbstractBackend.eigs:8 +#: tensornetwork.backends.abstract_backend.AbstractBackend.eigsh:8 +#: tensornetwork.backends.numpy.numpy_backend.NumPyBackend.eigsh:8 +msgid "`res = A(initial_state, *args)`." +msgstr "" + +#: of tensornetwork.backends.abstract_backend.AbstractBackend.eigs:9 +#: tensornetwork.backends.abstract_backend.AbstractBackend.eigsh:9 +#: tensornetwork.backends.numpy.numpy_backend.NumPyBackend.eigs:11 +#: tensornetwork.backends.numpy.numpy_backend.NumPyBackend.eigsh:9 +msgid "" +"An initial vector for the algorithm. If `None`, a random initial `Tensor`" +" is created using the `numpy.random.randn` method." +msgstr "" + +#: of tensornetwork.backends.abstract_backend.AbstractBackend.eigs:12 +#: tensornetwork.backends.abstract_backend.AbstractBackend.eigsh:12 +#: tensornetwork.backends.abstract_backend.AbstractBackend.eigsh_lanczos:10 +#: tensornetwork.backends.jax.jax_backend.JaxBackend.eigs:44 +#: tensornetwork.backends.jax.jax_backend.JaxBackend.eigsh:44 +#: tensornetwork.backends.jax.jax_backend.JaxBackend.eigsh_lanczos:42 +#: tensornetwork.backends.numpy.numpy_backend.NumPyBackend.eigs:14 +#: tensornetwork.backends.numpy.numpy_backend.NumPyBackend.eigsh:12 +#: tensornetwork.backends.numpy.numpy_backend.NumPyBackend.eigsh_lanczos:10 +#: tensornetwork.backends.pytorch.pytorch_backend.PyTorchBackend.eigsh_lanczos:10 +msgid "The shape of the input-dimension of `A`." +msgstr "" + +#: of tensornetwork.backends.abstract_backend.AbstractBackend.eigs:13 +#: tensornetwork.backends.abstract_backend.AbstractBackend.eigsh:13 +#: tensornetwork.backends.abstract_backend.AbstractBackend.eigsh_lanczos:11 +#: tensornetwork.backends.numpy.numpy_backend.NumPyBackend.eigs:15 +#: tensornetwork.backends.numpy.numpy_backend.NumPyBackend.eigsh:13 +#: tensornetwork.backends.numpy.numpy_backend.NumPyBackend.eigsh_lanczos:11 +#: tensornetwork.backends.pytorch.pytorch_backend.PyTorchBackend.eigsh_lanczos:11 +msgid "" +"The dtype of the input `A`. If both no `initial_state` is provided, a " +"random initial state with shape `shape` and dtype `dtype` is created." +msgstr "" + +#: of tensornetwork.backends.abstract_backend.AbstractBackend.eigs:15 +#: tensornetwork.backends.abstract_backend.AbstractBackend.eigsh:15 +#: tensornetwork.backends.abstract_backend.AbstractBackend.eigsh_lanczos:13 +#: tensornetwork.backends.jax.jax_backend.JaxBackend.eigs:47 +#: tensornetwork.backends.jax.jax_backend.JaxBackend.eigsh:47 +#: tensornetwork.backends.jax.jax_backend.JaxBackend.eigsh_lanczos:45 +#: tensornetwork.backends.numpy.numpy_backend.NumPyBackend.eigs:17 +#: tensornetwork.backends.numpy.numpy_backend.NumPyBackend.eigsh:15 +#: tensornetwork.backends.numpy.numpy_backend.NumPyBackend.eigsh_lanczos:13 +#: tensornetwork.backends.pytorch.pytorch_backend.PyTorchBackend.eigsh_lanczos:13 +msgid "The number of iterations (number of krylov vectors)." +msgstr "" + +#: of tensornetwork.backends.abstract_backend.AbstractBackend.eigs:16 +#: tensornetwork.backends.abstract_backend.AbstractBackend.eigsh:16 +#: tensornetwork.backends.abstract_backend.AbstractBackend.eigsh_lanczos:14 +#: tensornetwork.backends.numpy.numpy_backend.NumPyBackend.eigs:18 +#: tensornetwork.backends.numpy.numpy_backend.NumPyBackend.eigsh:16 +#: tensornetwork.backends.numpy.numpy_backend.NumPyBackend.eigsh_lanczos:14 +#: tensornetwork.backends.pytorch.pytorch_backend.PyTorchBackend.eigsh_lanczos:14 +msgid "" +"The nummber of eigenvector-eigenvalue pairs to be computed. If `numeig > " +"1`, `reorthogonalize` has to be `True`." +msgstr "" + +#: of tensornetwork.backends.abstract_backend.AbstractBackend.eigs:18 +#: tensornetwork.backends.abstract_backend.AbstractBackend.eigsh:18 +#: tensornetwork.backends.numpy.numpy_backend.NumPyBackend.eigs:20 +#: tensornetwork.backends.numpy.numpy_backend.NumPyBackend.eigsh:18 +msgid "The desired precision of the eigenvalus. Uses" +msgstr "" + +#: of tensornetwork.backends.abstract_backend.AbstractBackend.eigs:19 +#: tensornetwork.backends.abstract_backend.AbstractBackend.eigsh:19 +#: tensornetwork.backends.numpy.numpy_backend.NumPyBackend.eigsh:19 +msgid "" +"['LM' | 'SM' | 'LR' | 'SR' | 'LI' | 'SI'] Which `k` eigenvectors and " +"eigenvalues to find: 'LM' : largest magnitude 'SM' : smallest " +"magnitude 'LR' : largest real part 'SR' : smallest real part " +"'LI' : largest imaginary part 'SI' : smallest imaginary part Note " +"that not all of those might be supported by specialized backends." +msgstr "" + +#: of tensornetwork.backends.abstract_backend.AbstractBackend.eigs:19 +#: tensornetwork.backends.abstract_backend.AbstractBackend.eigsh:19 +#: tensornetwork.backends.numpy.numpy_backend.NumPyBackend.eigsh:19 +msgid "" +"['LM' | 'SM' | 'LR' | 'SR' | 'LI' | 'SI'] Which `k` eigenvectors and " +"eigenvalues to find:" +msgstr "" + +#: of tensornetwork.backends.abstract_backend.AbstractBackend.eigs:21 +#: tensornetwork.backends.abstract_backend.AbstractBackend.eigsh:21 +#: tensornetwork.backends.numpy.numpy_backend.NumPyBackend.eigsh:21 +msgid "" +"'LM' : largest magnitude 'SM' : smallest magnitude 'LR' : largest real " +"part 'SR' : smallest real part 'LI' : largest imaginary part 'SI' : " +"smallest imaginary part" +msgstr "" + +#: of tensornetwork.backends.abstract_backend.AbstractBackend.eigs:27 +#: tensornetwork.backends.abstract_backend.AbstractBackend.eigsh:27 +#: tensornetwork.backends.numpy.numpy_backend.NumPyBackend.eigsh:27 +msgid "Note that not all of those might be supported by specialized backends." +msgstr "" + +#: of tensornetwork.backends.abstract_backend.AbstractBackend.eigs:28 +#: tensornetwork.backends.abstract_backend.AbstractBackend.eigsh:28 +#: tensornetwork.backends.numpy.numpy_backend.NumPyBackend.eigs:28 +#: tensornetwork.backends.numpy.numpy_backend.NumPyBackend.eigsh:28 +msgid "The maximum number of iterations." +msgstr "" + +#: of tensornetwork.backends.abstract_backend.AbstractBackend.eigs:30 +#: tensornetwork.backends.abstract_backend.AbstractBackend.eigsh:30 +#: tensornetwork.backends.numpy.numpy_backend.NumPyBackend.eigs:30 +#: tensornetwork.backends.numpy.numpy_backend.NumPyBackend.eigsh:30 +msgid "" +"An array of `numeig` lowest eigenvalues `list`: A list of `numeig` lowest" +" eigenvectors" +msgstr "" + +#: of tensornetwork.backends.abstract_backend.AbstractBackend.eigs:32 +#: tensornetwork.backends.abstract_backend.AbstractBackend.eigsh:32 +#: tensornetwork.backends.numpy.numpy_backend.NumPyBackend.eigsh:32 +#: tensornetwork.matrixproductstates.base_mps.BaseMPS.apply_transfer_operator:12 +#: tensornetwork.matrixproductstates.base_mps.BaseMPS.get_tensor:10 +#: tensornetwork.matrixproductstates.base_mps.BaseMPS.position:10 +#: tensornetwork.matrixproductstates.finite_mps.FiniteMPS.canonicalize:9 +msgid "`Tensor`" +msgstr "" + +#: of tensornetwork.backends.abstract_backend.AbstractBackend.eigsh:1 +#: tensornetwork.backends.numpy.numpy_backend.NumPyBackend.eigsh:1 +msgid "" +"Lanczos method for finding the lowest eigenvector-eigenvalue pairs of a " +"symmetric (hermitian) linear operator `A`. `A` is a callable implementing" +" the matrix-vector product. If no `initial_state` is provided then " +"`shape` and `dtype` have to be passed so that a suitable initial state " +"can be randomly generated. :param A: A (sparse) implementation of a " +"linear operator :param arsg: A list of arguments to `A`. `A` will be " +"called as" +msgstr "" + +#: of tensornetwork.backends.abstract_backend.AbstractBackend.eigsh_lanczos:1 +msgid "" +"Lanczos method for finding the lowest eigenvector-eigenvalue pairs of " +"`A`. :param A: A (sparse) implementation of a linear operator." +msgstr "" + +#: of tensornetwork.backends.abstract_backend.AbstractBackend.eigsh_lanczos:4 +#: tensornetwork.backends.numpy.numpy_backend.NumPyBackend.eigsh_lanczos:4 +#: tensornetwork.backends.pytorch.pytorch_backend.PyTorchBackend.eigsh_lanczos:4 +msgid "" +"Call signature of `A` is `res = A(vector, *args)`, where `vector` can be " +"an arbitrary `Tensor`, and `res.shape` has to be `vector.shape`." +msgstr "" + +#: of tensornetwork.backends.abstract_backend.AbstractBackend.eigsh_lanczos:6 +#: tensornetwork.backends.jax.jax_backend.JaxBackend.eigs:40 +#: tensornetwork.backends.jax.jax_backend.JaxBackend.eigsh:40 +#: tensornetwork.backends.jax.jax_backend.JaxBackend.eigsh_lanczos:38 +#: tensornetwork.backends.numpy.numpy_backend.NumPyBackend.eigs:9 +#: tensornetwork.backends.numpy.numpy_backend.NumPyBackend.eigsh_lanczos:6 +#: tensornetwork.backends.pytorch.pytorch_backend.PyTorchBackend.eigsh_lanczos:6 +msgid "" +"A list of arguments to `A`. `A` will be called as `res = " +"A(initial_state, *args)`." +msgstr "" + +#: of tensornetwork.backends.abstract_backend.AbstractBackend.eigsh_lanczos:8 +#: tensornetwork.backends.jax.jax_backend.JaxBackend.eigsh_lanczos:40 +#: tensornetwork.backends.numpy.numpy_backend.NumPyBackend.eigsh_lanczos:8 +msgid "" +"An initial vector for the Lanczos algorithm. If `None`, a random initial " +"`Tensor` is created using the `backend.randn` method" +msgstr "" + +#: of tensornetwork.backends.abstract_backend.AbstractBackend.eigsh_lanczos:16 +msgid "" +"The desired precision of the eigenvalus. Uses " +"`backend.norm(eigvalsnew[0:numeig] - eigvalsold[0:numeig]) < tol` as " +"stopping criterion between two diagonalization steps of the tridiagonal " +"operator." +msgstr "" + +#: of tensornetwork.backends.abstract_backend.AbstractBackend.eigsh_lanczos:20 +#: tensornetwork.backends.jax.jax_backend.JaxBackend.eigsh_lanczos:52 +#: tensornetwork.backends.numpy.numpy_backend.NumPyBackend.eigsh_lanczos:20 +#: tensornetwork.backends.pytorch.pytorch_backend.PyTorchBackend.eigsh_lanczos:20 +msgid "" +"Stopping criterion for Lanczos iteration. If a Krylov vector :math: `x_n`" +" has an L2 norm :math:`\\lVert x_n\\rVert < delta`, the iteration is " +"stopped. It means that an (approximate) invariant subspace has been " +"found." +msgstr "" + +#: of tensornetwork.backends.abstract_backend.AbstractBackend.eigsh_lanczos:25 +#: tensornetwork.backends.numpy.numpy_backend.NumPyBackend.eigsh_lanczos:25 +#: tensornetwork.backends.pytorch.pytorch_backend.PyTorchBackend.eigsh_lanczos:25 +msgid "" +"The tridiagonal Operator is diagonalized every `ndiag` iterations to " +"check convergence." +msgstr "" + +#: of tensornetwork.backends.abstract_backend.AbstractBackend.eigsh_lanczos:27 +#: tensornetwork.backends.jax.jax_backend.JaxBackend.eigsh_lanczos:60 +#: tensornetwork.backends.numpy.numpy_backend.NumPyBackend.eigsh_lanczos:27 +#: tensornetwork.backends.pytorch.pytorch_backend.PyTorchBackend.eigsh_lanczos:27 +msgid "" +"If `True`, Krylov vectors are kept orthogonal by explicit " +"orthogonalization (more costly than `reorthogonalize=False`)" +msgstr "" + +#: of tensornetwork.backends.abstract_backend.AbstractBackend.eigsh_lanczos:30 +#: tensornetwork.backends.numpy.numpy_backend.NumPyBackend.eigsh_lanczos:30 +#: tensornetwork.backends.pytorch.pytorch_backend.PyTorchBackend.eigsh_lanczos:30 +msgid "" +"(eigvals, eigvecs) eigvals: A list of `numeig` lowest eigenvalues " +"eigvecs: A list of `numeig` lowest eigenvectors" +msgstr "" + +#: of tensornetwork.backends.abstract_backend.AbstractBackend.eigsh_lanczos:33 +#: tensornetwork.backends.jax.jax_backend.JaxBackend.eigs:62 +#: tensornetwork.backends.jax.jax_backend.JaxBackend.eigsh:62 +#: tensornetwork.backends.jax.jax_backend.JaxBackend.eigsh_lanczos:66 +#: tensornetwork.backends.numpy.numpy_backend.NumPyBackend.eigsh_lanczos:33 +#: tensornetwork.backends.pytorch.pytorch_backend.PyTorchBackend.eigsh_lanczos:33 +msgid "(eigvals, eigvecs)" +msgstr "" + +#: of tensornetwork.backends.abstract_backend.AbstractBackend.eigsh_lanczos:33 +#: tensornetwork.backends.numpy.numpy_backend.NumPyBackend.eigsh_lanczos:33 +#: tensornetwork.backends.pytorch.pytorch_backend.PyTorchBackend.eigsh_lanczos:33 +msgid "" +"eigvals: A list of `numeig` lowest eigenvalues eigvecs: A list of " +"`numeig` lowest eigenvectors" +msgstr "" + +#: of tensorcircuit.backends.cupy_backend.CuPyBackend.eigvalsh:1 +#: tensorcircuit.backends.jax_backend.JaxBackend.eigvalsh:1 +#: tensorcircuit.backends.numpy_backend.NumpyBackend.eigvalsh:1 +#: tensorcircuit.backends.pytorch_backend.PyTorchBackend.eigvalsh:1 +#: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.eigvalsh:1 +msgid "Get the eigenvalues of matrix ``a``." +msgstr "" + +#: of tensorcircuit.backends.cupy_backend.CuPyBackend.eigvalsh:5 +#: tensorcircuit.backends.jax_backend.JaxBackend.eigvalsh:5 +#: tensorcircuit.backends.numpy_backend.NumpyBackend.eigvalsh:5 +#: tensorcircuit.backends.pytorch_backend.PyTorchBackend.eigvalsh:5 +#: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.eigvalsh:5 +msgid "eigenvalues of ``a``" +msgstr "" + +#: of tensornetwork.backends.abstract_backend.AbstractBackend.einsum:1 +#: tensornetwork.backends.jax.jax_backend.JaxBackend.einsum:1 +#: tensornetwork.backends.numpy.numpy_backend.NumPyBackend.einsum:1 +#: tensornetwork.backends.pytorch.pytorch_backend.PyTorchBackend.einsum:1 +#: tensornetwork.backends.tensorflow.tensorflow_backend.TensorFlowBackend.einsum:1 +msgid "Calculate sum of products of tensors according to expression." +msgstr "" + +#: of tensornetwork.backends.abstract_backend.AbstractBackend.eps:1 +#: tensornetwork.backends.jax.jax_backend.JaxBackend.eps:1 +#: tensornetwork.backends.numpy.numpy_backend.NumPyBackend.eps:1 +#: tensornetwork.backends.pytorch.pytorch_backend.PyTorchBackend.eps:1 +#: tensornetwork.backends.tensorflow.tensorflow_backend.TensorFlowBackend.eps:1 +msgid "Return machine epsilon for given `dtype`" +msgstr "" + +#: of tensornetwork.backends.abstract_backend.AbstractBackend.eps:3 +#: tensornetwork.backends.jax.jax_backend.JaxBackend.eps:3 +#: tensornetwork.backends.numpy.numpy_backend.NumPyBackend.eps:3 +#: tensornetwork.backends.pytorch.pytorch_backend.PyTorchBackend.eps:3 +#: tensornetwork.backends.tensorflow.tensorflow_backend.TensorFlowBackend.eps:3 +msgid "A dtype." +msgstr "" + +#: of tensornetwork.backends.abstract_backend.AbstractBackend.eps:5 +#: tensornetwork.backends.jax.jax_backend.JaxBackend.eps:5 +#: tensornetwork.backends.numpy.numpy_backend.NumPyBackend.eps:5 +#: tensornetwork.backends.pytorch.pytorch_backend.PyTorchBackend.eps:5 +#: tensornetwork.backends.tensorflow.tensorflow_backend.TensorFlowBackend.eps:5 +msgid "Machine epsilon." +msgstr "" + +#: of tensornetwork.backends.abstract_backend.AbstractBackend.exp:1 +#: tensornetwork.backends.jax.jax_backend.JaxBackend.exp:1 +#: tensornetwork.backends.numpy.numpy_backend.NumPyBackend.exp:1 +#: tensornetwork.backends.tensorflow.tensorflow_backend.TensorFlowBackend.exp:1 +msgid "Return elementwise exp of `tensor`. :param tensor: A tensor." +msgstr "" + +#: of tensorcircuit.backends.cupy_backend.CuPyBackend.expm:1 +#: tensorcircuit.backends.jax_backend.JaxBackend.expm:1 +#: tensorcircuit.backends.numpy_backend.NumpyBackend.expm:1 +#: tensorcircuit.backends.pytorch_backend.PyTorchBackend.expm:1 +#: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.expm:1 +msgid "Return expm log of `matrix`, matrix exponential. :param matrix: A tensor." +msgstr "" + +#: of tensorcircuit.backends.cupy_backend.CuPyBackend.eye:4 +#: tensorcircuit.backends.jax_backend.JaxBackend.eye:4 +#: tensorcircuit.backends.numpy_backend.NumpyBackend.eye:4 +#: tensorcircuit.backends.pytorch_backend.PyTorchBackend.eye:4 +#: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.eye:4 +msgid "Return an identity matrix of dimension `dim`" +msgstr "" + +#: of tensorcircuit.backends.cupy_backend.CuPyBackend.eye:2 +#: tensorcircuit.backends.jax_backend.JaxBackend.eye:2 +#: tensorcircuit.backends.numpy_backend.NumpyBackend.eye:2 +#: tensorcircuit.backends.pytorch_backend.PyTorchBackend.eye:2 +#: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.eye:2 +msgid "" +"Depending on specific backends, `dim` has to be either an int (numpy, " +"torch, tensorflow) or a `ShapeType` object (for block-sparse backends). " +"Block-sparse behavior is currently not supported" +msgstr "" + +#: of tensorcircuit.backends.cupy_backend.CuPyBackend.eye:6 +#: tensorcircuit.backends.cupy_backend.CuPyBackend.eye:9 +#: tensorcircuit.backends.jax_backend.JaxBackend.eye:6 +#: tensorcircuit.backends.jax_backend.JaxBackend.eye:9 +#: tensorcircuit.backends.numpy_backend.NumpyBackend.eye:6 +#: tensorcircuit.backends.numpy_backend.NumpyBackend.eye:9 +#: tensorcircuit.backends.pytorch_backend.PyTorchBackend.eye:6 +#: tensorcircuit.backends.pytorch_backend.PyTorchBackend.eye:9 +#: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.eye:6 +#: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.eye:9 +msgid "The dimension of the returned matrix." +msgstr "" + +#: of tensorcircuit.backends.cupy_backend.CuPyBackend.eye:8 +#: tensorcircuit.backends.jax_backend.JaxBackend.eye:8 +#: tensorcircuit.backends.numpy_backend.NumpyBackend.eye:8 +#: tensorcircuit.backends.pytorch_backend.PyTorchBackend.eye:8 +#: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.eye:8 +msgid "The dtype of the returned matrix." +msgstr "" + +#: of tensorcircuit.backends.abstract_backend.ExtendedBackend.from_dlpack:1 +#: tensorcircuit.backends.jax_backend.JaxBackend.from_dlpack:1 +#: tensorcircuit.backends.pytorch_backend.PyTorchBackend.from_dlpack:1 +#: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.from_dlpack:1 +msgid "Transform a dlpack capsule to a tensor" +msgstr "" + +#: of tensorcircuit.backends.abstract_backend.ExtendedBackend.from_dlpack:3 +#: tensorcircuit.backends.jax_backend.JaxBackend.from_dlpack:3 +#: tensorcircuit.backends.pytorch_backend.PyTorchBackend.from_dlpack:3 +#: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.from_dlpack:3 +msgid "the dlpack capsule" +msgstr "" + +#: of tensorcircuit.backends.abstract_backend.ExtendedBackend.gather1d:1 +#: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.gather1d:1 +msgid "" +"Return ``operand[indices]``, both ``operand`` and ``indices`` are rank-1 " +"tensor." +msgstr "" + +#: of tensorcircuit.backends.abstract_backend.ExtendedBackend.gather1d:3 +#: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.gather1d:3 +msgid "rank-1 tensor" +msgstr "" + +#: of tensorcircuit.backends.abstract_backend.ExtendedBackend.gather1d:5 +#: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.gather1d:5 +msgid "rank-1 tensor with int dtype" +msgstr "" + +#: of tensorcircuit.backends.abstract_backend.ExtendedBackend.gather1d:7 +#: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.gather1d:7 +msgid "``operand[indices]``" +msgstr "" + +#: of +#: tensorcircuit.backends.abstract_backend.ExtendedBackend.get_random_state:1 +msgid "Get the backend specific random state object." +msgstr "" + +#: of +#: tensorcircuit.backends.abstract_backend.ExtendedBackend.get_random_state:3 +msgid "[description], defaults to be None" +msgstr "" + +#: of +#: tensorcircuit.backends.abstract_backend.ExtendedBackend.get_random_state:5 +msgid ":return:the backend specific random state object :rtype: Any" +msgstr "" + +#: of tensornetwork.backends.abstract_backend.AbstractBackend.gmres:1 +msgid "" +"GMRES solves the linear system A @ x = b for x given a vector `b` and a " +"general (not necessarily symmetric/Hermitian) linear operator `A`." +msgstr "" + +#: of tensornetwork.backends.abstract_backend.AbstractBackend.gmres:4 +msgid "" +"As a Krylov method, GMRES does not require a concrete matrix " +"representation of the n by n `A`, but only a function `vector1 = " +"A_mv(vector0, *A_args, **A_kwargs)` prescribing a one-to-one linear map " +"from vector0 to vector1 (that is, A must be square, and thus vector0 and " +"vector1 the same size). If `A` is a dense matrix, or if it is a " +"symmetric/Hermitian operator, a different linear solver will usually be " +"preferable." +msgstr "" + +#: of tensornetwork.backends.abstract_backend.AbstractBackend.gmres:12 +msgid "" +"GMRES works by first constructing the Krylov basis K = (x0, A_mv@x0, " +"A_mv@A_mv@x0, ..., (A_mv^num_krylov_vectors)@x_0) and then solving a " +"certain dense linear system K @ q0 = q1 from whose solution x can be " +"approximated. For `num_krylov_vectors = n` the solution is provably exact" +" in infinite precision, but the expense is cubic in `num_krylov_vectors` " +"so one is typically interested in the `num_krylov_vectors << n` case. The" +" solution can in this case be repeatedly improved, to a point, by " +"restarting the Arnoldi iterations each time `num_krylov_vectors` is " +"reached. Unfortunately the optimal parameter choices balancing expense " +"and accuracy are difficult to predict in advance, so applying this " +"function requires a degree of experimentation." +msgstr "" + +#: of tensornetwork.backends.abstract_backend.AbstractBackend.gmres:24 +msgid "" +"In a tensor network code one is typically interested in A_mv implementing" +" some tensor contraction. This implementation thus allows `b` and `x0` to" +" be of whatever arbitrary, though identical, shape `b = A_mv(x0, ...)` " +"expects. Reshaping to and from a matrix problem is handled internally." +msgstr "" + +#: of tensornetwork.backends.abstract_backend.AbstractBackend.gmres:29 +msgid "" +"A function `v0 = A_mv(v, *A_args, **A_kwargs)` where `v0` and `v` have " +"the same shape." +msgstr "" + +#: of tensornetwork.backends.abstract_backend.AbstractBackend.gmres:31 +msgid "The `b` in `A @ x = b`; it should be of the shape `A_mv` operates on." +msgstr "" + +#: of tensornetwork.backends.abstract_backend.AbstractBackend.gmres:33 +msgid "" +"Positional arguments to `A_mv`, supplied to this interface as a list. " +"Default: None." +msgstr "" + +#: of tensornetwork.backends.abstract_backend.AbstractBackend.gmres:36 +msgid "" +"Keyword arguments to `A_mv`, supplied to this interface as a dictionary. " +"Default: None." +msgstr "" + +#: of tensornetwork.backends.abstract_backend.AbstractBackend.gmres:39 +msgid "" +"An optional guess solution. Zeros are used by default. If `x0` is " +"supplied, its shape and dtype must match those of `b`, or an error will " +"be thrown. Default: zeros." +msgstr "" + +#: of tensornetwork.backends.abstract_backend.AbstractBackend.gmres:44 +#: tensornetwork.backends.abstract_backend.AbstractBackend.gmres:48 +msgid "" +"Solution tolerance to achieve, norm(residual) <= max(tol*norm(b), atol). " +"Default: tol=1E-05 atol=tol" +msgstr "" + +#: of tensornetwork.backends.abstract_backend.AbstractBackend.gmres:44 +#: tensornetwork.backends.abstract_backend.AbstractBackend.gmres:48 +msgid "" +"Solution tolerance to achieve, norm(residual) <= max(tol*norm(b), atol). " +"Default: tol=1E-05" +msgstr "" + +#: of tensornetwork.backends.abstract_backend.AbstractBackend.gmres:47 +#: tensornetwork.backends.abstract_backend.AbstractBackend.gmres:51 +msgid "atol=tol" +msgstr "" + +#: of tensornetwork.backends.abstract_backend.AbstractBackend.gmres:52 +msgid "" +": Size of the Krylov space to build at each restart. Expense is cubic " +"in this parameter. It must be positive. If greater than b.size, it will" +" be set to b.size. Default: 20" +msgstr "" + +#: of tensornetwork.backends.abstract_backend.AbstractBackend.gmres:55 +msgid ": Size of the Krylov space to build at each restart." +msgstr "" + +#: of tensornetwork.backends.abstract_backend.AbstractBackend.gmres:54 +msgid "" +"Expense is cubic in this parameter. It must be positive. If greater than " +"b.size, it will be set to b.size. Default: 20" +msgstr "" + +#: of tensornetwork.backends.abstract_backend.AbstractBackend.gmres:57 +msgid "" +"The Krylov space will be repeatedly rebuilt up to this many times. Large " +"values of this argument should be used only with caution, since " +"especially for nearly symmetric matrices and small `num_krylov_vectors` " +"convergence might well freeze at a value significantly larger than `tol`." +" Default: 1." +msgstr "" + +#: of tensornetwork.backends.abstract_backend.AbstractBackend.gmres:63 +msgid "" +"Inverse of the preconditioner of A; see the docstring for " +"`scipy.sparse.linalg.gmres`. This is only supported in the numpy backend." +" Supplying this argument to other backends will trigger " +"NotImplementedError. Default: None." +msgstr "" + +#: of tensornetwork.backends.abstract_backend.AbstractBackend.gmres:69 +msgid "" +"-if `x0` is supplied but its shape differs from that of `b`. -in " +"NumPy, if the ARPACK solver reports a breakdown (which usually " +"indicates some kind of floating point issue). -if num_krylov_vectors " +"is 0 or exceeds b.size. -if tol was negative. -if M was supplied " +"with any backend but NumPy." +msgstr "" + +#: of tensornetwork.backends.abstract_backend.AbstractBackend.gmres:71 +msgid "" +"The converged solution. It has the same shape as `b`. info : 0 if " +"convergence was achieved, the number of restarts otherwise." +msgstr "" + +#: of tensorcircuit.backends.cupy_backend.CuPyBackend.grad:1 +#: tensorcircuit.backends.jax_backend.JaxBackend.grad:1 +#: tensorcircuit.backends.numpy_backend.NumpyBackend.grad:1 +#: tensorcircuit.backends.pytorch_backend.PyTorchBackend.grad:1 +#: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.grad:1 +msgid "Return the function which is the grad function of input ``f``." +msgstr "" + +#: of tensorcircuit.backends.cupy_backend.CuPyBackend.grad:13 +#: tensorcircuit.backends.cupy_backend.CuPyBackend.value_and_grad:13 +#: tensorcircuit.backends.jax_backend.JaxBackend.grad:13 +#: tensorcircuit.backends.jax_backend.JaxBackend.value_and_grad:13 +#: tensorcircuit.backends.numpy_backend.NumpyBackend.grad:13 +#: tensorcircuit.backends.numpy_backend.NumpyBackend.value_and_grad:13 +#: tensorcircuit.backends.pytorch_backend.PyTorchBackend.grad:13 +#: tensorcircuit.backends.pytorch_backend.PyTorchBackend.value_and_grad:13 +#: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.grad:13 +#: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.value_and_grad:13 +msgid "the function to be differentiated" +msgstr "" + +#: of tensorcircuit.backends.cupy_backend.CuPyBackend.grad:15 +#: tensorcircuit.backends.cupy_backend.CuPyBackend.value_and_grad:15 +#: tensorcircuit.backends.jax_backend.JaxBackend.grad:15 +#: tensorcircuit.backends.jax_backend.JaxBackend.value_and_grad:15 +#: tensorcircuit.backends.numpy_backend.NumpyBackend.grad:15 +#: tensorcircuit.backends.numpy_backend.NumpyBackend.value_and_grad:15 +#: tensorcircuit.backends.pytorch_backend.PyTorchBackend.grad:15 +#: tensorcircuit.backends.pytorch_backend.PyTorchBackend.value_and_grad:15 +#: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.grad:15 +#: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.value_and_grad:15 +msgid "" +"the position of args in ``f`` that are to be differentiated, defaults to " +"be 0" +msgstr "" + +#: of tensorcircuit.backends.cupy_backend.CuPyBackend.grad:17 +#: tensorcircuit.backends.jax_backend.JaxBackend.grad:17 +#: tensorcircuit.backends.numpy_backend.NumpyBackend.grad:17 +#: tensorcircuit.backends.pytorch_backend.PyTorchBackend.grad:17 +#: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.grad:17 +msgid "the grad function of ``f`` with the same set of arguments as ``f``" +msgstr "" + +#: of tensorcircuit.backends.cupy_backend.CuPyBackend.i:1 +#: tensorcircuit.backends.jax_backend.JaxBackend.i:1 +#: tensorcircuit.backends.numpy_backend.NumpyBackend.i:1 +#: tensorcircuit.backends.pytorch_backend.PyTorchBackend.i:1 +#: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.i:1 +msgid "Return 1.j in as a tensor compatible with the backend." +msgstr "" + +#: of tensorcircuit.backends.cupy_backend.CuPyBackend.i:3 +#: tensorcircuit.backends.jax_backend.JaxBackend.i:3 +#: tensorcircuit.backends.numpy_backend.NumpyBackend.i:3 +#: tensorcircuit.backends.pytorch_backend.PyTorchBackend.i:3 +#: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.i:3 +msgid "\"complex64\" or \"complex128\"" +msgstr "" + +#: of tensorcircuit.backends.cupy_backend.CuPyBackend.i:5 +#: tensorcircuit.backends.jax_backend.JaxBackend.i:5 +#: tensorcircuit.backends.numpy_backend.NumpyBackend.i:5 +#: tensorcircuit.backends.pytorch_backend.PyTorchBackend.i:5 +#: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.i:5 +msgid "1.j tensor" +msgstr "" + +#: of tensorcircuit.backends.cupy_backend.CuPyBackend.imag:1 +#: tensorcircuit.backends.jax_backend.JaxBackend.imag:1 +#: tensorcircuit.backends.numpy_backend.NumpyBackend.imag:1 +#: tensorcircuit.backends.pytorch_backend.PyTorchBackend.imag:1 +#: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.imag:1 +msgid "Return the elementwise imaginary value of a tensor ``a``." +msgstr "" + +#: of tensorcircuit.backends.cupy_backend.CuPyBackend.imag:5 +#: tensorcircuit.backends.jax_backend.JaxBackend.imag:5 +#: tensorcircuit.backends.numpy_backend.NumpyBackend.imag:5 +#: tensorcircuit.backends.pytorch_backend.PyTorchBackend.imag:5 +#: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.imag:5 +msgid "imaginary value of ``a``" +msgstr "" + +#: of tensorcircuit.backends.abstract_backend.ExtendedBackend.implicit_randc:1 +#: tensorcircuit.backends.abstract_backend.ExtendedBackend.stateful_randc:1 +#: tensorcircuit.backends.abstract_backend.ExtendedBackend.stateful_randn:1 +#: tensorcircuit.backends.cupy_backend.CuPyBackend.stateful_randc:1 +#: tensorcircuit.backends.cupy_backend.CuPyBackend.stateful_randn:1 +#: tensorcircuit.backends.jax_backend.JaxBackend.implicit_randc:1 +#: tensorcircuit.backends.jax_backend.JaxBackend.stateful_randc:1 +#: tensorcircuit.backends.jax_backend.JaxBackend.stateful_randn:1 +#: tensorcircuit.backends.numpy_backend.NumpyBackend.stateful_randc:1 +#: tensorcircuit.backends.numpy_backend.NumpyBackend.stateful_randn:1 +#: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.stateful_randc:1 +#: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.stateful_randn:1 +msgid "[summary]" +msgstr "" + +#: of tensorcircuit.backends.abstract_backend.ExtendedBackend.implicit_randc:5 +#: tensorcircuit.backends.abstract_backend.ExtendedBackend.stateful_randc:5 +#: tensorcircuit.backends.cupy_backend.CuPyBackend.stateful_randc:5 +#: tensorcircuit.backends.jax_backend.JaxBackend.implicit_randc:5 +#: tensorcircuit.backends.jax_backend.JaxBackend.stateful_randc:5 +#: tensorcircuit.backends.numpy_backend.NumpyBackend.stateful_randc:5 +#: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.stateful_randc:5 +msgid "The possible options" +msgstr "" + +#: of tensorcircuit.backends.abstract_backend.ExtendedBackend.implicit_randc:7 +#: tensorcircuit.backends.abstract_backend.ExtendedBackend.stateful_randc:7 +#: tensorcircuit.backends.cupy_backend.CuPyBackend.stateful_randc:7 +#: tensorcircuit.backends.jax_backend.JaxBackend.implicit_randc:7 +#: tensorcircuit.backends.jax_backend.JaxBackend.stateful_randc:7 +#: tensorcircuit.backends.numpy_backend.NumpyBackend.stateful_randc:7 +#: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.stateful_randc:7 +msgid "Sampling output shape" +msgstr "" + +#: of tensorcircuit.backends.abstract_backend.ExtendedBackend.implicit_randc:9 +#: tensorcircuit.backends.abstract_backend.ExtendedBackend.stateful_randc:9 +#: tensorcircuit.backends.cupy_backend.CuPyBackend.stateful_randc:9 +#: tensorcircuit.backends.jax_backend.JaxBackend.implicit_randc:9 +#: tensorcircuit.backends.jax_backend.JaxBackend.stateful_randc:9 +#: tensorcircuit.backends.numpy_backend.NumpyBackend.stateful_randc:9 +#: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.stateful_randc:9 +msgid "" +"probability for each option in a, defaults to None, as equal probability " +"distribution" +msgstr "" + +#: of tensorcircuit.backends.abstract_backend.ExtendedBackend.implicit_randn:1 +#: tensorcircuit.backends.abstract_backend.ExtendedBackend.implicit_randu:1 +#: tensorcircuit.backends.jax_backend.JaxBackend.implicit_randn:1 +#: tensorcircuit.backends.jax_backend.JaxBackend.implicit_randu:1 +msgid "" +"Call the random normal function with the random state management behind " +"the scene." +msgstr "" + +#: of tensorcircuit.backends.abstract_backend.ExtendedBackend.implicit_randn:3 +#: tensorcircuit.backends.abstract_backend.ExtendedBackend.implicit_randn:7 +#: tensorcircuit.backends.abstract_backend.ExtendedBackend.implicit_randu:3 +#: tensorcircuit.backends.abstract_backend.ExtendedBackend.implicit_randu:7 +#: tensorcircuit.backends.abstract_backend.ExtendedBackend.stateful_randn:11 +#: tensorcircuit.backends.abstract_backend.ExtendedBackend.stateful_randu:9 +#: tensorcircuit.backends.cupy_backend.CuPyBackend.stateful_randn:11 +#: tensorcircuit.backends.cupy_backend.CuPyBackend.stateful_randu:9 +#: tensorcircuit.backends.jax_backend.JaxBackend.implicit_randn:3 +#: tensorcircuit.backends.jax_backend.JaxBackend.implicit_randn:7 +#: tensorcircuit.backends.jax_backend.JaxBackend.implicit_randu:3 +#: tensorcircuit.backends.jax_backend.JaxBackend.implicit_randu:7 +#: tensorcircuit.backends.jax_backend.JaxBackend.stateful_randn:11 +#: tensorcircuit.backends.jax_backend.JaxBackend.stateful_randu:9 +#: tensorcircuit.backends.numpy_backend.NumpyBackend.stateful_randn:11 +#: tensorcircuit.backends.numpy_backend.NumpyBackend.stateful_randu:9 +#: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.stateful_randn:11 +#: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.stateful_randu:9 +msgid "[description], defaults to 1" +msgstr "" + +#: of tensorcircuit.backends.abstract_backend.ExtendedBackend.implicit_randn:9 +#: tensorcircuit.backends.abstract_backend.ExtendedBackend.implicit_randu:9 +#: tensorcircuit.backends.jax_backend.JaxBackend.implicit_randn:9 +#: tensorcircuit.backends.jax_backend.JaxBackend.implicit_randu:9 +msgid "[description], defaults to \"32\"" +msgstr "" + +#: of tensornetwork.backends.abstract_backend.AbstractBackend.index_update:1 +#: tensornetwork.backends.jax.jax_backend.JaxBackend.index_update:1 +#: tensornetwork.backends.numpy.numpy_backend.NumPyBackend.index_update:1 +#: tensornetwork.backends.pytorch.pytorch_backend.PyTorchBackend.index_update:1 +#: tensornetwork.backends.tensorflow.tensorflow_backend.TensorFlowBackend.index_update:1 +msgid "Update `tensor` at elements defined by `mask` with value `assignee`." +msgstr "" + +#: of tensornetwork.backends.abstract_backend.AbstractBackend.index_update:3 +#: tensornetwork.backends.jax.jax_backend.JaxBackend.index_update:3 +#: tensornetwork.backends.numpy.numpy_backend.NumPyBackend.index_update:3 +#: tensornetwork.backends.pytorch.pytorch_backend.PyTorchBackend.index_update:3 +#: tensornetwork.backends.tensorflow.tensorflow_backend.TensorFlowBackend.index_update:3 +msgid "A `Tensor` object." +msgstr "" + +#: of tensornetwork.backends.abstract_backend.AbstractBackend.index_update:4 +#: tensornetwork.backends.jax.jax_backend.JaxBackend.index_update:4 +#: tensornetwork.backends.numpy.numpy_backend.NumPyBackend.index_update:4 +#: tensornetwork.backends.pytorch.pytorch_backend.PyTorchBackend.index_update:4 +#: tensornetwork.backends.tensorflow.tensorflow_backend.TensorFlowBackend.index_update:4 +msgid "A boolean mask." +msgstr "" + +#: of tensornetwork.backends.abstract_backend.AbstractBackend.index_update:5 +#: tensornetwork.backends.jax.jax_backend.JaxBackend.index_update:5 +#: tensornetwork.backends.numpy.numpy_backend.NumPyBackend.index_update:5 +#: tensornetwork.backends.pytorch.pytorch_backend.PyTorchBackend.index_update:5 +#: tensornetwork.backends.tensorflow.tensorflow_backend.TensorFlowBackend.index_update:5 +msgid "" +"A scalar `Tensor`. The values to assigned to `tensor` at positions where " +"`mask` is `True`." +msgstr "" + +#: of tensornetwork.backends.abstract_backend.AbstractBackend.inv:1 +#: tensornetwork.backends.jax.jax_backend.JaxBackend.inv:1 +#: tensornetwork.backends.numpy.numpy_backend.NumPyBackend.inv:1 +#: tensornetwork.backends.pytorch.pytorch_backend.PyTorchBackend.inv:1 +#: tensornetwork.backends.tensorflow.tensorflow_backend.TensorFlowBackend.inv:1 +msgid "Compute the matrix inverse of `matrix`." +msgstr "" + +#: of tensornetwork.backends.abstract_backend.AbstractBackend.inv:3 +#: tensornetwork.backends.jax.jax_backend.JaxBackend.inv:3 +#: tensornetwork.backends.numpy.numpy_backend.NumPyBackend.inv:3 +#: tensornetwork.backends.pytorch.pytorch_backend.PyTorchBackend.inv:3 +#: tensornetwork.backends.tensorflow.tensorflow_backend.TensorFlowBackend.inv:3 +msgid "A matrix." +msgstr "" + +#: of tensornetwork.backends.abstract_backend.AbstractBackend.inv:5 +#: tensornetwork.backends.jax.jax_backend.JaxBackend.inv:5 +#: tensornetwork.backends.numpy.numpy_backend.NumPyBackend.inv:5 +#: tensornetwork.backends.pytorch.pytorch_backend.PyTorchBackend.inv:5 +#: tensornetwork.backends.tensorflow.tensorflow_backend.TensorFlowBackend.inv:5 +msgid "The inverse of `matrix`" +msgstr "" + +#: of tensorcircuit.backends.abstract_backend.ExtendedBackend.is_sparse:1 +#: tensorcircuit.backends.cupy_backend.CuPyBackend.is_sparse:1 +#: tensorcircuit.backends.jax_backend.JaxBackend.is_sparse:1 +#: tensorcircuit.backends.numpy_backend.NumpyBackend.is_sparse:1 +#: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.is_sparse:1 +msgid "Determine whether the type of input ``a`` is ``sparse``." +msgstr "" + +#: of tensorcircuit.backends.abstract_backend.ExtendedBackend.is_sparse:3 +#: tensorcircuit.backends.cupy_backend.CuPyBackend.is_sparse:3 +#: tensorcircuit.backends.jax_backend.JaxBackend.is_sparse:3 +#: tensorcircuit.backends.numpy_backend.NumpyBackend.is_sparse:3 +#: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.is_sparse:3 +msgid "input matrix ``a``" +msgstr "" + +#: of tensorcircuit.backends.abstract_backend.ExtendedBackend.is_sparse:5 +#: tensorcircuit.backends.cupy_backend.CuPyBackend.is_sparse:5 +#: tensorcircuit.backends.jax_backend.JaxBackend.is_sparse:5 +#: tensorcircuit.backends.numpy_backend.NumpyBackend.is_sparse:5 +#: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.is_sparse:5 +msgid "a bool indicating whether the matrix ``a`` is sparse" +msgstr "" + +#: of tensorcircuit.backends.cupy_backend.CuPyBackend.is_tensor:1 +#: tensorcircuit.backends.jax_backend.JaxBackend.is_tensor:1 +#: tensorcircuit.backends.numpy_backend.NumpyBackend.is_tensor:1 +#: tensorcircuit.backends.pytorch_backend.PyTorchBackend.is_tensor:1 +#: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.is_tensor:1 +msgid "Return a boolean on whether ``a`` is a tensor in backend package." +msgstr "" + +#: of tensorcircuit.backends.cupy_backend.CuPyBackend.is_tensor:3 +#: tensorcircuit.backends.jax_backend.JaxBackend.is_tensor:3 +#: tensorcircuit.backends.numpy_backend.NumpyBackend.is_tensor:3 +#: tensorcircuit.backends.pytorch_backend.PyTorchBackend.is_tensor:3 +#: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.is_tensor:3 +msgid "a tensor to be determined" +msgstr "" + +#: of tensorcircuit.backends.cupy_backend.CuPyBackend.is_tensor:5 +#: tensorcircuit.backends.jax_backend.JaxBackend.is_tensor:5 +#: tensorcircuit.backends.numpy_backend.NumpyBackend.is_tensor:5 +#: tensorcircuit.backends.pytorch_backend.PyTorchBackend.is_tensor:5 +#: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.is_tensor:5 +msgid "whether ``a`` is a tensor" +msgstr "" + +#: of tensornetwork.backends.abstract_backend.AbstractBackend.item:1 +#: tensornetwork.backends.jax.jax_backend.JaxBackend.item:1 +#: tensornetwork.backends.numpy.numpy_backend.NumPyBackend.item:1 +#: tensornetwork.backends.pytorch.pytorch_backend.PyTorchBackend.item:1 +#: tensornetwork.backends.tensorflow.tensorflow_backend.TensorFlowBackend.item:1 +msgid "Return the item of a 1-element tensor." +msgstr "" + +#: of tensornetwork.backends.abstract_backend.AbstractBackend.item:3 +#: tensornetwork.backends.jax.jax_backend.JaxBackend.item:3 +#: tensornetwork.backends.numpy.numpy_backend.NumPyBackend.item:3 +#: tensornetwork.backends.pytorch.pytorch_backend.PyTorchBackend.item:3 +#: tensornetwork.backends.tensorflow.tensorflow_backend.TensorFlowBackend.item:3 +msgid "A 1-element tensor" +msgstr "" + +#: of tensornetwork.backends.abstract_backend.AbstractBackend.item:5 +#: tensornetwork.backends.jax.jax_backend.JaxBackend.item:5 +#: tensornetwork.backends.numpy.numpy_backend.NumPyBackend.item:5 +#: tensornetwork.backends.pytorch.pytorch_backend.PyTorchBackend.item:5 +#: tensornetwork.backends.tensorflow.tensorflow_backend.TensorFlowBackend.item:5 +msgid "The value in tensor." +msgstr "" + +#: of tensorcircuit.backends.abstract_backend.ExtendedBackend.jacrev:1 +msgid "Compute the Jacobian of ``f`` using reverse mode AD." +msgstr "" + +#: of tensorcircuit.backends.abstract_backend.ExtendedBackend.jacrev:3 +msgid "The function whose Jacobian is required" +msgstr "" + +#: of tensorcircuit.backends.abstract_backend.ExtendedBackend.jacfwd:5 +#: tensorcircuit.backends.abstract_backend.ExtendedBackend.jacrev:5 +msgid "the position of the arg as Jacobian input, defaults to 0" +msgstr "" + +#: of tensorcircuit.backends.abstract_backend.ExtendedBackend.jacrev:7 +msgid "outer tuple for output, inner tuple for input args" +msgstr "" + +#: of tensorcircuit.backends.abstract_backend.ExtendedBackend.jacfwd:1 +msgid "Compute the Jacobian of ``f`` using the forward mode AD." +msgstr "" + +#: of tensorcircuit.backends.abstract_backend.ExtendedBackend.jacfwd:3 +msgid "the function whose Jacobian is required" +msgstr "" + +#: of tensorcircuit.backends.abstract_backend.ExtendedBackend.jacfwd:7 +msgid "outer tuple for input args, inner tuple for outputs" +msgstr "" + +#: of tensorcircuit.backends.cupy_backend.CuPyBackend.jit:1 +#: tensorcircuit.backends.jax_backend.JaxBackend.jit:1 +#: tensorcircuit.backends.numpy_backend.NumpyBackend.jit:1 +#: tensorcircuit.backends.pytorch_backend.PyTorchBackend.jit:1 +#: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.jit:1 +msgid "" +"Return a jitted or graph-compiled version of `fun` for JAX backend. For " +"all other backends returns `fun`. :param fun: Callable :param args: " +"Arguments to `fun`. :param kwargs: Keyword arguments to `fun`." +msgstr "" + +#: of tensorcircuit.backends.cupy_backend.CuPyBackend.jit:7 +#: tensorcircuit.backends.jax_backend.JaxBackend.jit:7 +#: tensorcircuit.backends.numpy_backend.NumpyBackend.jit:7 +#: tensorcircuit.backends.pytorch_backend.PyTorchBackend.jit:7 +#: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.jit:7 +msgid "jitted/graph-compiled version of `fun`, or just `fun`." +msgstr "" + +#: of tensorcircuit.backends.cupy_backend.CuPyBackend.jvp:1 +#: tensorcircuit.backends.jax_backend.JaxBackend.jvp:1 +#: tensorcircuit.backends.numpy_backend.NumpyBackend.jvp:1 +#: tensorcircuit.backends.pytorch_backend.PyTorchBackend.jvp:1 +#: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.jvp:1 +msgid "" +"Function that computes a (forward-mode) Jacobian-vector product of ``f``." +" Strictly speaking, this function is value_and_jvp." +msgstr "" + +#: of tensorcircuit.backends.cupy_backend.CuPyBackend.jvp:4 +#: tensorcircuit.backends.jax_backend.JaxBackend.jvp:4 +#: tensorcircuit.backends.numpy_backend.NumpyBackend.jvp:4 +#: tensorcircuit.backends.pytorch_backend.PyTorchBackend.jvp:4 +#: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.jvp:4 +msgid "The function to compute jvp" +msgstr "" + +#: of tensorcircuit.backends.cupy_backend.CuPyBackend.jvp:6 +#: tensorcircuit.backends.cupy_backend.CuPyBackend.vjp:7 +#: tensorcircuit.backends.jax_backend.JaxBackend.jvp:6 +#: tensorcircuit.backends.jax_backend.JaxBackend.vjp:7 +#: tensorcircuit.backends.numpy_backend.NumpyBackend.jvp:6 +#: tensorcircuit.backends.numpy_backend.NumpyBackend.vjp:7 +#: tensorcircuit.backends.pytorch_backend.PyTorchBackend.jvp:6 +#: tensorcircuit.backends.pytorch_backend.PyTorchBackend.vjp:7 +#: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.jvp:6 +#: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.vjp:7 +msgid "input for ``f``" +msgstr "" + +#: of tensorcircuit.backends.cupy_backend.CuPyBackend.jvp:8 +#: tensorcircuit.backends.jax_backend.JaxBackend.jvp:8 +#: tensorcircuit.backends.numpy_backend.NumpyBackend.jvp:8 +#: tensorcircuit.backends.pytorch_backend.PyTorchBackend.jvp:8 +#: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.jvp:8 +msgid "tangents" +msgstr "" + +#: of tensorcircuit.backends.cupy_backend.CuPyBackend.jvp:10 +#: tensorcircuit.backends.jax_backend.JaxBackend.jvp:10 +#: tensorcircuit.backends.numpy_backend.NumpyBackend.jvp:10 +#: tensorcircuit.backends.pytorch_backend.PyTorchBackend.jvp:10 +#: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.jvp:10 +msgid "" +"(``f(*inputs)``, jvp_tensor), where jvp_tensor is the same shape as the " +"output of ``f``" +msgstr "" + +#: of tensorcircuit.backends.cupy_backend.CuPyBackend.kron:1 +#: tensorcircuit.backends.jax_backend.JaxBackend.kron:1 +#: tensorcircuit.backends.numpy_backend.NumpyBackend.kron:1 +#: tensorcircuit.backends.pytorch_backend.PyTorchBackend.kron:1 +#: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.kron:1 +msgid "Return the kronecker product of two matrices ``a`` and ``b``." +msgstr "" + +#: of tensorcircuit.backends.cupy_backend.CuPyBackend.kron:7 +#: tensorcircuit.backends.jax_backend.JaxBackend.kron:7 +#: tensorcircuit.backends.numpy_backend.NumpyBackend.kron:7 +#: tensorcircuit.backends.pytorch_backend.PyTorchBackend.kron:7 +#: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.kron:7 +msgid "kronecker product of ``a`` and ``b``" +msgstr "" + +#: of tensorcircuit.backends.cupy_backend.CuPyBackend.left_shift:1 +#: tensorcircuit.backends.jax_backend.JaxBackend.left_shift:1 +#: tensorcircuit.backends.numpy_backend.NumpyBackend.left_shift:1 +#: tensorcircuit.backends.pytorch_backend.PyTorchBackend.left_shift:1 +#: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.left_shift:1 +msgid "Shift the bits of an integer x to the left y bits." +msgstr "" + +#: of tensorcircuit.backends.cupy_backend.CuPyBackend.left_shift:3 +#: tensorcircuit.backends.cupy_backend.CuPyBackend.mod:3 +#: tensorcircuit.backends.cupy_backend.CuPyBackend.right_shift:3 +#: tensorcircuit.backends.jax_backend.JaxBackend.left_shift:3 +#: tensorcircuit.backends.jax_backend.JaxBackend.mod:3 +#: tensorcircuit.backends.jax_backend.JaxBackend.right_shift:3 +#: tensorcircuit.backends.numpy_backend.NumpyBackend.left_shift:3 +#: tensorcircuit.backends.numpy_backend.NumpyBackend.mod:3 +#: tensorcircuit.backends.numpy_backend.NumpyBackend.right_shift:3 +#: tensorcircuit.backends.pytorch_backend.PyTorchBackend.left_shift:3 +#: tensorcircuit.backends.pytorch_backend.PyTorchBackend.mod:3 +#: tensorcircuit.backends.pytorch_backend.PyTorchBackend.right_shift:3 +#: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.left_shift:3 +#: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.mod:3 +#: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.right_shift:3 +msgid "input values" +msgstr "" + +#: of tensorcircuit.backends.cupy_backend.CuPyBackend.left_shift:5 +#: tensorcircuit.backends.cupy_backend.CuPyBackend.right_shift:5 +#: tensorcircuit.backends.jax_backend.JaxBackend.left_shift:5 +#: tensorcircuit.backends.jax_backend.JaxBackend.right_shift:5 +#: tensorcircuit.backends.numpy_backend.NumpyBackend.left_shift:5 +#: tensorcircuit.backends.numpy_backend.NumpyBackend.right_shift:5 +#: tensorcircuit.backends.pytorch_backend.PyTorchBackend.left_shift:5 +#: tensorcircuit.backends.pytorch_backend.PyTorchBackend.right_shift:5 +#: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.left_shift:5 +#: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.right_shift:5 +msgid "Number of bits shift to ``x``" +msgstr "" + +#: of tensorcircuit.backends.cupy_backend.CuPyBackend.left_shift:7 +#: tensorcircuit.backends.cupy_backend.CuPyBackend.right_shift:7 +#: tensorcircuit.backends.jax_backend.JaxBackend.left_shift:7 +#: tensorcircuit.backends.jax_backend.JaxBackend.right_shift:7 +#: tensorcircuit.backends.numpy_backend.NumpyBackend.left_shift:7 +#: tensorcircuit.backends.numpy_backend.NumpyBackend.right_shift:7 +#: tensorcircuit.backends.pytorch_backend.PyTorchBackend.left_shift:7 +#: tensorcircuit.backends.pytorch_backend.PyTorchBackend.right_shift:7 +#: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.left_shift:7 +#: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.right_shift:7 +msgid "result with the same shape as ``x``" +msgstr "" + +#: of tensornetwork.backends.abstract_backend.AbstractBackend.log:1 +#: tensornetwork.backends.jax.jax_backend.JaxBackend.log:1 +#: tensornetwork.backends.numpy.numpy_backend.NumPyBackend.log:1 +#: tensornetwork.backends.tensorflow.tensorflow_backend.TensorFlowBackend.log:1 +msgid "Return elementwise natural logarithm of `tensor`. :param tensor: A tensor." +msgstr "" + +#: of tensorcircuit.backends.tensorflow_backend._matmul_tf:1 +#: tensornetwork.backends.abstract_backend.AbstractBackend.matmul:1 +#: tensornetwork.backends.jax.jax_backend.JaxBackend.matmul:1 +#: tensornetwork.backends.numpy.numpy_backend.NumPyBackend.matmul:1 +#: tensornetwork.backends.pytorch.pytorch_backend.PyTorchBackend.matmul:1 +msgid "" +"Perform a possibly batched matrix-matrix multiplication between `tensor1`" +" and `tensor2`. The following behaviour is similar to `numpy.matmul`: - " +"If both arguments are 2-D they are multiplied like conventional" +msgstr "" + +#: of tensorcircuit.backends.tensorflow_backend._matmul_tf:5 +#: tensornetwork.backends.abstract_backend.AbstractBackend.matmul:5 +#: tensornetwork.backends.jax.jax_backend.JaxBackend.matmul:5 +#: tensornetwork.backends.numpy.numpy_backend.NumPyBackend.matmul:5 +#: tensornetwork.backends.pytorch.pytorch_backend.PyTorchBackend.matmul:5 +msgid "matrices." +msgstr "" + +#: of tensorcircuit.backends.tensorflow_backend._matmul_tf:6 +#: tensornetwork.backends.abstract_backend.AbstractBackend.matmul:6 +#: tensornetwork.backends.jax.jax_backend.JaxBackend.matmul:6 +#: tensornetwork.backends.numpy.numpy_backend.NumPyBackend.matmul:6 +#: tensornetwork.backends.pytorch.pytorch_backend.PyTorchBackend.matmul:6 +msgid "" +"If either argument is N-D, N > 2, it is treated as a stack of matrices " +"residing in the last two indexes and broadcast accordingly." +msgstr "" + +#: of tensorcircuit.backends.tensorflow_backend._matmul_tf:8 +#: tensornetwork.backends.abstract_backend.AbstractBackend.matmul:8 +#: tensornetwork.backends.jax.jax_backend.JaxBackend.matmul:8 +#: tensornetwork.backends.numpy.numpy_backend.NumPyBackend.matmul:8 +#: tensornetwork.backends.pytorch.pytorch_backend.PyTorchBackend.matmul:8 +msgid "" +"Both arguments to `matmul` have to be tensors of order >= 2. :param " +"tensor1: An input tensor. :param tensor2: An input tensor." +msgstr "" + +#: of tensorcircuit.backends.tensorflow_backend._matmul_tf:12 +#: tensornetwork.backends.abstract_backend.AbstractBackend.matmul:12 +#: tensornetwork.backends.jax.jax_backend.JaxBackend.matmul:12 +#: tensornetwork.backends.numpy.numpy_backend.NumPyBackend.matmul:12 +#: tensornetwork.backends.pytorch.pytorch_backend.PyTorchBackend.matmul:12 +msgid "The result of performing the matmul." +msgstr "" + +#: of tensorcircuit.backends.cupy_backend.CuPyBackend.max:1 +#: tensorcircuit.backends.jax_backend.JaxBackend.max:1 +#: tensorcircuit.backends.numpy_backend.NumpyBackend.max:1 +#: tensorcircuit.backends.pytorch_backend.PyTorchBackend.max:1 +#: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.max:1 +msgid "Return the maximum of an array or maximum along an axis." +msgstr "" + +#: of tensorcircuit.backends.cupy_backend.CuPyBackend.max:5 +#: tensorcircuit.backends.cupy_backend.CuPyBackend.min:5 +#: tensorcircuit.backends.jax_backend.JaxBackend.max:5 +#: tensorcircuit.backends.jax_backend.JaxBackend.min:5 +#: tensorcircuit.backends.numpy_backend.NumpyBackend.max:5 +#: tensorcircuit.backends.numpy_backend.NumpyBackend.min:5 +#: tensorcircuit.backends.pytorch_backend.PyTorchBackend.max:5 +#: tensorcircuit.backends.pytorch_backend.PyTorchBackend.min:5 +#: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.max:5 +#: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.min:5 +#: tensorcircuit.keras.QuantumLayer.__init__:11 +msgid "[description], defaults to None" +msgstr "" + +#: of tensorcircuit.backends.cupy_backend.CuPyBackend.mean:1 +#: tensorcircuit.backends.jax_backend.JaxBackend.mean:1 +#: tensorcircuit.backends.numpy_backend.NumpyBackend.mean:1 +#: tensorcircuit.backends.pytorch_backend.PyTorchBackend.mean:1 +#: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.mean:1 +msgid "Compute the arithmetic mean for ``a`` along the specified ``axis``." +msgstr "" + +#: of tensorcircuit.backends.cupy_backend.CuPyBackend.mean:3 +#: tensorcircuit.backends.jax_backend.JaxBackend.mean:3 +#: tensorcircuit.backends.numpy_backend.NumpyBackend.mean:3 +#: tensorcircuit.backends.pytorch_backend.PyTorchBackend.mean:3 +#: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.mean:3 +msgid "tensor to take average" +msgstr "" + +#: of tensorcircuit.backends.cupy_backend.CuPyBackend.mean:5 +#: tensorcircuit.backends.jax_backend.JaxBackend.mean:5 +#: tensorcircuit.backends.numpy_backend.NumpyBackend.mean:5 +#: tensorcircuit.backends.pytorch_backend.PyTorchBackend.mean:5 +#: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.mean:5 +msgid "the axis to take mean, defaults to None indicating sum over flatten array" +msgstr "" + +#: of tensorcircuit.backends.cupy_backend.CuPyBackend.mean:7 +#: tensorcircuit.backends.jax_backend.JaxBackend.mean:7 +#: tensorcircuit.backends.numpy_backend.NumpyBackend.mean:7 +#: tensorcircuit.backends.pytorch_backend.PyTorchBackend.mean:7 +#: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.mean:7 +#: tensorcircuit.interfaces.torch.torch_interface_kws:26 +msgid "_description_, defaults to False" +msgstr "" + +#: of tensorcircuit.backends.cupy_backend.CuPyBackend.min:1 +#: tensorcircuit.backends.jax_backend.JaxBackend.min:1 +#: tensorcircuit.backends.numpy_backend.NumpyBackend.min:1 +#: tensorcircuit.backends.pytorch_backend.PyTorchBackend.min:1 +#: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.min:1 +msgid "Return the minimum of an array or minimum along an axis." +msgstr "" + +#: of tensorcircuit.backends.cupy_backend.CuPyBackend.mod:1 +#: tensorcircuit.backends.jax_backend.JaxBackend.mod:1 +#: tensorcircuit.backends.numpy_backend.NumpyBackend.mod:1 +#: tensorcircuit.backends.pytorch_backend.PyTorchBackend.mod:1 +#: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.mod:1 +msgid "" +"Compute y-mod of x (negative number behavior is not guaranteed to be " +"consistent)" +msgstr "" + +#: of tensorcircuit.backends.cupy_backend.CuPyBackend.mod:5 +#: tensorcircuit.backends.jax_backend.JaxBackend.mod:5 +#: tensorcircuit.backends.numpy_backend.NumpyBackend.mod:5 +#: tensorcircuit.backends.pytorch_backend.PyTorchBackend.mod:5 +#: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.mod:5 +msgid "mod ``y``" +msgstr "" + +#: of tensorcircuit.backends.cupy_backend.CuPyBackend.mod:7 +#: tensorcircuit.backends.jax_backend.JaxBackend.mod:7 +#: tensorcircuit.backends.numpy_backend.NumpyBackend.mod:7 +#: tensorcircuit.backends.pytorch_backend.PyTorchBackend.mod:7 +#: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.mod:7 +msgid "results" +msgstr "" + +#: of tensorcircuit.backends.cupy_backend.CuPyBackend.multiply:1 +#: tensornetwork.backends.jax.jax_backend.JaxBackend.multiply:1 +#: tensornetwork.backends.numpy.numpy_backend.NumPyBackend.multiply:1 +#: tensornetwork.backends.pytorch.pytorch_backend.PyTorchBackend.multiply:1 +#: tensornetwork.backends.tensorflow.tensorflow_backend.TensorFlowBackend.multiply:1 +msgid "Return the default multiplication of `tensor`." +msgstr "" + +#: of tensorcircuit.backends.cupy_backend.CuPyBackend.multiply:3 +#: tensornetwork.backends.jax.jax_backend.JaxBackend.multiply:3 +#: tensornetwork.backends.numpy.numpy_backend.NumPyBackend.multiply:3 +#: tensornetwork.backends.pytorch.pytorch_backend.PyTorchBackend.multiply:3 +#: tensornetwork.backends.tensorflow.tensorflow_backend.TensorFlowBackend.multiply:3 +msgid "" +"A backend can override such implementation. :param tensor1: A tensor. " +":param tensor2: A tensor." +msgstr "" + +#: of tensorcircuit.backends.cupy_backend.CuPyBackend.norm:1 +#: tensornetwork.backends.jax.jax_backend.JaxBackend.norm:1 +#: tensornetwork.backends.numpy.numpy_backend.NumPyBackend.norm:1 +#: tensornetwork.backends.pytorch.pytorch_backend.PyTorchBackend.norm:1 +#: tensornetwork.backends.tensorflow.tensorflow_backend.TensorFlowBackend.norm:1 +msgid "Calculate the L2-norm of the elements of `tensor`" +msgstr "" + +#: of tensorcircuit.backends.cupy_backend.CuPyBackend.numpy:1 +#: tensorcircuit.backends.jax_backend.JaxBackend.numpy:1 +#: tensorcircuit.backends.numpy_backend.NumpyBackend.numpy:1 +#: tensorcircuit.backends.pytorch_backend.PyTorchBackend.numpy:1 +#: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.numpy:1 +msgid "" +"Return the numpy array of a tensor ``a``, but may not work in a jitted " +"function." +msgstr "" + +#: of tensorcircuit.backends.cupy_backend.CuPyBackend.numpy:5 +#: tensorcircuit.backends.jax_backend.JaxBackend.numpy:5 +#: tensorcircuit.backends.numpy_backend.NumpyBackend.numpy:5 +#: tensorcircuit.backends.pytorch_backend.PyTorchBackend.numpy:5 +#: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.numpy:5 +msgid "numpy array of ``a``" +msgstr "" + +#: of tensorcircuit.backends.abstract_backend.ExtendedBackend.one_hot:1 +msgid "See doc for :py:meth:`onehot`" +msgstr "" + +#: of tensorcircuit.backends.cupy_backend.CuPyBackend.onehot:1 +#: tensorcircuit.backends.jax_backend.JaxBackend.onehot:1 +#: tensorcircuit.backends.numpy_backend.NumpyBackend.onehot:1 +#: tensorcircuit.backends.pytorch_backend.PyTorchBackend.onehot:1 +#: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.onehot:1 +msgid "" +"One-hot encodes the given ``a``. Each index in the input ``a`` is encoded" +" as a vector of zeros of length ``num`` with the element at index set to " +"one:" +msgstr "" + +#: of tensorcircuit.backends.cupy_backend.CuPyBackend.onehot:5 +#: tensorcircuit.backends.jax_backend.JaxBackend.onehot:5 +#: tensorcircuit.backends.numpy_backend.NumpyBackend.onehot:5 +#: tensorcircuit.backends.pytorch_backend.PyTorchBackend.onehot:5 +#: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.onehot:5 +msgid "input tensor" +msgstr "" + +#: of tensorcircuit.backends.cupy_backend.CuPyBackend.onehot:7 +#: tensorcircuit.backends.jax_backend.JaxBackend.onehot:7 +#: tensorcircuit.backends.numpy_backend.NumpyBackend.onehot:7 +#: tensorcircuit.backends.pytorch_backend.PyTorchBackend.onehot:7 +#: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.onehot:7 +msgid "number of features in onehot dimension" +msgstr "" + +#: of tensorcircuit.backends.cupy_backend.CuPyBackend.onehot:9 +#: tensorcircuit.backends.jax_backend.JaxBackend.onehot:9 +#: tensorcircuit.backends.numpy_backend.NumpyBackend.onehot:9 +#: tensorcircuit.backends.pytorch_backend.PyTorchBackend.onehot:9 +#: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.onehot:9 +msgid "onehot tensor with the last extra dimension" +msgstr "" + +#: of tensorcircuit.backends.cupy_backend.CuPyBackend.ones:1 +#: tensorcircuit.backends.jax_backend.JaxBackend.ones:1 +#: tensorcircuit.backends.numpy_backend.NumpyBackend.ones:1 +#: tensorcircuit.backends.pytorch_backend.PyTorchBackend.ones:1 +#: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.ones:1 +msgid "" +"Return an ones-matrix of dimension `dim` Depending on specific backends, " +"`dim` has to be either an int (numpy, torch, tensorflow) or a `ShapeType`" +" object (for block-sparse backends). Block-sparse behavior is currently " +"not supported :param shape: The dimension of the returned matrix. :type " +"shape: int :param dtype: The dtype of the returned matrix." +msgstr "" + +#: of tensorcircuit.backends.cupy_backend.CuPyBackend.outer_product:1 +#: tensorcircuit.backends.tensorflow_backend._outer_product_tf:1 +#: tensornetwork.backends.jax.jax_backend.JaxBackend.outer_product:1 +#: tensornetwork.backends.numpy.numpy_backend.NumPyBackend.outer_product:1 +#: tensornetwork.backends.pytorch.pytorch_backend.PyTorchBackend.outer_product:1 +msgid "Calculate the outer product of the two given tensors." +msgstr "" + +#: of tensornetwork.backends.abstract_backend.AbstractBackend.pivot:1 +msgid "" +"Reshapes a tensor into a matrix, whose columns (rows) are the vectorized " +"dimensions to the left (right) of pivot_axis." +msgstr "" + +#: of tensornetwork.backends.abstract_backend.AbstractBackend.pivot:4 +msgid "" +"In other words, with tensor.shape = (1, 2, 4, 5) and pivot_axis=2, this " +"function returns an (8, 5) matrix." +msgstr "" + +#: of tensornetwork.backends.abstract_backend.AbstractBackend.pivot:7 +msgid "The tensor to pivot." +msgstr "" + +#: of tensornetwork.backends.abstract_backend.AbstractBackend.pivot:8 +msgid "The axis about which to pivot." +msgstr "" + +#: of tensornetwork.backends.abstract_backend.AbstractBackend.pivot:10 +msgid "The pivoted tensor." +msgstr "" + +#: of tensornetwork.backends.abstract_backend.AbstractBackend.power:7 +#: tensornetwork.backends.numpy.numpy_backend.NumPyBackend.power:7 +#: tensornetwork.backends.tensorflow.tensorflow_backend.TensorFlowBackend.power:7 +msgid "Returns the exponentiation of tensor a raised to b." +msgstr "" + +#: of tensornetwork.backends.abstract_backend.AbstractBackend.power:4 +#: tensornetwork.backends.numpy.numpy_backend.NumPyBackend.power:4 +#: tensornetwork.backends.tensorflow.tensorflow_backend.TensorFlowBackend.power:4 +msgid "If b is a tensor, then the exponentiation is element-wise" +msgstr "" + +#: of tensornetwork.backends.abstract_backend.AbstractBackend.power:3 +#: tensornetwork.backends.numpy.numpy_backend.NumPyBackend.power:3 +#: tensornetwork.backends.tensorflow.tensorflow_backend.TensorFlowBackend.power:3 +msgid "" +"between the two tensors, with a as the base and b as the power. Note that" +" a and b must be broadcastable to the same shape if b is a tensor." +msgstr "" + +#: of tensornetwork.backends.abstract_backend.AbstractBackend.power:7 +#: tensornetwork.backends.numpy.numpy_backend.NumPyBackend.power:7 +#: tensornetwork.backends.tensorflow.tensorflow_backend.TensorFlowBackend.power:7 +msgid "If b is a scalar, then the exponentiation is each value in a" +msgstr "" + +#: of tensornetwork.backends.abstract_backend.AbstractBackend.power:7 +#: tensornetwork.backends.numpy.numpy_backend.NumPyBackend.power:7 +#: tensornetwork.backends.tensorflow.tensorflow_backend.TensorFlowBackend.power:7 +msgid "raised to the power of b." +msgstr "" + +#: of tensornetwork.backends.abstract_backend.AbstractBackend.power:9 +#: tensornetwork.backends.numpy.numpy_backend.NumPyBackend.power:9 +#: tensornetwork.backends.tensorflow.tensorflow_backend.TensorFlowBackend.power:9 +msgid "The tensor containing the bases." +msgstr "" + +#: of tensornetwork.backends.abstract_backend.AbstractBackend.power:10 +#: tensornetwork.backends.numpy.numpy_backend.NumPyBackend.power:10 +#: tensornetwork.backends.tensorflow.tensorflow_backend.TensorFlowBackend.power:10 +msgid "The tensor containing the powers; or a single scalar as the power." +msgstr "" + +#: of tensornetwork.backends.abstract_backend.AbstractBackend.power:12 +#: tensornetwork.backends.numpy.numpy_backend.NumPyBackend.power:12 +#: tensornetwork.backends.tensorflow.tensorflow_backend.TensorFlowBackend.power:12 +msgid "" +"The tensor that is each element of a raised to the power of b. Note " +"that the shape of the returned tensor is that produced by the broadcast" +" of a and b." +msgstr "" + +#: of tensornetwork.backends.abstract_backend.AbstractBackend.power:15 +#: tensornetwork.backends.numpy.numpy_backend.NumPyBackend.power:15 +#: tensornetwork.backends.tensorflow.tensorflow_backend.TensorFlowBackend.power:15 +msgid "The tensor that is each element of a raised to the" +msgstr "" + +#: of tensornetwork.backends.abstract_backend.AbstractBackend.power:15 +#: tensornetwork.backends.numpy.numpy_backend.NumPyBackend.power:15 +#: tensornetwork.backends.tensorflow.tensorflow_backend.TensorFlowBackend.power:15 +msgid "" +"power of b. Note that the shape of the returned tensor is that produced " +"by the broadcast of a and b." +msgstr "" + +#: of +#: tensorcircuit.backends.abstract_backend.ExtendedBackend.probability_sample:1 +msgid "" +"Drawn ``shots`` samples from probability distribution p, given the " +"external randomness determined by uniform distributed ``status`` tensor " +"or backend random generator ``g``. This method is similar with " +"``stateful_randc``, but it supports ``status`` beyond ``g``, which is " +"convenient when jit or vmap" +msgstr "" + +#: of +#: tensorcircuit.backends.abstract_backend.ExtendedBackend.probability_sample:6 +msgid "Number of samples to draw with replacement" +msgstr "" + +#: of +#: tensorcircuit.backends.abstract_backend.ExtendedBackend.probability_sample:8 +msgid "prbability vector" +msgstr "" + +#: of +#: tensorcircuit.backends.abstract_backend.ExtendedBackend.probability_sample:10 +msgid "" +"external randomness as a tensor with each element drawn uniformly from " +"[0, 1], defaults to None" +msgstr "" + +#: of +#: tensorcircuit.backends.abstract_backend.ExtendedBackend.probability_sample:13 +msgid "backend random genrator, defaults to None" +msgstr "" + +#: of +#: tensorcircuit.backends.abstract_backend.ExtendedBackend.probability_sample:15 +msgid "The drawn sample as an int tensor" +msgstr "" + +#: of tensornetwork.backends.abstract_backend.AbstractBackend.qr:1 +#: tensornetwork.backends.numpy.numpy_backend.NumPyBackend.qr:1 +msgid "Computes the QR decomposition of a tensor." +msgstr "" + +#: of tensornetwork.backends.abstract_backend.AbstractBackend.randn:1 +#: tensornetwork.backends.jax.jax_backend.JaxBackend.randn:1 +#: tensornetwork.backends.numpy.numpy_backend.NumPyBackend.randn:1 +#: tensornetwork.backends.pytorch.pytorch_backend.PyTorchBackend.randn:1 +#: tensornetwork.backends.tensorflow.tensorflow_backend.TensorFlowBackend.randn:1 +msgid "" +"Return a random-normal-matrix of dimension `dim` Depending on specific " +"backends, `dim` has to be either an int (numpy, torch, tensorflow) or a " +"`ShapeType` object (for block-sparse backends)." +msgstr "" + +#: of tensornetwork.backends.abstract_backend.AbstractBackend.randn:5 +#: tensornetwork.backends.jax.jax_backend.JaxBackend.randn:5 +#: tensornetwork.backends.numpy.numpy_backend.NumPyBackend.randn:5 +#: tensornetwork.backends.pytorch.pytorch_backend.PyTorchBackend.randn:5 +#: tensornetwork.backends.tensorflow.tensorflow_backend.TensorFlowBackend.randn:5 +msgid "" +"Block-sparse behavior is currently not supported :param shape: The " +"dimension of the returned matrix. :type shape: int :param dtype: The " +"dtype of the returned matrix. :param seed: The seed for the random number" +" generator" +msgstr "" + +#: of tensorcircuit.backends.abstract_backend.ExtendedBackend.random_split:1 +#: tensorcircuit.backends.jax_backend.JaxBackend.random_split:1 +msgid "" +"A jax like split API, but it doesn't split the key generator for other " +"backends. It is just for a consistent interface of random code; make sure" +" you know what the function actually does. This function is mainly a " +"utility to write backend agnostic code instead of doing magic things." +msgstr "" + +#: of tensornetwork.backends.abstract_backend.AbstractBackend.random_uniform:1 +#: tensornetwork.backends.jax.jax_backend.JaxBackend.random_uniform:1 +#: tensornetwork.backends.numpy.numpy_backend.NumPyBackend.random_uniform:1 +#: tensornetwork.backends.pytorch.pytorch_backend.PyTorchBackend.random_uniform:1 +#: tensornetwork.backends.tensorflow.tensorflow_backend.TensorFlowBackend.random_uniform:1 +msgid "Return a random uniform matrix of dimension `dim`." +msgstr "" + +#: of tensornetwork.backends.abstract_backend.AbstractBackend.random_uniform:3 +#: tensornetwork.backends.jax.jax_backend.JaxBackend.random_uniform:3 +#: tensornetwork.backends.numpy.numpy_backend.NumPyBackend.random_uniform:3 +#: tensornetwork.backends.pytorch.pytorch_backend.PyTorchBackend.random_uniform:3 +#: tensornetwork.backends.tensorflow.tensorflow_backend.TensorFlowBackend.random_uniform:3 +msgid "" +"Depending on specific backends, `dim` has to be either an int (numpy, " +"torch, tensorflow) or a `ShapeType` object (for block-sparse backends). " +"Block-sparse behavior is currently not supported :param shape: The " +"dimension of the returned matrix. :type shape: int :param boundaries: The" +" boundaries of the uniform distribution. :type boundaries: tuple :param " +"dtype: The dtype of the returned matrix. :param seed: The seed for the " +"random number generator" +msgstr "" + +#: of tensornetwork.backends.abstract_backend.AbstractBackend.random_uniform:14 +#: tensornetwork.backends.jax.jax_backend.JaxBackend.random_uniform:14 +#: tensornetwork.backends.numpy.numpy_backend.NumPyBackend.random_uniform:14 +#: tensornetwork.backends.pytorch.pytorch_backend.PyTorchBackend.random_uniform:14 +#: tensornetwork.backends.tensorflow.tensorflow_backend.TensorFlowBackend.random_uniform:14 +msgid "random uniform initialized tensor." +msgstr "" + +#: of tensorcircuit.backends.cupy_backend.CuPyBackend.real:1 +#: tensorcircuit.backends.jax_backend.JaxBackend.real:1 +#: tensorcircuit.backends.numpy_backend.NumpyBackend.real:1 +#: tensorcircuit.backends.pytorch_backend.PyTorchBackend.real:1 +#: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.real:1 +msgid "Return the elementwise real value of a tensor ``a``." +msgstr "" + +#: of tensorcircuit.backends.cupy_backend.CuPyBackend.real:5 +#: tensorcircuit.backends.jax_backend.JaxBackend.real:5 +#: tensorcircuit.backends.numpy_backend.NumpyBackend.real:5 +#: tensorcircuit.backends.pytorch_backend.PyTorchBackend.real:5 +#: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.real:5 +msgid "real value of ``a``" +msgstr "" + +#: of tensorcircuit.backends.cupy_backend.CuPyBackend.relu:1 +#: tensorcircuit.backends.jax_backend.JaxBackend.relu:1 +#: tensorcircuit.backends.numpy_backend.NumpyBackend.relu:1 +#: tensorcircuit.backends.pytorch_backend.PyTorchBackend.relu:1 +#: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.relu:1 +msgid "" +"Rectified linear unit activation function. Computes the element-wise " +"function:" +msgstr "" + +#: of tensorcircuit.backends.cupy_backend.CuPyBackend.relu:4 +#: tensorcircuit.backends.jax_backend.JaxBackend.relu:4 +#: tensorcircuit.backends.numpy_backend.NumpyBackend.relu:4 +#: tensorcircuit.backends.pytorch_backend.PyTorchBackend.relu:4 +#: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.relu:4 +msgid "\\mathrm{relu}(x)=\\max(x,0)" +msgstr "" + +#: of tensorcircuit.backends.cupy_backend.CuPyBackend.relu:11 +#: tensorcircuit.backends.jax_backend.JaxBackend.relu:11 +#: tensorcircuit.backends.numpy_backend.NumpyBackend.relu:11 +#: tensorcircuit.backends.pytorch_backend.PyTorchBackend.relu:11 +#: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.relu:11 +msgid "Tensor after relu" +msgstr "" + +#: of tensorcircuit.backends.cupy_backend.CuPyBackend.reshape:1 +#: tensornetwork.backends.jax.jax_backend.JaxBackend.reshape:1 +#: tensornetwork.backends.numpy.numpy_backend.NumPyBackend.reshape:1 +#: tensornetwork.backends.pytorch.pytorch_backend.PyTorchBackend.reshape:1 +#: tensornetwork.backends.tensorflow.tensorflow_backend.TensorFlowBackend.reshape:1 +msgid "Reshape tensor to the given shape." +msgstr "" + +#: of tensorcircuit.backends.cupy_backend.CuPyBackend.reshape:5 +#: tensornetwork.backends.jax.jax_backend.JaxBackend.reshape:5 +#: tensornetwork.backends.numpy.numpy_backend.NumPyBackend.reshape:5 +#: tensornetwork.backends.pytorch.pytorch_backend.PyTorchBackend.reshape:5 +#: tensornetwork.backends.tensorflow.tensorflow_backend.TensorFlowBackend.reshape:5 +msgid "The reshaped tensor." +msgstr "" + +#: of tensorcircuit.backends.abstract_backend.ExtendedBackend.reshape2:1 +msgid "Reshape a tensor to the [2, 2, ...] shape." +msgstr "" + +#: of tensorcircuit.backends.abstract_backend.ExtendedBackend.reshape2:5 +#: tensorcircuit.backends.abstract_backend.ExtendedBackend.reshapem:5 +msgid "the reshaped tensor" +msgstr "" + +#: of tensorcircuit.backends.abstract_backend.ExtendedBackend.reshapem:1 +msgid "Reshape a tensor to the [l, l] shape." +msgstr "" + +#: of tensorcircuit.backends.abstract_backend.ExtendedBackend.reverse:1 +#: tensorcircuit.backends.pytorch_backend.PyTorchBackend.reverse:1 +msgid "return ``a[::-1]``, only 1D tensor is guaranteed for consistent behavior" +msgstr "" + +#: of tensorcircuit.backends.abstract_backend.ExtendedBackend.reverse:3 +#: tensorcircuit.backends.pytorch_backend.PyTorchBackend.reverse:3 +msgid "1D tensor" +msgstr "" + +#: of tensorcircuit.backends.abstract_backend.ExtendedBackend.reverse:5 +#: tensorcircuit.backends.pytorch_backend.PyTorchBackend.reverse:5 +msgid "1D tensor in reverse order" +msgstr "" + +#: of tensorcircuit.backends.cupy_backend.CuPyBackend.right_shift:1 +#: tensorcircuit.backends.jax_backend.JaxBackend.right_shift:1 +#: tensorcircuit.backends.numpy_backend.NumpyBackend.right_shift:1 +#: tensorcircuit.backends.pytorch_backend.PyTorchBackend.right_shift:1 +#: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.right_shift:1 +msgid "Shift the bits of an integer x to the right y bits." +msgstr "" + +#: of tensornetwork.backends.abstract_backend.AbstractBackend.rq:1 +#: tensornetwork.backends.numpy.numpy_backend.NumPyBackend.rq:1 +msgid "Computes the RQ (reversed QR) decomposition of a tensor." +msgstr "" + +#: of tensorcircuit.backends.abstract_backend.ExtendedBackend.scan:1 +#: tensorcircuit.backends.jax_backend.JaxBackend.scan:1 +#: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.scan:1 +msgid "This API follows ``tf.scan`` covention, i.e. no ys supported as jax" +msgstr "" + +#: of tensorcircuit.backends.abstract_backend.ExtendedBackend.scatter:1 +#: tensorcircuit.backends.cupy_backend.CuPyBackend.scatter:1 +#: tensorcircuit.backends.jax_backend.JaxBackend.scatter:1 +#: tensorcircuit.backends.numpy_backend.NumpyBackend.scatter:1 +#: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.scatter:1 +msgid "" +"Roughly equivalent to operand[indices] = updates, indices only support " +"shape with rank 2 for now." +msgstr "" + +#: of tensorcircuit.backends.cupy_backend.CuPyBackend.searchsorted:1 +#: tensorcircuit.backends.jax_backend.JaxBackend.searchsorted:1 +#: tensorcircuit.backends.numpy_backend.NumpyBackend.searchsorted:1 +#: tensorcircuit.backends.pytorch_backend.PyTorchBackend.searchsorted:1 +#: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.searchsorted:1 +msgid "Find indices where elements should be inserted to maintain order." +msgstr "" + +#: of tensorcircuit.backends.cupy_backend.CuPyBackend.searchsorted:3 +#: tensorcircuit.backends.jax_backend.JaxBackend.searchsorted:3 +#: tensorcircuit.backends.numpy_backend.NumpyBackend.searchsorted:3 +#: tensorcircuit.backends.pytorch_backend.PyTorchBackend.searchsorted:3 +#: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.searchsorted:3 +msgid "input array sorted in ascending order" +msgstr "" + +#: of tensorcircuit.backends.cupy_backend.CuPyBackend.searchsorted:5 +#: tensorcircuit.backends.jax_backend.JaxBackend.searchsorted:5 +#: tensorcircuit.backends.numpy_backend.NumpyBackend.searchsorted:5 +#: tensorcircuit.backends.pytorch_backend.PyTorchBackend.searchsorted:5 +#: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.searchsorted:5 +msgid "value to inserted" +msgstr "" + +#: of tensorcircuit.backends.cupy_backend.CuPyBackend.searchsorted:7 +#: tensorcircuit.backends.jax_backend.JaxBackend.searchsorted:7 +#: tensorcircuit.backends.numpy_backend.NumpyBackend.searchsorted:7 +#: tensorcircuit.backends.pytorch_backend.PyTorchBackend.searchsorted:7 +#: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.searchsorted:7 +msgid "" +"If ‘left’, the index of the first suitable location found is given. If " +"‘right’, return the last such index. If there is no suitable index, " +"return either 0 or N (where N is the length of a), defaults to \"left\"" +msgstr "" + +#: of tensorcircuit.backends.cupy_backend.CuPyBackend.searchsorted:12 +#: tensorcircuit.backends.jax_backend.JaxBackend.searchsorted:12 +#: tensorcircuit.backends.numpy_backend.NumpyBackend.searchsorted:12 +#: tensorcircuit.backends.pytorch_backend.PyTorchBackend.searchsorted:12 +#: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.searchsorted:12 +msgid "" +"Array of insertion points with the same shape as v, or an integer if v is" +" a scalar." +msgstr "" + +#: of +#: tensornetwork.backends.abstract_backend.AbstractBackend.serialize_tensor:1 +#: tensornetwork.backends.numpy.numpy_backend.NumPyBackend.serialize_tensor:1 +msgid "Return a string that serializes the given tensor." +msgstr "" + +#: of tensorcircuit.backends.cupy_backend.CuPyBackend.sign:4 +#: tensornetwork.backends.abstract_backend.AbstractBackend.serialize_tensor:3 +#: tensornetwork.backends.jax.jax_backend.JaxBackend.sign:7 +#: tensornetwork.backends.numpy.numpy_backend.NumPyBackend.serialize_tensor:3 +#: tensornetwork.backends.numpy.numpy_backend.NumPyBackend.sign:7 +#: tensornetwork.backends.pytorch.pytorch_backend.PyTorchBackend.sign:7 +#: tensornetwork.backends.tensorflow.tensorflow_backend.TensorFlowBackend.sign:7 +msgid "The input tensor." +msgstr "" + +#: of +#: tensornetwork.backends.abstract_backend.AbstractBackend.serialize_tensor:5 +#: tensornetwork.backends.numpy.numpy_backend.NumPyBackend.serialize_tensor:5 +msgid "A string representing the serialized tensor." +msgstr "" + +#: of +#: tensorcircuit.backends.abstract_backend.ExtendedBackend.set_random_state:1 +#: tensorcircuit.backends.cupy_backend.CuPyBackend.set_random_state:1 +#: tensorcircuit.backends.jax_backend.JaxBackend.set_random_state:1 +#: tensorcircuit.backends.numpy_backend.NumpyBackend.set_random_state:1 +#: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.set_random_state:1 +msgid "Set the random state attached to the backend." +msgstr "" + +#: of +#: tensorcircuit.backends.abstract_backend.ExtendedBackend.set_random_state:3 +#: tensorcircuit.backends.cupy_backend.CuPyBackend.set_random_state:3 +#: tensorcircuit.backends.jax_backend.JaxBackend.set_random_state:3 +#: tensorcircuit.backends.numpy_backend.NumpyBackend.set_random_state:3 +#: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.set_random_state:3 +msgid "the random seed, defaults to be None" +msgstr "" + +#: of +#: tensorcircuit.backends.abstract_backend.ExtendedBackend.set_random_state:5 +#: tensorcircuit.backends.cupy_backend.CuPyBackend.set_random_state:5 +#: tensorcircuit.backends.jax_backend.JaxBackend.set_random_state:5 +#: tensorcircuit.backends.numpy_backend.NumpyBackend.set_random_state:5 +#: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.set_random_state:5 +msgid "" +"If set to be true, only get the random state in return instead of setting" +" the state on the backend" +msgstr "" + +#: of tensornetwork.backends.abstract_backend.AbstractBackend.shape_concat:1 +#: tensornetwork.backends.jax.jax_backend.JaxBackend.shape_concat:1 +#: tensornetwork.backends.numpy.numpy_backend.NumPyBackend.shape_concat:1 +#: tensornetwork.backends.pytorch.pytorch_backend.PyTorchBackend.shape_concat:1 +#: tensornetwork.backends.tensorflow.tensorflow_backend.TensorFlowBackend.shape_concat:1 +msgid "Concatenate a sequence of tensors together about the given axis." +msgstr "" + +#: of tensornetwork.backends.abstract_backend.AbstractBackend.shape_prod:1 +#: tensornetwork.backends.jax.jax_backend.JaxBackend.shape_prod:1 +#: tensornetwork.backends.numpy.numpy_backend.NumPyBackend.shape_prod:1 +#: tensornetwork.backends.pytorch.pytorch_backend.PyTorchBackend.shape_prod:1 +#: tensornetwork.backends.tensorflow.tensorflow_backend.TensorFlowBackend.shape_prod:1 +msgid "Take the product of all of the elements in values" +msgstr "" + +#: of tensornetwork.backends.abstract_backend.AbstractBackend.shape_tensor:1 +#: tensornetwork.backends.jax.jax_backend.JaxBackend.shape_tensor:1 +#: tensornetwork.backends.numpy.numpy_backend.NumPyBackend.shape_tensor:1 +#: tensornetwork.backends.pytorch.pytorch_backend.PyTorchBackend.shape_tensor:1 +#: tensornetwork.backends.tensorflow.tensorflow_backend.TensorFlowBackend.shape_tensor:1 +msgid "Get the shape of a tensor." +msgstr "" + +#: of tensornetwork.backends.abstract_backend.AbstractBackend.shape_tensor:5 +#: tensornetwork.backends.jax.jax_backend.JaxBackend.shape_tensor:5 +#: tensornetwork.backends.numpy.numpy_backend.NumPyBackend.shape_tensor:5 +#: tensornetwork.backends.pytorch.pytorch_backend.PyTorchBackend.shape_tensor:5 +#: tensornetwork.backends.tensorflow.tensorflow_backend.TensorFlowBackend.shape_tensor:5 +msgid "The shape of the input tensor returned as another tensor." +msgstr "" + +#: of tensorcircuit.backends.cupy_backend.CuPyBackend.shape_tuple:1 +#: tensornetwork.backends.jax.jax_backend.JaxBackend.shape_tuple:1 +#: tensornetwork.backends.numpy.numpy_backend.NumPyBackend.shape_tuple:1 +#: tensornetwork.backends.pytorch.pytorch_backend.PyTorchBackend.shape_tuple:1 +#: tensornetwork.backends.tensorflow.tensorflow_backend.TensorFlowBackend.shape_tuple:1 +msgid "Get the shape of a tensor as a tuple of integers." +msgstr "" + +#: of tensorcircuit.backends.cupy_backend.CuPyBackend.shape_tuple:5 +#: tensornetwork.backends.jax.jax_backend.JaxBackend.shape_tuple:5 +#: tensornetwork.backends.numpy.numpy_backend.NumPyBackend.shape_tuple:5 +#: tensornetwork.backends.pytorch.pytorch_backend.PyTorchBackend.shape_tuple:5 +#: tensornetwork.backends.tensorflow.tensorflow_backend.TensorFlowBackend.shape_tuple:5 +msgid "The shape of the input tensor returned as a tuple of ints." +msgstr "" + +#: of tensorcircuit.backends.cupy_backend.CuPyBackend.sigmoid:1 +#: tensorcircuit.backends.jax_backend.JaxBackend.sigmoid:1 +#: tensorcircuit.backends.numpy_backend.NumpyBackend.sigmoid:1 +#: tensorcircuit.backends.pytorch_backend.PyTorchBackend.sigmoid:1 +#: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.sigmoid:1 +msgid "Compute sigmoid of input ``a``" +msgstr "" + +#: of tensorcircuit.backends.cupy_backend.CuPyBackend.sign:1 +#: tensornetwork.backends.jax.jax_backend.JaxBackend.sign:1 +#: tensornetwork.backends.pytorch.pytorch_backend.PyTorchBackend.sign:1 +#: tensornetwork.backends.tensorflow.tensorflow_backend.TensorFlowBackend.sign:1 +msgid "" +"Returns an elementwise tensor with entries y[i] = 1, 0, -1 where " +"tensor[i] > 0, == 0, and < 0 respectively." +msgstr "" + +#: of tensorcircuit.backends.cupy_backend.CuPyBackend.sin:1 +#: tensorcircuit.backends.jax_backend.JaxBackend.sin:1 +#: tensorcircuit.backends.numpy_backend.NumpyBackend.sin:1 +#: tensorcircuit.backends.pytorch_backend.PyTorchBackend.sin:1 +#: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.sin:1 +msgid "Return sin of `tensor`. :param tensor: A tensor." +msgstr "" + +#: of tensorcircuit.backends.cupy_backend.CuPyBackend.sinh:1 +#: tensorcircuit.backends.jax_backend.JaxBackend.sinh:1 +#: tensorcircuit.backends.numpy_backend.NumpyBackend.sinh:1 +#: tensorcircuit.backends.pytorch_backend.PyTorchBackend.sinh:1 +#: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.sinh:1 +msgid "Return the sinh of a tensor ``a``." +msgstr "" + +#: of tensorcircuit.backends.cupy_backend.CuPyBackend.sinh:5 +#: tensorcircuit.backends.jax_backend.JaxBackend.sinh:5 +#: tensorcircuit.backends.numpy_backend.NumpyBackend.sinh:5 +#: tensorcircuit.backends.pytorch_backend.PyTorchBackend.sinh:5 +#: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.sinh:5 +msgid "sinh of ``a``" +msgstr "" + +#: of tensorcircuit.backends.cupy_backend.CuPyBackend.size:1 +#: tensorcircuit.backends.jax_backend.JaxBackend.size:1 +#: tensorcircuit.backends.numpy_backend.NumpyBackend.size:1 +#: tensorcircuit.backends.pytorch_backend.PyTorchBackend.size:1 +#: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.size:1 +msgid "Return the total number of elements in ``a`` in tensor form." +msgstr "" + +#: of tensorcircuit.backends.cupy_backend.CuPyBackend.size:5 +#: tensorcircuit.backends.jax_backend.JaxBackend.size:5 +#: tensorcircuit.backends.numpy_backend.NumpyBackend.size:5 +#: tensorcircuit.backends.pytorch_backend.PyTorchBackend.size:5 +#: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.size:5 +msgid "the total number of elements in ``a``" +msgstr "" + +#: of tensorcircuit.backends.abstract_backend.ExtendedBackend.sizen:1 +msgid "Return the total number of elements in tensor ``a``, but in integer form." +msgstr "" + +#: of tensorcircuit.backends.abstract_backend.ExtendedBackend.sizen:5 +msgid "the total number of elements in tensor ``a``" +msgstr "" + +#: of tensornetwork.backends.abstract_backend.AbstractBackend.slice:1 +#: tensornetwork.backends.jax.jax_backend.JaxBackend.slice:1 +#: tensornetwork.backends.numpy.numpy_backend.NumPyBackend.slice:1 +#: tensornetwork.backends.pytorch.pytorch_backend.PyTorchBackend.slice:1 +#: tensornetwork.backends.tensorflow.tensorflow_backend.TensorFlowBackend.slice:1 +msgid "Obtains a slice of a tensor based on start_indices and slice_sizes." +msgstr "" + +#: of tensornetwork.backends.abstract_backend.AbstractBackend.slice:4 +#: tensornetwork.backends.jax.jax_backend.JaxBackend.slice:4 +#: tensornetwork.backends.numpy.numpy_backend.NumPyBackend.slice:4 +#: tensornetwork.backends.pytorch.pytorch_backend.PyTorchBackend.slice:4 +#: tensornetwork.backends.tensorflow.tensorflow_backend.TensorFlowBackend.slice:4 +msgid "Tuple of integers denoting start indices of slice." +msgstr "" + +#: of tensornetwork.backends.abstract_backend.AbstractBackend.slice:5 +#: tensornetwork.backends.jax.jax_backend.JaxBackend.slice:5 +#: tensornetwork.backends.numpy.numpy_backend.NumPyBackend.slice:5 +#: tensornetwork.backends.pytorch.pytorch_backend.PyTorchBackend.slice:5 +#: tensornetwork.backends.tensorflow.tensorflow_backend.TensorFlowBackend.slice:5 +msgid "Tuple of integers denoting size of slice along each axis." +msgstr "" + +#: of tensorcircuit.backends.cupy_backend.CuPyBackend.softmax:1 +#: tensorcircuit.backends.jax_backend.JaxBackend.softmax:1 +#: tensorcircuit.backends.numpy_backend.NumpyBackend.softmax:1 +#: tensorcircuit.backends.pytorch_backend.PyTorchBackend.softmax:1 +#: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.softmax:1 +msgid "" +"Softmax function. Computes the function which rescales elements to the " +"range [0,1] such that the elements along axis sum to 1." +msgstr "" + +#: of tensorcircuit.backends.cupy_backend.CuPyBackend.softmax:4 +#: tensorcircuit.backends.jax_backend.JaxBackend.softmax:4 +#: tensorcircuit.backends.numpy_backend.NumpyBackend.softmax:4 +#: tensorcircuit.backends.pytorch_backend.PyTorchBackend.softmax:4 +#: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.softmax:4 +msgid "\\mathrm{softmax}(x) = \\frac{\\exp(x_i)}{\\sum_j \\exp(x_j)}" +msgstr "" + +#: of tensorcircuit.backends.cupy_backend.CuPyBackend.softmax:11 +#: tensorcircuit.backends.jax_backend.JaxBackend.softmax:11 +#: tensorcircuit.backends.numpy_backend.NumpyBackend.softmax:11 +#: tensorcircuit.backends.pytorch_backend.PyTorchBackend.softmax:11 +#: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.softmax:11 +msgid "" +"A dimension along which Softmax will be computed , defaults to None for " +"all axis sum." +msgstr "" + +#: of tensorcircuit.backends.cupy_backend.CuPyBackend.softmax:13 +#: tensorcircuit.backends.cupy_backend.CuPyBackend.stack:7 +#: tensorcircuit.backends.jax_backend.JaxBackend.softmax:13 +#: tensorcircuit.backends.jax_backend.JaxBackend.stack:7 +#: tensorcircuit.backends.numpy_backend.NumpyBackend.softmax:13 +#: tensorcircuit.backends.numpy_backend.NumpyBackend.stack:7 +#: tensorcircuit.backends.pytorch_backend.PyTorchBackend.softmax:13 +#: tensorcircuit.backends.pytorch_backend.PyTorchBackend.stack:7 +#: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.softmax:13 +#: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.stack:7 +msgid "concatenated tensor" +msgstr "" + +#: of tensorcircuit.backends.cupy_backend.CuPyBackend.solve:1 +#: tensorcircuit.backends.jax_backend.JaxBackend.solve:1 +#: tensorcircuit.backends.numpy_backend.NumpyBackend.solve:1 +#: tensorcircuit.backends.pytorch_backend.PyTorchBackend.solve:1 +#: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.solve:1 +msgid "Solve the linear system Ax=b and return the solution x." +msgstr "" + +#: of tensorcircuit.backends.cupy_backend.CuPyBackend.solve:3 +#: tensorcircuit.backends.jax_backend.JaxBackend.solve:3 +#: tensorcircuit.backends.numpy_backend.NumpyBackend.solve:3 +#: tensorcircuit.backends.pytorch_backend.PyTorchBackend.solve:3 +#: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.solve:3 +msgid "The multiplied matrix." +msgstr "" + +#: of tensorcircuit.backends.cupy_backend.CuPyBackend.solve:5 +#: tensorcircuit.backends.jax_backend.JaxBackend.solve:5 +#: tensorcircuit.backends.numpy_backend.NumpyBackend.solve:5 +#: tensorcircuit.backends.pytorch_backend.PyTorchBackend.solve:5 +#: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.solve:5 +msgid "The resulted matrix." +msgstr "" + +#: of tensorcircuit.backends.cupy_backend.CuPyBackend.solve:7 +#: tensorcircuit.backends.jax_backend.JaxBackend.solve:7 +#: tensorcircuit.backends.numpy_backend.NumpyBackend.solve:7 +#: tensorcircuit.backends.pytorch_backend.PyTorchBackend.solve:7 +#: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.solve:7 +msgid "The solution of the linear system." +msgstr "" + +#: of +#: tensorcircuit.backends.abstract_backend.ExtendedBackend.sparse_dense_matmul:1 +#: tensorcircuit.backends.cupy_backend.CuPyBackend.sparse_dense_matmul:1 +#: tensorcircuit.backends.jax_backend.JaxBackend.sparse_dense_matmul:1 +#: tensorcircuit.backends.numpy_backend.NumpyBackend.sparse_dense_matmul:1 +#: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.sparse_dense_matmul:1 +msgid "A sparse matrix multiplies a dense matrix." +msgstr "" + +#: of +#: tensorcircuit.backends.abstract_backend.ExtendedBackend.sparse_dense_matmul:3 +#: tensorcircuit.backends.abstract_backend.ExtendedBackend.to_dense:3 +#: tensorcircuit.backends.cupy_backend.CuPyBackend.sparse_dense_matmul:3 +#: tensorcircuit.backends.cupy_backend.CuPyBackend.to_dense:3 +#: tensorcircuit.backends.jax_backend.JaxBackend.sparse_dense_matmul:3 +#: tensorcircuit.backends.jax_backend.JaxBackend.to_dense:3 +#: tensorcircuit.backends.numpy_backend.NumpyBackend.sparse_dense_matmul:3 +#: tensorcircuit.backends.numpy_backend.NumpyBackend.to_dense:3 +#: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.sparse_dense_matmul:3 +#: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.to_dense:3 +msgid "a sparse matrix" +msgstr "" + +#: of +#: tensorcircuit.backends.abstract_backend.ExtendedBackend.sparse_dense_matmul:5 +#: tensorcircuit.backends.cupy_backend.CuPyBackend.sparse_dense_matmul:5 +#: tensorcircuit.backends.jax_backend.JaxBackend.sparse_dense_matmul:5 +#: tensorcircuit.backends.numpy_backend.NumpyBackend.sparse_dense_matmul:5 +#: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.sparse_dense_matmul:5 +msgid "a dense matrix" +msgstr "" + +#: of +#: tensorcircuit.backends.abstract_backend.ExtendedBackend.sparse_dense_matmul:7 +#: tensorcircuit.backends.cupy_backend.CuPyBackend.sparse_dense_matmul:7 +#: tensorcircuit.backends.jax_backend.JaxBackend.sparse_dense_matmul:7 +#: tensorcircuit.backends.numpy_backend.NumpyBackend.sparse_dense_matmul:7 +#: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.sparse_dense_matmul:7 +msgid "dense matrix" +msgstr "" + +#: of tensornetwork.backends.abstract_backend.AbstractBackend.sqrt:1 +#: tensornetwork.backends.jax.jax_backend.JaxBackend.sqrt:1 +#: tensornetwork.backends.numpy.numpy_backend.NumPyBackend.sqrt:1 +#: tensornetwork.backends.pytorch.pytorch_backend.PyTorchBackend.sqrt:1 +#: tensornetwork.backends.tensorflow.tensorflow_backend.TensorFlowBackend.sqrt:1 +msgid "Take the square root (element wise) of a given tensor." +msgstr "" + +#: of tensorcircuit.backends.abstract_backend.ExtendedBackend.sqrtmh:1 +msgid "Return the sqrtm of a Hermitian matrix ``a``." +msgstr "" + +#: of tensorcircuit.backends.abstract_backend.ExtendedBackend.sqrtmh:5 +msgid "sqrtm of ``a``" +msgstr "" + +#: of tensorcircuit.backends.cupy_backend.CuPyBackend.stack:1 +#: tensorcircuit.backends.jax_backend.JaxBackend.stack:1 +#: tensorcircuit.backends.numpy_backend.NumpyBackend.stack:1 +#: tensorcircuit.backends.pytorch_backend.PyTorchBackend.stack:1 +#: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.stack:1 +msgid "Concatenates a sequence of tensors ``a`` along a new dimension ``axis``." +msgstr "" + +#: of tensorcircuit.backends.cupy_backend.CuPyBackend.stack:3 +#: tensorcircuit.backends.jax_backend.JaxBackend.stack:3 +#: tensorcircuit.backends.numpy_backend.NumpyBackend.stack:3 +#: tensorcircuit.backends.pytorch_backend.PyTorchBackend.stack:3 +#: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.stack:3 +msgid "List of tensors in the same shape" +msgstr "" + +#: of tensorcircuit.backends.cupy_backend.CuPyBackend.stack:5 +#: tensorcircuit.backends.jax_backend.JaxBackend.stack:5 +#: tensorcircuit.backends.numpy_backend.NumpyBackend.stack:5 +#: tensorcircuit.backends.pytorch_backend.PyTorchBackend.stack:5 +#: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.stack:5 +msgid "the stack axis, defaults to 0" +msgstr "" + +#: of tensorcircuit.backends.abstract_backend.ExtendedBackend.stateful_randn:5 +#: tensorcircuit.backends.abstract_backend.ExtendedBackend.stateful_randu:3 +#: tensorcircuit.backends.cupy_backend.CuPyBackend.stateful_randn:5 +#: tensorcircuit.backends.cupy_backend.CuPyBackend.stateful_randu:3 +#: tensorcircuit.backends.jax_backend.JaxBackend.stateful_randn:5 +#: tensorcircuit.backends.jax_backend.JaxBackend.stateful_randu:3 +#: tensorcircuit.backends.numpy_backend.NumpyBackend.stateful_randn:5 +#: tensorcircuit.backends.numpy_backend.NumpyBackend.stateful_randu:3 +#: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.stateful_randn:5 +#: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.stateful_randu:3 +msgid "stateful register for each package" +msgstr "" + +#: of tensorcircuit.backends.abstract_backend.ExtendedBackend.stateful_randn:7 +#: tensorcircuit.backends.cupy_backend.CuPyBackend.stateful_randn:7 +#: tensorcircuit.backends.jax_backend.JaxBackend.stateful_randn:7 +#: tensorcircuit.backends.numpy_backend.NumpyBackend.stateful_randn:7 +#: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.stateful_randn:7 +msgid "shape of output sampling tensor" +msgstr "" + +#: of tensorcircuit.backends.abstract_backend.ExtendedBackend.stateful_randn:13 +#: tensorcircuit.backends.abstract_backend.ExtendedBackend.stateful_randu:11 +#: tensorcircuit.backends.cupy_backend.CuPyBackend.stateful_randn:13 +#: tensorcircuit.backends.cupy_backend.CuPyBackend.stateful_randu:11 +#: tensorcircuit.backends.jax_backend.JaxBackend.stateful_randn:13 +#: tensorcircuit.backends.jax_backend.JaxBackend.stateful_randu:11 +#: tensorcircuit.backends.numpy_backend.NumpyBackend.stateful_randn:13 +#: tensorcircuit.backends.numpy_backend.NumpyBackend.stateful_randu:11 +#: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.stateful_randn:13 +#: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.stateful_randu:11 +msgid "only real data type is supported, \"32\" or \"64\", defaults to \"32\"" +msgstr "" + +#: of tensorcircuit.backends.abstract_backend.ExtendedBackend.stateful_randu:1 +#: tensorcircuit.backends.cupy_backend.CuPyBackend.stateful_randu:1 +#: tensorcircuit.backends.jax_backend.JaxBackend.stateful_randu:1 +#: tensorcircuit.backends.numpy_backend.NumpyBackend.stateful_randu:1 +#: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.stateful_randu:1 +msgid "Uniform random sampler from ``low`` to ``high``." +msgstr "" + +#: of tensorcircuit.backends.abstract_backend.ExtendedBackend.stateful_randu:5 +#: tensorcircuit.backends.cupy_backend.CuPyBackend.stateful_randu:5 +#: tensorcircuit.backends.jax_backend.JaxBackend.stateful_randu:5 +#: tensorcircuit.backends.numpy_backend.NumpyBackend.stateful_randu:5 +#: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.stateful_randu:5 +msgid "shape of output sampling tensor, defaults to 1" +msgstr "" + +#: of tensorcircuit.backends.cupy_backend.CuPyBackend.std:1 +#: tensorcircuit.backends.jax_backend.JaxBackend.std:1 +#: tensorcircuit.backends.numpy_backend.NumpyBackend.std:1 +#: tensorcircuit.backends.pytorch_backend.PyTorchBackend.std:1 +#: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.std:1 +msgid "Compute the standard deviation along the specified axis." +msgstr "" + +#: of tensorcircuit.backends.cupy_backend.CuPyBackend.std:5 +#: tensorcircuit.backends.jax_backend.JaxBackend.std:5 +#: tensorcircuit.backends.numpy_backend.NumpyBackend.std:5 +#: tensorcircuit.backends.pytorch_backend.PyTorchBackend.std:5 +#: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.std:5 +msgid "" +"Axis or axes along which the standard deviation is computed, defaults to " +"None, implying all axis" +msgstr "" + +#: of tensorcircuit.backends.cupy_backend.CuPyBackend.std:8 +#: tensorcircuit.backends.jax_backend.JaxBackend.std:8 +#: tensorcircuit.backends.numpy_backend.NumpyBackend.std:8 +#: tensorcircuit.backends.pytorch_backend.PyTorchBackend.std:8 +#: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.std:8 +msgid "" +"If this is set to True, the axes which are reduced are left in the result" +" as dimensions with size one, defaults to False" +msgstr "" + +#: of tensorcircuit.backends.cupy_backend.CuPyBackend.stop_gradient:1 +#: tensorcircuit.backends.jax_backend.JaxBackend.stop_gradient:1 +#: tensorcircuit.backends.numpy_backend.NumpyBackend.stop_gradient:1 +#: tensorcircuit.backends.pytorch_backend.PyTorchBackend.stop_gradient:1 +#: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.stop_gradient:1 +msgid "Stop backpropagation from ``a``." +msgstr "" + +#: of tensornetwork.backends.abstract_backend.AbstractBackend.subtraction:1 +#: tensornetwork.backends.jax.jax_backend.JaxBackend.subtraction:1 +#: tensornetwork.backends.numpy.numpy_backend.NumPyBackend.subtraction:1 +#: tensornetwork.backends.pytorch.pytorch_backend.PyTorchBackend.subtraction:1 +#: tensornetwork.backends.tensorflow.tensorflow_backend.TensorFlowBackend.subtraction:1 +msgid "" +"Return the default substraction of `tensor`. A backend can override such " +"implementation. :param tensor1: A tensor. :param tensor2: A tensor." +msgstr "" + +#: of tensorcircuit.backends.cupy_backend.CuPyBackend.sum:1 +#: tensorcircuit.backends.numpy_backend._sum_numpy:1 +#: tensorcircuit.backends.pytorch_backend._sum_torch:1 +#: tensornetwork.backends.jax.jax_backend.JaxBackend.sum:1 +#: tensornetwork.backends.tensorflow.tensorflow_backend.TensorFlowBackend.sum:1 +msgid "" +"Sum elements of `tensor` along the specified `axis`. Results in a new " +"Tensor with the summed axis removed. :param tensor: An input tensor." +msgstr "" + +#: of tensorcircuit.backends.cupy_backend.CuPyBackend.sum:5 +#: tensorcircuit.backends.numpy_backend._sum_numpy:5 +#: tensorcircuit.backends.pytorch_backend._sum_torch:5 +#: tensornetwork.backends.jax.jax_backend.JaxBackend.sum:5 +#: tensornetwork.backends.tensorflow.tensorflow_backend.TensorFlowBackend.sum:5 +msgid "" +"The result of performing the summation. The order of the tensor will be" +" reduced by 1." +msgstr "" + +#: of tensorcircuit.backends.cupy_backend.CuPyBackend.sum:7 +#: tensorcircuit.backends.numpy_backend._sum_numpy:7 +#: tensorcircuit.backends.pytorch_backend._sum_torch:7 +#: tensornetwork.backends.jax.jax_backend.JaxBackend.sum:7 +#: tensornetwork.backends.tensorflow.tensorflow_backend.TensorFlowBackend.sum:7 +msgid "The result of performing the summation. The order of the tensor" +msgstr "" + +#: of tensorcircuit.backends.cupy_backend.CuPyBackend.sum:8 +#: tensorcircuit.backends.numpy_backend._sum_numpy:8 +#: tensorcircuit.backends.pytorch_backend._sum_torch:8 +#: tensornetwork.backends.jax.jax_backend.JaxBackend.sum:8 +#: tensornetwork.backends.tensorflow.tensorflow_backend.TensorFlowBackend.sum:8 +msgid "will be reduced by 1." +msgstr "" + +#: of tensorcircuit.backends.jax_backend._svd_jax:1 +#: tensorcircuit.backends.tensorflow_backend._svd_tf:1 +#: tensornetwork.backends.abstract_backend.AbstractBackend.svd:1 +#: tensornetwork.backends.numpy.numpy_backend.NumPyBackend.svd:1 +#: tensornetwork.backends.pytorch.pytorch_backend.PyTorchBackend.svd:1 +msgid "Computes the singular value decomposition (SVD) of a tensor." +msgstr "" + +#: of tensorcircuit.backends.jax_backend._svd_jax:3 +#: tensorcircuit.backends.tensorflow_backend._svd_tf:3 +#: tensornetwork.backends.abstract_backend.AbstractBackend.svd:3 +#: tensornetwork.backends.numpy.numpy_backend.NumPyBackend.svd:3 +#: tensornetwork.backends.pytorch.pytorch_backend.PyTorchBackend.svd:3 +msgid "" +"The SVD is performed by treating the tensor as a matrix, with an " +"effective left (row) index resulting from combining the axes " +"`tensor.shape[:pivot_axis]` and an effective right (column) index " +"resulting from combining the axes `tensor.shape[pivot_axis:]`." +msgstr "" + +#: of tensorcircuit.backends.jax_backend._svd_jax:8 +#: tensorcircuit.backends.tensorflow_backend._svd_tf:8 +#: tensornetwork.backends.abstract_backend.AbstractBackend.svd:8 +#: tensornetwork.backends.numpy.numpy_backend.NumPyBackend.svd:8 +#: tensornetwork.backends.pytorch.pytorch_backend.PyTorchBackend.svd:8 +msgid "" +"For example, if `tensor` had a shape (2, 3, 4, 5) and `pivot_axis` was 2," +" then `u` would have shape (2, 3, 6), `s` would have shape (6), and `vh` " +"would have shape (6, 4, 5)." +msgstr "" + +#: of tensorcircuit.backends.jax_backend._svd_jax:12 +#: tensorcircuit.backends.tensorflow_backend._svd_tf:12 +#: tensornetwork.backends.abstract_backend.AbstractBackend.svd:12 +#: tensornetwork.backends.numpy.numpy_backend.NumPyBackend.svd:12 +#: tensornetwork.backends.pytorch.pytorch_backend.PyTorchBackend.svd:12 +msgid "" +"If `max_singular_values` is set to an integer, the SVD is truncated to " +"keep at most this many singular values." +msgstr "" + +#: of tensorcircuit.backends.jax_backend._svd_jax:15 +#: tensorcircuit.backends.tensorflow_backend._svd_tf:15 +#: tensornetwork.backends.abstract_backend.AbstractBackend.svd:15 +#: tensornetwork.backends.numpy.numpy_backend.NumPyBackend.svd:15 +#: tensornetwork.backends.pytorch.pytorch_backend.PyTorchBackend.svd:15 +msgid "" +"If `max_truncation_error > 0`, as many singular values will be truncated " +"as possible, so that the truncation error (the norm of discarded singular" +" values) is at most `max_truncation_error`. If `relative` is set `True` " +"then `max_truncation_err` is understood relative to the largest singular " +"value." +msgstr "" + +#: of tensorcircuit.backends.jax_backend._svd_jax:21 +#: tensornetwork.backends.abstract_backend.AbstractBackend.svd:21 +#: tensornetwork.backends.numpy.numpy_backend.NumPyBackend.svd:21 +#: tensornetwork.backends.pytorch.pytorch_backend.PyTorchBackend.svd:21 +msgid "" +"If both `max_singular_values` and `max_truncation_error` are specified, " +"the number of retained singular values will be `min(max_singular_values, " +"nsv_auto_trunc)`, where `nsv_auto_trunc` is the number of singular values" +" that must be kept to maintain a truncation error smaller than " +"`max_truncation_error`." +msgstr "" + +#: of tensorcircuit.backends.jax_backend._svd_jax:27 +#: tensornetwork.backends.abstract_backend.AbstractBackend.svd:27 +#: tensornetwork.backends.numpy.numpy_backend.NumPyBackend.svd:27 +#: tensornetwork.backends.pytorch.pytorch_backend.PyTorchBackend.svd:27 +msgid "The output consists of three tensors `u, s, vh` such that: ```python" +msgstr "" + +#: of tensorcircuit.backends.jax_backend._svd_jax:29 +#: tensornetwork.backends.abstract_backend.AbstractBackend.svd:29 +#: tensornetwork.backends.numpy.numpy_backend.NumPyBackend.svd:29 +#: tensornetwork.backends.pytorch.pytorch_backend.PyTorchBackend.svd:29 +msgid "u[i1,...,iN, j] * s[j] * vh[j, k1,...,kM] == tensor[i1,...,iN, k1,...,kM]" +msgstr "" + +#: of tensorcircuit.backends.jax_backend._svd_jax:30 +#: tensornetwork.backends.abstract_backend.AbstractBackend.svd:30 +#: tensornetwork.backends.numpy.numpy_backend.NumPyBackend.svd:30 +#: tensornetwork.backends.pytorch.pytorch_backend.PyTorchBackend.svd:30 +msgid "" +"``` Note that the output ordering matches numpy.linalg.svd rather than " +"tf.svd." +msgstr "" + +#: of tensorcircuit.backends.jax_backend._svd_jax:33 +#: tensorcircuit.backends.pytorch_backend._qr_torch:18 +#: tensorcircuit.backends.pytorch_backend._rq_torch:18 +#: tensorcircuit.backends.tensorflow_backend._qr_tf:18 +#: tensorcircuit.backends.tensorflow_backend._rq_tf:18 +#: tensornetwork.backends.abstract_backend.AbstractBackend.svd:33 +#: tensornetwork.backends.numpy.numpy_backend.NumPyBackend.svd:33 +#: tensornetwork.backends.pytorch.pytorch_backend.PyTorchBackend.svd:33 +msgid "A tensor to be decomposed." +msgstr "" + +#: of tensorcircuit.backends.jax_backend._svd_jax:34 +#: tensorcircuit.backends.pytorch_backend._qr_torch:20 +#: tensorcircuit.backends.pytorch_backend._rq_torch:20 +#: tensorcircuit.backends.tensorflow_backend._qr_tf:20 +#: tensorcircuit.backends.tensorflow_backend._rq_tf:20 +#: tensornetwork.backends.abstract_backend.AbstractBackend.svd:34 +#: tensornetwork.backends.numpy.numpy_backend.NumPyBackend.svd:34 +#: tensornetwork.backends.pytorch.pytorch_backend.PyTorchBackend.svd:34 +msgid "Where to split the tensor's axes before flattening into a matrix." +msgstr "" + +#: of tensorcircuit.backends.jax_backend._svd_jax:36 +#: tensornetwork.backends.abstract_backend.AbstractBackend.svd:36 +#: tensornetwork.backends.numpy.numpy_backend.NumPyBackend.svd:36 +#: tensornetwork.backends.pytorch.pytorch_backend.PyTorchBackend.svd:36 +msgid "The number of singular values to keep, or `None` to keep them all." +msgstr "" + +#: of tensorcircuit.backends.jax_backend._svd_jax:38 +#: tensornetwork.backends.abstract_backend.AbstractBackend.svd:38 +#: tensornetwork.backends.numpy.numpy_backend.NumPyBackend.svd:38 +#: tensornetwork.backends.pytorch.pytorch_backend.PyTorchBackend.svd:38 +msgid "The maximum allowed truncation error or `None` to not do any truncation." +msgstr "" + +#: of tensorcircuit.backends.jax_backend._svd_jax:40 +#: tensorcircuit.cons.split_rules:7 +#: tensorcircuit.mps_base.FiniteMPS.apply_two_site_gate:24 +#: tensornetwork.backends.abstract_backend.AbstractBackend.svd:40 +#: tensornetwork.backends.numpy.numpy_backend.NumPyBackend.svd:40 +#: tensornetwork.backends.pytorch.pytorch_backend.PyTorchBackend.svd:40 +msgid "Multiply `max_truncation_err` with the largest singular value." +msgstr "" + +#: of tensorcircuit.backends.jax_backend._svd_jax:42 +#: tensornetwork.backends.abstract_backend.AbstractBackend.svd:42 +#: tensornetwork.backends.numpy.numpy_backend.NumPyBackend.svd:42 +#: tensornetwork.backends.pytorch.pytorch_backend.PyTorchBackend.svd:42 +msgid "" +"Left tensor factor. s: Vector of ordered singular values from largest to " +"smallest. vh: Right tensor factor. s_rest: Vector of discarded singular " +"values (length zero if no truncation)." +msgstr "" + +#: of tensorcircuit.backends.jax_backend._svd_jax:42 +#: tensornetwork.backends.abstract_backend.AbstractBackend.svd:42 +#: tensornetwork.backends.numpy.numpy_backend.NumPyBackend.svd:42 +#: tensornetwork.backends.pytorch.pytorch_backend.PyTorchBackend.svd:42 +msgid "" +"Left tensor factor. s: Vector of ordered singular values from largest to " +"smallest. vh: Right tensor factor. s_rest: Vector of discarded singular " +"values (length zero if no" +msgstr "" + +#: of tensorcircuit.backends.jax_backend._svd_jax:46 +#: tensorcircuit.backends.tensorflow_backend._svd_tf:49 +#: tensornetwork.backends.abstract_backend.AbstractBackend.svd:46 +#: tensornetwork.backends.numpy.numpy_backend.NumPyBackend.svd:46 +#: tensornetwork.backends.pytorch.pytorch_backend.PyTorchBackend.svd:46 +msgid "truncation)." +msgstr "" + +#: of tensorcircuit.backends.cupy_backend.CuPyBackend.switch:1 +#: tensorcircuit.backends.jax_backend.JaxBackend.switch:1 +#: tensorcircuit.backends.numpy_backend.NumpyBackend.switch:1 +#: tensorcircuit.backends.pytorch_backend.PyTorchBackend.switch:1 +#: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.switch:1 +msgid "``branches[index]()``" +msgstr "" + +#: of tensorcircuit.backends.cupy_backend.CuPyBackend.tan:1 +#: tensorcircuit.backends.jax_backend.JaxBackend.tan:1 +#: tensorcircuit.backends.numpy_backend.NumpyBackend.tan:1 +#: tensorcircuit.backends.pytorch_backend.PyTorchBackend.tan:1 +#: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.tan:1 +msgid "Return the tan of a tensor ``a``." +msgstr "" + +#: of tensorcircuit.backends.cupy_backend.CuPyBackend.tan:5 +#: tensorcircuit.backends.jax_backend.JaxBackend.tan:5 +#: tensorcircuit.backends.numpy_backend.NumpyBackend.tan:5 +#: tensorcircuit.backends.pytorch_backend.PyTorchBackend.tan:5 +#: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.tan:5 +msgid "tan of ``a``" +msgstr "" + +#: of tensorcircuit.backends.cupy_backend.CuPyBackend.tanh:1 +#: tensorcircuit.backends.jax_backend.JaxBackend.tanh:1 +#: tensorcircuit.backends.numpy_backend.NumpyBackend.tanh:1 +#: tensorcircuit.backends.pytorch_backend.PyTorchBackend.tanh:1 +#: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.tanh:1 +msgid "Return the tanh of a tensor ``a``." +msgstr "" + +#: of tensorcircuit.backends.cupy_backend.CuPyBackend.tanh:5 +#: tensorcircuit.backends.jax_backend.JaxBackend.tanh:5 +#: tensorcircuit.backends.numpy_backend.NumpyBackend.tanh:5 +#: tensorcircuit.backends.pytorch_backend.PyTorchBackend.tanh:5 +#: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.tanh:5 +msgid "tanh of ``a``" +msgstr "" + +#: of tensorcircuit.backends.cupy_backend.CuPyBackend.tensordot:1 +#: tensorcircuit.backends.tensorflow_backend._tensordot_tf:1 +#: tensornetwork.backends.jax.jax_backend.JaxBackend.tensordot:1 +#: tensornetwork.backends.numpy.numpy_backend.NumPyBackend.tensordot:1 +#: tensornetwork.backends.pytorch.pytorch_backend.PyTorchBackend.tensordot:1 +msgid "Do a tensordot of tensors `a` and `b` over the given axes." +msgstr "" + +#: of tensorcircuit.backends.cupy_backend.CuPyBackend.tensordot:4 +#: tensorcircuit.backends.tensorflow_backend._tensordot_tf:4 +#: tensornetwork.backends.jax.jax_backend.JaxBackend.tensordot:4 +#: tensornetwork.backends.numpy.numpy_backend.NumPyBackend.tensordot:4 +#: tensornetwork.backends.pytorch.pytorch_backend.PyTorchBackend.tensordot:4 +msgid "Another tensor." +msgstr "" + +#: of tensorcircuit.backends.cupy_backend.CuPyBackend.tensordot:5 +#: tensorcircuit.backends.tensorflow_backend._tensordot_tf:5 +#: tensornetwork.backends.jax.jax_backend.JaxBackend.tensordot:5 +#: tensornetwork.backends.numpy.numpy_backend.NumPyBackend.tensordot:5 +#: tensornetwork.backends.pytorch.pytorch_backend.PyTorchBackend.tensordot:5 +msgid "Two lists of integers. These values are the contraction axes." +msgstr "" + +#: of tensorcircuit.backends.cupy_backend.CuPyBackend.tile:1 +#: tensorcircuit.backends.jax_backend.JaxBackend.tile:1 +#: tensorcircuit.backends.numpy_backend.NumpyBackend.tile:1 +#: tensorcircuit.backends.pytorch_backend.PyTorchBackend.tile:1 +#: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.tile:1 +msgid "Constructs a tensor by tiling a given tensor." +msgstr "" + +#: of tensorcircuit.backends.cupy_backend.CuPyBackend.tile:5 +#: tensorcircuit.backends.jax_backend.JaxBackend.tile:5 +#: tensorcircuit.backends.numpy_backend.NumpyBackend.tile:5 +#: tensorcircuit.backends.pytorch_backend.PyTorchBackend.tile:5 +#: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.tile:5 +msgid "1d tensor with length the same as the rank of ``a``" +msgstr "" + +#: of tensorcircuit.backends.abstract_backend.ExtendedBackend.to_dense:1 +#: tensorcircuit.backends.cupy_backend.CuPyBackend.to_dense:1 +#: tensorcircuit.backends.jax_backend.JaxBackend.to_dense:1 +#: tensorcircuit.backends.numpy_backend.NumpyBackend.to_dense:1 +#: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.to_dense:1 +msgid "Convert a sparse matrix to dense tensor." +msgstr "" + +#: of tensorcircuit.backends.abstract_backend.ExtendedBackend.to_dense:5 +#: tensorcircuit.backends.cupy_backend.CuPyBackend.to_dense:5 +#: tensorcircuit.backends.jax_backend.JaxBackend.to_dense:5 +#: tensorcircuit.backends.numpy_backend.NumpyBackend.to_dense:5 +#: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.to_dense:5 +msgid "the resulted dense matrix" +msgstr "" + +#: of tensorcircuit.backends.abstract_backend.ExtendedBackend.to_dlpack:1 +#: tensorcircuit.backends.jax_backend.JaxBackend.to_dlpack:1 +#: tensorcircuit.backends.pytorch_backend.PyTorchBackend.to_dlpack:1 +#: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.to_dlpack:1 +msgid "Transform the tensor ``a`` as a dlpack capsule" +msgstr "" + +#: of tensornetwork.backends.abstract_backend.AbstractBackend.trace:1 +#: tensornetwork.backends.jax.jax_backend.JaxBackend.trace:1 +#: tensornetwork.backends.numpy.numpy_backend.NumPyBackend.trace:1 +#: tensornetwork.backends.pytorch.pytorch_backend.PyTorchBackend.trace:1 +#: tensornetwork.backends.tensorflow.tensorflow_backend.TensorFlowBackend.trace:1 +msgid "Return summed entries along diagonals." +msgstr "" + +#: of tensornetwork.backends.abstract_backend.AbstractBackend.trace:3 +#: tensornetwork.backends.jax.jax_backend.JaxBackend.trace:3 +#: tensornetwork.backends.numpy.numpy_backend.NumPyBackend.trace:3 +#: tensornetwork.backends.pytorch.pytorch_backend.PyTorchBackend.trace:3 +#: tensornetwork.backends.tensorflow.tensorflow_backend.TensorFlowBackend.trace:3 +msgid "" +"If tensor is 2-D, the sum is over the diagonal of tensor with the given " +"offset, i.e., the collection of elements of the form a[i, i+offset]. If a" +" has more than two dimensions, then the axes specified by axis1 and axis2" +" are used to determine the 2-D sub-array whose diagonal is summed." +msgstr "" + +#: of tensornetwork.backends.abstract_backend.AbstractBackend.trace:19 +#: tensornetwork.backends.jax.jax_backend.JaxBackend.trace:19 +#: tensornetwork.backends.numpy.numpy_backend.NumPyBackend.trace:19 +#: tensornetwork.backends.pytorch.pytorch_backend.PyTorchBackend.trace:31 +#: tensornetwork.backends.tensorflow.tensorflow_backend.TensorFlowBackend.trace:28 +msgid "The batched summed diagonals." +msgstr "" + +#: of tensorcircuit.backends.cupy_backend.CuPyBackend.transpose:1 +#: tensornetwork.backends.jax.jax_backend.JaxBackend.transpose:1 +#: tensornetwork.backends.numpy.numpy_backend.NumPyBackend.transpose:1 +#: tensornetwork.backends.pytorch.pytorch_backend.PyTorchBackend.transpose:1 +#: tensornetwork.backends.tensorflow.tensorflow_backend.TensorFlowBackend.transpose:1 +msgid "" +"Transpose a tensor according to a given permutation. By default the axes " +"are reversed. :param tensor: A tensor. :param perm: The permutation of " +"the axes." +msgstr "" + +#: of tensorcircuit.backends.cupy_backend.CuPyBackend.transpose:6 +#: tensornetwork.backends.jax.jax_backend.JaxBackend.transpose:6 +#: tensornetwork.backends.numpy.numpy_backend.NumPyBackend.transpose:6 +#: tensornetwork.backends.pytorch.pytorch_backend.PyTorchBackend.transpose:6 +#: tensornetwork.backends.tensorflow.tensorflow_backend.TensorFlowBackend.transpose:6 +msgid "The transposed tensor" +msgstr "" + +#: of tensorcircuit.backends.abstract_backend.ExtendedBackend.tree_flatten:1 +#: tensorcircuit.backends.jax_backend.JaxBackend.tree_flatten:1 +#: tensorcircuit.backends.pytorch_backend.PyTorchBackend.tree_flatten:1 +msgid "Flatten python structure to 1D list" +msgstr "" + +#: of tensorcircuit.backends.abstract_backend.ExtendedBackend.tree_flatten:3 +#: tensorcircuit.backends.jax_backend.JaxBackend.tree_flatten:3 +#: tensorcircuit.backends.pytorch_backend.PyTorchBackend.tree_flatten:3 +msgid "python structure to be flattened" +msgstr "" + +#: of tensorcircuit.backends.abstract_backend.ExtendedBackend.tree_flatten:5 +#: tensorcircuit.backends.jax_backend.JaxBackend.tree_flatten:5 +#: tensorcircuit.backends.pytorch_backend.PyTorchBackend.tree_flatten:5 +msgid "" +"The 1D list of flattened structure and treedef which can be used for " +"later unflatten" +msgstr "" + +#: of tensorcircuit.backends.abstract_backend.ExtendedBackend.tree_map:1 +#: tensorcircuit.backends.jax_backend.JaxBackend.tree_map:1 +#: tensorcircuit.backends.pytorch_backend.PyTorchBackend.tree_map:1 +msgid "Return the new tree map with multiple arg function ``f`` through pytrees." +msgstr "" + +#: of tensorcircuit.backends.abstract_backend.ExtendedBackend.tree_map:3 +#: tensorcircuit.backends.jax_backend.JaxBackend.tree_map:3 +#: tensorcircuit.backends.pytorch_backend.PyTorchBackend.tree_map:3 +msgid "The function" +msgstr "" + +#: of tensorcircuit.backends.abstract_backend.ExtendedBackend.tree_map:5 +#: tensorcircuit.backends.jax_backend.JaxBackend.tree_map:5 +#: tensorcircuit.backends.pytorch_backend.PyTorchBackend.tree_map:5 +msgid "inputs as any python structure" +msgstr "" + +#: of tensorcircuit.backends.abstract_backend.ExtendedBackend.tree_map:7 +#: tensorcircuit.backends.jax_backend.JaxBackend.tree_map:7 +#: tensorcircuit.backends.pytorch_backend.PyTorchBackend.tree_map:7 +msgid "raise when neither tensorflow or jax is installed." +msgstr "" + +#: of tensorcircuit.backends.abstract_backend.ExtendedBackend.tree_map:8 +#: tensorcircuit.backends.jax_backend.JaxBackend.tree_map:8 +#: tensorcircuit.backends.pytorch_backend.PyTorchBackend.tree_map:8 +msgid "The new tree map with the same structure but different values." +msgstr "" + +#: of tensorcircuit.backends.abstract_backend.ExtendedBackend.tree_unflatten:1 +#: tensorcircuit.backends.jax_backend.JaxBackend.tree_unflatten:1 +#: tensorcircuit.backends.pytorch_backend.PyTorchBackend.tree_unflatten:1 +msgid "Pack 1D list to pytree defined via ``treedef``" +msgstr "" + +#: of tensorcircuit.backends.abstract_backend.ExtendedBackend.tree_unflatten:3 +#: tensorcircuit.backends.jax_backend.JaxBackend.tree_unflatten:3 +#: tensorcircuit.backends.pytorch_backend.PyTorchBackend.tree_unflatten:3 +msgid "Def of pytree structure, the second return from ``tree_flatten``" +msgstr "" + +#: of tensorcircuit.backends.abstract_backend.ExtendedBackend.tree_unflatten:5 +#: tensorcircuit.backends.jax_backend.JaxBackend.tree_unflatten:5 +#: tensorcircuit.backends.pytorch_backend.PyTorchBackend.tree_unflatten:5 +msgid "the 1D list of flattened data structure" +msgstr "" + +#: of tensorcircuit.backends.abstract_backend.ExtendedBackend.tree_unflatten:7 +#: tensorcircuit.backends.jax_backend.JaxBackend.tree_unflatten:7 +#: tensorcircuit.backends.pytorch_backend.PyTorchBackend.tree_unflatten:7 +msgid "Packed pytree" +msgstr "" + +#: of tensorcircuit.backends.cupy_backend.CuPyBackend.unique_with_counts:1 +#: tensorcircuit.backends.jax_backend.JaxBackend.unique_with_counts:1 +#: tensorcircuit.backends.numpy_backend.NumpyBackend.unique_with_counts:1 +#: tensorcircuit.backends.pytorch_backend.PyTorchBackend.unique_with_counts:1 +#: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.unique_with_counts:1 +msgid "" +"Find the unique elements and their corresponding counts of the given " +"tensor ``a``." +msgstr "" + +#: of tensorcircuit.backends.cupy_backend.CuPyBackend.unique_with_counts:5 +#: tensorcircuit.backends.jax_backend.JaxBackend.unique_with_counts:5 +#: tensorcircuit.backends.numpy_backend.NumpyBackend.unique_with_counts:5 +#: tensorcircuit.backends.pytorch_backend.PyTorchBackend.unique_with_counts:5 +#: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.unique_with_counts:5 +msgid "Unique elements, corresponding counts" +msgstr "" + +#: of tensorcircuit.backends.cupy_backend.CuPyBackend.value_and_grad:1 +#: tensorcircuit.backends.jax_backend.JaxBackend.value_and_grad:1 +#: tensorcircuit.backends.numpy_backend.NumpyBackend.value_and_grad:1 +#: tensorcircuit.backends.pytorch_backend.PyTorchBackend.value_and_grad:1 +#: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.value_and_grad:1 +msgid "Return the function which returns the value and grad of ``f``." +msgstr "" + +#: of tensorcircuit.backends.cupy_backend.CuPyBackend.value_and_grad:17 +#: tensorcircuit.backends.jax_backend.JaxBackend.value_and_grad:17 +#: tensorcircuit.backends.numpy_backend.NumpyBackend.value_and_grad:17 +#: tensorcircuit.backends.pytorch_backend.PyTorchBackend.value_and_grad:17 +#: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.value_and_grad:17 +msgid "" +"the value and grad function of ``f`` with the same set of arguments as " +"``f``" +msgstr "" + +#: of +#: tensorcircuit.backends.cupy_backend.CuPyBackend.vectorized_value_and_grad:1 +#: tensorcircuit.backends.jax_backend.JaxBackend.vectorized_value_and_grad:1 +#: tensorcircuit.backends.numpy_backend.NumpyBackend.vectorized_value_and_grad:1 +#: tensorcircuit.backends.pytorch_backend.PyTorchBackend.vectorized_value_and_grad:1 +#: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.vectorized_value_and_grad:1 +msgid "" +"Return the VVAG function of ``f``. The inputs for ``f`` is (args[0], " +"args[1], args[2], ...), and the output of ``f`` is a scalar. Suppose " +"VVAG(f) is a function with inputs in the form (vargs[0], args[1], " +"args[2], ...), where vagrs[0] has one extra dimension than args[0] in the" +" first axis and consistent with args[0] in shape for remaining " +"dimensions, i.e. shape(vargs[0]) = [batch] + shape(args[0]). (We only " +"cover cases where ``vectorized_argnums`` defaults to 0 here for " +"demonstration). VVAG(f) returns a tuple as a value tensor with shape " +"[batch, 1] and a gradient tuple with shape: ([batch]+shape(args[argnum]) " +"for argnum in argnums). The gradient for argnums=k is defined as" +msgstr "" + +#: of +#: tensorcircuit.backends.cupy_backend.CuPyBackend.vectorized_value_and_grad:9 +#: tensorcircuit.backends.jax_backend.JaxBackend.vectorized_value_and_grad:9 +#: tensorcircuit.backends.numpy_backend.NumpyBackend.vectorized_value_and_grad:9 +#: tensorcircuit.backends.pytorch_backend.PyTorchBackend.vectorized_value_and_grad:9 +#: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.vectorized_value_and_grad:9 +msgid "" +"g^k = \\frac{\\partial \\sum_{i\\in batch} f(vargs[0][i], args[1], " +"...)}{\\partial args[k]}" +msgstr "" + +#: of +#: tensorcircuit.backends.cupy_backend.CuPyBackend.vectorized_value_and_grad:13 +#: tensorcircuit.backends.jax_backend.JaxBackend.vectorized_value_and_grad:13 +#: tensorcircuit.backends.numpy_backend.NumpyBackend.vectorized_value_and_grad:13 +#: tensorcircuit.backends.pytorch_backend.PyTorchBackend.vectorized_value_and_grad:13 +#: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.vectorized_value_and_grad:13 +msgid "Therefore, if argnums=0, the gradient is reduced to" +msgstr "" + +#: of +#: tensorcircuit.backends.cupy_backend.CuPyBackend.vectorized_value_and_grad:15 +#: tensorcircuit.backends.jax_backend.JaxBackend.vectorized_value_and_grad:15 +#: tensorcircuit.backends.numpy_backend.NumpyBackend.vectorized_value_and_grad:15 +#: tensorcircuit.backends.pytorch_backend.PyTorchBackend.vectorized_value_and_grad:15 +#: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.vectorized_value_and_grad:15 +msgid "g^0_i = \\frac{\\partial f(vargs[0][i])}{\\partial vargs[0][i]}" +msgstr "" + +#: of +#: tensorcircuit.backends.cupy_backend.CuPyBackend.vectorized_value_and_grad:19 +#: tensorcircuit.backends.jax_backend.JaxBackend.vectorized_value_and_grad:19 +#: tensorcircuit.backends.numpy_backend.NumpyBackend.vectorized_value_and_grad:19 +#: tensorcircuit.backends.pytorch_backend.PyTorchBackend.vectorized_value_and_grad:19 +#: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.vectorized_value_and_grad:19 +msgid "" +", which is specifically suitable for batched VQE optimization, where " +"args[0] is the circuit parameters." +msgstr "" + +#: of +#: tensorcircuit.backends.cupy_backend.CuPyBackend.vectorized_value_and_grad:21 +#: tensorcircuit.backends.jax_backend.JaxBackend.vectorized_value_and_grad:21 +#: tensorcircuit.backends.numpy_backend.NumpyBackend.vectorized_value_and_grad:21 +#: tensorcircuit.backends.pytorch_backend.PyTorchBackend.vectorized_value_and_grad:21 +#: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.vectorized_value_and_grad:21 +msgid "And if argnums=1, the gradient is like" +msgstr "" + +#: of +#: tensorcircuit.backends.cupy_backend.CuPyBackend.vectorized_value_and_grad:23 +#: tensorcircuit.backends.jax_backend.JaxBackend.vectorized_value_and_grad:23 +#: tensorcircuit.backends.numpy_backend.NumpyBackend.vectorized_value_and_grad:23 +#: tensorcircuit.backends.pytorch_backend.PyTorchBackend.vectorized_value_and_grad:23 +#: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.vectorized_value_and_grad:23 +msgid "" +"g^1_i = \\frac{\\partial \\sum_j f(vargs[0][j], args[1])}{\\partial " +"args[1][i]}\n" +"\n" +msgstr "" + +#: of +#: tensorcircuit.backends.cupy_backend.CuPyBackend.vectorized_value_and_grad:26 +#: tensorcircuit.backends.jax_backend.JaxBackend.vectorized_value_and_grad:26 +#: tensorcircuit.backends.numpy_backend.NumpyBackend.vectorized_value_and_grad:26 +#: tensorcircuit.backends.pytorch_backend.PyTorchBackend.vectorized_value_and_grad:26 +#: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.vectorized_value_and_grad:26 +msgid "" +", which is suitable for quantum machine learning scenarios, where ``f`` " +"is the loss function, args[0] corresponds to the input data and args[1] " +"corresponds to the weights in the QML model." +msgstr "" + +#: of +#: tensorcircuit.backends.cupy_backend.CuPyBackend.vectorized_value_and_grad:33 +#: tensorcircuit.backends.cupy_backend.CuPyBackend.vmap:6 +#: tensorcircuit.backends.jax_backend.JaxBackend.vectorized_value_and_grad:33 +#: tensorcircuit.backends.jax_backend.JaxBackend.vmap:6 +#: tensorcircuit.backends.numpy_backend.NumpyBackend.vectorized_value_and_grad:33 +#: tensorcircuit.backends.numpy_backend.NumpyBackend.vmap:6 +#: tensorcircuit.backends.pytorch_backend.PyTorchBackend.vectorized_value_and_grad:33 +#: tensorcircuit.backends.pytorch_backend.PyTorchBackend.vmap:6 +#: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.vectorized_value_and_grad:33 +#: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.vmap:6 +msgid "" +"the args to be vectorized, these arguments should share the same batch " +"shape in the fist dimension" +msgstr "" + +#: of tensorcircuit.backends.cupy_backend.CuPyBackend.vjp:1 +#: tensorcircuit.backends.jax_backend.JaxBackend.vjp:1 +#: tensorcircuit.backends.numpy_backend.NumpyBackend.vjp:1 +#: tensorcircuit.backends.pytorch_backend.PyTorchBackend.vjp:1 +#: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.vjp:1 +msgid "" +"Function that computes the dot product between a vector v and the " +"Jacobian of the given function at the point given by the inputs. (reverse" +" mode AD relevant) Strictly speaking, this function is value_and_vjp." +msgstr "" + +#: of tensorcircuit.backends.cupy_backend.CuPyBackend.vjp:5 +#: tensorcircuit.backends.jax_backend.JaxBackend.vjp:5 +#: tensorcircuit.backends.numpy_backend.NumpyBackend.vjp:5 +#: tensorcircuit.backends.pytorch_backend.PyTorchBackend.vjp:5 +#: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.vjp:5 +msgid "the function to carry out vjp calculation" +msgstr "" + +#: of tensorcircuit.backends.cupy_backend.CuPyBackend.vjp:9 +#: tensorcircuit.backends.jax_backend.JaxBackend.vjp:9 +#: tensorcircuit.backends.numpy_backend.NumpyBackend.vjp:9 +#: tensorcircuit.backends.pytorch_backend.PyTorchBackend.vjp:9 +#: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.vjp:9 +msgid "" +"value vector or gradient from downstream in reverse mode AD the same " +"shape as return of function ``f``" +msgstr "" + +#: of tensorcircuit.backends.cupy_backend.CuPyBackend.vjp:12 +#: tensorcircuit.backends.jax_backend.JaxBackend.vjp:12 +#: tensorcircuit.backends.numpy_backend.NumpyBackend.vjp:12 +#: tensorcircuit.backends.pytorch_backend.PyTorchBackend.vjp:12 +#: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.vjp:12 +msgid "(``f(*inputs)``, vjp_tensor), where vjp_tensor is the same shape as inputs" +msgstr "" + +#: of tensorcircuit.backends.cupy_backend.CuPyBackend.vmap:1 +#: tensorcircuit.backends.jax_backend.JaxBackend.vmap:1 +#: tensorcircuit.backends.numpy_backend.NumpyBackend.vmap:1 +#: tensorcircuit.backends.pytorch_backend.PyTorchBackend.vmap:1 +#: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.vmap:1 +msgid "" +"Return the vectorized map or batched version of ``f`` on the first extra " +"axis. The general interface supports ``f`` with multiple arguments and " +"broadcast in the fist dimension." +msgstr "" + +#: of tensorcircuit.backends.cupy_backend.CuPyBackend.vmap:4 +#: tensorcircuit.backends.jax_backend.JaxBackend.vmap:4 +#: tensorcircuit.backends.numpy_backend.NumpyBackend.vmap:4 +#: tensorcircuit.backends.pytorch_backend.PyTorchBackend.vmap:4 +#: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.vmap:4 +msgid "function to be broadcasted." +msgstr "" + +#: of tensorcircuit.backends.cupy_backend.CuPyBackend.vmap:9 +#: tensorcircuit.backends.jax_backend.JaxBackend.vmap:9 +#: tensorcircuit.backends.numpy_backend.NumpyBackend.vmap:9 +#: tensorcircuit.backends.pytorch_backend.PyTorchBackend.vmap:9 +#: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.vmap:9 +msgid "vmap version of ``f``" +msgstr "" + +#: of tensorcircuit.backends.cupy_backend.CuPyBackend.zeros:1 +#: tensorcircuit.backends.jax_backend.JaxBackend.zeros:1 +#: tensorcircuit.backends.numpy_backend.NumpyBackend.zeros:1 +#: tensorcircuit.backends.pytorch_backend.PyTorchBackend.zeros:1 +#: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.zeros:1 +msgid "" +"Return a zeros-matrix of dimension `dim` Depending on specific backends, " +"`dim` has to be either an int (numpy, torch, tensorflow) or a `ShapeType`" +" object (for block-sparse backends)." +msgstr "" + +#: of tensorcircuit.backends.cupy_backend.CuPyBackend.zeros:5 +#: tensorcircuit.backends.jax_backend.JaxBackend.zeros:5 +#: tensorcircuit.backends.numpy_backend.NumpyBackend.zeros:5 +#: tensorcircuit.backends.pytorch_backend.PyTorchBackend.zeros:5 +#: tensorcircuit.backends.tensorflow_backend.TensorFlowBackend.zeros:5 +msgid "" +"Block-sparse behavior is currently not supported :param shape: The " +"dimension of the returned matrix. :type shape: int :param dtype: The " +"dtype of the returned matrix." +msgstr "" + +#: ../../source/api/backends/jax_backend.rst:2 +msgid "tensorcircuit.backends.jax_backend" +msgstr "" + +#: of tensorcircuit.backends.jax_backend:1 +msgid "Backend magic inherited from tensornetwork: jax backend" +msgstr "" + +#: of tensorcircuit.backends.jax_backend.JaxBackend:1 +msgid "" +"Bases: :py:class:`~tensornetwork.backends.jax.jax_backend.JaxBackend`, " +":py:class:`~tensorcircuit.backends.abstract_backend.ExtendedBackend`" +msgstr "" + +#: of tensorcircuit.backends.jax_backend.JaxBackend:1 +msgid "" +"See the original backend API at `jax backend " +"`_" +msgstr "" + +#: of tensornetwork.backends.jax.jax_backend.JaxBackend.diagonal:16 +#: tensornetwork.backends.jax.jax_backend.JaxBackend.diagonal:19 +#: tensornetwork.backends.jax.jax_backend.JaxBackend.trace:12 +#: tensornetwork.backends.jax.jax_backend.JaxBackend.trace:15 +msgid "" +"Axis to be used as the first/second axis of the 2D sub-arrays from which " +"the diagonals should be taken. Defaults to second last/last axis." +msgstr "" + +#: of tensornetwork.backends.jax.jax_backend.JaxBackend.eigs:1 +msgid "" +"Implicitly restarted Arnoldi method for finding the lowest eigenvector-" +"eigenvalue pairs of a linear operator `A`. `A` is a function implementing" +" the matrix-vector product." +msgstr "" + +#: of tensornetwork.backends.jax.jax_backend.JaxBackend.eigs:6 +msgid "" +"WARNING: This routine uses jax.jit to reduce runtimes. jitting is " +"triggered at the first invocation of `eigs`, and on any subsequent calls " +"if the python `id` of `A` changes, even if the formal definition of `A` " +"stays the same. Example: the following will jit once at the beginning, " +"and then never again:" +msgstr "" + +#: of tensornetwork.backends.jax.jax_backend.JaxBackend.eigs:12 +#: tensornetwork.backends.jax.jax_backend.JaxBackend.eigsh:12 +#: tensornetwork.backends.jax.jax_backend.JaxBackend.eigsh_lanczos:10 +msgid "```python import jax import numpy as np def A(H,x):" +msgstr "" + +#: of tensornetwork.backends.jax.jax_backend.JaxBackend.eigs:16 +#: tensornetwork.backends.jax.jax_backend.JaxBackend.eigs:31 +#: tensornetwork.backends.jax.jax_backend.JaxBackend.eigsh:16 +#: tensornetwork.backends.jax.jax_backend.JaxBackend.eigsh:31 +#: tensornetwork.backends.jax.jax_backend.JaxBackend.eigsh_lanczos:14 +#: tensornetwork.backends.jax.jax_backend.JaxBackend.eigsh_lanczos:29 +msgid "return jax.np.dot(H,x)" +msgstr "" + +#: of tensornetwork.backends.jax.jax_backend.JaxBackend.eigs:19 +#: tensornetwork.backends.jax.jax_backend.JaxBackend.eigsh:19 +#: tensornetwork.backends.jax.jax_backend.JaxBackend.eigsh_lanczos:17 +msgid "for n in range(100):" +msgstr "" + +#: of tensornetwork.backends.jax.jax_backend.JaxBackend.eigs:18 +msgid "" +"H = jax.np.array(np.random.rand(10,10)) x = " +"jax.np.array(np.random.rand(10,10)) res = eigs(A, [H],x) #jitting is " +"triggerd only at `n=0`" +msgstr "" + +#: of tensornetwork.backends.jax.jax_backend.JaxBackend.eigs:23 +#: tensornetwork.backends.jax.jax_backend.JaxBackend.eigsh:23 +#: tensornetwork.backends.jax.jax_backend.JaxBackend.eigsh_lanczos:21 +msgid "" +"The following code triggers jitting at every iteration, which results in " +"considerably reduced performance" +msgstr "" + +#: of tensornetwork.backends.jax.jax_backend.JaxBackend.eigs:26 +#: tensornetwork.backends.jax.jax_backend.JaxBackend.eigsh:26 +#: tensornetwork.backends.jax.jax_backend.JaxBackend.eigsh_lanczos:24 +msgid "```python import jax import numpy as np for n in range(100):" +msgstr "" + +#: of tensornetwork.backends.jax.jax_backend.JaxBackend.eigs:30 +#: tensornetwork.backends.jax.jax_backend.JaxBackend.eigsh:30 +#: tensornetwork.backends.jax.jax_backend.JaxBackend.eigsh_lanczos:28 +msgid "def A(H,x):" +msgstr "" + +#: of tensornetwork.backends.jax.jax_backend.JaxBackend.eigs:32 +msgid "" +"H = jax.np.array(np.random.rand(10,10)) x = " +"jax.np.array(np.random.rand(10,10)) res = eigs(A, [H],x) #jitting is " +"triggerd at every step `n`" +msgstr "" + +#: of tensornetwork.backends.jax.jax_backend.JaxBackend.eigs:37 +#: tensornetwork.backends.jax.jax_backend.JaxBackend.eigsh:37 +#: tensornetwork.backends.jax.jax_backend.JaxBackend.eigsh_lanczos:35 +msgid "" +"A (sparse) implementation of a linear operator. Call signature of `A` is " +"`res = A(vector, *args)`, where `vector` can be an arbitrary `Tensor`, " +"and `res.shape` has to be `vector.shape`." +msgstr "" + +#: of tensornetwork.backends.jax.jax_backend.JaxBackend.eigs:42 +#: tensornetwork.backends.jax.jax_backend.JaxBackend.eigsh:42 +msgid "" +"An initial vector for the algorithm. If `None`, a random initial `Tensor`" +" is created using the `backend.randn` method" +msgstr "" + +#: of tensornetwork.backends.jax.jax_backend.JaxBackend.eigs:45 +#: tensornetwork.backends.jax.jax_backend.JaxBackend.eigsh:45 +#: tensornetwork.backends.jax.jax_backend.JaxBackend.eigsh_lanczos:43 +msgid "" +"The dtype of the input `A`. If no `initial_state` is provided, a random " +"initial state with shape `shape` and dtype `dtype` is created." +msgstr "" + +#: of tensornetwork.backends.jax.jax_backend.JaxBackend.eigs:48 +#: tensornetwork.backends.jax.jax_backend.JaxBackend.eigsh:48 +msgid "The number of eigenvector-eigenvalue pairs to be computed." +msgstr "" + +#: of tensornetwork.backends.jax.jax_backend.JaxBackend.eigs:49 +#: tensornetwork.backends.jax.jax_backend.JaxBackend.eigsh:49 +msgid "" +"The desired precision of the eigenvalues. For the jax backend this has " +"currently no effect, and precision of eigenvalues is not guaranteed. This" +" feature may be added at a later point. To increase precision the caller " +"can either increase `maxiter` or `num_krylov_vecs`." +msgstr "" + +#: of tensornetwork.backends.jax.jax_backend.JaxBackend.eigs:53 +#: tensornetwork.backends.jax.jax_backend.JaxBackend.eigsh:53 +msgid "" +"Flag for targetting different types of eigenvalues. Currently supported " +"are `which = 'LR'` (larges real part) and `which = 'LM'` (larges " +"magnitude)." +msgstr "" + +#: of tensornetwork.backends.jax.jax_backend.JaxBackend.eigs:56 +#: tensornetwork.backends.jax.jax_backend.JaxBackend.eigsh:56 +msgid "" +"Maximum number of restarts. For `maxiter=0` the routine becomes " +"equivalent to a simple Arnoldi method." +msgstr "" + +#: of tensornetwork.backends.jax.jax_backend.JaxBackend.eigs:59 +#: tensornetwork.backends.jax.jax_backend.JaxBackend.eigsh:59 +msgid "" +"(eigvals, eigvecs) eigvals: A list of `numeig` eigenvalues eigvecs: A " +"list of `numeig` eigenvectors" +msgstr "" + +#: of tensornetwork.backends.jax.jax_backend.JaxBackend.eigs:62 +#: tensornetwork.backends.jax.jax_backend.JaxBackend.eigsh:62 +msgid "" +"eigvals: A list of `numeig` eigenvalues eigvecs: A list of `numeig` " +"eigenvectors" +msgstr "" + +#: of tensornetwork.backends.jax.jax_backend.JaxBackend.eigsh:1 +msgid "" +"Implicitly restarted Lanczos method for finding the lowest eigenvector-" +"eigenvalue pairs of a symmetric (hermitian) linear operator `A`. `A` is a" +" function implementing the matrix-vector product." +msgstr "" + +#: of tensornetwork.backends.jax.jax_backend.JaxBackend.eigsh:6 +msgid "" +"WARNING: This routine uses jax.jit to reduce runtimes. jitting is " +"triggered at the first invocation of `eigsh`, and on any subsequent calls" +" if the python `id` of `A` changes, even if the formal definition of `A` " +"stays the same. Example: the following will jit once at the beginning, " +"and then never again:" +msgstr "" + +#: of tensornetwork.backends.jax.jax_backend.JaxBackend.eigsh:18 +msgid "" +"H = jax.np.array(np.random.rand(10,10)) x = " +"jax.np.array(np.random.rand(10,10)) res = eigsh(A, [H],x) #jitting is " +"triggerd only at `n=0`" +msgstr "" + +#: of tensornetwork.backends.jax.jax_backend.JaxBackend.eigsh:32 +msgid "" +"H = jax.np.array(np.random.rand(10,10)) x = " +"jax.np.array(np.random.rand(10,10)) res = eigsh(A, [H],x) #jitting is " +"triggerd at every step `n`" +msgstr "" + +#: of tensornetwork.backends.jax.jax_backend.JaxBackend.eigsh_lanczos:1 +msgid "" +"Lanczos method for finding the lowest eigenvector-eigenvalue pairs of a " +"hermitian linear operator `A`. `A` is a function implementing the matrix-" +"vector product. WARNING: This routine uses jax.jit to reduce runtimes. " +"jitting is triggered at the first invocation of `eigsh_lanczos`, and on " +"any subsequent calls if the python `id` of `A` changes, even if the " +"formal definition of `A` stays the same. Example: the following will jit " +"once at the beginning, and then never again:" +msgstr "" + +#: of tensornetwork.backends.jax.jax_backend.JaxBackend.eigsh_lanczos:16 +msgid "" +"H = jax.np.array(np.random.rand(10,10)) x = " +"jax.np.array(np.random.rand(10,10)) res = eigsh_lanczos(A, [H],x) " +"#jitting is triggerd only at `n=0`" +msgstr "" + +#: of tensornetwork.backends.jax.jax_backend.JaxBackend.eigsh_lanczos:30 +msgid "" +"H = jax.np.array(np.random.rand(10,10)) x = " +"jax.np.array(np.random.rand(10,10)) res = eigsh_lanczos(A, [H],x) " +"#jitting is triggerd at every step `n`" +msgstr "" + +#: of tensornetwork.backends.jax.jax_backend.JaxBackend.eigsh_lanczos:46 +msgid "" +"The number of eigenvector-eigenvalue pairs to be computed. If `numeig > " +"1`, `reorthogonalize` has to be `True`." +msgstr "" + +#: of tensornetwork.backends.jax.jax_backend.JaxBackend.eigsh_lanczos:48 +msgid "" +"The desired precision of the eigenvalues. For the jax backend this has " +"currently no effect, and precision of eigenvalues is not guaranteed. This" +" feature may be added at a later point. To increase precision the caller " +"can increase `num_krylov_vecs`." +msgstr "" + +#: of tensornetwork.backends.jax.jax_backend.JaxBackend.eigsh_lanczos:57 +msgid "" +"The tridiagonal Operator is diagonalized every `ndiag` iterations to " +"check convergence. This has currently no effect for the jax backend, but " +"may be added at a later point." +msgstr "" + +#: of tensornetwork.backends.jax.jax_backend.JaxBackend.eigsh_lanczos:63 +msgid "" +"(eigvals, eigvecs) eigvals: A jax-array containing `numeig` lowest " +"eigenvalues eigvecs: A list of `numeig` lowest eigenvectors" +msgstr "" + +#: of tensornetwork.backends.jax.jax_backend.JaxBackend.eigsh_lanczos:66 +msgid "" +"eigvals: A jax-array containing `numeig` lowest eigenvalues eigvecs: A " +"list of `numeig` lowest eigenvectors" +msgstr "" + +#: of tensornetwork.backends.jax.jax_backend.JaxBackend.power:1 +msgid "" +"Returns the power of tensor a to the value of b. In the case b is a " +"tensor, then the power is by element" +msgstr "" + +#: of tensornetwork.backends.jax.jax_backend.JaxBackend.power:3 +msgid "with a as the base and b as the exponent." +msgstr "" + +#: of tensornetwork.backends.jax.jax_backend.JaxBackend.power:5 +msgid "In the case b is a scalar, then the power of each value in a" +msgstr "" + +#: of tensornetwork.backends.jax.jax_backend.JaxBackend.power:5 +msgid "is raised to the exponent of b." +msgstr "" + +#: of tensornetwork.backends.jax.jax_backend.JaxBackend.power:7 +msgid "The tensor that contains the base." +msgstr "" + +#: of tensornetwork.backends.jax.jax_backend.JaxBackend.power:8 +msgid "The tensor that contains the exponent or a single scalar." +msgstr "" + +#: of tensorcircuit.backends.jax_backend._qr_jax:1 +msgid "" +"Computes the QR decomposition of a tensor. See " +"tensornetwork.backends.tensorflow.decompositions for details." +msgstr "" + +#: of tensorcircuit.backends.jax_backend._rq_jax:1 +msgid "" +"Computes the RQ (reversed QR) decomposition of a tensor. See " +"tensornetwork.backends.tensorflow.decompositions for details." +msgstr "" + +#: of tensornetwork.backends.jax.jax_backend.JaxBackend.sign:4 +msgid "" +"For complex input the behaviour of this function may depend on the " +"backend. The Jax backend version returns y[i] = x[i]/sqrt(x[i]^2)." +msgstr "" + +#: ../../source/api/backends/numpy_backend.rst:2 +msgid "tensorcircuit.backends.numpy_backend" +msgstr "" + +#: of tensorcircuit.backends.numpy_backend:1 +msgid "Backend magic inherited from tensornetwork: numpy backend" +msgstr "" + +#: of tensorcircuit.backends.numpy_backend.NumpyBackend:1 +msgid "" +"Bases: " +":py:class:`~tensornetwork.backends.numpy.numpy_backend.NumPyBackend`, " +":py:class:`~tensorcircuit.backends.abstract_backend.ExtendedBackend`" +msgstr "" + +#: of tensorcircuit.backends.numpy_backend.NumpyBackend:1 +msgid "" +"see the original backend API at `numpy backend " +"`_" +msgstr "" + +#: of tensornetwork.backends.numpy.numpy_backend.NumPyBackend.eigs:1 +msgid "" +"Arnoldi method for finding the lowest eigenvector-eigenvalue pairs of a " +"linear operator `A`. If no `initial_state` is provided then `shape` and " +"`dtype` are required so that a suitable initial state can be randomly " +"generated. This is a wrapper for scipy.sparse.linalg.eigs which only " +"supports a subset of the arguments of scipy.sparse.linalg.eigs." +msgstr "" + +#: of tensornetwork.backends.numpy.numpy_backend.NumPyBackend.eigs:8 +msgid "A (sparse) implementation of a linear operator" +msgstr "" + +#: of tensornetwork.backends.numpy.numpy_backend.NumPyBackend.eigs:21 +msgid "" +"['LM' | 'SM' | 'LR' | 'SR' | 'LI'] Which `k` eigenvectors and eigenvalues" +" to find: 'LM' : largest magnitude 'SM' : smallest magnitude " +"'LR' : largest real part 'SR' : smallest real part 'LI' : largest" +" imaginary part" +msgstr "" + +#: of tensornetwork.backends.numpy.numpy_backend.NumPyBackend.eigs:21 +msgid "" +"['LM' | 'SM' | 'LR' | 'SR' | 'LI'] Which `k` eigenvectors and eigenvalues" +" to find:" +msgstr "" + +#: of tensornetwork.backends.numpy.numpy_backend.NumPyBackend.eigs:23 +msgid "" +"'LM' : largest magnitude 'SM' : smallest magnitude 'LR' : largest real " +"part 'SR' : smallest real part 'LI' : largest imaginary part" +msgstr "" + +#: of tensornetwork.backends.numpy.numpy_backend.NumPyBackend.eigs:32 +msgid "`np.ndarray`" +msgstr "" + +#: of tensornetwork.backends.numpy.numpy_backend.NumPyBackend.eigsh_lanczos:1 +msgid "" +"Lanczos method for finding the lowest eigenvector-eigenvalue pairs of a " +"linear operator `A`. :param A: A (sparse) implementation of a linear " +"operator." +msgstr "" + +#: of tensornetwork.backends.numpy.numpy_backend.NumPyBackend.eigsh_lanczos:16 +msgid "" +"The desired precision of the eigenvalus. Uses " +"`np.linalg.norm(eigvalsnew[0:numeig] - eigvalsold[0:numeig]) < tol` as " +"stopping criterion between two diagonalization steps of the tridiagonal " +"operator." +msgstr "" + +#: of tensornetwork.backends.numpy.numpy_backend.NumPyBackend.sign:1 +msgid "" +"Returns an elementwise tensor with entries y[i] = 1, 0, -1 tensor[i] > 0," +" == 0, and < 0 respectively." +msgstr "" + +#: of tensornetwork.backends.numpy.numpy_backend.NumPyBackend.sign:4 +msgid "" +"For complex input the behaviour of this function may depend on the " +"backend. The NumPy version returns y[i] = x[i]/sqrt(x[i]^2)." +msgstr "" + +#: ../../source/api/backends/pytorch_backend.rst:2 +msgid "tensorcircuit.backends.pytorch_backend" +msgstr "" + +#: of tensorcircuit.backends.pytorch_backend:1 +msgid "Backend magic inherited from tensornetwork: pytorch backend" +msgstr "" + +#: of tensorcircuit.backends.pytorch_backend.PyTorchBackend:1 +msgid "" +"Bases: " +":py:class:`~tensornetwork.backends.pytorch.pytorch_backend.PyTorchBackend`," +" :py:class:`~tensorcircuit.backends.abstract_backend.ExtendedBackend`" +msgstr "" + +#: of tensorcircuit.backends.pytorch_backend.PyTorchBackend:1 +msgid "" +"See the original backend API at `pytorch backend " +"`_" +msgstr "" + +#: of tensorcircuit.backends.pytorch_backend.PyTorchBackend:4 +msgid "" +"Note the functionality provided by pytorch backend is incomplete, it " +"currenly lacks native efficicent jit and vmap support." +msgstr "" + +#: of tensornetwork.backends.pytorch.pytorch_backend.PyTorchBackend.diagonal:16 +#: tensornetwork.backends.pytorch.pytorch_backend.PyTorchBackend.diagonal:20 +#: tensornetwork.backends.tensorflow.tensorflow_backend.TensorFlowBackend.diagonal:16 +#: tensornetwork.backends.tensorflow.tensorflow_backend.TensorFlowBackend.diagonal:24 +msgid "" +"Axis to be used as the first/second axis of the 2D sub-arrays from which " +"the diagonals should be taken. Defaults to second-last and last axis " +"(note this differs from the NumPy defaults)." +msgstr "" + +#: of +#: tensornetwork.backends.pytorch.pytorch_backend.PyTorchBackend.eigsh_lanczos:1 +msgid "" +"Lanczos method for finding the lowest eigenvector-eigenvalue pairs of a " +"`LinearOperator` `A`. :param A: A (sparse) implementation of a linear " +"operator." +msgstr "" + +#: of +#: tensornetwork.backends.pytorch.pytorch_backend.PyTorchBackend.eigsh_lanczos:8 +msgid "" +"An initial vector for the Lanczos algorithm. If `None`, a random initial " +"`Tensor` is created using the `torch.randn` method" +msgstr "" + +#: of +#: tensornetwork.backends.pytorch.pytorch_backend.PyTorchBackend.eigsh_lanczos:16 +msgid "" +"The desired precision of the eigenvalus. Uses " +"`torch.norm(eigvalsnew[0:numeig] - eigvalsold[0:numeig]) < tol` as " +"stopping criterion between two diagonalization steps of the tridiagonal " +"operator." +msgstr "" + +#: of tensorcircuit.backends.pytorch_backend._qr_torch:1 +#: tensorcircuit.backends.tensorflow_backend._qr_tf:1 +msgid "" +"Computes the QR decomposition of a tensor. The QR decomposition is " +"performed by treating the tensor as a matrix, with an effective left " +"(row) index resulting from combining the axes `tensor.shape[:pivot_axis]`" +" and an effective right (column) index resulting from combining the axes " +"`tensor.shape[pivot_axis:]`." +msgstr "" + +#: of tensorcircuit.backends.pytorch_backend._qr_torch:9 +#: tensorcircuit.backends.tensorflow_backend._qr_tf:9 +msgid "" +"If `tensor` had a shape (2, 3, 4, 5) and `pivot_axis` was 2, then `q` " +"would have shape (2, 3, 6), and `r` would have shape (6, 4, 5). The " +"output consists of two tensors `Q, R` such that:" +msgstr "" + +#: of tensorcircuit.backends.pytorch_backend._qr_torch:14 +#: tensorcircuit.backends.pytorch_backend._rq_torch:14 +#: tensorcircuit.backends.tensorflow_backend._qr_tf:14 +#: tensorcircuit.backends.tensorflow_backend._rq_tf:14 +msgid "Q[i1,...,iN, j] * R[j, k1,...,kM] == tensor[i1,...,iN, k1,...,kM]" +msgstr "" + +#: of tensorcircuit.backends.pytorch_backend._qr_torch:16 +#: tensorcircuit.backends.pytorch_backend._rq_torch:16 +#: tensorcircuit.backends.tensorflow_backend._qr_tf:16 +#: tensorcircuit.backends.tensorflow_backend._rq_tf:16 +msgid "Note that the output ordering matches numpy.linalg.svd rather than tf.svd." +msgstr "" + +#: of tensorcircuit.backends.pytorch_backend._qr_torch:22 +#: tensorcircuit.backends.pytorch_backend._rq_torch:22 +#: tensorcircuit.backends.tensorflow_backend._qr_tf:22 +#: tensorcircuit.backends.tensorflow_backend._rq_tf:22 +msgid "a bool indicating whether the tenor is diagonal non-negative matrix." +msgstr "" + +#: of tensorcircuit.backends.pytorch_backend._qr_torch:24 +#: tensorcircuit.backends.pytorch_backend._rq_torch:24 +#: tensorcircuit.backends.tensorflow_backend._qr_tf:24 +#: tensorcircuit.backends.tensorflow_backend._rq_tf:24 +msgid "Q, the left tensor factor, and R, the right tensor factor." +msgstr "" + +#: of tensorcircuit.backends.pytorch_backend._rq_torch:1 +#: tensorcircuit.backends.tensorflow_backend._rq_tf:1 +msgid "" +"Computes the RQ decomposition of a tensor. The QR decomposition is " +"performed by treating the tensor as a matrix, with an effective left " +"(row) index resulting from combining the axes `tensor.shape[:pivot_axis]`" +" and an effective right (column) index resulting from combining the axes " +"`tensor.shape[pivot_axis:]`." +msgstr "" + +#: of tensorcircuit.backends.pytorch_backend._rq_torch:9 +#: tensorcircuit.backends.tensorflow_backend._rq_tf:9 +msgid "" +"If `tensor` had a shape (2, 3, 4, 5) and `pivot_axis` was 2, then `r` " +"would have shape (2, 3, 6), and `q` would have shape (6, 4, 5). The " +"output consists of two tensors `Q, R` such that:" +msgstr "" + +#: of tensornetwork.backends.pytorch.pytorch_backend.PyTorchBackend.sign:4 +msgid "" +"For complex input the behaviour of this function may depend on the " +"backend. The PyTorch version is not implemented in this case." +msgstr "" + +#: of tensornetwork.backends.pytorch.pytorch_backend.PyTorchBackend.trace:10 +msgid "" +"In the PyTorch backend the trace is always over the main diagonal of the " +"last two entries." +msgstr "" + +#: of tensornetwork.backends.pytorch.pytorch_backend.PyTorchBackend.trace:14 +msgid "" +"Offset of the diagonal from the main diagonal. This argument is not " +"supported by the PyTorch backend and an error will be raised if they are" +" specified." +msgstr "" + +#: of tensornetwork.backends.pytorch.pytorch_backend.PyTorchBackend.trace:18 +#: tensornetwork.backends.pytorch.pytorch_backend.PyTorchBackend.trace:24 +msgid "" +"Axis to be used as the first/second axis of the 2D sub-arrays from which " +"the diagonals should be taken. Defaults to first/second axis. These " +"arguments are not supported by the PyTorch backend and an error will be " +"raised if they are specified." +msgstr "" + +#: of tensorcircuit.backends.pytorch_backend.torch_jit_func:1 +msgid "" +"Delay the tracing of torch jit to the first run time: consistent with tf " +"and jax mechanism" +msgstr "" + +#: ../../source/api/backends/tensorflow_backend.rst:2 +msgid "tensorcircuit.backends.tensorflow_backend" +msgstr "" + +#: of tensorcircuit.backends.tensorflow_backend:1 +msgid "Backend magic inherited from tensornetwork: tensorflow backend" +msgstr "" + +#: of tensorcircuit.backends.tensorflow_backend.TensorFlowBackend:1 +msgid "" +"Bases: " +":py:class:`~tensornetwork.backends.tensorflow.tensorflow_backend.TensorFlowBackend`," +" :py:class:`~tensorcircuit.backends.abstract_backend.ExtendedBackend`" +msgstr "" + +#: of tensorcircuit.backends.tensorflow_backend.TensorFlowBackend:1 +msgid "" +"See the original backend API at `tensorflow backend " +"`_" +msgstr "" + +#: of +#: tensornetwork.backends.tensorflow.tensorflow_backend.TensorFlowBackend.diagonal:16 +#: tensornetwork.backends.tensorflow.tensorflow_backend.TensorFlowBackend.diagonal:24 +msgid "" +"Axis to be used as the first/second axis of the 2D sub-arrays from which " +"the diagonals should be taken. Defaults to second-last and last axis " +"(note this differs from the NumPy defaults). These arguments are not " +"supported in the TensorFlow backend and an error will be raised if they " +"are specified." +msgstr "" + +#: of +#: tensornetwork.backends.tensorflow.tensorflow_backend.TensorFlowBackend.diagonal:21 +#: tensornetwork.backends.tensorflow.tensorflow_backend.TensorFlowBackend.diagonal:29 +msgid "" +"These arguments are not supported in the TensorFlow backend and an error " +"will be raised if they are specified." +msgstr "" + +#: of +#: tensornetwork.backends.tensorflow.tensorflow_backend.TensorFlowBackend.sign:4 +msgid "" +"For complex input the behaviour of this function may depend on the " +"backend. The TensorFlow version returns y[i] = x[i] / abs(x[i])." +msgstr "" + +#: of tensorcircuit.backends.tensorflow_backend._svd_tf:21 +msgid "" +"If both `max_singular_values` snd `max_truncation_error` are specified, " +"the number of retained singular values will be `min(max_singular_values, " +"nsv_auto_trunc)`, where `nsv_auto_trunc` is the number of singular values" +" that must be kept to maintain a truncation error smaller than " +"`max_truncation_error`." +msgstr "" + +#: of tensorcircuit.backends.tensorflow_backend._svd_tf:27 +msgid "" +"The output consists of three tensors `u, s, vh` such that: ```python " +"u[i1,...,iN, j] * s[j] * vh[j, k1,...,kM] == tensor[i1,...,iN, k1,...,kM]" +" ``` Note that the output ordering matches numpy.linalg.svd rather than " +"tf.svd." +msgstr "" + +#: of tensorcircuit.backends.tensorflow_backend._svd_tf:33 +msgid "" +"Args: tf: The tensorflow module. tensor: A tensor to be decomposed. " +"pivot_axis: Where to split the tensor's axes before flattening into a" +msgstr "" + +#: of tensorcircuit.backends.tensorflow_backend._svd_tf:37 +msgid "matrix." +msgstr "" + +#: of tensorcircuit.backends.tensorflow_backend._svd_tf:38 +msgid "max_singular_values: The number of singular values to keep, or `None` to" +msgstr "" + +#: of tensorcircuit.backends.tensorflow_backend._svd_tf:39 +msgid "keep them all." +msgstr "" + +#: of tensorcircuit.backends.tensorflow_backend._svd_tf:40 +msgid "" +"max_truncation_error: The maximum allowed truncation error or `None` to " +"not" +msgstr "" + +#: of tensorcircuit.backends.tensorflow_backend._svd_tf:41 +msgid "do any truncation." +msgstr "" + +#: of tensorcircuit.backends.tensorflow_backend._svd_tf:42 +msgid "relative: Multiply `max_truncation_err` with the largest singular value." +msgstr "" + +#: of tensorcircuit.backends.tensorflow_backend._svd_tf:44 +msgid "" +"Returns: u: Left tensor factor. s: Vector of ordered singular values from" +" largest to smallest. vh: Right tensor factor. s_rest: Vector of " +"discarded singular values (length zero if no" +msgstr "" + +#: of +#: tensornetwork.backends.tensorflow.tensorflow_backend.TensorFlowBackend.trace:11 +msgid "" +"Offset of the diagonal from the main diagonal. This argument is not " +"supported in the TensorFlow backend and an error will be raised if they " +"are specified." +msgstr "" + +#: of +#: tensornetwork.backends.tensorflow.tensorflow_backend.TensorFlowBackend.trace:15 +#: tensornetwork.backends.tensorflow.tensorflow_backend.TensorFlowBackend.trace:21 +msgid "" +"Axis to be used as the first/second axis of the 2D sub-arrays from which " +"the diagonals should be taken. Defaults to first/second axis. These " +"arguments are not supported in the TensorFlow backend and an error will " +"be raised if they are specified." +msgstr "" + +#: ../../source/api/basecircuit.rst:2 +msgid "tensorcircuit.basecircuit" +msgstr "" + +#: of tensorcircuit.basecircuit:1 +msgid "Quantum circuit: common methods for all circuit classes as MixIn" +msgstr "" + +#: of tensorcircuit.basecircuit.BaseCircuit:1 +#: tensorcircuit.mpscircuit.MPSCircuit:1 +msgid "Bases: :py:class:`~tensorcircuit.abstractcircuit.AbstractCircuit`" +msgstr "" + +#: of tensorcircuit.basecircuit.BaseCircuit.amplitude:1 +msgid "" +"Returns the amplitude of the circuit given the bitstring l. For state " +"simulator, it computes :math:`\\langle l\\vert \\psi\\rangle`, for " +"density matrix simulator, it computes :math:`Tr(\\rho \\vert l\\rangle " +"\\langle 1\\vert)` Note how these two are different up to a square " +"operation." +msgstr "" + +#: of tensorcircuit.basecircuit.BaseCircuit.amplitude:16 +msgid "The bitstring of 0 and 1s." +msgstr "" + +#: of tensorcircuit.basecircuit.BaseCircuit.amplitude:18 +msgid "The amplitude of the circuit." +msgstr "" + +#: of tensorcircuit.basecircuit.BaseCircuit.copy:1 +msgid "copy all nodes and dangling edges correspondingly" +msgstr "" + +#: of tensorcircuit.basecircuit.BaseCircuit.expectation_before:1 +msgid "" +"Get the tensor network in the form of a list of nodes for the expectation" +" calculation before the real contraction" +msgstr "" + +#: of tensorcircuit.basecircuit.BaseCircuit.expectation_before:4 +#: tensorcircuit.interfaces.torch.torch_interface_kws:24 +#: tensorcircuit.quantum.sample2count:7 tensorcircuit.utils.benchmark:7 +msgid "_description_, defaults to True" +msgstr "" + +#: of tensorcircuit.basecircuit.BaseCircuit.get_quvector:1 +msgid "" +"Get the representation of the output state in the form of ``QuVector`` " +"while maintaining the circuit uncomputed" +msgstr "" + +#: of tensorcircuit.basecircuit.BaseCircuit.get_quvector:4 +#: tensorcircuit.mpscircuit.MPSCircuit.get_quvector:4 +msgid "``QuVector`` representation of the output state from the circuit" +msgstr "" + +#: of tensorcircuit.basecircuit.BaseCircuit.measure_jit:1 +msgid "" +"Take measurement to the given quantum lines. This method is jittable is " +"and about 100 times faster than unjit version!" +msgstr "" + +#: of tensorcircuit.basecircuit.BaseCircuit.measure_jit:4 +#: tensorcircuit.circuit.Circuit.measure_reference:16 +#: tensorcircuit.mpscircuit.MPSCircuit.measure:3 +msgid "Measure on which quantum line." +msgstr "" + +#: of tensorcircuit.basecircuit.BaseCircuit.measure_jit:6 +#: tensorcircuit.circuit.Circuit.measure_reference:17 +#: tensorcircuit.mpscircuit.MPSCircuit.measure:5 +msgid "If true, theoretical probability is also returned." +msgstr "" + +#: of tensorcircuit.basecircuit.BaseCircuit.measure_jit:8 +#: tensorcircuit.mpscircuit.MPSCircuit.measure:7 +msgid "external randomness, with shape [index], defaults to None" +msgstr "" + +#: of tensorcircuit.basecircuit.BaseCircuit.measure_jit:10 +#: tensorcircuit.circuit.Circuit.measure_reference:18 +#: tensorcircuit.mpscircuit.MPSCircuit.measure:9 +msgid "The sample output and probability (optional) of the quantum line." +msgstr "" + +#: of tensorcircuit.basecircuit.BaseCircuit.perfect_sampling:1 +msgid "" +"Sampling bistrings from the circuit output based on quantum amplitudes. " +"Reference: arXiv:1201.3974." +msgstr "" + +#: of tensorcircuit.basecircuit.BaseCircuit.perfect_sampling:4 +msgid "external randomness, with shape [nqubits], defaults to None" +msgstr "" + +#: of tensorcircuit.basecircuit.BaseCircuit.perfect_sampling:6 +msgid "Sampled bit string and the corresponding theoretical probability." +msgstr "" + +#: of tensorcircuit.basecircuit.BaseCircuit.probability:1 +msgid "get the 2^n length probability vector over computational basis" +msgstr "" + +#: of tensorcircuit.basecircuit.BaseCircuit.probability:3 +msgid "probability vector" +msgstr "" + +#: of tensorcircuit.basecircuit.BaseCircuit.readouterror_bs:1 +msgid "" +"Apply readout error to original probabilities of bit string and return " +"the noisy probabilities." +msgstr "" + +#: of tensorcircuit.basecircuit.BaseCircuit.readouterror_bs:10 +msgid "list of readout error for each qubits." +msgstr "" + +#: of tensorcircuit.basecircuit.BaseCircuit.readouterror_bs:12 +msgid "probabilities of bit string" +msgstr "" + +#: of tensorcircuit.basecircuit.BaseCircuit.replace_inputs:1 +msgid "Replace the input state with the circuit structure unchanged." +msgstr "" + +#: of tensorcircuit.basecircuit.BaseCircuit.replace_inputs:3 +msgid "Input wavefunction." +msgstr "" + +#: of tensorcircuit.basecircuit.BaseCircuit.sample:1 +msgid "batched sampling from state or circuit tensor network directly" +msgstr "" + +#: of tensorcircuit.basecircuit.BaseCircuit.sample:3 +msgid "number of samples, defaults to None" +msgstr "" + +#: of tensorcircuit.basecircuit.BaseCircuit.sample:5 +msgid "" +"if true, we sample from the final state if memory allows, True is " +"preferred, defaults to False" +msgstr "" + +#: of tensorcircuit.basecircuit.BaseCircuit.sample:8 +msgid "readout_error, defaults to None" +msgstr "" + +#: of tensorcircuit.basecircuit.BaseCircuit.sample:10 +msgid "" +"sample format, defaults to None as backward compatibility check the doc " +"in :py:meth:`tensorcircuit.quantum.measurement_results`" +msgstr "" + +#: of tensorcircuit.basecircuit.BaseCircuit.sample:13 +#: tensorcircuit.cloud.abstraction.Task.results:5 +#: tensorcircuit.quantum.measurement_counts:45 +#: tensorcircuit.quantum.sample2all:10 +msgid "alias for the argument ``format``" +msgstr "" + +#: of tensorcircuit.basecircuit.BaseCircuit.sample:15 +msgid "random generator, defaults to None" +msgstr "" + +#: of tensorcircuit.basecircuit.BaseCircuit.sample:17 +#: tensorcircuit.basecircuit.BaseCircuit.sample_expectation_ps:41 +#: tensorcircuit.quantum.measurement_counts:52 +msgid "" +"external randomness given by tensor uniformly from [0, 1], if set, can " +"overwrite random_generator" +msgstr "" + +#: of tensorcircuit.basecircuit.BaseCircuit.sample:20 +msgid "" +"List (if batch) of tuple (binary configuration tensor and corresponding " +"probability) if the format is None, and consistent with format when given" +msgstr "" + +#: of tensorcircuit.basecircuit.BaseCircuit.sample_expectation_ps:1 +msgid "" +"Compute the expectation with given Pauli string with measurement shots " +"numbers" +msgstr "" + +#: of tensorcircuit.basecircuit.BaseCircuit.sample_expectation_ps:31 +msgid "index for Pauli X, defaults to None" +msgstr "" + +#: of tensorcircuit.basecircuit.BaseCircuit.sample_expectation_ps:33 +msgid "index for Pauli Y, defaults to None" +msgstr "" + +#: of tensorcircuit.basecircuit.BaseCircuit.sample_expectation_ps:35 +msgid "index for Pauli Z, defaults to None" +msgstr "" + +#: of tensorcircuit.basecircuit.BaseCircuit.sample_expectation_ps:37 +#: tensorcircuit.noisemodel.sample_expectation_ps_noisfy:15 +msgid "" +"number of measurement shots, defaults to None, indicating analytical " +"result" +msgstr "" + +#: of tensorcircuit.basecircuit.BaseCircuit.sample_expectation_ps:39 +#: tensorcircuit.quantum.measurement_counts:50 +msgid "random_generator, defaults to None" +msgstr "" + +#: of tensorcircuit.basecircuit.BaseCircuit.sample_expectation_ps:44 +msgid "readout_error, defaults to None. Overrided if noise_conf is provided." +msgstr "" + +#: of tensorcircuit.basecircuit.BaseCircuit.to_graphviz:1 +msgid "" +"Not an ideal visualization for quantum circuit, but reserve here as a " +"general approach to show the tensornetwork [Deprecated, use " +"``Circuit.vis_tex`` or ``Circuit.draw`` instead]" +msgstr "" + +#: ../../source/api/channels.rst:2 +msgid "tensorcircuit.channels" +msgstr "" + +#: of tensorcircuit.channels:1 +msgid "Some common noise quantum channels." +msgstr "" + +#: of tensorcircuit.channels.KrausList:1 +msgid "Bases: :py:class:`list`" +msgstr "" + +#: of tensorcircuit.channels.KrausList.append:1 +msgid "Append object to the end of the list." +msgstr "" + +#: of tensorcircuit.channels.KrausList.clear:1 +msgid "Remove all items from list." +msgstr "" + +#: of tensorcircuit.channels.KrausList.copy:1 +msgid "Return a shallow copy of the list." +msgstr "" + +#: of tensorcircuit.channels.KrausList.count:1 +msgid "Return number of occurrences of value." +msgstr "" + +#: of tensorcircuit.channels.KrausList.extend:1 +msgid "Extend list by appending elements from the iterable." +msgstr "" + +#: of tensorcircuit.channels.KrausList.index:1 +msgid "Return first index of value." +msgstr "" + +#: of tensorcircuit.channels.KrausList.index:3 +#: tensorcircuit.channels.KrausList.remove:3 +msgid "Raises ValueError if the value is not present." +msgstr "" + +#: of tensorcircuit.channels.KrausList.insert:1 +msgid "Insert object before index." +msgstr "" + +#: of tensorcircuit.channels.KrausList.pop:1 +msgid "Remove and return item at index (default last)." +msgstr "" + +#: of tensorcircuit.channels.KrausList.pop:3 +msgid "Raises IndexError if list is empty or index is out of range." +msgstr "" + +#: of tensorcircuit.channels.KrausList.remove:1 +msgid "Remove first occurrence of value." +msgstr "" + +#: of tensorcircuit.channels.KrausList.reverse:1 +msgid "Reverse *IN PLACE*." +msgstr "" + +#: of tensorcircuit.channels.KrausList.sort:1 +msgid "Sort the list in ascending order and return None." +msgstr "" + +#: of tensorcircuit.channels.KrausList.sort:3 +msgid "" +"The sort is in-place (i.e. the list itself is modified) and stable (i.e. " +"the order of two equal elements is maintained)." +msgstr "" + +#: of tensorcircuit.channels.KrausList.sort:6 +msgid "" +"If a key function is given, apply it once to each list item and sort " +"them, ascending or descending, according to their function values." +msgstr "" + +#: of tensorcircuit.channels.KrausList.sort:9 +msgid "The reverse flag can be set to sort in descending order." +msgstr "" + +#: of tensorcircuit.channels.amplitudedampingchannel:1 +msgid "" +"Return an amplitude damping channel. Notice: Amplitude damping " +"corrspondings to p = 1." +msgstr "" + +#: of tensorcircuit.channels.amplitudedampingchannel:4 +msgid "" +"\\sqrt{p}\n" +"\\begin{bmatrix}\n" +" 1 & 0\\\\\n" +" 0 & \\sqrt{1-\\gamma}\\\\\n" +"\\end{bmatrix}\\qquad\n" +"\\sqrt{p}\n" +"\\begin{bmatrix}\n" +" 0 & \\sqrt{\\gamma}\\\\\n" +" 0 & 0\\\\\n" +"\\end{bmatrix}\\qquad\n" +"\\sqrt{1-p}\n" +"\\begin{bmatrix}\n" +" \\sqrt{1-\\gamma} & 0\\\\\n" +" 0 & 1\\\\\n" +"\\end{bmatrix}\\qquad\n" +"\\sqrt{1-p}\n" +"\\begin{bmatrix}\n" +" 0 & 0\\\\\n" +" \\sqrt{\\gamma} & 0\\\\\n" +"\\end{bmatrix}\n" +"\n" +msgstr "" + +#: of tensorcircuit.channels.amplitudedampingchannel:31 +msgid "the damping parameter of amplitude (:math:`\\gamma`)" +msgstr "" + +#: of tensorcircuit.channels.amplitudedampingchannel:33 +msgid ":math:`p`" +msgstr "" + +#: of tensorcircuit.channels.amplitudedampingchannel:35 +msgid "An amplitude damping channel with given :math:`\\gamma` and :math:`p`" +msgstr "" + +#: of tensorcircuit.channels.check_rep_transformation:1 +msgid "Check the convertation between those representations." +msgstr "" + +#: of tensorcircuit.channels.check_rep_transformation:3 +#: tensorcircuit.channels.kraus_to_super:23 +#: tensorcircuit.channels.kraus_to_super_gate:6 +msgid "A sequence of Gate" +msgstr "" + +#: of tensorcircuit.channels.check_rep_transformation:5 +#: tensorcircuit.channels.evol_kraus:13 tensorcircuit.channels.evol_superop:11 +msgid "Initial density matrix" +msgstr "" + +#: of tensorcircuit.channels.check_rep_transformation:7 +msgid "Whether print Kraus and new Kraus operators, defaults to False" +msgstr "" + +#: of tensorcircuit.channels.choi_to_kraus:1 +msgid "Convert the Choi matrix representation to Kraus operator representation." +msgstr "" + +#: of tensorcircuit.channels.choi_to_kraus:3 +msgid "This can be done by firstly geting eigen-decomposition of Choi-matrix" +msgstr "" + +#: of tensorcircuit.channels.choi_to_kraus:5 +msgid "" +"\\Lambda = \\sum_k \\gamma_k \\vert \\phi_k \\rangle \\langle \\phi_k " +"\\vert\n" +"\n" +msgstr "" + +#: of tensorcircuit.channels.choi_to_kraus:8 +msgid "Then define Kraus operators" +msgstr "" + +#: of tensorcircuit.channels.choi_to_kraus:10 +msgid "" +"K_k = \\sqrt{\\gamma_k} V_k\n" +"\n" +msgstr "" + +#: of tensorcircuit.channels.choi_to_kraus:13 +msgid "" +"where :math:`\\gamma_k\\geq0` and :math:`\\phi_k` is the col-val " +"vectorization of :math:`V_k` ." +msgstr "" + +#: of tensorcircuit.channels.choi_to_kraus tensorcircuit.channels.evol_kraus +#: tensorcircuit.channels.evol_superop +#: tensorcircuit.channels.kraus_identity_check +#: tensorcircuit.channels.kraus_to_super tensorcircuit.channels.super_to_choi +msgid "Examples" +msgstr "" + +#: of tensorcircuit.channels.choi_to_kraus:24 +#: tensorcircuit.channels.choi_to_super:3 +#: tensorcircuit.channels.kraus_to_choi:5 +#: tensorcircuit.channels.super_to_choi:28 +msgid "Choi matrix" +msgstr "" + +#: of tensorcircuit.channels.choi_to_kraus:26 +msgid "" +"A dictionary to restrict the calculation of kraus matrices. The " +"restriction can be the number of kraus terms, which is jitable. It can " +"also be the truncattion error, which is not jitable." +msgstr "" + +#: of tensorcircuit.channels.choi_to_kraus:29 +#: tensorcircuit.channels.krausgate_to_krausmatrix:5 +#: tensorcircuit.channels.krausmatrix_to_krausgate:5 +msgid "A list of Kraus operators" +msgstr "" + +#: of tensorcircuit.channels.choi_to_super:1 +msgid "Convert from Choi representation to Superoperator representation." +msgstr "" + +#: of tensorcircuit.channels.choi_to_super:5 +#: tensorcircuit.channels.evol_superop:13 +#: tensorcircuit.channels.super_to_choi:26 +#: tensorcircuit.channels.super_to_kraus:3 +msgid "Superoperator" +msgstr "" + +#: of tensorcircuit.channels.composedkraus:1 +msgid "Compose the noise channels" +msgstr "" + +#: of tensorcircuit.channels.composedkraus:3 +msgid "One noise channel" +msgstr "" + +#: of tensorcircuit.channels.composedkraus:5 +msgid "Another noise channel" +msgstr "" + +#: of tensorcircuit.channels.composedkraus:7 +msgid "Composed nosie channel" +msgstr "" + +#: of tensorcircuit.channels.depolarizingchannel:1 +msgid "Return a Depolarizing Channel" +msgstr "" + +#: of tensorcircuit.channels.depolarizingchannel:3 +msgid "" +"\\sqrt{1-p_x-p_y-p_z}\n" +"\\begin{bmatrix}\n" +" 1 & 0\\\\\n" +" 0 & 1\\\\\n" +"\\end{bmatrix}\\qquad\n" +"\\sqrt{p_x}\n" +"\\begin{bmatrix}\n" +" 0 & 1\\\\\n" +" 1 & 0\\\\\n" +"\\end{bmatrix}\\qquad\n" +"\\sqrt{p_y}\n" +"\\begin{bmatrix}\n" +" 0 & -1j\\\\\n" +" 1j & 0\\\\\n" +"\\end{bmatrix}\\qquad\n" +"\\sqrt{p_z}\n" +"\\begin{bmatrix}\n" +" 1 & 0\\\\\n" +" 0 & -1\\\\\n" +"\\end{bmatrix}\n" +"\n" +msgstr "" + +#: of tensorcircuit.channels.depolarizingchannel:30 +msgid ":math:`p_x`" +msgstr "" + +#: of tensorcircuit.channels.depolarizingchannel:32 +msgid ":math:`p_y`" +msgstr "" + +#: of tensorcircuit.channels.depolarizingchannel:34 +msgid ":math:`p_z`" +msgstr "" + +#: of tensorcircuit.channels.depolarizingchannel:36 +#: tensorcircuit.channels.generaldepolarizingchannel:39 +#: tensorcircuit.channels.isotropicdepolarizingchannel:28 +msgid "Sequences of Gates" +msgstr "" + +#: of tensorcircuit.channels.evol_kraus:1 +msgid "The dynamic evolution according to Kraus operators." +msgstr "" + +#: of tensorcircuit.channels.evol_kraus:3 +msgid "" +"\\rho' = \\sum_{k} K_k \\rho K_k^{\\dagger}\n" +"\n" +msgstr "" + +#: of tensorcircuit.channels.evol_kraus:15 +#: tensorcircuit.channels.super_to_kraus:5 +msgid "A list of Kraus operator" +msgstr "" + +#: of tensorcircuit.channels.evol_kraus:17 +#: tensorcircuit.channels.evol_superop:15 +msgid "Final density matrix" +msgstr "" + +#: of tensorcircuit.channels.evol_superop:1 +msgid "The dynamic evolution according to Superoperator." +msgstr "" + +#: of tensorcircuit.channels.generaldepolarizingchannel:1 +msgid "" +"Return a depolarizing channel. If :math:`p` is a float number, the one " +"qubit channel is" +msgstr "" + +#: of tensorcircuit.channels.generaldepolarizingchannel:4 +msgid "\\mathcal{E}(\\rho) = (1 - 3p)\\rho + p(X\\rho X + Y\\rho Y + Z\\rho Z)" +msgstr "" + +#: of tensorcircuit.channels.generaldepolarizingchannel:8 +msgid "Or alternatively" +msgstr "" + +#: of tensorcircuit.channels.generaldepolarizingchannel:10 +msgid "\\mathcal{E}(\\rho) = 4p \\frac{I}{2} + (1 - 4p) \\rho" +msgstr "" + +#: of tensorcircuit.channels.generaldepolarizingchannel:16 +msgid "" +"The definition of ``p`` in this method is different from " +":func:`isotropicdepolarizingchannel`." +msgstr "" + +#: of tensorcircuit.channels.generaldepolarizingchannel:19 +msgid "And if :math:`p` is a sequence, the one qubit channel is" +msgstr "" + +#: of tensorcircuit.channels.generaldepolarizingchannel:21 +msgid "" +"\\mathcal{E}(\\rho) = (1 - \\sum_i p_i) \\rho + p_1 X\\rho X + p_2 Y\\rho" +" Y + p_3 \\rho Z" +msgstr "" + +#: of tensorcircuit.channels.generaldepolarizingchannel:25 +msgid "The logic for two-qubit or more-qubit channel follows similarly." +msgstr "" + +#: of tensorcircuit.channels.generaldepolarizingchannel:35 +msgid "parameter for each Pauli channel" +msgstr "" + +#: of tensorcircuit.channels.generaldepolarizingchannel:37 +#: tensorcircuit.channels.isotropicdepolarizingchannel:26 +msgid "number of qubits, defaults 1" +msgstr "" + +#: of tensorcircuit.channels.is_hermitian_matrix:1 +msgid "Test if an array is a Hermitian matrix" +msgstr "" + +#: of tensorcircuit.channels.is_hermitian_matrix:3 +msgid "Matrix" +msgstr "" + +#: of tensorcircuit.channels.is_hermitian_matrix:5 +msgid "_description_, defaults to 1e-8" +msgstr "" + +#: of tensorcircuit.channels.is_hermitian_matrix:7 +msgid "_description_, defaults to 1e-5" +msgstr "" + +#: of tensorcircuit.channels.isotropicdepolarizingchannel:1 +msgid "Return an isotropic depolarizing channel." +msgstr "" + +#: of tensorcircuit.channels.isotropicdepolarizingchannel:3 +msgid "\\mathcal{E}(\\rho) = (1 - p)\\rho + p/(4^n-1)\\sum_j P_j \\rho P_j" +msgstr "" + +#: of tensorcircuit.channels.isotropicdepolarizingchannel:7 +msgid "" +"where $n$ is the number of qubits and $P_j$ are $n$-qubit Pauli strings " +"except $I$. Or alternatively" +msgstr "" + +#: of tensorcircuit.channels.isotropicdepolarizingchannel:10 +msgid "" +"\\mathcal{E}(\\rho) = \\frac{4^n}{4^n-1}p \\frac{I}{2} + (1 - " +"\\frac{4^n}{4^n-1}p) \\rho" +msgstr "" + +#: of tensorcircuit.channels.isotropicdepolarizingchannel:16 +msgid "" +"The definition of ``p`` in this method is different from " +":func:`generaldepolarizingchannel`." +msgstr "" + +#: of tensorcircuit.channels.isotropicdepolarizingchannel:24 +msgid "error probability" +msgstr "" + +#: of tensorcircuit.channels.kraus_identity_check:1 +msgid "Check identity of Kraus operators." +msgstr "" + +#: of tensorcircuit.channels.kraus_identity_check:3 +msgid "" +"\\sum_{k}^{} K_k^{\\dagger} K_k = I\n" +"\n" +msgstr "" + +#: of tensorcircuit.channels.kraus_identity_check:12 +msgid "List of Kraus operators." +msgstr "" + +#: of tensorcircuit.channels.kraus_to_choi:1 +msgid "Convert from Kraus representation to Choi representation." +msgstr "" + +#: of tensorcircuit.channels.kraus_to_choi:3 +msgid "A list Kraus operators" +msgstr "" + +#: of tensorcircuit.channels.kraus_to_super:1 +msgid "" +"Convert Kraus operator representation to Louivile-Superoperator " +"representation." +msgstr "" + +#: of tensorcircuit.channels.kraus_to_super:3 +msgid "" +"In the col-vec basis, the evolution of a state :math:`\\rho` in terms of " +"tensor components of superoperator :math:`\\varepsilon` can be expressed " +"as" +msgstr "" + +#: of tensorcircuit.channels.kraus_to_super:6 +msgid "" +"\\rho'_{mn} = \\sum_{\\mu \\nu}^{} \\varepsilon_{nm,\\nu \\mu} " +"\\rho_{\\mu \\nu}\n" +"\n" +msgstr "" + +#: of tensorcircuit.channels.kraus_to_super:9 +msgid "" +"The superoperator :math:`\\varepsilon` must make the dynamic map from " +":math:`\\rho` to :math:`\\rho'` to satisfy hermitian-perserving (HP), " +"trace-preserving (TP), and completely positive (CP)." +msgstr "" + +#: of tensorcircuit.channels.kraus_to_super:12 +msgid "We can construct the superoperator from Kraus operators by" +msgstr "" + +#: of tensorcircuit.channels.kraus_to_super:14 +msgid "" +"\\varepsilon = \\sum_{k} K_k^{*} \\otimes K_k\n" +"\n" +msgstr "" + +#: of tensorcircuit.channels.kraus_to_super:25 +msgid "The corresponding Tensor of Superoperator" +msgstr "" + +#: of tensorcircuit.channels.kraus_to_super_gate:1 +msgid "Convert Kraus operators to one Tensor (as one Super Gate)." +msgstr "" + +#: of tensorcircuit.channels.kraus_to_super_gate:3 +msgid "" +"\\sum_{k}^{} K_k \\otimes K_k^{*}\n" +"\n" +msgstr "" + +#: of tensorcircuit.channels.kraus_to_super_gate:8 +msgid "The corresponding Tensor of the list of Kraus operators" +msgstr "" + +#: of tensorcircuit.channels.krausgate_to_krausmatrix:1 +msgid "Convert Kraus of Gate form to Matrix form." +msgstr "" + +#: of tensorcircuit.channels.krausgate_to_krausmatrix:3 +#: tensorcircuit.channels.krausmatrix_to_krausgate:3 +msgid "A list of Kraus" +msgstr "" + +#: of tensorcircuit.channels.krausmatrix_to_krausgate:1 +msgid "Convert Kraus of Matrix form to Gate form." +msgstr "" + +#: of tensorcircuit.channels.phasedampingchannel:1 +msgid "Return a phase damping channel with given :math:`\\gamma`" +msgstr "" + +#: of tensorcircuit.channels.phasedampingchannel:3 +msgid "" +"\\begin{bmatrix}\n" +" 1 & 0\\\\\n" +" 0 & \\sqrt{1-\\gamma}\\\\\n" +"\\end{bmatrix}\\qquad\n" +"\\begin{bmatrix}\n" +" 0 & 0\\\\\n" +" 0 & \\sqrt{\\gamma}\\\\\n" +"\\end{bmatrix}\n" +"\n" +msgstr "" + +#: of tensorcircuit.channels.phasedampingchannel:18 +msgid "The damping parameter of phase (:math:`\\gamma`)" +msgstr "" + +#: of tensorcircuit.channels.phasedampingchannel:20 +msgid "A phase damping channel with given :math:`\\gamma`" +msgstr "" + +#: of tensorcircuit.channels.resetchannel:1 +#: tensorcircuit.channels.resetchannel:18 +msgid "Reset channel" +msgstr "" + +#: of tensorcircuit.channels.resetchannel:3 +msgid "" +"\\begin{bmatrix}\n" +" 1 & 0\\\\\n" +" 0 & 0\\\\\n" +"\\end{bmatrix}\\qquad\n" +"\\begin{bmatrix}\n" +" 0 & 1\\\\\n" +" 0 & 0\\\\\n" +"\\end{bmatrix}\n" +"\n" +msgstr "" + +#: of tensorcircuit.channels.reshuffle:1 +msgid "Reshuffle the dimension index of a matrix." +msgstr "" + +#: of tensorcircuit.channels.reshuffle:3 +msgid "Input matrix" +msgstr "" + +#: of tensorcircuit.channels.reshuffle:5 +msgid "required order" +msgstr "" + +#: of tensorcircuit.channels.reshuffle:7 +msgid "Reshuffled matrix" +msgstr "" + +#: of tensorcircuit.channels.super_to_choi:1 +msgid "Convert Louivile-Superoperator representation to Choi representation." +msgstr "" + +#: of tensorcircuit.channels.super_to_choi:3 +msgid "" +"In the col-vec basis, the evolution of a state :math:`\\rho` in terms of " +"Choi matrix :math:`\\Lambda` can be expressed as" +msgstr "" + +#: of tensorcircuit.channels.super_to_choi:6 +msgid "" +"\\rho'_{mn} = \\sum_{\\mu,\\nu}^{} \\Lambda_{\\mu m,\\nu n} \\rho_{\\mu " +"\\nu}\n" +"\n" +msgstr "" + +#: of tensorcircuit.channels.super_to_choi:9 +msgid "" +"The Choi matrix :math:`\\Lambda` must make the dynamic map from " +":math:`\\rho` to :math:`\\rho'` to satisfy hermitian-perserving (HP), " +"trace-preserving (TP), and completely positive (CP)." +msgstr "" + +#: of tensorcircuit.channels.super_to_choi:12 +msgid "" +"Interms of tensor components we have the relationship of Louivile-" +"Superoperator representation and Choi representation" +msgstr "" + +#: of tensorcircuit.channels.super_to_choi:15 +msgid "" +"\\Lambda_{mn,\\mu \\nu} = \\varepsilon_{\\nu n,\\mu m}\n" +"\n" +msgstr "" + +#: of tensorcircuit.channels.super_to_kraus:1 +msgid "Convert from Superoperator representation to Kraus representation." +msgstr "" + +#: of tensorcircuit.channels.thermalrelaxationchannel:1 +msgid "Return a thermal_relaxation_channel" +msgstr "" + +#: of tensorcircuit.channels.thermalrelaxationchannel:9 +msgid "the T1 relaxation time." +msgstr "" + +#: of tensorcircuit.channels.thermalrelaxationchannel:11 +msgid "the T2 dephasing time." +msgstr "" + +#: of tensorcircuit.channels.thermalrelaxationchannel:13 +msgid "gate time" +msgstr "" + +#: of tensorcircuit.channels.thermalrelaxationchannel:15 +msgid "" +"method to express error (default: \"ByChoi\"). When :math:`T1>T2`, choose" +" method \"ByKraus\" or \"ByChoi\" for jit. When :math:`T1.apply_list:1 +msgid "" +"Apply **ANY** gate with parameters on the circuit. See " +":py:meth:`tensorcircuit.gates.any_gate`." +msgstr "" + +#: of +#: tensorcircuit.abstractcircuit.AbstractCircuit.apply_general_variable_gate_delayed..apply_list:4 +#: tensorcircuit.abstractcircuit.AbstractCircuit.apply_general_variable_gate_delayed..apply_list:5 +#: tensorcircuit.circuit.Circuit.apply_general_kraus_delayed..apply:4 +#: tensorcircuit.densitymatrix.DMCircuit.apply_general_kraus_delayed..apply:4 +#: tensorcircuit.densitymatrix.DMCircuit2.apply_general_kraus_delayed..apply:4 +msgid "Qubit number that the gate applies on." +msgstr "" + +#: of +#: tensorcircuit.abstractcircuit.AbstractCircuit.apply_general_variable_gate_delayed..apply_list:6 +#: tensorcircuit.abstractcircuit.AbstractCircuit.apply_general_variable_gate_delayed..apply_list:7 +msgid "Parameters for the gate." +msgstr "" + +#: of +#: tensorcircuit.abstractcircuit.AbstractCircuit.apply_general_gate_delayed..apply_list:1 +msgid "" +"Apply **CNOT** gate on the circuit. See " +":py:meth:`tensorcircuit.gates.cnot_gate`." +msgstr "" + +#: of +#: tensorcircuit.abstractcircuit.AbstractCircuit.apply_general_gate_delayed..apply_list:5 +msgid "" +"Qubit number that the gate applies on. The matrix for the gate is .. " +"math:: \\begin{bmatrix} 1.+0.j & 0.+0.j & 0.+0.j & 0.+0.j\\\\" +" 0.+0.j & 1.+0.j & 0.+0.j & 0.+0.j\\\\ 0.+0.j & 0.+0.j & 0.+0.j & " +"1.+0.j\\\\ 0.+0.j & 0.+0.j & 1.+0.j & 0.+0.j \\end{bmatrix}" +msgstr "" + +#: of +#: tensorcircuit.abstractcircuit.AbstractCircuit.apply_general_gate_delayed..apply_list:5 +msgid "Qubit number that the gate applies on. The matrix for the gate is" +msgstr "" + +#: of +#: tensorcircuit.abstractcircuit.AbstractCircuit.apply_general_gate_delayed..apply_list:8 +msgid "" +"\\begin{bmatrix} 1.+0.j & 0.+0.j & 0.+0.j & 0.+0.j\\\\ 0.+0.j & " +"1.+0.j & 0.+0.j & 0.+0.j\\\\ 0.+0.j & 0.+0.j & 0.+0.j & 1.+0.j\\\\ " +"0.+0.j & 0.+0.j & 1.+0.j & 0.+0.j \\end{bmatrix}" +msgstr "" + +#: of +#: tensorcircuit.abstractcircuit.AbstractCircuit.apply_general_variable_gate_delayed..apply_list:1 +msgid "" +"Apply **CPHASE** gate with parameters on the circuit. See " +":py:meth:`tensorcircuit.gates.cphase_gate`." +msgstr "" + +#: of +#: tensorcircuit.abstractcircuit.AbstractCircuit.apply_general_variable_gate_delayed..apply_list:1 +msgid "" +"Apply **CR** gate with parameters on the circuit. See " +":py:meth:`tensorcircuit.gates.cr_gate`." +msgstr "" + +#: of +#: tensorcircuit.abstractcircuit.AbstractCircuit.apply_general_variable_gate_delayed..apply_list:1 +msgid "" +"Apply **CRX** gate with parameters on the circuit. See " +":py:meth:`tensorcircuit.gates.crx_gate`." +msgstr "" + +#: of +#: tensorcircuit.abstractcircuit.AbstractCircuit.apply_general_variable_gate_delayed..apply_list:1 +msgid "" +"Apply **CRY** gate with parameters on the circuit. See " +":py:meth:`tensorcircuit.gates.cry_gate`." +msgstr "" + +#: of +#: tensorcircuit.abstractcircuit.AbstractCircuit.apply_general_variable_gate_delayed..apply_list:1 +msgid "" +"Apply **CRZ** gate with parameters on the circuit. See " +":py:meth:`tensorcircuit.gates.crz_gate`." +msgstr "" + +#: of +#: tensorcircuit.abstractcircuit.AbstractCircuit.apply_general_variable_gate_delayed..apply_list:1 +msgid "" +"Apply **CU** gate with parameters on the circuit. See " +":py:meth:`tensorcircuit.gates.cu_gate`." +msgstr "" + +#: of +#: tensorcircuit.abstractcircuit.AbstractCircuit.apply_general_gate_delayed..apply_list:1 +msgid "" +"Apply **CY** gate on the circuit. See " +":py:meth:`tensorcircuit.gates.cy_gate`." +msgstr "" + +#: of +#: tensorcircuit.abstractcircuit.AbstractCircuit.apply_general_gate_delayed..apply_list:5 +msgid "" +"Qubit number that the gate applies on. The matrix for the gate is .. " +"math:: \\begin{bmatrix} 1.+0.j & 0.+0.j & 0.+0.j & 0.+0.j\\\\" +" 0.+0.j & 1.+0.j & 0.+0.j & 0.+0.j\\\\ 0.+0.j & 0.+0.j & 0.+0.j & " +"0.-1.j\\\\ 0.+0.j & 0.+0.j & 0.+1.j & 0.+0.j \\end{bmatrix}" +msgstr "" + +#: of +#: tensorcircuit.abstractcircuit.AbstractCircuit.apply_general_gate_delayed..apply_list:8 +msgid "" +"\\begin{bmatrix} 1.+0.j & 0.+0.j & 0.+0.j & 0.+0.j\\\\ 0.+0.j & " +"1.+0.j & 0.+0.j & 0.+0.j\\\\ 0.+0.j & 0.+0.j & 0.+0.j & 0.-1.j\\\\ " +"0.+0.j & 0.+0.j & 0.+1.j & 0.+0.j \\end{bmatrix}" +msgstr "" + +#: of +#: tensorcircuit.abstractcircuit.AbstractCircuit.apply_general_gate_delayed..apply_list:1 +msgid "" +"Apply **CZ** gate on the circuit. See " +":py:meth:`tensorcircuit.gates.cz_gate`." +msgstr "" + +#: of +#: tensorcircuit.abstractcircuit.AbstractCircuit.apply_general_gate_delayed..apply_list:5 +msgid "" +"Qubit number that the gate applies on. The matrix for the gate is .. " +"math:: \\begin{bmatrix} 1.+0.j & 0.+0.j & 0.+0.j & 0.+0.j\\\\" +" 0.+0.j & 1.+0.j & 0.+0.j & 0.+0.j\\\\ 0.+0.j & 0.+0.j & 1.+0.j & " +"0.+0.j\\\\ 0.+0.j & 0.+0.j & 0.+0.j & -1.+0.j \\end{bmatrix}" +msgstr "" + +#: of +#: tensorcircuit.abstractcircuit.AbstractCircuit.apply_general_gate_delayed..apply_list:8 +msgid "" +"\\begin{bmatrix} 1.+0.j & 0.+0.j & 0.+0.j & 0.+0.j\\\\ 0.+0.j & " +"1.+0.j & 0.+0.j & 0.+0.j\\\\ 0.+0.j & 0.+0.j & 1.+0.j & 0.+0.j\\\\ " +"0.+0.j & 0.+0.j & 0.+0.j & -1.+0.j \\end{bmatrix}" +msgstr "" + +#: of +#: tensorcircuit.abstractcircuit.AbstractCircuit.apply_general_variable_gate_delayed..apply_list:1 +msgid "" +"Apply **EXP** gate with parameters on the circuit. See " +":py:meth:`tensorcircuit.gates.exp_gate`." +msgstr "" + +#: of +#: tensorcircuit.abstractcircuit.AbstractCircuit.apply_general_variable_gate_delayed..apply_list:1 +msgid "" +"Apply **EXP1** gate with parameters on the circuit. See " +":py:meth:`tensorcircuit.gates.exp1_gate`." +msgstr "" + +#: of +#: tensorcircuit.abstractcircuit.AbstractCircuit.apply_general_gate_delayed..apply_list:1 +msgid "" +"Apply **FREDKIN** gate on the circuit. See " +":py:meth:`tensorcircuit.gates.fredkin_gate`." +msgstr "" + +#: of +#: tensorcircuit.abstractcircuit.AbstractCircuit.apply_general_gate_delayed..apply_list:5 +msgid "" +"Qubit number that the gate applies on. The matrix for the gate is .. " +"math:: \\begin{bmatrix} 1.+0.j & 0.+0.j & 0.+0.j & 0.+0.j & " +"0.+0.j & 0.+0.j & 0.+0.j & 0.+0.j\\\\ 0.+0.j & 1.+0.j & 0.+0.j & " +"0.+0.j & 0.+0.j & 0.+0.j & 0.+0.j & 0.+0.j\\\\ 0.+0.j & 0.+0.j & " +"1.+0.j & 0.+0.j & 0.+0.j & 0.+0.j & 0.+0.j & 0.+0.j\\\\ 0.+0.j & " +"0.+0.j & 0.+0.j & 1.+0.j & 0.+0.j & 0.+0.j & 0.+0.j & 0.+0.j\\\\ " +"0.+0.j & 0.+0.j & 0.+0.j & 0.+0.j & 1.+0.j & 0.+0.j & 0.+0.j & 0.+0.j\\\\" +" 0.+0.j & 0.+0.j & 0.+0.j & 0.+0.j & 0.+0.j & 0.+0.j & 1.+0.j & " +"0.+0.j\\\\ 0.+0.j & 0.+0.j & 0.+0.j & 0.+0.j & 0.+0.j & 1.+0.j & " +"0.+0.j & 0.+0.j\\\\ 0.+0.j & 0.+0.j & 0.+0.j & 0.+0.j & 0.+0.j & " +"0.+0.j & 0.+0.j & 1.+0.j \\end{bmatrix}" +msgstr "" + +#: of +#: tensorcircuit.abstractcircuit.AbstractCircuit.apply_general_gate_delayed..apply_list:8 +msgid "" +"\\begin{bmatrix} 1.+0.j & 0.+0.j & 0.+0.j & 0.+0.j & 0.+0.j & 0.+0.j &" +" 0.+0.j & 0.+0.j\\\\ 0.+0.j & 1.+0.j & 0.+0.j & 0.+0.j & 0.+0.j & " +"0.+0.j & 0.+0.j & 0.+0.j\\\\ 0.+0.j & 0.+0.j & 1.+0.j & 0.+0.j & " +"0.+0.j & 0.+0.j & 0.+0.j & 0.+0.j\\\\ 0.+0.j & 0.+0.j & 0.+0.j & " +"1.+0.j & 0.+0.j & 0.+0.j & 0.+0.j & 0.+0.j\\\\ 0.+0.j & 0.+0.j & " +"0.+0.j & 0.+0.j & 1.+0.j & 0.+0.j & 0.+0.j & 0.+0.j\\\\ 0.+0.j & " +"0.+0.j & 0.+0.j & 0.+0.j & 0.+0.j & 0.+0.j & 1.+0.j & 0.+0.j\\\\ " +"0.+0.j & 0.+0.j & 0.+0.j & 0.+0.j & 0.+0.j & 1.+0.j & 0.+0.j & 0.+0.j\\\\" +" 0.+0.j & 0.+0.j & 0.+0.j & 0.+0.j & 0.+0.j & 0.+0.j & 0.+0.j & 1.+0.j" +" \\end{bmatrix}" +msgstr "" + +#: of +#: tensorcircuit.abstractcircuit.AbstractCircuit.apply_general_gate_delayed..apply_list:1 +msgid "" +"Apply **H** gate on the circuit. See " +":py:meth:`tensorcircuit.gates.h_gate`." +msgstr "" + +#: of +#: tensorcircuit.abstractcircuit.AbstractCircuit.apply_general_gate_delayed..apply_list:5 +msgid "" +"Qubit number that the gate applies on. The matrix for the gate is .. " +"math:: \\begin{bmatrix} 0.70710677+0.j & 0.70710677+0.j\\\\ " +"0.70710677+0.j & -0.70710677+0.j \\end{bmatrix}" +msgstr "" + +#: of +#: tensorcircuit.abstractcircuit.AbstractCircuit.apply_general_gate_delayed..apply_list:8 +msgid "" +"\\begin{bmatrix} 0.70710677+0.j & 0.70710677+0.j\\\\ 0.70710677+0.j" +" & -0.70710677+0.j \\end{bmatrix}" +msgstr "" + +#: of +#: tensorcircuit.abstractcircuit.AbstractCircuit.apply_general_gate_delayed..apply_list:1 +msgid "" +"Apply **I** gate on the circuit. See " +":py:meth:`tensorcircuit.gates.i_gate`." +msgstr "" + +#: of +#: tensorcircuit.abstractcircuit.AbstractCircuit.apply_general_gate_delayed..apply_list:5 +msgid "" +"Qubit number that the gate applies on. The matrix for the gate is .. " +"math:: \\begin{bmatrix} 1.+0.j & 0.+0.j\\\\ 0.+0.j & 1.+0.j " +"\\end{bmatrix}" +msgstr "" + +#: of +#: tensorcircuit.abstractcircuit.AbstractCircuit.apply_general_gate_delayed..apply_list:8 +msgid "\\begin{bmatrix} 1.+0.j & 0.+0.j\\\\ 0.+0.j & 1.+0.j \\end{bmatrix}" +msgstr "" + +#: of +#: tensorcircuit.abstractcircuit.AbstractCircuit.apply_general_variable_gate_delayed..apply_list:1 +msgid "" +"Apply **ISWAP** gate with parameters on the circuit. See " +":py:meth:`tensorcircuit.gates.iswap_gate`." +msgstr "" + +#: of +#: tensorcircuit.abstractcircuit.AbstractCircuit.apply_general_variable_gate_delayed..apply_list:1 +msgid "" +"Apply mpo gate in MPO format on the circuit. See " +":py:meth:`tensorcircuit.gates.mpo_gate`." +msgstr "" + +#: of +#: tensorcircuit.abstractcircuit.AbstractCircuit.apply_general_variable_gate_delayed..apply_list:1 +msgid "" +"Apply multicontrol gate in MPO format on the circuit. See " +":py:meth:`tensorcircuit.gates.multicontrol_gate`." +msgstr "" + +#: of +#: tensorcircuit.abstractcircuit.AbstractCircuit.apply_general_variable_gate_delayed..apply_list:1 +msgid "" +"Apply **ORX** gate with parameters on the circuit. See " +":py:meth:`tensorcircuit.gates.orx_gate`." +msgstr "" + +#: of +#: tensorcircuit.abstractcircuit.AbstractCircuit.apply_general_variable_gate_delayed..apply_list:1 +msgid "" +"Apply **ORY** gate with parameters on the circuit. See " +":py:meth:`tensorcircuit.gates.ory_gate`." +msgstr "" + +#: of +#: tensorcircuit.abstractcircuit.AbstractCircuit.apply_general_variable_gate_delayed..apply_list:1 +msgid "" +"Apply **ORZ** gate with parameters on the circuit. See " +":py:meth:`tensorcircuit.gates.orz_gate`." +msgstr "" + +#: of +#: tensorcircuit.abstractcircuit.AbstractCircuit.apply_general_gate_delayed..apply_list:1 +msgid "" +"Apply **OX** gate on the circuit. See " +":py:meth:`tensorcircuit.gates.ox_gate`." +msgstr "" + +#: of +#: tensorcircuit.abstractcircuit.AbstractCircuit.apply_general_gate_delayed..apply_list:5 +msgid "" +"Qubit number that the gate applies on. The matrix for the gate is .. " +"math:: \\begin{bmatrix} 0.+0.j & 1.+0.j & 0.+0.j & 0.+0.j\\\\" +" 1.+0.j & 0.+0.j & 0.+0.j & 0.+0.j\\\\ 0.+0.j & 0.+0.j & 1.+0.j & " +"0.+0.j\\\\ 0.+0.j & 0.+0.j & 0.+0.j & 1.+0.j \\end{bmatrix}" +msgstr "" + +#: of +#: tensorcircuit.abstractcircuit.AbstractCircuit.apply_general_gate_delayed..apply_list:8 +msgid "" +"\\begin{bmatrix} 0.+0.j & 1.+0.j & 0.+0.j & 0.+0.j\\\\ 1.+0.j & " +"0.+0.j & 0.+0.j & 0.+0.j\\\\ 0.+0.j & 0.+0.j & 1.+0.j & 0.+0.j\\\\ " +"0.+0.j & 0.+0.j & 0.+0.j & 1.+0.j \\end{bmatrix}" +msgstr "" + +#: of +#: tensorcircuit.abstractcircuit.AbstractCircuit.apply_general_gate_delayed..apply_list:1 +msgid "" +"Apply **OY** gate on the circuit. See " +":py:meth:`tensorcircuit.gates.oy_gate`." +msgstr "" + +#: of +#: tensorcircuit.abstractcircuit.AbstractCircuit.apply_general_gate_delayed..apply_list:5 +msgid "" +"Qubit number that the gate applies on. The matrix for the gate is .. " +"math:: \\begin{bmatrix} 0.+0.j & 0.-1.j & 0.+0.j & 0.+0.j\\\\" +" 0.+1.j & 0.+0.j & 0.+0.j & 0.+0.j\\\\ 0.+0.j & 0.+0.j & 1.+0.j & " +"0.+0.j\\\\ 0.+0.j & 0.+0.j & 0.+0.j & 1.+0.j \\end{bmatrix}" +msgstr "" + +#: of +#: tensorcircuit.abstractcircuit.AbstractCircuit.apply_general_gate_delayed..apply_list:8 +msgid "" +"\\begin{bmatrix} 0.+0.j & 0.-1.j & 0.+0.j & 0.+0.j\\\\ 0.+1.j & " +"0.+0.j & 0.+0.j & 0.+0.j\\\\ 0.+0.j & 0.+0.j & 1.+0.j & 0.+0.j\\\\ " +"0.+0.j & 0.+0.j & 0.+0.j & 1.+0.j \\end{bmatrix}" +msgstr "" + +#: of +#: tensorcircuit.abstractcircuit.AbstractCircuit.apply_general_gate_delayed..apply_list:1 +msgid "" +"Apply **OZ** gate on the circuit. See " +":py:meth:`tensorcircuit.gates.oz_gate`." +msgstr "" + +#: of +#: tensorcircuit.abstractcircuit.AbstractCircuit.apply_general_gate_delayed..apply_list:5 +msgid "" +"Qubit number that the gate applies on. The matrix for the gate is .. " +"math:: \\begin{bmatrix} 1.+0.j & 0.+0.j & 0.+0.j & 0.+0.j\\\\" +" 0.+0.j & -1.+0.j & 0.+0.j & 0.+0.j\\\\ 0.+0.j & 0.+0.j & 1.+0.j & " +"0.+0.j\\\\ 0.+0.j & 0.+0.j & 0.+0.j & 1.+0.j \\end{bmatrix}" +msgstr "" + +#: of +#: tensorcircuit.abstractcircuit.AbstractCircuit.apply_general_gate_delayed..apply_list:8 +msgid "" +"\\begin{bmatrix} 1.+0.j & 0.+0.j & 0.+0.j & 0.+0.j\\\\ 0.+0.j & " +"-1.+0.j & 0.+0.j & 0.+0.j\\\\ 0.+0.j & 0.+0.j & 1.+0.j & 0.+0.j\\\\" +" 0.+0.j & 0.+0.j & 0.+0.j & 1.+0.j \\end{bmatrix}" +msgstr "" + +#: of +#: tensorcircuit.abstractcircuit.AbstractCircuit.apply_general_variable_gate_delayed..apply_list:1 +msgid "" +"Apply **PHASE** gate with parameters on the circuit. See " +":py:meth:`tensorcircuit.gates.phase_gate`." +msgstr "" + +#: of +#: tensorcircuit.abstractcircuit.AbstractCircuit.apply_general_variable_gate_delayed..apply_list:1 +msgid "" +"Apply **R** gate with parameters on the circuit. See " +":py:meth:`tensorcircuit.gates.r_gate`." +msgstr "" + +#: of +#: tensorcircuit.abstractcircuit.AbstractCircuit.apply_general_variable_gate_delayed..apply_list:1 +msgid "" +"Apply **RX** gate with parameters on the circuit. See " +":py:meth:`tensorcircuit.gates.rx_gate`." +msgstr "" + +#: of +#: tensorcircuit.abstractcircuit.AbstractCircuit.apply_general_variable_gate_delayed..apply_list:1 +msgid "" +"Apply **RXX** gate with parameters on the circuit. See " +":py:meth:`tensorcircuit.gates.rxx_gate`." +msgstr "" + +#: of +#: tensorcircuit.abstractcircuit.AbstractCircuit.apply_general_variable_gate_delayed..apply_list:1 +msgid "" +"Apply **RY** gate with parameters on the circuit. See " +":py:meth:`tensorcircuit.gates.ry_gate`." +msgstr "" + +#: of +#: tensorcircuit.abstractcircuit.AbstractCircuit.apply_general_variable_gate_delayed..apply_list:1 +msgid "" +"Apply **RYY** gate with parameters on the circuit. See " +":py:meth:`tensorcircuit.gates.ryy_gate`." +msgstr "" + +#: of +#: tensorcircuit.abstractcircuit.AbstractCircuit.apply_general_variable_gate_delayed..apply_list:1 +msgid "" +"Apply **RZ** gate with parameters on the circuit. See " +":py:meth:`tensorcircuit.gates.rz_gate`." +msgstr "" + +#: of +#: tensorcircuit.abstractcircuit.AbstractCircuit.apply_general_variable_gate_delayed..apply_list:1 +msgid "" +"Apply **RZZ** gate with parameters on the circuit. See " +":py:meth:`tensorcircuit.gates.rzz_gate`." +msgstr "" + +#: of +#: tensorcircuit.abstractcircuit.AbstractCircuit.apply_general_gate_delayed..apply_list:1 +msgid "" +"Apply **S** gate on the circuit. See " +":py:meth:`tensorcircuit.gates.s_gate`." +msgstr "" + +#: of +#: tensorcircuit.abstractcircuit.AbstractCircuit.apply_general_gate_delayed..apply_list:5 +msgid "" +"Qubit number that the gate applies on. The matrix for the gate is .. " +"math:: \\begin{bmatrix} 1.+0.j & 0.+0.j\\\\ 0.+0.j & 0.+1.j " +"\\end{bmatrix}" +msgstr "" + +#: of +#: tensorcircuit.abstractcircuit.AbstractCircuit.apply_general_gate_delayed..apply_list:8 +msgid "\\begin{bmatrix} 1.+0.j & 0.+0.j\\\\ 0.+0.j & 0.+1.j \\end{bmatrix}" +msgstr "" + +#: of +#: tensorcircuit.abstractcircuit.AbstractCircuit.apply_general_gate_delayed..apply_list:1 +msgid "" +"Apply **SD** gate on the circuit. See " +":py:meth:`tensorcircuit.gates.sd_gate`." +msgstr "" + +#: of +#: tensorcircuit.abstractcircuit.AbstractCircuit.apply_general_gate_delayed..apply_list:5 +msgid "" +"Qubit number that the gate applies on. The matrix for the gate is .. " +"math:: \\begin{bmatrix} 1.+0.j & 0.+0.j\\\\ 0.+0.j & 0.-1.j " +"\\end{bmatrix}" +msgstr "" + +#: of +#: tensorcircuit.abstractcircuit.AbstractCircuit.apply_general_gate_delayed..apply_list:8 +msgid "\\begin{bmatrix} 1.+0.j & 0.+0.j\\\\ 0.+0.j & 0.-1.j \\end{bmatrix}" +msgstr "" + +#: of +#: tensorcircuit.abstractcircuit.AbstractCircuit.apply_general_gate_delayed..apply_list:1 +msgid "" +"Apply **SWAP** gate on the circuit. See " +":py:meth:`tensorcircuit.gates.swap_gate`." +msgstr "" + +#: of +#: tensorcircuit.abstractcircuit.AbstractCircuit.apply_general_gate_delayed..apply_list:5 +msgid "" +"Qubit number that the gate applies on. The matrix for the gate is .. " +"math:: \\begin{bmatrix} 1.+0.j & 0.+0.j & 0.+0.j & 0.+0.j\\\\" +" 0.+0.j & 0.+0.j & 1.+0.j & 0.+0.j\\\\ 0.+0.j & 1.+0.j & 0.+0.j & " +"0.+0.j\\\\ 0.+0.j & 0.+0.j & 0.+0.j & 1.+0.j \\end{bmatrix}" +msgstr "" + +#: of +#: tensorcircuit.abstractcircuit.AbstractCircuit.apply_general_gate_delayed..apply_list:8 +msgid "" +"\\begin{bmatrix} 1.+0.j & 0.+0.j & 0.+0.j & 0.+0.j\\\\ 0.+0.j & " +"0.+0.j & 1.+0.j & 0.+0.j\\\\ 0.+0.j & 1.+0.j & 0.+0.j & 0.+0.j\\\\ " +"0.+0.j & 0.+0.j & 0.+0.j & 1.+0.j \\end{bmatrix}" +msgstr "" + +#: of +#: tensorcircuit.abstractcircuit.AbstractCircuit.apply_general_gate_delayed..apply_list:1 +msgid "" +"Apply **T** gate on the circuit. See " +":py:meth:`tensorcircuit.gates.t_gate`." +msgstr "" + +#: of +#: tensorcircuit.abstractcircuit.AbstractCircuit.apply_general_gate_delayed..apply_list:5 +msgid "" +"Qubit number that the gate applies on. The matrix for the gate is .. " +"math:: \\begin{bmatrix} 1. & +0.j & 0. & +0.j\\\\ 0. & +0.j " +"& 0.70710677+0.70710677j \\end{bmatrix}" +msgstr "" + +#: of +#: tensorcircuit.abstractcircuit.AbstractCircuit.apply_general_gate_delayed..apply_list:8 +msgid "" +"\\begin{bmatrix} 1. & +0.j & 0. & +0.j\\\\ 0. & +0.j & " +"0.70710677+0.70710677j \\end{bmatrix}" +msgstr "" + +#: of +#: tensorcircuit.abstractcircuit.AbstractCircuit.apply_general_gate_delayed..apply_list:1 +msgid "" +"Apply **TD** gate on the circuit. See " +":py:meth:`tensorcircuit.gates.td_gate`." +msgstr "" + +#: of +#: tensorcircuit.abstractcircuit.AbstractCircuit.apply_general_gate_delayed..apply_list:5 +msgid "" +"Qubit number that the gate applies on. The matrix for the gate is .. " +"math:: \\begin{bmatrix} 1. & +0.j & 0. & +0.j\\\\ 0. & +0.j " +"& 0.70710677-0.70710677j \\end{bmatrix}" +msgstr "" + +#: of +#: tensorcircuit.abstractcircuit.AbstractCircuit.apply_general_gate_delayed..apply_list:8 +msgid "" +"\\begin{bmatrix} 1. & +0.j & 0. & +0.j\\\\ 0. & +0.j & " +"0.70710677-0.70710677j \\end{bmatrix}" +msgstr "" + +#: of +#: tensorcircuit.abstractcircuit.AbstractCircuit.apply_general_gate_delayed..apply_list:1 +msgid "" +"Apply **TOFFOLI** gate on the circuit. See " +":py:meth:`tensorcircuit.gates.toffoli_gate`." +msgstr "" + +#: of +#: tensorcircuit.abstractcircuit.AbstractCircuit.apply_general_gate_delayed..apply_list:5 +msgid "" +"Qubit number that the gate applies on. The matrix for the gate is .. " +"math:: \\begin{bmatrix} 1.+0.j & 0.+0.j & 0.+0.j & 0.+0.j & " +"0.+0.j & 0.+0.j & 0.+0.j & 0.+0.j\\\\ 0.+0.j & 1.+0.j & 0.+0.j & " +"0.+0.j & 0.+0.j & 0.+0.j & 0.+0.j & 0.+0.j\\\\ 0.+0.j & 0.+0.j & " +"1.+0.j & 0.+0.j & 0.+0.j & 0.+0.j & 0.+0.j & 0.+0.j\\\\ 0.+0.j & " +"0.+0.j & 0.+0.j & 1.+0.j & 0.+0.j & 0.+0.j & 0.+0.j & 0.+0.j\\\\ " +"0.+0.j & 0.+0.j & 0.+0.j & 0.+0.j & 1.+0.j & 0.+0.j & 0.+0.j & 0.+0.j\\\\" +" 0.+0.j & 0.+0.j & 0.+0.j & 0.+0.j & 0.+0.j & 1.+0.j & 0.+0.j & " +"0.+0.j\\\\ 0.+0.j & 0.+0.j & 0.+0.j & 0.+0.j & 0.+0.j & 0.+0.j & " +"0.+0.j & 1.+0.j\\\\ 0.+0.j & 0.+0.j & 0.+0.j & 0.+0.j & 0.+0.j & " +"0.+0.j & 1.+0.j & 0.+0.j \\end{bmatrix}" +msgstr "" + +#: of +#: tensorcircuit.abstractcircuit.AbstractCircuit.apply_general_gate_delayed..apply_list:8 +msgid "" +"\\begin{bmatrix} 1.+0.j & 0.+0.j & 0.+0.j & 0.+0.j & 0.+0.j & 0.+0.j &" +" 0.+0.j & 0.+0.j\\\\ 0.+0.j & 1.+0.j & 0.+0.j & 0.+0.j & 0.+0.j & " +"0.+0.j & 0.+0.j & 0.+0.j\\\\ 0.+0.j & 0.+0.j & 1.+0.j & 0.+0.j & " +"0.+0.j & 0.+0.j & 0.+0.j & 0.+0.j\\\\ 0.+0.j & 0.+0.j & 0.+0.j & " +"1.+0.j & 0.+0.j & 0.+0.j & 0.+0.j & 0.+0.j\\\\ 0.+0.j & 0.+0.j & " +"0.+0.j & 0.+0.j & 1.+0.j & 0.+0.j & 0.+0.j & 0.+0.j\\\\ 0.+0.j & " +"0.+0.j & 0.+0.j & 0.+0.j & 0.+0.j & 1.+0.j & 0.+0.j & 0.+0.j\\\\ " +"0.+0.j & 0.+0.j & 0.+0.j & 0.+0.j & 0.+0.j & 0.+0.j & 0.+0.j & 1.+0.j\\\\" +" 0.+0.j & 0.+0.j & 0.+0.j & 0.+0.j & 0.+0.j & 0.+0.j & 1.+0.j & 0.+0.j" +" \\end{bmatrix}" +msgstr "" + +#: of +#: tensorcircuit.abstractcircuit.AbstractCircuit.apply_general_variable_gate_delayed..apply_list:1 +msgid "" +"Apply **U** gate with parameters on the circuit. See " +":py:meth:`tensorcircuit.gates.u_gate`." +msgstr "" + +#: of +#: tensorcircuit.abstractcircuit.AbstractCircuit.apply_general_gate_delayed..apply_list:1 +msgid "" +"Apply **WROOT** gate on the circuit. See " +":py:meth:`tensorcircuit.gates.wroot_gate`." +msgstr "" + +#: of +#: tensorcircuit.abstractcircuit.AbstractCircuit.apply_general_gate_delayed..apply_list:5 +msgid "" +"Qubit number that the gate applies on. The matrix for the gate is .. " +"math:: \\begin{bmatrix} 0.70710677+0.j & -0.5 & -0.5j\\\\ " +"0.5 & -0.5j & 0.70710677+0.j \\end{bmatrix}" +msgstr "" + +#: of +#: tensorcircuit.abstractcircuit.AbstractCircuit.apply_general_gate_delayed..apply_list:8 +msgid "" +"\\begin{bmatrix} 0.70710677+0.j & -0.5 & -0.5j\\\\ 0.5 & -0.5j & " +"0.70710677+0.j \\end{bmatrix}" +msgstr "" + +#: of +#: tensorcircuit.abstractcircuit.AbstractCircuit.apply_general_gate_delayed..apply_list:1 +msgid "" +"Apply **X** gate on the circuit. See " +":py:meth:`tensorcircuit.gates.x_gate`." +msgstr "" + +#: of +#: tensorcircuit.abstractcircuit.AbstractCircuit.apply_general_gate_delayed..apply_list:5 +msgid "" +"Qubit number that the gate applies on. The matrix for the gate is .. " +"math:: \\begin{bmatrix} 0.+0.j & 1.+0.j\\\\ 1.+0.j & 0.+0.j " +"\\end{bmatrix}" +msgstr "" + +#: of +#: tensorcircuit.abstractcircuit.AbstractCircuit.apply_general_gate_delayed..apply_list:8 +msgid "\\begin{bmatrix} 0.+0.j & 1.+0.j\\\\ 1.+0.j & 0.+0.j \\end{bmatrix}" +msgstr "" + +#: of +#: tensorcircuit.abstractcircuit.AbstractCircuit.apply_general_gate_delayed..apply_list:1 +msgid "" +"Apply **Y** gate on the circuit. See " +":py:meth:`tensorcircuit.gates.y_gate`." +msgstr "" + +#: of +#: tensorcircuit.abstractcircuit.AbstractCircuit.apply_general_gate_delayed..apply_list:5 +msgid "" +"Qubit number that the gate applies on. The matrix for the gate is .. " +"math:: \\begin{bmatrix} 0.+0.j & 0.-1.j\\\\ 0.+1.j & 0.+0.j " +"\\end{bmatrix}" +msgstr "" + +#: of +#: tensorcircuit.abstractcircuit.AbstractCircuit.apply_general_gate_delayed..apply_list:8 +msgid "\\begin{bmatrix} 0.+0.j & 0.-1.j\\\\ 0.+1.j & 0.+0.j \\end{bmatrix}" +msgstr "" + +#: of +#: tensorcircuit.abstractcircuit.AbstractCircuit.apply_general_gate_delayed..apply_list:1 +msgid "" +"Apply **Z** gate on the circuit. See " +":py:meth:`tensorcircuit.gates.z_gate`." +msgstr "" + +#: of +#: tensorcircuit.abstractcircuit.AbstractCircuit.apply_general_gate_delayed..apply_list:5 +msgid "" +"Qubit number that the gate applies on. The matrix for the gate is .. " +"math:: \\begin{bmatrix} 1.+0.j & 0.+0.j\\\\ 0.+0.j & -1.+0.j" +" \\end{bmatrix}" +msgstr "" + +#: of +#: tensorcircuit.abstractcircuit.AbstractCircuit.apply_general_gate_delayed..apply_list:8 +msgid "\\begin{bmatrix} 1.+0.j & 0.+0.j\\\\ 0.+0.j & -1.+0.j \\end{bmatrix}" +msgstr "" + +#: of tensorcircuit.circuit.Circuit.__init__:1 +msgid "Circuit object based on state simulator." +msgstr "" + +#: of tensorcircuit.circuit.Circuit.__init__:3 +#: tensorcircuit.mpscircuit.MPSCircuit.__init__:3 +msgid "The number of qubits in the circuit." +msgstr "" + +#: of tensorcircuit.circuit.Circuit.__init__:5 +msgid "" +"If not None, the initial state of the circuit is taken as ``inputs`` " +"instead of :math:`\\vert 0\\rangle^n` qubits, defaults to None." +msgstr "" + +#: of tensorcircuit.circuit.Circuit.__init__:8 +msgid "QuVector for a MPS like initial wavefunction." +msgstr "" + +#: of tensorcircuit.circuit.Circuit.__init__:10 +#: tensorcircuit.densitymatrix.DMCircuit.__init__:15 +msgid "" +"dict if two qubit gate is ready for split, including parameters for at " +"least one of ``max_singular_values`` and ``max_truncation_err``." +msgstr "" + +#: of +#: tensorcircuit.circuit.Circuit.apply_general_kraus_delayed..apply:1 +#: tensorcircuit.densitymatrix.DMCircuit.apply_general_kraus_delayed..apply:1 +#: tensorcircuit.densitymatrix.DMCircuit2.apply_general_kraus_delayed..apply:1 +msgid "" +"Apply amplitudedamping quantum channel on the circuit. See " +":py:meth:`tensorcircuit.channels.amplitudedampingchannel`" +msgstr "" + +#: of +#: tensorcircuit.circuit.Circuit.apply_general_kraus_delayed..apply:6 +msgid "uniform external random number between 0 and 1" +msgstr "" + +#: of +#: tensorcircuit.circuit.Circuit.apply_general_kraus_delayed..apply:8 +#: tensorcircuit.densitymatrix.DMCircuit.apply_general_kraus_delayed..apply:6 +#: tensorcircuit.densitymatrix.DMCircuit2.apply_general_kraus_delayed..apply:6 +msgid "Parameters for the channel." +msgstr "" + +#: of tensorcircuit.circuit.Circuit.general_kraus:1 +msgid "" +"Monte Carlo trajectory simulation of general Kraus channel whose Kraus " +"operators cannot be amplified to unitary operators. For unitary operators" +" composed Kraus channel, :py:meth:`unitary_kraus` is much faster." +msgstr "" + +#: of tensorcircuit.circuit.Circuit.general_kraus:5 +msgid "" +"This function is jittable in theory. But only jax+GPU combination is " +"recommended for jit since the graph building time is too long for other " +"backend options; though the running time of the function is very fast for" +" every case." +msgstr "" + +#: of tensorcircuit.circuit.Circuit.general_kraus:9 +msgid "A list of ``tn.Node`` for Kraus operators." +msgstr "" + +#: of tensorcircuit.circuit.Circuit.general_kraus:11 +msgid "The qubits index that Kraus channel is applied on." +msgstr "" + +#: of tensorcircuit.circuit.Circuit.general_kraus:13 +msgid "" +"Random tensor uniformly between 0 or 1, defaults to be None, when the " +"random number will be generated automatically" +msgstr "" + +#: of +#: tensorcircuit.circuit.Circuit.apply_general_kraus_delayed..apply:1 +#: tensorcircuit.densitymatrix.DMCircuit.apply_general_kraus_delayed..apply:1 +#: tensorcircuit.densitymatrix.DMCircuit2.apply_general_kraus_delayed..apply:1 +msgid "" +"Apply depolarizing quantum channel on the circuit. See " +":py:meth:`tensorcircuit.channels.depolarizingchannel`" +msgstr "" + +#: of tensorcircuit.circuit.Circuit.depolarizing_reference:1 +msgid "" +"Apply depolarizing channel in a Monte Carlo way, i.e. for each call of " +"this method, one of gates from X, Y, Z, I are applied on the circuit " +"based on the probability indicated by ``px``, ``py``, ``pz``." +msgstr "" + +#: of tensorcircuit.circuit.Circuit.depolarizing_reference:6 +msgid "The qubit that depolarizing channel is on" +msgstr "" + +#: of tensorcircuit.circuit.Circuit.depolarizing_reference:8 +msgid "probability for X noise" +msgstr "" + +#: of tensorcircuit.circuit.Circuit.depolarizing_reference:10 +msgid "probability for Y noise" +msgstr "" + +#: of tensorcircuit.circuit.Circuit.depolarizing_reference:12 +msgid "probability for Z noise" +msgstr "" + +#: of tensorcircuit.circuit.Circuit.depolarizing_reference:14 +msgid "random seed uniformly from 0 to 1, defaults to None (generated implicitly)" +msgstr "" + +#: of tensorcircuit.circuit.Circuit.depolarizing_reference:16 +msgid "int Tensor, the element lookup: [0: x, 1: y, 2: z, 3: I]" +msgstr "" + +#: of tensorcircuit.circuit.Circuit.expectation:1 +#: tensorcircuit.densitymatrix.DMCircuit.expectation:1 +msgid "Compute the expectation of corresponding operators." +msgstr "" + +#: of tensorcircuit.circuit.Circuit.expectation:24 +#: tensorcircuit.densitymatrix.DMCircuit.expectation:3 +msgid "" +"Operator and its position on the circuit, eg. ``(tc.gates.z(), [1, ]), " +"(tc.gates.x(), [2, ])`` is for operator :math:`Z_1X_2`." +msgstr "" + +#: of tensorcircuit.circuit.Circuit.expectation:27 +#: tensorcircuit.mpscircuit.MPSCircuit.expectation:6 +msgid "" +"If True, then the wavefunction tensor is cached for further expectation " +"evaluation, defaults to be true." +msgstr "" + +#: of tensorcircuit.circuit.Circuit.expectation:30 +msgid "whether enable light cone simplification, defaults to False" +msgstr "" + +#: of tensorcircuit.circuit.Circuit.expectation:39 +#: tensorcircuit.circuit.expectation:50 +msgid "\"Cannot measure two operators in one index\"" +msgstr "" + +#: of tensorcircuit.circuit.Circuit.expectation:40 +#: tensorcircuit.densitymatrix.DMCircuit.expectation:13 +msgid "Tensor with one element" +msgstr "" + +#: of +#: tensorcircuit.circuit.Circuit.apply_general_kraus_delayed..apply:1 +#: tensorcircuit.densitymatrix.DMCircuit.apply_general_kraus_delayed..apply:1 +#: tensorcircuit.densitymatrix.DMCircuit2.apply_general_kraus_delayed..apply:1 +msgid "" +"Apply generaldepolarizing quantum channel on the circuit. See " +":py:meth:`tensorcircuit.channels.generaldepolarizingchannel`" +msgstr "" + +#: of tensorcircuit.circuit.Circuit.get_quoperator:1 +msgid "" +"Get the ``QuOperator`` MPO like representation of the circuit unitary " +"without contraction." +msgstr "" + +#: of tensorcircuit.circuit.Circuit.get_quoperator:3 +msgid "" +"``QuOperator`` object for the circuit unitary (open indices for the input" +" state)" +msgstr "" + +#: of tensorcircuit.circuit.Circuit.is_valid:1 +msgid "[WIP], check whether the circuit is legal." +msgstr "" + +#: of tensorcircuit.circuit.Circuit.is_valid:3 +msgid "The bool indicating whether the circuit is legal" +msgstr "" + +#: of +#: tensorcircuit.circuit.Circuit.apply_general_kraus_delayed..apply:1 +#: tensorcircuit.densitymatrix.DMCircuit.apply_general_kraus_delayed..apply:1 +#: tensorcircuit.densitymatrix.DMCircuit2.apply_general_kraus_delayed..apply:1 +msgid "" +"Apply isotropicdepolarizing quantum channel on the circuit. See " +":py:meth:`tensorcircuit.channels.isotropicdepolarizingchannel`" +msgstr "" + +#: of tensorcircuit.circuit.Circuit.matrix:1 +msgid "" +"Get the unitary matrix for the circuit irrespective with the circuit " +"input state." +msgstr "" + +#: of tensorcircuit.circuit.Circuit.matrix:3 +msgid "The circuit unitary matrix" +msgstr "" + +#: of tensorcircuit.circuit.Circuit.measure_reference:1 +msgid "Take measurement on the given quantum lines by ``index``." +msgstr "" + +#: of tensorcircuit.circuit.Circuit.mid_measurement:1 +msgid "" +"Middle measurement in z-basis on the circuit, note the wavefunction " +"output is not normalized with ``mid_measurement`` involved, one should " +"normalize the state manually if needed. This is a post-selection method " +"as keep is provided as a prior." +msgstr "" + +#: of tensorcircuit.circuit.Circuit.mid_measurement:5 +msgid "The index of qubit that the Z direction postselection applied on." +msgstr "" + +#: of tensorcircuit.circuit.Circuit.mid_measurement:7 +msgid "0 for spin up, 1 for spin down, defaults to be 0." +msgstr "" + +#: of +#: tensorcircuit.circuit.Circuit.apply_general_kraus_delayed..apply:1 +#: tensorcircuit.densitymatrix.DMCircuit.apply_general_kraus_delayed..apply:1 +#: tensorcircuit.densitymatrix.DMCircuit2.apply_general_kraus_delayed..apply:1 +msgid "" +"Apply phasedamping quantum channel on the circuit. See " +":py:meth:`tensorcircuit.channels.phasedampingchannel`" +msgstr "" + +#: of tensorcircuit.circuit.Circuit.replace_mps_inputs:1 +msgid "" +"Replace the input state in MPS representation while keep the circuit " +"structure unchanged." +msgstr "" + +#: of tensorcircuit.circuit.Circuit.replace_mps_inputs:18 +msgid "(Nodes, dangling Edges) for a MPS like initial wavefunction." +msgstr "" + +#: of +#: tensorcircuit.circuit.Circuit.apply_general_kraus_delayed..apply:1 +#: tensorcircuit.densitymatrix.DMCircuit.apply_general_kraus_delayed..apply:1 +#: tensorcircuit.densitymatrix.DMCircuit2.apply_general_kraus_delayed..apply:1 +msgid "" +"Apply reset quantum channel on the circuit. See " +":py:meth:`tensorcircuit.channels.resetchannel`" +msgstr "" + +#: of tensorcircuit.circuit.Circuit.wavefunction:1 +#: tensorcircuit.mpscircuit.MPSCircuit.wavefunction:1 +msgid "Compute the output wavefunction from the circuit." +msgstr "" + +#: of tensorcircuit.circuit.Circuit.wavefunction:3 +msgid "" +"The str indicating the form of the output wavefunction. \"default\": " +"[-1], \"ket\": [-1, 1], \"bra\": [1, -1]" +msgstr "" + +#: of tensorcircuit.circuit.Circuit.wavefunction:6 +msgid "Tensor with the corresponding shape." +msgstr "" + +#: of +#: tensorcircuit.circuit.Circuit.apply_general_kraus_delayed..apply:1 +#: tensorcircuit.densitymatrix.DMCircuit.apply_general_kraus_delayed..apply:1 +#: tensorcircuit.densitymatrix.DMCircuit2.apply_general_kraus_delayed..apply:1 +msgid "" +"Apply thermalrelaxation quantum channel on the circuit. See " +":py:meth:`tensorcircuit.channels.thermalrelaxationchannel`" +msgstr "" + +#: of tensorcircuit.circuit.Circuit.unitary_kraus:1 +msgid "" +"Apply unitary gates in ``kraus`` randomly based on corresponding " +"``prob``. If ``prob`` is ``None``, this is reduced to kraus channel " +"language." +msgstr "" + +#: of tensorcircuit.circuit.Circuit.unitary_kraus:4 +msgid "List of ``tc.gates.Gate`` or just Tensors" +msgstr "" + +#: of tensorcircuit.circuit.Circuit.unitary_kraus:6 +msgid "prob list with the same size as ``kraus``, defaults to None" +msgstr "" + +#: of tensorcircuit.circuit.Circuit.unitary_kraus:8 +msgid "random seed between 0 to 1, defaults to None" +msgstr "" + +#: of tensorcircuit.circuit.Circuit.unitary_kraus:10 +msgid "shape [] int dtype tensor indicates which kraus gate is actually applied" +msgstr "" + +#: of tensorcircuit.circuit.expectation:1 +msgid "Compute :math:`\\langle bra\\vert ops \\vert ket\\rangle`." +msgstr "" + +#: of tensorcircuit.circuit.expectation:3 +msgid "Example 1 (:math:`bra` is same as :math:`ket`)" +msgstr "" + +#: of tensorcircuit.circuit.expectation:24 +msgid "Example 2 (:math:`bra` is different from :math:`ket`)" +msgstr "" + +#: of tensorcircuit.circuit.expectation:42 +msgid ":math:`ket`. The state in tensor or ``QuVector`` format" +msgstr "" + +#: of tensorcircuit.circuit.expectation:44 +msgid ":math:`bra`, defaults to None, which is the same as ``ket``." +msgstr "" + +#: of tensorcircuit.circuit.expectation:46 +msgid "" +":math:`bra` changes to the adjoint matrix of :math:`bra`, defaults to " +"True." +msgstr "" + +#: of tensorcircuit.circuit.expectation:48 +msgid "Normalize the :math:`ket` and :math:`bra`, defaults to False." +msgstr "" + +#: of tensorcircuit.circuit.expectation:51 +msgid "The result of :math:`\\langle bra\\vert ops \\vert ket\\rangle`." +msgstr "" + +#: ../../source/api/cloud.rst:2 +msgid "tensorcircuit.cloud" +msgstr "" + +#: ../../source/api/cloud/abstraction.rst:2 +msgid "tensorcircuit.cloud.abstraction" +msgstr "" + +#: of tensorcircuit.cloud.abstraction:1 +msgid "Abstraction for Provider, Device and Task" +msgstr "" + +#: of tensorcircuit.cloud.abstraction.Device:1 +msgid "Device abstraction for cloud connection, eg. quantum chips" +msgstr "" + +#: of tensorcircuit.cloud.abstraction.Device.list_properties:1 +msgid "List all device properties in as dict" +msgstr "" + +#: of tensorcircuit.cloud.abstraction.Device.native_gates:1 +msgid "List native gates supported for the device, str conforms qiskit convention" +msgstr "" + +#: of tensorcircuit.cloud.abstraction.Device.topology:1 +msgid "Get the bidirectional topology link list of the device" +msgstr "" + +#: of tensorcircuit.cloud.abstraction.Device.topology_graph:1 +msgid "Get the qubit topology in ``nx.Graph`` or directly visualize it" +msgstr "" + +#: of tensorcircuit.cloud.abstraction.Device.topology_graph:3 +#: tensorcircuit.vis.render_pdf:23 +msgid "[description], defaults to False" +msgstr "" + +#: of tensorcircuit.cloud.abstraction.Provider:1 +msgid "Provider abstraction for cloud connection, eg. \"tencent\", \"local\"" +msgstr "" + +#: of tensorcircuit.cloud.abstraction.TCException:1 +msgid "Bases: :py:class:`BaseException`" +msgstr "" + +#: of tensorcircuit.cloud.abstraction.TCException.with_traceback:1 +#: tensorcircuit.cloud.abstraction.TaskException.with_traceback:1 +#: tensorcircuit.cloud.abstraction.TaskFailed.with_traceback:1 +#: tensorcircuit.cloud.abstraction.TaskUnfinished.with_traceback:1 +#: tensorcircuit.cloud.utils.HttpStatusError.with_traceback:1 +msgid "" +"Exception.with_traceback(tb) -- set self.__traceback__ to tb and return " +"self." +msgstr "" + +#: of tensorcircuit.cloud.abstraction.Task:1 +msgid "Task abstraction for quantum jobs on the cloud" +msgstr "" + +#: of tensorcircuit.cloud.abstraction.Task.details:1 +msgid "Get the current task details" +msgstr "" + +#: of tensorcircuit.cloud.abstraction.Task.details:4 +msgid "whether return until task is finished, defaults to False" +msgstr "" + +#: of tensorcircuit.cloud.abstraction.Task.details:6 +#: tensorcircuit.cloud.abstraction.Task.results:10 +msgid "alias for the argument ``blocked``" +msgstr "" + +#: of tensorcircuit.cloud.abstraction.Task.get_device:1 +msgid "Query which device the task is run on" +msgstr "" + +#: of tensorcircuit.cloud.abstraction.Task.resubmit:1 +msgid "resubmit the task" +msgstr "" + +#: of tensorcircuit.cloud.abstraction.Task.resubmit:3 +msgid "the resubmitted task" +msgstr "" + +#: of tensorcircuit.cloud.abstraction.Task.results:1 +msgid "get task results of the qjob" +msgstr "" + +#: of tensorcircuit.cloud.abstraction.Task.results:3 +msgid "unsupported now, defaults to None, which is \"count_dict_bin\"" +msgstr "" + +#: of tensorcircuit.cloud.abstraction.Task.results:7 +msgid "" +"whether blocked to wait until the result is returned, defaults to False, " +"which raise error when the task is unfinished" +msgstr "" + +#: of tensorcircuit.cloud.abstraction.Task.results:12 +msgid "whether enable readout error mitigation, defaults to False" +msgstr "" + +#: of tensorcircuit.cloud.abstraction.Task.results:14 +msgid "option dict for ``ReadoutMit.cals_from_system``, defaults to None" +msgstr "" + +#: of tensorcircuit.cloud.abstraction.Task.results:17 +msgid "" +"if given, directly use the calibriation info on ``readout_mit``, defaults" +" to None" +msgstr "" + +#: of tensorcircuit.cloud.abstraction.Task.results:20 +msgid "option dict for ``ReadoutMit.apply_correction``, defaults to None" +msgstr "" + +#: of tensorcircuit.cloud.abstraction.Task.results:22 +msgid "count dict results" +msgstr "" + +#: of tensorcircuit.cloud.abstraction.Task.state:1 +msgid "Query the current task status" +msgstr "" + +#: of tensorcircuit.cloud.abstraction.TaskException:1 +msgid "Bases: :py:class:`~tensorcircuit.cloud.abstraction.TCException`" +msgstr "" + +#: of tensorcircuit.cloud.abstraction.TaskFailed:1 +#: tensorcircuit.cloud.abstraction.TaskUnfinished:1 +msgid "Bases: :py:class:`~tensorcircuit.cloud.abstraction.TaskException`" +msgstr "" + +#: ../../source/api/cloud/apis.rst:2 +msgid "tensorcircuit.cloud.apis" +msgstr "" + +#: of tensorcircuit.cloud.apis:1 +msgid "main entrypoints of cloud module" +msgstr "" + +#: of tensorcircuit.cloud.apis.get_device:1 +#: tensorcircuit.cloud.apis.set_device:1 +msgid "set the default device" +msgstr "" + +#: of tensorcircuit.cloud.apis.get_device:3 +#: tensorcircuit.cloud.apis.set_device:3 +msgid "provider of the device, defaults to None" +msgstr "" + +#: of tensorcircuit.cloud.apis.get_device:5 +#: tensorcircuit.cloud.apis.set_device:5 +msgid "the device, defaults to None" +msgstr "" + +#: of tensorcircuit.cloud.apis.get_device:7 +#: tensorcircuit.cloud.apis.set_device:7 +msgid "" +"whether set, defaults to True, if False, equivalent to ``get_device``, " +"defaults to True" +msgstr "" + +#: of tensorcircuit.cloud.apis.get_provider:1 +#: tensorcircuit.cloud.apis.set_provider:1 +msgid "set default provider for the program" +msgstr "" + +#: of tensorcircuit.cloud.apis.get_provider:5 +#: tensorcircuit.cloud.apis.set_provider:5 +msgid "whether set, defaults to True, if False, equivalent to ``get_provider``" +msgstr "" + +#: of tensorcircuit.cloud.apis.get_task:1 +msgid "" +"Get ``Task`` object from task string, the binding device can also be " +"provided" +msgstr "" + +#: of tensorcircuit.cloud.apis.get_task_details:1 +msgid "Get task details dict given task id" +msgstr "" + +#: of tensorcircuit.cloud.apis.get_task_details:7 +msgid "" +"whether make the returned dict more readable and more phythonic, defaults" +" to False" +msgstr "" + +#: of tensorcircuit.cloud.apis.get_token:1 +msgid "" +"Get API token setted for given provider or device, if no device token " +"saved, the corresponding provider tken is returned" +msgstr "" + +#: of tensorcircuit.cloud.apis.list_devices:1 +msgid "List all devices under a provider" +msgstr "" + +#: of tensorcircuit.cloud.apis.list_properties:1 +msgid "List properties of a given device" +msgstr "" + +#: of tensorcircuit.cloud.apis.list_properties:9 +msgid "Propeties dict" +msgstr "" + +#: of tensorcircuit.cloud.apis.list_providers:1 +msgid "list all cloud providers that tensorcircuit supports" +msgstr "" + +#: of tensorcircuit.cloud.apis.list_tasks:1 +msgid "List tasks based on given filters" +msgstr "" + +#: of tensorcircuit.cloud.apis.list_tasks:9 +msgid "list of task object that satisfy these filter criteria" +msgstr "" + +#: of tensorcircuit.cloud.apis.resubmit_task:1 +msgid "Rerun the given task" +msgstr "" + +#: of tensorcircuit.cloud.apis.set_token:1 +msgid "Set API token for given provider or specifically to given device" +msgstr "" + +#: of tensorcircuit.cloud.apis.set_token:3 +msgid "the API token, defaults to None" +msgstr "" + +#: of tensorcircuit.cloud.apis.set_token:9 +msgid "whether save on the disk, defaults to True" +msgstr "" + +#: of tensorcircuit.cloud.apis.set_token:11 +msgid "if True, clear all token saved, defaults to False" +msgstr "" + +#: of tensorcircuit.cloud.apis.submit_task:1 +msgid "submit task to the cloud platform, batch submission default enabled" +msgstr "" + +#: of tensorcircuit.cloud.apis.submit_task:5 +msgid ":py:meth:`tensorcircuit.cloud.tencent.submit_task`" +msgstr "" + +#: of tensorcircuit.cloud.apis.submit_task:13 +msgid "" +"all necessary keywords arguments for task submission, see detailed API in" +" each provider backend: 1. tencent - " +":py:meth:`tensorcircuit.cloud.tencent.submit_task`" +msgstr "" + +#: of tensorcircuit.cloud.apis.submit_task:17 +msgid "The task object" +msgstr "" + +#: ../../source/api/cloud/config.rst:2 +msgid "tensorcircuit.cloud.config" +msgstr "" + +#: ../../source/api/cloud/local.rst:2 +msgid "tensorcircuit.cloud.local" +msgstr "" + +#: of tensorcircuit.cloud.local:1 +msgid "Cloud provider from local machine" +msgstr "" + +#: ../../source/api/cloud/quafu_provider.rst:2 +msgid "tensorcircuit.cloud.quafu_provider" +msgstr "" + +#: ../../source/api/cloud/tencent.rst:2 +msgid "tensorcircuit.cloud.tencent" +msgstr "" + +#: of tensorcircuit.cloud.tencent:1 +msgid "Cloud provider from Tencent" +msgstr "" + +#: of tensorcircuit.cloud.tencent.submit_task:1 +msgid "" +"Submit task via tencent provider, we suggest to enable one of the " +"compiling functionality: either in tc: frontend or in qos: backend. If " +"both are enabled, try on your own risk, some qubit mapping may fail " +"silently. If the user directly provide ``source`` or qiskit Circuit in " +"``circuit``, the qubit mapping should be taken care of by the users." +msgstr "" + +#: of tensorcircuit.cloud.tencent.submit_task:10 +msgid "language choice for ``source``, defaults to \"OPENQASM\"" +msgstr "" + +#: of tensorcircuit.cloud.tencent.submit_task:12 +msgid "number of measurement shots, defaults to 1024" +msgstr "" + +#: of tensorcircuit.cloud.tencent.submit_task:14 +msgid "submit task protocol version, defaults to \"1\"" +msgstr "" + +#: of tensorcircuit.cloud.tencent.submit_task:16 +msgid "priority for the task queue, defaults to 1" +msgstr "" + +#: of tensorcircuit.cloud.tencent.submit_task:18 +msgid "tensorcircuit or qiskit circuit object, defaults to None" +msgstr "" + +#: of tensorcircuit.cloud.tencent.submit_task:20 +msgid "directly given circuit representation, defaults to None" +msgstr "" + +#: of tensorcircuit.cloud.tencent.submit_task:22 +msgid "remarks on the task, defaults to None" +msgstr "" + +#: of tensorcircuit.cloud.tencent.submit_task:24 +msgid "whether compiling in tc via qiskit compiling system, defaults to False" +msgstr "" + +#: of tensorcircuit.cloud.tencent.submit_task:27 +msgid "alias for the argument ``compiling``" +msgstr "" + +#: of tensorcircuit.cloud.tencent.submit_task:29 +msgid "compiling options for qiskit ``transpile`` method, defaults to None" +msgstr "" + +#: of tensorcircuit.cloud.tencent.submit_task:32 +msgid "alias for the argument ``compiled_options``" +msgstr "" + +#: of tensorcircuit.cloud.tencent.submit_task:34 +msgid "whether to insert swap if necessary in qos, defaults to True" +msgstr "" + +#: of tensorcircuit.cloud.tencent.submit_task:36 +msgid "whether to compile the gate in qos, defaults to True" +msgstr "" + +#: of tensorcircuit.cloud.tencent.submit_task:38 +msgid "whether to run an initial qubit mapping in qos, defaults to False" +msgstr "" + +#: of tensorcircuit.cloud.tencent.submit_task:41 +msgid "" +"when dry run, only compiled circuit is returned (no real circuit " +"execution), defaults to False" +msgstr "" + +#: of tensorcircuit.cloud.tencent.submit_task:44 +msgid "Task object or List of Task for batch submission" +msgstr "" + +#: ../../source/api/cloud/utils.rst:2 +msgid "tensorcircuit.cloud.utils" +msgstr "" + +#: of tensorcircuit.cloud.utils:1 +msgid "utility functions for cloud connection" +msgstr "" + +#: of tensorcircuit.cloud.utils.HttpStatusError:1 +msgid "Bases: :py:class:`Exception`" +msgstr "" + +#: of tensorcircuit.cloud.utils.HttpStatusError:1 +msgid "Used when the return request has http code beyond 200" +msgstr "" + +#: of tensorcircuit.cloud.utils.set_proxy:1 +msgid "" +"str. format as \"http://user:passwd@host:port\" user passwd part can be " +"omitted if not set. None for turning off the proxy." +msgstr "" + +#: ../../source/api/cloud/wrapper.rst:2 +msgid "tensorcircuit.cloud.wrapper" +msgstr "" + +#: of tensorcircuit.cloud.wrapper:1 +msgid "higher level wrapper shortcut for submit_task" +msgstr "" + +#: of tensorcircuit.cloud.wrapper.batch_expectation_ps:1 +msgid "" +"Unified interface to compute the Pauli string expectation lists or sums " +"via simulation or on real qpu. Error mitigation, circuit compilation and " +"Pauli string grouping are all built-in." +msgstr "" + +#: of tensorcircuit.cloud.wrapper.batch_expectation_ps:4 +msgid "" +"One line access to unlock the whole power or real quantum hardware on " +"quantum cloud." +msgstr "" + +#: of tensorcircuit.cloud.wrapper.batch_expectation_ps:18 +msgid "The target circuit to compute expectation" +msgstr "" + +#: of tensorcircuit.cloud.wrapper.batch_expectation_ps:20 +msgid "" +"List of Pauli string list, eg. [[0, 1, 0], [2, 3, 3]] represents [X1, " +"Y0Z1Z2]." +msgstr "" + +#: of tensorcircuit.cloud.wrapper.batch_expectation_ps:22 +msgid "" +"The device str or object for quantum cloud module, defaults to None, None" +" is for analytical exact simulation" +msgstr "" + +#: of tensorcircuit.cloud.wrapper.batch_expectation_ps:25 +msgid "" +"List of float to indicate the final return is the weighted sum of Pauli " +"string expectations, e.g. [2., -0.3] represents the final results is 2* " +"``pss`` [0]-0.3* ``pss`` [1] defaults to None, None indicate the list of " +"expectations for ``pss`` are all returned" +msgstr "" + +#: of tensorcircuit.cloud.wrapper.batch_expectation_ps:29 +msgid "measurement shots for each expectation estimation, defaults to 8192" +msgstr "" + +#: of tensorcircuit.cloud.wrapper.batch_expectation_ps:31 +msgid "whether enable readout error mitigation for the result, defaults to True" +msgstr "" + +#: of tensorcircuit.cloud.wrapper.batch_expectation_ps:33 +msgid "" +"List of Pauli string expectation or a weighted sum float for Pauli " +"strings, depending on ``ws``" +msgstr "" + +#: of tensorcircuit.cloud.wrapper.sample_expectation_ps:1 +msgid "" +"Deprecated, please use " +":py:meth:`tensorcircuit.cloud.wrapper.batch_expectation_ps`." +msgstr "" + +#: ../../source/api/compiler.rst:2 +msgid "tensorcircuit.compiler" +msgstr "" + +#: ../../source/api/compiler/composed_compiler.rst:2 +msgid "tensorcircuit.compiler.composed_compiler" +msgstr "" + +#: of tensorcircuit.compiler.composed_compiler:1 +msgid "object oriented compiler pipeline" +msgstr "" + +#: of tensorcircuit.compiler.composed_compiler.DefaultCompiler:1 +msgid "Bases: :py:class:`~tensorcircuit.compiler.composed_compiler.Compiler`" +msgstr "" + +#: of tensorcircuit.compiler.composed_compiler.DefaultCompiler.__init__:1 +msgid "" +"A fallback choice to compile circuit running on tencent quantum cloud " +"with rz as native gate" +msgstr "" + +#: of tensorcircuit.compiler.composed_compiler.DefaultCompiler.__init__:3 +msgid "" +"qiskit compiled options to be added options documented in " +"`qiskit.transpile` method, to use tencent quantum cloud, " +"`{\"coupling_map\": d.topology()}` is in general enough, where d is a " +"device object, defaults to None, i.e. no qubit mapping is applied" +msgstr "" + +#: ../../source/api/compiler/qiskit_compiler.rst:2 +msgid "tensorcircuit.compiler.qiskit_compiler" +msgstr "" + +#: of tensorcircuit.compiler.qiskit_compiler:1 +msgid "compiler interface via qiskit" +msgstr "" + +#: of tensorcircuit.compiler.qiskit_compiler.qiskit_compile:1 +msgid "" +"compile the circuit using ``qiskit.transpile`` method with some tricks " +"and hacks" +msgstr "" + +#: of tensorcircuit.compiler.qiskit_compiler.qiskit_compile:3 +msgid "circuit in ``tc.Circuit`` or ``qiskit.QuantumCircuit`` form" +msgstr "" + +#: of tensorcircuit.compiler.qiskit_compiler.qiskit_compile:5 +msgid "info for qubit mappings, defaults to None" +msgstr "" + +#: of tensorcircuit.compiler.qiskit_compiler.qiskit_compile:7 +msgid "output circuit format, defaults to \"tc\"" +msgstr "" + +#: of tensorcircuit.compiler.qiskit_compiler.qiskit_compile:9 +msgid "``qiskit.transpile`` options in a dict, defaults to None" +msgstr "" + +#: of tensorcircuit.compiler.qiskit_compiler.qiskit_compile:11 +msgid "Tuple containing the output circuit and the qubit mapping info dict" +msgstr "" + +#: ../../source/api/compiler/simple_compiler.rst:2 +msgid "tensorcircuit.compiler.simple_compiler" +msgstr "" + +#: of tensorcircuit.compiler.simple_compiler:1 +msgid "Very simple transformations that qiskit may even fail or hard to control" +msgstr "" + +#: ../../source/api/cons.rst:2 +msgid "tensorcircuit.cons" +msgstr "" + +#: of tensorcircuit.cons:1 +msgid "Constants and setups" +msgstr "" + +#: of tensorcircuit.cons.get_contractor:1 tensorcircuit.cons.set_contractor:1 +msgid "" +"To set runtime contractor of the tensornetwork for a better contraction " +"path. For more information on the usage of contractor, please refer to " +"independent tutorial." +msgstr "" + +#: of tensorcircuit.cons.get_contractor:4 tensorcircuit.cons.set_contractor:4 +msgid "" +"\"auto\", \"greedy\", \"branch\", \"plain\", \"tng\", \"custom\", " +"\"custom_stateful\". defaults to None (\"auto\")" +msgstr "" + +#: of tensorcircuit.cons.get_contractor:6 tensorcircuit.cons.set_contractor:6 +msgid "Valid for \"custom\" or \"custom_stateful\" as method, defaults to None" +msgstr "" + +#: of tensorcircuit.cons.get_contractor:8 tensorcircuit.cons.set_contractor:8 +msgid "" +"It is not very useful, as ``memory_limit`` leads to ``branch`` " +"contraction instead of ``greedy`` which is rather slow, defaults to None" +msgstr "" + +#: of tensorcircuit.cons.get_contractor:11 tensorcircuit.cons.set_contractor:11 +msgid "Tensornetwork version is too low to support some of the contractors." +msgstr "" + +#: of tensorcircuit.cons.get_contractor:12 tensorcircuit.cons.set_contractor:12 +msgid "Unknown method options." +msgstr "" + +#: of tensorcircuit.cons.get_contractor:13 tensorcircuit.cons.set_contractor:13 +msgid "The new tensornetwork with its contractor set." +msgstr "" + +#: of tensorcircuit.cons.get_dtype:1 tensorcircuit.cons.set_dtype:1 +msgid "Set the global runtime numerical dtype of tensors." +msgstr "" + +#: of tensorcircuit.cons.get_dtype:3 tensorcircuit.cons.set_dtype:3 +msgid "" +"\"complex64\"/\"float32\" or \"complex128\"/\"float64\", defaults to " +"None, which is equivalent to \"complex64\"." +msgstr "" + +#: of tensorcircuit.cons.get_dtype:6 tensorcircuit.cons.set_dtype:6 +msgid "complex dtype str and the corresponding real dtype str" +msgstr "" + +#: of tensorcircuit.cons.plain_contractor:1 +msgid "The naive state-vector simulator contraction path." +msgstr "" + +#: of tensorcircuit.cons.plain_contractor:3 +msgid "The list of ``tn.Node``." +msgstr "" + +#: of tensorcircuit.cons.plain_contractor:5 +msgid "The list of dangling node edges, defaults to be None." +msgstr "" + +#: of tensorcircuit.cons.plain_contractor:7 +msgid "The ``tn.Node`` after contraction" +msgstr "" + +#: of tensorcircuit.cons.runtime_backend:1 +msgid "Context manager to set with-level runtime backend" +msgstr "" + +#: of tensorcircuit.cons.runtime_backend:3 +#: tensorcircuit.cons.set_function_backend:3 +msgid "\"numpy\", \"tensorflow\", \"jax\", \"pytorch\", defaults to None" +msgstr "" + +#: of tensorcircuit.cons.runtime_backend tensorcircuit.cons.runtime_contractor +#: tensorcircuit.cons.runtime_dtype +msgid "yield" +msgstr "" + +#: of tensorcircuit.cons.runtime_backend:5 +msgid "the backend object" +msgstr "" + +#: of tensorcircuit.cons.runtime_contractor:1 +msgid "Context manager to change with-levek contractor" +msgstr "" + +#: of tensorcircuit.cons.runtime_dtype:1 +msgid "Context manager to set with-level runtime dtype" +msgstr "" + +#: of tensorcircuit.cons.runtime_dtype:3 +msgid "\"complex64\" or \"complex128\", defaults to None (\"complex64\")" +msgstr "" + +#: of tensorcircuit.cons.runtime_dtype:5 +msgid "complex dtype str and real dtype str" +msgstr "" + +#: of tensorcircuit.cons.set_tensornetwork_backend:1 +msgid "To set the runtime backend of tensorcircuit." +msgstr "" + +#: of tensorcircuit.cons.set_tensornetwork_backend:3 +msgid "" +"Note: ``tc.set_backend`` and ``tc.cons.set_tensornetwork_backend`` are " +"the same." +msgstr "" + +#: of tensorcircuit.cons.set_tensornetwork_backend:27 +msgid "" +"\"numpy\", \"tensorflow\", \"jax\", \"pytorch\". defaults to None, which " +"gives the same behavior as " +"``tensornetwork.backend_contextmanager.get_default_backend()``." +msgstr "" + +#: of tensorcircuit.cons.set_tensornetwork_backend:30 +msgid "Whether the object should be set as global." +msgstr "" + +#: of tensorcircuit.cons.set_function_backend:1 +msgid "Function decorator to set function-level runtime backend" +msgstr "" + +#: of tensorcircuit.cons.set_function_backend:5 +msgid "Decorated function" +msgstr "" + +#: of tensorcircuit.cons.set_function_contractor:1 +msgid "Function decorate to change function-level contractor" +msgstr "" + +#: of tensorcircuit.cons.set_function_dtype:1 +msgid "Function decorator to set function-level numerical dtype" +msgstr "" + +#: of tensorcircuit.cons.set_function_dtype:3 +msgid "\"complex64\" or \"complex128\", defaults to None" +msgstr "" + +#: of tensorcircuit.cons.set_function_dtype:5 +msgid "The decorated function" +msgstr "" + +#: of tensorcircuit.cons.split_rules:1 +msgid "Obtain the direcionary of truncation rules" +msgstr "" + +#: of tensorcircuit.cons.split_rules:3 +#: tensorcircuit.mps_base.FiniteMPS.apply_two_site_gate:11 +msgid "The maximum number of singular values to keep." +msgstr "" + +#: of tensorcircuit.cons.split_rules:5 +#: tensorcircuit.mps_base.FiniteMPS.apply_two_site_gate:13 +msgid "The maximum allowed truncation error." +msgstr "" + +#: ../../source/api/densitymatrix.rst:2 +msgid "tensorcircuit.densitymatrix" +msgstr "" + +#: of tensorcircuit.densitymatrix:1 +msgid "Quantum circuit class but with density matrix simulator" +msgstr "" + +#: of tensorcircuit.densitymatrix.DMCircuit.__init__:1 +msgid "The density matrix simulator based on tensornetwork engine." +msgstr "" + +#: of tensorcircuit.densitymatrix.DMCircuit.__init__:3 +msgid "Number of qubits" +msgstr "" + +#: of tensorcircuit.densitymatrix.DMCircuit.__init__:5 +msgid "if True, nothing initialized, only for internal use, defaults to False" +msgstr "" + +#: of tensorcircuit.densitymatrix.DMCircuit.__init__:7 +msgid "the state input for the circuit, defaults to None" +msgstr "" + +#: of tensorcircuit.densitymatrix.DMCircuit.__init__:9 +msgid "QuVector for a MPS like initial pure state." +msgstr "" + +#: of tensorcircuit.densitymatrix.DMCircuit.__init__:11 +msgid "the density matrix input for the circuit, defaults to None" +msgstr "" + +#: of tensorcircuit.densitymatrix.DMCircuit.__init__:13 +msgid "QuOperator for a MPO like initial density matrix." +msgstr "" + +#: of tensorcircuit.densitymatrix.DMCircuit.densitymatrix:1 +msgid "Return the output density matrix of the circuit." +msgstr "" + +#: of tensorcircuit.densitymatrix.DMCircuit.densitymatrix:3 +msgid "" +"check whether the final return is a legal density matrix, defaults to " +"False" +msgstr "" + +#: of tensorcircuit.densitymatrix.DMCircuit.densitymatrix:5 +msgid "whether to reuse previous results, defaults to True" +msgstr "" + +#: of tensorcircuit.densitymatrix.DMCircuit.densitymatrix:7 +msgid "The output densitymatrix in 2D shape tensor form" +msgstr "" + +#: of tensorcircuit.densitymatrix.DMCircuit.expectation:6 +msgid "whether contract the density matrix in advance, defaults to True" +msgstr "" + +#: of tensorcircuit.densitymatrix.DMCircuit.get_dm_as_quoperator:1 +msgid "" +"Get the representation of the output state in the form of ``QuOperator`` " +"while maintaining the circuit uncomputed" +msgstr "" + +#: of tensorcircuit.densitymatrix.DMCircuit.get_dm_as_quoperator:4 +msgid "``QuOperator`` representation of the output state from the circuit" +msgstr "" + +#: of tensorcircuit.densitymatrix.DMCircuit.to_circuit:1 +msgid "" +"convert into state simulator (current implementation ignores all noise " +"channels)" +msgstr "" + +#: of tensorcircuit.densitymatrix.DMCircuit.to_circuit:4 +msgid "kws to initialize circuit object, defaults to None" +msgstr "" + +#: of tensorcircuit.densitymatrix.DMCircuit.to_circuit:7 +msgid "Circuit with no noise" +msgstr "" + +#: of tensorcircuit.densitymatrix.DMCircuit.wavefunction:1 +msgid "" +"get the wavefunction of outputs, raise error if the final state is not " +"purified [Experimental: the phase factor is not fixed for different " +"backend]" +msgstr "" + +#: of tensorcircuit.densitymatrix.DMCircuit.wavefunction:5 +msgid "wavefunction vector" +msgstr "" + +#: of tensorcircuit.densitymatrix.DMCircuit2:1 +msgid "Bases: :py:class:`~tensorcircuit.densitymatrix.DMCircuit`" +msgstr "" + +#: ../../source/api/experimental.rst:2 +msgid "tensorcircuit.experimental" +msgstr "" + +#: of tensorcircuit.experimental:1 +msgid "Experimental features" +msgstr "" + +#: of tensorcircuit.experimental.evol_global:1 +msgid "" +"ode evolution of time dependent Hamiltonian on circuit of all qubits " +"[only jax backend support for now]" +msgstr "" + +#: of tensorcircuit.experimental.evol_global:6 +msgid "" +"h_fun should return a **SPARSE** Hamiltonian matrix with input arguments " +"time and *args" +msgstr "" + +#: of tensorcircuit.experimental.evol_local:1 +msgid "" +"ode evolution of time dependent Hamiltonian on circuit of given indices " +"[only jax backend support for now]" +msgstr "" + +#: of tensorcircuit.experimental.evol_local:8 +msgid "" +"h_fun should return a dense Hamiltonian matrix with input arguments time " +"and *args" +msgstr "" + +#: of tensorcircuit.experimental.evol_local:11 +msgid "evolution time" +msgstr "" + +#: of tensorcircuit.experimental.hamiltonian_evol:1 +msgid "" +"Fast implementation of static full Hamiltonian evolution (default as " +"imaginary time)" +msgstr "" + +#: of tensorcircuit.experimental.hamiltonian_evol:13 +msgid "result dynamics on ``tlist``" +msgstr "" + +#: of tensorcircuit.experimental.parameter_shift_grad:1 +msgid "" +"similar to `grad` function but using parameter shift internally instead " +"of AD, vmap is utilized for evaluation, so the speed is still ok" +msgstr "" + +#: of tensorcircuit.experimental.parameter_shift_grad:4 +#: tensorcircuit.experimental.parameter_shift_grad_v2:6 +msgid "quantum function with weights in and expectation out" +msgstr "" + +#: of tensorcircuit.experimental.parameter_shift_grad:6 +#: tensorcircuit.experimental.parameter_shift_grad_v2:8 +msgid "label which args should be differentiated, defaults to 0" +msgstr "" + +#: of tensorcircuit.experimental.parameter_shift_grad:9 +#: tensorcircuit.experimental.parameter_shift_grad_v2:11 +msgid "whether jit the original function `f` at the beginning, defaults to False" +msgstr "" + +#: of tensorcircuit.experimental.parameter_shift_grad:12 +#: tensorcircuit.experimental.parameter_shift_grad_v2:14 +msgid "" +"two floats for the delta shift on the numerator and dominator, defaults " +"to (pi/2, 2) for parameter shift" +msgstr "" + +#: of tensorcircuit.experimental.parameter_shift_grad:15 +#: tensorcircuit.experimental.parameter_shift_grad_v2:17 +msgid "the grad function" +msgstr "" + +#: of tensorcircuit.experimental.parameter_shift_grad_v2:1 +msgid "" +"similar to `grad` function but using parameter shift internally instead " +"of AD, vmap is utilized for evaluation, v2 also supports random generator" +" for finite measurememt shot, only jax backend is supported, since no " +"vmap randomness is available in tensorflow" +msgstr "" + +#: ../../source/api/gates.rst:2 +msgid "tensorcircuit.gates" +msgstr "" + +#: of tensorcircuit.gates:1 +msgid "" +"Declarations of single-qubit and two-qubit gates and their corresponding " +"matrix." +msgstr "" + +#: of tensorcircuit.gates.Gate:1 +msgid "Bases: :py:class:`~tensornetwork.network_components.Node`" +msgstr "" + +#: of tensorcircuit.gates.Gate:1 +msgid "Wrapper of tn.Node, quantum gate" +msgstr "" + +#: of tensornetwork.network_components.Node.__init__:1 +msgid "Create a node." +msgstr "" + +#: of tensornetwork.network_components.Node.__init__:3 +msgid "" +"The concrete that is represented by this node, or a `AbstractNode` " +"object. If a tensor is passed, it can be be either a numpy array or the " +"tensor-type of the used backend. If a `AbstractNode` is passed, the " +"passed node has to have the same backend as given by `backend`." +msgstr "" + +#: of tensornetwork.network_components.Node.__init__:7 +msgid "Name of the node. Used primarily for debugging." +msgstr "" + +#: of tensornetwork.network_components.AbstractNode.add_axis_names:3 +#: tensornetwork.network_components.Node.__init__:8 +msgid "List of names for each of the tensor's axes." +msgstr "" + +#: of tensornetwork.network_components.Node.__init__:9 +msgid "The name of the backend or an instance of a `AbstractBackend`." +msgstr "" + +#: of tensornetwork.network_components.AbstractNode.add_axis_names:5 +#: tensornetwork.network_components.Node.__init__:11 +msgid "" +"If there is a repeated name in `axis_names` or if the length doesn't " +"match the shape of the tensor." +msgstr "" + +#: of tensornetwork.network_components.AbstractNode.add_axis_names:1 +msgid "Add axis names to a Node." +msgstr "" + +#: of tensornetwork.network_components.AbstractNode.add_edge:1 +msgid "Add an edge to the node on the given axis." +msgstr "" + +#: of tensornetwork.network_components.AbstractNode.add_edge:3 +msgid "The edge to add." +msgstr "" + +#: of tensornetwork.network_components.AbstractNode.add_edge:4 +msgid "The axis the edge points to." +msgstr "" + +#: of tensornetwork.network_components.AbstractNode.add_edge:5 +msgid "If true, replace the existing edge with the new one." +msgstr "" + +#: of tensornetwork.network_components.AbstractNode.add_edge:7 +msgid "If the edge on axis is not dangling." +msgstr "" + +#: of tensornetwork.network_components.Node.from_serial_dict:1 +msgid "Return a node given a serialized dict representing it." +msgstr "" + +#: of tensornetwork.network_components.Node.from_serial_dict:3 +msgid "A python dict representing a serialized node." +msgstr "" + +#: of tensornetwork.network_components.Node.from_serial_dict:5 +msgid "A node." +msgstr "" + +#: of tensornetwork.network_components.AbstractNode.get_all_dangling:1 +msgid "Return the set of dangling edges connected to this node." +msgstr "" + +#: of tensornetwork.network_components.AbstractNode.get_all_nondangling:1 +msgid "Return the set of nondangling edges connected to this node." +msgstr "" + +#: of tensornetwork.network_components.AbstractNode.get_axis_number:1 +msgid "Get the axis number for a given axis name or value." +msgstr "" + +#: of tensornetwork.network_components.AbstractNode.get_dimension:1 +msgid "Get the dimension of the given axis." +msgstr "" + +#: of tensornetwork.network_components.AbstractNode.get_dimension:3 +msgid "The axis of the underlying tensor." +msgstr "" + +#: of tensornetwork.network_components.AbstractNode.get_dimension:5 +msgid "The dimension of the given axis." +msgstr "" + +#: of tensornetwork.network_components.AbstractNode.get_dimension:7 +msgid "if axis isn't an int or if axis is too large or small." +msgstr "" + +#: of tensornetwork.network_components.AbstractNode.get_rank:1 +msgid "Return rank of tensor represented by self." +msgstr "" + +#: of tensornetwork.network_components.AbstractNode.reorder_axes:1 +msgid "Reorder axes of the node's tensor." +msgstr "" + +#: of tensornetwork.network_components.AbstractNode.reorder_axes:3 +msgid "This will also update all of the node's edges." +msgstr "" + +#: of tensornetwork.network_components.AbstractNode.reorder_axes:5 +msgid "Permutation of the dimensions of the node's tensor." +msgstr "" + +#: of tensornetwork.network_components.AbstractNode.reorder_axes:7 +#: tensornetwork.network_components.AbstractNode.reorder_edges:9 +msgid "This node post reordering." +msgstr "" + +#: of tensornetwork.network_components.AbstractNode.reorder_axes:9 +#: tensornetwork.network_components.AbstractNode.reorder_edges:12 +msgid "If the Node has no tensor." +msgstr "" + +#: of tensornetwork.network_components.AbstractNode.reorder_edges:1 +msgid "Reorder the edges for this given Node." +msgstr "" + +#: of tensornetwork.network_components.AbstractNode.reorder_edges:3 +msgid "" +"This will reorder the node's edges and transpose the underlying tensor " +"accordingly." +msgstr "" + +#: of tensornetwork.network_components.AbstractNode.reorder_edges:6 +msgid "List of edges. The order in the list determines the new edge ordering." +msgstr "" + +#: of tensornetwork.network_components.AbstractNode.reorder_edges:11 +msgid "" +"If either the list of edges is not the same as expected or if you try" +" to reorder with a trace edge." +msgstr "" + +#: of tensornetwork.network_components.Node.to_serial_dict:1 +msgid "Return a serializable dict representing the node." +msgstr "" + +#: of tensornetwork.network_components.Node.to_serial_dict:3 +msgid "Returns: A dict object." +msgstr "" + +#: of tensorcircuit.gates.GateVF:1 +msgid "Bases: :py:class:`~tensorcircuit.gates.GateF`" +msgstr "" + +#: of tensorcircuit.gates.any_gate:1 +msgid "Note one should provide the gate with properly reshaped." +msgstr "" + +#: of tensorcircuit.gates.any_gate:3 +msgid "corresponding gate" +msgstr "" + +#: of tensorcircuit.gates.any_gate:5 +msgid "The name of the gate." +msgstr "" + +#: of tensorcircuit.gates.any_gate:7 +msgid "the resulted gate" +msgstr "" + +#: of tensorcircuit.gates.num_to_tensor:1 +msgid "Convert the inputs to Tensor with specified dtype." +msgstr "" + +#: of tensorcircuit.gates.num_to_tensor:35 +msgid "inputs" +msgstr "" + +#: of tensorcircuit.gates.num_to_tensor:37 +msgid "dtype of the output Tensors" +msgstr "" + +#: of tensorcircuit.gates.num_to_tensor:39 +msgid "List of Tensors" +msgstr "" + +#: of tensorcircuit.gates.bmatrix:1 +msgid "Returns a :math:`\\LaTeX` bmatrix." +msgstr "" + +#: of tensorcircuit.gates.bmatrix:13 +msgid "Formatted Display:" +msgstr "" + +#: of tensorcircuit.gates.bmatrix:15 +msgid "" +"\\begin{bmatrix} 1.+0.j & 0.+0.j\\\\ 0.+0.j & 1.+0.j \\end{bmatrix}" +"\n" +"\n" +msgstr "" + +#: of tensorcircuit.gates.bmatrix:18 +msgid "2D numpy array" +msgstr "" + +#: of tensorcircuit.gates.bmatrix:20 +msgid "ValueError(\"bmatrix can at most display two dimensions\")" +msgstr "" + +#: of tensorcircuit.gates.bmatrix:21 +msgid ":math:`\\LaTeX`-formatted string for bmatrix of the array a" +msgstr "" + +#: of tensorcircuit.gates.cr_gate:1 +msgid "" +"Controlled rotation gate. When the control qubit is 1, `rgate` is applied" +" to the target qubit." +msgstr "" + +#: of tensorcircuit.gates.cr_gate:3 tensorcircuit.gates.cr_gate:5 +#: tensorcircuit.gates.cr_gate:7 tensorcircuit.gates.exponential_gate:12 +#: tensorcircuit.gates.exponential_gate_unity:13 +#: tensorcircuit.gates.iswap_gate:12 tensorcircuit.gates.r_gate:9 +#: tensorcircuit.gates.r_gate:11 tensorcircuit.gates.r_gate:13 +#: tensorcircuit.gates.rgate_theoretical:8 +#: tensorcircuit.gates.rgate_theoretical:10 +#: tensorcircuit.gates.rgate_theoretical:12 tensorcircuit.gates.rx_gate:6 +#: tensorcircuit.gates.rxx_gate:13 tensorcircuit.gates.ry_gate:6 +#: tensorcircuit.gates.ryy_gate:13 tensorcircuit.gates.rz_gate:6 +#: tensorcircuit.gates.rzz_gate:13 +msgid "angle in radians" +msgstr "" + +#: of tensorcircuit.gates.cr_gate:10 +msgid "CR Gate" +msgstr "" + +#: of tensorcircuit.gates.exponential_gate_unity:1 +#: tensorcircuit.gates.rxx_gate:1 tensorcircuit.gates.ryy_gate:1 +#: tensorcircuit.gates.rzz_gate:1 +msgid "" +"Faster exponential gate directly implemented based on RHS. Only works " +"when :math:`U^2 = I` is an identity matrix." +msgstr "" + +#: of tensorcircuit.gates.exponential_gate_unity:3 +#: tensorcircuit.gates.rxx_gate:3 tensorcircuit.gates.ryy_gate:3 +#: tensorcircuit.gates.rzz_gate:3 +msgid "" +"\\textrm{exp}(U) &= e^{-j \\theta U} \\\\\n" +" &= \\cos(\\theta) I - j \\sin(\\theta) U \\\\\n" +"\n" +msgstr "" + +#: of tensorcircuit.gates.exponential_gate:6 +#: tensorcircuit.gates.exponential_gate_unity:7 +#: tensorcircuit.gates.multicontrol_gate:7 tensorcircuit.gates.rxx_gate:7 +#: tensorcircuit.gates.ryy_gate:7 tensorcircuit.gates.rzz_gate:7 +msgid "input unitary :math:`U`" +msgstr "" + +#: of tensorcircuit.gates.exponential_gate:8 +#: tensorcircuit.gates.exponential_gate:10 +#: tensorcircuit.gates.exponential_gate_unity:9 +#: tensorcircuit.gates.exponential_gate_unity:11 tensorcircuit.gates.rxx_gate:9 +#: tensorcircuit.gates.rxx_gate:11 tensorcircuit.gates.ryy_gate:9 +#: tensorcircuit.gates.ryy_gate:11 tensorcircuit.gates.rzz_gate:9 +#: tensorcircuit.gates.rzz_gate:11 +msgid "alias for the argument ``unitary``" +msgstr "" + +#: of tensorcircuit.gates.exponential_gate_unity:15 +#: tensorcircuit.gates.rxx_gate:15 tensorcircuit.gates.ryy_gate:15 +#: tensorcircuit.gates.rzz_gate:15 +msgid "if True, the angel theta is mutiplied by 1/2, defaults to False" +msgstr "" + +#: of tensorcircuit.gates.exponential_gate:14 +#: tensorcircuit.gates.exponential_gate_unity:18 +#: tensorcircuit.gates.rxx_gate:18 tensorcircuit.gates.ryy_gate:18 +#: tensorcircuit.gates.rzz_gate:18 +msgid "suffix of Gate name" +msgstr "" + +#: of tensorcircuit.gates.exponential_gate:15 +#: tensorcircuit.gates.exponential_gate_unity:20 +#: tensorcircuit.gates.rxx_gate:20 tensorcircuit.gates.ryy_gate:20 +#: tensorcircuit.gates.rzz_gate:20 +msgid "Exponential Gate" +msgstr "" + +#: of tensorcircuit.gates.exponential_gate:1 +msgid "Exponential gate." +msgstr "" + +#: of tensorcircuit.gates.exponential_gate:3 +msgid "" +"\\textrm{exp}(U) = e^{-j \\theta U}\n" +"\n" +msgstr "" + +#: of tensorcircuit.gates.get_u_parameter:1 +msgid "From the single qubit unitary to infer three angles of IBMUgate," +msgstr "" + +#: of tensorcircuit.gates.get_u_parameter:3 +msgid "numpy array, no backend agnostic version for now" +msgstr "" + +#: of tensorcircuit.gates.get_u_parameter:5 +msgid "theta, phi, lbd" +msgstr "" + +#: of tensorcircuit.gates.iswap_gate:1 +msgid "iSwap gate." +msgstr "" + +#: of tensorcircuit.gates.iswap_gate:3 +msgid "" +"\\textrm{iSwap}(\\theta) =\n" +"\\begin{pmatrix}\n" +" 1 & 0 & 0 & 0\\\\\n" +" 0 & \\cos(\\frac{\\pi}{2} \\theta ) & j \\sin(\\frac{\\pi}{2} \\theta" +" ) & 0\\\\\n" +" 0 & j \\sin(\\frac{\\pi}{2} \\theta ) & \\cos(\\frac{\\pi}{2} \\theta" +" ) & 0\\\\\n" +" 0 & 0 & 0 & 1\\\\\n" +"\\end{pmatrix}\n" +"\n" +msgstr "" + +#: of tensorcircuit.gates.iswap_gate:14 +msgid "iSwap Gate" +msgstr "" + +#: of tensorcircuit.gates.matrix_for_gate:1 +msgid "Convert Gate to numpy array." +msgstr "" + +#: of tensorcircuit.gates.matrix_for_gate:10 +msgid "input Gate" +msgstr "" + +#: of tensorcircuit.gates.matrix_for_gate:12 +msgid "Corresponding Tensor" +msgstr "" + +#: of tensorcircuit.gates.meta_gate:1 +msgid "" +"Inner helper function to generate gate functions, such as ``z()`` from " +"``_z_matrix``" +msgstr "" + +#: of tensorcircuit.gates.multicontrol_gate:1 +msgid "" +"Multicontrol gate. If the control qubits equal to ``ctrl``, :math:`U` is " +"applied to the target qubits." +msgstr "" + +#: of tensorcircuit.gates.multicontrol_gate:5 +msgid "" +"E.g., ``multicontrol_gate(tc.gates._zz_matrix, [1, 0, 1])`` returns a " +"gate of 5 qubits," +msgstr "" + +#: of tensorcircuit.gates.multicontrol_gate:4 +msgid "" +"where the last 2 qubits are applied :math:`ZZ` gate, if the first 3 " +"qubits are :math:`\\ket{101}`." +msgstr "" + +#: of tensorcircuit.gates.multicontrol_gate:9 +msgid "control bit sequence" +msgstr "" + +#: of tensorcircuit.gates.multicontrol_gate:11 +msgid "Multicontrol Gate" +msgstr "" + +#: of tensorcircuit.gates.phase_gate:1 +msgid "The phase gate" +msgstr "" + +#: of tensorcircuit.gates.phase_gate:3 +msgid "" +"\\textrm{phase}(\\theta) =\n" +"\\begin{pmatrix}\n" +" 1 & 0 \\\\\n" +" 0 & e^{i\\theta} \\\\\n" +"\\end{pmatrix}\n" +"\n" +msgstr "" + +#: of tensorcircuit.gates.phase_gate:10 +msgid "angle in radians, defaults to 0" +msgstr "" + +#: of tensorcircuit.gates.phase_gate:12 +msgid "phase gate" +msgstr "" + +#: of tensorcircuit.gates.r_gate:1 +msgid "General single qubit rotation gate" +msgstr "" + +#: of tensorcircuit.gates.r_gate:3 +msgid "" +"R(\\theta, \\alpha, \\phi) = j \\cos(\\theta) I\n" +"- j \\cos(\\phi) \\sin(\\alpha) \\sin(\\theta) X\n" +"- j \\sin(\\phi) \\sin(\\alpha) \\sin(\\theta) Y\n" +"- j \\sin(\\theta) \\cos(\\alpha) Z\n" +"\n" +msgstr "" + +#: of tensorcircuit.gates.r_gate:16 +msgid "R Gate" +msgstr "" + +#: of tensorcircuit.gates.random_single_qubit_gate:1 +msgid "Random single qubit gate described in https://arxiv.org/abs/2002.07730." +msgstr "" + +#: of tensorcircuit.gates.random_single_qubit_gate:3 +msgid "A random single-qubit gate" +msgstr "" + +#: of tensorcircuit.gates.random_two_qubit_gate:1 +msgid "Returns a random two-qubit gate." +msgstr "" + +#: of tensorcircuit.gates.random_two_qubit_gate:3 +msgid "A random two-qubit gate" +msgstr "" + +#: of tensorcircuit.gates.rgate_theoretical:1 +msgid "" +"Rotation gate implemented by matrix exponential. The output is the same " +"as `rgate`." +msgstr "" + +#: of tensorcircuit.gates.rgate_theoretical:3 +msgid "" +"R(\\theta, \\alpha, \\phi) = e^{-j \\theta \\left[\\sin(\\alpha) " +"\\cos(\\phi) X\n" +" + \\sin(\\alpha) \\sin(\\phi) " +"Y\n" +" + \\cos(\\alpha) Z\\right]}\n" +"\n" +msgstr "" + +#: of tensorcircuit.gates.rgate_theoretical:14 +msgid "Rotation Gate" +msgstr "" + +#: of tensorcircuit.gates.rx_gate:1 +msgid "Rotation gate along :math:`x` axis." +msgstr "" + +#: of tensorcircuit.gates.rx_gate:3 +msgid "" +"RX(\\theta) = e^{-j\\frac{\\theta}{2}X}\n" +"\n" +msgstr "" + +#: of tensorcircuit.gates.rx_gate:8 +msgid "RX Gate" +msgstr "" + +#: of tensorcircuit.gates.ry_gate:1 +msgid "Rotation gate along :math:`y` axis." +msgstr "" + +#: of tensorcircuit.gates.ry_gate:3 +msgid "" +"RY(\\theta) = e^{-j\\frac{\\theta}{2}Y}\n" +"\n" +msgstr "" + +#: of tensorcircuit.gates.ry_gate:8 +msgid "RY Gate" +msgstr "" + +#: of tensorcircuit.gates.rz_gate:1 +msgid "Rotation gate along :math:`z` axis." +msgstr "" + +#: of tensorcircuit.gates.rz_gate:3 +msgid "" +"RZ(\\theta) = e^{-j\\frac{\\theta}{2}Z}\n" +"\n" +msgstr "" + +#: of tensorcircuit.gates.rz_gate:8 +msgid "RZ Gate" +msgstr "" + +#: of tensorcircuit.gates.u_gate:1 +msgid "" +"IBMQ U gate following the converntion of OpenQASM3.0. See `OpenQASM doc " +"`_" +msgstr "" + +#: of tensorcircuit.gates.u_gate:4 +msgid "" +"\\begin{split}U(\\theta,\\phi,\\lambda) := \\left(\\begin{array}{cc}\n" +"\\cos(\\theta/2) & -e^{i\\lambda}\\sin(\\theta/2) \\\\\n" +"e^{i\\phi}\\sin(\\theta/2) & e^{i(\\phi+\\lambda)}\\cos(\\theta/2) " +"\\end{array}\\right).\\end{split}" +msgstr "" + +#: of tensorcircuit.gates.u_gate:10 tensorcircuit.gates.u_gate:12 +#: tensorcircuit.gates.u_gate:14 +msgid "_description_, defaults to 0" +msgstr "" + +#: ../../source/api/interfaces.rst:2 +msgid "tensorcircuit.interfaces" +msgstr "" + +#: ../../source/api/interfaces/numpy.rst:2 +msgid "tensorcircuit.interfaces.numpy" +msgstr "" + +#: of tensorcircuit.interfaces.numpy:1 +msgid "Interface wraps quantum function as a numpy function" +msgstr "" + +#: of tensorcircuit.interfaces.numpy.numpy_interface:1 +msgid "Convert ``fun`` on ML backend into a numpy function" +msgstr "" + +#: of tensorcircuit.interfaces.numpy.numpy_interface:23 +msgid "The quantum function" +msgstr "" + +#: of tensorcircuit.interfaces.numpy.numpy_interface:25 +#: tensorcircuit.interfaces.scipy.scipy_optimize_interface:39 +msgid "whether to jit ``fun``, defaults to True" +msgstr "" + +#: of tensorcircuit.interfaces.numpy.numpy_interface:27 +msgid "The numpy interface compatible version of ``fun``" +msgstr "" + +#: ../../source/api/interfaces/scipy.rst:2 +msgid "tensorcircuit.interfaces.scipy" +msgstr "" + +#: of tensorcircuit.interfaces.scipy:1 +msgid "Interface wraps quantum function as a scipy function for optimization" +msgstr "" + +#: of tensorcircuit.interfaces.scipy.scipy_optimize_interface:1 +msgid "Convert ``fun`` into a scipy optimize interface compatible version" +msgstr "" + +#: of tensorcircuit.interfaces.scipy.scipy_optimize_interface:35 +msgid "The quantum function with scalar out that to be optimized" +msgstr "" + +#: of tensorcircuit.interfaces.scipy.scipy_optimize_interface:37 +msgid "the shape of parameters that ``fun`` accepts, defaults to None" +msgstr "" + +#: of tensorcircuit.interfaces.scipy.scipy_optimize_interface:41 +msgid "" +"whether using gradient-based or gradient free scipy optimize interface, " +"defaults to True" +msgstr "" + +#: of tensorcircuit.interfaces.scipy.scipy_optimize_interface:44 +msgid "The scipy interface compatible version of ``fun``" +msgstr "" + +#: ../../source/api/interfaces/tensorflow.rst:2 +msgid "tensorcircuit.interfaces.tensorflow" +msgstr "" + +#: of tensorcircuit.interfaces.tensorflow:1 +msgid "Interface wraps quantum function as a tensorflow function" +msgstr "" + +#: of tensorcircuit.interfaces.tensorflow.tensorflow_interface:1 +msgid "" +"Wrap a quantum function on different ML backend with a tensorflow " +"interface." +msgstr "" + +#: of tensorcircuit.interfaces.tensorflow.tensorflow_interface:22 +#: tensorcircuit.interfaces.torch.torch_interface:28 +msgid "The quantum function with tensor in and tensor out" +msgstr "" + +#: of tensorcircuit.interfaces.tensorflow.tensorflow_interface:24 +msgid "output tf dtype or in str" +msgstr "" + +#: of tensorcircuit.interfaces.tensorflow.tensorflow_interface:26 +#: tensorcircuit.interfaces.torch.torch_interface:30 +msgid "whether to jit ``fun``, defaults to False" +msgstr "" + +#: of tensorcircuit.interfaces.tensorflow.tensorflow_interface:28 +#: tensorcircuit.interfaces.torch.torch_interface:32 +msgid "whether transform tensor backend via dlpack, defaults to False" +msgstr "" + +#: of tensorcircuit.interfaces.tensorflow.tensorflow_interface:30 +#: tensorcircuit.interfaces.torch.torch_interface:34 +msgid "" +"The same quantum function but now with torch tensor in and torch tensor " +"out while AD is also supported" +msgstr "" + +#: ../../source/api/interfaces/tensortrans.rst:2 +msgid "tensorcircuit.interfaces.tensortrans" +msgstr "" + +#: of tensorcircuit.interfaces.tensortrans:1 +msgid "general function for interfaces transformation" +msgstr "" + +#: of tensorcircuit.interfaces.tensortrans.args_to_tensor:1 +msgid "" +"Function decorator that automatically convert inputs to tensors on " +"current backend" +msgstr "" + +#: of tensorcircuit.interfaces.tensortrans.args_to_tensor:63 +msgid "" +"the wrapped function whose arguments in ``argnums`` position are expected" +" to be tensor format" +msgstr "" + +#: of tensorcircuit.interfaces.tensortrans.args_to_tensor:66 +msgid "position of args under the auto conversion, defaults to 0" +msgstr "" + +#: of tensorcircuit.interfaces.tensortrans.args_to_tensor:68 +msgid "" +"try reshape all input tensor as matrix with shape rank 2, defaults to " +"False" +msgstr "" + +#: of tensorcircuit.interfaces.tensortrans.args_to_tensor:71 +msgid "convert ``Gate`` to tensor, defaults to False" +msgstr "" + +#: of tensorcircuit.interfaces.tensortrans.args_to_tensor:73 +msgid "reshape tensor from ``Gate`` input as matrix, defaults to True" +msgstr "" + +#: of tensorcircuit.interfaces.tensortrans.args_to_tensor:75 +msgid "convert ``QuOperator`` to tensor, defaults to False" +msgstr "" + +#: of tensorcircuit.interfaces.tensortrans.args_to_tensor:77 +msgid "reshape tensor from ``QuOperator`` input as matrix, defaults to True" +msgstr "" + +#: of tensorcircuit.interfaces.tensortrans.args_to_tensor:79 +msgid "whether cast to backend dtype, defaults to True" +msgstr "" + +#: of tensorcircuit.interfaces.tensortrans.args_to_tensor:81 +msgid "The wrapped function" +msgstr "" + +#: of tensorcircuit.interfaces.tensortrans.general_args_to_numpy:1 +msgid "Given a pytree, get the corresponding numpy array pytree" +msgstr "" + +#: of tensorcircuit.interfaces.tensortrans.general_args_to_numpy:3 +msgid "pytree" +msgstr "" + +#: of tensorcircuit.interfaces.tensortrans.general_args_to_numpy:5 +msgid "the same format pytree with all tensor replaced by numpy array" +msgstr "" + +#: of tensorcircuit.interfaces.tensortrans.numpy_args_to_backend:1 +msgid "Given a pytree of numpy arrays, get the corresponding tensor pytree" +msgstr "" + +#: of tensorcircuit.interfaces.tensortrans.numpy_args_to_backend:3 +msgid "pytree of numpy arrays" +msgstr "" + +#: of tensorcircuit.interfaces.tensortrans.numpy_args_to_backend:5 +msgid "str of str of the same pytree shape as args, defaults to None" +msgstr "" + +#: of tensorcircuit.interfaces.tensortrans.numpy_args_to_backend:7 +msgid "" +"str or backend object, defaults to None, indicating the current default " +"backend" +msgstr "" + +#: of tensorcircuit.interfaces.tensortrans.numpy_args_to_backend:10 +msgid "" +"the same format pytree with all numpy array replaced by the tensors in " +"the target backend" +msgstr "" + +#: of tensorcircuit.interfaces.tensortrans.which_backend:1 +msgid "Given a tensor ``a``, return the corresponding backend" +msgstr "" + +#: of tensorcircuit.interfaces.tensortrans.which_backend:5 +msgid "" +"if true, return backend object, if false, return backend str, defaults to" +" True" +msgstr "" + +#: of tensorcircuit.interfaces.tensortrans.which_backend:8 +msgid "the backend object or backend str" +msgstr "" + +#: ../../source/api/interfaces/torch.rst:2 +msgid "tensorcircuit.interfaces.torch" +msgstr "" + +#: of tensorcircuit.interfaces.torch:1 +msgid "Interface wraps quantum function as a torch function" +msgstr "" + +#: of tensorcircuit.interfaces.torch.torch_interface:1 +msgid "Wrap a quantum function on different ML backend with a pytorch interface." +msgstr "" + +#: of tensorcircuit.interfaces.torch.torch_interface_kws:1 +msgid "" +"similar to py:meth:`tensorcircuit.interfaces.torch.torch_interface`, but " +"now the interface support static arguments for function ``f``, which is " +"not a tensor and can be used with keyword arguments" +msgstr "" + +#: ../../source/api/keras.rst:2 +msgid "tensorcircuit.keras" +msgstr "" + +#: of tensorcircuit.keras:1 +msgid "Keras layer for tc quantum function" +msgstr "" + +#: of tensorcircuit.keras.HardwareLayer:1 +msgid "Bases: :py:class:`~tensorcircuit.keras.QuantumLayer`" +msgstr "" + +#: of tensorcircuit.keras.HardwareLayer:1 +msgid "" +"Keras Layer wrapping quantum function with cloud qpu access (using " +":py:mod:`tensorcircuit.cloud` module)" +msgstr "" + +#: of tensorcircuit.keras.QuantumLayer.__init__:1 +msgid "" +"`QuantumLayer` wraps the quantum function `f` as a `keras.Layer` so that " +"tensorcircuit is better integrated with tensorflow. Note that the input " +"of the layer can be tensors or even list/dict of tensors." +msgstr "" + +#: of tensorcircuit.keras.QuantumLayer.__init__:5 +msgid "Callabel function." +msgstr "" + +#: of tensorcircuit.keras.QuantumLayer.__init__:7 +msgid "The shape of the weights." +msgstr "" + +#: of tensorcircuit.keras.QuantumLayer.__init__:9 +msgid "The initializer of the weights, defaults to \"glorot_uniform\"" +msgstr "" + +#: of tensorcircuit.keras.QuantumLayer.__init__:13 +msgid "The regularizer of the weights, defaults to None" +msgstr "" + +#: of tensorcircuit.keras.load_func:1 +msgid "" +"Load function from the files in the ``tf.savedmodel`` format. We can load" +" several functions at the same time, as they can be the same function of " +"different input shapes." +msgstr "" + +#: of tensorcircuit.keras.load_func:24 +msgid "" +"The fallback function when all functions loaded are failed, defaults to " +"None" +msgstr "" + +#: of tensorcircuit.keras.load_func:26 +msgid "" +"When there is not legal loaded function of the input shape and no " +"fallback callable." +msgstr "" + +#: of tensorcircuit.keras.load_func:27 +msgid "" +"A function that tries all loaded function against the input until the " +"first success one." +msgstr "" + +#: of tensorcircuit.keras.output_asis_loss:1 +msgid "The keras loss function that directly taking the model output as the loss." +msgstr "" + +#: of tensorcircuit.keras.output_asis_loss:3 +msgid "Ignoring this parameter." +msgstr "" + +#: of tensorcircuit.keras.output_asis_loss:5 +msgid "Model output." +msgstr "" + +#: of tensorcircuit.keras.output_asis_loss:7 +msgid "Model output, which is y_pred." +msgstr "" + +#: of tensorcircuit.keras.save_func:1 +msgid "Save tf function in the file (``tf.savedmodel`` format)." +msgstr "" + +#: of tensorcircuit.keras.save_func:30 +msgid "``tf.function`` ed function with graph building" +msgstr "" + +#: of tensorcircuit.keras.save_func:32 +msgid "the dir path to save the function" +msgstr "" + +#: ../../source/api/mps_base.rst:2 +msgid "tensorcircuit.mps_base" +msgstr "" + +#: of tensorcircuit.mps_base:1 +msgid "FiniteMPS from tensornetwork with bug fixed" +msgstr "" + +#: of tensorcircuit.mps_base.FiniteMPS:1 +msgid "Bases: :py:class:`~tensornetwork.matrixproductstates.finite_mps.FiniteMPS`" +msgstr "" + +#: of tensornetwork.matrixproductstates.finite_mps.FiniteMPS.__init__:4 +msgid "Initialize a `FiniteMPS`. If `canonicalize` is `True` the state" +msgstr "" + +#: of tensornetwork.matrixproductstates.finite_mps.FiniteMPS.__init__:2 +msgid "" +"is brought into canonical form, with `BaseMPS.center_position` at " +"`center_position`. if `center_position` is `None` and `canonicalize = " +"True`, `BaseMPS.center_position` is set to 0." +msgstr "" + +#: of tensornetwork.matrixproductstates.finite_mps.FiniteMPS.__init__:6 +msgid "A list of `Tensor` objects." +msgstr "" + +#: of tensornetwork.matrixproductstates.finite_mps.FiniteMPS.__init__:7 +msgid "The initial position of the center site." +msgstr "" + +#: of tensornetwork.matrixproductstates.finite_mps.FiniteMPS.__init__:8 +msgid "If `True` the mps is canonicalized at initialization." +msgstr "" + +#: of tensornetwork.matrixproductstates.finite_mps.FiniteMPS.__init__:9 +msgid "" +"The name of the backend that should be used to perform contractions. " +"Available backends are currently 'numpy', 'tensorflow', 'pytorch', 'jax'" +msgstr "" + +#: of tensornetwork.matrixproductstates.base_mps.BaseMPS.apply_one_site_gate:1 +msgid "" +"Apply a one-site gate to an MPS. This routine will in general destroy any" +" canonical form of the state. If a canonical form is needed, the user can" +" restore it using `FiniteMPS.position` :param gate: a one-body gate " +":param site: the site where the gate should be applied" +msgstr "" + +#: of +#: tensornetwork.matrixproductstates.base_mps.BaseMPS.apply_transfer_operator:1 +msgid "Compute the action of the MPS transfer-operator at site `site`." +msgstr "" + +#: of +#: tensornetwork.matrixproductstates.base_mps.BaseMPS.apply_transfer_operator:3 +msgid "A site of the MPS" +msgstr "" + +#: of +#: tensornetwork.matrixproductstates.base_mps.BaseMPS.apply_transfer_operator:4 +msgid "" +"* if `1, 'l'` or `'left'`: compute the left-action of the MPS transfer-" +"operator at `site` on the input `matrix`. * if `-1, 'r'` or `'right'`: " +"compute the right-action of the MPS transfer-operator at `site` on the " +"input `matrix`" +msgstr "" + +#: of +#: tensornetwork.matrixproductstates.base_mps.BaseMPS.apply_transfer_operator:5 +msgid "" +"if `1, 'l'` or `'left'`: compute the left-action of the MPS transfer-" +"operator at `site` on the input `matrix`." +msgstr "" + +#: of +#: tensornetwork.matrixproductstates.base_mps.BaseMPS.apply_transfer_operator:7 +msgid "" +"if `-1, 'r'` or `'right'`: compute the right-action of the MPS transfer-" +"operator at `site` on the input `matrix`" +msgstr "" + +#: of +#: tensornetwork.matrixproductstates.base_mps.BaseMPS.apply_transfer_operator:9 +msgid "A rank-2 tensor or matrix." +msgstr "" + +#: of +#: tensornetwork.matrixproductstates.base_mps.BaseMPS.apply_transfer_operator:11 +msgid "The result of applying the MPS transfer-operator to `matrix`" +msgstr "" + +#: of tensorcircuit.mps_base.FiniteMPS.apply_two_site_gate:1 +msgid "" +"Apply a two-site gate to an MPS. This routine will in general destroy any" +" canonical form of the state. If a canonical form is needed, the user can" +" restore it using `FiniteMPS.position`." +msgstr "" + +#: of tensorcircuit.mps_base.FiniteMPS.apply_two_site_gate:5 +msgid "A two-body gate." +msgstr "" + +#: of tensorcircuit.mps_base.FiniteMPS.apply_two_site_gate:7 +msgid "The first site where the gate acts." +msgstr "" + +#: of tensorcircuit.mps_base.FiniteMPS.apply_two_site_gate:9 +msgid "The second site where the gate acts." +msgstr "" + +#: of tensorcircuit.mps_base.FiniteMPS.apply_two_site_gate:15 +msgid "" +"An optional value to choose the MPS tensor at `center_position` to be " +"isometric after the application of the gate. Defaults to `site1`. If the " +"MPS is canonical (i.e.`BaseMPS.center_position != None`), and if the " +"orthogonality center coincides with either `site1` or `site2`, the " +"orthogonality center will be shifted to `center_position` (`site1` by " +"default). If the orthogonality center does not coincide with `(site1, " +"site2)` then `MPS.center_position` is set to `None`." +msgstr "" + +#: of tensorcircuit.mps_base.FiniteMPS.apply_two_site_gate:26 +msgid "" +"\"rank of gate is {} but has to be 4\", \"site1 = {} is not between 0 <= " +"site < N - 1 = {}\", \"site2 = {} is not between 1 <= site < N = " +"{}\",\"Found site2 ={}, site1={}. Only nearest neighbor gates are " +"currently supported\", \"f center_position = {center_position} not f in " +"{(site1, site2)} \", or \"center_position = {}, but gate is applied at " +"sites {}, {}. Truncation should only be done if the gate is applied at " +"the center position of the MPS.\"" +msgstr "" + +#: of tensorcircuit.mps_base.FiniteMPS.apply_two_site_gate:32 +msgid "A scalar tensor containing the truncated weight of the truncation." +msgstr "" + +#: of tensornetwork.matrixproductstates.base_mps.BaseMPS.bond_dimension:1 +msgid "The bond dimension of `bond`" +msgstr "" + +#: of tensorcircuit.mps_base.FiniteMPS.bond_dimensions:1 +msgid "A list of bond dimensions of `BaseMPS`" +msgstr "" + +#: of tensornetwork.matrixproductstates.finite_mps.FiniteMPS.canonicalize:1 +msgid "" +"Bring the MPS into canonical form according to `center_position`. If " +"`center_position` is `None`, the MPS is canonicalized with " +"`center_position = 0`." +msgstr "" + +#: of tensornetwork.matrixproductstates.finite_mps.FiniteMPS.canonicalize:5 +msgid "If `True`, normalize matrices when shifting the orthogonality center." +msgstr "" + +#: of tensornetwork.matrixproductstates.finite_mps.FiniteMPS.canonicalize:8 +msgid "The norm of the MPS." +msgstr "" + +#: of tensornetwork.matrixproductstates.finite_mps.FiniteMPS.check_canonical:1 +msgid "Check whether the MPS is in the expected canonical form." +msgstr "" + +#: of tensornetwork.matrixproductstates.finite_mps.FiniteMPS.check_canonical:3 +msgid "The L2 norm of the vector of local deviations." +msgstr "" + +#: of tensornetwork.matrixproductstates.base_mps.BaseMPS.check_orthonormality:1 +msgid "Check orthonormality of tensor at site `site`." +msgstr "" + +#: of tensornetwork.matrixproductstates.base_mps.BaseMPS.check_orthonormality:3 +msgid "" +"* if `'l'` or `'left'`: check left orthogonality * if `'r`' or `'right'`:" +" check right orthogonality" +msgstr "" + +#: of tensornetwork.matrixproductstates.base_mps.BaseMPS.check_orthonormality:4 +msgid "if `'l'` or `'left'`: check left orthogonality" +msgstr "" + +#: of tensornetwork.matrixproductstates.base_mps.BaseMPS.check_orthonormality:5 +msgid "if `'r`' or `'right'`: check right orthogonality" +msgstr "" + +#: of tensornetwork.matrixproductstates.base_mps.BaseMPS.check_orthonormality:6 +msgid "The site of the tensor." +msgstr "" + +#: of tensornetwork.matrixproductstates.base_mps.BaseMPS.check_orthonormality:8 +msgid "The L2 norm of the deviation from identity." +msgstr "" + +#: of tensornetwork.matrixproductstates.base_mps.BaseMPS.check_orthonormality:9 +msgid "scalar `Tensor`" +msgstr "" + +#: of +#: tensornetwork.matrixproductstates.base_mps.BaseMPS.check_orthonormality:11 +msgid "If which is different from 'l','left', 'r' or 'right'." +msgstr "" + +#: of tensornetwork.matrixproductstates.base_mps.BaseMPS.get_tensor:1 +msgid "Returns the `Tensor` object at `site`." +msgstr "" + +#: of tensornetwork.matrixproductstates.base_mps.BaseMPS.get_tensor:3 +msgid "" +"If `site==len(self) - 1` `BaseMPS.connector_matrix` is absorbed fromt the" +" right-hand side into the returned `Tensor` object." +msgstr "" + +#: of tensornetwork.matrixproductstates.base_mps.BaseMPS.get_tensor:7 +msgid "The site for which to return the `Node`." +msgstr "" + +#: of tensornetwork.matrixproductstates.base_mps.BaseMPS.get_tensor:9 +msgid "The tensor at `site`." +msgstr "" + +#: of tensornetwork.matrixproductstates.finite_mps.FiniteMPS.left_envs:1 +msgid "" +"Compute left reduced density matrices for site `sites`. This returns a " +"dict `left_envs` mapping sites (int) to Tensors. `left_envs[site]` is the" +" left-reduced density matrix to the left of site `site`." +msgstr "" + +#: of tensornetwork.matrixproductstates.finite_mps.FiniteMPS.left_envs:5 +#: tensornetwork.matrixproductstates.finite_mps.FiniteMPS.right_envs:5 +msgid "A list of sites of the MPS." +msgstr "" + +#: of tensornetwork.matrixproductstates.finite_mps.FiniteMPS.left_envs:8 +msgid "The left-reduced density matrices at each site in `sites`." +msgstr "" + +#: of tensornetwork.matrixproductstates.finite_mps.FiniteMPS.left_envs:10 +msgid "The left-reduced density matrices" +msgstr "" + +#: of tensornetwork.matrixproductstates.finite_mps.FiniteMPS.left_envs:11 +#: tensornetwork.matrixproductstates.finite_mps.FiniteMPS.right_envs:11 +msgid "at each site in `sites`." +msgstr "" + +#: of tensornetwork.matrixproductstates.finite_mps.FiniteMPS.left_envs:12 +#: tensornetwork.matrixproductstates.finite_mps.FiniteMPS.right_envs:12 +msgid "`dict` mapping `int` to `Tensor`" +msgstr "" + +#: of tensorcircuit.mps_base.FiniteMPS.measure_local_operator:1 +msgid "Measure the expectation value of local operators `ops` site `sites`." +msgstr "" + +#: of tensorcircuit.mps_base.FiniteMPS.measure_local_operator:3 +msgid "A list Tensors of rank 2; the local operators to be measured." +msgstr "" + +#: of tensorcircuit.mps_base.FiniteMPS.measure_local_operator:5 +msgid "Sites where `ops` act." +msgstr "" + +#: of tensorcircuit.mps_base.FiniteMPS.measure_local_operator:7 +msgid "measurements :math:`\\langle` `ops[n]`:math:`\\rangle` for n in `sites`" +msgstr "" + +#: of tensorcircuit.mps_base.FiniteMPS.measure_two_body_correlator:1 +msgid "" +"Compute the correlator :math:`\\langle` `op1[site1], " +"op2[s]`:math:`\\rangle` between `site1` and all sites `s` in `sites2`. If" +" `s == site1`, `op2[s]` will be applied first." +msgstr "" + +#: of tensorcircuit.mps_base.FiniteMPS.measure_two_body_correlator:6 +msgid "Tensor of rank 2; the local operator at `site1`." +msgstr "" + +#: of tensorcircuit.mps_base.FiniteMPS.measure_two_body_correlator:8 +msgid "Tensor of rank 2; the local operator at `sites2`." +msgstr "" + +#: of tensorcircuit.mps_base.FiniteMPS.measure_two_body_correlator:10 +msgid "The site where `op1` acts" +msgstr "" + +#: of tensorcircuit.mps_base.FiniteMPS.measure_two_body_correlator:12 +msgid "Sites where operator `op2` acts." +msgstr "" + +#: of tensorcircuit.mps_base.FiniteMPS.measure_two_body_correlator:14 +msgid "" +"Correlator :math:`\\langle` `op1[site1], op2[s]`:math:`\\rangle` for `s` " +":math:`\\in` `sites2`." +msgstr "" + +#: of tensorcircuit.mps_base.FiniteMPS.physical_dimensions:1 +msgid "A list of physical Hilbert-space dimensions of `BaseMPS`" +msgstr "" + +#: of tensornetwork.matrixproductstates.base_mps.BaseMPS.position:1 +msgid "Shift `center_position` to `site`." +msgstr "" + +#: of tensornetwork.matrixproductstates.base_mps.BaseMPS.position:3 +msgid "The site to which FiniteMPS.center_position should be shifted" +msgstr "" + +#: of tensornetwork.matrixproductstates.base_mps.BaseMPS.position:4 +msgid "If `True`, normalize matrices when shifting." +msgstr "" + +#: of tensornetwork.matrixproductstates.base_mps.BaseMPS.position:5 +msgid "If not `None`, truncate the MPS bond dimensions to `D`." +msgstr "" + +#: of tensornetwork.matrixproductstates.base_mps.BaseMPS.position:6 +msgid "" +"if not `None`, truncate each bond dimension, but keeping the truncation " +"error below `max_truncation_err`." +msgstr "" + +#: of tensornetwork.matrixproductstates.base_mps.BaseMPS.position:9 +msgid "The norm of the tensor at `FiniteMPS.center_position`" +msgstr "" + +#: of tensornetwork.matrixproductstates.base_mps.BaseMPS.position:12 +msgid "If `center_position` is `None`." +msgstr "" + +#: of tensornetwork.matrixproductstates.finite_mps.FiniteMPS.random:1 +msgid "" +"Initialize a random `FiniteMPS`. The resulting state is normalized. Its " +"center-position is at 0." +msgstr "" + +#: of tensornetwork.matrixproductstates.finite_mps.FiniteMPS.random:4 +msgid "A list of physical dimensions." +msgstr "" + +#: of tensornetwork.matrixproductstates.finite_mps.FiniteMPS.random:5 +msgid "A list of bond dimensions." +msgstr "" + +#: of tensornetwork.matrixproductstates.finite_mps.FiniteMPS.random:6 +msgid "A numpy dtype." +msgstr "" + +#: of tensornetwork.matrixproductstates.finite_mps.FiniteMPS.random:7 +msgid "An optional backend." +msgstr "" + +#: of tensornetwork.matrixproductstates.finite_mps.FiniteMPS.random:9 +msgid "`FiniteMPS`" +msgstr "" + +#: of tensornetwork.matrixproductstates.finite_mps.FiniteMPS.right_envs:1 +msgid "" +"Compute right reduced density matrices for site `sites. This returns a " +"dict `right_envs` mapping sites (int) to Tensors. `right_envs[site]` is " +"the right-reduced density matrix to the right of site `site`." +msgstr "" + +#: of tensornetwork.matrixproductstates.finite_mps.FiniteMPS.right_envs:8 +msgid "The right-reduced density matrices at each site in `sites`." +msgstr "" + +#: of tensornetwork.matrixproductstates.finite_mps.FiniteMPS.right_envs:10 +msgid "The right-reduced density matrices" +msgstr "" + +#: ../../source/api/mpscircuit.rst:2 +msgid "tensorcircuit.mpscircuit" +msgstr "" + +#: of tensorcircuit.mpscircuit:1 +msgid "Quantum circuit: MPS state simulator" +msgstr "" + +#: of tensorcircuit.mpscircuit.MPSCircuit:1 +msgid "``MPSCircuit`` class. Simple usage demo below." +msgstr "" + +#: of tensorcircuit.mpscircuit.MPSCircuit.MPO_to_gate:1 +msgid "Convert MPO to gate" +msgstr "" + +#: of tensorcircuit.mpscircuit.MPSCircuit.__init__:1 +msgid "MPSCircuit object based on state simulator." +msgstr "" + +#: of tensorcircuit.mpscircuit.MPSCircuit.__init__:5 +msgid "The center position of MPS, default to 0" +msgstr "" + +#: of tensorcircuit.mpscircuit.MPSCircuit.__init__:7 +msgid "" +"If not None, the initial state of the circuit is taken as ``tensors`` " +"instead of :math:`\\vert 0\\rangle^n` qubits, defaults to None. When " +"``tensors`` are specified, if ``center_position`` is None, then the " +"tensors are canonicalized, otherwise it is assumed the tensors are " +"already canonicalized at the ``center_position``" +msgstr "" + +#: of tensorcircuit.mpscircuit.MPSCircuit.__init__:12 +msgid "" +"If not None, it is transformed to the MPS form according to the split " +"rules" +msgstr "" + +#: of tensorcircuit.mpscircuit.MPSCircuit.__init__:14 +msgid "Split rules" +msgstr "" + +#: of tensorcircuit.mpscircuit.MPSCircuit.apply_general_gate:1 +msgid "Apply a general qubit gate on MPS." +msgstr "" + +#: of tensorcircuit.mpscircuit.MPSCircuit.apply_adjacent_double_gate:3 +#: tensorcircuit.mpscircuit.MPSCircuit.apply_double_gate:3 +#: tensorcircuit.mpscircuit.MPSCircuit.apply_general_gate:3 +msgid "The Gate to be applied" +msgstr "" + +#: of tensorcircuit.mpscircuit.MPSCircuit.apply_general_gate:6 +msgid "Qubit indices of the gate" +msgstr "" + +#: of tensorcircuit.mpscircuit.MPSCircuit.apply_general_gate:5 +msgid "\"MPS does not support application of gate on > 2 qubits.\"" +msgstr "" + +#: of tensorcircuit.mpscircuit.MPSCircuit.apply_MPO:1 +msgid "Apply a MPO to the MPS" +msgstr "" + +#: of tensorcircuit.mpscircuit.MPSCircuit.apply_adjacent_double_gate:1 +msgid "" +"Apply a double qubit gate on adjacent qubits of Matrix Product States " +"(MPS)." +msgstr "" + +#: of tensorcircuit.mpscircuit.MPSCircuit.apply_adjacent_double_gate:5 +#: tensorcircuit.mpscircuit.MPSCircuit.apply_double_gate:5 +msgid "The first qubit index of the gate" +msgstr "" + +#: of tensorcircuit.mpscircuit.MPSCircuit.apply_adjacent_double_gate:7 +#: tensorcircuit.mpscircuit.MPSCircuit.apply_double_gate:7 msgid "The second qubit index of the gate" msgstr "" -#: of tensorcircuit.mpscircuit.MPSCircuit.apply_adjacent_double_gate:10 -msgid "Center position of MPS, default is None" +#: of tensorcircuit.mpscircuit.MPSCircuit.apply_adjacent_double_gate:9 +msgid "Center position of MPS, default is None" +msgstr "" + +#: of tensorcircuit.mpscircuit.MPSCircuit.apply_double_gate:1 +msgid "Apply a double qubit gate on MPS." +msgstr "" + +#: of tensorcircuit.mpscircuit.MPSCircuit.apply_nqubit_gate:1 +msgid "Apply a n-qubit gate by transforming the gate to MPO" +msgstr "" + +#: of tensorcircuit.mpscircuit.MPSCircuit.apply_single_gate:1 +msgid "Apply a single qubit gate on MPS; no truncation is needed." +msgstr "" + +#: of tensorcircuit.mpscircuit.MPSCircuit.apply_single_gate:3 +msgid "gate to be applied" +msgstr "" + +#: of tensorcircuit.mpscircuit.MPSCircuit.apply_single_gate:5 +msgid "Qubit index of the gate" +msgstr "" + +#: of tensorcircuit.mpscircuit.MPSCircuit.conj:1 +msgid "Compute the conjugate of the current MPS." +msgstr "" + +#: of tensorcircuit.mpscircuit.MPSCircuit.conj:3 +#: tensorcircuit.mpscircuit.MPSCircuit.copy:3 +#: tensorcircuit.mpscircuit.MPSCircuit.copy_without_tensor:3 +msgid "The constructed MPS" +msgstr "" + +#: of tensorcircuit.mpscircuit.MPSCircuit.copy:1 +msgid "Copy the current MPS." +msgstr "" + +#: of tensorcircuit.mpscircuit.MPSCircuit.copy_without_tensor:1 +msgid "Copy the current MPS without the tensors." +msgstr "" + +#: of tensorcircuit.mpscircuit.MPSCircuit.expectation:1 +msgid "Compute the expectation of corresponding operators in the form of tensor." +msgstr "" + +#: of tensorcircuit.mpscircuit.MPSCircuit.expectation:3 +msgid "" +"Operator and its position on the circuit, eg. ``(gates.Z(), [1]), " +"(gates.X(), [2])`` is for operator :math:`Z_1X_2`" +msgstr "" + +#: of tensorcircuit.mpscircuit.MPSCircuit.expectation:9 +msgid "If not None, will be used as bra" +msgstr "" + +#: of tensorcircuit.mpscircuit.MPSCircuit.expectation:11 +msgid "Whether to conjugate the bra state" +msgstr "" + +#: of tensorcircuit.mpscircuit.MPSCircuit.expectation:13 +msgid "Whether to normalize the MPS" +msgstr "" + +#: of tensorcircuit.mpscircuit.MPSCircuit.expectation:15 +#: tensorcircuit.mpscircuit.MPSCircuit.set_split_rules:5 +#: tensorcircuit.mpscircuit.MPSCircuit.wavefunction_to_tensors:5 +msgid "Truncation split" +msgstr "" + +#: of tensorcircuit.mpscircuit.MPSCircuit.expectation:17 +msgid "The expectation of corresponding operators" +msgstr "" + +#: of tensorcircuit.mpscircuit.MPSCircuit.gate_to_MPO:1 +msgid "Convert gate to MPO form with identities at empty sites" +msgstr "" + +#: of tensorcircuit.mpscircuit.MPSCircuit.get_bond_dimensions:1 +msgid "Get the MPS bond dimensions" +msgstr "" + +#: of tensorcircuit.mpscircuit.MPSCircuit.get_bond_dimensions:3 +#: tensorcircuit.mpscircuit.MPSCircuit.get_tensors:3 +msgid "MPS tensors" +msgstr "" + +#: of tensorcircuit.mpscircuit.MPSCircuit.get_center_position:1 +msgid "Get the center position of the MPS" +msgstr "" + +#: of tensorcircuit.mpscircuit.MPSCircuit.get_center_position:3 +msgid "center position" +msgstr "" + +#: of tensorcircuit.mpscircuit.MPSCircuit.get_norm:1 +msgid "Get the normalized Center Position." +msgstr "" + +#: of tensorcircuit.mpscircuit.MPSCircuit.get_norm:3 +msgid "Normalized Center Position." +msgstr "" + +#: of tensorcircuit.mpscircuit.MPSCircuit.get_quvector:2 +msgid "Get the representation of the output state in the form of ``QuVector``" +msgstr "" + +#: of tensorcircuit.mpscircuit.MPSCircuit.get_quvector:2 +msgid "has to be full contracted in MPS" +msgstr "" + +#: of tensorcircuit.mpscircuit.MPSCircuit.get_tensors:1 +msgid "Get the MPS tensors" +msgstr "" + +#: of tensorcircuit.mpscircuit.MPSCircuit.is_valid:1 +msgid "Check whether the circuit is legal." +msgstr "" + +#: of tensorcircuit.mpscircuit.MPSCircuit.is_valid:3 +msgid "Whether the circuit is legal." +msgstr "" + +#: of tensorcircuit.mpscircuit.MPSCircuit.measure:1 +msgid "Take measurement to the given quantum lines." +msgstr "" + +#: of tensorcircuit.mpscircuit.MPSCircuit.mid_measurement:1 +msgid "" +"Middle measurement in the z-basis on the circuit, note the wavefunction " +"output is not normalized with ``mid_measurement`` involved, one should " +"normalized the state manually if needed." +msgstr "" + +#: of tensorcircuit.mpscircuit.MPSCircuit.mid_measurement:4 +msgid "The index of qubit that the Z direction postselection applied on" +msgstr "" + +#: of tensorcircuit.mpscircuit.MPSCircuit.mid_measurement:6 +msgid "0 for spin up, 1 for spin down, defaults to 0" +msgstr "" + +#: of tensorcircuit.mpscircuit.MPSCircuit.normalize:1 +msgid "Normalize MPS Circuit according to the center position." +msgstr "" + +#: of tensorcircuit.mpscircuit.MPSCircuit.position:1 +msgid "Wrapper of tn.FiniteMPS.position. Set orthogonality center." +msgstr "" + +#: of tensorcircuit.mpscircuit.MPSCircuit.position:4 +msgid "The orthogonality center" +msgstr "" + +#: of tensorcircuit.mpscircuit.MPSCircuit.proj_with_mps:1 +msgid "Compute the projection between `other` as bra and `self` as ket." +msgstr "" + +#: of tensorcircuit.mpscircuit.MPSCircuit.proj_with_mps:3 +msgid "ket of the other MPS, which will be converted to bra automatically" +msgstr "" + +#: of tensorcircuit.mpscircuit.MPSCircuit.proj_with_mps:5 +msgid "The projection in form of tensor" +msgstr "" + +#: of tensorcircuit.mpscircuit.MPSCircuit.reduce_dimension:1 +msgid "Reduce the bond dimension between two adjacent sites by SVD" +msgstr "" + +#: of tensorcircuit.mpscircuit.MPSCircuit.reduce_tensor_dimension:1 +msgid "Reduce the bond dimension between two general tensors by SVD" +msgstr "" + +#: of tensorcircuit.mpscircuit.MPSCircuit.set_split_rules:1 +msgid "" +"Set truncation split when double qubit gates are applied. If nothing is " +"specified, no truncation will take place and the bond dimension will keep" +" growing. For more details, refer to `split_tensor`." +msgstr "" + +#: of tensorcircuit.mpscircuit.MPSCircuit.slice:1 +msgid "Get a slice of the MPS (only for internal use)" +msgstr "" + +#: of tensorcircuit.mpscircuit.MPSCircuit.wavefunction:3 +msgid "the str indicating the form of the output wavefunction" +msgstr "" + +#: of tensorcircuit.mpscircuit.MPSCircuit.wavefunction:5 +msgid "Tensor with shape [1, -1]" +msgstr "" + +#: of tensorcircuit.mpscircuit.MPSCircuit.wavefunction:9 +msgid "i--A--B--j -> i--XX--j" +msgstr "" + +#: of tensorcircuit.mpscircuit.MPSCircuit.wavefunction_to_tensors:1 +msgid "Construct the MPS tensors from a given wavefunction." +msgstr "" + +#: of tensorcircuit.mpscircuit.MPSCircuit.wavefunction_to_tensors:3 +msgid "The given wavefunction (any shape is OK)" +msgstr "" + +#: of tensorcircuit.mpscircuit.MPSCircuit.wavefunction_to_tensors:7 +msgid "Physical dimension, 2 for MPS and 4 for MPO" +msgstr "" + +#: of tensorcircuit.mpscircuit.MPSCircuit.wavefunction_to_tensors:9 +msgid "Whether to normalize the wavefunction" +msgstr "" + +#: of tensorcircuit.mpscircuit.MPSCircuit.wavefunction_to_tensors:11 +msgid "The tensors" +msgstr "" + +#: of tensorcircuit.mpscircuit.split_tensor:1 +msgid "Split the tensor by SVD or QR depends on whether a truncation is required." +msgstr "" + +#: of tensorcircuit.mpscircuit.split_tensor:3 +msgid "The input tensor to split." +msgstr "" + +#: of tensorcircuit.mpscircuit.split_tensor:5 +msgid "Determine the orthogonal center is on the left tensor or the right tensor." +msgstr "" + +#: of tensorcircuit.mpscircuit.split_tensor:7 +msgid "Two tensors after splitting" +msgstr "" + +#: ../../source/api/noisemodel.rst:2 +msgid "tensorcircuit.noisemodel" +msgstr "" + +#: of tensorcircuit.noisemodel:1 +msgid "General Noise Model Construction." +msgstr "" + +#: of tensorcircuit.noisemodel.NoiseConf:1 +msgid "``Noise Configuration`` class." +msgstr "" + +#: of tensorcircuit.noisemodel.NoiseConf.__init__:1 +msgid "Establish a noise configuration." +msgstr "" + +#: of tensorcircuit.noisemodel.NoiseConf.add_noise:1 +msgid "" +"Add noise channels on specific gates and specific qubits in form of Kraus" +" operators." +msgstr "" + +#: of tensorcircuit.noisemodel.NoiseConf.add_noise:3 +msgid "noisy gate" +msgstr "" + +#: of tensorcircuit.noisemodel.NoiseConf.add_noise:5 +msgid "noise channel" +msgstr "" + +#: of tensorcircuit.noisemodel.NoiseConf.add_noise:7 +msgid "" +"the list of noisy qubit, defaults to None, indicating applying the noise " +"channel on all qubits" +msgstr "" + +#: of tensorcircuit.noisemodel.NoiseConf.add_noise_by_condition:1 +msgid "Add noise based on specified condition" +msgstr "" + +#: of tensorcircuit.noisemodel.NoiseConf.add_noise_by_condition:3 +msgid "a function to decide if the noise should be added to the qir." +msgstr "" + +#: of tensorcircuit.noisemodel.NoiseConf.add_noise_by_condition:5 +msgid "the error channel" +msgstr "" + +#: of tensorcircuit.noisemodel.NoiseConf.add_noise_by_condition:7 +msgid "the name of the condition. A metadata that does not affect the numerics." +msgstr "" + +#: of tensorcircuit.noisemodel.NoiseConf.channel_count:1 +msgid "Count the total number of channels in a given circuit" +msgstr "" + +#: of tensorcircuit.noisemodel.NoiseConf.channel_count:3 +msgid "the circuit to be counted" +msgstr "" + +#: of tensorcircuit.noisemodel.NoiseConf.channel_count:5 +msgid "the count" +msgstr "" + +#: of tensorcircuit.noisemodel.apply_qir_with_noise:1 +msgid "A newly defined circuit" +msgstr "" + +#: of tensorcircuit.noisemodel.apply_qir_with_noise:3 +msgid "The qir of the clean circuit" +msgstr "" + +#: of tensorcircuit.noisemodel.apply_qir_with_noise:5 +#: tensorcircuit.noisemodel.circuit_with_noise:5 +msgid "Noise Configuration" +msgstr "" + +#: of tensorcircuit.noisemodel.apply_qir_with_noise:7 +#: tensorcircuit.noisemodel.circuit_with_noise:7 +msgid "The status for Monte Carlo sampling, defaults to None" +msgstr "" + +#: of tensorcircuit.noisemodel.apply_qir_with_noise:9 +#: tensorcircuit.noisemodel.circuit_with_noise:9 +msgid "A newly constructed circuit with noise" +msgstr "" + +#: of tensorcircuit.noisemodel.circuit_with_noise:1 +msgid "Noisify a clean circuit." +msgstr "" + +#: of tensorcircuit.noisemodel.circuit_with_noise:3 +msgid "A clean circuit" +msgstr "" + +#: of tensorcircuit.noisemodel.expectation_noisfy:1 +msgid "Calculate expectation value with noise configuration." +msgstr "" + +#: of tensorcircuit.noisemodel.expectation_noisfy:3 +#: tensorcircuit.noisemodel.sample_expectation_ps_noisfy:3 +msgid "The clean circuit" +msgstr "" + +#: of tensorcircuit.noisemodel.expectation_noisfy:12 +msgid "expectation value with noise" +msgstr "" + +#: of tensorcircuit.noisemodel.sample_expectation_ps_noisfy:1 +msgid "Calculate sample_expectation_ps with noise configuration." +msgstr "" + +#: of tensorcircuit.noisemodel.sample_expectation_ps_noisfy:13 +msgid "" +"repetition time for Monte Carlo sampling for noisfy calculation, " +"defaults to 1000" +msgstr "" + +#: of tensorcircuit.noisemodel.sample_expectation_ps_noisfy:20 +msgid "" +"external randomness given by tensor uniformly from [0, 1], defaults to " +"None, used for measurement sampling" +msgstr "" + +#: of tensorcircuit.noisemodel.sample_expectation_ps_noisfy:23 +msgid "sample expectation value with noise" +msgstr "" + +#: ../../source/api/quantum.rst:2 +msgid "tensorcircuit.quantum" +msgstr "" + +#: of tensorcircuit.quantum:1 +msgid "Quantum state and operator class backend by tensornetwork" +msgstr "" + +#: of tensorcircuit.quantum +msgid "IMPORT" +msgstr "" + +#: of tensorcircuit.quantum.PauliString2COO:1 +#: tensorcircuit.quantum.PauliStringSum2COO_tf:1 +msgid "Generate tensorflow sparse matrix from Pauli string sum" +msgstr "" + +#: of tensorcircuit.quantum.PauliString2COO:3 +msgid "" +"1D Tensor representing for a Pauli string, e.g. [1, 0, 0, 3, 2] is for " +":math:`X_0Z_3Y_4`" +msgstr "" + +#: of tensorcircuit.quantum.PauliString2COO:6 +msgid "" +"the weight for the Pauli string defaults to None (all Pauli strings " +"weight 1.0)" +msgstr "" + +#: of tensorcircuit.quantum.PauliString2COO:9 +msgid "the tensorflow sparse matrix" +msgstr "" + +#: of tensorcircuit.quantum.PauliStringSum2COO:1 +#: tensorcircuit.quantum.PauliStringSum2COO_numpy:1 +msgid "" +"Generate sparse tensor from Pauli string sum. Currently requires " +"tensorflow installed" +msgstr "" + +#: of tensorcircuit.quantum.PauliStringSum2COO:4 +#: tensorcircuit.quantum.PauliStringSum2COO_numpy:4 +#: tensorcircuit.quantum.PauliStringSum2COO_tf:3 +#: tensorcircuit.quantum.PauliStringSum2Dense:5 +msgid "" +"2D Tensor, each row is for a Pauli string, e.g. [1, 0, 0, 3, 2] is for " +":math:`X_0Z_3Y_4`" +msgstr "" + +#: of tensorcircuit.quantum.PauliStringSum2COO:7 +#: tensorcircuit.quantum.PauliStringSum2COO_numpy:7 +#: tensorcircuit.quantum.PauliStringSum2COO_tf:6 +#: tensorcircuit.quantum.PauliStringSum2Dense:8 +msgid "" +"1D Tensor, each element corresponds the weight for each Pauli string " +"defaults to None (all Pauli strings weight 1.0)" +msgstr "" + +#: of tensorcircuit.quantum.PauliStringSum2COO:10 +#: tensorcircuit.quantum.PauliStringSum2COO_numpy:10 +#: tensorcircuit.quantum.PauliStringSum2Dense:11 +msgid "" +"default False. If True, return numpy coo else return backend compatible " +"sparse tensor" +msgstr "" + +#: of tensorcircuit.quantum.PauliStringSum2COO:13 +#: tensorcircuit.quantum.PauliStringSum2COO_numpy:13 +msgid "the scipy coo sparse matrix" +msgstr "" + +#: of tensorcircuit.quantum.PauliStringSum2COO_tf:9 +msgid "the tensorflow coo sparse matrix" +msgstr "" + +#: of tensorcircuit.quantum.PauliStringSum2Dense:1 +msgid "" +"Generate dense matrix from Pauli string sum. Currently requires " +"tensorflow installed." +msgstr "" + +#: of tensorcircuit.quantum.PauliStringSum2Dense:14 +msgid "the tensorflow dense matrix" +msgstr "" + +#: of tensorcircuit.quantum.QuAdjointVector:1 tensorcircuit.quantum.QuScalar:1 +#: tensorcircuit.quantum.QuVector:1 +msgid "Bases: :py:class:`~tensorcircuit.quantum.QuOperator`" +msgstr "" + +#: of tensorcircuit.quantum.QuAdjointVector:1 +msgid "Represents an adjoint (row) vector via a tensor network." +msgstr "" + +#: of tensorcircuit.quantum.QuAdjointVector.__init__:1 +msgid "" +"Constructs a new `QuAdjointVector` from a tensor network. This " +"encapsulates an existing tensor network, interpreting it as an adjoint " +"vector (row vector)." +msgstr "" + +#: of tensorcircuit.quantum.QuAdjointVector.__init__:5 +#: tensorcircuit.quantum.QuOperator.__init__:9 +msgid "The edges of the network to be used as the input edges." +msgstr "" + +#: of tensorcircuit.quantum.QuAdjointVector.__init__:7 +#: tensorcircuit.quantum.QuOperator.__init__:11 +#: tensorcircuit.quantum.QuVector.__init__:6 +msgid "" +"Nodes used to refer to parts of the tensor network that are not connected" +" to any input or output edges (for example: a scalar factor)." +msgstr "" + +#: of tensorcircuit.quantum.QuAdjointVector.__init__:10 +#: tensorcircuit.quantum.QuScalar.__init__:7 +#: tensorcircuit.quantum.QuVector.__init__:9 +msgid "Optional collection of edges to ignore when performing consistency checks." +msgstr "" + +#: of tensorcircuit.quantum.QuOperator.adjoint:1 +msgid "" +"The adjoint of the operator. This creates a new `QuOperator` with " +"complex-conjugate copies of all tensors in the network and with the input" +" and output edges switched." +msgstr "" + +#: of tensorcircuit.quantum.QuOperator.adjoint:5 +msgid "The adjoint of the operator." +msgstr "" + +#: of tensorcircuit.quantum.QuOperator.check_network:1 +msgid "" +"Check that the network has the expected dimensionality. This checks that " +"all input and output edges are dangling and that there are no other " +"dangling edges (except any specified in `ignore_edges`). If not, an " +"exception is raised." +msgstr "" + +#: of tensorcircuit.quantum.QuOperator.contract:1 +msgid "" +"Contract the tensor network in place. This modifies the tensor network " +"representation of the operator (or vector, or scalar), reducing it to a " +"single tensor, without changing the value." +msgstr "" + +#: of tensorcircuit.quantum.QuOperator.contract:5 +msgid "Manually specify the axis ordering of the final tensor." +msgstr "" + +#: of tensorcircuit.quantum.QuOperator.contract:7 +msgid "The present object." +msgstr "" + +#: of tensorcircuit.quantum.QuOperator.copy:1 +msgid "The deep copy of the operator." +msgstr "" + +#: of tensorcircuit.quantum.QuOperator.copy:3 +msgid "The new copy of the operator." +msgstr "" + +#: of tensorcircuit.quantum.QuOperator.eval:1 +msgid "" +"Contracts the tensor network in place and returns the final tensor. Note " +"that this modifies the tensor network representing the operator. The " +"default ordering for the axes of the final tensor is: `*out_edges, " +"*in_edges`. If there are any \"ignored\" edges, their axes come first: " +"`*ignored_edges, *out_edges, *in_edges`." +msgstr "" + +#: of tensorcircuit.quantum.QuOperator.eval:8 +#: tensorcircuit.quantum.QuOperator.eval_matrix:6 +msgid "" +"Manually specify the axis ordering of the final tensor. The default " +"ordering is determined by `out_edges` and `in_edges` (see above)." +msgstr "" + +#: of tensorcircuit.quantum.QuOperator.eval:11 +#: tensorcircuit.quantum.QuOperator.eval_matrix:9 +msgid "Node count '{}' > 1 after contraction!" +msgstr "" + +#: of tensorcircuit.quantum.QuOperator.eval:12 +msgid "The final tensor representing the operator." +msgstr "" + +#: of tensorcircuit.quantum.QuOperator.eval_matrix:1 +msgid "" +"Contracts the tensor network in place and returns the final tensor in two" +" dimentional matrix. The default ordering for the axes of the final " +"tensor is: (:math:`\\prod` dimension of out_edges, :math:`\\prod` " +"dimension of in_edges)" +msgstr "" + +#: of tensorcircuit.quantum.QuOperator.eval_matrix:10 +msgid "The two-dimentional tensor representing the operator." +msgstr "" + +#: of tensorcircuit.quantum.QuAdjointVector.from_tensor:1 +msgid "" +"Construct a `QuAdjointVector` directly from a single tensor. This first " +"wraps the tensor in a `Node`, then constructs the `QuAdjointVector` from " +"that `Node`." +msgstr "" + +#: of tensorcircuit.quantum.QuAdjointVector.from_tensor:27 +msgid "The tensor for constructing an QuAdjointVector." +msgstr "" + +#: of tensorcircuit.quantum.QuAdjointVector.from_tensor:29 +msgid "" +"Sequence of integer indices specifying the order in which to interpret " +"the axes as subsystems (input edges). If not specified, the axes are " +"taken in ascending order." +msgstr "" + +#: of tensorcircuit.quantum.QuAdjointVector.from_tensor:33 +msgid "The new constructed QuAdjointVector give from the given tensor." +msgstr "" + +#: of tensorcircuit.quantum.QuOperator.is_adjoint_vector:1 +msgid "" +"Returns a bool indicating if QuOperator is an adjoint vector. Examples " +"can be found in the `QuOperator.from_tensor`." +msgstr "" + +#: of tensorcircuit.quantum.QuOperator.is_scalar:1 +msgid "" +"Returns a bool indicating if QuOperator is a scalar. Examples can be " +"found in the `QuOperator.from_tensor`." +msgstr "" + +#: of tensorcircuit.quantum.QuOperator.is_vector:1 +msgid "" +"Returns a bool indicating if QuOperator is a vector. Examples can be " +"found in the `QuOperator.from_tensor`." +msgstr "" + +#: of tensorcircuit.quantum.QuAdjointVector.nodes:1 +#: tensorcircuit.quantum.QuOperator.nodes:1 +#: tensorcircuit.quantum.QuScalar.nodes:1 +#: tensorcircuit.quantum.QuVector.nodes:1 +msgid "All tensor-network nodes involved in the operator." +msgstr "" + +#: of tensorcircuit.quantum.QuOperator.norm:1 +msgid "" +"The norm of the operator. This is the 2-norm (also known as the Frobenius" +" or Hilbert-Schmidt norm)." +msgstr "" + +#: of tensorcircuit.quantum.QuOperator.partial_trace:1 +msgid "" +"The partial trace of the operator. Subsystems to trace out are supplied " +"as indices, so that dangling edges are connected to each other as: " +"`out_edges[i] ^ in_edges[i] for i in subsystems_to_trace_out` This does " +"not modify the original network. The original ordering of the remaining " +"subsystems is maintained." +msgstr "" + +#: of tensorcircuit.quantum.QuAdjointVector.reduced_density:16 +#: tensorcircuit.quantum.QuOperator.partial_trace:8 +#: tensorcircuit.quantum.QuVector.reduced_density:16 +msgid "Indices of subsystems to trace out." +msgstr "" + +#: of tensorcircuit.quantum.QuOperator.partial_trace:10 +msgid "A new QuOperator or QuScalar representing the result." +msgstr "" + +#: of tensorcircuit.quantum.QuAdjointVector.projector:1 +#: tensorcircuit.quantum.QuVector.projector:1 +msgid "" +"The projector of the operator. The operator, as a linear operator, on the" +" adjoint of the operator." +msgstr "" + +#: of tensorcircuit.quantum.QuAdjointVector.projector:4 +msgid "" +"Set :math:`A` is the operator in matrix form, then the projector of " +"operator is defined as: :math:`A^\\dagger A`" +msgstr "" + +#: of tensorcircuit.quantum.QuAdjointVector.projector:6 +#: tensorcircuit.quantum.QuVector.projector:6 +msgid "The projector of the operator." +msgstr "" + +#: of tensorcircuit.quantum.QuAdjointVector.reduced_density:1 +#: tensorcircuit.quantum.QuVector.reduced_density:1 +msgid "The reduced density of the operator." +msgstr "" + +#: of tensorcircuit.quantum.QuAdjointVector.reduced_density:3 +#: tensorcircuit.quantum.QuVector.reduced_density:3 +msgid "" +"Set :math:`A` is the matrix of the operator, then the reduced density is " +"defined as:" +msgstr "" + +#: of tensorcircuit.quantum.QuAdjointVector.reduced_density:5 +msgid "\\mathrm{Tr}_{subsystems}(A^\\dagger A)" +msgstr "" + +#: of tensorcircuit.quantum.QuAdjointVector.reduced_density:9 +#: tensorcircuit.quantum.QuVector.reduced_density:9 +msgid "" +"Firstly, take the projector of the operator, then trace out the " +"subsystems to trace out are supplied as indices, so that dangling edges " +"are connected to each other as: `out_edges[i] ^ in_edges[i] for i in " +"subsystems_to_trace_out` This does not modify the original network. The " +"original ordering of the remaining subsystems is maintained." +msgstr "" + +#: of tensorcircuit.quantum.QuAdjointVector.reduced_density:18 +#: tensorcircuit.quantum.QuVector.reduced_density:18 +msgid "" +"The QuOperator of the reduced density of the operator with given " +"subsystems." +msgstr "" + +#: of tensorcircuit.quantum.QuOperator.tensor_product:1 +msgid "" +"Tensor product with another operator. Given two operators `A` and `B`, " +"produces a new operator `AB` representing :math:`A ⊗ B`. The `out_edges` " +"(`in_edges`) of `AB` is simply the concatenation of the `out_edges` " +"(`in_edges`) of `A.copy()` with that of `B.copy()`: `new_out_edges = " +"[*out_edges_A_copy, *out_edges_B_copy]` `new_in_edges = " +"[*in_edges_A_copy, *in_edges_B_copy]`" +msgstr "" + +#: of tensorcircuit.quantum.QuOperator.tensor_product:20 +msgid "The other operator (`B`)." +msgstr "" + +#: of tensorcircuit.quantum.QuOperator.tensor_product:22 +msgid "The result (`AB`)." +msgstr "" + +#: of tensorcircuit.quantum.QuOperator.trace:1 +msgid "The trace of the operator." +msgstr "" + +#: of tensorcircuit.quantum.QuOperator:1 +msgid "" +"Represents a linear operator via a tensor network. To interpret a tensor " +"network as a linear operator, some of the dangling edges must be " +"designated as `out_edges` (output edges) and the rest as `in_edges` " +"(input edges). Considered as a matrix, the `out_edges` represent the row " +"index and the `in_edges` represent the column index. The (right) action " +"of the operator on another then consists of connecting the `in_edges` of " +"the first operator to the `out_edges` of the second. Can be used to do " +"simple linear algebra with tensor networks." +msgstr "" + +#: of tensorcircuit.quantum.QuOperator.__init__:1 +msgid "" +"Creates a new `QuOperator` from a tensor network. This encapsulates an " +"existing tensor network, interpreting it as a linear operator. The " +"network is checked for consistency: All dangling edges must either be in " +"`out_edges`, `in_edges`, or `ignore_edges`." +msgstr "" + +#: of tensorcircuit.quantum.QuOperator.__init__:7 +#: tensorcircuit.quantum.QuVector.__init__:4 +msgid "The edges of the network to be used as the output edges." +msgstr "" + +#: of tensorcircuit.quantum.QuOperator.__init__:15 +msgid "" +"Optional collection of dangling edges to ignore when performing " +"consistency checks." +msgstr "" + +#: of tensorcircuit.quantum.QuOperator.__init__:18 +msgid "" +"At least one reference node is required to specify a scalar. None " +"provided!" +msgstr "" + +#: of tensorcircuit.quantum.QuOperator.from_tensor:1 +msgid "" +"Construct a `QuOperator` directly from a single tensor. This first wraps " +"the tensor in a `Node`, then constructs the `QuOperator` from that " +"`Node`." +msgstr "" + +#: of tensorcircuit.quantum.QuOperator.from_tensor:28 +msgid "The tensor." +msgstr "" + +#: of tensorcircuit.quantum.QuOperator.from_tensor:30 +msgid "The axis indices of `tensor` to use as `out_edges`." +msgstr "" + +#: of tensorcircuit.quantum.QuOperator.from_tensor:32 +msgid "The axis indices of `tensor` to use as `in_edges`." +msgstr "" + +#: of tensorcircuit.quantum.QuOperator.from_tensor:34 +msgid "The new operator." +msgstr "" + +#: of tensorcircuit.quantum.QuScalar:1 +msgid "Represents a scalar via a tensor network." +msgstr "" + +#: of tensorcircuit.quantum.QuScalar.__init__:1 +msgid "" +"Constructs a new `QuScalar` from a tensor network. This encapsulates an " +"existing tensor network, interpreting it as a scalar." +msgstr "" + +#: of tensorcircuit.quantum.QuScalar.__init__:4 +msgid "" +"Nodes used to refer to the tensor network (need not be exhaustive - one " +"node from each disconnected subnetwork is sufficient)." +msgstr "" + +#: of tensorcircuit.quantum.QuScalar.from_tensor:1 +msgid "" +"Construct a `QuScalar` directly from a single tensor. This first wraps " +"the tensor in a `Node`, then constructs the `QuScalar` from that `Node`." +msgstr "" + +#: of tensorcircuit.quantum.QuScalar.from_tensor:22 +msgid "The tensor for constructing a new QuScalar." +msgstr "" + +#: of tensorcircuit.quantum.QuScalar.from_tensor:24 +msgid "The new constructed QuScalar from the given tensor." +msgstr "" + +#: of tensorcircuit.quantum.QuVector:1 +msgid "Represents a (column) vector via a tensor network." +msgstr "" + +#: of tensorcircuit.quantum.QuVector.__init__:1 +msgid "" +"Constructs a new `QuVector` from a tensor network. This encapsulates an " +"existing tensor network, interpreting it as a (column) vector." +msgstr "" + +#: of tensorcircuit.quantum.QuVector.from_tensor:1 +msgid "" +"Construct a `QuVector` directly from a single tensor. This first wraps " +"the tensor in a `Node`, then constructs the `QuVector` from that `Node`." +msgstr "" + +#: of tensorcircuit.quantum.QuVector.from_tensor:28 +msgid "The tensor for constructing a \"QuVector\"." +msgstr "" + +#: of tensorcircuit.quantum.QuVector.from_tensor:30 +msgid "" +"Sequence of integer indices specifying the order in which to interpret " +"the axes as subsystems (output edges). If not specified, the axes are " +"taken in ascending order." +msgstr "" + +#: of tensorcircuit.quantum.QuVector.from_tensor:34 +msgid "The new constructed QuVector from the given tensor." +msgstr "" + +#: of tensorcircuit.quantum.QuVector.projector:4 +msgid "" +"Set :math:`A` is the operator in matrix form, then the projector of " +"operator is defined as: :math:`A A^\\dagger`" +msgstr "" + +#: of tensorcircuit.quantum.QuVector.reduced_density:5 +msgid "\\mathrm{Tr}_{subsystems}(A A^\\dagger)" +msgstr "" + +#: of tensorcircuit.quantum.check_spaces:1 +msgid "" +"Check the vector spaces represented by two lists of edges are compatible." +" The number of edges must be the same and the dimensions of each pair of " +"edges must match. Otherwise, an exception is raised." +msgstr "" + +#: of tensorcircuit.quantum.check_spaces:5 tensorcircuit.quantum.check_spaces:7 +msgid "List of edges representing a many-body Hilbert space." +msgstr "" + +#: of tensorcircuit.quantum.check_spaces:10 +msgid "" +"Hilbert-space mismatch: \"Cannot connect {} subsystems with {} " +"subsystems\", or \"Input dimension {} != output dimension {}.\"" +msgstr "" + +#: of tensorcircuit.quantum.correlation_from_counts:1 +msgid "" +"Compute :math:`\\prod_{i\\in \\\\text{index}} s_i`, where the probability" +" for each bitstring is given as a vector ``results``. Results is in the " +"format of \"count_vector\"" +msgstr "" + +#: of tensorcircuit.quantum.correlation_from_counts:13 +#: tensorcircuit.quantum.correlation_from_samples:4 +msgid "list of int, indicating the position in the bitstring" +msgstr "" + +#: of tensorcircuit.quantum.correlation_from_counts:15 +msgid "probability vector of shape 2^n" +msgstr "" + +#: of tensorcircuit.quantum.correlation_from_counts:17 +msgid "Correlation expectation from measurement shots." +msgstr "" + +#: of tensorcircuit.quantum.correlation_from_samples:1 +msgid "" +"Compute :math:`\\prod_{i\\in \\\\text{index}} s_i (s=\\pm 1)`, Results is" +" in the format of \"sample_int\" or \"sample_bin\"" +msgstr "" + +#: of tensorcircuit.quantum.correlation_from_samples:6 +msgid "sample tensor" +msgstr "" + +#: of tensorcircuit.quantum.correlation_from_samples:10 +msgid "Correlation expectation from measurement shots" +msgstr "" + +#: of tensorcircuit.quantum.count_d2s:1 +msgid "" +"measurement shots results, dense representation to sparse tuple " +"representation non-jittable due to the non fixed return shape count_tuple" +" to count_vector" +msgstr "" + +#: of tensorcircuit.quantum.count_d2s:12 +msgid "cutoff to determine nonzero elements, defaults to 1e-7" +msgstr "" + +#: of tensorcircuit.quantum.count_s2d:1 +msgid "" +"measurement shots results, sparse tuple representation to dense " +"representation count_vector to count_tuple" +msgstr "" + +#: of tensorcircuit.quantum.count_tuple2dict:1 +msgid "count_tuple to count_dict_bin or count_dict_int" +msgstr "" + +#: of tensorcircuit.quantum.count_tuple2dict:3 +msgid "count_tuple format" +msgstr "" + +#: of tensorcircuit.quantum.count_tuple2dict:7 +#: tensorcircuit.quantum.count_vector2dict:7 +msgid "can be \"int\" or \"bin\", defaults to \"bin\"" +msgstr "" + +#: of tensorcircuit.quantum.count_tuple2dict:9 +msgid "count_dict" +msgstr "" + +#: of tensorcircuit.quantum.count_vector2dict:1 +msgid "convert_vector to count_dict_bin or count_dict_int" +msgstr "" + +#: of tensorcircuit.quantum.count_vector2dict:3 +msgid "tensor in shape [2**n]" +msgstr "" + +#: of tensorcircuit.quantum.double_state:1 +msgid "Compute the double state of the given Hamiltonian operator ``h``." +msgstr "" + +#: of tensorcircuit.quantum.double_state:3 tensorcircuit.quantum.gibbs_state:3 +#: tensorcircuit.quantum.truncated_free_energy:5 +msgid "Hamiltonian operator in form of Tensor." +msgstr "" + +#: of tensorcircuit.quantum.double_state:5 tensorcircuit.quantum.free_energy:17 +#: tensorcircuit.quantum.gibbs_state:5 +#: tensorcircuit.quantum.renyi_free_energy:16 +#: tensorcircuit.quantum.truncated_free_energy:7 +msgid "Constant for the optimization, default is 1." +msgstr "" + +#: of tensorcircuit.quantum.double_state:7 +msgid "The double state of ``h`` with the given ``beta``." +msgstr "" + +#: of tensorcircuit.quantum.eliminate_identities:1 +msgid "" +"Eliminates any connected CopyNodes that are identity matrices. This will " +"modify the network represented by `nodes`. Only identities that are " +"connected to other nodes are eliminated." +msgstr "" + +#: of tensorcircuit.quantum.eliminate_identities:5 +msgid "Collection of nodes to search." +msgstr "" + +#: of tensorcircuit.quantum.eliminate_identities:7 +msgid "" +"The Dictionary mapping remaining Nodes to any replacements, Dictionary " +"specifying all dangling-edge replacements." +msgstr "" + +#: of tensorcircuit.quantum.entropy:1 +msgid "Compute the entropy from the given density matrix ``rho``." +msgstr "" + +#: of tensorcircuit.quantum.entropy:30 tensorcircuit.quantum.free_energy:13 +#: tensorcircuit.quantum.renyi_entropy:3 +#: tensorcircuit.quantum.renyi_free_energy:12 +msgid "The density matrix in form of Tensor or QuOperator." +msgstr "" + +#: of tensorcircuit.quantum.entropy:32 tensorcircuit.quantum.free_energy:19 +msgid "Epsilon, default is 1e-12." +msgstr "" + +#: of tensorcircuit.quantum.entropy:34 +msgid "Entropy on the given density matrix." +msgstr "" + +#: of tensorcircuit.quantum.fidelity:1 +msgid "Return fidelity scalar between two states rho and rho0." +msgstr "" + +#: of tensorcircuit.quantum.fidelity:3 +msgid "\\operatorname{Tr}(\\sqrt{\\sqrt{rho} rho_0 \\sqrt{rho}})" +msgstr "" + +#: of tensorcircuit.quantum.fidelity:7 tensorcircuit.quantum.fidelity:9 +#: tensorcircuit.quantum.mutual_information:3 tensorcircuit.quantum.taylorlnm:3 +#: tensorcircuit.quantum.trace_distance:3 +#: tensorcircuit.quantum.trace_distance:5 +#: tensorcircuit.quantum.truncated_free_energy:3 +msgid "The density matrix in form of Tensor." +msgstr "" + +#: of tensorcircuit.quantum.fidelity:11 +msgid "The sqrtm of a Hermitian matrix ``a``." +msgstr "" + +#: of tensorcircuit.quantum.free_energy:1 +msgid "Compute the free energy of the given density matrix." +msgstr "" + +#: of tensorcircuit.quantum.free_energy:15 +#: tensorcircuit.quantum.renyi_free_energy:14 +msgid "Hamiltonian operator in form of Tensor or QuOperator." +msgstr "" + +#: of tensorcircuit.quantum.free_energy:22 +msgid "The free energy of the given density matrix with the Hamiltonian operator." +msgstr "" + +#: of tensorcircuit.quantum.generate_local_hamiltonian:1 +msgid "" +"Generate a local Hamiltonian operator based on the given sequence of " +"Tensor. Note: further jit is recommended. For large Hilbert space, sparse" +" Hamiltonian is recommended" +msgstr "" + +#: of tensorcircuit.quantum.generate_local_hamiltonian:5 +msgid "A sequence of Tensor." +msgstr "" + +#: of tensorcircuit.quantum.generate_local_hamiltonian:7 +msgid "Return Hamiltonian operator in form of matrix, defaults to True." +msgstr "" + +#: of tensorcircuit.quantum.generate_local_hamiltonian:9 +msgid "The Hamiltonian operator in form of QuOperator or matrix." +msgstr "" + +#: of tensorcircuit.quantum.gibbs_state:1 +msgid "Compute the Gibbs state of the given Hamiltonian operator ``h``." +msgstr "" + +#: of tensorcircuit.quantum.gibbs_state:7 +msgid "The Gibbs state of ``h`` with the given ``beta``." +msgstr "" + +#: of tensorcircuit.quantum.heisenberg_hamiltonian:1 +msgid "" +"Generate Heisenberg Hamiltonian with possible external fields. Currently " +"requires tensorflow installed" +msgstr "" + +#: of tensorcircuit.quantum.heisenberg_hamiltonian:13 +msgid "input circuit graph" +msgstr "" + +#: of tensorcircuit.quantum.heisenberg_hamiltonian:15 +msgid "zz coupling, default is 1.0" +msgstr "" + +#: of tensorcircuit.quantum.heisenberg_hamiltonian:17 +msgid "xx coupling, default is 1.0" +msgstr "" + +#: of tensorcircuit.quantum.heisenberg_hamiltonian:19 +msgid "yy coupling, default is 1.0" +msgstr "" + +#: of tensorcircuit.quantum.heisenberg_hamiltonian:21 +msgid "External field on z direction, default is 0.0" +msgstr "" + +#: of tensorcircuit.quantum.heisenberg_hamiltonian:23 +msgid "External field on y direction, default is 0.0" +msgstr "" + +#: of tensorcircuit.quantum.heisenberg_hamiltonian:25 +msgid "External field on x direction, default is 0.0" +msgstr "" + +#: of tensorcircuit.quantum.heisenberg_hamiltonian:27 +msgid "Whether to return sparse Hamiltonian operator, default is True." +msgstr "" + +#: of tensorcircuit.quantum.heisenberg_hamiltonian:29 +msgid "whether return the matrix in numpy or tensorflow form" +msgstr "" + +#: of tensorcircuit.quantum.heisenberg_hamiltonian:32 +msgid "Hamiltonian measurements" +msgstr "" + +#: of tensorcircuit.quantum.identity:1 +msgid "" +"Construct a 'QuOperator' representing the identity on a given space. " +"Internally, this is done by constructing 'CopyNode's for each edge, with " +"dimension according to 'space'." +msgstr "" + +#: of tensorcircuit.quantum.identity:26 +msgid "" +"A sequence of integers for the dimensions of the tensor product factors " +"of the space (the edges in the tensor network)." +msgstr "" + +#: of tensorcircuit.quantum.identity:29 +msgid "" +"The data type by np.* (for conversion to dense). defaults None to tc " +"dtype." +msgstr "" + +#: of tensorcircuit.quantum.identity:31 +msgid "The desired identity operator." +msgstr "" + +#: of tensorcircuit.quantum.measurement_counts:1 +msgid "" +"Simulate the measuring of each qubit of ``p`` in the computational basis," +" thus producing output like that of ``qiskit``." +msgstr "" + +#: of tensorcircuit.quantum.measurement_counts:4 +msgid "Six formats of measurement counts results:" +msgstr "" + +#: of tensorcircuit.quantum.measurement_counts:6 +msgid "\"sample_int\": # np.array([0, 0])" +msgstr "" + +#: of tensorcircuit.quantum.measurement_counts:8 +msgid "\"sample_bin\": # [np.array([1, 0]), np.array([1, 0])]" +msgstr "" + +#: of tensorcircuit.quantum.measurement_counts:10 +msgid "\"count_vector\": # np.array([2, 0, 0, 0])" +msgstr "" + +#: of tensorcircuit.quantum.measurement_counts:12 +msgid "\"count_tuple\": # (np.array([0]), np.array([2]))" +msgstr "" + +#: of tensorcircuit.quantum.measurement_counts:14 +msgid "\"count_dict_bin\": # {\"00\": 2, \"01\": 0, \"10\": 0, \"11\": 0}" +msgstr "" + +#: of tensorcircuit.quantum.measurement_counts:16 +msgid "\"count_dict_int\": # {0: 2, 1: 0, 2: 0, 3: 0}" +msgstr "" + +#: of tensorcircuit.quantum.measurement_counts:37 +msgid "" +"The quantum state, assumed to be normalized, as either a ket or density " +"operator." +msgstr "" + +#: of tensorcircuit.quantum.measurement_counts:39 +msgid "The number of counts to perform." +msgstr "" + +#: of tensorcircuit.quantum.measurement_counts:41 +msgid "alias for the argument ``counts``" +msgstr "" + +#: of tensorcircuit.quantum.measurement_counts:43 +msgid "defaults to be \"direct\", see supported format above" +msgstr "" + +#: of tensorcircuit.quantum.measurement_counts:47 +msgid "" +"if True, the `state` is directly regarded as a probability list, defaults" +" to be False" +msgstr "" + +#: of tensorcircuit.quantum.measurement_counts:55 +msgid "if True, jax backend try using a jittable count, defaults to False" +msgstr "" + +#: of tensorcircuit.quantum.measurement_counts:57 +msgid "The counts for each bit string measured." +msgstr "" + +#: of tensorcircuit.quantum.mutual_information:1 +msgid "Mutual information between AB subsystem described by ``cut``." +msgstr "" + +#: of tensorcircuit.quantum.mutual_information:5 +msgid "The AB subsystem." +msgstr "" + +#: of tensorcircuit.quantum.mutual_information:7 +msgid "The mutual information between AB subsystem described by ``cut``." +msgstr "" + +#: of tensorcircuit.quantum.ps2xyz:1 +msgid "pauli string list to xyz dict" +msgstr "" + +#: of tensorcircuit.quantum.ps2xyz:3 +msgid "# ps2xyz([1, 2, 2, 0]) = {\"x\": [0], \"y\": [1, 2], \"z\": []}" +msgstr "" + +#: of tensorcircuit.quantum.quantum_constructor:1 +msgid "" +"Constructs an appropriately specialized QuOperator. If there are no " +"edges, creates a QuScalar. If the are only output (input) edges, creates " +"a QuVector (QuAdjointVector). Otherwise creates a QuOperator." +msgstr "" + +#: of tensorcircuit.quantum.quantum_constructor:48 +msgid "A list of output edges." +msgstr "" + +#: of tensorcircuit.quantum.quantum_constructor:50 +msgid "A list of input edges." +msgstr "" + +#: of tensorcircuit.quantum.quantum_constructor:52 +msgid "" +"Reference nodes for the tensor network (needed if there is a. scalar " +"component)." +msgstr "" + +#: of tensorcircuit.quantum.quantum_constructor:55 +msgid "Edges to ignore when checking the dimensionality of the tensor network." +msgstr "" + +#: of tensorcircuit.quantum.quantum_constructor:58 +msgid "The new created QuOperator object." +msgstr "" + +#: of tensorcircuit.quantum.quimb2qop:1 +msgid "Convert MPO in Quimb package to QuOperator." +msgstr "" + +#: of tensorcircuit.quantum.quimb2qop:3 +msgid "MPO in the form of Quimb package" +msgstr "" + +#: of tensorcircuit.quantum.quimb2qop:5 tensorcircuit.quantum.tn2qop:5 +msgid "MPO in the form of QuOperator" +msgstr "" + +#: of tensorcircuit.quantum.reduced_density_matrix:1 +msgid "Compute the reduced density matrix from the quantum state ``state``." +msgstr "" + +#: of tensorcircuit.quantum.reduced_density_matrix:3 +msgid "The quantum state in form of Tensor or QuOperator." +msgstr "" + +#: of tensorcircuit.quantum.reduced_density_matrix:5 +msgid "" +"the index list that is traced out, if cut is a int, it indicates [0, cut]" +" as the traced out region" +msgstr "" + +#: of tensorcircuit.quantum.reduced_density_matrix:8 +msgid "probability decoration, default is None." +msgstr "" + +#: of tensorcircuit.quantum.reduced_density_matrix:10 +msgid "The reduced density matrix." +msgstr "" + +#: of tensorcircuit.quantum.renyi_entropy:1 +msgid "Compute the Rényi entropy of order :math:`k` by given density matrix." +msgstr "" + +#: of tensorcircuit.quantum.renyi_entropy:5 +#: tensorcircuit.quantum.renyi_free_energy:18 +msgid "The order of Rényi entropy, default is 2." +msgstr "" + +#: of tensorcircuit.quantum.renyi_entropy:7 +#: tensorcircuit.quantum.renyi_free_energy:20 +msgid "The :math:`k` th order of Rényi entropy." +msgstr "" + +#: of tensorcircuit.quantum.renyi_free_energy:1 +msgid "" +"Compute the Rényi free energy of the corresponding density matrix and " +"Hamiltonian." +msgstr "" + +#: of tensorcircuit.quantum.sample2all:1 +msgid "" +"transform ``sample_int`` or ``sample_bin`` form results to other forms " +"specified by ``format``" +msgstr "" + +#: of tensorcircuit.quantum.sample2all:3 +msgid "measurement shots results in ``sample_int`` or ``sample_bin`` format" +msgstr "" + +#: of tensorcircuit.quantum.sample2all:7 +msgid "" +"see the doc in the doc in " +":py:meth:`tensorcircuit.quantum.measurement_results`, defaults to " +"\"count_vector\"" +msgstr "" + +#: of tensorcircuit.quantum.sample2all:12 +msgid "only applicable to count transformation in jax backend, defaults to False" +msgstr "" + +#: of tensorcircuit.quantum.sample2all:14 +msgid "measurement results specified as ``format``" +msgstr "" + +#: of tensorcircuit.quantum.sample2count:1 +msgid "sample_int to count_tuple" +msgstr "" + +#: of tensorcircuit.quantum.sample_bin2int:1 +msgid "bin sample to int sample" +msgstr "" + +#: of tensorcircuit.quantum.sample_bin2int:3 +msgid "in shape [trials, n] of elements (0, 1)" +msgstr "" + +#: of tensorcircuit.quantum.sample_bin2int:7 +msgid "in shape [trials]" +msgstr "" + +#: of tensorcircuit.quantum.sample_int2bin:1 +msgid "int sample to bin sample" +msgstr "" + +#: of tensorcircuit.quantum.sample_int2bin:3 +msgid "in shape [trials] of int elements in the range [0, 2**n)" +msgstr "" + +#: of tensorcircuit.quantum.sample_int2bin:7 +msgid "in shape [trials, n] of element (0, 1)" +msgstr "" + +#: of tensorcircuit.quantum.spin_by_basis:1 +msgid "" +"Generate all n-bitstrings as an array, each row is a bitstring basis. " +"Return m-th col." +msgstr "" + +#: of tensorcircuit.quantum.spin_by_basis:9 +msgid "length of a bitstring" +msgstr "" + +#: of tensorcircuit.quantum.spin_by_basis:11 +msgid "m is transformed as " +"(01>-|10>)" +msgstr "" + +#: of tensorcircuit.templates.blocks.Bell_pair_block:3 +#: tensorcircuit.templates.blocks.qft:3 +msgid "Circuit in" +msgstr "" + +#: of tensorcircuit.templates.blocks.Bell_pair_block:5 +msgid "" +"pairs indices for Bell pairs, defaults to None, corresponds to neighbor " +"links" +msgstr "" + +#: of tensorcircuit.templates.blocks.Bell_pair_block:7 +msgid "Circuit out" +msgstr "" + +#: of tensorcircuit.templates.blocks.example_block:1 +msgid "" +"The circuit ansatz is firstly one layer of Hadamard gates and then we " +"have ``nlayers`` blocks of :math:`e^{i\\theta Z_iZ_{i+1}}` two-qubit gate" +" in ladder layout, following rx gate." +msgstr "" + +#: of tensorcircuit.templates.blocks.example_block:5 +msgid "The circuit" +msgstr "" + +#: of tensorcircuit.templates.blocks.example_block:7 +msgid "paramter tensor with 2*nlayer*n elements" +msgstr "" + +#: of tensorcircuit.templates.blocks.example_block:9 +msgid "number of ZZ+RX blocks, defaults to 2" +msgstr "" + +#: of tensorcircuit.templates.blocks.example_block:11 +msgid "whether use SVD split to reduce ZZ gate bond dimension, defaults to False" +msgstr "" + +#: of tensorcircuit.templates.blocks.example_block:14 +msgid "The circuit with example ansatz attached" +msgstr "" + +#: of tensorcircuit.templates.blocks.qft:1 +msgid "" +"This function applies quantum fourier transformation (QFT) to the " +"selected circuit lines" +msgstr "" + +#: of tensorcircuit.templates.blocks.qft:5 +msgid "the indices of the circuit lines to apply QFT" +msgstr "" + +#: of tensorcircuit.templates.blocks.qft:7 +msgid "Whether to include the final swaps in the QFT" +msgstr "" + +#: of tensorcircuit.templates.blocks.qft:9 +msgid "If True, the inverse Fourier transform is constructed" +msgstr "" + +#: of tensorcircuit.templates.blocks.qft:11 +msgid "If True, barriers are inserted as visualization improvement" +msgstr "" + +#: of tensorcircuit.templates.blocks.qft:13 +msgid "Circuit c" +msgstr "" + +#: of tensorcircuit.templates.blocks.state_centric:1 +msgid "" +"Function decorator wraps the function with the first input and output in " +"the format of circuit, the wrapped function has the first input and the " +"output as the state tensor." +msgstr "" + +#: of tensorcircuit.templates.blocks.state_centric:4 +msgid "Function with the fist input and the output as ``Circuit`` object." +msgstr "" + +#: of tensorcircuit.templates.blocks.state_centric:6 +msgid "" +"Wrapped function with the first input and the output as the state tensor " +"correspondingly." +msgstr "" + +#: ../../source/api/templates/chems.rst:2 +msgid "tensorcircuit.templates.chems" +msgstr "" + +#: of tensorcircuit.templates.chems:1 +msgid "Useful utilities for quantum chemistry related task" +msgstr "" + +#: ../../source/api/templates/dataset.rst:2 +msgid "tensorcircuit.templates.dataset" +msgstr "" + +#: of tensorcircuit.templates.dataset:1 +msgid "Quantum machine learning related data preprocessing and embedding" +msgstr "" + +#: ../../source/api/templates/ensemble.rst:2 +msgid "tensorcircuit.templates.ensemble" +msgstr "" + +#: of tensorcircuit.templates.ensemble:1 +msgid "Useful utilities for ensemble" +msgstr "" + +#: of tensorcircuit.templates.ensemble.bagging.append:1 +msgid "Add model to the voting method" +msgstr "" + +#: of tensorcircuit.templates.ensemble.bagging.eval:1 +msgid "" +"Expect input data to be a 2D array which a 1D array of yTrue followed by " +"a 1D array of yPred is expected to be the components of the 2D array" +msgstr "" + +#: of tensorcircuit.templates.ensemble.bagging.predict:1 +msgid "" +"Input data is expected to be a 2D array that the first layer is different" +" input data (into the trained models)" +msgstr "" + +#: of tensorcircuit.templates.ensemble.bagging.train:1 +msgid "" +"Train all models in the class, **kwargs expect to receive the argus that " +"can be directly sent to tf.fit Expected to be run after finishing compile" +msgstr "" + +#: ../../source/api/templates/graphs.rst:2 +msgid "tensorcircuit.templates.graphs" +msgstr "" + +#: of tensorcircuit.templates.graphs:1 +msgid "Some common graphs and lattices" +msgstr "" + +#: of tensorcircuit.templates.graphs.Grid2DCoord:1 +msgid "Two-dimensional grid lattice" +msgstr "" + +#: of tensorcircuit.templates.graphs.Grid2DCoord.__init__:1 +msgid "number of rows" +msgstr "" + +#: of tensorcircuit.templates.graphs.Grid2DCoord.__init__:3 +msgid "number of cols" +msgstr "" + +#: of tensorcircuit.templates.graphs.Grid2DCoord.all_cols:1 +msgid "return all col edge with 1d index encoding" +msgstr "" + +#: of tensorcircuit.templates.graphs.Grid2DCoord.all_cols:3 +#: tensorcircuit.templates.graphs.Grid2DCoord.all_rows:3 +msgid "" +"whether to include pbc edges (periodic boundary condition), defaults to " +"False" +msgstr "" + +#: of tensorcircuit.templates.graphs.Grid2DCoord.all_cols:6 +msgid "list of col edge" msgstr "" -#: of tensorcircuit.mpscircuit.MPSCircuit.apply_double_gate:1 +#: of tensorcircuit.templates.graphs.Grid2DCoord.all_rows:1 +msgid "return all row edge with 1d index encoding" +msgstr "" + +#: of tensorcircuit.templates.graphs.Grid2DCoord.all_rows:6 +msgid "list of row edge" +msgstr "" + +#: of tensorcircuit.templates.graphs.Grid2DCoord.lattice_graph:1 +msgid "Get the 2D grid lattice in ``nx.Graph`` format" +msgstr "" + +#: of tensorcircuit.templates.graphs.Grid2DCoord.lattice_graph:3 msgid "" -"Apply a double qubit gate on MPS. Truncation rule is specified by " -"`set_truncation_rule`." +"whether to include pbc edges (periodic boundary condition), defaults to " +"True" msgstr "" -#: of tensorcircuit.mpscircuit.MPSCircuit.apply_single_gate:1 +#: of tensorcircuit.templates.graphs.Line1D:1 +msgid "1D chain with ``n`` sites" +msgstr "" + +#: of tensorcircuit.templates.graphs.Line1D:5 +#: tensorcircuit.templates.measurements.heisenberg_measurements:34 +msgid "[description], defaults to True" +msgstr "" + +#: ../../source/api/templates/measurements.rst:2 +msgid "tensorcircuit.templates.measurements" +msgstr "" + +#: of tensorcircuit.templates.measurements.any_local_measurements:1 msgid "" -"Apply a single qubit gate on MPS, and the gate must be unitary; no " -"truncation is needed." +"This measurements pattern is specifically suitable for vmap. Parameterize" +" the local Pauli string to be measured." msgstr "" -#: of tensorcircuit.mpscircuit.MPSCircuit.apply_single_gate:3 -#: tensorcircuit.mpscircuit.MPSCircuit.expectation_double_gates:3 -msgid "gate to be applied" +#: of tensorcircuit.templates.measurements.any_local_measurements:19 +#: tensorcircuit.templates.measurements.any_measurements:26 +msgid "The circuit to be measured" msgstr "" -#: of tensorcircuit.mpscircuit.MPSCircuit.apply_single_gate:5 -#: tensorcircuit.mpscircuit.MPSCircuit.expectation_single_gate:5 -msgid "Qubit index of the gate" +#: of tensorcircuit.templates.measurements.any_local_measurements:21 +#: tensorcircuit.templates.measurements.any_measurements:28 +msgid "" +"parameter tensors determines what Pauli string to be measured, shape is " +"[nwires, 4] if ``onehot`` is False and [nwires] if ``onehot`` is True." msgstr "" -#: of tensorcircuit.mpscircuit.MPSCircuit.conj:1 -msgid "Compute the conjugate of the current MPS." +#: of tensorcircuit.templates.measurements.any_local_measurements:24 +#: tensorcircuit.templates.measurements.any_measurements:31 +msgid "" +"defaults to False. If set to be True, structures will first go through " +"onehot procedure." msgstr "" -#: of tensorcircuit.mpscircuit.MPSCircuit.conj:3 -#: tensorcircuit.mpscircuit.MPSCircuit.copy:3 -#: tensorcircuit.mpscircuit.MPSCircuit.copy_without_tensor:3 -#: tensorcircuit.mpscircuit.MPSCircuit.from_wavefunction:11 -msgid "The constructed MPS" +#: of tensorcircuit.templates.measurements.any_local_measurements:27 +msgid "" +"reuse the wavefunction when computing the expectations, defaults to be " +"True" msgstr "" -#: of tensorcircuit.mpscircuit.MPSCircuit.copy:1 -msgid "Copy the current MPS." +#: of tensorcircuit.templates.measurements.any_local_measurements:29 +#: tensorcircuit.templates.measurements.any_measurements:36 +msgid "The expectation value of given Pauli string by the tensor ``structures``." msgstr "" -#: of tensorcircuit.mpscircuit.MPSCircuit.copy_without_tensor:1 -msgid "Copy the current MPS without the tensors." +#: of tensorcircuit.templates.measurements.any_measurements:1 +msgid "" +"This measurements pattern is specifically suitable for vmap. Parameterize" +" the Pauli string to be measured." msgstr "" -#: of tensorcircuit.mpscircuit.MPSCircuit.expectation_double_gates:1 -msgid "Compute the expectation of the corresponding double qubit gate." +#: of tensorcircuit.templates.measurements.any_measurements:34 +msgid "" +"reuse the wavefunction when computing the expectations, defaults to be " +"False" msgstr "" -#: of tensorcircuit.mpscircuit.MPSCircuit.expectation_double_gates:5 -msgid "qubit index of the gate" +#: of tensorcircuit.templates.measurements.heisenberg_measurements:1 +msgid "" +"Evaluate Heisenberg energy expectation, whose Hamiltonian is defined on " +"the lattice graph ``g`` as follows: (e are edges in graph ``g`` where e1 " +"and e2 are two nodes for edge e and v are nodes in graph ``g``)" msgstr "" -#: of tensorcircuit.mpscircuit.MPSCircuit.expectation_single_gate:1 +#: of tensorcircuit.templates.measurements.heisenberg_measurements:4 msgid "" -"Compute the expectation of the corresponding single qubit gate in the " -"form of tensor." +"H = \\sum_{e\\in g} w_e (h_{xx} X_{e1}X_{e2} + h_{yy} Y_{e1}Y_{e2} + " +"h_{zz} Z_{e1}Z_{e2})\n" +" + \\sum_{v\\in g} (h_x X_v + h_y Y_v + h_z Z_v)" +msgstr "" + +#: of tensorcircuit.templates.measurements.heisenberg_measurements:18 +msgid "Circuit to be measured" +msgstr "" + +#: of tensorcircuit.templates.measurements.heisenberg_measurements:20 +msgid "Lattice graph defining Heisenberg Hamiltonian" +msgstr "" + +#: of tensorcircuit.templates.measurements.heisenberg_measurements:22 +#: tensorcircuit.templates.measurements.heisenberg_measurements:24 +#: tensorcircuit.templates.measurements.heisenberg_measurements:26 +msgid "[description], defaults to 1.0" msgstr "" -#: of tensorcircuit.mpscircuit.MPSCircuit.expectation_single_gate:3 -msgid "Gate to be applied" +#: of tensorcircuit.templates.measurements.heisenberg_measurements:28 +#: tensorcircuit.templates.measurements.heisenberg_measurements:30 +#: tensorcircuit.templates.measurements.heisenberg_measurements:32 +msgid "[description], defaults to 0.0" msgstr "" -#: of tensorcircuit.mpscircuit.MPSCircuit.expectation_single_gate:7 -msgid "The expectation of the corresponding single qubit gate" +#: of tensorcircuit.templates.measurements.heisenberg_measurements:36 +msgid "Value of Heisenberg energy" msgstr "" -#: of tensorcircuit.mpscircuit.MPSCircuit.expectation_two_gates_product:1 +#: of tensorcircuit.templates.measurements.mpo_expectation:1 msgid "" -"Compute the expectation of the direct product of the corresponding two " -"gates." +"Evaluate expectation of operator ``mpo`` defined in ``QuOperator`` MPO " +"format with the output quantum state from circuit ``c``." msgstr "" -#: of tensorcircuit.mpscircuit.MPSCircuit.expectation_two_gates_product:3 -msgid "First gate to be applied" +#: of tensorcircuit.templates.measurements.mpo_expectation:4 +msgid "The circuit for the output state" msgstr "" -#: of tensorcircuit.mpscircuit.MPSCircuit.expectation_two_gates_product:5 -msgid "Second gate to be applied" +#: of tensorcircuit.templates.measurements.mpo_expectation:6 +msgid "MPO operator" msgstr "" -#: of tensorcircuit.mpscircuit.MPSCircuit.expectation_two_gates_product:7 -msgid "Qubit index of the first gate" +#: of tensorcircuit.templates.measurements.mpo_expectation:8 +#: tensorcircuit.templates.measurements.operator_expectation:7 +#: tensorcircuit.templates.measurements.sparse_expectation:7 +msgid "a real and scalar tensor of shape [] as the expectation value" msgstr "" -#: of tensorcircuit.mpscircuit.MPSCircuit.expectation_two_gates_product:9 -msgid "Qubit index of the second gate" +#: of tensorcircuit.templates.measurements.operator_expectation:1 +msgid "" +"Evaluate Hamiltonian expectation where ``hamiltonian`` can be dense " +"matrix, sparse matrix or MPO." msgstr "" -#: of tensorcircuit.mpscircuit.MPSCircuit.expectation_two_gates_product:11 -msgid "The correlation of the corresponding two qubit gates" +#: of tensorcircuit.templates.measurements.operator_expectation:3 +#: tensorcircuit.templates.measurements.sparse_expectation:3 +msgid "The circuit whose output state is used to evaluate the expectation" msgstr "" -#: of tensorcircuit.mpscircuit.MPSCircuit.from_wavefunction:1 -msgid "Construct the MPS from a given wavefunction." +#: of tensorcircuit.templates.measurements.operator_expectation:5 +#: tensorcircuit.templates.measurements.sparse_expectation:5 +msgid "Hamiltonian matrix in COO_sparse_matrix form" msgstr "" -#: of tensorcircuit.mpscircuit.MPSCircuit.from_wavefunction:3 -msgid "The given wavefunction (any shape is OK)" +#: of tensorcircuit.templates.measurements.sparse_expectation:1 +msgid "" +"Evaluate Hamiltonian expectation where ``hamiltonian`` is kept in sparse " +"matrix form to save space" msgstr "" -#: of tensorcircuit.mpscircuit.MPSCircuit.general_expectation:1 -msgid "Compute the expectation of corresponding operators in the form of tensor." +#: of tensorcircuit.templates.measurements.spin_glass_measurements:1 +msgid "" +"Compute spin glass energy defined on graph ``g`` expectation for output " +"state of the circuit ``c``. The Hamiltonian to be evaluated is defined as" +" (first term is determined by node weights while the second term is " +"determined by edge weights of the graph):" +msgstr "" + +#: of tensorcircuit.templates.measurements.spin_glass_measurements:5 +msgid "H = \\sum_{v\\in g} w_v Z_v + \\sum_{e\\in g} w_e Z_{e1} Z_{e2}" +msgstr "" + +#: of tensorcircuit.templates.measurements.spin_glass_measurements:28 +msgid "The quantum circuit" +msgstr "" + +#: of tensorcircuit.templates.measurements.spin_glass_measurements:30 +msgid "The graph for spin glass Hamiltonian definition" msgstr "" -#: of tensorcircuit.mpscircuit.MPSCircuit.general_expectation:3 +#: of tensorcircuit.templates.measurements.spin_glass_measurements:32 msgid "" -"Operator and its position on the circuit, eg. ``(gates.Z(), [1]), " -"(gates.X(), [2])`` is for operator :math:`Z_1X_2`" +"Whether measure the circuit with reusing the wavefunction, defaults to " +"True" msgstr "" -#: of tensorcircuit.mpscircuit.MPSCircuit.general_expectation:6 -msgid "The expectation of corresponding operators" +#: of tensorcircuit.templates.measurements.spin_glass_measurements:34 +msgid "The spin glass energy expectation value" msgstr "" -#: of tensorcircuit.mpscircuit.MPSCircuit.get_norm:1 -msgid "Get the normalized Center Position." +#: ../../source/api/torchnn.rst:2 +msgid "tensorcircuit.torchnn" msgstr "" -#: of tensorcircuit.mpscircuit.MPSCircuit.get_norm:3 -msgid "Normalized Center Position." +#: of tensorcircuit.torchnn:1 +msgid "PyTorch nn Module wrapper for quantum function" msgstr "" -#: of tensorcircuit.mpscircuit.MPSCircuit.is_valid:1 -msgid "Check whether the circuit is legal." +#: of tensorcircuit.torchnn.HardwareNet:1 +msgid "Bases: :py:class:`~tensorcircuit.torchnn.QuantumNet`" msgstr "" -#: of tensorcircuit.mpscircuit.MPSCircuit.is_valid:3 -msgid "Whether the circuit is legal." +#: of tensorcircuit.torchnn.HardwareNet:1 +msgid "" +"PyTorch Layer wrapping quantum function with cloud qpu access (using " +":py:mod:`tensorcircuit.cloud` module)" msgstr "" -#: of tensorcircuit.mpscircuit.MPSCircuit.measure:1 -msgid "integer indicating the measure on which quantum line" +#: of tensorcircuit.torchnn.HardwareNet.__init__:1 +#: tensorcircuit.torchnn.QuantumNet.__init__:1 +msgid "PyTorch nn Module wrapper on quantum function ``f``." msgstr "" -#: of tensorcircuit.mpscircuit.MPSCircuit.measure:2 -msgid "if true, theoretical probability is also returned" +#: of tensorcircuit.torchnn.HardwareNet.__init__:32 +#: tensorcircuit.torchnn.QuantumNet.__init__:32 +msgid "Quantum function with tensor in (input and weights) and tensor out." msgstr "" -#: of tensorcircuit.mpscircuit.MPSCircuit.mid_measurement:1 +#: of tensorcircuit.torchnn.HardwareNet.__init__:34 +#: tensorcircuit.torchnn.QuantumNet.__init__:34 msgid "" -"Middle measurement in the z-basis on the circuit, note the wavefunction " -"output is not normalized with ``mid_measurement`` involved, one should " -"normalized the state manually if needed." +"list of shape tuple for different weights as the non-first parameters for" +" ``f``" msgstr "" -#: of tensorcircuit.mpscircuit.MPSCircuit.mid_measurement:4 -msgid "The index of qubit that the Z direction postselection applied on" +#: of tensorcircuit.torchnn.HardwareNet.__init__:36 +#: tensorcircuit.torchnn.QuantumNet.__init__:36 +msgid "function that gives the shape tuple returns torch tensor, defaults to None" msgstr "" -#: of tensorcircuit.mpscircuit.MPSCircuit.mid_measurement:6 -msgid "0 for spin up, 1 for spin down, defaults to 0" +#: of tensorcircuit.torchnn.HardwareNet.__init__:38 +#: tensorcircuit.torchnn.QuantumNet.__init__:38 +msgid "whether apply vmap (batch input) on ``f``, defaults to True" msgstr "" -#: of tensorcircuit.mpscircuit.MPSCircuit.normalize:1 -msgid "Normalize MPS Circuit according to the center position." +#: of tensorcircuit.torchnn.HardwareNet.__init__:40 +#: tensorcircuit.torchnn.QuantumNet.__init__:40 +msgid "" +"which position of input should be batched, need to be customized when " +"multiple inputs for the torch model, defaults to be 0." msgstr "" -#: of tensorcircuit.mpscircuit.MPSCircuit.position:1 -msgid "Wrapper of tn.FiniteMPS.position. Set orthogonality center." +#: of tensorcircuit.torchnn.HardwareNet.__init__:43 +#: tensorcircuit.torchnn.QuantumNet.__init__:43 +msgid "whether transform ``f`` with torch interface, defaults to True" msgstr "" -#: of tensorcircuit.mpscircuit.MPSCircuit.position:4 -msgid "The orthogonality center" +#: of tensorcircuit.torchnn.HardwareNet.__init__:45 +#: tensorcircuit.torchnn.QuantumNet.__init__:45 +msgid "whether jit ``f``, defaults to True" msgstr "" -#: of tensorcircuit.mpscircuit.MPSCircuit.proj_with_mps:1 -msgid "Compute the projection between `other` as bra and `self` as ket." +#: of tensorcircuit.torchnn.HardwareNet.__init__:47 +#: tensorcircuit.torchnn.QuantumNet.__init__:47 +msgid "whether enbale dlpack in interfaces, defaults to False" msgstr "" -#: of tensorcircuit.mpscircuit.MPSCircuit.proj_with_mps:3 -msgid "ket of the other MPS, which will be converted to bra automatically" +#: of torch.nn.modules.module.Module.add_module:1 +msgid "Adds a child module to the current module." msgstr "" -#: of tensorcircuit.mpscircuit.MPSCircuit.proj_with_mps:5 -msgid "The projection in form of tensor" +#: of torch.nn.modules.module.Module.add_module:3 +msgid "The module can be accessed as an attribute using the given name." msgstr "" -#: of tensorcircuit.mpscircuit.MPSCircuit.set_truncation_rule:1 +#: of torch.nn.modules.module.Module.add_module:5 msgid "" -"Set truncation rules when double qubit gates are applied. If nothing is " -"specified, no truncation will take place and the bond dimension will keep" -" growing. For more details, refer to `split_tensor`." +"name of the child module. The child module can be accessed from this " +"module using the given name" msgstr "" -#: of tensorcircuit.mpscircuit.MPSCircuit.wavefunction:3 -msgid "the str indicating the form of the output wavefunction" +#: of torch.nn.modules.module.Module.add_module:8 +msgid "child module to be added to the module." msgstr "" -#: of tensorcircuit.mpscircuit.MPSCircuit.wavefunction:5 -msgid "Tensor with shape [1, -1]" +#: of torch.nn.modules.module.Module.apply:1 +msgid "" +"Applies ``fn`` recursively to every submodule (as returned by " +"``.children()``) as well as self. Typical use includes initializing the " +"parameters of a model (see also :ref:`nn-init-doc`)." msgstr "" -#: of tensorcircuit.mpscircuit.split_tensor:1 -msgid "Split the tensor by SVD or QR depends on whether a truncation is required." +#: of torch.nn.modules.module.Module.apply:5 +msgid "function to be applied to each submodule" msgstr "" -#: of tensorcircuit.mpscircuit.split_tensor:3 -msgid "The input tensor to split." +#: of torch.nn.modules.module.Module.apply:8 +#: torch.nn.modules.module.Module.bfloat16:6 +#: torch.nn.modules.module.Module.cpu:6 torch.nn.modules.module.Module.cuda:14 +#: torch.nn.modules.module.Module.double:6 +#: torch.nn.modules.module.Module.eval:13 +#: torch.nn.modules.module.Module.float:6 torch.nn.modules.module.Module.half:6 +#: torch.nn.modules.module.Module.ipu:14 +#: torch.nn.modules.module.Module.requires_grad_:17 +#: torch.nn.modules.module.Module.to:45 +#: torch.nn.modules.module.Module.to_empty:7 +#: torch.nn.modules.module.Module.train:12 +#: torch.nn.modules.module.Module.type:9 torch.nn.modules.module.Module.xpu:14 +msgid "self" msgstr "" -#: of tensorcircuit.mpscircuit.split_tensor:5 -msgid "Determine the orthogonal center is on the left tensor or the right tensor." +#: of torch.nn.modules.module.Module.apply:11 +#: torch.nn.modules.module.Module.buffers:10 +#: torch.nn.modules.module.Module.modules:10 +#: torch.nn.modules.module.Module.named_buffers:15 +#: torch.nn.modules.module.Module.named_children:6 +#: torch.nn.modules.module.Module.named_modules:16 +#: torch.nn.modules.module.Module.named_parameters:16 +#: torch.nn.modules.module.Module.parameters:12 +#: torch.nn.modules.module.Module.register_buffer:25 +#: torch.nn.modules.module.Module.state_dict:38 +msgid "Example::" msgstr "" -#: of tensorcircuit.mpscircuit.split_tensor:13 -msgid "Two tensors after splitting" +#: of torch.nn.modules.module.Module.bfloat16:1 +msgid "Casts all floating point parameters and buffers to ``bfloat16`` datatype." msgstr "" -#: ../../source/api/quantum.rst:2 -msgid "tensorcircuit.quantum" +#: of torch.nn.modules.module.Module.bfloat16:4 +#: torch.nn.modules.module.Module.cpu:4 torch.nn.modules.module.Module.cuda:8 +#: torch.nn.modules.module.Module.double:4 +#: torch.nn.modules.module.Module.float:4 torch.nn.modules.module.Module.half:4 +#: torch.nn.modules.module.Module.ipu:8 torch.nn.modules.module.Module.to:29 +#: torch.nn.modules.module.Module.type:4 torch.nn.modules.module.Module.xpu:8 +msgid "This method modifies the module in-place." msgstr "" -#: of tensorcircuit.quantum:1 -msgid "Quantum state and operator class backend by tensornetwork" +#: of torch.nn.modules.module.Module.buffers:1 +msgid "Returns an iterator over module buffers." msgstr "" -#: of tensorcircuit.quantum -msgid "IMPORT" +#: of torch.nn.modules.module.Module.buffers:3 +msgid "" +"if True, then yields buffers of this module and all submodules. " +"Otherwise, yields only buffers that are direct members of this module." +msgstr "" + +#: of torch.nn.modules.module.Module.buffers +#: torch.nn.modules.module.Module.children +#: torch.nn.modules.module.Module.modules +#: torch.nn.modules.module.Module.named_buffers +#: torch.nn.modules.module.Module.named_children +#: torch.nn.modules.module.Module.named_modules +#: torch.nn.modules.module.Module.named_parameters +#: torch.nn.modules.module.Module.parameters +msgid "Yields" +msgstr "" + +#: of torch.nn.modules.module.Module.buffers:8 +msgid "*torch.Tensor* -- module buffer" +msgstr "" + +#: of torch.nn.modules.module.Module.children:1 +msgid "Returns an iterator over immediate children modules." +msgstr "" + +#: of torch.nn.modules.module.Module.children:3 +msgid "*Module* -- a child module" +msgstr "" + +#: of torch.nn.modules.module.Module.cpu:1 +msgid "Moves all model parameters and buffers to the CPU." +msgstr "" + +#: of torch.nn.modules.module.Module.cuda:1 +msgid "Moves all model parameters and buffers to the GPU." +msgstr "" + +#: of torch.nn.modules.module.Module.cuda:3 +msgid "" +"This also makes associated parameters and buffers different objects. So " +"it should be called before constructing optimizer if the module will live" +" on GPU while being optimized." +msgstr "" + +#: of torch.nn.modules.module.Module.cuda:10 +#: torch.nn.modules.module.Module.ipu:10 torch.nn.modules.module.Module.xpu:10 +msgid "if specified, all parameters will be copied to that device" +msgstr "" + +#: of torch.nn.modules.module.Module.double:1 +msgid "Casts all floating point parameters and buffers to ``double`` datatype." +msgstr "" + +#: of torch.nn.modules.module.Module.eval:1 +msgid "Sets the module in evaluation mode." +msgstr "" + +#: of torch.nn.modules.module.Module.eval:3 +#: torch.nn.modules.module.Module.train:3 +msgid "" +"This has any effect only on certain modules. See documentations of " +"particular modules for details of their behaviors in training/evaluation " +"mode, if they are affected, e.g. :class:`Dropout`, :class:`BatchNorm`, " +"etc." +msgstr "" + +#: of torch.nn.modules.module.Module.eval:8 +msgid "This is equivalent with :meth:`self.train(False) `." +msgstr "" + +#: of torch.nn.modules.module.Module.eval:10 +msgid "" +"See :ref:`locally-disable-grad-doc` for a comparison between `.eval()` " +"and several similar mechanisms that may be confused with it." +msgstr "" + +#: of torch.nn.modules.module.Module.extra_repr:1 +msgid "Set the extra representation of the module" +msgstr "" + +#: of torch.nn.modules.module.Module.extra_repr:3 +msgid "" +"To print customized extra information, you should re-implement this " +"method in your own modules. Both single-line and multi-line strings are " +"acceptable." +msgstr "" + +#: of torch.nn.modules.module.Module.float:1 +msgid "Casts all floating point parameters and buffers to ``float`` datatype." +msgstr "" + +#: of tensorcircuit.torchnn.HardwareNet.forward:1 +#: tensorcircuit.torchnn.QuantumNet.forward:1 +msgid "Defines the computation performed at every call." +msgstr "" + +#: of tensorcircuit.torchnn.HardwareNet.forward:3 +#: tensorcircuit.torchnn.QuantumNet.forward:3 +msgid "Should be overridden by all subclasses." msgstr "" -#: of tensorcircuit.quantum.PauliString2COO:1 -#: tensorcircuit.quantum.PauliStringSum2COO_tf:1 -msgid "Generate tensorflow sparse matrix from Pauli string sum" +#: of tensorcircuit.torchnn.HardwareNet.forward:6 +#: tensorcircuit.torchnn.QuantumNet.forward:6 +msgid "" +"Although the recipe for forward pass needs to be defined within this " +"function, one should call the :class:`Module` instance afterwards instead" +" of this since the former takes care of running the registered hooks " +"while the latter silently ignores them." msgstr "" -#: of tensorcircuit.quantum.PauliString2COO:3 +#: of torch.nn.modules.module.Module.get_buffer:1 msgid "" -"1D Tensor representing for a Pauli string, e.g. [1, 0, 0, 3, 2] is for " -":math:`X_0Z_3Y_4`" +"Returns the buffer given by ``target`` if it exists, otherwise throws an " +"error." msgstr "" -#: of tensorcircuit.quantum.PauliString2COO:6 +#: of torch.nn.modules.module.Module.get_buffer:4 +#: torch.nn.modules.module.Module.get_parameter:4 msgid "" -"the weight for the Pauli string defaults to None (all Pauli strings " -"weight 1.0)" +"See the docstring for ``get_submodule`` for a more detailed explanation " +"of this method's functionality as well as how to correctly specify " +"``target``." msgstr "" -#: of tensorcircuit.quantum.PauliString2COO:9 -msgid "the tensorflow sparse matrix" +#: of torch.nn.modules.module.Module.get_buffer:8 +msgid "" +"The fully-qualified string name of the buffer to look for. (See " +"``get_submodule`` for how to specify a fully-qualified string.)" msgstr "" -#: of tensorcircuit.quantum.PauliStringSum2COO:1 -#: tensorcircuit.quantum.PauliStringSum2COO_numpy:1 -msgid "Generate sparse tensor from Pauli string sum" +#: of torch.nn.modules.module.Module.get_buffer:12 +msgid "The buffer referenced by ``target``" msgstr "" -#: of tensorcircuit.quantum.PauliStringSum2COO:3 -#: tensorcircuit.quantum.PauliStringSum2COO_numpy:3 -#: tensorcircuit.quantum.PauliStringSum2COO_tf:3 -#: tensorcircuit.quantum.PauliStringSum2Dense:3 +#: of torch.nn.modules.module.Module.get_buffer:15 msgid "" -"2D Tensor, each row is for a Pauli string, e.g. [1, 0, 0, 3, 2] is for " -":math:`X_0Z_3Y_4`" +"If the target string references an invalid path or resolves to " +"something that is not a buffer" msgstr "" -#: of tensorcircuit.quantum.PauliStringSum2COO:6 -#: tensorcircuit.quantum.PauliStringSum2COO_numpy:6 -#: tensorcircuit.quantum.PauliStringSum2COO_tf:6 -#: tensorcircuit.quantum.PauliStringSum2Dense:6 +#: of torch.nn.modules.module.Module.get_extra_state:1 msgid "" -"1D Tensor, each element corresponds the weight for each Pauli string " -"defaults to None (all Pauli strings weight 1.0)" +"Returns any extra state to include in the module's state_dict. Implement " +"this and a corresponding :func:`set_extra_state` for your module if you " +"need to store extra state. This function is called when building the " +"module's `state_dict()`." msgstr "" -#: of tensorcircuit.quantum.PauliStringSum2COO:9 -#: tensorcircuit.quantum.PauliStringSum2COO_numpy:9 -#: tensorcircuit.quantum.PauliStringSum2Dense:9 +#: of torch.nn.modules.module.Module.get_extra_state:6 msgid "" -"default False. If True, return numpy coo else return backend compatible " -"sparse tensor" +"Note that extra state should be picklable to ensure working serialization" +" of the state_dict. We only provide provide backwards compatibility " +"guarantees for serializing Tensors; other objects may break backwards " +"compatibility if their serialized pickled form changes." msgstr "" -#: of tensorcircuit.quantum.PauliStringSum2COO:12 -#: tensorcircuit.quantum.PauliStringSum2COO_numpy:12 -msgid "the scipy coo sparse matrix" +#: of torch.nn.modules.module.Module.get_extra_state:11 +msgid "Any extra state to store in the module's state_dict" msgstr "" -#: of tensorcircuit.quantum.PauliStringSum2COO_tf:9 -msgid "the tensorflow coo sparse matrix" +#: of torch.nn.modules.module.Module.get_parameter:1 +msgid "" +"Returns the parameter given by ``target`` if it exists, otherwise throws " +"an error." msgstr "" -#: of tensorcircuit.quantum.PauliStringSum2Dense:1 -msgid "Generate dense matrix from Pauli string sum" +#: of torch.nn.modules.module.Module.get_parameter:8 +msgid "" +"The fully-qualified string name of the Parameter to look for. (See " +"``get_submodule`` for how to specify a fully-qualified string.)" msgstr "" -#: of tensorcircuit.quantum.PauliStringSum2Dense:12 -msgid "the tensorflow dense matrix" +#: of torch.nn.modules.module.Module.get_parameter:12 +msgid "The Parameter referenced by ``target``" msgstr "" -#: of tensorcircuit.quantum.QuAdjointVector:1 tensorcircuit.quantum.QuScalar:1 -#: tensorcircuit.quantum.QuVector:1 -msgid "Bases: :py:class:`tensorcircuit.quantum.QuOperator`" +#: of torch.nn.modules.module.Module.get_parameter:15 +msgid "" +"If the target string references an invalid path or resolves to " +"something that is not an ``nn.Parameter``" msgstr "" -#: of tensorcircuit.quantum.QuAdjointVector:1 -msgid "Represents an adjoint (row) vector via a tensor network." +#: of torch.nn.modules.module.Module.get_submodule:1 +msgid "" +"Returns the submodule given by ``target`` if it exists, otherwise throws " +"an error." msgstr "" -#: of tensorcircuit.quantum.QuAdjointVector.__init__:1 +#: of torch.nn.modules.module.Module.get_submodule:4 msgid "" -"Constructs a new `QuAdjointVector` from a tensor network. This " -"encapsulates an existing tensor network, interpreting it as an adjoint " -"vector (row vector)." +"For example, let's say you have an ``nn.Module`` ``A`` that looks like " +"this:" msgstr "" -#: of tensorcircuit.quantum.QuAdjointVector.__init__:5 -#: tensorcircuit.quantum.QuOperator.__init__:9 -msgid "The edges of the network to be used as the input edges." +#: of torch.nn.modules.module.Module.get_submodule:18 +msgid "" +"(The diagram shows an ``nn.Module`` ``A``. ``A`` has a nested submodule " +"``net_b``, which itself has two submodules ``net_c`` and ``linear``. " +"``net_c`` then has a submodule ``conv``.)" msgstr "" -#: of tensorcircuit.quantum.QuAdjointVector.__init__:7 -#: tensorcircuit.quantum.QuOperator.__init__:11 -#: tensorcircuit.quantum.QuVector.__init__:6 +#: of torch.nn.modules.module.Module.get_submodule:22 msgid "" -"Nodes used to refer to parts of the tensor network that are not connected" -" to any input or output edges (for example: a scalar factor)." +"To check whether or not we have the ``linear`` submodule, we would call " +"``get_submodule(\"net_b.linear\")``. To check whether we have the " +"``conv`` submodule, we would call " +"``get_submodule(\"net_b.net_c.conv\")``." msgstr "" -#: of tensorcircuit.quantum.QuAdjointVector.__init__:10 -#: tensorcircuit.quantum.QuScalar.__init__:7 -#: tensorcircuit.quantum.QuVector.__init__:9 -msgid "Optional collection of edges to ignore when performing consistency checks." +#: of torch.nn.modules.module.Module.get_submodule:27 +msgid "" +"The runtime of ``get_submodule`` is bounded by the degree of module " +"nesting in ``target``. A query against ``named_modules`` achieves the " +"same result, but it is O(N) in the number of transitive modules. So, for " +"a simple check to see if some submodule exists, ``get_submodule`` should " +"always be used." msgstr "" -#: of tensorcircuit.quantum.QuAdjointVector.from_tensor:1 +#: of torch.nn.modules.module.Module.get_submodule:34 msgid "" -"Construct a `QuAdjointVector` directly from a single tensor. This first " -"wraps the tensor in a `Node`, then constructs the `QuAdjointVector` from " -"that `Node`." +"The fully-qualified string name of the submodule to look for. (See above " +"example for how to specify a fully-qualified string.)" msgstr "" -#: of tensorcircuit.quantum.QuAdjointVector.from_tensor:27 -msgid "The tensor for constructing an QuAdjointVector." +#: of torch.nn.modules.module.Module.get_submodule:38 +msgid "The submodule referenced by ``target``" msgstr "" -#: of tensorcircuit.quantum.QuAdjointVector.from_tensor:29 +#: of torch.nn.modules.module.Module.get_submodule:41 msgid "" -"Sequence of integer indices specifying the order in which to interpret " -"the axes as subsystems (input edges). If not specified, the axes are " -"taken in ascending order." +"If the target string references an invalid path or resolves to " +"something that is not an ``nn.Module``" msgstr "" -#: of tensorcircuit.quantum.QuAdjointVector.from_tensor:33 -msgid "The new constructed QuAdjointVector give from the given tensor." +#: of torch.nn.modules.module.Module.half:1 +msgid "Casts all floating point parameters and buffers to ``half`` datatype." msgstr "" -#: of tensorcircuit.quantum.QuAdjointVector.projector:1 -#: tensorcircuit.quantum.QuVector.projector:1 -msgid "" -"The projector of the operator. The operator, as a linear operator, on the" -" adjoint of the operator." +#: of torch.nn.modules.module.Module.ipu:1 +msgid "Moves all model parameters and buffers to the IPU." msgstr "" -#: of tensorcircuit.quantum.QuAdjointVector.projector:4 +#: of torch.nn.modules.module.Module.ipu:3 msgid "" -"Set :math:`A` is the operator in matrix form, then the projector of " -"operator is defined as: :math:`A^\\dagger A`" +"This also makes associated parameters and buffers different objects. So " +"it should be called before constructing optimizer if the module will live" +" on IPU while being optimized." msgstr "" -#: of tensorcircuit.quantum.QuAdjointVector.projector:6 -#: tensorcircuit.quantum.QuVector.projector:6 -msgid "The projector of the operator." +#: of torch.nn.modules.module.Module.load_state_dict:1 +msgid "" +"Copies parameters and buffers from :attr:`state_dict` into this module " +"and its descendants. If :attr:`strict` is ``True``, then the keys of " +":attr:`state_dict` must exactly match the keys returned by this module's " +":meth:`~torch.nn.Module.state_dict` function." msgstr "" -#: of tensorcircuit.quantum.QuAdjointVector.reduced_density:1 -#: tensorcircuit.quantum.QuVector.reduced_density:1 -msgid "The reduced density of the operator." +#: of torch.nn.modules.module.Module.load_state_dict:6 +msgid "a dict containing parameters and persistent buffers." msgstr "" -#: of tensorcircuit.quantum.QuAdjointVector.reduced_density:3 -#: tensorcircuit.quantum.QuVector.reduced_density:3 +#: of torch.nn.modules.module.Module.load_state_dict:9 msgid "" -"Set :math:`A` is the matrix of the operator, then the reduced density is " -"defined as:" -msgstr "" - -#: of tensorcircuit.quantum.QuAdjointVector.reduced_density:5 -msgid "\\mathrm{Tr}_{subsystems}(A^\\dagger A)" +"whether to strictly enforce that the keys in :attr:`state_dict` match the" +" keys returned by this module's :meth:`~torch.nn.Module.state_dict` " +"function. Default: ``True``" msgstr "" -#: of tensorcircuit.quantum.QuAdjointVector.reduced_density:9 -#: tensorcircuit.quantum.QuVector.reduced_density:9 +#: of torch.nn.modules.module.Module.load_state_dict:14 msgid "" -"Firstly, take the projector of the operator, then trace out the " -"subsystems to trace out are supplied as indices, so that dangling edges " -"are connected to each other as: `out_edges[i] ^ in_edges[i] for i in " -"subsystems_to_trace_out` This does not modify the original network. The " -"original ordering of the remaining subsystems is maintained." +"* **missing_keys** is a list of str containing the missing keys * " +"**unexpected_keys** is a list of str containing the unexpected keys" msgstr "" -#: of tensorcircuit.quantum.QuAdjointVector.reduced_density:16 -#: tensorcircuit.quantum.QuOperator.partial_trace:8 -#: tensorcircuit.quantum.QuVector.reduced_density:16 -msgid "Indices of subsystems to trace out." +#: of torch.nn.modules.module.Module.load_state_dict:14 +msgid "**missing_keys** is a list of str containing the missing keys" msgstr "" -#: of tensorcircuit.quantum.QuAdjointVector.reduced_density:18 -#: tensorcircuit.quantum.QuVector.reduced_density:18 -msgid "" -"The QuOperator of the reduced density of the operator with given " -"subsystems." +#: of torch.nn.modules.module.Module.load_state_dict:15 +msgid "**unexpected_keys** is a list of str containing the unexpected keys" msgstr "" -#: of tensorcircuit.quantum.QuOperator:1 -msgid "" -"Represents a linear operator via a tensor network. To interpret a tensor " -"network as a linear operator, some of the dangling edges must be " -"designated as `out_edges` (output edges) and the rest as `in_edges` " -"(input edges). Considered as a matrix, the `out_edges` represent the row " -"index and the `in_edges` represent the column index. The (right) action " -"of the operator on another then consists of connecting the `in_edges` of " -"the first operator to the `out_edges` of the second. Can be used to do " -"simple linear algebra with tensor networks." +#: of torch.nn.modules.module.Module.load_state_dict:16 +msgid "``NamedTuple`` with ``missing_keys`` and ``unexpected_keys`` fields" msgstr "" -#: of tensorcircuit.quantum.QuOperator.__init__:1 +#: of torch.nn.modules.module.Module.load_state_dict:20 msgid "" -"Creates a new `QuOperator` from a tensor network. This encapsulates an " -"existing tensor network, interpreting it as a linear operator. The " -"network is checked for consistency: All dangling edges must either be in " -"`out_edges`, `in_edges`, or `ignore_edges`." +"If a parameter or buffer is registered as ``None`` and its corresponding " +"key exists in :attr:`state_dict`, :meth:`load_state_dict` will raise a " +"``RuntimeError``." msgstr "" -#: of tensorcircuit.quantum.QuOperator.__init__:7 -#: tensorcircuit.quantum.QuVector.__init__:4 -msgid "The edges of the network to be used as the output edges." +#: of torch.nn.modules.module.Module.modules:1 +msgid "Returns an iterator over all modules in the network." msgstr "" -#: of tensorcircuit.quantum.QuOperator.__init__:15 -msgid "" -"Optional collection of dangling edges to ignore when performing " -"consistency checks." +#: of torch.nn.modules.module.Module.modules:3 +msgid "*Module* -- a module in the network" msgstr "" -#: of tensorcircuit.quantum.QuOperator.__init__:18 +#: of torch.nn.modules.module.Module.modules:7 +#: torch.nn.modules.module.Module.named_modules:13 msgid "" -"At least one reference node is required to specify a scalar. None " -"provided!" +"Duplicate modules are returned only once. In the following example, ``l``" +" will be returned only once." msgstr "" -#: of tensorcircuit.quantum.QuOperator.adjoint:1 +#: of torch.nn.modules.module.Module.named_buffers:1 msgid "" -"The adjoint of the operator. This creates a new `QuOperator` with " -"complex-conjugate copies of all tensors in the network and with the input" -" and output edges switched." +"Returns an iterator over module buffers, yielding both the name of the " +"buffer as well as the buffer itself." msgstr "" -#: of tensorcircuit.quantum.QuOperator.adjoint:5 -msgid "The adjoint of the operator." +#: of torch.nn.modules.module.Module.named_buffers:4 +msgid "prefix to prepend to all buffer names." msgstr "" -#: of tensorcircuit.quantum.QuOperator.check_network:1 +#: of torch.nn.modules.module.Module.named_buffers:6 msgid "" -"Check that the network has the expected dimensionality. This checks that " -"all input and output edges are dangling and that there are no other " -"dangling edges (except any specified in `ignore_edges`). If not, an " -"exception is raised." +"if True, then yields buffers of this module and all submodules. " +"Otherwise, yields only buffers that are direct members of this module. " +"Defaults to True." msgstr "" -#: of tensorcircuit.quantum.QuOperator.contract:1 -msgid "" -"Contract the tensor network in place. This modifies the tensor network " -"representation of the operator (or vector, or scalar), reducing it to a " -"single tensor, without changing the value." +#: of torch.nn.modules.module.Module.named_buffers:10 +msgid "whether to remove the duplicated buffers in the result. Defaults to True." msgstr "" -#: of tensorcircuit.quantum.QuOperator.contract:5 -msgid "Manually specify the axis ordering of the final tensor." +#: of torch.nn.modules.module.Module.named_buffers:13 +msgid "*(str, torch.Tensor)* -- Tuple containing the name and buffer" msgstr "" -#: of tensorcircuit.quantum.QuOperator.contract:7 -msgid "The present object." +#: of torch.nn.modules.module.Module.named_children:1 +msgid "" +"Returns an iterator over immediate children modules, yielding both the " +"name of the module as well as the module itself." msgstr "" -#: of tensorcircuit.quantum.QuOperator.copy:1 -msgid "The deep copy of the operator." +#: of torch.nn.modules.module.Module.named_children:4 +msgid "*(str, Module)* -- Tuple containing a name and child module" msgstr "" -#: of tensorcircuit.quantum.QuOperator.copy:3 -msgid "The new copy of the operator." +#: of torch.nn.modules.module.Module.named_modules:1 +msgid "" +"Returns an iterator over all modules in the network, yielding both the " +"name of the module as well as the module itself." msgstr "" -#: of tensorcircuit.quantum.QuOperator.eval:1 -msgid "" -"Contracts the tensor network in place and returns the final tensor. Note " -"that this modifies the tensor network representing the operator. The " -"default ordering for the axes of the final tensor is: `*out_edges, " -"*in_edges`. If there are any \"ignored\" edges, their axes come first: " -"`*ignored_edges, *out_edges, *in_edges`." +#: of torch.nn.modules.module.Module.named_modules:4 +msgid "a memo to store the set of modules already added to the result" msgstr "" -#: of tensorcircuit.quantum.QuOperator.eval:8 -#: tensorcircuit.quantum.QuOperator.eval_matrix:6 -msgid "" -"Manually specify the axis ordering of the final tensor. The default " -"ordering is determined by `out_edges` and `in_edges` (see above)." +#: of torch.nn.modules.module.Module.named_modules:5 +msgid "a prefix that will be added to the name of the module" msgstr "" -#: of tensorcircuit.quantum.QuOperator.eval:11 -#: tensorcircuit.quantum.QuOperator.eval_matrix:9 -msgid "Node count '{}' > 1 after contraction!" +#: of torch.nn.modules.module.Module.named_modules:6 +msgid "whether to remove the duplicated module instances in the result or not" msgstr "" -#: of tensorcircuit.quantum.QuOperator.eval:12 -msgid "The final tensor representing the operator." +#: of torch.nn.modules.module.Module.named_modules:9 +msgid "*(str, Module)* -- Tuple of name and module" msgstr "" -#: of tensorcircuit.quantum.QuOperator.eval_matrix:1 +#: of torch.nn.modules.module.Module.named_parameters:1 msgid "" -"Contracts the tensor network in place and returns the final tensor in two" -" dimentional matrix. The default ordering for the axes of the final " -"tensor is: (:math:`\\prod` dimension of out_edges, :math:`\\prod` " -"dimension of in_edges)" +"Returns an iterator over module parameters, yielding both the name of the" +" parameter as well as the parameter itself." msgstr "" -#: of tensorcircuit.quantum.QuOperator.eval_matrix:10 -msgid "The two-dimentional tensor representing the operator." +#: of torch.nn.modules.module.Module.named_parameters:4 +msgid "prefix to prepend to all parameter names." msgstr "" -#: of tensorcircuit.quantum.QuOperator.from_tensor:1 +#: of torch.nn.modules.module.Module.named_parameters:6 +#: torch.nn.modules.module.Module.parameters:5 msgid "" -"Construct a `QuOperator` directly from a single tensor. This first wraps " -"the tensor in a `Node`, then constructs the `QuOperator` from that " -"`Node`." +"if True, then yields parameters of this module and all submodules. " +"Otherwise, yields only parameters that are direct members of this module." msgstr "" -#: of tensorcircuit.quantum.QuOperator.from_tensor:28 -msgid "The tensor." +#: of torch.nn.modules.module.Module.named_parameters:10 +msgid "" +"whether to remove the duplicated parameters in the result. Defaults to " +"True." msgstr "" -#: of tensorcircuit.quantum.QuOperator.from_tensor:30 -msgid "The axis indices of `tensor` to use as `out_edges`." +#: of torch.nn.modules.module.Module.named_parameters:14 +msgid "*(str, Parameter)* -- Tuple containing the name and parameter" msgstr "" -#: of tensorcircuit.quantum.QuOperator.from_tensor:32 -msgid "The axis indices of `tensor` to use as `in_edges`." +#: of torch.nn.modules.module.Module.parameters:1 +msgid "Returns an iterator over module parameters." msgstr "" -#: of tensorcircuit.quantum.QuOperator.from_tensor:34 -msgid "The new operator." +#: of torch.nn.modules.module.Module.parameters:3 +msgid "This is typically passed to an optimizer." msgstr "" -#: of tensorcircuit.quantum.QuOperator.is_adjoint_vector:1 -msgid "" -"Returns a bool indicating if QuOperator is an adjoint vector. Examples " -"can be found in the `QuOperator.from_tensor`." +#: of torch.nn.modules.module.Module.parameters:10 +msgid "*Parameter* -- module parameter" msgstr "" -#: of tensorcircuit.quantum.QuOperator.is_scalar:1 +#: of torch.nn.modules.module.Module.register_backward_hook:1 +#: torch.nn.modules.module.Module.register_full_backward_hook:1 +msgid "Registers a backward hook on the module." +msgstr "" + +#: of torch.nn.modules.module.Module.register_backward_hook:3 msgid "" -"Returns a bool indicating if QuOperator is a scalar. Examples can be " -"found in the `QuOperator.from_tensor`." +"This function is deprecated in favor of " +":meth:`~torch.nn.Module.register_full_backward_hook` and the behavior of " +"this function will change in future versions." msgstr "" -#: of tensorcircuit.quantum.QuOperator.is_vector:1 +#: of torch.nn.modules.module.Module.register_backward_hook:6 +#: torch.nn.modules.module.Module.register_forward_hook:37 +#: torch.nn.modules.module.Module.register_forward_pre_hook:40 +#: torch.nn.modules.module.Module.register_full_backward_hook:39 +#: torch.nn.modules.module.Module.register_full_backward_pre_hook:34 +#: torch.nn.modules.module.Module.register_load_state_dict_post_hook:21 msgid "" -"Returns a bool indicating if QuOperator is a vector. Examples can be " -"found in the `QuOperator.from_tensor`." +"a handle that can be used to remove the added hook by calling " +"``handle.remove()``" msgstr "" -#: of tensorcircuit.quantum.QuOperator.nodes:1 -msgid "All tensor-network nodes involved in the operator." +#: of torch.nn.modules.module.Module.register_backward_hook:8 +#: torch.nn.modules.module.Module.register_forward_hook:39 +#: torch.nn.modules.module.Module.register_forward_pre_hook:42 +#: torch.nn.modules.module.Module.register_full_backward_hook:41 +#: torch.nn.modules.module.Module.register_full_backward_pre_hook:36 +#: torch.nn.modules.module.Module.register_load_state_dict_post_hook:23 +msgid ":class:`torch.utils.hooks.RemovableHandle`" msgstr "" -#: of tensorcircuit.quantum.QuOperator.norm:1 -msgid "" -"The norm of the operator. This is the 2-norm (also known as the Frobenius" -" or Hilbert-Schmidt norm)." +#: of torch.nn.modules.module.Module.register_buffer:1 +msgid "Adds a buffer to the module." msgstr "" -#: of tensorcircuit.quantum.QuOperator.partial_trace:1 +#: of torch.nn.modules.module.Module.register_buffer:3 msgid "" -"The partial trace of the operator. Subsystems to trace out are supplied " -"as indices, so that dangling edges are connected to each other as: " -"`out_edges[i] ^ in_edges[i] for i in subsystems_to_trace_out` This does " -"not modify the original network. The original ordering of the remaining " -"subsystems is maintained." +"This is typically used to register a buffer that should not to be " +"considered a model parameter. For example, BatchNorm's ``running_mean`` " +"is not a parameter, but is part of the module's state. Buffers, by " +"default, are persistent and will be saved alongside parameters. This " +"behavior can be changed by setting :attr:`persistent` to ``False``. The " +"only difference between a persistent buffer and a non-persistent buffer " +"is that the latter will not be a part of this module's " +":attr:`state_dict`." msgstr "" -#: of tensorcircuit.quantum.QuOperator.partial_trace:10 -msgid "A new QuOperator or QuScalar representing the result." +#: of torch.nn.modules.module.Module.register_buffer:12 +msgid "Buffers can be accessed as attributes using given names." msgstr "" -#: of tensorcircuit.quantum.QuOperator.tensor_product:1 +#: of torch.nn.modules.module.Module.register_buffer:14 msgid "" -"Tensor product with another operator. Given two operators `A` and `B`, " -"produces a new operator `AB` representing :math:`A ⊗ B`. The `out_edges` " -"(`in_edges`) of `AB` is simply the concatenation of the `out_edges` " -"(`in_edges`) of `A.copy()` with that of `B.copy()`: `new_out_edges = " -"[*out_edges_A_copy, *out_edges_B_copy]` `new_in_edges = " -"[*in_edges_A_copy, *in_edges_B_copy]`" +"name of the buffer. The buffer can be accessed from this module using the" +" given name" msgstr "" -#: of tensorcircuit.quantum.QuOperator.tensor_product:20 -msgid "The other operator (`B`)." +#: of torch.nn.modules.module.Module.register_buffer:17 +msgid "" +"buffer to be registered. If ``None``, then operations that run on " +"buffers, such as :attr:`cuda`, are ignored. If ``None``, the buffer is " +"**not** included in the module's :attr:`state_dict`." msgstr "" -#: of tensorcircuit.quantum.QuOperator.tensor_product:22 -msgid "The result (`AB`)." +#: of torch.nn.modules.module.Module.register_buffer:21 +msgid "whether the buffer is part of this module's :attr:`state_dict`." msgstr "" -#: of tensorcircuit.quantum.QuOperator.trace:1 -msgid "The trace of the operator." +#: of torch.nn.modules.module.Module.register_forward_hook:1 +msgid "Registers a forward hook on the module." msgstr "" -#: of tensorcircuit.quantum.QuScalar:1 -msgid "Represents a scalar via a tensor network." +#: of torch.nn.modules.module.Module.register_forward_hook:3 +msgid "" +"The hook will be called every time after :func:`forward` has computed an " +"output." msgstr "" -#: of tensorcircuit.quantum.QuScalar.__init__:1 +#: of torch.nn.modules.module.Module.register_forward_hook:5 msgid "" -"Constructs a new `QuScalar` from a tensor network. This encapsulates an " -"existing tensor network, interpreting it as a scalar." +"If ``with_kwargs`` is ``False`` or not specified, the input contains only" +" the positional arguments given to the module. Keyword arguments won't be" +" passed to the hooks and only to the ``forward``. The hook can modify the" +" output. It can modify the input inplace but it will not have effect on " +"forward since this is called after :func:`forward` is called. The hook " +"should have the following signature::" msgstr "" -#: of tensorcircuit.quantum.QuScalar.__init__:4 +#: of torch.nn.modules.module.Module.register_forward_hook:14 msgid "" -"Nodes used to refer to the tensor network (need not be exhaustive - one " -"node from each disconnected subnetwork is sufficient)." +"If ``with_kwargs`` is ``True``, the forward hook will be passed the " +"``kwargs`` given to the forward function and be expected to return the " +"output possibly modified. The hook should have the following signature::" msgstr "" -#: of tensorcircuit.quantum.QuScalar.from_tensor:1 +#: of torch.nn.modules.module.Module.register_forward_hook:20 +#: torch.nn.modules.module.Module.register_forward_pre_hook:23 +msgid "The user defined hook to be registered." +msgstr "" + +#: of torch.nn.modules.module.Module.register_forward_hook:22 msgid "" -"Construct a `QuScalar` directly from a single tensor. This first wraps " -"the tensor in a `Node`, then constructs the `QuScalar` from that `Node`." +"If ``True``, the provided ``hook`` will be fired before all existing " +"``forward`` hooks on this :class:`torch.nn.modules.Module`. Otherwise, " +"the provided ``hook`` will be fired after all existing ``forward`` hooks " +"on this :class:`torch.nn.modules.Module`. Note that global ``forward`` " +"hooks registered with :func:`register_module_forward_hook` will fire " +"before all hooks registered by this method. Default: ``False``" msgstr "" -#: of tensorcircuit.quantum.QuScalar.from_tensor:22 -msgid "The tensor for constructing a new QuScalar." +#: of torch.nn.modules.module.Module.register_forward_hook:32 +msgid "" +"If ``True``, the ``hook`` will be passed the kwargs given to the forward " +"function. Default: ``False``" msgstr "" -#: of tensorcircuit.quantum.QuScalar.from_tensor:24 -msgid "The new constructed QuScalar from the given tensor." +#: of torch.nn.modules.module.Module.register_forward_pre_hook:1 +msgid "Registers a forward pre-hook on the module." msgstr "" -#: of tensorcircuit.quantum.QuVector:1 -msgid "Represents a (column) vector via a tensor network." +#: of torch.nn.modules.module.Module.register_forward_pre_hook:3 +msgid "The hook will be called every time before :func:`forward` is invoked." msgstr "" -#: of tensorcircuit.quantum.QuVector.__init__:1 +#: of torch.nn.modules.module.Module.register_forward_pre_hook:6 msgid "" -"Constructs a new `QuVector` from a tensor network. This encapsulates an " -"existing tensor network, interpreting it as a (column) vector." +"If ``with_kwargs`` is false or not specified, the input contains only the" +" positional arguments given to the module. Keyword arguments won't be " +"passed to the hooks and only to the ``forward``. The hook can modify the " +"input. User can either return a tuple or a single modified value in the " +"hook. We will wrap the value into a tuple if a single value is returned " +"(unless that value is already a tuple). The hook should have the " +"following signature::" msgstr "" -#: of tensorcircuit.quantum.QuVector.from_tensor:1 +#: of torch.nn.modules.module.Module.register_forward_pre_hook:16 msgid "" -"Construct a `QuVector` directly from a single tensor. This first wraps " -"the tensor in a `Node`, then constructs the `QuVector` from that `Node`." +"If ``with_kwargs`` is true, the forward pre-hook will be passed the " +"kwargs given to the forward function. And if the hook modifies the input," +" both the args and kwargs should be returned. The hook should have the " +"following signature::" msgstr "" -#: of tensorcircuit.quantum.QuVector.from_tensor:28 -msgid "The tensor for constructing a \"QuVector\"." +#: of torch.nn.modules.module.Module.register_forward_pre_hook:25 +msgid "" +"If true, the provided ``hook`` will be fired before all existing " +"``forward_pre`` hooks on this :class:`torch.nn.modules.Module`. " +"Otherwise, the provided ``hook`` will be fired after all existing " +"``forward_pre`` hooks on this :class:`torch.nn.modules.Module`. Note that" +" global ``forward_pre`` hooks registered with " +":func:`register_module_forward_pre_hook` will fire before all hooks " +"registered by this method. Default: ``False``" msgstr "" -#: of tensorcircuit.quantum.QuVector.from_tensor:30 +#: of torch.nn.modules.module.Module.register_forward_pre_hook:35 msgid "" -"Sequence of integer indices specifying the order in which to interpret " -"the axes as subsystems (output edges). If not specified, the axes are " -"taken in ascending order." +"If true, the ``hook`` will be passed the kwargs given to the forward " +"function. Default: ``False``" msgstr "" -#: of tensorcircuit.quantum.QuVector.from_tensor:34 -msgid "The new constructed QuVector from the given tensor." +#: of torch.nn.modules.module.Module.register_full_backward_hook:3 +msgid "" +"The hook will be called every time the gradients with respect to a module" +" are computed, i.e. the hook will execute if and only if the gradients " +"with respect to module outputs are computed. The hook should have the " +"following signature::" msgstr "" -#: of tensorcircuit.quantum.QuVector.projector:4 +#: of torch.nn.modules.module.Module.register_full_backward_hook:10 msgid "" -"Set :math:`A` is the operator in matrix form, then the projector of " -"operator is defined as: :math:`A A^\\dagger`" +"The :attr:`grad_input` and :attr:`grad_output` are tuples that contain " +"the gradients with respect to the inputs and outputs respectively. The " +"hook should not modify its arguments, but it can optionally return a new " +"gradient with respect to the input that will be used in place of " +":attr:`grad_input` in subsequent computations. :attr:`grad_input` will " +"only correspond to the inputs given as positional arguments and all kwarg" +" arguments are ignored. Entries in :attr:`grad_input` and " +":attr:`grad_output` will be ``None`` for all non-Tensor arguments." msgstr "" -#: of tensorcircuit.quantum.QuVector.reduced_density:5 -msgid "\\mathrm{Tr}_{subsystems}(A A^\\dagger)" +#: of torch.nn.modules.module.Module.register_full_backward_hook:19 +#: torch.nn.modules.module.Module.register_full_backward_pre_hook:14 +msgid "" +"For technical reasons, when this hook is applied to a Module, its forward" +" function will receive a view of each Tensor passed to the Module. " +"Similarly the caller will receive a view of each Tensor returned by the " +"Module's forward function." msgstr "" -#: of tensorcircuit.quantum.check_spaces:1 +#: of torch.nn.modules.module.Module.register_full_backward_hook:24 msgid "" -"Check the vector spaces represented by two lists of edges are compatible." -" The number of edges must be the same and the dimensions of each pair of " -"edges must match. Otherwise, an exception is raised." +"Modifying inputs or outputs inplace is not allowed when using backward " +"hooks and will raise an error." msgstr "" -#: of tensorcircuit.quantum.check_spaces:5 tensorcircuit.quantum.check_spaces:7 -msgid "List of edges representing a many-body Hilbert space." +#: of torch.nn.modules.module.Module.register_full_backward_hook:27 +#: torch.nn.modules.module.Module.register_full_backward_pre_hook:22 +msgid "The user-defined hook to be registered." msgstr "" -#: of tensorcircuit.quantum.check_spaces:10 +#: of torch.nn.modules.module.Module.register_full_backward_hook:29 msgid "" -"Hilbert-space mismatch: \"Cannot connect {} subsystems with {} " -"subsystems\", or \"Input dimension {} != output dimension {}.\"" +"If true, the provided ``hook`` will be fired before all existing " +"``backward`` hooks on this :class:`torch.nn.modules.Module`. Otherwise, " +"the provided ``hook`` will be fired after all existing ``backward`` hooks" +" on this :class:`torch.nn.modules.Module`. Note that global ``backward`` " +"hooks registered with :func:`register_module_full_backward_hook` will " +"fire before all hooks registered by this method." msgstr "" -#: of tensorcircuit.quantum.correlation_from_counts:1 -msgid "" -"Compute :math:`\\prod_{i\\in \\text{index}} s_i`, where the probability " -"for each bitstring is given as a vector ``results``." +#: of torch.nn.modules.module.Module.register_full_backward_pre_hook:1 +msgid "Registers a backward pre-hook on the module." msgstr "" -#: of tensorcircuit.quantum.correlation_from_counts:12 -msgid "list of int, indicating the position in the bitstring" +#: of torch.nn.modules.module.Module.register_full_backward_pre_hook:3 +msgid "" +"The hook will be called every time the gradients for the module are " +"computed. The hook should have the following signature::" msgstr "" -#: of tensorcircuit.quantum.correlation_from_counts:14 -msgid "probability vector of shape 2^n" +#: of torch.nn.modules.module.Module.register_full_backward_pre_hook:8 +msgid "" +"The :attr:`grad_output` is a tuple. The hook should not modify its " +"arguments, but it can optionally return a new gradient with respect to " +"the output that will be used in place of :attr:`grad_output` in " +"subsequent computations. Entries in :attr:`grad_output` will be ``None`` " +"for all non-Tensor arguments." msgstr "" -#: of tensorcircuit.quantum.correlation_from_counts:16 -msgid "Correlation expectation from measurement shots." +#: of torch.nn.modules.module.Module.register_full_backward_pre_hook:19 +msgid "" +"Modifying inputs inplace is not allowed when using backward hooks and " +"will raise an error." msgstr "" -#: of tensorcircuit.quantum.double_state:1 -msgid "Compute the double state of the given Hamiltonian operator ``h``." +#: of torch.nn.modules.module.Module.register_full_backward_pre_hook:24 +msgid "" +"If true, the provided ``hook`` will be fired before all existing " +"``backward_pre`` hooks on this :class:`torch.nn.modules.Module`. " +"Otherwise, the provided ``hook`` will be fired after all existing " +"``backward_pre`` hooks on this :class:`torch.nn.modules.Module`. Note " +"that global ``backward_pre`` hooks registered with " +":func:`register_module_full_backward_pre_hook` will fire before all hooks" +" registered by this method." msgstr "" -#: of tensorcircuit.quantum.double_state:3 tensorcircuit.quantum.gibbs_state:3 -#: tensorcircuit.quantum.truncated_free_energy:5 -msgid "Hamiltonian operator in form of Tensor." +#: of torch.nn.modules.module.Module.register_load_state_dict_post_hook:1 +msgid "" +"Registers a post hook to be run after module's ``load_state_dict`` is " +"called." msgstr "" -#: of tensorcircuit.quantum.double_state:5 tensorcircuit.quantum.free_energy:17 -#: tensorcircuit.quantum.gibbs_state:5 -#: tensorcircuit.quantum.renyi_free_energy:16 -#: tensorcircuit.quantum.truncated_free_energy:7 -msgid "Constant for the optimization, default is 1." +#: of torch.nn.modules.module.Module.register_load_state_dict_post_hook:5 +msgid "It should have the following signature::" msgstr "" -#: of tensorcircuit.quantum.double_state:7 -msgid "The double state of ``h`` with the given ``beta``." +#: of torch.nn.modules.module.Module.register_load_state_dict_post_hook:5 +msgid "hook(module, incompatible_keys) -> None" msgstr "" -#: of tensorcircuit.quantum.eliminate_identities:1 +#: of torch.nn.modules.module.Module.register_load_state_dict_post_hook:7 msgid "" -"Eliminates any connected CopyNodes that are identity matrices. This will " -"modify the network represented by `nodes`. Only identities that are " -"connected to other nodes are eliminated." +"The ``module`` argument is the current module that this hook is " +"registered on, and the ``incompatible_keys`` argument is a ``NamedTuple``" +" consisting of attributes ``missing_keys`` and ``unexpected_keys``. " +"``missing_keys`` is a ``list`` of ``str`` containing the missing keys and" +" ``unexpected_keys`` is a ``list`` of ``str`` containing the unexpected " +"keys." msgstr "" -#: of tensorcircuit.quantum.eliminate_identities:5 -msgid "Collection of nodes to search." +#: of torch.nn.modules.module.Module.register_load_state_dict_post_hook:13 +msgid "The given incompatible_keys can be modified inplace if needed." msgstr "" -#: of tensorcircuit.quantum.eliminate_identities:7 +#: of torch.nn.modules.module.Module.register_load_state_dict_post_hook:15 msgid "" -"The Dictionary mapping remaining Nodes to any replacements, Dictionary " -"specifying all dangling-edge replacements." +"Note that the checks performed when calling :func:`load_state_dict` with " +"``strict=True`` are affected by modifications the hook makes to " +"``missing_keys`` or ``unexpected_keys``, as expected. Additions to either" +" set of keys will result in an error being thrown when ``strict=True``, " +"and clearing out both missing and unexpected keys will avoid an error." msgstr "" -#: of tensorcircuit.quantum.entropy:1 -msgid "Compute the entropy from the given density matrix ``rho``." +#: of torch.nn.modules.module.Module.register_module:1 +msgid "Alias for :func:`add_module`." msgstr "" -#: of tensorcircuit.quantum.entropy:30 tensorcircuit.quantum.free_energy:13 -#: tensorcircuit.quantum.renyi_entropy:3 -#: tensorcircuit.quantum.renyi_free_energy:12 -msgid "The density matrix in form of Tensor or QuOperator." +#: of torch.nn.modules.module.Module.register_parameter:1 +msgid "Adds a parameter to the module." msgstr "" -#: of tensorcircuit.quantum.entropy:32 tensorcircuit.quantum.free_energy:19 -msgid "Epsilon, default is 1e-12." +#: of torch.nn.modules.module.Module.register_parameter:3 +msgid "The parameter can be accessed as an attribute using given name." msgstr "" -#: of tensorcircuit.quantum.entropy:34 -msgid "Entropy on the given density matrix." +#: of torch.nn.modules.module.Module.register_parameter:5 +msgid "" +"name of the parameter. The parameter can be accessed from this module " +"using the given name" msgstr "" -#: of tensorcircuit.quantum.fidelity:1 -msgid "Return fidelity scalar between two states rho and rho0." +#: of torch.nn.modules.module.Module.register_parameter:8 +msgid "" +"parameter to be added to the module. If ``None``, then operations that " +"run on parameters, such as :attr:`cuda`, are ignored. If ``None``, the " +"parameter is **not** included in the module's :attr:`state_dict`." msgstr "" -#: of tensorcircuit.quantum.fidelity:3 -msgid "\\operatorname{Tr}(\\sqrt{\\sqrt{rho} rho_0 \\sqrt{rho}})" +#: of torch.nn.modules.module.Module.register_state_dict_pre_hook:1 +msgid "" +"These hooks will be called with arguments: ``self``, ``prefix``, and " +"``keep_vars`` before calling ``state_dict`` on ``self``. The registered " +"hooks can be used to perform pre-processing before the ``state_dict`` " +"call is made." msgstr "" -#: of tensorcircuit.quantum.fidelity:7 tensorcircuit.quantum.fidelity:9 -#: tensorcircuit.quantum.mutual_information:3 tensorcircuit.quantum.taylorlnm:3 -#: tensorcircuit.quantum.trace_distance:3 -#: tensorcircuit.quantum.trace_distance:5 -#: tensorcircuit.quantum.truncated_free_energy:3 -msgid "The density matrix in form of Tensor." +#: of torch.nn.modules.module.Module.requires_grad_:1 +msgid "Change if autograd should record operations on parameters in this module." msgstr "" -#: of tensorcircuit.quantum.fidelity:11 -msgid "The sqrtm of a Hermitian matrix ``a``." +#: of torch.nn.modules.module.Module.requires_grad_:4 +msgid "" +"This method sets the parameters' :attr:`requires_grad` attributes in-" +"place." msgstr "" -#: of tensorcircuit.quantum.free_energy:1 -msgid "Compute the free energy of the given density matrix." +#: of torch.nn.modules.module.Module.requires_grad_:7 +msgid "" +"This method is helpful for freezing part of the module for finetuning or " +"training parts of a model individually (e.g., GAN training)." msgstr "" -#: of tensorcircuit.quantum.free_energy:15 -#: tensorcircuit.quantum.renyi_free_energy:14 -msgid "Hamiltonian operator in form of Tensor or QuOperator." +#: of torch.nn.modules.module.Module.requires_grad_:10 +msgid "" +"See :ref:`locally-disable-grad-doc` for a comparison between " +"`.requires_grad_()` and several similar mechanisms that may be confused " +"with it." msgstr "" -#: of tensorcircuit.quantum.free_energy:22 -msgid "The free energy of the given density matrix with the Hamiltonian operator." +#: of torch.nn.modules.module.Module.requires_grad_:13 +msgid "" +"whether autograd should record operations on parameters in this module. " +"Default: ``True``." msgstr "" -#: of tensorcircuit.quantum.generate_local_hamiltonian:1 +#: of torch.nn.modules.module.Module.set_extra_state:1 msgid "" -"Generate a local Hamiltonian operator based on the given sequence of " -"Tensor. Note: further jit is recommended. For large Hilbert space, sparse" -" Hamiltonian is recommended" +"This function is called from :func:`load_state_dict` to handle any extra " +"state found within the `state_dict`. Implement this function and a " +"corresponding :func:`get_extra_state` for your module if you need to " +"store extra state within its `state_dict`." msgstr "" -#: of tensorcircuit.quantum.generate_local_hamiltonian:5 -msgid "A sequence of Tensor." +#: of torch.nn.modules.module.Module.set_extra_state:6 +msgid "Extra state from the `state_dict`" msgstr "" -#: of tensorcircuit.quantum.generate_local_hamiltonian:7 -msgid "Return Hamiltonian operator in form of matrix, defaults to True." +#: of torch.nn.modules.module.Module.share_memory:1 +msgid "See :meth:`torch.Tensor.share_memory_`" msgstr "" -#: of tensorcircuit.quantum.generate_local_hamiltonian:9 -msgid "The Hamiltonian operator in form of QuOperator or matrix." +#: of torch.nn.modules.module.Module.state_dict:1 +msgid "" +"Returns a dictionary containing references to the whole state of the " +"module." msgstr "" -#: of tensorcircuit.quantum.gibbs_state:1 -msgid "Compute the Gibbs state of the given Hamiltonian operator ``h``." +#: of torch.nn.modules.module.Module.state_dict:3 +msgid "" +"Both parameters and persistent buffers (e.g. running averages) are " +"included. Keys are corresponding parameter and buffer names. Parameters " +"and buffers set to ``None`` are not included." msgstr "" -#: of tensorcircuit.quantum.gibbs_state:7 -msgid "The Gibbs state of ``h`` with the given ``beta``." +#: of torch.nn.modules.module.Module.state_dict:8 +msgid "" +"The returned object is a shallow copy. It contains references to the " +"module's parameters and buffers." msgstr "" -#: of tensorcircuit.quantum.heisenberg_hamiltonian:1 -msgid "Generate Heisenberg Hamiltonian with possible external fields." +#: of torch.nn.modules.module.Module.state_dict:12 +msgid "" +"Currently ``state_dict()`` also accepts positional arguments for " +"``destination``, ``prefix`` and ``keep_vars`` in order. However, this is " +"being deprecated and keyword arguments will be enforced in future " +"releases." msgstr "" -#: of tensorcircuit.quantum.heisenberg_hamiltonian:12 -msgid "input circuit graph" +#: of torch.nn.modules.module.Module.state_dict:18 +msgid "" +"Please avoid the use of argument ``destination`` as it is not designed " +"for end-users." msgstr "" -#: of tensorcircuit.quantum.heisenberg_hamiltonian:14 -msgid "zz coupling, default is 1.0" +#: of torch.nn.modules.module.Module.state_dict:21 +msgid "" +"If provided, the state of module will be updated into the dict and the " +"same object is returned. Otherwise, an ``OrderedDict`` will be created " +"and returned. Default: ``None``." msgstr "" -#: of tensorcircuit.quantum.heisenberg_hamiltonian:16 -msgid "xx coupling, default is 1.0" +#: of torch.nn.modules.module.Module.state_dict:26 +msgid "" +"a prefix added to parameter and buffer names to compose the keys in " +"state_dict. Default: ``''``." msgstr "" -#: of tensorcircuit.quantum.heisenberg_hamiltonian:18 -msgid "yy coupling, default is 1.0" +#: of torch.nn.modules.module.Module.state_dict:29 +msgid "" +"by default the :class:`~torch.Tensor` s returned in the state dict are " +"detached from autograd. If it's set to ``True``, detaching will not be " +"performed. Default: ``False``." msgstr "" -#: of tensorcircuit.quantum.heisenberg_hamiltonian:20 -msgid "External field on z direction, default is 0.0" +#: of torch.nn.modules.module.Module.state_dict:35 +msgid "a dictionary containing a whole state of the module" msgstr "" -#: of tensorcircuit.quantum.heisenberg_hamiltonian:22 -msgid "External field on y direction, default is 0.0" +#: of torch.nn.modules.module.Module.to:1 +msgid "Moves and/or casts the parameters and buffers." msgstr "" -#: of tensorcircuit.quantum.heisenberg_hamiltonian:24 -msgid "External field on x direction, default is 0.0" +#: of torch.nn.modules.module.Module.to:3 +msgid "This can be called as" msgstr "" -#: of tensorcircuit.quantum.heisenberg_hamiltonian:26 -msgid "Whether to return sparse Hamiltonian operator, default is True." +#: of torch.nn.modules.module.Module.to:17 +msgid "" +"Its signature is similar to :meth:`torch.Tensor.to`, but only accepts " +"floating point or complex :attr:`dtype`\\ s. In addition, this method " +"will only cast the floating point or complex parameters and buffers to " +":attr:`dtype` (if given). The integral parameters and buffers will be " +"moved :attr:`device`, if that is given, but with dtypes unchanged. When " +":attr:`non_blocking` is set, it tries to convert/move asynchronously with" +" respect to the host if possible, e.g., moving CPU Tensors with pinned " +"memory to CUDA devices." msgstr "" -#: of tensorcircuit.quantum.heisenberg_hamiltonian:28 -msgid "whether return the matrix in numpy or tensorflow form" +#: of torch.nn.modules.module.Module.to:26 +msgid "See below for examples." msgstr "" -#: of tensorcircuit.quantum.heisenberg_hamiltonian:31 -msgid "Hamiltonian measurements" +#: of torch.nn.modules.module.Module.to:31 +msgid "the desired device of the parameters and buffers in this module" msgstr "" -#: of tensorcircuit.quantum.identity:1 +#: of torch.nn.modules.module.Module.to:34 msgid "" -"Construct a 'QuOperator' representing the identity on a given space. " -"Internally, this is done by constructing 'CopyNode's for each edge, with " -"dimension according to 'space'." +"the desired floating point or complex dtype of the parameters and buffers" +" in this module" msgstr "" -#: of tensorcircuit.quantum.identity:26 +#: of torch.nn.modules.module.Module.to:37 msgid "" -"A sequence of integers for the dimensions of the tensor product factors " -"of the space (the edges in the tensor network)." +"Tensor whose dtype and device are the desired dtype and device for all " +"parameters and buffers in this module" msgstr "" -#: of tensorcircuit.quantum.identity:29 +#: of torch.nn.modules.module.Module.to:40 msgid "" -"The data type by np.* (for conversion to dense). defaults None to tc " -"dtype." +"the desired memory format for 4D parameters and buffers in this module " +"(keyword only argument)" msgstr "" -#: of tensorcircuit.quantum.identity:31 -msgid "The desired identity operator." +#: of torch.nn.modules.module.Module.to:48 +msgid "Examples::" msgstr "" -#: of tensorcircuit.quantum.measurement_counts:1 +#: of torch.nn.modules.module.Module.to_empty:1 msgid "" -"Simulate the measuring of each qubit of ``p`` in the computational basis," -" thus producing output like that of ``qiskit``." +"Moves the parameters and buffers to the specified device without copying " +"storage." msgstr "" -#: of tensorcircuit.quantum.measurement_counts:14 -msgid "" -"The quantum state, assumed to be normalized, as either a ket or density " -"operator." +#: of torch.nn.modules.module.Module.to_empty:3 +msgid "The desired device of the parameters and buffers in this module." msgstr "" -#: of tensorcircuit.quantum.measurement_counts:16 -msgid "The number of counts to perform." +#: of torch.nn.modules.module.Module.train:1 +msgid "Sets the module in training mode." msgstr "" -#: of tensorcircuit.quantum.measurement_counts:18 +#: of torch.nn.modules.module.Module.train:8 msgid "" -"Defaults True. The bool indicating whether the return form is in the form" -" of two array or one of the same length as the ``state`` (if " -"``sparse=False``)." +"whether to set training mode (``True``) or evaluation mode (``False``). " +"Default: ``True``." msgstr "" -#: of tensorcircuit.quantum.measurement_counts:21 -msgid "The counts for each bit string measured." +#: of torch.nn.modules.module.Module.type:1 +msgid "Casts all parameters and buffers to :attr:`dst_type`." msgstr "" -#: of tensorcircuit.quantum.mutual_information:1 -msgid "Mutual information between AB subsystem described by ``cut``." +#: of torch.nn.modules.module.Module.type:6 +msgid "the desired type" msgstr "" -#: of tensorcircuit.quantum.mutual_information:5 -msgid "The AB subsystem." +#: of torch.nn.modules.module.Module.xpu:1 +msgid "Moves all model parameters and buffers to the XPU." msgstr "" -#: of tensorcircuit.quantum.mutual_information:7 -msgid "The mutual information between AB subsystem described by ``cut``." +#: of torch.nn.modules.module.Module.xpu:3 +msgid "" +"This also makes associated parameters and buffers different objects. So " +"it should be called before constructing optimizer if the module will live" +" on XPU while being optimized." msgstr "" -#: of tensorcircuit.quantum.quantum_constructor:1 +#: of torch.nn.modules.module.Module.zero_grad:1 msgid "" -"Constructs an appropriately specialized QuOperator. If there are no " -"edges, creates a QuScalar. If the are only output (input) edges, creates " -"a QuVector (QuAdjointVector). Otherwise creates a QuOperator." +"Sets gradients of all model parameters to zero. See similar function " +"under :class:`torch.optim.Optimizer` for more context." msgstr "" -#: of tensorcircuit.quantum.quantum_constructor:48 -msgid "A list of output edges." +#: of torch.nn.modules.module.Module.zero_grad:4 +msgid "" +"instead of setting to zero, set the grads to None. See " +":meth:`torch.optim.Optimizer.zero_grad` for details." msgstr "" -#: of tensorcircuit.quantum.quantum_constructor:50 -msgid "A list of input edges." +#: of tensorcircuit.torchnn.QuantumNet:1 +msgid "Bases: :py:class:`~torch.nn.modules.module.Module`" msgstr "" -#: of tensorcircuit.quantum.quantum_constructor:52 +#: ../../source/api/translation.rst:2 +msgid "tensorcircuit.translation" +msgstr "" + +#: of tensorcircuit.translation:1 +msgid "Circuit object translation in different packages" +msgstr "" + +#: of tensorcircuit.translation.eqasm2tc:1 +msgid "Translation qexe/eqasm instruction to tensorcircuit Circuit object" +msgstr "" + +#: of tensorcircuit.translation.eqasm2tc:7 +msgid "lines of ignored code at the head and the tail, defaults to (6, 1)" +msgstr "" + +#: of tensorcircuit.translation.perm_matrix:1 msgid "" -"Reference nodes for the tensor network (needed if there is a. scalar " -"component)." +"Generate a permutation matrix P. Due to the different convention or " +"qubits' order in qiskit and tensorcircuit, the unitary represented by the" +" same circuit is different. They are related by this permutation matrix " +"P: P @ U_qiskit @ P = U_tc" msgstr "" -#: of tensorcircuit.quantum.quantum_constructor:55 -msgid "Edges to ignore when checking the dimensionality of the tensor network." +#: of tensorcircuit.translation.perm_matrix:7 +#: tensorcircuit.translation.qir2cirq:15 +#: tensorcircuit.translation.qir2qiskit:16 +#: tensorcircuit.translation.qiskit2tc:14 tensorcircuit.vis.qir2tex:12 +msgid "# of qubits" msgstr "" -#: of tensorcircuit.quantum.quantum_constructor:58 -msgid "The new created QuOperator object." +#: of tensorcircuit.translation.perm_matrix:9 +msgid "The permutation matrix P" msgstr "" -#: of tensorcircuit.quantum.quimb2qop:1 -msgid "Convert MPO in Quimb package to QuOperator." +#: of tensorcircuit.translation.qir2cirq:1 +msgid "" +"Generate a cirq circuit using the quantum intermediate representation " +"(qir) in tensorcircuit." msgstr "" -#: of tensorcircuit.quantum.quimb2qop:3 -msgid "MPO in the form of Quimb package" +#: of tensorcircuit.translation.qir2cirq:17 +#: tensorcircuit.translation.qir2qiskit:18 +msgid "" +"The extra quantum IR of tc circuit including measure and reset on " +"hardware, defaults to None" msgstr "" -#: of tensorcircuit.quantum.quimb2qop:5 tensorcircuit.quantum.tn2qop:5 -msgid "MPO in the form of QuOperator" +#: of tensorcircuit.translation.qir2cirq:20 +msgid "qiskit cirq object" msgstr "" -#: of tensorcircuit.quantum.reduced_density_matrix:1 -msgid "Compute the reduced density matrix from the quantum state ``state``." +#: of tensorcircuit.translation.qir2cirq:23 +msgid "" +"#TODO(@erertertet): add default theta to iswap gate add more cirq built-" +"in gate instead of customized add unitary test with tolerance add support" +" of cirq built-in ControlledGate for multiplecontroll support more " +"element in qir, e.g. barrier, measure..." msgstr "" -#: of tensorcircuit.quantum.reduced_density_matrix:3 -msgid "The quantum state in form of Tensor or QuOperator." +#: of tensorcircuit.translation.qir2json:1 +msgid "" +"transform qir to json compatible list of dict where array is replaced by " +"real and imaginary list" msgstr "" -#: of tensorcircuit.quantum.reduced_density_matrix:5 +#: of tensorcircuit.translation.qir2qiskit:1 msgid "" -"the index list that is traced out, if cut is a int, it indicates [0, cut]" -" as the traced out region" +"Generate a qiskit quantum circuit using the quantum intermediate " +"representation (qir) in tensorcircuit." msgstr "" -#: of tensorcircuit.quantum.reduced_density_matrix:8 -msgid "probability decoration, default is None." +#: of tensorcircuit.translation.qir2qiskit:21 +msgid "Circuit initial state in qiskit format" msgstr "" -#: of tensorcircuit.quantum.reduced_density_matrix:10 -msgid "The reduced density matrix." +#: of tensorcircuit.translation.qir2qiskit:23 +msgid "qiskit QuantumCircuit object" msgstr "" -#: of tensorcircuit.quantum.renyi_entropy:1 -msgid "Compute the Rényi entropy of order :math:`k` by given density matrix." +#: of tensorcircuit.translation.qiskit2tc:1 +msgid "Generate a tensorcircuit circuit using the quantum circuit data in qiskit." msgstr "" -#: of tensorcircuit.quantum.renyi_entropy:5 -#: tensorcircuit.quantum.renyi_free_energy:18 -msgid "The order of Rényi entropy, default is 2." +#: of tensorcircuit.translation.qiskit2tc:12 +msgid "Quantum circuit data from qiskit." msgstr "" -#: of tensorcircuit.quantum.renyi_entropy:7 -#: tensorcircuit.quantum.renyi_free_energy:20 -msgid "The :math:`k` th order of Rényi entropy." +#: of tensorcircuit.translation.qiskit2tc:16 +msgid "Input state of the circuit. Default is None." msgstr "" -#: of tensorcircuit.quantum.renyi_free_energy:1 -msgid "" -"Compute the Rényi free energy of the corresponding density matrix and " -"Hamiltonian." +#: of tensorcircuit.translation.qiskit2tc:18 +msgid "``Circuit``, ``DMCircuit`` or ``MPSCircuit``" msgstr "" -#: of tensorcircuit.quantum.spin_by_basis:1 -msgid "" -"Generate all n-bitstrings as an array, each row is a bitstring basis. " -"Return m-th col." +#: of tensorcircuit.translation.qiskit2tc:26 +msgid "A quantum circuit in tensorcircuit" +msgstr "" + +#: of tensorcircuit.translation.qiskit_from_qasm_str_ordered_measure:1 +msgid "" +"qiskit ``from_qasm_str`` method cannot keep the order of measure as the " +"qasm file, we provide this alternative function in case the order of " +"measure instruction matters" msgstr "" -#: of tensorcircuit.quantum.spin_by_basis:9 -msgid "length of a bitstring" +#: of tensorcircuit.translation.qiskit_from_qasm_str_ordered_measure:4 +msgid "open qasm str" msgstr "" -#: of tensorcircuit.quantum.spin_by_basis:11 -msgid "m is transformed as " -"(01>-|10>)" +"A left compression means we will try to shift gates from left to right if" +" possible. Default is false." msgstr "" -#: of tensorcircuit.templates.blocks.Bell_pair_block:3 -msgid "Circuit in" +#: of tensorcircuit.vis.qir2tex:27 +msgid "" +"If true, the tex code will be designed to generate a standalone document." +" Default is false which means the generated tex code is just a quantikz " +"code block." msgstr "" -#: of tensorcircuit.templates.blocks.Bell_pair_block:5 +#: of tensorcircuit.vis.qir2tex:30 msgid "" -"pairs indices for Bell pairs, defaults to None, corresponds to neighbor " -"links" +"If true, a string table of tex code will also be returned. Default is " +"false." msgstr "" -#: of tensorcircuit.templates.blocks.Bell_pair_block:7 -msgid "Circuit out" +#: of tensorcircuit.vis.qir2tex:33 +msgid "" +"Tex code of circuit visualization based on quantikz package. If " +"return_string_table is true, a string table of tex code will also be " +"returned." msgstr "" -#: of tensorcircuit.templates.blocks.example_block:1 +#: of tensorcircuit.vis.render_pdf:1 msgid "" -"The circuit ansatz is firstly one layer of Hadamard gates and then we " -"have ``nlayers`` blocks of :math:`e^{i\\theta Z_iZ_{i+1}}` two-qubit gate" -" in ladder layout, following rx gate." +"Generate the PDF file with given latex string and filename. Latex command" +" and file path can be specified. When notebook is True, convert the " +"output PDF file to image and return a Image object." msgstr "" -#: of tensorcircuit.templates.blocks.example_block:5 -msgid "The circuit" +#: of tensorcircuit.vis.render_pdf:15 +msgid "String of latex content" msgstr "" -#: of tensorcircuit.templates.blocks.example_block:7 -msgid "paramter tensor with 2*nlayer*n elements" +#: of tensorcircuit.vis.render_pdf:17 +msgid "File name, defaults to random UUID `str(uuid4())`" msgstr "" -#: of tensorcircuit.templates.blocks.example_block:9 -msgid "number of ZZ+RX blocks, defaults to 2" +#: of tensorcircuit.vis.render_pdf:19 +msgid "Executable Latex command, defaults to `pdflatex`" msgstr "" -#: of tensorcircuit.templates.blocks.example_block:11 -msgid "whether use SVD split to reduce ZZ gate bond dimension, defaults to False" +#: of tensorcircuit.vis.render_pdf:21 +msgid "File path, defaults to current working place `os.getcwd()`" msgstr "" -#: of tensorcircuit.templates.blocks.example_block:14 -msgid "The circuit with example ansatz attached" +#: of tensorcircuit.vis.render_pdf:25 +msgid "if notebook is True, return `Image` object; otherwise return `None`" msgstr "" -#: of tensorcircuit.templates.blocks.state_centric:1 -msgid "" -"Function decorator wraps the function with the first input and output in " -"the format of circuit, the wrapped function has the first input and the " -"output as the state tensor." -msgstr "" +#~ msgid "" +#~ "This is a method that implementers " +#~ "of subclasses of `Layer` or `Model` " +#~ "can override if they need a " +#~ "state-creation step in-between layer " +#~ "instantiation and layer call." +#~ msgstr "" + +#~ msgid "This is typically used to create the weights of `Layer` subclasses." +#~ msgstr "" + +#~ msgid "" +#~ "Note here that `call()` method in " +#~ "`tf.keras` is little bit different from" +#~ " `keras` API. In `keras` API, you " +#~ "can pass support masking for layers " +#~ "as additional arguments. Whereas `tf.keras`" +#~ " has `compute_mask()` method to support " +#~ "masking." +#~ msgstr "" + +#~ msgid "Modules for DQAS framework" +#~ msgstr "" + +#~ msgid "DQAS framework entrypoint" +#~ msgstr "" + +#~ msgid "Parameters" +#~ msgstr "" + +#~ msgid "" +#~ "function with input of data instance," +#~ " circuit parameters theta and structural" +#~ " paramter k, return tuple of " +#~ "objective value and gradient with " +#~ "respect to theta" +#~ msgstr "" + +#~ msgid "data generator as dataset" +#~ msgstr "" + +#~ msgid "list of operations as primitive operator pool" +#~ msgstr "" + +#~ msgid "the default layer number of the circuit ansatz" +#~ msgstr "" + +#~ msgid "" +#~ "shape of circuit parameter pool, in " +#~ "general p_stp*l, where l is the " +#~ "max number of circuit parameters for " +#~ "op in the operator pool" +#~ msgstr "" + +#~ msgid "the same as p in the most times" +#~ msgstr "" + +#~ msgid "batch size of one epoch" +#~ msgstr "" + +#~ msgid "prethermal update times" +#~ msgstr "" + +#~ msgid "training epochs" +#~ msgstr "" + +#~ msgid "parallel thread number, 0 to disable multiprocessing model by default" +#~ msgstr "" + +#~ msgid "set verbose log to print" +#~ msgstr "" + +#~ msgid "function to output verbose information" +#~ msgstr "" + +#~ msgid "function return intermiediate result for final history list" +#~ msgstr "" + +#~ msgid "cutoff probability to avoid peak distribution" +#~ msgstr "" + +#~ msgid "" +#~ "function accepting list of objective " +#~ "values and return the baseline value " +#~ "used in the next round" +#~ msgstr "" + +#~ msgid "return noise with the same shape as circuit parameter pool" +#~ msgstr "" + +#~ msgid "initial values for circuit parameter pool" +#~ msgstr "" + +#~ msgid "initial values for probabilistic model parameters" +#~ msgstr "" -#: of tensorcircuit.templates.blocks.state_centric:4 -msgid "Function with the fist input and the output as ``Circuit`` object." -msgstr "" +#~ msgid "optimizer for circuit parameters theta" +#~ msgstr "" -#: of tensorcircuit.templates.blocks.state_centric:6 -msgid "" -"Wrapped function with the first input and the output as the state tensor " -"correspondingly." -msgstr "" +#~ msgid "optimizer for model parameters alpha" +#~ msgstr "" -#: ../../source/api/templates/chems.rst:2 -msgid "tensorcircuit.templates.chems" -msgstr "" +#~ msgid "optimizer for circuit parameters in prethermal stage" +#~ msgstr "" -#: of tensorcircuit.templates.chems:1 -msgid "Useful utilities for quantum chemistry related task" -msgstr "" +#~ msgid "fixed structural parameters for prethermal training" +#~ msgstr "" -#: of tensorcircuit.templates.chems.get_ps:1 -msgid "" -"Get Pauli string array and weights array for a qubit Hamiltonian as a sum" -" of Pauli strings defined in openfermion ``QubitOperator``." -msgstr "" +#~ msgid "regularization function for model parameters alpha" +#~ msgstr "" -#: of tensorcircuit.templates.chems.get_ps:4 -msgid "``openfermion.ops.operators.qubit_operator.QubitOperator``" -msgstr "" +#~ msgid "regularization function for circuit parameters theta" +#~ msgstr "" -#: of tensorcircuit.templates.chems.get_ps:6 -msgid "The number of qubits" -msgstr "" +#~ msgid "Returns" +#~ msgstr "" -#: of tensorcircuit.templates.chems.get_ps:8 -msgid "Pauli String array and weights array" -msgstr "" +#~ msgid "" +#~ "The probabilistic model based DQAS, can" +#~ " use extensively for DQAS case for" +#~ " ``NMF`` probabilistic model." +#~ msgstr "" -#: ../../source/api/templates/dataset.rst:2 -msgid "tensorcircuit.templates.dataset" -msgstr "" +#~ msgid "vag func, return loss and nabla lnp" +#~ msgstr "" -#: of tensorcircuit.templates.dataset:1 -msgid "Quantum machine learning related data preprocessing and embedding" -msgstr "" +#~ msgid "keras model" +#~ msgstr "" -#: ../../source/api/templates/graphs.rst:2 -msgid "tensorcircuit.templates.graphs" -msgstr "" +#~ msgid "sample func of logic with keras model input" +#~ msgstr "" -#: of tensorcircuit.templates.graphs:1 -msgid "Some common graphs and lattices" -msgstr "" +#~ msgid "input data pipeline generator" +#~ msgstr "" -#: of tensorcircuit.templates.graphs.Grid2DCoord:1 -msgid "Two-dimensional grid lattice" -msgstr "" +#~ msgid "operation pool" +#~ msgstr "" -#: of tensorcircuit.templates.graphs.Grid2DCoord.__init__:1 -msgid "number of rows" -msgstr "" +#~ msgid "depth for DQAS" +#~ msgstr "" -#: of tensorcircuit.templates.graphs.Grid2DCoord.__init__:3 -msgid "number of cols" -msgstr "" +#~ msgid "parallel kernels" +#~ msgstr "" -#: of tensorcircuit.templates.graphs.Grid2DCoord.all_cols:1 -msgid "return all col edge with 1d index encoding" -msgstr "" +#~ msgid "final loss function in terms of average of sub loss for each circuit" +#~ msgstr "" -#: of tensorcircuit.templates.graphs.Grid2DCoord.all_cols:3 -#: tensorcircuit.templates.graphs.Grid2DCoord.all_rows:3 -msgid "" -"whether to include pbc edges (periodic boundary condition), defaults to " -"False" -msgstr "" +#~ msgid "derivative function for ``loss_func``" +#~ msgstr "" -#: of tensorcircuit.templates.graphs.Grid2DCoord.all_cols:6 -msgid "list of col edge" -msgstr "" +#~ msgid "" +#~ "Call in customized functions and grab" +#~ " variables within DQAS framework function" +#~ " by var name str." +#~ msgstr "" -#: of tensorcircuit.templates.graphs.Grid2DCoord.all_rows:1 -msgid "return all row edge with 1d index encoding" -msgstr "" +#~ msgid "The DQAS framework function" +#~ msgstr "" -#: of tensorcircuit.templates.graphs.Grid2DCoord.all_rows:6 -msgid "list of row edge" -msgstr "" +#~ msgid "Variables within the DQAS framework" +#~ msgstr "" -#: of tensorcircuit.templates.graphs.Grid2DCoord.lattice_graph:1 -msgid "Get the 2D grid lattice in ``nx.Graph`` format" -msgstr "" +#~ msgid "Return type" +#~ msgstr "" -#: of tensorcircuit.templates.graphs.Grid2DCoord.lattice_graph:3 -msgid "" -"whether to include pbc edges (periodic boundary condition), defaults to " -"True" -msgstr "" +#~ msgid "" +#~ "This function works only when nnp " +#~ "has the same shape as stp, i.e." +#~ " one parameter for each op." +#~ msgstr "" -#: of tensorcircuit.templates.graphs.Line1D:1 -msgid "1D chain with ``n`` sites" -msgstr "" +#~ msgid "The kernel for multiprocess to run parallel in DQAS function/" +#~ msgstr "" -#: of tensorcircuit.templates.graphs.Line1D:5 -#: tensorcircuit.templates.measurements.heisenberg_measurements:34 -msgid "[description], defaults to True" -msgstr "" +#~ msgid "" +#~ "parallel variational parameter training and" +#~ " search to avoid local minimum not" +#~ " limited to qaoa setup as the " +#~ "function name indicates, as long as " +#~ "you provided suitable `vag_func`" +#~ msgstr "" -#: ../../source/api/templates/measurements.rst:2 -msgid "tensorcircuit.templates.measurements" -msgstr "" +#~ msgid "data input generator for vag_func" +#~ msgstr "" -#: of tensorcircuit.templates.measurements.any_measurements:1 -msgid "" -"This measurements pattern is specifically suitable for vmap. Parameterize" -" the Pauli string to be measured." -msgstr "" +#~ msgid "vag_kernel" +#~ msgstr "" -#: of tensorcircuit.templates.measurements.any_measurements -#: tensorcircuit.templates.measurements.heisenberg_measurements -#: tensorcircuit.templates.measurements.spin_glass_measurements -msgid "example" -msgstr "" +#~ msgid "number of tries" +#~ msgstr "" -#: of tensorcircuit.templates.measurements.any_measurements:26 -msgid "The circuit to be measured" -msgstr "" +#~ msgid "" +#~ "for optimization problem the input is" +#~ " in general fixed so batch is " +#~ "often 1" +#~ msgstr "" -#: of tensorcircuit.templates.measurements.any_measurements:28 -msgid "" -"parameter tensors determines what Pauli string to be measured, shape is " -"[nwires, 4] if ``onehot`` is False and [nwires] if ``onehot`` is True." -msgstr "" +#~ msgid "number of parallel jobs" +#~ msgstr "" -#: of tensorcircuit.templates.measurements.any_measurements:31 -msgid "" -"defaults to False. If set to be True, structures will first go through " -"onehot procedure." -msgstr "" +#~ msgid "mean value of normal distribution for nnp" +#~ msgstr "" -#: of tensorcircuit.templates.measurements.any_measurements:34 -msgid "The expectation value of given Pauli string by the tensor ``structures``." -msgstr "" +#~ msgid "std deviation of normal distribution for nnp" +#~ msgstr "" -#: of tensorcircuit.templates.measurements.heisenberg_measurements:1 -msgid "" -"Evaluate Heisenberg energy expectation, whose Hamiltonian is defined on " -"the lattice graph ``g`` as follows: (e are edges in graph ``g`` where e1 " -"and e2 are two nodes for edge e and v are nodes in graph ``g``)" -msgstr "" +#~ msgid "Doesn't support prob model DQAS search." +#~ msgstr "" -#: of tensorcircuit.templates.measurements.heisenberg_measurements:4 -msgid "" -"H = \\sum_{e\\in g} w_e (h_{xx} X_{e1}X_{e2} + h_{yy} Y_{e1}Y_{e2} + " -"h_{zz} Z_{e1}Z_{e2})\n" -" + \\sum_{v\\in g} (h_x X_v + h_y Y_v + h_z Z_v)" -msgstr "" +#~ msgid "Modules for graph instance data and more" +#~ msgstr "" -#: of tensorcircuit.templates.measurements.heisenberg_measurements:18 -msgid "Circuit to be measured" -msgstr "" +#~ msgid "```python d = nx.to_dict_of_dicts(g) ```" +#~ msgstr "" -#: of tensorcircuit.templates.measurements.heisenberg_measurements:20 -msgid "Lattice graph defining Heisenberg Hamiltonian" -msgstr "" +#~ msgid "1D PBC chain with n sites." +#~ msgstr "" -#: of tensorcircuit.templates.measurements.heisenberg_measurements:22 -#: tensorcircuit.templates.measurements.heisenberg_measurements:24 -#: tensorcircuit.templates.measurements.heisenberg_measurements:26 -msgid "[description], defaults to 1.0" -msgstr "" +#~ msgid "The number of nodes" +#~ msgstr "" -#: of tensorcircuit.templates.measurements.heisenberg_measurements:28 -#: tensorcircuit.templates.measurements.heisenberg_measurements:30 -#: tensorcircuit.templates.measurements.heisenberg_measurements:32 -msgid "[description], defaults to 0.0" -msgstr "" +#~ msgid "The resulted graph g" +#~ msgstr "" -#: of tensorcircuit.templates.measurements.heisenberg_measurements:36 -msgid "Value of Heisenberg energy" -msgstr "" +#~ msgid "all graphs with m edge out from g" +#~ msgstr "" -#: of tensorcircuit.templates.measurements.mpo_expectation:1 -msgid "" -"Evaluate expectation of operator ``mpo`` defined in ``QuOperator`` MPO " -"format with the output quantum state from circuit ``c``." -msgstr "" +#~ msgid "" +#~ "Generate a reduced graph with given " +#~ "ratio of edges compared to the " +#~ "original graph g." +#~ msgstr "" -#: of tensorcircuit.templates.measurements.mpo_expectation:4 -msgid "The circuit for the output state" -msgstr "" +#~ msgid "The base graph" +#~ msgstr "" -#: of tensorcircuit.templates.measurements.mpo_expectation:6 -msgid "MPO operator" -msgstr "" +#~ msgid "number of edges kept, default half of the edges" +#~ msgstr "" -#: of tensorcircuit.templates.measurements.mpo_expectation:8 -#: tensorcircuit.templates.measurements.operator_expectation:7 -#: tensorcircuit.templates.measurements.sparse_expectation:7 -msgid "a real and scalar tensor of shape [] as the expectation value" -msgstr "" +#~ msgid "The resulted reduced graph" +#~ msgstr "" -#: of tensorcircuit.templates.measurements.operator_expectation:1 -msgid "" -"Evaluate Hamiltonian expectation where ``hamiltonian`` can be dense " -"matrix, sparse matrix or MPO." -msgstr "" +#~ msgid "Split the graph in exactly ``split`` piece evenly." +#~ msgstr "" -#: of tensorcircuit.templates.measurements.operator_expectation:3 -#: tensorcircuit.templates.measurements.sparse_expectation:3 -msgid "The circuit whose output state is used to evaluate the expectation" -msgstr "" +#~ msgid "The mother graph" +#~ msgstr "" -#: of tensorcircuit.templates.measurements.operator_expectation:5 -#: tensorcircuit.templates.measurements.sparse_expectation:5 -msgid "Hamiltonian matrix in COO_sparse_matrix form" -msgstr "" +#~ msgid "The number of the graph we want to divide into, defaults to 2" +#~ msgstr "" -#: of tensorcircuit.templates.measurements.sparse_expectation:1 -msgid "" -"Evaluate Hamiltonian expectation where ``hamiltonian`` is kept in sparse " -"matrix form to save space" -msgstr "" +#~ msgid "List of graph instance of size ``split``" +#~ msgstr "" -#: of tensorcircuit.templates.measurements.spin_glass_measurements:1 -msgid "" -"Compute spin glass energy defined on graph ``g`` expectation for output " -"state of the circuit ``c``. The Hamiltonian to be evaluated is defined as" -" (first term is determined by node weights while the second term is " -"determined by edge weights of the graph):" -msgstr "" +#~ msgid "Module for functions adding layers of circuits" +#~ msgstr "" -#: of tensorcircuit.templates.measurements.spin_glass_measurements:5 -msgid "H = \\sum_{v\\in g} w_v Z_v + \\sum_{e\\in g} w_e Z_{e1} Z_{e2}" -msgstr "" +#~ msgid "Hlayer" +#~ msgstr "" -#: of tensorcircuit.templates.measurements.spin_glass_measurements:28 -msgid "The quantum circuit" -msgstr "" +#~ msgid "anyrxlayer" +#~ msgstr "" -#: of tensorcircuit.templates.measurements.spin_glass_measurements:30 -msgid "The graph for spin glass Hamiltonian definition" -msgstr "" +#~ msgid "anyrylayer" +#~ msgstr "" -#: of tensorcircuit.templates.measurements.spin_glass_measurements:32 -msgid "" -"Whether measure the circuit with reusing the wavefunction, defaults to " -"True" -msgstr "" +#~ msgid "anyrzlayer" +#~ msgstr "" -#: of tensorcircuit.templates.measurements.spin_glass_measurements:34 -msgid "The spin glass energy expectation value" -msgstr "" +#~ msgid "anyswaplayer" +#~ msgstr "" -#: ../../source/api/torchnn.rst:2 -msgid "tensorcircuit.torchnn" -msgstr "" +#~ msgid "anyxxlayer" +#~ msgstr "" -#: of tensorcircuit.torchnn:1 -msgid "PyTorch nn Module wrapper for quantum function" -msgstr "" +#~ msgid "anyxylayer" +#~ msgstr "" -#: of tensorcircuit.torchnn.QuantumNet:1 -msgid "Bases: :py:class:`torch.nn.modules.module.Module`" -msgstr "" +#~ msgid "anyxzlayer" +#~ msgstr "" -#: of tensorcircuit.torchnn.QuantumNet.__init__:1 -msgid "PyTorch nn Module wrapper on quantum function ``f``." -msgstr "" +#~ msgid "anyyxlayer" +#~ msgstr "" -#: of tensorcircuit.torchnn.QuantumNet.__init__:32 -msgid "Quantum function with tensor in (input and weights) and tensor out." -msgstr "" +#~ msgid "anyyylayer" +#~ msgstr "" -#: of tensorcircuit.torchnn.QuantumNet.__init__:34 -msgid "" -"list of shape tuple for different weights as the non-first parameters for" -" ``f``" -msgstr "" +#~ msgid "anyyzlayer" +#~ msgstr "" -#: of tensorcircuit.torchnn.QuantumNet.__init__:36 -msgid "function that gives the shape tuple returns torch tensor, defaults to None" -msgstr "" +#~ msgid "anyzxlayer" +#~ msgstr "" -#: of tensorcircuit.torchnn.QuantumNet.__init__:38 -msgid "whether apply vmap (batch input) on ``f``, defaults to True" -msgstr "" +#~ msgid "anyzylayer" +#~ msgstr "" -#: of tensorcircuit.torchnn.QuantumNet.__init__:40 -msgid "whether transform ``f`` with torch interface, defaults to True" -msgstr "" +#~ msgid "anyzzlayer" +#~ msgstr "" -#: of tensorcircuit.torchnn.QuantumNet.__init__:42 -msgid "whether jit ``f``, defaults to True" -msgstr "" +#~ msgid "cnotlayer" +#~ msgstr "" -#: of tensorcircuit.torchnn.QuantumNet.forward:1 -msgid "Defines the computation performed at every call." -msgstr "" +#~ msgid "rxlayer" +#~ msgstr "" -#: of tensorcircuit.torchnn.QuantumNet.forward:3 -msgid "Should be overridden by all subclasses." -msgstr "" +#~ msgid "rylayer" +#~ msgstr "" -#: of tensorcircuit.torchnn.QuantumNet.forward:6 -msgid "" -"Although the recipe for forward pass needs to be defined within this " -"function, one should call the :class:`Module` instance afterwards instead" -" of this since the former takes care of running the registered hooks " -"while the latter silently ignores them." -msgstr "" +#~ msgid "rzlayer" +#~ msgstr "" -#: ../../source/api/translation.rst:2 -msgid "tensorcircuit.translation" -msgstr "" +#~ msgid "swaplayer" +#~ msgstr "" -#: of tensorcircuit.translation:1 -msgid "Circuit object translation in different packages" -msgstr "" +#~ msgid "xxgate" +#~ msgstr "" -#: of tensorcircuit.translation.perm_matrix:1 -msgid "" -"Generate a permutation matrix P. Due to the different convention or " -"qubits' order in qiskit and tensorcircuit, the unitary represented by the" -" same circuit is different. They are related by this permutation matrix " -"P: P @ U_qiskit @ P = U_tc" -msgstr "" +#~ msgid "xxlayer" +#~ msgstr "" -#: of tensorcircuit.translation.perm_matrix:7 -#: tensorcircuit.translation.qir2qiskit:16 -#: tensorcircuit.translation.qiskit2tc:14 tensorcircuit.vis.qir2tex:12 -msgid "# of qubits" -msgstr "" +#~ msgid "xygate" +#~ msgstr "" -#: of tensorcircuit.translation.perm_matrix:9 -msgid "The permutation matrix P" -msgstr "" +#~ msgid "xylayer" +#~ msgstr "" -#: of tensorcircuit.translation.qir2qiskit:1 -msgid "" -"Generate a qiskit quantum circuit using the quantum intermediate " -"representation (qir) in tensorcircuit." -msgstr "" +#~ msgid "xzgate" +#~ msgstr "" -#: of tensorcircuit.translation.qir2qiskit:18 -msgid "qiskit QuantumCircuit object" -msgstr "" +#~ msgid "xzlayer" +#~ msgstr "" -#: of tensorcircuit.translation.qiskit2tc:1 -msgid "Generate a tensorcircuit circuit using the quantum circuit data in qiskit." -msgstr "" +#~ msgid "yxgate" +#~ msgstr "" -#: of tensorcircuit.translation.qiskit2tc:12 -msgid "Quantum circuit data from qiskit." -msgstr "" +#~ msgid "yxlayer" +#~ msgstr "" -#: of tensorcircuit.translation.qiskit2tc:16 -msgid "Input state of the circuit. Default is None." -msgstr "" +#~ msgid "yygate" +#~ msgstr "" -#: of tensorcircuit.translation.qiskit2tc:18 -msgid "A quantum circuit in tensorcircuit" -msgstr "" +#~ msgid "yylayer" +#~ msgstr "" -#: ../../source/api/utils.rst:2 -msgid "tensorcircuit.utils" -msgstr "" +#~ msgid "yzgate" +#~ msgstr "" -#: of tensorcircuit.utils:1 -msgid "Helper functions" -msgstr "" +#~ msgid "yzlayer" +#~ msgstr "" -#: of tensorcircuit.utils.append:1 -msgid "Functional programming paradigm to build function pipeline" -msgstr "" +#~ msgid "zxgate" +#~ msgstr "" -#: of tensorcircuit.utils.append:9 -msgid "The function which are attached with other functions" -msgstr "" +#~ msgid "zxlayer" +#~ msgstr "" -#: of tensorcircuit.utils.append:11 -msgid "Function to be attached" -msgstr "" +#~ msgid "zygate" +#~ msgstr "" -#: of tensorcircuit.utils.append:13 -msgid "The final results after function pipeline" -msgstr "" +#~ msgid "zylayer" +#~ msgstr "" -#: of tensorcircuit.utils.benchmark:1 -msgid "benchmark jittable function with staging time and running time" -msgstr "" +#~ msgid "zzgate" +#~ msgstr "" -#: of tensorcircuit.utils.benchmark:5 -msgid "_description_, defaults to 5" -msgstr "" +#~ msgid "zzlayer" +#~ msgstr "" -#: of tensorcircuit.utils.benchmark:7 -msgid "_description_, defaults to True" -msgstr "" +#~ msgid "$$e^{-i heta_i \\sigma}$$" +#~ msgstr "" -#: of tensorcircuit.utils.is_m1mac:1 -msgid "check whether the running platform is MAC with M1 chip" -msgstr "" +#~ msgid "" +#~ "The following function should be used" +#~ " to generate layers with special " +#~ "case. As its soundness depends on " +#~ "the nature of the task or problem," +#~ " it doesn't always make sense." +#~ msgstr "" -#: of tensorcircuit.utils.is_m1mac:3 -msgid "True for MAC M1 platform" -msgstr "" +#~ msgid "$$e^{-i heta \\sigma}$$" +#~ msgstr "" -#: of tensorcircuit.utils.return_partial:1 -msgid "" -"Return a callable function for output ith parts of the original output " -"along the first axis. Original output supports List and Tensor." -msgstr "" +#~ msgid "$$e^{-i heta \\sigma}$$" +#~ msgstr "" -#: of tensorcircuit.utils.return_partial:20 -msgid "The function to be applied this method" -msgstr "" +#~ msgid "" +#~ "A collection of useful function snippets" +#~ " that irrelevant with the main " +#~ "modules or await for furthere refactor" +#~ msgstr "" -#: of tensorcircuit.utils.return_partial:22 -msgid "The ith parts of original output along the first axis (axis=0 or dim=0)" -msgstr "" +#~ msgid "Bases: :py:class:`object`" +#~ msgstr "" -#: of tensorcircuit.utils.return_partial:24 -msgid "The modified callable function" -msgstr "" +#~ msgid "" +#~ "color cirq circuit SVG for given " +#~ "gates, a small tool to hack the" +#~ " cirq SVG" +#~ msgstr "" -#: ../../source/api/vis.rst:2 -msgid "tensorcircuit.vis" -msgstr "" +#~ msgid "integer coordinate which gate is colored" +#~ msgstr "" -#: of tensorcircuit.vis:1 -msgid "Visualization on circuits" -msgstr "" +#~ msgid "transform repr form of an array to real numpy array" +#~ msgstr "" -#: of tensorcircuit.vis.gate_name_trans:1 -msgid "" -"Translating from the gate name to gate information including the number " -"of control qubits and the reduced gate name." -msgstr "" +#~ msgid "DQAS application kernels as vag functions" +#~ msgstr "" -#: of tensorcircuit.vis.gate_name_trans:10 -msgid "String of gate name" -msgstr "" +#~ msgid "1D array for full wavefunction, the basis is in lexcical order" +#~ msgstr "" -#: of tensorcircuit.vis.gate_name_trans:12 -msgid "# of control qubits, reduced gate name" -msgstr "" +#~ msgid "nx.Graph" +#~ msgstr "" -#: of tensorcircuit.vis.qir2tex:1 -msgid "" -"Generate Tex code from 'qir' string to illustrate the circuit structure. " -"This visualization is based on quantikz package." -msgstr "" +#~ msgid "transformation functions before averaged" +#~ msgstr "" -#: of tensorcircuit.vis.qir2tex:10 -msgid "The quantum intermediate representation of a circuit in tensorcircuit." -msgstr "" +#~ msgid "as f3" +#~ msgstr "" -#: of tensorcircuit.vis.qir2tex:14 -msgid "Initial state, default is an all zero state '000...000'." -msgstr "" +#~ msgid "maxcut energy for n qubit wavefunction i-th basis" +#~ msgstr "" -#: of tensorcircuit.vis.qir2tex:16 -msgid "Measurement Basis, default is None which means no" -msgstr "" +#~ msgid "ranged from 0 to 2**n-1" +#~ msgstr "" -#: of tensorcircuit.vis.qir2tex:17 -msgid "" -"measurement in the end of the circuit. :type measure: Optional[List[str]]" -" :param rcompress: If true, a right compression of the circuit will be " -"conducted. A right compression means we will try to shift gates from " -"right to left if possible. Default is false. :type rcompress: bool :param" -" lcompress: If true, a left compression of the circuit will be conducted." -" A left compression means we will try to shift gates from left to right " -"if possible. Default is false. :type lcompress: bool :param standalone: " -"If true, the tex code will be designed to generate a standalone document." -" Default is false which means the generated tex code is just a quantikz " -"code block. :type standalone: bool :param return_string_table: If true, a" -" string table of tex code will also be returned. Default is false. :type " -"return_string_table: bool :return: Tex code of circuit visualization " -"based on quantikz package. If return_string_table is true, a string table" -" of tex code will also be returned. :rtype: Union[str, Tuple[str, " -"List[List[str]]]]" -msgstr "" +#~ msgid "number of qubits" +#~ msgstr "" -#: of tensorcircuit.vis.render_pdf:1 -msgid "" -"Generate the PDF file with given latex string and filename. Latex command" -" and file path can be specified. When notebook is True, convert the " -"output PDF file to image and return a Image object." -msgstr "" +#~ msgid "" +#~ "deprecated as non tf and non " +#~ "flexible, use the combination of " +#~ "``reduced_density_matrix`` and ``entropy`` instead." +#~ msgstr "" -#: of tensorcircuit.vis.render_pdf:15 -msgid "String of latex content" -msgstr "" +#~ msgid "deprecated, current version in tc.quantum" +#~ msgstr "" -#: of tensorcircuit.vis.render_pdf:17 -msgid "File name, defaults to random UUID `str(uuid4())`" -msgstr "" +#~ msgid "" +#~ "value and gradient, currently only " +#~ "tensorflow backend is supported jax and" +#~ " numpy seems to be slow in " +#~ "circuit simulation anyhow. *deprecated*" +#~ msgstr "" -#: of tensorcircuit.vis.render_pdf:19 -msgid "Executable Latex command, defaults to `pdflatex`" -msgstr "" +#~ msgid "if lbd=0, take energy as objective" +#~ msgstr "" -#: of tensorcircuit.vis.render_pdf:21 -msgid "File path, defaults to current working place `os.getcwd()`" -msgstr "" +#~ msgid "if as default 0, overlap will not compute in the process" +#~ msgstr "" -#: of tensorcircuit.vis.render_pdf:23 -msgid "[description], defaults to False" -msgstr "" +#~ msgid "Fill single qubit gates according to placeholder on circuit" +#~ msgstr "" -#: of tensorcircuit.vis.render_pdf:25 -msgid "if notebook is True, return `Image` object; otherwise return `None`" -msgstr "" +#~ msgid "Hamiltonian measurements for Heisenberg model on graph lattice g" +#~ msgstr "" -#~ msgid "" -#~ "This is a method that implementers " -#~ "of subclasses of `Layer` or `Model` " -#~ "can override if they need a " -#~ "state-creation step in-between layer " -#~ "instantiation and layer call." +#~ msgid "short cut for ``cirq.LineQubit(i)``" #~ msgstr "" -#~ msgid "This is typically used to create the weights of `Layer` subclasses." +#~ msgid "QAOA block encoding kernel, support 2 params in one op" #~ msgstr "" #~ msgid "" -#~ "Note here that `call()` method in " -#~ "`tf.keras` is little bit different from" -#~ " `keras` API. In `keras` API, you " -#~ "can pass support masking for layers " -#~ "as additional arguments. Whereas `tf.keras`" -#~ " has `compute_mask()` method to support " -#~ "masking." +#~ "training QAOA with only optimizing " +#~ "circuit parameters, can be well replaced" +#~ " with more general function `DQAS_search`" #~ msgstr "" -#~ msgid "Modules for DQAS framework" +#~ msgid "multi parameter for one layer" #~ msgstr "" -#~ msgid "DQAS framework entrypoint" +#~ msgid "kw arguments for measurements_func" #~ msgstr "" -#~ msgid "Parameters" +#~ msgid "loss function, gradient of nnp" #~ msgstr "" #~ msgid "" -#~ "function with input of data instance," -#~ " circuit parameters theta and structural" -#~ " paramter k, return tuple of " -#~ "objective value and gradient with " -#~ "respect to theta" +#~ "tensorflow quantum backend compare to " +#~ "qaoa_vag which is tensorcircuit backend" #~ msgstr "" -#~ msgid "data generator as dataset" +#~ msgid "Hamiltonian for tfim on lattice defined by graph g" #~ msgstr "" -#~ msgid "list of operations as primitive operator pool" +#~ msgid "cirq.PauliSum as operators for tfq expectation layer" #~ msgstr "" -#~ msgid "the default layer number of the circuit ansatz" +#~ msgid "" +#~ "generate random wavefunction from " +#~ "approximately Haar measure, reference: " +#~ "https://doi.org/10.1063/1.4983266" #~ msgstr "" -#~ msgid "" -#~ "shape of circuit parameter pool, in " -#~ "general p_stp*l, where l is the " -#~ "max number of circuit parameters for " -#~ "op in the operator pool" +#~ msgid "repetition of the blocks" #~ msgstr "" -#~ msgid "the same as p in the most times" +#~ msgid "random Haar measure approximation" #~ msgstr "" -#~ msgid "batch size of one epoch" +#~ msgid "cirq.Circuit, empty circuit" #~ msgstr "" -#~ msgid "prethermal update times" +#~ msgid "# of qubit" #~ msgstr "" -#~ msgid "training epochs" +#~ msgid "" +#~ "One-hot variational autoregressive models " +#~ "for multiple categorical choices beyond " +#~ "binary" #~ msgstr "" -#~ msgid "parallel thread number, 0 to disable multiprocessing model by default" +#~ msgid "Bases: :py:class:`keras.engine.training.Model`" #~ msgstr "" -#~ msgid "set verbose log to print" +#~ msgid "Calls the model on new inputs and returns the outputs as tensors." #~ msgstr "" -#~ msgid "function to output verbose information" +#~ msgid "" +#~ "In this case `call()` just reapplies " +#~ "all ops in the graph to the " +#~ "new inputs (e.g. build a new " +#~ "computational graph from the provided " +#~ "inputs)." #~ msgstr "" -#~ msgid "function return intermiediate result for final history list" +#~ msgid "" +#~ "Note: This method should not be " +#~ "called directly. It is only meant " +#~ "to be overridden when subclassing " +#~ "`tf.keras.Model`. To call a model on " +#~ "an input, always use the `__call__()`" +#~ " method, i.e. `model(inputs)`, which relies" +#~ " on the underlying `call()` method." #~ msgstr "" -#~ msgid "cutoff probability to avoid peak distribution" +#~ msgid "Args:" #~ msgstr "" #~ msgid "" -#~ "function accepting list of objective " -#~ "values and return the baseline value " -#~ "used in the next round" +#~ "inputs: Input tensor, or dict/list/tuple " +#~ "of input tensors. training: Boolean or" +#~ " boolean scalar tensor, indicating whether" +#~ " to run" #~ msgstr "" -#~ msgid "return noise with the same shape as circuit parameter pool" +#~ msgid "the `Network` in training mode or inference mode." #~ msgstr "" -#~ msgid "initial values for circuit parameter pool" +#~ msgid "mask: A mask or list of masks. A mask can be either a boolean tensor or" #~ msgstr "" -#~ msgid "initial values for probabilistic model parameters" +#~ msgid "None (no mask). For more details, check the guide" #~ msgstr "" -#~ msgid "optimizer for circuit parameters theta" +#~ msgid "[here](https://www.tensorflow.org/guide/keras/masking_and_padding)." #~ msgstr "" -#~ msgid "optimizer for model parameters alpha" +#~ msgid "Returns:" #~ msgstr "" -#~ msgid "optimizer for circuit parameters in prethermal stage" +#~ msgid "" +#~ "A tensor if there is a single " +#~ "output, or a list of tensors if" +#~ " there are more than one outputs." #~ msgstr "" -#~ msgid "fixed structural parameters for prethermal training" +#~ msgid "Bases: :py:class:`keras.engine.base_layer.Layer`" #~ msgstr "" -#~ msgid "regularization function for model parameters alpha" +#~ msgid "" +#~ "Creates the variables of the layer " +#~ "(optional, for subclass implementers)." #~ msgstr "" -#~ msgid "regularization function for circuit parameters theta" +#~ msgid "" +#~ "This is a method that implementers " +#~ "of subclasses of `Layer` or `Model` " +#~ "can override if they need a " +#~ "state-creation step in-between layer " +#~ "instantiation and layer call. It is " +#~ "invoked automatically before the first " +#~ "execution of `call()`." #~ msgstr "" -#~ msgid "Returns" +#~ msgid "" +#~ "This is typically used to create " +#~ "the weights of `Layer` subclasses (at" +#~ " the discretion of the subclass " +#~ "implementer)." +#~ msgstr "" + +#~ msgid "input_shape: Instance of `TensorShape`, or list of instances of" #~ msgstr "" #~ msgid "" -#~ "The probabilistic model based DQAS, can" -#~ " use extensively for DQAS case for" -#~ " ``NMF`` probabilistic model." +#~ "`TensorShape` if the layer expects a " +#~ "list of inputs (one instance per " +#~ "input)." +#~ msgstr "" + +#~ msgid "This is where the layer's logic lives." +#~ msgstr "" + +#~ msgid "" +#~ "The `call()` method may not create " +#~ "state (except in its first invocation," +#~ " wrapping the creation of variables " +#~ "or other resources in `tf.init_scope()`). " +#~ "It is recommended to create state " +#~ "in `__init__()`, or the `build()` method" +#~ " that is called automatically before " +#~ "`call()` executes the first time." #~ msgstr "" -#~ msgid "vag func, return loss and nabla lnp" +#~ msgid "inputs: Input tensor, or dict/list/tuple of input tensors." #~ msgstr "" -#~ msgid "keras model" +#~ msgid "" +#~ "The first positional `inputs` argument " +#~ "is subject to special rules: - " +#~ "`inputs` must be explicitly passed. A" +#~ " layer cannot have zero" #~ msgstr "" -#~ msgid "sample func of logic with keras model input" +#~ msgid "" +#~ "arguments, and `inputs` cannot be " +#~ "provided via the default value of " +#~ "a keyword argument." #~ msgstr "" -#~ msgid "input data pipeline generator" +#~ msgid "NumPy array or Python scalar values in `inputs` get cast as tensors." #~ msgstr "" -#~ msgid "operation pool" +#~ msgid "Keras mask metadata is only collected from `inputs`." #~ msgstr "" -#~ msgid "depth for DQAS" +#~ msgid "" +#~ "Layers are built (`build(input_shape)` method)" +#~ " using shape info from `inputs` only." #~ msgstr "" -#~ msgid "parallel kernels" +#~ msgid "`input_spec` compatibility is only checked against `inputs`." #~ msgstr "" -#~ msgid "final loss function in terms of average of sub loss for each circuit" +#~ msgid "" +#~ "Mixed precision input casting is only" +#~ " applied to `inputs`. If a layer " +#~ "has tensor arguments in `*args` or " +#~ "`**kwargs`, their casting behavior in " +#~ "mixed precision should be handled " +#~ "manually." #~ msgstr "" -#~ msgid "derivative function for ``loss_func``" +#~ msgid "The SavedModel input specification is generated using `inputs` only." #~ msgstr "" #~ msgid "" -#~ "Call in customized functions and grab" -#~ " variables within DQAS framework function" -#~ " by var name str." +#~ "Integration with various ecosystem packages" +#~ " like TFMOT, TFLite, TF.js, etc is" +#~ " only supported for `inputs` and not" +#~ " for tensors in positional and " +#~ "keyword arguments." #~ msgstr "" -#~ msgid "The DQAS framework function" +#~ msgid "*args: Additional positional arguments. May contain tensors, although" #~ msgstr "" -#~ msgid "Variables within the DQAS framework" +#~ msgid "this is not recommended, for the reasons above." #~ msgstr "" -#~ msgid "Return type" +#~ msgid "**kwargs: Additional keyword arguments. May contain tensors, although" #~ msgstr "" #~ msgid "" -#~ "This function works only when nnp " -#~ "has the same shape as stp, i.e." -#~ " one parameter for each op." +#~ "this is not recommended, for the " +#~ "reasons above. The following optional " +#~ "keyword arguments are reserved: - " +#~ "`training`: Boolean scalar tensor of " +#~ "Python boolean indicating" #~ msgstr "" -#~ msgid "The kernel for multiprocess to run parallel in DQAS function/" +#~ msgid "whether the `call` is meant for training or inference." #~ msgstr "" #~ msgid "" -#~ "parallel variational parameter training and" -#~ " search to avoid local minimum not" -#~ " limited to qaoa setup as the " -#~ "function name indicates, as long as " -#~ "you provided suitable `vag_func`" +#~ "`mask`: Boolean input mask. If the " +#~ "layer's `call()` method takes a `mask`" +#~ " argument, its default value will be" +#~ " set to the mask generated for " +#~ "`inputs` by the previous layer (if " +#~ "`input` did come from a layer that" +#~ " generated a corresponding mask, i.e. " +#~ "if it came from a Keras layer " +#~ "with masking support)." #~ msgstr "" -#~ msgid "data input generator for vag_func" +#~ msgid "A tensor or list/tuple of tensors." #~ msgstr "" -#~ msgid "vag_kernel" +#~ msgid "Relevant classes for VQNHE" #~ msgstr "" -#~ msgid "number of tries" +#~ msgid "" +#~ "Bases: " +#~ ":py:class:`keras.optimizer_v2.learning_rate_schedule.LearningRateSchedule`" #~ msgstr "" -#~ msgid "" -#~ "for optimization problem the input is" -#~ " in general fixed so batch is " -#~ "often 1" +#~ msgid "Dense layer but with complex weights, used for building complex RBM" #~ msgstr "" -#~ msgid "number of parallel jobs" +#~ msgid "VQNHE" #~ msgstr "" -#~ msgid "mean value of normal distribution for nnp" +#~ msgid "[description]" #~ msgstr "" -#~ msgid "std deviation of normal distribution for nnp" +#~ msgid "VQE" #~ msgstr "" -#~ msgid "Doesn't support prob model DQAS search." +#~ msgid "Backend register" #~ msgstr "" -#~ msgid "Modules for graph instance data and more" +#~ msgid "Get the `tc.backend` object." #~ msgstr "" -#~ msgid "```python d = nx.to_dict_of_dicts(g) ```" +#~ msgid "\"numpy\", \"tensorflow\", \"jax\", \"pytorch\"" #~ msgstr "" -#~ msgid "1D PBC chain with n sites." +#~ msgid "Raises" #~ msgstr "" -#~ msgid "The number of nodes" +#~ msgid "Backend doesn't exist for `backend` argument." #~ msgstr "" -#~ msgid "The resulted graph g" +#~ msgid "The `tc.backend` object that with all registered universal functions." #~ msgstr "" -#~ msgid "all graphs with m edge out from g" +#~ msgid "Backend magic inherited from tensornetwork: jax backend" #~ msgstr "" -#~ msgid "" -#~ "Generate a reduced graph with given " -#~ "ratio of edges compared to the " -#~ "original graph g." +#~ msgid "Bases: :py:class:`tensornetwork.backends.jax.jax_backend.JaxBackend`" #~ msgstr "" -#~ msgid "The base graph" +#~ msgid "" +#~ "See the original backend API at " +#~ "``jax backend``. " +#~ "`_" #~ msgstr "" -#~ msgid "number of edges kept, default half of the edges" +#~ msgid "Returns the elementwise absolute value of tensor. Args:" #~ msgstr "" -#~ msgid "The resulted reduced graph" +#~ msgid "tensor: An input tensor." #~ msgstr "" -#~ msgid "Split the graph in exactly ``split`` piece evenly." +#~ msgid "tensor: Its elementwise absolute value." #~ msgstr "" -#~ msgid "The mother graph" +#~ msgid "Return the index of maximum of an array an axis." #~ msgstr "" -#~ msgid "The number of the graph we want to divide into, defaults to 2" +#~ msgid "[description], defaults to 0, different behavior from numpy defaults!" #~ msgstr "" -#~ msgid "List of graph instance of size ``split``" +#~ msgid "Return the index of minimum of an array an axis." #~ msgstr "" -#~ msgid "Module for functions adding layers of circuits" +#~ msgid "Cast the tensor dtype of a ``a``." #~ msgstr "" -#~ msgid "Hlayer" +#~ msgid "tensor" #~ msgstr "" -#~ msgid "anyrxlayer" +#~ msgid "\"float32\", \"float64\", \"complex64\", \"complex128\"" #~ msgstr "" -#~ msgid "anyrylayer" +#~ msgid "``a`` of new dtype" #~ msgstr "" -#~ msgid "anyrzlayer" +#~ msgid "Join a sequence of arrays along an existing axis." #~ msgstr "" -#~ msgid "anyswaplayer" +#~ msgid "[description], defaults to 0" #~ msgstr "" -#~ msgid "anyxxlayer" +#~ msgid "" +#~ "The native cond for XLA compiling, " +#~ "wrapper for ``tf.cond`` and limited " +#~ "functionality of ``jax.lax.cond``." #~ msgstr "" -#~ msgid "anyxylayer" +#~ msgid "Convert a np.array or a tensor to a tensor type for the backend." #~ msgstr "" -#~ msgid "anyxzlayer" +#~ msgid "" +#~ "Generate the coo format sparse matrix" +#~ " from indices and values, which is" +#~ " the only sparse format supported in" +#~ " different ML backends." #~ msgstr "" -#~ msgid "anyyxlayer" +#~ msgid "shape [n, 2] for n non zero values in the returned matrix" #~ msgstr "" -#~ msgid "anyyylayer" +#~ msgid "shape [n]" #~ msgstr "" -#~ msgid "anyyzlayer" +#~ msgid "Tuple[int, ...]" #~ msgstr "" -#~ msgid "anyzxlayer" +#~ msgid "Return the expm of ``a``, matrix exponential." #~ msgstr "" -#~ msgid "anyzylayer" +#~ msgid "tensor in matrix form" #~ msgstr "" -#~ msgid "anyzzlayer" +#~ msgid "matrix exponential of matrix ``a``" #~ msgstr "" -#~ msgid "cnotlayer" +#~ msgid "Return the cosine of a tensor ``a``." #~ msgstr "" -#~ msgid "rxlayer" +#~ msgid "cosine of ``a``" #~ msgstr "" -#~ msgid "rylayer" +#~ msgid "Return the cumulative sum of the elements along a given axis." #~ msgstr "" -#~ msgid "rzlayer" +#~ msgid "" +#~ "The default behavior is the same " +#~ "as numpy, different from tf/torch as " +#~ "cumsum of the flatten 1D array, " +#~ "defaults to None" #~ msgstr "" -#~ msgid "swaplayer" +#~ msgid "Return the copy of tensor ''a''." #~ msgstr "" -#~ msgid "xxgate" +#~ msgid "Return an identity matrix of dimension `dim`" #~ msgstr "" -#~ msgid "xxlayer" +#~ msgid "" +#~ "Depending on specific backends, `dim` " +#~ "has to be either an int (numpy," +#~ " torch, tensorflow) or a `ShapeType` " +#~ "object (for block-sparse backends). " +#~ "Block-sparse behavior is currently not " +#~ "supported" #~ msgstr "" -#~ msgid "xygate" +#~ msgid "" +#~ "N (int): The dimension of the " +#~ "returned matrix. dtype: The dtype of " +#~ "the returned matrix. M (int): The " +#~ "dimension of the returned matrix." #~ msgstr "" -#~ msgid "xylayer" +#~ msgid "Return the function which is the grad function of input ``f``." #~ msgstr "" -#~ msgid "xzgate" +#~ msgid "Example" #~ msgstr "" -#~ msgid "xzlayer" +#~ msgid "the function to be differentiated" #~ msgstr "" -#~ msgid "yxgate" +#~ msgid "" +#~ "the position of args in ``f`` that" +#~ " are to be differentiated, defaults " +#~ "to be 0" #~ msgstr "" -#~ msgid "yxlayer" +#~ msgid "the grad function of ``f`` with the same set of arguments as ``f``" #~ msgstr "" -#~ msgid "yygate" +#~ msgid "Return 1.j in as a tensor compatible with the backend." #~ msgstr "" -#~ msgid "yylayer" +#~ msgid "\"complex64\" or \"complex128\"" #~ msgstr "" -#~ msgid "yzgate" +#~ msgid "1.j tensor" #~ msgstr "" -#~ msgid "yzlayer" +#~ msgid "Return the elementwise imaginary value of a tensor ``a``." #~ msgstr "" -#~ msgid "zxgate" +#~ msgid "imaginary value of ``a``" #~ msgstr "" -#~ msgid "zxlayer" +#~ msgid "[summary]" #~ msgstr "" -#~ msgid "zygate" +#~ msgid "The possible options" #~ msgstr "" -#~ msgid "zylayer" +#~ msgid "Sampling output shape" #~ msgstr "" -#~ msgid "zzgate" +#~ msgid "" +#~ "probability for each option in a, " +#~ "defaults to None, as equal probability" +#~ " distribution" #~ msgstr "" -#~ msgid "zzlayer" +#~ msgid "" +#~ "Call the random normal function with " +#~ "the random state management behind the" +#~ " scene." #~ msgstr "" -#~ msgid "$$e^{-i heta_i \\sigma}$$" +#~ msgid "[description], defaults to 1" #~ msgstr "" -#~ msgid "" -#~ "The following function should be used" -#~ " to generate layers with special " -#~ "case. As its soundness depends on " -#~ "the nature of the task or problem," -#~ " it doesn't always make sense." +#~ msgid "[description], defaults to \"32\"" #~ msgstr "" -#~ msgid "$$e^{-i heta \\sigma}$$" +#~ msgid "Determine whether the type of input ``a`` is ``sparse``." #~ msgstr "" -#~ msgid "$$e^{-i heta \\sigma}$$" +#~ msgid "input matrix ``a``" #~ msgstr "" -#~ msgid "" -#~ "A collection of useful function snippets" -#~ " that irrelevant with the main " -#~ "modules or await for furthere refactor" +#~ msgid "a bool indicating whether the matrix ``a`` is sparse" #~ msgstr "" -#~ msgid "Bases: :py:class:`object`" +#~ msgid "Return a boolean on whether ``a`` is a tensor in backend package." #~ msgstr "" -#~ msgid "" -#~ "color cirq circuit SVG for given " -#~ "gates, a small tool to hack the" -#~ " cirq SVG" +#~ msgid "a tensor to be determined" #~ msgstr "" -#~ msgid "integer coordinate which gate is colored" +#~ msgid "whether ``a`` is a tensor" #~ msgstr "" -#~ msgid "transform repr form of an array to real numpy array" +#~ msgid "Return the jitted version of function ``f``." #~ msgstr "" -#~ msgid "DQAS application kernels as vag functions" +#~ msgid "function to be jitted" #~ msgstr "" -#~ msgid "1D array for full wavefunction, the basis is in lexcical order" +#~ msgid "" +#~ "index of args that doesn't regarded " +#~ "as tensor, only work for jax " +#~ "backend" #~ msgstr "" -#~ msgid "nx.Graph" +#~ msgid "" +#~ "whether open XLA compilation, only works" +#~ " for tensorflow backend, defaults False " +#~ "since several ops has no XLA " +#~ "correspondence" #~ msgstr "" -#~ msgid "transformation functions before averaged" +#~ msgid "jitted version of ``f``" #~ msgstr "" -#~ msgid "as f3" +#~ msgid "" +#~ "Function that computes a (forward-mode)" +#~ " Jacobian-vector product of ``f``. " +#~ "Strictly speaking, this function is " +#~ "value_and_jvp." #~ msgstr "" -#~ msgid "maxcut energy for n qubit wavefunction i-th basis" +#~ msgid "The function to compute jvp" #~ msgstr "" -#~ msgid "ranged from 0 to 2**n-1" +#~ msgid "input for ``f``" #~ msgstr "" -#~ msgid "number of qubits" +#~ msgid "tangents" #~ msgstr "" #~ msgid "" -#~ "deprecated as non tf and non " -#~ "flexible, use the combination of " -#~ "``reduced_density_matrix`` and ``entropy`` instead." +#~ "(``f(*inputs)``, jvp_tensor), where jvp_tensor " +#~ "is the same shape as the output" +#~ " of ``f``" #~ msgstr "" -#~ msgid "deprecated, current version in tc.quantum" +#~ msgid "Return the kronecker product of two matrices ``a`` and ``b``." #~ msgstr "" -#~ msgid "" -#~ "value and gradient, currently only " -#~ "tensorflow backend is supported jax and" -#~ " numpy seems to be slow in " -#~ "circuit simulation anyhow. *deprecated*" +#~ msgid "kronecker product of ``a`` and ``b``" #~ msgstr "" -#~ msgid "if lbd=0, take energy as objective" +#~ msgid "Return the maximum of an array or maximum along an axis." #~ msgstr "" -#~ msgid "if as default 0, overlap will not compute in the process" +#~ msgid "[description], defaults to None" #~ msgstr "" -#~ msgid "Fill single qubit gates according to placeholder on circuit" +#~ msgid "Return the minimum of an array or minimum along an axis." #~ msgstr "" -#~ msgid "Hamiltonian measurements for Heisenberg model on graph lattice g" +#~ msgid "" +#~ "Return the numpy array of a tensor" +#~ " ``a``, but may not work in a" +#~ " jitted function." #~ msgstr "" -#~ msgid "short cut for ``cirq.LineQubit(i)``" +#~ msgid "numpy array of ``a``" #~ msgstr "" -#~ msgid "QAOA block encoding kernel, support 2 params in one op" +#~ msgid "" +#~ "One-hot encodes the given ``a``. " +#~ "Each index in the input ``a`` is" +#~ " encoded as a vector of zeros " +#~ "of length ``num`` with the element " +#~ "at index set to one:" #~ msgstr "" -#~ msgid "" -#~ "training QAOA with only optimizing " -#~ "circuit parameters, can be well replaced" -#~ " with more general function `DQAS_search`" +#~ msgid "input tensor" #~ msgstr "" -#~ msgid "multi parameter for one layer" +#~ msgid "number of features in onehot dimension" #~ msgstr "" -#~ msgid "kw arguments for measurements_func" +#~ msgid "onehot tensor with the last extra dimension" #~ msgstr "" -#~ msgid "loss function, gradient of nnp" +#~ msgid "" +#~ "Return an ones-matrix of dimension " +#~ "`dim` Depending on specific backends, " +#~ "`dim` has to be either an int " +#~ "(numpy, torch, tensorflow) or a " +#~ "`ShapeType` object (for block-sparse " +#~ "backends). Block-sparse behavior is " +#~ "currently not supported Args:" #~ msgstr "" #~ msgid "" -#~ "tensorflow quantum backend compare to " -#~ "qaoa_vag which is tensorcircuit backend" +#~ "shape (int): The dimension of the " +#~ "returned matrix. dtype: The dtype of " +#~ "the returned matrix." #~ msgstr "" -#~ msgid "Hamiltonian for tfim on lattice defined by graph g" +#~ msgid "" +#~ "A jax like split API, but it " +#~ "doesn't split the key generator for " +#~ "other backends. It is just for a" +#~ " consistent interface of random code; " +#~ "make sure you know what the " +#~ "function actually does. This function is" +#~ " mainly a utility to write backend" +#~ " agnostic code instead of doing magic" +#~ " things." #~ msgstr "" -#~ msgid "cirq.PauliSum as operators for tfq expectation layer" +#~ msgid "Return the elementwise real value of a tensor ``a``." #~ msgstr "" -#~ msgid "" -#~ "generate random wavefunction from " -#~ "approximately Haar measure, reference: " -#~ "https://doi.org/10.1063/1.4983266" +#~ msgid "real value of ``a``" #~ msgstr "" -#~ msgid "repetition of the blocks" +#~ msgid "" +#~ "Rectified linear unit activation function. " +#~ "Computes the element-wise function:" #~ msgstr "" -#~ msgid "random Haar measure approximation" +#~ msgid "\\mathrm{relu}(x)=\\max(x,0)" #~ msgstr "" -#~ msgid "cirq.Circuit, empty circuit" +#~ msgid "Input tensor" #~ msgstr "" -#~ msgid "# of qubit" +#~ msgid "Tensor after relu" #~ msgstr "" #~ msgid "" -#~ "One-hot variational autoregressive models " -#~ "for multiple categorical choices beyond " -#~ "binary" +#~ "Roughly equivalent to operand[indices] = " +#~ "updates, indices only support shape with" +#~ " rank 2 for now." #~ msgstr "" -#~ msgid "Bases: :py:class:`keras.engine.training.Model`" +#~ msgid "Set the random state attached to the backend." #~ msgstr "" -#~ msgid "Calls the model on new inputs and returns the outputs as tensors." +#~ msgid "the random seed, defaults to be None" #~ msgstr "" #~ msgid "" -#~ "In this case `call()` just reapplies " -#~ "all ops in the graph to the " -#~ "new inputs (e.g. build a new " -#~ "computational graph from the provided " -#~ "inputs)." +#~ "If set to be true, only get " +#~ "the random state in return instead " +#~ "of setting the state on the " +#~ "backend" #~ msgstr "" -#~ msgid "" -#~ "Note: This method should not be " -#~ "called directly. It is only meant " -#~ "to be overridden when subclassing " -#~ "`tf.keras.Model`. To call a model on " -#~ "an input, always use the `__call__()`" -#~ " method, i.e. `model(inputs)`, which relies" -#~ " on the underlying `call()` method." +#~ msgid "Return the elementwise sine of a tensor ``a``." #~ msgstr "" -#~ msgid "Args:" +#~ msgid "sine of ``a``" #~ msgstr "" -#~ msgid "" -#~ "inputs: Input tensor, or dict/list/tuple " -#~ "of input tensors. training: Boolean or" -#~ " boolean scalar tensor, indicating whether" -#~ " to run" +#~ msgid "Return the total number of elements in ``a`` in tensor form." #~ msgstr "" -#~ msgid "the `Network` in training mode or inference mode." +#~ msgid "the total number of elements in ``a``" #~ msgstr "" -#~ msgid "mask: A mask or list of masks. A mask can be either a boolean tensor or" +#~ msgid "" +#~ "Softmax function. Computes the function " +#~ "which rescales elements to the range " +#~ "[0,1] such that the elements along " +#~ "axis sum to 1." #~ msgstr "" -#~ msgid "None (no mask). For more details, check the guide" +#~ msgid "\\mathrm{softmax}(x) = \\frac{\\exp(x_i)}{\\sum_j \\exp(x_j)}" #~ msgstr "" -#~ msgid "[here](https://www.tensorflow.org/guide/keras/masking_and_padding)." +#~ msgid "Tensor" #~ msgstr "" -#~ msgid "Returns:" +#~ msgid "" +#~ "A dimension along which Softmax will " +#~ "be computed , defaults to None for" +#~ " all axis sum." #~ msgstr "" -#~ msgid "" -#~ "A tensor if there is a single " -#~ "output, or a list of tensors if" -#~ " there are more than one outputs." +#~ msgid "concatenated tensor" #~ msgstr "" -#~ msgid "Bases: :py:class:`keras.engine.base_layer.Layer`" +#~ msgid "Solve the linear system Ax=b and return the solution x." #~ msgstr "" -#~ msgid "" -#~ "Creates the variables of the layer " -#~ "(optional, for subclass implementers)." +#~ msgid "The multiplied matrix." #~ msgstr "" -#~ msgid "" -#~ "This is a method that implementers " -#~ "of subclasses of `Layer` or `Model` " -#~ "can override if they need a " -#~ "state-creation step in-between layer " -#~ "instantiation and layer call. It is " -#~ "invoked automatically before the first " -#~ "execution of `call()`." +#~ msgid "The resulted matrix." #~ msgstr "" -#~ msgid "" -#~ "This is typically used to create " -#~ "the weights of `Layer` subclasses (at" -#~ " the discretion of the subclass " -#~ "implementer)." +#~ msgid "The solution of the linear system." #~ msgstr "" -#~ msgid "input_shape: Instance of `TensorShape`, or list of instances of" +#~ msgid "A sparse matrix multiplies a dense matrix." #~ msgstr "" -#~ msgid "" -#~ "`TensorShape` if the layer expects a " -#~ "list of inputs (one instance per " -#~ "input)." +#~ msgid "a sparse matrix" #~ msgstr "" -#~ msgid "This is where the layer's logic lives." +#~ msgid "a dense matrix" +#~ msgstr "" + +#~ msgid "dense matrix" #~ msgstr "" #~ msgid "" -#~ "The `call()` method may not create " -#~ "state (except in its first invocation," -#~ " wrapping the creation of variables " -#~ "or other resources in `tf.init_scope()`). " -#~ "It is recommended to create state " -#~ "in `__init__()`, or the `build()` method" -#~ " that is called automatically before " -#~ "`call()` executes the first time." +#~ "Concatenates a sequence of tensors ``a``" +#~ " along a new dimension ``axis``." #~ msgstr "" -#~ msgid "inputs: Input tensor, or dict/list/tuple of input tensors." +#~ msgid "List of tensors in the same shape" #~ msgstr "" -#~ msgid "" -#~ "The first positional `inputs` argument " -#~ "is subject to special rules: - " -#~ "`inputs` must be explicitly passed. A" -#~ " layer cannot have zero" +#~ msgid "the stack axis, defaults to 0" #~ msgstr "" -#~ msgid "" -#~ "arguments, and `inputs` cannot be " -#~ "provided via the default value of " -#~ "a keyword argument." +#~ msgid "stateful register for each package" #~ msgstr "" -#~ msgid "NumPy array or Python scalar values in `inputs` get cast as tensors." +#~ msgid "shape of output sampling tensor" #~ msgstr "" -#~ msgid "Keras mask metadata is only collected from `inputs`." +#~ msgid "only real data type is supported, \"32\" or \"64\", defaults to \"32\"" #~ msgstr "" -#~ msgid "" -#~ "Layers are built (`build(input_shape)` method)" -#~ " using shape info from `inputs` only." +#~ msgid "Uniform random sampler from ``low`` to ``high``." #~ msgstr "" -#~ msgid "`input_spec` compatibility is only checked against `inputs`." +#~ msgid "shape of output sampling tensor, defaults to 1" #~ msgstr "" -#~ msgid "" -#~ "Mixed precision input casting is only" -#~ " applied to `inputs`. If a layer " -#~ "has tensor arguments in `*args` or " -#~ "`**kwargs`, their casting behavior in " -#~ "mixed precision should be handled " -#~ "manually." +#~ msgid "Stop backpropagation from ``a``." +#~ msgstr "" + +#~ msgid "``branches[index]()``" #~ msgstr "" -#~ msgid "The SavedModel input specification is generated using `inputs` only." +#~ msgid "Constructs a tensor by tiling a given tensor." #~ msgstr "" -#~ msgid "" -#~ "Integration with various ecosystem packages" -#~ " like TFMOT, TFLite, TF.js, etc is" -#~ " only supported for `inputs` and not" -#~ " for tensors in positional and " -#~ "keyword arguments." +#~ msgid "1d tensor with length the same as the rank of ``a``" #~ msgstr "" -#~ msgid "*args: Additional positional arguments. May contain tensors, although" +#~ msgid "Convert a sparse matrix to dense tensor." #~ msgstr "" -#~ msgid "this is not recommended, for the reasons above." +#~ msgid "the resulted dense matrix" #~ msgstr "" -#~ msgid "**kwargs: Additional keyword arguments. May contain tensors, although" +#~ msgid "" +#~ "Find the unique elements and their " +#~ "corresponding counts of the given tensor" +#~ " ``a``." #~ msgstr "" -#~ msgid "" -#~ "this is not recommended, for the " -#~ "reasons above. The following optional " -#~ "keyword arguments are reserved: - " -#~ "`training`: Boolean scalar tensor of " -#~ "Python boolean indicating" +#~ msgid "Unique elements, corresponding counts" #~ msgstr "" -#~ msgid "whether the `call` is meant for training or inference." +#~ msgid "Return the function which returns the value and grad of ``f``." #~ msgstr "" #~ msgid "" -#~ "`mask`: Boolean input mask. If the " -#~ "layer's `call()` method takes a `mask`" -#~ " argument, its default value will be" -#~ " set to the mask generated for " -#~ "`inputs` by the previous layer (if " -#~ "`input` did come from a layer that" -#~ " generated a corresponding mask, i.e. " -#~ "if it came from a Keras layer " -#~ "with masking support)." +#~ "the value and grad function of " +#~ "``f`` with the same set of " +#~ "arguments as ``f``" #~ msgstr "" -#~ msgid "A tensor or list/tuple of tensors." +#~ msgid "" +#~ "Return the VVAG function of ``f``. " +#~ "The inputs for ``f`` is (args[0], " +#~ "args[1], args[2], ...), and the output" +#~ " of ``f`` is a scalar. Suppose " +#~ "VVAG(f) is a function with inputs " +#~ "in the form (vargs[0], args[1], args[2]," +#~ " ...), where vagrs[0] has one extra" +#~ " dimension than args[0] in the first" +#~ " axis and consistent with args[0] in" +#~ " shape for remaining dimensions, i.e. " +#~ "shape(vargs[0]) = [batch] + shape(args[0])." +#~ " (We only cover cases where " +#~ "``vectorized_argnums`` defaults to 0 here " +#~ "for demonstration). VVAG(f) returns a " +#~ "tuple as a value tensor with shape" +#~ " [batch, 1] and a gradient tuple " +#~ "with shape: ([batch]+shape(args[argnum]) for " +#~ "argnum in argnums). The gradient for " +#~ "argnums=k is defined as" #~ msgstr "" -#~ msgid "Relevant classes for VQNHE" +#~ msgid "" +#~ "g^k = \\frac{\\partial \\sum_{i\\in batch} " +#~ "f(vargs[0][i], args[1], ...)}{\\partial args[k]}" #~ msgstr "" -#~ msgid "" -#~ "Bases: " -#~ ":py:class:`keras.optimizer_v2.learning_rate_schedule.LearningRateSchedule`" +#~ msgid "Therefore, if argnums=0, the gradient is reduced to" #~ msgstr "" -#~ msgid "Dense layer but with complex weights, used for building complex RBM" +#~ msgid "g^0_i = \\frac{\\partial f(vargs[0][i])}{\\partial vargs[0][i]}" #~ msgstr "" -#~ msgid "VQNHE" +#~ msgid "" +#~ ", which is specifically suitable for " +#~ "batched VQE optimization, where args[0] " +#~ "is the circuit parameters." #~ msgstr "" -#~ msgid "[description]" +#~ msgid "And if argnums=1, the gradient is like" #~ msgstr "" -#~ msgid "VQE" +#~ msgid "" +#~ "g^1_i = \\frac{\\partial \\sum_j " +#~ "f(vargs[0][j], args[1])}{\\partial args[1][i]}\n" +#~ "\n" #~ msgstr "" -#~ msgid "Backend register" +#~ msgid "" +#~ ", which is suitable for quantum " +#~ "machine learning scenarios, where ``f`` " +#~ "is the loss function, args[0] " +#~ "corresponds to the input data and " +#~ "args[1] corresponds to the weights in" +#~ " the QML model." #~ msgstr "" -#~ msgid "Get the `tc.backend` object." +#~ msgid "" +#~ "the args to be vectorized, these " +#~ "arguments should share the same batch" +#~ " shape in the fist dimension" #~ msgstr "" -#~ msgid "\"numpy\", \"tensorflow\", \"jax\", \"pytorch\"" +#~ msgid "" +#~ "Function that computes the dot product" +#~ " between a vector v and the " +#~ "Jacobian of the given function at " +#~ "the point given by the inputs. " +#~ "(reverse mode AD relevant) Strictly " +#~ "speaking, this function is value_and_vjp." #~ msgstr "" -#~ msgid "Raises" +#~ msgid "the function to carry out vjp calculation" #~ msgstr "" -#~ msgid "Backend doesn't exist for `backend` argument." +#~ msgid "" +#~ "value vector or gradient from downstream" +#~ " in reverse mode AD the same " +#~ "shape as return of function ``f``" #~ msgstr "" -#~ msgid "The `tc.backend` object that with all registered universal functions." +#~ msgid "" +#~ "(``f(*inputs)``, vjp_tensor), where vjp_tensor " +#~ "is the same shape as inputs" #~ msgstr "" -#~ msgid "Backend magic inherited from tensornetwork: jax backend" +#~ msgid "" +#~ "Return the vectorized map or batched " +#~ "version of ``f`` on the first " +#~ "extra axis. The general interface " +#~ "supports ``f`` with multiple arguments " +#~ "and broadcast in the fist dimension." #~ msgstr "" -#~ msgid "Bases: :py:class:`tensornetwork.backends.jax.jax_backend.JaxBackend`" +#~ msgid "function to be broadcasted." +#~ msgstr "" + +#~ msgid "vmap version of ``f``" #~ msgstr "" #~ msgid "" -#~ "See the original backend API at " -#~ "``jax backend``. " -#~ "`_" +#~ "Return a zeros-matrix of dimension " +#~ "`dim` Depending on specific backends, " +#~ "`dim` has to be either an int " +#~ "(numpy, torch, tensorflow) or a " +#~ "`ShapeType` object (for block-sparse " +#~ "backends)." #~ msgstr "" -#~ msgid "Returns the elementwise absolute value of tensor. Args:" +#~ msgid "Block-sparse behavior is currently not supported Args:" #~ msgstr "" -#~ msgid "tensor: An input tensor." +#~ msgid "Backend magic inherited from tensornetwork: numpy backend" #~ msgstr "" -#~ msgid "tensor: Its elementwise absolute value." +#~ msgid "" +#~ "Bases: " +#~ ":py:class:`tensornetwork.backends.numpy.numpy_backend.NumPyBackend`" #~ msgstr "" -#~ msgid "Return the index of maximum of an array an axis." +#~ msgid "" +#~ "see the original backend API at " +#~ "`numpy backend " +#~ "`_" #~ msgstr "" -#~ msgid "[description], defaults to 0, different behavior from numpy defaults!" +#~ msgid "Backend magic inherited from tensornetwork: pytorch backend" #~ msgstr "" -#~ msgid "Return the index of minimum of an array an axis." +#~ msgid "" +#~ "Bases: " +#~ ":py:class:`tensornetwork.backends.pytorch.pytorch_backend.PyTorchBackend`" #~ msgstr "" -#~ msgid "Cast the tensor dtype of a ``a``." +#~ msgid "" +#~ "See the original backend API at " +#~ "``pytorch backend``. " +#~ "``_" #~ msgstr "" -#~ msgid "tensor" +#~ msgid "" +#~ "Note the functionality provided by " +#~ "pytorch backend is incomplete, it " +#~ "currenly lacks native efficicent jit and" +#~ " vmap support." #~ msgstr "" -#~ msgid "\"float32\", \"float64\", \"complex64\", \"complex128\"" +#~ msgid "Backend magic inherited from tensornetwork: tensorflow backend" #~ msgstr "" -#~ msgid "``a`` of new dtype" +#~ msgid "" +#~ "Bases: " +#~ ":py:class:`tensornetwork.backends.tensorflow.tensorflow_backend.TensorFlowBackend`" #~ msgstr "" -#~ msgid "Join a sequence of arrays along an existing axis." +#~ msgid "" +#~ "See the original backend API at " +#~ "`'tensorflow backend''. " +#~ "`_" #~ msgstr "" -#~ msgid "[description], defaults to 0" +#~ msgid "Some common noise quantum channels." #~ msgstr "" #~ msgid "" -#~ "The native cond for XLA compiling, " -#~ "wrapper for ``tf.cond`` and limited " -#~ "functionality of ``jax.lax.cond``." +#~ "Return an amplitude damping channel. " +#~ "Notice: Amplitude damping corrspondings to " +#~ "p = 1." #~ msgstr "" -#~ msgid "Convert a np.array or a tensor to a tensor type for the backend." +#~ msgid "" +#~ "\\sqrt{p}\n" +#~ "\\begin{bmatrix}\n" +#~ " 1 & 0\\\\\n" +#~ " 0 & \\sqrt{1-\\gamma}\\\\\n" +#~ "\\end{bmatrix}\\qquad\n" +#~ "\\sqrt{p}\n" +#~ "\\begin{bmatrix}\n" +#~ " 0 & \\sqrt{\\gamma}\\\\\n" +#~ " 0 & 0\\\\\n" +#~ "\\end{bmatrix}\\qquad\n" +#~ "\\sqrt{1-p}\n" +#~ "\\begin{bmatrix}\n" +#~ " \\sqrt{1-\\gamma} & 0\\\\\n" +#~ " 0 & 1\\\\\n" +#~ "\\end{bmatrix}\\qquad\n" +#~ "\\sqrt{1-p}\n" +#~ "\\begin{bmatrix}\n" +#~ " 0 & 0\\\\\n" +#~ " \\sqrt{\\gamma} & 0\\\\\n" +#~ "\\end{bmatrix}\n" +#~ "\n" #~ msgstr "" -#~ msgid "" -#~ "Generate the coo format sparse matrix" -#~ " from indices and values, which is" -#~ " the only sparse format supported in" -#~ " different ML backends." +#~ msgid "the damping parameter of amplitude (:math:`\\gamma`)" #~ msgstr "" -#~ msgid "shape [n, 2] for n non zero values in the returned matrix" +#~ msgid ":math:`p`" #~ msgstr "" -#~ msgid "shape [n]" +#~ msgid "An amplitude damping channel with given :math:`\\gamma` and :math:`p`" #~ msgstr "" -#~ msgid "Tuple[int, ...]" +#~ msgid "Return a Depolarizing Channel" #~ msgstr "" -#~ msgid "Return the expm of ``a``, matrix exponential." +#~ msgid "" +#~ "\\sqrt{1-p_x-p_y-p_z}\n" +#~ "\\begin{bmatrix}\n" +#~ " 1 & 0\\\\\n" +#~ " 0 & 1\\\\\n" +#~ "\\end{bmatrix}\\qquad\n" +#~ "\\sqrt{p_x}\n" +#~ "\\begin{bmatrix}\n" +#~ " 0 & 1\\\\\n" +#~ " 1 & 0\\\\\n" +#~ "\\end{bmatrix}\\qquad\n" +#~ "\\sqrt{p_y}\n" +#~ "\\begin{bmatrix}\n" +#~ " 0 & -1j\\\\\n" +#~ " 1j & 0\\\\\n" +#~ "\\end{bmatrix}\\qquad\n" +#~ "\\sqrt{p_z}\n" +#~ "\\begin{bmatrix}\n" +#~ " 1 & 0\\\\\n" +#~ " 0 & -1\\\\\n" +#~ "\\end{bmatrix}\n" +#~ "\n" #~ msgstr "" -#~ msgid "tensor in matrix form" +#~ msgid ":math:`p_x`" #~ msgstr "" -#~ msgid "matrix exponential of matrix ``a``" +#~ msgid ":math:`p_y`" #~ msgstr "" -#~ msgid "Return the cosine of a tensor ``a``." +#~ msgid ":math:`p_z`" #~ msgstr "" -#~ msgid "cosine of ``a``" +#~ msgid "Sequences of Gates" #~ msgstr "" -#~ msgid "Return the cumulative sum of the elements along a given axis." +#~ msgid "Convert Kraus operators to one Tensor (as one Super Gate)." #~ msgstr "" #~ msgid "" -#~ "The default behavior is the same " -#~ "as numpy, different from tf/torch as " -#~ "cumsum of the flatten 1D array, " -#~ "defaults to None" +#~ "\\sum_{k}^{} K_k \\otimes K_k^{\\dagger}\n" +#~ "\n" #~ msgstr "" -#~ msgid "Return the copy of tensor ''a''." +#~ msgid "A sequence of Gate" #~ msgstr "" -#~ msgid "Return an identity matrix of dimension `dim`" +#~ msgid "The corresponding Tensor of the list of Kraus operators" #~ msgstr "" -#~ msgid "" -#~ "Depending on specific backends, `dim` " -#~ "has to be either an int (numpy," -#~ " torch, tensorflow) or a `ShapeType` " -#~ "object (for block-sparse backends). " -#~ "Block-sparse behavior is currently not " -#~ "supported" +#~ msgid "Return a phase damping channel with given :math:`\\gamma`" #~ msgstr "" #~ msgid "" -#~ "N (int): The dimension of the " -#~ "returned matrix. dtype: The dtype of " -#~ "the returned matrix. M (int): The " -#~ "dimension of the returned matrix." +#~ "\\begin{bmatrix}\n" +#~ " 1 & 0\\\\\n" +#~ " 0 & \\sqrt{1-\\gamma}\\\\\n" +#~ "\\end{bmatrix}\\qquad\n" +#~ "\\begin{bmatrix}\n" +#~ " 0 & 0\\\\\n" +#~ " 0 & \\sqrt{\\gamma}\\\\\n" +#~ "\\end{bmatrix}\n" +#~ "\n" #~ msgstr "" -#~ msgid "Return the function which is the grad function of input ``f``." +#~ msgid "The damping parameter of phase (:math:`\\gamma`)" #~ msgstr "" -#~ msgid "Example" +#~ msgid "A phase damping channel with given :math:`\\gamma`" #~ msgstr "" -#~ msgid "the function to be differentiated" +#~ msgid "Reset channel" #~ msgstr "" #~ msgid "" -#~ "the position of args in ``f`` that" -#~ " are to be differentiated, defaults " -#~ "to be 0" +#~ "\\begin{bmatrix}\n" +#~ " 1 & 0\\\\\n" +#~ " 0 & 0\\\\\n" +#~ "\\end{bmatrix}\\qquad\n" +#~ "\\begin{bmatrix}\n" +#~ " 0 & 1\\\\\n" +#~ " 0 & 0\\\\\n" +#~ "\\end{bmatrix}\n" +#~ "\n" #~ msgstr "" -#~ msgid "the grad function of ``f`` with the same set of arguments as ``f``" +#~ msgid "Check identity of a single qubit Kraus operators." #~ msgstr "" -#~ msgid "Return 1.j in as a tensor compatible with the backend." +#~ msgid "Examples:" #~ msgstr "" -#~ msgid "\"complex64\" or \"complex128\"" +#~ msgid "" +#~ "\\sum_{k}^{} K_k^{\\dagger} K_k = I\n" +#~ "\n" #~ msgstr "" -#~ msgid "1.j tensor" +#~ msgid "List of Kraus operators." #~ msgstr "" -#~ msgid "Return the elementwise imaginary value of a tensor ``a``." +#~ msgid "Quantum circuit: state simulator" #~ msgstr "" -#~ msgid "imaginary value of ``a``" +#~ msgid "``Circuit`` class. Simple usage demo below." #~ msgstr "" -#~ msgid "[summary]" +#~ msgid "Apply any gate with parameters on the circuit." #~ msgstr "" -#~ msgid "The possible options" +#~ msgid "Qubit number than the gate applies on." #~ msgstr "" -#~ msgid "Sampling output shape" +#~ msgid "Parameters for the gate" #~ msgstr "" -#~ msgid "" -#~ "probability for each option in a, " -#~ "defaults to None, as equal probability" -#~ " distribution" +#~ msgid "Apply cnot gate on the circuit." #~ msgstr "" #~ msgid "" -#~ "Call the random normal function with " -#~ "the random state management behind the" -#~ " scene." +#~ "Qubit number than the gate applies " +#~ "on. The matrix for the gate is" +#~ " .. math:: \\begin{bmatrix} 1.+0.j " +#~ "& 0.+0.j & 0.+0.j & 0.+0.j\\\\ " +#~ "0.+0.j & 1.+0.j & 0.+0.j & " +#~ "0.+0.j\\\\ 0.+0.j & 0.+0.j & 0.+0.j" +#~ " & 1.+0.j\\\\ 0.+0.j & 0.+0.j &" +#~ " 1.+0.j & 0.+0.j \\end{bmatrix}" #~ msgstr "" -#~ msgid "[description], defaults to 1" +#~ msgid "Qubit number than the gate applies on. The matrix for the gate is" #~ msgstr "" -#~ msgid "[description], defaults to \"32\"" +#~ msgid "" +#~ "\\begin{bmatrix} 1.+0.j & 0.+0.j & " +#~ "0.+0.j & 0.+0.j\\\\ 0.+0.j & 1.+0.j" +#~ " & 0.+0.j & 0.+0.j\\\\ 0.+0.j &" +#~ " 0.+0.j & 0.+0.j & 1.+0.j\\\\ " +#~ "0.+0.j & 0.+0.j & 1.+0.j & 0.+0.j" +#~ " \\end{bmatrix}" #~ msgstr "" -#~ msgid "Determine whether the type of input ``a`` is ``sparse``." +#~ msgid "Apply cr gate with parameters on the circuit." #~ msgstr "" -#~ msgid "input matrix ``a``" +#~ msgid "Apply crx gate with parameters on the circuit." #~ msgstr "" -#~ msgid "a bool indicating whether the matrix ``a`` is sparse" +#~ msgid "Apply cry gate with parameters on the circuit." #~ msgstr "" -#~ msgid "Return a boolean on whether ``a`` is a tensor in backend package." +#~ msgid "Apply crz gate with parameters on the circuit." #~ msgstr "" -#~ msgid "a tensor to be determined" +#~ msgid "Apply cy gate on the circuit." #~ msgstr "" -#~ msgid "whether ``a`` is a tensor" +#~ msgid "" +#~ "Qubit number than the gate applies " +#~ "on. The matrix for the gate is" +#~ " .. math:: \\begin{bmatrix} 1.+0.j " +#~ "& 0.+0.j & 0.+0.j & 0.+0.j\\\\ " +#~ "0.+0.j & 1.+0.j & 0.+0.j & " +#~ "0.+0.j\\\\ 0.+0.j & 0.+0.j & 0.+0.j" +#~ " & 0.-1.j\\\\ 0.+0.j & 0.+0.j &" +#~ " 0.+1.j & 0.+0.j \\end{bmatrix}" #~ msgstr "" -#~ msgid "Return the jitted version of function ``f``." +#~ msgid "" +#~ "\\begin{bmatrix} 1.+0.j & 0.+0.j & " +#~ "0.+0.j & 0.+0.j\\\\ 0.+0.j & 1.+0.j" +#~ " & 0.+0.j & 0.+0.j\\\\ 0.+0.j &" +#~ " 0.+0.j & 0.+0.j & 0.-1.j\\\\ " +#~ "0.+0.j & 0.+0.j & 0.+1.j & 0.+0.j" +#~ " \\end{bmatrix}" #~ msgstr "" -#~ msgid "function to be jitted" +#~ msgid "Apply cz gate on the circuit." #~ msgstr "" #~ msgid "" -#~ "index of args that doesn't regarded " -#~ "as tensor, only work for jax " -#~ "backend" +#~ "Qubit number than the gate applies " +#~ "on. The matrix for the gate is" +#~ " .. math:: \\begin{bmatrix} 1.+0.j " +#~ "& 0.+0.j & 0.+0.j & 0.+0.j\\\\ " +#~ "0.+0.j & 1.+0.j & 0.+0.j & " +#~ "0.+0.j\\\\ 0.+0.j & 0.+0.j & 1.+0.j" +#~ " & 0.+0.j\\\\ 0.+0.j & 0.+0.j &" +#~ " 0.+0.j & -1.+0.j \\end{bmatrix}" #~ msgstr "" #~ msgid "" -#~ "whether open XLA compilation, only works" -#~ " for tensorflow backend, defaults False " -#~ "since several ops has no XLA " -#~ "correspondence" -#~ msgstr "" - -#~ msgid "jitted version of ``f``" +#~ "\\begin{bmatrix} 1.+0.j & 0.+0.j & " +#~ "0.+0.j & 0.+0.j\\\\ 0.+0.j & 1.+0.j" +#~ " & 0.+0.j & 0.+0.j\\\\ 0.+0.j &" +#~ " 0.+0.j & 1.+0.j & 0.+0.j\\\\ " +#~ "0.+0.j & 0.+0.j & 0.+0.j & -1.+0.j" +#~ " \\end{bmatrix}" #~ msgstr "" -#~ msgid "" -#~ "Function that computes a (forward-mode)" -#~ " Jacobian-vector product of ``f``. " -#~ "Strictly speaking, this function is " -#~ "value_and_jvp." +#~ msgid "Apply exp gate with parameters on the circuit." #~ msgstr "" -#~ msgid "The function to compute jvp" +#~ msgid "Apply exp1 gate with parameters on the circuit." #~ msgstr "" -#~ msgid "input for ``f``" +#~ msgid "Apply h gate on the circuit." #~ msgstr "" -#~ msgid "tangents" +#~ msgid "" +#~ "Qubit number than the gate applies " +#~ "on. The matrix for the gate is" +#~ " .. math:: \\begin{bmatrix} " +#~ "0.70710677+0.j & 0.70710677+0.j\\\\ " +#~ "0.70710677+0.j & -0.70710677+0.j \\end{bmatrix}" #~ msgstr "" #~ msgid "" -#~ "(``f(*inputs)``, jvp_tensor), where jvp_tensor " -#~ "is the same shape as the output" -#~ " of ``f``" +#~ "\\begin{bmatrix} 0.70710677+0.j & " +#~ "0.70710677+0.j\\\\ 0.70710677+0.j & " +#~ "-0.70710677+0.j \\end{bmatrix}" #~ msgstr "" -#~ msgid "Return the kronecker product of two matrices ``a`` and ``b``." +#~ msgid "Apply i gate on the circuit." #~ msgstr "" -#~ msgid "kronecker product of ``a`` and ``b``" +#~ msgid "" +#~ "Qubit number than the gate applies " +#~ "on. The matrix for the gate is" +#~ " .. math:: \\begin{bmatrix} 1.+0.j " +#~ "& 0.+0.j\\\\ 0.+0.j & 1.+0.j " +#~ "\\end{bmatrix}" #~ msgstr "" -#~ msgid "Return the maximum of an array or maximum along an axis." +#~ msgid "" +#~ "\\begin{bmatrix} 1.+0.j & 0.+0.j\\\\ " +#~ "0.+0.j & 1.+0.j \\end{bmatrix}" #~ msgstr "" -#~ msgid "[description], defaults to None" +#~ msgid "Apply iswap gate on the circuit." #~ msgstr "" -#~ msgid "Return the minimum of an array or minimum along an axis." +#~ msgid "" +#~ "Qubit number than the gate applies " +#~ "on. The matrix for the gate is" +#~ " .. math:: \\begin{bmatrix} 1.+0.j " +#~ "& 0.+0.j & 0.+0.j & 0.+0.j\\\\ " +#~ "0.+0.j & 0.+0.j & 0.+1.j & " +#~ "0.+0.j\\\\ 0.+0.j & 0.+1.j & 0.+0.j" +#~ " & 0.+0.j\\\\ 0.+0.j & 0.+0.j &" +#~ " 0.+0.j & 1.+0.j \\end{bmatrix}" #~ msgstr "" #~ msgid "" -#~ "Return the numpy array of a tensor" -#~ " ``a``, but may not work in a" -#~ " jitted function." +#~ "\\begin{bmatrix} 1.+0.j & 0.+0.j & " +#~ "0.+0.j & 0.+0.j\\\\ 0.+0.j & 0.+0.j" +#~ " & 0.+1.j & 0.+0.j\\\\ 0.+0.j &" +#~ " 0.+1.j & 0.+0.j & 0.+0.j\\\\ " +#~ "0.+0.j & 0.+0.j & 0.+0.j & 1.+0.j" +#~ " \\end{bmatrix}" #~ msgstr "" -#~ msgid "numpy array of ``a``" +#~ msgid "Apply r gate with parameters on the circuit." #~ msgstr "" -#~ msgid "" -#~ "One-hot encodes the given ``a``. " -#~ "Each index in the input ``a`` is" -#~ " encoded as a vector of zeros " -#~ "of length ``num`` with the element " -#~ "at index set to one:" +#~ msgid "Apply rx gate with parameters on the circuit." #~ msgstr "" -#~ msgid "input tensor" +#~ msgid "Apply ry gate with parameters on the circuit." #~ msgstr "" -#~ msgid "number of features in onehot dimension" +#~ msgid "Apply rz gate with parameters on the circuit." #~ msgstr "" -#~ msgid "onehot tensor with the last extra dimension" +#~ msgid "Apply s gate on the circuit." #~ msgstr "" #~ msgid "" -#~ "Return an ones-matrix of dimension " -#~ "`dim` Depending on specific backends, " -#~ "`dim` has to be either an int " -#~ "(numpy, torch, tensorflow) or a " -#~ "`ShapeType` object (for block-sparse " -#~ "backends). Block-sparse behavior is " -#~ "currently not supported Args:" +#~ "Qubit number than the gate applies " +#~ "on. The matrix for the gate is" +#~ " .. math:: \\begin{bmatrix} 1.+0.j " +#~ "& 0.+0.j\\\\ 0.+0.j & 0.+1.j " +#~ "\\end{bmatrix}" #~ msgstr "" #~ msgid "" -#~ "shape (int): The dimension of the " -#~ "returned matrix. dtype: The dtype of " -#~ "the returned matrix." +#~ "\\begin{bmatrix} 1.+0.j & 0.+0.j\\\\ " +#~ "0.+0.j & 0.+1.j \\end{bmatrix}" +#~ msgstr "" + +#~ msgid "Apply sd gate on the circuit." #~ msgstr "" #~ msgid "" -#~ "A jax like split API, but it " -#~ "doesn't split the key generator for " -#~ "other backends. It is just for a" -#~ " consistent interface of random code; " -#~ "make sure you know what the " -#~ "function actually does. This function is" -#~ " mainly a utility to write backend" -#~ " agnostic code instead of doing magic" -#~ " things." +#~ "Qubit number than the gate applies " +#~ "on. The matrix for the gate is" +#~ " .. math:: \\begin{bmatrix} 1.+0.j " +#~ "& 0.+0.j\\\\ 0.+0.j & 0.-1.j " +#~ "\\end{bmatrix}" #~ msgstr "" -#~ msgid "Return the elementwise real value of a tensor ``a``." +#~ msgid "" +#~ "\\begin{bmatrix} 1.+0.j & 0.+0.j\\\\ " +#~ "0.+0.j & 0.-1.j \\end{bmatrix}" #~ msgstr "" -#~ msgid "real value of ``a``" +#~ msgid "Apply swap gate on the circuit." #~ msgstr "" #~ msgid "" -#~ "Rectified linear unit activation function. " -#~ "Computes the element-wise function:" +#~ "Qubit number than the gate applies " +#~ "on. The matrix for the gate is" +#~ " .. math:: \\begin{bmatrix} 1.+0.j " +#~ "& 0.+0.j & 0.+0.j & 0.+0.j\\\\ " +#~ "0.+0.j & 0.+0.j & 1.+0.j & " +#~ "0.+0.j\\\\ 0.+0.j & 1.+0.j & 0.+0.j" +#~ " & 0.+0.j\\\\ 0.+0.j & 0.+0.j &" +#~ " 0.+0.j & 1.+0.j \\end{bmatrix}" #~ msgstr "" -#~ msgid "\\mathrm{relu}(x)=\\max(x,0)" +#~ msgid "" +#~ "\\begin{bmatrix} 1.+0.j & 0.+0.j & " +#~ "0.+0.j & 0.+0.j\\\\ 0.+0.j & 0.+0.j" +#~ " & 1.+0.j & 0.+0.j\\\\ 0.+0.j &" +#~ " 1.+0.j & 0.+0.j & 0.+0.j\\\\ " +#~ "0.+0.j & 0.+0.j & 0.+0.j & 1.+0.j" +#~ " \\end{bmatrix}" #~ msgstr "" -#~ msgid "Input tensor" +#~ msgid "Apply t gate on the circuit." #~ msgstr "" -#~ msgid "Tensor after relu" +#~ msgid "" +#~ "Qubit number than the gate applies " +#~ "on. The matrix for the gate is" +#~ " .. math:: \\begin{bmatrix} 1. &" +#~ " +0.j & 0. & +0.j\\\\ 0. &" +#~ " +0.j & 0.70710677+0.70710677j \\end{bmatrix}" #~ msgstr "" #~ msgid "" -#~ "Roughly equivalent to operand[indices] = " -#~ "updates, indices only support shape with" -#~ " rank 2 for now." +#~ "\\begin{bmatrix} 1. & +0.j & 0. " +#~ "& +0.j\\\\ 0. & +0.j & " +#~ "0.70710677+0.70710677j \\end{bmatrix}" #~ msgstr "" -#~ msgid "Set the random state attached to the backend." +#~ msgid "Apply td gate on the circuit." #~ msgstr "" -#~ msgid "the random seed, defaults to be None" +#~ msgid "" +#~ "Qubit number than the gate applies " +#~ "on. The matrix for the gate is" +#~ " .. math:: \\begin{bmatrix} 1. &" +#~ " +0.j & 0. & +0.j\\\\ 0. &" +#~ " +0.j & 0.70710677-0.70710677j \\end{bmatrix}" #~ msgstr "" #~ msgid "" -#~ "If set to be true, only get " -#~ "the random state in return instead " -#~ "of setting the state on the " -#~ "backend" +#~ "\\begin{bmatrix} 1. & +0.j & 0. " +#~ "& +0.j\\\\ 0. & +0.j & " +#~ "0.70710677-0.70710677j \\end{bmatrix}" #~ msgstr "" -#~ msgid "Return the elementwise sine of a tensor ``a``." +#~ msgid "Apply toffoli gate on the circuit." #~ msgstr "" -#~ msgid "sine of ``a``" +#~ msgid "" +#~ "Qubit number than the gate applies " +#~ "on. The matrix for the gate is" +#~ " .. math:: \\begin{bmatrix} 1.+0.j " +#~ "& 0.+0.j & 0.+0.j & 0.+0.j & " +#~ "0.+0.j & 0.+0.j & 0.+0.j & " +#~ "0.+0.j\\\\ 0.+0.j & 1.+0.j & 0.+0.j" +#~ " & 0.+0.j & 0.+0.j & 0.+0.j &" +#~ " 0.+0.j & 0.+0.j\\\\ 0.+0.j & " +#~ "0.+0.j & 1.+0.j & 0.+0.j & 0.+0.j" +#~ " & 0.+0.j & 0.+0.j & 0.+0.j\\\\" +#~ " 0.+0.j & 0.+0.j & 0.+0.j & " +#~ "1.+0.j & 0.+0.j & 0.+0.j & 0.+0.j" +#~ " & 0.+0.j\\\\ 0.+0.j & 0.+0.j &" +#~ " 0.+0.j & 0.+0.j & 1.+0.j & " +#~ "0.+0.j & 0.+0.j & 0.+0.j\\\\ 0.+0.j" +#~ " & 0.+0.j & 0.+0.j & 0.+0.j &" +#~ " 0.+0.j & 1.+0.j & 0.+0.j & " +#~ "0.+0.j\\\\ 0.+0.j & 0.+0.j & 0.+0.j" +#~ " & 0.+0.j & 0.+0.j & 0.+0.j &" +#~ " 0.+0.j & 1.+0.j\\\\ 0.+0.j & " +#~ "0.+0.j & 0.+0.j & 0.+0.j & 0.+0.j" +#~ " & 0.+0.j & 1.+0.j & 0.+0.j " +#~ "\\end{bmatrix}" #~ msgstr "" -#~ msgid "Return the total number of elements in ``a`` in tensor form." +#~ msgid "" +#~ "\\begin{bmatrix} 1.+0.j & 0.+0.j & " +#~ "0.+0.j & 0.+0.j & 0.+0.j & 0.+0.j" +#~ " & 0.+0.j & 0.+0.j\\\\ 0.+0.j &" +#~ " 1.+0.j & 0.+0.j & 0.+0.j & " +#~ "0.+0.j & 0.+0.j & 0.+0.j & " +#~ "0.+0.j\\\\ 0.+0.j & 0.+0.j & 1.+0.j" +#~ " & 0.+0.j & 0.+0.j & 0.+0.j &" +#~ " 0.+0.j & 0.+0.j\\\\ 0.+0.j & " +#~ "0.+0.j & 0.+0.j & 1.+0.j & 0.+0.j" +#~ " & 0.+0.j & 0.+0.j & 0.+0.j\\\\" +#~ " 0.+0.j & 0.+0.j & 0.+0.j & " +#~ "0.+0.j & 1.+0.j & 0.+0.j & 0.+0.j" +#~ " & 0.+0.j\\\\ 0.+0.j & 0.+0.j &" +#~ " 0.+0.j & 0.+0.j & 0.+0.j & " +#~ "1.+0.j & 0.+0.j & 0.+0.j\\\\ 0.+0.j" +#~ " & 0.+0.j & 0.+0.j & 0.+0.j &" +#~ " 0.+0.j & 0.+0.j & 0.+0.j & " +#~ "1.+0.j\\\\ 0.+0.j & 0.+0.j & 0.+0.j" +#~ " & 0.+0.j & 0.+0.j & 0.+0.j &" +#~ " 1.+0.j & 0.+0.j \\end{bmatrix}" #~ msgstr "" -#~ msgid "the total number of elements in ``a``" +#~ msgid "Apply wroot gate on the circuit." #~ msgstr "" #~ msgid "" -#~ "Softmax function. Computes the function " -#~ "which rescales elements to the range " -#~ "[0,1] such that the elements along " -#~ "axis sum to 1." +#~ "Qubit number than the gate applies " +#~ "on. The matrix for the gate is" +#~ " .. math:: \\begin{bmatrix} " +#~ "0.70710677+0.j & -0.5 & -0.5j\\\\ 0.5" +#~ " & -0.5j & 0.70710677+0.j \\end{bmatrix}" #~ msgstr "" -#~ msgid "\\mathrm{softmax}(x) = \\frac{\\exp(x_i)}{\\sum_j \\exp(x_j)}" +#~ msgid "" +#~ "\\begin{bmatrix} 0.70710677+0.j & -0.5 &" +#~ " -0.5j\\\\ 0.5 & -0.5j & " +#~ "0.70710677+0.j \\end{bmatrix}" #~ msgstr "" -#~ msgid "Tensor" +#~ msgid "Apply x gate on the circuit." #~ msgstr "" #~ msgid "" -#~ "A dimension along which Softmax will " -#~ "be computed , defaults to None for" -#~ " all axis sum." +#~ "Qubit number than the gate applies " +#~ "on. The matrix for the gate is" +#~ " .. math:: \\begin{bmatrix} 0.+0.j " +#~ "& 1.+0.j\\\\ 1.+0.j & 0.+0.j " +#~ "\\end{bmatrix}" #~ msgstr "" -#~ msgid "concatenated tensor" +#~ msgid "" +#~ "\\begin{bmatrix} 0.+0.j & 1.+0.j\\\\ " +#~ "1.+0.j & 0.+0.j \\end{bmatrix}" #~ msgstr "" -#~ msgid "Solve the linear system Ax=b and return the solution x." +#~ msgid "Apply y gate on the circuit." #~ msgstr "" -#~ msgid "The multiplied matrix." +#~ msgid "" +#~ "Qubit number than the gate applies " +#~ "on. The matrix for the gate is" +#~ " .. math:: \\begin{bmatrix} 0.+0.j " +#~ "& 0.-1.j\\\\ 0.+1.j & 0.+0.j " +#~ "\\end{bmatrix}" #~ msgstr "" -#~ msgid "The resulted matrix." +#~ msgid "" +#~ "\\begin{bmatrix} 0.+0.j & 0.-1.j\\\\ " +#~ "0.+1.j & 0.+0.j \\end{bmatrix}" #~ msgstr "" -#~ msgid "The solution of the linear system." +#~ msgid "Apply z gate on the circuit." #~ msgstr "" -#~ msgid "A sparse matrix multiplies a dense matrix." +#~ msgid "" +#~ "Qubit number than the gate applies " +#~ "on. The matrix for the gate is" +#~ " .. math:: \\begin{bmatrix} 1.+0.j " +#~ "& 0.+0.j\\\\ 0.+0.j & -1.+0.j " +#~ "\\end{bmatrix}" #~ msgstr "" -#~ msgid "a sparse matrix" +#~ msgid "" +#~ "\\begin{bmatrix} 1.+0.j & 0.+0.j\\\\ " +#~ "0.+0.j & -1.+0.j \\end{bmatrix}" #~ msgstr "" -#~ msgid "a dense matrix" +#~ msgid "Circuit object based on state simulator." #~ msgstr "" -#~ msgid "dense matrix" +#~ msgid "The number of qubits in the circuit." #~ msgstr "" #~ msgid "" -#~ "Concatenates a sequence of tensors ``a``" -#~ " along a new dimension ``axis``." +#~ "If not None, the initial state of" +#~ " the circuit is taken as ``inputs``" +#~ " instead of :math:`\\vert 0\\rangle^n` " +#~ "qubits, defaults to None" #~ msgstr "" -#~ msgid "List of tensors in the same shape" +#~ msgid "(Nodes, dangling Edges) for a MPS like initial wavefunction" #~ msgstr "" -#~ msgid "the stack axis, defaults to 0" +#~ msgid "" +#~ "dict if two qubit gate is ready" +#~ " for split, including parameters for " +#~ "at least one of ``max_singular_values`` " +#~ "and ``max_truncation_err``." #~ msgstr "" -#~ msgid "stateful register for each package" +#~ msgid "" +#~ "Monte Carlo trajectory simulation of " +#~ "general Kraus channel whose Kraus " +#~ "operators cannot be amplified to unitary" +#~ " operators. For unitary operators composed" +#~ " Kraus channel, :py:meth:`unitary_kraus` is " +#~ "much faster." #~ msgstr "" -#~ msgid "shape of output sampling tensor" +#~ msgid "" +#~ "This function is jittable in theory. " +#~ "But only jax+GPU combination is " +#~ "recommended for jit since the graph " +#~ "building time is too long for " +#~ "other backend options; though the " +#~ "running time of the function is " +#~ "very fast for every case." #~ msgstr "" -#~ msgid "only real data type is supported, \"32\" or \"64\", defaults to \"32\"" +#~ msgid "list of ``tn.Node`` for Kraus operators" #~ msgstr "" -#~ msgid "Uniform random sampler from ``low`` to ``high``." +#~ msgid "the qubits index that Kraus channel is applied on" #~ msgstr "" -#~ msgid "shape of output sampling tensor, defaults to 1" +#~ msgid "" +#~ "random tensor between 0 or 1, " +#~ "defaults to be None, the random " +#~ "number will be generated automatically" #~ msgstr "" -#~ msgid "Stop backpropagation from ``a``." +#~ msgid "Compute the expectation of corresponding operators." #~ msgstr "" -#~ msgid "``branches[index]()``" +#~ msgid "" +#~ "operator and its position on the " +#~ "circuit, eg. ``(tc.gates.z(), [1, ]), " +#~ "(tc.gates.x(), [2, ])`` is for operator" +#~ " :math:`Z_1X_2`" #~ msgstr "" -#~ msgid "Constructs a tensor by tiling a given tensor." +#~ msgid "" +#~ "if True, then the wavefunction tensor" +#~ " is cached for further expectation " +#~ "evaluation, defaults to be true" #~ msgstr "" -#~ msgid "1d tensor with length the same as the rank of ``a``" +#~ msgid "Tensor with one element" #~ msgstr "" -#~ msgid "Convert a sparse matrix to dense tensor." +#~ msgid "[WIP], check whether the circuit is legal." #~ msgstr "" -#~ msgid "the resulted dense matrix" +#~ msgid "the bool indicating whether the circuit is legal" #~ msgstr "" -#~ msgid "" -#~ "Find the unique elements and their " -#~ "corresponding counts of the given tensor" -#~ " ``a``." +#~ msgid "Take measurement to the given quantum lines." #~ msgstr "" -#~ msgid "Unique elements, corresponding counts" +#~ msgid "measure on which quantum line" #~ msgstr "" -#~ msgid "Return the function which returns the value and grad of ``f``." +#~ msgid "if true, theoretical probability is also returned" #~ msgstr "" #~ msgid "" -#~ "the value and grad function of " -#~ "``f`` with the same set of " -#~ "arguments as ``f``" +#~ "Middle measurement in z-basis on the " +#~ "circuit, note the wavefunction output is" +#~ " not normalized with ``mid_measurement`` " +#~ "involved, one should normalize the state" +#~ " manually if needed." #~ msgstr "" -#~ msgid "" -#~ "Return the VVAG function of ``f``. " -#~ "The inputs for ``f`` is (args[0], " -#~ "args[1], args[2], ...), and the output" -#~ " of ``f`` is a scalar. Suppose " -#~ "VVAG(f) is a function with inputs " -#~ "in the form (vargs[0], args[1], args[2]," -#~ " ...), where vagrs[0] has one extra" -#~ " dimension than args[0] in the first" -#~ " axis and consistent with args[0] in" -#~ " shape for remaining dimensions, i.e. " -#~ "shape(vargs[0]) = [batch] + shape(args[0])." -#~ " (We only cover cases where " -#~ "``vectorized_argnums`` defaults to 0 here " -#~ "for demonstration). VVAG(f) returns a " -#~ "tuple as a value tensor with shape" -#~ " [batch, 1] and a gradient tuple " -#~ "with shape: ([batch]+shape(args[argnum]) for " -#~ "argnum in argnums). The gradient for " -#~ "argnums=k is defined as" +#~ msgid "the index of qubit that the Z direction postselection applied on" #~ msgstr "" -#~ msgid "" -#~ "g^k = \\frac{\\partial \\sum_{i\\in batch} " -#~ "f(vargs[0][i], args[1], ...)}{\\partial args[k]}" +#~ msgid "0 for spin up, 1 for spin down, defaults to be 0" #~ msgstr "" -#~ msgid "Therefore, if argnums=0, the gradient is reduced to" +#~ msgid "Reference: arXiv:1201.3974." #~ msgstr "" -#~ msgid "g^0_i = \\frac{\\partial f(vargs[0][i])}{\\partial vargs[0][i]}" +#~ msgid "sampled bit string and the corresponding theoretical probability" #~ msgstr "" -#~ msgid "" -#~ ", which is specifically suitable for " -#~ "batched VQE optimization, where args[0] " -#~ "is the circuit parameters." +#~ msgid "Replace the input state with the circuit structure unchanged." #~ msgstr "" -#~ msgid "And if argnums=1, the gradient is like" +#~ msgid "Input wavefunction." #~ msgstr "" #~ msgid "" -#~ "g^1_i = \\frac{\\partial \\sum_j " -#~ "f(vargs[0][j], args[1])}{\\partial args[1][i]}\n" -#~ "\n" +#~ "Replace the input state in MPS " +#~ "representation while keep the circuit " +#~ "structure unchanged." #~ msgstr "" -#~ msgid "" -#~ ", which is suitable for quantum " -#~ "machine learning scenarios, where ``f`` " -#~ "is the loss function, args[0] " -#~ "corresponds to the input data and " -#~ "args[1] corresponds to the weights in" -#~ " the QML model." +#~ msgid "Compute the output wavefunction from the circuit." #~ msgstr "" -#~ msgid "" -#~ "the args to be vectorized, these " -#~ "arguments should share the same batch" -#~ " shape in the fist dimension" +#~ msgid "the str indicating the form of the output wavefunction" #~ msgstr "" -#~ msgid "" -#~ "Function that computes the dot product" -#~ " between a vector v and the " -#~ "Jacobian of the given function at " -#~ "the point given by the inputs. " -#~ "(reverse mode AD relevant) Strictly " -#~ "speaking, this function is value_and_vjp." +#~ msgid "Tensor with the corresponding shape" #~ msgstr "" -#~ msgid "the function to carry out vjp calculation" +#~ msgid "Compute :math:`\\langle bra\\vert ops \\vert ket\\rangle`" #~ msgstr "" -#~ msgid "" -#~ "value vector or gradient from downstream" -#~ " in reverse mode AD the same " -#~ "shape as return of function ``f``" +#~ msgid "Example 1 (:math:`bra` is same as :math:`ket`)" #~ msgstr "" -#~ msgid "" -#~ "(``f(*inputs)``, vjp_tensor), where vjp_tensor " -#~ "is the same shape as inputs" +#~ msgid "Example 2 (:math:`bra` is different from :math:`ket`)" #~ msgstr "" -#~ msgid "" -#~ "Return the vectorized map or batched " -#~ "version of ``f`` on the first " -#~ "extra axis. The general interface " -#~ "supports ``f`` with multiple arguments " -#~ "and broadcast in the fist dimension." +#~ msgid "[description], defaults to None, which is the same as ``ket``" #~ msgstr "" -#~ msgid "function to be broadcasted." +#~ msgid "[description], defaults to True" #~ msgstr "" -#~ msgid "vmap version of ``f``" +#~ msgid "[description], defaults to False" #~ msgstr "" #~ msgid "" -#~ "Return a zeros-matrix of dimension " -#~ "`dim` Depending on specific backends, " -#~ "`dim` has to be either an int " -#~ "(numpy, torch, tensorflow) or a " -#~ "`ShapeType` object (for block-sparse " -#~ "backends)." +#~ "Not an ideal visualization for quantum" +#~ " circuit, but reserve here as a " +#~ "general approch to show tensornetwork " +#~ "[Deperacted, use ``qir2tex instead``]" #~ msgstr "" -#~ msgid "Block-sparse behavior is currently not supported Args:" +#~ msgid "Constants and setups" #~ msgstr "" -#~ msgid "Backend magic inherited from tensornetwork: numpy backend" +#~ msgid "" +#~ "To set runtime contractor of the " +#~ "tensornetwork for a better contraction " +#~ "path." #~ msgstr "" #~ msgid "" -#~ "Bases: " -#~ ":py:class:`tensornetwork.backends.numpy.numpy_backend.NumPyBackend`" +#~ "\"auto\", \"greedy\", \"branch\", \"plain\", " +#~ "\"tng\", \"custom\", \"custom_stateful\". defaults" +#~ " to None (\"auto\")" +#~ msgstr "" + +#~ msgid "Valid for \"custom\" or \"custom_stateful\" as method, defaults to None" #~ msgstr "" #~ msgid "" -#~ "see the original backend API at " -#~ "`numpy backend " -#~ "`_" +#~ "It is not very useful, as " +#~ "``memory_limit`` leads to ``branch`` " +#~ "contraction instead of ``greedy`` which " +#~ "is rather slow, defaults to None" #~ msgstr "" -#~ msgid "Backend magic inherited from tensornetwork: pytorch backend" +#~ msgid "Tensornetwork version is too low to support some of the contractors." #~ msgstr "" -#~ msgid "" -#~ "Bases: " -#~ ":py:class:`tensornetwork.backends.pytorch.pytorch_backend.PyTorchBackend`" +#~ msgid "Unknown method options." #~ msgstr "" -#~ msgid "" -#~ "See the original backend API at " -#~ "``pytorch backend``. " -#~ "``_" +#~ msgid "The new tensornetwork with its contractor set." +#~ msgstr "" + +#~ msgid "To set the runtime numerical dtype of tensors." #~ msgstr "" #~ msgid "" -#~ "Note the functionality provided by " -#~ "pytorch backend is incomplete, it " -#~ "currenly lacks native efficicent jit and" -#~ " vmap support." +#~ "\"complex64\" or \"complex128\", defaults to" +#~ " None, which is equivalent to " +#~ "\"complex64\"." #~ msgstr "" -#~ msgid "Backend magic inherited from tensornetwork: tensorflow backend" +#~ msgid "The naive state-vector simulator contraction path." #~ msgstr "" -#~ msgid "" -#~ "Bases: " -#~ ":py:class:`tensornetwork.backends.tensorflow.tensorflow_backend.TensorFlowBackend`" +#~ msgid "The list of ``tn.Node``." #~ msgstr "" -#~ msgid "" -#~ "See the original backend API at " -#~ "`'tensorflow backend''. " -#~ "`_" +#~ msgid "The list of dangling node edges, defaults to be None." #~ msgstr "" -#~ msgid "Some common noise quantum channels." +#~ msgid "The ``tn.Node`` after contraction" +#~ msgstr "" + +#~ msgid "To set the runtime backend of tensorcircuit." #~ msgstr "" #~ msgid "" -#~ "Return an amplitude damping channel. " -#~ "Notice: Amplitude damping corrspondings to " -#~ "p = 1." +#~ "Note: ``tc.set_backend`` and " +#~ "``tc.cons.set_tensornetwork_backend`` are the same." #~ msgstr "" #~ msgid "" -#~ "\\sqrt{p}\n" -#~ "\\begin{bmatrix}\n" -#~ " 1 & 0\\\\\n" -#~ " 0 & \\sqrt{1-\\gamma}\\\\\n" -#~ "\\end{bmatrix}\\qquad\n" -#~ "\\sqrt{p}\n" -#~ "\\begin{bmatrix}\n" -#~ " 0 & \\sqrt{\\gamma}\\\\\n" -#~ " 0 & 0\\\\\n" -#~ "\\end{bmatrix}\\qquad\n" -#~ "\\sqrt{1-p}\n" -#~ "\\begin{bmatrix}\n" -#~ " \\sqrt{1-\\gamma} & 0\\\\\n" -#~ " 0 & 1\\\\\n" -#~ "\\end{bmatrix}\\qquad\n" -#~ "\\sqrt{1-p}\n" -#~ "\\begin{bmatrix}\n" -#~ " 0 & 0\\\\\n" -#~ " \\sqrt{\\gamma} & 0\\\\\n" -#~ "\\end{bmatrix}\n" -#~ "\n" +#~ "\"numpy\", \"tensorflow\", \"jax\", \"pytorch\". " +#~ "defaults to None, which gives the " +#~ "same behavior as " +#~ "``tensornetwork.backend_contextmanager.get_default_backend()``." #~ msgstr "" -#~ msgid "the damping parameter of amplitude (:math:`\\gamma`)" +#~ msgid "Whether the object should be set as global." #~ msgstr "" -#~ msgid ":math:`p`" +#~ msgid "Quantum circuit class but with density matrix simulator" #~ msgstr "" -#~ msgid "An amplitude damping channel with given :math:`\\gamma` and :math:`p`" +#~ msgid "Quantum circuit class but with density matrix simulator: v2" #~ msgstr "" -#~ msgid "Return a Depolarizing Channel" +#~ msgid "Bases: :py:class:`tensorcircuit.densitymatrix.DMCircuit`" +#~ msgstr "" + +#~ msgid "Experimental features" #~ msgstr "" #~ msgid "" -#~ "\\sqrt{1-p_x-p_y-p_z}\n" -#~ "\\begin{bmatrix}\n" -#~ " 1 & 0\\\\\n" -#~ " 0 & 1\\\\\n" -#~ "\\end{bmatrix}\\qquad\n" -#~ "\\sqrt{p_x}\n" -#~ "\\begin{bmatrix}\n" -#~ " 0 & 1\\\\\n" -#~ " 1 & 0\\\\\n" -#~ "\\end{bmatrix}\\qquad\n" -#~ "\\sqrt{p_y}\n" -#~ "\\begin{bmatrix}\n" -#~ " 0 & -1j\\\\\n" -#~ " 1j & 0\\\\\n" -#~ "\\end{bmatrix}\\qquad\n" -#~ "\\sqrt{p_z}\n" -#~ "\\begin{bmatrix}\n" -#~ " 1 & 0\\\\\n" -#~ " 0 & -1\\\\\n" -#~ "\\end{bmatrix}\n" -#~ "\n" +#~ "Declarations of single-qubit and two-" +#~ "qubit gates and their corresponding " +#~ "matrix." #~ msgstr "" -#~ msgid ":math:`p_x`" +#~ msgid "Bases: :py:class:`tensornetwork.network_components.Node`" #~ msgstr "" -#~ msgid ":math:`p_y`" +#~ msgid "Wrapper of tn.Node, quantum gate" #~ msgstr "" -#~ msgid ":math:`p_z`" +#~ msgid "Bases: :py:class:`tensorcircuit.gates.GateF`" #~ msgstr "" -#~ msgid "Sequences of Gates" +#~ msgid "Note one should provide the gate with properly reshaped." #~ msgstr "" -#~ msgid "Convert Kraus operators to one Tensor (as one Super Gate)." +#~ msgid "corresponding gate" #~ msgstr "" -#~ msgid "" -#~ "\\sum_{k}^{} K_k \\otimes K_k^{\\dagger}\n" -#~ "\n" +#~ msgid "The name of the gate." #~ msgstr "" -#~ msgid "A sequence of Gate" +#~ msgid "the resulted gate" #~ msgstr "" -#~ msgid "The corresponding Tensor of the list of Kraus operators" +#~ msgid "Convert the inputs to Tensor with specified dtype." #~ msgstr "" -#~ msgid "Return a phase damping channel with given :math:`\\gamma`" +#~ msgid "inputs" +#~ msgstr "" + +#~ msgid "dtype of the output Tensors" +#~ msgstr "" + +#~ msgid "List of Tensors" +#~ msgstr "" + +#~ msgid "Returns a LaTeX bmatrix." +#~ msgstr "" + +#~ msgid "Formatted Display:" #~ msgstr "" #~ msgid "" -#~ "\\begin{bmatrix}\n" -#~ " 1 & 0\\\\\n" -#~ " 0 & \\sqrt{1-\\gamma}\\\\\n" -#~ "\\end{bmatrix}\\qquad\n" -#~ "\\begin{bmatrix}\n" -#~ " 0 & 0\\\\\n" -#~ " 0 & \\sqrt{\\gamma}\\\\\n" -#~ "\\end{bmatrix}\n" +#~ "\\begin{bmatrix} 1.+0.j & 0.+0.j\\\\ " +#~ "0.+0.j & 1.+0.j \\end{bmatrix}\n" #~ "\n" #~ msgstr "" -#~ msgid "The damping parameter of phase (:math:`\\gamma`)" +#~ msgid "2D numpy array" #~ msgstr "" -#~ msgid "A phase damping channel with given :math:`\\gamma`" +#~ msgid "ValueError(\"bmatrix can at most display two dimensions\")" #~ msgstr "" -#~ msgid "Reset channel" +#~ msgid "latex str for bmatrix of array a" #~ msgstr "" #~ msgid "" -#~ "\\begin{bmatrix}\n" -#~ " 1 & 0\\\\\n" -#~ " 0 & 0\\\\\n" -#~ "\\end{bmatrix}\\qquad\n" -#~ "\\begin{bmatrix}\n" -#~ " 0 & 1\\\\\n" -#~ " 0 & 0\\\\\n" -#~ "\\end{bmatrix}\n" -#~ "\n" +#~ "Controlled rotation gate, when the " +#~ "control bit is 1, `rgate` is " +#~ "applied on the target gate." #~ msgstr "" -#~ msgid "Check identity of a single qubit Kraus operators." +#~ msgid "angle in radians" #~ msgstr "" -#~ msgid "Examples:" +#~ msgid "CR Gate" #~ msgstr "" #~ msgid "" -#~ "\\sum_{k}^{} K_k^{\\dagger} K_k = I\n" -#~ "\n" +#~ "Faster exponential gate, directly implemented" +#~ " based on RHS, only work when: " +#~ ":math:`U^2` is identity matrix." #~ msgstr "" -#~ msgid "List of Kraus operators." +#~ msgid "" +#~ "\\rm{exp}(U) &= e^{-i \\theta U} \\\\\n" +#~ " &= \\cos(\\theta) I - j \\sin(\\theta) U \\\\\n" +#~ "\n" #~ msgstr "" -#~ msgid "Quantum circuit: state simulator" +#~ msgid "input unitary (U)" #~ msgstr "" -#~ msgid "``Circuit`` class. Simple usage demo below." +#~ msgid "suffix of Gate name" #~ msgstr "" -#~ msgid "Apply any gate with parameters on the circuit." +#~ msgid "Exponential Gate" #~ msgstr "" -#~ msgid "Qubit number than the gate applies on." +#~ msgid "Exponential gate." #~ msgstr "" -#~ msgid "Parameters for the gate" +#~ msgid "" +#~ "\\rm{exp}(U) = e^{-i \\theta U}\n" +#~ "\n" #~ msgstr "" -#~ msgid "Apply cnot gate on the circuit." +#~ msgid "iSwap gate." #~ msgstr "" #~ msgid "" -#~ "Qubit number than the gate applies " -#~ "on. The matrix for the gate is" -#~ " .. math:: \\begin{bmatrix} 1.+0.j " -#~ "& 0.+0.j & 0.+0.j & 0.+0.j\\\\ " -#~ "0.+0.j & 1.+0.j & 0.+0.j & " -#~ "0.+0.j\\\\ 0.+0.j & 0.+0.j & 0.+0.j" -#~ " & 1.+0.j\\\\ 0.+0.j & 0.+0.j &" -#~ " 1.+0.j & 0.+0.j \\end{bmatrix}" +#~ "iSwap(\\theta) =\n" +#~ "\\begin{pmatrix}\n" +#~ " 1 & 0 & 0 & 0\\\\\n" +#~ " 0 & \\cos(\\frac{\\pi}{2} \\theta )" +#~ " & j \\sin(\\frac{\\pi}{2} \\theta ) " +#~ "& 0\\\\\n" +#~ " 0 & j \\sin(\\frac{\\pi}{2} \\theta" +#~ " ) & \\cos(\\frac{\\pi}{2} \\theta ) " +#~ "& 0\\\\\n" +#~ " 0 & 0 & 0 & 1\\\\\n" +#~ "\\end{pmatrix}\n" +#~ "\n" #~ msgstr "" -#~ msgid "Qubit number than the gate applies on. The matrix for the gate is" +#~ msgid "iSwap Gate" #~ msgstr "" -#~ msgid "" -#~ "\\begin{bmatrix} 1.+0.j & 0.+0.j & " -#~ "0.+0.j & 0.+0.j\\\\ 0.+0.j & 1.+0.j" -#~ " & 0.+0.j & 0.+0.j\\\\ 0.+0.j &" -#~ " 0.+0.j & 0.+0.j & 1.+0.j\\\\ " -#~ "0.+0.j & 0.+0.j & 1.+0.j & 0.+0.j" -#~ " \\end{bmatrix}" +#~ msgid "Convert Gate to numpy array." #~ msgstr "" -#~ msgid "Apply cr gate with parameters on the circuit." +#~ msgid "input Gate" #~ msgstr "" -#~ msgid "Apply crx gate with parameters on the circuit." +#~ msgid "corresponding Tensor" #~ msgstr "" -#~ msgid "Apply cry gate with parameters on the circuit." +#~ msgid "" +#~ "Inner helper function to generate gate" +#~ " functions, such as ``z()`` from " +#~ "``_z_matrix``" #~ msgstr "" -#~ msgid "Apply crz gate with parameters on the circuit." +#~ msgid "General single qubit rotation gate" #~ msgstr "" -#~ msgid "Apply cy gate on the circuit." +#~ msgid "" +#~ "R(\\theta, \\phi, \\alpha) = i \\cos(\\theta) I\n" +#~ "\n" #~ msgstr "" #~ msgid "" -#~ "Qubit number than the gate applies " -#~ "on. The matrix for the gate is" -#~ " .. math:: \\begin{bmatrix} 1.+0.j " -#~ "& 0.+0.j & 0.+0.j & 0.+0.j\\\\ " -#~ "0.+0.j & 1.+0.j & 0.+0.j & " -#~ "0.+0.j\\\\ 0.+0.j & 0.+0.j & 0.+0.j" -#~ " & 0.-1.j\\\\ 0.+0.j & 0.+0.j &" -#~ " 0.+1.j & 0.+0.j \\end{bmatrix}" +#~ "- i \\cos(\\phi) \\sin(\\alpha) \\sin(\\theta) X\n" +#~ "\n" #~ msgstr "" #~ msgid "" -#~ "\\begin{bmatrix} 1.+0.j & 0.+0.j & " -#~ "0.+0.j & 0.+0.j\\\\ 0.+0.j & 1.+0.j" -#~ " & 0.+0.j & 0.+0.j\\\\ 0.+0.j &" -#~ " 0.+0.j & 0.+0.j & 0.-1.j\\\\ " -#~ "0.+0.j & 0.+0.j & 0.+1.j & 0.+0.j" -#~ " \\end{bmatrix}" +#~ "- i \\sin(\\phi) \\sin(\\alpha) \\sin(\\theta) Y\n" +#~ "\n" #~ msgstr "" -#~ msgid "Apply cz gate on the circuit." +#~ msgid "" +#~ "- i \\sin(\\theta) \\cos(\\alpha) Z\n" +#~ "\n" #~ msgstr "" -#~ msgid "" -#~ "Qubit number than the gate applies " -#~ "on. The matrix for the gate is" -#~ " .. math:: \\begin{bmatrix} 1.+0.j " -#~ "& 0.+0.j & 0.+0.j & 0.+0.j\\\\ " -#~ "0.+0.j & 1.+0.j & 0.+0.j & " -#~ "0.+0.j\\\\ 0.+0.j & 0.+0.j & 1.+0.j" -#~ " & 0.+0.j\\\\ 0.+0.j & 0.+0.j &" -#~ " 0.+0.j & -1.+0.j \\end{bmatrix}" +#~ msgid "R Gate" #~ msgstr "" -#~ msgid "" -#~ "\\begin{bmatrix} 1.+0.j & 0.+0.j & " -#~ "0.+0.j & 0.+0.j\\\\ 0.+0.j & 1.+0.j" -#~ " & 0.+0.j & 0.+0.j\\\\ 0.+0.j &" -#~ " 0.+0.j & 1.+0.j & 0.+0.j\\\\ " -#~ "0.+0.j & 0.+0.j & 0.+0.j & -1.+0.j" -#~ " \\end{bmatrix}" +#~ msgid "Random single qubit gate described in https://arxiv.org/abs/2002.07730." #~ msgstr "" -#~ msgid "Apply exp gate with parameters on the circuit." +#~ msgid "A random single qubit gate" #~ msgstr "" -#~ msgid "Apply exp1 gate with parameters on the circuit." +#~ msgid "Returns a random two-qubit gate." #~ msgstr "" -#~ msgid "Apply h gate on the circuit." +#~ msgid "a random two-qubit gate" #~ msgstr "" #~ msgid "" -#~ "Qubit number than the gate applies " -#~ "on. The matrix for the gate is" -#~ " .. math:: \\begin{bmatrix} " -#~ "0.70710677+0.j & 0.70710677+0.j\\\\ " -#~ "0.70710677+0.j & -0.70710677+0.j \\end{bmatrix}" +#~ "Rotation gate, which is in matrix " +#~ "exponential form, shall give the same" +#~ " result as `rgate`." #~ msgstr "" #~ msgid "" -#~ "\\begin{bmatrix} 0.70710677+0.j & " -#~ "0.70710677+0.j\\\\ 0.70710677+0.j & " -#~ "-0.70710677+0.j \\end{bmatrix}" +#~ "mx = \\sin(\\alpha) \\cos(\\phi) X\n" +#~ "\n" #~ msgstr "" -#~ msgid "Apply i gate on the circuit." +#~ msgid "" +#~ "my = \\sin(\\alpha) \\sin(\\phi) Y\n" +#~ "\n" #~ msgstr "" #~ msgid "" -#~ "Qubit number than the gate applies " -#~ "on. The matrix for the gate is" -#~ " .. math:: \\begin{bmatrix} 1.+0.j " -#~ "& 0.+0.j\\\\ 0.+0.j & 1.+0.j " -#~ "\\end{bmatrix}" +#~ "mz = \\cos(\\alpha) Z\n" +#~ "\n" #~ msgstr "" #~ msgid "" -#~ "\\begin{bmatrix} 1.+0.j & 0.+0.j\\\\ " -#~ "0.+0.j & 1.+0.j \\end{bmatrix}" +#~ "R(\\theta, \\alpha, \\phi) = e^{-i\\theta (mx+my+mz)}\n" +#~ "\n" #~ msgstr "" -#~ msgid "Apply iswap gate on the circuit." +#~ msgid "Rotation Gate" #~ msgstr "" -#~ msgid "" -#~ "Qubit number than the gate applies " -#~ "on. The matrix for the gate is" -#~ " .. math:: \\begin{bmatrix} 1.+0.j " -#~ "& 0.+0.j & 0.+0.j & 0.+0.j\\\\ " -#~ "0.+0.j & 0.+0.j & 0.+1.j & " -#~ "0.+0.j\\\\ 0.+0.j & 0.+1.j & 0.+0.j" -#~ " & 0.+0.j\\\\ 0.+0.j & 0.+0.j &" -#~ " 0.+0.j & 1.+0.j \\end{bmatrix}" +#~ msgid "Rotation gate along X axis." #~ msgstr "" #~ msgid "" -#~ "\\begin{bmatrix} 1.+0.j & 0.+0.j & " -#~ "0.+0.j & 0.+0.j\\\\ 0.+0.j & 0.+0.j" -#~ " & 0.+1.j & 0.+0.j\\\\ 0.+0.j &" -#~ " 0.+1.j & 0.+0.j & 0.+0.j\\\\ " -#~ "0.+0.j & 0.+0.j & 0.+0.j & 1.+0.j" -#~ " \\end{bmatrix}" +#~ "RX(\\theta) = e^{-i\\frac{\\theta}{2}X}\n" +#~ "\n" #~ msgstr "" -#~ msgid "Apply r gate with parameters on the circuit." +#~ msgid "RX Gate" #~ msgstr "" -#~ msgid "Apply rx gate with parameters on the circuit." +#~ msgid "Rotation gate along Y axis." #~ msgstr "" -#~ msgid "Apply ry gate with parameters on the circuit." +#~ msgid "" +#~ "RY(\\theta) = e^{-i\\frac{\\theta}{2}Y}\n" +#~ "\n" #~ msgstr "" -#~ msgid "Apply rz gate with parameters on the circuit." +#~ msgid "RY Gate" #~ msgstr "" -#~ msgid "Apply s gate on the circuit." +#~ msgid "Rotation gate along Z axis." #~ msgstr "" #~ msgid "" -#~ "Qubit number than the gate applies " -#~ "on. The matrix for the gate is" -#~ " .. math:: \\begin{bmatrix} 1.+0.j " -#~ "& 0.+0.j\\\\ 0.+0.j & 0.+1.j " -#~ "\\end{bmatrix}" +#~ "RZ(\\theta) = e^{-i\\frac{\\theta}{2}Z}\n" +#~ "\n" #~ msgstr "" -#~ msgid "" -#~ "\\begin{bmatrix} 1.+0.j & 0.+0.j\\\\ " -#~ "0.+0.j & 0.+1.j \\end{bmatrix}" +#~ msgid "RZ Gate" #~ msgstr "" -#~ msgid "Apply sd gate on the circuit." +#~ msgid "Interfaces bridging different backends" #~ msgstr "" -#~ msgid "" -#~ "Qubit number than the gate applies " -#~ "on. The matrix for the gate is" -#~ " .. math:: \\begin{bmatrix} 1.+0.j " -#~ "& 0.+0.j\\\\ 0.+0.j & 0.-1.j " -#~ "\\end{bmatrix}" +#~ msgid "Keras layer for tc quantum function" #~ msgstr "" #~ msgid "" -#~ "\\begin{bmatrix} 1.+0.j & 0.+0.j\\\\ " -#~ "0.+0.j & 0.-1.j \\end{bmatrix}" +#~ "`QuantumLayer` wraps the quantum function " +#~ "`f` as a `keras.Layer` so that " +#~ "tensorcircuit is better integrated with " +#~ "tensorflow." #~ msgstr "" -#~ msgid "Apply swap gate on the circuit." +#~ msgid "[description], defaults to \"glorot_uniform\"" #~ msgstr "" #~ msgid "" -#~ "Qubit number than the gate applies " -#~ "on. The matrix for the gate is" -#~ " .. math:: \\begin{bmatrix} 1.+0.j " -#~ "& 0.+0.j & 0.+0.j & 0.+0.j\\\\ " -#~ "0.+0.j & 0.+0.j & 1.+0.j & " -#~ "0.+0.j\\\\ 0.+0.j & 1.+0.j & 0.+0.j" -#~ " & 0.+0.j\\\\ 0.+0.j & 0.+0.j &" -#~ " 0.+0.j & 1.+0.j \\end{bmatrix}" +#~ "Load function from the files in " +#~ "the ``tf.savedmodel`` format. We can " +#~ "load several functions at the same " +#~ "time, as they can be the same " +#~ "function of different input shapes." #~ msgstr "" #~ msgid "" -#~ "\\begin{bmatrix} 1.+0.j & 0.+0.j & " -#~ "0.+0.j & 0.+0.j\\\\ 0.+0.j & 0.+0.j" -#~ " & 1.+0.j & 0.+0.j\\\\ 0.+0.j &" -#~ " 1.+0.j & 0.+0.j & 0.+0.j\\\\ " -#~ "0.+0.j & 0.+0.j & 0.+0.j & 1.+0.j" -#~ " \\end{bmatrix}" +#~ "The fallback function when all functions" +#~ " loaded are failed, defaults to None" #~ msgstr "" -#~ msgid "Apply t gate on the circuit." +#~ msgid "" +#~ "When there is not legal loaded " +#~ "function of the input shape and no" +#~ " fallback callable." #~ msgstr "" #~ msgid "" -#~ "Qubit number than the gate applies " -#~ "on. The matrix for the gate is" -#~ " .. math:: \\begin{bmatrix} 1. &" -#~ " +0.j & 0. & +0.j\\\\ 0. &" -#~ " +0.j & 0.70710677+0.70710677j \\end{bmatrix}" +#~ "A function that tries all loaded " +#~ "function against the input until the " +#~ "first success one." #~ msgstr "" #~ msgid "" -#~ "\\begin{bmatrix} 1. & +0.j & 0. " -#~ "& +0.j\\\\ 0. & +0.j & " -#~ "0.70710677+0.70710677j \\end{bmatrix}" +#~ "The keras loss function that directly" +#~ " taking the model output as the " +#~ "loss." +#~ msgstr "" + +#~ msgid "Save tf function in the file (``tf.savedmodel`` format)." +#~ msgstr "" + +#~ msgid "``tf.function`` ed function with graph building" +#~ msgstr "" + +#~ msgid "the dir path to save the function" #~ msgstr "" -#~ msgid "Apply td gate on the circuit." +#~ msgid "FiniteMPS from tensornetwork with bug fixed" #~ msgstr "" #~ msgid "" -#~ "Qubit number than the gate applies " -#~ "on. The matrix for the gate is" -#~ " .. math:: \\begin{bmatrix} 1. &" -#~ " +0.j & 0. & +0.j\\\\ 0. &" -#~ " +0.j & 0.70710677-0.70710677j \\end{bmatrix}" +#~ "Bases: " +#~ ":py:class:`tensornetwork.matrixproductstates.finite_mps.FiniteMPS`" #~ msgstr "" #~ msgid "" -#~ "\\begin{bmatrix} 1. & +0.j & 0. " -#~ "& +0.j\\\\ 0. & +0.j & " -#~ "0.70710677-0.70710677j \\end{bmatrix}" +#~ "Apply a two-site gate to an " +#~ "MPS. This routine will in general " +#~ "destroy any canonical form of the " +#~ "state. If a canonical form is " +#~ "needed, the user can restore it " +#~ "using `FiniteMPS.position`." #~ msgstr "" -#~ msgid "Apply toffoli gate on the circuit." +#~ msgid "A two-body gate." #~ msgstr "" -#~ msgid "" -#~ "Qubit number than the gate applies " -#~ "on. The matrix for the gate is" -#~ " .. math:: \\begin{bmatrix} 1.+0.j " -#~ "& 0.+0.j & 0.+0.j & 0.+0.j & " -#~ "0.+0.j & 0.+0.j & 0.+0.j & " -#~ "0.+0.j\\\\ 0.+0.j & 1.+0.j & 0.+0.j" -#~ " & 0.+0.j & 0.+0.j & 0.+0.j &" -#~ " 0.+0.j & 0.+0.j\\\\ 0.+0.j & " -#~ "0.+0.j & 1.+0.j & 0.+0.j & 0.+0.j" -#~ " & 0.+0.j & 0.+0.j & 0.+0.j\\\\" -#~ " 0.+0.j & 0.+0.j & 0.+0.j & " -#~ "1.+0.j & 0.+0.j & 0.+0.j & 0.+0.j" -#~ " & 0.+0.j\\\\ 0.+0.j & 0.+0.j &" -#~ " 0.+0.j & 0.+0.j & 1.+0.j & " -#~ "0.+0.j & 0.+0.j & 0.+0.j\\\\ 0.+0.j" -#~ " & 0.+0.j & 0.+0.j & 0.+0.j &" -#~ " 0.+0.j & 1.+0.j & 0.+0.j & " -#~ "0.+0.j\\\\ 0.+0.j & 0.+0.j & 0.+0.j" -#~ " & 0.+0.j & 0.+0.j & 0.+0.j &" -#~ " 0.+0.j & 1.+0.j\\\\ 0.+0.j & " -#~ "0.+0.j & 0.+0.j & 0.+0.j & 0.+0.j" -#~ " & 0.+0.j & 1.+0.j & 0.+0.j " -#~ "\\end{bmatrix}" +#~ msgid "The first site where the gate acts." #~ msgstr "" -#~ msgid "" -#~ "\\begin{bmatrix} 1.+0.j & 0.+0.j & " -#~ "0.+0.j & 0.+0.j & 0.+0.j & 0.+0.j" -#~ " & 0.+0.j & 0.+0.j\\\\ 0.+0.j &" -#~ " 1.+0.j & 0.+0.j & 0.+0.j & " -#~ "0.+0.j & 0.+0.j & 0.+0.j & " -#~ "0.+0.j\\\\ 0.+0.j & 0.+0.j & 1.+0.j" -#~ " & 0.+0.j & 0.+0.j & 0.+0.j &" -#~ " 0.+0.j & 0.+0.j\\\\ 0.+0.j & " -#~ "0.+0.j & 0.+0.j & 1.+0.j & 0.+0.j" -#~ " & 0.+0.j & 0.+0.j & 0.+0.j\\\\" -#~ " 0.+0.j & 0.+0.j & 0.+0.j & " -#~ "0.+0.j & 1.+0.j & 0.+0.j & 0.+0.j" -#~ " & 0.+0.j\\\\ 0.+0.j & 0.+0.j &" -#~ " 0.+0.j & 0.+0.j & 0.+0.j & " -#~ "1.+0.j & 0.+0.j & 0.+0.j\\\\ 0.+0.j" -#~ " & 0.+0.j & 0.+0.j & 0.+0.j &" -#~ " 0.+0.j & 0.+0.j & 0.+0.j & " -#~ "1.+0.j\\\\ 0.+0.j & 0.+0.j & 0.+0.j" -#~ " & 0.+0.j & 0.+0.j & 0.+0.j &" -#~ " 1.+0.j & 0.+0.j \\end{bmatrix}" +#~ msgid "The second site where the gate acts." #~ msgstr "" -#~ msgid "Apply wroot gate on the circuit." +#~ msgid "The maximum number of singular values to keep." #~ msgstr "" -#~ msgid "" -#~ "Qubit number than the gate applies " -#~ "on. The matrix for the gate is" -#~ " .. math:: \\begin{bmatrix} " -#~ "0.70710677+0.j & -0.5 & -0.5j\\\\ 0.5" -#~ " & -0.5j & 0.70710677+0.j \\end{bmatrix}" +#~ msgid "The maximum allowed truncation error." #~ msgstr "" #~ msgid "" -#~ "\\begin{bmatrix} 0.70710677+0.j & -0.5 &" -#~ " -0.5j\\\\ 0.5 & -0.5j & " -#~ "0.70710677+0.j \\end{bmatrix}" -#~ msgstr "" - -#~ msgid "Apply x gate on the circuit." +#~ "An optional value to choose the " +#~ "MPS tensor at `center_position` to be" +#~ " isometric after the application of " +#~ "the gate. Defaults to `site1`. If " +#~ "the MPS is canonical " +#~ "(i.e.`BaseMPS.center_position != None`), and " +#~ "if the orthogonality center coincides " +#~ "with either `site1` or `site2`, the " +#~ "orthogonality center will be shifted to" +#~ " `center_position` (`site1` by default). If" +#~ " the orthogonality center does not " +#~ "coincide with `(site1, site2)` then " +#~ "`MPS.center_position` is set to `None`." #~ msgstr "" -#~ msgid "" -#~ "Qubit number than the gate applies " -#~ "on. The matrix for the gate is" -#~ " .. math:: \\begin{bmatrix} 0.+0.j " -#~ "& 1.+0.j\\\\ 1.+0.j & 0.+0.j " -#~ "\\end{bmatrix}" +#~ msgid "Multiply `max_truncation_err` with the largest singular value." #~ msgstr "" #~ msgid "" -#~ "\\begin{bmatrix} 0.+0.j & 1.+0.j\\\\ " -#~ "1.+0.j & 0.+0.j \\end{bmatrix}" +#~ "\"rank of gate is {} but has " +#~ "to be 4\", \"site1 = {} is " +#~ "not between 0 <= site < N -" +#~ " 1 = {}\", \"site2 = {} is " +#~ "not between 1 <= site < N =" +#~ " {}\",\"Found site2 ={}, site1={}. Only " +#~ "nearest neighbor gates are currently " +#~ "supported\", \"f center_position = " +#~ "{center_position} not f in {(site1, " +#~ "site2)} \", or \"center_position = {}," +#~ " but gate is applied at sites " +#~ "{}, {}. Truncation should only be " +#~ "done if the gate is applied at " +#~ "the center position of the MPS.\"" #~ msgstr "" -#~ msgid "Apply y gate on the circuit." +#~ msgid "A scalar tensor containing the truncated weight of the truncation." #~ msgstr "" -#~ msgid "" -#~ "Qubit number than the gate applies " -#~ "on. The matrix for the gate is" -#~ " .. math:: \\begin{bmatrix} 0.+0.j " -#~ "& 0.-1.j\\\\ 0.+1.j & 0.+0.j " -#~ "\\end{bmatrix}" +#~ msgid "Measure the expectation value of local operators `ops` site `sites`." #~ msgstr "" -#~ msgid "" -#~ "\\begin{bmatrix} 0.+0.j & 0.-1.j\\\\ " -#~ "0.+1.j & 0.+0.j \\end{bmatrix}" +#~ msgid "A list Tensors of rank 2; the local operators to be measured." #~ msgstr "" -#~ msgid "Apply z gate on the circuit." +#~ msgid "Sites where `ops` act." #~ msgstr "" -#~ msgid "" -#~ "Qubit number than the gate applies " -#~ "on. The matrix for the gate is" -#~ " .. math:: \\begin{bmatrix} 1.+0.j " -#~ "& 0.+0.j\\\\ 0.+0.j & -1.+0.j " -#~ "\\end{bmatrix}" +#~ msgid "measurements :math:`\\langle` `ops[n]`:math:`\\rangle` for n in `sites`" #~ msgstr "" #~ msgid "" -#~ "\\begin{bmatrix} 1.+0.j & 0.+0.j\\\\ " -#~ "0.+0.j & -1.+0.j \\end{bmatrix}" +#~ "Compute the correlator :math:`\\langle` " +#~ "`op1[site1], op2[s]`:math:`\\rangle` between `site1`" +#~ " and all sites `s` in `sites2`. " +#~ "If `s == site1`, `op2[s]` will be" +#~ " applied first." #~ msgstr "" -#~ msgid "Circuit object based on state simulator." +#~ msgid "Tensor of rank 2; the local operator at `site1`." #~ msgstr "" -#~ msgid "The number of qubits in the circuit." +#~ msgid "Tensor of rank 2; the local operator at `sites2`." #~ msgstr "" -#~ msgid "" -#~ "If not None, the initial state of" -#~ " the circuit is taken as ``inputs``" -#~ " instead of :math:`\\vert 0\\rangle^n` " -#~ "qubits, defaults to None" +#~ msgid "The site where `op1` acts" #~ msgstr "" -#~ msgid "(Nodes, dangling Edges) for a MPS like initial wavefunction" +#~ msgid "Sites where operator `op2` acts." #~ msgstr "" #~ msgid "" -#~ "dict if two qubit gate is ready" -#~ " for split, including parameters for " -#~ "at least one of ``max_singular_values`` " -#~ "and ``max_truncation_err``." +#~ "Correlator :math:`\\langle` `op1[site1], " +#~ "op2[s]`:math:`\\rangle` for `s` :math:`\\in` " +#~ "`sites2`." #~ msgstr "" -#~ msgid "" -#~ "Monte Carlo trajectory simulation of " -#~ "general Kraus channel whose Kraus " -#~ "operators cannot be amplified to unitary" -#~ " operators. For unitary operators composed" -#~ " Kraus channel, :py:meth:`unitary_kraus` is " -#~ "much faster." +#~ msgid "Quantum circuit: MPS state simulator" #~ msgstr "" -#~ msgid "" -#~ "This function is jittable in theory. " -#~ "But only jax+GPU combination is " -#~ "recommended for jit since the graph " -#~ "building time is too long for " -#~ "other backend options; though the " -#~ "running time of the function is " -#~ "very fast for every case." +#~ msgid "``MPSCircuit`` class. Simple usage demo below." #~ msgstr "" -#~ msgid "list of ``tn.Node`` for Kraus operators" +#~ msgid "MPSCircuit object based on state simulator." #~ msgstr "" -#~ msgid "the qubits index that Kraus channel is applied on" +#~ msgid "" +#~ "If not None, the initial state of" +#~ " the circuit is taken as ``tensors``" +#~ " instead of :math:`\\vert 0\\rangle^n` " +#~ "qubits, defaults to None" #~ msgstr "" -#~ msgid "" -#~ "random tensor between 0 or 1, " -#~ "defaults to be None, the random " -#~ "number will be generated automatically" +#~ msgid "The center position of MPS, default to 0" #~ msgstr "" -#~ msgid "Compute the expectation of corresponding operators." +#~ msgid "Apply a general qubit gate on MPS." #~ msgstr "" -#~ msgid "" -#~ "operator and its position on the " -#~ "circuit, eg. ``(tc.gates.z(), [1, ]), " -#~ "(tc.gates.x(), [2, ])`` is for operator" -#~ " :math:`Z_1X_2`" +#~ msgid "The Gate to be applied" #~ msgstr "" -#~ msgid "" -#~ "if True, then the wavefunction tensor" -#~ " is cached for further expectation " -#~ "evaluation, defaults to be true" +#~ msgid "Qubit indices of the gate" #~ msgstr "" -#~ msgid "Tensor with one element" +#~ msgid "\"MPS does not support application of gate on > 2 qubits.\"" #~ msgstr "" -#~ msgid "[WIP], check whether the circuit is legal." +#~ msgid "" +#~ "Apply a double qubit gate on " +#~ "adjacent qubits of Matrix Product States" +#~ " (MPS). Truncation rule is specified " +#~ "by `set_truncation_rule`." #~ msgstr "" -#~ msgid "the bool indicating whether the circuit is legal" +#~ msgid "The first qubit index of the gate" #~ msgstr "" -#~ msgid "Take measurement to the given quantum lines." +#~ msgid "The second qubit index of the gate" #~ msgstr "" -#~ msgid "measure on which quantum line" +#~ msgid "Center position of MPS, default is None" #~ msgstr "" -#~ msgid "if true, theoretical probability is also returned" +#~ msgid "" +#~ "Apply a double qubit gate on MPS." +#~ " Truncation rule is specified by " +#~ "`set_truncation_rule`." #~ msgstr "" #~ msgid "" -#~ "Middle measurement in z-basis on the " -#~ "circuit, note the wavefunction output is" -#~ " not normalized with ``mid_measurement`` " -#~ "involved, one should normalize the state" -#~ " manually if needed." +#~ "Apply a single qubit gate on MPS," +#~ " and the gate must be unitary; " +#~ "no truncation is needed." #~ msgstr "" -#~ msgid "the index of qubit that the Z direction postselection applied on" +#~ msgid "gate to be applied" #~ msgstr "" -#~ msgid "0 for spin up, 1 for spin down, defaults to be 0" +#~ msgid "Qubit index of the gate" #~ msgstr "" -#~ msgid "Reference: arXiv:1201.3974." +#~ msgid "Compute the conjugate of the current MPS." #~ msgstr "" -#~ msgid "sampled bit string and the corresponding theoretical probability" +#~ msgid "The constructed MPS" #~ msgstr "" -#~ msgid "Replace the input state with the circuit structure unchanged." +#~ msgid "Copy the current MPS." #~ msgstr "" -#~ msgid "Input wavefunction." +#~ msgid "Copy the current MPS without the tensors." #~ msgstr "" -#~ msgid "" -#~ "Replace the input state in MPS " -#~ "representation while keep the circuit " -#~ "structure unchanged." +#~ msgid "Compute the expectation of the corresponding double qubit gate." +#~ msgstr "" + +#~ msgid "qubit index of the gate" #~ msgstr "" -#~ msgid "Compute the output wavefunction from the circuit." +#~ msgid "" +#~ "Compute the expectation of the " +#~ "corresponding single qubit gate in the" +#~ " form of tensor." #~ msgstr "" -#~ msgid "the str indicating the form of the output wavefunction" +#~ msgid "Gate to be applied" #~ msgstr "" -#~ msgid "Tensor with the corresponding shape" +#~ msgid "The expectation of the corresponding single qubit gate" #~ msgstr "" -#~ msgid "Compute :math:`\\langle bra\\vert ops \\vert ket\\rangle`" +#~ msgid "" +#~ "Compute the expectation of the direct" +#~ " product of the corresponding two " +#~ "gates." #~ msgstr "" -#~ msgid "Example 1 (:math:`bra` is same as :math:`ket`)" +#~ msgid "First gate to be applied" #~ msgstr "" -#~ msgid "Example 2 (:math:`bra` is different from :math:`ket`)" +#~ msgid "Second gate to be applied" #~ msgstr "" -#~ msgid "[description], defaults to None, which is the same as ``ket``" +#~ msgid "Qubit index of the first gate" #~ msgstr "" -#~ msgid "[description], defaults to True" +#~ msgid "Qubit index of the second gate" #~ msgstr "" -#~ msgid "[description], defaults to False" +#~ msgid "The correlation of the corresponding two qubit gates" #~ msgstr "" -#~ msgid "" -#~ "Not an ideal visualization for quantum" -#~ " circuit, but reserve here as a " -#~ "general approch to show tensornetwork " -#~ "[Deperacted, use ``qir2tex instead``]" +#~ msgid "Construct the MPS from a given wavefunction." #~ msgstr "" -#~ msgid "Constants and setups" +#~ msgid "The given wavefunction (any shape is OK)" #~ msgstr "" #~ msgid "" -#~ "To set runtime contractor of the " -#~ "tensornetwork for a better contraction " -#~ "path." +#~ "Compute the expectation of corresponding " +#~ "operators in the form of tensor." #~ msgstr "" #~ msgid "" -#~ "\"auto\", \"greedy\", \"branch\", \"plain\", " -#~ "\"tng\", \"custom\", \"custom_stateful\". defaults" -#~ " to None (\"auto\")" +#~ "Operator and its position on the " +#~ "circuit, eg. ``(gates.Z(), [1]), (gates.X()," +#~ " [2])`` is for operator :math:`Z_1X_2`" #~ msgstr "" -#~ msgid "Valid for \"custom\" or \"custom_stateful\" as method, defaults to None" +#~ msgid "The expectation of corresponding operators" #~ msgstr "" -#~ msgid "" -#~ "It is not very useful, as " -#~ "``memory_limit`` leads to ``branch`` " -#~ "contraction instead of ``greedy`` which " -#~ "is rather slow, defaults to None" +#~ msgid "Get the normalized Center Position." #~ msgstr "" -#~ msgid "Tensornetwork version is too low to support some of the contractors." +#~ msgid "Normalized Center Position." #~ msgstr "" -#~ msgid "Unknown method options." +#~ msgid "Check whether the circuit is legal." #~ msgstr "" -#~ msgid "The new tensornetwork with its contractor set." +#~ msgid "Whether the circuit is legal." #~ msgstr "" -#~ msgid "To set the runtime numerical dtype of tensors." +#~ msgid "integer indicating the measure on which quantum line" #~ msgstr "" #~ msgid "" -#~ "\"complex64\" or \"complex128\", defaults to" -#~ " None, which is equivalent to " -#~ "\"complex64\"." +#~ "Middle measurement in the z-basis on " +#~ "the circuit, note the wavefunction " +#~ "output is not normalized with " +#~ "``mid_measurement`` involved, one should " +#~ "normalized the state manually if needed." #~ msgstr "" -#~ msgid "The naive state-vector simulator contraction path." +#~ msgid "The index of qubit that the Z direction postselection applied on" #~ msgstr "" -#~ msgid "The list of ``tn.Node``." +#~ msgid "0 for spin up, 1 for spin down, defaults to 0" #~ msgstr "" -#~ msgid "The list of dangling node edges, defaults to be None." +#~ msgid "Normalize MPS Circuit according to the center position." #~ msgstr "" -#~ msgid "The ``tn.Node`` after contraction" +#~ msgid "Wrapper of tn.FiniteMPS.position. Set orthogonality center." #~ msgstr "" -#~ msgid "To set the runtime backend of tensorcircuit." +#~ msgid "The orthogonality center" #~ msgstr "" -#~ msgid "" -#~ "Note: ``tc.set_backend`` and " -#~ "``tc.cons.set_tensornetwork_backend`` are the same." +#~ msgid "Compute the projection between `other` as bra and `self` as ket." #~ msgstr "" -#~ msgid "" -#~ "\"numpy\", \"tensorflow\", \"jax\", \"pytorch\". " -#~ "defaults to None, which gives the " -#~ "same behavior as " -#~ "``tensornetwork.backend_contextmanager.get_default_backend()``." +#~ msgid "ket of the other MPS, which will be converted to bra automatically" #~ msgstr "" -#~ msgid "Whether the object should be set as global." +#~ msgid "The projection in form of tensor" #~ msgstr "" -#~ msgid "Quantum circuit class but with density matrix simulator" +#~ msgid "" +#~ "Set truncation rules when double qubit" +#~ " gates are applied. If nothing is " +#~ "specified, no truncation will take place" +#~ " and the bond dimension will keep " +#~ "growing. For more details, refer to " +#~ "`split_tensor`." #~ msgstr "" -#~ msgid "Quantum circuit class but with density matrix simulator: v2" +#~ msgid "Tensor with shape [1, -1]" #~ msgstr "" -#~ msgid "Bases: :py:class:`tensorcircuit.densitymatrix.DMCircuit`" +#~ msgid "" +#~ "Split the tensor by SVD or QR " +#~ "depends on whether a truncation is " +#~ "required." #~ msgstr "" -#~ msgid "Experimental features" +#~ msgid "The input tensor to split." #~ msgstr "" #~ msgid "" -#~ "Declarations of single-qubit and two-" -#~ "qubit gates and their corresponding " -#~ "matrix." -#~ msgstr "" - -#~ msgid "Bases: :py:class:`tensornetwork.network_components.Node`" +#~ "Determine the orthogonal center is on" +#~ " the left tensor or the right " +#~ "tensor." #~ msgstr "" -#~ msgid "Wrapper of tn.Node, quantum gate" +#~ msgid "Two tensors after splitting" #~ msgstr "" -#~ msgid "Bases: :py:class:`tensorcircuit.gates.GateF`" +#~ msgid "Quantum state and operator class backend by tensornetwork" #~ msgstr "" -#~ msgid "Note one should provide the gate with properly reshaped." +#~ msgid "Bases: :py:class:`tensorcircuit.quantum.QuOperator`" #~ msgstr "" -#~ msgid "corresponding gate" +#~ msgid "Represents an adjoint (row) vector via a tensor network." #~ msgstr "" -#~ msgid "The name of the gate." +#~ msgid "" +#~ "Constructs a new `QuAdjointVector` from " +#~ "a tensor network. This encapsulates an" +#~ " existing tensor network, interpreting it" +#~ " as an adjoint vector (row vector)." #~ msgstr "" -#~ msgid "the resulted gate" +#~ msgid "The edges of the network to be used as the input edges." #~ msgstr "" -#~ msgid "Convert the inputs to Tensor with specified dtype." +#~ msgid "" +#~ "Nodes used to refer to parts of" +#~ " the tensor network that are not " +#~ "connected to any input or output " +#~ "edges (for example: a scalar factor)." #~ msgstr "" -#~ msgid "inputs" +#~ msgid "" +#~ "Optional collection of edges to ignore" +#~ " when performing consistency checks." #~ msgstr "" -#~ msgid "dtype of the output Tensors" +#~ msgid "" +#~ "Construct a `QuAdjointVector` directly from" +#~ " a single tensor. This first wraps" +#~ " the tensor in a `Node`, then " +#~ "constructs the `QuAdjointVector` from that " +#~ "`Node`." #~ msgstr "" -#~ msgid "List of Tensors" +#~ msgid "The tensor for constructing an QuAdjointVector." #~ msgstr "" -#~ msgid "Returns a LaTeX bmatrix." +#~ msgid "" +#~ "Sequence of integer indices specifying " +#~ "the order in which to interpret " +#~ "the axes as subsystems (input edges)." +#~ " If not specified, the axes are " +#~ "taken in ascending order." #~ msgstr "" -#~ msgid "Formatted Display:" +#~ msgid "The new constructed QuAdjointVector give from the given tensor." #~ msgstr "" #~ msgid "" -#~ "\\begin{bmatrix} 1.+0.j & 0.+0.j\\\\ " -#~ "0.+0.j & 1.+0.j \\end{bmatrix}\n" -#~ "\n" -#~ msgstr "" - -#~ msgid "2D numpy array" +#~ "Represents a linear operator via a " +#~ "tensor network. To interpret a tensor" +#~ " network as a linear operator, some" +#~ " of the dangling edges must be " +#~ "designated as `out_edges` (output edges) " +#~ "and the rest as `in_edges` (input " +#~ "edges). Considered as a matrix, the " +#~ "`out_edges` represent the row index and" +#~ " the `in_edges` represent the column " +#~ "index. The (right) action of the " +#~ "operator on another then consists of " +#~ "connecting the `in_edges` of the first" +#~ " operator to the `out_edges` of the" +#~ " second. Can be used to do " +#~ "simple linear algebra with tensor " +#~ "networks." #~ msgstr "" -#~ msgid "ValueError(\"bmatrix can at most display two dimensions\")" +#~ msgid "" +#~ "Creates a new `QuOperator` from a " +#~ "tensor network. This encapsulates an " +#~ "existing tensor network, interpreting it " +#~ "as a linear operator. The network " +#~ "is checked for consistency: All dangling" +#~ " edges must either be in `out_edges`," +#~ " `in_edges`, or `ignore_edges`." #~ msgstr "" -#~ msgid "latex str for bmatrix of array a" +#~ msgid "The edges of the network to be used as the output edges." #~ msgstr "" #~ msgid "" -#~ "Controlled rotation gate, when the " -#~ "control bit is 1, `rgate` is " -#~ "applied on the target gate." +#~ "Optional collection of dangling edges to" +#~ " ignore when performing consistency checks." #~ msgstr "" -#~ msgid "angle in radians" +#~ msgid "" +#~ "At least one reference node is " +#~ "required to specify a scalar. None " +#~ "provided!" #~ msgstr "" -#~ msgid "CR Gate" +#~ msgid "" +#~ "The adjoint of the operator. This " +#~ "creates a new `QuOperator` with " +#~ "complex-conjugate copies of all tensors " +#~ "in the network and with the input" +#~ " and output edges switched." #~ msgstr "" #~ msgid "" -#~ "Faster exponential gate, directly implemented" -#~ " based on RHS, only work when: " -#~ ":math:`U^2` is identity matrix." +#~ "Check that the network has the " +#~ "expected dimensionality. This checks that " +#~ "all input and output edges are " +#~ "dangling and that there are no " +#~ "other dangling edges (except any " +#~ "specified in `ignore_edges`). If not, an" +#~ " exception is raised." #~ msgstr "" #~ msgid "" -#~ "\\rm{exp}(U) &= e^{-i \\theta U} \\\\\n" -#~ " &= \\cos(\\theta) I - j \\sin(\\theta) U \\\\\n" -#~ "\n" +#~ "Contract the tensor network in place." +#~ " This modifies the tensor network " +#~ "representation of the operator (or " +#~ "vector, or scalar), reducing it to " +#~ "a single tensor, without changing the" +#~ " value." #~ msgstr "" -#~ msgid "input unitary (U)" +#~ msgid "Manually specify the axis ordering of the final tensor." #~ msgstr "" -#~ msgid "suffix of Gate name" +#~ msgid "The present object." #~ msgstr "" -#~ msgid "Exponential Gate" +#~ msgid "" +#~ "Contracts the tensor network in place" +#~ " and returns the final tensor. Note" +#~ " that this modifies the tensor " +#~ "network representing the operator. The " +#~ "default ordering for the axes of " +#~ "the final tensor is: `*out_edges, " +#~ "*in_edges`. If there are any \"ignored\"" +#~ " edges, their axes come first: " +#~ "`*ignored_edges, *out_edges, *in_edges`." #~ msgstr "" -#~ msgid "Exponential gate." +#~ msgid "" +#~ "Manually specify the axis ordering of" +#~ " the final tensor. The default " +#~ "ordering is determined by `out_edges` " +#~ "and `in_edges` (see above)." #~ msgstr "" -#~ msgid "" -#~ "\\rm{exp}(U) = e^{-i \\theta U}\n" -#~ "\n" +#~ msgid "Node count '{}' > 1 after contraction!" #~ msgstr "" -#~ msgid "iSwap gate." +#~ msgid "The final tensor representing the operator." #~ msgstr "" #~ msgid "" -#~ "iSwap(\\theta) =\n" -#~ "\\begin{pmatrix}\n" -#~ " 1 & 0 & 0 & 0\\\\\n" -#~ " 0 & \\cos(\\frac{\\pi}{2} \\theta )" -#~ " & j \\sin(\\frac{\\pi}{2} \\theta ) " -#~ "& 0\\\\\n" -#~ " 0 & j \\sin(\\frac{\\pi}{2} \\theta" -#~ " ) & \\cos(\\frac{\\pi}{2} \\theta ) " -#~ "& 0\\\\\n" -#~ " 0 & 0 & 0 & 1\\\\\n" -#~ "\\end{pmatrix}\n" -#~ "\n" +#~ "Construct a `QuOperator` directly from a" +#~ " single tensor. This first wraps the" +#~ " tensor in a `Node`, then constructs" +#~ " the `QuOperator` from that `Node`." #~ msgstr "" -#~ msgid "iSwap Gate" +#~ msgid "The tensor." #~ msgstr "" -#~ msgid "Convert Gate to numpy array." +#~ msgid "The axis indices of `tensor` to use as `out_edges`." #~ msgstr "" -#~ msgid "input Gate" +#~ msgid "The axis indices of `tensor` to use as `in_edges`." #~ msgstr "" -#~ msgid "corresponding Tensor" +#~ msgid "The new operator." #~ msgstr "" -#~ msgid "" -#~ "Inner helper function to generate gate" -#~ " functions, such as ``z()`` from " -#~ "``_z_matrix``" +#~ msgid "All tensor-network nodes involved in the operator." #~ msgstr "" -#~ msgid "General single qubit rotation gate" +#~ msgid "" +#~ "The norm of the operator. This is" +#~ " the 2-norm (also known as the " +#~ "Frobenius or Hilbert-Schmidt norm)." #~ msgstr "" #~ msgid "" -#~ "R(\\theta, \\phi, \\alpha) = i \\cos(\\theta) I\n" -#~ "\n" +#~ "The partial trace of the operator. " +#~ "Subsystems to trace out are supplied " +#~ "as indices, so that dangling edges " +#~ "are connected to each other as: " +#~ "`out_edges[i] ^ in_edges[i] for i in " +#~ "subsystems_to_trace_out` This does not modify" +#~ " the original network. The original " +#~ "ordering of the remaining subsystems is" +#~ " maintained." #~ msgstr "" -#~ msgid "" -#~ "- i \\cos(\\phi) \\sin(\\alpha) \\sin(\\theta) X\n" -#~ "\n" +#~ msgid "Indices of subsystems to trace out." #~ msgstr "" -#~ msgid "" -#~ "- i \\sin(\\phi) \\sin(\\alpha) \\sin(\\theta) Y\n" -#~ "\n" +#~ msgid "A new QuOperator or QuScalar representing the result." #~ msgstr "" #~ msgid "" -#~ "- i \\sin(\\theta) \\cos(\\alpha) Z\n" -#~ "\n" +#~ "Tensor product with another operator. " +#~ "Given two operators `A` and `B`, " +#~ "produces a new operator `AB` " +#~ "representing `A` ⊗ `B`. The `out_edges`" +#~ " (`in_edges`) of `AB` is simply the" +#~ " concatenation of the `out_edges` " +#~ "(`in_edges`) of `A.copy()` with that of" +#~ " `B.copy()`: `new_out_edges = [*out_edges_A_copy," +#~ " *out_edges_B_copy]` `new_in_edges = " +#~ "[*in_edges_A_copy, *in_edges_B_copy]`" #~ msgstr "" -#~ msgid "R Gate" +#~ msgid "The other operator (`B`)." #~ msgstr "" -#~ msgid "Random single qubit gate described in https://arxiv.org/abs/2002.07730." +#~ msgid "The result (`AB`)." #~ msgstr "" -#~ msgid "A random single qubit gate" +#~ msgid "The trace of the operator." #~ msgstr "" -#~ msgid "Returns a random two-qubit gate." +#~ msgid "Represents a scalar via a tensor network." #~ msgstr "" -#~ msgid "a random two-qubit gate" +#~ msgid "" +#~ "Constructs a new `QuScalar` from a " +#~ "tensor network. This encapsulates an " +#~ "existing tensor network, interpreting it " +#~ "as a scalar." #~ msgstr "" #~ msgid "" -#~ "Rotation gate, which is in matrix " -#~ "exponential form, shall give the same" -#~ " result as `rgate`." +#~ "Nodes used to refer to the tensor" +#~ " network (need not be exhaustive -" +#~ " one node from each disconnected " +#~ "subnetwork is sufficient)." #~ msgstr "" #~ msgid "" -#~ "mx = \\sin(\\alpha) \\cos(\\phi) X\n" -#~ "\n" +#~ "Construct a `QuScalar` directly from a" +#~ " single tensor. This first wraps the" +#~ " tensor in a `Node`, then constructs" +#~ " the `QuScalar` from that `Node`." #~ msgstr "" -#~ msgid "" -#~ "my = \\sin(\\alpha) \\sin(\\phi) Y\n" -#~ "\n" +#~ msgid "The tensor for constructing a new QuScalar." #~ msgstr "" -#~ msgid "" -#~ "mz = \\cos(\\alpha) Z\n" -#~ "\n" +#~ msgid "The new constructed QuScalar from the given tensor." +#~ msgstr "" + +#~ msgid "Represents a (column) vector via a tensor network." #~ msgstr "" #~ msgid "" -#~ "R(\\theta, \\alpha, \\phi) = e^{-i\\theta (mx+my+mz)}\n" -#~ "\n" +#~ "Constructs a new `QuVector` from a " +#~ "tensor network. This encapsulates an " +#~ "existing tensor network, interpreting it " +#~ "as a (column) vector." #~ msgstr "" -#~ msgid "Rotation Gate" +#~ msgid "" +#~ "Construct a `QuVector` directly from a" +#~ " single tensor. This first wraps the" +#~ " tensor in a `Node`, then constructs" +#~ " the `QuVector` from that `Node`." #~ msgstr "" -#~ msgid "Rotation gate along X axis." +#~ msgid "The tensor for constructing a \"QuVector\"." #~ msgstr "" #~ msgid "" -#~ "RX(\\theta) = e^{-i\\frac{\\theta}{2}X}\n" -#~ "\n" +#~ "Sequence of integer indices specifying " +#~ "the order in which to interpret " +#~ "the axes as subsystems (output edges)." +#~ " If not specified, the axes are " +#~ "taken in ascending order." #~ msgstr "" -#~ msgid "RX Gate" +#~ msgid "The new constructed QuVector from the given tensor." #~ msgstr "" -#~ msgid "Rotation gate along Y axis." +#~ msgid "" +#~ "Check the vector spaces represented by" +#~ " two lists of edges are compatible." +#~ " The number of edges must be " +#~ "the same and the dimensions of " +#~ "each pair of edges must match. " +#~ "Otherwise, an exception is raised. " +#~ ":param edges_1: List of edges " +#~ "representing a many-body Hilbert space." +#~ " :type edges_1: Sequence[Edge] :param " +#~ "edges_2: List of edges representing a" +#~ " many-body Hilbert space. :type " +#~ "edges_2: Sequence[Edge]" #~ msgstr "" #~ msgid "" -#~ "RY(\\theta) = e^{-i\\frac{\\theta}{2}Y}\n" -#~ "\n" +#~ "Hilbert-space mismatch: \"Cannot connect " +#~ "{} subsystems with {} subsystems\", or" +#~ " \"Input dimension {} != output " +#~ "dimension {}.\"" #~ msgstr "" -#~ msgid "RY Gate" +#~ msgid "" +#~ "Eliminates any connected CopyNodes that " +#~ "are identity matrices. This will modify" +#~ " the network represented by `nodes`. " +#~ "Only identities that are connected to" +#~ " other nodes are eliminated." #~ msgstr "" -#~ msgid "Rotation gate along Z axis." +#~ msgid "Collection of nodes to search." #~ msgstr "" #~ msgid "" -#~ "RZ(\\theta) = e^{-i\\frac{\\theta}{2}Z}\n" -#~ "\n" +#~ "The Dictionary mapping remaining Nodes " +#~ "to any replacements, Dictionary specifying " +#~ "all dangling-edge replacements." #~ msgstr "" -#~ msgid "RZ Gate" +#~ msgid "Compute the entropy from the given density matrix ``rho``." #~ msgstr "" -#~ msgid "Interfaces bridging different backends" +#~ msgid "[description], defaults to 1e-12" #~ msgstr "" -#~ msgid "Keras layer for tc quantum function" +#~ msgid "" +#~ "Note: further jit is recommended. For" +#~ " large Hilbert space, sparse Hamiltonian" +#~ " is recommended" #~ msgstr "" #~ msgid "" -#~ "`QuantumLayer` wraps the quantum function " -#~ "`f` as a `keras.Layer` so that " -#~ "tensorcircuit is better integrated with " -#~ "tensorflow." +#~ "Construct a 'QuOperator' representing the " +#~ "identity on a given space. Internally," +#~ " this is done by constructing " +#~ "'CopyNode's for each edge, with " +#~ "dimension according to 'space'." #~ msgstr "" -#~ msgid "[description], defaults to \"glorot_uniform\"" +#~ msgid "" +#~ "A sequence of integers for the " +#~ "dimensions of the tensor product factors" +#~ " of the space (the edges in the" +#~ " tensor network)." #~ msgstr "" -#~ msgid "" -#~ "Load function from the files in " -#~ "the ``tf.savedmodel`` format. We can " -#~ "load several functions at the same " -#~ "time, as they can be the same " -#~ "function of different input shapes." +#~ msgid "The data type (for conversion to dense)." #~ msgstr "" -#~ msgid "" -#~ "The fallback function when all functions" -#~ " loaded are failed, defaults to None" +#~ msgid "The desired identity operator." #~ msgstr "" #~ msgid "" -#~ "When there is not legal loaded " -#~ "function of the input shape and no" -#~ " fallback callable." +#~ "Simulate the measuring of each qubit " +#~ "of ``p`` in the computational basis, " +#~ "thus producing output like that of " +#~ "``qiskit``." #~ msgstr "" #~ msgid "" -#~ "A function that tries all loaded " -#~ "function against the input until the " -#~ "first success one." +#~ "The quantum state, assumed to be " +#~ "normalized, as either a ket or " +#~ "density operator." +#~ msgstr "" + +#~ msgid "The number of counts to perform." #~ msgstr "" #~ msgid "" -#~ "The keras loss function that directly" -#~ " taking the model output as the " -#~ "loss." +#~ "Defaults True. The bool indicating " +#~ "whether the return form is in the" +#~ " form of two array or one of" +#~ " the same length as the ``state`` " +#~ "(if ``sparse=False``)." #~ msgstr "" -#~ msgid "Save tf function in the file (``tf.savedmodel`` format)." +#~ msgid "The counts for each bit string measured." #~ msgstr "" -#~ msgid "``tf.function`` ed function with graph building" +#~ msgid "" +#~ "Constructs an appropriately specialized " +#~ "QuOperator. If there are no edges, " +#~ "creates a QuScalar. If the are " +#~ "only output (input) edges, creates a " +#~ "QuVector (QuAdjointVector). Otherwise creates " +#~ "a QuOperator." #~ msgstr "" -#~ msgid "the dir path to save the function" +#~ msgid "" +#~ "op = qu.quantum_constructor([], [psi_node[0], " +#~ "psi_node[1]]) >>> show_attributes(op) op.is_scalar()" +#~ " -> False op.is_vector() -> " +#~ "False op.is_adjoint_vector() -> True " +#~ "len(op.out_edges) -> 0 len(op.in_edges)" +#~ " -> 2 >>> # psi_node[0] -> " +#~ "op.in_edges[0] >>> # psi_node[1] -> " +#~ "op.in_edges[1]" #~ msgstr "" -#~ msgid "FiniteMPS from tensornetwork with bug fixed" +#~ msgid "output edges." #~ msgstr "" -#~ msgid "" -#~ "Bases: " -#~ ":py:class:`tensornetwork.matrixproductstates.finite_mps.FiniteMPS`" +#~ msgid "in edges." #~ msgstr "" #~ msgid "" -#~ "Apply a two-site gate to an " -#~ "MPS. This routine will in general " -#~ "destroy any canonical form of the " -#~ "state. If a canonical form is " -#~ "needed, the user can restore it " -#~ "using `FiniteMPS.position`." +#~ "reference nodes for the tensor network" +#~ " (needed if there is a scalar " +#~ "component)." #~ msgstr "" -#~ msgid "A two-body gate." +#~ msgid "edges to ignore when checking the dimensionality of the tensor network." #~ msgstr "" -#~ msgid "The first site where the gate acts." +#~ msgid "The new created QuOperator object." #~ msgstr "" -#~ msgid "The second site where the gate acts." +#~ msgid "Compute the reduced density matrix from the quantum state ``state``." #~ msgstr "" -#~ msgid "The maximum number of singular values to keep." +#~ msgid "Compute the trace of several inputs ``o`` as tensor or ``QuOperator``." #~ msgstr "" -#~ msgid "The maximum allowed truncation error." +#~ msgid "\\mathrm{Tr}(\\prod_i O_i)" #~ msgstr "" -#~ msgid "" -#~ "An optional value to choose the " -#~ "MPS tensor at `center_position` to be" -#~ " isometric after the application of " -#~ "the gate. Defaults to `site1`. If " -#~ "the MPS is canonical " -#~ "(i.e.`BaseMPS.center_position != None`), and " -#~ "if the orthogonality center coincides " -#~ "with either `site1` or `site2`, the " -#~ "orthogonality center will be shifted to" -#~ " `center_position` (`site1` by default). If" -#~ " the orthogonality center does not " -#~ "coincide with `(site1, site2)` then " -#~ "`MPS.center_position` is set to `None`." +#~ msgid "the trace of several inputs" #~ msgstr "" -#~ msgid "Multiply `max_truncation_err` with the largest singular value." +#~ msgid "Tensornetwork Simplification" #~ msgstr "" #~ msgid "" -#~ "\"rank of gate is {} but has " -#~ "to be 4\", \"site1 = {} is " -#~ "not between 0 <= site < N -" -#~ " 1 = {}\", \"site2 = {} is " -#~ "not between 1 <= site < N =" -#~ " {}\",\"Found site2 ={}, site1={}. Only " -#~ "nearest neighbor gates are currently " -#~ "supported\", \"f center_position = " -#~ "{center_position} not f in {(site1, " -#~ "site2)} \", or \"center_position = {}," -#~ " but gate is applied at sites " -#~ "{}, {}. Truncation should only be " -#~ "done if the gate is applied at " -#~ "the center position of the MPS.\"" +#~ "Get the new shape of two nodes," +#~ " also supporting to return original " +#~ "shapes of two nodes." #~ msgstr "" -#~ msgid "A scalar tensor containing the truncated weight of the truncation." +#~ msgid "node one" #~ msgstr "" -#~ msgid "Measure the expectation value of local operators `ops` site `sites`." +#~ msgid "node two" #~ msgstr "" -#~ msgid "A list Tensors of rank 2; the local operators to be measured." +#~ msgid "Whether to include original shape of two nodes, default is True." #~ msgstr "" -#~ msgid "Sites where `ops` act." +#~ msgid "The new shape of the two nodes." #~ msgstr "" -#~ msgid "measurements :math:`\\langle` `ops[n]`:math:`\\rangle` for n in `sites`" +#~ msgid "" +#~ "Contract between Node ``a`` and ``b``," +#~ " with correct shape only and no " +#~ "calculation" #~ msgstr "" -#~ msgid "" -#~ "Compute the correlator :math:`\\langle` " -#~ "`op1[site1], op2[s]`:math:`\\rangle` between `site1`" -#~ " and all sites `s` in `sites2`. " -#~ "If `s == site1`, `op2[s]` will be" -#~ " applied first." +#~ msgid "Shortcuts for measurement patterns on circuit" #~ msgstr "" -#~ msgid "Tensor of rank 2; the local operator at `site1`." +#~ msgid "Some common graphs and lattices" #~ msgstr "" -#~ msgid "Tensor of rank 2; the local operator at `sites2`." +#~ msgid "1D chain with ``n`` sites" #~ msgstr "" -#~ msgid "The site where `op1` acts" +#~ msgid "" +#~ "This measurements pattern is specifically " +#~ "suitable for vmap. Parameterize the " +#~ "Pauli string to be measured." #~ msgstr "" -#~ msgid "Sites where operator `op2` acts." +#~ msgid "" +#~ "parameter tensors determines what Pauli " +#~ "string to be measured, shape is " +#~ "[nwires, 4] if onehot is False." #~ msgstr "" #~ msgid "" -#~ "Correlator :math:`\\langle` `op1[site1], " -#~ "op2[s]`:math:`\\rangle` for `s` :math:`\\in` " -#~ "`sites2`." +#~ "[description], defaults to False. If set" +#~ " to be True, structures will first" +#~ " go through onehot procedure." #~ msgstr "" -#~ msgid "Quantum circuit: MPS state simulator" +#~ msgid "COO_sparse_matrix" #~ msgstr "" -#~ msgid "``MPSCircuit`` class. Simple usage demo below." +#~ msgid "a real and scalar tensor of shape []" #~ msgstr "" -#~ msgid "MPSCircuit object based on state simulator." +#~ msgid "Helper functions" #~ msgstr "" #~ msgid "" -#~ "If not None, the initial state of" -#~ " the circuit is taken as ``tensors``" -#~ " instead of :math:`\\vert 0\\rangle^n` " -#~ "qubits, defaults to None" +#~ "Return a callable function for output" +#~ " ith parts of the original output " +#~ "along the first axis. Original output" +#~ " supports List and Tensor." #~ msgstr "" -#~ msgid "The center position of MPS, default to 0" +#~ msgid "The function to be applied this method" #~ msgstr "" -#~ msgid "Apply a general qubit gate on MPS." +#~ msgid "The ith parts of original output along the first axis (axis=0 or dim=0)" #~ msgstr "" -#~ msgid "The Gate to be applied" +#~ msgid "The modified callable function" #~ msgstr "" -#~ msgid "Qubit indices of the gate" +#~ msgid "Visualization on circuits" #~ msgstr "" -#~ msgid "\"MPS does not support application of gate on > 2 qubits.\"" +#~ msgid "# TODO(@YHPeter): add examples" #~ msgstr "" #~ msgid "" -#~ "Apply a double qubit gate on " -#~ "adjacent qubits of Matrix Product States" -#~ " (MPS). Truncation rule is specified " -#~ "by `set_truncation_rule`." +#~ "Generate the PDF file with given " +#~ "latex string and filename. Latex command" +#~ " and file path can be specified. " +#~ "When notebook is True, convert the " +#~ "output PDF file to image and " +#~ "return a Image object." #~ msgstr "" -#~ msgid "The first qubit index of the gate" +#~ msgid "String of latex content" #~ msgstr "" -#~ msgid "The second qubit index of the gate" +#~ msgid "File name, defaults to random UUID `str(uuid4())`" #~ msgstr "" -#~ msgid "Center position of MPS, default is None" +#~ msgid "Executable Latex command, defaults to `pdflatex`" #~ msgstr "" -#~ msgid "" -#~ "Apply a double qubit gate on MPS." -#~ " Truncation rule is specified by " -#~ "`set_truncation_rule`." +#~ msgid "File path, defaults to current working place `os.getcwd()`" #~ msgstr "" -#~ msgid "" -#~ "Apply a single qubit gate on MPS," -#~ " and the gate must be unitary; " -#~ "no truncation is needed." +#~ msgid "if notebook is True, return `Image` object; otherwise return `None`" #~ msgstr "" -#~ msgid "gate to be applied" +#~ msgid "_summary_" #~ msgstr "" -#~ msgid "Qubit index of the gate" +#~ msgid "_description_" #~ msgstr "" -#~ msgid "Compute the conjugate of the current MPS." +#~ msgid "[description], default is None." #~ msgstr "" -#~ msgid "The constructed MPS" +#~ msgid "_description_, default is (1, -1)." #~ msgstr "" -#~ msgid "Copy the current MPS." +#~ msgid "Apply fredkin gate on the circuit." #~ msgstr "" -#~ msgid "Copy the current MPS without the tensors." +#~ msgid "Apply orx gate with parameters on the circuit." #~ msgstr "" -#~ msgid "Compute the expectation of the corresponding double qubit gate." +#~ msgid "Apply ory gate with parameters on the circuit." #~ msgstr "" -#~ msgid "qubit index of the gate" +#~ msgid "Apply orz gate with parameters on the circuit." #~ msgstr "" -#~ msgid "" -#~ "Compute the expectation of the " -#~ "corresponding single qubit gate in the" -#~ " form of tensor." +#~ msgid "Apply ox gate on the circuit." #~ msgstr "" -#~ msgid "Gate to be applied" +#~ msgid "Apply oy gate on the circuit." #~ msgstr "" -#~ msgid "The expectation of the corresponding single qubit gate" +#~ msgid "Apply oz gate on the circuit." #~ msgstr "" #~ msgid "" -#~ "Compute the expectation of the direct" -#~ " product of the corresponding two " -#~ "gates." +#~ "Random tensor between 0 or 1, " +#~ "defaults to be None, the random " +#~ "number will be generated automatically" #~ msgstr "" -#~ msgid "First gate to be applied" +#~ msgid "The str indicating the form of the output wavefunction." #~ msgstr "" -#~ msgid "Second gate to be applied" +#~ msgid "" +#~ "A collection of useful function snippets" +#~ " that irrelevant with the main " +#~ "modules or await for further refactor" #~ msgstr "" -#~ msgid "Qubit index of the first gate" +#~ msgid "VQNHE application" #~ msgstr "" -#~ msgid "Qubit index of the second gate" +#~ msgid "Apply **ANY** gate with parameters on the circuit." #~ msgstr "" -#~ msgid "The correlation of the corresponding two qubit gates" +#~ msgid "Apply **CR** gate with parameters on the circuit." #~ msgstr "" -#~ msgid "Construct the MPS from a given wavefunction." +#~ msgid "Apply **CRX** gate with parameters on the circuit." #~ msgstr "" -#~ msgid "The given wavefunction (any shape is OK)" +#~ msgid "Apply **CRY** gate with parameters on the circuit." #~ msgstr "" -#~ msgid "" -#~ "Compute the expectation of corresponding " -#~ "operators in the form of tensor." +#~ msgid "Apply **CRZ** gate with parameters on the circuit." #~ msgstr "" -#~ msgid "" -#~ "Operator and its position on the " -#~ "circuit, eg. ``(gates.Z(), [1]), (gates.X()," -#~ " [2])`` is for operator :math:`Z_1X_2`" +#~ msgid "Apply **EXP** gate with parameters on the circuit." #~ msgstr "" -#~ msgid "The expectation of corresponding operators" +#~ msgid "Apply **EXP1** gate with parameters on the circuit." #~ msgstr "" -#~ msgid "Get the normalized Center Position." +#~ msgid "Apply **FREDKIN** gate on the circuit." #~ msgstr "" -#~ msgid "Normalized Center Position." +#~ msgid "" +#~ "Qubit number than the gate applies " +#~ "on. The matrix for the gate is" +#~ " .. math:: \\begin{bmatrix} 1.+0.j " +#~ "& 0.+0.j & 0.+0.j & 0.+0.j & " +#~ "0.+0.j & 0.+0.j & 0.+0.j & " +#~ "0.+0.j\\\\ 0.+0.j & 1.+0.j & 0.+0.j" +#~ " & 0.+0.j & 0.+0.j & 0.+0.j &" +#~ " 0.+0.j & 0.+0.j\\\\ 0.+0.j & " +#~ "0.+0.j & 1.+0.j & 0.+0.j & 0.+0.j" +#~ " & 0.+0.j & 0.+0.j & 0.+0.j\\\\" +#~ " 0.+0.j & 0.+0.j & 0.+0.j & " +#~ "1.+0.j & 0.+0.j & 0.+0.j & 0.+0.j" +#~ " & 0.+0.j\\\\ 0.+0.j & 0.+0.j &" +#~ " 0.+0.j & 0.+0.j & 1.+0.j & " +#~ "0.+0.j & 0.+0.j & 0.+0.j\\\\ 0.+0.j" +#~ " & 0.+0.j & 0.+0.j & 0.+0.j &" +#~ " 0.+0.j & 0.+0.j & 1.+0.j & " +#~ "0.+0.j\\\\ 0.+0.j & 0.+0.j & 0.+0.j" +#~ " & 0.+0.j & 0.+0.j & 1.+0.j &" +#~ " 0.+0.j & 0.+0.j\\\\ 0.+0.j & " +#~ "0.+0.j & 0.+0.j & 0.+0.j & 0.+0.j" +#~ " & 0.+0.j & 0.+0.j & 1.+0.j " +#~ "\\end{bmatrix}" #~ msgstr "" -#~ msgid "Check whether the circuit is legal." +#~ msgid "Apply **ISWAP** gate on the circuit." #~ msgstr "" -#~ msgid "Whether the circuit is legal." +#~ msgid "Apply **ORX** gate with parameters on the circuit." #~ msgstr "" -#~ msgid "integer indicating the measure on which quantum line" +#~ msgid "Apply **ORY** gate with parameters on the circuit." #~ msgstr "" -#~ msgid "" -#~ "Middle measurement in the z-basis on " -#~ "the circuit, note the wavefunction " -#~ "output is not normalized with " -#~ "``mid_measurement`` involved, one should " -#~ "normalized the state manually if needed." +#~ msgid "Apply **ORZ** gate with parameters on the circuit." #~ msgstr "" -#~ msgid "The index of qubit that the Z direction postselection applied on" +#~ msgid "Apply **OX** gate on the circuit." #~ msgstr "" -#~ msgid "0 for spin up, 1 for spin down, defaults to 0" +#~ msgid "" +#~ "Qubit number than the gate applies " +#~ "on. The matrix for the gate is" +#~ " .. math:: \\begin{bmatrix} 0.+0.j " +#~ "& 1.+0.j & 0.+0.j & 0.+0.j\\\\ " +#~ "1.+0.j & 0.+0.j & 0.+0.j & " +#~ "0.+0.j\\\\ 0.+0.j & 0.+0.j & 1.+0.j" +#~ " & 0.+0.j\\\\ 0.+0.j & 0.+0.j &" +#~ " 0.+0.j & 1.+0.j \\end{bmatrix}" #~ msgstr "" -#~ msgid "Normalize MPS Circuit according to the center position." +#~ msgid "Apply **OY** gate on the circuit." +#~ msgstr "" + +#~ msgid "" +#~ "Qubit number than the gate applies " +#~ "on. The matrix for the gate is" +#~ " .. math:: \\begin{bmatrix} 0.+0.j " +#~ "& 0.-1.j & 0.+0.j & 0.+0.j\\\\ " +#~ "0.+1.j & 0.+0.j & 0.+0.j & " +#~ "0.+0.j\\\\ 0.+0.j & 0.+0.j & 1.+0.j" +#~ " & 0.+0.j\\\\ 0.+0.j & 0.+0.j &" +#~ " 0.+0.j & 1.+0.j \\end{bmatrix}" #~ msgstr "" -#~ msgid "Wrapper of tn.FiniteMPS.position. Set orthogonality center." +#~ msgid "Apply **OZ** gate on the circuit." #~ msgstr "" -#~ msgid "The orthogonality center" +#~ msgid "" +#~ "Qubit number than the gate applies " +#~ "on. The matrix for the gate is" +#~ " .. math:: \\begin{bmatrix} 1.+0.j " +#~ "& 0.+0.j & 0.+0.j & 0.+0.j\\\\ " +#~ "0.+0.j & -1.+0.j & 0.+0.j & " +#~ "0.+0.j\\\\ 0.+0.j & 0.+0.j & 1.+0.j" +#~ " & 0.+0.j\\\\ 0.+0.j & 0.+0.j &" +#~ " 0.+0.j & 1.+0.j \\end{bmatrix}" #~ msgstr "" -#~ msgid "Compute the projection between `other` as bra and `self` as ket." +#~ msgid "Apply **R** gate with parameters on the circuit." #~ msgstr "" -#~ msgid "ket of the other MPS, which will be converted to bra automatically" +#~ msgid "Apply **RX** gate with parameters on the circuit." #~ msgstr "" -#~ msgid "The projection in form of tensor" +#~ msgid "Apply **RY** gate with parameters on the circuit." #~ msgstr "" -#~ msgid "" -#~ "Set truncation rules when double qubit" -#~ " gates are applied. If nothing is " -#~ "specified, no truncation will take place" -#~ " and the bond dimension will keep " -#~ "growing. For more details, refer to " -#~ "`split_tensor`." +#~ msgid "Apply **RZ** gate with parameters on the circuit." #~ msgstr "" -#~ msgid "Tensor with shape [1, -1]" +#~ msgid "Apply **SD** gate on the circuit." #~ msgstr "" -#~ msgid "" -#~ "Split the tensor by SVD or QR " -#~ "depends on whether a truncation is " -#~ "required." +#~ msgid "Apply **TD** gate on the circuit." #~ msgstr "" -#~ msgid "The input tensor to split." +#~ msgid "Apply **TOFFOLI** gate on the circuit." #~ msgstr "" #~ msgid "" -#~ "Determine the orthogonal center is on" -#~ " the left tensor or the right " -#~ "tensor." -#~ msgstr "" - -#~ msgid "Two tensors after splitting" -#~ msgstr "" - -#~ msgid "Quantum state and operator class backend by tensornetwork" +#~ "Apply unitary gates in ``kraus`` " +#~ "randomly based on corresponding ``prob``." #~ msgstr "" -#~ msgid "Bases: :py:class:`tensorcircuit.quantum.QuOperator`" +#~ msgid "Get the eigenvalues of matrix ``a``." #~ msgstr "" -#~ msgid "Represents an adjoint (row) vector via a tensor network." +#~ msgid "eigenvalues of ``a``" #~ msgstr "" #~ msgid "" -#~ "Constructs a new `QuAdjointVector` from " -#~ "a tensor network. This encapsulates an" -#~ " existing tensor network, interpreting it" -#~ " as an adjoint vector (row vector)." -#~ msgstr "" - -#~ msgid "The edges of the network to be used as the input edges." +#~ "Apply **ANY** gate with parameters on" +#~ " the circuit. See " +#~ ":py:meth:`tensorcircuit.gates.any_gate`." #~ msgstr "" -#~ msgid "" -#~ "Nodes used to refer to parts of" -#~ " the tensor network that are not " -#~ "connected to any input or output " -#~ "edges (for example: a scalar factor)." +#~ msgid "Qubit number that the gate applies on." #~ msgstr "" #~ msgid "" -#~ "Optional collection of edges to ignore" -#~ " when performing consistency checks." +#~ "Apply **CNOT** gate on the circuit. " +#~ "See :py:meth:`tensorcircuit.gates.cnot_gate`." #~ msgstr "" #~ msgid "" -#~ "Construct a `QuAdjointVector` directly from" -#~ " a single tensor. This first wraps" -#~ " the tensor in a `Node`, then " -#~ "constructs the `QuAdjointVector` from that " -#~ "`Node`." +#~ "Qubit number that the gate applies " +#~ "on. The matrix for the gate is" +#~ " .. math:: \\begin{bmatrix} 1.+0.j " +#~ "& 0.+0.j & 0.+0.j & 0.+0.j\\\\ " +#~ "0.+0.j & 1.+0.j & 0.+0.j & " +#~ "0.+0.j\\\\ 0.+0.j & 0.+0.j & 0.+0.j" +#~ " & 1.+0.j\\\\ 0.+0.j & 0.+0.j &" +#~ " 1.+0.j & 0.+0.j \\end{bmatrix}" #~ msgstr "" -#~ msgid "The tensor for constructing an QuAdjointVector." +#~ msgid "Qubit number that the gate applies on. The matrix for the gate is" #~ msgstr "" #~ msgid "" -#~ "Sequence of integer indices specifying " -#~ "the order in which to interpret " -#~ "the axes as subsystems (input edges)." -#~ " If not specified, the axes are " -#~ "taken in ascending order." +#~ "Apply **CR** gate with parameters on " +#~ "the circuit. See " +#~ ":py:meth:`tensorcircuit.gates.cr_gate`." #~ msgstr "" -#~ msgid "The new constructed QuAdjointVector give from the given tensor." +#~ msgid "" +#~ "Apply **CRX** gate with parameters on" +#~ " the circuit. See " +#~ ":py:meth:`tensorcircuit.gates.crx_gate`." #~ msgstr "" #~ msgid "" -#~ "Represents a linear operator via a " -#~ "tensor network. To interpret a tensor" -#~ " network as a linear operator, some" -#~ " of the dangling edges must be " -#~ "designated as `out_edges` (output edges) " -#~ "and the rest as `in_edges` (input " -#~ "edges). Considered as a matrix, the " -#~ "`out_edges` represent the row index and" -#~ " the `in_edges` represent the column " -#~ "index. The (right) action of the " -#~ "operator on another then consists of " -#~ "connecting the `in_edges` of the first" -#~ " operator to the `out_edges` of the" -#~ " second. Can be used to do " -#~ "simple linear algebra with tensor " -#~ "networks." +#~ "Apply **CRY** gate with parameters on" +#~ " the circuit. See " +#~ ":py:meth:`tensorcircuit.gates.cry_gate`." #~ msgstr "" #~ msgid "" -#~ "Creates a new `QuOperator` from a " -#~ "tensor network. This encapsulates an " -#~ "existing tensor network, interpreting it " -#~ "as a linear operator. The network " -#~ "is checked for consistency: All dangling" -#~ " edges must either be in `out_edges`," -#~ " `in_edges`, or `ignore_edges`." +#~ "Apply **CRZ** gate with parameters on" +#~ " the circuit. See " +#~ ":py:meth:`tensorcircuit.gates.crz_gate`." #~ msgstr "" -#~ msgid "The edges of the network to be used as the output edges." +#~ msgid "" +#~ "Apply **CY** gate on the circuit. " +#~ "See :py:meth:`tensorcircuit.gates.cy_gate`." #~ msgstr "" #~ msgid "" -#~ "Optional collection of dangling edges to" -#~ " ignore when performing consistency checks." +#~ "Qubit number that the gate applies " +#~ "on. The matrix for the gate is" +#~ " .. math:: \\begin{bmatrix} 1.+0.j " +#~ "& 0.+0.j & 0.+0.j & 0.+0.j\\\\ " +#~ "0.+0.j & 1.+0.j & 0.+0.j & " +#~ "0.+0.j\\\\ 0.+0.j & 0.+0.j & 0.+0.j" +#~ " & 0.-1.j\\\\ 0.+0.j & 0.+0.j &" +#~ " 0.+1.j & 0.+0.j \\end{bmatrix}" #~ msgstr "" #~ msgid "" -#~ "At least one reference node is " -#~ "required to specify a scalar. None " -#~ "provided!" +#~ "Apply **CZ** gate on the circuit. " +#~ "See :py:meth:`tensorcircuit.gates.cz_gate`." #~ msgstr "" #~ msgid "" -#~ "The adjoint of the operator. This " -#~ "creates a new `QuOperator` with " -#~ "complex-conjugate copies of all tensors " -#~ "in the network and with the input" -#~ " and output edges switched." +#~ "Qubit number that the gate applies " +#~ "on. The matrix for the gate is" +#~ " .. math:: \\begin{bmatrix} 1.+0.j " +#~ "& 0.+0.j & 0.+0.j & 0.+0.j\\\\ " +#~ "0.+0.j & 1.+0.j & 0.+0.j & " +#~ "0.+0.j\\\\ 0.+0.j & 0.+0.j & 1.+0.j" +#~ " & 0.+0.j\\\\ 0.+0.j & 0.+0.j &" +#~ " 0.+0.j & -1.+0.j \\end{bmatrix}" #~ msgstr "" #~ msgid "" -#~ "Check that the network has the " -#~ "expected dimensionality. This checks that " -#~ "all input and output edges are " -#~ "dangling and that there are no " -#~ "other dangling edges (except any " -#~ "specified in `ignore_edges`). If not, an" -#~ " exception is raised." +#~ "Apply **EXP** gate with parameters on" +#~ " the circuit. See " +#~ ":py:meth:`tensorcircuit.gates.exp_gate`." #~ msgstr "" #~ msgid "" -#~ "Contract the tensor network in place." -#~ " This modifies the tensor network " -#~ "representation of the operator (or " -#~ "vector, or scalar), reducing it to " -#~ "a single tensor, without changing the" -#~ " value." +#~ "Apply **EXP1** gate with parameters on" +#~ " the circuit. See " +#~ ":py:meth:`tensorcircuit.gates.exp1_gate`." #~ msgstr "" -#~ msgid "Manually specify the axis ordering of the final tensor." +#~ msgid "" +#~ "Apply **FREDKIN** gate on the circuit." +#~ " See :py:meth:`tensorcircuit.gates.fredkin_gate`." #~ msgstr "" -#~ msgid "The present object." +#~ msgid "" +#~ "Qubit number that the gate applies " +#~ "on. The matrix for the gate is" +#~ " .. math:: \\begin{bmatrix} 1.+0.j " +#~ "& 0.+0.j & 0.+0.j & 0.+0.j & " +#~ "0.+0.j & 0.+0.j & 0.+0.j & " +#~ "0.+0.j\\\\ 0.+0.j & 1.+0.j & 0.+0.j" +#~ " & 0.+0.j & 0.+0.j & 0.+0.j &" +#~ " 0.+0.j & 0.+0.j\\\\ 0.+0.j & " +#~ "0.+0.j & 1.+0.j & 0.+0.j & 0.+0.j" +#~ " & 0.+0.j & 0.+0.j & 0.+0.j\\\\" +#~ " 0.+0.j & 0.+0.j & 0.+0.j & " +#~ "1.+0.j & 0.+0.j & 0.+0.j & 0.+0.j" +#~ " & 0.+0.j\\\\ 0.+0.j & 0.+0.j &" +#~ " 0.+0.j & 0.+0.j & 1.+0.j & " +#~ "0.+0.j & 0.+0.j & 0.+0.j\\\\ 0.+0.j" +#~ " & 0.+0.j & 0.+0.j & 0.+0.j &" +#~ " 0.+0.j & 0.+0.j & 1.+0.j & " +#~ "0.+0.j\\\\ 0.+0.j & 0.+0.j & 0.+0.j" +#~ " & 0.+0.j & 0.+0.j & 1.+0.j &" +#~ " 0.+0.j & 0.+0.j\\\\ 0.+0.j & " +#~ "0.+0.j & 0.+0.j & 0.+0.j & 0.+0.j" +#~ " & 0.+0.j & 0.+0.j & 1.+0.j " +#~ "\\end{bmatrix}" #~ msgstr "" #~ msgid "" -#~ "Contracts the tensor network in place" -#~ " and returns the final tensor. Note" -#~ " that this modifies the tensor " -#~ "network representing the operator. The " -#~ "default ordering for the axes of " -#~ "the final tensor is: `*out_edges, " -#~ "*in_edges`. If there are any \"ignored\"" -#~ " edges, their axes come first: " -#~ "`*ignored_edges, *out_edges, *in_edges`." +#~ "Apply **H** gate on the circuit. " +#~ "See :py:meth:`tensorcircuit.gates.h_gate`." #~ msgstr "" #~ msgid "" -#~ "Manually specify the axis ordering of" -#~ " the final tensor. The default " -#~ "ordering is determined by `out_edges` " -#~ "and `in_edges` (see above)." +#~ "Qubit number that the gate applies " +#~ "on. The matrix for the gate is" +#~ " .. math:: \\begin{bmatrix} " +#~ "0.70710677+0.j & 0.70710677+0.j\\\\ " +#~ "0.70710677+0.j & -0.70710677+0.j \\end{bmatrix}" #~ msgstr "" -#~ msgid "Node count '{}' > 1 after contraction!" +#~ msgid "" +#~ "Apply **I** gate on the circuit. " +#~ "See :py:meth:`tensorcircuit.gates.i_gate`." #~ msgstr "" -#~ msgid "The final tensor representing the operator." +#~ msgid "" +#~ "Qubit number that the gate applies " +#~ "on. The matrix for the gate is" +#~ " .. math:: \\begin{bmatrix} 1.+0.j " +#~ "& 0.+0.j\\\\ 0.+0.j & 1.+0.j " +#~ "\\end{bmatrix}" #~ msgstr "" #~ msgid "" -#~ "Construct a `QuOperator` directly from a" -#~ " single tensor. This first wraps the" -#~ " tensor in a `Node`, then constructs" -#~ " the `QuOperator` from that `Node`." +#~ "Apply **ISWAP** gate on the circuit. " +#~ "See :py:meth:`tensorcircuit.gates.iswap_gate`." #~ msgstr "" -#~ msgid "The tensor." +#~ msgid "" +#~ "Qubit number that the gate applies " +#~ "on. The matrix for the gate is" +#~ " .. math:: \\begin{bmatrix} 1.+0.j " +#~ "& 0.+0.j & 0.+0.j & 0.+0.j\\\\ " +#~ "0.+0.j & 0.+0.j & 0.+1.j & " +#~ "0.+0.j\\\\ 0.+0.j & 0.+1.j & 0.+0.j" +#~ " & 0.+0.j\\\\ 0.+0.j & 0.+0.j &" +#~ " 0.+0.j & 1.+0.j \\end{bmatrix}" #~ msgstr "" -#~ msgid "The axis indices of `tensor` to use as `out_edges`." +#~ msgid "" +#~ "Apply **ORX** gate with parameters on" +#~ " the circuit. See " +#~ ":py:meth:`tensorcircuit.gates.orx_gate`." #~ msgstr "" -#~ msgid "The axis indices of `tensor` to use as `in_edges`." +#~ msgid "" +#~ "Apply **ORY** gate with parameters on" +#~ " the circuit. See " +#~ ":py:meth:`tensorcircuit.gates.ory_gate`." #~ msgstr "" -#~ msgid "The new operator." +#~ msgid "" +#~ "Apply **ORZ** gate with parameters on" +#~ " the circuit. See " +#~ ":py:meth:`tensorcircuit.gates.orz_gate`." #~ msgstr "" -#~ msgid "All tensor-network nodes involved in the operator." +#~ msgid "" +#~ "Apply **OX** gate on the circuit. " +#~ "See :py:meth:`tensorcircuit.gates.ox_gate`." #~ msgstr "" #~ msgid "" -#~ "The norm of the operator. This is" -#~ " the 2-norm (also known as the " -#~ "Frobenius or Hilbert-Schmidt norm)." +#~ "Qubit number that the gate applies " +#~ "on. The matrix for the gate is" +#~ " .. math:: \\begin{bmatrix} 0.+0.j " +#~ "& 1.+0.j & 0.+0.j & 0.+0.j\\\\ " +#~ "1.+0.j & 0.+0.j & 0.+0.j & " +#~ "0.+0.j\\\\ 0.+0.j & 0.+0.j & 1.+0.j" +#~ " & 0.+0.j\\\\ 0.+0.j & 0.+0.j &" +#~ " 0.+0.j & 1.+0.j \\end{bmatrix}" #~ msgstr "" #~ msgid "" -#~ "The partial trace of the operator. " -#~ "Subsystems to trace out are supplied " -#~ "as indices, so that dangling edges " -#~ "are connected to each other as: " -#~ "`out_edges[i] ^ in_edges[i] for i in " -#~ "subsystems_to_trace_out` This does not modify" -#~ " the original network. The original " -#~ "ordering of the remaining subsystems is" -#~ " maintained." +#~ "Apply **OY** gate on the circuit. " +#~ "See :py:meth:`tensorcircuit.gates.oy_gate`." #~ msgstr "" -#~ msgid "Indices of subsystems to trace out." +#~ msgid "" +#~ "Qubit number that the gate applies " +#~ "on. The matrix for the gate is" +#~ " .. math:: \\begin{bmatrix} 0.+0.j " +#~ "& 0.-1.j & 0.+0.j & 0.+0.j\\\\ " +#~ "0.+1.j & 0.+0.j & 0.+0.j & " +#~ "0.+0.j\\\\ 0.+0.j & 0.+0.j & 1.+0.j" +#~ " & 0.+0.j\\\\ 0.+0.j & 0.+0.j &" +#~ " 0.+0.j & 1.+0.j \\end{bmatrix}" #~ msgstr "" -#~ msgid "A new QuOperator or QuScalar representing the result." +#~ msgid "" +#~ "Apply **OZ** gate on the circuit. " +#~ "See :py:meth:`tensorcircuit.gates.oz_gate`." #~ msgstr "" #~ msgid "" -#~ "Tensor product with another operator. " -#~ "Given two operators `A` and `B`, " -#~ "produces a new operator `AB` " -#~ "representing `A` ⊗ `B`. The `out_edges`" -#~ " (`in_edges`) of `AB` is simply the" -#~ " concatenation of the `out_edges` " -#~ "(`in_edges`) of `A.copy()` with that of" -#~ " `B.copy()`: `new_out_edges = [*out_edges_A_copy," -#~ " *out_edges_B_copy]` `new_in_edges = " -#~ "[*in_edges_A_copy, *in_edges_B_copy]`" +#~ "Qubit number that the gate applies " +#~ "on. The matrix for the gate is" +#~ " .. math:: \\begin{bmatrix} 1.+0.j " +#~ "& 0.+0.j & 0.+0.j & 0.+0.j\\\\ " +#~ "0.+0.j & -1.+0.j & 0.+0.j & " +#~ "0.+0.j\\\\ 0.+0.j & 0.+0.j & 1.+0.j" +#~ " & 0.+0.j\\\\ 0.+0.j & 0.+0.j &" +#~ " 0.+0.j & 1.+0.j \\end{bmatrix}" #~ msgstr "" -#~ msgid "The other operator (`B`)." +#~ msgid "" +#~ "Apply **R** gate with parameters on " +#~ "the circuit. See " +#~ ":py:meth:`tensorcircuit.gates.r_gate`." #~ msgstr "" -#~ msgid "The result (`AB`)." +#~ msgid "" +#~ "Apply **RX** gate with parameters on " +#~ "the circuit. See " +#~ ":py:meth:`tensorcircuit.gates.rx_gate`." #~ msgstr "" -#~ msgid "The trace of the operator." +#~ msgid "" +#~ "Apply **RY** gate with parameters on " +#~ "the circuit. See " +#~ ":py:meth:`tensorcircuit.gates.ry_gate`." #~ msgstr "" -#~ msgid "Represents a scalar via a tensor network." +#~ msgid "" +#~ "Apply **RZ** gate with parameters on " +#~ "the circuit. See " +#~ ":py:meth:`tensorcircuit.gates.rz_gate`." #~ msgstr "" #~ msgid "" -#~ "Constructs a new `QuScalar` from a " -#~ "tensor network. This encapsulates an " -#~ "existing tensor network, interpreting it " -#~ "as a scalar." +#~ "Apply **S** gate on the circuit. " +#~ "See :py:meth:`tensorcircuit.gates.s_gate`." #~ msgstr "" #~ msgid "" -#~ "Nodes used to refer to the tensor" -#~ " network (need not be exhaustive -" -#~ " one node from each disconnected " -#~ "subnetwork is sufficient)." +#~ "Qubit number that the gate applies " +#~ "on. The matrix for the gate is" +#~ " .. math:: \\begin{bmatrix} 1.+0.j " +#~ "& 0.+0.j\\\\ 0.+0.j & 0.+1.j " +#~ "\\end{bmatrix}" #~ msgstr "" #~ msgid "" -#~ "Construct a `QuScalar` directly from a" -#~ " single tensor. This first wraps the" -#~ " tensor in a `Node`, then constructs" -#~ " the `QuScalar` from that `Node`." +#~ "Apply **SD** gate on the circuit. " +#~ "See :py:meth:`tensorcircuit.gates.sd_gate`." #~ msgstr "" -#~ msgid "The tensor for constructing a new QuScalar." +#~ msgid "" +#~ "Qubit number that the gate applies " +#~ "on. The matrix for the gate is" +#~ " .. math:: \\begin{bmatrix} 1.+0.j " +#~ "& 0.+0.j\\\\ 0.+0.j & 0.-1.j " +#~ "\\end{bmatrix}" #~ msgstr "" -#~ msgid "The new constructed QuScalar from the given tensor." +#~ msgid "" +#~ "Apply **SWAP** gate on the circuit. " +#~ "See :py:meth:`tensorcircuit.gates.swap_gate`." #~ msgstr "" -#~ msgid "Represents a (column) vector via a tensor network." +#~ msgid "" +#~ "Qubit number that the gate applies " +#~ "on. The matrix for the gate is" +#~ " .. math:: \\begin{bmatrix} 1.+0.j " +#~ "& 0.+0.j & 0.+0.j & 0.+0.j\\\\ " +#~ "0.+0.j & 0.+0.j & 1.+0.j & " +#~ "0.+0.j\\\\ 0.+0.j & 1.+0.j & 0.+0.j" +#~ " & 0.+0.j\\\\ 0.+0.j & 0.+0.j &" +#~ " 0.+0.j & 1.+0.j \\end{bmatrix}" #~ msgstr "" #~ msgid "" -#~ "Constructs a new `QuVector` from a " -#~ "tensor network. This encapsulates an " -#~ "existing tensor network, interpreting it " -#~ "as a (column) vector." +#~ "Apply **T** gate on the circuit. " +#~ "See :py:meth:`tensorcircuit.gates.t_gate`." #~ msgstr "" #~ msgid "" -#~ "Construct a `QuVector` directly from a" -#~ " single tensor. This first wraps the" -#~ " tensor in a `Node`, then constructs" -#~ " the `QuVector` from that `Node`." +#~ "Qubit number that the gate applies " +#~ "on. The matrix for the gate is" +#~ " .. math:: \\begin{bmatrix} 1. &" +#~ " +0.j & 0. & +0.j\\\\ 0. &" +#~ " +0.j & 0.70710677+0.70710677j \\end{bmatrix}" #~ msgstr "" -#~ msgid "The tensor for constructing a \"QuVector\"." +#~ msgid "" +#~ "Apply **TD** gate on the circuit. " +#~ "See :py:meth:`tensorcircuit.gates.td_gate`." #~ msgstr "" #~ msgid "" -#~ "Sequence of integer indices specifying " -#~ "the order in which to interpret " -#~ "the axes as subsystems (output edges)." -#~ " If not specified, the axes are " -#~ "taken in ascending order." +#~ "Qubit number that the gate applies " +#~ "on. The matrix for the gate is" +#~ " .. math:: \\begin{bmatrix} 1. &" +#~ " +0.j & 0. & +0.j\\\\ 0. &" +#~ " +0.j & 0.70710677-0.70710677j \\end{bmatrix}" #~ msgstr "" -#~ msgid "The new constructed QuVector from the given tensor." +#~ msgid "" +#~ "Apply **TOFFOLI** gate on the circuit." +#~ " See :py:meth:`tensorcircuit.gates.toffoli_gate`." #~ msgstr "" #~ msgid "" -#~ "Check the vector spaces represented by" -#~ " two lists of edges are compatible." -#~ " The number of edges must be " -#~ "the same and the dimensions of " -#~ "each pair of edges must match. " -#~ "Otherwise, an exception is raised. " -#~ ":param edges_1: List of edges " -#~ "representing a many-body Hilbert space." -#~ " :type edges_1: Sequence[Edge] :param " -#~ "edges_2: List of edges representing a" -#~ " many-body Hilbert space. :type " -#~ "edges_2: Sequence[Edge]" +#~ "Qubit number that the gate applies " +#~ "on. The matrix for the gate is" +#~ " .. math:: \\begin{bmatrix} 1.+0.j " +#~ "& 0.+0.j & 0.+0.j & 0.+0.j & " +#~ "0.+0.j & 0.+0.j & 0.+0.j & " +#~ "0.+0.j\\\\ 0.+0.j & 1.+0.j & 0.+0.j" +#~ " & 0.+0.j & 0.+0.j & 0.+0.j &" +#~ " 0.+0.j & 0.+0.j\\\\ 0.+0.j & " +#~ "0.+0.j & 1.+0.j & 0.+0.j & 0.+0.j" +#~ " & 0.+0.j & 0.+0.j & 0.+0.j\\\\" +#~ " 0.+0.j & 0.+0.j & 0.+0.j & " +#~ "1.+0.j & 0.+0.j & 0.+0.j & 0.+0.j" +#~ " & 0.+0.j\\\\ 0.+0.j & 0.+0.j &" +#~ " 0.+0.j & 0.+0.j & 1.+0.j & " +#~ "0.+0.j & 0.+0.j & 0.+0.j\\\\ 0.+0.j" +#~ " & 0.+0.j & 0.+0.j & 0.+0.j &" +#~ " 0.+0.j & 1.+0.j & 0.+0.j & " +#~ "0.+0.j\\\\ 0.+0.j & 0.+0.j & 0.+0.j" +#~ " & 0.+0.j & 0.+0.j & 0.+0.j &" +#~ " 0.+0.j & 1.+0.j\\\\ 0.+0.j & " +#~ "0.+0.j & 0.+0.j & 0.+0.j & 0.+0.j" +#~ " & 0.+0.j & 1.+0.j & 0.+0.j " +#~ "\\end{bmatrix}" #~ msgstr "" #~ msgid "" -#~ "Hilbert-space mismatch: \"Cannot connect " -#~ "{} subsystems with {} subsystems\", or" -#~ " \"Input dimension {} != output " -#~ "dimension {}.\"" +#~ "Apply **WROOT** gate on the circuit. " +#~ "See :py:meth:`tensorcircuit.gates.wroot_gate`." #~ msgstr "" #~ msgid "" -#~ "Eliminates any connected CopyNodes that " -#~ "are identity matrices. This will modify" -#~ " the network represented by `nodes`. " -#~ "Only identities that are connected to" -#~ " other nodes are eliminated." +#~ "Qubit number that the gate applies " +#~ "on. The matrix for the gate is" +#~ " .. math:: \\begin{bmatrix} " +#~ "0.70710677+0.j & -0.5 & -0.5j\\\\ 0.5" +#~ " & -0.5j & 0.70710677+0.j \\end{bmatrix}" #~ msgstr "" -#~ msgid "Collection of nodes to search." +#~ msgid "" +#~ "Apply **X** gate on the circuit. " +#~ "See :py:meth:`tensorcircuit.gates.x_gate`." #~ msgstr "" #~ msgid "" -#~ "The Dictionary mapping remaining Nodes " -#~ "to any replacements, Dictionary specifying " -#~ "all dangling-edge replacements." +#~ "Qubit number that the gate applies " +#~ "on. The matrix for the gate is" +#~ " .. math:: \\begin{bmatrix} 0.+0.j " +#~ "& 1.+0.j\\\\ 1.+0.j & 0.+0.j " +#~ "\\end{bmatrix}" #~ msgstr "" -#~ msgid "Compute the entropy from the given density matrix ``rho``." +#~ msgid "" +#~ "Apply **Y** gate on the circuit. " +#~ "See :py:meth:`tensorcircuit.gates.y_gate`." #~ msgstr "" -#~ msgid "[description], defaults to 1e-12" +#~ msgid "" +#~ "Qubit number that the gate applies " +#~ "on. The matrix for the gate is" +#~ " .. math:: \\begin{bmatrix} 0.+0.j " +#~ "& 0.-1.j\\\\ 0.+1.j & 0.+0.j " +#~ "\\end{bmatrix}" #~ msgstr "" #~ msgid "" -#~ "Note: further jit is recommended. For" -#~ " large Hilbert space, sparse Hamiltonian" -#~ " is recommended" +#~ "Apply **Z** gate on the circuit. " +#~ "See :py:meth:`tensorcircuit.gates.z_gate`." #~ msgstr "" #~ msgid "" -#~ "Construct a 'QuOperator' representing the " -#~ "identity on a given space. Internally," -#~ " this is done by constructing " -#~ "'CopyNode's for each edge, with " -#~ "dimension according to 'space'." +#~ "Qubit number that the gate applies " +#~ "on. The matrix for the gate is" +#~ " .. math:: \\begin{bmatrix} 1.+0.j " +#~ "& 0.+0.j\\\\ 0.+0.j & -1.+0.j " +#~ "\\end{bmatrix}" #~ msgstr "" #~ msgid "" -#~ "A sequence of integers for the " -#~ "dimensions of the tensor product factors" -#~ " of the space (the edges in the" -#~ " tensor network)." +#~ "Apply unitary gates in ``kraus`` " +#~ "randomly based on corresponding ``prob``. " +#~ "If ``prob`` is ``None``, this is " +#~ "reduced to kraus channel language." #~ msgstr "" -#~ msgid "The data type (for conversion to dense)." +#~ msgid "The density matrix simulator based on tensornetwork engine." #~ msgstr "" -#~ msgid "The desired identity operator." +#~ msgid "Number of qubits" #~ msgstr "" -#~ msgid "" -#~ "Simulate the measuring of each qubit " -#~ "of ``p`` in the computational basis, " -#~ "thus producing output like that of " -#~ "``qiskit``." +#~ msgid "if True, nothing initialized, only for internal use, defaults to False" #~ msgstr "" -#~ msgid "" -#~ "The quantum state, assumed to be " -#~ "normalized, as either a ket or " -#~ "density operator." +#~ msgid "the state input for the circuit, defaults to None" #~ msgstr "" -#~ msgid "The number of counts to perform." +#~ msgid "the density matrix input for the circuit, defaults to None" #~ msgstr "" #~ msgid "" -#~ "Defaults True. The bool indicating " -#~ "whether the return form is in the" -#~ " form of two array or one of" -#~ " the same length as the ``state`` " -#~ "(if ``sparse=False``)." +#~ "Apply amplitudedamping quantum channel on " +#~ "the circuit. See " +#~ ":py:meth:`tensorcircuit.channels.amplitudedampingchannel`" #~ msgstr "" -#~ msgid "The counts for each bit string measured." +#~ msgid "Parameters for the channel." #~ msgstr "" -#~ msgid "" -#~ "Constructs an appropriately specialized " -#~ "QuOperator. If there are no edges, " -#~ "creates a QuScalar. If the are " -#~ "only output (input) edges, creates a " -#~ "QuVector (QuAdjointVector). Otherwise creates " -#~ "a QuOperator." +#~ msgid "Return the output density matrix of the circuit." #~ msgstr "" #~ msgid "" -#~ "op = qu.quantum_constructor([], [psi_node[0], " -#~ "psi_node[1]]) >>> show_attributes(op) op.is_scalar()" -#~ " -> False op.is_vector() -> " -#~ "False op.is_adjoint_vector() -> True " -#~ "len(op.out_edges) -> 0 len(op.in_edges)" -#~ " -> 2 >>> # psi_node[0] -> " -#~ "op.in_edges[0] >>> # psi_node[1] -> " -#~ "op.in_edges[1]" +#~ "check whether the final return is " +#~ "a legal density matrix, defaults to " +#~ "False" #~ msgstr "" -#~ msgid "output edges." +#~ msgid "whether to reuse previous results, defaults to True" #~ msgstr "" -#~ msgid "in edges." +#~ msgid "The output densitymatrix in 2D shape tensor form" #~ msgstr "" #~ msgid "" -#~ "reference nodes for the tensor network" -#~ " (needed if there is a scalar " -#~ "component)." -#~ msgstr "" - -#~ msgid "edges to ignore when checking the dimensionality of the tensor network." +#~ "Apply depolarizing quantum channel on " +#~ "the circuit. See " +#~ ":py:meth:`tensorcircuit.channels.depolarizingchannel`" #~ msgstr "" -#~ msgid "The new created QuOperator object." +#~ msgid "" +#~ "Apply phasedamping quantum channel on " +#~ "the circuit. See " +#~ ":py:meth:`tensorcircuit.channels.phasedampingchannel`" #~ msgstr "" -#~ msgid "Compute the reduced density matrix from the quantum state ``state``." +#~ msgid "" +#~ "Apply reset quantum channel on the " +#~ "circuit. See " +#~ ":py:meth:`tensorcircuit.channels.resetchannel`" #~ msgstr "" -#~ msgid "Compute the trace of several inputs ``o`` as tensor or ``QuOperator``." +#~ msgid "Generate tensorflow sparse matrix from Pauli string sum" #~ msgstr "" -#~ msgid "\\mathrm{Tr}(\\prod_i O_i)" +#~ msgid "" +#~ "1D Tensor representing for a Pauli " +#~ "string, e.g. [1, 0, 0, 3, 2] " +#~ "is for :math:`X_0Z_3Y_4`" #~ msgstr "" -#~ msgid "the trace of several inputs" +#~ msgid "" +#~ "the weight for the Pauli string " +#~ "defaults to None (all Pauli strings " +#~ "weight 1.0)" #~ msgstr "" -#~ msgid "Tensornetwork Simplification" +#~ msgid "the tensorflow sparse matrix" #~ msgstr "" #~ msgid "" -#~ "Get the new shape of two nodes," -#~ " also supporting to return original " -#~ "shapes of two nodes." +#~ "2D Tensor, each row is for a " +#~ "Pauli string, e.g. [1, 0, 0, 3," +#~ " 2] is for :math:`X_0Z_3Y_4`" #~ msgstr "" -#~ msgid "node one" +#~ msgid "" +#~ "1D Tensor, each element corresponds the" +#~ " weight for each Pauli string " +#~ "defaults to None (all Pauli strings " +#~ "weight 1.0)" #~ msgstr "" -#~ msgid "node two" +#~ msgid "the tensorflow coo sparse matrix" #~ msgstr "" -#~ msgid "Whether to include original shape of two nodes, default is True." +#~ msgid "Generate scipy sparse matrix from Pauli string sum" #~ msgstr "" -#~ msgid "The new shape of the two nodes." +#~ msgid "the scipy coo sparse matrix" #~ msgstr "" -#~ msgid "" -#~ "Contract between Node ``a`` and ``b``," -#~ " with correct shape only and no " -#~ "calculation" +#~ msgid "Generate tensorflow dense matrix from Pauli string sum" #~ msgstr "" -#~ msgid "Shortcuts for measurement patterns on circuit" +#~ msgid "the tensorflow dense matrix" #~ msgstr "" -#~ msgid "Some common graphs and lattices" +#~ msgid "" +#~ "The projector of the operator. The " +#~ "operator, as a linear operator, on " +#~ "the adjoint of the operator." #~ msgstr "" -#~ msgid "1D chain with ``n`` sites" +#~ msgid "" +#~ "Set :math:`A` is the operator in " +#~ "matrix form, then the projector of " +#~ "operator is defined as: :math:`A^\\dagger " +#~ "A`" #~ msgstr "" -#~ msgid "" -#~ "This measurements pattern is specifically " -#~ "suitable for vmap. Parameterize the " -#~ "Pauli string to be measured." +#~ msgid "The projector of the operator." #~ msgstr "" -#~ msgid "" -#~ "parameter tensors determines what Pauli " -#~ "string to be measured, shape is " -#~ "[nwires, 4] if onehot is False." +#~ msgid "The reduced density of the operator." #~ msgstr "" #~ msgid "" -#~ "[description], defaults to False. If set" -#~ " to be True, structures will first" -#~ " go through onehot procedure." +#~ "Set :math:`A` is the matrix of the" +#~ " operator, then the reduced density " +#~ "is defined as:" #~ msgstr "" -#~ msgid "COO_sparse_matrix" +#~ msgid "\\mathrm{Tr}_{subsystems}(A^\\dagger A)" #~ msgstr "" -#~ msgid "a real and scalar tensor of shape []" +#~ msgid "" +#~ "Firstly, take the projector of the " +#~ "operator, then trace out the subsystems" +#~ " to trace out are supplied as " +#~ "indices, so that dangling edges are " +#~ "connected to each other as: " +#~ "`out_edges[i] ^ in_edges[i] for i in " +#~ "subsystems_to_trace_out` This does not modify" +#~ " the original network. The original " +#~ "ordering of the remaining subsystems is" +#~ " maintained." #~ msgstr "" -#~ msgid "Helper functions" +#~ msgid "" +#~ "The QuOperator of the reduced density" +#~ " of the operator with given " +#~ "subsystems." #~ msgstr "" #~ msgid "" -#~ "Return a callable function for output" -#~ " ith parts of the original output " -#~ "along the first axis. Original output" -#~ " supports List and Tensor." +#~ "Contracts the tensor network in place" +#~ " and returns the final tensor in " +#~ "two dimentional matrix. The default " +#~ "ordering for the axes of the final" +#~ " tensor is: (:math:`\\prod` dimension of" +#~ " out_edges, :math:`\\prod` dimension of " +#~ "in_edges)" #~ msgstr "" -#~ msgid "The function to be applied this method" +#~ msgid "The two-dimentional tensor representing the operator." #~ msgstr "" -#~ msgid "The ith parts of original output along the first axis (axis=0 or dim=0)" +#~ msgid "" +#~ "Returns a bool indicating if QuOperator" +#~ " is an adjoint vector. Examples can" +#~ " be found in the `QuOperator.from_tensor`." #~ msgstr "" -#~ msgid "The modified callable function" +#~ msgid "" +#~ "Returns a bool indicating if QuOperator" +#~ " is a scalar. Examples can be " +#~ "found in the `QuOperator.from_tensor`." #~ msgstr "" -#~ msgid "Visualization on circuits" +#~ msgid "" +#~ "Returns a bool indicating if QuOperator" +#~ " is a vector. Examples can be " +#~ "found in the `QuOperator.from_tensor`." #~ msgstr "" -#~ msgid "# TODO(@YHPeter): add examples" +#~ msgid "" +#~ "Tensor product with another operator. " +#~ "Given two operators `A` and `B`, " +#~ "produces a new operator `AB` " +#~ "representing :math:`A ⊗ B`. The " +#~ "`out_edges` (`in_edges`) of `AB` is " +#~ "simply the concatenation of the " +#~ "`out_edges` (`in_edges`) of `A.copy()` with" +#~ " that of `B.copy()`: `new_out_edges = " +#~ "[*out_edges_A_copy, *out_edges_B_copy]` `new_in_edges " +#~ "= [*in_edges_A_copy, *in_edges_B_copy]`" #~ msgstr "" #~ msgid "" -#~ "Generate the PDF file with given " -#~ "latex string and filename. Latex command" -#~ " and file path can be specified. " -#~ "When notebook is True, convert the " -#~ "output PDF file to image and " -#~ "return a Image object." +#~ "Set :math:`A` is the operator in " +#~ "matrix form, then the projector of " +#~ "operator is defined as: :math:`A " +#~ "A^\\dagger`" #~ msgstr "" -#~ msgid "String of latex content" +#~ msgid "\\mathrm{Tr}_{subsystems}(A A^\\dagger)" #~ msgstr "" -#~ msgid "File name, defaults to random UUID `str(uuid4())`" +#~ msgid "Compute the double state of the given Hamiltonian operator ``h``." #~ msgstr "" -#~ msgid "Executable Latex command, defaults to `pdflatex`" +#~ msgid "Hamiltonian operator in form of Tensor." #~ msgstr "" -#~ msgid "File path, defaults to current working place `os.getcwd()`" +#~ msgid "The double state of ``h`` with the given ``beta``." #~ msgstr "" -#~ msgid "if notebook is True, return `Image` object; otherwise return `None`" +#~ msgid "Return fidelity scalar between two states rho and rho0." #~ msgstr "" -#~ msgid "_summary_" +#~ msgid "\\operatorname{Tr}(\\sqrt{\\sqrt{rho} rho_0 \\sqrt{rho}})" #~ msgstr "" -#~ msgid "_description_" +#~ msgid "The density matrix in form of Tensor." #~ msgstr "" -#~ msgid "[description], default is None." +#~ msgid "The sqrtm of a Hermitian matrix ``a``." #~ msgstr "" -#~ msgid "_description_, default is (1, -1)." +#~ msgid "Compute the Gibbs state of the given Hamiltonian operator ``h``." #~ msgstr "" -#~ msgid "Apply fredkin gate on the circuit." +#~ msgid "The Gibbs state of ``h`` with the given ``beta``." #~ msgstr "" -#~ msgid "Apply orx gate with parameters on the circuit." +#~ msgid "Mutual information between AB subsystem described by ``cut``." #~ msgstr "" -#~ msgid "Apply ory gate with parameters on the circuit." +#~ msgid "The AB subsystem." #~ msgstr "" -#~ msgid "Apply orz gate with parameters on the circuit." +#~ msgid "The mutual information between AB subsystem described by ``cut``." #~ msgstr "" -#~ msgid "Apply ox gate on the circuit." +#~ msgid "Taylor expansion of :math:`ln(x+1)`." #~ msgstr "" -#~ msgid "Apply oy gate on the circuit." +#~ msgid "The :math:`k` th order, default is 2." #~ msgstr "" -#~ msgid "Apply oz gate on the circuit." +#~ msgid "The :math:`k` th order of Taylor expansion of :math:`ln(x+1)`." #~ msgstr "" #~ msgid "" -#~ "Random tensor between 0 or 1, " -#~ "defaults to be None, the random " -#~ "number will be generated automatically" +#~ "Compute the trace distance between two" +#~ " density matrix ``rho`` and ``rho2``." #~ msgstr "" -#~ msgid "The str indicating the form of the output wavefunction." +#~ msgid "Epsilon, defaults to 1e-12" #~ msgstr "" -#~ msgid "" -#~ "A collection of useful function snippets" -#~ " that irrelevant with the main " -#~ "modules or await for further refactor" +#~ msgid "The trace distance between two density matrix ``rho`` and ``rho2``." #~ msgstr "" -#~ msgid "VQNHE application" +#~ msgid "\\operatorname{Tr}(\\prod_i O_i)" #~ msgstr "" -#~ msgid "Apply **ANY** gate with parameters on the circuit." +#~ msgid "" +#~ "Compute the truncated free energy from" +#~ " the given density matrix ``rho``." #~ msgstr "" -#~ msgid "Apply **CR** gate with parameters on the circuit." +#~ msgid "The :math:`k` th order, defaults to 2" #~ msgstr "" -#~ msgid "Apply **CRX** gate with parameters on the circuit." +#~ msgid "The :math:`k` th order of the truncated free energy." #~ msgstr "" -#~ msgid "Apply **CRY** gate with parameters on the circuit." +#~ msgid "" +#~ "The circuit ansatz is firstly one " +#~ "layer of Hadamard gates and then " +#~ "we have ``nlayers`` blocks of " +#~ ":math:`e^{i\\theta Z_iZ_{i+1}}` two-qubit gate" +#~ " in ladder layout, following rx gate." #~ msgstr "" -#~ msgid "Apply **CRZ** gate with parameters on the circuit." +#~ msgid "The circuit" #~ msgstr "" -#~ msgid "Apply **EXP** gate with parameters on the circuit." +#~ msgid "paramter tensor with 2*nlayer*n elements" #~ msgstr "" -#~ msgid "Apply **EXP1** gate with parameters on the circuit." +#~ msgid "number of ZZ+RX blocks, defaults to 2" #~ msgstr "" -#~ msgid "Apply **FREDKIN** gate on the circuit." +#~ msgid "" +#~ "whether use SVD split to reduce ZZ" +#~ " gate bond dimension, defaults to " +#~ "False" #~ msgstr "" -#~ msgid "" -#~ "Qubit number than the gate applies " -#~ "on. The matrix for the gate is" -#~ " .. math:: \\begin{bmatrix} 1.+0.j " -#~ "& 0.+0.j & 0.+0.j & 0.+0.j & " -#~ "0.+0.j & 0.+0.j & 0.+0.j & " -#~ "0.+0.j\\\\ 0.+0.j & 1.+0.j & 0.+0.j" -#~ " & 0.+0.j & 0.+0.j & 0.+0.j &" -#~ " 0.+0.j & 0.+0.j\\\\ 0.+0.j & " -#~ "0.+0.j & 1.+0.j & 0.+0.j & 0.+0.j" -#~ " & 0.+0.j & 0.+0.j & 0.+0.j\\\\" -#~ " 0.+0.j & 0.+0.j & 0.+0.j & " -#~ "1.+0.j & 0.+0.j & 0.+0.j & 0.+0.j" -#~ " & 0.+0.j\\\\ 0.+0.j & 0.+0.j &" -#~ " 0.+0.j & 0.+0.j & 1.+0.j & " -#~ "0.+0.j & 0.+0.j & 0.+0.j\\\\ 0.+0.j" -#~ " & 0.+0.j & 0.+0.j & 0.+0.j &" -#~ " 0.+0.j & 0.+0.j & 1.+0.j & " -#~ "0.+0.j\\\\ 0.+0.j & 0.+0.j & 0.+0.j" -#~ " & 0.+0.j & 0.+0.j & 1.+0.j &" -#~ " 0.+0.j & 0.+0.j\\\\ 0.+0.j & " -#~ "0.+0.j & 0.+0.j & 0.+0.j & 0.+0.j" -#~ " & 0.+0.j & 0.+0.j & 1.+0.j " -#~ "\\end{bmatrix}" +#~ msgid "The circuit with example ansatz attached" #~ msgstr "" -#~ msgid "Apply **ISWAP** gate on the circuit." +#~ msgid "" +#~ "Function decorator wraps the function " +#~ "with the first input and output in" +#~ " the format of circuit, the wrapped" +#~ " function has the first input and " +#~ "the output as the state tensor." #~ msgstr "" -#~ msgid "Apply **ORX** gate with parameters on the circuit." +#~ msgid "Function with the fist input and the output as ``Circuit`` object." #~ msgstr "" -#~ msgid "Apply **ORY** gate with parameters on the circuit." +#~ msgid "" +#~ "Wrapped function with the first input" +#~ " and the output as the state " +#~ "tensor correspondingly." #~ msgstr "" -#~ msgid "Apply **ORZ** gate with parameters on the circuit." +#~ msgid "Two-dimensional grid lattice" #~ msgstr "" -#~ msgid "Apply **OX** gate on the circuit." +#~ msgid "number of rows" #~ msgstr "" -#~ msgid "" -#~ "Qubit number than the gate applies " -#~ "on. The matrix for the gate is" -#~ " .. math:: \\begin{bmatrix} 0.+0.j " -#~ "& 1.+0.j & 0.+0.j & 0.+0.j\\\\ " -#~ "1.+0.j & 0.+0.j & 0.+0.j & " -#~ "0.+0.j\\\\ 0.+0.j & 0.+0.j & 1.+0.j" -#~ " & 0.+0.j\\\\ 0.+0.j & 0.+0.j &" -#~ " 0.+0.j & 1.+0.j \\end{bmatrix}" +#~ msgid "number of cols" #~ msgstr "" -#~ msgid "Apply **OY** gate on the circuit." +#~ msgid "return all col edge with 1d index encoding" #~ msgstr "" #~ msgid "" -#~ "Qubit number than the gate applies " -#~ "on. The matrix for the gate is" -#~ " .. math:: \\begin{bmatrix} 0.+0.j " -#~ "& 0.-1.j & 0.+0.j & 0.+0.j\\\\ " -#~ "0.+1.j & 0.+0.j & 0.+0.j & " -#~ "0.+0.j\\\\ 0.+0.j & 0.+0.j & 1.+0.j" -#~ " & 0.+0.j\\\\ 0.+0.j & 0.+0.j &" -#~ " 0.+0.j & 1.+0.j \\end{bmatrix}" +#~ "whether to include pbc edges (periodic" +#~ " boundary condition), defaults to False" #~ msgstr "" -#~ msgid "Apply **OZ** gate on the circuit." +#~ msgid "list of col edge" #~ msgstr "" -#~ msgid "" -#~ "Qubit number than the gate applies " -#~ "on. The matrix for the gate is" -#~ " .. math:: \\begin{bmatrix} 1.+0.j " -#~ "& 0.+0.j & 0.+0.j & 0.+0.j\\\\ " -#~ "0.+0.j & -1.+0.j & 0.+0.j & " -#~ "0.+0.j\\\\ 0.+0.j & 0.+0.j & 1.+0.j" -#~ " & 0.+0.j\\\\ 0.+0.j & 0.+0.j &" -#~ " 0.+0.j & 1.+0.j \\end{bmatrix}" +#~ msgid "return all row edge with 1d index encoding" #~ msgstr "" -#~ msgid "Apply **R** gate with parameters on the circuit." +#~ msgid "list of row edge" #~ msgstr "" -#~ msgid "Apply **RX** gate with parameters on the circuit." +#~ msgid "Get the 2D grid lattice in ``nx.Graph`` format" #~ msgstr "" -#~ msgid "Apply **RY** gate with parameters on the circuit." +#~ msgid "" +#~ "whether to include pbc edges (periodic" +#~ " boundary condition), defaults to True" #~ msgstr "" -#~ msgid "Apply **RZ** gate with parameters on the circuit." +#~ msgid "" +#~ "Generate a permutation matrix P. Due " +#~ "to the different convention or qubits'" +#~ " order in qiskit and tensorcircuit, " +#~ "the unitary represented by the same " +#~ "circuit is different. They are related" +#~ " by this permutation matrix P: P " +#~ "@ U_qiskit @ P = U_tc" #~ msgstr "" -#~ msgid "Apply **SD** gate on the circuit." +#~ msgid "# of qubits" #~ msgstr "" -#~ msgid "Apply **TD** gate on the circuit." +#~ msgid "The permutation matrix P" #~ msgstr "" -#~ msgid "Apply **TOFFOLI** gate on the circuit." +#~ msgid "" +#~ "Generate a qiskit quantum circuit using" +#~ " the quantum intermediate representation " +#~ "(qir) in tensorcircuit." #~ msgstr "" -#~ msgid "" -#~ "Apply unitary gates in ``kraus`` " -#~ "randomly based on corresponding ``prob``." +#~ msgid "qiskit QuantumCircuit object" #~ msgstr "" -#~ msgid "Get the eigenvalues of matrix ``a``." +#~ msgid "" +#~ "Generate a tensorcircuit circuit using " +#~ "the quantum circuit data in qiskit." #~ msgstr "" -#~ msgid "eigenvalues of ``a``" +#~ msgid "Quantum circuit data from qiskit." #~ msgstr "" -#~ msgid "" -#~ "Apply **ANY** gate with parameters on" -#~ " the circuit. See " -#~ ":py:meth:`tensorcircuit.gates.any_gate`." +#~ msgid "Input state of the circuit. Default is None." #~ msgstr "" -#~ msgid "Qubit number that the gate applies on." +#~ msgid "A quantum circuit in tensorcircuit" #~ msgstr "" #~ msgid "" -#~ "Apply **CNOT** gate on the circuit. " -#~ "See :py:meth:`tensorcircuit.gates.cnot_gate`." +#~ "Translating from the gate name to " +#~ "gate information including the number of" +#~ " control qubits and the reduced gate" +#~ " name." #~ msgstr "" -#~ msgid "" -#~ "Qubit number that the gate applies " -#~ "on. The matrix for the gate is" -#~ " .. math:: \\begin{bmatrix} 1.+0.j " -#~ "& 0.+0.j & 0.+0.j & 0.+0.j\\\\ " -#~ "0.+0.j & 1.+0.j & 0.+0.j & " -#~ "0.+0.j\\\\ 0.+0.j & 0.+0.j & 0.+0.j" -#~ " & 1.+0.j\\\\ 0.+0.j & 0.+0.j &" -#~ " 1.+0.j & 0.+0.j \\end{bmatrix}" +#~ msgid "String of gate name" #~ msgstr "" -#~ msgid "Qubit number that the gate applies on. The matrix for the gate is" +#~ msgid "# of control qubits, reduced gate name" #~ msgstr "" #~ msgid "" -#~ "Apply **CR** gate with parameters on " -#~ "the circuit. See " -#~ ":py:meth:`tensorcircuit.gates.cr_gate`." +#~ "Generate Tex code from 'qir' string " +#~ "to illustrate the circuit structure. " +#~ "This visualization is based on quantikz" +#~ " package." #~ msgstr "" -#~ msgid "" -#~ "Apply **CRX** gate with parameters on" -#~ " the circuit. See " -#~ ":py:meth:`tensorcircuit.gates.crx_gate`." +#~ msgid "The quantum intermediate representation of a circuit in tensorcircuit." #~ msgstr "" -#~ msgid "" -#~ "Apply **CRY** gate with parameters on" -#~ " the circuit. See " -#~ ":py:meth:`tensorcircuit.gates.cry_gate`." +#~ msgid "Initial state, default is an all zero state '000...000'." #~ msgstr "" -#~ msgid "" -#~ "Apply **CRZ** gate with parameters on" -#~ " the circuit. See " -#~ ":py:meth:`tensorcircuit.gates.crz_gate`." +#~ msgid "Measurement Basis, default is None which means no" #~ msgstr "" #~ msgid "" -#~ "Apply **CY** gate on the circuit. " -#~ "See :py:meth:`tensorcircuit.gates.cy_gate`." +#~ "measurement in the end of the " +#~ "circuit. :type measure: Optional[List[str]] " +#~ ":param rcompress: If true, a right " +#~ "compression of the circuit will be " +#~ "conducted. A right compression means we" +#~ " will try to shift gates from " +#~ "right to left if possible. Default " +#~ "is false. :type rcompress: bool :param" +#~ " lcompress: If true, a left " +#~ "compression of the circuit will be " +#~ "conducted. A left compression means we" +#~ " will try to shift gates from " +#~ "left to right if possible. Default " +#~ "is false. :type lcompress: bool :param" +#~ " standalone: If true, the tex code" +#~ " will be designed to generate a " +#~ "standalone document. Default is false " +#~ "which means the generated tex code " +#~ "is just a quantikz code block. " +#~ ":type standalone: bool :param " +#~ "return_string_table: If true, a string " +#~ "table of tex code will also be " +#~ "returned. Default is false. :type " +#~ "return_string_table: bool :return: Tex code" +#~ " of circuit visualization based on " +#~ "quantikz package. If return_string_table is" +#~ " true, a string table of tex " +#~ "code will also be returned. :rtype: " +#~ "Union[str, Tuple[str, List[List[str]]]]" #~ msgstr "" -#~ msgid "" -#~ "Qubit number that the gate applies " -#~ "on. The matrix for the gate is" -#~ " .. math:: \\begin{bmatrix} 1.+0.j " -#~ "& 0.+0.j & 0.+0.j & 0.+0.j\\\\ " -#~ "0.+0.j & 1.+0.j & 0.+0.j & " -#~ "0.+0.j\\\\ 0.+0.j & 0.+0.j & 0.+0.j" -#~ " & 0.-1.j\\\\ 0.+0.j & 0.+0.j &" -#~ " 0.+1.j & 0.+0.j \\end{bmatrix}" +#~ msgid ":math:`ket`." #~ msgstr "" #~ msgid "" -#~ "Apply **CZ** gate on the circuit. " -#~ "See :py:meth:`tensorcircuit.gates.cz_gate`." +#~ "Get Pauli string array and weights " +#~ "array for a qubit Hamiltonian as a" +#~ " sum of Pauli strings defined in " +#~ "openfermion QubitOperator." #~ msgstr "" -#~ msgid "" -#~ "Qubit number that the gate applies " -#~ "on. The matrix for the gate is" -#~ " .. math:: \\begin{bmatrix} 1.+0.j " -#~ "& 0.+0.j & 0.+0.j & 0.+0.j\\\\ " -#~ "0.+0.j & 1.+0.j & 0.+0.j & " -#~ "0.+0.j\\\\ 0.+0.j & 0.+0.j & 1.+0.j" -#~ " & 0.+0.j\\\\ 0.+0.j & 0.+0.j &" -#~ " 0.+0.j & -1.+0.j \\end{bmatrix}" +#~ msgid "Apply mpo gate in MPO format on the circuit." #~ msgstr "" -#~ msgid "" -#~ "Apply **EXP** gate with parameters on" -#~ " the circuit. See " -#~ ":py:meth:`tensorcircuit.gates.exp_gate`." +#~ msgid "Apply multicontrol gate in MPO format on the circuit." #~ msgstr "" -#~ msgid "" -#~ "Apply **EXP1** gate with parameters on" -#~ " the circuit. See " -#~ ":py:meth:`tensorcircuit.gates.exp1_gate`." +#~ msgid "Returns the amplitude of the circuit given the bitstring l." #~ msgstr "" -#~ msgid "" -#~ "Apply **FREDKIN** gate on the circuit." -#~ " See :py:meth:`tensorcircuit.gates.fredkin_gate`." +#~ msgid "Apply the gate to two bits with given indexes." +#~ msgstr "" + +#~ msgid "The Gate applied on bits." #~ msgstr "" -#~ msgid "" -#~ "Qubit number that the gate applies " -#~ "on. The matrix for the gate is" -#~ " .. math:: \\begin{bmatrix} 1.+0.j " -#~ "& 0.+0.j & 0.+0.j & 0.+0.j & " -#~ "0.+0.j & 0.+0.j & 0.+0.j & " -#~ "0.+0.j\\\\ 0.+0.j & 1.+0.j & 0.+0.j" -#~ " & 0.+0.j & 0.+0.j & 0.+0.j &" -#~ " 0.+0.j & 0.+0.j\\\\ 0.+0.j & " -#~ "0.+0.j & 1.+0.j & 0.+0.j & 0.+0.j" -#~ " & 0.+0.j & 0.+0.j & 0.+0.j\\\\" -#~ " 0.+0.j & 0.+0.j & 0.+0.j & " -#~ "1.+0.j & 0.+0.j & 0.+0.j & 0.+0.j" -#~ " & 0.+0.j\\\\ 0.+0.j & 0.+0.j &" -#~ " 0.+0.j & 0.+0.j & 1.+0.j & " -#~ "0.+0.j & 0.+0.j & 0.+0.j\\\\ 0.+0.j" -#~ " & 0.+0.j & 0.+0.j & 0.+0.j &" -#~ " 0.+0.j & 0.+0.j & 1.+0.j & " -#~ "0.+0.j\\\\ 0.+0.j & 0.+0.j & 0.+0.j" -#~ " & 0.+0.j & 0.+0.j & 1.+0.j &" -#~ " 0.+0.j & 0.+0.j\\\\ 0.+0.j & " -#~ "0.+0.j & 0.+0.j & 0.+0.j & 0.+0.j" -#~ " & 0.+0.j & 0.+0.j & 1.+0.j " -#~ "\\end{bmatrix}" +#~ msgid "The index of the bit to apply the Gate." #~ msgstr "" -#~ msgid "" -#~ "Apply **H** gate on the circuit. " -#~ "See :py:meth:`tensorcircuit.gates.h_gate`." +#~ msgid "Apply the gate to the bit with the given index." #~ msgstr "" -#~ msgid "" -#~ "Qubit number that the gate applies " -#~ "on. The matrix for the gate is" -#~ " .. math:: \\begin{bmatrix} " -#~ "0.70710677+0.j & 0.70710677+0.j\\\\ " -#~ "0.70710677+0.j & -0.70710677+0.j \\end{bmatrix}" +#~ msgid "The Gate applied on the bit." #~ msgstr "" #~ msgid "" -#~ "Apply **I** gate on the circuit. " -#~ "See :py:meth:`tensorcircuit.gates.i_gate`." +#~ "Return the list of nodes that " +#~ "consititues the expectation value just " +#~ "before the contraction." #~ msgstr "" -#~ msgid "" -#~ "Qubit number that the gate applies " -#~ "on. The matrix for the gate is" -#~ " .. math:: \\begin{bmatrix} 1.+0.j " -#~ "& 0.+0.j\\\\ 0.+0.j & 1.+0.j " -#~ "\\end{bmatrix}" +#~ msgid "whether contract the output state firstly, defaults to True" #~ msgstr "" -#~ msgid "" -#~ "Apply **ISWAP** gate on the circuit. " -#~ "See :py:meth:`tensorcircuit.gates.iswap_gate`." +#~ msgid "The tensor network for the expectation" #~ msgstr "" #~ msgid "" -#~ "Qubit number that the gate applies " -#~ "on. The matrix for the gate is" -#~ " .. math:: \\begin{bmatrix} 1.+0.j " -#~ "& 0.+0.j & 0.+0.j & 0.+0.j\\\\ " -#~ "0.+0.j & 0.+0.j & 0.+1.j & " -#~ "0.+0.j\\\\ 0.+0.j & 0.+1.j & 0.+0.j" -#~ " & 0.+0.j\\\\ 0.+0.j & 0.+0.j &" -#~ " 0.+0.j & 1.+0.j \\end{bmatrix}" +#~ "if true, we sample from the final" +#~ " state if memory allsows, True is " +#~ "prefered, defaults to False" #~ msgstr "" #~ msgid "" -#~ "Apply **ORX** gate with parameters on" -#~ " the circuit. See " -#~ ":py:meth:`tensorcircuit.gates.orx_gate`." +#~ "List (if batch) of tuple (binary " +#~ "configuration tensor and correponding " +#~ "probability)" #~ msgstr "" -#~ msgid "" -#~ "Apply **ORY** gate with parameters on" -#~ " the circuit. See " -#~ ":py:meth:`tensorcircuit.gates.ory_gate`." +#~ msgid "Sampling bistrings from the circuit output based on quantum amplitudes." #~ msgstr "" -#~ msgid "" -#~ "Apply **ORZ** gate with parameters on" -#~ " the circuit. See " -#~ ":py:meth:`tensorcircuit.gates.orz_gate`." +#~ msgid "tensorcircuit.densitymatrix2" #~ msgstr "" -#~ msgid "" -#~ "Apply **OX** gate on the circuit. " -#~ "See :py:meth:`tensorcircuit.gates.ox_gate`." +#~ msgid "Apply **CNOT** gate on the circuit." #~ msgstr "" -#~ msgid "" -#~ "Qubit number that the gate applies " -#~ "on. The matrix for the gate is" -#~ " .. math:: \\begin{bmatrix} 0.+0.j " -#~ "& 1.+0.j & 0.+0.j & 0.+0.j\\\\ " -#~ "1.+0.j & 0.+0.j & 0.+0.j & " -#~ "0.+0.j\\\\ 0.+0.j & 0.+0.j & 1.+0.j" -#~ " & 0.+0.j\\\\ 0.+0.j & 0.+0.j &" -#~ " 0.+0.j & 1.+0.j \\end{bmatrix}" +#~ msgid "Apply **CY** gate on the circuit." #~ msgstr "" -#~ msgid "" -#~ "Apply **OY** gate on the circuit. " -#~ "See :py:meth:`tensorcircuit.gates.oy_gate`." +#~ msgid "Apply **CZ** gate on the circuit." #~ msgstr "" -#~ msgid "" -#~ "Qubit number that the gate applies " -#~ "on. The matrix for the gate is" -#~ " .. math:: \\begin{bmatrix} 0.+0.j " -#~ "& 0.-1.j & 0.+0.j & 0.+0.j\\\\ " -#~ "0.+1.j & 0.+0.j & 0.+0.j & " -#~ "0.+0.j\\\\ 0.+0.j & 0.+0.j & 1.+0.j" -#~ " & 0.+0.j\\\\ 0.+0.j & 0.+0.j &" -#~ " 0.+0.j & 1.+0.j \\end{bmatrix}" +#~ msgid "Apply **H** gate on the circuit." #~ msgstr "" -#~ msgid "" -#~ "Apply **OZ** gate on the circuit. " -#~ "See :py:meth:`tensorcircuit.gates.oz_gate`." +#~ msgid "Apply **I** gate on the circuit." #~ msgstr "" -#~ msgid "" -#~ "Qubit number that the gate applies " -#~ "on. The matrix for the gate is" -#~ " .. math:: \\begin{bmatrix} 1.+0.j " -#~ "& 0.+0.j & 0.+0.j & 0.+0.j\\\\ " -#~ "0.+0.j & -1.+0.j & 0.+0.j & " -#~ "0.+0.j\\\\ 0.+0.j & 0.+0.j & 1.+0.j" -#~ " & 0.+0.j\\\\ 0.+0.j & 0.+0.j &" -#~ " 0.+0.j & 1.+0.j \\end{bmatrix}" +#~ msgid "Apply **S** gate on the circuit." #~ msgstr "" -#~ msgid "" -#~ "Apply **R** gate with parameters on " -#~ "the circuit. See " -#~ ":py:meth:`tensorcircuit.gates.r_gate`." +#~ msgid "Apply **SWAP** gate on the circuit." #~ msgstr "" -#~ msgid "" -#~ "Apply **RX** gate with parameters on " -#~ "the circuit. See " -#~ ":py:meth:`tensorcircuit.gates.rx_gate`." +#~ msgid "Apply **T** gate on the circuit." #~ msgstr "" -#~ msgid "" -#~ "Apply **RY** gate with parameters on " -#~ "the circuit. See " -#~ ":py:meth:`tensorcircuit.gates.ry_gate`." +#~ msgid "Apply **WROOT** gate on the circuit." #~ msgstr "" -#~ msgid "" -#~ "Apply **RZ** gate with parameters on " -#~ "the circuit. See " -#~ ":py:meth:`tensorcircuit.gates.rz_gate`." +#~ msgid "Apply **X** gate on the circuit." #~ msgstr "" -#~ msgid "" -#~ "Apply **S** gate on the circuit. " -#~ "See :py:meth:`tensorcircuit.gates.s_gate`." +#~ msgid "Apply **Y** gate on the circuit." #~ msgstr "" -#~ msgid "" -#~ "Qubit number that the gate applies " -#~ "on. The matrix for the gate is" -#~ " .. math:: \\begin{bmatrix} 1.+0.j " -#~ "& 0.+0.j\\\\ 0.+0.j & 0.+1.j " -#~ "\\end{bmatrix}" +#~ msgid "Apply **Z** gate on the circuit." #~ msgstr "" #~ msgid "" -#~ "Apply **SD** gate on the circuit. " -#~ "See :py:meth:`tensorcircuit.gates.sd_gate`." +#~ "Compute :math:`\\prod_{i\\in \\text{index}} s_i`," +#~ " where the probability for each " +#~ "bitstring is given as a vector " +#~ "``results``." #~ msgstr "" -#~ msgid "" -#~ "Qubit number that the gate applies " -#~ "on. The matrix for the gate is" -#~ " .. math:: \\begin{bmatrix} 1.+0.j " -#~ "& 0.+0.j\\\\ 0.+0.j & 0.-1.j " -#~ "\\end{bmatrix}" +#~ msgid "Generate sparse tensor from Pauli string sum" #~ msgstr "" -#~ msgid "" -#~ "Apply **SWAP** gate on the circuit. " -#~ "See :py:meth:`tensorcircuit.gates.swap_gate`." +#~ msgid "Generate dense matrix from Pauli string sum" #~ msgstr "" -#~ msgid "" -#~ "Qubit number that the gate applies " -#~ "on. The matrix for the gate is" -#~ " .. math:: \\begin{bmatrix} 1.+0.j " -#~ "& 0.+0.j & 0.+0.j & 0.+0.j\\\\ " -#~ "0.+0.j & 0.+0.j & 1.+0.j & " -#~ "0.+0.j\\\\ 0.+0.j & 1.+0.j & 0.+0.j" -#~ " & 0.+0.j\\\\ 0.+0.j & 0.+0.j &" -#~ " 0.+0.j & 1.+0.j \\end{bmatrix}" +#~ msgid "Generate Heisenberg Hamiltonian with possible external fields." #~ msgstr "" -#~ msgid "" -#~ "Apply **T** gate on the circuit. " -#~ "See :py:meth:`tensorcircuit.gates.t_gate`." +#~ msgid "calibration qubit list" #~ msgstr "" -#~ msgid "" -#~ "Qubit number that the gate applies " -#~ "on. The matrix for the gate is" -#~ " .. math:: \\begin{bmatrix} 1. &" -#~ " +0.j & 0. & +0.j\\\\ 0. &" -#~ " +0.j & 0.70710677+0.70710677j \\end{bmatrix}" +#~ msgid "tensorcircuit.cloud" #~ msgstr "" -#~ msgid "" -#~ "Apply **TD** gate on the circuit. " -#~ "See :py:meth:`tensorcircuit.gates.td_gate`." +#~ msgid "tensorcircuit.cloud.config" #~ msgstr "" -#~ msgid "" -#~ "Qubit number that the gate applies " -#~ "on. The matrix for the gate is" -#~ " .. math:: \\begin{bmatrix} 1. &" -#~ " +0.j & 0. & +0.j\\\\ 0. &" -#~ " +0.j & 0.70710677-0.70710677j \\end{bmatrix}" +#~ msgid "gate name list to be counted, defaults to None (counting all gates)" #~ msgstr "" #~ msgid "" -#~ "Apply **TOFFOLI** gate on the circuit." -#~ " See :py:meth:`tensorcircuit.gates.toffoli_gate`." +#~ "Bases: " +#~ ":py:class:`tensornetwork.backends.jax.jax_backend.JaxBackend`, " +#~ ":py:class:`tensorcircuit.backends.abstract_backend.ExtendedBackend`" #~ msgstr "" #~ msgid "" -#~ "Qubit number that the gate applies " -#~ "on. The matrix for the gate is" -#~ " .. math:: \\begin{bmatrix} 1.+0.j " -#~ "& 0.+0.j & 0.+0.j & 0.+0.j & " -#~ "0.+0.j & 0.+0.j & 0.+0.j & " -#~ "0.+0.j\\\\ 0.+0.j & 1.+0.j & 0.+0.j" -#~ " & 0.+0.j & 0.+0.j & 0.+0.j &" -#~ " 0.+0.j & 0.+0.j\\\\ 0.+0.j & " -#~ "0.+0.j & 1.+0.j & 0.+0.j & 0.+0.j" -#~ " & 0.+0.j & 0.+0.j & 0.+0.j\\\\" -#~ " 0.+0.j & 0.+0.j & 0.+0.j & " -#~ "1.+0.j & 0.+0.j & 0.+0.j & 0.+0.j" -#~ " & 0.+0.j\\\\ 0.+0.j & 0.+0.j &" -#~ " 0.+0.j & 0.+0.j & 1.+0.j & " -#~ "0.+0.j & 0.+0.j & 0.+0.j\\\\ 0.+0.j" -#~ " & 0.+0.j & 0.+0.j & 0.+0.j &" -#~ " 0.+0.j & 1.+0.j & 0.+0.j & " -#~ "0.+0.j\\\\ 0.+0.j & 0.+0.j & 0.+0.j" -#~ " & 0.+0.j & 0.+0.j & 0.+0.j &" -#~ " 0.+0.j & 1.+0.j\\\\ 0.+0.j & " -#~ "0.+0.j & 0.+0.j & 0.+0.j & 0.+0.j" -#~ " & 0.+0.j & 1.+0.j & 0.+0.j " -#~ "\\end{bmatrix}" +#~ "Bases: " +#~ ":py:class:`tensornetwork.backends.numpy.numpy_backend.NumPyBackend`," +#~ " " +#~ ":py:class:`tensorcircuit.backends.abstract_backend.ExtendedBackend`" #~ msgstr "" #~ msgid "" -#~ "Apply **WROOT** gate on the circuit. " -#~ "See :py:meth:`tensorcircuit.gates.wroot_gate`." +#~ "Bases: " +#~ ":py:class:`tensornetwork.backends.pytorch.pytorch_backend.PyTorchBackend`," +#~ " " +#~ ":py:class:`tensorcircuit.backends.abstract_backend.ExtendedBackend`" #~ msgstr "" #~ msgid "" -#~ "Qubit number that the gate applies " -#~ "on. The matrix for the gate is" -#~ " .. math:: \\begin{bmatrix} " -#~ "0.70710677+0.j & -0.5 & -0.5j\\\\ 0.5" -#~ " & -0.5j & 0.70710677+0.j \\end{bmatrix}" +#~ "Bases: " +#~ ":py:class:`tensornetwork.backends.tensorflow.tensorflow_backend.TensorFlowBackend`," +#~ " " +#~ ":py:class:`tensorcircuit.backends.abstract_backend.ExtendedBackend`" #~ msgstr "" -#~ msgid "" -#~ "Apply **X** gate on the circuit. " -#~ "See :py:meth:`tensorcircuit.gates.x_gate`." +#~ msgid "Bases: :py:class:`tensorcircuit.abstractcircuit.AbstractCircuit`" #~ msgstr "" -#~ msgid "" -#~ "Qubit number that the gate applies " -#~ "on. The matrix for the gate is" -#~ " .. math:: \\begin{bmatrix} 0.+0.j " -#~ "& 1.+0.j\\\\ 1.+0.j & 0.+0.j " -#~ "\\end{bmatrix}" +#~ msgid "Return a Depolarizing Channel for 1 qubit or 2 qubits" #~ msgstr "" -#~ msgid "" -#~ "Apply **Y** gate on the circuit. " -#~ "See :py:meth:`tensorcircuit.gates.y_gate`." +#~ msgid "number of qubits, 1 and 2 are avaliable, defaults 1" #~ msgstr "" -#~ msgid "" -#~ "Qubit number that the gate applies " -#~ "on. The matrix for the gate is" -#~ " .. math:: \\begin{bmatrix} 0.+0.j " -#~ "& 0.-1.j\\\\ 0.+1.j & 0.+0.j " -#~ "\\end{bmatrix}" +#~ msgid "Bases: :py:class:`tensorcircuit.basecircuit.BaseCircuit`" #~ msgstr "" -#~ msgid "" -#~ "Apply **Z** gate on the circuit. " -#~ "See :py:meth:`tensorcircuit.gates.z_gate`." +#~ msgid "Bases: :py:class:`torch.nn.modules.module.Module`" #~ msgstr "" #~ msgid "" -#~ "Qubit number that the gate applies " -#~ "on. The matrix for the gate is" -#~ " .. math:: \\begin{bmatrix} 1.+0.j " -#~ "& 0.+0.j\\\\ 0.+0.j & -1.+0.j " -#~ "\\end{bmatrix}" +#~ "This allows better BC support for " +#~ ":meth:`load_state_dict`. In :meth:`state_dict`, the" +#~ " version number will be saved as " +#~ "in the attribute `_metadata` of the " +#~ "returned state dict, and thus pickled." +#~ " `_metadata` is a dictionary with " +#~ "keys that follow the naming convention" +#~ " of state dict. See " +#~ "``_load_from_state_dict`` on how to use " +#~ "this information in loading." #~ msgstr "" #~ msgid "" -#~ "Apply unitary gates in ``kraus`` " -#~ "randomly based on corresponding ``prob``. " -#~ "If ``prob`` is ``None``, this is " -#~ "reduced to kraus channel language." +#~ "If new parameters/buffers are added/removed" +#~ " from a module, this number shall " +#~ "be bumped, and the module's " +#~ "`_load_from_state_dict` method can compare the" +#~ " version number and do appropriate " +#~ "changes if the state dict is from" +#~ " before the change." #~ msgstr "" -#~ msgid "The density matrix simulator based on tensornetwork engine." +#~ msgid "" +#~ "Note that extra state should be " +#~ "pickleable to ensure working serialization " +#~ "of the state_dict. We only provide " +#~ "provide backwards compatibility guarantees for" +#~ " serializing Tensors; other objects may " +#~ "break backwards compatibility if their " +#~ "serialized pickled form changes." #~ msgstr "" -#~ msgid "Number of qubits" +#~ msgid "*(string, torch.Tensor)* -- Tuple containing the name and buffer" #~ msgstr "" -#~ msgid "if True, nothing initialized, only for internal use, defaults to False" +#~ msgid "*(string, Module)* -- Tuple containing a name and child module" #~ msgstr "" -#~ msgid "the state input for the circuit, defaults to None" +#~ msgid "*(string, Module)* -- Tuple of name and module" #~ msgstr "" -#~ msgid "the density matrix input for the circuit, defaults to None" +#~ msgid "*(string, Parameter)* -- Tuple containing the name and parameter" #~ msgstr "" #~ msgid "" -#~ "Apply amplitudedamping quantum channel on " -#~ "the circuit. See " -#~ ":py:meth:`tensorcircuit.channels.amplitudedampingchannel`" +#~ "The hook will be called every time" +#~ " after :func:`forward` has computed an " +#~ "output. It should have the following " +#~ "signature::" #~ msgstr "" -#~ msgid "Parameters for the channel." +#~ msgid "" +#~ "The input contains only the positional" +#~ " arguments given to the module. " +#~ "Keyword arguments won't be passed to " +#~ "the hooks and only to the " +#~ "``forward``. The hook can modify the " +#~ "output. It can modify the input " +#~ "inplace but it will not have " +#~ "effect on forward since this is " +#~ "called after :func:`forward` is called." #~ msgstr "" -#~ msgid "Return the output density matrix of the circuit." +#~ msgid "" +#~ "The hook will be called every time" +#~ " before :func:`forward` is invoked. It " +#~ "should have the following signature::" #~ msgstr "" #~ msgid "" -#~ "check whether the final return is " -#~ "a legal density matrix, defaults to " -#~ "False" +#~ "The input contains only the positional" +#~ " arguments given to the module. " +#~ "Keyword arguments won't be passed to " +#~ "the hooks and only to the " +#~ "``forward``. The hook can modify the " +#~ "input. User can either return a " +#~ "tuple or a single modified value " +#~ "in the hook. We will wrap the " +#~ "value into a tuple if a single " +#~ "value is returned(unless that value is" +#~ " already a tuple)." #~ msgstr "" -#~ msgid "whether to reuse previous results, defaults to True" +#~ msgid "" +#~ "The hook will be called every time" +#~ " the gradients with respect to module" +#~ " inputs are computed. The hook should" +#~ " have the following signature::" #~ msgstr "" -#~ msgid "The output densitymatrix in 2D shape tensor form" +#~ msgid "Returns a dictionary containing a whole state of the module." #~ msgstr "" #~ msgid "" -#~ "Apply depolarizing quantum channel on " -#~ "the circuit. See " -#~ ":py:meth:`tensorcircuit.channels.depolarizingchannel`" +#~ "Visualise the circuit. This method " +#~ "recevies the keywords as same as " +#~ "qiskit.circuit.QuantumCircuit.draw. More details can" +#~ " be found here: " +#~ "https://qiskit.org/documentation/stubs/qiskit.circuit.QuantumCircuit.draw.html." #~ msgstr "" -#~ msgid "" -#~ "Apply phasedamping quantum channel on " -#~ "the circuit. See " -#~ ":py:meth:`tensorcircuit.channels.phasedampingchannel`" +#~ msgid "the corresponding qubit" +#~ msgstr "" + +#~ msgid "Bases: :py:class:`~keras.engine.training.Model`" #~ msgstr "" #~ msgid "" -#~ "Apply reset quantum channel on the " -#~ "circuit. See " -#~ ":py:meth:`tensorcircuit.channels.resetchannel`" +#~ "This method can also be called " +#~ "directly on a Functional Model during" +#~ " construction. In this case, any loss" +#~ " Tensors passed to this Model must" +#~ " be symbolic and be able to be" +#~ " traced back to the model's `Input`s." +#~ " These losses become part of the " +#~ "model's topology and are tracked in " +#~ "`get_config`." #~ msgstr "" -#~ msgid "Generate tensorflow sparse matrix from Pauli string sum" +#~ msgid "" +#~ "Additional keyword arguments for backward " +#~ "compatibility. Accepted values: inputs - " +#~ "Deprecated, will be automatically inferred." #~ msgstr "" #~ msgid "" -#~ "1D Tensor representing for a Pauli " -#~ "string, e.g. [1, 0, 0, 3, 2] " -#~ "is for :math:`X_0Z_3Y_4`" +#~ "Additional keyword arguments for backward " +#~ "compatibility. Accepted values:" #~ msgstr "" -#~ msgid "" -#~ "the weight for the Pauli string " -#~ "defaults to None (all Pauli strings " -#~ "weight 1.0)" +#~ msgid "inputs - Deprecated, will be automatically inferred." #~ msgstr "" -#~ msgid "the tensorflow sparse matrix" +#~ msgid "Deprecated, will be automatically inferred." #~ msgstr "" -#~ msgid "" -#~ "2D Tensor, each row is for a " -#~ "Pauli string, e.g. [1, 0, 0, 3," -#~ " 2] is for :math:`X_0Z_3Y_4`" +#~ msgid "Whether to use `ResourceVariable`." #~ msgstr "" #~ msgid "" -#~ "1D Tensor, each element corresponds the" -#~ " weight for each Pauli string " -#~ "defaults to None (all Pauli strings " -#~ "weight 1.0)" +#~ "When giving unsupported dtype and no " +#~ "initializer or when trainable has " +#~ "been set to True with synchronization" +#~ " set as `ON_READ`." #~ msgstr "" -#~ msgid "the tensorflow coo sparse matrix" +#~ msgid "This is an alias of `self.__call__`." #~ msgstr "" -#~ msgid "Generate scipy sparse matrix from Pauli string sum" +#~ msgid "Input tensor(s)." #~ msgstr "" -#~ msgid "the scipy coo sparse matrix" +#~ msgid "additional positional arguments to be passed to `self.call`." #~ msgstr "" -#~ msgid "Generate tensorflow dense matrix from Pauli string sum" +#~ msgid "additional keyword arguments to be passed to `self.call`." #~ msgstr "" -#~ msgid "the tensorflow dense matrix" +#~ msgid "Output tensor(s)." #~ msgstr "" #~ msgid "" -#~ "The projector of the operator. The " -#~ "operator, as a linear operator, on " -#~ "the adjoint of the operator." +#~ "1. In case of invalid user-" +#~ "provided data (not of type tuple," +#~ " list, `TensorShape`, or dict). " +#~ "2. If the model requires call " +#~ "arguments that are agnostic to " +#~ "the input shapes (positional or keyword" +#~ " arg in call signature). 3. If" +#~ " not all layers were properly built." +#~ " 4. If float type inputs are " +#~ "not supported within the layers." #~ msgstr "" #~ msgid "" -#~ "Set :math:`A` is the operator in " -#~ "matrix form, then the projector of " -#~ "operator is defined as: :math:`A^\\dagger " -#~ "A`" +#~ "In case of invalid user-provided " +#~ "data (not of type tuple, list," +#~ " `TensorShape`, or dict). 2. If " +#~ "the model requires call arguments that" +#~ " are agnostic to the input " +#~ "shapes (positional or keyword arg in " +#~ "call signature). 3. If not all " +#~ "layers were properly built. 4. If" +#~ " float type inputs are not supported" +#~ " within the layers." #~ msgstr "" -#~ msgid "The projector of the operator." +#~ msgid "" +#~ "A mask or list of masks. A " +#~ "mask can be either a boolean " +#~ "tensor or None (no mask). For more" +#~ " details, check the guide " +#~ "[here](https://www.tensorflow.org/guide/keras/masking_and_padding)." #~ msgstr "" -#~ msgid "The reduced density of the operator." +#~ msgid "" +#~ "A mask or list of masks. A " +#~ "mask can be either a boolean " +#~ "tensor or None (no mask). For more" +#~ " details, check the guide" #~ msgstr "" #~ msgid "" -#~ "Set :math:`A` is the matrix of the" -#~ " operator, then the reduced density " -#~ "is defined as:" +#~ "Loss function. Maybe be a string " +#~ "(name of loss function), or a " +#~ "`tf.keras.losses.Loss` instance. See " +#~ "`tf.keras.losses`. A loss function is " +#~ "any callable with the signature `loss" +#~ " = fn(y_true, y_pred)`, where `y_true` " +#~ "are the ground truth values, and " +#~ "`y_pred` are the model's predictions. " +#~ "`y_true` should have shape `(batch_size, " +#~ "d0, .. dN)` (except in the case" +#~ " of sparse loss functions such as " +#~ "sparse categorical crossentropy which expects" +#~ " integer arrays of shape `(batch_size, " +#~ "d0, .. dN-1)`). `y_pred` should have " +#~ "shape `(batch_size, d0, .. dN)`. The " +#~ "loss function should return a float " +#~ "tensor. If a custom `Loss` instance " +#~ "is used and reduction is set to" +#~ " `None`, return value has shape " +#~ "`(batch_size, d0, .. dN-1)` i.e. per-" +#~ "sample or per-timestep loss values; " +#~ "otherwise, it is a scalar. If the" +#~ " model has multiple outputs, you can" +#~ " use a different loss on each " +#~ "output by passing a dictionary or " +#~ "a list of losses. The loss value" +#~ " that will be minimized by the " +#~ "model will then be the sum of " +#~ "all individual losses, unless `loss_weights`" +#~ " is specified." #~ msgstr "" -#~ msgid "\\mathrm{Tr}_{subsystems}(A^\\dagger A)" +#~ msgid "" +#~ "List of metrics to be evaluated by" +#~ " the model during training and " +#~ "testing. Each of this can be a " +#~ "string (name of a built-in " +#~ "function), function or a " +#~ "`tf.keras.metrics.Metric` instance. See " +#~ "`tf.keras.metrics`. Typically you will use " +#~ "`metrics=['accuracy']`. A function is any " +#~ "callable with the signature `result =" +#~ " fn(y_true, y_pred)`. To specify different" +#~ " metrics for different outputs of a" +#~ " multi-output model, you could also" +#~ " pass a dictionary, such as " +#~ "`metrics={'output_a': 'accuracy', 'output_b': " +#~ "['accuracy', 'mse']}`. You can also pass" +#~ " a list to specify a metric or" +#~ " a list of metrics for each " +#~ "output, such as `metrics=[['accuracy'], " +#~ "['accuracy', 'mse']]` or `metrics=['accuracy', " +#~ "['accuracy', 'mse']]`. When you pass the" +#~ " strings 'accuracy' or 'acc', we " +#~ "convert this to one of " +#~ "`tf.keras.metrics.BinaryAccuracy`, " +#~ "`tf.keras.metrics.CategoricalAccuracy`, " +#~ "`tf.keras.metrics.SparseCategoricalAccuracy` based on " +#~ "the loss function used and the " +#~ "model output shape. We do a " +#~ "similar conversion for the strings " +#~ "'crossentropy' and 'ce' as well." +#~ msgstr "" + +#~ msgid "" +#~ "Optional list or dictionary specifying " +#~ "scalar coefficients (Python floats) to " +#~ "weight the loss contributions of " +#~ "different model outputs. The loss value" +#~ " that will be minimized by the " +#~ "model will then be the *weighted " +#~ "sum* of all individual losses, weighted" +#~ " by the `loss_weights` coefficients. If" +#~ " a list, it is expected to have" +#~ " a 1:1 mapping to the model's " +#~ "outputs. If a dict, it is expected" +#~ " to map output names (strings) to" +#~ " scalar coefficients." +#~ msgstr "" + +#~ msgid "" +#~ "Optional list or dictionary specifying " +#~ "scalar coefficients (Python floats) to " +#~ "weight the loss contributions of " +#~ "different model outputs. The loss value" +#~ " that will be minimized by the " +#~ "model will then be the *weighted " +#~ "sum* of all individual losses, weighted" +#~ " by the `loss_weights` coefficients." +#~ msgstr "" + +#~ msgid "If a list, it is expected to have a 1:1 mapping to the model's" +#~ msgstr "" + +#~ msgid "" +#~ "outputs. If a dict, it is expected" +#~ " to map output names (strings) to " +#~ "scalar coefficients." +#~ msgstr "" + +#~ msgid "" +#~ "Bool. Defaults to `False`. If `True`," +#~ " this `Model`'s logic will not be " +#~ "wrapped in a `tf.function`. Recommended " +#~ "to leave this as `None` unless " +#~ "your `Model` cannot be run inside " +#~ "a `tf.function`. `run_eagerly=True` is not " +#~ "supported when using " +#~ "`tf.distribute.experimental.ParameterServerStrategy`." +#~ msgstr "" + +#~ msgid "" +#~ "Int. Defaults to 1. The number of" +#~ " batches to run during each " +#~ "`tf.function` call. Running multiple batches" +#~ " inside a single `tf.function` call " +#~ "can greatly improve performance on TPUs" +#~ " or small models with a large " +#~ "Python overhead. At most, one full " +#~ "epoch will be run each execution. " +#~ "If a number larger than the size" +#~ " of the epoch is passed, the " +#~ "execution will be truncated to the " +#~ "size of the epoch. Note that if" +#~ " `steps_per_execution` is set to `N`, " +#~ "`Callback.on_batch_begin` and `Callback.on_batch_end` " +#~ "methods will only be called every " +#~ "`N` batches (i.e. before/after each " +#~ "`tf.function` execution)." #~ msgstr "" #~ msgid "" -#~ "Firstly, take the projector of the " -#~ "operator, then trace out the subsystems" -#~ " to trace out are supplied as " -#~ "indices, so that dangling edges are " -#~ "connected to each other as: " -#~ "`out_edges[i] ^ in_edges[i] for i in " -#~ "subsystems_to_trace_out` This does not modify" -#~ " the original network. The original " -#~ "ordering of the remaining subsystems is" -#~ " maintained." +#~ "If the layer has not been built," +#~ " this method will call `build` on " +#~ "the layer. This assumes that the " +#~ "layer will later be used with " +#~ "inputs that match the input shape " +#~ "provided here." #~ msgstr "" #~ msgid "" -#~ "The QuOperator of the reduced density" -#~ " of the operator with given " -#~ "subsystems." +#~ "Shape tuple (tuple of integers) or " +#~ "list of shape tuples (one per " +#~ "output tensor of the layer). Shape " +#~ "tuples can include None for free " +#~ "dimensions, instead of an integer." +#~ msgstr "" + +#~ msgid "An input shape tuple." #~ msgstr "" #~ msgid "" -#~ "Contracts the tensor network in place" -#~ " and returns the final tensor in " -#~ "two dimentional matrix. The default " -#~ "ordering for the axes of the final" -#~ " tensor is: (:math:`\\prod` dimension of" -#~ " out_edges, :math:`\\prod` dimension of " -#~ "in_edges)" +#~ "Single TensorSpec or nested structure of" +#~ " TensorSpec objects, describing how the" +#~ " layer would transform the provided " +#~ "input." #~ msgstr "" -#~ msgid "The two-dimentional tensor representing the operator." +#~ msgid "Single TensorSpec or nested structure of TensorSpec objects, describing" #~ msgstr "" -#~ msgid "" -#~ "Returns a bool indicating if QuOperator" -#~ " is an adjoint vector. Examples can" -#~ " be found in the `QuOperator.from_tensor`." +#~ msgid "how the layer would transform the provided input." #~ msgstr "" #~ msgid "" -#~ "Returns a bool indicating if QuOperator" -#~ " is a scalar. Examples can be " -#~ "found in the `QuOperator.from_tensor`." +#~ "Input data. It could be: - A " +#~ "Numpy array (or array-like), or a" +#~ " list of arrays (in case the " +#~ "model has multiple inputs). - A " +#~ "TensorFlow tensor, or a list of " +#~ "tensors (in case the model has " +#~ "multiple inputs). - A dict mapping " +#~ "input names to the corresponding " +#~ "array/tensors, if the model has named" +#~ " inputs. - A `tf.data` dataset. " +#~ "Should return a tuple of either " +#~ "`(inputs, targets)` or `(inputs, targets," +#~ " sample_weights)`. - A generator or " +#~ "`keras.utils.Sequence` returning `(inputs, targets)`" +#~ " or `(inputs, targets, sample_weights)`. " +#~ "A more detailed description of unpacking" +#~ " behavior for iterator types (Dataset, " +#~ "generator, Sequence) is given in the " +#~ "`Unpacking behavior for iterator-like " +#~ "inputs` section of `Model.fit`." +#~ msgstr "" + +#~ msgid "0 or 1. Verbosity mode. 0 = silent, 1 = progress bar." #~ msgstr "" #~ msgid "" -#~ "Returns a bool indicating if QuOperator" -#~ " is a vector. Examples can be " -#~ "found in the `QuOperator.from_tensor`." +#~ "Optional Numpy array of weights for " +#~ "the test samples, used for weighting " +#~ "the loss function. You can either " +#~ "pass a flat (1D) Numpy array with" +#~ " the same length as the input " +#~ "samples (1:1 mapping between weights " +#~ "and samples), or in the case of" +#~ " temporal data, you can pass a" +#~ " 2D array with shape `(samples, " +#~ "sequence_length)`, to apply a different " +#~ "weight to every timestep of every" +#~ " sample. This argument is not " +#~ "supported when `x` is a dataset, " +#~ "instead pass sample weights as the " +#~ "third element of `x`." #~ msgstr "" #~ msgid "" -#~ "Tensor product with another operator. " -#~ "Given two operators `A` and `B`, " -#~ "produces a new operator `AB` " -#~ "representing :math:`A ⊗ B`. The " -#~ "`out_edges` (`in_edges`) of `AB` is " -#~ "simply the concatenation of the " -#~ "`out_edges` (`in_edges`) of `A.copy()` with" -#~ " that of `B.copy()`: `new_out_edges = " -#~ "[*out_edges_A_copy, *out_edges_B_copy]` `new_in_edges " -#~ "= [*in_edges_A_copy, *in_edges_B_copy]`" +#~ "List of `keras.callbacks.Callback` instances. " +#~ "List of callbacks to apply during " +#~ "evaluation. See " +#~ "[callbacks](/api_docs/python/tf/keras/callbacks)." +#~ msgstr "" + +#~ msgid "" +#~ "`Model.evaluate` is not yet supported " +#~ "with `tf.distribute.experimental.ParameterServerStrategy`." +#~ msgstr "" + +#~ msgid "" +#~ "Trains the model for a fixed " +#~ "number of epochs (iterations on a " +#~ "dataset)." #~ msgstr "" #~ msgid "" -#~ "Set :math:`A` is the operator in " -#~ "matrix form, then the projector of " -#~ "operator is defined as: :math:`A " -#~ "A^\\dagger`" +#~ "Input data. It could be: - A " +#~ "Numpy array (or array-like), or a" +#~ " list of arrays (in case the " +#~ "model has multiple inputs). - A " +#~ "TensorFlow tensor, or a list of " +#~ "tensors (in case the model has " +#~ "multiple inputs). - A dict mapping " +#~ "input names to the corresponding " +#~ "array/tensors, if the model has named" +#~ " inputs. - A `tf.data` dataset. " +#~ "Should return a tuple of either " +#~ "`(inputs, targets)` or `(inputs, targets," +#~ " sample_weights)`. - A generator or " +#~ "`keras.utils.Sequence` returning `(inputs, targets)`" +#~ " or `(inputs, targets, sample_weights)`. " +#~ "- A `tf.keras.utils.experimental.DatasetCreator`, " +#~ "which wraps a callable that takes " +#~ "a single argument of type " +#~ "`tf.distribute.InputContext`, and returns a " +#~ "`tf.data.Dataset`. `DatasetCreator` should be " +#~ "used when users prefer to specify " +#~ "the per-replica batching and sharding" +#~ " logic for the `Dataset`. See " +#~ "`tf.keras.utils.experimental.DatasetCreator` doc for " +#~ "more information. A more detailed " +#~ "description of unpacking behavior for " +#~ "iterator types (Dataset, generator, Sequence)" +#~ " is given below. If using " +#~ "`tf.distribute.experimental.ParameterServerStrategy`, only " +#~ "`DatasetCreator` type is supported for " +#~ "`x`." +#~ msgstr "" + +#~ msgid "" +#~ "A more detailed description of unpacking" +#~ " behavior for iterator types (Dataset, " +#~ "generator, Sequence) is given below. If" +#~ " using `tf.distribute.experimental.ParameterServerStrategy`," +#~ " only `DatasetCreator` type is supported" +#~ " for `x`." +#~ msgstr "" + +#~ msgid "" +#~ "'auto', 0, 1, or 2. Verbosity " +#~ "mode. 0 = silent, 1 = progress " +#~ "bar, 2 = one line per epoch. " +#~ "'auto' defaults to 1 for most " +#~ "cases, but 2 when used with " +#~ "`ParameterServerStrategy`. Note that the " +#~ "progress bar is not particularly useful" +#~ " when logged to a file, so " +#~ "verbose=2 is recommended when not " +#~ "running interactively (eg, in a " +#~ "production environment)." +#~ msgstr "" + +#~ msgid "" +#~ "Float between 0 and 1. Fraction " +#~ "of the training data to be used" +#~ " as validation data. The model will" +#~ " set apart this fraction of the " +#~ "training data, will not train on " +#~ "it, and will evaluate the loss " +#~ "and any model metrics on this " +#~ "data at the end of each epoch." +#~ " The validation data is selected " +#~ "from the last samples in the `x`" +#~ " and `y` data provided, before " +#~ "shuffling. This argument is not " +#~ "supported when `x` is a dataset, " +#~ "generator or `keras.utils.Sequence` instance. " +#~ "`validation_split` is not yet supported " +#~ "with `tf.distribute.experimental.ParameterServerStrategy`." +#~ msgstr "" + +#~ msgid "Float between 0 and 1." +#~ msgstr "" + +#~ msgid "" +#~ "Fraction of the training data to " +#~ "be used as validation data. The " +#~ "model will set apart this fraction " +#~ "of the training data, will not " +#~ "train on it, and will evaluate the" +#~ " loss and any model metrics on " +#~ "this data at the end of each " +#~ "epoch. The validation data is selected" +#~ " from the last samples in the " +#~ "`x` and `y` data provided, before " +#~ "shuffling. This argument is not " +#~ "supported when `x` is a dataset, " +#~ "generator or" +#~ msgstr "" + +#~ msgid "`keras.utils.Sequence` instance." +#~ msgstr "" + +#~ msgid "" +#~ "`validation_split` is not yet supported " +#~ "with `tf.distribute.experimental.ParameterServerStrategy`." +#~ msgstr "" + +#~ msgid "" +#~ "Data on which to evaluate the loss" +#~ " and any model metrics at the " +#~ "end of each epoch. The model will" +#~ " not be trained on this data. " +#~ "Thus, note the fact that the " +#~ "validation loss of data provided using" +#~ " `validation_split` or `validation_data` is " +#~ "not affected by regularization layers " +#~ "like noise and dropout. `validation_data` " +#~ "will override `validation_split`. `validation_data`" +#~ " could be: - A tuple `(x_val, " +#~ "y_val)` of Numpy arrays or tensors." +#~ " - A tuple `(x_val, y_val, " +#~ "val_sample_weights)` of NumPy arrays. - " +#~ "A `tf.data.Dataset`. - A Python " +#~ "generator or `keras.utils.Sequence` returning " +#~ "`(inputs, targets)` or `(inputs, targets, " +#~ "sample_weights)`. `validation_data` is not yet" +#~ " supported with " +#~ "`tf.distribute.experimental.ParameterServerStrategy`." +#~ msgstr "" + +#~ msgid "" +#~ "Optional dictionary mapping class indices " +#~ "(integers) to a weight (float) value," +#~ " used for weighting the loss function" +#~ " (during training only). This can be" +#~ " useful to tell the model to " +#~ "\"pay more attention\" to samples from" +#~ " an under-represented class." +#~ msgstr "" + +#~ msgid "" +#~ "Optional Numpy array of weights for " +#~ "the training samples, used for weighting" +#~ " the loss function (during training " +#~ "only). You can either pass a flat" +#~ " (1D) Numpy array with the same " +#~ "length as the input samples (1:1 " +#~ "mapping between weights and samples), " +#~ "or in the case of temporal data," +#~ " you can pass a 2D array with" +#~ " shape `(samples, sequence_length)`, to " +#~ "apply a different weight to every " +#~ "timestep of every sample. This argument" +#~ " is not supported when `x` is a" +#~ " dataset, generator, or `keras.utils.Sequence`" +#~ " instance, instead provide the " +#~ "sample_weights as the third element of" +#~ " `x`." +#~ msgstr "" + +#~ msgid "Optional Numpy array of weights for" +#~ msgstr "" + +#~ msgid "" +#~ "the training samples, used for weighting" +#~ " the loss function (during training " +#~ "only). You can either pass a flat" +#~ " (1D) Numpy array with the same " +#~ "length as the input samples (1:1 " +#~ "mapping between weights and samples), or" +#~ " in the case of temporal data, " +#~ "you can pass a 2D array with " +#~ "shape `(samples, sequence_length)`, to apply" +#~ " a different weight to every timestep" +#~ " of every sample. This argument is" +#~ " not supported when `x` is a " +#~ "dataset, generator, or" +#~ msgstr "" + +#~ msgid "`keras.utils.Sequence` instance, instead provide the sample_weights" +#~ msgstr "" + +#~ msgid "as the third element of `x`." +#~ msgstr "" + +#~ msgid "" +#~ "Integer or `None`. Total number of " +#~ "steps (batches of samples) before " +#~ "declaring one epoch finished and " +#~ "starting the next epoch. When training" +#~ " with input tensors such as " +#~ "TensorFlow data tensors, the default " +#~ "`None` is equal to the number of" +#~ " samples in your dataset divided by" +#~ " the batch size, or 1 if that" +#~ " cannot be determined. If x is " +#~ "a `tf.data` dataset, and 'steps_per_epoch' " +#~ "is None, the epoch will run until" +#~ " the input dataset is exhausted. When" +#~ " passing an infinitely repeating dataset," +#~ " you must specify the `steps_per_epoch` " +#~ "argument. If `steps_per_epoch=-1` the training" +#~ " will run indefinitely with an " +#~ "infinitely repeating dataset. This argument" +#~ " is not supported with array inputs." +#~ " When using " +#~ "`tf.distribute.experimental.ParameterServerStrategy`: * " +#~ "`steps_per_epoch=None` is not supported." #~ msgstr "" -#~ msgid "\\mathrm{Tr}_{subsystems}(A A^\\dagger)" +#~ msgid "" +#~ "Integer or `None`. Total number of " +#~ "steps (batches of samples) before " +#~ "declaring one epoch finished and " +#~ "starting the next epoch. When training" +#~ " with input tensors such as " +#~ "TensorFlow data tensors, the default " +#~ "`None` is equal to the number of" +#~ " samples in your dataset divided by" +#~ " the batch size, or 1 if that" +#~ " cannot be determined. If x is " +#~ "a `tf.data` dataset, and 'steps_per_epoch' " +#~ "is None, the epoch will run until" +#~ " the input dataset is exhausted. When" +#~ " passing an infinitely repeating dataset," +#~ " you must specify the `steps_per_epoch` " +#~ "argument. If `steps_per_epoch=-1` the training" +#~ " will run indefinitely with an " +#~ "infinitely repeating dataset. This argument" +#~ " is not supported with array inputs." +#~ " When using " +#~ "`tf.distribute.experimental.ParameterServerStrategy`:" #~ msgstr "" -#~ msgid "Compute the double state of the given Hamiltonian operator ``h``." +#~ msgid "" +#~ "Only relevant if validation data is " +#~ "provided. Integer or `collections.abc.Container` " +#~ "instance (e.g. list, tuple, etc.). If" +#~ " an integer, specifies how many " +#~ "training epochs to run before a " +#~ "new validation run is performed, e.g." +#~ " `validation_freq=2` runs validation every " +#~ "2 epochs. If a Container, specifies " +#~ "the epochs on which to run " +#~ "validation, e.g. `validation_freq=[1, 2, 10]`" +#~ " runs validation at the end of " +#~ "the 1st, 2nd, and 10th epochs." #~ msgstr "" -#~ msgid "Hamiltonian operator in form of Tensor." +#~ msgid "" +#~ "tf.keras.utils.Sequence to the `x` argument" +#~ " of fit, which will in fact " +#~ "yield not only features (x) but " +#~ "optionally targets (y) and sample " +#~ "weights. Keras requires that the output" +#~ " of such iterator-likes be " +#~ "unambiguous. The iterator should return " +#~ "a tuple of length 1, 2, or " +#~ "3, where the optional second and " +#~ "third elements will be used for y" +#~ " and sample_weight respectively. Any other" +#~ " type provided will be wrapped in " +#~ "a length one tuple, effectively treating" +#~ " everything as 'x'. When yielding " +#~ "dicts, they should still adhere to " +#~ "the top-level tuple structure. e.g. " +#~ "`({\"x0\": x0, \"x1\": x1}, y)`. Keras" +#~ " will not attempt to separate " +#~ "features, targets, and weights from the" +#~ " keys of a single dict." #~ msgstr "" -#~ msgid "The double state of ``h`` with the given ``beta``." +#~ msgid "A notable unsupported data type is the namedtuple. The reason is that" #~ msgstr "" -#~ msgid "Return fidelity scalar between two states rho and rho0." +#~ msgid "" +#~ "it behaves like both an ordered " +#~ "datatype (tuple) and a mapping datatype" +#~ " (dict). So given a namedtuple of " +#~ "the form:" #~ msgstr "" -#~ msgid "\\operatorname{Tr}(\\sqrt{\\sqrt{rho} rho_0 \\sqrt{rho}})" +#~ msgid "Retrieves losses relevant to a specific set of inputs." #~ msgstr "" -#~ msgid "The density matrix in form of Tensor." +#~ msgid "Input tensor or list/tuple of input tensors." #~ msgstr "" -#~ msgid "The sqrtm of a Hermitian matrix ``a``." +#~ msgid "List of loss tensors of the layer that depend on `inputs`." #~ msgstr "" -#~ msgid "Compute the Gibbs state of the given Hamiltonian operator ``h``." +#~ msgid "Retrieves updates relevant to a specific set of inputs." #~ msgstr "" -#~ msgid "The Gibbs state of ``h`` with the given ``beta``." +#~ msgid "List of update ops of the layer that depend on `inputs`." #~ msgstr "" -#~ msgid "Mutual information between AB subsystem described by ``cut``." +#~ msgid "Deprecated, do NOT use! Only for compatibility with external Keras." #~ msgstr "" -#~ msgid "The AB subsystem." +#~ msgid "" +#~ "Loads all layer weights, either from " +#~ "a TensorFlow or an HDF5 weight " +#~ "file." #~ msgstr "" -#~ msgid "The mutual information between AB subsystem described by ``cut``." +#~ msgid "" +#~ "If `by_name` is False weights are " +#~ "loaded based on the network's topology." +#~ " This means the architecture should " +#~ "be the same as when the weights" +#~ " were saved. Note that layers that" +#~ " don't have weights are not taken " +#~ "into account in the topological " +#~ "ordering, so adding or removing layers" +#~ " is fine as long as they don't" +#~ " have weights." #~ msgstr "" -#~ msgid "Taylor expansion of :math:`ln(x+1)`." +#~ msgid "" +#~ "If `by_name` is True, weights are " +#~ "loaded into layers only if they " +#~ "share the same name. This is " +#~ "useful for fine-tuning or transfer-" +#~ "learning models where some of the " +#~ "layers have changed." #~ msgstr "" -#~ msgid "The :math:`k` th order, default is 2." +#~ msgid "" +#~ "Only topological loading (`by_name=False`) is" +#~ " supported when loading weights from " +#~ "the TensorFlow format. Note that " +#~ "topological loading differs slightly between" +#~ " TensorFlow and HDF5 formats for " +#~ "user-defined classes inheriting from " +#~ "`tf.keras.Model`: HDF5 loads based on a" +#~ " flattened list of weights, while the" +#~ " TensorFlow format loads based on the" +#~ " object-local names of attributes to" +#~ " which layers are assigned in the " +#~ "`Model`'s constructor." #~ msgstr "" -#~ msgid "The :math:`k` th order of Taylor expansion of :math:`ln(x+1)`." +#~ msgid "" +#~ "String, path to the weights file " +#~ "to load. For weight files in " +#~ "TensorFlow format, this is the file " +#~ "prefix (the same as was passed to" +#~ " `save_weights`). This can also be a" +#~ " path to a SavedModel saved from " +#~ "`model.save`." #~ msgstr "" #~ msgid "" -#~ "Compute the trace distance between two" -#~ " density matrix ``rho`` and ``rho2``." +#~ "Boolean, whether to load weights by " +#~ "name or by topological order. Only " +#~ "topological loading is supported for " +#~ "weight files in TensorFlow format." #~ msgstr "" -#~ msgid "Epsilon, defaults to 1e-12" +#~ msgid "" +#~ "Boolean, whether to skip loading of " +#~ "layers where there is a mismatch " +#~ "in the number of weights, or a " +#~ "mismatch in the shape of the " +#~ "weight (only valid when `by_name=True`)." #~ msgstr "" -#~ msgid "The trace distance between two density matrix ``rho`` and ``rho2``." +#~ msgid "" +#~ "Optional `tf.train.CheckpointOptions` object that" +#~ " specifies options for loading weights." #~ msgstr "" -#~ msgid "\\operatorname{Tr}(\\prod_i O_i)" +#~ msgid "" +#~ "When loading a weight file in " +#~ "TensorFlow format, returns the same " +#~ "status object as `tf.train.Checkpoint.restore`. " +#~ "When graph building, restore ops are " +#~ "run automatically as soon as the " +#~ "network is built (on first call " +#~ "for user-defined classes inheriting from" +#~ " `Model`, immediately if it is " +#~ "already built). When loading weights in" +#~ " HDF5 format, returns `None`." #~ msgstr "" #~ msgid "" -#~ "Compute the truncated free energy from" -#~ " the given density matrix ``rho``." +#~ "When loading a weight file in " +#~ "TensorFlow format, returns the same " +#~ "status object as `tf.train.Checkpoint.restore`. " +#~ "When graph building, restore ops are " +#~ "run automatically as soon as the " +#~ "network is built (on first call " +#~ "for user-defined classes inheriting from" +#~ " `Model`, immediately if it is " +#~ "already built)." #~ msgstr "" -#~ msgid "The :math:`k` th order, defaults to 2" +#~ msgid "When loading weights in HDF5 format, returns `None`." #~ msgstr "" -#~ msgid "The :math:`k` th order of the truncated free energy." +#~ msgid "If `h5py` is not available and the weight file is in HDF5 format." #~ msgstr "" -#~ msgid "" -#~ "The circuit ansatz is firstly one " -#~ "layer of Hadamard gates and then " -#~ "we have ``nlayers`` blocks of " -#~ ":math:`e^{i\\theta Z_iZ_{i+1}}` two-qubit gate" -#~ " in ladder layout, following rx gate." +#~ msgid "If `skip_mismatch` is set to `True` when `by_name` is `False`." #~ msgstr "" -#~ msgid "The circuit" +#~ msgid "" +#~ "Returns the model's metrics added using" +#~ " `compile()`, `add_metric()` APIs." #~ msgstr "" -#~ msgid "paramter tensor with 2*nlayer*n elements" +#~ msgid "" +#~ "Computation is done in batches. This " +#~ "method is designed for performance in" +#~ " large scale inputs. For small amount" +#~ " of inputs that fit in one " +#~ "batch, directly using `__call__()` is " +#~ "recommended for faster execution, e.g., " +#~ "`model(x)`, or `model(x, training=False)` if" +#~ " you have layers such as " +#~ "`tf.keras.layers.BatchNormalization` that behaves " +#~ "differently during inference. Also, note " +#~ "the fact that test loss is not " +#~ "affected by regularization layers like " +#~ "noise and dropout." #~ msgstr "" -#~ msgid "number of ZZ+RX blocks, defaults to 2" +#~ msgid "Verbosity mode, 0 or 1." #~ msgstr "" #~ msgid "" -#~ "whether use SVD split to reduce ZZ" -#~ " gate bond dimension, defaults to " -#~ "False" +#~ "List of `keras.callbacks.Callback` instances. " +#~ "List of callbacks to apply during " +#~ "prediction. See " +#~ "[callbacks](/api_docs/python/tf/keras/callbacks)." #~ msgstr "" -#~ msgid "The circuit with example ansatz attached" +#~ msgid "If `model.predict_on_batch` is wrapped in a `tf.function`." #~ msgstr "" #~ msgid "" -#~ "Function decorator wraps the function " -#~ "with the first input and output in" -#~ " the format of circuit, the wrapped" -#~ " function has the first input and " -#~ "the output as the state tensor." +#~ "This method should contain the " +#~ "mathematical logic for one step of " +#~ "inference. This typically includes the " +#~ "forward pass." #~ msgstr "" -#~ msgid "Function with the fist input and the output as ``Circuit`` object." +#~ msgid "Saves the model to Tensorflow SavedModel or a single HDF5 file." #~ msgstr "" #~ msgid "" -#~ "Wrapped function with the first input" -#~ " and the output as the state " -#~ "tensor correspondingly." +#~ "Please see `tf.keras.models.save_model` or the" +#~ " [Serialization and Saving " +#~ "guide](https://keras.io/guides/serialization_and_saving/) for" +#~ " details." #~ msgstr "" -#~ msgid "Two-dimensional grid lattice" +#~ msgid "String, PathLike, path to SavedModel or H5 file to save the model." #~ msgstr "" -#~ msgid "number of rows" +#~ msgid "If True, save optimizer's state together." #~ msgstr "" -#~ msgid "number of cols" +#~ msgid "" +#~ "Either `'tf'` or `'h5'`, indicating " +#~ "whether to save the model to " +#~ "Tensorflow SavedModel or HDF5. Defaults " +#~ "to 'tf' in TF 2.X, and 'h5' " +#~ "in TF 1.X." #~ msgstr "" -#~ msgid "return all col edge with 1d index encoding" +#~ msgid "" +#~ "Signatures to save with the SavedModel." +#~ " Applicable to the 'tf' format only." +#~ " Please see the `signatures` argument " +#~ "in `tf.saved_model.save` for details." #~ msgstr "" #~ msgid "" -#~ "whether to include pbc edges (periodic" -#~ " boundary condition), defaults to False" +#~ "(only applies to SavedModel format) " +#~ "`tf.saved_model.SaveOptions` object that specifies" +#~ " options for saving to SavedModel." #~ msgstr "" -#~ msgid "list of col edge" +#~ msgid "" +#~ "(only applies to SavedModel format) When" +#~ " enabled, the SavedModel will store " +#~ "the function traces for each layer. " +#~ "This can be disabled, so that only" +#~ " the configs of each layer are " +#~ "stored. Defaults to `True`. Disabling " +#~ "this will decrease serialization time " +#~ "and reduce file size, but it " +#~ "requires that all custom layers/models " +#~ "implement a `get_config()` method." #~ msgstr "" -#~ msgid "return all row edge with 1d index encoding" +#~ msgid "```python from keras.models import load_model" #~ msgstr "" -#~ msgid "list of row edge" +#~ msgid "" +#~ "model.save('my_model.h5') # creates a HDF5" +#~ " file 'my_model.h5' del model # " +#~ "deletes the existing model" #~ msgstr "" -#~ msgid "Get the 2D grid lattice in ``nx.Graph`` format" +#~ msgid "" +#~ "# returns a compiled model # " +#~ "identical to the previous one model " +#~ "= load_model('my_model.h5') ```" +#~ msgstr "" + +#~ msgid "Returns the `tf.TensorSpec` of call inputs as a tuple `(args, kwargs)`." #~ msgstr "" #~ msgid "" -#~ "whether to include pbc edges (periodic" -#~ " boundary condition), defaults to True" +#~ "# arg_specs is `[tf.TensorSpec(...), ...]`." +#~ " kwarg_specs, in this example, is #" +#~ " an empty dict since functional " +#~ "models do not use keyword arguments. " +#~ "arg_specs, kwarg_specs = model.save_spec()" #~ msgstr "" #~ msgid "" -#~ "Generate a permutation matrix P. Due " -#~ "to the different convention or qubits'" -#~ " order in qiskit and tensorcircuit, " -#~ "the unitary represented by the same " -#~ "circuit is different. They are related" -#~ " by this permutation matrix P: P " -#~ "@ U_qiskit @ P = U_tc" +#~ "'serving_default': serve.get_concrete_function(*arg_specs, " +#~ "**kwarg_specs)" #~ msgstr "" -#~ msgid "# of qubits" +#~ msgid "" +#~ "The TensorFlow format matches objects " +#~ "and variables by starting at a " +#~ "root object, `self` for `save_weights`, " +#~ "and greedily matching attribute names. " +#~ "For `Model.save` this is the `Model`," +#~ " and for `Checkpoint.save` this is " +#~ "the `Checkpoint` even if the " +#~ "`Checkpoint` has a model attached. This" +#~ " means saving a `tf.keras.Model` using " +#~ "`save_weights` and loading into a " +#~ "`tf.train.Checkpoint` with a `Model` attached" +#~ " (or vice versa) will not match " +#~ "the `Model`'s variables. See the [guide" +#~ " to training " +#~ "checkpoints](https://www.tensorflow.org/guide/checkpoint) for" +#~ " details on the TensorFlow format." #~ msgstr "" -#~ msgid "The permutation matrix P" +#~ msgid "" +#~ "Either 'tf' or 'h5'. A `filepath` " +#~ "ending in '.h5' or '.keras' will " +#~ "default to HDF5 if `save_format` is " +#~ "`None`. Otherwise `None` defaults to " +#~ "'tf'." +#~ msgstr "" + +#~ msgid "If `h5py` is not available when attempting to save in HDF5 format." #~ msgstr "" #~ msgid "" -#~ "Generate a qiskit quantum circuit using" -#~ " the quantum intermediate representation " -#~ "(qir) in tensorcircuit." +#~ "Relative or absolute positions of log" +#~ " elements in each line. If not " +#~ "provided, defaults to `[.33, .55, .67," +#~ " 1.]`." #~ msgstr "" -#~ msgid "qiskit QuantumCircuit object" +#~ msgid "" +#~ "Print function to use. Defaults to " +#~ "`print`. It will be called on each" +#~ " line of the summary. You can " +#~ "set it to a custom function in " +#~ "order to capture the string summary." #~ msgstr "" #~ msgid "" -#~ "Generate a tensorcircuit circuit using " -#~ "the quantum circuit data in qiskit." +#~ "Whether to expand the nested models. " +#~ "If not provided, defaults to `False`." #~ msgstr "" -#~ msgid "Quantum circuit data from qiskit." +#~ msgid "" +#~ "Input data. It could be: - A " +#~ "Numpy array (or array-like), or a" +#~ " list of arrays (in case the " +#~ "model has multiple inputs). - A " +#~ "TensorFlow tensor, or a list of " +#~ "tensors (in case the model has " +#~ "multiple inputs). - A dict mapping " +#~ "input names to the corresponding " +#~ "array/tensors, if the model has " +#~ "named inputs." #~ msgstr "" -#~ msgid "Input state of the circuit. Default is None." +#~ msgid "A dict mapping input names to the corresponding array/tensors, if" #~ msgstr "" -#~ msgid "A quantum circuit in tensorcircuit" +#~ msgid "the model has named inputs." #~ msgstr "" -#~ msgid "" -#~ "Translating from the gate name to " -#~ "gate information including the number of" -#~ " control qubits and the reduced gate" -#~ " name." +#~ msgid "If `model.test_on_batch` is wrapped in a `tf.function`." #~ msgstr "" -#~ msgid "String of gate name" +#~ msgid "Additional keyword arguments to be passed to `json.dumps()`." #~ msgstr "" -#~ msgid "# of control qubits, reduced gate name" +#~ msgid "" +#~ "Optional dictionary mapping class indices " +#~ "(integers) to a weight (float) to " +#~ "apply to the model's loss for the" +#~ " samples from this class during " +#~ "training. This can be useful to " +#~ "tell the model to \"pay more " +#~ "attention\" to samples from an under-" +#~ "represented class." #~ msgstr "" #~ msgid "" -#~ "Generate Tex code from 'qir' string " -#~ "to illustrate the circuit structure. " -#~ "This visualization is based on quantikz" -#~ " package." +#~ "This method can be overridden to " +#~ "support custom training logic. For " +#~ "concrete examples of how to override " +#~ "this method see [Customizing what " +#~ "happends in " +#~ "fit](https://www.tensorflow.org/guide/keras/customizing_what_happens_in_fit)." +#~ " This method is called by " +#~ "`Model.make_train_function`." #~ msgstr "" -#~ msgid "The quantum intermediate representation of a circuit in tensorcircuit." +#~ msgid "" +#~ "This method should contain the " +#~ "mathematical logic for one step of " +#~ "training. This typically includes the " +#~ "forward pass, loss calculation, " +#~ "backpropagation, and metric updates." #~ msgstr "" -#~ msgid "Initial state, default is an all zero state '000...000'." +#~ msgid "Bases: :py:class:`~keras.engine.base_layer.Layer`" #~ msgstr "" -#~ msgid "Measurement Basis, default is None which means no" +#~ msgid "" +#~ "Input tensor, or dict/list/tuple of " +#~ "input tensors. The first positional " +#~ "`inputs` argument is subject to special" +#~ " rules: - `inputs` must be explicitly" +#~ " passed. A layer cannot have zero" +#~ " arguments, and `inputs` cannot be " +#~ "provided via the default value of " +#~ "a keyword argument. - NumPy array " +#~ "or Python scalar values in `inputs` " +#~ "get cast as tensors. - Keras mask" +#~ " metadata is only collected from " +#~ "`inputs`. - Layers are built " +#~ "(`build(input_shape)` method) using shape " +#~ "info from `inputs` only. - `input_spec`" +#~ " compatibility is only checked against " +#~ "`inputs`. - Mixed precision input " +#~ "casting is only applied to `inputs`." +#~ " If a layer has tensor arguments" +#~ " in `*args` or `**kwargs`, their " +#~ "casting behavior in mixed precision " +#~ "should be handled manually. - The " +#~ "SavedModel input specification is generated" +#~ " using `inputs` only. - Integration " +#~ "with various ecosystem packages like " +#~ "TFMOT, TFLite, TF.js, etc is only " +#~ "supported for `inputs` and not for " +#~ "tensors in positional and keyword " +#~ "arguments." #~ msgstr "" #~ msgid "" -#~ "measurement in the end of the " -#~ "circuit. :type measure: Optional[List[str]] " -#~ ":param rcompress: If true, a right " -#~ "compression of the circuit will be " -#~ "conducted. A right compression means we" -#~ " will try to shift gates from " -#~ "right to left if possible. Default " -#~ "is false. :type rcompress: bool :param" -#~ " lcompress: If true, a left " -#~ "compression of the circuit will be " -#~ "conducted. A left compression means we" -#~ " will try to shift gates from " -#~ "left to right if possible. Default " -#~ "is false. :type lcompress: bool :param" -#~ " standalone: If true, the tex code" -#~ " will be designed to generate a " -#~ "standalone document. Default is false " -#~ "which means the generated tex code " -#~ "is just a quantikz code block. " -#~ ":type standalone: bool :param " -#~ "return_string_table: If true, a string " -#~ "table of tex code will also be " -#~ "returned. Default is false. :type " -#~ "return_string_table: bool :return: Tex code" -#~ " of circuit visualization based on " -#~ "quantikz package. If return_string_table is" -#~ " true, a string table of tex " -#~ "code will also be returned. :rtype: " -#~ "Union[str, Tuple[str, List[List[str]]]]" +#~ "Additional keyword arguments. May contain " +#~ "tensors, although this is not " +#~ "recommended, for the reasons above. The" +#~ " following optional keyword arguments are" +#~ " reserved: - `training`: Boolean scalar " +#~ "tensor of Python boolean indicating " +#~ "whether the `call` is meant for " +#~ "training or inference. - `mask`: Boolean" +#~ " input mask. If the layer's `call()`" +#~ " method takes a `mask` argument, " +#~ "its default value will be set to" +#~ " the mask generated for `inputs` by" +#~ " the previous layer (if `input` did" +#~ " come from a layer that generated" +#~ " a corresponding mask, i.e. if it " +#~ "came from a Keras layer with " +#~ "masking support)." #~ msgstr "" -#~ msgid ":math:`ket`." +#~ msgid "" +#~ "Bases: " +#~ ":py:class:`~keras.optimizer_v2.learning_rate_schedule.LearningRateSchedule`" #~ msgstr "" #~ msgid "" #~ "Get Pauli string array and weights " #~ "array for a qubit Hamiltonian as a" #~ " sum of Pauli strings defined in " -#~ "openfermion QubitOperator." +#~ "openfermion ``QubitOperator``." +#~ msgstr "" + +#~ msgid "``openfermion.ops.operators.qubit_operator.QubitOperator``" +#~ msgstr "" + +#~ msgid "The number of qubits" +#~ msgstr "" + +#~ msgid "Pauli String array and weights array" #~ msgstr "" diff --git a/docs/source/locale/zh/LC_MESSAGES/contribs.po b/docs/source/locale/zh/LC_MESSAGES/contribs.po index 232ced74..004ca724 100644 --- a/docs/source/locale/zh/LC_MESSAGES/contribs.po +++ b/docs/source/locale/zh/LC_MESSAGES/contribs.po @@ -9,14 +9,472 @@ msgid "" msgstr "" "Project-Id-Version: tensorcircuit \n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2022-05-16 15:04+0800\n" +"POT-Creation-Date: 2023-07-14 15:43+0800\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=utf-8\n" "Content-Transfer-Encoding: 8bit\n" -"Generated-By: Babel 2.9.1\n" +"Generated-By: Babel 2.12.1\n" + +#: ../../source/contribs/development_Mac.md:1 +#: ../../source/contribs/development_MacARM.md:1 +#: ../../source/contribs/development_MacM2.md:1 +msgid "Tensorcircuit Installation Guide on MacOS" +msgstr "" + +#: ../../source/contribs/development_Mac.md:3 +msgid "Contributed by [_Mark (Zixuan) Song_](https://marksong.tech)" +msgstr "" + +#: ../../source/contribs/development_Mac.md:5 +msgid "" +"Apple has updated Tensorflow (for MacOS) so that installation on M-series" +" (until M2) and Intel-series Mac can follow the exact same procedure." +msgstr "" + +#: ../../source/contribs/development_Mac.md:7 +#: ../../source/contribs/development_MacARM.md:8 +#: ../../source/contribs/development_MacM2.md:10 +msgid "Starting From Scratch" +msgstr "" + +#: ../../source/contribs/development_Mac.md:9 +msgid "For completely new Macos or Macos without Xcode and Homebrew installed." +msgstr "" + +#: ../../source/contribs/development_Mac.md:11 +#: ../../source/contribs/development_MacARM.md:12 +#: ../../source/contribs/development_MacM2.md:12 +msgid "Install Xcode Command Line Tools" +msgstr "" + +#: ../../source/contribs/development_Mac.md:13 +#: ../../source/contribs/development_MacARM.md:14 +#: ../../source/contribs/development_MacM2.md:14 +msgid "Need graphical access to the machine." +msgstr "" + +#: ../../source/contribs/development_Mac.md:15 +#: ../../source/contribs/development_MacARM.md:16 +#: ../../source/contribs/development_MacM2.md:16 +msgid "Run `xcode-select --install` to install if on optimal internet." +msgstr "" + +#: ../../source/contribs/development_Mac.md:17 +msgid "" +"Or Download it from [Apple](https://developer.apple.com/download/more/) " +"Command Line Tools installation image then install it if the internet " +"connection is weak." +msgstr "" + +#: ../../source/contribs/development_Mac.md:19 +#: ../../source/contribs/development_MacARM.md:20 +#: ../../source/contribs/development_MacM2.md:20 +msgid "Install Miniconda" +msgstr "" + +#: ../../source/contribs/development_Mac.md:21 +msgid "" +"Due to the limitation of MacOS and packages, the latest version of Python" +" does not always function as desired, thus miniconda installation is " +"advised to solve the issues." +msgstr "" + +#: ../../source/contribs/development_Mac.md:30 +#: ../../source/contribs/development_MacARM.md:37 +msgid "Install TC Backends" +msgstr "" + +#: ../../source/contribs/development_Mac.md:32 +msgid "There are four backends to choose from, Numpy, Tensorflow, Jax, and Torch." +msgstr "" + +#: ../../source/contribs/development_Mac.md:34 +#: ../../source/contribs/development_MacARM.md:41 +msgid "Install Jax, Pytorch, Qiskit, Cirq (Optional)" +msgstr "" + +#: ../../source/contribs/development_Mac.md:40 +#: ../../source/contribs/development_MacARM.md:47 +msgid "Install Tensorflow (Optional)" +msgstr "" + +#: ../../source/contribs/development_Mac.md:42 +msgid "Installation" +msgstr "" + +#: ../../source/contribs/development_Mac.md:44 +msgid "For Tensorflow version 2.13 or later:" +msgstr "" + +#: ../../source/contribs/development_Mac.md:50 +msgid "For Tensorflow version 2.12 or earlier:" +msgstr "" + +#: ../../source/contribs/development_Mac.md:56 +#: ../../source/contribs/development_MacARM.md:57 +#: ../../source/contribs/development_MacARM.md:89 +msgid "Verify Tensorflow Installation" +msgstr "" + +#: ../../source/contribs/development_Mac.md:74 +#: ../../source/contribs/development_MacARM.md:107 +msgid "Install Tensorcircuit" +msgstr "" + +#: ../../source/contribs/development_Mac.md:80 +msgid "" +"Until July 2023, this has been tested on Intel Macs running Ventura, M1 " +"Macs running Ventura, M2 Macs running Ventura, and M2 Macs running Sonoma" +" beta." +msgstr "" + +#: ../../source/contribs/development_MacARM.md:3 +msgid "Contributed by Mark (Zixuan) Song" +msgstr "" + +#: ../../source/contribs/development_MacARM.md:5 +#: ../../source/contribs/development_MacM2.md:5 +msgid "" +".. warning:: This page is deprecated. Please visit `the update " +"tutorial `_ for the latest information." +msgstr "" + +#: ../../source/contribs/development_MacARM.md:10 +msgid "For completely new macos or macos without xcode and brew" +msgstr "" + +#: ../../source/contribs/development_MacARM.md:18 +#: ../../source/contribs/development_MacM2.md:18 +msgid "" +"Or Download from [Apple](https://developer.apple.com/download/more/) " +"Command Line Tools installation image then install if internet connection" +" is weak." +msgstr "" + +#: ../../source/contribs/development_MacARM.md:22 +msgid "" +"Due to the limitation of MacOS and packages, the lastest version of " +"python does not always function as desired, thus miniconda installation " +"is advised to solve the issues." +msgstr "" + +#: ../../source/contribs/development_MacARM.md:31 +msgid "Install TC Prerequisites" +msgstr "" + +#: ../../source/contribs/development_MacARM.md:39 +msgid "There are four backends to choose from, Numpy, Tensorflow, Jax, Torch." +msgstr "" + +#: ../../source/contribs/development_MacARM.md:49 +msgid "Install Tensorflow without MacOS optimization" +msgstr "" + +#: ../../source/contribs/development_MacARM.md:75 +msgid "Install Tensorflow with MacOS optimization (Recommended)" +msgstr "" + +#: ../../source/contribs/development_MacARM.md:77 +msgid "For tensorflow version 2.13 or later:" +msgstr "" + +#: ../../source/contribs/development_MacARM.md:83 +msgid "For tensorflow version 2.12 or earlier:" +msgstr "" + +#: ../../source/contribs/development_MacARM.md:113 +msgid "Testing Platform (Tested Jun 2023)" +msgstr "" + +#: ../../source/contribs/development_MacARM.md:115 +msgid "Platform 1:" +msgstr "" + +#: ../../source/contribs/development_MacARM.md:116 +msgid "MacOS Ventura 13.1 (Build version 22C65)" +msgstr "" + +#: ../../source/contribs/development_MacARM.md:117 +msgid "M1 Ultra" +msgstr "" + +#: ../../source/contribs/development_MacARM.md:118 +msgid "Platform 2:" +msgstr "" + +#: ../../source/contribs/development_MacARM.md:119 +msgid "MacOS Ventura 13.2 (Build version 22D49)" +msgstr "" + +#: ../../source/contribs/development_MacARM.md:120 +msgid "M1 Ultra (Virtual)" +msgstr "" + +#: ../../source/contribs/development_MacARM.md:121 +msgid "Platform 4:" +msgstr "" + +#: ../../source/contribs/development_MacARM.md:122 +msgid "MacOS Sonoma 14.0 Beta 2 (Build version 23A5276g)" +msgstr "" + +#: ../../source/contribs/development_MacARM.md:123 +msgid "M2 Max" +msgstr "" + +#: ../../source/contribs/development_MacM1.rst:2 +msgid "Run TensorCircuit on TensorlowBackend with Apple M1" +msgstr "" + +#: ../../source/contribs/development_MacM1.rst:3 +msgid "Contributed by (Yuqin Chen)" +msgstr "" + +#: ../../source/contribs/development_MacM1.rst:7 +msgid "" +"This page is deprecated. Please visit `the update tutorial " +"`_ for the latest information." +msgstr "" + +#: ../../source/contribs/development_MacM1.rst:11 +msgid "Why We Can't Run TensorCircuit on TensorlowBackend with Apple M1" +msgstr "" + +#: ../../source/contribs/development_MacM1.rst:12 +msgid "" +"TensorCircuit requires Tensorflow to support TensorflowBackend. However " +"for Apple M1, Tensorflow package cannot be properly installed by a usual " +"method like \"pip install tensorflow\". As well, the TensorCircuit " +"package cannot be properly installed by a usual method \"pip install " +"tensorcircuit\" All we need is to properly install tensorflow on Apple M1" +" Pro and then download the TensorCircuit package to the local and install" +" it." +msgstr "" + +#: ../../source/contribs/development_MacM1.rst:16 +msgid "Install tensorflow on Apple M1" +msgstr "" + +#: ../../source/contribs/development_MacM1.rst:17 +msgid "" +"According to the instructions below or the installation manual on Apple's" +" official website `tensorflow-metal PluggableDevice " +"`_, you can install" +" tensorflow step by step." +msgstr "" + +#: ../../source/contribs/development_MacM1.rst:19 +msgid "**Step1: Environment setup**" +msgstr "" + +#: ../../source/contribs/development_MacM1.rst:21 +msgid "x86 : AMD Create virtual environment (recommended):" +msgstr "" + +#: ../../source/contribs/development_MacM1.rst:32 +msgid "NOTE: python version 3.8 required" +msgstr "" + +#: ../../source/contribs/development_MacM1.rst:34 +msgid "arm64 : Apple Silicon" +msgstr "" + +#: ../../source/contribs/development_MacM1.rst:36 +msgid "Download and install Conda env:" +msgstr "" + +#: ../../source/contribs/development_MacM1.rst:46 +msgid "Install the TensorFlow dependencies:" +msgstr "" + +#: ../../source/contribs/development_MacM1.rst:52 +msgid "When upgrading to new base TensorFlow version, recommend:" +msgstr "" + +#: ../../source/contribs/development_MacM1.rst:70 +msgid "tensorflow-deps versions are following base TensorFlow versions so:" +msgstr "" + +#: ../../source/contribs/development_MacM1.rst:72 +msgid "for v2.5" +msgstr "" + +#: ../../source/contribs/development_MacM1.rst:80 +msgid "for v2.6" +msgstr "" + +#: ../../source/contribs/development_MacM1.rst:87 +msgid "**Step2: Install base TensorFlow**" +msgstr "" + +#: ../../source/contribs/development_MacM1.rst:93 +msgid "**Step3: Install tensorflow-metal plugin**" +msgstr "" + +#: ../../source/contribs/development_MacM1.rst:101 +msgid "Install TensorCircuit on Apple M1" +msgstr "" + +#: ../../source/contribs/development_MacM1.rst:102 +msgid "" +"After properly install tensorflow, you can continue install " +"TensorCircuit. Up to now, for Apple M1, the Tensorcircuit package can not" +" be installed by simply conducting \"pip install tensorcircuit\", which " +"will lead to improper way for Tensorflow installation. One need to " +"download the installation package to the local, only in this way the " +"installation proceess can recognize the Apple M1 environment." +msgstr "" + +#: ../../source/contribs/development_MacM1.rst:107 +msgid "One should download the TensorCircuit package to local at first." +msgstr "" + +#: ../../source/contribs/development_MacM1.rst:114 +msgid "Then unpackage it, and cd into the folder with \"setup.py\". Conducting" +msgstr "" + +#: ../../source/contribs/development_MacM2.md:3 +msgid "Contributed by [Hong-Ye Hu](https://github.com/hongyehu)" +msgstr "" + +#: ../../source/contribs/development_MacM2.md:8 +msgid "" +"The key issue addressed in this document is **how to install both " +"TensorFlow and Jax on a M2 chip MacOS without conflict**." +msgstr "" + +#: ../../source/contribs/development_MacM2.md:22 +msgid "" +"Due to the limitation of MacOS and packages, the lastest version of " +"python does not always function as desired, thus miniconda installation " +"is advised to solve the issues. And use anaconda virtual environment is " +"always a good habit." +msgstr "" + +#: ../../source/contribs/development_MacM2.md:30 +msgid "Install Packages" +msgstr "" + +#: ../../source/contribs/development_MacM2.md:31 +msgid "" +"First, create a virtual environment, and make sure the python version is " +"3.8.5 by" +msgstr "" + +#: ../../source/contribs/development_MacM2.md:36 +msgid "" +"Then, install the TensorFlow from `.whl` file (file can be downloaded " +"from this " +"[URL](https://drive.google.com/drive/folders/1oSipZLnoeQB0Awz8U68KYeCPsULy_dQ7))." +" This will install TensorFlow version 2.4.1" +msgstr "" + +#: ../../source/contribs/development_MacM2.md:40 +msgid "Next, one need to install **Jax** and **Optax** by" +msgstr "" + +#: ../../source/contribs/development_MacM2.md:45 +msgid "" +"Now, hopefully, you should be able to use both Jax and TensorFlow in this" +" environment. But sometimes, it may give you an error \"ERROR: package " +"Chardet not found.\". If that is the case, you can install it by `conda " +"install chardet`. Lastly, install tensorcircuit" +msgstr "" + +#: ../../source/contribs/development_MacM2.md:51 +msgid "" +"This is the solution that seems to work for M2-chip MacOS. Please let me " +"know if there is a better solution!" +msgstr "" + +#: ../../source/contribs/development_Mac_cn.md:1 +msgid "MacOS Tensorcircuit 安装教程" +msgstr "" + +#: ../../source/contribs/development_Mac_cn.md:3 +msgid "[_Mark (Zixuan) Song_](https://marksong.tech) 撰写" +msgstr "" + +#: ../../source/contribs/development_Mac_cn.md:5 +msgid "由于苹果更新了Tensorflow,因此M系列(直到M2)和英特尔系列Mac上的安装可以遵循完全相同的过程。" +msgstr "" + +#: ../../source/contribs/development_Mac_cn.md:7 +msgid "从头开始" +msgstr "" + +#: ../../source/contribs/development_Mac_cn.md:9 +msgid "对于全新的Macos或未安装Xcode和Homebrew的Macos。" +msgstr "" + +#: ../../source/contribs/development_Mac_cn.md:11 +msgid "安装Xcode命令行工具" +msgstr "" + +#: ../../source/contribs/development_Mac_cn.md:13 +msgid "需要对机器的图形访问。" +msgstr "" + +#: ../../source/contribs/development_Mac_cn.md:15 +msgid "如果网络良好,请运行`xcode-select --install`进行安装。" +msgstr "" + +#: ../../source/contribs/development_Mac_cn.md:17 +msgid "或者,如果网络连接较弱,请从[苹果](https://developer.apple.com/download/more/)下载命令行工具安装映像,然后进行安装。" +msgstr "" + +#: ../../source/contribs/development_Mac_cn.md:19 +msgid "安装Miniconda" +msgstr "" + +#: ../../source/contribs/development_Mac_cn.md:21 +msgid "由于MacOS和软件包的限制,因此建议安装miniconda以解决问题。" +msgstr "" + +#: ../../source/contribs/development_Mac_cn.md:30 +msgid "安装TC后端" +msgstr "" + +#: ../../source/contribs/development_Mac_cn.md:32 +msgid "有四个后端可供选择,Numpy,Tensorflow,Jax和Torch。" +msgstr "" + +#: ../../source/contribs/development_Mac_cn.md:34 +msgid "安装Jax,Pytorch,Qiskit,Cirq(可选)" +msgstr "" + +#: ../../source/contribs/development_Mac_cn.md:40 +msgid "安装Tensorflow(可选)" +msgstr "" + +#: ../../source/contribs/development_Mac_cn.md:42 +msgid "安装步骤" +msgstr "" + +#: ../../source/contribs/development_Mac_cn.md:44 +msgid "Tensorflow版本2.13或之后:" +msgstr "" + +#: ../../source/contribs/development_Mac_cn.md:50 +msgid "Tensorflow版本2.12或之前:" +msgstr "" + +#: ../../source/contribs/development_Mac_cn.md:56 +msgid "验证Tensorflow安装" +msgstr "" + +#: ../../source/contribs/development_Mac_cn.md:74 +msgid "安装Tensorcircuit" +msgstr "" + +#: ../../source/contribs/development_Mac_cn.md:80 +msgid "" +"直到2023年7月,这已在运行Ventura的英特尔i9 Mac、运行Ventura的M1 Mac、运行Ventura的M2 " +"Mac、运行Sonoma测试版的M2 Mac上进行了测试。" +msgstr "" #: ../../source/contribs/development_windows.rst:2 msgid "Run TensorCircuit on Windows Machine with Docker" @@ -270,3 +728,197 @@ msgid "" "problems or have anything for discussion with other contributors*" msgstr "" +#: ../../source/contribs/development_wsl2.rst:2 +msgid "Run TensorCirit on Windows with WSL2 (Windows Subsystem for Linux 2)" +msgstr "" + +#: ../../source/contribs/development_wsl2.rst:4 +msgid "Contributed by `YHPeter `_ (Peter Yu)" +msgstr "" + +#: ../../source/contribs/development_wsl2.rst:6 +msgid "" +"Reminder, if you are not supposed to use JAX, you can still use " +"Numpy/Tensorflow/Pytorch backend to run demonstrations." +msgstr "" + +#: ../../source/contribs/development_wsl2.rst:8 +msgid "" +"Step 1. Install WSL2, follow the official installation instruction: " +"https://docs.microsoft.com/en-us/windows/wsl/install" +msgstr "" + +#: ../../source/contribs/development_wsl2.rst:11 +msgid "" +"Step 2. Install CUDA for GPU support, if you want to used GPU " +"accelerator. The official CUDA installation for WSL2: " +"https://docs.nvidia.com/cuda/wsl-user-guide/index.html#ch02-getting-" +"started" +msgstr "" + +#: ../../source/contribs/development_wsl2.rst:15 +msgid "Step 3. Follow the Linux Installation Instructions to finish installing." +msgstr "" + +#: ../../source/contribs/development_wsl2.rst:18 +msgid "**System Support Summary**" +msgstr "" + +#: ../../source/contribs/development_wsl2.rst:21 +msgid "Backend" +msgstr "" + +#: ../../source/contribs/development_wsl2.rst:22 +msgid "Numpy" +msgstr "" + +#: ../../source/contribs/development_wsl2.rst:23 +msgid "TensorFlow" +msgstr "" + +#: ../../source/contribs/development_wsl2.rst:24 +msgid "JAX" +msgstr "" + +#: ../../source/contribs/development_wsl2.rst:25 +msgid "Pytorch" +msgstr "" + +#: ../../source/contribs/development_wsl2.rst:26 +msgid "Suggested Package Version" +msgstr "" + +#: ../../source/contribs/development_wsl2.rst:27 +msgid ">= 1.20.0" +msgstr "" + +#: ../../source/contribs/development_wsl2.rst:28 +msgid ">= 2.7.0" +msgstr "" + +#: ../../source/contribs/development_wsl2.rst:29 +msgid ">= 0.3.0" +msgstr "" + +#: ../../source/contribs/development_wsl2.rst:30 +msgid ">= 1.12" +msgstr "" + +#: ../../source/contribs/development_wsl2.rst:31 +msgid "OS Support without GPU Accelerator" +msgstr "" + +#: ../../source/contribs/development_wsl2.rst:32 +#: ../../source/contribs/development_wsl2.rst:33 +#: ../../source/contribs/development_wsl2.rst:34 +#: ../../source/contribs/development_wsl2.rst:35 +msgid "Windows/MacOS/Linux" +msgstr "" + +#: ../../source/contribs/development_wsl2.rst:36 +msgid "OS Support with GPU Accelerator" +msgstr "" + +#: ../../source/contribs/development_wsl2.rst:37 +msgid "No Support for GPU" +msgstr "" + +#: ../../source/contribs/development_wsl2.rst:38 +msgid "" +"Windows(WSL2, docker)/`MacOS `_/Linux" +msgstr "" + +#: ../../source/contribs/development_wsl2.rst:39 +msgid "Windows(WSL2, docker)/MacOS/Linux" +msgstr "" + +#: ../../source/contribs/development_wsl2.rst:40 +msgid "Windows(WSL2, docker)/MacOS(torch>=1.12)/Linux" +msgstr "" + +#: ../../source/contribs/development_wsl2.rst:41 +msgid "Platform with TPU Accelerator" +msgstr "" + +#: ../../source/contribs/development_wsl2.rst:42 +msgid "No Support for TPU" +msgstr "" + +#: ../../source/contribs/development_wsl2.rst:43 +msgid "" +"`GCP - Tensorflow with TPU `_" +msgstr "" + +#: ../../source/contribs/development_wsl2.rst:44 +msgid "" +"`GCP - JAX with TPU `_" +msgstr "" + +#: ../../source/contribs/development_wsl2.rst:45 +msgid "" +"`GCP - Pytorch with TPU `_" +msgstr "" + +#: ../../source/contribs/development_wsl2.rst:47 +msgid "Tips: Currently, we don't suggest you to use TPU accelerator." +msgstr "" + +#~ msgid "" +#~ "Or Download from " +#~ "[Apple](https://developer.apple.com/download/more/) Command " +#~ "Line Tools installation image then " +#~ "install if internet connection is weak." +#~ msgstr "" + +#~ msgid "There are four backends to choose from, Tensorflow, Jax, Torch." +#~ msgstr "" + +#~ msgid "" +#~ "FYI: Error can occur when machine " +#~ "learning training or gpu related code" +#~ " is involved." +#~ msgstr "" + +#~ msgid "Testing Platform" +#~ msgstr "" + +#~ msgid "Install Tensorflow (Recommended Approach)" +#~ msgstr "" + +#~ msgid "" +#~ "❗️ Tensorflow with MacOS optimization " +#~ "would not function correctly in version" +#~ " 2.11.0 and before. Do not use " +#~ "this version of tensorflow if you " +#~ "intented to train any machine learning" +#~ " model." +#~ msgstr "" + +#~ msgid "" +#~ "FYI: Error can occur when machine " +#~ "learning training or gpu related code" +#~ " is involved." +#~ msgstr "" + +#~ msgid "" +#~ "⚠️ Tensorflow without macos optimization " +#~ "does not support Metal API and " +#~ "utilizing GPU (both intel chips and " +#~ "M-series chips) until at least " +#~ "tensorflow 2.11. Tensorflow-macos would " +#~ "fail when running `tc.backend.to_dense()`" +#~ msgstr "" + +#~ msgid "Testing Platform (Tested Feb 2023)" +#~ msgstr "" + +#~ msgid "" +#~ "This page is deprecated. Please visit" +#~ " `the update tutorial `_" +#~ " for latest information." +#~ msgstr "" + diff --git a/docs/source/locale/zh/LC_MESSAGES/faq.po b/docs/source/locale/zh/LC_MESSAGES/faq.po index d7eb9c4b..768580a2 100644 --- a/docs/source/locale/zh/LC_MESSAGES/faq.po +++ b/docs/source/locale/zh/LC_MESSAGES/faq.po @@ -8,7 +8,7 @@ msgid "" msgstr "" "Project-Id-Version: tensorcircuit\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2022-05-17 16:24+0800\n" +"POT-Creation-Date: 2023-01-13 11:04+0800\n" "PO-Revision-Date: 2022-05-11 17:52+0800\n" "Last-Translator: Xinghan Yang \n" "Language: cn\n" @@ -190,16 +190,12 @@ msgid "" "backend for quantum-classical hybrid machine learning tasks, where " "``QuantumLayer`` plays an important role. For PyTorch, we can in " "principle wrap the corresponding quantum function into a PyTorch module, " -"but we currently have no built-in support for this wrapper. In terms of " -"the Jax backend, we highly suggested keeping the functional programming " -"paradigm for such machine learning tasks. Besides, it is worth noting " -"that, jit and vmap are automatically taken care of in ``QuantumLayer``." +"we currently have the built-in support for this wrapper as " +"``tc.TorchLayer``. In terms of the Jax backend, we highly suggested " +"keeping the functional programming paradigm for such machine learning " +"tasks. Besides, it is worth noting that, jit and vmap are automatically " +"taken care of in ``QuantumLayer``." msgstr "" -"由于 PyTorch 没有成熟的 vmap 和 jit 支持,而且 Jax 没有原生的经典 ML 层,我们强烈推荐 TensorFlow " -"作为量子经典混合机器学习任务的后端,其中 QuantumLayer 起着重要作用。 对于 PyTorch,我们原则上可以将相应的量子函数包装到 " -"PyTorch 模块中,但我们目前没有内置支持这个包装器。在 Jax " -"后端方面,我们强烈建议保留函数式编程范式来处理此类机器学习任务。此外,值得注意的是,jit 和 vmap 在 QuantumLayer " -"中是自动处理的。" #: ../../source/faq.rst:74 msgid "When do I need to customize the contractor and how?" @@ -346,29 +342,47 @@ msgstr "" #: ../../source/faq.rst:155 msgid "" +"How to understand difference between ``tc.array_to_tensor`` and " +"``tc.backend.convert_to_tensor``?" +msgstr "" + +#: ../../source/faq.rst:157 +msgid "" +"``tc.array_to_tensor`` convert array to tensor as well as automatically " +"cast the type to the default dtype of TensorCircuit, i.e. ``tc.dtypestr``" +" and it also support to specify dtype as ``tc.array_to_tensor( , " +"dtype=\"complex128\")``. Instead, ``tc.backend.convert_to_tensor`` keeps " +"the dtype of the input array, and to cast it as complex dtype, we have to" +" explicitly call ``tc.backend.cast`` after conversion. Besides, " +"``tc.array_to_tensor`` also accepts multiple inputs as ``a_tensor, " +"b_tensor = tc.array_to_tensor(a_array, b_array)``." +msgstr "" + +#: ../../source/faq.rst:165 +msgid "" "How to arrange the circuit gate placement in the visualization from " "``c.tex()``?" msgstr "" -#: ../../source/faq.rst:157 +#: ../../source/faq.rst:167 msgid "" "Try ``lcompress=True`` or ``rcompress=True`` option in " ":py:meth:`tensorcircuit.circuit.Circuit.tex` API to make the circuit " "align from the left or from the right." msgstr "" -#: ../../source/faq.rst:159 +#: ../../source/faq.rst:169 msgid "" "Or try ``c.unitary(0, unitary=tc.backend.eye(2), name=\"invisible\")`` to" " add placeholder on the circuit which is invisible for circuit " "visualization." msgstr "" -#: ../../source/faq.rst:162 +#: ../../source/faq.rst:172 msgid "How to get the entanglement entropy from the circuit output?" msgstr "" -#: ../../source/faq.rst:164 +#: ../../source/faq.rst:174 msgid "Try the following:" msgstr "" @@ -535,3 +549,30 @@ msgstr "" #~ msgid "Try the following:" #~ msgstr "尝试以下方法:" +#~ msgid "" +#~ "Since PyTorch doesn't have mature vmap" +#~ " and jit support and Jax doesn't " +#~ "have native classical ML layers, we " +#~ "highly recommend TensorFlow as the " +#~ "backend for quantum-classical hybrid " +#~ "machine learning tasks, where ``QuantumLayer``" +#~ " plays an important role. For " +#~ "PyTorch, we can in principle wrap " +#~ "the corresponding quantum function into " +#~ "a PyTorch module, but we currently " +#~ "have no built-in support for this" +#~ " wrapper. In terms of the Jax " +#~ "backend, we highly suggested keeping the" +#~ " functional programming paradigm for such" +#~ " machine learning tasks. Besides, it " +#~ "is worth noting that, jit and vmap" +#~ " are automatically taken care of in" +#~ " ``QuantumLayer``." +#~ msgstr "" +#~ "由于 PyTorch 没有成熟的 vmap 和 jit 支持,而且" +#~ " Jax 没有原生的经典 ML 层,我们强烈推荐 TensorFlow " +#~ "作为量子经典混合机器学习任务的后端,其中 QuantumLayer 起着重要作用。 对于 " +#~ "PyTorch,我们原则上可以将相应的量子函数包装到 PyTorch 模块中,但我们目前没有内置支持这个包装器。在" +#~ " Jax 后端方面,我们强烈建议保留函数式编程范式来处理此类机器学习任务。此外,值得注意的是,jit 和" +#~ " vmap 在 QuantumLayer 中是自动处理的。" + diff --git a/docs/source/locale/zh/LC_MESSAGES/index.po b/docs/source/locale/zh/LC_MESSAGES/index.po index e9fd014b..20ed436d 100644 --- a/docs/source/locale/zh/LC_MESSAGES/index.po +++ b/docs/source/locale/zh/LC_MESSAGES/index.po @@ -6,134 +6,354 @@ # msgid "" msgstr "" -"Project-Id-Version: tensorcircuit\n" +"Project-Id-Version: tensorcircuit\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2022-07-01 10:20+0800\n" -"PO-Revision-Date: 2022-04-16 22:37+0800\n" +"POT-Creation-Date: 2023-05-28 14:36+0800\n" +"PO-Revision-Date: 2023-05-28 14:39+0800\n" "Last-Translator: Xinghan Yang\n" -"Language: cn\n" "Language-Team: \n" +"Language: cn\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=utf-8\n" "Content-Transfer-Encoding: 8bit\n" "Generated-By: Babel 2.9.1\n" +"X-Generator: Poedit 3.2.2\n" #: ../../source/index.rst:2 -msgid "Guide to TensorCircuit" -msgstr "TensorCircuit 指南" +msgid "TensorCircuit Documentation" +msgstr "参考文档" + +#: ../../source/index.rst:8 +msgid "**Welcome and congratulations! You have found TensorCircuit.** 👏" +msgstr "**祝贺你发现了 TensorCircuit!** 👏" + +#: ../../source/index.rst:11 +msgid "Introduction" +msgstr "介绍" -#: ../../source/index.rst:7 +#: ../../source/index.rst:13 msgid "" -"TensorCircuit is an open source quantum circuit and algorithm simulation " -"framework." -msgstr "TensorCircuit 是开源的量子线路和量子算法模拟软件框架。" +"TensorCircuit is an open-source high-performance quantum computing software " +"framework in Python." +msgstr "TensorCircuit 是基于 Python 的开源高性能量子计算软件框架。" -#: ../../source/index.rst:9 -msgid "It is built for human beings. 👽" +#: ../../source/index.rst:15 +msgid "It is built for humans. 👽" msgstr "适合人类。👽" -#: ../../source/index.rst:11 +#: ../../source/index.rst:17 msgid "It is designed for speed, flexibility and elegance. 🚀" msgstr "速度,灵活,优雅。🚀" -#: ../../source/index.rst:13 +#: ../../source/index.rst:19 msgid "It is empowered by advanced tensor network simulator engine. 🔋" msgstr "先进张量网络引擎赋能。🔋" -#: ../../source/index.rst:15 +#: ../../source/index.rst:21 +msgid "" +"It is ready for quantum hardware access with CPU/GPU/QPU (local/cloud) hybrid " +"solutions. 🖥" +msgstr "量子硬件支持,优雅 CPU/GPU/QPU 混合部署方案。 🖥" + +#: ../../source/index.rst:23 msgid "" "It is implemented with industry-standard machine learning frameworks: " "TensorFlow, JAX, and PyTorch. 🤖" msgstr "业界标准机器学习框架 TensorFlow,JAX,PyTorch 实现。🤖" -#: ../../source/index.rst:17 +#: ../../source/index.rst:25 msgid "" "It is compatible with machine learning engineering paradigms: automatic " -"differentiation, just-in-time compilation, vectorized parallelism and GPU" -" acceleration. 🛠" +"differentiation, just-in-time compilation, vectorized parallelism and GPU " +"acceleration. 🛠" msgstr "与机器学习工程实践兼容:自动微分,即时编译,向量并行化和 GPU 加速。🛠" -#: ../../source/index.rst:20 -msgid "Links" -msgstr "重要链接" +#: ../../source/index.rst:27 +msgid "" +"With the help of TensorCircuit, now get ready to efficiently and elegantly " +"solve interesting and challenging quantum computing problems: from academic " +"research prototype to industry application deployment." +msgstr "" +"有了 TensorCircuit,你现在可以高效优雅地解决量子计算中的各种问题:从学术研究的原" +"型开发到工业应用的部署。" -#: ../../source/index.rst:22 -msgid "Source code: https://github.com/tencent-quantum-lab/tensorcircuit" -msgstr "源代码: https://github.com/tencent-quantum-lab/tensorcircuit" +#: ../../source/index.rst:33 +msgid "Relevant Links" +msgstr "相关链接" -#: ../../source/index.rst:24 -msgid "Issue Tracker: https://github.com/tencent-quantum-lab/tensorcircuit/issues" -msgstr "问题跟踪: https://github.com/tencent-quantum-lab/tensorcircuit/issues" +#: ../../source/index.rst:35 +msgid "" +"TensorCircuit is created and maintained by `Shi-Xin Zhang `_ and this version is released by `Tencent Quantum Lab `_." +msgstr "" +"TensorCircuit 由 `Shi-Xin Zhang `_ 创建和维" +"护;此版本由 `腾讯量子实验室 `_ 发布。" -#: ../../source/index.rst:26 -msgid "Forum: https://github.com/tencent-quantum-lab/tensorcircuit/discussions" -msgstr "论坛社区: https://github.com/tencent-quantum-lab/tensorcircuit/discussions" +#: ../../source/index.rst:37 +msgid "" +"The current core authors of TensorCircuit are `Shi-Xin Zhang `_ and `Yu-Qin Chen `_. We also " +"thank `contributions `_ from the lab and the open source community." +msgstr "" +"TensorCircuit 当前主要作者为 `Shi-Xin Zhang `_,`Yu-Qin Chen `_。同时感谢来自实验室和开源社" +"区的 `贡献 `_。" -#: ../../source/index.rst:28 -msgid "Documentation: https://tensorcircuit.readthedocs.io" -msgstr "文档: https://tensorcircuit.readthedocs.io" +#: ../../source/index.rst:40 +msgid "" +"If you have any further questions or collaboration ideas, please use the issue " +"tracker or forum below, or send email to shixinzhang#tencent.com." +msgstr "" +"如果关于 TensorCircuit 有任何问题咨询或合作意向,请在 issue 或 discussion 提问," +"或发送邮件到 shixinzhang#tencent.com。" -#: ../../source/index.rst:30 -msgid "Whitepaper: https://arxiv.org/abs/2205.10091" -msgstr "白皮书文章: https://arxiv.org/abs/2205.10091" +#: ../../source/index.rst:45 +msgid "Source code" +msgstr "源代码" -#: ../../source/index.rst:32 -msgid "PyPI page: https://pypi.org/project/tensorcircuit" -msgstr "PyPI 页面: https://pypi.org/project/tensorcircuit" +#: ../../source/index.rst:49 +msgid "GitHub" +msgstr "" -#: ../../source/index.rst:34 -msgid "" -"DockerHub page: " -"https://hub.docker.com/repository/docker/tensorcircuit/tensorcircuit" -msgstr "DockerHub 页面: https://hub.docker.com/repository/docker/tensorcircuit/tensorcircuit" +#: ../../source/index.rst:52 +msgid "Documentation" +msgstr "参考文档" -#: ../../source/index.rst:36 +#: ../../source/index.rst:56 +msgid "Readthedocs" +msgstr "" + +#: ../../source/index.rst:59 +msgid "Whitepaper" +msgstr "白皮书" + +#: ../../source/index.rst:63 +msgid "*Quantum* journal" +msgstr "Quantum 期刊" + +#: ../../source/index.rst:66 +msgid "Issue Tracker" +msgstr "问题跟踪" + +#: ../../source/index.rst:70 +msgid "GitHub Issues" +msgstr "" + +#: ../../source/index.rst:73 +msgid "Forum" +msgstr "论坛" + +#: ../../source/index.rst:77 +msgid "GitHub Discussions" +msgstr "" + +#: ../../source/index.rst:80 +msgid "PyPI" +msgstr "" + +#: ../../source/index.rst:84 +msgid "``pip install``" +msgstr "" + +#: ../../source/index.rst:87 +msgid "DockerHub" +msgstr "" + +#: ../../source/index.rst:91 +msgid "``docker pull``" +msgstr "" + +#: ../../source/index.rst:94 +msgid "Application" +msgstr "应用" + +#: ../../source/index.rst:98 +msgid "Research using TC" +msgstr "研究项目" + +#: ../../source/index.rst:101 +msgid "Cloud" +msgstr "量子云" + +#: ../../source/index.rst:104 +msgid "Tencent Quantum Cloud" +msgstr "腾讯量子云平台" + +#: ../../source/index.rst:131 +msgid "Unified Quantum Programming" +msgstr "统一量子编程" + +#: ../../source/index.rst:133 msgid "" -"Binder online: https://mybinder.org/v2/gh/refraction-ray/tc-" -"env/master?urlpath=git-pull?repo=https://github.com/tencent-quantum-" -"lab/tensorcircuit&urlpath=lab/tree/tensorcircuit/&branch=master" +"TensorCircuit is unifying infrastructures and interfaces for quantum computing." +msgstr "TensorCircuit 尝试统一量子计算的基础设施和编程界面。" + +#: ../../source/index.rst:140 +msgid "Unified Backends" +msgstr "统一后端" + +#: ../../source/index.rst:144 +msgid "Jax/TensorFlow/PyTorch/Numpy/Cupy" +msgstr "" + +#: ../../source/index.rst:146 +msgid "Unified Devices" +msgstr "统一设备" + +#: ../../source/index.rst:150 +msgid "CPU/GPU/TPU" +msgstr "" + +#: ../../source/index.rst:152 +msgid "Unified Providers" +msgstr "统一平台" + +#: ../../source/index.rst:156 +msgid "QPUs from different vendors" +msgstr "不同供应商的 QPU" + +#: ../../source/index.rst:158 +msgid "Unified Resources" +msgstr "统一资源" + +#: ../../source/index.rst:162 +msgid "local/cloud/HPC" +msgstr "本地/云/集群" + +#: ../../source/index.rst:170 +msgid "Unified Interfaces" +msgstr "统一接口" + +#: ../../source/index.rst:174 +msgid "numerical sim/hardware exp" +msgstr "数值模拟/硬件实验" + +#: ../../source/index.rst:176 +msgid "Unified Engines" +msgstr "统一引擎" + +#: ../../source/index.rst:180 +msgid "ideal/noisy/approximate simulation" +msgstr "理想/含噪/近似模拟" + +#: ../../source/index.rst:182 +msgid "Unified Representations" +msgstr "统一表示" + +#: ../../source/index.rst:186 +msgid "from/to_IR/qiskit/openqasm/json" msgstr "" -"在线 Binder Jupyter: https://mybinder.org/v2/gh/refraction-ray/tc-" -"env/master?urlpath=git-pull?repo=https://github.com/tencent-quantum-" -"lab/tensorcircuit&urlpath=lab/tree/tensorcircuit/&branch=master" -#: ../../source/index.rst:41 +#: ../../source/index.rst:188 +msgid "Unified Pipelines" +msgstr "统一流程" + +#: ../../source/index.rst:192 +msgid "stateless functional programming/stateful ML models" +msgstr "函数式编程/面向对象模型" + +#: ../../source/index.rst:198 msgid "Reference Documentation" msgstr "参考文档" -#: ../../source/index.rst:43 +#: ../../source/index.rst:200 msgid "" -"The following documentation sections briefly introduce TensorCircuit to " -"the users and developpers." +"The following documentation sections briefly introduce TensorCircuit to the " +"users and developpers." msgstr "以下文档向用户和开发者简要介绍了 TensorCircuit 软件。" -#: ../../source/index.rst:56 +#: ../../source/index.rst:213 msgid "Tutorials" msgstr "教程" -#: ../../source/index.rst:58 +#: ../../source/index.rst:215 msgid "" -"The following documentation sections include integrated examples in the " -"form of Jupyter Notebook." -msgstr "以下 Jupyter Notebook 格式的文档包括了一系列使用 TensorCircuit 的集成案例。" +"The following documentation sections include integrated examples in the form of " +"Jupyter Notebook." +msgstr "" +"以下 Jupyter Notebook 格式的文档包括了一系列使用 TensorCircuit 的集成案例。" -#: ../../source/index.rst:72 +#: ../../source/index.rst:229 msgid "API References" msgstr "API 参考" -#: ../../source/index.rst:81 +#: ../../source/index.rst:238 msgid "Indices and Tables" msgstr "索引和表格" -#: ../../source/index.rst:83 +#: ../../source/index.rst:240 msgid ":ref:`genindex`" msgstr ":ref:`genindex`" -#: ../../source/index.rst:84 +#: ../../source/index.rst:241 msgid ":ref:`modindex`" msgstr ":ref:`modindex`" -#: ../../source/index.rst:85 +#: ../../source/index.rst:242 msgid ":ref:`search`" msgstr ":ref:`search`" +#~ msgid "" +#~ "Binder online: https://mybinder.org/v2/gh/refraction-ray/tc-env/master?" +#~ "urlpath=git-pull?repo=https://github.com/tencent-quantum-lab/" +#~ "tensorcircuit&urlpath=lab/tree/tensorcircuit/&branch=master" +#~ msgstr "" +#~ "在线 Binder Jupyter: https://mybinder.org/v2/gh/refraction-ray/tc-env/master?" +#~ "urlpath=git-pull?repo=https://github.com/tencent-quantum-lab/" +#~ "tensorcircuit&urlpath=lab/tree/tensorcircuit/&branch=master" + +#~ msgid "Software Whitepaper: https://arxiv.org/abs/2205.10091" +#~ msgstr "白皮书文章: https://arxiv.org/abs/2205.10091" + +#~ msgid "Guide to TensorCircuit" +#~ msgstr "TensorCircuit 指南" + +#~ msgid "Links" +#~ msgstr "重要链接" + +#~ msgid "Source code: https://github.com/tencent-quantum-lab/tensorcircuit" +#~ msgstr "源代码: https://github.com/tencent-quantum-lab/tensorcircuit" + +#~ msgid "Documentation: https://tensorcircuit.readthedocs.io" +#~ msgstr "文档: https://tensorcircuit.readthedocs.io" + +#~ msgid "" +#~ "Software Whitepaper (published in Quantum): https://quantum-journal.org/" +#~ "papers/q-2023-02-02-912/" +#~ msgstr "" +#~ "软件白皮书 (发表于 Quantum): https://quantum-journal.org/papers/" +#~ "q-2023-02-02-912/" + +#~ msgid "" +#~ "Issue Tracker: https://github.com/tencent-quantum-lab/tensorcircuit/issues" +#~ msgstr "问题跟踪: https://github.com/tencent-quantum-lab/tensorcircuit/issues" + +#~ msgid "Forum: https://github.com/tencent-quantum-lab/tensorcircuit/discussions" +#~ msgstr "" +#~ "论坛社区: https://github.com/tencent-quantum-lab/tensorcircuit/discussions" + +#~ msgid "PyPI page: https://pypi.org/project/tensorcircuit" +#~ msgstr "PyPI 页面: https://pypi.org/project/tensorcircuit" + +#~ msgid "" +#~ "DockerHub page: https://hub.docker.com/repository/docker/tensorcircuit/" +#~ "tensorcircuit" +#~ msgstr "" +#~ "DockerHub 页面: https://hub.docker.com/repository/docker/tensorcircuit/" +#~ "tensorcircuit" + +#~ msgid "" +#~ "Research and projects based on TensorCircuit: https://github.com/tencent-" +#~ "quantum-lab/tensorcircuit#research-and-applications" +#~ msgstr "" +#~ "基于 TensorCircuit 的研究和项目: https://github.com/tencent-quantum-lab/" +#~ "tensorcircuit#research-and-applications" + +#~ msgid "Tencent Quantum Cloud Service: https://quantum.tencent.com/cloud/" +#~ msgstr "腾讯量子云服务: https://quantum.tencent.com/cloud/" + +#~ msgid "Research based on TC" +#~ msgstr "基于 TC 的研究项目" diff --git a/docs/source/locale/zh/LC_MESSAGES/infras.po b/docs/source/locale/zh/LC_MESSAGES/infras.po index 89bd7dcc..b285ed77 100644 --- a/docs/source/locale/zh/LC_MESSAGES/infras.po +++ b/docs/source/locale/zh/LC_MESSAGES/infras.po @@ -8,7 +8,7 @@ msgid "" msgstr "" "Project-Id-Version: tensorcircuit\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2022-06-27 20:10+0800\n" +"POT-Creation-Date: 2023-05-27 18:52+0800\n" "PO-Revision-Date: 2022-04-18 20:44+0800\n" "Last-Translator: Xinghan Yang\n" "Language: cn\n" @@ -39,6 +39,13 @@ msgstr "**核心模块:**" #: ../../source/infras.rst:13 msgid "" +":py:mod:`tensorcircuit.abstractcircuit` and " +":py:mod:`tensorcircuit.basecircuit`: Hierarchical abstraction of circuit " +"class." +msgstr "" + +#: ../../source/infras.rst:15 +msgid "" ":py:mod:`tensorcircuit.circuit`: The core object " ":py:obj:`tensorcircuit.circuit.Circuit`. It supports circuit " "construction, simulation, representation, and visualization without noise" @@ -47,7 +54,7 @@ msgstr "" ":py:mod:`tensorcircuit.circuit`: 核心对象 " ":py:obj:`tensorcircuit.circuit.Circuit`.它支持使用蒙特卡洛轨迹方法的无噪声或有噪声的电路构建、仿真、表示和可视化。" -#: ../../source/infras.rst:15 +#: ../../source/infras.rst:17 msgid "" ":py:mod:`tensorcircuit.cons`: Runtime ML backend, dtype and contractor " "setups. We provide three sets of set methods for global setup, function " @@ -59,7 +66,7 @@ msgstr "" "我们为全局设置、使用函数装饰器的函数级别设置和使用 ``with`` 上下文管理器的上下文设置提供了三组设置方法.我们还在此模块中提供了定制的 " "contractor 基础设施。" -#: ../../source/infras.rst:17 +#: ../../source/infras.rst:19 msgid "" ":py:mod:`tensorcircuit.gates`: Definition of quantum gates, either fixed " "ones or parameterized ones, as well as " @@ -68,11 +75,11 @@ msgstr "" ":py:mod:`tensorcircuit.gates`: 固定或参数化的量子门的定义,以及用于门的 " ":py:obj:`tensorcircuit.gates.GateF` 类。" -#: ../../source/infras.rst:19 +#: ../../source/infras.rst:21 msgid "**Backend Agnostic Abstraction:**" msgstr "**后端无关抽象:**" -#: ../../source/infras.rst:21 +#: ../../source/infras.rst:23 msgid "" ":py:mod:`tensorcircuit.backends` provides a set of backend API and the " "corresponding implementation on Numpy, Jax, TensorFlow, and PyTorch " @@ -82,46 +89,47 @@ msgstr "" ":py:mod:`tensorcircuit.backends` 提供了一组后端 API 以及在 Numpy、Jax、TensorFlow 和 " "PyTorch 后端上的对应实现。这些后端继承自 TensorNetwork 包并且是高度定制的。" -#: ../../source/infras.rst:23 +#: ../../source/infras.rst:25 msgid "**Noisy Simulation Related Modules:**" msgstr "**噪声模拟相关模块:**" -#: ../../source/infras.rst:25 +#: ../../source/infras.rst:27 msgid ":py:mod:`tensorcircuit.channels`: Definition of quantum noise channels." msgstr ":py:mod:`tensorcircuit.channels`: 量子噪声通道的定义。" -#: ../../source/infras.rst:27 +#: ../../source/infras.rst:29 +#, fuzzy msgid "" -":py:mod:`tensorcircuit.densitymatrix`: Referenced implementation of " -"``tc.DMCircuit`` class, with similar set API of ``tc.Circuit`` while " -"simulating the noise in the full form of the density matrix." +":py:mod:`tensorcircuit.densitymatrix`: Referenced and highly efficient " +"implementation of ``tc.DMCircuit`` class, with similar set API of " +"``tc.Circuit`` while simulating the noise in the full form of the density" +" matrix." msgstr "" ":py:mod:`tensorcircuit.densitymatrix`: Referenced implementation of " "``tc.DMCircuit`` 类的引用实现,具有 ``tc.Circuit`` 的类似集合 API,同时以密度矩阵的完整形式模拟噪声。" -#: ../../source/infras.rst:29 +#: ../../source/infras.rst:31 +#, fuzzy msgid "" -":py:mod:`tensorcircuit.densitymatrix2`: Highly efficient implementation " -"of :py:obj:`tensorcircuit.densitymatrix2.DMCircuit2` class, always " -"preferred than the referenced implementation." -msgstr "" -":py:mod:`tensorcircuit.densitymatrix2`: " -":py:obj:`tensorcircuit.densitymatrix2.DMCircuit2` 类的高效实现,总是比参考的实现更适用。" +":py:mod:`tensorcircuit.noisemodel`: The global noise configuration and " +"circuit noisy method APIs" +msgstr ":py:mod:`tensorcircuit.vis`: 用于电路可视化的代码" -#: ../../source/infras.rst:31 +#: ../../source/infras.rst:33 msgid "**ML Interfaces Related Modules:**" msgstr "**机器学习接口相关模块:**" -#: ../../source/infras.rst:33 +#: ../../source/infras.rst:35 +#, fuzzy msgid "" ":py:mod:`tensorcircuit.interfaces`: Provide interfaces when quantum " "simulation backend is different from neural libraries. Currently include " -"PyTorch and scipy optimizer interfaces." +"PyTorch, TensorFlow, NumPy and SciPy optimizer interfaces." msgstr "" ":py:mod:`tensorcircuit.interfaces`: 当量子模拟后端与神经库不同时提供接口。 目前包括 PyTorch 和 " "scipy 优化器接口。" -#: ../../source/infras.rst:35 +#: ../../source/infras.rst:37 msgid "" ":py:mod:`tensorcircuit.keras`: Provide TensorFlow Keras layers, as well " "as wrappers of jitted function, save/load from tf side." @@ -129,15 +137,15 @@ msgstr "" ":py:mod:`tensorcircuit.keras`: 提供 TensorFlow Keras 层,以及可及时编译函数的包装器,从 " "TensorFlow 端保存/加载." -#: ../../source/infras.rst:37 +#: ../../source/infras.rst:39 msgid ":py:mod:`tensorcircuit.torchnn`: Provide PyTorch nn Modules." msgstr "" -#: ../../source/infras.rst:39 +#: ../../source/infras.rst:41 msgid "**MPS and MPO Utiliy Modules:**" msgstr "**MPS 和 MPO 实用模块:**" -#: ../../source/infras.rst:41 +#: ../../source/infras.rst:43 msgid "" ":py:mod:`tensorcircuit.quantum`: Provide definition and classes for " "Matrix Product States as well as Matrix Product Operators, we also " @@ -147,11 +155,11 @@ msgstr "" ":py:mod:`tensorcircuit.quantum`: " "提供矩阵乘积状态以及矩阵乘积算子的定义和类,我们还在这个模块中包含了各种量子物理和量子信息量。" -#: ../../source/infras.rst:43 +#: ../../source/infras.rst:45 msgid "**MPS Based Simulator Modules:**" msgstr "**基于 MPS 的模拟器模块:**" -#: ../../source/infras.rst:45 +#: ../../source/infras.rst:47 msgid "" ":py:mod:`tensorcircuit.mps_base`: Customized and jit/AD compatible MPS " "class from TensorNetwork package." @@ -159,7 +167,7 @@ msgstr "" ":py:mod:`tensorcircuit.mps_base`: 来自 TensorNetwork 包的自定义并且即时编译/自动微分兼容的 " "MPS 类。" -#: ../../source/infras.rst:47 +#: ../../source/infras.rst:49 msgid "" ":py:mod:`tensorcircuit.mpscircuit`: " ":py:obj:`tensorcircuit.mpscircuit.MPSCircuit` class with similar (but " @@ -170,59 +178,85 @@ msgstr "" ":py:obj:`tensorcircuit.mpscircuit.MPSCircuit` 类具有与 " "``tc.Circuit``,类似(但略有不同)的 API,其中仿真引擎基于 MPS TEBD。" -#: ../../source/infras.rst:49 +#: ../../source/infras.rst:51 msgid "**Supplemental Modules:**" msgstr "**支持模块:**" -#: ../../source/infras.rst:51 +#: ../../source/infras.rst:53 msgid "" ":py:mod:`tensorcircuit.simplify`: Provide tools and utility functions to " "simplify the tensornetworks before the real contractions." msgstr ":py:mod:`tensorcircuit.simplify`: 提供工具和实用函数以在真正收缩之前简化张量网络。" -#: ../../source/infras.rst:53 +#: ../../source/infras.rst:55 msgid "" ":py:mod:`tensorcircuit.experimental`: Experimental functions, long and " "stable support is not guaranteed." msgstr ":py:mod:`tensorcircuit.experimental`: 实验函数,不保证有持久且稳定的支持。" -#: ../../source/infras.rst:55 +#: ../../source/infras.rst:57 msgid "" ":py:mod:`tensorcircuit.utils`: Some general function tools that are not " "quantum at all." msgstr ":py:mod:`tensorcircuit.utils`: 一些与量子完全无关的通用工具函数。" -#: ../../source/infras.rst:57 +#: ../../source/infras.rst:59 msgid ":py:mod:`tensorcircuit.vis`: Visualization code for circuit drawing." msgstr ":py:mod:`tensorcircuit.vis`: 用于电路可视化的代码" -#: ../../source/infras.rst:59 +#: ../../source/infras.rst:61 msgid "" ":py:mod:`tensorcircuit.translation`: Translate circuit object to circuit " "object in other quantum packages." msgstr ":py:mod:`tensorcircuit.translation`: 将电路对象转换为其他量子包中的电路对象。" -#: ../../source/infras.rst:61 +#: ../../source/infras.rst:63 +msgid "**Processing and error mitigation on sample results:**" +msgstr "" + +#: ../../source/infras.rst:65 +msgid "" +":py:mod:`tensorcircuit.results`: Provide tools to process count dict and " +"to apply error mitigation." +msgstr "" + +#: ../../source/infras.rst:67 +msgid "**Cloud quantum hardware access module:**" +msgstr "" + +#: ../../source/infras.rst:69 +msgid "" +":py:mod:`tensorcircuit.cloud`: Provide quantum cloud SDK that can access " +"and program the real quantum hardware." +msgstr "" + +#: ../../source/infras.rst:71 +msgid "" +":py:mod:`tensorcircuit.compiler`: Provide compiler chains to compile and " +"transform quantum circuits." +msgstr "" + +#: ../../source/infras.rst:73 msgid "**Shortcuts and Templates for Circuit Manipulation:**" msgstr "**电路操作的快捷方式和模板:**" -#: ../../source/infras.rst:63 +#: ../../source/infras.rst:75 msgid "" ":py:mod:`tensorcircuit.templates`: provide handy shortcuts functions for " "expectation or circuit building patterns." msgstr ":py:mod:`tensorcircuit.templates`: 为期望或电路构建模式提供方便的快捷函数。" -#: ../../source/infras.rst:65 +#: ../../source/infras.rst:77 msgid "**Applications:**" msgstr "**应用:**" -#: ../../source/infras.rst:67 +#: ../../source/infras.rst:79 msgid "" ":py:mod:`tensorcircuit.applications`: most code here is not maintained " "and deprecated, use at your own risk." msgstr ":py:mod:`tensorcircuit.applications`: 这里的大多数代码都没有维护并且被弃用了,使用风险自负。" -#: ../../source/infras.rst:71 +#: ../../source/infras.rst:83 msgid "" "Recommend reading order -- only read the part of code you care about for " "your purpose. If you want to get an overview of the codebase, please read" @@ -231,11 +265,11 @@ msgstr "" "推荐阅读顺序——只阅读你关心的部分代码。如果您想了解代码库的概述,之后可以阅读 ``tc.circuit`` 后面的 ``tc.cons`` 和 " "``tc.gates``。" -#: ../../source/infras.rst:76 +#: ../../source/infras.rst:88 msgid "Relation between TensorCircuit and TensorNetwork" msgstr "TensorCircuit 和 TensorNetwork 之间的关系" -#: ../../source/infras.rst:78 +#: ../../source/infras.rst:90 msgid "" "TensorCircuit has a strong connection with the `TensorNetwork package " "`_ released by Google. Since the" @@ -249,7 +283,7 @@ msgstr "" "包的文档和教程很差,大多数时候,我们需要深入研究 TensorNetwork 的代码库来弄清楚发生了什么。换句话说,要阅读 " "TensorCircuit 代码库,可能需要经常参考 TensorNetwork 代码库。" -#: ../../source/infras.rst:80 +#: ../../source/infras.rst:92 msgid "" "Inside TensorCircuit, we heavily utilize TensorNetwork-related APIs from " "the TensorNetwork package and highly customized several modules from " @@ -258,7 +292,7 @@ msgstr "" "在 TensorCircuit 内部,我们大量使用了 TensorNetwork 包中与 TensorNetwork 相关的 " "API,并通过继承和重写从 TensorNetwork 中高度定制了几个模块:" -#: ../../source/infras.rst:82 +#: ../../source/infras.rst:94 msgid "" "We implement our own /backends from TensorNetwork's /backends by adding " "much more APIs and fixing lots of bugs in TensorNetwork's implementations" @@ -268,7 +302,7 @@ msgstr "" "我们从 TensorNetwork 的后端实现我们自己的后端,方法是添加更多 API,并通过猴子补丁修复 TensorNetwork " "在某些后端的实现中的许多错误。(上游是不活跃的,反馈不够灵敏)" -#: ../../source/infras.rst:84 +#: ../../source/infras.rst:96 msgid "" "We borrow TensorNetwork's code in /quantum to our ``tc.quantum`` module, " "since TensorNetwork has no ``__init__.py`` file to export these MPO and " @@ -279,7 +313,7 @@ msgstr "" "TensorNetwork 没有 ``__init__.py`` 文件来导出这些 MPO 和 MPS " "相关对象。当然,从那时起,我们已经取得了实质性的代码改进。" -#: ../../source/infras.rst:86 +#: ../../source/infras.rst:98 msgid "" "We borrow the TensorNetwork's code in /matrixproductstates as " "``tc.mps_base`` for bug fixing and jit/AD compatibility, so that we have " @@ -288,11 +322,15 @@ msgstr "" "我们借用 /matrixproductstates 中 TensorNetwork 的代码作为 ``tc.mps_base`` " "用于错误修复和即时编译/自动微分兼容性,以便我们更好地支持基于 MPS 的量子电路模拟器。" -#: ../../source/infras.rst:90 +#: ../../source/infras.rst:102 +msgid "Relations of Circuit-like classes" +msgstr "" + +#: ../../source/infras.rst:114 msgid "QuOperator/QuVector and MPO/MPS" msgstr "QuOperator/QuVector 和 MPO/MPS" -#: ../../source/infras.rst:92 +#: ../../source/infras.rst:116 msgid "" ":py:class:`tensorcircuit.quantum.QuOperator`, " ":py:class:`tensorcircuit.quantum.QuVector` and " @@ -306,19 +344,19 @@ msgstr "" ":py:class:`tensorcircuit.quantum.QuAdjointVector` 是从 TensorNetwork " "包中采用的类。它们的行为类似于与其他成分交互时的矩阵/向量(列或行),而内部结构由张量网络维护以提高效率和紧凑性。" -#: ../../source/infras.rst:95 +#: ../../source/infras.rst:119 msgid "" "We use code examples and associated tensor diagrams to illustrate these " "object abstractions." msgstr "我们使用代码示例和相关的张量图来说明这些对象抽象。" -#: ../../source/infras.rst:99 +#: ../../source/infras.rst:123 msgid "" "``QuOperator`` can express MPOs and ``QuVector`` can express MPSs, but " "they can express more than these fixed structured tensor networks." msgstr "``QuOperator`` 可以表达 MPO,``QuVector`` 可以表达 MPS,但它们可以表达的不仅仅是这些固定的结构化张量网络。" -#: ../../source/infras.rst:127 +#: ../../source/infras.rst:151 msgid "" "Note how in this example, ``matrix`` is not a typical MPO but still can " "be expressed as ``QuOperator``. Indeed, any tensor network with two sets " @@ -330,7 +368,7 @@ msgstr "" "``QuOperator``。事实上,任何具有两组相同维度的悬边的张量网络都可以被视为 `` QuOperator``。``QuVector`` " "更加灵活,因为我们可以将所有悬空边视为向量维度。" -#: ../../source/infras.rst:129 +#: ../../source/infras.rst:153 msgid "" "Also, note how ``^`` is overloaded as ``tn.connect`` to connect edges " "between different nodes in TensorNetwork. And indexing the node gives the" @@ -339,7 +377,7 @@ msgstr "" "还要注意 ``^`` 是如何被重载为 ``tn.connect`` 以连接 TensorNetwork " "中不同节点之间的边。索引节点给出了节点的边,例如 ``n1[0]`` 意味着 ``节点 n1`` 的第一条边。" -#: ../../source/infras.rst:131 +#: ../../source/infras.rst:155 msgid "" "The convention to define the ``QuOperator`` is firstly giving " "``out_edges`` (left index or row index of the matrix) and then giving " @@ -349,7 +387,7 @@ msgstr "" "定义 ``QuOperator`` 的惯例是首先给出 ``out_edges``(矩阵的左索引或行索引),然后给出 " "``in_edges``(矩阵的右索引或列索引)。边列表包含来自 TensorNetwork 库的边对象。" -#: ../../source/infras.rst:133 +#: ../../source/infras.rst:157 msgid "" "Such QuOperator/QuVector abstraction support various calculations only " "possible on matrix/vectors, such as matmul (``@``), adjoint " @@ -365,3 +403,76 @@ msgstr "" "(``*``)、张量乘积(``|``)和偏迹(``.partial_trace(subsystems_to_trace_out)``)。要提取这些对象的矩阵信息,我们可以使用" " ``.eval()`` 或 ``.eval_matrix() ``,前者保留了张量网络的形状信息,而后者给出了形状秩为2的矩阵表示。" +#: ../../source/infras.rst:162 +msgid "Quantum Cloud SDK: Layerwise API design" +msgstr "" + +#: ../../source/infras.rst:164 +msgid "From lower level to higher level, a view of API layers invoking QPU calls" +msgstr "" + +#: ../../source/infras.rst:166 +msgid "" +"Vendor specific implementation of functional API in, e.g., " +":py:mod:`tensorcircuit.cloud.tencent`" +msgstr "" + +#: ../../source/infras.rst:168 +msgid "" +"Provider agnostic functional lower level API for task/device management " +"in :py:mod:`tensorcircuit.cloud.apis`" +msgstr "" + +#: ../../source/infras.rst:170 +msgid "" +"Object oriented abstraction for Provider/Device/Task in " +":py:mod:`tensorcircuit.cloud.abstraction`" +msgstr "" + +#: ../../source/infras.rst:172 +msgid "" +"Unified batch submission interface as standarized in " +":py:meth:`tensorcircuit.cloud.wrapper.batch_submit_template`" +msgstr "" + +#: ../../source/infras.rst:174 +msgid "" +"Numerical and experimental unified all-in-one interface as " +":py:meth:`tensorcircuit.cloud.wrapper.batch_expectation_ps`" +msgstr "" + +#: ../../source/infras.rst:176 +msgid "" +"Application level code with QPU calls built directly on " +"``batch_expectation_ps`` or more fancy algorithms can be built on " +"``batch_submit_func`` so that these algorithms can be reused as long as " +"one function ``batch_submit_func`` is defined for a given vendor (cheaper" +" than defining a new provider from lower level)." +msgstr "" + +#: ../../source/infras.rst:181 +msgid "" +"For compiler, error mitigation and results post-processing parts, they " +"can be carefully designed to decouple with the QPU calls, so they are " +"separately implemented in :py:mod:`tensorcircuit.compiler` and " +":py:mod:`tensorcircuit.results`, and they can be independently useful " +"even without tc's cloud access." +msgstr "" + +#~ msgid "" +#~ ":py:mod:`tensorcircuit.densitymatrix2`: Highly efficient" +#~ " implementation of " +#~ ":py:obj:`tensorcircuit.densitymatrix2.DMCircuit2` class, " +#~ "always preferred than the referenced " +#~ "implementation." +#~ msgstr "" +#~ ":py:mod:`tensorcircuit.densitymatrix2`: " +#~ ":py:obj:`tensorcircuit.densitymatrix2.DMCircuit2` " +#~ "类的高效实现,总是比参考的实现更适用。" + +#~ msgid "" +#~ ":py:mod:`tensorcircuit.results`: Provide tools to" +#~ " process count dict and to apply " +#~ "error mitigation" +#~ msgstr "" + diff --git a/docs/source/locale/zh/LC_MESSAGES/quickstart.po b/docs/source/locale/zh/LC_MESSAGES/quickstart.po index 9e6e65f7..b4275455 100644 --- a/docs/source/locale/zh/LC_MESSAGES/quickstart.po +++ b/docs/source/locale/zh/LC_MESSAGES/quickstart.po @@ -8,37 +8,124 @@ msgid "" msgstr "" "Project-Id-Version: tensorcircuit\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2022-05-16 15:04+0800\n" -"PO-Revision-Date: 2022-04-11 08:23+0800\n" +"POT-Creation-Date: 2023-07-14 15:43+0800\n" +"PO-Revision-Date: 2023-05-07 11:01+0800\n" "Last-Translator: Xinghan Yang\n" "Language: cn\n" "Language-Team: Xinghan Yang\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=utf-8\n" "Content-Transfer-Encoding: 8bit\n" -"Generated-By: Babel 2.9.1\n" +"Generated-By: Babel 2.12.1\n" #: ../../source/quickstart.rst:3 msgid "Quick Start" msgstr "快速上手" -#: ../../source/quickstart.rst:7 +#: ../../source/quickstart.rst:6 +msgid "Installation" +msgstr "安装" + +#: ../../source/quickstart.rst:8 +msgid "For x86 Linux," +msgstr "x64 Linux" + +#: ../../source/quickstart.rst:10 +msgid "``pip install tensorcircuit``" +msgstr "``pip install tensorcircuit``" + +#: ../../source/quickstart.rst:12 +msgid "" +"is in general enough. Either pip from conda or other python env managers " +"is fine." +msgstr "" + +#: ../../source/quickstart.rst:15 +msgid "" +"Since there are many optional packages for various features, the users " +"may need to install more pip packages when required." +msgstr "" + +#: ../../source/quickstart.rst:18 +msgid "For Linux with Nvidia GPU," +msgstr "" + +#: ../../source/quickstart.rst:19 +msgid "" +"please refer to the GPU aware installation guide of corresponding machine" +" learning frameworks: `TensorFlow " +"`_, `Jax " +"`_, or `PyTorch " +"`_." +msgstr "" + +#: ../../source/quickstart.rst:24 +msgid "Docker is also recommended (especially Linux + Nvidia GPU setup):" +msgstr "" + +#: ../../source/quickstart.rst:26 +msgid "" +"``sudo docker run -it --network host --gpus all " +"tensorcircuit/tensorcircuit``." +msgstr "" + +#: ../../source/quickstart.rst:28 +msgid "" +"For more details on docker setup, please refer to `docker readme " +"`_." +msgstr "" + +#: ../../source/quickstart.rst:30 +msgid "" +"For Windows, due to the lack of support for Jax, we recommend to use " +"docker or WSL, please refer to `TC via windows docker " +"`_ or `TC via WSL " +"`_." +msgstr "" + +#: ../../source/quickstart.rst:32 +msgid "For MacOS, please refer to `TC on Mac `_." +msgstr "For MacOS, please refer to `在Mac上安装TC `_." + +#: ../../source/quickstart.rst:34 +msgid "" +"Overall, the installation of TensorCircuit is simple, since it is purely " +"in Python and hence very portable. As long as the users can take care of " +"the installation of ML frameworks on the corresponding system, " +"TensorCircuit will work as expected." +msgstr "" + +#: ../../source/quickstart.rst:37 +msgid "" +"To debug the installation issue or report bugs, please check the " +"environment information by ``tc.about()``." +msgstr "" + +#: ../../source/quickstart.rst:40 +msgid "" +"We also provide a nightly build of tensorcircuit via PyPI which can be " +"accessed by ``pip uninstall tensorcircuit``, then ``pip install " +"tensorcircuit-nightly``" +msgstr "" + +#: ../../source/quickstart.rst:46 msgid "Circuit Object" msgstr "电路对象" -#: ../../source/quickstart.rst:9 +#: ../../source/quickstart.rst:48 msgid "The basic object for TensorCircuit is ``tc.Circuit``." msgstr "TensorCircuit的基本对象是 ``tc.Circuit``。" -#: ../../source/quickstart.rst:11 +#: ../../source/quickstart.rst:50 msgid "Initialize the circuit with the number of qubits ``c=tc.Circuit(n)``." msgstr "用量子比特数(n) ``c=tc.Circuit(n)`` 来初始化电路。" -#: ../../source/quickstart.rst:13 +#: ../../source/quickstart.rst:52 msgid "**Input States:**" msgstr "**输入状态:**" -#: ../../source/quickstart.rst:15 +#: ../../source/quickstart.rst:54 msgid "" "The default input function for the circuit is :math:`\\vert 0^n " "\\rangle`. One can change this to other wavefunctions by directly feeding" @@ -47,17 +134,17 @@ msgstr "" "电路的默认输入函数是 :math:`\\vert 0^n \\rangle` 。可以通过直接输入输入状态向量 w 将其更改为其他波函数: " "``c=tc.Circuit(n, inputs=w)``。" -#: ../../source/quickstart.rst:17 +#: ../../source/quickstart.rst:56 msgid "" "One can also feed matrix product states as input states for the circuit, " "but we leave MPS/MPO usage for future sections." msgstr "也可以将矩阵乘积状态作为电路的输入状态,但我们将矩阵乘积状态/矩阵乘积算子的使用留待后续讲解。" -#: ../../source/quickstart.rst:19 +#: ../../source/quickstart.rst:58 msgid "**Quantum Gates:**" msgstr "**量子门:**" -#: ../../source/quickstart.rst:21 +#: ../../source/quickstart.rst:60 msgid "" "We can apply gates on circuit objects. For example, using ``c.H(1)`` or " "``c.rx(2, theta=0.2)``, we can apply Hadamard gate on qubit 1 (0-based) " @@ -66,15 +153,15 @@ msgstr "" "我们可以将门应用于电路对象。 例如,使用 ``c.H(1)`` 或 ``c.rx(2, theta=0.2)``,我们可以将 Hadamard " "门应用于量子比特1 (基于0)或将 Rx 门应用于量子比特2 :math:`e^{-i\\theta/2 X}`。" -#: ../../source/quickstart.rst:23 +#: ../../source/quickstart.rst:62 msgid "The same rule also applies to multi-qubit gates, such as ``c.cnot(0, 1)``." msgstr "同样的规则亦适用于多量子比特门,例如 ``c.cnot(0, 1)`` 。" -#: ../../source/quickstart.rst:25 +#: ../../source/quickstart.rst:64 msgid "There are also highly customizable gates, two instances are:" msgstr "这些量子门也是高度可定制的,下面是两个例子" -#: ../../source/quickstart.rst:27 +#: ../../source/quickstart.rst:66 msgid "" "``c.exp1(0, 1, unitary=m, theta=0.2)`` which is for the exponential gate " ":math:`e^{i\\theta m}` of any matrix m as long as :math:`m^2=1`." @@ -82,55 +169,55 @@ msgstr "" "``c.exp1(0, 1, unitary=m, theta=0.2)`` 用于任何矩阵 m 的指数门 :math:`e^{i\\theta " "m}`,只要 m 满足 :math:`m^2=1`。" -#: ../../source/quickstart.rst:29 +#: ../../source/quickstart.rst:68 msgid "" "``c.any(0, 1, unitary=m)`` which is for applying the unitary gate m on " "the circuit." msgstr "``c.any(0, 1, unitary=m)`` 在电路上作用任意的幺正量子门。" -#: ../../source/quickstart.rst:31 +#: ../../source/quickstart.rst:70 msgid "These two examples are flexible and support gates on any number of qubits." msgstr "这两个例子很灵活,支持任意数量的量子比特上的门。" -#: ../../source/quickstart.rst:33 +#: ../../source/quickstart.rst:72 msgid "**Measurements and Expectations:**" msgstr "**测量与期望**" -#: ../../source/quickstart.rst:35 +#: ../../source/quickstart.rst:74 msgid "" "The most straightforward way to get the output from the circuit object is" " by getting the output wavefunction in vector form as ``c.state()``." msgstr "从电路对象中获取输出的最直接的方法是通过 ``c.state()`` 以向量形式获取输出波函数。" -#: ../../source/quickstart.rst:37 +#: ../../source/quickstart.rst:76 msgid "" "For bitstring sampling, we have ``c.perfect_sampling()`` which returns " "the bitstring and the corresponding probability amplitude." msgstr "对于位串采样,我们有 ``c.perfect_sampling()``,它返回位串和相应的概率幅度。" -#: ../../source/quickstart.rst:39 +#: ../../source/quickstart.rst:78 msgid "" "To measure part of the qubits, we can use ``c.measure(0, 1)``, if we want" " to know the corresponding probability of the measurement output, try " "``c.measure(0, 1, with_prob=True)``. The measure API is by default non-" "jittable, but we also have a jittable version as ``c.measure_jit(0, 1)``." msgstr "" -"要测量部分量子比特,我们可以使用 ``c.measure(0, 1)``,如果我们想知道测量的结果的对应概率,可以尝试 ``c.measure(0, 1, " -"with_prob=True)``。 测量 API 在默认情况下是不可即时编译的 ,但我们也有一个可即时编译的版本,如 " -"``c.measure_jit(0, 1)``。" +"要测量部分量子比特,我们可以使用 ``c.measure(0, 1)``,如果我们想知道测量的结果的对应概率,可以尝试 " +"``c.measure(0, 1, with_prob=True)``。 测量 API 在默认情况下是不可即时编译的 " +",但我们也有一个可即时编译的版本,如 ``c.measure_jit(0, 1)``。" -#: ../../source/quickstart.rst:41 +#: ../../source/quickstart.rst:80 msgid "" "The measurement and sampling utilize advanced algorithms based on " "tensornetwork and thus require no knowledge or space for the full " "wavefunction." msgstr "测量和采样使用了基于张量网络的高级算法,因此不需要任何相关知识或者空间来获取全波函数。" -#: ../../source/quickstart.rst:43 +#: ../../source/quickstart.rst:82 msgid "See the example below:" msgstr "请看下面的例子:" -#: ../../source/quickstart.rst:61 +#: ../../source/quickstart.rst:100 msgid "" "To compute expectation values for local observables, we have " "``c.expectation([tc.gates.z(), [0]], [tc.gates.z(), [1]])`` for " @@ -142,43 +229,47 @@ msgstr "" "``c.expectation([tc.gates.x(), [0]])`` 对应的期望为 :math:`\\langle X_0 " "\\rangle`时." -#: ../../source/quickstart.rst:63 +#: ../../source/quickstart.rst:102 msgid "" "This expectation API is rather flexible, as one can measure an m on " "several qubits as ``c.expectation([m, [0, 1, 2]])``." msgstr "因为可以在几个量子比特上测量一个 m,这种计算期望值的 API 相当灵活:``c.expectation([m, [0, 1, 2]])``。" -#: ../../source/quickstart.rst:65 +#: ../../source/quickstart.rst:104 msgid "" "We can also extract the unitary matrix underlying the whole circuit as " "follows:" msgstr "我们还可以提取整个电路下面的幺正矩阵,如下所示:" -#: ../../source/quickstart.rst:78 +#: ../../source/quickstart.rst:117 msgid "**Circuit Transformations:**" msgstr "**电路可视化**" -#: ../../source/quickstart.rst:80 +#: ../../source/quickstart.rst:119 msgid "" "We currently support transform ``tc.Circuit`` from and to Qiskit " "``QuantumCircuit`` object." msgstr "我们目前支持 ``tc.Circuit`` 与 Qiskit ``QuantumCircuit`` 对象之间的互相转换。" -#: ../../source/quickstart.rst:82 +#: ../../source/quickstart.rst:121 msgid "" "Export to Qiskit (possible for further hardware experiment, compiling, " "and visualization): ``c.to_qiskit()``." msgstr "导出到 Qiskit(可能用于进一步的硬件实验、编译和可视化):``c.to_qiskit()``。" -#: ../../source/quickstart.rst:84 -msgid "Import from Qiskit: ``c = tc.Circuit.from_qiskit(QuantumCircuit, n)``" -msgstr "从 Qiskit 导入:``c = tc.Circuit.from_qiskit(QuantumCircuit, n)``" +#: ../../source/quickstart.rst:123 +msgid "" +"Import from Qiskit: ``c = tc.Circuit.from_qiskit(QuantumCircuit, n)``. " +"Parameterized Qiskit circuit is supported by passing the parameters to " +"the ``binding_parameters`` argument of the ``from_qiskit`` function, " +"similar to the ``assign_parameters`` function in Qiskit." +msgstr "" -#: ../../source/quickstart.rst:86 +#: ../../source/quickstart.rst:127 msgid "**Circuit Visualization:**" msgstr "**电路可视化**" -#: ../../source/quickstart.rst:88 +#: ../../source/quickstart.rst:129 msgid "" "``c.vis_tex()`` can generate tex code for circuit visualization based on " "LaTeX `quantikz `__ package." @@ -186,14 +277,14 @@ msgstr "" "``c.vis_tex()`` 可以基于 `quantikz `__ " "package 生成用于电路可视化的 tex 代码。" -#: ../../source/quickstart.rst:90 +#: ../../source/quickstart.rst:131 msgid "" "There are also some automatic pipeline helper functions to directly " "generate figures from tex code, but they require extra installations in " "the environment." msgstr "还有一些自动辅助函数可以直接从 tex 代码生成图形,但它们需要在环境中进行额外安装。" -#: ../../source/quickstart.rst:92 +#: ../../source/quickstart.rst:133 msgid "" "``render_pdf(tex)`` function requires full installation of LaTeX locally." " And in the Jupyter environment, we may prefer ``render_pdf(tex, " @@ -205,29 +296,29 @@ msgstr "" "``render_pdf(tex, notebook=True)`` 来返回 jpg 图形,这需要安装 wand magicwand 库,请参阅 " "`这里 `__ 。" -#: ../../source/quickstart.rst:94 +#: ../../source/quickstart.rst:135 msgid "" "Or since we can transform ``tc.Circuit`` into QuantumCircuit easily, we " "have a simple pipeline to first transform ``tc.Circuit`` into Qiskit and " "then call the visualization built in Qiskit. Namely, we have ``c.draw()``" " API." msgstr "" -"从 Qiskit 导入:``c = tc.Circuit.from_qiskit(QuantumCircuit, n)`` 或者因为我们可以轻松地将 " -"``tc.Circuit`` 转换为 QuantumCircuit,我们有一个简单的管道来首先转换 ``tc.Circuit`` 为 " -"Qiskit,然后调用 Qiskit 中内置的可视化。 也就是说,我们有 ``c.draw()`` API。" +"从 Qiskit 导入:``c = tc.Circuit.from_qiskit(QuantumCircuit, n)`` " +"或者因为我们可以轻松地将 ``tc.Circuit`` 转换为 QuantumCircuit,我们有一个简单的管道来首先转换 " +"``tc.Circuit`` 为 Qiskit,然后调用 Qiskit 中内置的可视化。 也就是说,我们有 ``c.draw()`` API。" -#: ../../source/quickstart.rst:96 +#: ../../source/quickstart.rst:137 msgid "**Circuit Intermediate Representation:**" msgstr "**电路中间表示:**" -#: ../../source/quickstart.rst:98 +#: ../../source/quickstart.rst:139 msgid "" "TensorCircuit provides its own circuit IR as a python list of dicts. This" " IR can be further utilized to run compiling, generate serialization " "qasm, or render circuit figures." msgstr "TensorCircuit 提供自己的中间表示是元素是字典的列表。此中间表示可进一步用于运行编译、生成序列化 qasm 或渲染电路图。" -#: ../../source/quickstart.rst:100 +#: ../../source/quickstart.rst:141 msgid "" "The IR is given as a list, each element is a dict containing information " "on one gate that is applied to the circuit. Note gate attr in the dict is" @@ -236,18 +327,18 @@ msgstr "" "中间表示以列表形式给出,每个元素都是一个字典,其中包含应用于电路的一个量子门的信息。 注意字典中的 gate atrr " "实际上是一个返回此量子门的节点的 python 函数。" -#: ../../source/quickstart.rst:112 +#: ../../source/quickstart.rst:153 msgid "Programming Paradigm" msgstr "编程范式" -#: ../../source/quickstart.rst:114 +#: ../../source/quickstart.rst:155 msgid "" "The most common case and the most typical programming paradigm for " "TensorCircuit are to evaluate the circuit output and the corresponding " "quantum gradients, which is common in variational quantum algorithms." msgstr "TensorCircuit 最常见的情况和最典型的编程范式是评估电路的输出以及相应的量子梯度,这在变分量子算法中很常见。" -#: ../../source/quickstart.rst:141 +#: ../../source/quickstart.rst:182 #, fuzzy msgid "" "Also for a non-quantum example (linear regression) demonstrating the " @@ -264,7 +355,7 @@ msgstr "" "dev/blob/master/examples/universal_lr.py>`_ 。 " "这个例子可能对机器学习的用户更友好,因为它纯粹是经典的,同时也展示了 TensorCircuit 的主要特征和范式。" -#: ../../source/quickstart.rst:144 +#: ../../source/quickstart.rst:185 msgid "" "If the user has no intention to maintain the application code in a " "backend agnostic fashion, the API for ML frameworks can be more handily " @@ -273,11 +364,11 @@ msgstr "" "如果用户无意以与后端无关的方式维护应用程序代码,则可以更方便地使用用于机器学习框架的 API 并将其与 TensorCircuit API " "交替使用。" -#: ../../source/quickstart.rst:179 +#: ../../source/quickstart.rst:220 msgid "Automatic Differentiation, JIT, and Vectorized Parallelism" msgstr "自动微分、即时编译和矢量化并行 " -#: ../../source/quickstart.rst:181 +#: ../../source/quickstart.rst:222 msgid "" "For concepts of AD, JIT and VMAP, please refer to `Jax documentation " "`__ ." @@ -285,7 +376,7 @@ msgstr "" "关于自动微分、即时编译和向量并行化,请参考 `Jax 文档 " "`__ 。" -#: ../../source/quickstart.rst:183 +#: ../../source/quickstart.rst:224 msgid "" "The related API design in TensorCircuit closely follows the functional " "programming design pattern in Jax with some slight differences. So we " @@ -295,21 +386,21 @@ msgstr "" "TensorCircuit 中的相关 API 设计与 Jax 中的函数式编程的设计模式密切相关,但是略有不同。因此,我们强烈建议用户学习一些有关 " "Jax 的基础知识,无论他们打算使用哪种机器学习后端。" -#: ../../source/quickstart.rst:185 +#: ../../source/quickstart.rst:226 msgid "**AD Support:**" msgstr "**自动微分支持**" -#: ../../source/quickstart.rst:187 +#: ../../source/quickstart.rst:228 msgid "" "Gradients, vjps, jvps, natural gradients, Jacobians, and Hessians. AD is " "the base for all modern machine learning libraries." msgstr "梯度、矢量雅可比乘积、自然梯度、 Jacobian 矩阵和 Hessian 矩阵。自动微分是所有现代机器学习库的基础。" -#: ../../source/quickstart.rst:191 +#: ../../source/quickstart.rst:232 msgid "**JIT Support:**" msgstr "**自动微分支持**" -#: ../../source/quickstart.rst:193 +#: ../../source/quickstart.rst:234 msgid "" "Parameterized quantum circuits can run in a blink. Always use jit if the " "circuit will get evaluations multiple times, it can greatly boost the " @@ -324,11 +415,11 @@ msgstr "" " 即时编译,否则,即时编译的函数可能会返回意外结果或每次在点击时都重新编译(浪费大量时间)。要了解更多关于即时编译机制的信息,可以参考关于 " "``tf.function`` 或 ``jax.jit`` 的文档或博客,即使这两者仍然存在细微差别。" -#: ../../source/quickstart.rst:197 +#: ../../source/quickstart.rst:238 msgid "**VMAP Support:**" msgstr "**自动微分支持**" -#: ../../source/quickstart.rst:199 +#: ../../source/quickstart.rst:240 msgid "" "Inputs, parameters, measurements, circuit structures, and Monte Carlo " "noise can all be evaluated in parallel. To learn more about vmap " @@ -338,11 +429,11 @@ msgstr "" "输入、参数、测量、电路结构、蒙特卡洛噪声都可以并行测算。 要了解有关矢量并行化机制的更多信息,可以参考 ``tf.vectorized_map``" " 或 ``jax.vmap`` 上的文档或博客。" -#: ../../source/quickstart.rst:204 +#: ../../source/quickstart.rst:245 msgid "Backend Agnosticism" msgstr "后端无关特性" -#: ../../source/quickstart.rst:206 +#: ../../source/quickstart.rst:247 msgid "" "TensorCircuit supports TensorFlow, Jax, and PyTorch backends. We " "recommend using TensorFlow or Jax backend since PyTorch lacks advanced " @@ -351,16 +442,16 @@ msgstr "" "TensorCircuit 支持 TensorFlow、Jax 和 PyTorch 后端。 我们建议使用 TensorFlow 或 Jax " "后端,因为 PyTorch 缺乏高级 jit 和 vmap 功能。" -#: ../../source/quickstart.rst:208 +#: ../../source/quickstart.rst:249 msgid "" "The backend can be set as ``K=tc.set_backend(\"jax\")`` and ``K`` is the " "backend with a full set of APIs as a conventional ML framework, which can" " also be accessed by ``tc.backend``." msgstr "" -"后端可以设置为 ``K=tc.set_backend(\"jax\")`` ,``K``作为常规机器学习框架的全套API的后端,也可以通过``tc " -".backend`` 被访问。" +"后端可以设置为 ``K=tc.set_backend(\"jax\")`` ,``K``作为常规机器学习框架的全套API的后端,也可以通过``tc" +" .backend`` 被访问。" -#: ../../source/quickstart.rst:231 +#: ../../source/quickstart.rst:272 #, fuzzy msgid "" "The supported APIs in the backend come from two sources, one part is " @@ -375,11 +466,15 @@ msgstr "" " 另一个来自 `TensorCircuit package `__。" -#: ../../source/quickstart.rst:371 +#: ../../source/quickstart.rst:427 +msgid "​" +msgstr "" + +#: ../../source/quickstart.rst:430 msgid "Switch the Dtype" msgstr "转换 dtype" -#: ../../source/quickstart.rst:373 +#: ../../source/quickstart.rst:432 msgid "" "TensorCircuit supports simulation using 32/64 bit precession. The default" " dtype is 32-bit as \"complex64\". Change this by " @@ -389,24 +484,24 @@ msgstr "" "\"complex64\"。可以通过 ``tc.set_dtype(\"complex128\")`` 把 dtype 改为 \"complex" " 128\" 。" -#: ../../source/quickstart.rst:376 +#: ../../source/quickstart.rst:435 msgid "" "``tc.dtypestr`` always returns the current dtype string: either " "\"complex64\" or \"complex128\"." msgstr "``tc.dtypestr`` 总会返回当前的 dtype 字符串: 不是 \"complex64\" 就是 \"complex128\"." -#: ../../source/quickstart.rst:380 +#: ../../source/quickstart.rst:439 msgid "Setup the Contractor" msgstr "设置 contractor" -#: ../../source/quickstart.rst:382 +#: ../../source/quickstart.rst:441 msgid "" "TensorCircuit is a tensornetwork contraction-based quantum circuit " "simulator. A contractor is for searching for the optimal contraction path" " of the circuit tensornetwork." msgstr "TensorCircuit 是一个基于张量网络收缩的量子电路模拟器。 contractor 用于搜索电路张量网络的最佳收缩路径。" -#: ../../source/quickstart.rst:384 +#: ../../source/quickstart.rst:443 msgid "" "There are various advanced contractors provided by third-party packages, " "such as `opt-einsum `__ and " @@ -416,7 +511,7 @@ msgstr "" "`__ 和 `cotengra " "`__ 。" -#: ../../source/quickstart.rst:386 +#: ../../source/quickstart.rst:445 msgid "" "`opt-einsum` is shipped with TensorNetwork package. To use cotengra, one " "needs to pip install it; kahypar is also recommended to install with " @@ -425,11 +520,11 @@ msgstr "" "`opt-einsum` 随 TensorNetwork 软件包一起。如要使用 cotengra,则需要 pip 安装它; 还建议安装 " "cotengra 随 kahypar 一起使用。" -#: ../../source/quickstart.rst:388 +#: ../../source/quickstart.rst:447 msgid "Some setup cases:" msgstr "一些设置案例:" -#: ../../source/quickstart.rst:414 +#: ../../source/quickstart.rst:473 #, fuzzy msgid "" "For advanced configurations on cotengra contractors, please refer to " @@ -444,25 +539,25 @@ msgstr "" "`contractor 教程 `__." -#: ../../source/quickstart.rst:416 +#: ../../source/quickstart.rst:475 msgid "**Setup in Function or Context Level**" msgstr "**函数和上下文级别的设置**" -#: ../../source/quickstart.rst:418 +#: ../../source/quickstart.rst:477 msgid "" "Beside global level setup, we can also setup the backend, the dtype, and " "the contractor at the function level or context manager level:" msgstr "除了全局级别设置,我们还可以在函数级别或上下文管理器级别设置后端、dtype 和contractor:" -#: ../../source/quickstart.rst:436 +#: ../../source/quickstart.rst:495 msgid "Noisy Circuit Simulation" msgstr "噪声电路模拟" -#: ../../source/quickstart.rst:438 +#: ../../source/quickstart.rst:497 msgid "**Monte Carlo State Simulator:**" msgstr "**蒙特卡洛态模拟器**" -#: ../../source/quickstart.rst:440 +#: ../../source/quickstart.rst:499 msgid "" "For the Monte Carlo trajectory noise simulator, the unitary Kraus channel" " can be handled easily. TensorCircuit also supports fully jittable and " @@ -471,22 +566,40 @@ msgstr "" "对于蒙特卡洛轨迹噪声模拟器,可以轻松处理幺正的 Kraus 通道。 不过,TensorCircuit 还支持完全可即时编译和可微分的通用 " "Kraus 通道蒙特卡罗模拟。" -#: ../../source/quickstart.rst:452 +#: ../../source/quickstart.rst:526 msgid "**Density Matrix Simulator:**" msgstr "**密度矩阵模拟器**" -#: ../../source/quickstart.rst:454 +#: ../../source/quickstart.rst:528 msgid "" "Density matrix simulator ``tc.DMCircuit`` simulates the noise in a full " "form, but takes twice qubits to do noiseless simulation. The API is the " "same as ``tc.Circuit``." msgstr "密度矩阵模拟器``tc.DMCircuit`` 以完整形式模拟噪声,但需要两倍的量子比特。API 与 ``tc.Circuit`` 基本相同。" -#: ../../source/quickstart.rst:458 +#: ../../source/quickstart.rst:547 +msgid "**Experiment with quantum errors:**" +msgstr "" + +#: ../../source/quickstart.rst:549 +msgid "Multiple quantum errors can be added on circuit." +msgstr "" + +#: ../../source/quickstart.rst:565 +msgid "**Experiment with readout error:**" +msgstr "" + +#: ../../source/quickstart.rst:567 +msgid "" +"Readout error can be added in experiments for sampling and expectation " +"value calculation." +msgstr "" + +#: ../../source/quickstart.rst:593 msgid "MPS and MPO" msgstr "矩阵乘积状态和矩阵乘积算子" -#: ../../source/quickstart.rst:460 +#: ../../source/quickstart.rst:595 msgid "" "TensorCircuit has its class for MPS and MPO originally defined in " "TensorNetwork as ``tc.QuVector``, ``tc.QuOperator``." @@ -494,7 +607,7 @@ msgstr "" "TensorCircuit 有自己的 MPS 和 MPO 类,起初在 TensorNetwork 中定义为“tc.QuVector” 和 " "“tc.QuOperator”。" -#: ../../source/quickstart.rst:462 +#: ../../source/quickstart.rst:597 msgid "" "``tc.QuVector`` can be extracted from ``tc.Circuit`` as the tensor " "network form for the output state (uncontracted) by ``c.quvector()``." @@ -502,7 +615,7 @@ msgstr "" "作为``c.quvector()`` 的输出状态(未收缩)的张量网络形式,``tc.QuVector`` 可以从``tc.Circuit`` " "中提取。" -#: ../../source/quickstart.rst:464 +#: ../../source/quickstart.rst:599 msgid "" "The QuVector forms a wavefunction w, which can also be fed into Circuit " "as the inputs state as ``c=tc.Circuit(n, mps_inputs=w)``." @@ -510,61 +623,61 @@ msgstr "" "QuVector 形成一个波函数 w,它也可以作为 ``c=tc.Circuit(n, mps_inputs=w)`` 的输入状态输入到 " "Circuit 中。" -#: ../../source/quickstart.rst:466 +#: ../../source/quickstart.rst:601 msgid "MPS as input state for circuit" msgstr "MPS 作为电路的输入状态" -#: ../../source/quickstart.rst:468 +#: ../../source/quickstart.rst:603 msgid "" "The MPS/QuVector representation of the input state has a more efficient " "and compact form." msgstr "输入状态的 MPS/QuVector 表示具有更高效和紧凑的形式。" -#: ../../source/quickstart.rst:480 +#: ../../source/quickstart.rst:615 msgid "MPS as (uncomputed) output state for circuit" msgstr "MPS 作为电路的(未计算的)输出状态" -#: ../../source/quickstart.rst:482 +#: ../../source/quickstart.rst:617 msgid "" "For example, a quick way to calculate the wavefunction overlap without " "explicitly computing the state amplitude is given as below:" msgstr "例如,在不显式计算状态幅度的情况下,计算波函数重叠的快速方法如下:" -#: ../../source/quickstart.rst:499 +#: ../../source/quickstart.rst:634 msgid "MPO as the gate on the circuit" msgstr "MPO 作为电路上的门" -#: ../../source/quickstart.rst:501 +#: ../../source/quickstart.rst:636 msgid "" "Instead of a common quantum gate in matrix/node format, we can directly " "apply a gate in MPO/QuOperator format." msgstr "代替矩阵/节点格式的普通量子门,我们可以直接应用 MPO/QuOperator 格式的门。" -#: ../../source/quickstart.rst:512 +#: ../../source/quickstart.rst:647 msgid "" "The representative gate defined in MPO format is the ``multicontrol`` " "gate." msgstr "以 MPO 格式定义的代表门是 ``multicontrol`` 门。" -#: ../../source/quickstart.rst:514 +#: ../../source/quickstart.rst:649 msgid "MPO as the operator for expectation evaluation on a circuit" msgstr "MPO作为电路期望估测算子" -#: ../../source/quickstart.rst:516 +#: ../../source/quickstart.rst:651 msgid "" "We can also measure operator expectation on the circuit output state " "where the operator is in MPO/QuOperator format." msgstr "我们还可以测量运算符对 MPO/QuOperator 格式的电路输出状态的期望。" -#: ../../source/quickstart.rst:528 +#: ../../source/quickstart.rst:663 msgid "Interfaces" msgstr "接口" -#: ../../source/quickstart.rst:530 +#: ../../source/quickstart.rst:665 msgid "**PyTorch Interface to Hybrid with PyTorch Modules:**" msgstr "**与 PyTorch 模块混合的 PyTorch 接口:**" -#: ../../source/quickstart.rst:532 +#: ../../source/quickstart.rst:667 msgid "" "As we have mentioned in the backend section, the PyTorch backend may lack" " advanced features. This doesn't mean we cannot hybrid the advanced " @@ -575,45 +688,110 @@ msgstr "" "正如我们在后端部分提到的,PyTorch 后端可能缺少高级功能。 这并不意味着我们不能将高级量子电路模块与 PyTorch 神经模块混合。 " "我们可以在 TensorFlow 或 Jax 后端运行量子函数,同时使用 Torch 接口包装它。 " -#: ../../source/quickstart.rst:560 +#: ../../source/quickstart.rst:694 +msgid "" +"For a GPU/CPU, torch/tensorflow, quantum/classical hybrid machine " +"learning pipeline enabled by tensorcircuit, see `example script " +"`__." +msgstr "" + +#: ../../source/quickstart.rst:696 +msgid "" +"There is also a more flexible torch interface that support static non-" +"tensor inputs as keyword arguments, which can be utilized as below:" +msgstr "" + +#: ../../source/quickstart.rst:710 +msgid "" +"We also provider wrapper of quantum function for torch module as " +":py:meth:`tensorcircuit.TorchLayer` alias to " +":py:meth:`tensorcircuit.torchnn.QuantumNet`." +msgstr "" + +#: ../../source/quickstart.rst:712 +msgid "" +"For ``TorchLayer``, ``use_interface=True`` is by default, which natively " +"allow the quantum function defined on other tensorcircuit backends, such " +"as jax or tf for speed consideration." +msgstr "" + +#: ../../source/quickstart.rst:714 +msgid "" +"``TorchLayer`` can process multiple input arguments as multiple function " +"inputs, following torch practice." +msgstr "" + +#: ../../source/quickstart.rst:742 +msgid "**TensorFlow interfaces:**" +msgstr "" + +#: ../../source/quickstart.rst:744 +msgid "" +"Similar rules apply similar as torch interface. The interface can even be" +" used within jit environment outside. See " +":py:meth:`tensorcircuit.interfaces.tensorflow.tensorflow_interface`." +msgstr "" + +#: ../../source/quickstart.rst:747 +msgid "" +"We also provider ``enable_dlpack=True`` option in torch and tf " +"interfaces, which allow the tensor transformation happen without memory " +"transfer via dlpack, higher version of tf or torch package required." +msgstr "" + +#: ../../source/quickstart.rst:750 +msgid "" +"We also provider wrapper of quantum function for keras layer as " +":py:meth:`tensorcircuit.KerasLayer` alias to " +":py:meth:`tensorcircuit.keras.KerasLayer`." +msgstr "" + +#: ../../source/quickstart.rst:752 +msgid "" +"``KerasLayer`` can process multiple input arguments with the input as a " +"dict, following the common keras practice, see example below." +msgstr "" + +#: ../../source/quickstart.rst:774 msgid "**Scipy Interface to Utilize Scipy Optimizers:**" msgstr "**使用 scipy接口使用scipy优化器:**" -#: ../../source/quickstart.rst:562 +#: ../../source/quickstart.rst:776 msgid "" "Automatically transform quantum functions as scipy-compatible values and " "grad functions as provided for scipy interface with ``jac=True``." msgstr "为带有 jac=True 的 scipy 接口自动将量子函数转换为与 scipy 兼容的 value 和 grad 函数。" -#: ../../source/quickstart.rst:588 +#: ../../source/quickstart.rst:802 msgid "Templates as Shortcuts" msgstr "捷径模板" -#: ../../source/quickstart.rst:590 +#: ../../source/quickstart.rst:804 msgid "**Measurements:**" msgstr "**测量**" -#: ../../source/quickstart.rst:592 +#: ../../source/quickstart.rst:806 msgid "Ising type Hamiltonian defined on a general graph" msgstr "在一般图上定义的伊辛型哈密顿量" -#: ../../source/quickstart.rst:594 +#: ../../source/quickstart.rst:808 msgid "" "See " ":py:meth:`tensorcircuit.templates.measurements.spin_glass_measurements`" msgstr "参考 :py:meth:`tensorcircuit.templates.measurements.spin_glass_measurements`" -#: ../../source/quickstart.rst:596 +#: ../../source/quickstart.rst:810 msgid "Heisenberg Hamiltonian on a general graph with possible external fields" msgstr "具有可能存在的外场的一般图上的海森堡哈密顿量" -#: ../../source/quickstart.rst:598 +#: ../../source/quickstart.rst:812 msgid "" "See " ":py:meth:`tensorcircuit.templates.measurements.heisenberg_measurements`" msgstr "参考 :py:meth:`tensorcircuit.templates.measurements.heisenberg_measurements`" -#: ../../source/quickstart.rst:600 +#: ../../source/quickstart.rst:814 msgid "**Circuit Blocks:**" msgstr "**电路块**" @@ -682,3 +860,16 @@ msgstr "**电路块**" #~ "`wand `__ " #~ "及其二进制绑定以及 LaTeX 的安装。" +#~ msgid "Import from Qiskit: ``c = tc.Circuit.from_qiskit(QuantumCircuit, n)``" +#~ msgstr "从 Qiskit 导入:``c = tc.Circuit.from_qiskit(QuantumCircuit, n)``" + +#~ msgid "For x86 Linux or Mac," +#~ msgstr "" + +#~ msgid "" +#~ "For Mac with M series chips (arm" +#~ " architecture), please refer to `TC " +#~ "on Mac M series " +#~ "`_." +#~ msgstr "" + diff --git a/docs/source/locale/zh/LC_MESSAGES/sharpbits.po b/docs/source/locale/zh/LC_MESSAGES/sharpbits.po index b36da0fc..5b2bfadc 100644 --- a/docs/source/locale/zh/LC_MESSAGES/sharpbits.po +++ b/docs/source/locale/zh/LC_MESSAGES/sharpbits.po @@ -9,7 +9,7 @@ msgid "" msgstr "" "Project-Id-Version: tensorcircuit \n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2022-06-27 20:10+0800\n" +"POT-Creation-Date: 2023-05-07 10:47+0800\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" @@ -20,22 +20,22 @@ msgstr "" #: ../../source/sharpbits.rst:3 msgid "TensorCircuit: The Sharp Bits 🔪" -msgstr "" +msgstr "TensorCircuit: 常见错误 🔪" #: ../../source/sharpbits.rst:5 msgid "" "Be fast is never for free, though much cheaper in TensorCircuit, but you " "have to be cautious especially in terms of AD, JIT compatibility. We will" " go through the main sharp edges 🔪 in this note." -msgstr "" +msgstr "虽然在TensorCircuit中速度很快,但是你必须小心,尤其是在AD和JIT兼容性方面。" #: ../../source/sharpbits.rst:9 msgid "Jit Compatibility" -msgstr "" +msgstr "Jit 兼容性" #: ../../source/sharpbits.rst:12 msgid "Non tensor input or varying shape tensor input" -msgstr "" +msgstr "非向量输入或者变化形状的向量输入" #: ../../source/sharpbits.rst:14 msgid "" @@ -43,11 +43,11 @@ msgid "" " otherwise the recompilation is incurred which is time-consuming. " "Therefore, if there are input args that are non-tensor or varying shape " "tensors and frequently change, jit is not recommend." -msgstr "" +msgstr "输入必须是张量形式,且输入张量的形状必须固定,否则会重新编译,这是非常耗时的。因此,如果有输入参数是非张量或者变化形状的张量,且经常变化,不建议使用jit。" #: ../../source/sharpbits.rst:38 msgid "Mix use of numpy and ML backend APIs" -msgstr "" +msgstr "混合使用numpy和ML后端API" #: ../../source/sharpbits.rst:40 msgid "" @@ -58,47 +58,95 @@ msgid "" "For numpy ops, they will be only called in jit staging time (the first " "run)." msgstr "" +"为了使函数可jit和可AD,函数中的每个操作都应该通过ML后端(``tc.backend`` API或者直接调用后端API ``tf`` 或者 " +"``jax``)。这是因为ML后端必须创建计算图来\"进行AD和JIT转换。对于numpy操作,它们只会在jit编译阶段被调用(第一次运行)。" #: ../../source/sharpbits.rst:54 msgid "" "Numpy call inside jitted function can be helpful if you are sure of the " "behavior is what you expect." -msgstr "" +msgstr "如果你确定numpy调用的行为是你期望的,那么在jit函数中调用numpy是有帮助的。" #: ../../source/sharpbits.rst:83 msgid "list append under if" -msgstr "" +msgstr "if下的list append" #: ../../source/sharpbits.rst:85 msgid "" "Append something to a Python list within if whose condition is based on " "tensor values will lead to wrong results. Actually values of both branch " "will be attached to the list. See example below." -msgstr "" +msgstr "在if条件基于张量值的情况下,将内容附加到Python列表中会导致错误的结果。实际上,两个分支的值都会被附加到列表中。参见下面的例子。" #: ../../source/sharpbits.rst:108 msgid "" "The above code raise ``ConcretizationTypeError`` exception directly for " "Jax backend since Jax jit doesn't support tensor value if condition." -msgstr "" +msgstr "上面的代码直接为Jax后端引发了``ConcretizationTypeError``异常,因为Jax jit不支持张量值if条件。" #: ../../source/sharpbits.rst:110 msgid "Similarly, conditional gate application must be takend carefully." -msgstr "" +msgstr "类似地,必须小心地应用条件门。" #: ../../source/sharpbits.rst:145 -msgid "AD Consistency" +msgid "Tensor variables consistency" +msgstr "" + +#: ../../source/sharpbits.rst:148 +msgid "" +"All tensor variables' backend (tf vs jax vs ..), dtype (float vs " +"complex), shape and device (cpu vs gpu) must be compatible/consistent." +msgstr "" + +#: ../../source/sharpbits.rst:150 +msgid "Inspect the backend, dtype, shape and device using the following codes." msgstr "" -#: ../../source/sharpbits.rst:147 +#: ../../source/sharpbits.rst:162 +msgid "" +"If the backend is inconsistent, one can convert the tensor backend via " +":py:meth:`tensorcircuit.interfaces.tensortrans.general_args_to_backend`." +msgstr "" + +#: ../../source/sharpbits.rst:173 +msgid "" +"If the dtype is inconsistent, one can convert the tensor dtype using " +"``tc.backend.cast``." +msgstr "" + +#: ../../source/sharpbits.rst:184 +msgid "" +"Also note the jax issue on float64/complex128, see `jax gotcha " +"`_." +msgstr "" + +#: ../../source/sharpbits.rst:186 +msgid "" +"If the shape is not consistent, one can convert the shape by " +"``tc.backend.reshape``." +msgstr "" + +#: ../../source/sharpbits.rst:188 +msgid "" +"If the device is not consistent, one can move the tensor between devices " +"by ``tc.backend.device_move``." +msgstr "" + +#: ../../source/sharpbits.rst:192 +msgid "AD Consistency" +msgstr "AD一致性" + +#: ../../source/sharpbits.rst:194 msgid "" "TF and JAX backend manage the differentiation rules differently for " "complex-valued function (actually up to a complex conjuagte). See issue " "discussion `tensorflow issue " "`_." msgstr "" +"TF和JAX后端对复值函数的微分规则的管理方式不同(实际上是复共轭)。参见讨论 `tensorflow issue " +"`_。" -#: ../../source/sharpbits.rst:149 +#: ../../source/sharpbits.rst:196 msgid "" "In TensorCircuit, currently we make the difference in AD transparent, " "namely, when switching the backend, the AD behavior and result for " @@ -108,12 +156,18 @@ msgid "" "careful when dealing with AD on complex valued function in a backend " "agnostic way in TensorCircuit." msgstr "" +"在TensorCircuit中,我们目前使AD的差异透明,即在切换后端时,复值函数的AD行为和结果可能不同,并由相应后端框架的本质行为决定。所有与AD相关的操作,如" +" ``grad`` 或者 ``jacrev`` " +"都可能受到影响。因此,用户在TensorCircuit中以后端无关的方式处理复值函数的AD时必须小心。" -#: ../../source/sharpbits.rst:152 +#: ../../source/sharpbits.rst:199 msgid "" "See example script on computing Jacobian with different modes on " "different backends: `jacobian_cal.py `_. Also see the " "code below for a reference:" msgstr "" +"参考不同后端的不同模式下计算Jacobian的示例脚本:`jacobian_cal.py `_。另外请参考下面的代码:" diff --git a/docs/source/modules.rst b/docs/source/modules.rst index 33ae1cb2..0144a199 100644 --- a/docs/source/modules.rst +++ b/docs/source/modules.rst @@ -1,21 +1,28 @@ tensorcircuit -================================================== +================================================================================ .. toctree:: + ./api/about.rst ./api/abstractcircuit.rst ./api/applications.rst ./api/backends.rst ./api/basecircuit.rst ./api/channels.rst ./api/circuit.rst + ./api/cloud.rst + ./api/compiler.rst ./api/cons.rst ./api/densitymatrix.rst ./api/experimental.rst + ./api/fgs.rst ./api/gates.rst ./api/interfaces.rst ./api/keras.rst ./api/mps_base.rst ./api/mpscircuit.rst + ./api/noisemodel.rst ./api/quantum.rst + ./api/results.rst + ./api/shadows.rst ./api/simplify.rst ./api/templates.rst ./api/torchnn.rst diff --git a/docs/source/modules.rst.backup b/docs/source/modules.rst.backup index 28261e24..280c7584 100644 --- a/docs/source/modules.rst.backup +++ b/docs/source/modules.rst.backup @@ -1,200 +1,29 @@ -tensorcircuit API -===================== - +tensorcircuit +================================================== .. toctree:: - :maxdepth: 4 - - -tensorcircuit.backends module ------------------------------------- - -.. automodule:: tensorcircuit.backends - :members: - :undoc-members: - :show-inheritance: - -tensorcircuit.backends.backend_factory module -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -.. automodule:: tensorcircuit.backends.backend_factory - :members: - :undoc-members: - :show-inheritance: - -tensorcircuit.backends.jax_backend module -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -.. automodule:: tensorcircuit.backends.jax_backend - :members: - :undoc-members: - :show-inheritance: - -tensorcircuit.backends.numpy_backend module -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -.. automodule:: tensorcircuit.backends.numpy_backend - :members: - :undoc-members: - :show-inheritance: - -tensorcircuit.backends.pytorch_backend module -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -.. automodule:: tensorcircuit.backends.pytorch_backend - :members: - :undoc-members: - :show-inheritance: - -tensorcircuit.backends.tensorflow_backend module -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -.. automodule:: tensorcircuit.backends.tensorflow_backend - :members: - :undoc-members: - :show-inheritance: - -tensorcircuit.channels module -------------------------------------- - -.. automodule:: tensorcircuit.channels - :members: - :undoc-members: - :show-inheritance: - -tensorcircuit.circuit module -------------------------------------- - -.. automodule:: tensorcircuit.circuit - :members: - :undoc-members: - :show-inheritance: - - -tensorcircuit.cons module -------------------------------------- - -.. automodule:: tensorcircuit.cons - :members: - :undoc-members: - :show-inheritance: - -tensorcircuit.densitymatrix module -------------------------------------- - -.. automodule:: tensorcircuit.densitymatrix - :members: - :undoc-members: - :show-inheritance: - -tensorcircuit.densitymatrix2 module -------------------------------------- - -.. automodule:: tensorcircuit.densitymatrix2 - :members: - :undoc-members: - :show-inheritance: - -tensorcircuit.experimental module -------------------------------------- - -.. automodule:: tensorcircuit.experimental - :members: - :undoc-members: - :show-inheritance: - - -tensorcircuit.gates module ---------------------------------- - -.. automodule:: tensorcircuit.gates - :members: - :undoc-members: - :show-inheritance: - -tensorcircuit.interfaces module ---------------------------------- - -.. automodule:: tensorcircuit.interfaces - :members: - :undoc-members: - :show-inheritance: - -tensorcircuit.keras module ---------------------------------- - -.. automodule:: tensorcircuit.keras - :members: - :undoc-members: - :show-inheritance: - -tensorcircuit.mpscircuit module ---------------------------------- - -.. automodule:: tensorcircuit.mpscircuit - :members: - :undoc-members: - :show-inheritance: - -tensorcircuit.quantum module ---------------------------------- - -.. automodule:: tensorcircuit.quantum - :members: - :undoc-members: - :show-inheritance: - -tensorcircuit.simplify module ---------------------------------- - -.. automodule:: tensorcircuit.simplify - :members: - :undoc-members: - :show-inheritance: - -tensorcircuit.templates module ------------------------------------- - -.. automodule:: tensorcircuit.templates - :members: - :undoc-members: - :show-inheritance: - -tensorcircuit.templates.blocks module -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -.. automodule:: tensorcircuit.templates.blocks - :members: - :undoc-members: - :show-inheritance: - -tensorcircuit.templates.graphs module -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -.. automodule:: tensorcircuit.templates.graphs - :members: - :undoc-members: - :show-inheritance: - -tensorcircuit.templates.measurements module -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -.. automodule:: tensorcircuit.templates.measurements - :members: - :undoc-members: - :show-inheritance: - -tensorcircuit.utils module ---------------------------------- - -.. automodule:: tensorcircuit.utils - :members: - :undoc-members: - :show-inheritance: - -tensorcircuit.vis module ---------------------------------- - -.. automodule:: tensorcircuit.vis - :members: - :undoc-members: - :show-inheritance: \ No newline at end of file + ./api/about.rst + ./api/abstractcircuit.rst + ./api/applications.rst + ./api/backends.rst + ./api/basecircuit.rst + ./api/channels.rst + ./api/circuit.rst + ./api/cloud.rst + ./api/compiler.rst + ./api/cons.rst + ./api/densitymatrix.rst + ./api/experimental.rst + ./api/gates.rst + ./api/interfaces.rst + ./api/keras.rst + ./api/mps_base.rst + ./api/mpscircuit.rst + ./api/noisemodel.rst + ./api/quantum.rst + ./api/results.rst + ./api/simplify.rst + ./api/templates.rst + ./api/torchnn.rst + ./api/translation.rst + ./api/utils.rst + ./api/vis.rst \ No newline at end of file diff --git a/docs/source/quickstart.rst b/docs/source/quickstart.rst index 50b972c3..647066b6 100644 --- a/docs/source/quickstart.rst +++ b/docs/source/quickstart.rst @@ -2,6 +2,45 @@ Quick Start ================ +Installation +-------------- + +- For x86 Linux, + +``pip install tensorcircuit`` + +is in general enough. +Either pip from conda or other python env managers is fine. + +Since there are many optional packages for various features, +the users may need to install more pip packages when required. + +- For Linux with Nvidia GPU, +please refer to the GPU aware installation guide of corresponding machine learning frameworks: +`TensorFlow `_, +`Jax `_, +or `PyTorch `_. + +Docker is also recommended (especially Linux + Nvidia GPU setup): + +``sudo docker run -it --network host --gpus all tensorcircuit/tensorcircuit``. + +For more details on docker setup, please refer to `docker readme `_. + +- For Windows, due to the lack of support for Jax, we recommend to use docker or WSL, please refer to `TC via windows docker `_ or `TC via WSL `_. + +- For MacOS, please refer to `TC on Mac `_. + +Overall, the installation of TensorCircuit is simple, since it is purely in Python and hence very portable. +As long as the users can take care of the installation of ML frameworks on the corresponding system, TensorCircuit will work as expected. + +To debug the installation issue or report bugs, please check the environment information by ``tc.about()``. + +.. Note:: + We also provide a nightly build of tensorcircuit via PyPI which can be accessed by + ``pip uninstall tensorcircuit``, then + ``pip install tensorcircuit-nightly`` + Circuit Object ------------------ @@ -81,7 +120,9 @@ We currently support transform ``tc.Circuit`` from and to Qiskit ``QuantumCircui Export to Qiskit (possible for further hardware experiment, compiling, and visualization): ``c.to_qiskit()``. -Import from Qiskit: ``c = tc.Circuit.from_qiskit(QuantumCircuit, n)`` +Import from Qiskit: ``c = tc.Circuit.from_qiskit(QuantumCircuit, n)``. +Parameterized Qiskit circuit is supported by passing the parameters to the ``binding_parameters`` argument +of the ``from_qiskit`` function, similar to the ``assign_parameters`` function in Qiskit. **Circuit Visualization:** @@ -107,6 +148,8 @@ The IR is given as a list, each element is a dict containing information on one >>> c.to_qir() [{'gate': cnot, 'index': (0, 1), 'name': 'cnot', 'split': None}, {'gate': crx, 'index': (1, 0), 'name': 'crx', 'split': None, 'parameters': {'theta': 0.2}}] +We can also create new copied circuit via ``c.copy()`` which internally utilize the ``qir``. + Programming Paradigm ------------------------- @@ -262,9 +305,12 @@ and the other part is implemented in `TensorCircuit package >> c = tc.Circuit(2) - >>> c.unitary_kraus(tc.channels.depolarizingchannel(0.2, 0.2, 0.2), 0) - 0.0 - >>> c.general_kraus(tc.channels.resetchannel(), 1) - 0.0 - >>> c.state() - array([0.+0.j, 0.+0.j, 0.+1.j, 0.+0.j], dtype=complex64) + def noisecircuit(random): + c = tc.Circuit(1) + c.x(0) + c.thermalrelaxation( + 0, + t1=300, + t2=400, + time=1000, + method="ByChoi", + excitedstatepopulation=0, + status=random, + ) + return c.expectation_ps(z=[0]) + + + K = tc.set_backend("tensorflow") + noisec_vmap = K.jit(K.vmap(noisecircuit, vectorized_argnums=0)) + nmc = 10000 + random = K.implicit_randu(nmc) + valuemc = K.mean(K.numpy(noisec_vmap(random))) + # (0.931+0j) + **Density Matrix Simulator:** Density matrix simulator ``tc.DMCircuit`` simulates the noise in a full form, but takes twice qubits to do noiseless simulation. The API is the same as ``tc.Circuit``. +.. code-block:: python + + def noisecircuitdm(): + dmc = tc.DMCircuit(1) + dmc.x(0) + dmc.thermalrelaxation( + 0, t1=300, t2=400, time=1000, method="ByChoi", excitedstatepopulation=0 + ) + return dmc.expectation_ps(z=[0]) + + + K = tc.set_backend("tensorflow") + noisec_jit = K.jit(noisecircuitdm) + valuedm = noisec_jit() + # (0.931+0j) + + +**Experiment with quantum errors:** + +Multiple quantum errors can be added on circuit. + +.. code-block:: python + + c = tc.Circuit(1) + c.x(0) + c.thermalrelaxation( + 0, t1=300, t2=400, time=1000, method="ByChoi", excitedstatepopulation=0 + ) + c.generaldepolarizing(0, p=0.01, num_qubits=1) + c.phasedamping(0, gamma=0.2) + c.amplitudedamping(0, gamma=0.25, p=0.2) + c.reset(0) + c.expectation_ps(z=[0]) + + +**Experiment with readout error:** + +Readout error can be added in experiments for sampling and expectation value calculation. + +.. code-block:: python + + c = tc.Circuit(3) + c.X(0) + readout_error = [] + readout_error.append([0.9, 0.75]) # readout error of qubit 0 p0|0=0.9, p1|1=0.75 + readout_error.append([0.4, 0.7]) # readout error of qubit 1 + readout_error.append([0.7, 0.9]) # readout error of qubit 2 + value = c.sample_expectation_ps(z=[0, 1, 2], readout_error=readout_error) + # tf.Tensor(0.039999977, shape=(), dtype=float32) + instances = c.sample( + batch=3, + allow_state=True, + readout_error=readout_error, + random_generator=tc.backend.get_random_state(42), + format_="sample_bin" + ) + # tf.Tensor( + # [[1 0 0] + # [1 0 0] + # [1 0 1]], shape=(3, 3), dtype=int32) + MPS and MPO ---------------- @@ -565,6 +693,85 @@ As we have mentioned in the backend section, the PyTorch backend may lack advanc print(a.grad) +For a GPU/CPU, torch/tensorflow, quantum/classical hybrid machine learning pipeline enabled by tensorcircuit, see `example script `__. + +There is also a more flexible torch interface that support static non-tensor inputs as keyword arguments, which can be utilized as below: + +.. code-block:: python + + def f(a, i): + s = 0. + for _ in range(i): + s += a + return s + + f_torch = tc.interfaces.torch_interface_kws(f) + f_torch(torch.ones([2]), i=3) + + +We also provider wrapper of quantum function for torch module as :py:meth:`tensorcircuit.TorchLayer` alias to :py:meth:`tensorcircuit.torchnn.QuantumNet`. + +For ``TorchLayer``, ``use_interface=True`` is by default, which natively allow the quantum function defined on other tensorcircuit backends, such as jax or tf for speed consideration. + +``TorchLayer`` can process multiple input arguments as multiple function inputs, following torch practice. + +.. code-block:: python + + n = 3 + p = 0.1 + K = tc.backend + torchb = tc.get_backend("pytorch") + + def f(state, noise, weights): + c = tc.Circuit(n, inputs=state) + for i in range(n): + c.rz(i, theta=weights[i]) + for i in range(n): + c.depolarizing(i, px=p, py=p, pz=p, status=noise[i]) + return K.real(c.expectation_ps(x=[0])) + + layer = tc.TorchLayer(f, [n], use_vmap=True, vectorized_argnums=[0, 1]) + state = torchb.ones([2, 2**n]) / 2 ** (n / 2) + noise = 0.2 * torchb.ones([2, n], dtype="float32") + l = layer(state,noise) + lsum = torchb.sum(l) + print(l) + lsum.backward() + for p in layer.parameters(): + print(p.grad) + + +**TensorFlow interfaces:** + +Similar rules apply similar as torch interface. The interface can even be used within jit environment outside. +See :py:meth:`tensorcircuit.interfaces.tensorflow.tensorflow_interface`. + +We also provider ``enable_dlpack=True`` option in torch and tf interfaces, which allow the tensor transformation happen without memory transfer via dlpack, +higher version of tf or torch package required. + +We also provider wrapper of quantum function for keras layer as :py:meth:`tensorcircuit.KerasLayer` alias to :py:meth:`tensorcircuit.keras.KerasLayer`. + +``KerasLayer`` can process multiple input arguments with the input as a dict, following the common keras practice, see example below. + +.. code-block:: python + + def f(inputs, weights): + state = inputs["state"] + noise = inputs["noise"] + c = tc.Circuit(n, inputs=state) + for i in range(n): + c.rz(i, theta=weights[i]) + for i in range(n): + c.depolarizing(i, px=p, py=p, pz=p, status=noise[i]) + return K.real(c.expectation_ps(x=[0])) + + layer = tc.KerasLayer(f, [n]) + v = {"state": K.ones([1, 2**n]) / 2 ** (n / 2), "noise": 0.2 * K.ones([1, n])} + with tf.GradientTape() as tape: + l = layer(v) + grad = tape.gradient(l, layer.trainable_variables) + + **Scipy Interface to Utilize Scipy Optimizers:** diff --git a/docs/source/sharpbits.rst b/docs/source/sharpbits.rst index ffce825c..a48edfab 100644 --- a/docs/source/sharpbits.rst +++ b/docs/source/sharpbits.rst @@ -141,6 +141,53 @@ Similarly, conditional gate application must be takend carefully. # +Tensor variables consistency +------------------------------------------------------- + + +All tensor variables' backend (tf vs jax vs ..), dtype (float vs complex), shape and device (cpu vs gpu) must be compatible/consistent. + +Inspect the backend, dtype, shape and device using the following codes. + +.. code-block:: python + + for backend in ["numpy", "tensorflow", "jax", "pytorch"]: + with tc.runtime_backend(backend): + a = tc.backend.ones([2, 3]) + print("tensor backend:", tc.interfaces.which_backend(a)) + print("tensor dtype:", tc.backend.dtype(a)) + print("tensor shape:", tc.backend.shape_tuple(a)) + print("tensor device:", tc.backend.device(a)) + +If the backend is inconsistent, one can convert the tensor backend via :py:meth:`tensorcircuit.interfaces.tensortrans.general_args_to_backend`. + +.. code-block:: python + + for backend in ["numpy", "tensorflow", "jax", "pytorch"]: + with tc.runtime_backend(backend): + a = tc.backend.ones([2, 3]) + print("tensor backend:", tc.interfaces.which_backend(a)) + b = tc.interfaces.general_args_to_backend(a, target_backend="jax", enable_dlpack=False) + print("tensor backend:", tc.interfaces.which_backend(b)) + +If the dtype is inconsistent, one can convert the tensor dtype using ``tc.backend.cast``. + +.. code-block:: python + + for backend in ["numpy", "tensorflow", "jax", "pytorch"]: + with tc.runtime_backend(backend): + a = tc.backend.ones([2, 3]) + print("tensor dtype:", tc.backend.dtype(a)) + b = tc.backend.cast(a, dtype="float64") + print("tensor dtype:", tc.backend.dtype(b)) + +Also note the jax issue on float64/complex128, see `jax gotcha `_. + +If the shape is not consistent, one can convert the shape by ``tc.backend.reshape``. + +If the device is not consistent, one can move the tensor between devices by ``tc.backend.device_move``. + + AD Consistency --------------------- diff --git a/docs/source/statics/landscape.jpg b/docs/source/statics/landscape.jpg new file mode 100644 index 00000000..3ffd7902 Binary files /dev/null and b/docs/source/statics/landscape.jpg differ diff --git a/docs/source/statics/qd_alg.jpg b/docs/source/statics/qd_alg.jpg new file mode 100644 index 00000000..3300f074 Binary files /dev/null and b/docs/source/statics/qd_alg.jpg differ diff --git a/docs/source/statics/tianxuan_s1.png b/docs/source/statics/tianxuan_s1.png new file mode 100644 index 00000000..983ac187 Binary files /dev/null and b/docs/source/statics/tianxuan_s1.png differ diff --git a/docs/source/tutorial.rst b/docs/source/tutorial.rst index cd6938e5..e5697960 100644 --- a/docs/source/tutorial.rst +++ b/docs/source/tutorial.rst @@ -6,16 +6,26 @@ Jupyter Tutorials tutorials/circuit_basics.ipynb tutorials/qaoa.ipynb + tutorials/qaoa_bo.ipynb + tutorials/qaoa_nae3sat.ipynb + tutorials/qaoa_quantum_dropout.ipynb tutorials/tfim_vqe.ipynb tutorials/mnist_qml.ipynb tutorials/torch_qml.ipynb tutorials/qml_scenarios.ipynb tutorials/vqe_h2o.ipynb tutorials/tfim_vqe_diffreph.ipynb + tutorials/mera.ipynb tutorials/gradient_benchmark.ipynb tutorials/contractors.ipynb tutorials/operator_spreading.ipynb tutorials/optimization_and_expressibility.ipynb tutorials/vqex_mbl.ipynb tutorials/dqas.ipynb - tutorials/barren_plateaus.ipynb \ No newline at end of file + tutorials/barren_plateaus.ipynb + tutorials/qubo_problem.ipynb + tutorials/portfolio_optimization.ipynb + tutorials/imag_time_evo.ipynb + tutorials/classical_shadows.ipynb + tutorials/sklearn_svc.ipynb + tutorials/qcloud_sdk_demo.ipynb \ No newline at end of file diff --git a/docs/source/tutorial_cn.rst b/docs/source/tutorial_cn.rst index e3c062a5..956d03b6 100644 --- a/docs/source/tutorial_cn.rst +++ b/docs/source/tutorial_cn.rst @@ -12,10 +12,12 @@ tutorials/qml_scenarios_cn.ipynb tutorials/vqe_h2o_cn.ipynb tutorials/tfim_vqe_diffreph_cn.ipynb + tutorials/mera_cn.ipynb tutorials/gradient_benchmark_cn.ipynb tutorials/contractors_cn.ipynb tutorials/operator_spreading_cn.ipynb tutorials/optimization_and_expressibility_cn.ipynb tutorials/vqex_mbl_cn.ipynb tutorials/dqas_cn.ipynb - tutorials/barren_plateaus_cn.ipynb \ No newline at end of file + tutorials/barren_plateaus_cn.ipynb + tutorials/sklearn_svc_cn.ipynb \ No newline at end of file diff --git a/docs/source/tutorials/barren_plateaus_cn.ipynb b/docs/source/tutorials/barren_plateaus_cn.ipynb index 4288475f..6f5a8a49 100644 --- a/docs/source/tutorials/barren_plateaus_cn.ipynb +++ b/docs/source/tutorials/barren_plateaus_cn.ipynb @@ -158,7 +158,9 @@ " dtype=\"float32\",\n", ")\n", "\n", - "e, grad = op_expectation_vmap_vvag(params, seed, n, nlayers) # 不同随机电路的 ZZ 可观测量和梯度的期望\n", + "e, grad = op_expectation_vmap_vvag(\n", + " params, seed, n, nlayers\n", + ") # 不同随机电路的 ZZ 可观测量和梯度的期望\n", "\n", "grad_var = tf.math.reduce_std(tf.math.reduce_std(grad, axis=0), axis=0)[\n", " 0, 0\n", diff --git a/docs/source/tutorials/benchmark_circuits.ipynb b/docs/source/tutorials/benchmark_circuits.ipynb new file mode 100644 index 00000000..e2e5eb1d --- /dev/null +++ b/docs/source/tutorials/benchmark_circuits.ipynb @@ -0,0 +1,211 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Benchmark Chips" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Benchmark chips using mirror circuit" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "metadata": {}, + "outputs": [], + "source": [ + "import networkx as nx\n", + "from tensorcircuit.results import qem\n", + "from tensorcircuit.results.qem import benchmark_circuits\n", + "import random\n", + "import numpy as np\n", + "\n", + "from tensorcircuit.cloud import apis\n", + "from tensorcircuit.results import counts\n", + "from tensorcircuit.compiler.qiskit_compiler import qiskit_compile\n", + "from matplotlib import cm\n", + "\n", + "%matplotlib inline\n", + "import matplotlib.pyplot as plt" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "[[0, 1], [6, 2], [4, 0], [0, 4], [8, 4], [1, 5], [3, 7], [0, 3], [2, 0], [5, 1], [3, 0], [7, 3], [0, 2], [2, 6], [4, 8], [1, 0]]\n" + ] + } + ], + "source": [ + "d = apis.get_device(\"tianxuan_s1\")\n", + "couple_list = d.topology()\n", + "print(couple_list)" + ] + }, + { + "cell_type": "code", + "execution_count": 15, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "nq: [0, 1] depth: 1 success 4.1484000000000005\n", + "nq: [0, 1] depth: 2 success 3.7722999999999995\n", + "nq: [0, 1, 2] depth: 1 success 3.2711\n", + "nq: [0, 1, 2] depth: 2 success 3.2814\n", + "nq: [0, 1, 2, 3] depth: 1 success 3.1682000000000006\n", + "nq: [0, 1, 2, 3] depth: 2 success 2.5158\n", + "nq: [0, 1, 2, 3, 4] depth: 1 success 2.1039999999999996\n", + "nq: [0, 1, 2, 3, 4] depth: 2 success 1.4552999999999998\n" + ] + }, + { + "data": { + "image/png": "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", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "result_list = []\n", + "\n", + "\n", + "for nq in range(2, 6):\n", + " qlis = list(range(nq))\n", + " graph = []\n", + " for i in range(len(couple_list)):\n", + " if couple_list[i][0] in qlis and couple_list[i][1] in qlis:\n", + " graph.append(couple_list[i])\n", + "\n", + " G = nx.Graph()\n", + " G.add_edges_from(graph)\n", + " nx.draw(G, with_labels=True)\n", + "\n", + " for depth in range(1, 3):\n", + " A = 0\n", + " for numc in range(5):\n", + " c, ideal = benchmark_circuits.mirror_circuit(\n", + " depth=depth,\n", + " two_qubit_gate_prob=1,\n", + " connectivity_graph=G,\n", + " seed=random.randint(0, 100),\n", + " ) # includes np.sqrt(X)^{\\dagger} gate,identity gate\n", + "\n", + " c1, info = qiskit_compile(\n", + " c,\n", + " compiled_options={\n", + " \"basis_gates\": [\"h\", \"rz\", \"x\", \"y\", \"z\", \"cz\"],\n", + " \"optimization_level\": 2,\n", + " # \"coupling_map\": d.topology(),\n", + " },\n", + " )\n", + "\n", + " t = apis.submit_task(\n", + " circuit=c1,\n", + " shots=10000,\n", + " device=d,\n", + " enable_qos_qubit_mapping=False,\n", + " enable_qos_gate_decomposition=False,\n", + " )\n", + " raw_count = t.results(blocked=True) # position_counts=logical_counts\n", + " # print(\"raw\",raw_count[list(ideal.keys())[0]]/10000)\n", + "\n", + " A += raw_count[list(ideal.keys())[0]] / 10000\n", + " print(\"nq:\", qlis, \"depth:\", depth, \"success\", A)\n", + " result_list.append(A)" + ] + }, + { + "cell_type": "code", + "execution_count": 16, + "metadata": {}, + "outputs": [], + "source": [ + "result_list1 = [i / 5 for i in result_list]\n", + "result_list2 = np.reshape(result_list1, [4, 2])" + ] + }, + { + "cell_type": "code", + "execution_count": 17, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "Text(0, 0.5, 'Depth')" + ] + }, + "execution_count": 17, + "metadata": {}, + "output_type": "execute_result" + }, + { + "data": { + "image/png": "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", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "fig, ax = plt.subplots()\n", + "Z = np.transpose(result_list2)\n", + "X = list(range(2, 6))\n", + "Y = list(range(1, 3))\n", + "p = ax.pcolor(X, Y, Z, vmin=Z.min(), vmax=Z.max(), cmap=\"rainbow\")\n", + "cb = fig.colorbar(p, ax=ax)\n", + "plt.xlabel(\"Nqubit\")\n", + "plt.ylabel(\"Depth\")" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3.8.13 ('tf')", + "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.8.13" + }, + "orig_nbformat": 4, + "vscode": { + "interpreter": { + "hash": "4d2770c334f3778740193ba4b3686745ae5c2e3ee10c8c0d673798cf1c2fcefe" + } + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/docs/source/tutorials/classical_shadows.ipynb b/docs/source/tutorials/classical_shadows.ipynb new file mode 100644 index 00000000..e6776482 --- /dev/null +++ b/docs/source/tutorials/classical_shadows.ipynb @@ -0,0 +1,795 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "source": [ + "# Classical Shadows in Pauli Basis" + ], + "metadata": { + "collapsed": false + } + }, + { + "cell_type": "markdown", + "source": [ + "## Overview" + ], + "metadata": { + "collapsed": false + } + }, + { + "cell_type": "markdown", + "source": [ + "[Classical shadows](https://www.nature.com/articles/s41567-020-0932-7) formalism is an efficient method to estimate multiple observables. In this tutorial, we will show how to use the ``shadows`` module in ``TensorCircuit`` to implement classic shadows in Pauli basis." + ], + "metadata": { + "collapsed": false + } + }, + { + "cell_type": "markdown", + "source": [ + "Let's first briefly review the classical shadows in Pauli basis. For an $n$-qubit quantum state $\\rho$, we randomly perform Pauli projection measurement on each qubit and obtain a snapshot like $\\{1,-1,-1,1,\\cdots,1,-1\\}$. This process is equivalent to apply a random unitary $U_i$ to $\\rho$ and measure in computational basis to obtain $|b_i\\rangle=|s_{i1}\\cdots s_{in}\\rangle,\\ s_{ij}\\in\\{0,1\\}$:\n", + "$$\n", + "\\rho\\rightarrow U_i\\rho U_i^{\\dagger}\\xrightarrow{measure}|b_i\\rangle\\langle b_i|,\n", + "$$\n", + "where $U_i=\\bigotimes_{j=1}^{n}u_{ij}$, $u_{ij}\\in\\{H, HS^{\\dagger}, \\mathbb{I}\\}$ correspond to the projection measurements of Pauli $X$, $Y$, $Z$ respectively. Then we reverse the operation to get the equivalent measurement result on $\\rho$:\n", + "$$\n", + "\\rho\\xrightarrow{measure}U_i^{\\dagger}|b_i\\rangle\\langle b_i| U_i.\n", + "$$\n", + "Moreover, we perform $N$ random measurements and view their average as a quantum channel:\n", + "$$\n", + "\\mathbb{E}\\left[U_i^{\\dagger}|b_i\\rangle\\langle b_i|U_i\\right]=\\mathcal{M}(\\rho),\n", + "$$\n", + "we can invert the channel to get the approximation of $\\rho$:\n", + "$$\n", + "\\rho=\\mathbb{E}\\left[\\mathcal{M}^{-1}(U_i^{\\dagger}|b_i\\rangle\\langle b_i|U_i)\\right].\n", + "$$\n", + "We call each $\\rho_i=\\mathcal{M}^{-1}(U_i^{\\dagger}|b_i\\rangle\\langle b_i|U_i)$ a shadow snapshot state and their ensemble $S(\\rho;N)=\\{\\rho_i|i=1,\\cdots,N\\}$ classical shadows." + ], + "metadata": { + "collapsed": false + } + }, + { + "cell_type": "markdown", + "source": [ + "In Pauli basis, we have a simple expression of $\\mathcal{M}^{-1}$:\n", + "$$\n", + "\\begin{split}\n", + " \\rho_i&=\\mathcal{M}^{-1}(U_i^{\\dagger}|b_i\\rangle\\langle b_i|U_i)=\\bigotimes_{j=1}^{n}\\left(3u_{ij}^{\\dagger}|s_{ij}\\rangle\\langle s_{ij}|u_{ij}-\\mathbb{I}\\right),\\\\\n", + " \\rho&=\\frac{1}{N}\\sum_{i=1}^{N}\\rho_i\\ .\n", + "\\end{split}\n", + "$$\n", + "For an observable Pauli string $O=\\bigotimes_{j=1}^{n}P_j,\\ P_j\\in\\{\\mathbb{I}, X, Y, Z\\}$, we can directly use $\\rho$ to calculate $\\langle O\\rangle=\\text{Tr}(O\\rho)$. In practice, we will divide the classical shadows into $K$ parts to calculate the expectation values independently and take the median to avoid the influence of outliers:\n", + "$$\n", + "\\langle O\\rangle=\\text{median}\\{\\langle O_{(1)}\\rangle,\\cdots,\\langle O_{(K)}\\rangle\\},\n", + "$$\n", + "where\n", + "$$\n", + "\\begin{split}\n", + " \\langle O_{(k)}\\rangle&=\\frac{1}{\\lceil N/K\\rceil}\\sum_{i=(k-1)\\lceil N/K\\rceil+1}^{k\\lceil N/K\\rceil}\\text{Tr}\\left[\\bigotimes_{j=1}^{n}P_j(3u_{ij}^{\\dagger}|s_{ij}\\rangle\\langle s_{ij}|u_{ij}-\\mathbb{I})\\right]\\\\\n", + " &=\\frac{1}{\\lceil N/K\\rceil}\\sum_{i=(k-1)\\lceil N/K\\rceil+1}^{k\\lceil N/K\\rceil}\\prod_{j=1}^n\\text{Tr}\\left[3P_j u_{ij}^{\\dagger}|s_{ij}\\rangle\\langle s_{ij}|u_{ij}\\right].\n", + "\\end{split}\n", + "$$" + ], + "metadata": { + "collapsed": false + } + }, + { + "cell_type": "markdown", + "source": [ + "## Setup" + ], + "metadata": { + "collapsed": false + } + }, + { + "cell_type": "code", + "execution_count": 13, + "outputs": [ + { + "data": { + "text/plain": "('complex128', 'float64')" + }, + "execution_count": 13, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "import tensorcircuit as tc\n", + "from tensorcircuit import shadows\n", + "import numpy as np\n", + "from functools import partial\n", + "import time\n", + "import matplotlib.pyplot as plt\n", + "\n", + "tc.set_backend(\"jax\")\n", + "tc.set_dtype(\"complex128\")" + ], + "metadata": { + "collapsed": false, + "ExecuteTime": { + "end_time": "2023-08-09T04:51:21.649753Z", + "start_time": "2023-08-09T04:51:21.598273400Z" + } + } + }, + { + "cell_type": "markdown", + "source": [ + "## Construct the Classical Shadow Snapshots" + ], + "metadata": { + "collapsed": false + } + }, + { + "cell_type": "markdown", + "source": [ + "We first set the number of qubits $n$ and the number of repeated measurements $r$ on each Pauli string. Then from the target observable Pauli strings $\\{O_i|i=1,\\cdots,M\\}$ (0, 1, 2, and 3 correspond to $\\mathbb{I}$, $X$, $Y$, and $Z$, respectively), the error $\\epsilon$ and the rate of failure $\\delta$, we can use ``shadow_bound`` function to get the total number of snapshots $N$ and the number of equal parts $K$ to split the shadow snapshot states to compute the median of means:\n", + "$$\n", + "\\begin{split}\n", + " K&=2\\log(2M/\\delta),\\\\\n", + " N&=K\\frac{34}{\\epsilon^2}\\max_{1\\le i\\le M}\\left\\|O_i-\\frac{\\text{Tr}(O_i)}{2^n}\\mathbb{I}\\right\\|^2_{\\text{shadow}}=K\\frac{34}{\\epsilon^2}3^{\\max_{1\\le i\\le M}k_i},\n", + "\\end{split}\n", + "$$\n", + "where $k_i$ is the number of nontrivial Pauli matrices in $O_i$. Please refer to the Theorem S1 and Lemma S3 in [Huang, Kueng and Preskill (2020)](https://www.nature.com/articles/s41567-020-0932-7) for the details of proof. It should be noted that ``shadow_bound`` has a certain degree of overestimation of $N$, and so many measurements are not really needed in practice. Moreover, ``shadow_bound`` is not jitable and no need to jit." + ], + "metadata": { + "collapsed": false + } + }, + { + "cell_type": "code", + "execution_count": 14, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "N: 489600\tK: 16\tnumber of Pauli strings: 97920\n" + ] + } + ], + "source": [ + "n, r = 8, 5\n", + "ps = [\n", + " [1, 0, 0, 0, 0, 0, 0, 2],\n", + " [0, 3, 0, 0, 0, 0, 1, 0],\n", + " [0, 0, 2, 0, 0, 3, 0, 0],\n", + " [0, 0, 0, 1, 2, 0, 0, 0],\n", + " [0, 0, 0, 3, 1, 0, 0, 0],\n", + " [0, 0, 0, 0, 0, 3, 0, 0],\n", + " [0, 0, 0, 0, 0, 0, 2, 0],\n", + " [3, 0, 0, 0, 0, 0, 0, 1],\n", + " [0, 2, 0, 0, 0, 0, 3, 0],\n", + " [0, 0, 1, 0, 0, 2, 0, 0],\n", + "]\n", + "\n", + "epsilon, delta = 0.1, 0.01\n", + "N, K = shadows.shadow_bound(ps, epsilon, delta)\n", + "nps = N // r # number of random selected Pauli strings\n", + "print(f\"N: {N}\\tK: {K}\\tnumber of Pauli strings: {nps}\")" + ], + "metadata": { + "collapsed": false, + "ExecuteTime": { + "end_time": "2023-08-09T04:51:21.707924900Z", + "start_time": "2023-08-09T04:51:21.604342400Z" + } + } + }, + { + "cell_type": "markdown", + "source": [ + "Then we use random quantum circuit to generate an entangled state." + ], + "metadata": { + "collapsed": false + } + }, + { + "cell_type": "code", + "execution_count": 15, + "outputs": [], + "source": [ + "nlayers = 10\n", + "thetas = 2 * np.random.rand(nlayers, n) - 1\n", + "\n", + "c = tc.Circuit(n)\n", + "for i in range(n):\n", + " c.H(i)\n", + "for i in range(nlayers):\n", + " for j in range(n):\n", + " c.cnot(j, (j + 1) % n)\n", + " for j in range(n):\n", + " c.rz(j, theta=thetas[i, j] * np.pi)\n", + "\n", + "psi = c.state()" + ], + "metadata": { + "collapsed": false, + "ExecuteTime": { + "end_time": "2023-08-09T04:51:21.814604700Z", + "start_time": "2023-08-09T04:51:21.615066400Z" + } + } + }, + { + "cell_type": "markdown", + "source": [ + "We randomly generate Pauli strings. Since the function after just-in-time (jit) compilation does not support random sampling, we need to generate all random states in advance, that is, variable ``status``." + ], + "metadata": { + "collapsed": false + } + }, + { + "cell_type": "code", + "execution_count": 16, + "outputs": [], + "source": [ + "pauli_strings = tc.backend.convert_to_tensor(np.random.randint(1, 4, size=(nps, n)))\n", + "status = tc.backend.convert_to_tensor(np.random.rand(nps, r))" + ], + "metadata": { + "collapsed": false, + "ExecuteTime": { + "end_time": "2023-08-09T04:51:21.815119500Z", + "start_time": "2023-08-09T04:51:21.813574500Z" + } + } + }, + { + "cell_type": "markdown", + "source": [ + "If ``measurement_only=True`` (default ``False``), the outputs of ``shadow_snapshots`` are snapshot bit strings $\\{b_i=s_{i1}\\cdots s_{in}\\ |i=1,\\cdots,N,\\ s_{ij}\\in\\{0,1\\}\\}$, otherwise the outputs are snapshot states $\\{u_{ij}^{\\dagger}|s_{ij}\\rangle\\langle s_{ij}| u_{ij}\\ |i=1,\\cdots,N,\\ j=1,\\cdots,n\\}$. If you only need to generate one batch of snapshots or generate multiple batches of snapshots with different ``nps`` or ``r``, jit cannot provide speedup. Jit will only accelerate when the same shape of snapshots are generated multiple times." + ], + "metadata": { + "collapsed": false + } + }, + { + "cell_type": "code", + "execution_count": 17, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "shape of snapshot states: (97920, 5, 8, 2, 2)\n" + ] + } + ], + "source": [ + "@partial(tc.backend.jit, static_argnums=(3,))\n", + "def shadow_ss(psi, pauli_strings, status, measurement_only=False):\n", + " return shadows.shadow_snapshots(\n", + " psi, pauli_strings, status, measurement_only=measurement_only\n", + " )\n", + "\n", + "\n", + "ss_states = shadow_ss(psi, pauli_strings, status) # jit is not necessary here\n", + "print(\"shape of snapshot states:\", ss_states.shape)" + ], + "metadata": { + "collapsed": false, + "ExecuteTime": { + "end_time": "2023-08-09T04:51:24.715816800Z", + "start_time": "2023-08-09T04:51:21.883673400Z" + } + } + }, + { + "cell_type": "markdown", + "source": [ + "## Estimate the Expectation Values of Observables" + ], + "metadata": { + "collapsed": false + } + }, + { + "cell_type": "markdown", + "source": [ + "Since the operation of taking the median is not jitable, the outputs of ``expectation_ps_shadows`` have $K$ values, and we need to take the median of them." + ], + "metadata": { + "collapsed": false + } + }, + { + "cell_type": "code", + "execution_count": 18, + "outputs": [], + "source": [ + "def shadow_expec(snapshots_states, ob):\n", + " return shadows.expectation_ps_shadow(snapshots_states, ps=ob, k=K)\n", + "\n", + "\n", + "sejit = tc.backend.jit(shadow_expec)" + ], + "metadata": { + "collapsed": false, + "ExecuteTime": { + "end_time": "2023-08-09T04:51:25.082792700Z", + "start_time": "2023-08-09T04:51:24.750909500Z" + } + } + }, + { + "cell_type": "markdown", + "source": [ + "It can be seen from the running time that every time the number of Pauli strings changes, ``shadow_expec`` will be recompiled, but for the same number of Pauli strings but different observables, ``shadow_expec`` will only be compiled once. In the end, the absolute errors given by classical shadows are much smaller than the $\\epsilon=0.1$ we set, so ``shadow_bound`` gives a very loose upper bound." + ], + "metadata": { + "collapsed": false + } + }, + { + "cell_type": "code", + "execution_count": 19, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "observable: No.0\tnumber of Pauli strings: 1000\ttime: 0.9454922676086426\n", + "observable: No.1\tnumber of Pauli strings: 1000\ttime: 0.001840353012084961\n", + "observable: No.2\tnumber of Pauli strings: 1000\ttime: 0.0014374256134033203\n", + "observable: No.3\tnumber of Pauli strings: 1000\ttime: 0.0013763904571533203\n", + "observable: No.4\tnumber of Pauli strings: 1000\ttime: 0.0013554096221923828\n", + "observable: No.5\tnumber of Pauli strings: 1000\ttime: 0.0013613700866699219\n", + "observable: No.6\tnumber of Pauli strings: 1000\ttime: 0.0013325214385986328\n", + "observable: No.7\tnumber of Pauli strings: 1000\ttime: 0.0013136863708496094\n", + "observable: No.8\tnumber of Pauli strings: 1000\ttime: 0.0013325214385986328\n", + "observable: No.9\tnumber of Pauli strings: 1000\ttime: 0.0013117790222167969\n", + "observable: No.0\tnumber of Pauli strings: 10000\ttime: 0.9969091415405273\n", + "observable: No.1\tnumber of Pauli strings: 10000\ttime: 0.012966394424438477\n", + "observable: No.2\tnumber of Pauli strings: 10000\ttime: 0.012765884399414062\n", + "observable: No.3\tnumber of Pauli strings: 10000\ttime: 0.012974739074707031\n", + "observable: No.4\tnumber of Pauli strings: 10000\ttime: 0.012665033340454102\n", + "observable: No.5\tnumber of Pauli strings: 10000\ttime: 0.012889623641967773\n", + "observable: No.6\tnumber of Pauli strings: 10000\ttime: 0.013180255889892578\n", + "observable: No.7\tnumber of Pauli strings: 10000\ttime: 0.012682914733886719\n", + "observable: No.8\tnumber of Pauli strings: 10000\ttime: 0.012678146362304688\n", + "observable: No.9\tnumber of Pauli strings: 10000\ttime: 0.012636423110961914\n", + "observable: No.0\tnumber of Pauli strings: 20000\ttime: 1.015674352645874\n", + "observable: No.1\tnumber of Pauli strings: 20000\ttime: 0.025749921798706055\n", + "observable: No.2\tnumber of Pauli strings: 20000\ttime: 0.02578139305114746\n", + "observable: No.3\tnumber of Pauli strings: 20000\ttime: 0.02524733543395996\n", + "observable: No.4\tnumber of Pauli strings: 20000\ttime: 0.025956153869628906\n", + "observable: No.5\tnumber of Pauli strings: 20000\ttime: 0.02669525146484375\n", + "observable: No.6\tnumber of Pauli strings: 20000\ttime: 0.026634931564331055\n", + "observable: No.7\tnumber of Pauli strings: 20000\ttime: 0.026463985443115234\n", + "observable: No.8\tnumber of Pauli strings: 20000\ttime: 0.0273745059967041\n", + "observable: No.9\tnumber of Pauli strings: 20000\ttime: 0.026821374893188477\n", + "observable: No.0\tnumber of Pauli strings: 30000\ttime: 1.0086262226104736\n", + "observable: No.1\tnumber of Pauli strings: 30000\ttime: 0.03922295570373535\n", + "observable: No.2\tnumber of Pauli strings: 30000\ttime: 0.038678884506225586\n", + "observable: No.3\tnumber of Pauli strings: 30000\ttime: 0.03868269920349121\n", + "observable: No.4\tnumber of Pauli strings: 30000\ttime: 0.04024958610534668\n", + "observable: No.5\tnumber of Pauli strings: 30000\ttime: 0.03927755355834961\n", + "observable: No.6\tnumber of Pauli strings: 30000\ttime: 0.039815664291381836\n", + "observable: No.7\tnumber of Pauli strings: 30000\ttime: 0.04002213478088379\n", + "observable: No.8\tnumber of Pauli strings: 30000\ttime: 0.03934764862060547\n", + "observable: No.9\tnumber of Pauli strings: 30000\ttime: 0.04060721397399902\n", + "observable: No.0\tnumber of Pauli strings: 40000\ttime: 1.2912404537200928\n", + "observable: No.1\tnumber of Pauli strings: 40000\ttime: 0.05326724052429199\n", + "observable: No.2\tnumber of Pauli strings: 40000\ttime: 0.05272030830383301\n", + "observable: No.3\tnumber of Pauli strings: 40000\ttime: 0.054486989974975586\n", + "observable: No.4\tnumber of Pauli strings: 40000\ttime: 0.0538792610168457\n", + "observable: No.5\tnumber of Pauli strings: 40000\ttime: 0.05555129051208496\n", + "observable: No.6\tnumber of Pauli strings: 40000\ttime: 0.05361533164978027\n", + "observable: No.7\tnumber of Pauli strings: 40000\ttime: 0.05325675010681152\n", + "observable: No.8\tnumber of Pauli strings: 40000\ttime: 0.05487465858459473\n", + "observable: No.9\tnumber of Pauli strings: 40000\ttime: 0.05441641807556152\n", + "observable: No.0\tnumber of Pauli strings: 50000\ttime: 0.999931812286377\n", + "observable: No.1\tnumber of Pauli strings: 50000\ttime: 0.06137228012084961\n", + "observable: No.2\tnumber of Pauli strings: 50000\ttime: 0.06159329414367676\n", + "observable: No.3\tnumber of Pauli strings: 50000\ttime: 0.06138134002685547\n", + "observable: No.4\tnumber of Pauli strings: 50000\ttime: 0.060491085052490234\n", + "observable: No.5\tnumber of Pauli strings: 50000\ttime: 0.06045842170715332\n", + "observable: No.6\tnumber of Pauli strings: 50000\ttime: 0.0629739761352539\n", + "observable: No.7\tnumber of Pauli strings: 50000\ttime: 0.06146860122680664\n", + "observable: No.8\tnumber of Pauli strings: 50000\ttime: 0.061437129974365234\n", + "observable: No.9\tnumber of Pauli strings: 50000\ttime: 0.061475515365600586\n", + "observable: No.0\tnumber of Pauli strings: 60000\ttime: 1.03033447265625\n", + "observable: No.1\tnumber of Pauli strings: 60000\ttime: 0.06811189651489258\n", + "observable: No.2\tnumber of Pauli strings: 60000\ttime: 0.06913161277770996\n", + "observable: No.3\tnumber of Pauli strings: 60000\ttime: 0.06945395469665527\n", + "observable: No.4\tnumber of Pauli strings: 60000\ttime: 0.06843304634094238\n", + "observable: No.5\tnumber of Pauli strings: 60000\ttime: 0.06950116157531738\n", + "observable: No.6\tnumber of Pauli strings: 60000\ttime: 0.07009696960449219\n", + "observable: No.7\tnumber of Pauli strings: 60000\ttime: 0.06856656074523926\n", + "observable: No.8\tnumber of Pauli strings: 60000\ttime: 0.06969141960144043\n", + "observable: No.9\tnumber of Pauli strings: 60000\ttime: 0.0678703784942627\n", + "observable: No.0\tnumber of Pauli strings: 70000\ttime: 1.0191996097564697\n", + "observable: No.1\tnumber of Pauli strings: 70000\ttime: 0.07705307006835938\n", + "observable: No.2\tnumber of Pauli strings: 70000\ttime: 0.07620859146118164\n", + "observable: No.3\tnumber of Pauli strings: 70000\ttime: 0.07670450210571289\n", + "observable: No.4\tnumber of Pauli strings: 70000\ttime: 0.0766746997833252\n", + "observable: No.5\tnumber of Pauli strings: 70000\ttime: 0.07552027702331543\n", + "observable: No.6\tnumber of Pauli strings: 70000\ttime: 0.07716488838195801\n", + "observable: No.7\tnumber of Pauli strings: 70000\ttime: 0.07647228240966797\n", + "observable: No.8\tnumber of Pauli strings: 70000\ttime: 0.07623863220214844\n", + "observable: No.9\tnumber of Pauli strings: 70000\ttime: 0.07545590400695801\n", + "observable: No.0\tnumber of Pauli strings: 80000\ttime: 1.0341267585754395\n", + "observable: No.1\tnumber of Pauli strings: 80000\ttime: 0.08658909797668457\n", + "observable: No.2\tnumber of Pauli strings: 80000\ttime: 0.08614206314086914\n", + "observable: No.3\tnumber of Pauli strings: 80000\ttime: 0.0857245922088623\n", + "observable: No.4\tnumber of Pauli strings: 80000\ttime: 0.08441877365112305\n", + "observable: No.5\tnumber of Pauli strings: 80000\ttime: 0.08495783805847168\n", + "observable: No.6\tnumber of Pauli strings: 80000\ttime: 0.08582544326782227\n", + "observable: No.7\tnumber of Pauli strings: 80000\ttime: 0.0861966609954834\n", + "observable: No.8\tnumber of Pauli strings: 80000\ttime: 0.08437252044677734\n", + "observable: No.9\tnumber of Pauli strings: 80000\ttime: 0.0852508544921875\n", + "observable: No.0\tnumber of Pauli strings: 90000\ttime: 1.031904697418213\n", + "observable: No.1\tnumber of Pauli strings: 90000\ttime: 0.09243512153625488\n", + "observable: No.2\tnumber of Pauli strings: 90000\ttime: 0.09180665016174316\n", + "observable: No.3\tnumber of Pauli strings: 90000\ttime: 0.09398865699768066\n", + "observable: No.4\tnumber of Pauli strings: 90000\ttime: 0.09126615524291992\n", + "observable: No.5\tnumber of Pauli strings: 90000\ttime: 0.09299516677856445\n", + "observable: No.6\tnumber of Pauli strings: 90000\ttime: 0.09152960777282715\n", + "observable: No.7\tnumber of Pauli strings: 90000\ttime: 0.09381914138793945\n", + "observable: No.8\tnumber of Pauli strings: 90000\ttime: 0.09076142311096191\n", + "observable: No.9\tnumber of Pauli strings: 90000\ttime: 0.08988714218139648\n", + "observable: No.0\tnumber of Pauli strings: 97920\ttime: 1.050750494003296\n", + "observable: No.1\tnumber of Pauli strings: 97920\ttime: 0.09993815422058105\n", + "observable: No.2\tnumber of Pauli strings: 97920\ttime: 0.10038924217224121\n", + "observable: No.3\tnumber of Pauli strings: 97920\ttime: 0.09895133972167969\n", + "observable: No.4\tnumber of Pauli strings: 97920\ttime: 0.09973955154418945\n", + "observable: No.5\tnumber of Pauli strings: 97920\ttime: 0.10170507431030273\n", + "observable: No.6\tnumber of Pauli strings: 97920\ttime: 0.09864187240600586\n", + "observable: No.7\tnumber of Pauli strings: 97920\ttime: 0.09945058822631836\n", + "observable: No.8\tnumber of Pauli strings: 97920\ttime: 0.09991574287414551\n", + "observable: No.9\tnumber of Pauli strings: 97920\ttime: 0.09802794456481934\n" + ] + }, + { + "data": { + "text/plain": "
", + "image/png": "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" + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "bz = 1000\n", + "\n", + "exact = []\n", + "for ob in ps:\n", + " exact.append(tc.backend.real(c.expectation_ps(ps=ob)))\n", + "exact = np.asarray(exact)[:, None]\n", + "\n", + "bzs, res = [], []\n", + "for i in range(bz, nps + bz, bz):\n", + " res.append([])\n", + " ss_states_batch = ss_states[:i]\n", + " bzs.append(ss_states_batch.shape[0])\n", + " t0 = time.time()\n", + " for j, ob in enumerate(ps):\n", + " expcs = sejit(ss_states_batch, ob)\n", + " res[-1].append(np.median(expcs))\n", + " t = time.time()\n", + " if i == bz or i % (bz * 10) == 0 or i >= nps:\n", + " print(\n", + " f\"observable: No.{j}\\tnumber of Pauli strings: {bzs[-1]}\\ttime: {t - t0}\"\n", + " )\n", + " t0 = t\n", + "res = np.asarray(res).T\n", + "bzs = np.asarray(bzs) * r\n", + "\n", + "error = np.abs(res - exact)\n", + "plt.figure()\n", + "plt.xlabel(\"N\")\n", + "plt.ylabel(\"Error\")\n", + "for i, y in enumerate(error):\n", + " plt.plot(bzs, y, label=f\"No.{i}\")\n", + "plt.legend()\n", + "plt.show()" + ], + "metadata": { + "collapsed": false, + "ExecuteTime": { + "end_time": "2023-08-09T04:53:57.091936Z", + "start_time": "2023-08-09T04:51:25.071809100Z" + } + } + }, + { + "cell_type": "markdown", + "source": [ + "## Estimate the Entanglement Entropy" + ], + "metadata": { + "collapsed": false + } + }, + { + "cell_type": "markdown", + "source": [ + "We can also use classical shadows to calculate entanglement entropy. ``entropy_shadow`` first reconstructs the reduced density matrix, then solves the eigenvalues and finally calculates the entanglement entropy from non-negative eigenvalues. Since the time and space complexity of reconstructing the density matrix is exponential with respect to the system size, this method is only efficient when the reduced system size is constant. ``entropy_shadow`` is jitable, but it will only accelerate when the reduced sub systems have the same shape." + ], + "metadata": { + "collapsed": false + } + }, + { + "cell_type": "code", + "execution_count": 20, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "sub: [1, 4]\ttime: 0.1788625717163086\texact: 1.3222456063743848\tshadow entropy: 1.323659494989336\n", + "sub: [2, 7]\ttime: 0.03922629356384277\texact: 1.313932933107046\tshadow entropy: 1.315937416629094\n", + "sub: [3, 6]\ttime: 0.03881263732910156\texact: 1.342032043092265\tshadow entropy: 1.3427963599754822\n", + "sub: [0, 5]\ttime: 0.03893852233886719\texact: 1.3458706554536102\tshadow entropy: 1.346507278883028\n", + "sub: [7, 0]\ttime: 0.039243221282958984\texact: 1.3496695271830874\tshadow entropy: 1.3504498597279848\n", + "sub: [1, 4, 7]\ttime: 0.2567908763885498\texact: 1.8741003653918944\tshadow entropy: 1.8777698830270055\n", + "sub: [0, 3, 6]\ttime: 0.11173439025878906\texact: 1.8633273061204374\tshadow entropy: 1.8638162138941512\n", + "sub: [5, 4, 2]\ttime: 0.11144137382507324\texact: 1.908653965480665\tshadow entropy: 1.9084333256925539\n", + "sub: [7, 2, 5]\ttime: 0.1119997501373291\texact: 1.8916979397866593\tshadow entropy: 1.88828077605345\n", + "sub: [0, 1, 2]\ttime: 0.11184382438659668\texact: 1.8717789129825904\tshadow entropy: 1.8724197548909916\n" + ] + } + ], + "source": [ + "subs = [\n", + " [1, 4],\n", + " [2, 7],\n", + " [3, 6],\n", + " [0, 5],\n", + " [7, 0],\n", + " [1, 4, 7],\n", + " [0, 3, 6],\n", + " [5, 4, 2],\n", + " [7, 2, 5],\n", + " [0, 1, 2],\n", + "]\n", + "\n", + "\n", + "@tc.backend.jit\n", + "def shadow_ent(snapshots_states, sub, alpha=2):\n", + " return shadows.entropy_shadow(snapshots_states, sub=sub, alpha=alpha)\n", + "\n", + "\n", + "for sub in subs:\n", + " exact_rdm = tc.quantum.reduced_density_matrix(\n", + " psi, cut=[i for i in range(n) if i not in sub]\n", + " )\n", + " exact_ent = tc.quantum.renyi_entropy(exact_rdm, k=2)\n", + "\n", + " t0 = time.time()\n", + " ent = shadow_ent(ss_states, sub)\n", + " t = time.time()\n", + " print(f\"sub: {sub}\\ttime: {t - t0}\\texact: {exact_ent}\\tshadow entropy: {ent}\")" + ], + "metadata": { + "collapsed": false, + "ExecuteTime": { + "end_time": "2023-08-09T04:53:58.139584Z", + "start_time": "2023-08-09T04:53:57.091936Z" + } + } + }, + { + "cell_type": "markdown", + "source": [ + "On the other hand, for the second order Renyi entropy, we have another method to calculate it in polynomial time by random measurements:\n", + "$$\n", + "\\begin{split}\n", + " R_A^2&=-\\log\\left(\\text{Tr}(\\rho_A^2)\\right),\\\\\n", + " \\text{Tr}(\\rho_A^2)&=2^k\\sum_{b,b'\\in\\{0,1\\}^k}(-2)^{-H(b,b')}\\overline{P(b)P(b')},\n", + "\\end{split}\n", + "$$\n", + "where $A$ is the $k$-d reduced system, $H(b,b')$ is the Hamming distance between $b$ and $b'$, $P(b)$ is the probability for measuring $\\rho_A$ and obtaining the outcomes $b$ thus we need a larger $r$ to obtain a good enough priori probability, and the overline means the average on all random selected Pauli strings. Please refer to [Brydges, et al. (2019)](https://www.science.org/doi/full/10.1126/science.aau4963) for more details. We can use ``renyi_entropy_2`` to implement this method, but it is not jitable because we need to build the dictionary based on the bit strings obtained by measurements, which is a dynamical process. Compared with ``entropy_shadow``, it cannot filter out non-negative eigenvalues, so the accuracy is slightly worse." + ], + "metadata": { + "collapsed": false + } + }, + { + "cell_type": "code", + "execution_count": 21, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "sub: [1, 4]\ttime: 3.794407606124878\tshadow entropy 2: 1.2866729353788704\n", + "sub: [2, 7]\ttime: 3.796651840209961\tshadow entropy 2: 1.2875279355654872\n", + "sub: [3, 6]\ttime: 3.760688066482544\tshadow entropy 2: 1.314993963087972\n", + "sub: [0, 5]\ttime: 3.765700101852417\tshadow entropy 2: 1.317198599992926\n", + "sub: [7, 0]\ttime: 3.784120559692383\tshadow entropy 2: 1.3218300427352758\n", + "sub: [1, 4, 7]\ttime: 3.859661817550659\tshadow entropy 2: 1.7701671972428563\n", + "sub: [0, 3, 6]\ttime: 3.874009132385254\tshadow entropy 2: 1.7684695179560657\n", + "sub: [5, 4, 2]\ttime: 3.831859827041626\tshadow entropy 2: 1.8037352454314826\n", + "sub: [7, 2, 5]\ttime: 3.824885368347168\tshadow entropy 2: 1.8006117836020135\n", + "sub: [0, 1, 2]\ttime: 3.8377578258514404\tshadow entropy 2: 1.782393800345501\n" + ] + } + ], + "source": [ + "nps, r = 1000, 500\n", + "\n", + "pauli_strings = tc.backend.convert_to_tensor(np.random.randint(1, 4, size=(nps, n)))\n", + "status = tc.backend.convert_to_tensor(np.random.rand(nps, r))\n", + "\n", + "snapshots = shadows.shadow_snapshots(psi, pauli_strings, status, measurement_only=True)\n", + "\n", + "t0 = time.time()\n", + "for sub in subs:\n", + " ent2 = shadows.renyi_entropy_2(snapshots, sub)\n", + "\n", + " t = time.time()\n", + " print(f\"sub: {sub}\\ttime: {t - t0}\\tshadow entropy 2: {ent2}\")\n", + " t0 = t" + ], + "metadata": { + "collapsed": false, + "ExecuteTime": { + "end_time": "2023-08-09T04:54:36.511330700Z", + "start_time": "2023-08-09T04:53:58.139683500Z" + } + } + }, + { + "cell_type": "markdown", + "source": [ + "## Reconstruct the Density Matrix" + ], + "metadata": { + "collapsed": false + } + }, + { + "cell_type": "markdown", + "source": [ + "We can use ``global_shadow_state``, ``global_shadow_state1`` or ``global_shadow_state2`` to reconstruct the density matrix. These three functions use different methods, but the results are exactly the same. All functions are jitable, but since we only use each of them once here, they are not wrapped. In terms of implementation details, ``global_shadow_state`` uses ``kron`` and is recommended, the other two use ``einsum``." + ], + "metadata": { + "collapsed": false + } + }, + { + "cell_type": "code", + "execution_count": 22, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "exact:\n", + " [[0.5+0.j 0. +0.j 0. +0.j 0.5+0.j]\n", + " [0. +0.j 0. +0.j 0. +0.j 0. +0.j]\n", + " [0. +0.j 0. +0.j 0. +0.j 0. +0.j]\n", + " [0.5+0.j 0. +0.j 0. +0.j 0.5+0.j]]\n", + "\n", + "shadow state: error: 0.02441655995426051\n", + " [[ 0.49141+0.j 0.00159+0.00219j 0.00378+0.00306j 0.5004 +0.0081j ]\n", + " [ 0.00159-0.00219j 0.0019 +0.j -0.00855+0.00297j -0.00567-0.00126j]\n", + " [ 0.00378-0.00306j -0.00855-0.00297j 0.00805+0.j -0.00273-0.00249j]\n", + " [ 0.5004 -0.0081j -0.00567+0.00126j -0.00273+0.00249j 0.49864+0.j ]]\n", + "\n", + "shadow state 1: error: 0.02441655995426051\n", + " [[ 0.49141+0.j 0.00159+0.00219j 0.00378+0.00306j 0.5004 +0.0081j ]\n", + " [ 0.00159-0.00219j 0.0019 +0.j -0.00855+0.00297j -0.00567-0.00126j]\n", + " [ 0.00378-0.00306j -0.00855-0.00297j 0.00805+0.j -0.00273-0.00249j]\n", + " [ 0.5004 -0.0081j -0.00567+0.00126j -0.00273+0.00249j 0.49864+0.j ]]\n", + "\n", + "shadow state 2: error: 0.02441655995426051\n", + " [[ 0.49141+0.j 0.00159+0.00219j 0.00378+0.00306j 0.5004 +0.0081j ]\n", + " [ 0.00159-0.00219j 0.0019 +0.j -0.00855+0.00297j -0.00567-0.00126j]\n", + " [ 0.00378-0.00306j -0.00855-0.00297j 0.00805+0.j -0.00273-0.00249j]\n", + " [ 0.5004 -0.0081j -0.00567+0.00126j -0.00273+0.00249j 0.49864+0.j ]]\n" + ] + } + ], + "source": [ + "n, nps, r = 2, 10000, 5\n", + "\n", + "c = tc.Circuit(n)\n", + "c.H(0)\n", + "c.cnot(0, 1)\n", + "\n", + "psi = c.state()\n", + "bell_state = psi[:, None] @ psi[None, :]\n", + "\n", + "pauli_strings = tc.backend.convert_to_tensor(np.random.randint(1, 4, size=(nps, n)))\n", + "status = tc.backend.convert_to_tensor(np.random.rand(nps, r))\n", + "lss_states = shadows.shadow_snapshots(psi, pauli_strings, status)\n", + "sdw_state = shadows.global_shadow_state(lss_states)\n", + "sdw_state1 = shadows.global_shadow_state1(lss_states)\n", + "sdw_state2 = shadows.global_shadow_state2(lss_states)\n", + "\n", + "print(\"exact:\\n\", bell_state)\n", + "print(f\"\\nshadow state: error: {np.linalg.norm(bell_state - sdw_state)}\\n\", sdw_state)\n", + "print(\n", + " f\"\\nshadow state 1: error: {np.linalg.norm(bell_state - sdw_state)}\\n\", sdw_state1\n", + ")\n", + "print(\n", + " f\"\\nshadow state 2: error: {np.linalg.norm(bell_state - sdw_state)}\\n\", sdw_state2\n", + ")" + ], + "metadata": { + "collapsed": false, + "ExecuteTime": { + "end_time": "2023-08-09T04:54:36.634162200Z", + "start_time": "2023-08-09T04:54:36.517005400Z" + } + } + }, + { + "cell_type": "code", + "execution_count": 23, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "OS info: Linux-5.4.119-1-tlinux4-0010.2-x86_64-with-glibc2.28\n", + "Python version: 3.10.11\n", + "Numpy version: 1.23.5\n", + "Scipy version: 1.11.0\n", + "Pandas version: 2.0.2\n", + "TensorNetwork version: 0.4.6\n", + "Cotengra version: 0.2.1.dev15+g120379e\n", + "TensorFlow version: 2.12.0\n", + "TensorFlow GPU: []\n", + "TensorFlow CUDA infos: {'cpu_compiler': '/dt9/usr/bin/gcc', 'cuda_compute_capabilities': ['sm_35', 'sm_50', 'sm_60', 'sm_70', 'sm_75', 'compute_80'], 'cuda_version': '11.8', 'cudnn_version': '8', 'is_cuda_build': True, 'is_rocm_build': False, 'is_tensorrt_build': True}\n", + "Jax version: 0.4.13\n", + "Jax installation doesn't support GPU\n", + "JaxLib version: 0.4.13\n", + "PyTorch version: 2.0.1\n", + "PyTorch GPU support: False\n", + "PyTorch GPUs: []\n", + "Cupy is not installed\n", + "Qiskit version: 0.24.1\n", + "Cirq version: 1.1.0\n", + "TensorCircuit version 0.10.0\n" + ] + } + ], + "source": [ + "tc.about()" + ], + "metadata": { + "collapsed": false, + "ExecuteTime": { + "end_time": "2023-08-09T04:54:36.638744200Z", + "start_time": "2023-08-09T04:54:36.634714500Z" + } + } + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 2 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython2", + "version": "2.7.6" + } + }, + "nbformat": 4, + "nbformat_minor": 0 +} diff --git a/docs/source/tutorials/dqas.ipynb b/docs/source/tutorials/dqas.ipynb index d66f75e6..d652a3f0 100644 --- a/docs/source/tutorials/dqas.ipynb +++ b/docs/source/tutorials/dqas.ipynb @@ -554,7 +554,6 @@ "nnp = K.implicit_randn(stddev=0.02, shape=[p, 6], dtype=rtype)\n", "stp = K.implicit_randn(stddev=0.02, shape=[p, 8], dtype=rtype)\n", "for epoch in range(epochs):\n", - "\n", " infd, (gnnp, gstp) = vag2(nnp, stp)\n", "\n", " nnp = network_opt.update(gnnp, nnp)\n", diff --git a/docs/source/tutorials/dqas_cn.ipynb b/docs/source/tutorials/dqas_cn.ipynb index 504dfc79..6306ef2b 100644 --- a/docs/source/tutorials/dqas_cn.ipynb +++ b/docs/source/tutorials/dqas_cn.ipynb @@ -550,7 +550,6 @@ "nnp = K.implicit_randn(stddev=0.02, shape=[p, 6], dtype=rtype)\n", "stp = K.implicit_randn(stddev=0.02, shape=[p, 8], dtype=rtype)\n", "for epoch in range(epochs):\n", - "\n", " infd, (gnnp, gstp) = vag2(nnp, stp)\n", "\n", " nnp = network_opt.update(gnnp, nnp)\n", diff --git a/docs/source/tutorials/error_mitigation.ipynb b/docs/source/tutorials/error_mitigation.ipynb new file mode 100644 index 00000000..abb35992 --- /dev/null +++ b/docs/source/tutorials/error_mitigation.ipynb @@ -0,0 +1,613 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# QEM Applications" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": {}, + "outputs": [], + "source": [ + "import tensorcircuit as tc\n", + "from tensorcircuit.results import qem\n", + "from tensorcircuit.results.qem import qem_methods, benchmark_circuits\n", + "from tensorcircuit.results.qem import (\n", + " zne_option,\n", + " apply_zne,\n", + " dd_option,\n", + " apply_dd,\n", + " apply_rc,\n", + ")\n", + "\n", + "from tensorcircuit.cloud import apis\n", + "from tensorcircuit.results import counts\n", + "from tensorcircuit.results.readout_mitigation import ReadoutMit\n", + "from tensorcircuit.compiler.qiskit_compiler import qiskit_compile\n", + "\n", + "from functools import partial\n", + "import numpy as np\n", + "import networkx as nx" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## ZNE" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "[[0, 1], [6, 2], [4, 0], [0, 4], [8, 4], [1, 5], [3, 7], [0, 3], [2, 0], [5, 1], [3, 0], [7, 3], [0, 2], [2, 6], [4, 8], [1, 0]]\n" + ] + } + ], + "source": [ + "d = apis.get_device(\"tianxuan_s1\")\n", + "couple_list = d.topology()\n", + "print(couple_list)" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": {}, + "outputs": [], + "source": [ + "mit = tc.results.rem.ReadoutMit(\n", + " \"tianxuan_s1?o=0\"\n", + ") # set ?o=0 for readout mitigation initialization\n", + "mit.cals_from_system(qubits=9, shots=4096)" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "metadata": {}, + "outputs": [ + { + "data": { + "image/png": "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", + "text/plain": [ + "
" + ] + }, + "execution_count": 5, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "graph = [(2, 0), (3, 7), (0, 3), (1, 5), (2, 6)]\n", + "weight = [1] * len(graph)\n", + "params = np.array([[1, 1], [1, 1]])\n", + "optimization_level = 2\n", + "\n", + "\n", + "c = benchmark_circuits.QAOA_circuit(graph, weight, params)\n", + "c.to_qiskit().draw(\"mpl\")" + ] + }, + { + "cell_type": "code", + "execution_count": 31, + "metadata": {}, + "outputs": [], + "source": [ + "c1, info = qiskit_compile(\n", + " c,\n", + " compiled_options={\n", + " \"basis_gates\": [\"h\", \"rz\", \"x\", \"y\", \"z\", \"cz\"],\n", + " \"optimization_level\": optimization_level,\n", + " # \"coupling_map\": d.topology(),\n", + " },\n", + ")" + ] + }, + { + "cell_type": "code", + "execution_count": 32, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "ideal 0.9400000000000001\n", + "raw 0.37379999999999997\n", + "read 0.4909275773352902\n" + ] + } + ], + "source": [ + "t = apis.submit_task(circuit=c, shots=10000, device=\"simulator:aer\")\n", + "raw_count = t.results(blocked=True)\n", + "ideal = sum(\n", + " [\n", + " -counts.expectation(raw_count, z=[graph[e][0], graph[e][1]]) * weight[e]\n", + " for e in range(len(graph))\n", + " ]\n", + ")\n", + "print(\"ideal\", ideal)\n", + "\n", + "t = apis.submit_task(\n", + " circuit=c1,\n", + " shots=10000,\n", + " device=d,\n", + " enable_qos_qubit_mapping=False,\n", + " enable_qos_gate_decomposition=False,\n", + ")\n", + "raw_count = t.results(blocked=True)\n", + "raw = sum(\n", + " [\n", + " -counts.expectation(raw_count, z=[graph[e][0], graph[e][1]]) * weight[e]\n", + " for e in range(len(graph))\n", + " ]\n", + ")\n", + "print(\"raw\", raw)\n", + "read = sum(\n", + " [\n", + " -mit.expectation(raw_count, z=[graph[e][0], graph[e][1]], **info) * weight[e]\n", + " for e in range(len(graph))\n", + " ]\n", + ")\n", + "print(\"read\", read)" + ] + }, + { + "cell_type": "code", + "execution_count": 33, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "zne 0.6603988839789069\n" + ] + }, + { + "data": { + "image/png": "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nDkTWlrvTtyOiYlBs2bNMHPmTN6AmoiIVApLHNF/aGtrY/r06bh69SpsbW0BAHl5eQgICICjoyPu3r0rcEIiIqJ3WOKICmFvb4+rV69i2rRp0NTUBPDuYtJGRkYCJyMiInqHJY7oI3R0dDBr1ixcvnwZ1tbWCAoKkt0dhIiISGgscUSf0bRpU0RFRWHAgAFy469evcLSpUuRl5cnUDIiIirLWOKIikBbWxsikUhuzNvbG7/88gtatWqFmzdvCpSMiIjKKpY4oi8QFxeHPXv2AACuXLmCxo0bY9GiRcjPzxc4GRERlRUscURfwNraGhcuXED9+vUBADk5OZgwYQLatGmD27dvC5yOiIjKApY4oi/UokULXL9+Hb/88ovso9aIiAjY29tj2bJlXJUjIqJixRJH9BX09fWxePFi/P3336hbty4A4O3bt/D19UW7du14XTkiIio2LHFEStCqVStER0fj559/lq3KXbhwARcuXBA4GRERlVYscURKYmBggGXLluHcuXOoXbs2evTogeHDhwsdi4iISiktoQMQlTZt27ZFTEwM3rx5U+CyJKdPn0bHjh2hocG/n4iI6OvwvyRExaBcuXIwNTWVGzt69ChcXFzg7OyMR48eCROMiIhKDZY4ohLw9u1b/PDDDwCAs2fPwsbGBuvWrYNUKhU4GRERqSuWOKISoKenh61bt6JmzZoAgMzMTPz444/o3Lkznjx5InA6IiJSRyxxRCWkU6dOiI2NxahRo2Rjp0+fhrW1NTZu3MhVOSIiUghLHFEJMjIywrp163Dy5EnUqFEDAJCRkQEvLy907doV//77r8AJiYhIXbDEEQmgc+fOiIuLw/fffy8bO3nyJHx8fARMRURE6oQljkggxsbG2LRpE44fP45q1arB0NAQy5YtEzoWERGpCV4njkhgXbt2RVxcHG7cuCE78eG9xMREmJmZFbjeHBEREVfiiFRAxYoV0b59e7mx9PR0ODo6olevXkhISBAmGBERqSyWOCIV5efnhydPnuDIkSOwsrLC77//zjNYiYhIhiWOSEV169YNVapUAQC8fv0aQ4cOxXfffYekpCSBkxERkSpgiSNSUb1790Z8fDwGDhwoGzt48CCsrKywa9cursoREZVxLHFEKszU1BQ7d+7Evn37ULlyZQDAq1evMGjQIPTv3x/JyckCJyQiIqGwxBGpgb59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", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "def execute(circuit):\n", + " t = apis.submit_task(\n", + " circuit=circuit,\n", + " shots=10000,\n", + " device=d,\n", + " enable_qos_qubit_mapping=False,\n", + " enable_qos_gate_decomposition=False,\n", + " )\n", + " count = t.results(blocked=True)\n", + " a = sum(\n", + " [\n", + " -mit.expectation(count, z=[graph[e][0], graph[e][1]], **info) * weight[e]\n", + " for e in range(len(graph))\n", + " ]\n", + " )\n", + " # a = sum([-counts.expectation(raw_count, z=[graph[e][0], graph[e][1]])*w[e] for e in range(len(graph))])\n", + " return a\n", + "\n", + "\n", + "random_state = np.random.RandomState(0)\n", + "noise_scaling_function = partial(\n", + " zne_option.scaling.fold_gates_at_random,\n", + " fidelities={\"single\": 1.0},\n", + " random_state=random_state,\n", + ")\n", + "factory = zne_option.inference.PolyFactory(scale_factors=[1, 3, 5], order=1)\n", + "# factory = zne_option.inference.ExpFactory(scale_factors=[1,1.5,2],asymptote=0.)\n", + "# factory = zne_option.inference.RichardsonFactory(scale_factors=[1,1.5,2])\n", + "# factory = zne_option.inference.AdaExpFactory(steps=5, asymptote=0.)\n", + "\n", + "result = apply_zne(\n", + " circuit=c1,\n", + " executor=execute,\n", + " factory=factory,\n", + " scale_noise=noise_scaling_function,\n", + " num_to_average=1,\n", + ")\n", + "_ = factory.plot_fit()\n", + "print(\"zne\", result)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## DD" + ] + }, + { + "cell_type": "code", + "execution_count": 35, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "[[18, 17], [7, 17], [3, 4], [14, 4], [4, 3], [5, 4], [16, 17], [12, 13], [14, 13], [3, 13], [8, 9], [10, 0], [9, 8], [19, 9], [1, 0], [19, 18], [17, 18], [8, 18], [13, 14], [15, 5], [6, 5], [15, 14], [18, 19], [4, 5], [5, 6], [18, 8], [4, 14], [14, 15], [0, 1], [11, 1], [5, 15], [1, 2], [10, 11], [2, 1], [11, 10], [0, 10], [1, 11], [9, 19], [16, 6], [6, 7], [15, 16], [7, 6], [16, 15], [12, 2], [6, 16], [3, 2], [12, 11], [17, 7], [8, 7], [17, 16], [2, 3], [11, 12], [13, 3], [2, 12], [13, 12], [7, 8]]\n" + ] + } + ], + "source": [ + "d = apis.get_device(\"tianshu_s1\")\n", + "couple_list = d.topology()\n", + "print(couple_list)" + ] + }, + { + "cell_type": "code", + "execution_count": 37, + "metadata": {}, + "outputs": [ + { + "data": { + "image/png": "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", + "text/plain": [ + "
" + ] + }, + "execution_count": 37, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "c, ideal = benchmark_circuits.ghz_circuit(8)\n", + "c.to_qiskit().draw(\"mpl\") # logical circuit" + ] + }, + { + "cell_type": "code", + "execution_count": 38, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "{'logical_physical_mapping': {0: 0, 1: 1, 2: 2, 3: 3, 4: 4, 5: 5, 6: 6, 7: 7}, 'positional_logical_mapping': {0: 0, 1: 1, 2: 2, 3: 3, 4: 4, 5: 5, 6: 6, 7: 7}}\n" + ] + } + ], + "source": [ + "c1, info = qiskit_compile(\n", + " c,\n", + " compiled_options={\n", + " \"basis_gates\": [\"h\", \"rz\", \"x\", \"y\", \"z\", \"cz\"],\n", + " \"optimization_level\": 2,\n", + " # \"coupling_map\": d.topology(),\n", + " },\n", + ")\n", + "\n", + "print(info)" + ] + }, + { + "cell_type": "code", + "execution_count": 40, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "ideal 1.0\n", + "raw 0.156\n" + ] + } + ], + "source": [ + "zpauli = [0, 6] # logical\n", + "\n", + "t = apis.submit_task(circuit=c, shots=10000, device=\"simulator:aer\")\n", + "raw_count = t.results(blocked=True)\n", + "ideal = counts.expectation(raw_count, z=zpauli)\n", + "print(\"ideal\", ideal)\n", + "\n", + "\n", + "t = apis.submit_task(\n", + " circuit=c1,\n", + " shots=10000,\n", + " device=d,\n", + " enable_qos_qubit_mapping=False,\n", + " enable_qos_gate_decomposition=False,\n", + ")\n", + "raw_count = t.results(blocked=True) # position_counts=logical_counts\n", + "raw = counts.expectation(raw_count, z=zpauli)\n", + "print(\"raw\", raw)" + ] + }, + { + "cell_type": "code", + "execution_count": 45, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "mit 0.1734\n" + ] + }, + { + "data": { + "image/png": "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W9KOt6QPWqUkSxqSJV14jvSp0VKKJ8+IvSHZJy2dJX1usvTCPmn3Iendo/9Y/o050rnDuvYJAOby5Nv4rl27JPV8G8qkSZP029/+Vg888IDa2to0YsQI/du//Ztuu+02paSkxLtUAAAA9FFGqnTpeV1f8Kah2dIV07q+vvVY1+wNS123LAEwH8HGR3z3u9/Vd7/73XiXBAAAAAAAouDJx+N8XLABAAAAAADM4ckZG5WVlW6XAAAAAAAAHODJGRsAAAAAACAxEGwAAAAAAABjEWwAAAAAAABjEWwAAAAAAABjEWwAAAAAAABjEWwAAAAAAABjEWwAAAAAAABjEWwAAAAAAABjEWwAAAAAAABjEWwAAAAAAABjEWwAAAAAAABjWbZt224XgVPZttQRcruK3kvxSZblXHu2bas1bNAASMpI8slyaBC83n8AALzOtHNByfnzwVj51mOSLcmSdP9St6s5Pc4HzRsDzofdk+x2AeiZZUmpHn51LMtSps+7A+D1/gMA4HVePxcE54MSY4De41YUAAAAAABgLIINAAAAAABgLIINAAAAAABgLIINAAAAAABgLIINAAAAAABgLIINAAAAAABgLIINAAAAAABgLIINAAAAAABgLIINAAAAAABgLIINAAAAAABgLIINAAAAAABgLIINAAAAAABgLIINAAAAAABgLIINAAAAAABgLIINAAAAAABgLIINAAAAAABgLIINAAAAAABgrGS3C0DPbFvqCLldRe+l+CTLcq4927bVGjZoACRlJPlkOTQIXu8/AACA15l2PSA5f00A9BbBRj/VEZJuKXe7it4rXSKlOrg3tYZDGly5xbkG46Bp7jxl+pwZBK/3HwAAwOtMux6QnL8mAHqLW1EAAAAAAICxCDYAAAAAAICxCDYAAAAAAICxCDYAAAAAAICxCDYAAAAAAICxCDYAAAAAAICxCDYAAAAAAICxCDYAAAAAAICxCDYAAAAAAICxCDYAAAAAAICxCDYAAAAAAICxCDYAAAAAAICxCDYAAAAAAICxEj7YCAQCKikp0fjx45WWlqaRI0dq5cqVamlp0XXXXSfLsrR27Vq3ywQAAAAQQ8GQtP8dyf77v+2PXRuJqOW49Eaj9NpBqdYvtXe6XRGcktDBxs6dOzVlyhTdc8898vv9mjhxojo7O7VmzRotWbJENTU1kqSioiJ3C42h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", + "text/plain": [ + "
" + ] + }, + "execution_count": 45, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "def execute(circuit):\n", + " t = apis.submit_task(\n", + " circuit=circuit,\n", + " shots=10000,\n", + " device=d,\n", + " enable_qos_qubit_mapping=False,\n", + " enable_qos_gate_decomposition=False,\n", + " )\n", + " count = t.results(blocked=True) # physical_counts\n", + "\n", + " n = len(info[\"logical_physical_mapping\"])\n", + " physical_qubits = [info[\"logical_physical_mapping\"][i] for i in range(n)]\n", + " count = counts.marginal_count(count, physical_qubits)\n", + "\n", + " # a = mit.expectation(count, z=zpauli,**info)\n", + " a = counts.expectation(count, z=zpauli)\n", + " return a\n", + "\n", + "\n", + "mitigated_result = apply_dd(\n", + " circuit=c1,\n", + " executor=execute,\n", + " rule=dd_option.rules.yy,\n", + " rule_args={\"spacing\": -1},\n", + " full_output=True,\n", + " ignore_idle_qubit=True,\n", + " fulldd=False,\n", + ")\n", + "\n", + "print(\"mit\", mitigated_result[0])\n", + "mitigated_result[1].to_qiskit().draw(\"mpl\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## RC" + ] + }, + { + "cell_type": "code", + "execution_count": 48, + "metadata": {}, + "outputs": [ + { + "data": { + "image/png": "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6t8W1pBY+PZcGJVy4XkHIsu27UCvmTNaR0cHqampWJZFe3s7SUlJpx/v7VhWUVER9913H9/61reYM2cOCQkJEclp2jGysrvN3CUh4nRNrf57LZ7vrh0TR8Mds2BcWmRzhdKge+vZuXMntm2Tm5t7usQAhg0bxk033dTrmAkTJpx3mojIQHb5CPj+Amhug+p90HoMbNu/xXZ1FowdEe2EF2/QFdmOHTuAvm9NJSLiNGOGw61XRTtFeKjILmCQ7XkVETGOI0/26Iu2yEREnGXQbZGdug+jiIg4w6DbIhMREWdRkYmIiNFUZCIiYjQVmYiIGE1FJiIiRlORiYiI0VRkIiJiNBWZiIgYTUUmIiJGU5GJiIjRBt33kZnCtuGEN9op+i8+Fiwr2ilEZDBSkYmIiNG0a1FERIymIhMREaOpyERExGgqMhERMZqKTEREjKYiExERo6nIRETEaCoyERExmopMRESMpiITERGjqchERMRoKjIRETGaikxERIymIhMREaOpyERExGgqMhERMZqKTEREjKYiExERo6nIRETEaCoyERExmopMRESMpiITERGjqchERMRoKjIRETGaikxERIz2/wHX/E5Spy81bwAAAABJRU5ErkJggg==", + "text/plain": [ + "
" + ] + }, + "execution_count": 48, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "c = tc.Circuit(5)\n", + "c.x(0)\n", + "c.h(2)\n", + "c.cz(0, 1)\n", + "c.cz(2, 3)\n", + "c.y(1)\n", + "c.h(2)\n", + "c.cz(0, 1)\n", + "c.cz(2, 3)\n", + "c.h(4)\n", + "c.cz(3, 4)\n", + "\n", + "c.to_qiskit().draw(\"mpl\")" + ] + }, + { + "cell_type": "code", + "execution_count": 50, + "metadata": {}, + "outputs": [ + { + "data": { + "image/png": "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", + "text/plain": [ + "
" + ] + }, + "execution_count": 50, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "circuit = qem.rc_circuit(c)\n", + "circuit = qem.washcircuit(circuit, qlist=list(range(circuit.circuit_param[\"nqubits\"])))\n", + "circuit.to_qiskit().draw(\"mpl\")" + ] + }, + { + "cell_type": "code", + "execution_count": 54, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "{'logical_physical_mapping': {0: 0, 1: 1, 2: 2, 3: 3, 4: 4}, 'positional_logical_mapping': {0: 0, 1: 1, 2: 2, 3: 3, 4: 4}}\n" + ] + }, + { + "data": { + "image/png": "iVBORw0KGgoAAAANSUhEUgAAAbIAAAFvCAYAAAAv007tAAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjYuMywgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy/P9b71AAAACXBIWXMAAA9hAAAPYQGoP6dpAAAn8UlEQVR4nO3de3xU9YH38c9JQsiFRCCgQRIuIQlyS1AiEi9oKHSliJe1VrcB7Zbdts+agq+yxG59Wes+u6WxdF0Rn12067rd51ma1kuLRLdq4yWyygYiihAJAkFyGXRIAklIgJk5zx+zIIEQMsNc8jv5vl+vvJQ55zf5wjkz3znXsWzbthERETFUTLQDiIiIXAwVmYiIGE1FJiIiRlORiYiI0VRkIiJiNBWZiIgYTUUmIiJGU5GJiIjRVGQiImI0FZmIiBhNRSYiIkZTkYmIiNFUZCIiYjQVmYiIGE1FJiIiRlORiYiI0VRkIiJiNBWZiIgYTUUmIiJGU5GJiIjRVGQiImI0FZmIiBhNRSYiIkZTkYmIiNFUZCIiYjQVmYiIGE1FJiIiRlORiYiI0VRkIiJiNBWZiIgYTUUmIiJGU5GJiIjRVGQiImI0FZmIiBhNRSYiIkZTkYmIiNFUZCIiYrS4aAeQ3tm2zTGfN9ox+i0pJhbLsqIdQ0QGIRXZAHXM52VE5evRjtFvrfMWkByr1UlEIk+7FkVExGgqMhERMZqKTEREjKYiExERo6nIRETEaCoyERExmopMRESMpiITERGjqchERMRoKjIRETGaikxERIymIhMREaOpyERExGiDosjcbjelpaVkZ2eTkJBAZmYmK1asoLOzk2XLlmFZFuvWrYt2TAmj7pOwbT+8WQtvfwI7G8Hri3YqiaTWTnjvU6jcBVW7od4Nth3tVBIKjv/eje3bt7Nw4UJcLhfJyclMnTqVpqYm1q5dy969e2lpaQFg5syZ0Q0aJt5nn8P3698Q+4MHiLn5qz2m2baNd9UPsWtriVu3FmvihOiEDKPWTnh9p7/Ejnt6TrskEa7NgaIpEO/4V8LgVe+GN3b6P7ycXVwZI2DuFXD1RNDX6ZnL0VtkbrebxYsX43K5WLlyJc3NzdTU1OByuSgrK6OiooLq6mosyyIvLy/accMiZmkxTBiPd/0z2F+4e0zzvfg77I92ELN0iSNLrKEF/uE/4b/2nFtiAEe64NWP4Kk3oPN45PNJ+G3bD2tfg48bet/6amiF/3gPyreAT1tnxnJ0kS1fvpyGhgZKSkpYs2YNKSkpp6eVlpaSn5+Px+NhwoQJpKamRjFp+FhDhhC3aiV0d+P9h388/bh9sAHfc7/CumIyMXfdGb2AYdLaCU+/Ce3dF573wGH4l7e1q9FpdjfD/3uvfwX1/l7YtD3skSRMHFtktbW1lJeXM2rUKFavXt3rPLNmzQIgPz+/x+P79+/n1ltvJSUlhREjRnDvvfdy+PDhsGcOFysnm5h7voG9rQZfxavYXi/ex9aAbRO7aiVWbGy0I4ZcZS0c7UeJnbLvC9jREL48Elm2DRs/CGwr661aaDsWvkwSPo4tsg0bNuDz+SguLmbYsGG9zpOYmAj0LLL29naKiopoaGhgw4YNPP3001RVVXHLLbfg85n7kT2m+M8gKwvvM7/E99Q/Y++uI+Zb92JlZkQ7WsgdPwnV+wIft7ku9FkkOurd0Nga2Bif7T8ZRMzj2EPclZWVABQVFZ13noYG/0fwM4vs6aefprGxkXfeeYdx48YBkJGRwbXXXsvGjRu5/fbbwxc6jKy4OOJW/QDP9x/At6kCa/o0Yv709mjHCovaZv9ZioHacwiOdkFqYugzSWTVHAhyXD0sdObhckdzbJEdOOBfk8ePH9/rdI/Hw+bNm4GeRbZp0yauv/760yUGUFhYSFZWFi+//HJQRVZQUIDL5QpojB0fD+ufCvh39Sk5GYYMAY8H6+oCrJjQbZDn5uRinTgRsue7GJMK7+PK2/8+qLGFNyzgiKs2xIkk0q755lNk5t8W8LimL46SkTE1DInkQtLT09m6dWtQYx1bZJ2dnQB0dXX1Or28vBy3201KSgoTJ048/fiuXbu46667zpl/2rRp7Nq1K6gsLpeLxsbGwAYlDGVIUL+td7Zt4/3F4+A5CeMy8f3Hr4m5cS7W5WNC8vxNzU3QPTBO/Rtx+IugxzY3HaS1OcBlJQNOZ0d7UONOnjge+GtVos6xRZaenk5rays1NTUUFhb2mNbc3MyqVasAyMvLwzrjApLW1laGDx9+zvONHDmS3bt3B50lUHZ8PMG/HZ/L97uN2B9+RMyf30dM4Rw8938f7y8eJ3ZNWY+/f7AuH3P5gNkii/O0Af7yDuTv5j3ZTWqCTdLYsWFKJpHiPXYoqHFdrQcYq+UfFcG8T57i2CKbP38+tbW1lJWVsWDBAnJzcwGorq5m6dKluN3+a6oicSF0MJvLnV4PIypfD8nvtxsb8T37HNbkXGK+8XWs2FhilhTj+9d/w/e7jcTeEfgumLPV7akjOXZgrE4+G376MrjbAyvowskJPLkvuA8rMrC0dsLf/j7wO3eU3F3Av/9Ip6+axrFnLZaWlpKWlsbBgweZNm0aM2bMICcnh9mzZ5OVlcW8efOAc0+9HzFiBG1tbec8X0tLCyNHjoxE9JCyfT68P/8H8PmIXfWD06fax3zj61i5OfiefQ67qTnKKUMrxoLrcgIfd31u6LNIdIxIhukBblglxcOVvR9SlwHOsUWWkZFBVVUVixYtIiEhgfr6ekaOHMn69eupqKigrs5/rvXZRTZlypRej4Xt2rWLKVOmRCR7KPmefxF7Vy0x9y3BOuMEFis2lti//gH4vHh/8Ti2w246d0Mu5FzW//kXTINxaeHLI5F359X+25D1hwUUF+pWZaZybJGBv5Q2bdpEe3s77e3tbNmyhe985zt0dnZSX19PTEwM06dP7zHmlltu4d133z19aj7Ali1b2Lt3L4sXL470X+Gi2J99hu/f/h1ryhXE3Pmn50y3JownZkkx9o6P8f1uYxQShk9cLPzFjTDl8gvPu2AafC3/wvOJWYYnQckCGJXS93xxsfDnc2Ga8y6pHDQs22kfxfthy5YtzJkzh8mTJ/PJJ5/0mHb06FFmzJjBqFGjePTRR+nu7qa0tJTRo0fz3nvvERPCU9b7EspjZJHQOm/BgDlGdiafDZ80weY9sKsRzlzZC7P9uyAzzNtjLAE44fFfV/Zunf/+m2f6kxn+9WB4UnSySWgMvHeeCNixYwdw7m5FgNTUVCorK1mxYgX33HMPcXFx3HLLLTz++OMRKzEJnRgLpo71/xw7AT/67ZfT7r4merkkcuLjYM4kuCYLOrrh4Re/nKaLn51BRdaLSZMmsWnTpkhGkghIivcfC7Hx/1cGF8uClEStA040KDcxLlRkIiJijkG5RXbqPowiImK+QblFJiIizqEiExERo6nIRETEaCoyERExmopMRESMpiITERGjqchERMRoKjIRETGaikxERIymIhMREaOpyERExGiD8l6LJkiKiaV13oJox+i3pJjYaEcQkUFKRTZAWZY1IL+oUkRkoNGuRRERMZqKTEREjKYiExERo6nIRETEaCoyERExmopMRESMpiITERGjqchERMRoKjIRETGaikxERIymIhMREaOpyERExGgqMhERMZqKTEREjKYiExERo6nIRETEaCoyERExmr6CWGQAsm2bYz5vtGMEJCkmFsuyoh1DBiEVmcgAdMznZUTl69GOEZDWeQtIjtVbikSedi2KiIjRVGQiImI0FZmIiBhNRSYiIkZTkYmIiNFUZCIiYjQVmYiIGE1FJiIiRlORiYiI0VRkIiJiNBWZiIgYTUUmIiJGU5GJiIjRBkWRud1uSktLyc7OJiEhgczMTFasWEFnZyfLli3DsizWrVsX7ZgSZj4f2P/z/3afc4pTeX1a9k7k+O9c2L59OwsXLsTlcpGcnMzUqVNpampi7dq17N27l5aWFgBmzpwZ3aASNp8dhnfr4IMDPR/ftB2uy4ERyVGJFTGev/177P96j9g1ZcRMn3bOdN/HO/H+9YNY1xYS9+OHopAwvHw+2NXkXwc+af7ycRuo2g0FEyExPmrxJAQs27Yd+wHF7XZz5ZVX0tDQwMqVK3nkkUdISUkB4LHHHuPBBx8kLi4Or9dLW1sbqampUU4soeTxwq/fh63155/HsuD2q+DGKyIWq186vZ6QfR+Z3XYEz3f+FyQlEvdPT2ElJnw5rbsbz/fuh2NdxD3zT1iXXBL07xmI30d25Bg88zY0tJx/nsQh8O25kJMeuVwSWo7etbh8+XIaGhooKSlhzZo1p0sMoLS0lPz8fDweDxMmTFCJOYzPB7/a3HeJAdg2vLQN3qyNSKyosIZfQuyKEmhqxvfLf+kxzfcv/wpNzcQ+8P2LKrGBqKMb1r3Rd4kBdJ2Ef34T9h6KTC4JPccWWW1tLeXl5YwaNYrVq1f3Os+sWbMAyM/PP/3YqeKbPXs2Q4cO1Ve3G+q9T+Gjg/2f//c10NwWtjhRF3PdtVjz5+Hb9Aq+D7YD4PvwI3wbN2HN/wox1xZGN2AYvLQNvmjv37xeHzz3rn8rXszj2CLbsGEDPp+P4uJihg0b1us8iYmJQM8i+/TTT3nhhRdIT0/n6quvjkhWCS3b9h8PCdTmIMaYJPavvgdpaXh/8Tj24cN4f/GPkJZG7P3fi3a0kDvaBds/C2xMe3dgH35k4HBskVVWVgJQVFR03nkaGhqAnkU2d+5cmpub2bhxI/Pnzw9vSAmLejc0Hwl8XPV+OO4JfZ6Bwho2jNgfPACff+E/LnboELErH8BKdt7ZLv+9z7+VFajNe0KfRcJvYB2ZDaEDB/ynqI0fP77X6R6Ph82bNwM9iywmJvTdXlBQgMvlCvnzSu8mzv4ms+58LOBxxz2QXzCXDve+MKQKjB0fD+ufCvnzxhRchf21hfheeZWYry0kZtZVIXvu3JxcrBMnQvZ8F+Pqu59g/FV3Bjxu5z43GRkzQx9ILig9PZ2tW7cGNdaxRdbZ2QlAV1dXr9PLy8txu92kpKQwceLEsGZxuVw0NjaG9XfIl9I6jgU99nDrEdwDYVklDGVImJ7amnoFvPKq/78h1NTcBN3HQ/qcwco7GdzBLis2Xq9VAzm2yNLT02ltbaWmpobCwp4Hspubm1m1ahUAeXl5YT+hIz1d5/VGUuKQwPcp2baNZVlckhTH0LFjw5AqwDzx8XwR7RABunzM5QNmiyzW1/sH2As5eayFsQNg+Q9GF/M+6dgimz9/PrW1tZSVlbFgwQJyc3MBqK6uZunSpbjdbiAyF0IHu7kswTl2An7yIpwI4EO5ZVlMGAWf1m4LX7AAhPI6skip21M3YK4jq3PB//lj4ONuvWECv1zeEPpAElaOPdmjtLSUtLQ0Dh48yLRp05gxYwY5OTnMnj2brKws5s2bB/Q8PibOkBQPs4LYW3x9buizSHTkXAaXBnhpqAVcmx2WOBJmji2yjIwMqqqqWLRoEQkJCdTX1zNy5EjWr19PRUUFdXX+c61VZM40b6r/jg39lTEC8seFL49ElmXBLTMDG1OYDaNSLjyfDDwDYz9AmEyZMoVNmzad83hHRwf19fXExMQwffr0KCSTcBudAn95Ezz9FnSf7Hve9EvgL4tgSGwkkkmk5GXCnQXw4tYL3yg4LxPu1GWjxnJ0kZ3Pzp07sW2b3NxckpKSzpn+/PPPA7Br164ef54wYQIFBQWRCyoXJetSeOBP4D8/8l/o6jvr3SxhCMzOgpvz/LsjB4uYry4g5qsLoh0jIm6Y7N/Kev1j2NfL2TMjk/3z3DgZwnDljUTIoCyyHTt2AOffrXjXXXf1+uf77ruP5557LqzZJLTSL4Fv3eC/eez2z/x3fIiNgbRhMHM8DB2Ur4DBZcrl/p/GVtjV6L+3YnwcZI6EKWNUYE4wKF/GFyoyB38hwKB1SdLAu8O9RNbYEf4fcZ5B+VnkQkUmIiLmGJRbZKfuwygiIuYblFtkIiLiHCoyERExmopMRESMpiITERGjqchERMRoKjIRETGaikxERIymIhMREaOpyERExGgqMhERMZqKTEREjGbZutW7yIBj2zbHfN5oxwhIUkwslmVFO4YMQioyERExmnYtioiI0VRkIiJiNBWZiIgYTUUmIiJGU5GJiIjRVGQiImI0FZmIiBhNRSYiIkZTkYmIiNFUZCIiYjQVmYiIGE1FJiIiRlORiYiI0VRkIiJiNBWZiIgYTUUmIiJGU5GJiIjRVGQiImK0uGgHkN7ZNpzwRjtF/8XHgmVFO4VzmLb8QeuARI+KbIA64YUHy6Odov/K7oahWptCxrTlD1oHJHq0a1FERIymIhMREaOpyERExGgqMhERMZqKTEREjKYiExERo6nIRETEaCoyERExmopMRESMpiITERGjqchERMRoKjIRETGaikxERIw2KIrM7XZTWlpKdnY2CQkJZGZmsmLFCjo7O1m2bBmWZbFu3bpoxwyLhl1v8cQSi20Va847zxNLLH6/5pYIpoos24a9h+A3W+CXb8Oz78DGD+Dzo9FOFn5a/n4eL9TUw//dDM+8Bf/2LlTugo7uaCeTUHD8ly5s376dhQsX4nK5SE5OZurUqTQ1NbF27Vr27t1LS0sLADNnzoxuUAmL2ib4fQ24jpw7rXIXTE6Hr8+G0SmRzybhZ9vwzm54Yye0n1VaHxyAVz6EgolwxywYOiQ6GeXiOXqLzO12s3jxYlwuFytXrqS5uZmamhpcLhdlZWVUVFRQXV2NZVnk5eVFO66E2LZ6/6fv3krslN0u+Mc/QFNrpFJJpNg2vLgNXtp2bomd4vHB+3th3RvQdSKy+SR0HF1ky5cvp6GhgZKSEtasWUNKypcfu0tLS8nPz8fj8TBhwgRSU1OjmFRC7YAb/uM98NkXnrfzODz9lt7InObdOqja3b95D7bAv28Obx4JH8cWWW1tLeXl5YwaNYrVq1f3Os+sWbMAyM/PP/3Y888/z5133sn48eNJSkriiiuu4KGHHqKjoyMiucPFc+IYXe3uXn+cqLIWvL7+z992DKr3hy9PtA225e/1+XcnBmJXEzS0hCePhJdjj5Ft2LABn89HcXExw4YN63WexMREoGeRrVmzhnHjxvHTn/6UjIwMtm/fzqOPPsrbb7/NO++8Q0yMmd3//guP8P4Lj0Q7RkQcOQY7DgY+7t06uCEXLCv0maJtMC1/gI8b4EhX4OM274G7rwl9HgkvxxZZZWUlAEVFReedp6GhAehZZC+//DKjR48+/ecbb7yR0aNHU1xczLvvvsvcuXPDlDi8phd9h5xr7up12ks/WxDhNOG129W/XYpn+/wotHRCWu+fe4w2mJY/+E/yieQ4iS7HFtmBAwcAGD9+fK/TPR4Pmzf7d4qfWWRnltgpBQUFADQ2NgaVpaCgAJfLFdCY2CGJ3PF3e4L6fb0Znp7DuOnzQ/Z8Z8vNycF7MoiPwGEw6do/58rb/ndQY+cWfZUjzbtCnChwpi1/GFjrwDXF/0Rm3uKAx33R2kFGxhVhSCQXkp6eztatW4Ma69gi6+zsBKCrq/cXVnl5OW63m5SUFCZOnNjnc7355psATJkyJagsLpcr4BKMG5oU1O+KlqbmJjzHj0U7BgCXHAruAwdA48F9HLmI8aFi2vKHgbUOtLcdDmrcia72oD+wSvQ4tsjS09NpbW2lpqaGwsLCHtOam5tZtWoVAHl5eVh9HBRpbGzk4Ycf5uabbw76WrP09PSAx8QOSQzqd0XL5WMuHzCfxu0O/9a4bdt9Ltuzdbd/QcpQL8PGjg1XtH4zbfnDwFoHug/XBTXuSONHjB0Ay38wCuZ98hTHFtn8+fOpra2lrKyMBQsWkJubC0B1dTVLly7F7fafrdVXOXV0dHDbbbcRHx/Ps88+G3SWYDaXj3vgwfKgf2XE1e3Zw9ABtDatfQ32fRHYWRuL54zmnw8MjFMXTVv+MLDWge6T8MiL/n/HQDxa8idM+WlDeEJJ2Jh5Cl4/lJaWkpaWxsGDB5k2bRozZswgJyeH2bNnk5WVxbx584Cex8fO1NXVxeLFi9m/fz+vvfYaY8aMiWR8uUg3BniYIy4WCrPDk0UiL2EIXDMpsDGXpcJkvcyN5Ngiy8jIoKqqikWLFpGQkEB9fT0jR45k/fr1VFRUUFfn3/XQW5GdPHmSr3/962zdupVXX32VqVOnRjq+XKT8cVDUz0OaFrD0WhjpwLMVB7PFV8KkS/s3b/JQWHYjxDjw0ovBwLJtO4gTlc3W0dFBamoqlmXR3t5OUtKXB9Z9Ph/33HMPGzdu5JVXXjm95RZppu1aKrubAbNb6RTbhtd3wh92nP/i6OSh8M1CmDbADouYtvxhYK4DJzyw4X3/fRXPJ/0S+PZcuFQ39zHWAFvtImPnzp3Ytk1ubm6PEgO4//77+e1vf8sPf/hDkpKSeP/9909PmzRpUq+n58vAZFnw1elwbTb89z7YWt/znopLrvVvuQ2JjVpECbP4OLjverg5D/5rD+xqhC/av5z+V1+BnMuceRH8YOLYXYt92bFjB9D7bsVXX30VgJ/97GcUFhb2+KmoqIhoTgmNYQkwbyqUfs2/GxH8/y2YqBIbLC5L9d/h/qFbe64DuekqMScYlFtkfRVZfX19hNOIiMjF0BaZiIgYbVBukZ26D6OIiJhvUG6RiYiIc6jIRETEaCoyERExmopMRESMpiITERGjqchERMRoKjIRETGaikxERIymIhMREaOpyERExGgqMhERMdqgvNeiCeJj/V9UaIp4fR1KSJm2/EHrgESPimyAsqyB9227Ejla/iL9p12LIiJiNBWZiIgYTUUmIiJGU5GJiIjRVGQiImI0FZmIiBhNRSYiIkZTkYmIiNFUZCIiYjQVmYiIGE1FJiIiRlORiYiI0VRkIiJiNBWZiIgYTUUmIiJGU5GJiIjRVGQiImI0FZmIiBhNRSYiIkZTkYmIiNFUZCIiYjQVmYiIGE1FJiIiRlORiYiI0VRkIiJiNBWZiIgYTUUmIiJGU5GJiIjRVGQiImI0FZmIiBhNRSYiIkZTkYmIiNEGRZG53W5KS0vJzs4mISGBzMxMVqxYQWdnJ8uWLcOyLNatWxftmBJGPhsOuMH+nz/bfc4tTnTCA3tcg3cdsG1oaoWdDfBxAzS0+B9zgrhoBwi37du3s3DhQlwuF8nJyUydOpWmpibWrl3L3r17aWlpAWDmzJnRDSph0X0S3vsUNu8Bd3vPaWtfg+ty4crxEGNFJ5+EX0sHVNXBlr1w7ETPac9VwdzJkHVpdLJFgscL1fv9r4GGlp7TxgyH63Ng9iQYEhuVeCFh2bZTOvlcbrebK6+8koaGBlauXMkjjzxCSkoKAI899hgPPvggcXFxeL1e2traSE1NjXJiCaXWTlj/JriO9D1ffiYsuc7sF7L07tND8Mu3/R9o+nLLTJg/LSKRIqrrBDz7Duw51Pd8E0fBX9wEyUMjEivkHF1k3/zmN9mwYQMlJSU8+eST50yfOXMmH374IRMnTmTfvn1RSCjhcuw4PPEaHDrav/mvHA9Lr9OWmZM0tMCTr8NxT//mv7MAbpgc3kyR5PH6P8hdqMROmTgK/mq+mR/oHHuMrLa2lvLyckaNGsXq1at7nWfWrFkA5Ofnn36sqqqK+fPnM2bMGIYOHUpGRgZ33303tbW1EcktofHWJ/0vMYAPDviPn4hzvLSt/yUG8PsPoPN4+PJEWs2B/pcYwH63f/eriRxbZBs2bMDn81FcXMywYcN6nScxMRHoWWStra3MmDGDtWvX8tprr1FWVsbOnTspLCykoaEhItnl4ni8/uNigXq3LvRZJDqa22Dv54GN8Xjhvx20YyaY9XlznZkngDj2ZI/KykoAioqKzjvPqWI6s8huvfVWbr311h7zXX311UyePJkXXniBFStWhCGthNLuZmjvDnzcx43+T+SmHieQL1UHWUjV+6BoSmizRMOhI/DZ4cDHNR/x75LNTAt9pnBybJEdOHAAgPHjx/c63ePxsHnzZqBnkfUmLc2/VOPigvvnKigowOXSfqtIyZqzlKvu6H13cl9sG66+9iscPbQ7DKkkkmb/2TrGzbw94HH1TW1kZEwPfaAIuzTnBub+xYagxt75Z8to2vWHECe6sPT0dLZu3RrUWMcWWWdnJwBdXV29Ti8vL8ftdpOSksLEiRPPme71evH5fBw4cIC/+Zu/IT09nW984xtBZXG5XDQ2NgY1VgI3sq0t6LGHDrlo0bIyXtexY0GN8/l8jnitxgz/Iuixhw+7jfs3cGyRpaen09raSk1NDYWFhT2mNTc3s2rVKgDy8vKwrHNPVbvxxhtPb7FlZ2dTWVnJ6NGjg84ikRNPZ1DjfF4PqYkWiWPHhjiRRNyJtqCGdR91MdYByz8pzn/BnG3bvb6/9ebUvImxx6Pyb3Ax75OOPf1++fLlPPnkk2RmZvLGG2+Qm5sLQHV1NUuXLmXfvn2cPHmS+++/v9e7euzevZu2tjb279/Pz3/+cz7//HM2b97MuHHjIv1XkQB5ffDoS3A0wONkeZnw7bnhySSR5ToCP9sU+LjbZ8FNV4Q+TzQ8/gf/3WwCcflwWPU16Gf3DRiOPWuxtLSUtLQ0Dh48yLRp05gxYwY5OTnMnj2brKws5s2bB5z/+NjkyZO55ppruOeee/jjH/9Ie3s7jz32WCT/ChKk2BgozAl83PW5oc8i0ZF+CeRcFtiYIbEw+9yjDMa6PojXwHW55pUYOLjIMjIyqKqqYtGiRSQkJFBfX8/IkSNZv349FRUV1NX5z0290IkeAMOHDyc7O5tPPw3inG6JipuugDGX9H/+WRMCf+OTge2OWTA0gIMnt8+CJAedsXrVBJg8pv/zT7oUrskKW5ywcuyuxb50dHSQmpqKZVm0t7eTlJTU5/yff/45kyZN4t577+Wpp56KUEq5WEeO+e9s0NTW93xXjofiQogz8I4G0rd9n/tvUXX2PRbPdttVzjjt/mzdJ+Ff34HdFzhpetKlsGyuuUU+KItsy5YtzJkzh8mTJ/PJJ5/0mLZkyRKys7OZOXMmw4cPZ8+ePTz++OM0NzdTXV1NdnZ2lFJLMI6fhC37/BeHfn7WnT5yLvPvSsnL1K2pnKztmH/5v/8pdJxx547YGJg5zn9bqgmjopcv3Lw+2Fbvv9j5wFnXlmWM9O9SL5hg9gc5x5612JcdO3YAve9WnDNnDr/61a944okn6O7uJjMzk6KiIn70ox+d95o0GbiGDvHf3fyGXGhohfYuiImBtGEwOiXa6SQShif5bwp88wz/RcJdJyA+zn/n92EJ0U4XfrExMDvL/3P2STArbzbzmNjZVGRnKSkpoaSkJNKRJMwsCzJHRjuFRFNcrLO/rqU/0i8BC/93sVk4o8TAwSd79KWvIhMREbMMyi2yU/dhFBER8w3KLTIREXEOFZmIiBhNRSYiIkZTkYmIiNFUZCIiYjQVmYiIGE1FJiIiRlORiYiI0VRkIiJiNBWZiIgYTUUmIiJGU5GJiIjRVGQiImI0FZmIiBhNRSYiIkZTkYmIiNFUZCIiYjQVmYiIGE1FJiIiRlORiYiI0VRkIiJiNBWZiIgYTUUmIiJGU5GJiIjRVGQiImI0FZmIiBhNRSYiIkZTkYmIiNFUZCIiYjQVmYiIGC0u2gGkd7YNJ7zRTtF/8bFgWdFOISKDkYpsgDrhhQfLo52i/8ruhqFam0QkCrRrUUREjKYiExERo6nIRETEaCoyERExmopMRESMpiITERGjqchERMRoKjIRETGaikxERIymIhMREaOpyERExGgqMhERMZqKTEREjKYiExERozm+yNxuN6WlpWRnZ5OQkEBmZiYrVqygs7OTZcuWYVkW69ati3bMsGnY9RZPLLHYVrHmvPM8scTi92tuiWAqEYmkrhNQtRvWvAr2/zxmA2/VwrHj0UwWGo7+Bqnt27ezcOFCXC4XycnJTJ06laamJtauXcvevXtpaWkBYObMmdENKiISJlv2wgtb4YTn3Gm/q4GKD+G2q+D63MhnCxXHbpG53W4WL16My+Vi5cqVNDc3U1NTg8vloqysjIqKCqqrq7Esi7y8vGjHFREJuardsOH93kvslJNeeL4a/rgzcrlCzbFFtnz5choaGigpKWHNmjWkpKScnlZaWkp+fj4ej4cJEyaQmpoaxaQiIqH32WF4cWv/5395O+z9PGxxwsqRRVZbW0t5eTmjRo1i9erVvc4za9YsAPLz88/7PAsXLsSyLH7yk5+EI2ZEeU4co6vd3euPiDhPVd2Xx8P6653dYYkSdo48RrZhwwZ8Ph/FxcUMGzas13kSExOB8xfZb37zG7Zv3x6uiBH3/guP8P4Lj0Q7hohEQOdx+KA+8HE7DsKRY3BJUsgjhZUji6yyshKAoqKi887T0NAA9F5kR48e5YEHHmDNmjUsWbLkovMUFBTgcrkCGhM7JJE7/m7PRf/uU6YXfYeca+7qddpLP1tw0c+fm5OD92TXRT+PiFy8UROv4abvvRDwOJ8NX1m8FNfuN8OQqm/p6els3RrAvtAzOLLIDhw4AMD48eN7ne7xeNi8eTPQe5E99NBD5ObmUlxcHJIic7lcNDY2BjQmbmhoPxINT89h3PT5IX3OMzU1N+E5fixszy8i/ReX1hn02CPtXQG/X0WbI4uss9O/ELu6et9CKC8vx+12k5KSwsSJE3tM27p1K8888wzbtm0LWZ709PSAx8QOSQzZ74+Ey8dcri0ykQEiNTk+6LEpSXGMHTs2hGn6J5j3yVMcWWTp6em0trZSU1NDYWFhj2nNzc2sWrUKgLy8PCzLOj3N6/Xy3e9+l5KSEqZNmxayPMFsLh/3wIPlIYsQdnV79jDUkWuTiHk8XvjJ76CjO7BxCUPgvTd+bdxr2ZFnLc6f79+FVlZWRl1d3enHq6urKSoqwu32n6l39oXQ69at49ChQ444S1FEBq+4WCicFPi42VkYV2Lg0CIrLS0lLS2NgwcPMm3aNGbMmEFOTg6zZ88mKyuLefPmAT2Pj7ndbh5++GF+/OMf4/F4aGtro62tDYDu7m7a2trw+XzR+OuIiATsulxICmAPY8IQmDs5fHnCyZFFlpGRQVVVFYsWLSIhIYH6+npGjhzJ+vXrqaioOL2VdmaRNTQ00N7ezne/+11GjBhx+gf8W3YjRozgs88+i8rfR0QkUMOT4C9v6t8W1pBY+PZcGJVy4XkHIsu27UCvmTNaR0cHqampWJZFe3s7SUlJpx/v7VhWUVER9913H9/61reYM2cOCQkJEclp2jGysrvN3CUh4nRNrf57LZ7vrh0TR8Mds2BcWmRzhdKge+vZuXMntm2Tm5t7usQAhg0bxk033dTrmAkTJpx3mojIQHb5CPj+Amhug+p90HoMbNu/xXZ1FowdEe2EF2/QFdmOHTuAvm9NJSLiNGOGw61XRTtFeKjILmCQ7XkVETGOI0/26Iu2yEREnGXQbZGdug+jiIg4w6DbIhMREWdRkYmIiNFUZCIiYjQVmYiIGE1FJiIiRlORiYiI0VRkIiJiNBWZiIgYTUUmIiJGU5GJiIjRBt33kZnCtuGEN9op+i8+Fiwr2ilEZDBSkYmIiNG0a1FERIymIhMREaOpyERExGgqMhERMZqKTEREjKYiExERo6nIRETEaCoyERExmopMRESMpiITERGjqchERMRoKjIRETGaikxERIymIhMREaOpyERExGgqMhERMZqKTEREjKYiExERo6nIRETEaCoyERExmopMRESMpiITERGjqchERMRoKjIRETGaikxERIz2/wHX/E5Spy81bwAAAABJRU5ErkJggg==", + "text/plain": [ + "
" + ] + }, + "execution_count": 54, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "c1, info = qiskit_compile(\n", + " c,\n", + " compiled_options={\n", + " \"basis_gates\": [\"h\", \"rz\", \"x\", \"y\", \"z\", \"cz\"],\n", + " \"optimization_level\": 2,\n", + " # \"coupling_map\": d.topology(),\n", + " },\n", + ")\n", + "\n", + "print(info)\n", + "c1.to_qiskit().draw(\"mpl\")" + ] + }, + { + "cell_type": "code", + "execution_count": 57, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "ideal -1.0\n", + "raw -0.3894\n" + ] + } + ], + "source": [ + "zpauli = [1, 2] # logical\n", + "\n", + "t = apis.submit_task(circuit=c, shots=10000, device=\"simulator:aer\")\n", + "raw_count = t.results(blocked=True)\n", + "ideal = counts.expectation(raw_count, z=zpauli)\n", + "print(\"ideal\", ideal)\n", + "\n", + "\n", + "t = apis.submit_task(\n", + " circuit=c1,\n", + " shots=10000,\n", + " device=d,\n", + " enable_qos_qubit_mapping=False,\n", + " enable_qos_gate_decomposition=False,\n", + ")\n", + "raw_count = t.results(blocked=True) # position_counts=logical_counts\n", + "raw = counts.expectation(raw_count, z=zpauli)\n", + "print(\"raw\", raw)" + ] + }, + { + "cell_type": "code", + "execution_count": 58, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "mit -0.39233333333333337\n", + "mit -0.4132\n" + ] + } + ], + "source": [ + "def execute(circuit):\n", + " t = apis.submit_task(\n", + " circuit=circuit,\n", + " shots=10000,\n", + " device=d,\n", + " enable_qos_qubit_mapping=False,\n", + " enable_qos_gate_decomposition=False,\n", + " )\n", + " count = t.results(blocked=True) # physical_counts\n", + "\n", + " n = len(info[\"logical_physical_mapping\"])\n", + " physical_qubits = [info[\"logical_physical_mapping\"][i] for i in range(n)]\n", + " count = counts.marginal_count(count, physical_qubits)\n", + "\n", + " # a = mit.expectation(count, z=zpauli,**info)\n", + " a = counts.expectation(count, z=zpauli)\n", + " return a\n", + "\n", + "\n", + "mitigated_result = apply_rc(\n", + " circuit=c1, executor=execute, num_to_average=6, simplify=True\n", + ")\n", + "\n", + "print(\"mit\", mitigated_result[0])\n", + "\n", + "\n", + "mitigated_result = apply_rc(\n", + " circuit=c1, executor=execute, num_to_average=6, simplify=False\n", + ")\n", + "\n", + "print(\"mit\", mitigated_result[0])" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3.8.13 ('tf')", + "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.8.13" + }, + "orig_nbformat": 4, + "vscode": { + "interpreter": { + "hash": "4d2770c334f3778740193ba4b3686745ae5c2e3ee10c8c0d673798cf1c2fcefe" + } + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/docs/source/tutorials/imag_time_evo.ipynb b/docs/source/tutorials/imag_time_evo.ipynb new file mode 100644 index 00000000..f698e793 --- /dev/null +++ b/docs/source/tutorials/imag_time_evo.ipynb @@ -0,0 +1,1301 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "dc0db886", + "metadata": {}, + "source": [ + "# Solving the Ground State of Hamiltonian by Imaginary-time Evolution" + ] + }, + { + "cell_type": "markdown", + "id": "aecf6615", + "metadata": {}, + "source": [ + "## Overview" + ] + }, + { + "cell_type": "markdown", + "id": "b533d43e", + "metadata": {}, + "source": [ + "Imaginary-time evolution (IME) is a method to solve the ground state of the Hamiltonian, which is more efficient than naive gradient descent and will not fall into a local minimum.\n", + "\n", + "However, exact imaginary-time evolution takes exponentially more space and time to execute on a classical computer. On the other hand, it is non-unitary and also cannot be implemented on a quantum computer. Therefore, we consider the approximate imaginary-time evolution, also known as the quantum natural gradient (QNG), that is, at each step we adjust the parameters in the quantum circuit to realize the imaginary-time evolution in a very short time $\\tau$. Let the parameters in the circuit be $\\boldsymbol{\\theta}$ and the output of the circuit is $|\\psi\\rangle$, define\n", + "$$\n", + "\\begin{split}\n", + " |\\psi_\\tau\\rangle&=e^{-\\tau\\hat{H}}|\\psi\\rangle\\approx|\\psi\\rangle-\\tau\\hat{H}|\\psi\\rangle,\\\\\n", + " |\\psi'\\rangle&=|\\psi(\\boldsymbol{\\theta}-\\tau\\boldsymbol{\\delta})\\rangle\\approx|\\psi\\rangle-\\tau\\sum_j\\boldsymbol{\\delta}_j|\\partial_{\\boldsymbol{\\theta}_{j}}\\psi\\rangle,\n", + "\\end{split}\n", + "$$\n", + "and the overlap is\n", + "$$\n", + "O=\\sqrt{\\frac{\\langle\\psi_\\tau\\rangle|\\psi'\\rangle\\langle\\psi'|\\psi_\\tau\\rangle}{\\langle\\psi_\\tau\\rangle|\\psi_\\tau\\rangle\\langle\\psi'|\\psi'\\rangle}}.\n", + "$$\n", + "Let $\\partial|O|^2/\\partial\\boldsymbol{\\delta}=0$, we can get $\\boldsymbol{A\\delta=C}$, then the update method is as follows\n", + "$$\n", + "\\boldsymbol{\\theta}^{n+1}=\\boldsymbol{\\theta}^{n}-\\tau\\boldsymbol{\\delta}=\\boldsymbol{\\theta}^{n}-\\tau\\boldsymbol{A}^{-1}\\boldsymbol{C},\n", + "$$\n", + "where $\\boldsymbol{A}$ is the quantum Fisher information matrix and the matrix element\n", + "$$\n", + "\\boldsymbol{A}_{ij}=\\Re\\left[\\frac{\\langle\\partial_{\\boldsymbol{\\theta}_{i}}\\psi|\\partial_{\\boldsymbol{\\theta}_{j}}\\psi\\rangle}{\\langle\\psi|\\psi\\rangle}-\\frac{\\langle\\partial_{\\boldsymbol{\\theta}_{i}}\\psi|\\psi\\rangle}{\\langle\\psi|\\psi\\rangle}\\frac{\\langle\\psi|\\partial_{\\boldsymbol{\\theta}_{j}}\\psi\\rangle}{\\langle\\psi|\\psi\\rangle}\\right];\n", + "$$\n", + "$\\boldsymbol{C}$ is the gradient vector of energy versus parameters and the vector element\n", + "$$\n", + "\\boldsymbol{C}_j=\\Re\\left[\\frac{\\langle\\psi|\\hat{H}|\\partial_{\\boldsymbol{\\theta}_{j}}\\psi\\rangle}{\\langle\\psi|\\psi\\rangle}-\\frac{\\langle\\psi|\\hat{H}|\\psi\\rangle}{\\langle\\psi|\\psi\\rangle}\\frac{\\langle\\psi|\\partial_{\\boldsymbol{\\theta}_{j}}\\psi\\rangle}{\\langle\\psi|\\psi\\rangle}\\right].\n", + "$$\n", + "Since $|\\psi\\rangle$ is represented by quantum circuit and naturally normalized, the second term of $\\boldsymbol{C}$ vanishes. And because the global phase is not important, we can add a $U(1)$ gauge to make the second term of $\\boldsymbol{A}$ vanishes. Related theoretical work can refer to [Yuan, Endo, Zhao, Li and Benjamin](https://doi.org/10.22331/q-2019-10-07-191) and [Stokes, Izaac, Killoran and Carleo](https://doi.org/10.22331/q-2020-05-25-269), which will also show how to measure $\\boldsymbol{A}$ and $\\boldsymbol{C}$ in circuit." + ] + }, + { + "cell_type": "markdown", + "source": [ + "In fields other than quantum computation, equivalent forms of imaginary-time evolution are stochastic reconfiguration in variational Monte Carlo method and natural gradient method and Gauss-Newton method in classical optimization. In stochastic reconfiguration, $|\\psi\\rangle$ is not normalized, so $\\boldsymbol{A}$ and $\\boldsymbol{C}$ are the form shown above. In classical optimization, the second terms of $\\boldsymbol{A}$ and $\\boldsymbol{C}$ vanish automatically when the output is the classical probability distribution. Therefore, when $|\\psi\\rangle$ is similar to the classical probability distribution, that is, when there is no sign structure and phase information, the second term of $\\boldsymbol{A}$ will automatically vanish." + ], + "metadata": { + "collapsed": false + } + }, + { + "cell_type": "markdown", + "source": [ + "In this tutorial, we solve a classical Hamiltonian and a quantum Hamiltonian by the approximate imaginary-time evolution and demonstrate various forms of code implementation." + ], + "metadata": { + "collapsed": false + } + }, + { + "cell_type": "markdown", + "source": [ + "## Setup" + ], + "metadata": { + "collapsed": false + } + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b0def04d", + "metadata": {}, + "outputs": [], + "source": [ + "import tensorcircuit as tc\n", + "from tensorcircuit import experimental\n", + "import optax\n", + "import jax.numpy as jnp\n", + "import tensorflow as tf\n", + "import networkx as nx\n", + "import matplotlib.pyplot as plt\n", + "import numpy as np\n", + "import cotengra as ctg\n", + "import time\n", + "from functools import partial\n", + "from IPython.display import clear_output\n", + "\n", + "K = tc.set_backend(\"jax\")\n", + "tc.set_dtype(\"complex128\")\n", + "\n", + "# cotengra package to speed up the calculation\n", + "opt_ctg = ctg.ReusableHyperOptimizer(\n", + " methods=[\"greedy\", \"kahypar\"],\n", + " parallel=True,\n", + " minimize=\"combo\",\n", + " max_time=20,\n", + " max_repeats=128,\n", + " progbar=True,\n", + ")\n", + "\n", + "tc.set_contractor(\"custom\", optimizer=opt_ctg, preprocessing=True)" + ] + }, + { + "cell_type": "markdown", + "source": [ + "## Classical Hamiltonian" + ], + "metadata": { + "collapsed": false + } + }, + { + "cell_type": "markdown", + "source": [ + "The classical Hamiltonian is the Hamiltonian of NAE3SAT, which can be solved by QAOA. Please refer to the tutorial of [QAOA for NAE3SAT](qaoa_nae3sat.ipynb) for details.\n", + "\n", + "Let the set of clauses in the NAE3SAT be $\\mathcal{C}$. In each clause, there are three literals and each literal is represented by a spin. Spins up ($s=1$, $\\text{bit}=0$) and down ($s=-1$, $\\text{bit}=1$) represent false and true respectively. For the clause $(s_i,\\ s_j,\\ s_k)\\in\\mathcal{C}$, $s_i,\\ s_j,\\ s_k$ cannot be 1 or -1 at the same time. The Hamiltonian of the NAE3SAT is as follows\n", + "$$\n", + "\\begin{split}\n", + " \\hat{H}_C&=\\sum_{(i,j,k)\\in\\mathcal{C}}\\left[(s_i+s_j+s_k)^2-1\\right]/2\\\\\n", + " &=\\sum_{(i,j,k)\\in\\mathcal{C}}(s_i s_j+s_j s_k+s_k s_i)+|\\mathcal{C}|,\n", + "\\end{split}\n", + "$$\n", + "where $|\\mathcal{C}|$ is the number of clauses in $\\mathcal{C}$. When all clauses are true, $\\hat{H}_C$ takes the minimum value 0, and the corresponding bit string is the solution of the NAE3SAT." + ], + "metadata": { + "collapsed": false + } + }, + { + "cell_type": "markdown", + "id": "6e407437", + "metadata": {}, + "source": [ + "### Define the Hamiltonian" + ] + }, + { + "cell_type": "markdown", + "source": [ + "We select the hard problem in the tutorial of [QAOA for NAE3SAT](qaoa_nae3sat.ipynb). We first construct the graph by the clauses." + ], + "metadata": { + "collapsed": false + } + }, + { + "cell_type": "code", + "execution_count": 3, + "id": "f1532831", + "metadata": { + "ExecuteTime": { + "end_time": "2023-07-13T02:33:37.851074700Z", + "start_time": "2023-07-13T02:33:37.537921300Z" + } + }, + "outputs": [ + { + "data": { + "text/plain": "
", + "image/png": "iVBORw0KGgoAAAANSUhEUgAAAgQAAAGFCAYAAACCBut2AAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjcuMSwgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy/bCgiHAAAACXBIWXMAAA9hAAAPYQGoP6dpAAEAAElEQVR4nOyddVhUXdfG7wm6QQEl7QBbUcSmFLDz8bFRsbAD48HuwsbuDmxFUQwMsAULwSBUVLpjZn1/+DIfSM0MM2Ds33VxAefss/Y6zDD7PmuvvTaHiAgMBoPBYDD+arjl7QCDwWAwGIzyhwkCBoPBYDAYTBAwGAwGg8FggoDBYDAYDAaYIGAwGAwGgwEmCBgMBoPBYIAJAgaDwWAwGAD44jQSCoX49OkTNDQ0wOFw5O0Tg8FgMBgMGUBESE5ORuXKlcHlFh8DEEsQfPr0CSYmJjJxjsFgMBgMRtkSGRkJY2PjYtuIJQg0NDREBjU1NUvvGYPBYDAYDLmTlJQEExMT0TheHGIJgtxpAk1NTSYIGAwGg8H4zRBnup8lFTIYDAaDwWCCgMFgMBgMBhMEDAaDwWAwwAQBg8FgMBgMMEHAYDAYDAYDTBAwGAwGg8EAEwQMBoPBYDDABAGDwWAwGAwwQcBgMBgMBgNMEDAYDAaDwQATBAwGg8FgMMAEAYPBYDAYDDBBwGAwGAwGA0wQMBgMBoPBABMEDAaDwWAwwAQBg8FgMBgMAPzydoDB+N1IzczBh9hUZOUIocjnwlxPDWpK7F+JwWD83rBPMQZDDN7GJONgYAT833xFRFwaKM85DgBTXVW0r6WPf5ubooaBRnm5yWAwGFLDISIqqVFSUhK0tLSQmJgITU3NsvCLwfgliIxLwyyfYNwO+w4elwOBsOh/l9zzratXwJLu9WCiq1qGnjIYDEZBJBm/WQ4Bg1EERx5EwG7tTdx9FwsAxYqBvOfvvouF3dqbOPIgQu4+MhgMhqxgUwYMRiFs9H+LVVdCpbpWICQIhASPU8H4npKJce1ryNg7BoPBkD0sQsBg/MSRBxFSi4GfWXUlFEdZpIDBYPwGsAgBg5GHyLg0zD37osjzwqx0JAWeQuanN8j6HAphRgr0nCZCvb5dkdd4nn2BltUqsJwCBoPxS8MiBAxGHmb5BCOnmFwBYVoSEu8cRnZsJBT0q4hlM0dImOUTLCsXGQwGQy6wCAGD8T/exiTjdtj3Ytvw1HVhPG4/eOo6yPz8Fl/2TirRrkBIuB32HWFfk1Fdny1JZDAYvyYsQsBg/I+DgRHgcTnFtuHwFcBT15HYNo/LwYH7LJeAwWD8ujBBwGD8D/83X0tcWigtAiHBP/SrXGwzGAyGLGCCgMEAkJKZg4i4NLn2ERGbhtTMHLn2wWAwGNLCBAGDAeBjbCrkExv4fwjAh9hUOffCYDAY0sEEAYMBICtH+Ef1w2AwGJLCBAGDAUCRXzb/CmXVD4PBYEgK+3RiMACY66mh+PUFpYfzv34YDAbjV4QJAgYDgJoSH6ZyriQoSIzBqOFDceDAAcTExMi1LwaDwZAUVpiIwfgf7WvpY3/gxxKXHiY9OgdhRioEKXEAgPSwIOQk/yhopNmkM7jKBaMAXA5QU0uAF/df4MCBAwCAhg0bwtHREY6OjrCxsYGioqKM74jBYDDEh0NEJSZXS7KfMoPxu/I2Jhn2XrdKbBe1eRgESYXXFDAatRN8bYNCz/lNaoPq+hr4+vUrrl69Cl9fX1y5cgUxMTFQU1ND+/bt4eDgAEdHR9SoUQMcjrwnMRgMxp+OJOM3EwQMRh4G7gzE3Xexsi1QREI0N9fG0VGtC5wSCoV4/vw5rly5Al9fXwQEBCArKwvm5uZwdHSEg4MDbG1toaWlJTt/GAzGXwMTBAyGlETGpcFu7U1kymx5IIFyspF5ag42r1yIHj16FNs6NTUVN27cgK+vL3x9fREaGgoej4cWLVqIpheaNGkCHo8nI/8YDMafjCTjN0sqZDDyYKKriqH1ZLkSgINp7c3Qol4N9OzZE//88w++fy96AyU1NTU4Oztj/fr1ePPmDd6/f4/NmzfDwMAAq1evRvPmzaGvr4++ffti165diIqKkqGvDAbjb4ZFCBiMPNy8eROdO3eGaaeRSKnavtT2Uu8ehiU3GmfPnsXZs2fh7u4OBQUFeHt7o3v37hLZysnJQWBgoGh6ISgoCEQECwsLUe5BmzZtoKKiUmq/GQzGnwGbMmAwpODChQvo1asXbGxscPr0aZx/FYc5Ps+RnSMAhyfBghwSQkmBjwVdLGCUGQEnJyfUq1cPFy9eRHp6OkaNGoUzZ86gX79+2LBhAypUqCCVv3FxcfDz8xNNL0RHR0NZWRlt2rQR5R9YWFiw5EQG4y+GCQIGQ0KOHDmCgQMHwtnZGUeOHIGysjLS0tJQt1lrqLUfjlQNU/C4nGKTDXPPp79/jI2DbNDbqQMAICgoCI6OjqhevTp8fX2ho6ODQ4cOlSpa8DNEhFevXonEwc2bN5GRkQEjIyNR9MDOzg56enql6ofBYPxeMEHAYEjAtm3bMGrUKPz777/YtWsXFBQUAABz587FsmXLEBISAmga4mBgBPxDvyIiNi3/RkhEUMxOQv+2DfBvc1P829kWABAYGCh6On/y5Ans7e1hbGyMq1evomLFivj8+TNGjRqFs2fPol+/fti4caPMBuz09HTcvn1bNL0QEhICDoeDpk2bipITmzdvLrpXBoPxZyLR+E1ikJiYSAAoMTFRnOYMxm/DihUrCACNHTuWBAKB6Hh4eDgpKSnRrFmzClzz6FkIKehXoT3nblBIdAJN9ZhNhoaGJBQKiYjI39+fANDRo0fzXRcSEkIGBgZUp04d+vTpExERCYVC2r9/P+no6JC+vj6dOnVKLvcZFRVFu3btor59+5Kuri4BIE1NTerevTtt2bKF3r17J5d+GQxG+SLJ+M0EAeOvRCgU0uzZswkAzZo1SzSY59KlSxcyNjamlJSUAtfeuXOHANDLly+JiMjHx4cAUGRkpKiNi4sLVa1alTIzM/Nd++bNGzIyMqLq1avTx48fRcc/ffpEnTt3JgD0zz//0Pfv32V5u/nIycmhoKAgWrhwIbVq1Yp4PB4BoBo1atC4cePo3LlzlJycLLf+GQxG2cEEAYNRDAKBgMaNG0cAaPny5QXOX7hwgQDQsWPHCr3+3LlzBIA+f/5MRETR0dEEgE6ePClqExISQlwul9avX1/g+vDwcDI3NyczMzMKDw8XHS+raMHPJCQkkI+PD40aNYqqVKlCAEhBQYHatWtHS5cupcePH+eLnjAYjN8HJggYjCLIzs6mgQMHEofDIW9v7wLnMzIyqHr16tShQ4cCUYNc9u7dSwAoIyNDdMzY2JhmzJiRr52rqytVqFCBEhISCtiIiIigGjVqkJGREb1+/TrfubKMFvyMUCik0NBQ2rBhA3Xu3JnU1NQIAOnr69OAAQNo37599OXLlzLzh8FglA4mCBiMQkhPT6du3boRn8+nQ4cOFdpmyZIlxOfz6cWLF0XaWbt2LampqeU71qNHD2rfvn2+Y1FRUaSiolJoHgLRj4G/bt26ZGBgQMHBwfnO5Y0WGBgYkI+Pjxh3KHsyMzPJ39+fPDw8qFGjRgSAAFDDhg1pxowZdO3atXzCiMFg/FowQcBg/ERycjLZ2tqSsrIynTt3rtA2kZGRpKqqSpMmTSrWlqenJ5mYmOQ7tmzZMtLQ0KCcnJx8x2fNmkUqKioUFRVVqK2vX79SgwYNSE9Pjx4/flzgfN5oQf/+/cs0WlAYMTExdODAARo4cCAZGBgQAFJTUyNnZ2dav349vXnzpsjICoPBKHuYIGAw8hAXF0ctWrQgdXV18vf3L7Jd3759ycDAoNAQf17GjRtH9evXz3fs+vXrBKBAZCEhIYEqVKhArq6uRdqLjY2lZs2akba2Nt2/f7/AeaFQSPv27SNtbe1yjRb8jEAgoCdPntDy5cupQ4cOpKioSADI3NycRo4cSSdPnizxb8lgMOQLEwQMxv/48uUL1a9fn3R1dSkoKKjIdrkD+t69e0u02b9/f2rXrl2+Y4mJicThcGj37t0F2q9fv564XC6FhIQUaTMhIYFatmxJGhoadPv27ULb/GrRgp9JSUmh8+fPk7u7O9WsWZMAEI/HIxsbG5o/fz7dv3+/QASFwWDIF0nGb1aYiPHH8vHjR9jb2yMlJQVXrlyBpaVloe2ys7PRqFEjaGlp4fbt2+Byi9/zq1OnTlBVVcXJkyfzHbewsEDbtm2xefPmfMezsrJQt25d1KlTB+fOnSvSbkpKCrp06YLAwECcPXsWtra2BdoQEQ4cOIDx48dDSUkJW7duRdeuXYv1t7z48OGDqDDStWvXkJiYCF1dXdjZ2YlKKxsbG5e3mwzGHw0rTMT463n9+jWZmJhQlSpVKCwsrNi2a9euJQ6HU+gcfmE0b96chg8fXuD4kCFDqEmTJoVec/ToUQJQ7JQFEVFaWho5OjqSkpISXbhwoch20dHR5OLi8stGC34mOzubAgICyNPTk5o3b04cDocAUN26dWnSpEl0+fJlSktLK283GYw/DjZlwPirefLkCVWsWJHq1q1bZDJfLp8/fyZNTU0aPXq02PZr1KhB06ZNK3B88+bNxOfzKT09vcA5oVBIzZo1o2bNmpWYdJeRkUFdu3YlBQWFYmsR/JxbcPr0abHvobyJjY2lo0eP0rBhw8jIyIgAkJKSEtnb29OqVasoODiYJScyGDKACQLGX0tAQABpaWlRkyZN6Nu3byW2Hzx4MOnp6VFsbKzYfejp6dHSpUsLHH/48CEBoHv37hV63Y0bNwotaVwYWVlZ1KdPH+LxeHT48OFi2+aNFvz7778S3cuvgFAopBcvXtCaNWvI0dGRlJWVCQAZGRnR0KFD6fDhw798BITB+FVhgoDxV+Lr60uqqqrUpk0bsd6ruSWIt27dKnYfAoGAuFxuoddkZmaSkpISrVu3rsjriyppXBi5RZS4XC7t2bOn2LZCoZD27t37W0YLfiYtLY2uXLlCU6ZMIUtLSwJAHA6HmjVrRnPmzKHbt29TVlZWebvJYPwWMEHA+Os4efIkKSoqkpOTk1hz0Tk5OdSoUSNq3LixRJnvCQkJBICOHz9e6PkWLVrQv//+W+T1uSWNixMNeREIBDRixAgCUGhlxZ+Jjo4mZ2fn3zZaUBi5GzP169eP9PT0RBszdevWjW3MxGCUABMEjL+KPXv2EJfLpb59+4r15E1E5O3tTQDo7t27EvX17t07AkB+fn6Fnh8/fjzVqFGjWBvFlTQuDKFQSO7u7gSAvLy8xGr/p0QLfqa4jZnGjh1LZ8+eZRszMRh5YIKA8dewbt06AkAjRowQ+0n/+/fvpKurS0OGDJG4v9w8gaJWJBw4cIAAFPtkXlJJ48IQCoU0ffp0AlBo/kJh5I0WDBgw4I+IFvxMSRszPXr0iG3MxPirYYKA8ccjFAppwYIFBICmTp0qUUb6qFGjSFNTU6pNeq5cuUIA6MOHD4WeDw0NJQDk6+tbrJ2SShoXhlAopLlz5xIAmjt3rlj3LBQKac+ePaSlpUWGhoZ05swZsfv73ShuY6Z///2XbczE+CthgoDxRyMUCmny5MkEgBYtWiSRGHj06BFxOByxQu+FkVtPICkpqUjftLW1aeHChcXaEaekcVEsXbqUAND06dPFvveoqKg/PlrwM8VtzDR9+nS2MRPjr4AJAsYfS05ODrm6uhIAWr9+vUTXCgQCsra2JktLS8rOzpaq/y1bthCPxyt2IHZwcKDOnTuXaEucksZF4eXlRQDI3d1d7JD4z9GCs2fPStzv70xhGzOpqqqSs7MzrVu3jl6/fs1qHzD+OJggYPyRZGZmUp8+fYjL5Yq158DP7NmzR6xqgcWxePFiqlixYrFt5syZQwYGBiUOLpmZmVStWjVycXGRypfcxMgRI0ZINE8eFRVFTk5Of1W04GeK2pjJzMyMbczE+KNggoDxx5GamkqdOnUiRUXFYqv3FUVCQgLp6+tTv379SuXHlClTqFatWsW2OXv2LAGgjx8/lmhP3JLGRZG7wmLgwIESRT3+9mjBzxS1MVPLli3ZxkyM3xomCBh/FAkJCdS6dWtSVVWlK1euSGVj4sSJpKamRpGRkaXyZdiwYWRtbV1sm8+fPxdbqyAvkpQ0LoojR44Qj8ej3r17S1ywh0ULCuf9+/e0detW6tGjB2lpaREA0tHRod69e9OOHTtK/T5iMMoKJggYfwzfvn2jxo0bk7a2Nt25c0cqGyEhIcTj8cRerlcc3bp1IycnpxLbmZqa0tSpU8WyKUlJ46I4deoUKSgoUNeuXSVOlBMKhbR7927S0tKiSpUq/fXRgp/Jzs6mO3fuiDZm4nK5bGMmxm8DEwSMP4KoqCiqU6cO6evr09OnT6WyIRQKqX379lSjRg2ZZJS3adOGBgwYUGK7Xr16UZs2bcS2K0lJ46K4cOECKSkpUceOHaUaoPJGCwYOHEhxcXFS+/InwzZmYvxOMEHA+O0JCwsjc3NzMjExoTdv3khtJ3eO/tKlSzLxy9LSksaPH19iuxUrVpCqqqrY8/qSljQuCj8/P1JVVaX27dtLVbGPRQsko6iNmSpXrkxDhgxhGzMxyh0mCBi/NcHBwWRoaEg1a9YUKzGvKJKTk8nY2Ji6du0qM9+MjIxo3rx5JbbLnQZ4/vy52LYlLWlcFLdu3SINDQ2ysbGR2lbeaMGgQYNYtEBMituYafbs2XTr1i22MROjTGGCgPHbEhgYSDo6OtSgQYNSV5WbOXMmKSkpyXTzGxUVFbGe4pOTk4nL5dKOHTvEti1NSeOiuH//Pmlra1PTpk2lThT8OVpw7ty5Uvv1t1HSxkzh4eHl7SLjD4cJAsZvyfXr10ldXZ2sra1L/UT65s0bUlBQoLlz58rGOfrx9AeA9u/fL1Z7S0tLGjlypER9SFPSuCgeP35Menp61KBBA/r69avUdiIjI6lTp04sWlBKitqYqXr16r/NxkwpGdkUEp1Ajz/GUUh0AqVkSFfgi1F2SDJ+c4iIUAJJSUnQ0tJCYmIiNDU1S2rOYEjMuXPn0Lt3b7Rp0wY+Pj5QU1OT2hYRwcnJCa9fv8bLly+hoqIiEx8/ffoEIyMjXLhwAU5OTiW2d3V1xePHj/HkyROx+0hMTET16tXRtWtX7NixozTuAgBevHgBW1tb6Orq4tq1a6hUqZJUdogIe/bswaRJk6Cqqopt27bBxcWl1P79zSQmJsLf3x++vr7w9fXF+/fvoaCgABsbGzg4OMDR0RENGzYEl8stVz/fxiTjYGAE/N98RURcGvIOGBwAprqqaF9LH/82N0UNA43ycpNRBJKM3+X7TmMwABw6dAjdu3eHk5MTzp07VyoxAPwQF5cvX8batWtlJgYAID4+HgCgq6srVnsrKysEBwcjLS1N7D60tLTg6emJ3bt348WLF1L5mRcLCwvcunULSUlJaNOmDSIjI6Wyw+FwMHToUISEhKBhw4bo3LkzBg8eLPqbMCRHS0sL3bp1w5YtWxAeHo7Q0FCsWbMGGhoaWLx4MZo0aYJKlSphwIAB2L9/P758+VKm/kXGpWHgzkDYe93C/sCP+PiTGAB+bA7xMS4N+wM/wt7rFgbuDERknPjvd8avBYsQMMoVb29vjBkzBoMGDcKOHTvA5/NLZS8jIwN169ZFjRo1cPnyZXA4HBl5Cty+fRtt2rTB69evUatWrRLbP3nyBI0bN0ZAQABsbGzE7icrKwt169ZFnTp1cO7cudK4LOLdu3ewtbUFAFy/fh1VqlSR2lZutGDixIlQV1fHtm3b4OzsLBM/GT/IysrC3bt3RdGD3ChTgwYN4OjoCEdHR9jY2EBJSUku/R95EIG5Z18gR0gQCEscIkTwuBzwuRzM72KBfs1M5eIbQzJYhIDxW7B8+XKMHj0a48aNw65du0otBgBg5cqViIqKwvr162UqBgAgLi4OgPgRAktLSygrKyMoKEiifhQVFbFkyRKcP38eN27ckNTNQqlatSpu3boFPp+P1q1bIzQ0VGpbudGCFy9eoH79+nBxcWHRAhmjqKiIdu3aYenSpXj8+DFiYmJw4MAB1K9fH3v37hVNAzk7O2P9+vV48+YNxHi2E4uN/m/hcSoYmTlCicQAAAiEhMwcITxOBWOj/1uZ+MMoO1iEgFHmEBFmz56NpUuX4r///sP8+fNlMnh/+PABderUwfjx47F8+XIZeJqf3bt3Y9iwYcjOzhZbvNjY2MDU1BSHDx+WqC8iQvPmzQEAgYGBMhM3nz9/hq2tLeLi4nDt2jVYWFiUyh6LFpQ9QqEQz58/x5UrV+Dr64uAgABkZWXBzMwMjo6OcHBwgK2tLbS1tSW2feRBBDxOBcvM1+U96qEvixSUKyxCwPhlEQqFGDt2LJYuXYpVq1ZhwYIFMhvspkyZAl1dXcyZM0cm9n4mPj4empqaEkUyrKysJI4QAD+ewleuXIkHDx7g+PHjEl9fFJUqVcKNGzdgYGCAdu3a4enTp6WyV1i0YMiQISxaIEe4XC4aNmyI6dOn49q1a4iLi8P58+fRpUsX3Lx5E7169UKFChVgY2ODBQsWIDAwEAKBoES7kXFpmHu26LyV7LhofDuzHFGbBiNiVU9EbxuFhIDDEGZnFHmN59kXLKfgN4IJAkaZkZ2djUGDBsHb2xvbtm3DlClTZGb7ypUrOHXqFFatWgUNDflkOsfFxUFHR0eia6ysrPDu3Tt8//5d4v7atm0LFxcXzJw5E1lZWRJfXxT6+vrw9/eHubk52rdvL5Vg+RljY2NcvHgRO3fuhI+PDywtLXHhwgUZeMsoCTU1NdHUwevXr/H+/Xts3rwZhoaGWLNmDVq0aIGKFSuiT58+2LlzJ6Kiogq1M8snGDlFTBHkJH3Dl72TkRn9BhqNXaBjNwJKRrWRGHAQ38+sLNK3HCFhlo/sIg4M+cIEAaNMyMjIQK9evXD06FEcPnwYI0aMkJntrKwsjB8/Hm3atEG/fv1kZvdn4uLixM4fyMXKygoA8ODBA6n6XLZsGT58+ABvb2+pri8KXV1d+Pn5oW7durCzs0NAQECpbXI4HAwbNgwhISH5ogUJCQmld5ghNubm5hg5ciROnjyJ79+/486dO3B3d0dERARGjhwJExMTWFhYYNKkSbh8+TLS0tLwNiYZt8O+F5kzkBriD2FmKvR7z4WWdW9oNOyICs4ToWbZAelhgRBkpBR6nUBIuB32HWFfk+V5ywwZwQQBQ+6kpKTA2dkZV65cwZkzZ9C3b1+Z2l+3bh3CwsKwYcMGmScS5kUaQVC1alXo6upK/RRuYWGBoUOHYuHChUhMTJTKRlFoaWnB19cXTZs2haOjI65duyYTuyYmJvmiBRYWFrh48aJMbDMkg8/no2XLlpg/fz7u37+Pb9++4ejRo2jRogWOHz+OTp06QVdXF71nrQenwKLC/0eY9SPsz1PTznecp64LcLjgcIueRuNxOThwP0Im98OQL0wQMORKXFwc7Ozs8ODBA/j6+opV0EcSPn36hAULFmDs2LGoX7++TG3/THx8vMSCgMPhSJ1HkMv8+fORmpqKFStWSG2jKNTV1XHhwgW0bt0azs7OuHTpkkzs5o0W1KtXD87Ozhg6dCiLFpQzurq6oqmDyMhIvHjxAkuXLkWqljkIRYtpZdN6AIDYi+uRFfMOOUnfkPrqFpKfXIRGk87gKioXea1ASPAP/Srze2HIHiYIGHLjy5cvaNeuHcLCwnD9+nW0adNG5n1Mnz4dKioqmD9/vsxt/4w0OQTA/ycWSrsszMjICJMmTcLatWsRHR0tlY3iUFFRwZkzZ+Do6IiuXbvi9OnTMrNtYmKCS5cuYceOHTh16hSLFvxCcDgc1K1bFyPGuCNHSbvYtipVm0Cr9QBkfHiKz7vHI3rzUHw/swIaTTpD167k6b+I2DSkZubIyHOGvGCCgCEXPnz4gFatWiE2Nha3bt1C06ZNZd7HrVu3cPDgQSxbtkyqJVaSIs2UAfBDEHz//h0fPnyQuu/p06dDTU0Nc+fOldpGcSgpKeHEiRPo1q2bKNdDVnA4HLi6urJowS/Kx9jUYiYL/h++lgGUTCyg23EcKnafBbX69ki6ewxJj0ounkUAPsSmltpXhnxhgoAhc16/fo3WrVuDiBAQEIC6devKvI+cnBy4u7vDysoKQ4YMkbn9wpBWEDRr1gwASjVtIOuSxoWhoKCAQ4cOoX///ujfvz/27t0rU/s/RwssLS1ZtOAXICtHWGKb1Jc3EXd5I/Q6jYdGw45QrdUSFZwmQK2eLRJu7IEgPUkm/TDKFyYIGDLl8ePHaN26NbS0tHD79u1SlcgtDm9vbwQHB2Pjxo1lsvmLQCBAYmKiVIJAX18f5ubmpV7e5+bmhipVqsDDw6NUdoqDz+djz549GDZsGIYMGYJt27bJ1H7eaIGlpSWLFvwCKPJL/v9JfnwRigZVwdeskO+4anUrUHYmsmLeyaQfRvnCXiGGzAgICED79u1RpUoV3Lx5E5UrV5ZLP1+/fsV///2H4cOHi56+5U1iYiKISKocAkD6AkV5kUdJ48LgcrnYunUrxo0bBzc3N6xbt07mfRQWLZBVQiNDPGJiYrBhwwaM+rdnifktgrQEEBV8wifh/woeCYsvfMQBYK5Xuk3LGPKHCQKGTPD19YWDgwMaNWqEa9euQU9PT259zZo1CxwOB0uWLJFbHz8j6T4GP2NlZYVHjx4hJ6d0iVW9e/dGs2bNMH36dJnVri8MLpeL9evXY9q0aZg4cSKWLVsm8z5+jhY4OTlh2LBhLFogJ759+4bdu3ejY8eO0NHRgaGhIcaPH4+AG9dAycWvAlDQqYysmHBkx+VPak19eRPgcKFQ0bzY6031VKGmVPq9Shjyhb1CjFJz4sQJ9O/fH46Ojjh27JhMtxz+maCgIOzcuRObNm1ChQoVSr5ARshCEKSnp+PFixdo0KCB1H7kljRu164djh8/jj59+khtS5y+li9fDlVVVcycORMZGRmYO3euzGs95EYLdu7cicmTJ+PKlSvYvn07OnXqJNN+/ja+f/8OPz8/HD16FAEBAfmqZWpqasLe3h49evSAUCjEuoB3yFCvAA6XV6gtzeY9kf7uEb4cmAGNJs7gqWgiLSwIGe8eQb2BA/gaRT8A8LgctK+pL/P7Y8getrkRo1Ts2rULI0aMQN++fbF3714oKCjIrS+hUIgWLVogOzsbDx8+BI9X+IeXPPD19UXHjh0RGRkJY2Njia9PTU2FpqYmvL29ZVKlsXPnznj58iVevXoFRUXFUtsriWXLlmHmzJmYPn06li1bJrcCUBERERgxYgSuXLmCoUOHYs2aNWWyguRPIDY2Fjdu3MDJkyfh7++PL1++iM4pKyujYcOG6NmzJ3r37g1lZWV4e3tj8+bN+Pr1K0wsmoHbufgVLJmf3iAh4BCyY95BkJ4MvrYB1C1todmiZ5FCIhe/SW1QXV8+JcUZxSPJ+M0iBAyp8fLywqRJk+Dm5oZNmzbJfYDevXs3Hjx4gICAgDIVA8D/RwikzSFQU1ODpaUlgoKCZCIIli1bhvr168Pb2xvjx48vtb2S8PDwgLKyMiZNmoSMjAx4eXnJRRSYmpri8uXLLFogBnFxcbh16xbOnz8PX1/ffHsU8Hg8WFhYoEuXLujduzcaNGgALpeLp0+fYt68eTh06JCoTXJyMlI/h8NCMQ3ROWpFli9WqlwLBn0kq/fB43LQsqoeEwO/CUwQMCSGiLBgwQLMmzdP7k+MucTHx8PDwwMDBgyAjY2NXPsqjLi4OCgqKkJVVVVqG7JILMwlb0njwYMHQ0tLSyZ2i2PixIlQVlbG6NGjkZGRgS1btshlhQeHw8Hw4cPh4OCA4cOHi3ILVq9e/VdHC+Lj43H79m1cuXIFFy9exPv37/OdNzMzQ6dOndCzZ0/Y2NiIpu4EAgHOnTsHLy8v3LhxA0ZGRnB2dsadO3cQHByMCRMmYObMmUgWKsJu7c0iBYE08LkcLOleT2b2GPKFJRUyJIKIMHnyZMybNw9LlizB8uXL5S4GAMDT0xMZGRlyKd8rDrk1CEpzr1ZWVggJCUFqqmwKtMizpHFRjBo1Crt378aOHTswdOjQUidJFoepqSl8fX2xbds2HD9+/K9biZCYmIjz589j4sSJqFWrFnR1ddG1a1ds2rQJ79+/h56eHvr27YujR4/i27dv+PDhA7Zs2QI7OzuoqKggOTkZ69evR61atdCtWzdkZGRg6tSpUFNTw+nTp+Ho6IjQ0FAsX74c2traMNFVxfwuFjK9hwVdLGCiK72IZpQtLELAEBuBQICRI0di165d2LRpE8aMGVMm/T579gybN2/GihUrUKlSpTLp82ek2cfgZ6ysrCAUCkW1GkqLkZERJk+ejDVr1mDMmDEwMjIqtU1xGDJkCJSVlTFgwABkZGTgwIEDcssd4XA4GDFiBBwdHf/4aEFSUhJu374Nf39/XL58GS9fvgQRgcPhgIigoqKC1q1bo2vXrnBwcEC1atUKFajv37/Hhg0bsHPnTqSlpaF3796YOXMm9u7di1WrVsHW1hZHjhxBo0aNClybFuyH+Ju+0Gk7qNT3M82hFvo2My21HUbZwSIEDLHIzMxEv379sHfvXuzfv7/MxAARYdy4cahVq1aZzJUXhbT7GOTFwsICqqqqMps2AORf0rgo+vXrh+PHj8PHxwe9e/dGZmamXPsrLFpw+fJlufYpb5KTk3Hp0iXMmDEDjRo1gra2NlxcXODl5YUXL16Ay+XCyspKtFNhUlISfH19MWbMGFSvXj2fGCAi3Lp1Cz169ED16tWxd+9ejB07Fv7+/hAIBBg+fDgSEhJw6dIlXL16tVAxcO3aNbi5uaFfPW0s7V4PfA6BBJJFgHhcDpT4XCzvUQ9j21cv9d+IUbawCAGjRNLS0tCjRw/4+/vj5MmT6Nq1a5n1fejQIQQEBODq1atyXcFQEtKWLc4Ln89HkyZNZCoINDU14enpiYkTJ2LixImwtLSUme2S6N69O06fPo0ePXqgW7duOHXqlFyXnP4cLejUqROGDRuGNWvWlEkORWlJSUnBnTt34O/vj2vXruHx48cQCoXg8/miqZeaNWvCyckJ9vb2aNOmDdTV1Yu1mZmZiaNHj8LLywtPnjxB7dq1sXnzZjg5OWH16tXo0KEDKlasiF27dmHQoEFFJuO+fPkSPXv2RIcOHbBx40bweDwsGjcACk37IV3LDDwOICgmtYCEAnC4PLSsqocl3euxaYLfFRKDxMREAkCJiYniNGf8QSQkJFCrVq1ITU2N/Pz8yrTvxMREMjQ0pF69epVpv4VhY2NDgwcPLrWdKVOmkLm5eekdykNmZiZVq1aNnJ2dZWpXXPz8/EhVVZXat29PycnJZdKnUCikbdu2kYaGBhkbG9OlS5fKpF9JSElJIV9fX5o5cya1aNGCeDweASBFRUXicrkEgPT19WnQoEG0f/9++vTpk9i2Y2JiaMGCBWRoaEgAqGPHjnT58mVKSUmh5cuXk5aWFmloaNDixYspNTW1WFtfvnwhc3NzsrS0pISEBCIiOnHiBAGg27dvU+iXJHLdfJkqj9xGpjPOkZnHedGXucd5arPiOlXtPYNc+g8v1d+LIR8kGb+ZIGAUydevX6lRo0akra1N9+7dK/P+p06dSioqKvTx48cy7/tn6tatS5MmTSq1naNHjxIAiomJkYFXBe36+/vL1K643Lp1izQ0NMjGxkY0qJQFHz9+JHt7ewJAw4YNK9O+fyY1NZWuXr1Ks2fPppYtW4oEgJKSEikqKhIAUlVVJRcXF/Ly8qIXL16QUCiUqI9nz57RsGHDSElJiVRUVGjUqFH08uVLEggEtG/fPjIxMSE+n09jx44V6z2WlpZGzZs3J0NDQ9H/mUAgoPr165Otra2o3bp160hBQYE4Csp08OJNevwxjkKiEyglI5uIiDZs2EA8Ho+ioqIkuh+G/GGCgFFqIiMjqXbt2mRgYEDPnj0r8/5fvXpFfD6fFi1aVOZ9F4ahoSEtWLCg1Hbev39PAOj8+fMy8Or/EQqF1KxZM2rWrJnEg4ysuH//Pmlra1OzZs0oNjZWrGtSMrIpJDqhwAAjCUKhkLZu3Urq6upkbGxMly9fltiGNKSlpdG1a9dozpw51KpVK+Lz+QSAlJWVSU1NjQAQj8cja2tr8vT0pFu3blFmZqbE/QgEAjp79ix16NCBAJCRkREtW7ZM9De+evUqNWzYkABQjx496M2bN2Lb7dWrF6moqNCDBw9Ex0+ePEkA6NatW6Jjw4cPp2rVqhEACg0NLWArMTGR1NXVac6cORLfH0O+MEHAKBWhoaFkZmZGpqamhf7zyxuhUEh2dnZUtWpVSk9PL/P+C/NHUVGRNm7cKBNbFStWJE9PTxl4lp8bN24QADp69KjMbYvL48ePSU9Pjxo0aEBfv34ttE3olySaeyaE2qy4TuZ5ws95Q9Bzz4RQ6Jckifr+8OED2dnZEQBydXWVebQgPT2d/P39ydPTk9q0aUMKCgoEgFRUVEhHR4c4HA4BoFq1atHYsWPp9OnTpfIhOTmZNmzYQNWrVycAZGVlRYcPH6asrCwiInr+/Dl17NiRAJC1tTUFBARIZH/GjBnE4XDIx8dHdEwgEFCDBg2oQ4cO+do2b96cWrduTQAoPj6+UHvjxo0jfX19ysjIkMgPhnxhgoAhNc+fPycDAwOqVasWRURElIsPuU8o586dK5f+fyYlJYUA0KFDh2Riz9nZmTp27CgTWz/j4uJCVatWlepJVFYEBweTgYEB1a1bN9+8eERsKg3YcZ/MPM5T1VkX8gmBn79yzw/YcZ8iYoufA8+LLKMFGRkZdOPGDZo3bx61bdtWFPZXUVEhfX19kSCoWLEi/fPPP7Rz506ZTG+9f/+epkyZQlpaWsTj8ahv3775puyioqJo2LBhxOVyqXr16nTixAmJo0Lbtm0jALRmzZp8x318fAgA3bx5U3RMIBCQmpoadevWjRQUFIrs69WrVwSA9u/fL5EvDPkiyfjN9jJgiLh//z6cnJxgZmYGX19f6OuX/YYkaWlpqFOnDurVq4fz58+Xef+FERUVBRMTE1y+fBmOjo6ltrdgwQKsW7cO379/l3lRpxcvXqB+/fpYu3ZtuS7TfPPmDWxtbaGqqopr167hzhfC3LMvkCMkiSrh8bgc8LkczO9igX4SrGn/+PEjhg8fDj8/P7i6umL16tUlrkTIzMxEUFAQ/P39cePGDdy7dw8ZGRlQVVWFrq4uEhISkJKSAmVlZbRp0wb29vaws7ND/fr1S12xkYhw584deHl5wcfHB1paWhg5ciTGjh0LExMTAD8+h1esWIE1a9ZATU0Nnp6ecHNzk3gvi6tXr6JTp05wc3PDxo0bRe9BIkLjxo2hra0Nf39/Uft3796hWrVq+Pfff3H9+nV8+vSpSNsODg5ITExEYGCgFH8FhjxgexkwJOb69evo0qULGjZsiPPnz5db0Zdly5bhy5cvuHbtWrn0Xxil3cfgZ6ysrBAXFyf6oJUl5VHSuDBq1aqFW7duoUOHDmg9ajFQv7NUdgT/ExAep4LxPSUT49rXEOs6MzMz0T4IU6ZMga+vL3bs2JFP0GVlZeHBgwciAXD37l2kp6dDTU0N+vr60NTUREZGBtLT02FgYIABAwbA3t4eLVu2hLKyslT38zNZWVk4duwYvLy88OjRI9SqVQsbN27EoEGDoKamBgDIzs7Gtm3bMH/+fCQnJ2PSpEmYMWOGVK9tSEgIevXqBQcHB6xbty6fID179iyePn2aTwwAQHBwMIAfy2YrVqxYrH13d3d06dIFQUFBsLKyktg/RvnCChMxcObMGTg5OcHGxga+vr7lJgbCw8OxYsUKTJs2DdWr/zpFTUq79fHPNGvWDABkWo8gL+VR0rgwqlatiunep6UWAz+z6koojj6IELs9h8PByJEjERISgtq1a6Njx47o0qULPD094eDgAB0dHbRq1QrLli1DZGQkDAwMwOPxRKWlu3XrhmPHjuHbt294+PAhli5dig4dOshEDHz//h2LFy+Gubk5Bg4cCD09PVy8eBEvX77E6NGjoaamBiLCqVOnYGFhAXd3dzg7O+Pt27dYsmSJVGLgy5cvcHZ2hrm5OY4ePQo+//+fB4kI8+fPR9u2bdGuXbt81wUHB0NHRwdpaWklCgInJydUqVIFGzdulNg/RvnDBMFfzsGDB9GzZ0+4uLjg7NmzoqeS8mDSpEnQ19fHzJkzy82HwpC1INDT00O1atXkJghySxqvXbsW0dHRculDHCLj0rDuzmex2ibePYqPy1zwaUfxFTA9z75AZFyaWDazs7Nx//59HD58GBwOB4qKijh37hwWLlyIN2/ewMzMDKqqqkhNTcW3b9/QpEkTbNq0CWFhYXj37h22bt2K3r17Q09PT6z+xCEkJAQjRoyAiYkJFi1ahM6dOyMkJAS+vr7o1KmTaOrh3r17aN26NXr27ImqVaviyZMn2L17t1RbbwM/puI6d+6M7OxsXLhwARoa+XcfPH/+PJ48eVJoxcvg4GDUq1cP3759K1EQ8Hg8jBkzBkePHsXXr1+l8pVRfjBB8BezefNmDBw4EIMGDcKRI0egpKRUbr5cvHgR586dE82P/krEx8eDw+HINPwuy50PC6O8ShrnZZZPMHLEyBfISfqOxHvHwFEo+ck7R0iY5RNc+LmcHAQFBWH58uXo1KkTdHV1YW1tjYULFyImJgb16tUTDe4RERFISEjAlClTEBQUhG/fvuHEiRNwc3OT+TSOUCjEhQsXYG9vj3r16uHixYvw9PREZGQktm7dCguL/99Q6O3bt+jVqxdatmyJlJQUXLlyBZcvX0aDBg1K1f+AAQPw8uVLnD9/voCoICLMmzcPrVu3LhAdACQTBAAwbNgw8Hg8bNu2TWqfGeUDEwR/KUuXLsXYsWMxfvx47NixI1/4sKzJzMzEhAkTYGtri549e5abH0URFxcHLS2tIsu+SoOVlRUeP36M7OxsmdnMS25J4927dyMkJEQufRTH25hk3A77LlYCYbz/TihVrgVFw5KniQRCwu2w7wj7moycnBw8ePAAK1euhLOzM3R1ddG8eXMsWLAA3759Q8OGDVGtWjWkpaXh+fPnyM7OxuDBg3Hx4kWsW7cOycnJ2LNnD+Lj42X62uaSkpKCzZs3o06dOnBxcUFCQgIOHjyI9+/fY+bMmahQoYKo7bdv3zB+/HjUrVsXgYGB2Lt3Lx49egR7e/tS+zFjxgycPn0aR44cQePGjQucv3DhAh4/fox58+YVSHLNzMxEaGioRIJAV1cXAwYMgLe3t9ze3wz5wATBXwYRwcPDA7NmzcLcuXOxdu1auexpLwlr1qzBhw8fsH79+jLZSllSZLGPwc9YWVkhIyNDroO1m5sbqlSpAg8PD7n1URQHAyPA45b8WmZEhCDt9R3o2I4U2zYHhD6zN0JPTw9WVlaYO3cuYmNj0aJFCzRu3Bg5OTl49OgR3r9/j9atW+PgwYP48uULnj17htWrV6NTp04YP348goODUbNmTTg6OmLEiBFISkoqzS2LiIiIwPTp02FiYgJ3d3c0aNAAd+7cQVBQEPr3759vVUB6ejqWLl0q2pBo4cKFCA0NLXbfAUnw9vbGqlWr4OXlhc6dC+Zy5OYOtGrVCu3bty9w/tWrVxAIBLCwsEBsbKxYggAAxo0bh+joaJw+fbq0t8AoQ9gqg78IoVCIsWPHwtvbG2vWrMGkSZPK2yVERkZi0aJFoqejXxF5CIJGjRqBx+MhKCio0J3nZIGioiKWLFmCvn374saNG4WGg+WF/5uvJUYHSChA3FVvqDdwgKK+udi2CRwkqhmjXbt2SExMxLNnzxAYGAgNDQ20a9cOQ4YMgb29PWrVqlWswDQ3N8fVq1exbds2TJ06VbQSwcHBQWxfRD4R4d69e/Dy8sKpU6egrq4uWjZoZmZWoL1AIMD+/fvx33//4cuXLxgzZgz++++/fFGD0nL58mWMGzcO7u7uRS5BvXjxIh4+fIirV68W+rfKXWFgbGwMgUAgtiCoX78+2rRpgw0bNqB3797S3wSjTGERgr+E7OxsDBw4ENu2bcPOnTt/CTEAAFOnToWGhka5znWXRHx8vMwFgYqKCurXry/XPAIA6N27N5o1a4Zp06ZBKBTKta9cUjJzECFG4l/Kk0vISfoG7TYDJe4jR1kHF69cQ05ODiZMmICAgADExsbi7NmzcHd3R+3atcWKNnE4HLi5ueWLFowcOVLsaEF2djYOHTqE5s2bw8bGBk+fPsW6desQFRWFFStWFCoGrly5gsaNG2Po0KFo0aIFXr16hXXr1slUDDx//hx9+vRBp06dsHbt2kLb5EYHbGxsYGtrW2ib4OBgmJmZISMjAwDEFgTAjyWIt2/fxrNnzyS/AUa5wATBX0BGRgZ69uyJ48eP48iRIxg2bFh5uwTgR+2DY8eOYeXKlb90wau4uDiZ1SDIi7wTC4EfA97KlSvx8OFDHD9+XK595fIxNhUlZQ4I0pOQcPsgtFv2BU9V8mRNDoeDO89DERAQgHnz5sHGxqZU22PnRgu2bNmCw4cPw9LSEleuXCmyfWxsLJYuXQpzc3P8+++/0NLSwvnz5/H69WuMHTu20G2Lnz17BkdHRzg6OkJDQwP37t3D8ePHZb7E9tOnT3B2dkb16tVx+PDhIqceLl++jAcPHmDu3LlFiqe8CYWAZIKga9euMDIyYksQfyOYIPjDSU5OhpOTE/z8/HD27NlfJnyXnZ0Nd3d32NjYYMCAAeXtTrHIY8oA+CEIXrx4geTkZJnbzkvbtm3h4uKCWbNmISsrS659AUBWTsmRiIRb+8FVUYdGU+lrFCgoqUh9bWFwOByMGjUKwcHBqFGjRqHRgpcvX8LNzQ3GxsaYP38+nJycEBwcjKtXr8LZ2bnQfJzIyEgMGTIEjRo1wocPH3Dq1Cncvn0bLVq0kKn/AJCamorOnTuDiHDu3LlChQnw/9EBa2tr2NnZFWmvNIJAQUEBo0ePxsGDB0VLdxm/NkwQ/MHExsbC1tYWjx49gq+vLzp27FjeLonYuHEjXr9+na906q+KPAUBEeHx48cyt/0zy5Ytw4cPH+Dt7S33vhT5xX+sZMdFI+WpLzSadIEgOQ45CTHISYgBCbJBQgFyEmIgSC9ZJJXUj7SYm5vDz89PFC2wsLDA4sWL4ejoCAsLC5w7dw5z5sxBZGQktm/fDktLy0LtJCYmYubMmahZsyYuXryIjRs3IiQkBN27d5fLe14gEKB///4IDQ3FhQsXYGRkVGRbX19fBAYGFhsdiI+PR3R0tEgQcLlcif8PRowYAYFAgF27dkl0HaN8YILgD+Xz589o164d3r9/D39/f7Ru3bq8XRLx5csXzJ07F6NGjULDhg3L250SkUcOAQDUqVMHampqcp82APKXNE5MTJRrX+Z6aihuuBMkxwIkRLzfVkR7u4q+sj69QU5cNKK9XZF453CxfXD+14+84HA4GDhwIDw8PBAXF4c5c+bg8ePH2LZtGz58+IDZs2cX+bSclZWF9evXo1q1ali3bh2mTp2KsLAwjBkzplTTGiUxdepUnD9/HkePHi22bkFudKBFixbFJlDmJhTmCoIKFSpIvCJJX18fffv2xaZNmyAQCCS6llH2sFUGfyDv37+HnZ0dMjMzcfv2bdSuXbu8XcqHh4cHFBUVsXDhwvJ2pUSys7ORlJQklxwCHo+Hpk2bltlGMPPnz8ehQ4ewYsUKLF68WG79qCnxYaqrio9FJBYqVDRDxR6zCxxPuLUfwqx06NqNBF+7UrF9mOqpQk1JPh9fkZGR2LRpE7Zt24bExER069YN5ubm8Pb2xqJFi2Bubl5ofQAiwsmTJzFz5ky8e/cOQ4cOxfz584t9UpcVGzduhJeXFzZu3AgnJ6di2169ehX379/HpUuXio1UBAcHQ0FBAbVq1RK7BkFhuLu7Y//+/bhw4QK6dOkilQ1G2cAiBH8Yr169QuvWrcHhcBAQEPDLiYG7d+9i7969WLJkiVyeumVNQkICANmVLf6ZskgszEXeJY2JCC9evPhR9Cf0PkhY+BMhT1ULqjWtC3xxVTTBVVSBak3r4pchkhDVVTMhxkatEnH//n3069cPVapUwZYtWzBs2DCEh4fj5MmTWL16NV68eIHq1avDwcEBbm5u+XIL7ty5g5YtW6J3796oUaMGnj59ih07dpSJGLhw4QImTJiAiRMnYuzYscW2za1KaGVlVeLOncHBwahduzYUFBTw9etXqQVBs2bN0Lx5c5Zc+BvABMEfxKNHj9C6dWvo6Ojg9u3bMDc3L2+X8iEQCDBu3Dg0adIErq6u5e2OWMh6H4OfsbKyQmRkJD5/Fq/mf2mRdUnjz58/Y//+/Rg8eDCMjY1haWn5o49Pj8Hhyr76HwCAw8U+zxGwsLDA5s2bkZKSIrWp7OxsHDlyBC1atIC1tTUePXoELy8vREVFYdWqVfn+h3JXImzevBkHDx5EvXr1sHv3bvTo0QOtWrVCZmYm/Pz8cPHiRdSrV08GN1oyT58+Rb9+/eDi4oJVq1aV2N7Pzw/37t0rtCrhz+QmFAIoVYQA+FGo6OrVq3j9+rXUNhjyhwmCP4Tbt2+jQ4cOqFatGm7evIlKlYoPuZYH27dvx5MnT7Bx40a5lIqVB2UhCADgwYMHcrH/M6UtaZySkoKLFy9i0qRJsLS0ROXKlTFo0CA8e/YM/fv3h6+vL2JjY9HfpQMyPz4FCXLEtm347zJUHr652DY8DtCqegVcOb4PderUgbu7O4yMjDBx4kS8fftW7L7i4uKwfPlyVK1aFf/88w/U1NRw9uxZvHnzBuPGjSuw+U8uXC4Xo0ePxo0bNyAQCDBs2DBRcaOHDx8WuZ5fHkRHR8PFxQU1a9bEoUOHSvyfys0daNasWYkJxkSEkJAQmQmC3r17Q19fn0UJfnGYIPgDuHTpEhwcHNC0aVP4+fn9kqH42NhYzJ49W1SM5XchPj4eAOSSQwAAJiYmMDAwKLNpA0CyksY5OTm4f/8+Fi1ahLZt20JXVxfOzs44efIkmjdvjkOHDiEmJgZPnz7FypUrYWRkhPbt22PMmDH4fnGDWOWLxYWIkJOdhc6GyWjfvj1OnjyJ9+/fY8yYMThw4ABq1qwJJycnXLp0qcgiTK9evcLo0aNhbGyMuXPnwsHBAc+ePcO1a9fQuXPnEpPm0tLSsGjRIrRv3x4pKSno3r07hEIhFi1ahOvXr8vsXksiJSUFLi4u4HK5OHfunFgbgl27dg137twpdmVBLhEREUhKSpKZIFBSUoKbmxv27t0rsxLRDNnDBMFvzrFjx9ClSxc4ODgUuq3pr8Ls2bMhEAiwdOnS8nZFInIjBPISBBwOp0zzCID/L2l84cIF3LhxI985IsLbt2+xefNm9OjRAxUqVIC1tTVWrlwJHR0drF27Fq9fv8bHjx+xc+dO/PPPP9DX10dGRgb+++8/NGzYEI8fP4aOjg5OH9yJJT0bysxvDocDg8gb+KezA+bMmYOcnByYmppi6dKliIyMxK5du/Dlyxc4OTmhdu3aWLduHRITE0FEou2F69atCx8fH8ycORMRERHYuXMn6tevX2LfAoEAO3fuRI0aNbBgwQKMGDEC4eHhOHXqFEJCQlCtWjXY29vDzc1N7nUlBAIB+vXrh/DwcJw/fx6VK1cu8Zrc6EDTpk1LTDoE8q8wICJ8//69VIIA+CFEMzIysHfv3lLZYcgREoPExEQCQImJieI0Z5QRO3bsIC6XS//++y9lZWWVtztF8vDhQ+JwOLRu3brydkVi1q1bRyoqKnLtY+HChaStrU0CgUCu/eRFKBRSs2bNqGnTpvTlyxc6cuQIubq6kpmZGQEgPp9PrVq1ovnz59Pdu3cpOzu7SFvXr1+natWqEZfLJQDUpUsX+vr1q+j8huuhZOZxnkxnnCMzj/NSf228/pZycnJoyZIlxOPxyMbGhj5+/FjgvgICAqhfv37E4/FIUVGRdHR0CAA1atSI9u7dSxkZGRL9nS5evEiWlpYEgPr27Uvh4eEF2gkEAtq0aROpqamRqakpXb16Vew+JMXd3Z14PB5dvnxZ7GuuXbtGAOjcuXNitV+yZAlpaWmRUCik+Ph4AkBHjx6V1mURffr0oZo1a5bpe/1vR5Lxm0UIflPWrFmD4cOHw83NDfv27ZPr+ubSIBQKMW7cOFhYWGDMmDHl7Y7EyKsoUV6srKyQkJCAsLAwufaTS3p6Oq5du4bq1avj4cOHMDQ0RL9+/XDv3j107doV586dQ1xcHG7fvg1PT09YW1sXuj12bGwshg0bhg4dOiAqKgrKysrYs2cPTp8+ne9pckzbatB5exEcQTZ4Es4g8LgcKPG5WN6jHsa2rw4ej4eZM2fi1q1biIqKQoMGDeDj4yNqz+FwYG5ujipVqkBDQwNZWVlIS/ux/FFHRwdaWlpib/X9+PFj2Nvbw8nJCTo6OggMDMSRI0dQtWrVAm25XC7GjBmD4OBgVK1aFfb29hg1apTMowXr16/Hhg0bsGnTphJXCeRC/4sONGnSBM7OzmJdExwcDEtLS3A4HKmqFBaFu7s7QkNDcfXq1VLbYsgBWSsMhnwRCoXk6elJAMjDw4OEQmF5u1Qsu3fvJgB048aN8nZFKsaPH0+WlpZy7SM2NpYA0P79++ViXyAQ0KNHj2j58uVkZ2dHysrKBIAMDQ3JyMiIKlasSO/evRPbnlAopAMHDlCFChVISUmJAFDr1q3p/fv3hbZfu3YtAaDjF6/TgB33yczjPFWddaHYaEDu+QE77lNEbGqhduPi4qhnz54EgMaMGUO3bt2if/75h/h8PmloaNCkSZMoPDycMjIy6MCBA9S8eXMCQObm5rRixQqKjY0t1O6HDx9owIABBIBq165NZ86ckej/TF7RgjNnzhCHw6GpU6dKdN3169cJAJ05c0bsaywtLWnUqFFERHTnzh0CQMHBwRL1WxhCoZAaNmxILi4upbbFEA9Jxm8mCH4jBAIBjR8/ngDQ0qVLy9udEklISCB9fX36559/ytsVqRkwYAC1adNG7v3UqFGD3N3dZWbvw4cPtH37durTpw/p6ekRAFJVVaVOnTrRmjVrKDg4mIRCIYWEhBCXyxV7Oic8PJwcHBwIAGlpaZGCggKtXLmScnJyCm0fGhpKKioq+e4t9EsSzT0TQm1WXiezn6YRzD3OU5uV12numRB6G5NUoj9ZWVk0fPhw4nA4BIBMTExo3bp1RX5WBQUF0cCBA0lRUZGUlZXJ1dWVnj59SkRE8fHxNG3aNFJSUiIDAwPy9vYudqqkJN69e0ft2rUjAOTm5kZJSSXfT1E8fPiQVFVVqUePHhKH29u2bUuNGjUSW9RkZmYSn8+nTZs2ERHR6dOnCQB9+fJFYr8LY8eOHcThcAqdemHIHiYI/kCys7NpyJAhxOFwaMuWLeXtjlhMnDiR1NTUKCoqqrxdkRonJyfq1q2b3Pv5999/qXnz5lJfHx8fT6dOnaLRo0dT9erVCQBxuVxq3rw5zZ49m27cuEGZmZmFXuvq6koVKlSghISEIu1nZWXR8uXLSUVFhbS1tYnP51O9evXo2bNnRV6Tk5NDNjY2VK1aNUpJSSm0TZMWLamv2xRauHk/KehXodjEwtv9TFxcHK1YsYJMTEwIADVr1oyMjY1JRUWFdu7cWeLgFxMTQ4sWLSIjIyMCQFWrViV1dXVSVVWluXPnlmrwzkveaIGZmRn5+flJbCMiIoIqVapEzZo1o9TUwqMlReHv708A6PTp02JfExwcTADo1q1bRES0fft2AlAqcZSXtLQ00tXVpcmTJ8vEHqN4mCD4w8jIyKAePXoQj8ejgwcPlrc7YhEcHEw8Ho+WLVtW3q6UihYtWtCwYcPk3s+6detIUVGxyEH7ZzIzM+nGjRs0e/Zsat68uSihr3r16jR69Gg6efIkxcXFiWUrKiqKVFRUaNasWYWeDwoKogYNGhCXyxUNoNOmTSsxOW/NmjUEgG7evFlkG3Nzc5o5cyZdvnyZANCHDx+Ktfn69WsaM2YMqaqqkqKiIg0ZMoSePHlCRESpqak0YsQIAkD//PNPiZ9XuVMf+vr6BEA0jbJo0SKKiYkp9lpJCQ8PF0ULRo0aJbbgSEpKovr165OpqSl9/vxZ4n7btWtHDRs2lGjK49ChQwRA9P5ZsmQJ6erqStx3cUyfPp20tbWLFIoM2cEEwR9ESkoKOTg4kJKSEp09e7a83RELoVBI7dq1o5o1a0qU0f0rUqtWLZoyZYrc+7l37x4BoAcPHhR6XigUUnBwMK1Zs4acnJxITU2NAJCenh716dOHtm/fXuQcvjjMnj2bVFRU8kVzkpKSaPz48cThcMjMzIxUVVXJzMxMrHyQN2/ekLKyMo0fP77Ydurq6rR69Wp6/vw5AaC7d+8WaCMUCunKlSvk5OREAEhfX5/mzZtXZAj7yJEjpKmpSVWrVqWgoKBC29y8eZOaNWtGAMjFxYVevHhBT58+JVdXV1JWViZFRUUaNGhQka+HNAgEAtq4caPY0YLs7Gzq1KkTaWpqSjV/f/PmTQJAPj4+El03c+ZMMjY2Fv0+adIkqlWrlsT9F8f79++Jy+XS1q1bZWqXURAmCP4Q4uPjqWXLlqSurk7Xr18vb3fE5siRIwRAomVRvyoVK1akxYsXy72f9PT0fPO2RETR0dG0d+9eGjBgABkaGhIAUlJSIjs7O1q2bBk9evRIZsu3EhMTqUKFCuTq6kpEPxLYjI2NSVVVVbTkbvDgwWJ9BuTk5JC1tXWxUwVEP+4ZAO3bt4++f/9OAOjEiROi82lpabRt2zaysLAgANSgQQPas2ePWCIzPDycrKysiM/n08qVK0V/p5cvX1KXLl0IADVp0oT8/f0LXBsbG0srVqwQLcFs3rw5HThwQOzojTi+lRQtEAqFNHr0aOLxeHTlyhWp+unQoQM1aNBA4sRjFxcX6tixo+j3AQMGUKtWraTyoTi6du1KlpaWv3xi9O8OEwR/ADExMdSwYUPS0dGh+/fvl7c7YpOcnExGRkZlMu8ub4RCIfF4vDLL2WjYsCF16NCBJkyYQHXr1hWFsRs1akTTp0+nK1euUFpamtz6X79+PXG5XLKzsxMNmBUqVCA9PT06efKk2HZWrVpFHA5HNAddFJGRkQSALl68SEKhkBQVFWn9+vUUHR1Ns2fPJj09PeJwONS1a1fy9/eXeODIysqi6dOnEwBq3749DRo0iHg8Hpmbm9OhQ4dKFFM5OTl0+vRpsrW1JQBkYGBAnp6eFB0dLZEfhZEbLciNuly7di3f+dzplm3btkll/9atWwRAotctFzMzM5o2bZrod0dHR+revbtUfhSHn5/fb70C6XeBCYLfnIiICKpZsyYZGhrKZKlPWeLh4UHKysoSLWP7Vcl938uiIEthZGdn0927d2n+/PnUunVrUR6Aqakpubq60pEjR/IV+JEnAoGANmzYQBwOhxQVFUWDoJOTk0Rz169fvyZlZWWaOHFiiW0fP36cb5qkUqVKZGFhQXw+n9TV1WnChAkUFhYm9T0R/RCoAwYMIA6HQxwOh0aNGiXVNNaLFy9o9OjRpKamRnw+n/r160d37twp9dNteHg4tW3bNl+0wMfHhzgcDs2YMUNqu7a2tlSvXj2JI0i57/l9+/aJjjVu3JhGjhwptS9FIRQKqU6dOtSzZ0+Z22b8P0wQ/Ma8efOGTE1NydzcvNQfhmXNmzdvSEFBgebNm1fersiEDx8+EACpQ7Y/IxQK6fXr17Rx40bq2rUraWpqipbvde/enQYOHEgAKD4+Xib9iUtISAi1bNlSFBXInZrw9vaWaMDLnSqoUaOGWNnwV65cIQC0adMmsrGxIQCkrq5Oa9euLXbFgzhkZ2fTtm3byNDQkBQVFWnUqFHUtm1b4nA4NGvWLKkre8bHx9PatWtFKzkaN25Mu3btovT0dKl9zRstMDQ0JCUlJerVq5fU00EBAQEFpl/EJbfmQG6iJhGRiYkJzZkzRypfSmLTpk3E4/EoIiJCLvYZTBD8tjx79oz09fWpdu3aFBkZWd7uSIRQKKSOHTuSubm5XMPaZUnuE+zDhw+ltvH161c6dOgQDRs2TLRETkFBgdq0aUMLFy6ke/fuiZZzvXjxggAUCB/Li/T0dJozZw4pKChQzZo1aeDAgcThcEhNTY0sLS0lHpBWrlxJHA6HAgICSmwbHx9P/fv3F02LtG3blqytrcnOzk7a2yGiH+/Dc+fOiaZc+vfvL4pWCQQCWrp0KfF4PLK2ti5xRUNxCAQCunDhAnXq1EmU3Onh4VGglLIk3Lp1ixQVFQkAjRgxgpKTk6WyY29vL9XrR0Tk7e1NPB5PFEURCoWkrKwst7LjycnJpKmpWeQKF0bpYYLgN+Tu3bukra1NjRs3LrMwsSw5c+aMVBnNvzK5c5ySTH+kpaWRr68vTZ06lRo2bCga8CwsLGjixIl04cKFIj/oc3JySENDo0yKTl2/fp1q1KhBioqKNHbsWGrUqBHx+XxasGCBqO79kSNHxLb36tUrUlJSokmTJhXbLjQ0lMaNG0dqamrE4/GIy+WKBJe7uztZWFhIfU8PHjwQJeu1a9euyBUCd+/eJTMzM9LW1pbqKfpnQkNDacKECaSpqUlcLpd69Oghcc5DQkICWVpakrm5OS1ZsoRUVVXJ3NxcYnGY+4R/7NgxSW+DiIjGjh1LderUEf2enJxMAOjQoUNS2ROH8ePHU4UKFUoVZWEUDRMEvxlXr14lNTU1at26dalDpeVBWloaValShRwdHf+ojOFjx44RgGJfk5ycHHr48CEtXbqUOnToICrlW6lSJRo0aBDt27ePPn36JHaf7du3l0sCVy7fv3+noUOHisoNz5w5k5SVlal27dr5BlAXFxeqWrWqWJn1OTk51KJFiyKnCoRCIfn5+ZGLiwtxOByqWLEieXp60sSJE8nExETUbunSpaSjoyPxPb1//57++ecfAkB169al8+fPl/g+jI+Pp169eonm7mUR1UpKSqJNmzZRnTp1CABZWlqSt7d3iWvts7KyyN7enrS0tOjly5dERBQWFibKLRg9erTY0QIHBweysLCQerqhTZs21KdPH9Hv7969IwBy3azpzZs3BID27Nkjtz7+Zpgg+I3w8fEhRUVF6tSpk8RVyH4V5s+fTwoKCvT69evydkWmbN26lbhcboEP13fv3tG2bduod+/epKurSwBITU2NnJ2dycvLi0JCQqQWRjNmzCAjIyNZuJ+PvPsPaGlp0fLly0WJg+7u7gXee5KUNF6xYkWhUwVpaWm0Y8cO0bLFevXq5ZtvHzVqFDVq1EjUfu/evQRA7ME5NjaWpkyZQoqKilSpUiXavn27RNX0hEIhbd26lZSVlcnS0pJevHgh9rUl2b169Sp17dqVOBwOaWtr0+TJkwst1SsUCmnkyJHE5/MLRANyEz3FjRbcvXu3VEmwQqGQdHR0aOHChaJjgYGBBEBU3lledOzYkZo0afJHPVD8KjBB8Juwb98+4vF41Lt3b5mtcS5r3r9/T8rKyqXKiP5VWbp0Kenp6VFcXBydOHGCRo0aRdWqVROVBW7RogX9999/dOvWLZm9fidPniQAMi33nHf/gb59+9KWLVtIW1ubKleuXGzCpKurK+np6RUbIXn58iUpKSnlK0P76dMn+u+//6hChQrE4XCoc+fOdO3atQIf9j179iQHBwfR71evXiUAJda4z8jIoFWrVpGOjg6pqanR/PnzS1XxLjg4mCwsLEhFRYW2b98u00Hp/fv3NG3aNNLR0SEOh0MuLi7k6+sr6mPlypUEgHbt2lWkjbCwMGrTpo1oE6eiogWOjo5Ut25dqaMDUVFRBcocnz9/ngDIZKllceT2c+/ePbn28zfCBMFvwMaNGwkAubq6FrkxzO9A9+7dqXLlylInQP2KZGRkkL+/P7Vo0YKUlJREywFr1qxJY8aMIR8fH7mtBMhdmy+LXIy8+w+YmprS0aNHRaH1vn37FrnbXy4llTTOzs4mKysrqlmzJqWlpdGjR49o4MCBpKCgQGpqauTu7k6hoaFF2m/bti31799f9HtuUuXt27cLbS8QCOjgwYNkbm5OPB6PRo0aJbMNd1JTU2nkyJGiv42sp+5SU1Np+/btVL9+fQJAtWrVIldXVwIgVkKdQCCg9evXi6IFPxcqu3//vsR5Hz9z6dKlAoIsd7dSeT+wCAQCqlatWr73A0M2MEHwCyMUCmnx4sUEgCZPnvxbh8hy688fPny4vF0pFUKhkJ49e0arV6+mjh07kqqqKgEgZWVl0tPTo507d5Yqe1xSXypVqkQzZ84slZ28+w9MnjyZzp49S0ZGRqStrS1RglhhJY1zWbZsGXE4HFqyZAm1bt2aAJCZmRmtXr1aLMFUt25dmjBhguj3+Pj4IkPe169fFy2J7Nq1K7169Urse5CEY8eOkaamJlWpUkUuBcGEQiHdvHmTOnToQACIz+fTuHHjxJ5uKypa0KlTJ6pTp06pHi5WrFhBampq+SIMy5cvJy0tLaltSsKaNWtIQUFBqj0bGEXDBMEvilAopGnTphEAWrBgwW8tBjIzM6lmzZrUtm3b3/I+oqKiaPfu3dS/f38yMDAQCQB7e3tasWIFPXnyhLp3756vhGtZ0bVrV7K1tZXq2rz7DzRq1IgCAgJEW2bb2tpKvN7755LGudy/f594PJ6olkKrVq3o5MmTEs3h6+vr55uvFgqFpKKiQl5eXqJjISEh5OzsTADIysqq2I2SZMW7d++oefPmxOfzacWKFTIrD53L+/fvSV9fn5o2bUozZsygihUrEgBycHCgc+fOldhf3mhBlSpVaNOmTTJZCTBw4MACO25OnTqVqlevXiq74hIfH0+qqqo0f/78Munvb4EJgl+QnJwcUUhSXmt6y5IVK1YQj8ej58+fl7crYpGYmEhnz54ld3d3URY4h8OhJk2a0IwZM8jPz6/Asqd27dqVSwhz8eLFpKmpKfFAlHf/gdWrV9P9+/epTp06pKysTF5eXlIPbLkljYODg+nt27c0btw40TTKP//8I9UGQAKBgHg8Hnl7e+c7Xq1aNZo+fTp9+vSJhg8fTlwul6pWrUpHjx4tU+GZlZVFHh4eBIAcHR1lNjURHx9PdevWpapVq4qWF6enp9PevXupadOmBPzYilmcKEveaIG2tnappzkaNmxIw4cPz3ds8ODBZG1tXSq7kuDm5kaVKlX6bXOqfkWYIPjFyMrKon79+hGXy6Xdu3eXtzulJioqitTV1Uvcya48ycrKooCAAJo3bx7Z2NgQn88nAGRubk4jRoygo0eP0rdv34q1Ub9+fRo3blwZefz/5CbXiRsWj46Opp49exIA6tSpE719+5YWLVpEfD6fGjVqVOrs+YyMDKpcuTIZGBiIChcBKNXum7GxsYVW02vRogXVq1ePVFVVSVdXl9auXVuuO2b6+vqSvr4+GRgYlLpiZVZWFtna2pKOjk6hUwRCoZDu3btH/fv3JwUFBVJVVSU3N7diy5fn5g4oKipSlSpVpN4ELTs7m5SUlAo8rDg5OVGXLl2ksikNwcHBpc6FYOSHCYJfiLS0NHJ2diYFBQWpNhr5Fenfvz9VrFixzEvsFodQKKRXr17R+vXrqUuXLqShoSF6curRowdt2bKFwsLCJHrKNDY2Jk9PTzl6XTgJCQkEgPbu3VtsO4FAQFu2bCFNTU3S19enI0eOUGhoKFlbWxOXy6XZs2eX6kkrPT2ddu3aJUqEA0BDhgwhBQWFfJvfSMPr168JgGgKIDs7m7Zs2ULKysrE5XJp+vTpv8z768uXL+Tg4EAcDoc8PDykKnssFArJ1dWVFBQUCt1h8Wc+f/5M8+fPp0qVKokKLRU2JePi4kK1atWiN2/eiLUSoShevnxJAESCIiUjm0KiE6ihbTfqM3IypWSIPxVUWtq1a0c2NjZl1t+fDhMEvwiJiYnUtm1bUlFRIV9f3/J2Rybk7rFe3DKpsuLLly908OBBGjJkCBkbG4vKArdr144WLVpEgYGBpUqyUlNTo7Vr18rOYQmoXbs2jR07tsjzefcfGDFiBMXGxtK2bdtITU2NqlatSnfu3JG678+fP9PcuXNJX1+fAJCzszNduXKFmjRpQqqqqlSrVq1SV5XLrbcfEhJCp0+fplq1ahEAqlOnDlWrVq1UtuWBQCCg5cuXE5/PpxYtWtD79+8lun7p0qViibyfyczMpMOHD4tea1NTU1q6dCl9+/aNHjx4QABo//79Ih/XrVtHKioqVKVKFbGERy5Hjx4lBT0Tmn70AbVZcZ3MPc6TWZ4vc4/z1GbFdZp7JoRCvxTcrlmW5C69ffz4sVz7+VtgguAX4Pv379S0aVPS0tISq7b770B2djbVq1ePmjdvLvNEK3FITU2lS5cu0ZQpU/I9tdarV48mT55MFy9eLNV69LxkZGRI9QEuKwYNGkTNmjUrcDzv/gO1atWimzdv0ufPn8nFxaXUNfCfPHlCgwcPJkVFRVJVVaWxY8fSmzdvROeHDx9OAPIlAkqLj48PASBra2tRwuPjx49pxYoVpKmpWWr78uL+/ftkbm5OWlpaYpcHPnr0KAEodbTp0aNHNGTIEFJSUiIlJSUyMTEhMzOzAlGDt2/filZ9jB07tsT3Q0RsKrXwOEBmHuep6qwL+YTAz1+55wfsuE8RsfIppJadnU0mJiY0bNgwudj/22CCoJyJjo6munXrUsWKFfPtGva7s379euJwOFIlkUlDTk4OBQUF0eLFi6l9+/aijV8qV65MgwcPpgMHDshtidLnz58JAJ07d04u9kti48aNpKCgkG/+PO/+A3PnzqWMjAzy8fGhChUqkL6+vlS+5uTkkI+Pj6hMrqmpKa1cuZLi4uLytQsODiZFRUWqVq2a2CWNiyI8PJyaNWsmKu976dIl0VTOgQMHCMAvXdciPj6eevfuTQDIzc2t2MqKd+/eJSUlJerfv7/MkiK/fv1KY8eOFQliGxsbOnLkSL6pDHGjBYeDPlLNORfJbMbZYoVAYcKg5pyLdDhIPstxlyxZQsrKyvT9+3e52P+bYIKgHAkPD6cqVaqQsbHxH1XKNyYmhrS0tOSyL3pewsPDydvbm3r27Ek6OjoE/NgSt3PnzrRu3Tp6+fJlmWSb5xbJKU3ovTQEBQURAAoMDCyw/8DLly8pMTFRdKxbt24Sb4iVmJhIa9eupSpVqogGlePHjxe6bDArK4uaNGlCderUoUePHold0vhnvn//ThMnTiQFBQXS1NQkNTW1AlM6169fJwDFFjT6FRAKhbRt2zZSUVEhCwsLCgkJKdAmPDycKlasSK1atZJ5YmSXLl2oevXqdOzYMdGGTpUqVaL58+fnWxGRN1owbty4fEJrw/VQiURAUV8brsv+tfr27RspKSnRsmXLZG77b4MJgnLixYsXVLlyZapRo0aptlb9FRk2bBjp6OiUmJkvKbGxsXT8+HEaOXKkaHDi8XjUsmVLmjt3Lt2+fVvqvetLQ+4cd+5mM2VNRkYGKSoq0qBBg0T7D2zbto0EAgHdvHmTzM3NSV1dnXbt2iWRQAoPD6cJEyaQhoYG8fl86t+/PwUGBhZ7zaJFi4jL5YraDR8+vMSSxnlJT08XFbjR0NCgRYsWkbu7O9WqVatA25+TDX91QkJCRGWPt27dKnot4uLiqHbt2lS9enWZ/8/kbsuddzrr+fPnNHLkSFJRUSEFBQX6999/RYWVCosWHA76KBMxkPt1RA6RgiFDhpCpqalEtS0YBWGCoBx48OAB6enpUf369WW2ZvlXIXeDk82bN5faVkZGBl27do08PDyoadOmxOFwRKVcx40bR2fOnPkldnw8e/YsASi31zI8PFxU9Kdv3770+fNnysjIoOnTpxOHw6FWrVqJvS2zUCikGzduULdu3YjD4ZCuri7NmjVLrP0Snj9/TgoKCuTh4SE6Fh0dXWxJ41wEAgHt27ePTE1Nic/n09ixYykmJoaIfhTBadWqVYFrkpKSfrvql2lpaTRq1CgCQL1796aYmBhq37496erqyiXS0a1bN6pevXqhA2VcXBytWrVKJK6bNWtG+/bto4yMDFG0gK9lQFVmnCGTycdJy+YfUq7SmLjK6gSA9JwmFhjsDQetJvVGTqRoUI3A5f2oSPlTm5pzLso8p+DRo0cyK+P9N8MEQRlz48YN0tDQoBYtWhSYe/3dEQgE1LRpU2rYsKFUGfsCgYCePn1KK1euJAcHB1JRUSEApK+vT/3796ddu3ZJXD2vLNizZ0+Z1HD/mbz7D6irq5OxsTER/RiY69evTwoKCrRs2TKxXouMjAzas2cPNWzYkIAfWwNv27ZN7F01s7KyqHHjxlS3bt0CIe/Zs2eTsrIyRUZGFnqtn58fNWrUiABQjx498iUnEv0otdutW7dCr1VTU6PVq1eL5eOvxPHjx0lTU5PU1dVJQUGBbt26JfM+njx5QgBKrGeSk5ND586dE21qpa+vT3PmzKGIiAhq7XmMTKefIaNRO39E5DQrkpJpvSIFgZbNPwQunxQNqxNf16hQQVB11gUasEP2pZ6tra2pQ4cOMrf7NyHJ+M0Fo1RcuHABHTt2hJWVFa5evQodHZ3ydkmm7Nq1Cw8fPsTGjRvB4/HEuiYyMhK7d+9G//79UalSJTRs2BCenp7gcDhYuHAhnj59is+fP+PgwYMYOnQoTExM5HwXkhMXFwd1dXUoKiqWWZ8PHjxAs2bNMHPmTIwePRpr1qxBVFQUFi1ahKZNm0IoFCIoKAgzZswo9rWIiYnB/PnzYWZmhiFDhqBSpUrw9fVFSEgIRowYAVVVVbH8Wb58OZ49e4Y9e/ZASUkp37np06dDXV0dc+fOzXc8ODgYnTp1gp2dHZSVlREQEICTJ0+iZs2a+dp9+/YNFStWLLTfypUr49OnT2L5+CvRq1cvjBgxAikpKRAIBLh79y6EQqFM+1iwYAGqVq2KAQMGFNuOx+PBxcUFvr6+ePXqFfr06QMvLy/UaNoGEVmq4HB54KnrwnjcfhiP2Q2d9sOKtKXR2Akmk46i0hAvqJg3LLSNQEi4HfYdYV+TS3N7BXB3d8f169fx4sULmdplFA4TBKXg6NGj6NatGzp27Ijz589DXV29vF2SKXFxcfDw8MDAgQNhY2NTZLvExEScOXMG7u7uqF27NkxNTeHq6oq3b9/C1dUV169fR1xcHC5fvowpU6agQYMG4HJ/7bdefHx8mYm75ORkTJgwAc2bNweXy0VQUBBWr16NatWqAQD+++8/uLu748GDB2jYsGGRdp49e4Zhw4bB1NQUK1asQI8ePfDq1StcvHgRDg4O4HA4Yvv0/PlzLFiwANOnT0ezZs0KnNfU1ISnpyf27NmDkJAQREdHw9XVFQ0bNkRYWBhOnDiBO3fuFPm++f79OypUqFDoud9VEBw+fBirV6+Gp6cnZsyYgZkzZ6Jjx4748uWLTOw/f/4cPj4+mDNnDvh8vtjX1a5dGxs2bEB0dDQ6uS8GhAIAAIevAJ56ye9xnpoOuApKJbfjcnDgfoTYfolDz549YWhoiE2bNsnULqNwxH9XMfKxfft2uLm5YcCAAdi1a5dE/6C/C56ensjKysLy5cvzHc/OzkZgYCCuXr2Kq1evIigoCAKBAFWrVoWdnR0WLVqEDh06QFdXt5w8Lz1xcXFl4v/Zs2cxduxYxMXFYdWqVRg/fjx4PB727duHcePGgcPhYNiwYVi1alWh1wsEAly4cAFeXl7w9/eHsbExFi5ciOHDh0vtf3Z2NoYMGYKaNWsWiADkxc3NDWvXrkX37t0RHR0NVVVVeHl5wc3NrcTIyp8WIQgICMCQIUMwcOBAzJs3DxwOBx06dMCAAQPQoEED7N+/Hw4ODqXqY8GCBahSpUqJ0YGi0NTURJxSJSA1rVR+FIVASPAP/Yp5sJCZTUVFRbi5uWHVqlVYunQptLS0ZGabUZBf+zHtF2XVqlUYOXIkxowZgz179vyRYuDp06fYsmUL5s2bB0NDQ7x8+RLr169H586doauri9atW2PDhg0wMjLC5s2bER4ejvDwcGzduhW9evX6rcUAIH9B8OnTJ/Tq1Qtdu3ZFvXr18OLFC0yePBkJCQno3bs3Bg8ejO7du6Nt27b4/v17geuTk5Oxfv161KpVC127dkV6ejqOHj2Kd+/eYfr06aXyfdmyZXj+/HmhUwW5ZGdnY/v27YiNjUVYWBi6d++O8PBwuLu7lygG0tPTkZqa+sdECMLCwtCtWzdYW1tj+/btokiMnZ0dnj17hkaNGsHR0REeHh7Izs6Wqo/nz5/j5MmTmD17NhQUFKSykZKZg4g4+YiBXCJi05CamSNTm25ubsjMzMTu3btlapdRECYIJICIMGfOHEybNg2zZ8/Ghg0bfvnQtzQQEUaOHAlDQ0M8ffoUJiYmsLCwwLRp05CamopZs2bhwYMH+PbtG44fP46RI0eiatWq5e22TJGXIBAKhfD29kadOnVw+/ZtHDlyBBcuXIC5uTkuXbqEevXqwd/fH8ePH8fevXvRsmVLBAYGgogAAO/fv8eUKVNgbGyMyZMno1mzZrh37x7u3buHPn36SD1Y5PL8+XMsXLgQM2bMQNOmTQucJyL4+PjA0tIS7u7u6N69O+rXr4/Q0FBoaGiI1UeuwPkTIgRxcXFwdnaGnp4eTp06VUBAGRgY4OLFi1i5ciVWr16N1q1b4/379xL3s3DhQpibm2PQoEFS+/oxNhUk9dXiQQA+xKbK1GalSpXQu3dvbNq0SeY5GYz8/HmPtnJCKBRiwoQJ2LhxI1asWIFp06aVt0syJTU1Fbdu3cLVq1dx4sQJREZGAvgxQPTr1w/29vZo3bq12Alpvzvx8fEyT3Z88eIFRo4cibt372L48OFYvnw5dHV1kZqaiqlTp8Lb2xsdO3bErl27UKlSJQCAlZUVlixZglOnTuHQoUM4ffo0tLS0MGbMGIwdOxbGxsYy8y87OxuDBw9GrVq14OnpWeD8vXv3MG3aNNy5cwcODg44duwYGjRogJs3b6Jdu3Y4fvw4+vbtW2I/uYKguAhBamoqkpOTxRYZ5UFmZia6d++OuLg43L9/v0gByeVyMXXqVLRp0wb9+vVDw4YNsX37dvTp00esfkJCQnDixAls375dLMGXnZ2NhIQEJCQkID4+XvT9xZdUAAaS3KJUZOXIftAeN24cbGxs4Ovri06dOsncPuMHTBCIQU5ODlxdXbF//35s3boVI0eOLG+XSo1AIMCjR49EeQB3795FdnY2jIyMEBcXh+bNm+PMmTMwMJD/B8iviCwjBBkZGVi8eDGWL1+OqlWr4ubNm2jTpg0AIDAwEAMHDkRUVBQ2b96MUaNGiULOWVlZiIj4kaTVq1cv1K5dG5s3b8aAAQOgpqYmE9/ysmTJEgQHByMwMDDfk25YWBhmzpyJEydOoH79+vD19c03H962bVu4uLhg1qxZ6N69u1j5A0DxEQLgx7RKrVq1SntbcoGIMGLECNy/fx/Xr18XJYAWh5WVFZ48eQI3Nzf07dsXfn5+8PLyKiCyiQhpaWmigXz8+PHQ19cHl8vF+vXrER8fn2+gz/2e+5WaWvgTuoJ+FVQetkEm918cinzZR02tra3RuHFjbNiwgQkCOcIEQQlkZmaiX79+OH/+PA4dOoR+/fqVt0tSQUQIDw/H1atX4efnh+vXryMhIQEaGhpo37491qxZA3t7e2zfvh1btmzBsWPH/loxAMhOEPj7+8PNzQ0fPnzArFmzMHPmTCgpKSE7OxuLFi3C4sWL0aRJE5w/f160NO/bt2/w9vbG5s2b8eXLFygpKaFLly44cuSI3Kaonj59ikWLFmHmzJlo0qQJgB9P8gsWLMCWLVtgaGiIPXv2YMCAAYUueVy2bBnq168Pb29vjB8/vti+SooQ5EZHfmVBsHDhQuzfvx+HDh0qdCWFQCBAYmJioYN248aNkZycjF27duHo0aOoV68ecnJy8g30heUauLq6QlFREaqqqlBUVASHwwERITs7GxkZGUhPT8/XXllZGQYGBqhcuTLMzMxgZF4VJ0AAxF9tIikcAOZ6sherHA4H7u7uGDp0KMLCwlC9enWZ98FggqBYUlNT0a1bNwQEBOD06dNwdnYub5ckIjY2FteuXROJgA8fPoDP56NFixaYOHEi7O3tYWVlJUqKfPXqFdatW4d58+bB1NS0nL0vP4RCIeLj40slCGJjYzFt2jTs3r0brVu3xpkzZ1CnTh0AwJs3bzBgwAA8efIEnp6emDVrFvh8PoKDg7Fu3TocOHAAXC4XgwYNwvjx4/Hff//h+/fvchMDWVlZGDJkCOrUqYP//vsP6enp8PLywrJlywD8GPwmTJgAFRWVIm1YWFhg2LBhWLBgAQYPHlxsNvi3b9+goqJSZJQjryAoTzIyMvIN5Lk/X7lyBfv27YONjQ38/Pxw/PjxAk/siYmJRdrV0tKCjo4OatSogcjISNy7dw/16tVD3bp1IRAIkJ2djbS0NCQlJSE0NBTp6emiHJKsrCxkZWWhQoUKMDIygrGxcZHfNTU1Cyw1fbDSHx/lmFhoqqcKNSX5DCt9+/bF1KlTsWnTJqxdu1YuffztMEFQBPHx8XB2dkZwcDAuXbqEdu3albdLJZKRkYE7d+6IpgGePHkCIkKdOnXQpUsX2NnZoV27doXOyxIRxo8fDzMzM0yZMqUcvP91SEpKAhFJVYeAiHDo0CFMnDgR2dnZ2LZtG1xdXcHlckFE2LRpE6ZPnw4TExPcu3cPTZo0wcWLF+Hl5YVr167ByMgI8+bNw4gRI6CnpwfgR6h58eLFEAgEYheHkoQlS5YgJCQE9+7dw+HDhzFnzhx8+fIFY8aMwZw5c4oM7f/M/PnzcfDgQaxYsQKLFy8usl1xNQgAQF1dHZqamqUWBEKhEMnJyYUO6uIcy8jIKNK2srIyvn37hpycHOjo6MDQ0BB16tSBjo4OtLW1oaOjAw0NDQiFQmRmZiI1NRWJiYmIjY3Fp0+fEB0djaioKGRnZ0MoFOLZs2d49uwZeDweKlWqBCMjI1SsWBFPnz5Fjx490LNnTxgbG8PY2BiVK1eGsrKyVH+TdrUqYv+9DxD+L0qQ9OgchBmpEKTEAQDSw4KQk/wjgqPZpDO4ymrISfyKlJDrAIDML2EAgIQ7RwAAfC19qFt2APCjDkH7mvpS+SUOKioqGDFiBDZv3oyFCxf+cXVffgWYICiEmJgYODg4ICoqCtevXy+0MMuvQO4HiZ+fH65evYrbt28jIyMDBgYGsLOzw/jx42FraytW4tmpU6fg5+eH8+fPS/1h86cQF/fjw1HSCMG7d+8wevRoXLlyBX379oWXlxcMDQ0B/HjaHTp0KK5cuYKxY8fC09MTx44dw7///ou3b9/CysoKhw8fRs+ePQskjllZWSE5ORlv3rxB3bp1ZXOT/+Pp06dYvHgx+vbtixEjRuDZs2fo1asXli5dKnFYtnLlypg8eTJWr16N0aNHF/m+K64GQV5bnz59QnZ2tsQDee7PiYmJRWalq6urQ0dHJ98AXqNGjQLHcr/r6OggLi4O3bp1Q4MGDXDq1Cl8+/YNUVFRosE9KioKL1++FB378uWL6Mke+DGg5T69m5qaomXLlqLf37x5g6VLl0JbWxtHjhyBtbU1/vnnH5iYmODw4cOlrpiZk5ODAwcOYPuy9VDsvlB0PCnQB4Kkr6Lf00LvAqF3f/yNLNr/EAQJX5B4+0A+e7m/K5lYigSBQEgY0EK+kcXRo0djxYoVOHDgAEaNGiXXvv5GOJT3HVsESUlJ0NLSQmJiIjQ1NcvCr3IjIiICdnZ2SElJwdWrV2FhIbsiG7IgIiJCFAG4du0avn//DlVVVbRt2xZ2dnawt7eHpaWlRFXp0tLSULt2bTRo0ADnzp2To/e/Bw8fPkSzZs3w5MmTYisD5pKdnY21a9di3rx5qFixIjZv3pxveunYsWMYNWoUlJWVRWv8d+zYgZSUFPTq1QsTJ05EixYtirSflJQEbW1t7Nq1C0OGDJHBHf4gKysLlpaW+Pz5M1JSUmBjY4NVq1YV60tJJCUloVq1aujSpQt27twJIkJqamq+wdrDwwNJSUkYPnx4kYP627dvIRAIkJNT+Jp2Ho9X6KAtzjFtbe0ia4cQEeLj4/MN9NHR0QgLC4OPjw8EAgGUlZULTAno6OiUGMLX0dEp9v/y48eP6N+/PwIDA+Hu7o5169aJEk2lJTMzEzt37sTcuXNFuRt1xmxGppYpBDJcg8jjctCyqh72uzaXndEi6NGjB0JDQxEcHCzR59zfiiTjN4sQ5OHNmzewt7cHn89HQEDAL7G2PiEhATdu3BCJgLdv34LL5aJp06Zwc3ODnZ0drK2tiywgIw5Lly5FTEwMvLy8ZOf4b4wkEYIHDx5gxIgRCA4OxsSJEzF//nxRKDMhIQHjxo3DwYMH0b59e2hoaGDo0KHQ1NSEm5sbxo4dK1auhqamJurUqYOgoCCZCYLIyEh07twZb9++hampKfbt24du3boV+gErEAjyLWMr6elcTU0Nu3btgo+PD5KTk4sc1GfNmlVg0DY3NxcNnCkpKfD09Cx0UFdXV5d4MBAIBIiJicHjx4/zDfa5T/e5P+edKuBwODAwMEBKSgpycnLQt29f1K1bt8CAL4vluGZmZrh58ybmzZuHxYsXQ0lJSeqM+tTUVHh7e2Px4sWIj48HANja2mL9+vXQMDSH3dqbEGQLABkNqHwuB0u615OJrZJwd3dHhw4d4O/vjw4dOpRJn38LLELwP54+fQoHBwdUrFgRV65cgZGRUbn4kZWVhfv374umAYKCgiAUClGtWjXY29vD3t4e7du3l1md/fDwcNStWxfTp0/HwoULS77gL+Do0aPo168fkpKSilwHn5ycjDlz5mDDhg2ideW52fkAcP36dQwaNAhxcXEwNDTE+/fvUatWLUyYMAGDBg2SeNng0KFD8fz5czx69Eiqe0pPT0d8fDwiIyOxceNGHD16FNnZ2ahbty569+6NpKSkIgf65OTCN6zhcDiiBLm8g7WmpiZOnToFQ0NDjB8/vsATe5cuXWBvb4+NGzcW6e+MGTNw8uRJhIWFiXV/GRkZiI6OLjDQ5/3++fNnCAQC0TWKioolPtUbGBhg6NChOHXqFPz9/UsVPRGX169fo27dutDQ0ICysjL27dsHR0dHsa5NSEjAhg0bsHLlStHr1qZNG6xduxaNGzcWtTvyIAIep4Jl5vPyHvXQt1nZJCITEerVq4eaNWvi1KlTZdLn78xfGyFIzczBh9hUZOUIocjnwlxPTayM17t378LJyQk1atTApUuXik14kjVEhJcvX4pWAty4cQOpqanQ09ODra0thg0bBjs7O1SpUkUu/U+aNAmGhoaYOXOmXOz/jsTFxYHP5xeZtJR3/4GVK1diwoQJohB0RkYGJk2aBG9vbygqKiIrKws1atTApk2b4OjoKPVKgaZNm2L//v149eoVMjIyJJpPT0hIQGZmZqF2w8LC4O3tnW/QNjIygoWFRYmhd01NzSLvp2PHjqKn6Z8TcuPj40UrCYqiUqVK+PTpkygxsKhBPvf7z+WdNTU1RYN63bp1YW9vX2DAr1ChQolRhrlz5+LQoUM4duxYmYgBAFi0aBEqV66Me/fuwc3NDR07dsS0adOwaNGiInMJvn79ijVr1mDDhg2iVQktWrTAqlWrCl0W2dWyIkZN3A/tNgOl9pOIwOFwMM2hVpmJAeCHEB03bhzGjh2Ljx8/wszMrMz6/tP57QXB25hkHAyMgP+br4iIS8tXmpMDwFRXFe1r6ePf5qaoYVDwae/q1avo1q0bmjVrhrNnz5ZJBOTz58+iCICfnx8+f/4MJSUltGrVCv/99x/s7OzQqFEjuZdFvnDhAs6dO4cTJ078sRUIpRGJuTUIfh4sPn36hPHjx+PkyZPo1KkTNm/eDHNzc9H5EydOYMSIEUhISACfz8egQYMwceJEUR5KVlaWxAN53gQ5Iio0qVBDQ6PAoF2nTh1oa2tDW1sbHz58wMWLF/H161c4OTlBR0cHhw8fRkBAgNwGud69e2PVqlWYNm0aAgMDRe9loVCI2NhYVKhQAUKhsEBiXu73x48fIz09HZqamgUK7ejr64sG9byJeXm/y6LC4b59+7BgwQIsW7YMvXv3LrU9cXjz5g0OHz6M9evXw8TEBOfPn8fatWvh4eGBGzdu4MiRI/mmMiMjI7Fq1Sps3boVOTk5EAgEaNSoEVauXIkOHToUKXhevnyJxLtHMX38aOwNSUOOkCAQip9UwONyQAIBvl/aDPNW7gDKti7AgAED4OHhgS1btoiWxzJKz287ZRAZl4ZZPsG4HfYdPC6n2Ddz7vnW1StgSfd6MNH9MfidOnUK//zzD+zt7XH8+PFi11mXhpSUFNy8eVMkAnL39m7YsKFoGqBVq1Zy678wMjIyYGlpCXNzc1y9evWPSs4prUicMmUKLl68iFevXgH4MYht3boVM2bMgJKSEubNm4dWrVohISEBcXFxuHnzJg4ePCiqFVCjRg2YmZkhNTU136D+c+GYXPh8fomJcerq6pgwYQJGjBiBYcOGic5raWkVmSB3584dTJs2Dffu3UOnTp2wfPlyZGVloXnz5vjvv/+K3cmwtGRlZcHHxwf9+vUTLWeNiorCu3fvcObMGVSsWLFAAR4+n4/KlSvD2NgYSkpK8Pf3x7Rp09CkSRPRQF+pUqVS5cuIy40bN+Dg4IBBgwbl27BI3gwcOBDXr19HeHh4vtU+Dx48QL9+/fD9+3ds27YNTZo0wbJly7B3715wOBxkZ2fDwsICS5cuhYuLS4n+7tmzB0OHDkVycjLis7iiz1IOCFRM4aK8n6ULu9TFpJGD4Ofnh4CAADRo0EBmfwdxmDx5Mvbt24fIyMgy/ez83ZBk/P4tBcGRBxGYe/aFVKqWz+VgfhcLZL70x7Bhw9C7d2/s27ev1Mt68pKTk4OHDx+KIgD37t1DdnY2TExMRALA1tZW7PXd8mDJkiWYO3cunj17JvOlbOWFJCKRywGEBNTR4aBrpVRw0uJET+U+Pj6IjY1F48aN8enTJ4SHhyMjI0NUGa4olJWVUaVKFejp6YmV9Z77s5qamlgDjrW1NapWrYqDBw8W2y40NBQeHh7w8fERPS3a2toiMzMTTZs2BZ/PR1BQkPS75qWklBjCj4mJyXeNmpqaKNP+/v37+Pfffws83eeW5wV+LOGsVq0arl69Cjs7O6n8lJbXr1/D2toaTZo0waVLl0q9YZS4hIaGok6dOli3bh3GjRtX4HxSUhL69euHS5cuAQCUlJSQmZmJGjVqYNGiRejVq5fYUcUpU6bg9OnTCA8PFx17G5OMQQu34yuvAoSqegWFtJ4q2tfUx4AWpqiu/0NIp6amok2bNvj69SuCgoJKnAqSJWFhYahZsyZ27tyJoUOHllm/vxt/tCDY6P8Wq66EltpO/M196GuphS1btpS62AsRISwsTLQSwN/fX/S36tChg2g5YI0aNX6JJ/GIiAjUrl0bY8aMwapVq8rbHYkgIlGCXN6nb7/36Tj/WQVCcIp9wilgT5ADEgqQcG0b+B+DoKOjg9jYWPD5fOjr6+P169fQ1tZGr1690KhRIwgEAty6dQuXL19GSkoKuFwuKlasiGPHjqF169ZyvHNgwoQJuHjxIt6+fVvo+a9fv2L+/PnYunUrjIyMsHjxYvTv3180SMyZMwfLly/Hw4cPC32aIyLExsYWmX2f+z0pKSnfdXp6eoUm5BkbGyM9PR3du3fH2rVrMWHCBAQEBKB169Z4+fKlqHJjYaSnp0NVVRV79+4t1Q5/kvLt2ze0aNECysrKuHPnDrS1tcus78GDB8PPz69AdAAAgoKCsHjxYpw9e1aUm8Ln8zF37lx4eHhIvAW7g4MDVFVVcfr06XzHq1Spgm7dumHRspViT7V9+vQJVlZWMDQ0xM2bN+Wyz0ZRODs74/Pnz3j06NEv8dn6K/LHJhUeeRAhEzEAADptB6FDj3pSi4Fv377h+vXrIhEQEREBPp8Pa2trTJkyBfb29qKnsV+NqVOnQktLq9Ad7coCoVCIxMREqebTExISkJWVlc+epnUf6LQdJEpykgQOjw8Ojw/dju6Y6lAT49rXQP369fHhwweEhYXB09MTHh4eePLkCby8vHDy5EmoqalBX18fycnJGDJkCNauXVsmu/JZWVlh/fr1BfZZSEtLw9q1a7F8+XJwuVwsXboU7u7u+QaV+/fvY+nSpRg2bBjevn2LGzduFBjso6Oj8yUfcrlcVK5cWTTA16lTp8DAX7ly5RLDta6urli4cCGGDBlS4j4GuaioqEBHR6dMyxdnZGSga9euSElJwbVr18pUDLx9+xYHDhzA2rVrRa8bEeHGjRtYsmQJ/Pz8oKWlBQ6HAz09PYwYMQKnTp3CokWLoKenl29TLHEIDg7G8OHD8x379OkTPnz4ABsbG6gp8WFRuejy03mpXLkyzp8/j1atWmHgwIE4ceJEmW0L7+7ujk6dOuHevXto2bJlmfT5J/PbRAgi49Jgt/YmMgvZWjPr20ckBhxC1pcwCFITwFFQgoKeCTSb94BqjaILZSjxufCb1FaUU1Ac6enpCAgIEE0DPHnyBMCPGu65EYC2bdv+8uU0r127Bjs7O+zfvx8DBgyQ2k5mZqZU5WDj4+NFpYELQ0tLS6wwe+73wO88rLv3vVBblJONhNsHkPrCH8KMFChUNId2m4FQqdKoyPuqlfgQV7bMQ+XKlXHp0iW8ePECXl5eCAoKQo0aNdC+fXucOnUKHA4HO3bsQJcuXaT+G0rK27dvUbNmTVy+fBmOjo4QCATYu3cv5syZg+/fv6Nr167o0KEDkpKSCjzlf/78OZ8tZWXlYpfbGRkZwcDAQCaC9tOnT6hevTomTZoEc3NzuLm5ITs7u0QxbmlpCVtbW6xbt67UPpSEUChE//79cebMGdy8eRNWVlZy7zMvQ4YMga+vL969ewdlZWVcuHABS5Yswb1791ChQgUkJCRAW1sbs2bNwqhRo6CiooKMjAxRbf8ePXpgx44dYi1H/v79OypWrIijR4/m24L5+PHj6NOnDz59+iRV6P/8+fPo2rUrpkyZghUrVkh8vTQIhULUrl0bTZo0weHDh8ukz9+NP3LKYODOQNx9F1vovHB6+AMkPTwHJaPa4KnrgrIzkfbmLjKjXkC34zhoNOxYqM3iqmsJhUI8ffpUFAEICAhAZmYmDA0NYW9vDzs7O9jZ2Ym2av0dyM7ORoMGDaCnp4ebN28iJSVF4nKwuT8XVeddQUFB7IpxPx/T0tKSKGJTnEgEgG9nViDtzR1oNu0Kvm5lpAb7IfPzWxj8swTKJgUrUBIRIMhG0uFpaFzLDG/evEF0dDTs7OwwcuRIXL58Gbt27UKXLl2wfft26OvLr257rj8JCQmiwT0yMhLu7u6wsLAAh8NBSEhIocsJtbW1RSF7IyMjvHnzBvfu3cP69evRqlUrsarmyZo5c+Zg9erVmDBhAnbs2FFgmWBhODg4QEtLC8ePH5e7f7Nnz8bSpUtx/Phx9OzZU+795SU8PBy1atXCypUrYWRkhCVLluDZs2cwMjLCt2/foKysjOnTp2PChAmFPnD4+PjA1dUVGhoaRe6+mJfcgj4/T9tMnDgRZ8+exbt376S+l/Xr12PChAnYtm0bRowYIbUdSVi3bh2mTp2Kjx8//lafx2XFHycI3sYkw97rlkTXkFCAz3smgnKyYTTSu9i2fpPaoLq+Bj58+CBaCXDt2jXExsZCTU0Nbdu2FYmA3A/jX4Hs7GyJns5fv36NqKgoaGlpITk5udg675KWg839WVVVtewysosRiZmf3uDLvinQbj8MWs17AAAoJwufdowFT00LhgOLyJ0gIdI/PEXcyR+79k2YMAGJiYkYNGgQvn37Bi8vLwwbNqzU95hbNa+4xLyoqKh8KxM4HA74fD6EQiEEAgEqVaqE7t27o0WLFqLB38jIKN8c7sOHD9GiRQvMnTsX//33X6l8Lg25JY0NDAwgEAhEKziKY/DgwQgLC8OdO3fk6tuuXbvg6uqKlStXYurUqXLtqzCGDBmC06dPo2LFiggLC0PVqlXx+fNncDgcTJo0CVOmTCnxyT8iIgL9+/fH/fv3sWDBAsyYMaNIcb1+/XpMnz4dKSkp+SJAzZo1Q+3atbF//36p74WI4O7ujq1bt+LSpUtlkhCamJgIIyMjTJ06FfPmzZN7f78bf1wOwcHAiBKzxn+Gw+WBr1EBmV8KT8DKhQuC67K9+HRuHcLCwsDlcmFlZYXRo0fD3t4eLVq0kOkKhLwQEdLS0qQOvf+8PjuX3DrveQdtFRUVxMTEoFGjRujTp0+Rg7qWllaZZVWXhrcxybgdVvRTZtqbOwCHmy86xOErQr2BPRJu7kNO0jfwNQtZ5cHhQqVKY3iu3owZowZj7ty5WL58OVq2bAk/Pz+xyllnZmYWWzUvN4RfXNW8pk2b5vsdADZu3IhDhw6Bx+Ph9OnT6NKlS7HCJDMzE0OGDEGDBg3g4eFRot/yRFNTE56enpgwYYJY+0MAP+amb92S7EFAUq5duwY3Nze4ubmV+S6f6enpWL58Ofbu3QsAMDIygqamJqKjozF27FjMmDFD7CiUqakpbty4gfnz52POnDm4du0aDhw4UGjoPzg4GHXq1MknBlJTU/HkyRO4urqW6p44HA68vLwQHh6OXr164d69e8Umj8oCLS0tDBo0CFu3bsWsWbPk9nn9N/BbCAL/N1/FEgPCrAxQTiaEmWlIfxuI9HePoFqn+MxvITj4mK0OZwcHrFixAu3bt5comUggEBSbIFfcoP7zOuy85CZV5R20zczM0LBhwxKf2DU0NAoMFLl71Pv5+Um8i9+vSEkiMSvmHRR0jcBVyp8folippuh8oYIAP6JLT5M10Lx5c7x48QJLlizBtGnTwOPxRPPzxT3VF1c1r3bt2rCzsyswZ19U1byEhAQsWbIE69evh5aWFkaPHo0tW7agYcOGJUYp5s+fj9DQUDx69OiXEHlubm7w8PAQO1Ewd8dDaZJFxeHVq1fo2bMnbG1tsXHjxjKLbCUnJ2PLli1Ys2YNYmJiwOfzoaGhgdDQUIwYMQKzZ8+WqnQ6n8/HwoUL0b59ewwYMAANGjTA3r17C+yHEBwcjHr18u87EBQUBIFAUOJ0g7h+HD16FDY2NnB2dkZgYKDcl1iPGzcOW7ZswYkTJ9C/f3+59vUn88sLgpTMHETEpYnVNv76DqQ8vfzjFw4XqjWtoeswusTrOOoVMXnUTGSmJuHly5cSPbH/vARLZPN/dd5/HrSNjY3FCr3LsvjKnTt3sG/fPmzbtu2PEANAySJRkBIHnnrBMCtPXVd0vig4XB7uRyQjKyoKHTt2hJ+fH/bu3YuoqCikpKTka6uvry8a1K2trWVWNS8rK0u073tGRgZmzJiBqVOnIjU1FVu2bEFQUFCxJVsfPHiA5cuXY8GCBQU+/MsLRUVFGBoa4t27d7hx40aBksY/U7lyZWRlZSEuLg56enoy9SUmJgZOTk4wNTXFsWPHymQ1UGxsLDZs2ID169cjJSUFDRo0QExMDAQCAbp27QpPT0+ZlCjv0KEDnj17hiFDhsDJyQlTpkzBkiVLoKioCKFQiJCQkAJ5Enfu3IGWlpbMdnfV1NTE+fPn0bx5c3Tr1g3Xrl2T67bqdevWha2tLTZs2MAEQSn45QXBx9hUiDtRoNmsK1Rrt4IgORZprwNAJAQEhT+B54UA1LFqg+yv7/Md5/P50NTUhIaGBjQ0NEQ/16hRQ/R73vO5x7S0tKCmpiZaelPYk8fPxzgcDrKyshATE4OYmBixrympjVAoxPDhw2FhYQFra2u8evVKKjuyuEZWbdKyBCWKRMrJAngFn4o5fMX/P18MfJ1K4GloIzY2FkZGRqhfv36BwV4eVfOICMeOHcOsWbPw4cMHuLq6Yt68eaJkKQ0NDZiamiIoKKjIcroZGRkYPHgwGjVqhBkzZsjUv9KSk5ODSpUqFShpXBi59/zp0yeZCoL09HR07doVGRkZuHnzptzzoj5//ow1a9Zgy5YtEAqFaNOmDd68eYOHDx9CSUkJ9+7dQ6NGRa98kYaKFSvi3LlzWLduHWbMmIGbN2/iyJEj4HA4SE1NLSAS79y5A2tra5kuFzQzM8PZs2fRrl07DB06FIcOHZJrFMbd3R3dunXDw4cP0bRpU7n18yfzywuCrCIyyAtDQc8ECnomAAD1eraIOfIfvp5YAMNBa0p8I3IKGTxycnIQFxcn2g73d+dXeVIsLQr6VVB52IZi23D4ioWKwVwhkCsMiryew8HFgIdir8WWBbdv38bUqVMRFBQEFxcXnD17ttAnNisrKwQFBRVpZ968eQgPD8ejR49+uToYsbGxGDx4MDZv3ozjx4+jb9++RbbNKwhk9d4VCoUYNGgQnj9/jlu3bom1/bS0fPjwAStXrsTOnTuhpKQER0dHvHjxAr6+vrC1tUVUVBQWLVokczGQC5fLxaRJk9C6dWv069cPjRo1EmX+5/17CoVC3Lt3Ty4JlVZWVti/fz969eqFmjVrYv78+TLvIxcXFxeYmZlh48aN2LNnj9z6+ZP5tT4tCkGRL71iVa1tg7jLG5ETFw0FPeNi23IhRMWKFaGnpwc9PT1UqFABurq6qFChguhY7u+5c/SFLdD4+Zg0bWRlNz4+Hv369UObNm0wa9asX84/aduEJwiw7LGgwPG88NR1IUiOLXA8d6ogd+qgOCQRo6Xh9evXmDFjBs6ePYsmTZrg+vXraN++fZHtraysMG/ePOTk5BQY8AMDA7Fy5UosXLgQlpaW8nZdItLT05Gamgpra2tERERg1qxZ6N69e5FJYIaGhgBQoIZCaZg1axZOnjyJU6dOye0p8vXr11i2bBkOHjwIbW1t9O3bF8+ePcOpU6dgZ2eHPXv2YNeuXXj+/DlGjy55SrO0NG3aFI8fP8bo0aOxZs0aKCoqQkvr/4XuixcvkJiYKJP8gcLo2bMnli1bBg8PD9SoUaNU9U+Kg8fjYcyYMfD09MTKlSvLtTT878ovLwjM9dTAAcSeNsgLZf9Yoy3MLDwbP09LLJs9BfHfvuDLlx9fMTExePbsGWJiYgpsSqOoqAgDAwMYGBjA0NCw2O+amprltkzRzc0NXC4Xu3btkvua+bKk8qdELHscUGwbRf2qSPr4HMLMtHyJhVmfflS6VDQoebXArh3b4NCsLpo0aSKX9c1fvnzB/PnzsX37dpiYmODQoUPo27dviWFbKysrpKWl4dWrV/me9DIyMjBkyBA0btwY06dPl7m/pSVvlcJly5ahfv36+D/2rjueyvd/X+dw7D0TItqlokI7LWnSHkZ7a0ehNBQq7a1FgzSotPcuSoMW7RKysznO+/eH33m+5OAcjurz+XS9XudVnue+7+d+znju636P671z507MmjVLYHtpaWloaGiITa3Q398fvr6+WL9+PWxtbcUyZmk8efIEq1evxokTJ6Cjo4MpU6YgKioKgYGB6NChA65fv45u3brh48eP2L9/P1avXv3LZH6VlJRw6NAhxMXF4fHjx2jXrh2Cg4PRqlUr3L17FxISErUqxuTi4oLY2FhMmDABBgYGtSbzPWHCBHh6emLPnj1/S7pXA388IZCXlkQ9NTl8qsRnXJyTAQl5lTLHqJiLnJhrYElKg6NRuVlQXZowwcleoOgHESErKwtJSUkMUfj53+fPn+Py5ctITEwsJ6srIyNTJWng/ytOlcNHjx7B398fmzZt+leRAUA4kijXpCN+RJxE1tMLpXQIipAdfRlSdRtXmGHAgAhBuzdj68qSxahOnTpo06YN2rZtizZt2tSIJOTk5MDPzw9r1qwBh8OBr68vZs6cKXQ8Qps2bcBmsxEREVGGEHh6euL9+/eIior641wFQIncN1Di327evDnGjx+PFStWMBkwgqCjoyMWQnD58mVMmzYN06dPx5w5c2o8Xmncu3cPq1atwrlz52BkZIRFixYhIiIC27Ztg5mZGc6dO4c+ffowGwNvb2+oqKhg+vTpYp1HVWCxWMjOzsbIkSPx4sULWFhYYP369UwMQ22SExaLhR07duDDhw+ws7PDgwcP0KCB+Esmq6urY/To0dixYwcWLlz4R/4O/mT8I4SJlp1+gYMPP1UYVf79hBeoMBfS+i0goaiO4ux05Ly8AW7qV6h2nwAlc7uKB+cV40fUWeTeDkDPnj1hZ2eHgQMHVsvcRETIzMwUSBx+PpaUlAQul1umv7y8vFDEoU6dOpXqx/N4PHTs2BE5OTl/7OJQU3Rde71SkggAyWE+yI29D6V2gyCpWhc50VdRkBAL7ZGrIFOvcnO6gbocbszvhs+fP+Px48fM69GjR0hNLXFFiEoSuFwu9u/fj6VLlyItLQ3Ozs5wc3OrVuaHiYkJOnTogF27dgEoqVXQsWNHeHl5/bE7o0uXLsHa2hofP36EgYFBGUnjVatWCezTp08fyMnJ4eTJk9W+bkxMDDp27IiOHTvi9OnTYvk9EBGuXLmCVatW4ebNm2jWrBns7e1x7949hIeHo1mzZli5ciXs7OzKWAg/f/6MBg0awMvL65dbcQoKCiAvL49t27bByckJLi4u2LJlC+Tk5ODg4ICdOysXcBMH0tPT0b59exAR7t+/XytZT0+fPoWpqSmOHz/+y1Un/0T855QKc17eRPbzyyhM/gheXhbYUrKQqtMAim0GVFrLgI8Dw4zx5OZ5hIWF4c6dElN0x44dYWdnB1tbW7GkAv0MHo+H9PT0Cq0Opf/9/v17OVVBJSWlCslCTEwMNm3ahJCQEAwcOPCX1I//1aiKJAIlAYQZt0pqGRTnZ0NKyxAqne0ha9Sm8sF5xRjb0RjLBgqWN/6ZJDx+/Jgxh/NJQmmioKOjg7Nnz8LV1RUvX77EqFGjsGrVqhp9ryZMmICoqCg8efIE+fn5MDU1haKiIu7du/fHEsDDhw/D3t4eOTk5kJMrcePwJY3j4uKgp1c+zmf8+PF4+fIlHjx4UK1rJiYmwsLCAioqKrhz506Ni1DxeDycPn0aq1evRmRkJNq2bYuxY8fi1q1bCAkJgbGxMZYtW4ZRo0YJVAqcNm0ajh8/jg8fPvzyuif8hfLu3btMIaD9+/dj/PjxUFdXR1hYGDp16lTr83j79i0sLS1hYmKCixcv1oqQUOfOnSEpKYnr16+Lfex/Gv51hACoXKa2uqBiLjR56bi4eACT1vT9+3ecOXMGYWFhuHz5MgoKCtCqVSuGHLRs2fKXxwQUFxcjNTW1QmtD6X9TUlLKBeKpqKgIJA4/H9PS0vojBGyEQXXkrEVBn+JI+LjNEUqkiojw5csXPHr0SCBJ4JerNTAwgLOzM0aNGlXjmIRdu3ZhxowZ+PHjB5YtW4ZNmzbhyZMnaNasWY3GrU1s2rQJixcvRm7u/yw7fEnjgQMHYu/eveX6eHh4IDAwEJ8/fxb5erm5uejWrRvi4+Px8OFDgYRDWHC5XBw9ehTe3t548eIFunTpggkTJuD69esIDAxE3bp1sXTpUowdO7bC3xDfOrBixYrfohx58OBBODo6lnmOHz9+HMOGDUO7du3w+PFjLF++HIsXL65xSfiqcOfOHfTo0QNjxozB3r17xf5MPXr0KEaOHInnz5//a7Krqot/JSGoqpCNqCAisHhcpB2aC07BD6xatQqTJk0q80PIysrChQsXEBYWhvDwcPz48YOpF25nZ4cOHTrU+g9HVMyaNQt79uxBWFgYeDxepdYHvum7NNTV1YVyWWhqav72ex+8+Rqi4rMBthjnwSsGJ+0Dvh1xg4yMDJYsWYLp06eLvIv58OEDUyxGXV0dBgYG+Pz5s0BLQml3g7APxidPnsDMzAw7d+7E9OnTsXr16j9Oc+BneHh44ODBg/j06VOZ41u2bMGcOXPw7NmzcpkR27dvx+zZs1FQUCBSjjyPx8PQoUNx6dIl3Lp1C2ZmZtWac0FBAQIDA+Hr64t3797BxsYGkydPxqVLl5jqgu7u7pg8eXKVwjvTp09HSEgIPnz48EvKZf8MFxcXhISE4OPHj8yxuXPnIiwsDHFxcVi5ciVWrlyJbt264dChQ7VeKIhvMfL29hY7QSoqKoKhoSH69+/PuNX+q/hXEgIACI78jEUno8U2XsHtfeDF3UHr1q1x6dIltG7dGlu2bBFoNissLMSNGzcQGhqKU6dOISEhAZqamhg4cCDs7OzQo0ePWlXiEgbR0dEwNTXF6tWrhfJPFhYWIjk5uUqrQ1JSEjIyMsr0ZbFY0NTUrNLqUKdOHairq4u9Pvrhw4cxbaEH1Ow3CBQgqja4hRgo+RyLZ02Gp6cn9u7dC0NDQ3h7e2PYsGFVLthpaWlYvXo1tmzZAjU1NaxYsQLjxo2DpKQkY0koHY9Q2pKgra1dJh6hMpJQVFTEiGLVr18fd+/e/WNdBXxMnToVkZGRePz4cZnjhYWFaNasGZo0aYLw8PAy58LCwmBnZ4ekpCSRgmMXLlyI9evX49SpU+jfv7/Ic83JyYG/vz/WrVuHb9++YciQIZg6dSrOnz+Pbdu2QVZWFi4uLnB2dhYqGO/Lly9o0KABli1b9ttiPGxsbCApKYkzZ84wx8zNzdGoUSMcOnQIAHDjxg2MGTMGhYWFOHDgAPr161erc1q2bBmWL1+OY8eOYejQoWIde+XKlfDx8cHXr1+FKgv9b8W/lhAAwNbrcVh3KbbG4yzs3RiDm8jDwcEB165dg6OjI6KjoxEVFYUxY8ZgzZo1FTJkHo+HiIgIhIaGIjQ0FHFxcVBQUICNjQ3s7OzQt2/fCqOmawtEBCsrKyQkJCA6Olrsfrn8/Hx8//69UuLA/39WVlaZvhISEtDS0hIqYFJNTa3SRTcvL4+xgtjb26PH5KVYdq7m3wc+cq/vhsvQzoxIy4sXL+Dq6oqzZ8/CwsIC69atE0gYCwoKsHXrVqxatQqFhYVwcXHBvHnzqvQT/0wS+EShNEkQFLjIYrGgq6uLxMRExMTE1HoBGXFgyJAhyMnJwYULF8qdO3bsGIYPH86k5vEREREBCwsLPH36FK1atRLqOjt37sS0adOwefNmODs7izTHzMxMbNu2DRs2bEB6ejrGjBmD6dOnIzw8HBs3bgSLxcK8efMwd+5ckX7jM2bMQHBwMD5+/PhbrAMAoKenB0dHR6xevRpAiUtFWVkZmzdvLqOHkJKSgrFjx+Ls2bOYN28evL29a7XA25gxYxAaGoqbN2+KNfUxMTER9erVg4+PD+bNmye2cf9p+FcTAqDEUuB5+gW4PBIppkCCzYIkm4UVA5tjRLuSVEQejwdfX18sWbIE5ubmGDhwIPz8/JCfn48lS5Zgzpw5lf4YiAivXr1iyAG/kEz37t2ZjAVBFcfEjeDgYIwaNQoXL15E7969a/16lSE3N7fKQEn+v6X9yQDA4XCgpaUl0NpQXFyMrVu34tu3b1i7di2mT58ONpstNpI4v2dDzOrZGHv27ClX9e369etYsGABoqKiYGtrCx8fHzRu3Bg8Hg9Hjx6Fm5sbvnz5gkmTJsHT05MR1akOBJGEx48fM2l72traMDIywv3796GgoIDXr1+L5G74XejatSv09fWZ3WhpEBEsLS3B4/HKSBp//foV9YwaYufhE2hn0R5SkmwYqstDXlqwNeTChQvo378/pk+fjs2bNws9N35p661bt6KgoADjx4/H9OnTcfr0aaxduxYFBQWYOXMmXFxcoKGhIdJ9f/36FcbGxli6dCnc3d1F6isupKenQ01NDUeOHMGoUaMAlFgDrKys8OzZM7Rs2bJMeyLCpk2b4OLigpYtWyI4OLhW0gSBks1Gjx498O7dOzx8+LDSGh2iwt7eHvfv30dsbOxvd3H+LvzrCQFQElPgFhqN229TqiyNzD/fuYEGVtuZQF9Nrlybe/fuYfTo0cjIyMCmTZsQFRWFbdu2wdjYGBs3bixXMazCeX35grCwMISFheHmzZvg8XiwtLRk4g4aNmxY7XuuCNnZ2WjcuDEsLCxqlJ71O5CdnS2U1SE+Pr5cmqa0tDRDGAr02iDdqBfAlgBLhJgC4hVDRoqDFQObo6eRPDQ0NHDy5EnY2ZVPVeXxeAgODoabmxu+fv2Kfv364fPnz3j69CkGDhwIHx+fWtupExG+fv2KR48e4cGDB9i6dSvy8/OZ7BO+JaF0hsOfRhKaNWsGa2trbNiwQeD5mzdvolu3bggODoZZt744/PAzrr1JwqfU3DL3wQJQT00OVo21MMaiHhpql+y4nz9/jk6dOqFr164ICwsTagH4+vUr/Pz8sHv3brBYLEydOhUzZszAqVOn4O3tjfT0dEyZMgVubm7VJvbOzs44fPgwPn78+Nuen7du3ULXrl0RHR3NxGmsWrUKa9asQVpaWoXv1ePHjzFy5EgkJiZi586dGDNmTK3MLzk5GRYWFpCTk2MKLYkDDx8+hKWlJcLDw2vd/fGn4j9BCPiIS8rC4YefcT32Oz6n5pYRqyEiGGrIw6qRFuwt66GBVuWmuvT0dEyaNAknTpzA1KlTMX78eLi4uODGjRsYMGAANmzYAGNjY6HnlpqaivDwcISFheHixYvIy8tD8+bNGXJgZmYmlgf24sWLsXHjRrx69QqGhoY1Hu9PQl5eHmbPng1/f38MHz4crq6uZUjE169fce7cOcTExEC5rhGUekwBW7c5iFdcKTFgEQ/EYiP/41O05cXCfc5U5ObmomfPnrhx4wa6du1aYd8nT55gzJgxePXqFdhsNsaNG4fNmzczqXS1jfnz52Pbtm04ffo0rK2t4eHhASISaEn4OXBRV1f3t5EELS0tzJ49u9JdsvXg0XijbAbUaSoS0Z/dQQt2vbtAU1MTt27dqtJV8+7dO/j6+uLAgQOQl5fHrFmzMG3aNJw6dQorV65EYmIixo4diyVLltRoxxofHw8jIyMsWbIEHh4e1R6npti2bRvmzp2LnJwcJguib9++4PF4Al04pZGVlYXp06fj0KFDGDt2LLZs2VIrKZOvXr1C+/btmQVcXHoR5ubmUFdXr/I+/60Qaf0mIZCZmUkAKDMzU5jmvw3Z+UUUE59BUZ/SaFfIWWJxZOj9+/cijcHj8WjHjh0kIyNDLVq0oJiYGDp69Cjp6emRtLQ0ubu7U3Z2tshzy8nJodDQUHJ0dCRVVVUCQPr6+uTs7EzXrl2joqIikcckInrz5g1xOBxavnx5tfr/yXj16hWZmJiQjIwM7d27l3g8XpnzT548oRYtWpCUlBT5+flRcXExERG9Scgk15BH1H7VBTJYFF725XqGGsw9RI1GuZNOE1NisViEEtFD5iUnJ0cNGjSgjh070pAhQ2j69Om0YsUKWrNmDfXu3ZtYLBbp6enR9u3bac6cOcThcKhu3bq0d+9e4nK5tfqe3Llzh1gsFq1Zs4Z4PB6pq6vTsmXLmPM8Ho8+f/5MoaGh5OHhQX369CFNTU3m3rS0tKhv3760ZMkSCgsLoy9fvpR7X2sDxcXFxGazadeuXRW2CYr4RA3dz1K9hWHlP7dKXkaLw8lgYSjpdR1OX79+rXQeMTExNGbMGGKz2aSlpUU+Pj6UlpZGAQEBZGRkRCwWi0aNGkVv3rwRy307OzuTiooKZWRkiGW86mLKlClkYmLC/F1cXEwqKiq0YsUKoccICAggeXl5aty4MT158qQWZkl05coVkpSUpGnTpontexkQEEAA6PXr12IZ758GUdbvfxUhKI34+HgCQKGhodXq//z5c2ratCnJysqSv78/ZWVlkbu7O0lJSZGenh4FBwdX+wtbWFhIV69epZkzZ5Kenh4BIDU1NXJycqKwsDDKyckRahwej0fW1tZkaGhIubm51ZrLn4pDhw6RvLw8NWnShJ4/f17mHJfLJR8fH+JwONSqVaty50uDTxKXbtpHUtpG9CO3oMz5tLQ0UlVVpWHDhtHq1asJAHl4eNDChQvJwcGBevfuTS1atCB5eflyxAEAKSsrk5GREbPoqqurk5OTE/n7+1N4eDhFRkbSly9fqKCgoIIZCo+cnBxq2LAhWVpaMsTDxsaG+vbtW2m/n0mCjY1NOZJgY2NTqyQhJSWFANCJEycEnt9yLVYkEvDzq57rGTJYFE5brsUKHD8yMpJsbW0ZIr5lyxbKzs6mkJAQatKkCQEgW1vbSr9LoiI+Pp6kpaVFWnRrCx06dKDRo0czf0dHRxMAunr1qkjjvH79mlq3bk1SUlK0ZcuWWiGT/v7+BIA2btwolvHy8vJIU1OTZs2aJZbx/mn4Swio5CGooaFRo51zTk4OTZo0iQDQiBEjKCMjg96+fUsDBw4kANStW7caP0B4PB5FRkaSm5sbNWvWjNmh2tnZUWBgIKWmplbYNywsjABQWFhYjebwJyE3N5d5z+3t7SkrK6vM+ffv31OnTp2IxWKRq6sr5efnCzVuSEgIAaD09PRy59atW0cSEhK0bt06AsAQsqKiItqxYwdpa2uTtLQ0LViwgN68eUNPnz6lCxcuUEBAAPn6+tK8efNo9OjR1K5dO5KTkxNIHPikr2nTpmRlZUUjR46kOXPmkLe3N+3fv5/OnTtHUVFR9O3btwqtRXPnziVpaWl69eoVc8zT05M0NDREfjALIglaWlrlSIKHh4dYSMKrV68IAN26davcuaCITzUiAz+/giM+Mfd448YN6t27NwGghg0b0t69eyk/P5/Cw8OpdevWBICsra0pIiKi2vdWEWbPnv1HWAd4PB4pKSmRt7c3c2znzp0kISFRLWtnfn4+zZo1iwDQoEGDKCUlRZzTJSIiFxcXYrFYdPr0abGM5+bmRoqKivTjxw+xjPdPwl9C8P/o0aMHDR48uMbjBAcHk5KSEtWvX58ePnxIRETnz5+nRo0akYSEBDk7O1NaWlqNr0NU4gLw8fEhS0tLAkASEhLUo0cP2rJlC3358oVpl5ubS4aGhtSnT59fYvL9FXj9+nWFLgIej0f79u0jBQUFMjQ0FLiwVIbLly8TAIEupLy8PNLT0yNTU1OSlpam4uJiOnXqFLNztLe3p48fPwp1HR6PR2FhYdSoUSMCQP369aOAgADat28feXt70+zZs2nkyJHUrVs3atq0KeM+Kv1isVikqalJLVq0oJ49e9KYMWNo5MiRBIBGjRpFFy9epGfPnlFiYiKdPn26wvsSFTwej758+SIUSQgNDRWJJNy+fZsA0MuXL8sc/5yaQ408zpH+vGOk3HEUydQ3I7aMQom1pe8cgQt+3Yk7SKa+GbE4MsSWUSD55lakN+swc76RxzkKPHGWOnbsSACoZcuWFBwcTFwul65evUrt27cnANSlSxeRv0fC4tu3byQjI1PGnfO78PHjRwJA4eHhzDEHBwdq06ZNjcY9deoUqampkZ6entjfx+LiYho8eDDJy8uLxT3x5csXkpCQoK1bt9Z8cv8w/CUE/4+5c+eSsbGxWMZ69+4dmZubk6SkJK1Zs4aKi4upoKCAfH19SUFBgTQ0NMjf31+sPuT4+HjasWMH9e7dmyQlJQkAtWvXjlatWkUzZswgDocjNl/n78bhw4crdBEkJSXRoEGDCACNHz++Wt/DyMhIAkBRUVECz+/du5dxAXTp0oUAUPfu3enx48fVup/CwkLavn07aWlpkYyMDC1atKjCnWJ+fj59/vyZIiIi6MyZM+Tv709eXl40c+ZMGjZsGHXs2JE4HA5JSEiUIw9sNpsAkIGBAfXu3ZscHR3JxcWF/Pz86PDhw3TlyhWKiYmh5ORkJsZCFPBJQlhYmFAk4fPnzwJJwsmTJwkAJScnlzluv+cBGbmdJd2pJe+/hJImSdczqZAQ6E4/QGxZJZJU0SHVnpNJpYsjsWUUiKNVn+otDC1p53KKtEasIAsLCzp9+jTxeDy6d+8ede/enfkNXbx4sVaJ9Jw5c0hZWVmgRepXIzw8nADQp0+fmGNGRkZiMaF/+fKFunTpQmw2m1asWCHW519OTg61bduWdHV1q4wNEQZDhw6lJk2a/Gs2UMLiLyH4fxw4cIAAiM1MVFBQQC4uLoyZMTExkYhKFm57e3sCQG3atKH79++L5XqlkZ6eTocPH6ahQ4cyZmk1NTVydXWlBw8eVOth/yegKhfB6dOnSUtLizQ0NKodD0JE9Pbt20p9pm/evCEpKSkCQC1atKBz586J5cHx48cPWrJkCcnKypKGhgZt3rxZ5HiC2bNnk4yMDL1584Zyc3Ppw4cP9ODBAwoLC6Ndu3aRiooKtWrVigYPHkwdO3YkY2NjgTEPkpKSVLduXTIzMyMbGxsaN24cLVq0iDZu3EhBQUF0/fp1evnyJaWlpVV676VJwpIlS8qRBE1NzXIkYdeuXcRiscosGLGJP/4XA7AglPRmHiSDReFUx2lDhYRAwbQvsSSlSXfaPuaY1kivkt9Dn5ll2sYm/qCoqCjq168fASATExMKCwur9QUhISGBZGRkyNPTs1avIyy8vb1JWVmZue+EhAQCQEePHhXL+Fwul5YtW0ZsNpu6desmlsWbj2/fvjHWu5+fDaLixo0bBIAuX74sptn9M/CXEPw/njx5QgDo7t27Yh33woULpKWlRdra2mW+XHfu3GH8kk5OTpSQkCDW6/IxYMAAUlNTIwcHB9LQ0CAAVLduXZo2bRpdunRJLAFsvwKvX7+mli1bkoyMDO3Zs6fMgzorK4shCv3792fIV3WRmppKAOj48eNljqekpDCZAnxCcPHixRpdSxDi4+NpwoQJxGazqUGDBnTs2DGhFqZbt24Ri8UiPz+/CtuMGDGCOnXqVO54VlYWvX37lu7evUsnTpyg7du309KlS2nKlCk0aNAgsrS0JENDQ5KRkSlHHqSkpEhfX5/atm1L/fv3p4kTJ5K7uztt2bKFQkJC6NatW/TmzRvKzMwkHo9XjiT07du3DEmQl5cnDodThiQsPRVNRm5nyy36lRECtpwKyTXpVO64pJouyRi0Yv6uvzic2k1dw8QOBAUF/TLSPHfuXFJSUhKbG7GmGD16NHXs2JH5+/jx4wRArAs3UcmCq6urS+rq6nTmzBmxjfv06VNSUFCggQMH1sgCwePxyMTEhAYOHCi2uf0T8JcQ/D/y8/NJUlKStm/fLvaxExISqGfPnsRisWjx4sVUWFhIRCVseefOnaSmpkaKiork5+fHnBMHLly4QAAoODiYud7Nmzdpzpw5ZGBgwJi9x4wZQ8eOHasxq64t8F0EjRs3LuciuHv3LhkZGZG8vDzt3r1bLDs6LpdLAGjPnj1EVBI34OvrS8rKyqSgoEBeXl5kbW1Nqqqq1LZt21rbRT5//pxsbGwIALVv375SspqdnU3GxsbUsWPHSh+Efn5+JCsrW+3UVR6PR5mZmRQbG0u3bt2iY8eO0ZYtW8jd3Z0mTpxI/fv3p3bt2pG+vj5Dmkq/ZGRkyNDQkCwsLGjQoEE0efJkWrp0KW3dupV2795N3t7e1KpVK5KTkyNtbW2mn/70vQJjBCoiBLozSix+Kt3Glusj39yK2DKKZTMPZuyjvXv3Vvt9qQ4SExNJVlaWlixZ8suuWRVMTExo6tSpzN9z584lAwODWrlWcnIy9e/fnwDQnDlzhA76rQpnz54lNptN8+fPr9E4u3fvJhaLRR8+fBDLvP4J+EsISsHExISmTJlSK2MXFxeTt7c3SUhIkKWlZZkvWWpqKk2fPp3YbDY1adKELl26VOPrFRQUUKNGjahbt24CFywej0dPnjyhpUuXUsuWLQkASUtL04ABA2jv3r30/fv3Gs+hpijtIhgzZkwZwlJQUEBubm7EZrOpffv2FBcXJ9ZrKykpka+vLx08eJDq1atHEhISNH36dEpKSiKiktQsa2trAkDHjh0T67V/xpUrVxhr0uDBgyk2tny63KxZsxhXQWXgB+w9ffq0tqbLgMfjUVpaGr169YquX79OwcHBtHHjRlq8eDGNGzeO+vbtS2ZmZqSrq8vEvZR+ycvLk4GBATVu0YoM/j9VUFhCwBzvP69cHyWLwQSA6i0IZY4ZLgqn7PxfRwaIiObPn09KSkqVZgf9ShQWFhKHw6Ft27Yxx8zNzcukIIobPB6PNm3aRFJSUmRmZibwu10dbNmyhQDQzp07qz1GdnY2qaio0MKFC8Uyp38C/hKCUrC3t6f27dvX6jXu3btHBgYGpKysXG4hefLkCXXu3JkAkJ2dXY2Yqa+vL0lISFB0dLRQ7d++fUt+fn5Mmh6bzaYuXbrQhg0bfgtDrsxF8OLFCzI1NSVJSUlatWpVrezq+G4e/mfxs1BJ06ZNae7cudSnTx9q1KhRre8si4uLKTAwkPT19UlSUpJmzpzJkLabN28SAFq/fn2V42RnZ5OEhATt3r27VucrKoqLiyklJYViYmKoXbt21LZtW/Lz8yMXFxcaPH5WhWmDFREC7TE+BIA0BrmW66PcoSQLQ39OcJnjMfG/LuWPbx3w8PD4ZdesCny9gZs3bxJRSaCepKRkGYJQW4iKiqKGDRuSgoICHTx4UCxjOjs7k4SERI02WPPnzydVVVWh9V7+6RBl/RZvTdo/EC1btsTz588ZzffaQPv27fH06VP06tULw4YNw9SpU5GXlwcAaN26NW7evIkjR47g4cOHaNq0KZYtW8acFxbx8fFYsWIFZs6cWa5mfEUwNjbGvHnzcPv2bSQkJGDXrl1QUFCAq6sr6tevDzMzM6xYsQLR0dGgqhWsa4SgoCC0bdsWBQUFiIiIwIQJE8BiscDj8bBp0yaYmZkhPz8fDx8+hJubm1hL+cbExKBv3774/v07AOD27ds4efIkGjduXKZdWloa1NTUsHr1asTGxmL//v1im4MgsNlsODg44M2bN/Dy8kJgYCCMjY2xbNkyODk5oWPHjpg1a1aV48jLy6NFixaIiIio1fmKCjabDXV1dTRv3hw8Hg9mZmaYN28efH194eG5TOTxWJLSAAAqLip3jooLy7Tho5Bbe7/7n7Fu3TpISkpi7ty5v+yaVSE6uqRcvImJCQAgMjISXC4XHTt2rPVrm5qa4vHjxxg8eDAcHBzg5OSE7OzsGo25fv16WFtbY+jQoXjx4kW1xpg+fToyMjJw5MiRGs3l34h/PSFo1aoVcnJy8P79+1q9joqKCkJCQrBr1y4EBATA3Nyc+cKyWCyMGjUKb968wZw5c7B69Wo0bdoUJ06cEHohXrhwIeTl5bFs2bJqzU9bWxsTJ07E2bNnkZKSgqNHj6Jx48ZYt24dWrZsiYYNG2LBggW4e/cuiouLq3UNQcjLy8OUKVMwevRoDBo0CI8ePWIeTl++fEHv3r0xZ84cTJ06FY8fP4aZmZnYrh0fH48JEyagVatWiI2NRbNmzWBlZSWwfDERMYTA1NQUo0aNqhZxqw5kZWXh6uqKd+/eYfz48Vi5ciU+ffqEAQMGCD2Gubn5H0cISiMlJaVMlUApSdEfPRIKJTXti7PTyp0rzk4HW0YRLElOmePVuU518P37d2zfvh2zZs2CmpraL7mmMIiOjoauri5UVUveu7t370JJSUnoTUVNoaioiICAAAQGBuLEiRNo06YNnjx5Uu3xJCUlERwcDAMDA/Tr1w9JSUkij2FkZIR+/fph69attb4R+qfhP0EIAODZs2e1fi0Wi4XJkycjMjISPB4P7dq1w549e5gvnYKCAry9vfHixQs0b94cQ4cORa9evfDy5ctKx7158yaCgoLg6+sLFRWVGs9TUVERw4cPR1BQEJKTk3H+/Hn06NEDhw4dQqdOnVC3bl1MnjwZ586dQ0FBQbWv8+bNG1haWiIwMBB79uzBwYMHmaIoR44cgYmJCV6/fo3Lly9j48aNkJWVrfG9ASXFPDw8PNCwYUOcOnUKGzduxMuXL9G4cWNkZGQI7JOTk4OioiLmYb5ixQp8//4dW7ZsEcuchIGGhgbs7OzA4/HQsmVLLFq0CG3atMGlS5eq7Gtubo6YmBjk5OT8gpmKjuTk5DKEgH58ByDaw1hSUQNsOWUUJr4td64gIRZS2vXLHiTCpROHa91CCAB+fn5gs9l/lHUAKCEEfAIOlBACS0vLX14K2MHBAVFRUZCXl4elpSU2b95c7cVYUVER4eHhKCgogK2tbbVIu7OzM549e4Y7d+5Uaw7/VvzrCYG2tja0tbV/CSHgo0WLFoiMjIS9vT0mTZqEkSNHIjMzkznfsGFDnD17FmfOnMHHjx/RsmVLzJ07t0wbPrhcLmbOnAlLS0s4OjqKfa7S0tLo06cPdu3ahW/fvuHu3btwcnLC9evX0a9fP2hqamLkyJEIDg7Gjx8/hB6X7yLguwH4LoK0tDSMHDkSY8aMQd++fREdHY2ePXuK5V6Kioqwfft2NGjQAH5+fpg9ezbevXsHZ2dnSElJQVVVFenp6QL78o/zd1INGjTA5MmTmRK4vwLZ2dkYP348OnfujKioKNy/fx8KCgqwtraGtbU1nj9/XmFfc3Nz8Hg8REVF/ZK5ioLc3Fzk5uYiISEBs2fPRqNGjWDStBG46YkijyXXuAPy3kaC+yOZOZb38Sm4afGQa1LW8iORlw7X+XPQqlUrqKmpoV+/fvDx8cGdO3dqRHR/RnJyMrZu3QpnZ2eoq6uLbVxxoDQh4PF4uHfv3i9xFwhCo0aNcP/+fUyfPh2zZ8/GoEGDkJqaWq2x6tWrh9OnT+PZs2cYN26cyISvZ8+eaNy48S8l/P8IiDso4U9E7969f1vu6dGjR0lJSYkMDQ3pwYMH5c7n5+fT6tWrSU5OjrS0tGjfvn1l8qU3bdpELBaLHj169CunTTwej6Kjo2nlypVkZmbG5Kbb2NjQrl27KtQFyM3NpcmTJxMAGj16dBlRqEuXLlHdunVJRUWFgoKCxDrXkydPUqNGjYjFYpGTkxN9/vy5XLv58+dTo0aNBI7x9OlTAlBG0z4hIYHk5ORo0aJFYptrZZgxYwbJysqWya7g31vDhg2JxWLR2LFjy0hY81FUVERycnK0bt26XzJXYRAXF0ebN2+mbt26/S/VUF+fJk+eTKGhobT4eFQZHQLVXlNIubM9KZj2JQAk16gDKXe2J+XO9qQ/5+j/KxXuL6VUOIVUuv6/UqGmYZkMAyO3s+R5KoZycnLo+vXrtHLlSurduzcpKCgw2TedO3emxYsX07lz52pUb8DV1ZUUFBTKqTD+bvCf24GBgURUUukRAF25cuU3z6xEcExdXZ10dXWZgMfq4MSJE8RisaoVyLllyxaSkJAQux7Dn4a/WQY/YeHChWRoaPjbrv/+/XtG9tjX11egQMqXL18YvXoLCwuKiIigpKQkUlZWrrW0SVHw6dMn2rRpE3Xr1o3YbDaxWCzq0KEDrV27lt6+fUtEJWp//CwCf39/JosgJyeHnJ2dCQD16tVL4IJWXdy7d4/RrO/Vq1eluucrV64kLS0tgeeuXbtGAJh74cPd3Z1kZWUpPj5ebHOu7PqbNm0SeL6wsJC2bt1KmpqaJCsrS25ubuV+j507d6bhw4fX6jwrQ25uLp07d46cnZ2pQYMGBIA4HA61a9eOUcYrnVlSWqnQYFE4SSj9T8jo55fu1P9pFuhM2EYy9U2JxZEmtrQ8yTfvRnrOB8tlHsQllVcoLSoqosePH9PGjRtpyJAhjHgSi8WiVq1a0cyZM+no0aNCf97JyckkLy//y0ijKLh79y4BYH4Tu3btIgkJiT9Gm+Tr16/UtWtXYrPZtGzZsmqLDq1ZUyJAFRAQIFK/zMxMUlBQ+KOyQmoDfwnBTzh48CAB+K1VxwoLCxnZ4969e1e4w75x4waZmJgQi8WiRo0akYqKSq1UE6sJkpOTaf/+/TRo0CBG5U5PT4+kpKTIwMCgTD58ZGQkNWnShGRkZGjz5s1iU4uLi4ujoUOHElBSvEYYdcGtW7cSh8MRqOHAV2/7WV0uIyOD1NTUapWUZWVlkaGhIXXp0qXK9yczM5Pc3NxIRkaGNDU1aevWrYzw1fz583858Y2NjaVNmzZRnz59mO9CvXr1aMqUKRQWFkY/fvxgxLQEFYji1zIQZ7VDI7ezZL+nvDVOEHg8HsXGxtLevXtp3LhxDJEBQEZGRkwp69evXwv83ixatIjk5eX/OOsA0f8qGvLFgRwdHcnMzOw3z6osuFwuLV++nEmJrs5mgcfj0cSJE4nD4dCNGzdE6jtz5kzS0tISm4DSn4i/hOAnPH/+nADBpVd/NS5evMjkw1eUS1tUVEQLFiwgACQrK0sbN278pWproiA5OZkpL8vhcAgoKbTj7OxM48aNIwkJCWrTpk25Knc1uZ6zszNJSkqSnp4eHThwQOidxeHDhwmAwJKv/v7+xGKxBC7I/PLItVVIavr06SQnJ1fOOlEZvnz5QmPHjiUWi0UNGzakEydOUHBwMAFghJZqAzk5OXT27FmaOXMmGRsbM66kHj160Lp16+jFixflFs5Dhw4RAIF5359SsqmBW3iFIkXVeTXyOEefU6ufY/7t2zc6duwYzZo1i0xNTZkCUpqammRnZ0fr16+niIgISkxMJAUFBXJxcan2tWoTM2bMoKZNmzJ/Gxsbk7Oz82+cUcW4efMm6enpkZqaWrVKHhcWFlL37t1JVVVVpN8pvyy3uHQS/kT81SH4CU2aNIGUlNQvDSysCL1798azZ8/QsmVLWFtbY/HixSgqKptXzWKxcP36dZiYmMDe3h5z585F69atcf369d80a8GIjY1Fz549cevWLfj7+yM7OxuXL19Gp06dsHPnTuzfvx9SUlJo0aIF4uLiapTCl5eXBx8fHxgbG+PAgQNYsWIFYmNj4eTkJHTEND9gUFCmQVpaGlRUVMBml/9JzJgxAzo6OliyZEm1518Rrl27hu3btzP3Jiz09PSwf/9+PH36FEZGRhgyZAjWrVsHoCTXXFwgIsTGxmLTpk3o06cP1NXV0a9fP5w5cwa9evXCqVOnkJqaiitXrmD+/Plo1qwZWCxWmTGSk5MhKysLOTm5Msfv3LkDh8F9kRi+GfipT02wYmBz6KvJVd2wAujo6GDo0KHYtGkToqKikJ6ejgsXLmDy5MlIS0uDm5sbzM3Noa+vj9zcXAAlnyP//38KSgcUJiUl4d27d78toLAqdOnSBU+fPkWnTp0wcOBAzJ49W6TATw6Hg+PHj0NbWxv9+/cXOlixSZMm6NWr19/gQj7EzTD+VLRu3ZomTpz4u6fBoLi4mHx8fATKHu/evZsA0J07d4iI6PHjx9ShQwcCQMOGDStTxvR34ciRI6SgoECNGjWiZ8+eEVGJ6W7nzp0kJydHxsbGtGfPHlq8eDE1adKEka0dMmQIHTx4UOjCL1wulw4cOEB6enokKSlJzs7O1ZZgvnfvHgEQqPTo6upaaalsfnlkcQZ38l0FXbt2rbEr5dKlS9SqVSsCQE2bNq2R7HNOTg6Fh4fTjBkzyMjIiLEC9OzZk/z8/Ojly5ci1Xpwd3enevXqMX9HRkZSnz59CAC1atWKzpw5Q5uvxtbMMvD/FgafM0+qfd/CIj8/n86dO0dSUlJkbGxMqqqqBJRUk7SwsKD58+dTWFjYb3Uj8Hg8UlVVpZUrVxJRSfAdALHG79QGeDwebdmyhaSkpMjU1FRkq9zbt29JQ0ODunTpInSRt9OnTxMAevjwYXWm/Mfjr8tAAJycnKhdu3a/exrlUFr2OCQkhFJTU0ldXZ0cHR3LtOPxeBQYGEh16tQhWVlZWrlyJeXl5f3y+ebm5tKUKVMIAI0aNYrJIkhISKC+fUuiw6dMmVIucOnVq1fk7e1N5ubmzMOzZ8+etG3btgqjfEsvckOHDq2xJjrfPCjIdTRp0qRKvx9FRUXUtGlT6tWrV43mUBrTpk0jOTk5evfunVjG43K51KpVK5KRkSEOh0OzZs0SalHi8Xj0+vVr2rBhA/Xu3ZukpaUJABkaGtK0adPo9OnTNQpEmzx5MpmZmVF0dDTZ2dkRAGrSpAmFhISUIUJBEZ+okcc5kWMKjNzOUkP3s1S381CysLCg3Nzcas9VWLi7u5OcnBwlJSVRcXExRUdH0/bt22nUqFGkp6fHxCE0bdqUJk+eTIGBgfThw4daL73Mx9evXwkAhYWFERHRvHnzaq2gUW3gyZMn1KhRI5KXlxc5WPDOnTskJSVFTk5OQr3fXC6X6tevTw4ODtWd7h+Nv4RAANavX0+ysrI1Kp9ZW0hPT6dhw4YRAGrWrBkpKChUWDo5MzOTFixYQJKSklS/fv1fUt+djzdv3jALTukqhCdOnCB1dXXS1tam8PDwKsf5+vUrbdu2jXr27MkUwDE3Nydvb2969eoVPX36lIlL6NChA927d08s809MTCQAdOrUqXLnhg4dSr179660/8mTJ8WWtnX16lUCQFu2bKnxWKWxfPlyUlVVpVWrVpGioiIpKSmRj49PuUUyOzubzpw5Q9OnT6f69eszVoBevXrR+vXr6dWrV2L7XvXu3Zt0dHSIxWJR/fr1KSAgoMLf4efUHLLf86BkoV9cNREwWBRO9nse0OfUHIqMjCQ5OTkaPHhwrZY6Tk1NJUVFRVqwYEGFbT5+/EgHDx6kKVOmULNmzRiCoKenRyNHjqRt27bR8+fPa22e58+fJwAM2bSwsKjVgka1gaysLHJyciIA5ODgUCaFuSrw44VWrVolVPu1a9eSlJRUrcbf/C78JQQCcOXKFQJQrqDNnwIej0ceHh4EgOrUqUMxMTGVtn/16hWzaFpbW9f6fQUFBZVzEWRkZDA/WDs7u2qZ8tPS0ujgwYM0ZMgQJkodAKmqqpKvr69YCVx+fn6F6Undu3enkSNHVtqfx+ORhYVFjcsj//jxgwwMDKhbt25iXxD4C0FcXBx9//6dZs6cyQRg+vj4kJ+fH/Xq1YuxAtSvX5+mT59OZ86cERhsWRN8+vSJJkyYwATH7ty5U2gzbmziD3LaHE51J+8uRwQMF4VTl7XXyPNUTLnUwlOnThGLxapxmdzKsGTJEpKVla0wU0gQkpOT6dSpU7RgwQKytLRkiLCKigr169ePfHx86M6dO2KLdl+zZg3Jy8tTcXEx5ebmlqt4+E/CwYMHSUFBgRo2bEiPHz8Wut/y5cuZdNeqkJqaylhe/234SwgEIDk5Wegvx+8Aj8ejjh07kpGRETVr1oxkZWXL7MIr6hMWFkb169cnSUlJWrBggdg/o7y8PJo6dWo5F8GNGzfIwMCAFBUV6cCBAzVaIDMyMmjx4sUkIyNDysrKZGlpSWpqaiX557q6NGPGDLp8+TKTXlcT8LM2fkbr1q1p+vTpVfa/fv06ATUrjzx16lSSl5en9+/fV3uMipCSkkIA6PDhw5SdnU2nT5+mUaNGkZycHJNv37ZtW9q4cSO9efOmVqxLCQkJ5OzsTFJSUqSpqUlaWlo0c+ZMkcfx8vIiZWVlysorpJj4DIr6lEYx8RlVljTevHkzAaCtW7dW9xYqRFpaGikpKdG8efNqNE5OTg5du3aNVqxYQb169SJ5eXkCQDIyMtSlSxdyc3Oj8+fPV/v37ODgQBYWFkT0v8qZv6I8dm0hNjaWzMzMiMPh0MaNG4X63vJ4PLK3tydpaWm6f/9+le0nTZpEurq6YnnO/En4SwgqQN26dcnNze13T0Mg+FoJV69epZycHEbtb9iwYZSenl5p39zcXFqxYgXJyspSnTp1KDAwUCwPer6LQFpamiEn+fn5tHDhQmKxWNSlS5calVEuKCigzZs3k4aGBsnKypK7uzvzHSsqKqIbN27Q7NmzqV69esxuyt7enk6cOFHt3WzdunXJ09Oz3PF69eoJLVBSk/LIly9fJgC1slvj8Xj08uVLUldXJ319fZKSkmLy6WfMmEFr165lYjj69OlDz58/F+v1U1JSyMXFhWRlZUlFRYVWrVpFWVlZpKGhQV5eXiKPN3jwYLKysqrWXObMmUNsNpvOnDlTrf4VYenSpSQrK1uhS6+6KCoqokePHtGGDRto8ODBjGASm82m1q1bk7OzM4WEhNC3b9+EGq90EPXq1atJSUnpj3SXioL8/HyaO3cuAaD+/fsLFR+Tn59PnTp1Ik1NzSoJ+LNnzwgAhYSEiGvKfwT+EoIKYGNjQ/369fvd0yiHzMxMqlOnTjmVuZCQEEb2WBiG++nTJyYWoUOHDiKZ135GaRcBf2fx7NkzMjExISkpKVq7dm21HzA8Ho+OHTtGDRo0IBaLRePHj680+pnH41FUVBQtWbKETExMmJ3UwIEDaf/+/SJFczdv3pxmz55d7riioiL5+fkJNUZUVBQBoN27dwt9XaKSz7levXpkZWUlNldBVlYWnTp1iqZOnUoGBgbMIqKsrCzQCsDj8ej48ePUoEEDYrPZNH78+BpLt2ZmZpKnpycpKiqSvLw8eXh4MCSWy+USm82mXbt2iTyuoaFhtU3/XC6XbG1tSU5OTmyZIenp6aSsrExz584Vy3iVgcfj0Zs3b2jPnj00duxYRvMBABkbG9PYsWNpz549Aq08RUVFJC0tzahe9uvXr8r4mH8SwsPDSUNDg+rWrUvXr1+vsn1ycjJjea1KnK5Lly7UuXNnMc30z8BfQlABFi1aRHp6er97GuUwf/58kpOTE6i///79e7KwsCBJSUny8fERaiG5evUqNW/enFgsFk2ePFmkBVOQi4DL5dKaNWtISkqKTExMmBiC6uDOnTvUvn37Gu1S4+LiaO3atdSxY0disVjEZrOpW7dutGnTJoFqeKXRsWPHchkchYWFBID2798v9BxGjRpFdevWFSmiffLkyTV2FfB4PHrx4gWtW7eOevTowVgBjI2NaebMmXT27Fny9fUlaWnpSk2ffOuMuro6ycrKkoeHh0hBW0QlgYm+vr6kpqZGMjIyNH/+/HJxJHxX3YkTJ0QaOzU1lXF9VBc5OTnUrl07qlOnjlhSdT09PUlGRkbs1gFhER8fTyEhIeTs7EytW7cmFotFAEhLS4sGDx5MGzZsoEePHjFCbNeuXaPi4mJSVVWl5cuX/5Y51xa+fv1K3bp1IxaLRUuXLq3SWvfq1StSUVGhXr16Vfq7OHbs2D/evfIz/hKCChAUFEQAKDU19XdPhcGLFy9IUlKy0mjYwsJCcnV1JaBEr1+YYKbCwkLauHEjKSsrk6qqKm3durXKH01sbGw5F8GHDx+oS5cuxGKxaOHChdUOenrz5g2Tcta6dWu6fPlytcb5GQkJCbRr1y6ysbFhlBLNzMxoxYoVFB0dXW731L9/fxowYECZY0lJSRVmH1SEuLg4pjaFMLh06RIBoO3btwt9DT6ysrIoLCyMpkyZwrhPZGRkyMbGhjZv3lxOc4CvYS+MhSgjI4MWLVpEMjIypKWlRdu3b6/Sh5qfn0+bN28mbW1t4nA4NG3atAqtDJWlelYGcQUBJyYmkqGhIbVo0aJG0uV864Ag69LvQkZGBp0/f57c3NyoS5cuTKAoPzjX1dWV9u3bJ7bMmD8NXC6XVq5cSWw2mzp37ixwQ1UaV69eJUlJSZoyZUqFLtXCwkLS1dX9ozRraoq/hKACvHjxggAIZWb6FeDxeNSjRw9q0KCBUAutMLLHPyMpKYkmTJhALBaLWrZsWWFlseDg4DIuAh6PRwcOHCBFRUUyMDAQWSO89PWnT59OEhISpK+vT4GBgbWWapWZmUlBQUE0YsQIUlRUJADUoEEDWrBgAd29e5eKi4vJwcGBOnXqVKYff9G6ffu2SNebPn06qaioVCmylJmZSfr6+tS9e3eh7p3H41FMTAytXbuWunfvzhCdhg0b0qxZs+j8+fOVWiZyc3NJQkKCduzYIfS9fP78mRwdHYnFYlHjxo0FprMWFhaSv78/6evrE5vNprFjx1Zp7bh16xYBEFm6es2aNaSgoCCW78rLly9JRUWFevbsWe2AseXLl5OMjIzQPvzfgfz8fLpz5w51796dpKWlSUVFhXEztGvXjhYsWECnTp3642qj1BS3bt1iZI+rIvV8grR+/foK23h5eZGsrOwftXGsCf4SggrA960JijL/HeCbp86ePSt0n4SEBOrVqxezAxD2ARcREcEElI0cOZLx2QtyESQnJ9PgwYMJADk5OVXrc8/JySEvLy9SVFQkZWVl8vX1/aVCSnw1uUmTJjEBWtra2tSiRQsyMDAok/7GVzCsKtXzZwhbHnnSpEmkoKBQaQDmjx8/KDQ0lCZPnkz6+vrMTq9v3760ZcsWkeocEBGZmprSuHHjROpDVCII07NnTwJAnTt3pgcPHhCXy6VDhw4xhX9GjBhBr169Emo8vnaDqKp9I0eOLEfcaoJr164Rh8Oh8ePHixxwm5GRQSoqKjRr1iyxzac2MWjQIOrVqxcVFxfTwIEDSV9fn0aOHEm6uroMQWjWrBlNmTKFDh48WKWb7Z+A1NRUsrW1JQDk7Oxc6bNm0aJFxGKxGNGmn5GUlMTESf0b8JcQVII2bdpU60EpbmRnZ5O+vn4587UwKC4uJl9fX0YqVVifdHFxMe3bt4+0tLRIXl6e5s2bx7gIdu3aRTwej8LDw0lbW5vU1dVF9vsSlZjx9u7dS3Xr1iUOh0Nz5sz57TsSLpdLd+7cofnz5zMys0pKSjRy5Eg6evQohYSEEIBq7f6qKo988eJFAlBut87j8Sg6OprWrFlDVlZWIlsBqsKUKVOoefPm1erL4/Ho/PnzTACnkpISAaABAwaI7FvdtWsXsVgskQNQGzVqJPYFODAwkACInPGwYsUKkpaWrvUS2OKCkZERkxbZoEEDpqAR3wUYGBhIkydPpqZNmzIEQV9fn0aNGkXbt2+n6OjoWhV2qi3weDzaunUrSUtLU6tWrSp0NxUXF9OQIUNITk6uQreag4MDGRoa/uMzM4j+EoJKMX78+D+iBKiHhwdJS0uLvPMrjfv375OhoSEpKSmJlCqTkZHByAxzOBzavHkzZWVlMZLEffv2FTlw6udFZMSIETW6t9rC+vXrSUZGhpYvX06mpqYEgBGJ2b59u0hiM0SVl0fOyMggfX196tmzJ/F4PMrMzKSTJ0/SpEmTGHlbWVlZ6tevH23dulWs79fevXuJxWKJHChI9L/P0szMjACQtLQ0SUpKVovcrVq1ijQ0NETqw3/eHDhwQKR+woAvVnPo0CGh56KqqvrHVgn8GVlZWUyALF+ZMzg4uML2ycnJFBYWRvPnzydzc3Pmt6Cqqkr9+/cnX19funv3rtCCUn8Cnj59So0bNyZ5efkKNVL4Aad169YVmOEUEREhclzRn4q/hKASbNq0iaSlpX9rOeG4uDiSkpKiJUuW1His0rLHkydPFlhitjRKuwhsbGyoa9euBIDk5ORIRkaGdu7cKbJJNSoqinr06FHGzPyn4sCBAwSAecB9+PCBbG1tic1mE5vNJhaLRZ06dSI/Pz+hawzwyyP/XGthwoQJjEuhW7duzMO2cePGNGfOHLp48WKtuVGio6OrFS9z48YN6tSpEwGgjh070vXr1xn3j4KCAikrK9OaNWuEnvecOXOoSZMmIs2BH3cgbp0EohKy4+TkRFJSUhXG05SGl5cXSUlJ1Tg181fhwYMHBJQU4eK7a0QpaJSdnU1Xr16lZcuWUc+ePcsIJnXt2pXc3d3pwoULf/xakJ2dTePGjSMANGbMGIHEOCEhgerVq0etW7cWWKvDwsJCrLVLfhf+EoJKwFeaE9VfLE7079+f6tWrV+XiLSx4PB7t3r2bZGRkqHnz5hXeW2xsLLVu3ZpxERQUFJC7uzuxWCySkpIiDodDixYtErqQzadPn8jBwYFYLBY1adKETp069cvqKlQXp06dIgBlNMs9PT1JV1eXvn//Tvv27aMBAwYwEdstW7akpUuX0pMnTyq8t7y8PNLT06Phw4dTZmYmnThxgqnmxydb/fv3p23btomtkFFV4HK5JC8vL3QWxMOHD5nYlDZt2tD58+fL3W/pAFEDAwM6dOhQlaZle3t7kfO6N27cSDIyMrVG2gsKCqh79+6kqqpaaRYD3zowY8aMWplHbcDf35/YbDbl5ubS/Pnzy1SZrA4KCwspMjKS1q9fT3Z2dqSpqcloXZiamtKsWbPo2LFjvy0VsyocPnyYFBUVqUGDBgL1KJ4/f06Kioo0YMCAcu4BvlicsPEyfyr+EoJKkJaWRgDoyJEjv+X6Z86cIQB0/PhxsY8dExNDzZs3JxkZGSYmgI/g4GBSVFSkhg0b0tOnT+nly5fUpk0bkpSUpBUrVlBmZiYtXbqUpKWlSVdXl44cOVLhApienk4uLi4kLS1NWlpatGPHjt9qcREFfBnX0guBs7MzmZiYlGmXlZVFx48fpzFjxpCysjIBJdX/5syZQzdv3mQeHjwej549e0ZDhgwhACQhIcG4IerVq1erVoCq0LVrVxoyZEilbZ4+fUoDBw5kAs1OnDhRJal7/fo1E8BlZmZG165dq7CttbU12dnZiTRvR0dHMjc3F6mPqEhPT6dmzZqRkZFRhQVtVq1aRVJSUn98yeDSmDVrFjVq1IiIiCwtLWnUqFFiHZ9fGdPf35+cnJyY8tj8jJ5x48bR3r17KTY29o/ZHMTFxVGbNm2Iw+HQ+vXry83r3LlzxGazywlO5efnk5aW1j+KEArCX0JQBfT19cnV1fWXXzcvL4+MjY0Zn3JtICcnh4kFGDZsGCUkJNC0adOY7IKMjAzavHkzycjIUOPGjSkyMrJM//fv3zN6AZ07dy4TRFZQUEAbNmwgNTU1kpOTo6VLl1bLR/07wRdtKa38OGbMGOratWuFfQoKCujSpUs0bdo00tHRIQCkqKhIjRo1InV1dSYWQEFBgZo0aULDhg0jRUVFsYjh1AQLFy4kfX19gedev35NI0aMYESNDh06JHIA1a1bt8jCwoIAUL9+/QRaptq0aUOTJ08WadwWLVrQ1KlTRepTHXz8+JG0tbUFlkz+8eMHqampCVXf4k+ClZUVDRkyhCloVBv1HH5GfHw8HT16lGbOnEmtWrViBJO0tbVpyJAhtHHjRnr8+PFv3TQUFBTQ/PnzmRipnwW0tm3bJlAnZMmSJaSgoPCPXvv+EoIq0L9/f+rTp88vv66XlxdJSkqKnJNdHYSEhJCioiJxOBzicDi0a9cu+vLlC2MWdnZ2rtRlcenSJWrSpAmx2WyaNm0a7dmzh4yMjIjNZtPEiRP/MRHXP+PLly8EgM6fP88cs7GxqXQXy+Px6OnTp+Tt7U1dunRhrAB8lUAZGRmys7Oj2bNnM7slUWWNawP8tNbS2RMfPnygsWPHEpvNJn19ffL3969RMRcej0dHjx4t890ofb169eqJVD8kJyeH2Gz2L3v/KiqZvHr1auJwOFWK3fxJ4PF4pKGhQcuWLWPiMJ48efLL55GRkUHnzp2jxYsXU+fOnRn3m6KiIvXu3ZtWrlxJ169fr1EWTXVx9uxZ0tDQIB0dnXKWrdmzZ5OEhESZZ8PXr19JUlKSNm/ezBzLzi8SqdjW78ZfQlAF3N3dSUdH55de89OnTyQrK1tpDXVxIjg4mOTl5UlGRoYkJCRo1KhRpKysTHXr1hVa1KiwsJBmzJhBbDabANRYtvhPAD8Ku7TLyNLSksaPH1+mXXp6OoWEhND48eOpbt26BIDk5eVp4MCBtGPHDkZT4OXLl7R69Wpq164dQwYkJCRo69atv500ffr0iYmUjo+Pp2nTphGHwyFtbW3atGmTWF0Z+fn55axHWVlZJCcnRxs2bBB6nNJBcb8KP5dMzsrKInV19V9ipRAnEhISCCiRifb29iZFRcU/Im0uLy+Pbt++Td7e3tS3b1/GBcfhcMjS0pIWLlxIp0+f/mVCQPHx8dS9e3disVi0ZMkSxnLB5XKpX79+pKioSNHR0Uz74cOHU4M2nWnpqWjqsuYaGQoqx72mpBx3bOKfZzH9SwiqAD/v/GezUW1i6NChpKOjU+vvYV5eHuMiGDFiBMXFxTH5xtra2kJbJ169esX4lk1MTKh3794EgExNTenOnTu1eg+1CR6PR5KSkmWqDTZq1Ijmz59PUVFRtGrVKurUqRNjBWjWrBnNnz+frly5UqWa5PDhwxk9AT6JsrS0JB8fH3rz5k1t31o58Hg80tTUJAsLC5KRkSFVVVXy8fGpdqVIYZCenk4LFy5k4ktETR/ctm0bSUpKVlsiu7rgl0zetm0b+fj4EIfD+e0uH1HBl8eOjY2l/v37/7ER8lwul549e0Zbt26lESNGMIQbADVv3pymTp1Khw8frtX3n8vlkpeXF0lISFCnTp2Ya/348YNatWpF9erVo4SEBPqcmkP91p0ng0XhVP8nIvDzy8jtLBksCif7PQ/oc6p4AsbFgb+EoAq8fv2aAIhNT78q8HXZhc19ri7i4uKYLIKdO3fSpUuXSFdXl1RUVGjx4sWkra1NWlpadPHixQrHSEhIoKlTpzKR5IcPH2ZMqffu3aM2bdoQALK3t/+jZVwrg6amJnl5eTFWAGlpaUbqWF5engYNGkQ7d+4UScHt7NmzBID8/f2pT58+ZGxsTPv37yc7OzuSlZVlyIWbmxtFRkbWesBVeno6eXh4kISEBElISJCnp2eNtPxFxcePH5lYlHr16tHp06eFuucJEyZQ69atf8EMy2POnDnEYrFISUlJoK7En47169eTrKwsFRYWkpqaGi1btux3T0ko8Hg8ev/+PQUEBNCkSZOoSZMmDEGoV68ejRkzhnbs2EExMTFiF0y6c+cO1atXj1RVVSk0NJSISmS8dXR0qMWgKdTI4xyz0Av7MnI7S408zlFQxJ9BKP8SgirA5XJJVlZW6HK3NUFhYSE1bdqUOnXqVKuLwNGjR5ksgvv37zP+7B49ejB+0MTERGan7+LiUsZ3nJ2dTcuXLyd5eXlSUVGhdevWCTQpc7lc8vf3Jw0NDVJQUKA1a9b8Y0RL+GWU1dXVSVdXl7ECACVFo65evVqte0lPT6e6deuStbU1c43ScQQ5OTkUFhZGTk5OpKamRgBIT0+PZs6cSVevXq2RD/9nZGVl0apVq0hFRYVkZWWpU6dOpKSk9FuU5x49ekQAGHdK165dKSIiotI+pqam5dw3vwpcLpeaN29OACg8PPy3zKEmGDduHLVt25Zevnz5Szc8tYHv379TaGgozZs3j9q1a8f8VtXU1GjAgAG0Zs0aunfvnliePWlpaQx5nTFjBuXl5dGigzdKFnjXMyKRgZ9fW67FVj2BWsZfQiAEzM3Ny5XBrQ34+fkRm82utXKaeXl5NH36dMZFcPPmTWratCnJyMjQxo0byy0EP8sex8bGkr+/P+no6JCUlBTNnz9fKF9eWloaOTs7E5vNpsaNG9OFCxdq5f5qirS0NAoODqaxY8dSnTp1GHO+gYEB7dq1i2JiYggAHT16tNrXGDt2LCkpKZVJT6uoPHJRURFdu3aNnJ2dmZoFqqqq5OjoSKGhodXWpsjLy6MNGzaQlpYWSUlJkbOzM3379o2RTv4dLosLFy4QAPr48SOdPXuWWWxHjhwpUG47Pz//l0XGC0J2djZpaGiQpqYm6ejo/ONcBm3btqVx48YxWgT/tAygypCdnU1XrlwhT09P6tGjB8nJyTHZPd26dSMPDw+6ePFite+Zx+PR9u3bSVpampr0HV8jEvDzK/g3Wwr+EgIhMGnSJGrVqlWtXuPbt2+kqKhIM2fOrJXx4+LiyNTUlKSlpWnbtm20cuVKkpSUJFNTU3rx4kWlfe/fv0/a2tqMr3vUqFFC10QojefPnzNqhwMHDvxlwjsVobi4mB49ekQrV66kDh06MPfXokULWrhwIV27do169epFgwcPJqKSqPua7KbCw8MJAO3du7fMcWHKI/N4PHr06BF5eHgwi6WsrCzZ2trSgQMHhCJmBQUFtHPnTsbiMWHChDKujtTUVAJABw8erNb91QR8YRc+ySkqKipDPufNm1fmHh8/fkwA6N69e798rkREa9euJUlJSYqIiBBLyeRfCb7Vc/369TR27FgyNTX93VOqVRQWFtLDhw/Jz8+PbG1tSUNDgyH7ZmZmNHv2bDp+/LjIUuQX7z4mg4WhVE+AZaCO03pSNOtHHI16xOJIk4SSJsk16UR1J++qlBA08jj3W2MK/hICIbB161bicDi1au52cHAgDQ2NKsvjVgd8F0GDBg3o9OnT1L59e2Kz2eTu7l7lPT169IisrKwIAKM8NmnSpGrvTvmpZ3p6eiQtLU0eHh5iU2EUBqmpqRQUFESOjo6kra3NpDjZ2dnR7t27y6WOjRw5krp3705E/1uEKipyUhnS0tJIR0eHbGxsBLqDhC2PzEdsbCytWbOG2rdvz2QrdO/enTZv3lzuHrhcLgUEBFD9+vWJxWLR6NGjy0kn89GwYcPfosW/YcMGkpOTK3c8OzubVqxYQfLy8qSqqkrr1q2j/Px8Zmf7K787peekqalJEydOJCLxlEz+lYiNjWWIbcOGDWttE/Kngsfj0atXr2j37t3k4OBA9evXZ9yBDRs2pPHjx9O+ffsoLi6uUtet/Z4HZLRY8MIu17gDScirkmKbAaRm40zKne2JLa9CLI4M6UzYWmlMgf2e3yfn/pcQCIHbt28TgFpLo+OP7+/vL9Zxf3YRbNq0ieTl5cnIyIju3r1bad8PHz7Q6NGjCQA1bdqUzpw5Q8XFxeTv70+ysrLUrFmzMuk2oiI7O5vc3d1JSkqK9PX16ejRo7USN8G3AqxYsYIhQvxsCBcXF7p+/XqlpGjatGnMDury5csEoNLSxBXB0dGRlJWVK1SyE7Y8siB8+/aNdu7cSdbW1kzmQps2bWjlypW0fv16aty4MQEgOzu7KjX/x4wZQxYWFiLPoaZwc3OrVDo3MTGRCWA1NDSknj17VrtCY02xbt06kpSULGMlq0nJ5F+NEydOEACmhkVQUNDvntJvx9evXyk4OJhmzJhBLVu2ZAST6tSpQ0OHDqVNmzZRVFQUk5oZm/ij0p2+tv1aqrcwtMyxupN3ESQ4JN+8W5Wug7ik3+PC+UsIhEBGRgYBoMDAQLGPzeVyqVWrVtSuXTuxBnOVdhH4+vpSv379mN19ZfUH0tLSaP78+SQlJUV16tSh3bt3l1MNKy17XJ0CR6Xx9u1bGjBgAAEgKyurGpEMPlJSUujIkSPk6OjIpLMpKirS4MGDyd/fXyR52cWLF5OhoSER/S8FVVTT8OnTpwkA7du3r9J2VZVHFgYZGRl0+PBh6tSpE0N+5OTkyNHRke7du1fld2zTpk0kJSX1y4M/J0+eTG3atKmy3cuXL5kUV3V1dbpx48YvmN3/kJOTQ1paWjRhwoRy5/glk1etWvVL5yQqli1bRhoaGkxBo3+SoNKvQnp6Op09e5YWLVpEnTp1YoTFFBUVydramvou2U/1K7AOVPaS0jYmKW3jKjMPPE/9nvo5oqzfbPxHoaysDENDQzx//lzsY+/atQvPnj3D1q1bwWaL5y0+duwYzMzMkJWVBW9vb6xduxaRkZE4c+YMdu/eDQUFhXJ9CgoK4OfnB2NjY+zcuRNubm6Ii4vDpEmTICkpWaZt8+bNERkZibFjx2Lq1KkYPnw4MjIyqjVXY2NjnD59GufOncPXr1/RunVrzJ49W6TxeDweIiMjsWLFCrRv3x5aWloYPXo0nj59inHjxuHGjRtITU3FiRMnMHHiROjp6Qk9tqqqKtLT0wEAaWlpkJCQgJKSktD909PTMWXKFPTt2xdjx46ttO3ChQshKyuLFStWCD3+z3j8+DG2bt2KO3fuoEOHDvD19cXo0aNx/vx5dOjQAXp6epg2bRouXryIwsLCcv0tLCxQWFhYK9/1ypCcnAwNDY0q2zVt2hQnTpyAlJQU5OTk0K1bNwwcOBCvXr36BbMEdu7cidTUVLi5uZU75+DggOXLl8Pd3R1Hjhz5JfOpDqKjo2FiYoJ79+5BX18f+vr6v3tKfxxUVFTQt29feHt74/bt28jMzMStW7ewePFisNlsPE8pBo9EG5OIUJybAbZc5c+PYh7heuz3Gsz+1+A/SwgAoFWrVnj27JlYx0xJSYGHhwcmTJgAc3PzGo+Xn5+PmTNnYvjw4ejVqxcsLCwwb948dOrUCTExMejfv3+5PjweD0FBQWjSpAlcXV0xfPhwvH37Fp6engKJAx+ysrLYsWMHjh07hsuXL6N169a4f/9+teduY2ODmJgYrF69Gvv27UPDhg2xZ88e8Hg8ge1TU1Nx5MgRODg4oE6dOjA3N4efnx90dXWxe/dufP36Fc+ePYOPjw+6du0KDodTrXmpqqrix48f4PF4SEtLg6qqKlgsltD9Z8+ejdzcXOzevbvKfsrKynBzc8OePXsQFxcn0jzv37+PHj16oEePHuByubh06RJu3boFFxcX+Pv7IyEhAbdu3cKoUaNw8eJF9OnTB1paWhgzZgyOHTuG7OxsACXfcw6Hg4iICJGuX1OkpKRAU1NTqLavX79GYWEhAgMDERQUxCxwU6dORWJiYq3NMTc3F2vWrIGjoyOMjIwEtlmyZAmcnJwwbtw43Lp1q9bmUhPw36+7d++iY8eOv3s6/wjIyMigc+fOWLx4MUJCT0NSuY7IY+S8uIHirFTIN+lcZdvPqbnIKeBWZ6q/DH8JgZgJgbu7O4gI3t7eNR7r7du36NChA/bs2YP58+fj8ePHOHXqFPbt24eTJ08KfNjeuHEDFhYWGD16NFq2bIno6Gjs3LkTdeoI/2UfOnQonj59irp166Jz587w8fGpcBGvClJSUnBxccGbN2/Qp08fTJo0CRYWFnj48CF4PB4iIiKwfPlyWFpaQlNTE2PGjEF0dDQmTJiAmzdvIiUlBcePH8eECROgq6tbrTn8DFVVVRARMjMzkZaWBjU1NaH7njlzBgcPHsTGjRuFns+MGTOgo6MDDw8Podo/efIE/fv3R4cOHZCcnIywsDA8fPgQvXr1KkNAJCQk0LlzZ/j5+eHdu3d4+vQp5s6dixcvXmD48OHQ0NDAgAEDcPjwYTRr1uyXEwJhLQQAEBUVBQAwMzPDyJEj8fr1a6xZswYhISFo0KABVqxYgZycHLHPcffu3UhJSYG7u3uFbVgsFnbv3o1OnTrB1tYWb968Efs8aoK8vDy8ffsWTZo0wePHj/8SgmrgU2oORDQOoCj1C9Iu74C0bhPIm/Sosj0B+Jgq/u+wOPGfJwTfv38X2w7k0aNH8Pf3x8qVK4XeGVUEvovgx48fGDVqFNavXw99fX08f/4c48aNK7czffnyJQYMGAArKyuwWCzcuHEDp06dQtOmTat1fUNDQ9y8eRMuLi5wc3ODtbV1jd6nunXr4uDBgwgPD8f3799haWkJOTk5WFhYYMOGDdDX18eePXsQHx+Pp0+fwtvbG126dKm2FaAyqKioACgx/aenp0NVVVWofmlpaZg8eTL69esHJycnoa8nIyOD5cuXIyQkBI8fP66w3cuXLzFs2DCYmZkhLi4OQUFBePr0KQYNGlSlJYLFYqFVq1bw9PTE06dP8e7dO3h7eyMzMxOTJk3C8+fPERISgg0bNuDDhw9Cz70mEMVCEBUVhYYNGzKuG2lpacybNw/v3r3D1KlTsWrVKjRo0AD+/v7gcsWzy8rLy4Ovry8cHBxgbGxcaVspKSmcOHECOjo66Nu3L75//3PMvy9fvgSPxwObzUZhYeF/nhAUFBQgISEB0dHROHfuHHbs2IElS5Zg0qRJsLW1RefOndGiRQsYGBhAXV0d8vLyaGfRXqRrFGen4/ux5WBLy0PDdjFYbAmh+hVyq7ex+lVgEVGVxOjHjx9QVlZGZmamSL7WPx3v3r1DgwYNcOHCBVhbW9doLB6Phw4dOiAvLw+PHz8u56MXFvn5+ViwYAG2bdsGa2trxMfH482bN1i5ciUWLFgACYmyX7yEhAR4enpi7969MDAwgLe3N4YNGya22AUAuHLlCuzt7UFECAwMFOm9Ki4uxqNHj3D+/HmcP38ekZGRICLo6ekhJSUFbDYby5cvx+zZs2tl8ReEqKgotGnTBo8fP4aXlxfy8/Nx7ty5Kvs5ODggPDwcL168QN26dUW6JpfLRcuWLaGnp4dLly6VOff+/XssW7YMhw8fhr6+Pjw9PeHg4FDt79DP+P79OxYtWoT9+/dDWloaBQUFaNWqFezs7GBnZwcTExORXCbCoLi4GBwOBzt37sTkyZOrbN+lSxfUrVsXwcHBAs9/+PAB7u7uCAoKQvPmzeHr64u+ffvWaN6bNm3C/Pnz8fr1azRo0ECoPh8/foSlpSXq16+Pa9euQVZWttrXFxcOHDiAcePGYfny5Vi7di3S09PF9t35XSgqKkJGRgbS09ORlpaGpKQkfPnyBfHx8UhMTERycjJSU1ORkZGBrKws5OTkIC8vD0VFRVVaM9lsNjgcDmRkZCAvLw9FRUXI6zVGaruqv6cAwMvPQeKRxSj+kQxte19IadQT+r7OOndC87rKQrcXB0RZv//Z35oaon79+lBQUMCzZ89qTAgCAgLw8OFD3Lx5s9o/xnfv3mH48OF48eIFBg8ejDNnzqBx48aIiIhA69aty7TNzs7G2rVrsW7dOsjIyMDPzw/Tpk2DtLR0je5DEHr27Ilnz57ByckJffr0wcKFC+Hl5QUpKSmB7ZOTk3Hx4kWcP38ely5dQkpKCpSVldG7d29MnToVffr0gY6ODlJTU7FkyRK4urpi37592Lx5M3r27Cn2+f8MvkWA/7ARJiDx9OnTOHToEAICAkQmAwAgKSmJVatWYfDgwbh69Sp69OiBr1+/YuXKldi3bx80NTWxZcsWTJw4scL3tbrQ0tLCwoULsX//fhw/fhx5eXkICwvD+vXrsWzZMhgZGcHW1ha2trbo0KFDOdJZHaSnp4OIhLIQ8Hg8xk1SEerXr48jR45g3rx5WLBgAfr37w8rKyusXbsWbdq0EXl++fn58PX1hb29vdBkACixnIWHh6Nr165wcHBASEiIWMl3dRAdHQ0jIyM8evQIlpaWfwwZKC4uRmZmJmOJS0tLQ1paGr59+4Zv374xC3taWlqZhT0/P19oK5CUlBSzsGtpaUFVVRVqamrQ1NSEjo4O6tatCz09PdSvXx86OjpQU1MT+PvKKeCixbKLVboNiFuI78dXgJseD+2RXiKRARYAQ3V5odv/DvwZ35zfBDabDRMTkxrHEWRkZMDV1RWjR49Gly5dqjXGsWPHMHHiRKipqaFFixYIDQ3FvHnz4OXlBRkZGaYdl8vF3r174enpiYyMDMyaNQuLFy8W2uxdXWhra+PcuXPw8/ODm5sbbt68iaCgIBgZGaG4uBiRkZGMFeDRo0cgIpiammLy5MmwsbER+KBSV1fH9u3bMXnyZDg7O6NXr14YPHgw/Pz8YGhoWGv3UtplkJaWhpYtW1baPjU1FVOmTEH//v3h4OBQ7eva2trCwsICCxYsQNeuXbFz504oKCjAx8cH06ZNg5ycXLXHrgqNGzeGoqIiYmJisGjRIgwbNgyFhYW4fv06wsLCcOTIEaxfvx5aWloYOHAgbG1t0aNHjzLfPVGQnJwMAELFELx9+xbZ2dkwMzOrsm3btm1x/fp1nD17Fi4uLmjbti3GjBkDLy8vkb4z/v7+SEpKqjR2oLI5BAUFwdbWFq6urli7dq3IY4gT/IDCO3fuYObMmWIdm4iQlZWFtLQ0ZmHn/25SUlLw7ds3JCUlMQt7ZmYms7AXFBQIfR0JCQnIyspCXl4eOjo6UFFRgbq6OjQ1NVGnTh3o6upCX18fdevWhYaGBjQ0NKCkpCQWMkZEiH0ZDTleLnLYFf8GiVeM5DBfFHx7Da0hHpDWFc0dW09dDvLSf/aS+2fP7hegVatWuHPnTo3G8PT0RF5eXrUeDAUFBZg/fz62bdsGc3NzvHr1CsXFxbh69SqsrKyYdkSEM2fOwNXVFa9fv4a9vT28vLxgYGBQo7mLAjabjYULF6JLly4YPnw4WrRoAVNTU7x58wapqalQUVFB7969MX36dFhbW0NHR0eocVu3bo1bt24hKCgICxcuRNOmTbFo0SK4uLjUiklWWVkZLBZL6BiCWbNmIT8/H7t27aqRiTojIwPGxsY4cuQI3rx5A3d3d8yZMweKiorVHlNYsNlstGvXrkxgoZSUFKytrWFtbY1t27YhIiICoaGhCA0NxZ49e6CgoAAbGxvY2dmhb9++UFYW3tSZkpICAEJZCPgBhaampkKNzWKx0L9/f/Tp0wf79u3D0qVLcezYMcyaNQtubm5Vfp75+fnw8fHBmDFj0LBhQ6Gu+TMGDhyITZs2YdasWahfvz6mT59erXHEgejoaNja2iI1NVVg/AARITc3t8xi/vOu/fv37/j+/Xu5hT03NxdCeJXLQEZGBgoKCtDV1YWKigo0NDTKLOy6urrQ0NCAuro68/odrpeEhAQcOXIEAQEBiI6ORt0Bs8Fp1gNgCSYZ6df2Iu/tQ8g2MEdxXjayY66XOa/QwkpgPwCQYLNg1UhLrPOvFYhb2OCfhh07dtSo/vqzZ8+IzWbTmjVrRO779u1bMjMzIykpKTI1NSUA5ODgUE4kJyIigrp06UIAqHv37tWS2a0puFwu3bt3j5YsWUJt27ZlZEEBUOvWrenKlSvlxI6qgx8/fpCrqytxOBwyMDCgEydO1IpKnIqKCq1Zs4bk5ORo48aNFbYLDQ2tsYDVjx8/aMWKFaSsrExycnJkZGREDRo0EMv7JQoWLVpEurq6Vbbj8Xj04sUL8vLyYspdczgcsra2pp07dwpV9pqvnJecnFxl24ULF5KBgYEwtyAQWVlZ5OnpSXJycqSmpkbr16+v9Pe8ZcsWYrPZYin4NHv2bGKz2b+kOmJ+fj59+/aNXrx4QXfu3KEzZ87Q1q1bGRVLADR8+HDq1asXtW7dmgwMDEhFRYUkJSXL/F5Lv/jqfYJeEhISpKSkRPr6+mRiYkLdu3enkSNH0pw5c2jNmjW0f/9+On36NN29e5dev35NycnJjOrfn4rc3FwKCgoiGxsbYrPZJCUlRUOGDKFZs2aRgYlFpeJC0votKnyvAPwrlAr/00GFQEmud4cOHRAVFSX0DoUPIkK3bt2QlJSE58+fi+T75bsI5OXlUVhYCCLCrl27MHToUKbN+/fv4ebmhqNHj6JFixZYs2YN+vTpI/YAsIqQlJRUJhaAn7Pfu3dv2NjYoHfv3jh37hycnZ1Rv359BAcHw8TERCzXjouLw5w5c3Du3Dn07NkTmzdvrnbGhCAYGRlh6NChWLt2LQIDAwW6AlJTU9G8eXOYm5vj1KlTIr/veXl52LZtG3x8fJCdnY1p06Zh0aJF+PbtG8zMzLB7925MmjRJXLdUJUJDQzF48GB8/fpVpBTOL1++ICwsDGFhYbh58yZ4PB4sLS1hZ2cHW1tbgbvs3bt3Y9q0aSgsLKwyJqFHjx5QVlbGyZMnRb6n0hAUYDt8+PAyn1t+fj4aNGgAKysrHDx4sEbXA0r85EOGDMGVK1dw69atKt0ePwfLCdq18/9OTU1FSkoK0tLS8OPHj0pN8CwWq9KdvKysLJSUlKCqqgoNDQ1oaWmhTp060NbWLrNTL/2Sl5f/Zc+a2gQR4e7duwgMDERISAgyMzPRvn17ODg4gMPhYM2aNYiLi8OwYcNQ1Gkqniflo1hUhaJKIMFmoYOROg5OsBDbmKJApPVb3Azjn4asrCwCQPv37xe5b1BQEAGgixcvCt0nPz+fZsyYQQDI2NiYAFCfPn3K7LpSUlJozpw5xOFwSEdHh/bs2fNLmDeXy6W7d++Sh4cHs+PA/+8+PDw86O7duwJ3tS9evKAWLVqQjIwM7dixQ6w7+jNnzpCxsTFJSkrSvHnzxPYdbN3WggaPn0VSOo1oR3A4ZeeXv69Ro0aRqqqqUDvi0igoKKCtW7eSjo4OSUpK0pQpU8pJyVZUHrk28fXrVwJAoaGh1R4jJSWFDhw4QLa2tiQrK0sAqHnz5uTh4UGPHj1iPnsvLy/S0NCocjwej0cqKiq0cuXKas/pZ8TExFD//v0JAJmbm9OtW7eYc1u3biU2m02vX7+u9vhcLpdSU1Pp7du3FBkZSadOnSIjIyNSUlIiFxcXWrBgAU2YMIEGDx5M3bp1o1atWpGenh7Jy8tXuLvk71alpaVJSkqKkagW1E5FRYWMjIzI3NycmjVrRhISEqSiokLt27enPXv2UGhoKN26dYtevHhBiYmJ/4jiTLWB9+/f0/Lly5nnbL169cjDw4Nev35NoaGh1KJFyY6/X79+FBUVRUREn1NzqJHHObGWP/4nVTv8z1sIAKBBgwYYMGAANmzYIHSf7OxsNG7cGJaWljhx4oRQffhZBNHR0VBRUUFWVhbWr1+PqVOngsViIT8/H1u2bMGqVatQXFwMV1dXzJ07F/LytReZmpSUhAsXLjBWgPT0dKipqTFWAGtra2hra1c5Tl5eHubNm4edO3diyJAh8Pf3F1ugY35+PtavX49Vq1ZBUVERPj4+cHR0FDmgKC4pC4cffsb1N9/xKTUHKLX7YQGopyYHq8ZaGGNRDzH3rmDw4ME4ePAg7O3thRqfy+Xi4MGDWL58OT5//gx7e3t4enoKzHF/+/YtmjZtilWrVsHFxUWk+6gJdHV14eTkhNWrV9d4rNzcXFy6dAmhoaE4c+YM0tPTUa9ePdja2iI+Ph4vXryoUn74w4cPMDIywtmzZ9G3b98az6k0rl+/joULF+Lx48cYNGgQVq5cib59+6JLly44dOgQsrKyKt2pC9q1p6enIzMzs8LdOIvFgqysLNhsNogIXC63wp29rKwsVFVVoampyQTKCdqplz6upKRUZtc+adIkPHjwADExMThy5AhGjRol1vfwn4YfP37g2LFjCAwMxK1btyAvL49hw4bB0dERXbp0wdWrV+Hh4YHIyEh0794dXl5eaN++rAZBcORnLDoZLbY5+Q42wYh2wmcjiBuirN9/CQGAIUOGID09HdeuXRO6z6JFi7Bp0ya8fv1aqMA+vtqehIQEMjMz0bZtWxw8eBCNGjUCj8fDkSNH4O7ujvj4eEyZMgVLly4VaiEWFVwuFw8fPmQyAvgBXW3btoWNjQ1sbGxgbm5e7dSzEydOYMKECVBRUUFQUFC5H1tN8OXLFyxcuBBHjx6FpaUltmzZgrZt21bdLy0XbqHRuP02BRJsVqXmQP754q8xaJ4XjfPHDlZpNuXxeAgJCYGnpydiY2MxdOhQLF++HM2aNau034wZM3DkyBG8f/++1rNE+LCzs0NWVhauXLki1nGLiopw+/ZthIaGIiwsDF+/foWkpCTGjBkDOzs79O7dW2Dg2IkTJzB06FAkJCSIpKYJVB4sVzpg7unTp3jy5Any8/MBlASVZmdno7i4uNyYLBYL0tLSkJKSgoSEBFgsFng8HrOwFxUVCZyLqqoqFBUVER8fD1VVVdjY2EBTU7PCBV5NTa3aGRylYWlpCVlZWdy4cQOfPn1CvXq/b+H5XSguLsaVK1cQGBiI0NBQ5Ofno0ePHnB0dMTgwYMhLy+P27dvw93dHbdv34alpSVWrVqF7t27Vzjm1utxWHcptsZzW9i7MWZYCZ/WWhv4SwhExIoVK7B582YkJycL5TN78+YNTExM4OHhgaVLl1batqCgAAsWLMDWrVuhqqqKzMxMeHp6ws3NDZKSkrh69SoWLlyIJ0+ewNbWFj4+PmjcuLG4bg0AkJiYyFgBLl++zFgBrK2tGSuAlpb4ImA/fvyI0aNHIyIiAitXroSrq6tYc7Vv3rwJZ2dnxMTEYPz48fD29q4wmj048jM8T78Al0ci+QWJVwwZjiSWD2qBkRWweyLC6dOnsWTJEkRHR6Nfv35YuXKl0LEoiYmJMDY2xqxZs8QidS0MvL294ePjg/T09FrLnycidOjQgbnGq1evICcnB2tra9jZ2aF///4MAXJ3d8e+ffsQFRUlcDde1a5dUDEnoCTSXUZGhlnYeTwevn//zuzsK/K5S0lJVbpbF/RSUVFhCPT169dhbW0NR0dH+Pv716oPnsfjQUlJCW3btsW7d+/w5cuXWrvWn4gXL14gMDAQhw4dwrdv39CkSRM4OTlhzJgxTHGnyMhILFmyBBcvXkTr1q3h5eUltKBVdZ8dEmwWJNksrBjY/LdaBvj4SwhExKlTp2BraytUsBURoU+fPoiLi8OLFy8qTZd59+4dRowYgWfPnoHFYsHQ0BCHDx9Gu3btEBMTAxcXF5w/fx6WlpZYu3YtOnXqJJb74XK5ePDgAWMFePLkCVgsVhkrQLt27cQiQFMRioqKsGzZMnh7e6NHjx44ePCgyDvAysDlcrFz504sWbIERIQVK1Zg+vTpZbQOxMXyF/RuhJlW/wucIyJcuXIFHh4eiIiIgJWVFby8vNChQweRx/bw8MD69evx9u3bagkeiYqrV6+iZ8+eePXqFZo0aSK2cX8OlrO3t4eBgQHs7OwQFxeHqKgovHnzhtEnkJeXh6SkJLKysipUlpOWloa8vDyzY2ez2WCxWCguLgaXy0V+fj5yc3ORl5cnsL+ysjKzaOfk5DDS0F+/fkVERASUlJQwfvx4jBw5ElpaWlBXV4ecnFyNF/GDBw/C0dERq1atElhBUVx4//49jI2N0bRpU7Rs2bJClcd/E5KTkxEcHIyAgAA8fvwYampqGDVqFBwdHdGuXTvms4uJicGSJUsQFhaGpk2bYsWKFRg8eLDIJLg61sXODTSw2s4E+mq1pysiCv4SAhHx8eNH1K9fXyg/ZlhYGOzs7HDq1CkMHDiwwnbHjx/H+PHjweVykZeXhxkzZmDNmjVIT0/H0qVLceDAAdSvXx8+Pj4YMmRIjR9CCQkJZawAGRkZUFdXL2MFqGl9hergypUrcHBwAI/HE1n2WBgkJyfDw8MD/v7+aN68ObZs2YJu3brVmh/wzp07cHd3x61bt4QyPVaFzMxMGBkZYdiwYdi5c6fY5lvZ9VRUVBAQEABHR8cy535WlhNlp56VlSXwehwOB4qKipCTk4OMjAxYLBays7ORlZXFVGNks9mQlpZGYWGhQDO+pKRklb71n19qamoMOSwsLETDhg3RoUMHBAUFAaj9DJ7ly5czctSjR48Wy5g/g7+R4XA48PPzg7Ozc61c53ejsLAQZ8+eRUBAAM6ePQsA6NevHxwdHdGvX78y6qxxcXHw9PREcHAw6tevj2XLlmH06NE13vww8Uex3/E5NbeMoiELJaJDVo20YG9ZDw20al9XRBT8zTIQETwej5SUlGj16tWVtsvNzSUDAwOysbGpMJI+Pz+fZs6cyeRua2tr04ULF+jHjx/k4eFBsrKypK6uTps2baKCgoJqz7moqIhu3bpFixcvptatWzM5xebm5uTp6UkPHjz4Y3KCk5KSyNramgDQwoULa3TfFeHRo0fUvn17AkADR4+nhu5nBUb8yrfoUWkuse6MAwL7NXQLJ6sBwxjdhfDwcLFlU6xbt44kJCQoNja2xmPxeDzKzMykjx8/UlRUFF29epWOHz9Ou3fvJl9fX1q0aBGpqKiQsbEx9ezZk9q0aUP169cnFRWVCnPSJSQkSE1NjerVq0dNmjShVq1aUbt27cjCwoIsLCyobdu21LJlS2rUqBHp6emRkpJShe+vgoICGRgYkJmZGfN51alThzgcDgEgHR0dGj58OO3fv5/evn1LmZmZNX6fd+3aRSwWi168eFHu3MOHD6lz584EgHr06MFEm9cUPB6PHB0dSUpKqkyWgzixcuVKUlRUJAC/RZukNsHj8ejhw4c0Y8YMUlNTY7KdNm/eTN+/fy/X/uPHjzRhwgSSkJAgXV1d2rVrV61lV2TnF1FMfAZFfUqjmPgMgRlKfxL+ZhlUA507d0YdPQMs89uGQi4PUpJsGKrLl5Ga5JvAY2JiBOZev3v3DkOHDsXz58/B4/EwdOhQbNmyBSdPnsSyZcuQlZWFOXPmYNGiRSKpvvHx7du3MlaAzMxMaGhoMFaA3r17/xYrgDDg8XhYv349Fi9eDFNTUwQHB1dYf74m1zh06BDcrySCrdNEYAWygvhXKEr/uWojIe3iNkgqa6PuxO0Cx6ZiLiRS32GNTb1qmR4rQ35+PrODPXr0qFDBchX9PyMjo8JgOWVlZeZ3XFRUBFNTU0hJSTG74uLiYhQWFiIvLw85OTnIyspCRkaGQJM8m82GmpqawN25oqIili1bhlmzZmHw4MFldu2ld3NnzpzBwIED8fnzZ2hoaODq1asIDQ3F6dOnkZKSgrp162LQoEGws7ND165dq1XjgW8dsLS0xNGjRwW2of+PBXF1dUVsbCyjAlrTAL3CwkL06dMHz549w71798QeGzRixAhERkbi+/fvyMjI+GNqGNQEX79+ZWqGvH79GnXr1oW9vT0cHR3RvHnzcu0TEhKwevVq7Nq1CyoqKnBzc8PUqVPFErD5b8Ffl4EI4JuCQu6+QC5LrsJUtK56Eujb0RRz584VmLJ14sQJODk5IT8/H7KystixYwfk5eWxaNEixMXFwcHBAV5eXkywizAoKirC/fv3mVgAfiyCubk5EwvQpk2bWo0FEDciIiIwatQopKSkYPfu3RgxYoRYx49LykKvjbdE6pP/5QWSDrtCpYsjlDsMr7TtlbldhDYJFhQUCG12f/PmDd6+fQt1dXVkZWVVGCynqKgIVVVVqKioQEFBAXJycpCWloakpCST6vbzwv7jxw/mOoLIgqysrNABdHwzvbKycoWk6NOnTzA0NKyyiujy5cuxdetWfP/+vYypnsvl4t69e4yM8qdPn6CsrIz+/fvDzs4O1tbWUFBQEOoz8Pf3x+TJkxEdHY0WLVpU2raoqAh79uzBsmXLkJmZiTlz5mDx4sXVIu98ZGRkoGPHjsjPz8eDBw/EStibNWuGvLw8GBsbiz1r5FciJycHoaGhCAgIwNWrVyEjIwM7Ozs4OjqiZ8+eAp9vqamp8PX1xdatWyEtLQ0XFxc4OzsL/b34L+EvIRAC1QkWoW8vcWnVODTW+1/BloKCAsydOxc7duwAUGJpmDdvHvz8/HDnzh306tULa9asKVetsCLEx8eXsQL8+PEDmpqaZawAwhSM+ZORmZmJKVOm4OjRo5g4cSI2bdoktsI+y06/wMGHn0SKCk69uB3ZT85Dd+oeSKpUnOrJZgFddIDe6plCLfQVBbvx88/V1NSYdDUZGRlcuXIF8vLy6N27N4CShbH0ws7376ekpFTos+drx1e2qCcnJ2PmzJk4ffo0evToIfaiSo8fP0bbtm3x+PHjSpX7Bg0ahPz8fFy8eLHCNkSEZ8+eMemMz58/h4yMDHr16gU7OzsMGDCgwt9DYWEhGjdujHbt2iEkJETo+WdlZTGVROXk5LBkyRJMmzat2lUoa6NkckFBAeTl5SEjI4P58+dj+fLlNR7zV4LH4+HWrVsICAjA8ePHkZ2djS5dusDR0RHDhg2rcJ3JzMzE+vXrsWHDBhAR5s6di3nz5jEFy/6iPP4SgipQ3XQSNggcSQksH9gcI9vVw/v379G/f3+8fv0aEhISWLBgAeLi4nDixAmYmJhg7dq1VQbRFRUV4d69e4wV4Pnz52CxWLCwsChjBfjdJVbFDSLCvn374OzsDENDQxw9elQsssdd117Hp7Rc4edRzMXXrY7gqOuhjv2aKtsXpX3Dt92TweFwmAWdv7irqKhAVlYW0tLSTA47/b84UroltgAAO+VJREFUDX9hz87ORkZGBlJTU5mXIGsAh8MROoCO/1JVVRXKbFxQUAAlJSX4+fmJvToeAFy4cAE2NjZV5sXr6+vD3t5epJTLd+/e4dSpUwgNDcXdu3fBYrHQuXNnRka5tCbInj17MGnSJDx//rxa361v375h6dKl2L9/P+rXrw9vb28MHTq0WoGHjx49QteuXWFjYyOWkslPnz5l0lsvXrzIkMg/HXFxcQgMDMTBgwfx6dMnGBkZwcnJCfb29pW6EHNycrBlyxasWbMGeXl5mDlzJlxcXP5YF+mfhL+EoBKIKxWtj04hDriMQkFBAYyNjWFpaYmQkBBoaWnBy8sLDg4OFZry4+PjGQJw5coVxgrQp08fxgqgrq5e4zn+E/Dy5UuMHDkScXFxZVQbfwb9fxnWynbmyek/cEXFpozbpyrkvo1A8vEVUOs9HYpmQijlEaFP9mXkZKYxevP8V0ZGhsDcdkVFRaEi5NXU1DBp0iSw2WxERkbWKgm0sLBA48aNERgYKPaxDx06BAcHB+Tk5FRoffj+/Tu0tbUREhKCYcOGVes6SUlJOHPmDEJDQ3HlyhUUFhbC1NQUdnZ26NevH4YMGYI2bdrg+PHjNbkdREdHw9XVlUkRXrduncCqglXh1KlTsLOzw/z582tcMpmf2shisZCRkfFHP5fT09MREhKCgIAA3L9/H0pKShgxYgQcHR3RsWPHSglWfn4+du/ejdWrVyMtLQ2TJk2Cu7v7L0nR/bdAlPX7nx+FIgKCIz9XSAYKEt8i42YgCuJLpFal6zaBqtU4SGkLZq0XEqTAadwF7ZTy8Pz5c5w+fRrLly/H7Nmzyz0Ei4qKcPfuXYYEREdHg81mw8LCAgsWLICNjQ3MzMz+dVaAn/FzsBx/QZ8xYwYCAwMxffp0rFu3DqampsjOzi7TriL/N1BiJldVVYVivaZAe9F2bzkvbwJsScg1FVIDgsXC9Ucx0JQsgLq6Opo1a1bpDl5NTU0kU/OGDRtgZWWF0NBQDBkyRKR7EQXm5ua4fPlyrYydnJwMOTm5Sl0RT548AYAqiwFVBm1tbUycOBETJ05EVlYWzp8/j9DQUKxdu5YRDOvSpQvu3r0LS0vLasfamJiY4Ny5c4yIWKdOnWBnZwcfHx80atRI6HEGDRqEjRs3Yvbs2TAyMsK0adOqNR+ghKQoKCjA2Nj4jyQDRUVFuHTpEgICAnD69GkUFRXB2toaQUFBGDRoUJVuk6KiIhw4cAArV65EfHw8nJycsHTpUhgaGv6aG/iP4j9jIfiSloueG26igFteBKUg8S2SDrlAQlEDiq37gEDIijoHXn4WdBzXg6OuV64PEQHFRUjaPxOTRw/BkiVLypivvn79WsYKkJWVBS0trTJWADU1tVq959qCKMFyP/9dUbCcgoICZGRkkJaWBg6HAwsLCzRs2LCMSV7Q/5WVlZkH/ZPP6bDbcU/o++AV5uHrFnvI1GsJrWGeQvdb3lEBnZrqoU6dOrVSZ8LGxgYfPnxATExMrUWO83eY6enpYve/uru74/Dhw/j48WOFbby9veHr64v09HSxq/llZ2ejYcOGkJGRQV5eHpKSkqCtrY2BAwfCzs4O3bt3L5PtIApKy4x/+/YNU6ZMgaenp0im6zlz5mDLli04c+ZMtes32NjY4Pbt23BycsK2bduqNUZt4NmzZwgICMDhw4fx/ft3tGjRglEP1NHRqbJ/cXExgoKCsGzZMrx79w4jR47EsmXLxJ6h8V/CXwuBALiFRoNbQbxA5u1DYElKoY7jOkjIlrxh8s2t8G33FGTcDITm4PJqYywWC5CQRF/PQ9jsbIXCwkJcv36dIQExMTFgs9mwtLSEi4sLbGxsYGpq+sdYAbhcbrXS2oQJliu9aDds2LDMAi5ocVdRUQGHwwFQEqE+evRo3L17F9bW1nB1dRV6ZyclKdp7mxv7AFRUAPnm3UTqN2nCOBR9/wCgxB1Qp06dci8dHZ0yf2tqagq9uK9evRpmZmY4cOAAJk6cKNLchIW5uTmAEmnXXr16iXXs5OTkKgNf+eXGa0Pa99ixY0hMTMTTp09hYmKCBw8eMBkL/v7+UFRURN++fWFnZwcbGxuRNjlsNhv29vYYOnQoNm/ejNWrVyMwMBCLFi3CnDlzhArQ9PPzw8ePHzF8+HDcvn1b5LLrQMnCm5OTUy3XhbiRmJiII0eOICAgAM+fP4empibGjBkDR0dHtG7dWqjPmIhw8uRJLF26FC9fvsTAgQNx8uRJtGzZ8hfcwV/w8Z+wEFSVivZ5/TDIGrWBpu2iMse/H1uOvI9PoD87CGypik1cTd8fx51zJZGy2trajBWgV69etWoF4PF4yMzMrNZOvTJluaoWcEH/V1VVFVvuL5fLxbJly7B69Wp0794dBw8eFGp3kVPARYtlFyFsmGhSiCcKvr6EnvNBsDnCzZ0F4PhIA2SkJCExMbHMKyEhgfl/ampqmX5sNhuamppVEoc6depASUkJY8aMwa1btxAXFyeWqPSfwePxoKamhoULF8Ld3V2sYw8ePBh5eXk4f/58hW2MjY1ha2sLPz8/sV6by+WiSZMmaNmyJU6ePFnmHBHhxYsXCAsLQ2hoKKKioiAlJYUePXrA1tYWgwYNErmgWEpKCry8vLB9+3ah4of4yMnJgZWVFb5+/YqHDx+KlI7Mr0UClGQwCFNcTdzIz8/H6dOnERAQgIsXL0JCQgIDBgyAk5MT+vTpwxD8qkBEuHDhAjw8PBAVFYVevXrBy8uLIax/UXP8tRD8hMMPP1eaWkjFRWBJlvfzsjjSQDEXRcmfIK0rWPedeMX4Im2IRYsWwcbGBq1btxbJClBRsJwwO/WKyrCy2exyi7a2tjaaNm1a5UIvDi33mkJSUhJeXl6wsrKCvb09WrVqhcDAQPTp06fSfvLSkqinJidUlkFxbibyPz6FfNMuQpMBoESitE2rFgAqz2kvLCzE9+/fyxEF/is2Nha3bt1CQkICU4WPD74uwLdv39CuXTt06dJFIHnQ1taudiocm81Gu3btEBERUa3+lSE5OblSX296ejrev39fo/iBinD48GG8e/dOYCAhi8VCixYt0KJFC3h4eODTp09MxsK0adMwdepUdOjQAba2trCzsxNYtvpnaGhoYOPGjXB2dsbixYsxbtw4bNiwAWvXrq008l9eXh5nzpyBpaUl+vbtizt37lSqd5BTwMXH1BwUcnmIef4ULI4MNFWVfml1QyLC/fv3ERAQgKNHjyIzM5OpOjpixAiRNz83btyAh4cH7t69i06dOuHGjRvo2rVrLc3+L4TBf4IQXH/zvdL0Qo6aHgq+vQHxihl1OyouQsG3NwAAblYqKvI4stgSUG/ZBXOmmSM9PR0vXrwQaacuTLBc6UXbyMioyp26oqLiH+OaqAl69OiBZ8+ewcnJCTY2NliwYAFWrVpV6SJo1VhLKB2CnFe3AF6xSO4CCTYLVo2EqwopJSUFPT096OmVjz8pDT4hFEQcwsLCEBsbCxaLheTk5DLV+vhQV1cXymWhpqZWjuiZm5tj7969ICKxksCUlBS0a9euwvPiCCgUBC6Xi5UrV2LQoEFC6X4YGBhg1qxZmDVrFlJSUhAeHo7Q0FAsWbIECxcuRIsWLWBnZwc7O7sqTd/GxsYICQnBgwcPsGDBAlhbW6N3795Ys2YNWrVqJbCPtrY2zp07hw4dOmDYsGE4e/ZsmZ01o5//5js+p5XVz9efdwwyRVlYfuYlxljUQ0Pt2tPP//jxIw4ePIjAwEC8ffsW+vr6mDFjBhwdHavl23/48CE8PDxw5coVtGnTBhcuXEDv3r1/+0bkL/4DhCC7gIvPVewYFc36Iu3idqSe2wwlyyEA8ZB57yiKs9MBAMQVHAjHx8eUHCiqaoCK8sudU1BQKLdom5iYVLlTLx0s91+GlpYWzp49y8ge37x5s1LZ4zEW9XDg/scqx815cQNsORXIGLYWei7FPIK9pXh3ZCwWC0pKSlBSUioXsT579mwYGxujf//+8Pb2BpfLRUpKSoVWhy9fviAiIgKJiYnlXEIcDqccacjMzERSUhL27NmDFi1aMMdr6qKoKoYgKioKcnJyIkXoC4MjR47g3bt3IokQ8aGhoYGxY8di7NixyMnJwcWLFxEaGootW7Zg5cqVMDAwYCwHHTt2rDAexNLSErdv30ZYWBhcXV1hamoKR0dHeHl5CSSHTZs2xcmTJ2FtbY1p06bB398fX9PzqhRNY7FYKJBSwsGHn3Dg/kexV9jLysrC8ePHERAQgJs3b0JeXh5Dhw7Frl270K1bt2ptOJ49e4YlS5bgzJkzaN68OU6ePAlbW9u/ROAPwr8+huDFt0z023KnynbpNwPx4+FJgMcFAEjVaQgZIzP8uHcUmoPdIdeofaX9le9vhwplQ0FBAUpKSlBWVoaKigrk5eUhKysLGRkZyMrKlvn/z//+fExYP9x/BXzZ4+TkZOzevRsjR44U2M5h70Pce58qkuhUVZBgs9DBSB0HJ1iIbUxhUN3yyDk5OUhKSqqQPCQmJiI+Ph4JCQnl+iopKQmMbfjZ8qChoVGOtBYXF4PD4WDXrl2YNGmSwLmNHj0anz59wt27d0V7MyoBl8tFs2bN0LRpU5w6dUps4xYVFeHmzZsICwtDWFgY4uPjoa6uzmQs9OzZs0ICVVRUhN27d2P58uXIysrC3Llz4erqKtA1EBgYCCcnJzgs24mH3Hoii6ZJsFmQZLMY0bTqoLi4GNeuXUNAQABOnjyJ/Px8WFlZwcnJCYMHD662LPDr16/h6emJkJAQNGjQAMuXL8eIESP+bnh+Ef4KE5WCKKloxfnZKEr+BLa0PKS0DJF+MwA/7h+DzsTtkNKo/Edm9PYE2OmfkZeXh/z8fOTl5ZX5f35+PgoKCkSau4SEhFDEoapzoo7xJ/9Qf/z4gSlTpiA4OBgTJkzApk2byqX+VZZiWl1IS7JxZW7XX17jvLbLI+vp6cHOzg5TpkyplDwkJCQgPT29TF82mw0tLa0yJEFJSQmbNm3C/PnzMWjQIOa4goICsxNs0qQJevXqhS1btojtPvhplI8ePUKbNm3ENm5p8Hg8PHr0iAlKfP36NeTl5dGnTx9GDElQCuePHz/g6+uL9evXQ0FBAZ6enpgyZUo5wj94yW5EcXVRUhiy+rvmBb0bYaZV+eJrFeHVq1cICAjAoUOHEB8fj0aNGjHqgTWJUfjw4QOWL1+OgwcPQldXF56ennB0dPy70fnF+EsISkFYC4EgJATMRXF2OnSn7wOLVbmJ7KxzJzSvW3kRFB6Ph/z8/DIkQRBxqOiYqO35/y8qKhLpvjkcTo1JhajHZGRkhDZDEhH279+PmTNnVih7HBz5GYtORot035XBd7AJRlRz51VT+Pn5wdXVFa9evRJYZbMmGDJkCNLS0nD9+vUq2xYUFDBWh8rcFvHx8eX6ysnJMemXDx8+RPfu3WFlZVXO8qClpSXyglFcXIxmzZqhUaNGOHPmjEh9a4LXr18z5CAiIgKSkpKwsrJiMhZ0dXXLtP/69SuWLFmCgIAANGjQAD4+PrCzswOLxfrl39fU1FQEBQUhICAAjx49gqqqKkaOHAknJyeYm5vXyIwfHx8PLy8v7NmzB+rq6nB3d8fkyZOrrf3wFzXDX0JQCqKmojH9Xt1Cyqk1ULUaDyWLwZW2ZQGIWWZdplTyn4Ti4uJaIRpVHasoWLIiSElJiUQ0cnNzcenSJaSlpaF///7o1q0b00ZWVhY3U2QQ/rnmwZU594Jw1HM8unfvXuOxqoOfyyOLE76+vvDy8kJGRoZYLEO3bt1C165d8fDhQygpKZUjD8+ePcPFixfRsGFDZGZmIjk5uVygpIaGRoUpmaWPqaiogMVi4fDhw7C3t0dkZCTatm1b43uoDuLj45mMhRs3boDL5cLCwoKJOygdfPf8+XO4uLjg4sWL6NChA1yW+8LlZpZAi1b+p+dICiqvgwIAdRzWVZj9JMiiVVhYiHPnziEgIABnz54FEaFv375wdHRE//79a7xgJycnw8fHB9u3b4ecnBxcXV0xc+ZMsRfP+gvR8JcQ/ISqCt7kf45B5t0gyNQ3BVtWCYXfXiP7+RXI1DeF1tClTOZBRTBQl8PNBVbinvY/HkVFRbVOPnJzc5GUlIScnBxISEiUIyEKLXtDtdcUsNgSYEmIQNh4xQDxoPnpOj5dD0Jqaiq6d+8OIyMjsbhmpKSkRNqF7du3DxMmTBC7Sfz69evo3r07YmJiBNabFxUnT57EkCFDkJKSIrAex+bNm+Hi4oKsrCxwOBwUFRUhOTm5Qj2H0sdycnLKjCUlJQVtbW0kJydDWVkZtra2AsmDtra22DQyhEF6ejrOnj2L0NBQXLhwAbm5uWjSpAmTsdC2bVuwWCxcvnwZCxcuREKTIZA1bA0IsELyCYFimwGQ0ikbhClrZAYJOcFWSX7MS+B4czx+/BgBAQEICir5HpuamsLJyQmjRo2ClpZwWTOVISMjA+vWrcPGjRvBZrMxf/58zJ079x+5Vvwb8VeH4CdUlYomoagOsNn48fAkeIV5kFTRhkoXByiZ21ZJBkRJRfuvgcPhgMPhQFGx9lKi+Dh58iQmTJgAJSUlBAQEwNTUlCENH77/wMZ7SXiWVAg2C6gsVosFAoEF9eJUGKdFApQCbXNzPHjwAFevXsWnT5/A4XAEEh1RwGKxRCIVUlJSUFNTw9ChQzF+/Phqu2Z+Nse3adMGLBYLERERYiEEycnJjA6GIERFRaFly5bMPDgcDurWrStUwGR2dnY5knD16lWcOnUKZmZmiIyMRGJiIpKSksoRQxUVlUrFoPjH1NXVa5yyq6qqCnt7e9jb2yMvLw+XL19GaGgodu/eDW9vb+jq6sLW1ha2trY4dOYK+m9/WOWY0vrNId9EyHobKMmIuf02Bc0su+N1xA3UqVMH48aNg6Ojo1iqigIln8emTZuwbt06FBQUYNasWVi4cOF/pjDbvxH/CQtBVUqFNcWVuV3QQKv2F72/qBx82eOHDx9ixYoV5WSPmbzu2O/4nFo2r5uFEtEhq0ZasLesV+7zLCgowPDhw3HhwgWcOHEC/fv3L3OeiFBQUCB2a0jpc9+/f0d8fDxUVVXB4/GQl5dXYW2IiiAhIVGOLHz+/BnKyspo2rRpjWNGDh06hMOHD+PZs2cCA1VbtmyJDh06iCVAsri4GC1atED9+vVx7ty5MsdTU1OrtDokJiYiIyOj3Pujra0tFHkQtY4Fl8vFnTt3mLiDz58/o06/WZBu0VOgdQD4n4VAw3YRZOubgcWRrnKTwgfxiqGTHYdlA5qjZ8+eYquLkZeXh507d8Lb2xuZmZmYOnUqFi9ejDp16ohl/L8QL/66DATg35SK9hcVo7TssZWVFQ4dOiRQ9ri08puUJBuG6vJVxoAUFhZi5MiRCA8PR0hICGxtbWvpLgSDiNC+fXvweDw8fPgQLBarXKBqdchHeHg4kpOT0adPH6HHEDVQVVJSEjIyMsjOzoaamho0NTVrHJz68OFDeHt7Y9++fWjXrp3AMara7fOLHwlDHn4mXwoKCpUSB/5xQXUsiAhPnjyB47EPyEbF7gw+IWBJyYIK8wAWG9L6zaFqNf7/2rvzuJrT93/gr/fp1GmhVLKLbGVL0YKQaETDDDNDJEPTZwwfa4VhlCTiY2f4GutkMJhpaux79i1KMhliMBFaLC1KdXpfvz9m6jdNdTrndKK4nv/Mo3Pe93XuzKPeV/f7vq8LsoYVbzDV5OPM/Px8bNmyBfPnz8fTp0/h7e2NwMDAN1otkamOE4IyvEtH0VjFoqKiMHLkSBQWFmLr1q0YMGCARuIWFBRg5MiRiIyMxK5du6q0RXFZTp06BRcXF4SHh2vss9etW4fJkycjMzNT6aJEcrm8zBMzM2bMwJMnT/C///2vVDJx+/ZtrF69Gt7e3jAxMVE5gcnNzYUoqvbzW7RRtbKnZnR1dVFYWIjc3Fy8evUKr169QmZmJl6+fFlcfTQ9PR2pqaml+lgIglCij0VR8mBSryH+L60lFB0xfP3od2RdiYReCztI9I1QkJ6EzOhIUMFrNPBaAp0Gissra2LDc2FhIbZv347g4GA8ePAAnp6emDt3Llq1aqV2TPbmcEJQjnfpKBqrWGpqKsaMGYNDhw7B398foaGhatf+/ye5XI5Ro0bh559/xo4dO+Dh4aGB2SpP0+2RY2JiYGdnhwsXLqBbN8UFuCri5uaGWrVq4Zdffin13nfffYdJkyYhKytL7U1+RRtVd+7cWVyPwtLSsspP0Sjxa7IEmUwGHR0daGtrQyKRFG8gFUURcrkcBQUFEI0aof7olar/G7x4jCebJ0HWtD3qe8yr8HpljkSXRRRFhIeHIygoCLdu3cInn3yC4OBgdOiguI8Hq154U2E5htubIz07D0uPJlY61vR+lpwMVHP16tXD/v37sWLFCsycObO47LEyTWsUkUql2LZtG6RSKTw9PVFYWAhPT08Nzbpimm6P3LFjR8hkMkRHR1c6IUhPT4eFhUWZ78XGxqJ9+/aV2vGvra0NLS0trFq1Cm5ubm8kGSMi5OfnazzReCExwkM15qNt3Ah6rR2Rk3ihRP+V8uSruCpKRDhw4AACAgJw/fp1DBgwANu3b6+ygk+s+nivEgIAmOjSGnVryRC0N0Ht8qDzPmrPyUANUXQMqlevXhg+fDhsbW2xfv16jBgxolJxpVIpwsLCIJVKMWrUKMjlcnz++ecamrVitra2GDFiBObOnYuRI0dWuveAjo4ObG1tNdL5UFEfg9jYWI00NAoPD8fNmzexadOmSsdShiAIkMlkkMlkCjsSqqoyRdOkhnWBQjmoIA+CTPEjSx2p8qcmTpw4gYCAAFy6dAnOzs44e/YsevRQ/nQDq9lqfks8NQy3N8dxX2d0b/HX8RgtieLz4EXvd29hiuO+zpwM1ED29va4du0aBg4cCE9PT/j4+JQ6164qLS0tbN68GV988QXGjBmDLVu2aGi2FZs3bx5SUlKwZs0ajcRzcHDQSEKQnp4OMzOzUq/n5+fjxo0blU4IRFHEvHnz8MEHH1R6NeNta25qoHaBYvnLpxCkOhB0FK+2CH9/TkUuXLiAPn36wNXVFaIo4tixYzh58iQnA++Z9zIhAICmJvrY5uOIY1N7YZRjMzQz1S/1wyngr126oxyb4bhvL2zzceQNhDWYoaEhduzYgS1btmDXrl2ws7NDfHx8pWJKJBKsX78e48aNg4+PDzZs2KCh2SrWqlUrjB07FgsXLix1dE4dDg4OuHv3Lp4/f652jFevXiE3N7fMFYKbN28iPz+/0glBREQEEhISEBQUVKk41YGBTArzCn6fFOZklHotP+Uecu5EQ7e5bYUl1c1N9RVuKIyNjcWHH34IJycnPHv2DHv27MGlS5fg6urKXQjfQ+/dI4N/a12/NuZ+1B5z0V6to2isZhEEAd7e3ujatSuGDx8OBwcHLF++HOPHj1f7F6BEIsHatWshlUrx1VdfoaCgABMmTNDwzEsLDAxEWFgYFi9ejNDQ0ErFcnBwAABcuXIFbm5uasVIT08HgDJXCGJjYyEIAjp16qT2HEVRRHBwMFxdXeHk5KR2nOqkoqJpab/+DxJtHcgat/37lMFDZF8/DEFbBuPeYxTGVlQ07ebNm5gzZw5++eUXtGnTBrt27cLQoUMrXZSJ1Wz8f/8fDGRStG9kBFtzY7RvZMTJwDusbdu2uHTpEnx8fDBhwoTiJj/qEgQBq1atgq+vLyZOnIhVq1ZpcLZla9CgAXx9fbFy5Uo8fvy4UrFatWqFOnXqVOqxQVpaGgCUuUIQExMDKysrlYv5/FNkZCR+++23d2J1oMhIR3OF+5j023RFYU4mMqN/xfOj65Bz6yz023RHwzEroF23qcLYhSLBq2vJx5t//PEHRo0ahQ4dOiAmJgbff/89EhIS4OHhwckA4xUC9v7S09PD2rVr4erqii+++AI2NjbYuXOn2n99CoKAZcuWQVtbG1OnToVcLoe/v7+GZ13S9OnTsW7dOoSEhGDdunVqxxEEodL7CCpaIajM44KivQN9+vR5p55rt65fGzYNZIh7nAOUcVrA0O4jGNp9pHLcoqJpRRU3Hz58iJCQEGzZsgX169fH2rVr4ePjo5FjuOzdwSkhe+8NGTIE169fh7m5OZydnbFgwQKVOzUWEQQBixYtwjfffINp06Zh0aJFGp5tSUZGRvjmm2+wceNG3Llzp1KxHB0dER0drfKZ+yLlrRDI5XJcv369UgnBnj17EB8f/06tDsjlcixatAhH53sDpLmCaQAglQgIHdIRKSkpmDp1Klq1aoXIyEgsXrwYd+/exfjx4zkZYKVwQsAYAHNzc5w6dQqzZs1CYGAg+vXrp/YyvCAImD9/PoKCgjBr1izMnz9fw7MtacKECWjYsCECAgIqFcfBwQGpqalISkpSa3x6ejoMDAxKHYO8ffs2cnNz1U4IivYOuLi4oFevXmrFqG5u376NHj16YPbs2Zj0hSdCBmum4VCRr/s2x/8tCUGLFi0QFhaGwMBA3Lt3D35+fpU+psreXfzIgLG/SaVShISEwMXFBV5eXujUqRO2bt0Kd3d3lWMJgoC5c+dCKpUiMDAQcrkcQUFBVbJzW1dXF8HBwfDx8cGMGTPULiBjb28PAIiOjkazZs1UHl9eDYLY2FgAgI2NjVrz2rt3L65fv45Tp06pNb46EUURq1evxqxZs9C0aVOcO3eu+PhkRp6okaJpXaTJ8P34r4JZU6dOxbRp08rtPsnYP/EKAWP/0qdPH1y/fh329vb48MMP4e/vr3JXwSIBAQFYuHAhgoODERgYqPZyfEU+//xztG3bFrNmzVI7Rv369dGsWTO19xGUV4MgNjYWLVu2RJ06dVSOSUQIDg5G79694ezsrNa8qot79+7BxcUFvr6+GDt2LOLi4krUUpjo0hqLPukImVRSYW2Uf9OSAFoQ8frMZhxYMhk+Pj64d+8eFixYwMkAUxonBIyVwczMDPv378eyZcvw7bffwsnJCXfv3lUr1syZM7F06VIsWLAAM2fOrJKkQCqVYsGCBTh27BhOnDihdpzKbCxUtEKg7qrF3r17ERcXV6P3DhARvvvuO1hbWyMpKQknT57EqlWroK9fugaBukXTCh4mIHnjOAyxro+7d+9i+fLlqFev7COHjJWHEwLGyiGRSODn54cLFy7gxYsX6Ny5M3bu3KlWLH9/f6xcuRKLFy+Gv79/lSQFgwcPhqOjI2bNmqV2fAcHB1y9ehVyuVzlsWWtEIiiiGvXrqm1f6BodaBXr17o3bu3yuOrg4cPH8LNzQ3jx4+Hl5cX4uPjK/xelC2aZqwth/z3KDzZ9F/0kdzEzeizWLduHZo0aVJV3w5715ESMjIyCABlZGQoczlj75yMjAzy9PQkAOTt7U3Z2dlqxVmzZg0BoEmTJpEoihqeJdHJkycJAIWHh6s1/vTp0wSA4uPjVR5raWlJvr6+JV5LTEwkAHT06FGV4+3du5cAUFRUlMpj3zZRFGnLli1kaGhIjRs3psOHD1cqXvbrAvot+SXFPHhGSzfvolZW7QkADR06lG7evKmhWbN3kSr3b04IGFOSKIr0/fffk76+PllZWVFcXJxacdavX08AaPz48VRYWKjhWRL179+fLC0tqaCgQOWxWVlZJJFIaNOmTSqPNTU1pdDQ0BKv7dq1iwBQWlqaSrFEUaTOnTtTz549qyRxqkqPHz+mgQMHEgAaPXo0vXjxotIxRVGkyMhI6tChAwGggQMH0rVr1yodl737OCFgrAr9/vvvZG1tTTKZjNasWaPWDWvz5s0kCAJ9+eWXGk8KYmNjCQBt3LhRrfEdO3aksWPHqjRGLpeTIAi0YcOGEq/PmDGDzM3NVZ7Dvn37CAAdP35c5bFviyiK9OOPP5KxsTHVr1+f9uzZo5GYR44cIXt7ewJAffv2pQsXLmhgtux9wQkBY1UsNzeXJkyYQABo8ODB9OzZM5VjhIWFkSAI5O3tTXK5XKPzGzFiBDVu3JhycnJUHuvj40M2NjYqjUlNTSUAFBkZWeJ1V1dXGjx4sEqxRFEkOzs7cnJyqjGrA6mpqfTZZ58RAPLw8KD09PRKxzxz5gz17NmTAFC3bt1q5KMT9vZxQsDYGxIREUHGxsbUtGlTOnv2rMrjd+zYQRKJhEaNGqXRpODOnTsklUpp8eLFKo9dv349aWlp0atXr5Qek5CQQABK/BuIokgmJiY0b948lT7/wIEDBICOHTum0ri3JSIigszMzMjU1JR2795d6XjR0dHk5uZGAMjW1pYOHDhQYxIjVv1wQsDYG/Tnn39Sjx49SCKRUEhIiMo39l27dpGWlhaNGDFCref+5fnvf/9LxsbGKj/DvnbtGgGgc+fOKT2maDPirVu3il978OABAaD9+/crHUcURbK3t6fu3btX+5vg8+fPaeTIkQSAPv74Y3r69Gml4sXHx9PgwYMJALVt25bCw8OrZI8Je79wQsDYG1ZQUECBgYEkCAK5uLhQcnKySuPDw8NJKpXS0KFDKT8/XyNzevLkCenr69OsWbNUGpefn096enq0fPlypceEh4cTgBJL5REREQSAHj9+rHScgwcPEgA6cuSISnN+0w4cOEANGzYkIyMj+uGHHyqVvCQmJtKIESNIEARq0aIF/fDDDxp/hMTeX5wQMPaWREVFUcOGDalu3boq/WVMRPTrr7+StrY2DRkyhPLy8jQyn9mzZ5Oenp7KCYqTkxMNHz5c6eu/++47kkgkJf6iDQgIoAYNGigdQxRFcnR0pG7dulXb1YGMjAzy8fEhAOTm5kYPHz5UO9aDBw/oiy++IC0tLWrSpAmtX79eY8kgY0U4IWDsLUpNTSV3d3cCQH5+fird3Pft20c6Ojo0aNAgev36daXn8vLlSzIxMaFx48apNM7X15datGih9PUhISFkZmZW4jV3d3dyd3dXOsahQ4cIQKXP7FeV48ePk7m5OdWqVYs2bNigdtLy+PFjmjhxImlra1O9evVo5cqVlJubq+HZMvYXTggYe8sKCwtp+fLlpK2tTV26dKE7d+4oPfbQoUMkk8nI3d1dIzeKpUuXkpaWFiUmJio9ZufOnSrVD5gyZQq1a9eu+GtRFKl+/foUEBCg1HhRFKlr167k6OhY7VYHsrOzi0+UuLi40P3799WKk56eTtOnTyc9PT2qU6cOhYaGUlZWlmYny9i/cELAWDVx9epVatmyJdWqVYt27Nih9LijR4+Srq4uubm5qXV08J9yc3OpSZMm5OHhofSYP/74gwDQwYMHlbre09OTevXqVfx1cnIyAaCIiAilxh85coQA0KFDh5Se45tw9uxZatmyJenp6dHq1avV2uT38uVLmjNnDtWuXZtq1apFgYGBGilWxJgyOCFgrBpRt+zxiRMnSF9fn1xdXVU6AliWzZs3EwC6evWqUteLokimpqY0d+5cpa7v168fffrpp8VfFxUWevDggVKf1a1bN3JwcKg2qwM5OTnk7+9PgiBQ9+7dVVpdKZKdnU0LFy4kY2Nj0tXVpWnTpqlcsZGxyuKEgLFq5p9ljy0tLZUuO3v69GkyMDCg3r17V2p5uaCggNq2bUv9+vVTesyAAQOU3gNga2tLX331VfHXwcHBZGJiotQN/ujRowSADhw4oPTcqtLly5fJysqKZDIZLVmyROUd/7m5ubRy5UqqV68eaWtr04QJE1Q6acGYJqly/+Zuh4y9AYIgYMyYMYiJiYGuri66du2KNWvWVNiVsFevXjhy5AiuXr0Kd3d3ZGVlqfX5Re2Rjx49iqioKKXGFLVCrmiOQOlOh7GxsejcuTMEQXH7Xvq7o6GdnR0GDBig1LyqSl5eHmbPno3u3bujVq1aiI2NxbRp06ClpaXU+IKCAmzcuBGtW7eGn58fBg4ciMTERKxZswYNGzas4tkzpgGazjAYY4rl5ubSxIkTiwvaKFP2+OLFi2RoaEjdu3dX++ew6Fifvb29Un+5F1UMvHfvXoVxdXV1aeXKlcWvNW3alGbMmFHhZxw/flzl4kVV4dq1a2RtbU1SqZRCQkJUOv4nl8tp27Zt1LJlSwJAw4cPp9u3b1fhbBlTHj8yYKwGiIyMVKnscXR0NNWpU4ccHR3V3pSmSnvkov4Eu3btUnhddnY2AaDt27eXGFdRGV9RFKlHjx5kZ2f31vYO5Ofn07x580gqlZK1tbVKHQRFUaTw8HBq165dcXJ3/fr1qpssY2rghICxGiIpKam47PG8efMqfF4dExNDxsbGZGdnp1ZDJSLV2iNbWFiQn5+fwmvu379forpg0YmBio5anjhxggDQ3r17lZ+8BiUkJJCdnR1paWnR7Nmzla4XIYoiHTx4kDp37kwAqF+/fnT58uUqni1j6uGEgLEapKCggObMmUOCIFDv3r3p0aNHCq+Pi4sjU1NTsrGxUaurnirtkT08PKhHjx4Kr7ly5QoBoJiYGCIiWrhwIRkaGio8oieKIvXq1Ys6d+78xlcH5HI5LV68mGQyGVlZWal0Mz958iQ5OTkRAOrRowedPn26CmfKWOVxQsBYDRQVFUWNGjUiU1PTCp+px8fHk5mZGVlbW1NqaqrKn6Vse+Rly5aRnp6ewtWEov4DSUlJREQ0dOhQcnZ2Vhg3KiqKANCePXtUnntlJCYmUvfu3UkQBPL391e6xsPFixfJ1dWVAJCdnR0dPny42hyRZEwRPmXAWA3k4uKCuLg4ODo6YuDAgfDz80NeXl6Z13bs2BGnTp1CSkoKXFxckJKSotJnzZs3DykpKVizZo3C6xwcHJCbm4uEhIRyr0lPTwcA1K1bF8D/P2GgSHBwMGxtbTFo0CCV5q0uURTx7bffolOnTkhJScGZM2ewdOlS6OnpKRwXFxeHQYMGoVu3bnj69CkiIyMRHR0NNze3Ck9QMFbjaDrDYIxVjiiKSpc9/v3336lhw4ZkZWWl8ll3ZdojZ2dnk5aWFq1fv77ca5YtW0YGBgZERPTixQsCQNu2bSv3+qKNjb/++qtK81XX/fv3ycXFhQDQhAkTlCoM9fvvv9OwYcMIALVu3Zp+/PFH7kDIaiReIWCsBhMEAb6+vrh48SIyMjJga2uLHTt2lHmtlZUVTp8+jaysLPTu3RvJyclKf05gYCDy8vKwePHicq8xMDBAhw4dEB0dXe416enpxasDcXFxAKBwhSA4OBg2Njb46KOPlJ6rOogIGzduRMeOHfHHH3/g+PHjWLNmDQwMDModc//+fYwZMwbt27fHpUuXsGnTJty8eRMjRoxQuh4BYzUVJwSMVVNdunRBbGwsPv74Y3h5ecHb2xvZ2dmlrmvdujVOnz6N169fw9nZGQ8fPlQqfoMGDeDr64uVK1fi8ePH5V5XVKCoPGlpacVFiWJjY6GnpwdLS8syrz1z5gxOnTqFOXPmVOmS+6NHj+Du7o6xY8fCw8MDN27cQN++fcu9Pjk5GePHj0ebNm1w5MgRrFq1ComJifDx8YFUKq2yeTJWrWh6yYExplmiKFJYWFiFZY/v379PzZs3JwsLC6V6CBAp1x5548aNJJFIyi2dPHjwYOrfvz8REY0cOZK6detWbqw+ffqQtbW1Wk2ClCGKIm3dupWMjIyoYcOGFZZDTk1NJT8/P9LV1SUTExNavHhxpftGMFad8CMDxt4hgiBg9OjRiI2Nha6uLhwdHcsse9y8eXOcPn0agiDA2dkZ9+7dqzC2kZERvvnmG2zcuBF37twp8xoHBweIoojY2Ngy3//3CkF5jwvOnj2LqKgoBAUFQSLR/K+elJQUDBkyBKNHj8bAgQPx22+/wd3dvcxrX7x4gYCAAFhYWGDTpk2YNWsW7t+/j+nTp0NfX1/jc2OsRtB0hsEYqzq5ubk0adKk4sp4ZdUhePjwIbVu3ZqaNm1aYXGgopiK2iMXFBSQvr4+LVmypMz3LS0tydfXl7Kzs0kQBNq8eXOZ1/Xt25c6duxYJasDP/30E5mampKZmZnClstZWVk0f/58qlOnDunp6dHXX3+tVi0HxmoKrkPA2Dvu119/JRMTE2rSpAmdOXOm1PvJyclkaWlJjRo1UqqufkXtkXv27ElDhw4t8z0TExMKDQ2lc+fOEYAyH2kUvffzzz9XOBdVpKenk4eHBwGgTz/9tNyaDDk5ObRs2TKqW7cu6ejo0OTJk+nJkycanQtj1REnBIy9B5KSkqhnz57llj1+8uQJtWvXjho0aEA3b95UGKui9sj+/v7UrFmzMscJgkAbNmyg1atXk46OTpklgD/44APq0KGDRlcH9uzZQ/Xr1ydjY2P68ccfyywUlJeXR+vWraNGjRqRlpYWffnll/Tnn39qbA6MVXecEDD2nqio7HFKSgp16NCB6tWrR7/99pvCWBEREQSATpw4Ueq93bt3EwB6+vRpqfgAKDIyksaMGUNdunQpNfb8+fMEgH766Sc1vsPSXrx4QZ9//jkBoIEDB5ZZf0Eul1NYWBhZWFiQIAjk5eWl1OMTxt41nBAw9p45efJkcdnjffv2lXgvLS2NOnXqRHXr1lXYjU9Re+SiBkb/jp2QkEAA6OzZs2RtbU1ffvllqbj9+vWj9u3ba2R14PDhw9S4cWMyNDSkLVu2lJpnYWEh7d69m6ysrIofI1SUCDH2LuNTBoy9Z3r37o3r16+ja9euGDRoEHx9fYvLHtetWxdRUVEwNzeHi4sLrl27VmYMQRCwaNEiXLlyBRERESXea9asGczMzErVI0hLSwMAGBoaIiEhodQJg4sXL+Lo0aOYM2dOpU4WZGVl4auvvkL//v3Rrl073LhxA97e3sW1DIgI+/fvR+fOneHh4QELCwtcvXoV4eHhaN++vdqfy9h7RdMZBmPs7RFFkVasWEHa2trUuXNnSkxMLH7v+fPnZG9vT3Xq1KErV66UG6O89sgffvghubm5lXgtPDycANDRo0cJQKnOgW5ubtSuXbtKrQ6cPHmSmjdvTgYGBrRu3boSqwKiKNLx48epa9euBIB69+5N586dU/uzGHvX8AoBY+8pQRAwdepUXLx4EZmZmejcuTO2b98OADA2NsaxY8dgZWUFV1dXXL58ucwYoaGhuH37NsLCwkq8XlSxkP5R/yAtLQ0SiQR3796FlpYWrK2ti9+7fPkyjhw5gsDAQLVWB3JycjBlyhS4uLjA3Nwc8fHxGDduXPGqwIULF9CnTx+4urpCFEUcO3YMUVFRcHJyUvmzGGPgFQLG3lWZmZnk5eVFAGj06NHFlQYzMjLIycmJateuTefPny9zbFntkQ8dOkQASmzOCwkJITMzMxo7dixZW1uXiDFgwACysrJSqynQ+fPnqXXr1qSrq0srVqwoscIQExNDAwYMIADUqVMn2rt3L7ciZqwcvELAGEPt2rWxbds2bN26FeHh4ejSpQvi4uJgaGiIw4cPw9bWFm5ubjh79mypsWW1R7a3tweAEvsIiqoU/rtCYXR0NA4dOoQ5c+ao1BTo9evX+Prrr9GzZ0+YmJggLi4OU6dOhUQiQUJCAj777DN06dIF9+7dw+7duxEbG4tBgwZxK2LGNEHTGQZjrPq5desW2djYkI6ODq1evZpEUaTs7Gzq06cP6evr08mTJ0uNKas9csuWLWnKlCnFX3t6elKPHj2K4xZxd3dXeXXg6tWr1K5dO9LR0aGFCxcW72G4e/cueXl5kSAI1Lx5cwoLCyu1v4ExVjZeIWCMlWBpaYmLFy9i3LhxmDx5MgYPHozXr19j3759cHJygru7O06cOFFiTFntkf/d+TAtLQ0ymQz5+fnFKwRXrlzBwYMHERAQoNTqQH5+PoKCguDo6AiZTIaYmBjMnDkTT548wdixY2FpaYmoqCisXbsWt2/fxujRo7kDIWNVQdMZBmOsetuzZ09x2ePTp09Tbm4u9e/fn3R1denw4cMlrp09ezbp6elRcnIyERGtWLGCZDIZvcjKod+SX1LH3oOo64ChJNHRK96jMHDgQGrTpo1SqwPx8fFkY2NDUqmUgoKCKD8/n54+fUpTpkwhHR0dqlu3Li1btqzEXgbGmPJUuX8LRP9qmVaGzMxMGBkZISMjA4aGhlWfpTDGqtSjR4/g6emJ8+fPIygoCNOmTYOHhweOHTuGiIiI4i6BGRkZaNGiBYYNGwa/uYuxdM9l7I+9D23jRiUDEqGZqQHaGRM2zxyDLStD4eXlVe7ny+VyLFmyBEFBQWjTpg22bt0KCwsLLFmyBKtXr4a2tjamT5+OyZMno3bt2lX5T8HYO02V+zcnBIy9p+RyOebPn4+QkBD07NkTW7ZsgZ+fHw4dOoTw8HAMGjQIADBn8WpsiM2EroUttASgUNFvDBIBQQKnlqZY9Ik1mpqUbiV869YtjB49GlevXsX06dPh7++PdevWYdmyZSgsLMTUqVPh7+8PY2PjKvrOGXt/qHL/5j0EjL2npFIp5s6dixMnTuDOnTtwcHDAmDFjMGjQIHzyySeIjIzEritJ2J3VGrJmf9UXUJgMAIDw16+US/efw3XFaey6klT8VmFhIZYvXw5bW1u8fPkSJ06cgKmpKdq2bYvQ0FD85z//wb179zB//nxOBhh7C3iFgDGG9PR0eHt7Y//+/Zg0aRKePHmC40+kMOpZ/rK/sqb1a4MB5hKMGTMG586dw8SJE4sfD6Snp+M///kPAgIC0LhxYw18J4yxf+JHBowxlRERvv32W0yfPh0t+41CTochGouddXwdDNMTMGzYMOzatQuPHj3CqFGjEBQUBAsLC419DmOsJE4IGGNqO3Q2GuP3JYMk2uUW/Ml7ehcZ535E3qObIHkBpHXqo5ZNfxjafVTqWiKChOSQHArFvRtXMGzYMMydOxdt27at6m+FsfeeKvdvPszLGCvhx0SCllSn3P0CufdjkRo+Dzr1W8Ko+3AIOrqQv3yKwqz0Mq8XBAGiKMCg1xe49sMG2NjYVN3kGWNq44SAMVbsTkoWzt4t+8YOAGJeDtL3L4deS3uYDZkFQVBuX7KgJUV27aao1ailpqbKGNMwPmXAGCu243IStCTl9wV4dfMUxFcvYdzrcwiCBGL+axCJSsXWkgjYfimp4gsZY28FrxAwxoqdvJ2KQrH8bUWvH8RBkOlDnv0MqRHzIX+eDEFbFwYdXGDS90sIUp1yxxaKhJOJqZiL9lUxdcZYJXFCwBgDAGTnyZH0PEfhNQXPHwNiIdJ+CUEt637QdR6N10k3kBWzD+LrVzD7eIbC8UnPcvAqTw4DGf/qYay64Z9KxhgA4M9nr1DRkSMqeA0qyEMt2wEw+eArAIC+ZXdQYQGy4w6joOdIaJuUX0+AADx49grtGxlpbuKMMY3gPQSMMQBAvrzivQBFjwQM2jqXeN2gXW8AQF7yLY18DmPszeOEgDEGANCRVvzrQKuW6V//NahT8nWDv/7iF19na+RzGGNvHv9kMsYAAM1NDVD++YK/6DT469igPOtZidflWc8BAFr6ih8FCH9/DmOs+uGEgDEGADCQSWFeRnfCEtdY9QQAZMcfLfF6dvxRQKIFmXlHhePNTfV5QyFj1RT/ZDLGirlY1sO2y3+We/RQp0FLGFh/gFfxx5AmitA174DXSTeQc+scDLsNhbS2abmxtSQCXNrUq6qpM8YqiRMCxlixkY7mCLv4QOE1pm4TIDU0Q3b8ceQkXoTUyAzGfb+Eof3HCscVigSvruYanC1jTJM4IWCMFWtdvzZ6tqqLC/eelbtKIGhJUaeHJ+r08FQ6rpZEQPcWpmhVr7ampsoY0zDeQ8AYKyF0SEdIFZQvVodUIiB0iOL9BYyxt4sTAsZYCU1N9BH8kWbLC8/7qD2aVrBhkTH2dnFCwBgrZbi9Oab1a6ORWNP7WcLDnvcOMFbd8R4CxliZJrq0Rt1aMgTtTYBcJIVNj/5NSyJAKhEw76P2nAwwVkPwCgFjrFzD7c1x3NcZ3Vv8XaGwgr0FRe93b2GK477OnAwwVoPwCgFjTKGmJvrY5uOIOylZ2HE5CScTU5H0LKdEIyQBfxUdcmlTD15dzfk0AWM1kEBEFa4DZmZmwsjICBkZGTA0NHwT82KMVWOv8uR48OwV8uUidKQSNDc14AqEjFVDqty/+SeYMaYyA5mUWxgz9o7hPQSMMcYY44SAMcYYY5wQMMYYYwycEDDGGGMMnBAwxhhjDJwQMMYYYwycEDDGGGMMnBAwxhhjDJwQMMYYYwycEDDGGGMMnBAwxhhjDJwQMMYYYwycEDDGGGMMnBAwxhhjDJwQMMYYYwycEDDGGGMMgFSZi4gIAJCZmVmlk2GMMcaY5hTdt4vu44oolRBkZWUBAJo2bVqJaTHGGGPsbcjKyoKRkZHCawRSIm0QRRGPHz9G7dq1IQiCxibIGGOMsapDRMjKykKjRo0gkSjeJaBUQsAYY4yxdxtvKmSMMcYYJwSMMcYY44SAMcYYY+CEgDHGGGPghIAxxhhj4ISAMcYYY+CEgDHGGGMA/h9PKG8N4ZfesAAAAABJRU5ErkJggg==" + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "# a classical Hamiltonian instance\n", + "clauses = [\n", + " [4, 1, 7],\n", + " [5, 11, 8],\n", + " [4, 1, 8],\n", + " [4, 11, 8],\n", + " [4, 1, 10],\n", + " [5, 11, 8],\n", + " [4, 1, 8],\n", + " [1, 11, 8],\n", + " [4, 1, 7],\n", + " [0, 11, 8],\n", + " [4, 1, 10],\n", + " [4, 11, 8],\n", + " [5, 0, 10],\n", + " [0, 6, 7],\n", + " [5, 0, 11],\n", + " [0, 6, 7],\n", + " [5, 0, 9],\n", + " [3, 6, 7],\n", + " [5, 0, 8],\n", + " [5, 6, 7],\n", + " [5, 0, 10],\n", + " [3, 6, 7],\n", + " [5, 0, 10],\n", + " [1, 6, 7],\n", + " [2, 4, 6],\n", + " [1, 8, 11],\n", + " [2, 4, 6],\n", + " [2, 8, 11],\n", + " [2, 4, 9],\n", + " [5, 8, 11],\n", + " [2, 4, 10],\n", + " [2, 8, 11],\n", + " [2, 4, 10],\n", + " [4, 8, 11],\n", + " [2, 4, 8],\n", + " [4, 8, 11],\n", + " [3, 0, 9],\n", + " [5, 11, 7],\n", + " [3, 0, 10],\n", + " [2, 11, 7],\n", + " [3, 0, 9],\n", + " [0, 11, 7],\n", + " [3, 0, 9],\n", + " [5, 11, 7],\n", + " [3, 0, 10],\n", + " [3, 11, 7],\n", + " [3, 0, 7],\n", + " [4, 11, 7],\n", + " [5, 0, 10],\n", + " [4, 0, 10],\n", + " [2, 5, 6],\n", + " [2, 11, 10],\n", + " [2, 6, 10],\n", + " [2, 4, 9],\n", + " [0, 9, 10],\n", + " [3, 0, 7],\n", + " [2, 5, 6],\n", + " [1, 10, 9],\n", + " [1, 4, 11],\n", + " [5, 10, 11],\n", + " [0, 4, 8],\n", + " [0, 9, 8],\n", + " [2, 11, 10],\n", + " [2, 8, 6],\n", + " [3, 6, 7],\n", + " [0, 8, 10],\n", + " [4, 0, 9],\n", + " [3, 5, 8],\n", + " [5, 11, 10],\n", + " [2, 11, 10],\n", + " [4, 11, 8],\n", + " [1, 3, 11],\n", + "]\n", + "factor = 1 / len(clauses) / 4\n", + "\n", + "# convert to a NetworkX graph\n", + "graph = nx.Graph()\n", + "for i, j, k in clauses:\n", + " graph.add_edge(i, j, weight=0)\n", + " graph.add_edge(j, k, weight=0)\n", + " graph.add_edge(k, i, weight=0)\n", + "for i, j, k in clauses:\n", + " graph[i][j][\"weight\"] += 1\n", + " graph[j][k][\"weight\"] += 1\n", + " graph[k][i][\"weight\"] += 1\n", + "pos = nx.spring_layout(graph)\n", + "nx.draw_networkx(graph, with_labels=True, pos=pos)\n", + "ax = plt.gca()\n", + "ax.set_facecolor(\"w\")" + ] + }, + { + "cell_type": "markdown", + "source": [ + "Then we construct the Hamiltonian from the graph." + ], + "metadata": { + "collapsed": false + } + }, + { + "cell_type": "code", + "execution_count": null, + "outputs": [], + "source": [ + "def b2s(bit):\n", + " return 1 - 2 * int(bit)\n", + "\n", + "\n", + "def energy(cfg):\n", + " E = 0.25\n", + " for a, b in graph.edges:\n", + " E += cfg[a] * cfg[b] * graph[a][b][\"weight\"] * factor\n", + " return E\n", + "\n", + "\n", + "def construct_Ham():\n", + " num_nodes = graph.number_of_nodes()\n", + " Es = []\n", + " for i in range(2**num_nodes):\n", + " case = f\"{bin(i)[2:]:0>{num_nodes}}\"\n", + " Es.append(energy(list(map(b2s, case))))\n", + " return jnp.asarray(Es)\n", + "\n", + "\n", + "Hv = construct_Ham()" + ], + "metadata": { + "collapsed": false + } + }, + { + "cell_type": "markdown", + "source": [ + "Here we calculate $e^{-\\tau\\hat{H}}$ in advance to perform exact imaginary-time evolution for comparison with approximate imaginary-time evolution." + ], + "metadata": { + "collapsed": false + } + }, + { + "cell_type": "code", + "execution_count": 5, + "outputs": [], + "source": [ + "tau_c = 0.025\n", + "exp_tauHv = K.exp(-tau_c * Hv)" + ], + "metadata": { + "collapsed": false, + "ExecuteTime": { + "end_time": "2023-07-13T02:33:37.999586200Z", + "start_time": "2023-07-13T02:33:37.923540200Z" + } + } + }, + { + "cell_type": "markdown", + "source": [ + "### Variational wave function" + ], + "metadata": { + "collapsed": false + } + }, + { + "cell_type": "markdown", + "source": [ + "Here we choose regular QAOA ansatz as the parameterized quantum circuit ([PQC](https://tensorcircuit.readthedocs.io/en/latest/textbook/chap5.html?highlight=变分))." + ], + "metadata": { + "collapsed": false + } + }, + { + "cell_type": "code", + "execution_count": 6, + "outputs": [], + "source": [ + "nlayers = 20 # the number of layers\n", + "\n", + "\n", + "@partial(K.jit, static_argnums=(1, 2))\n", + "def wfn_classical(params, each=1, return_loss=False):\n", + " n = graph.number_of_nodes() # the number of nodes\n", + "\n", + " # PQC loop\n", + " def pqc_loop(s_, params_):\n", + " c_ = tc.Circuit(n, inputs=s_)\n", + " for j in range(each):\n", + " # driving layer\n", + " for a, b in graph.edges:\n", + " c_.RZZ(a, b, theta=graph[a][b][\"weight\"] * params_[2 * j] * factor)\n", + " # mixing layer\n", + " for i in range(n):\n", + " c_.RX(i, theta=params_[2 * j + 1])\n", + " s_ = c_.state()\n", + " return s_\n", + "\n", + " c0 = tc.Circuit(n)\n", + " for i in range(n):\n", + " c0.H(i)\n", + " s0 = c0.state()\n", + " s = K.scan(pqc_loop, K.reshape(params, [nlayers // each, 2 * each]), s0)\n", + " c = tc.Circuit(n, inputs=s)\n", + "\n", + " if return_loss:\n", + " loss = 0.25\n", + " for a, b in graph.edges:\n", + " loss += c.expectation_ps(z=[a, b]) * graph[a][b][\"weight\"] * factor\n", + " return K.real(loss)\n", + " else:\n", + " return c.state()" + ], + "metadata": { + "collapsed": false, + "ExecuteTime": { + "end_time": "2023-07-13T02:33:38.001837Z", + "start_time": "2023-07-13T02:33:37.969064900Z" + } + } + }, + { + "cell_type": "markdown", + "source": [ + "### Optimization" + ], + "metadata": { + "collapsed": false + } + }, + { + "cell_type": "markdown", + "source": [ + "We use two methods to calculate $\\boldsymbol{\\delta}$, one is to calculate directly according to the expressions of $\\boldsymbol{A}$ and $\\boldsymbol{C}$, and the other is to call the existing API to calculate $\\boldsymbol{A}$ and $\\boldsymbol{C}$. The former only needs to calculate the $|\\partial_{\\boldsymbol{\\theta}_{j}}\\psi\\rangle$ once, while the latter needs to calculate that twice, but the code of the latter is more concise. In each method, we set the parameter ``fixed_global_phase`` to decide whether to fix the global phase, that is, whether the second term of $\\boldsymbol{A}$ vanishes.\n", + "\n", + "Then we choose the existing optimizer, SGD, to implement the update step. Since compared with naive gradient descent, the approximate imaginary-time evolution has been corrected on the update step size, the adaptive optimizer improved for the naive gradient descent such as Adam is not suitable for the approximate imaginary-time evolution. When update by the adaptive optimizer, the loss function fluctuates greatly. On the other hand, the update method of SGD without momentum is naive update, which is convenient for comparison with the exact imaginary-time evolution." + ], + "metadata": { + "collapsed": false + } + }, + { + "cell_type": "code", + "execution_count": 7, + "outputs": [], + "source": [ + "@partial(K.jit, static_argnums=(3,))\n", + "def d_params(params, psi, eps=1e-6, fixed_global_phase=False):\n", + " psi = psi[:, None]\n", + " psiHT = K.conj(K.transpose(psi))\n", + " par_psi = K.jacfwd(wfn_classical, argnums=0)\n", + " jac = par_psi(params)\n", + " jacHT = K.conj(K.transpose(jac))\n", + "\n", + " A = K.real(jacHT @ jac)\n", + " if not fixed_global_phase:\n", + " A -= K.real(jacHT @ psi @ psiHT @ jac)\n", + " # protection\n", + " A += eps * K.eye(params.shape[-1], dtype=A.dtype)\n", + "\n", + " C = K.real((psiHT * Hv) @ jac)[0]\n", + "\n", + " return K.solve(A, C, assume_a=\"sym\")\n", + "\n", + "\n", + "@partial(K.jit, static_argnums=(2,))\n", + "def d_params_api(params, eps=1e-6, fixed_global_phase=False):\n", + " if fixed_global_phase:\n", + " qng = experimental.qng(\n", + " wfn_classical, kernel=\"dynamics\", postprocess=None, mode=\"fwd\"\n", + " )\n", + " else:\n", + " qng = experimental.qng(\n", + " wfn_classical, kernel=\"qng\", postprocess=None, mode=\"fwd\"\n", + " )\n", + " A = K.real(qng(params))\n", + " # protection\n", + " A += eps * K.eye(params.shape[-1], dtype=A.dtype)\n", + "\n", + " vag = K.value_and_grad(partial(wfn_classical, return_loss=True), argnums=0)\n", + " loss, C2 = vag(params)\n", + "\n", + " return loss, K.solve(A, C2 / 2, assume_a=\"sym\")\n", + "\n", + "\n", + "if K.name == \"jax\":\n", + " opt = K.optimizer(optax.sgd(tau_c))\n", + "else:\n", + " opt = K.optimizer(tf.keras.optimizers.SGD(tau_c))" + ], + "metadata": { + "collapsed": false, + "ExecuteTime": { + "end_time": "2023-07-13T02:33:38.002380500Z", + "start_time": "2023-07-13T02:33:37.969607700Z" + } + } + }, + { + "cell_type": "code", + "execution_count": 8, + "outputs": [ + { + "data": { + "text/plain": "
", + "image/png": "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6qkK38+tt97K//73vwrfD0gAqjIa3DoIAHXb7awb86eLqxFCiJpn1KhRNG/enKSkJObNm0f79u1JTk7GaDS6ujRSUlIYMWIEcXFxuLm5ERISwm233cbMmTPJz893touKikKlUqFSqfD09KRly5Z89dVX19z2Bx98wCuvvFLsuc2bN6PRaOjVq1ex506cOOHcl0qlIiAggG7durF7925nm9tvv52RI0c6H7/66qu8/PLL2O32G/guXB8JQFVEfMOhFOa5gU8OGWdWubocIYSocRITE+nSpQu1a9fG19cXvV5PaGgoKpXKpXUdP36cFi1asGrVKt555x12797N5s2beemll1i+fDmrVxedQuWNN94gOTmZ3bt306ZNG/r06cOmTZuuuP3PPvuM9u3bU6dOnWLPzZ49m+HDh/Pbb79x7ty5El+/evVqkpOT+fnnn8nNzaVnz55XPGrWs2dPcnJy+Omnn0r/DbhBEoCqCK1Gz9nkWgDUvmUHFlPFp2MhhLgaRVGw5dlcsihK6WfGj4qKYurUqUXWNW/enHHjxjkfq1QqPvvsM+6//348PDyoW7cu33//PXD5SEZ6ejqDBw9GpVIxb968YqfABg8eTNOmTSksLATAbDbTokUL+vfv79zPd999R8uWLXFzcyMmJobx48djtVqdzx89epROnTrh5uZGo0aN+OWXX67Zv6effhqtVsuOHTt4+OGHadiwITExMdx33338+OOP3HPPPUXae3t7ExoaSr169ZgxYwbu7u788MMPV9z+4sWLi20DIDc3lyVLljBs2DB69erFvHnzSnx9QEAAoaGhtG7dmsmTJ5OamsrWrSXf31Kj0XDXXXexePHia/a7rLQVvgdRbvzq3glKIob4LWx8+xi3v1nP1SUJIWowe76dDV4bXLLvjrkd0XhqynWb48eP57333mPSpEl8+OGH9OvXj5MnTxIREUFycjL169fnjTfeoE+fPhiNxmIf4tOmTaNZs2a8/PLLvP/++7zyyitkZmYyffp0ADZs2ED//v2ZNm0aHTt2JDEx0TmuZuzYsdjtdh544AFCQkLYunUrWVlZRU4PlSQ9Pd155MfT07PENlc7QqXVatHpdJjN5hKfz8jI4MCBA7Ru3brYc0uXLqVBgwbUr1+fxx57jJEjRzJmzJir7s/d3R3givsDaNu2LRMnTrzi8+VFjgBVIV3a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" + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Epoch 1999: 6.1109702587127686\tTotal: 12327.309196949005\n" + ] + } + ], + "source": [ + "steps_classical = 2000\n", + "\n", + "# initial parameters\n", + "params = K.implicit_randn(shape=(2 * nlayers,), stddev=0.1)\n", + "params_fgp = K.copy(params)\n", + "params_api = K.copy(params)\n", + "params_fgp_api = K.copy(params)\n", + "psi_exact = wfn_classical(params)\n", + "\n", + "losses, losses_fgp, losses_exact = [], [], []\n", + "losses_api, losses_fgp_api = [], []\n", + "\n", + "eps = 1e-6\n", + "t0 = ts = time.time()\n", + "\n", + "for i in range(steps_classical):\n", + " psi = wfn_classical(params)\n", + " loss = K.real(K.tensordot(K.conj(psi) * Hv, psi, 1))\n", + " delta = d_params(params, psi, eps)\n", + " params = opt.update(delta, params)\n", + " losses.append(K.numpy(loss))\n", + "\n", + " psi_fgp = wfn_classical(params_fgp)\n", + " loss_fgp = K.real(K.tensordot(K.conj(psi_fgp) * Hv, psi_fgp, 1))\n", + " delta_fgp = d_params(params_fgp, psi_fgp, eps, fixed_global_phase=True)\n", + " params_fgp = opt.update(delta_fgp, params_fgp)\n", + " losses_fgp.append(K.numpy(loss_fgp))\n", + "\n", + " loss_api, delta_api = d_params_api(params_api, eps)\n", + " params_api = opt.update(delta_api, params_api)\n", + " losses_api.append(K.numpy(loss_api))\n", + "\n", + " loss_fgp_api, delta_fgp_api = d_params_api(\n", + " params_fgp_api, eps, fixed_global_phase=True\n", + " )\n", + " params_fgp_api = opt.update(delta_fgp_api, params_fgp_api)\n", + " losses_fgp_api.append(K.numpy(loss_fgp_api))\n", + "\n", + " loss_exact = K.real(K.tensordot(K.conj(psi_exact) * Hv, psi_exact, 1))\n", + " psi_exact *= exp_tauHv\n", + " psi_exact /= K.norm(psi_exact)\n", + " losses_exact.append(K.numpy(loss_exact))\n", + "\n", + " eps *= 0.999\n", + "\n", + " # visualise the progress\n", + " clear_output(wait=True)\n", + " plt.xlabel(\"Iteration\")\n", + " plt.ylabel(\"Cost\")\n", + " plt.plot(range(i + 1), losses_exact, c=\"r\", label=\"exact\")\n", + " plt.plot(range(i + 1), losses, c=\"b\", label=\"unfixed GP\")\n", + " plt.plot(range(i + 1), losses_fgp, c=\"g\", label=\"fixed GP\")\n", + " plt.plot(range(i + 1), losses_api, c=\"m\", label=\"unfixed GP (API)\")\n", + " plt.plot(range(i + 1), losses_fgp_api, c=\"y\", label=\"fixed GP (API)\")\n", + " plt.legend()\n", + " plt.show()\n", + "\n", + " te = time.time()\n", + " print(f\"Epoch {i}: {te - ts}\\tTotal: {te - t0}\")\n", + " ts = te" + ], + "metadata": { + "collapsed": false + } + }, + { + "cell_type": "markdown", + "id": "98a6b152", + "metadata": {}, + "source": [ + "### Results" + ] + }, + { + "cell_type": "markdown", + "source": [ + "We first show the overlap between the final states obtained by different methods. The final states obtained by different methods but with the same parameter of ``fixed_global_phase`` are almost the same, which are also close to the exact final state. And the final states obtained by the same method but with the different parameter of ``fixed_global_phase`` has a global phase difference." + ], + "metadata": { + "collapsed": false + } + }, + { + "cell_type": "code", + "execution_count": 9, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "overlap between psi_fgp and psi\n", + "|overlap|: 0.9184369993048669\n", + "overlap: (-0.4580633040968157+0.7960556080651879j)\n", + "\n", + "overlap between psi_fgp_api and psi_api\n", + "|overlap|: 0.9184279183739732\n", + "overlap: (-0.4588479071459403+0.7955931368209131j)\n", + "\n", + "overlap between psi_api and psi\n", + "|overlap|: 0.999999996801054\n", + "overlap: (0.999999545918014-0.0009496135391365943j)\n", + "\n", + "overlap between psi_fgp_api and psi_fgp\n", + "|overlap|: 0.9999999922593946\n", + "overlap: (0.9999999907968652+5.408381530766986e-05j)\n", + "\n", + "overlap between psi_exact and psi\n", + "|overlap|: 0.8876639929202528\n", + "overlap: (-0.45130602703961775+0.7643757153944926j)\n", + "\n", + "overlap between psi_exact and psi_fgp\n", + "|overlap|: 0.9290015132797724\n", + "overlap: (0.9278995939751679-0.04523444679473629j)\n", + "\n", + "overlap between psi_exact and psi_api\n", + "|overlap|: 0.8876569548840061\n", + "overlap: (-0.45202998890392976+0.7639396302624046j)\n", + "\n", + "overlap between psi_exact and psi_fgp_api\n", + "|overlap|: 0.9290084278770248\n", + "overlap: (0.9279048483224752-0.04526865942553761j)\n" + ] + } + ], + "source": [ + "psi = wfn_classical(params)\n", + "psi_fgp = wfn_classical(params_fgp)\n", + "psi_api = wfn_classical(params_api)\n", + "psi_fgp_api = wfn_classical(params_fgp_api)\n", + "\n", + "overlap = K.tensordot(K.conj(psi_fgp), psi, 1)\n", + "print(\n", + " f\"overlap between psi_fgp and psi\\n|overlap|: {K.abs(overlap)}\\noverlap: {overlap}\\n\"\n", + ")\n", + "overlap = K.tensordot(K.conj(psi_fgp_api), psi_api, 1)\n", + "print(\n", + " f\"overlap between psi_fgp_api and psi_api\\n|overlap|: {K.abs(overlap)}\\noverlap: {overlap}\\n\"\n", + ")\n", + "overlap = K.tensordot(K.conj(psi_api), psi, 1)\n", + "print(\n", + " f\"overlap between psi_api and psi\\n|overlap|: {K.abs(overlap)}\\noverlap: {overlap}\\n\"\n", + ")\n", + "overlap = K.tensordot(K.conj(psi_fgp_api), psi_fgp, 1)\n", + "print(\n", + " f\"overlap between psi_fgp_api and psi_fgp\\n|overlap|: {K.abs(overlap)}\\noverlap: {overlap}\\n\"\n", + ")\n", + "overlap = K.tensordot(K.conj(psi_exact), psi, 1)\n", + "print(\n", + " f\"overlap between psi_exact and psi\\n|overlap|: {K.abs(overlap)}\\noverlap: {overlap}\\n\"\n", + ")\n", + "overlap = K.tensordot(K.conj(psi_exact), psi_fgp, 1)\n", + "print(\n", + " f\"overlap between psi_exact and psi_fgp\\n|overlap|: {K.abs(overlap)}\\noverlap: {overlap}\\n\"\n", + ")\n", + "overlap = K.tensordot(K.conj(psi_exact), psi_api, 1)\n", + "print(\n", + " f\"overlap between psi_exact and psi_api\\n|overlap|: {K.abs(overlap)}\\noverlap: {overlap}\\n\"\n", + ")\n", + "overlap = K.tensordot(K.conj(psi_exact), psi_fgp_api, 1)\n", + "print(\n", + " f\"overlap between psi_exact and psi_fgp_api\\n|overlap|: {K.abs(overlap)}\\noverlap: {overlap}\"\n", + ")" + ], + "metadata": { + "collapsed": false, + "ExecuteTime": { + "end_time": "2023-07-13T05:59:13.932343300Z", + "start_time": "2023-07-13T05:59:13.843507900Z" + } + } + }, + { + "cell_type": "markdown", + "source": [ + "Then we show the exact solution by the brutal force method." + ], + "metadata": { + "collapsed": false + } + }, + { + "cell_type": "code", + "execution_count": 10, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "min cost: 0.0\n", + "exact solution: ['000000111111', '111111000000']\n" + ] + }, + { + "data": { + "text/plain": "
", + "image/png": "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T2vLGe/fD8TGAn366DxVAFQZcw0i+lswQ0BEAAADNwu0rKy0r6Ghrd8fP177eu5cu6ebGbolQUR/GwYERAQA8LC2NhwUuLlpX+/tM9D8s4E9Au73Uf2QiLoWAwIiAgAMd3CAuaHf4L29ta+Vla3fb/7Z2VnvqY0iCGLnQqJuiwEBEQEARjk6tl+DAABCQrSv337b+v1vvtE2QTJQZ2AkAwKiboubCokIABDu6goXCwtUNTbqH+TlpX2qICND+7TBkCFAcTGQnQ3MnPn/txTuYQ7gaWdn9LGx6ZK1E9H9Y0BARAAAazMzzPfwwPpr19DU3sBFi4AnntAGBTk52r8vWABMm6b3lCYAr7ObIVG3xuZGRNSiqqEBPvn5+K2hAYbrDxrHAtrbEceHDuVTBkT/YR35/uYeAiJq4WJpia0+PiYLBhQAFmZmSBs4kMEAUTfHgICIWol0c8NqT8/7nkf588+Xfn4YYGt73/MRUddiQEBEbazo3x//rVIB0G4I7CgLAFaKgv/198fzejYaElH3woCAiNpQFAXv9O+PY4GB6P1nwSJjEv7NwcMIR0ecGTECU9zdu3CVRGRKDAiISK+xzs44P3IkPvDygtdf0v6WigJzaAOAv3ZI/C8nJ+zw80PO0KHwsrP7zy+YiDqNjx0SUbvszc2xoHdv/MvDA2f++AOFajWKa2tR1dgIMwA9rKww3MEBIx0coOJeAaIHFgMCIjKKoigIeOwxBDz22N+9FCLqArxlQERERAwIiIiIiAEBERERgQEBERERgQEBERERgQEBERERgQEBERERgQEBERERgQEBERERgQEBERERgQEBERERgQEBERERgQEBERERgQEBERERgQEBERERgQEBERERgQEBERERAbAwZpCIAABqamq6dDFERERkOs3f283f4+0xKiBQq9UAgL59+97HsoiIiOjvoFar4eTk1O4YRYwIGzQaDW7cuAEHBwcoimKyBRIREVHXERGo1Wp4eHjAzKz9XQJGBQRERET0cOOmQiIiImJAQERERAwIiIiICAwIiIiICAwIiIiICAwIiIiICAwIiIiICMD/AesKzAgE7WDEAAAAAElFTkSuQmCC" + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "idx = [f\"{bin(i)[2:]:0>{graph.number_of_nodes()}}\" for i in np.where(Hv < 1e-6)[0]]\n", + "print(f\"min cost: {0.}\\nexact solution: {idx}\")\n", + "\n", + "# plot NetworkX graph\n", + "colors = [\"r\" if idx[0][i] == \"0\" else \"c\" for i in graph.nodes]\n", + "nx.draw_networkx(graph, with_labels=True, node_color=colors, pos=pos)\n", + "ax = plt.gca()\n", + "ax.set_facecolor(\"w\")" + ], + "metadata": { + "collapsed": false, + "ExecuteTime": { + "end_time": "2023-07-13T05:59:14.080058300Z", + "start_time": "2023-07-13T05:59:13.925677800Z" + } + } + }, + { + "cell_type": "markdown", + "source": [ + "Then we use the bit string with the maximum probability as the approximate solution since we know all information of the probability distribution of the output quantum state, but which is not feasible in the experiment. For the hard problem with rough energy landscape, approximate imaginary-time evolution with 20-layer ansatz gives mediocre results, similar to those obtained by the Adam optimizer with 30-layer ansatz, which can be found in the tutorial of [QAOA for NAE3SAT](qaoa_nae3sat.ipynb). However, this does not mean that approximate imaginary-time evolution is more practical than Adam optimizer because under the same ansatz, the former consumes much more time for each step update than the latter. In contrast, the exact imaginary-time evolution gives excellent results but takes exponential time to compute on a classical computer." + ], + "metadata": { + "collapsed": false + } + }, + { + "cell_type": "code", + "execution_count": 11, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "cost by exact IME: 0.01636900218695893\n", + "cost: 0.03377396891545763\t0.02951298530564577 (fgp)\n", + "cost (API): 0.033775089880425574\t0.0295121140061756 (fgp)\n", + "\n", + "max prob by exact IME: 0.13847431901217763\n", + "max prob: 0.03930045644879558\t0.05100224184204695 (fgp)\n", + "max prob (API): 0.03929777441862845\t0.05100594691342963 (fgp)\n", + "\n", + "bit strings by exact IME: ['111111000000']\n", + "bit strings: ['111111000000']\t['000000111111'] (fgp)\n", + "bit strings (API): ['000000111111']\t['111111000000'] (fgp)\n" + ] + } + ], + "source": [ + "loss = K.real(K.tensordot(K.conj(psi) * Hv, psi, 1))\n", + "loss_fgp = K.real(K.tensordot(K.conj(psi_fgp) * Hv, psi_fgp, 1))\n", + "loss_api = K.real(K.tensordot(K.conj(psi_api) * Hv, psi_api, 1))\n", + "loss_fgp_api = K.real(K.tensordot(K.conj(psi_fgp_api) * Hv, psi_fgp_api, 1))\n", + "loss_exact = K.real(K.tensordot(K.conj(psi_exact) * Hv, psi_exact, 1))\n", + "print(f\"cost by exact IME: {K.numpy(loss_exact)}\")\n", + "print(f\"cost: {K.numpy(loss)}\\t{K.numpy(loss_fgp)} (fgp)\")\n", + "print(f\"cost (API): {K.numpy(loss_api)}\\t{K.numpy(loss_fgp_api)} (fgp)\\n\")\n", + "\n", + "\n", + "# find the states with max probabilities\n", + "def find_max(psi):\n", + " probs = K.numpy(K.real(K.conj(psi) * psi))\n", + " max_prob = max(probs)\n", + " index = np.where(probs == max_prob)[0]\n", + " states = []\n", + " for i in index:\n", + " states.append(f\"{bin(i)[2:]:0>{graph.number_of_nodes()}}\")\n", + " return max_prob, states\n", + "\n", + "\n", + "prob, states = find_max(psi)\n", + "prob_fpg, states_fpg = find_max(psi_fgp)\n", + "prob_api, states_api = find_max(psi_api)\n", + "prob_fpg_api, states_fpg_api = find_max(psi_fgp_api)\n", + "prob_exact, states_exact = find_max(psi_exact)\n", + "print(f\"max prob by exact IME: {prob_exact}\")\n", + "print(f\"max prob: {prob}\\t{prob_fpg} (fgp)\")\n", + "print(f\"max prob (API): {prob_api}\\t{prob_fpg_api} (fgp)\\n\")\n", + "print(f\"bit strings by exact IME: {states_exact}\")\n", + "print(f\"bit strings: {states}\\t{states_fpg} (fgp)\")\n", + "print(f\"bit strings (API): {states_api}\\t{states_fpg_api} (fgp)\")" + ], + "metadata": { + "collapsed": false, + "ExecuteTime": { + "end_time": "2023-07-13T05:59:14.197783300Z", + "start_time": "2023-07-13T05:59:14.080058300Z" + } + } + }, + { + "cell_type": "markdown", + "id": "4ae99ab9", + "metadata": {}, + "source": [ + "## Quantum Hamiltonian" + ] + }, + { + "cell_type": "markdown", + "id": "720dd1a4", + "metadata": {}, + "source": [ + "The quantum Hamiltonian is the Hamiltonian of the transverse and longitudinal field Ising model." + ] + }, + { + "cell_type": "markdown", + "source": [ + "### Define the Hamiltonian" + ], + "metadata": { + "collapsed": false + } + }, + { + "cell_type": "markdown", + "source": [ + "Here we also calculate $e^{-\\tau\\hat{H}}$ in advance to perform exact imaginary-time evolution for comparison with approximate imaginary-time evolution." + ], + "metadata": { + "collapsed": false + } + }, + { + "cell_type": "code", + "execution_count": 13, + "id": "2115bb6d", + "metadata": { + "ExecuteTime": { + "end_time": "2023-07-13T06:54:56.088379500Z", + "start_time": "2023-07-13T06:54:55.808504500Z" + } + }, + "outputs": [], + "source": [ + "N = 10\n", + "g = tc.templates.graphs.Line1D(N, pbc=False)\n", + "h = tc.quantum.heisenberg_hamiltonian(\n", + " g, hzz=1, hyy=0, hxx=0, hz=0.5, hx=1, hy=0, sparse=True\n", + ")\n", + "H = tc.array_to_tensor(h.todense())\n", + "\n", + "tau_q = 0.001\n", + "exp_tauH = K.expm(-tau_q * H)" + ] + }, + { + "cell_type": "markdown", + "source": [ + "### Variational wave function" + ], + "metadata": { + "collapsed": false + } + }, + { + "cell_type": "markdown", + "source": [ + "Compared with the regular QAOA ansatz in the classic example, this ansatz has a higher parameter density and the initial state is $|0\\rangle$ instead of $|+\\rangle$." + ], + "metadata": { + "collapsed": false + } + }, + { + "cell_type": "code", + "execution_count": 14, + "outputs": [], + "source": [ + "l = 10 # the number of layers\n", + "\n", + "\n", + "@partial(K.jit, static_argnums=(1,))\n", + "def wfn_quantum(theta, each=1):\n", + " # PQC loop\n", + " def pqc_loop(s_, theta_):\n", + " c_ = tc.Circuit(N, inputs=s_)\n", + " for i in range(each):\n", + " for j in range(N):\n", + " c_.RZZ(j, (j + 1) % N, theta=theta_[i, j, 0])\n", + " for j in range(N):\n", + " c_.RX(j, theta=theta_[i, j, 1])\n", + " s_ = c_.state()\n", + " return s_\n", + "\n", + " c0 = tc.Circuit(N)\n", + " s0 = c0.state()\n", + " s = K.scan(pqc_loop, K.reshape(theta, [l // each, each, N, 2]), s0)\n", + " c = tc.Circuit(N, inputs=s)\n", + "\n", + " return c.state()" + ], + "metadata": { + "collapsed": false, + "ExecuteTime": { + "end_time": "2023-07-13T06:54:58.940024800Z", + "start_time": "2023-07-13T06:54:58.920216200Z" + } + } + }, + { + "cell_type": "markdown", + "source": [ + "### Optimization" + ], + "metadata": { + "collapsed": false + } + }, + { + "cell_type": "markdown", + "source": [ + "We also use two methods to calculate $\\boldsymbol{\\delta}$, but make some changes in the method of directly calling the API and the update method. When calculating $\\boldsymbol{A}$, we call ``qng2`` instead of ``qng``, and when calculating $\\boldsymbol{C}$, we call ``dynamics_rhs`` instead of calculating the energy gradient by ``value_and_grad``. For the update method, we do not call the existing optimizer but directly adopt the naive update method." + ], + "metadata": { + "collapsed": false + } + }, + { + "cell_type": "code", + "execution_count": 15, + "outputs": [], + "source": [ + "@partial(K.jit, static_argnums=(3,))\n", + "def d_theta(theta, psi, eps=1e-6, fixed_global_phase=False):\n", + " psi = psi[:, None]\n", + " psiHT = K.conj(K.transpose(psi))\n", + " par_psi = K.jacfwd(wfn_quantum, argnums=0)\n", + " jac = par_psi(theta)\n", + " jacHT = K.conj(K.transpose(jac))\n", + "\n", + " A = K.real(jacHT @ jac)\n", + " if not fixed_global_phase:\n", + " A -= K.real(jacHT @ psi @ psiHT @ jac)\n", + " # protection\n", + " A += eps * K.eye(theta.shape[-1], dtype=A.dtype)\n", + "\n", + " C = K.real(psiHT @ K.sparse_dense_matmul(h, jac))[0]\n", + "\n", + " return K.solve(A, C, assume_a=\"sym\")\n", + "\n", + "\n", + "@partial(K.jit, static_argnums=(2,))\n", + "def d_theta_api(theta, eps=1e-6, fixed_global_phase=False):\n", + " if fixed_global_phase:\n", + " qng = experimental.qng2(\n", + " wfn_quantum, kernel=\"dynamics\", postprocess=None, mode=\"fwd\"\n", + " )\n", + " else:\n", + " qng = experimental.qng2(wfn_quantum, kernel=\"qng\", postprocess=None, mode=\"fwd\")\n", + " A = K.real(qng(theta))\n", + " # protection\n", + " A += eps * K.eye(theta.shape[-1], dtype=A.dtype)\n", + "\n", + " vag = experimental.dynamics_rhs(wfn_quantum, h)\n", + " C = vag(theta)\n", + "\n", + " return K.solve(A, C, assume_a=\"sym\")\n", + "\n", + "\n", + "@K.jit\n", + "def update(theta, delta, tau):\n", + " return theta - K.cast(tau * delta, dtype=theta.dtype)" + ], + "metadata": { + "collapsed": false, + "ExecuteTime": { + "end_time": "2023-07-13T06:55:01.610891800Z", + "start_time": "2023-07-13T06:55:01.549133100Z" + } + } + }, + { + "cell_type": "code", + "execution_count": 16, + "outputs": [ + { + "data": { + "text/plain": "
", + "image/png": "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tfesLklEpICMDPDzs2bdaWlwlu/5Iuvbty89e/YE4OWXX+att95i1apVdO7c2XVKKzAwkLCwsDPO7+fnx2effca1116Lv78/kyZNYuHCha6nhI8dO5annnqKPn36ABAVFcULL7zAk08+yXPPPcf8+fPZtm0bc+fOJSIiwlVHly5dzlrzrl27ME2T+vXrFxhftWpVsrKyAHjooYd45ZVXTps3JyeH119/nZSUFDp06HCBP62yTcGnFIqsV5uFH19JjRsXEFznd+d/byw+jywiUp5dfvnlrve+vr4EBASQkJBwQcto27YtI0aM4IUXXmDkyJG0b9/eNW3Dhg0sXbrUdYQHID8/n6ysLDIyMti6dSuRkZGu0HNyeUWxatUqHA4HvXr1cp16O2nkyJE888wzZGVl4efnx/jx4+natWuR1lNWKfiUUtGtnyA7bxF+DXfww7hXuP3/nrK6JBERFx8f55EXq9ZdWDabDfNf58Zyc3NPa+fu7l7gs2EYOByOC6rL4XCwdOlS7HY7u3btKjAtLS2NsWPHcscdd5w2n5eX1wWt56To6GgMw2D79u0FxkdFRQHg7e192jxPPPEEffv2xc/Pj9DQUMs7Z1tBwaeUatetE9++0oyqbdaSnfcToOAjIqWHYRT/6aaSEBwcTFxcnOtzamoqsbGxJbKuCRMmsG3bNv744w86derEtGnT6NevHwAtWrRg+/btREdHn3Hehg0bsn//fuLi4ggPDwdgxYoV51xflSpVuPHGG3nnnXd45JFHztrP51RVq1Y9aw0VhTo3l2I5+d0BCG39F7v+XGVtMSIiZVCHDh349NNPWbx4MX///Td9+vTBbrcX+3rWrVvH6NGj+eijj7jqqquYOHEiQ4cOZffu3QCMHj2aTz75hLFjx7J582a2bt3KjBkzeOaZZwDo2LEj9erVo0+fPmzYsIHFixfz9NNPn3e97777Lnl5ebRq1YqvvvqKrVu3sn37dj777DO2bdtWItta1in4lGL3PDmKnH3VMbyyWTRznNXliIiUOaNGjeLaa6/llltuoWvXrnTv3p06deoU6zqysrLo3bs3ffv2dV0+PnDgQK6//nruvfde8vPz6dSpE7NmzeK3337jiiuu4Morr+SNN96gZs2agPOU3A8//EBmZiatW7fm/vvvL9Af6Gzq1KnDunXr6NixI6NGjaJp06a0atWK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P7/gHf2ff/554uLiWLduHVdccQU9evRg2bJlZ13+Rx99RLt27ahZs+Zp06ZMmcIjjzzCn3/+yaFDh844//z584mLi2Pu3LmkpaXRpUuXsx4l69KlC8ePH+fXX38t/A+gCBR8yhn/qp0BqNR4G4l79llcjYiUV6Zpkp+eb8lgXsAtO2rVqsWkSZMKjGvWrBljxoxxfTYMg48++ojbb78dHx8f6taty08//QT8c+QiMTGR/v37YxgG06dPP+1UV//+/bn88svJznY+LDonJ4fmzZtz3333udbz448/0qJFC7y8vIiKimLs2LHk5eW5pu/cuZNrrrkGLy8vGjVqxLx58867fUOGDMHNzY01a9Zwzz330LBhQ6KioujWrRu//PILt956a4H2/v7+hIWFUa9ePSZPnoy3tzc///zzWZc/Y8aM05YBkJaWxldffcXgwYPp2rUr06dPP+P8VapUISwsjFatWvHaa69x+PBhVq5ceca2drudm2++mRkzZpx3uy+GW4kuXS65bo8+xJ8/vYA9OJFZ70+gz3jdyVlEip8jw8Fiv8WWrPvqtKux+9qLdZljx47l1VdfZcKECbz99tv06tWLvXv3EhkZSVxcHPXr1+f555+nR48eBAYGnvbl/dZbb9G0aVOeeuop3njjDZ5++mmSk5N55513AFi8eDH33Xcfb731FldffTUxMTGufjPPPfccDoeDO+64g9DQUFauXElKSkqB00BnkpiY6DrS4+vre8Y25zoi5ebmhru7Ozk5OWecnpSUxJYtW2jVqtVp077++msaNGhA/fr16d27N8OGDWPUqFHnXJ+3tzfAWdcH0Lp1a8aPH3/W6cVBR3zKGXdPNw5vcj601N17rcXViIiUDX379qVnz55ER0fz8ssvk5aWxqpVq7Db7a5TWoGBgYSFhbm+wE/l5+fHZ599xuTJkxk9ejSTJk3i008/JSAgAHAGq6eeeoo+ffoQFRXFjTfeyAsvvMAHHzjvuzZ//ny2bdvGJ598QtOmTbnmmmt4+eWXz1nzrl27ME2T+vXrFxhftWpV/Pz88PPzY+TIkWecNycnh3HjxpGSkkKHDh3O2Gbfvn2YpklERMRp06ZMmULv3r0B6Ny5MykpKfzxxx9nrTU5OZkXXngBPz8/WrdufdZ2ERER7N+/v0T7+eiITzmUldUaWExo423kZWfj5ulpdUki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" + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Epoch 1999: 0.9534459114074707\tTotal: 1922.968999862671\n" + ] + } + ], + "source": [ + "steps_quantum = 2000\n", + "\n", + "# initial parameters\n", + "theta = K.implicit_randn(shape=(2 * l * N,), stddev=0.1)\n", + "theta_fgp = K.copy(theta)\n", + "theta_api = K.copy(theta)\n", + "theta_fgp_api = K.copy(theta)\n", + "psi_exact = wfn_quantum(theta)[:, None]\n", + "\n", + "losses, losses_fgp, losses_exact = [], [], []\n", + "losses_api, losses_fgp_api = [], []\n", + "\n", + "eps = 1e-6\n", + "t0 = ts = time.time()\n", + "\n", + "for i in range(steps_quantum):\n", + " psi = wfn_quantum(theta)\n", + " loss = K.real(K.conj(psi)[None, :] @ K.sparse_dense_matmul(h, psi[:, None]))[0, 0]\n", + " delta = d_theta(theta, psi, eps)\n", + " theta = update(theta, delta, tau_q)\n", + " losses.append(loss)\n", + "\n", + " psi_fgp = wfn_quantum(theta_fgp)\n", + " loss_fgp = K.real(\n", + " K.conj(psi_fgp)[None, :] @ K.sparse_dense_matmul(h, psi_fgp[:, None])\n", + " )[0, 0]\n", + " delta_fgp = d_theta(theta_fgp, psi_fgp, eps, fixed_global_phase=True)\n", + " theta_fgp = update(theta_fgp, delta_fgp, tau_q)\n", + " losses_fgp.append(loss_fgp)\n", + "\n", + " psi_api = wfn_quantum(theta_api)\n", + " loss_api = K.real(\n", + " K.conj(psi_api)[None, :] @ K.sparse_dense_matmul(h, psi_api[:, None])\n", + " )[0, 0]\n", + " delta_api = d_theta_api(theta_api, eps)\n", + " theta_api = update(theta_api, delta_api, tau_q)\n", + " losses_api.append(loss_api)\n", + "\n", + " psi_fgp_api = wfn_quantum(theta_fgp_api)\n", + " loss_fgp_api = K.real(\n", + " K.conj(psi_fgp_api)[None, :] @ K.sparse_dense_matmul(h, psi_fgp_api[:, None])\n", + " )[0, 0]\n", + " delta_fgp_api = d_theta_api(theta_fgp_api, eps, fixed_global_phase=True)\n", + " theta_fgp_api = update(theta_fgp_api, delta_fgp_api, tau_q)\n", + " losses_fgp_api.append(loss_fgp_api)\n", + "\n", + " loss_exact = K.real(\n", + " K.transpose(K.conj(psi_exact)) @ K.sparse_dense_matmul(h, psi_exact)\n", + " )[0, 0]\n", + " psi_exact = exp_tauH @ psi_exact\n", + " psi_exact /= K.norm(psi_exact)\n", + " losses_exact.append(K.numpy(loss_exact))\n", + "\n", + " eps *= 0.999\n", + "\n", + " # visualise the progress\n", + " clear_output(wait=True)\n", + " plt.xlabel(\"Iteration\")\n", + " plt.ylabel(\"Energy\")\n", + " plt.plot(range(i + 1), losses_exact, c=\"r\", label=\"exact\")\n", + " plt.plot(range(i + 1), losses, c=\"b\", label=\"unfixed GP\")\n", + " plt.plot(range(i + 1), losses_fgp, c=\"g\", label=\"fixed GP\")\n", + " plt.plot(range(i + 1), losses_api, c=\"m\", label=\"unfixed GP (API)\")\n", + " plt.plot(range(i + 1), losses_fgp_api, c=\"y\", label=\"fixed GP (API)\")\n", + " plt.legend()\n", + " plt.show()\n", + "\n", + " te = time.time()\n", + " print(f\"Epoch {i}: {te - ts}\\tTotal: {te - t0}\")\n", + " ts = te" + ], + "metadata": { + "collapsed": false + } + }, + { + "cell_type": "markdown", + "source": [ + "### Results" + ], + "metadata": { + "collapsed": false + } + }, + { + "cell_type": "markdown", + "source": [ + "We first show the overlap between the final states obtained by different methods. Similar to the classical example, all final states obtained by approximate imaginary-time evolution are close to the exact final state. However, the final states obtained by the same method but with fixed and unfixed global phases are closer and have only one global phase difference." + ], + "metadata": { + "collapsed": false + } + }, + { + "cell_type": "code", + "execution_count": 17, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "overlap between psi_fgp and psi\n", + "|overlap|: 0.9946308905819007\n", + "overlap: (-0.740140807928335-0.6644412637238422j)\n", + "\n", + "overlap between psi_fgp_api and psi_api\n", + "|overlap|: 0.9989073073947322\n", + "overlap: (0.027072660263567746+0.9985403746633621j)\n", + "\n", + "overlap between psi_api and psi\n", + "|overlap|: 0.9986726114097008\n", + "overlap: (-0.7188677460240553+0.6932359976993155j)\n", + "\n", + "overlap between psi_fgp_api and psi_fgp\n", + "|overlap|: 0.9997543606572588\n", + "overlap: (0.9987177429478946+0.04551539930910164j)\n", + "\n", + "overlap between psi_exact and psi\n", + "|overlap|: 0.8722054344579724\n", + "overlap: (-0.6784167782997877-0.5481724134059986j)\n", + "\n", + "overlap between psi_exact and psi_fgp\n", + "|overlap|: 0.8783189735765569\n", + "overlap: (0.8783163699487184-0.002138603442025992j)\n", + "\n", + "overlap between psi_exact and psi_api\n", + "|overlap|: 0.8763977936851081\n", + "overlap: (0.08830490967227977+0.8719376902645599j)\n", + "\n", + "overlap between psi_exact and psi_fgp_api\n", + "|overlap|: 0.8776241084735666\n", + "overlap: (0.876306874146697-0.048065976503842395j)\n" + ] + } + ], + "source": [ + "psi = wfn_quantum(theta)\n", + "psi_fgp = wfn_quantum(theta_fgp)\n", + "psi_api = wfn_quantum(theta_api)\n", + "psi_fgp_api = wfn_quantum(theta_fgp_api)\n", + "\n", + "overlap = K.tensordot(K.conj(psi_fgp), psi, 1)\n", + "print(\n", + " f\"overlap between psi_fgp and psi\\n|overlap|: {K.abs(overlap)}\\noverlap: {overlap}\\n\"\n", + ")\n", + "overlap = K.tensordot(K.conj(psi_fgp_api), psi_api, 1)\n", + "print(\n", + " f\"overlap between psi_fgp_api and psi_api\\n|overlap|: {K.abs(overlap)}\\noverlap: {overlap}\\n\"\n", + ")\n", + "overlap = K.tensordot(K.conj(psi_api), psi, 1)\n", + "print(\n", + " f\"overlap between psi_api and psi\\n|overlap|: {K.abs(overlap)}\\noverlap: {overlap}\\n\"\n", + ")\n", + "overlap = K.tensordot(K.conj(psi_fgp_api), psi_fgp, 1)\n", + "print(\n", + " f\"overlap between psi_fgp_api and psi_fgp\\n|overlap|: {K.abs(overlap)}\\noverlap: {overlap}\\n\"\n", + ")\n", + "overlap = K.tensordot(K.conj(psi_exact[:, 0]), psi, 1)\n", + "print(\n", + " f\"overlap between psi_exact and psi\\n|overlap|: {K.abs(overlap)}\\noverlap: {overlap}\\n\"\n", + ")\n", + "overlap = K.tensordot(K.conj(psi_exact[:, 0]), psi_fgp, 1)\n", + "print(\n", + " f\"overlap between psi_exact and psi_fgp\\n|overlap|: {K.abs(overlap)}\\noverlap: {overlap}\\n\"\n", + ")\n", + "overlap = K.tensordot(K.conj(psi_exact[:, 0]), psi_api, 1)\n", + "print(\n", + " f\"overlap between psi_exact and psi_api\\n|overlap|: {K.abs(overlap)}\\noverlap: {overlap}\\n\"\n", + ")\n", + "overlap = K.tensordot(K.conj(psi_exact[:, 0]), psi_fgp_api, 1)\n", + "print(\n", + " f\"overlap between psi_exact and psi_fgp_api\\n|overlap|: {K.abs(overlap)}\\noverlap: {overlap}\"\n", + ")" + ], + "metadata": { + "collapsed": false, + "ExecuteTime": { + "end_time": "2023-07-13T07:30:17.637583800Z", + "start_time": "2023-07-13T07:30:17.598679600Z" + } + } + }, + { + "cell_type": "markdown", + "source": [ + "Then we compare the approximate ground state energy with the exact result. The differences are very small, but the result obtained by exact imaginary-time evolution is indeed the closest to the real ground state energy." + ], + "metadata": { + "collapsed": false + } + }, + { + "cell_type": "code", + "execution_count": 18, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "exact ground state energy: -12.669360644773814\n", + "\n", + "ground state energy by exact IME: -12.635107323414964\n", + "ground state energy: -12.455023327986686\t-12.457418138786805 (fgp)\n", + "ground state energy (API): -12.454034180766776\t-12.456395342061295 (fgp)\n" + ] + } + ], + "source": [ + "E = K.eigvalsh(H)[0]\n", + "print(f\"exact ground state energy: {E}\\n\")\n", + "loss_exact = K.real(\n", + " K.transpose(K.conj(psi_exact)) @ K.sparse_dense_matmul(h, psi_exact)\n", + ")[0, 0]\n", + "print(f\"ground state energy by exact IME: {K.numpy(loss_exact)}\")\n", + "loss = K.real(K.conj(psi)[None, :] @ K.sparse_dense_matmul(h, psi[:, None]))[0, 0]\n", + "loss_fgp = K.real(\n", + " K.conj(psi_fgp)[None, :] @ K.sparse_dense_matmul(h, psi_fgp[:, None])\n", + ")[0, 0]\n", + "print(f\"ground state energy: {K.numpy(loss)}\\t{K.numpy(loss_fgp)} (fgp)\")\n", + "loss_api = K.real(\n", + " K.conj(psi_api)[None, :] @ K.sparse_dense_matmul(h, psi_api[:, None])\n", + ")[0, 0]\n", + "loss_fgp_api = K.real(\n", + " K.conj(psi_fgp_api)[None, :] @ K.sparse_dense_matmul(h, psi_fgp_api[:, None])\n", + ")[0, 0]\n", + "print(f\"ground state energy (API): {K.numpy(loss_api)}\\t{K.numpy(loss_fgp_api)} (fgp)\")" + ], + "metadata": { + "collapsed": false, + "ExecuteTime": { + "end_time": "2023-07-13T07:30:28.049137Z", + "start_time": "2023-07-13T07:30:27.749665300Z" + } + } + }, + { + "cell_type": "code", + "execution_count": 12, + "id": "89ed16e3", + "metadata": { + "collapsed": false, + "jupyter": { + "outputs_hidden": false + }, + "ExecuteTime": { + "end_time": "2023-07-13T06:54:06.262972100Z", + "start_time": "2023-07-13T06:54:06.180740100Z" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "OS info: Linux-5.4.119-1-tlinux4-0010.2-x86_64-with-glibc2.28\n", + "Python version: 3.10.11\n", + "Numpy version: 1.23.5\n", + "Scipy version: 1.11.0\n", + "Pandas version: 2.0.2\n", + "TensorNetwork version: 0.4.6\n", + "Cotengra version: 0.2.1.dev15+g120379e\n", + "TensorFlow version: 2.12.0\n", + "TensorFlow GPU: []\n", + "TensorFlow CUDA infos: {'cpu_compiler': '/dt9/usr/bin/gcc', 'cuda_compute_capabilities': ['sm_35', 'sm_50', 'sm_60', 'sm_70', 'sm_75', 'compute_80'], 'cuda_version': '11.8', 'cudnn_version': '8', 'is_cuda_build': True, 'is_rocm_build': False, 'is_tensorrt_build': True}\n", + "Jax version: 0.4.13\n", + "Jax installation doesn't support GPU\n", + "JaxLib version: 0.4.13\n", + "PyTorch version: 2.0.1\n", + "PyTorch GPU support: False\n", + "PyTorch GPUs: []\n", + "Cupy is not installed\n", + "Qiskit version: 0.24.1\n", + "Cirq version: 1.1.0\n", + "TensorCircuit version 0.10.0\n" + ] + } + ], + "source": [ + "tc.about()" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "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.10.9" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/docs/source/tutorials/mera.ipynb b/docs/source/tutorials/mera.ipynb new file mode 100644 index 00000000..b4e144cf --- /dev/null +++ b/docs/source/tutorials/mera.ipynb @@ -0,0 +1,313 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "source": [ + "# MERA" + ], + "metadata": {} + }, + { + "cell_type": "markdown", + "source": [ + "## Overview\n", + "\n", + "In this tutorial, we'll show you how to implement MERA (multi-scale entangled renormalization ansatz) with TensorCircuit, but not in physics perspective." + ], + "metadata": {} + }, + { + "cell_type": "markdown", + "source": [ + "## Background\n", + "\n", + "MERA is a kind of VQE starts from only one qubit in the $\\ket{0}$ state, and progressively enlarges the Hilbert space by tensoring on new qubits.\n", + "In the MERA we are going to train (denoted by $U(\\theta)$), we use parameterized quantum gates $e^{i\\theta XX}$, $e^{i\\theta ZZ}$ as two-qubit gates and $e^{i\\theta X}$, $e^{i\\theta Z}$ as single-qubit gates.\n", + "The Hamiltonian we choose as the example is from TFIM as $\\hat{H}_{Ising}=J\\sum_{i}{Z_{i}Z_{i+1}}-B_{x}\\sum_{i}{X_{i}}$. \n", + "And the loss function to be minimized in this task is $\\mathcal{L}_{MERA}(\\rm{\\theta})=\\langle 0^n\\vert U(\\theta)^\\dagger \\hat{H} U(\\theta)\\vert 0^n\\rangle$. " + ], + "metadata": {} + }, + { + "cell_type": "markdown", + "source": [ + "## Setup" + ], + "metadata": {} + }, + { + "cell_type": "code", + "execution_count": 1, + "source": [ + "import numpy as np\n", + "import tensorflow as tf\n", + "import tensorcircuit as tc\n", + "\n", + "tc.set_backend(\"tensorflow\")\n", + "tc.set_dtype(\"complex128\")" + ], + "outputs": [ + { + "output_type": "execute_result", + "data": { + "text/plain": [ + "('complex128', 'float64')" + ] + }, + "metadata": {}, + "execution_count": 1 + } + ], + "metadata": { + "scrolled": false + } + }, + { + "cell_type": "markdown", + "source": [ + "## Energy\n", + "We first design the Hamiltonian energy expectation function as loss.\n", + "$$ \\hat{H}_{Ising}=J\\sum_{i}{Z_{i}Z_{i+1}}-B_{x}\\sum_{i}{X_{i}} $$" + ], + "metadata": {} + }, + { + "cell_type": "code", + "execution_count": 2, + "source": [ + "def energy(c: tc.Circuit, j: float = 1.0, hx: float = 1.0):\n", + " e = 0.0\n", + " n = c._nqubits\n", + " # \n", + " for i in range(n - 1):\n", + " e += j * c.expectation((tc.gates.z(), [i]), (tc.gates.z(), [i + 1]))\n", + " # \n", + " for i in range(n):\n", + " e -= hx * c.expectation((tc.gates.x(), [i]))\n", + " return tc.backend.real(e)" + ], + "outputs": [], + "metadata": {} + }, + { + "cell_type": "markdown", + "source": [ + "## MERA circuit\n", + "\n", + "Now we design the circuit. We use $\\theta$ as input." + ], + "metadata": {} + }, + { + "cell_type": "code", + "execution_count": 5, + "source": [ + "def MERA(params, n):\n", + " params = tc.backend.cast(params, \"complex128\")\n", + " c = tc.Circuit(n)\n", + "\n", + " idx = 0 # index of params\n", + "\n", + " for i in range(n):\n", + " c.rx(i, theta=params[2 * i])\n", + " c.rz(i, theta=params[2 * i + 1])\n", + " idx += 2 * n\n", + "\n", + " for n_layer in range(1, int(np.log2(n)) + 1):\n", + " n_qubit = 2**n_layer # number of qubits involving\n", + " step = int(n / n_qubit)\n", + "\n", + " # even\n", + " for i in range(step, n - step, 2 * step):\n", + " c.exp1(i, i + step, theta=params[idx], unitary=tc.gates._xx_matrix)\n", + " c.exp1(i, i + step, theta=params[idx + 1], unitary=tc.gates._zz_matrix)\n", + " idx += 2\n", + "\n", + " # odd\n", + " for i in range(0, n, 2 * step):\n", + " c.exp1(i, i + step, theta=params[idx], unitary=tc.gates._xx_matrix)\n", + " c.exp1(i, i + step, theta=params[idx + 1], unitary=tc.gates._zz_matrix)\n", + " idx += 2\n", + "\n", + " # single qubit\n", + " for i in range(0, n, step):\n", + " c.rx(i, theta=params[idx])\n", + " c.rz(i, theta=params[idx + 1])\n", + " idx += 2\n", + "\n", + " # measure\n", + " e = energy(c)\n", + " return e\n", + " # return c, idx" + ], + "outputs": [], + "metadata": {} + }, + { + "cell_type": "markdown", + "source": [ + "We can visualize the MERA circuit. \n", + "\n", + "Hint: Please change return to `return c, idx`, which will only be used here. After visulization, don't forget to restore the return and run the code block above again." + ], + "metadata": {} + }, + { + "cell_type": "code", + "execution_count": 4, + "source": [ + "n = 8\n", + "cirq, idx = MERA(np.zeros(1000), n)\n", + "print(\"The number of parameters is\", idx)\n", + "cirq.draw()" + ], + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": [ + "The number of parameters is 66\n" + ] + }, + { + "output_type": "execute_result", + "data": { + "text/plain": [ + "
" + ], + "image/svg+xml": "\n\n\n \n \n \n \n 2022-12-05T21:29:46.404387\n image/svg+xml\n \n \n Matplotlib v3.5.3, https://matplotlib.org/\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 \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 \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 \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 \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 \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 \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 \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 \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 \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 \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 \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 \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 \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 \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 \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 \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 \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 \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 \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 \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 \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 \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 \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 \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 \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 \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 \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 \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 \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 \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 \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 \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 \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 \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 \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 \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 \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 \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 \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 \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 \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 \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 \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 \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 \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 \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 \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 \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 \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" + }, + "metadata": {}, + "execution_count": 4 + } + ], + "metadata": { + "scrolled": true + } + }, + { + "cell_type": "markdown", + "source": [ + "## Train\n", + "\n", + "Now we can train the MERA circuit with tensorflow." + ], + "metadata": {} + }, + { + "cell_type": "code", + "execution_count": 6, + "source": [ + "MERA_tfim_vvag = tc.backend.jit(tc.backend.vectorized_value_and_grad(MERA))\n", + "\n", + "\n", + "def batched_train(n, batch=10, maxiter=10000, lr=0.005):\n", + " params = tf.Variable(\n", + " initial_value=tf.random.normal(\n", + " shape=[batch, idx], stddev=1, dtype=getattr(tf, tc.rdtypestr)\n", + " )\n", + " )\n", + " opt = tf.keras.optimizers.Adam(lr)\n", + " lowest_energy = 1e5\n", + " for i in range(maxiter):\n", + " e, grad = MERA_tfim_vvag(params, n)\n", + " opt.apply_gradients([(grad, params)])\n", + " if tf.reduce_min(e) < lowest_energy:\n", + " lowest_energy = tf.reduce_min(e)\n", + " if i % 200 == 0:\n", + " print(e)\n", + " return lowest_energy\n", + "\n", + "\n", + "n = 8\n", + "lowest_energy = batched_train(n, batch=5, maxiter=2000, lr=0.007)" + ], + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": [ + "tf.Tensor([-0.6449017 0.14083987 0.17227418 1.42731099 0.93767164], shape=(5,), dtype=float64)\n", + "tf.Tensor([-9.57952648 -9.15354269 -9.53415983 -9.55291257 -9.46880555], shape=(5,), dtype=float64)\n", + "tf.Tensor([-9.63166728 -9.60922826 -9.59883555 -9.66639936 -9.60174669], shape=(5,), dtype=float64)\n", + "tf.Tensor([-9.65441326 -9.61830383 -9.6219077 -9.68289435 -9.61427165], shape=(5,), dtype=float64)\n", + "tf.Tensor([-9.66991104 -9.6307931 -9.64993901 -9.71396225 -9.63848947], shape=(5,), dtype=float64)\n", + "tf.Tensor([-9.67960751 -9.64303661 -9.67696885 -9.76317346 -9.6507455 ], shape=(5,), dtype=float64)\n", + "tf.Tensor([-9.68303361 -9.6575349 -9.70118521 -9.7740601 -9.65751254], shape=(5,), dtype=float64)\n", + "tf.Tensor([-9.68481667 -9.67473162 -9.71392119 -9.78200161 -9.66880068], shape=(5,), dtype=float64)\n", + "tf.Tensor([-9.6864865 -9.67835678 -9.73033137 -9.79128949 -9.68317883], shape=(5,), dtype=float64)\n", + "tf.Tensor([-9.68762425 -9.67928153 -9.77502182 -9.79465957 -9.69252806], shape=(5,), dtype=float64)\n" + ] + } + ], + "metadata": { + "scrolled": false + } + }, + { + "cell_type": "markdown", + "source": [ + "## Compare\n", + "\n", + "We can compare the ground energy we get by MERA with DMRG." + ], + "metadata": {} + }, + { + "cell_type": "code", + "execution_count": 7, + "source": [ + "# DMRG\n", + "import quimb\n", + "\n", + "h = quimb.tensor.tensor_gen.MPO_ham_ising(n, j=4.0, bx=2.0, S=0.5, cyclic=False)\n", + "dmrg = quimb.tensor.tensor_dmrg.DMRG(\n", + " h, bond_dims=[10, 20, 100, 100, 200], cutoffs=1e-13\n", + ")\n", + "dmrg.solve(tol=1e-9, verbosity=0)\n", + "energy_DMRG = dmrg.energy\n", + "\n", + "# Compare\n", + "print(\"DMRG solution: \", energy_DMRG)\n", + "print(\"MERA solution: \", lowest_energy.numpy())" + ], + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": [ + "DMRG solution: -9.837951447459426\n", + "MERA solution: -9.795198473308487\n" + ] + } + ], + "metadata": {} + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3.9.12 64-bit", + "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.9.12" + }, + "vscode": { + "interpreter": { + "hash": "18d2a9923f839b0d86cf68fd09770e726264cf9d62311eaf57b1fff0ca4bed8e" + } + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/docs/source/tutorials/mera_cn.ipynb b/docs/source/tutorials/mera_cn.ipynb new file mode 100644 index 00000000..b804e868 --- /dev/null +++ b/docs/source/tutorials/mera_cn.ipynb @@ -0,0 +1,313 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "source": [ + "# MERA" + ], + "metadata": {} + }, + { + "cell_type": "markdown", + "source": [ + "## 概述\n", + "\n", + "在本教程中,我们将不涉及物理层面的探讨,而将演示如何使用TensorCircuit实现MERA (多尺度纠缠重整化假设,multi-scale entangled renormalization ansatz)。" + ], + "metadata": {} + }, + { + "cell_type": "markdown", + "source": [ + "## 背景\n", + "\n", + "MERA是VQE的其中一种。它由一个量子比特的 $\\ket{0}$ 态开始,逐层添加新的量子比特以扩张希尔伯特空间。\n", + "在将要训练的MERA(记作 $U(\\theta)$ )中,我们使用可变参量子门 $e^{i\\theta XX}$ 、$e^{i\\theta ZZ}$ 作为双比特门,以及 $e^{i\\theta X}$ 、$e^{i\\theta Z}$ 作为单比特门。\n", + "在本教程中,我们使用的哈密顿量是横场伊辛模型的哈密顿量 $\\hat{H}_{Ising}=J\\sum_{i}{Z_{i}Z_{i+1}}-B_{x}\\sum_{i}{X_{i}}$。\n", + "我们要减小的损失函数是 $\\mathcal{L}_{MERA}(\\rm{\\theta})=\\langle 0^n\\vert U(\\theta)^\\dagger \\hat{H} U(\\theta)\\vert 0^n\\rangle$。" + ], + "metadata": {} + }, + { + "cell_type": "markdown", + "source": [ + "## 设置" + ], + "metadata": {} + }, + { + "cell_type": "code", + "execution_count": 1, + "source": [ + "import numpy as np\n", + "import tensorflow as tf\n", + "import tensorcircuit as tc\n", + "\n", + "tc.set_backend(\"tensorflow\")\n", + "tc.set_dtype(\"complex128\")" + ], + "outputs": [ + { + "output_type": "execute_result", + "data": { + "text/plain": [ + "('complex128', 'float64')" + ] + }, + "metadata": {}, + "execution_count": 1 + } + ], + "metadata": { + "scrolled": false + } + }, + { + "cell_type": "markdown", + "source": [ + "## 能量\n", + "我们先设计哈密顿量的能量期望函数作为损失函数。\n", + "$$ \\hat{H}_{Ising}=J\\sum_{i}{Z_{i}Z_{i+1}}-B_{x}\\sum_{i}{X_{i}} $$" + ], + "metadata": {} + }, + { + "cell_type": "code", + "execution_count": 2, + "source": [ + "def energy(c: tc.Circuit, j: float = 1.0, hx: float = 1.0):\n", + " e = 0.0\n", + " n = c._nqubits\n", + " # \n", + " for i in range(n - 1):\n", + " e += j * c.expectation((tc.gates.z(), [i]), (tc.gates.z(), [i + 1]))\n", + " # \n", + " for i in range(n):\n", + " e -= hx * c.expectation((tc.gates.x(), [i]))\n", + " return tc.backend.real(e)" + ], + "outputs": [], + "metadata": {} + }, + { + "cell_type": "markdown", + "source": [ + "## MERA 电路\n", + "\n", + "现在,我们设计电路。我们用 $\\theta$ 作为输入。" + ], + "metadata": {} + }, + { + "cell_type": "code", + "execution_count": 5, + "source": [ + "def MERA(params, n):\n", + " params = tc.backend.cast(params, \"complex128\")\n", + " c = tc.Circuit(n)\n", + "\n", + " idx = 0 # index of params\n", + "\n", + " for i in range(n):\n", + " c.rx(i, theta=params[2 * i])\n", + " c.rz(i, theta=params[2 * i + 1])\n", + " idx += 2 * n\n", + "\n", + " for n_layer in range(1, int(np.log2(n)) + 1):\n", + " n_qubit = 2**n_layer # number of qubits involving\n", + " step = int(n / n_qubit)\n", + "\n", + " # 偶数层\n", + " for i in range(step, n - step, 2 * step):\n", + " c.exp1(i, i + step, theta=params[idx], unitary=tc.gates._xx_matrix)\n", + " c.exp1(i, i + step, theta=params[idx + 1], unitary=tc.gates._zz_matrix)\n", + " idx += 2\n", + "\n", + " # 奇数层\n", + " for i in range(0, n, 2 * step):\n", + " c.exp1(i, i + step, theta=params[idx], unitary=tc.gates._xx_matrix)\n", + " c.exp1(i, i + step, theta=params[idx + 1], unitary=tc.gates._zz_matrix)\n", + " idx += 2\n", + "\n", + " # 单比特门\n", + " for i in range(0, n, step):\n", + " c.rx(i, theta=params[idx])\n", + " c.rz(i, theta=params[idx + 1])\n", + " idx += 2\n", + "\n", + " # 测量\n", + " e = energy(c)\n", + " return e\n", + " # return c, idx" + ], + "outputs": [], + "metadata": {} + }, + { + "cell_type": "markdown", + "source": [ + "我们可以将MERA电路可视化。 \n", + "\n", + "注意:请把return改为`return c, idx`。这个return只有在这儿会被用到。可视化完成后,请别忘了将return还原并重新运行上方代码块。" + ], + "metadata": {} + }, + { + "cell_type": "code", + "execution_count": 4, + "source": [ + "n = 8\n", + "cirq, idx = MERA(np.zeros(1000), n)\n", + "print(\"The number of parameters is\", idx)\n", + "cirq.draw()" + ], + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": [ + "The number of parameters is 66\n" + ] + }, + { + "output_type": "execute_result", + "data": { + "text/plain": [ + "
" + ], + "image/svg+xml": "\n\n\n \n \n \n \n 2022-12-05T21:29:46.404387\n image/svg+xml\n \n \n Matplotlib v3.5.3, https://matplotlib.org/\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 \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 \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 \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 \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 \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 \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 \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 \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 \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 \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 \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 \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 \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 \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 \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 \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 \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 \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 \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 \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 \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 \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 \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 \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 \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 \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 \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 \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 \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 \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 \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 \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 \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 \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 \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 \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 \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 \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 \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 \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 \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 \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 \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 \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 \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 \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 \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 \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 \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" + }, + "metadata": {}, + "execution_count": 4 + } + ], + "metadata": { + "scrolled": true + } + }, + { + "cell_type": "markdown", + "source": [ + "## 训练\n", + "\n", + "现在,我们使用tensorflow训练MERA电路。" + ], + "metadata": {} + }, + { + "cell_type": "code", + "execution_count": 6, + "source": [ + "MERA_tfim_vvag = tc.backend.jit(tc.backend.vectorized_value_and_grad(MERA))\n", + "\n", + "\n", + "def batched_train(n, batch=10, maxiter=10000, lr=0.005):\n", + " params = tf.Variable(\n", + " initial_value=tf.random.normal(\n", + " shape=[batch, idx], stddev=1, dtype=getattr(tf, tc.rdtypestr)\n", + " )\n", + " )\n", + " opt = tf.keras.optimizers.Adam(lr)\n", + " lowest_energy = 1e5\n", + " for i in range(maxiter):\n", + " e, grad = MERA_tfim_vvag(params, n)\n", + " opt.apply_gradients([(grad, params)])\n", + " if tf.reduce_min(e) < lowest_energy:\n", + " lowest_energy = tf.reduce_min(e)\n", + " if i % 200 == 0:\n", + " print(e)\n", + " return lowest_energy\n", + "\n", + "\n", + "n = 8\n", + "lowest_energy = batched_train(n, batch=5, maxiter=2000, lr=0.007)" + ], + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": [ + "tf.Tensor([-0.6449017 0.14083987 0.17227418 1.42731099 0.93767164], shape=(5,), dtype=float64)\n", + "tf.Tensor([-9.57952648 -9.15354269 -9.53415983 -9.55291257 -9.46880555], shape=(5,), dtype=float64)\n", + "tf.Tensor([-9.63166728 -9.60922826 -9.59883555 -9.66639936 -9.60174669], shape=(5,), dtype=float64)\n", + "tf.Tensor([-9.65441326 -9.61830383 -9.6219077 -9.68289435 -9.61427165], shape=(5,), dtype=float64)\n", + "tf.Tensor([-9.66991104 -9.6307931 -9.64993901 -9.71396225 -9.63848947], shape=(5,), dtype=float64)\n", + "tf.Tensor([-9.67960751 -9.64303661 -9.67696885 -9.76317346 -9.6507455 ], shape=(5,), dtype=float64)\n", + "tf.Tensor([-9.68303361 -9.6575349 -9.70118521 -9.7740601 -9.65751254], shape=(5,), dtype=float64)\n", + "tf.Tensor([-9.68481667 -9.67473162 -9.71392119 -9.78200161 -9.66880068], shape=(5,), dtype=float64)\n", + "tf.Tensor([-9.6864865 -9.67835678 -9.73033137 -9.79128949 -9.68317883], shape=(5,), dtype=float64)\n", + "tf.Tensor([-9.68762425 -9.67928153 -9.77502182 -9.79465957 -9.69252806], shape=(5,), dtype=float64)\n" + ] + } + ], + "metadata": { + "scrolled": false + } + }, + { + "cell_type": "markdown", + "source": [ + "## 对比\n", + "\n", + "我们可以把我们用MERA得到的基态能量和DMRG进行对比。" + ], + "metadata": {} + }, + { + "cell_type": "code", + "execution_count": 7, + "source": [ + "# DMRG\n", + "import quimb\n", + "\n", + "h = quimb.tensor.tensor_gen.MPO_ham_ising(n, j=4.0, bx=2.0, S=0.5, cyclic=False)\n", + "dmrg = quimb.tensor.tensor_dmrg.DMRG(\n", + " h, bond_dims=[10, 20, 100, 100, 200], cutoffs=1e-13\n", + ")\n", + "dmrg.solve(tol=1e-9, verbosity=0)\n", + "energy_DMRG = dmrg.energy\n", + "\n", + "# Compare\n", + "print(\"DMRG solution: \", energy_DMRG)\n", + "print(\"MERA solution: \", lowest_energy.numpy())" + ], + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": [ + "DMRG solution: -9.837951447459426\n", + "MERA solution: -9.795198473308487\n" + ] + } + ], + "metadata": {} + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3.9.12 64-bit", + "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.9.12" + }, + "vscode": { + "interpreter": { + "hash": "18d2a9923f839b0d86cf68fd09770e726264cf9d62311eaf57b1fff0ca4bed8e" + } + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/docs/source/tutorials/nnvqe.ipynb b/docs/source/tutorials/nnvqe.ipynb new file mode 100644 index 00000000..ba48f3f3 --- /dev/null +++ b/docs/source/tutorials/nnvqe.ipynb @@ -0,0 +1,4015 @@ +{ + "cells": [ + { + "attachments": {}, + "cell_type": "markdown", + "id": "64ba95d6", + "metadata": {}, + "source": [ + "#
Neural Network encoded Variational Quantum Eigensolver (NN-VQE)" + ] + }, + { + "cell_type": "markdown", + "id": "b65f64bf", + "metadata": {}, + "source": [ + "## Overview\n", + "\n", + "In this tutorial, we'll show you a general framework called neural network encoded variational quantum algorithms (NN-VQAs) with TensorCircuit. NN-VQA feeds input (such as parameters of a Hamiltonian) from a given problem to a neural network and uses its outputs to parameterize an ansatz circuit for the standard VQA. Here, we take NN-variational quantum eigensolver (VQE) to illustrate concretely." + ] + }, + { + "cell_type": "markdown", + "id": "831930ae", + "metadata": {}, + "source": [ + "## Setup" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "id": "4e1651b9", + "metadata": { + "scrolled": false + }, + "outputs": [], + "source": [ + "import numpy as np\n", + "import matplotlib.pyplot as plt\n", + "import tensorflow as tf\n", + "import tensorcircuit as tc\n", + "import cotengra\n", + "import quimb\n", + "from tqdm.notebook import tqdm\n", + "from functools import partial\n", + "\n", + "optc = cotengra.ReusableHyperOptimizer(\n", + " methods=[\"greedy\"],\n", + " parallel=\"ray\",\n", + " minimize=\"combo\",\n", + " max_time=30,\n", + " max_repeats=1024,\n", + " progbar=True,\n", + ")\n", + "tc.set_contractor(\"custom\", optimizer=optc, preprocessing=True)\n", + "\n", + "K = tc.set_backend(\"tensorflow\")\n", + "tc.set_dtype(\"complex128\")" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "d78b480b", + "metadata": {}, + "source": [ + "## Energy\n", + "\n", + "The Hamiltonian used in this tutorial is a 1D XXZ model with periodic boundary conditions, with the transverse field strength $\\lambda$ and the anisotropy parameter $\\Delta$. The Hamiltonian energy expectation function is chosen as loss.\n", + "\n", + "$$ \\hat{H}_{XXZ}=\\sum_{i}{ \\left( X_{i}X_{i+1}+Y_{i}Y_{i+1}+\\Delta Z_{i}Z_{i+1} \\right) } + \\lambda \\sum_{i}{Z_{i}} $$" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "id": "fff67346", + "metadata": {}, + "outputs": [], + "source": [ + "def energy(c: tc.Circuit, lamb: float = 1.0, delta: float = 1.0):\n", + " e = 0.0\n", + " n = c._nqubits\n", + " for i in range(n):\n", + " e += lamb * c.expectation((tc.gates.z(), [i])) # \n", + " for i in range(n):\n", + " e += c.expectation(\n", + " (tc.gates.x(), [i]), (tc.gates.x(), [(i + 1) % n])\n", + " ) # \n", + " e += c.expectation(\n", + " (tc.gates.y(), [i]), (tc.gates.y(), [(i + 1) % n])\n", + " ) # \n", + " e += delta * c.expectation(\n", + " (tc.gates.z(), [i]), (tc.gates.z(), [(i + 1) % n])\n", + " ) # \n", + " return K.real(e)" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "0ad6a7a6", + "metadata": {}, + "source": [ + "## Ansatz circuit\n", + "\n", + "Now we design the circuit. We choose multi-scale entangled renormalization ansatz (MERA) as the ansatz here, $d$ is the circuit depth. (see [MERA tutorial](https://tensorcircuit.readthedocs.io/en/latest/tutorials/mera.html))" + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "id": "445b7c86", + "metadata": {}, + "outputs": [], + "source": [ + "def MERA(\n", + " inp, n, d=1, lamb=1.0, energy_flag=False\n", + "): # for single-parameter 1D XXZ model, we fix lamb\n", + " params = K.cast(inp[\"params\"], \"complex128\")\n", + " delta = K.cast(inp[\"delta\"], \"complex128\")\n", + " c = tc.Circuit(n)\n", + "\n", + " idx = 0\n", + "\n", + " for i in range(n):\n", + " c.rx(i, theta=params[3 * i])\n", + " c.rz(i, theta=params[3 * i + 1])\n", + " c.rx(i, theta=params[3 * i + 2])\n", + " idx += 3 * n\n", + "\n", + " for n_layer in range(1, int(np.log2(n)) + 1):\n", + " n_qubit = 2**n_layer # number of qubits involving\n", + " step = int(n / n_qubit)\n", + "\n", + " for _ in range(d): # circuit depth\n", + " # even\n", + " for i in range(step, n - step, 2 * step):\n", + " c.rxx(i, i + step, theta=params[idx])\n", + " c.rzz(i, i + step, theta=params[idx + 1])\n", + " idx += 2\n", + "\n", + " # odd\n", + " for i in range(0, n, 2 * step):\n", + " c.rxx(i, i + step, theta=params[idx])\n", + " c.rzz(i, i + step, theta=params[idx + 1])\n", + " idx += 2\n", + "\n", + " # single qubit\n", + " for i in range(0, n, step):\n", + " c.rx(i, theta=params[idx])\n", + " c.rz(i, theta=params[idx + 1])\n", + " idx += 2\n", + "\n", + " if energy_flag:\n", + " return energy(c, lamb, delta) # return Hamiltonian energy expectation\n", + " else:\n", + " return c, idx # return the circuit & number of circuit parameters" + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "id": "6daa2f64", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "The number of parameters is 74\n" + ] + }, + { + "data": { + "image/svg+xml": [ + "\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " 2023-08-06T16:31:47.918946\n", + " image/svg+xml\n", + " \n", + " \n", + " Matplotlib v3.5.3, https://matplotlib.org/\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", + " \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", + " \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", + " \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", + " \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", + " \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", + " \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", + " \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", + " \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", + " \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", + " \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", + " \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", + " \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", + " \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", + " \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", + " \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", + " \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", + " \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", + " \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", + " \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", + " \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", + " \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", + " \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", + " \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", + " \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", + " \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", + " \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", + " \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", + " \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", + " \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", + " \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", + " \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", + " \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", + " \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", + " \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", + " \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", + " \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", + " \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", + " \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", + " \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", + " \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", + " \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", + " \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", + " \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", + " \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", + " \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", + " \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", + " \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", + " \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", + " \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", + " \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", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "\n" + ], + "text/plain": [ + "
" + ] + }, + "execution_count": 9, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# circuit visulization\n", + "n = 8\n", + "d = 1\n", + "cirq, idx = MERA({\"params\": np.zeros(3000), \"delta\": 0.0}, n, d, 1.0)\n", + "print(\"The number of parameters is\", idx)\n", + "cirq.draw()" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "bfe2fbee", + "metadata": {}, + "source": [ + "## NN-VQE\n", + "\n", + "Design the NN-VQE. We use a neural network to transform the Hamiltonian parameters to the optimized parameters in the parameterized quantum circuit (PQC) for VQE." + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "id": "1ac050a8", + "metadata": {}, + "outputs": [], + "source": [ + "def NN_MERA(n, d, lamb, NN_shape, stddev):\n", + " input = tf.keras.layers.Input(shape=[1]) # input layer\n", + "\n", + " x = tf.keras.layers.Dense(\n", + " units=NN_shape,\n", + " kernel_initializer=tf.keras.initializers.RandomNormal(stddev=stddev),\n", + " activation=\"ReLU\",\n", + " )(\n", + " input\n", + " ) # hidden layer\n", + "\n", + " x = tf.keras.layers.Dropout(0.05)(x) # dropout layer\n", + "\n", + " _, idx = MERA(\n", + " {\"params\": np.zeros(3000), \"delta\": 0.0}, n, d, 1.0, energy_flag=False\n", + " )\n", + " params = tf.keras.layers.Dense(\n", + " units=idx,\n", + " kernel_initializer=tf.keras.initializers.RandomNormal(stddev=stddev),\n", + " activation=\"sigmoid\",\n", + " )(\n", + " x\n", + " ) # output layer\n", + "\n", + " qlayer = tc.KerasLayer(partial(MERA, n=n, d=d, lamb=lamb, energy_flag=True)) # PQC\n", + "\n", + " output = qlayer({\"params\": 6.3 * params, \"delta\": input}) # NN-VQE output\n", + "\n", + " m = tf.keras.Model(inputs=input, outputs=output)\n", + "\n", + " return m" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "e9afb1a5", + "metadata": {}, + "source": [ + "## Train\n", + "\n", + "Now we can train the NN-VQE with TensorFlow." + ] + }, + { + "cell_type": "code", + "execution_count": 23, + "id": "873ebd5e", + "metadata": { + "scrolled": false + }, + "outputs": [], + "source": [ + "def train(n, d, lamb, delta, NN_shape, maxiter=10000, lr=0.005, stddev=1.0):\n", + " exp_lr = tf.keras.optimizers.schedules.ExponentialDecay(\n", + " initial_learning_rate=lr, decay_steps=1000, decay_rate=0.7\n", + " )\n", + " opt = tf.keras.optimizers.Adam(exp_lr) # optimizer\n", + "\n", + " m = NN_MERA(n, d, lamb, NN_shape, stddev)\n", + " for i in range(maxiter):\n", + " with tf.GradientTape() as tape:\n", + " e = tf.zeros([1], dtype=tf.float64)\n", + " for de in delta:\n", + " e += m(K.reshape(de, [1])) # sum up energies of all training points\n", + " grads = tape.gradient(e, m.variables)\n", + " opt.apply_gradients(zip(grads, m.variables))\n", + " if i % 500 == 0:\n", + " print(\"epoch\", i, \":\", e)\n", + "\n", + " m.save_weights(\"NN-VQE.weights.h5\") # save the trained model" + ] + }, + { + "cell_type": "code", + "execution_count": 24, + "id": "e8df3d67", + "metadata": { + "scrolled": false + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "epoch 0 : tf.Tensor([[117.53523392]], shape=(1, 1), dtype=float64)\n", + "epoch 500 : tf.Tensor([[-361.85937039]], shape=(1, 1), dtype=float64)\n", + "epoch 1000 : tf.Tensor([[-365.35288984]], shape=(1, 1), dtype=float64)\n", + "epoch 1500 : tf.Tensor([[-366.65891358]], shape=(1, 1), dtype=float64)\n", + "epoch 2000 : tf.Tensor([[-366.94258369]], shape=(1, 1), dtype=float64)\n" + ] + } + ], + "source": [ + "n = 8 # number of qubits\n", + "d = 2 # circuit depth\n", + "lamb = 0.75 # fixed\n", + "delta = np.linspace(-3.0, 3.0, 20, dtype=\"complex128\") # training set\n", + "NN_shape = 20 # node number of the hidden layer\n", + "maxiter = 2500 # maximum iteration for the optimization\n", + "lr = 0.009 # learning rate\n", + "stddev = 0.1 # the initial standard deviation of the NN\n", + "\n", + "with tf.device(\"/cpu:0\"):\n", + " train(n, d, lamb, delta, NN_shape=NN_shape, maxiter=maxiter, lr=lr, stddev=stddev)" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "c5a7cdb0", + "metadata": {}, + "source": [ + "## Test\n", + "\n", + "We use a test set beyond the training set to test the accuracy and generalization capability of NN-VQE." + ] + }, + { + "cell_type": "code", + "execution_count": 47, + "id": "f15f4f68", + "metadata": {}, + "outputs": [ + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "4a3ceb8bdf90463f88050b771fde6925", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "0it [00:00, ?it/s]" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "test_delta = np.linspace(-4.0, 4.0, 201) # test set\n", + "test_energies = tf.zeros_like(test_delta).numpy()\n", + "m = NN_MERA(n, d, lamb, NN_shape, stddev)\n", + "m.load_weights(\"NN-VQE.weights.h5\")\n", + "for i, de in tqdm(enumerate(test_delta)):\n", + " test_energies[i] = m(K.reshape(de, [1]))" + ] + }, + { + "cell_type": "markdown", + "id": "924027f8", + "metadata": {}, + "source": [ + "## Compare\n", + "\n", + "We compare the results of NN-VQE with the analytical ones to calculate the ground-state energy relative error. From the figure, we can see that NN-VQE is able to estimate the ground-state energies of parameterized Hamiltonians with high precision without fine-tuning and has a favorable generalization capability." + ] + }, + { + "cell_type": "code", + "execution_count": 48, + "id": "c8668a13", + "metadata": {}, + "outputs": [], + "source": [ + "analytical_energies = [] # analytical result\n", + "for i in test_delta:\n", + " h = quimb.tensor.tensor_builder.MPO_ham_XXZ(\n", + " n, i * 4, jxy=4.0, bz=2.0 * 0.75, S=0.5, cyclic=True\n", + " )\n", + " h = h.to_dense()\n", + " analytical_energies.append(np.min(quimb.eigvalsh(h)))" + ] + }, + { + "cell_type": "code", + "execution_count": 49, + "id": "42799e3e", + "metadata": {}, + "outputs": [ + { + "data": { + "image/svg+xml": [ + "\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " 2023-08-06T23:44:26.082098\n", + " image/svg+xml\n", + " \n", + " \n", + " Matplotlib v3.5.3, https://matplotlib.org/\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", + " \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", + " \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", + " \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", + " \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", + " \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", + " \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", + " \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", + " \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" + ], + "text/plain": [ + "
" + ] + }, + "metadata": { + "needs_background": "light" + }, + "output_type": "display_data" + } + ], + "source": [ + "# relative error\n", + "plt.plot(\n", + " test_delta,\n", + " (test_energies - analytical_energies) / np.abs(analytical_energies),\n", + " \"-\",\n", + " color=\"b\",\n", + ")\n", + "plt.xlabel(\"Delta\", fontsize=14)\n", + "plt.ylabel(\"GS Relative Error\", fontsize=14)\n", + "plt.axvspan(-3.0, 3.0, color=\"darkgrey\", alpha=0.5) # training set span\n", + "plt.show()" + ] + }, + { + "cell_type": "markdown", + "id": "5f9bda8a", + "metadata": {}, + "source": [ + "To get more detailed information or further study, please refer to our [paper](https://arxiv.org/abs/2308.01068) and [GitHub](https://github.com/JachyMeow/NN-VQA)." + ] + } + ], + "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.9.12" + }, + "vscode": { + "interpreter": { + "hash": "18d2a9923f839b0d86cf68fd09770e726264cf9d62311eaf57b1fff0ca4bed8e" + } + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/docs/source/tutorials/nnvqe_cn.ipynb b/docs/source/tutorials/nnvqe_cn.ipynb new file mode 100644 index 00000000..056990bb --- /dev/null +++ b/docs/source/tutorials/nnvqe_cn.ipynb @@ -0,0 +1,4015 @@ +{ + "cells": [ + { + "attachments": {}, + "cell_type": "markdown", + "id": "64ba95d6", + "metadata": {}, + "source": [ + "#
神经网络编码的变分量子本征值求解器(NN-VQE)" + ] + }, + { + "cell_type": "markdown", + "id": "b65f64bf", + "metadata": {}, + "source": [ + "## 概述\n", + "\n", + "在本教程中,我们将使用TensorCircuit展示一个量子计算通用框架——神经网络编码的变分量子算法(neural network encoded variational quantum algorithms,NN-VQAs)。NN-VQA将一个给定问题的参量(如哈密顿量的参数)作为神经网络的输入,并使用其输出来参数化标准的变分量子算法(variational quantum algorithms,VQAs)的线路拟设(ansatz circuit)。在本文中,我们以神经网络编码的变分量子本征值求解器(NN-variational quantum eigensolver,NN-VQE)来具体说明。" + ] + }, + { + "cell_type": "markdown", + "id": "831930ae", + "metadata": {}, + "source": [ + "## 设置" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "id": "4e1651b9", + "metadata": { + "scrolled": false + }, + "outputs": [], + "source": [ + "import numpy as np\n", + "import matplotlib.pyplot as plt\n", + "import tensorflow as tf\n", + "import tensorcircuit as tc\n", + "import cotengra\n", + "import quimb\n", + "from tqdm.notebook import tqdm\n", + "from functools import partial\n", + "\n", + "optc = cotengra.ReusableHyperOptimizer(\n", + " methods=[\"greedy\"],\n", + " parallel=\"ray\",\n", + " minimize=\"combo\",\n", + " max_time=30,\n", + " max_repeats=1024,\n", + " progbar=True,\n", + ")\n", + "tc.set_contractor(\"custom\", optimizer=optc, preprocessing=True)\n", + "\n", + "K = tc.set_backend(\"tensorflow\")\n", + "tc.set_dtype(\"complex128\")" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "d78b480b", + "metadata": {}, + "source": [ + "## 能量\n", + "\n", + "本教程所使用的哈密顿量是具有周期性边界条件的一维XXZ伊辛模型。它具有横向场强$\\lambda$和各向异性参数$\\Delta$。我们选择哈密顿量的能量期望函数作为损失函数。\n", + "\n", + "$$ \\hat{H}_{XXZ}=\\sum_{i}{ \\left( X_{i}X_{i+1}+Y_{i}Y_{i+1}+\\Delta Z_{i}Z_{i+1} \\right) } + \\lambda \\sum_{i}{Z_{i}} $$" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "id": "fff67346", + "metadata": {}, + "outputs": [], + "source": [ + "def energy(c: tc.Circuit, lamb: float = 1.0, delta: float = 1.0):\n", + " e = 0.0\n", + " n = c._nqubits\n", + " for i in range(n):\n", + " e += lamb * c.expectation((tc.gates.z(), [i])) # \n", + " for i in range(n):\n", + " e += c.expectation(\n", + " (tc.gates.x(), [i]), (tc.gates.x(), [(i + 1) % n])\n", + " ) # \n", + " e += c.expectation(\n", + " (tc.gates.y(), [i]), (tc.gates.y(), [(i + 1) % n])\n", + " ) # \n", + " e += delta * c.expectation(\n", + " (tc.gates.z(), [i]), (tc.gates.z(), [(i + 1) % n])\n", + " ) # \n", + " return K.real(e)" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "0ad6a7a6", + "metadata": {}, + "source": [ + "## 线路拟设\n", + "\n", + "现在我们来设计线路。我们选择多尺度纠缠重整化拟设(multi-scale entangled renormalization ansatz,MERA)作为线路拟设,其中$d$为线路深度。(详见[MERA教程](https://tensorcircuit.readthedocs.io/zh/latest/tutorials/mera_cn.html))" + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "id": "445b7c86", + "metadata": {}, + "outputs": [], + "source": [ + "def MERA(\n", + " inp, n, d=1, lamb=1.0, energy_flag=False\n", + "): # 对于单变量一维XXZ模型,我们固定lamb\n", + " params = K.cast(inp[\"params\"], \"complex128\")\n", + " delta = K.cast(inp[\"delta\"], \"complex128\")\n", + " c = tc.Circuit(n)\n", + "\n", + " idx = 0\n", + "\n", + " for i in range(n):\n", + " c.rx(i, theta=params[3 * i])\n", + " c.rz(i, theta=params[3 * i + 1])\n", + " c.rx(i, theta=params[3 * i + 2])\n", + " idx += 3 * n\n", + "\n", + " for n_layer in range(1, int(np.log2(n)) + 1):\n", + " n_qubit = 2**n_layer # 涉及的量子比特数\n", + " step = int(n / n_qubit)\n", + "\n", + " for _ in range(d): # 线路深度\n", + " # 偶数层\n", + " for i in range(step, n - step, 2 * step):\n", + " c.rxx(i, i + step, theta=params[idx])\n", + " c.rzz(i, i + step, theta=params[idx + 1])\n", + " idx += 2\n", + "\n", + " # 奇数层\n", + " for i in range(0, n, 2 * step):\n", + " c.rxx(i, i + step, theta=params[idx])\n", + " c.rzz(i, i + step, theta=params[idx + 1])\n", + " idx += 2\n", + "\n", + " # 单比特门\n", + " for i in range(0, n, step):\n", + " c.rx(i, theta=params[idx])\n", + " c.rz(i, theta=params[idx + 1])\n", + " idx += 2\n", + "\n", + " if energy_flag:\n", + " return energy(c, lamb, delta) # 返回哈密顿量的能量期望\n", + " else:\n", + " return c, idx # 返回线路&线路参量数" + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "id": "6daa2f64", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "The number of parameters is 74\n" + ] + }, + { + "data": { + "image/svg+xml": [ + "\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " 2023-08-06T16:31:47.918946\n", + " image/svg+xml\n", + " \n", + " \n", + " Matplotlib v3.5.3, https://matplotlib.org/\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", + " \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", + " \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", + " \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", + " \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", + " \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", + " \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", + " \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", + " \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", + " \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", + " \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", + " \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", + " \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", + " \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", + " \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", + " \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", + " \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", + " \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", + " \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", + " \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", + " \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", + " \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", + " \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", + " \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", + " \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", + " \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", + " \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", + " \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", + " \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", + " \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", + " \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", + " \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", + " \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", + " \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", + " \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", + " \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", + " \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", + " \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", + " \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", + " \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", + " \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", + " \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", + " \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", + " \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", + " \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", + " \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", + " \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", + " \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", + " \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", + " \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", + " \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", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "\n" + ], + "text/plain": [ + "
" + ] + }, + "execution_count": 9, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# 线路可视化\n", + "n = 8\n", + "d = 1\n", + "cirq, idx = MERA({\"params\": np.zeros(3000), \"delta\": 0.0}, n, d, 1.0)\n", + "print(\"The number of parameters is\", idx)\n", + "cirq.draw()" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "bfe2fbee", + "metadata": {}, + "source": [ + "## NN-VQE\n", + "\n", + "设计NN-VQE。我们使用神经网络将哈密顿量参数转换为VQE的变分量子线路(parameterized quantum ciecuit,PQC)的优化参数。" + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "id": "1ac050a8", + "metadata": {}, + "outputs": [], + "source": [ + "def NN_MERA(n, d, lamb, NN_shape, stddev):\n", + " input = tf.keras.layers.Input(shape=[1]) # 输入层\n", + "\n", + " x = tf.keras.layers.Dense(\n", + " units=NN_shape,\n", + " kernel_initializer=tf.keras.initializers.RandomNormal(stddev=stddev),\n", + " activation=\"ReLU\",\n", + " )(\n", + " input\n", + " ) # 隐层\n", + "\n", + " x = tf.keras.layers.Dropout(0.05)(x) # dropout层\n", + "\n", + " _, idx = MERA(\n", + " {\"params\": np.zeros(3000), \"delta\": 0.0}, n, d, 1.0, energy_flag=False\n", + " )\n", + " params = tf.keras.layers.Dense(\n", + " units=idx,\n", + " kernel_initializer=tf.keras.initializers.RandomNormal(stddev=stddev),\n", + " activation=\"sigmoid\",\n", + " )(\n", + " x\n", + " ) # 输出层\n", + "\n", + " qlayer = tc.KerasLayer(partial(MERA, n=n, d=d, lamb=lamb, energy_flag=True)) # PQC\n", + "\n", + " output = qlayer({\"params\": 6.3 * params, \"delta\": input}) # NN-VQE输出\n", + "\n", + " m = tf.keras.Model(inputs=input, outputs=output)\n", + "\n", + " return m" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "e9afb1a5", + "metadata": {}, + "source": [ + "## 训练\n", + "\n", + "现在我们用TensorFlow来训练NN-VQE。" + ] + }, + { + "cell_type": "code", + "execution_count": 23, + "id": "873ebd5e", + "metadata": { + "scrolled": false + }, + "outputs": [], + "source": [ + "def train(n, d, lamb, delta, NN_shape, maxiter=10000, lr=0.005, stddev=1.0):\n", + " exp_lr = tf.keras.optimizers.schedules.ExponentialDecay(\n", + " initial_learning_rate=lr, decay_steps=1000, decay_rate=0.7\n", + " )\n", + " opt = tf.keras.optimizers.Adam(exp_lr) # 优化器\n", + "\n", + " m = NN_MERA(n, d, lamb, NN_shape, stddev)\n", + " for i in range(maxiter):\n", + " with tf.GradientTape() as tape:\n", + " e = tf.zeros([1], dtype=tf.float64)\n", + " for de in delta:\n", + " e += m(K.reshape(de, [1])) # 将所有训练点的能量相加\n", + " grads = tape.gradient(e, m.variables)\n", + " opt.apply_gradients(zip(grads, m.variables))\n", + " if i % 500 == 0:\n", + " print(\"epoch\", i, \":\", e)\n", + "\n", + " m.save_weights(\"NN-VQE.weights.h5\") # 保存已训练的模型" + ] + }, + { + "cell_type": "code", + "execution_count": 24, + "id": "e8df3d67", + "metadata": { + "scrolled": false + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "epoch 0 : tf.Tensor([[117.53523392]], shape=(1, 1), dtype=float64)\n", + "epoch 500 : tf.Tensor([[-361.85937039]], shape=(1, 1), dtype=float64)\n", + "epoch 1000 : tf.Tensor([[-365.35288984]], shape=(1, 1), dtype=float64)\n", + "epoch 1500 : tf.Tensor([[-366.65891358]], shape=(1, 1), dtype=float64)\n", + "epoch 2000 : tf.Tensor([[-366.94258369]], shape=(1, 1), dtype=float64)\n" + ] + } + ], + "source": [ + "n = 8 # 量子比特数\n", + "d = 2 # 线路深度\n", + "lamb = 0.75 # 固定参数\n", + "delta = np.linspace(-3.0, 3.0, 20, dtype=\"complex128\") # 训练集\n", + "NN_shape = 20 # 隐层节点数\n", + "maxiter = 2500 # 最大迭代轮数\n", + "lr = 0.009 # 学习率\n", + "stddev = 0.1 # 神经网络参数初始值的标准差\n", + "\n", + "with tf.device(\"/cpu:0\"):\n", + " train(n, d, lamb, delta, NN_shape=NN_shape, maxiter=maxiter, lr=lr, stddev=stddev)" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "c5a7cdb0", + "metadata": {}, + "source": [ + "## 测试\n", + "\n", + "我们使用较训练集更大的测试集来测试NN-VQE的准确性和泛化能力。" + ] + }, + { + "cell_type": "code", + "execution_count": 47, + "id": "f15f4f68", + "metadata": {}, + "outputs": [ + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "4a3ceb8bdf90463f88050b771fde6925", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "0it [00:00, ?it/s]" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "test_delta = np.linspace(-4.0, 4.0, 201) # 测试集\n", + "test_energies = tf.zeros_like(test_delta).numpy()\n", + "m = NN_MERA(n, d, lamb, NN_shape, stddev)\n", + "m.load_weights(\"NN-VQE.weights.h5\")\n", + "for i, de in tqdm(enumerate(test_delta)):\n", + " test_energies[i] = m(K.reshape(de, [1]))" + ] + }, + { + "cell_type": "markdown", + "id": "924027f8", + "metadata": {}, + "source": [ + "## 对比\n", + "\n", + "我们将NN-VQE的结果与解析解相比,计算基态能量相对误差。从图中可以看出,NN-VQE能够在无微调(fine-tuning)的情况下准确估计参数化哈密顿量的基态能量,且具有良好的泛化能力。" + ] + }, + { + "cell_type": "code", + "execution_count": 48, + "id": "c8668a13", + "metadata": {}, + "outputs": [], + "source": [ + "analytical_energies = [] # 解析解\n", + "for i in test_delta:\n", + " h = quimb.tensor.tensor_builder.MPO_ham_XXZ(\n", + " n, i * 4, jxy=4.0, bz=2.0 * 0.75, S=0.5, cyclic=True\n", + " )\n", + " h = h.to_dense()\n", + " analytical_energies.append(np.min(quimb.eigvalsh(h)))" + ] + }, + { + "cell_type": "code", + "execution_count": 49, + "id": "42799e3e", + "metadata": {}, + "outputs": [ + { + "data": { + "image/svg+xml": [ + "\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " 2023-08-06T23:44:26.082098\n", + " image/svg+xml\n", + " \n", + " \n", + " Matplotlib v3.5.3, https://matplotlib.org/\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", + " \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", + " \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", + " \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", + " \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", + " \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", + " \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", + " \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", + " \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" + ], + "text/plain": [ + "
" + ] + }, + "metadata": { + "needs_background": "light" + }, + "output_type": "display_data" + } + ], + "source": [ + "# 基态能量相对误差\n", + "plt.plot(\n", + " test_delta,\n", + " (test_energies - analytical_energies) / np.abs(analytical_energies),\n", + " \"-\",\n", + " color=\"b\",\n", + ")\n", + "plt.xlabel(\"Delta\", fontsize=14)\n", + "plt.ylabel(\"GS Relative Error\", fontsize=14)\n", + "plt.axvspan(-3.0, 3.0, color=\"darkgrey\", alpha=0.5) # 训练集区间\n", + "plt.show()" + ] + }, + { + "cell_type": "markdown", + "id": "5f9bda8a", + "metadata": {}, + "source": [ + "想要获得更详细的信息或进一步的研究,请参考我们的[论文](https://arxiv.org/abs/2308.01068)和[GitHub](https://github.com/JachyMeow/NN-VQA)." + ] + } + ], + "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.9.12" + }, + "vscode": { + "interpreter": { + "hash": "18d2a9923f839b0d86cf68fd09770e726264cf9d62311eaf57b1fff0ca4bed8e" + } + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/docs/source/tutorials/optimization_and_expressibility_cn.ipynb b/docs/source/tutorials/optimization_and_expressibility_cn.ipynb index 3fdaa7a8..e09df93c 100644 --- a/docs/source/tutorials/optimization_and_expressibility_cn.ipynb +++ b/docs/source/tutorials/optimization_and_expressibility_cn.ipynb @@ -276,7 +276,9 @@ "@tf.function\n", "def entropy(v):\n", " ψ = get_state(v)\n", - " ρ_reduced = tc.quantum.reduced_density_matrix(ψ, NA) # 部分描绘出子系统A得到的降密度矩阵\n", + " ρ_reduced = tc.quantum.reduced_density_matrix(\n", + " ψ, NA\n", + " ) # 部分描绘出子系统A得到的降密度矩阵\n", " S = tc.quantum.renyi_entropy(ρ_reduced) # Renyi 纠缠熵\n", " return S" ] diff --git a/docs/source/tutorials/portfolio_optimization.ipynb b/docs/source/tutorials/portfolio_optimization.ipynb new file mode 100644 index 00000000..7ec25888 --- /dev/null +++ b/docs/source/tutorials/portfolio_optimization.ipynb @@ -0,0 +1,499 @@ +{ + "cells": [ + { + "attachments": {}, + "cell_type": "markdown", + "id": "6ddb8a88-779a-43f7-ae14-115463bd87f5", + "metadata": {}, + "source": [ + "# Portfolio Optimization\n", + "\n", + "In this tutorial, we demonstrate the transformation of financial portfolio optimization into a quadratic unconstrained binary optimization (QUBO) problem. Subsequently, we employ the Quantum Approximate Optimization Algorithm (QAOA) to solve it. We will conduct a comparative analysis between the outcomes obtained through QAOA and those derived from classical brute force searching. Additionally, we explore the potential for enhancing results by customizing the QAOA 'mixer' component.\n", + "\n", + "## Introduction\n", + "\n", + "Consider the following scenario: Xiaoming, an astute individual, possesses a sum of money represented by $B$ and is contemplating investing it in the stock market. The market contains $n$ shares, each having an identical price. Xiaoming's objective is to maximize returns while minimizing risk, taking into account the varying levels of risk tolerance among individuals. Xiaoming's personal risk tolerance is represented by $q$. In light of these considerations, the question arises: Which shares should Xiaoming choose to construct an optimal portfolio?\n", + "\n", + "Xiaoming's predicament falls under the purview of portfolio optimization problems. These problems are classified as Quadratic Unconstrained Binary Optimization (QUBO) problems, wherein binary numbers are utilized to represent decisions. In this case, \"1\" signifies selection, while \"0\" denotes the opposite. To address the challenge of portfolio optimization, the Quantum Approximate Optimization Algorithm (QAOA) is employed.\n", + "\n", + "## Solving portfolio optimization problems with QAOA\n", + "\n", + "In a simple boolean Markowitz portfolio optimization problem, we wish to solve \n", + "\n", + "$$\n", + "\\min_{x\\in\\{0,1\\}^n}\\quad q x^T \\Sigma x - \\mu^T x\n", + "$$\n", + "\n", + "subject to a constraint\n", + "\n", + "$$\n", + "1^T x = B\n", + "$$\n", + "\n", + "where \n", + "* $n$: number of assets under consideration\n", + "* $q > 0 $: risk-appetite\n", + "* $\\Sigma \\in \\mathbb{R}^{n\\times n}$: covariance matrix of the assets\n", + "* $\\mu\\in\\mathbb{R}^n$: mean return of the assets\n", + "* $B$: budget (i.e., the total number of assets out of $n$ that can be selected)\n", + "\n", + "Our first step is to convert this constrained quadratic programming problem into a QUBO. We do this by adding a penalty factor $t$ and considering the alternative problem:\n", + "\n", + "$$\n", + "\\min_{x\\in\\{0,1\\}^n}\\quad q x^T \\Sigma x - \\mu^Tx + t(1^Tx-B)^2\n", + "$$\n", + "\n", + "The linear terms $\\mu^Tx = \\mu_1 x_1 + \\mu_2 x_2+\\ldots$ can all be transformed into squared forms, exploiting the properties of boolean variables where $0^2=0$ and $1^2=1$. The same trick is applied to the middle term of $t(1^Tx-B)^2$. Then the function is written as\n", + "\n", + "$$\n", + "\\min_{x\\in\\{0,1\\}^n}\\quad q x^T \\Sigma x - \\sum_{i=1}^n\\mu_i x_i^2 + t(1^Tx-B)^2\n", + "$$\n", + "\n", + "which is a QUBO problem\n", + "\n", + "$$\n", + "\\min_{x\\in\\{0,1\\}^n}\\quad x^T Q x + tB^2\n", + "$$\n", + "\n", + "where matrix $Q$ is\n", + "\n", + "$$\n", + "Q = q\\Sigma -\\mu\\begin{pmatrix}1 & \\\\ & 1\\\\ & & \\ddots\\end{pmatrix} + t\\begin{pmatrix}1 -2B & 1 & \\ldots & 1 \\\\\n", + "1 & 1-2B & 1 & \\ldots \\\\1 & 1 & 1-2B \\\\\n", + "\\vdots\\end{pmatrix}\n", + "$$\n", + "\n", + "and we ignore the constant term $t B^2$. We can now solve this by QAOA.\n", + "\n", + "## Set up" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "id": "4f4feab6", + "metadata": {}, + "outputs": [], + "source": [ + "import tensorcircuit as tc\n", + "import numpy as np\n", + "import tensorflow as tf\n", + "import matplotlib.pyplot as plt\n", + "\n", + "from tensorcircuit.templates.ansatz import QAOA_ansatz_for_Ising\n", + "from tensorcircuit.templates.conversions import QUBO_to_Ising\n", + "from tensorcircuit.applications.optimization import QUBO_QAOA, QAOA_loss\n", + "from tensorcircuit.applications.finance.portfolio import StockData, QUBO_from_portfolio\n", + "\n", + "K = tc.set_backend(\"tensorflow\")\n", + "tf.compat.v1.logging.set_verbosity(tf.compat.v1.logging.ERROR)" + ] + }, + { + "cell_type": "markdown", + "id": "55659298", + "metadata": {}, + "source": [ + "## Import data\n", + "\n", + "To optimize a portfolio, it is essential to utilize historical data from various stocks within the same time frame. Websites such as [Nasdaq](Nasdaq.com) and [Yahoo Finance](https://finance.yahoo.com/) provide access to such data. The next step involves transforming this data into an annualized return ($\\mu$) and covariance matrix ($\\sigma$), which can be recognized by the Quantum Approximate Optimization Algorithm (QAOA). To simplify this process, we can utilize the `StockData` class. This class performs the necessary calculations for annualized return and covariance, as outlined in this [paper](https://doi.org/10.1007/s11128-022-03766-5). They are:\n", + "\n", + "$$\n", + "\\mu = \\left[\\prod ^m_{k=1}\\left(1+r_k^{(i)}\\right)\\right]^{\\frac{252}{m}}\\\\\n", + "\\sigma_{ij}=\\frac{252}{m}\\sum^m_{k=1}\\left(r_k^{(i)}-\\overline{r^{(i)}}\\right)\\left(r_k^{(j)}-\\overline{r^{(j)}}\\right)\n", + "$$\n", + "\n", + "Here is a demonstration of how to use this class:" + ] + }, + { + "cell_type": "code", + "execution_count": 12, + "id": "62bf0673", + "metadata": {}, + "outputs": [], + "source": [ + "import random\n", + "\n", + "# randomly generate historical data of 6 stocks in 1000 days\n", + "data = [[random.random() for i in range(1000)] for j in range(6)]\n", + "stock_data = StockData(data) # Create an instance\n", + "\n", + "# calculate the annualized return and covariance matrix\n", + "mu = stock_data.get_return()\n", + "sigma = stock_data.get_covariance()\n", + "\n", + "# some other information can also be obtained from this class\n", + "n_stocks = stock_data.n_stocks # number of stocks\n", + "n_days = stock_data.n_days # length of the time span\n", + "daily_change = stock_data.daily_change # relative change of each day" + ] + }, + { + "cell_type": "markdown", + "id": "0fb1d227", + "metadata": {}, + "source": [ + "In this analysis, we have carefully chosen six prominent stocks: Apple Inc. (AAPL), Microsoft Corporation (MSFT), NVIDIA Corporation (NVDA), Pfizer Inc. (PFE), Levi Strauss & Co. (LEVI), and Cisco Systems, Inc. (CSCO). We acquired their historical data spanning from 09/07/2022 to 09/07/2023 from Yahoo Finance. Here are the return and covariance associated with this dataset" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "id": "a7eb3a74", + "metadata": {}, + "outputs": [], + "source": [ + "# real-world stock data, calculated using the class above\n", + "# stock name: aapl, msft, nvda, pfe, levi, csco\n", + "# from 09/07/2022 to 09/07/2023\n", + "mu = [1.21141, 1.15325, 2.06457, 0.63539, 0.63827, 1.12224]\n", + "\n", + "sigma = np.array(\n", + " [\n", + " [0.08488, 0.06738, 0.09963, 0.02124, 0.05516, 0.04059],\n", + " [0.06738, 0.10196, 0.11912, 0.02163, 0.0498, 0.04049],\n", + " [0.09963, 0.11912, 0.31026, 0.01977, 0.10415, 0.06179],\n", + " [0.02124, 0.02163, 0.01977, 0.05175, 0.01792, 0.02137],\n", + " [0.05516, 0.0498, 0.10415, 0.01792, 0.19366, 0.0432],\n", + " [0.04059, 0.04049, 0.06179, 0.02137, 0.0432, 0.05052],\n", + " ]\n", + ")" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "f6dc53d4-7ed0-436d-aa1f-8674c56e756e", + "metadata": {}, + "source": [ + "Using this mean and covariance data, we can now define our portfolio optimization problem, convert it to a QUBO matrix, and then extract the Pauli terms and weights, which is similar to what we do in the [QUBO tutorial](https://tensorcircuit.readthedocs.io/en/latest/tutorials/qubo_problem.html)." + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "id": "3f6edcd5-3c10-49fc-86ea-160fc6d3187e", + "metadata": {}, + "outputs": [], + "source": [ + "q = 0.5 # the risk preference of investor\n", + "budget = 4 # Note that in this example, there are 6 assets, but a budget of only 4\n", + "penalty = 1.2\n", + "\n", + "Q = QUBO_from_portfolio(sigma, mu, q, budget, penalty)\n", + "portfolio_pauli_terms, portfolio_weights, portfolio_offset = QUBO_to_Ising(Q)" + ] + }, + { + "cell_type": "markdown", + "id": "730da712", + "metadata": {}, + "source": [ + "## Classical method\n", + "\n", + "We first use brute force to calculate the cost of each combination, which is the expectation value $x^TQx$ for each $x\\in\\{0, 1\\}^n$ ($n$ is the number of stocks). It will give us a clue about the performance of QAOA." + ] + }, + { + "cell_type": "code", + "execution_count": 31, + "id": "168f7c36", + "metadata": {}, + "outputs": [], + "source": [ + "def print_Q_cost(Q, wrap=False, reverse=False):\n", + " n_stocks = len(Q)\n", + " states = []\n", + " for i in range(2**n_stocks):\n", + " a = f\"{bin(i)[2:]:0>{n_stocks}}\"\n", + " n_ones = 0\n", + " for j in a:\n", + " if j == \"1\":\n", + " n_ones += 1\n", + " states.append(a)\n", + "\n", + " cost_dict = {}\n", + " for selection in states:\n", + " x = np.array([int(bit) for bit in selection])\n", + " cost_dict[selection] = np.dot(x, np.dot(Q, x))\n", + " cost_sorted = dict(sorted(cost_dict.items(), key=lambda item: item[1]))\n", + " if reverse == True:\n", + " cost_sorted = dict(\n", + " sorted(cost_dict.items(), key=lambda item: item[1], reverse=True)\n", + " )\n", + " num = 0\n", + " print(\"\\n-------------------------------------\")\n", + " print(\" selection\\t |\\t cost\")\n", + " print(\"-------------------------------------\")\n", + " for k, v in cost_sorted.items():\n", + " print(\"%10s\\t |\\t%.4f\" % (k, v))\n", + " num += 1\n", + " if (num >= 8) & (wrap == True):\n", + " break\n", + " print(\" ...\\t |\\t ...\")\n", + " print(\"-------------------------------------\")" + ] + }, + { + "cell_type": "code", + "execution_count": 32, + "id": "6a9d41c9", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "\n", + "-------------------------------------\n", + " selection\t |\t cost\n", + "-------------------------------------\n", + " 111001\t |\t-24.0487\n", + " 101101\t |\t-23.7205\n", + " 111100\t |\t-23.6414\n", + " 011101\t |\t-23.6340\n", + " 101011\t |\t-23.5123\n", + " 011011\t |\t-23.4316\n", + " 111010\t |\t-23.4269\n", + " 111101\t |\t-23.3742\n", + " ...\t |\t ...\n", + "-------------------------------------\n" + ] + } + ], + "source": [ + "print_Q_cost(Q, wrap=True)" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "c34f3398", + "metadata": {}, + "source": [ + "### Use QAOA\n", + "\n", + "Here, a standard QAOA ansatz with 12 layers is used. This circuit will be trained for 1000 iterations." + ] + }, + { + "cell_type": "code", + "execution_count": 33, + "id": "9249ea96", + "metadata": {}, + "outputs": [ + { + "data": { + "image/png": "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xowZY2bOnGkSiYT5zne+Q10fxHbhhReau+++21x22WXGGGMuvfTSks8Hok7j8bjZsmWLefzxx81JJ51kZs+ebdrb2803v/nNgfgN9iuxnNurr75q5s+f770PBALmww8/NLfeeqv1sh2q27Bhw4wxxnzuc58zkkxdXZ1JJpNm5syZ3jEnnniiMcaY008/3Uj5/+FkMhlz1FFHecdce+21Zvfu3SYSiVj/TZW0xWIx895775np06ebpUuXekGEeh6Ybe7cuebFF1/c5zGbN2823/3ud733dXV1prOz08yePdtIMhMmTDDGGDN16lTvmAsuuMBks1lz9NFHW/+NlbL9+te/Ng8//HDJvl/+8pfm8ccfp64HaOsriAxEnV533XVmx44dJf9uzJ0716xdu/agy+zU0EwkEtHUqVO1ZMkSb58xRkuWLNGZZ55psWSHtsGDB0uSdu7cKUmaOnWqqqqqSur5vffe08aNG716PvPMM/Xmm29q27Zt3jHPPfecBg8erE996lNlLH3la2xs1OLFi/XCCy+U7KeeB8Yll1yiFStW6KmnnlJzc7NWrVqla665xvv8+OOP19FHH11Sz4lEQsuXLy+p5127dmnlypXeMUuWLFEul9Ppp59evh9T4V5++WVNnz5d48aNkySdfPLJOuuss/Tb3/5WEnXth4Gq0zPPPFMvvvii0um0d8xzzz2nCRMmaMiQIQdVxop/6N1AGjZsmMLhsJqbm0v2Nzc3a8KECZZKdWgLBAJ64IEH9NJLL+ntt9+WJI0YMULJZFJ79uwpOba5uVkjRozwjunrv0PxM+TNnj1bU6ZM6XMeE/U8ME444QRdf/31uv/++/X9739f06ZN049//GOlUik99thjXj31VY8967ln2JOkbDarnTt3Us893HPPPaqrq9O7776rbDarUCik22+/XU8++aQkUdc+GKg6HTFihNavX7/XdxQ/271798cuo1NBBAOvsbFREydO1FlnnWW7KIedY445Rj/60Y80Y8YMlrn2UTAY1IoVK3T77bdLklavXq2JEyfquuuu02OPPWa5dIeXWbNm6aqrrtKVV16pt99+W5MnT9YDDzygzZs3U9cOc2popqWlRZlMRvX19SX76+vrtXXrVkulOnTNnz9fX/rSl3TeeeepqanJ279161ZFo1FvyKaoZz1v3bq1z/8Oxc+QH3qpr6/XqlWrlE6nlU6nde655+of/uEflE6n1dzcTD0PgC1btuidd94p2bd27Vode+yxkrrraV//bmzdunWvu5VCoZCOOOII6rmH++67T/fcc49+/vOf66233tITTzyhefPmqaGhQRJ17YeBqlM//y1xKoik02mtXLlS06dP9/YFAgFNnz5dr7zyisWSHXrmz5+vyy+/XJ///Oe1YcOGks9WrlypVCpVUs/jx4/XmDFjvHp+5ZVXNGnSJA0fPtw7ZsaMGdqzZ89ejYKrXnjhBU2cOFGTJ0/2tj//+c9atGiRJk+erBUrVlDPA2DZsmU68cQTS/aNHz9eGzdulCStX79eW7ZsKanneDyu008/vaSehw4dqilTpnjHfP7zn1cwGNTy5cvL8CsODbW1tcrlciX7stmsgsF8U0RdD7yBqtNXXnlFZ599tsLh7oGUGTNm6N133z2oYZki67N8y7nNmjXLdHZ2mq9+9atmwoQJ5ic/+YnZuXNnyV0FbPveGhsbza5du8zZZ59t6uvrva26uto75qGHHjIbNmww5557rpkyZYpZtmyZWbZsmfd58bbS3/3ud+bkk082559/vmlubua20v1sPe+aoZ4HZjv11FNNKpUyDQ0NZuzYseaKK64wbW1t5sorr/SOueWWW8zOnTvNxRdfbCZOnGieeeaZPm9/XLlypZk2bZr5zGc+Y9577z2nbynta1u4cKHZtGmTd/vuZZddZrZt22buuece6vogtlgsZk455RRzyimnGGOMufHGG80pp5xiRo8ePWB1WldXZ7Zs2WIeffRRc9JJJ5lZs2aZtrY2bt/9uNvf//3fmw0bNpiuri7z6quvmtNOO816mQ6l7aN87Wtf846JRqPmwQcfNDt27DBtbW3m6aefNvX19SXfc+yxx5rFixeb9vZ2s23bNnPfffeZUChk/fdV8tY7iFDPA7N98YtfNGvWrDGdnZ3mnXfeMddcc81ex9x5551my5YtprOz0/z+978348aNK/l86NChZtGiRSaRSJjdu3eb//zP/zSxWMz6b6ukbdCgQWbevHlmw4YNpqOjw7z//vvm7rvv3utWcur6wLZzzjmnz3+TFy5cOKB1OmnSJPPiiy+azs5Os2nTJnPLLbcMSPkDhRcAAABl59QcEQAAUFkIIgAAwBqCCAAAsIYgAgAArCGIAAAAawgiAADAGoIIAACwhiACAACsIYgAAABrCCIAAMAagggAALCGIAIAAKz5/+r+so8y6PhtAAAAAElFTkSuQmCC", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "iterations = 1000\n", + "nlayers = 12\n", + "loss_list = []\n", + "\n", + "\n", + "# define a callback function to recode the loss\n", + "def record_loss(loss, params):\n", + " loss_list.append(loss)\n", + "\n", + "\n", + "# apply QAOA on this portfolio optimization problem\n", + "final_params = QUBO_QAOA(Q, nlayers, iterations, callback=record_loss)\n", + "\n", + "p = plt.plot(loss_list)" + ] + }, + { + "cell_type": "markdown", + "id": "9126333d", + "metadata": {}, + "source": [ + "Create a function to visualize the results, listing all combinations in descending order of probability." + ] + }, + { + "cell_type": "code", + "execution_count": 34, + "id": "4a2c60e4", + "metadata": {}, + "outputs": [], + "source": [ + "def print_result_prob(c, wrap=False, reverse=False):\n", + " states = []\n", + " n_qubits = c._nqubits\n", + " for i in range(2**n_qubits):\n", + " a = f\"{bin(i)[2:]:0>{n_qubits}}\"\n", + " states.append(a)\n", + " # Generate all possible binary states for the given number of qubits\n", + "\n", + " probs = K.numpy(c.probability()).round(decimals=4)\n", + " # Calculate the probabilities of each state using the circuit's probability method\n", + "\n", + " sorted_indices = np.argsort(probs)[::-1]\n", + " if reverse == True:\n", + " sorted_indices = sorted_indices[::-1]\n", + " state_sorted = np.array(states)[sorted_indices]\n", + " prob_sorted = np.array(probs)[sorted_indices]\n", + " # Sort the states and probabilities in descending order based on the probabilities\n", + "\n", + " print(\"\\n-------------------------------------\")\n", + " print(\" selection\\t |\\tprobability\")\n", + " print(\"-------------------------------------\")\n", + " if wrap == False:\n", + " for i in range(len(states)):\n", + " print(\"%10s\\t |\\t %.4f\" % (state_sorted[i], prob_sorted[i]))\n", + " # Print the sorted states and their corresponding probabilities\n", + " elif wrap == True:\n", + " for i in range(4):\n", + " print(\"%10s\\t |\\t %.4f\" % (state_sorted[i], prob_sorted[i]))\n", + " print(\" ... ...\")\n", + " for i in range(-5, -1):\n", + " print(\"%10s\\t |\\t %.4f\" % (state_sorted[i], prob_sorted[i]))\n", + " print(\"-------------------------------------\")" + ] + }, + { + "cell_type": "code", + "execution_count": 35, + "id": "680f52d2", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "\n", + "-------------------------------------\n", + " selection\t |\tprobability\n", + "-------------------------------------\n", + " 111001\t |\t 0.1169\n", + " 101101\t |\t 0.0872\n", + " 111100\t |\t 0.0823\n", + " 101011\t |\t 0.0809\n", + " ... ...\n", + " 000100\t |\t 0.0000\n", + " 010000\t |\t 0.0000\n", + " 000010\t |\t 0.0000\n", + " 000001\t |\t 0.0000\n", + "-------------------------------------\n" + ] + } + ], + "source": [ + "c_final = QAOA_ansatz_for_Ising(\n", + " final_params, nlayers, portfolio_pauli_terms, portfolio_weights\n", + ")\n", + "print_result_prob(c_final, wrap=True)" + ] + }, + { + "cell_type": "markdown", + "id": "71c7a0e0", + "metadata": {}, + "source": [ + "The highest probability corresponds to the best combination, which is consistent with what we found with classical brute force search.\n", + "\n", + "## Use XY mixer to improve the performance\n", + "\n", + "In the context of QAOA, XY mixers serve as a specific type of quantum gate to augment the optimization process. XY mixers, which are quantum gates introducing qubit interactions through controlled rotations, enable modification of the quantum system's state. The utilization of XY mixers in QAOA provides several advantages. They facilitate more efficient exploration of the solution space, thereby potentially improving the algorithm's overall performance. Moreover, XY mixers can amplify gradients of the objective function during optimization and enhance quantum circuit depth. Notably, in scenarios like portfolio optimization (covered in another tutorial), where constraints exist, XY mixers preserve the constraints associated with individual combinations while allowing for smooth transitions between them.\n", + "\n", + "It is crucial to consider that the choice of mixers relies on the specific problem under consideration, its unique characteristics, and the quantum hardware available." + ] + }, + { + "cell_type": "code", + "execution_count": 38, + "id": "3d558b9f", + "metadata": {}, + "outputs": [ + { + "data": { + "image/png": "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VqxYoY6ODrtLGdY4zvHBcY4fjnV8cJzjYygc52E5YRcAAAxfjLwAAICEQngBAAAJhfACAAASCuEFAAAkFMJLP33lK1/RgQMH1NbWpl27dqmwsNDukhLKt771Lf3lL3+Rz+dTbW2tfvvb32rGjBk9+qSkpGj16tWqr69Xc3OzfvOb32jChAk9+kyaNEm///3v1dLSotraWn3/+9+Xy+WK51dJKA888ICMMXr00UcjyzjOsTNx4kQ9/fTTqq+vV2trq958803Nnz+/R58HH3xQR48eVWtrq/7whz9o+vTpPdZnZmZqw4YNampqktfr1bp165Senh7PrzGkOZ1OPfTQQ6qsrFRra6v279+v73znOx/px3GOzic+8Qk999xzqqqqkjFG11577Uf6xOKYzp49Wzt27FBbW5s++OADffOb34zZdzC0vttNN91k2tvbzW233WbOP/988/jjj5uGhgYzfvx422tLlLZlyxbzuc99zlxwwQVmzpw55ve//705ePCgSUtLi/T52c9+Zg4dOmSuuOIKU1BQYF577TWzc+fOyHqn02nefPNNs3XrVjN37lxz5ZVXmrq6OvMf//Eftn+/odguuugiU1lZafbs2WMeffRRjnOM29ixY82BAwfMf/7nf5rCwkJz7rnnmuLiYjN16tRIn/vvv994vV5zzTXXmNmzZ5vf/e535v333zcpKSmRPps3bzZ//etfzcUXX2wuvfRS8+6775qNGzfa/v2GSlu+fLk5duyY+dSnPmUmT55srr/+euPz+cxXv/pVjvMg2pVXXmm++93vmuuuu84YY8y1117bY30sjqnH4zHV1dXm6aefNhdccIG5+eabTUtLi/niF78Yi+9g/0Ec6m3Xrl3msccei7x3OBzmyJEj5oEHHrC9tkRt48aNM8YY84lPfMJIMhkZGaajo8Ncf/31kT4zZ840xhizYMECI3X+xxYMBs2ECRMifb70pS+ZxsZG43a7bf9OQ6mlp6ebd955xyxZssS8/PLLkfDCcY5dW7lypdmxY0effY4ePWr+5V/+JfI+IyPDtLW1mZtvvtlIMvn5+cYYY+bPnx/ps3TpUhMKhUxubq7t33EotOeff96sW7eux7Lf/OY35umnn+Y4x6j1Fl5icUzvuusuc/z48R7/bqxcudLs27dv0DVz2ugM3G635s+fr23btkWWGWO0bds2FRUV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", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "loss_list = []\n", + "final_params = QUBO_QAOA(Q, nlayers, iterations, mixer=\"XY\", callback=record_loss)\n", + "\n", + "p = plt.plot(loss_list)" + ] + }, + { + "cell_type": "code", + "execution_count": 39, + "id": "d83fc94c", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "\n", + "-------------------------------------\n", + " selection\t |\tprobability\n", + "-------------------------------------\n", + " 111001\t |\t 0.1553\n", + " 001001\t |\t 0.1260\n", + " 101001\t |\t 0.1180\n", + " 111000\t |\t 0.0934\n", + " ... ...\n", + " 100110\t |\t 0.0000\n", + " 000111\t |\t 0.0000\n", + " 000101\t |\t 0.0000\n", + " 000100\t |\t 0.0000\n", + "-------------------------------------\n" + ] + } + ], + "source": [ + "c_final = QAOA_ansatz_for_Ising(\n", + " final_params, nlayers, portfolio_pauli_terms, portfolio_weights, mixer=\"XY\"\n", + ")\n", + "print_result_prob(c_final, wrap=True)" + ] + }, + { + "cell_type": "markdown", + "id": "917c9415", + "metadata": {}, + "source": [ + "Compared with the standard X mixer, the XY mixer gives a higher probability of measuring the best result." + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "tc_dev", + "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.11.4" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/docs/source/tutorials/qaoa.ipynb b/docs/source/tutorials/qaoa.ipynb index 82460c81..2e1f44ee 100644 --- a/docs/source/tutorials/qaoa.ipynb +++ b/docs/source/tutorials/qaoa.ipynb @@ -1,75 +1,220 @@ { "cells": [ { + "attachments": {}, "cell_type": "markdown", + "id": "dc0db886", + "metadata": {}, "source": [ "# Quantum Approximation Optimization Algorithm (QAOA)" - ], - "metadata": {} + ] }, { + "attachments": {}, "cell_type": "markdown", + "id": "aecf6615", + "metadata": {}, "source": [ "## Overview" - ], - "metadata": {} + ] }, { + "attachments": {}, "cell_type": "markdown", + "id": "eaf8cd3f", + "metadata": {}, "source": [ - "QAOA is a hybrid classical-quantum algorithm that combines quantum circuits, and classical optimization of those circuits. In this tutorial, we utilize QAOA to solve the maximum cut (MAX CUT) combinatorial optimization problem: Given a graph $G=(V, E)$ with nodes $V$ and edges $E$, find a subset $S \\in V$ such that the number of edges between $S$ and $S \\backslash V$ is maximized. And this problem can be reduced to that of finding the ground state of an antiferromagnetic Ising model whose Hamiltonian reads:\n", + "QAOA is a hybrid classical-quantum algorithm that combines quantum circuits, and classical optimization of those circuits. In this tutorial, we utilize QAOA to solve the maximum cut (Max-Cut) combinatorial optimization problem, as proposed by [Farhi, Goldstone, and Gutmann (2014)](https://arxiv.org/abs/1411.4028)." + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "16ad937f", + "metadata": {}, + "source": [ + "## Max-Cut Problem" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "197d6df4", + "metadata": {}, + "source": [ + "In graph theory, a graph is composed of $n$ nodes, also known as vertices, and $m$ edges that connect the nodes. Note that there is not necessarily a link between any pair of nodes. Those $n$ nodes are divided into two sets. The number of edges that are cut by a partition/partitions of the nodes is what is going to be maximized." + ] + }, + { + "attachments": { + "16c7f2fb-b981-4798-8346-da54e3bcbaeb.png": { + "image/png": "iVBORw0KGgoAAAANSUhEUgAAB6wAAAJgCAIAAACx3QrqAAAEuGlUWHRYTUw6Y29tLmFkb2JlLnhtcAAAAAAAPD94cGFja2V0IGJlZ2luPSLvu78iIGlkPSJXNU0wTXBDZWhpSHpyZVN6TlRjemtjOWQiPz4KPHg6eG1wbWV0YSB4bWxuczp4PSJhZG9iZTpuczptZXRhLyIgeDp4bXB0az0iWE1QIENvcmUgNS41LjAiPgogPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4KICA8cmRmOkRlc2NyaXB0aW9uIHJkZjphYm91dD0iIgogICAgeG1sbnM6ZXhpZj0iaHR0cDovL25zLmFkb2JlLmNvbS9leGlmLzEuMC8iCiAgICB4bWxuczp0aWZmPSJodHRwOi8vbnMuYWRvYmUuY29tL3RpZmYvMS4wLyIKICAgIHhtbG5zOnBob3Rvc2hvcD0iaHR0cDovL25zLmFkb2JlLmNvbS9waG90b3Nob3AvMS4wLyIKICAgIHhtbG5zOnhtcD0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wLyIKICAgIHhtbG5zOnhtcE1NPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvbW0vIgogICAgeG1sbnM6c3RFdnQ9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZUV2ZW50IyIKICAgZXhpZjpQaXhlbFhEaW1lbnNpb249IjE5NjQiCiAgIGV4aWY6UGl4ZWxZRGltZW5zaW9uPSI2MDgiCiAgIGV4aWY6Q29sb3JTcGFjZT0iMSIKICAgdGlmZjpJbWFnZVdpZHRoPSIxOTY0IgogICB0aWZmOkltYWdlTGVuZ3RoPSI2MDgiCiAgIHRpZmY6UmVzb2x1dGlvblVuaXQ9IjIiCiAgIHRpZmY6WFJlc29sdXRpb249IjE0NC8xIgogICB0aWZmOllSZXNvbHV0aW9uPSIxNDQvMSIKICAgcGhvdG9zaG9wOkNvbG9yTW9kZT0iMyIKICAgcGhvdG9zaG9wOklDQ1Byb2ZpbGU9InNSR0IgSUVDNjE5NjYtMi4xIgogICB4bXA6TW9kaWZ5RGF0ZT0iMjAyMy0wNC0yMVQxMzozMDoyOSswMTowMCIKICAgeG1wOk1ldGFkYXRhRGF0ZT0iMjAyMy0wNC0yMVQxMzozMDoyOSswMTowMCI+CiAgIDx4bXBNTTpIaXN0b3J5PgogICAgPHJkZjpTZXE+CiAgICAgPHJkZjpsaQogICAgICBzdEV2dDphY3Rpb249InByb2R1Y2VkIgogICAgICBzdEV2dDpzb2Z0d2FyZUFnZW50PSJBZmZpbml0eSBQaG90byAxLjEwLjUiCiAgICAgIHN0RXZ0OndoZW49IjIwMjMtMDQtMjFUMTM6MzA6MjkrMDE6MDAiLz4KICAgIDwvcmRmOlNlcT4KICAgPC94bXBNTTpIaXN0b3J5PgogIDwvcmRmOkRlc2NyaXB0aW9uPgogPC9yZGY6UkRGPgo8L3g6eG1wbWV0YT4KPD94cGFja2V0IGVuZD0iciI/PpKdWA8AAAGAaUNDUHNSR0IgSUVDNjE5NjYtMi4xAAAokXWR3yuDURjHP7aJmKZQLqglXG2aqcWNMmnUkmbKr5vt3S+1H2/vO0lulVtFiRu/LvgLuFWulSJScss1ccN6Pe+22pI9p+c8n/M953k65zlgCaeVjG7zQCab10IBv3N+YdHZ8IqNDux0Q0TR1bGZmSA17euBOjPeuc1atc/9a82xuK5AXaPwqKJqeeFJ4eBaXjV5V7hdSUViwufCLk0uKHxv6tESv5mcLPGPyVo4NA6WVmFnsoqjVayktIywvJzeTHpVKd/HfIk9np2bldgj3oVOiAB+nEwxwTg+BhmR2YcbLwOyoka+p5g/TU5yFZlV1tFYIUmKPC5RV6V6XGJC9LiMNOtm///2VU8MeUvV7X6ofzGMjz5o2IHCtmF8HxtG4QSsz3CVreTnjmD4U/TtitZ7CI5NuLiuaNE9uNyCzic1okWKklXckkjA+xm0LEDbLTQtlXpW3uf0EcIb8lU3sH8A/XLesfwLC4tnvJTYXoMAAAAJcEhZcwAAFiUAABYlAUlSJPAAACAASURBVHic7N15fFz1ee/x58yZfdHiRd7lBRtjjDcpGBOPgkm6xMEpThOWXMgN0NxmARK4aUmgLTi5DYSEBgLclm4hyS1pIUkDqYPTpA0mGrOYSN4QYPAi29h41zIzGs1y5tw/RpZlWbZG0jlzzpz5vP/gNTOac37Py2Dx01fPPD9F13UBAAAAAAAAAMCJXFYXAAAAAAAAAACAWQjBAQAAAAAAAACORQgOAAAAAAAAAHAsQnAAAAAAAAAAgGMRggMAAAAAAAAAHIsQHAAAAAAAAADgWITgAAAAAAAAAADHIgQHAAAAAAAAADgWITgAAAAAAAAAwLEIwQEAAAAAAAAAjkUIDgAAAAAAAABwLEJwAAAAAAAAAIBjEYIDAAAAAAAAAByLEBwAAAAAAAAA4FiE4AAAAAAAAAAAxyIEBwAAAAAAAAA4FiE4AAAAAAAAAMCxCMEBAAAAAAAAAI5FCA4AAAAAAAAAcCxCcAAAAAAAAACAYxGCAwAAAAAAAAAcixAcAAAAAAAAAOBYhOAAAAAAAAAAAMciBAcAAAAAAAAAOBYhOAAAAAAAAADAsQjBAQAAAAAAAACORQgOAAAAAAAAAHAsQnAAAAAAAAAAgGMRggMAAAAAAAAAHIsQHAAAAAAAAADgWITgAAAAAAAAAADHIgQHAAAAAAAAADgWITgAAAAAAAAAwLEIwQEAAAAAAAAAjkUIDgAAAAAAAABwLEJwAAAAAAAAAIBjEYIDAAAAAAAAAByLEBwAAAAAAAAA4FiE4AAAAAAAAAAAxyIEBwAAAAAAAAA4FiE4AAAAAAAAAMCxCMEBAAAAAAAAAI5FCA4AAAAAAAAAcCxCcAAAAAAAAACAYxGCAwAAAAAAAAAcixAcAAAAAAAAAOBYhOAAAAAAAAAAAMciBAcAAAAAAAAAOBYhOAAAAAAAAADAsQjBAQAAAAAAAACORQgOAAAAAAAAAHAsQnAAAAAAAAAAgGMRggMAAAAAAAAAHIsQHAAAAAAAAADgWITgAAAAAAAAAADHIgQHAAAAAAAAADgWITgAAAAAAAAAwLEIwQEAAAAAAAAAjkUIDgAAAAAAAABwLEJwAAAAAAAAAIBjEYIDAAAAAAAAAByLEBwAAAAAAAAA4FiE4AAAAAAAAAAAxyIEBwAAAAAAAAA4FiE4AAAAAAAAAMCxCMEBAAAAAAAAAI5FCA4AAAAAAAAAcCxCcAAAAAAAAACAYxGCAwAAAAAAAAAcixAcAAAAAAAAAOBYhOAAAAAAAAAAAMciBAcAAAAAAAAAOBYhOAAAAAAAAADAsQjBAQAAAAAAAACORQgOAAAAAAAAAHAsQnAAAAAAAAAAgGMRggMAAAAAAAAAHIsQHAAAAAAA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" + } + }, + "cell_type": "markdown", + "id": "10c267f3", + "metadata": {}, + "source": [ + "As illustrated in the graph on the left below, a graph consists of 4 nodes (or vertices) represented by grey circles, and 4 edges, which are black lines that connect the nodes. Max-Cut problem is to find the maximum number of cut when nodes are divided into two set. \n", "\n", - "$$H_C = \\frac{1}{2} \\sum_{i,j\\in E} C_{ij} \\sigma^{z}_{i} \\sigma^{z}_{j},$$\n", + "For instance, in the right graph, nodes 0 and 1 are in set 0 (light blue), and nodes 2 and 3 are in set 1 (red). If we partition the nodes between these two sets, it will cut two edges (colored pink). Therefore, the number of cut edges in this case is 2. Basically, to find the maximum cut, we need to find the maximum number of edges where the corresponding nodes belong to different sets.\n", "\n", - "where $\\sigma^{z}_{i}$ is the Pauli-z matrix on $i$th qubit and $C_{ij}$ is the weight of the edge between nodes $i$ and $j$. We set $C_{ij}=1$ for simplicity. If $\\sigma^{z}_{i}=\\sigma^{z}_{j}$, $i,j\\in S$ or $i,j\\in S \\backslash V$; if $\\sigma^{z}_{i}= -\\sigma^{z}_{j}$, $i\\in S, j\\in S \\backslash V$ or $i\\in S \\backslash V, j \\in S$. Obviously, the number of edges between $S$ and $S \\backslash V$ is maximized with the graph structure decoded from the ground state." - ], - "metadata": {} + "If we write down the set numbers that follow the node sequence (from 0 to 3), we can represent this instance as a bit string \"0011\". Since switching the names of the sets doesn't influence the results, the bit string \"1100\" gives the same number of edge cut. In general, we can use a binary string of length $n$ to represent a partition of $n$ nodes into two disjoint sets. This representation is commonly used in graph theory and combinatorial optimization, and it enables us to apply various computational techniques, including classical and quantum algorithms, to solve problems such as Max-Cut.\n", + "\n", + "![network1.png](attachment:16c7f2fb-b981-4798-8346-da54e3bcbaeb.png)" + ] }, { + "attachments": {}, "cell_type": "markdown", + "id": "2908c43d", + "metadata": {}, "source": [ - "## Setup" - ], - "metadata": { - "collapsed": false - } + "The Max-Cut problem is a NP-complete problem. Classically, one way to approach this problem is to brutally test every possible bit string, which takes an exponential amount of time as the number of nodes, denoted by $n$, grows. Specifically, for $n$ nodes, there are $2^n$ combinations to test. However, QAOA offers a more efficient way to solve this problem, where $n$ qubits are used to represent $n$ nodes. Nodes are divided according to the energy level of the qubits." + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "4d28f719", + "metadata": {}, + "source": [ + "A cost (objective) function of the $\\alpha$-th edge (which connects $j$-th and $k$-th nodes) is defined\n", + "\n", + "$$\n", + "C_\\alpha=\\frac12(1-\\sigma_z^j\\sigma_z^k)\n", + "$$\n", + "\n", + "When $C_\\alpha = 1$, the $\\alpha$-th is accounted a cut edge. It will happen if and only if $j$-th and $k$-th nodes are in different set. The total number of edge cut is written as\n", + "\n", + "$$ \n", + "C(z)=\\sum_\\alpha^mC_\\alpha(z)\n", + "$$\n", + "\n", + "where $z = z_1z_2 . . . z_n$ is the bit string. Max-Cut asks for a string $z$ for which $C(z)$ is maximized. This problem is equivlant to finding the ground state of a cost Hamiltonian\n", + "\n", + "$$\n", + "H_C = \\frac{1}{2} \\sum_{i,j} \\sigma^{j}_{z} \\sigma^{k}_{z}\n", + "$$" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "871bff2e", + "metadata": {}, + "source": [ + "## QAOA for Max-Cut" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "598dc69e", + "metadata": {}, + "source": [ + "The Quantum Approximate Optimization Algorithm (QAOA) utilizes a parameterized quantum circuit ([PQC](https://tensorcircuit.readthedocs.io/en/latest/textbook/chap5.html?highlight=变分)) to generate a quantum state that represents a potential solution to the Max Cut problem. The initial state, denoted as $|s\\rangle$, is a uniform superposition over computational basis states.\n", + "\n", + "$$\n", + "|s\\rangle=\\frac{1}{\\sqrt{2^n}}\\sum_z|z\\rangle\n", + "$$\n", + "\n", + "This state is then evolved by a unitary operator that consists of $q$ layers, denoted as\n", + "\n", + "$$\n", + "U(\\vec{\\beta}, \\vec{\\gamma}) = V_{p}U_{p} \\cdots V_{1}U_{1},\n", + "$$\n", + "\n", + "where $U_{j}= e^{-i\\gamma_{j}H_{C}}$ and $V_{j}= e^{-i \\beta_{j} H_m}$. $H_C$ is the cost Hamiltonian introduced in previous section and the mixer Hamiltonian $H_m=\\sum_j\\sigma^j_x$ is used to mix the quantum state to explore different solutions. The unitary operator is parameterized by $2p$ angle parameters $\\gamma_1, \\gamma_2, \\dots, \\gamma_p$ and $\\beta_1, \\beta_2, \\dots ,\\beta_p$. It is worth noting that every $\\gamma$ is restricted to lie between $0$ and $2\\pi$, and every $\\beta$ is restricted to lie between $0$ and $\\pi$ due to the integer eigenvalues of $H_C$.\n", + "\n", + "It is important and also tricky to find a proper $p$. As introduced by [Farhi, Goldstone, and Gutmann (2014)](https://arxiv.org/abs/1411.4028), $p$ should be equal to the maximum steps between any pair of nodes. For example, in the graph above, the max steps are found between node 3 and node 1 or 0, and this number is 2. Therefore, a 2-layer PQC should be used.\n", + "\n", + "Begin with a set of initial parameters, the quantum state is obtained from the PQC and then the expected value of the cost Hamiltonian is calculated. A classical optimizer is then used to vary the parameters until a lower exptected value is found. This process is iterated a certain number of times, and the lowest expected value is approximated as the ground state energy of the cost Hamiltonian. By using the parameters that correspond to the lowest expected value, an approximate solution to the Max-Cut problem can be obtained.\n", + "\n", + "In summary, a general QAOA algorithm follows these steps:\n", + "\n", + "1. Define the cost fucntion according to the problem.\n", + "\n", + "2. Define the parameterized quantum circuit with cost Hamiltonian and mixer Hamiltonian.\n", + "\n", + "3. Define a group of initial parameters.\n", + "\n", + "4. calculate the cost function of this group of parameters.\n", + "\n", + "5. If a lower cost is found, accept it. Otherwise, keep the previously found cost.\n", + "\n", + "6. Find a new group of parameters with an optimizer.\n", + "\n", + "7. Repreat procedures 4-6 for given times. Then output the lowest cost and corresponding parameters." + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "940fa22b", + "metadata": {}, + "source": [ + "## The code" + ] }, { "cell_type": "code", - "execution_count": 1, + "execution_count": 103, + "id": "b0def04d", + "metadata": {}, + "outputs": [], "source": [ "import tensorcircuit as tc\n", "import tensorflow as tf\n", "import networkx as nx\n", + "import matplotlib.pyplot as plt\n", + "import numpy as np\n", + "from IPython.display import clear_output\n", + "import random\n", "\n", "K = tc.set_backend(\"tensorflow\")\n", "\n", "nlayers = 3 # the number of layers\n", - "ncircuits = 2 # the number of circuits with different initial parameters" - ], - "outputs": [], - "metadata": {} + "ncircuits = 6 # six circuits with different initial parameters are going to be optimized at the same time\n", + "nnodes = 8 # the number of nodes" + ] }, { + "attachments": {}, "cell_type": "markdown", + "id": "6e407437", + "metadata": {}, "source": [ - "## Define the Graph" - ], - "metadata": {} + "### Define the Graph" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "c82972a4", + "metadata": {}, + "source": [ + "The degree of a graph is defined as the maximum number of edges connected to single nodes. For example, the graph below has a fixed degree of 3." + ] }, { "cell_type": "code", - "execution_count": 2, + "execution_count": 104, + "id": "f1532831", + "metadata": {}, + "outputs": [ + { + "data": { + "image/png": "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", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], "source": [ - "def dict2graph(d):\n", - " g = nx.to_networkx_graph(d)\n", - " for e in g.edges:\n", - " if not g[e[0]][e[1]].get(\"weight\"):\n", - " g[e[0]][e[1]][\"weight\"] = 1.0\n", - " nx.draw(g, with_labels=True)\n", - " return g\n", - "\n", - "\n", "# a graph instance\n", "# each node is connected to three nodes\n", "# for example, node 0 is connected to nodes 1,7,3\n", @@ -77,56 +222,44 @@ " 0: {1: {\"weight\": 1.0}, 7: {\"weight\": 1.0}, 3: {\"weight\": 1.0}},\n", " 1: {0: {\"weight\": 1.0}, 2: {\"weight\": 1.0}, 3: {\"weight\": 1.0}},\n", " 2: {1: {\"weight\": 1.0}, 3: {\"weight\": 1.0}, 5: {\"weight\": 1.0}},\n", - " 4: {7: {\"weight\": 1.0}, 6: {\"weight\": 1.0}, 5: {\"weight\": 1.0}},\n", - " 7: {4: {\"weight\": 1.0}, 6: {\"weight\": 1.0}, 0: {\"weight\": 1.0}},\n", " 3: {1: {\"weight\": 1.0}, 2: {\"weight\": 1.0}, 0: {\"weight\": 1.0}},\n", - " 6: {7: {\"weight\": 1.0}, 4: {\"weight\": 1.0}, 5: {\"weight\": 1.0}},\n", + " 4: {7: {\"weight\": 1.0}, 6: {\"weight\": 1.0}, 5: {\"weight\": 1.0}},\n", " 5: {6: {\"weight\": 1.0}, 4: {\"weight\": 1.0}, 2: {\"weight\": 1.0}},\n", + " 6: {7: {\"weight\": 1.0}, 4: {\"weight\": 1.0}, 5: {\"weight\": 1.0}},\n", + " 7: {4: {\"weight\": 1.0}, 6: {\"weight\": 1.0}, 0: {\"weight\": 1.0}},\n", "}\n", + "pos = nx.spring_layout(nx.to_networkx_graph(example_graph_dict))\n", + "colors = [\"c\" for key in example_graph_dict.items()]\n", "\n", - "example_graph = dict2graph(example_graph_dict)" - ], - "outputs": [ - { - "output_type": "display_data", - "data": { - "text/plain": [ - "
" - ], - "image/png": "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" - }, - "metadata": {} - } - ], - "metadata": {} + "# convert to a NetworkX graph\n", + "example_graph = nx.to_networkx_graph(example_graph_dict)\n", + "nx.draw_networkx(example_graph, with_labels=True, node_color=colors, pos=pos)\n", + "ax = plt.gca()\n", + "ax.set_facecolor(\"w\")" + ] }, { + "attachments": {}, "cell_type": "markdown", + "id": "c944f6dd", + "metadata": {}, "source": [ - "## Parameterized Quantum Circuit (PQC)" - ], - "metadata": {} - }, - { - "cell_type": "markdown", - "source": [ - "The PQC with $p$ layers can be written as:\n", - "$$\n", - "U(\\vec{\\beta}, \\vec{\\gamma}) = V_{p}U_{p} \\cdots V_{1}U_{1},\n", - "$$\n", - "where $U_{j}= e^{-i\\gamma_{j}H_{C}}$ and $V_{j}= e^{-i \\beta_{j} \\sum_{k} \\sigma^{x}_{k}}$." - ], - "metadata": {} + "### Parameterized Quantum Circuit (PQC)" + ] }, { "cell_type": "code", - "execution_count": 3, + "execution_count": 105, + "id": "055d1257", + "metadata": {}, + "outputs": [], "source": [ - "def QAOAansatz(params, g=example_graph):\n", + "def QAOAansatz(params, g=example_graph, return_circuit=False):\n", " n = len(g.nodes) # the number of nodes\n", " c = tc.Circuit(n)\n", " for i in range(n):\n", " c.H(i)\n", + "\n", " # PQC\n", " for j in range(nlayers):\n", " # U_j\n", @@ -141,106 +274,499 @@ " for i in range(n):\n", " c.rx(i, theta=params[2 * j + 1])\n", "\n", + " # whether to return the circuit\n", + " if return_circuit is True:\n", + " return c\n", + "\n", " # calculate the loss function\n", " loss = 0.0\n", " for e in g.edges:\n", - " loss += c.expectation_ps(z=[e[0], e[1]])\n", + " loss += c.expectation_ps(z=[e[0], e[1]]) * g[e[0]][e[1]].get(\"weight\", 1.0)\n", "\n", " return K.real(loss)" - ], + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "5b159920", + "metadata": {}, + "source": [ + "### Optimization" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "6d07ee61", + "metadata": {}, + "source": [ + "Here, several circuits with different initial parameters are optimized/trained at the same time.\n", + "\n", + "Optimizers are used to find the minimum value." + ] + }, + { + "cell_type": "code", + "execution_count": 106, + "id": "b8d63c5d", + "metadata": {}, "outputs": [], - "metadata": {} + "source": [ + "# use vvag to get the losses and gradients with different random circuit instances\n", + "QAOA_vvag = K.jit(\n", + " tc.backend.vvag(QAOAansatz, argnums=0, vectorized_argnums=0), static_argnums=(1, 2)\n", + ")" + ] + }, + { + "cell_type": "code", + "execution_count": 107, + "id": "c51b17a7", + "metadata": {}, + "outputs": [ + { + "data": { + "image/png": "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", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "params = K.implicit_randn(\n", + " shape=[ncircuits, 2 * nlayers], stddev=0.1\n", + ") # initial parameters\n", + "opt = K.optimizer(tf.keras.optimizers.Adam(1e-2))\n", + "\n", + "list_of_loss = [[] for i in range(ncircuits)]\n", + "\n", + "for i in range(300):\n", + " loss, grads = QAOA_vvag(params, example_graph)\n", + " params = opt.update(grads, params) # gradient descent\n", + "\n", + " # visualise the progress\n", + " clear_output(wait=True)\n", + " list_of_loss = np.hstack((list_of_loss, K.numpy(loss)[:, np.newaxis]))\n", + " plt.xlabel(\"Iteration\")\n", + " plt.ylabel(\"Cost\")\n", + " for index in range(ncircuits):\n", + " plt.plot(range(i + 1), list_of_loss[index])\n", + " legend = [\"circuit %d\" % leg for leg in range(ncircuits)]\n", + " plt.legend(legend)\n", + " plt.show()" + ] }, { + "attachments": {}, "cell_type": "markdown", + "id": "98a6b152", + "metadata": {}, "source": [ - "## Main Optimization Loop" + "### Results" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "fc1c257d", + "metadata": {}, + "source": [ + "After inputing the optimized parameters back to the ansatz circuit, we can measure the probablitiy distribution of the quantum states. The states with highest probability are supposed to be the ones corresponding to Max Cut." + ] + }, + { + "cell_type": "code", + "execution_count": 108, + "id": "843c0ad3", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Circuit #0\n", + "cost: -6.011465 \n", + "bit strings: ['01010101', '10101010'] \n", + "\n", + "Circuit #1\n", + "cost: -6.0114813 \n", + "bit strings: ['01010101', '10101010'] \n", + "\n", + "Circuit #2\n", + "cost: -6.0113897 \n", + "bit strings: ['01010101', '10101010'] \n", + "\n", + "Circuit #3\n", + "cost: -6.001644 \n", + "bit strings: ['01010101', '10101010'] \n", + "\n", + "Circuit #4\n", + "cost: -6.011491 \n", + "bit strings: ['01010101', '10101010'] \n", + "\n", + "Circuit #5\n", + "cost: -5.1607475 \n", + "bit strings: ['01010101', '10101010'] \n", + "\n" + ] + } ], - "metadata": {} + "source": [ + "# print all results\n", + "for num_circuit in range(ncircuits):\n", + " c = QAOAansatz(params=params[num_circuit], g=example_graph, return_circuit=True)\n", + " loss = QAOAansatz(params=params[num_circuit], g=example_graph)\n", + "\n", + " # find the states with max probabilities\n", + " probs = K.numpy(c.probability()).round(decimals=4)\n", + " index = np.where(probs == max(probs))[0]\n", + " states = []\n", + " for i in index:\n", + " states.append(f\"{bin(i)[2:]:0>{c._nqubits}}\")\n", + " print(\"Circuit #%d\" % num_circuit)\n", + " print(\"cost:\", K.numpy(loss), \"\\nbit strings:\", states, \"\\n\")" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "c27fb3f6", + "metadata": {}, + "source": [ + "Based on the results, quantum states with the highest probabilities are $\\ket{01010101}$ and $\\ket{10101010}$. This outcome aligns with our intuition, as swapping the labels of the groups would have an impact on the final result.\n", + "\n", + "The network plot corresponding to $\\ket{01010101}$ is:" + ] }, { "cell_type": "code", - "execution_count": 4, + "execution_count": 109, + "id": "fb183e97", + "metadata": {}, + "outputs": [ + { + "data": { + "image/png": "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qlatysqVK3P1mhs2bGD48OGMHDmSL774IlevLYTIeUOGDEGv1zN48GCMRiM//fST7CsirILd/iutVq0aV69e5eHDh7i5ueX49fbu3Uvfvn3p0aMH8+bNk0FGQtio/v37o9fr6d+/P0ajkV9++QW9Xq92WUJkyG47tZ6eUZDTTpw4QdeuXfHx8WHJkiXSdCiEjevVqxerV69m/fr19O7dm4SEBLVLEiJDdhsGqlSpApDjgwjPnTtH+/btqVGjBmvXrsXJySlHryeEsAzdunVj/fr1bN++nZ49exIfH692SUKky27DgJubG15eXjk6iPDKlSv4+vpSvHhxtmzZgqura45dSwhheTp27MimTZvYs2cP3bp149GjR2qXJESa7DYMQM7uUXDr1i3atm2Ls7MzO3bsIH829lIXQli/tm3bsnXrVg4ePEjnzp2JjY1VuyQhnmPXYSCnphfev3+fdu3aERsby65du/D09DT7NYQQ1qNly5bs2LGD48eP06FDB2JiYtQuSYhU7DoMVK1alStXrpg1qT969IjOnTsTFhbGrl27KFu2rNnOLYSwXk2bNmXXrl388ccftGvXjgcPHqhdkhAp7DoMVKtWDTDfjAKDwUCvXr04ffo0W7duTTm/EEIANG7cmD179nDhwgXatGlDVFSU2iUJAdh5GDDnjIKkpCSGDBnCnj17WL9+vWp7HgghLFv9+vUJCgri8uXLtGrVijt37qhdkhD2HQbc3d0pXbr0S4cBRVEYP348q1atYvny5bRp08ZMFQohbFHt2rXZt28fN27coGXLlty+fVvtkoSds+swAOYZRPjBBx8wf/58Fi5cSI8ePcxUmRDCllWvXp19+/Zx9+5dWrRoQUREhNolCTtm92HgZacXfvXVV3z++ed8++23DBs2zIyVCSFsnbe3N8HBwcTExODj40N4eLjaJQk7ZfdhoFq1aly+fDlbi4EsXLiQd955hw8//JDJkyfnQHVCCFtXsWJFgoODMRgM+Pj4cPXqVbVLEnbI7sOAt7c3SsGC/PjXX2y4c4etd+9yPjaWJEXJ8H2rV69m9OjRjBs3jmnTpuVStUIIW1SuXDmCg4MBaN68OaGhoSpXJOyNBsj4rodpoF10dHQulJN7TsbEMPv6dTZERnIvKem51/NotTTLm5dxJUrQqUABHLT/5qadO3fSuXNnevfuzZIlS9Bq7T5TCSHMIDw8nFatWvHo0SP27t1LxYoV1S5J2AgPD48MF7uyuzAQGhfH8AsXCH7wAAeNhsQMWgB0QBJQ2smJRZUr06ZAAQ4fPkybNm1o2bIl69atk61JhRBmFRERQevWrbl//z579uzB29tb7ZKEDZAw8JQfIiIY/88/JCpKhiHgWVogGejj5MSOLl2oVa0a27dvJ0+ePDlWqxDCft2+fZs2bdpw69Yt9uzZQ/Xq1dUuSVg5CQOPfRsWxlshIS93kuRkPM6dI2TQIArly2eWuoQQIi137tyhbdu2hIWFsXv3bmrXrq12ScKKZRYG7KKz+9fbt18+CABotcRUr86Ht269/LmEECIDhQoVYs+ePZQtW5ZWrVpx4sQJtUsSNszmWwZuGQxUPnqU6KSktD/oF1/Ajh3pn+DXX6Fw4eee3lGzJr4FCpitTiGESMuDBw/o0KEDZ8+eZceOHTRu3FjtkoQVsvtugsHnz7P81i2eny/w2NmzcONG6ucUBb77DooWhcWLn3uLFvB0dORy48boZSaBECKHxcTE0LFjR06fPs22bdto2rSp2iUJK5NZGHDIxVpy3c2EhIyDAEC1aqbH0/78E+LjIZ09BpKB6wYDG+/epWcarQZCCGFO7u7ubN++nS5dutCuXTs2b95My5Yt1S5L2BCb/lr7w82bmTd7pGX3btBo0g0DYJp2OPv69eyWJoQQL8TV1ZXNmzfTtGlTOnbsyM6dO9UuSdgQmw4DQVFRLx4GEhNh3z5Ta0GxYukelgQcfPAAY3LyS1QohBBZ5+LiwoYNG2jdujVdu3Zl69atapckbITNhgFFUTgeE/PiYeD4cYiOzrBV4AmjonAuG3saCCFEdjk7O7N27Vo6dOhA9+7d2bBhg9olCRtgs2HgQWIi0WksM5yp3bvBwQFatMjS4SFxcS9+DSGEeAmOjo78+uuvdO/enV69erF69Wq1SxJWzmbDgOEFVhhMERcHhw5BgwaQN2/WriPdBEIIFej1epYtW0afPn3o168fy5cvV7skYcVsdjZBnuxM+Tt4MMNZBGleR6d78esIIYQZODg48PPPP6PX6xk0aBAGg4GhQ4eqXZawQjYbBtwdHCii13PbaMz6m3bvhjx54JVXsvyWqi4u2ahOCCHMQ6fT8cMPP6DX6xk2bBhGo5GRI0eqXZawMjYbBgAaeXiw5e5dstSQf/8+nDwJrVqBs3OWzu+q1VJeNisSQqhMq9Uyf/58HB0deeONNzAYDIwbN07tsoQVsekw0LFAATbfvZu1g/fuhaSkLHcROAAdCxZEq9Fkv0AhhDATrVbLrFmzcHR0ZPz48RgMBvz9/dUuS1gJmw4DA4sW5a2QEB5lZZDf7t2QPz/Uq5elcycC40uUeLkChRDCjDQaDd9++y1OTk5MnjwZo9HIlClT1C5LWAGbDgPuDg5MLFGCr8LCMu8qmD07y+d10Gio4epK8yzOOBBCiNyi0Wj4/PPPcXR05J133iEhIYEPP/xQ7bKEhbPpMADwsZcXv0ZGci0+PuM9Cl7Qz1WqoJEuAiGEBdJoNEybNg29Xs9HH32EwWBg6tSp8jtLpMvmw4CLTscyb298Tp8mWVGyt1fBMz4uUoTqbm5mOJMQQuScjz76CCcnJ959912MRiPTp0+XQCDSZLOLDj2tcd68rKteHQeNhpddFcBlzRp+7dOHW7dumaU2IYTISe+88w7fffcdX3zxBW+99RZKdhZkEzbPLsIAmEb+B9euTUknpxf+0A6YFjH6vlIlTo4fz927d2nRogURERE5UaoQQpiVv78/s2bN4rvvvmPixIkky8qp4hl2EwYAmuTNy7mGDZlcsiTOWi0aIKMGM93j1zsULMj5hg0ZWbw4VapUITg4mJiYGHx8fAgPD8+d4oUQ4iWMHz+eBQsWMGfOHMaMGSOBQKSigcy70d3d3YmOjs6FcnLPg8REfr55k81373I8JoaoxMSU15y1Wmq7udEqXz5GenpSNo2FhUJCQmjVqhUODg7s3buX0qVL52b5QgiRLT/99BPDhg1j6NChLFy4EJ0sqW4XPDw8iImJSfd1uw0DT1MUhbtGI7HJyThqNBRxdESXhUE2V65coVWrViiKQlBQEGXLls2FaoUQ4uUsW7aMwYMH069fP3766SccHGx+LLndyywM2FU3QXo0Gg2FHB0p4+yMp5NTloIAgJeXF8HBweh0Onx8fLh06VIOVyqEEC9vwIABrFixgl9//ZUBAwZgfJE9XIRNkjDwkkqVKkVwcDB58uTBx8eHv//+W+2ShBAiU71792bVqlWsX7+ePn36YDAY1C5JqEjCgBmUKFGC4OBg8uXLR4sWLTh37pzaJQkhRKa6d+/OunXr2LZtGz169CA+Pl7tkoRKJAyYSbFixdi7dy+FCxemRYsW/Pnnn2qXJIQQmerUqRMbN25kz549dOvWjbi4OLVLEiqQMGBGRYoUISgoiBIlStCyZUtOnz6tdklCCJEpX19ftmzZwsGDB+ncuTOxsbFqlyRymYQBMytUqBB79uzBy8uLVq1acfLkSbVLEkKITLVq1Yrt27dz7NgxOnTokOHIc2F7JAzkgAIFCrB7924qVapE69atOXr0qNolCSFEppo1a8bOnTv5448/aNeuHQ8ePFC7JJFLJAzkkHz58rFz506qV69O27Zt+e2339QuSQghMtWkSRN2797N+fPnadu2LVFRUWqXJHKBhIEc5OHhwfbt26lTpw7t2rVj//79apckhBCZatCgAUFBQYSGhtK6dWvu3Lmjdkkih0kYyGFubm5s3bqVRo0a0aFDB4KCgtQuSQghMlWnTh327t1LeHg4rVq14vbt22qXJHKQhIFc4OrqyubNm2natCmdOnVi586dapckhBCZqlGjBvv27SMyMlJ2arVxEgZySZ48ediwYQOtW7ema9eubN26Ve2ShBAiU1WrViU4OJjo6GhatGjB9evX1S5J5AAJA7nI2dmZNWvW0L59e7p3787GjRvVLkkIITJVqVIlgoODiY+Pp3nz5ly9elXtkoSZSRjIZU5OTqxatYquXbvSs2dP1qxZo3ZJQgiRqfLly7N//34URcHHx4fQ0FC1SxJmJGFABXq9nhUrVtCrVy/69u3LypUr1S5JCCEyVaZMGfbv34+joyM+Pj78888/apckzETCgEocHBxYsmQJ/fv3Z8CAASxdulTtkoQQIlMlS5Zk3759uLm54ePjw4ULF9QuSZiBhAEVOTg4sHjxYoYOHcqQIUNYvHix2iUJIUSmihcvzr59+yhYsCA+Pj789ddfapckXpKEAZXpdDoWLlzIG2+8wfDhw/n+++/VLkkIITJVtGhR9u7dS/HixWnRooVszGblJAxYAK1Wy9y5cxk3bhyjRo1izpw5apckhBCZko3ZbIeEAQuh1WoJDAzE39+f8ePHExAQoHZJQgiRqScbs1WuXJnWrVtz5MgRtUsS2eCgdgHiXxqNhm+//Ra9Xo+/vz9Go5G3335b7bKEECJD+fLlY8eOHXTq1AlfX1+2bt1K06ZN1S5LvAAJAxZGo9Hw5Zdf4ujoyJQpUzAajbz33ntqlyWEEBny8PBg27ZtdO3alfbt27N582ZatGihdlkiiyQMWCCNRsP//d//odfref/99zEajXz88cdqlyWEEBlyc3Nj8+bNdO/enY4dO7Jhwwbatm2rdlkiCyQMWCiNRsMnn3yCXq/ngw8+wGg0Mm3aNDQajdqlCSFEulxcXNi4cSM9e/akS5curFu3jg4dOqhdlsiEhAEL9/7776PX63nnnXcwGAx88cUXEgiEEBbN2dmZtWvX0rdvX7p3756yBLuwXBIGrMCUKVPQ6/VMnjwZo9HIN998I4FACGHRnuzDMmDAAHr27MmKFSvo2bOn2mWJdEgYsBL+/v7o9XomTJiA0Whk5syZEgiEEBZNr9ezfPlyhgwZQt++fVm6dCn9+vVTuyyRBgkDVmT8+PHo9XpGjx6N0Whkzpw5aLWyVIQQwnI92YdFr9czcOBADAYDQ4YMUbss8QwJA1Zm1KhR6PV6RowYgdFoZMGCBeh0OrXLEkKIdOl0On788UccHR15/fXXMRqNjBgxQu2yxFMkDFihYcOGodfrGTp0KEajkR9//FECgRDComm1WubPn49er2fkyJEYDAbGjh2rdlniMQkDVmrQoEE4ODgwaNAgjEYjS5YswcFB/jqFEJZLq9Uye/ZsHB0dGTduHAaDAT8/P7XLEkgYsGr9+vVDr9fTr18/EhMTWbZsGXq9Xu2yhBAiXRqNhu+++w5HR0dZdt2CSBiwcj179mT16tX07t2bPn36sHLlShwdHdUuSwgh0vVk2XUnJyemTJlCQkICH3zwgdpl2TUJAzagW7durFu3jh49eqSEAycnJ7XLEkKIdD1Zdt3R0ZEPP/wQg8HAp59+KlOmVSJhwEZ06tSJjRs30r17d7p3787atWvJkyeP2mUJIUSGPvroI/R6Pe+99x4Gg4Hp06dLIFCBhAEb0q5dOzZv3kyXLl3o2rUrGzZswMXFRe2yhBAiQ//5z39wcnLizTffxGAw8PXXX0sgyGUSBmxM69at2bZtG506daJTp05s2rQJNzc3tcsSQogMTZ48GUdHRyZMmIDBYJBVVnOZLF9ng3x8fNixYwcnT56kQ4cOxMTEqF2SEEJkavz48cyfP59Zs2YxZswYkpOT1S7JbkgYsFGvvvoqO3fu5MyZM/j6+vLgwQO1SxJCiEyNGjWKH374ge+//54RI0aQlJSkdkl2QcKADWvcuDF79uzhwoULtGnThqioKLVLEkKITA0bNowlS5bw888/M3ToUBITE9UuyeZJGLBx9evXJygoiMuXL9O6dWvu3r2rdklCCJGpgQMHsnz5clasWMHAgQMxGo1ql2TTJAzYgTp16hAUFER4eDgtW7YkMjJS7ZKEECJTffr0YdWqVaxbt46+fftiMBjULslmSRiwEzVr1mTfvn3cvn2bFi1acPPmTbVLEkKITL322musXbuWLVu20LNnT+Lj49UuySZJGLAjVatWJTg4mPv379OiRQtu3LihdklCCJGpzp07s3HjRnbv3k337t2Ji4tTuySbI2HAzlSuXJng4GBiY2Px8fEhLCxM7ZKEECJT7dq1Y8uWLRw4cIDOnTsTGxurdkk2RcKAHapQoQL79+/HaDTi4+PD1atX1S5JCCEy1apVK7Zt28bRo0fp2LGjrKFiRhIG7FTZsmUJDg5Go9Hg4+NDaGio2iUJIUSmmjdvzs6dOzl9+jTt2rWTNVTMRMKAHStTpgzBwcE4Ojri4+PDP//8o3ZJQgiRqVdeeYVdu3Zx/vx5fH19ZQ0VM5AwYOdKlizJvn37cHNzw8fHhwsXLqhdkhBCZKphw4bs2bOHS5cuyRoqZiBhQFC8eHH27dtHgQIFaNGiBWfPnlW7JCGEyFTdunXZu3dvyhoqt2/fVrskq6UBlMwOcnd3Jzo6OhfKEWqKjIykTZs2REREsHv3bmrWrKl2SUIIkalz587RqlUrChYsyO7du/H09Ez/YKMRzp6FU6fgzh3Tc4UKQZ06UK0a6PW5U3Qu8/DwyHDApYQBkcrdu3dp27YtV69eZffu3dSpU0ftkoQQIlN///03rVq1ws3NjaCgIEqUKJH6gBMnYPZsWL4cEhJMz+l0pp9PNkNycoL+/WH8eKhfP/eKzwWZhQHpJhCpFCxYkD179lC+fHlatWrF8ePH1S5JCCEyVblyZfbv3098fDw+Pj5cu3bN9EJkJPTpAw0awNKl/wYBMIWAp3dFTEgwHdOggek9drR0u4QB8Zz8+fOza9cuvL29adOmDUeOHFG7JCGEyFT58uUJDg4mKSkJHx8fIlasgMqVYe1a0wFZ2f3wyTFr15reGxyccwVbEAkDIk158+Zlx44d1KxZE19fXw4ePKh2SUIIkSkvLy/2799PM4OBgv37ozx4kPrbf1YlJcGDB+DrC0FB5i/UwkgYEOlyd3dn27Zt1KtXj/bt2xNsJwlZCGHdSiUmsvjePXSAJjk5+ydKTja1FHTpApcvm60+SyRhQGTIzc2NLVu20KRJEzp06MCePXvULkkIIdKXnAxDh6JNTESXxsvHgQlANcAVKA30AS5mdD6DAYYNAyXT8fZWS8KAyJSLiwsbN27Ex8eHzp07s2PHDrVLEkKItK1aBQcOpDs+4EtgDdAaCABGAfuBusBf6Z0zMdE0duDXX81fr4WQqYUiyxISEujVqxc7d+5kzZo1dO7cWe2ShBAitSZN4Ngx0zf6NBwC6gOOTz33D1AD6AUsTe+8Oh00bAiHDpmx2NwjUwuF2Tg5ObFmzRo6depEjx49WL9+vdolCSHEvy5cgCNH0g0CAK+QOggAVMTUbXA+o3MnJcHhw6Zr2CAJA+KFODo6snLlSrp3707v3r1ZvXq12iUJIYTJb79l620KcAsolJWDrbRlIDMSBsQL0+v1LFu2jD59+tCvXz9WrFihdklCCGFaZTAbywn/AlwH+mZ2oF4PJ09mozDL56B2AcI6OTg48PPPP+Pg4MDAgQMxGo0MHjxY7bKEEPYsPNy098ALuACMB5oAQzM72GiEsLDs1WbhJAyIbNPpdPzwww/o9XqGDh2K0Whk+PDhapclhLBXLxgEbgKdgLzAakhzKuLLXsNaSBgQL0Wn07FgwQL0ej0jRowgMTGRUaNGqV2WEMIeubuDVpvhAMInHgAdgPvAAaB4Vs6v1YKHx8tUaLEkDIiXptVqmTNnDnq9ntGjR2M0Ghk/frzaZQkh7E3NmrBuXaaHxQNdMC00tBuomtXzazRQo0a2y7NkEgaEWWg0GgICAtDr9UyYMAGDwcDkyZPVLksIYU/q1ct0H4IkTAMFDwMbMI0VyLKkJNM1bJCEAWE2Go2Gr7/+Gr1ez5tvvonRaOSdd95RuywhhL1o0cLUVZDB4jpvARsxtQzc4/lFhgZldH53d/DxeckiLZOEAWFWGo2Gzz//HEdHR959912MRiMffPCB2mUJIeyBiwuMGAGzZqW7HPHpxz83PX48K90woNPByJGma9ggCQPC7DQaDdOmTUOv1/Phhx9iNBr55JNP0Gg0apcmhLB1/v4wb166YWBfds+r14OfX3bfbfEkDIgc89FHH6HX63nvvfcwGo189tlnEgiEEDmrTBn45hsw9yDmb781ndtGSRgQOeo///kPer2et99+G4PBwP/+9z8JBEKInDVmDMrOnSgbN6J92W2HtVro2hVGjzZPbRZKwoDIcW+99RZ6vR4/Pz+MRiPfffedBAIhRM7Ravm6Xj2qbthAR0zb82aLRgMdOsDy5aZQYMMkDIhcMWnSJPR6PePGjcNoNBIYGIjWxv/jEkKo4/vvv+edjz9m2kcf0Umng//7P9MLmUw7TKF7vBbhxx/De+9la78DayNhQOSasWPHotfrGTVqFEajkXnz5kkgEEKY1erVqxkzZgwTJkzgw6lTTd/uu3WD99+H7dtN3/AV5blVCpM1GtPvo+Rk8PWFzz+HWrVU+hS5T8KAyFUjR45Er9czbNgwjEYjCxcuRKfL0orgQgiRod27dzNw4ED69u1LQEDAv92RtWvD1q1w+bKpyf/YMTh6FO7fB+BuUhKhRYrQYNw4GDAAvLzU+giq0WDayjlD7u7uREdH50I5wl4sW7aMwYMHM2DAAH788UccHCSXCiGy79ixY7Rq1YrmzZuzfv16HB0ds/zeUaNGcfz4cU6dOpWDFarLw8ODmAwWY5LfwEIVAwYMwMHBgQEDBpCYmMjPP/+M3g765YQQ5nf+/Hk6duxIzZo1WbVq1QsFAQBvb2+WLl1KcnKy3XZdShgQqunTpw8ODg707dsXo9HIsmXLXvg/YiGEfbt27Rq+vr54enqyefNmXF1dX/gc3t7exMXFce3aNbzssIsAwD4jkLAYPXr0YO3atWzatIk+ffqQkJCgdklCCCsRGRmJr68vDg4O7NixgwIFCmTrPFWqVAFMLQz2SsKAUF2XLl1Yv34927dvp0ePHsTHx6tdkhDCwsXExNCxY0eioqLYtWsXxYsXz/a5SpcujYuLCxcuXDBjhdZFwoCwCB06dGDjxo0EBQXRrVs34uLi1C5JCGGhEhIS6N69OxcvXmTHjh1UqFDhpc6n1WqpXLmytAwIYQl8fX3ZsmULBw8epHPnzsTGxqpdkhDCwiQlJTFgwAAOHTrEpk2bqF27tlnOW6VKFQkDQliKVq1asW3bNo4ePUrHjh0znAojhLAviqIwZswYNmzYwK+//krz5s3Ndm5vb28JA0JYkubNm7Nz505OnTpF+/btZY0LIQQA77//PgsXLuSHH36gS5cuZj23t7c3d+/e5c6dO2Y9r7WQMCAs0iuvvMLu3bs5e/Ysvr6+3H+8UpgQwj59/fXXfPHFF3z33XcMGTLE7Oe39xkFEgaExWrYsCF79uzh4sWLtGnThnv37qldkhBCBYsXL2bKlCm8//77+Pv758g1KlasiFarlTAghCWqV68ee/fu5cqVK7Ru3dpum/CEsFcbNmxg5MiRjBo1is8++yzHruPk5ET58uXtdnqhhAFh8WrVqsW+ffu4ceMGrVq14vbt22qXJITIBcHBwfTt25fXXnuNOXPm/LvxUA6x50GEEgaEVahevTr79u0jMjKSFi1aEBERoXZJQogcdOrUKbp06UKzZs1YunRpruxuas/TCyUMCKvh7e1NcHAw0dHRtGjRguvXr6tdkhAiB1y8eJF27drh7e3NunXrcHJyypXrent7c/XqVR49epQr17MkEgaEValUqRLBwcHExcXh4+PDtWvX1C5JCGFG169fx9fXl0KFCrFlyxbc3Nxy7dre3t4A/P3337l2TUshYUBYnfLlyxMcHExSUhI+Pj5cuXJF7ZKEEGZw7949fH19URSFnTt3UqhQoVy9vj1PL5QwIKxS2bJlCQ4ORqfT4ePjQ0hIiNolCSFewsOHD+nYsSO3b99m586dlCxZMtdryJs3L56enhIGhLAmpUuXJjg4GGdnZ3x8fLh48aLaJQkhssFgMNCzZ0/Onj3L9u3bqVy5smq1eHt72+X0QgkDwqqVKFGCffv24eHhQYsWLewy0QthzZKSkhgyZAj79u1j48aN1KtXT9V67HV6oYQBYfU8PT3Zu3cvBQsWpEWLFvz1119qlySEyAJFUZg4cSKrVq1i+fLltGzZUu2SqFKlChcvXiQxMVHtUnKVhAFhE4oWLcrevXvx9PSkZcuW/PHHH2qXJITIxKeffsrcuXNZsGABPXr0ULscwNQyYDQauXz5stql5CoJA8JmFCpUiKCgIMqUKUOrVq34/fff1S5JCJGOmTNnMm3aNL788ktGjBihdjkpnkwvtLeuAgkDwqYUKFCA3bt3U6FCBVq3bs2xY8fULkkI8YxffvkFPz8/pkyZwjvvvKN2Oal4enri7u4uYUAIa5cvXz527txJ1apVadu2LYcPH1a7JCHEY1u2bOH1119n+PDhfPnll2qX8xyNRmOXgwglDAiblDdvXrZv306tWrXw9fXlwIEDapckhN07ePAgvXr1onPnzsyfPz/HNx7KLnucXuigdgFC5BR3d3e2bdtGly5daN++PZs3b05/tLKiwO+/w7FjcOoU3Lljeq5QIahTBxo2hHr1wEJ/eQlh6c6cOUPnzp1p3Lgxy5cvx8HBcm8/T/ZEUBTFYgOLuWkAJbOD3N3diY6OzoVyhDC/R48e0b17dw4ePMiGDRto27btvy/GxcGiRTBzJvzzj+lmr9PBk2lFDg6QlGQKBhUqwMSJMHIkuLio82GEsEIhISE0bdqU4sWLs3fvXjw8PNQuKUMbNmyge/fuXL9+neLFi6tdjll4eHgQExOT7uvSTSBsnouLCxs3bqRly5Z06dKFbdu2mV44fBhq1IBJk+DSJdNzivJvEADTn5XHeTkkBPz9oXp1OHgwVz+DENYqIiICX19fPDw82LZtm8UHAfh3RoE9dRVIGBB2wdnZmbVr19KuXTu6d+/On6NHw6uvwpUrppu9kmkD2b/HXbsGzZvDd9/leN1CWLP79+/Tvn17EhIS2LlzJ0WKFFG7pCwpV64cer3ergYRWm6njRBm5uTkxKpVq/i5YUNqLFhgejIp6cVP9OQ9b75pCgdvvmm+IoWwEY8ePaJLly6Eh4dz4MABypQpo3ZJWebg4EDFihXtKgxIy4CwK47HjzPizBnznfCtt2D/fvOdTwgbYDQa6dOnD6dOnWLr1q1UrVpV7ZJemL1NL5QwIOxHXBwMHoxGm/4/+wTgXaA4kAdoBOzK6Jw6HQweDLGx5qxUCKuVnJzM8OHD2blzJ2vXrqVRo0Zql5Qt9ja9UMKAsB/ff28aI5BB18DrwLfAQCAA0AEdgXSHCyYlQXg4zJ1r1lKFsEaKojB58mR++eUXli5diq+vr9olZZu3tzc3btzgwYMHapeSKyQMCPugKKbpgxk4BqwAPge+AkYBQUAZIMMFU5OTYdYs008h7Nh///tfZs6cyZw5c+jTp4/a5byUKlWqAPYzo0DCgLAPx4+bpgZmMGtgNaaWgFFPPecMjAAOA2EZnf/qVdNURSHs1Ny5c/noo4/47LPPGDNmjNrlvLTKlSsDEgaEsC3HjmW6euApoBLw7Czoho9/ns7ozVqt6RpC2KGVK1cyfvx4/P39ef/999UuxyxcXV0pU6aM3QwilDAg7MPvv5sG+2UgAvBM4/knz93I6M0ajekaQtiZHTt2MHjwYAYOHMg333xjU8v3VqlSRcKAEDblzp3UKwumIQ5wSuN556deT1dSEkRGZq82IazUkSNH6NGjB76+vvzwww9oM5ipY43saXqhbf3NCZGeLKwwmAfT1MJnxT/1eoaXkAGEwo6cPXuWTp06UbduXX799Vf0er3aJZmdt7c3oaGhJCSk9ZvBtsgKhMI+FCxo2nQog9YBT+B6Gs9HPP6Z0XYlicDqPXuYVrUq5cuXf+7h5eWFk1Na7Q5CWJ8rV67g6+tLyZIl2bRpEy42unFXlSpVSEpK4tKlS1SrVk3tcnKUhAFhH+rUgSVLMjykNrAXiCb1IMKjT72eHp1GQ/HOnWldujShoaFs376dy5cvYzAYANBoNJQqVYpy5cqlGRby5cuXzQ8mRO66ffs2vr6+5MmThx07dtj0v90nGxadP39ewoAQNqFhw0zXAegFfA0sAN5+/FwC8COmlQhLZfBejaLQ/M03ae7jk/JcUlIS169fJyQkJNXj1KlTrF69OtViJgUKFEgzJJQvXx5PT0+b64sV1ik6Opr27dsTExPDb7/9RrFixdQuKUcVLlyYggUL2sX0QgkDwj40agSlS5t2HEzvEKA38B5wG6gA/ARcARZlcGoFuOvkxLHoaNonJ6fcuHU6HaVLl6Z06dK0bNky9XsUhXv37qUEhNDQ0JQ/HzhwgOvX/+2wcHZ2pmzZstL9IFQVHx9P165duXz5MsHBwZQrV07tknKFvQwilDAg7INWCxMnwrvvZthC8DPwEbAEiAJqApuB5hmdW6NhZaFCTOjalUqVKjFp0iSGDh2Km5tbBm/RULBgQQoWLEjDhg2fez0uLo7Lly8/16qwbds2Ll++jNFoTDlPqVKl0m1VyJs3b+b/3wiRicTERPr168exY8fYtWsXNWvWVLukXFOlShVOnjypdhk5ToPpi02G3N3diY6OzoVyhMhBMTHg7Q0REeZbOlirhaJFUc6f5/DZs8yYMYO1a9fi5ubGyJEjmTBhAl5eXua51mNJSUmEh4en2aoQEhKSqvuhYMGC6Y5TkO4HkRWKojB8+HCWLl3Khg0b6Nixo9ol5apvv/2Wjz76iJiYGKv+78XDw4OYmJh0X5cwIOxLUBC0bm3ec+7YAU9tyHLt2jVmz57NggULiI6Opnv37vj7+9O0adMcX5Dl2e6HZx83bvy7dJKzs3O6QcHLywtHR8ccrVVYPkVRmDJlCt988w1Lly5l4MCBapeU67Zt20bHjh25cuUKZcqUUbucbJMwIMSzpk+HDz4wz7k+/RQ++STNl2JjY1myZAkBAQFcuHCBunXr4ufnR9++fVXr53/06FGq7oenWxWe7n7QarUZzn6Q7gf78OWXX/Kf//yHgIAAJk2apHY5qrh8+TLlypVj27ZttG/fXu1ysk3CgBDPUhRTIPjwQ1Mz/4t2GTx5z6efwscfZ7rnQXJyMrt27WLGjBls376dokWLMnbsWMaMGUPRokWz/znM7Nnuh2cfT/8OKFiwYIazH2xpSVp7tXDhQt544w0+/vhjpk6dqnY5qklOTsbNzY3//ve/TJ48We1ysk3CgBDp2bULhg6FW7eyHgi0WihSBH78EbLxLeHChQvMnDmTn376icTERPr374+fnx916tR54XPlJkVRuHv3bqpw8HSrwtPdD3ny5KFcuXJptipI94N1WLt2Lb1792bMmDHMmjXL7sNdnTp1aNiwIfPnz1e7lGyTMCBERqKjYdYsmD0bbtz4dzOjpCTTz6f/t6cnjBtnmpXwks3kUVFRLFy4kMDAQMLCwmjevDn+/v507doVXSYbKlmiZ7sfnn5cuXLlue6H9FoVPDye3TNS5LagoCA6dOhAjx49+OWXX6x60Jy5DBgwgPDwcPbv3692KdkmYUCIrEhKggMHTNsQ//67adOh5GRTK0DdutCgATRvblrS2IwSExNZv349M2bM4LfffsPLy4uJEycyYsQIm+mXT0pKIiwsLM2ZD9L9YFlOnDhBy5YtefXVV9m4caO04jw2bdo0AgMDibTizcgkDAhhJU6cOEFAQAArV67EycmJ119/nUmTJlGxYkW1S8sxaXU/PP2IiIhIOfZJ90NaQaFMmTJy43pJFy5coFmzZlSoUIHdu3fj6uqqdkkWY9WqVfTp04fIyEgKFSqkdjnZImFACCsTERHB3LlzmTt3Lnfv3qVTp074+fnRunVru/tm/OjRo1StCU//WbofzCcsLIxXX30VDw8P9u/fT4ECBdQuyaL89ddf1KhRgwMHDtC0aVO1y8kWCQNCWKn4+HiWL1/OjBkzOHPmDNWqVcPPz49BgwaRJ09mGyrbvqe7H9J6PP2Lr1ChQukGhWLFitldyHranTt3aNasGfHx8Rw8eJASJUqoXZLFSUhIwMXFhXnz5vHGG2+oXU62SBgQwsopisK+ffsICAhg48aNFChQgFGjRjF+/Hj5xZ0ORVG4c+dOurMfpPvBJCYmhtatW3P16lUOHjxo011SL6tixYp06dKFb7/9Vu1SskXCgBA2JCQkhFmzZrFo0SLi4uLo3bs3fn5+NGrUSO3SrMqz3Q/Pzn5ITEwETN0PpUuXTrdVwd3dXeVPkn0JCQl06tSJ48ePs2/fPouf3qq2rl27kpiYyNatW9UuJVskDAhhg6Kjo1m8eDEzZ84kJCSExo0b4+fnR8+ePdHr9WqXZ9USExNTdT88Gxqe/oVauHDhdFsVLLn7ISkpiX79+rFp0yZ27NiBz1Nbb4u0vfvuu/z6669cvnxZ7VKyRcKAEDYsKSmJrVu3MmPGDIKCgihRogTjx49n1KhRFCxYUO3ybE5a3Q9PP27evJlyrIuLS4bdD2qFNkVRGDNmDIsWLWLt2rV07dpVlTqszY8//siIESN4+PAhLi4uapfzwiQMCGEnzpw5w8yZM1m6dClarZbBgwczadIkqlWrpnZpdiM2NjalJeHZFgVL6X744IMPmD59OosXL2bo0KE5dh1bc+TIEZo0acKpU6eoXbu22uW8MAkDQtiZyMhIFixYwOzZs4mIiKBt27b4+fnRoUMHWU1ORc92Pzz7ePjwYcqxhQsXTjcoFC1aNNvdD99++y1vvfUW33zzDW+++aa5PppduH//Pvnz52fZsmX0799f7XJemIQBIeyUwWBg9erVfPfdd5w4cYKKFSsyadIkXn/9ddzc3NQuTzxFURQiIyPTHadgju6Hn376iddff5333nuP6dOn59ZHsymenp688cYbTJs2Te1SXpiEASHsnKIoHD58mICAANasWYObmxsjR45kwoQJeHl5qV2eyIKnux+efVy9ejWl+0Gn06XZ/RAWFsZbb73F8OHDmT9/vsUObLRUiqJw6uFD+k+dSqKXF3VbtkQDFNHrqePuTkN3d6q7ulr0/68SBoQQKa5du8acOXNYsGABDx48oHv37vj5+dGsWTOL/kUm0peYmMi1a9fSbVXIje4HW/UoKYmFEREEXr/Opbg40/bnycloHm8m5qDRYFRMt9Dqrq5MKlGCocWK4WiB3XESBoQQz4mNjWXp0qXMmDGDCxcuUKdOHfz9/enbty9OTk5qlyfM5Pfff6dFixZUrlyZcePGpQoNISEh3Lp1K+VYV1fXdLsfSpcubXdTVg/cv8/g8+e5lpAAZH6j1ALJQFUXF5Z6e1PHwtagkDAghEhXcnIyu3btIiAggG3btlG0aFHGjh3LmDFjKFq0qNrliZfwzz//0LRpU8qUKcOePXvSnKHw8OHDDGc/JD3eylun01GmTJl0w4KtjUH56to13g0NRQskveB7n2xAvqByZYZ7epq5suyTMCCEyJILFy4QGBjI4sWLSUxMpH///vj5+cnKdFboxo0bvPrqqzg5OXHw4MFs7bT3bPfDs4/Y2NiUY4sUKZJu90ORIkWsqvvhf4+DgDksrFyZERYSCCQMCCFeSFRUFAsXLmTWrFlcu3aN5s2b4+/vT9euXdHpdJmfQKjq3r17+Pj48ODBA3777TdKlSpl9msoisLt27fTbFGw5u6HPVFRtPnjD7OdTwscr1ePuhbQZSBhQAiRLYmJiaxfv56AgAAOHjyIl5cXEydOZPjw4eTLl0/t8kQaYmNjadu2LRcvXuTgwYNUqVJFlTqe7n5Ia/bDs90PaQWFcuXK5Wr3Q0xiIt7HjhFhMJCc3kFxcbBiBZw/DxcuQEwMvPsutG+f5uE6oLKLC6fq11d9UKGEASHESzt58iQBAQGsWLECR0dHhg0bxqRJk2SXOwtiMBjo1q0bBw8eJCgoiAYNGqhdUpqMRmNK90NagUGt7ofpV6/y0eXL6QcBgJs3oX9/KFoUPD3h9OkMw8AT31eqxMjixc1Wa3ZIGBBCmE1ERARz585l3rx5REZG0qlTJ/z9/WndurVV9QvbmuTkZAYOHMjatWvZunUrrVu3VrukbHm6+yGtx+3bt1OOdXNzy7D7wcHBIcvXTVIUSh0+TITBkPGBBgM8fAgFCsDff8OYMZmGAS1QzdWVP+rXV/W/EQkDQgizi4+PZ/ny5QQEBPDHH39QrVo1/Pz8GDhwoFVu4mLNFEVh4sSJzJ07l1WrVtGjRw+1S8oxMTExqVoTnv5zVrsfypcvj6ura6rzBkVF0fpFxwpkMQw88Wf9+lRXcdaFhAEhRI5RFIXg4GACAgLYsGED+fPnZ/To0YwbN46SJUuqXZ5d+PTTT5k6dSrff/89I0eOVLsc1Tzd/ZDW49GjRynHFi1aNFU4OFu7Nmvy5s24i+BZLxgGFqk81VDCgBAiV4SGhhIYGMiiRYuIi4ujV69e+Pn50bhxY7VLs1mBgYFMmjSJL774gnfffVftciyWoijcunUr3VUab48dC82awYvMlnmBMKDXaHjD05PZlSq95CfJPgkDQohcFR0dzeLFi5k5cyYhISE0atQIf39/evbsaVHTyKzdL7/8wqBBg3jrrbf46quvZMzGS2hy/DhHnhq4mCUvEAa0QK/ChVmp4nbimYUBy1tAWQhh1Tw8PJg0aRJ///03GzduxNXVlf79+1O2bFk+//xz7t69q3aJVm/btm28/vrrvP766xIEzCCn189Q4MW6IFQgYUAIkSN0Oh1dunRhz549nDlzhvbt2zN16lRKlizJqFGjOHv2rNolWqVDhw7Rs2dPOnbsyPfffy9BwAwKOzrm6M3QQaOh4AvMblCDhAEhRI6rUaMGCxcuJCwsjA8//JDNmzdTvXp12rZty5YtW0hOtvTvTZbhzz//pFOnTjRs2JAVK1a80PQ58a/ExER+//13Zs2axcCBA9m3YAHJj7eBzpHrKYrFbVz0LAkDQohcU7hwYT744AOuXLnCL7/8woMHD+jcuTNVqlRh1qxZqbbbFamFhobSrl07ypYty4YNG8iTJ4/aJVmNO3fusHnzZt5//31atmxJ3rx5qVevHm+++SYhISG09PSEHAxWCtDAwsOADCAUQqhGURSOHDnCjBkzWLNmDW5ubowYMYIJEyZQtmxZtcuzGDdv3qRp06ZotVoOHjxIkSJF1C7JYiUlJXHu3DkOHz7MoUOHOHz4MBcvXgRMUwpfeeUVXnnlFZo0aUK9evVwdnbGkJyM56FD3MtK68C6daaFh+7cgY0bTbMQnqzE+dpr8MxaAhqgfJ48XGzYUBYdEkKIzISFhTF79mwWLFjAgwcP6NatG/7+/jRr1syu+8Xv379PixYtiIyM5LfffsPLy0vtkizK/fv3OXr0aMrN/+jRo0RHR6PT6ahVqxZNmjRJufl7eXml+2/pw9BQvrh2LfMti/v1g6c2Ykpl+XIoVizVUxpgRoUKTFJ53Q0JA0IIqxIbG8vSpUsJCAjg/Pnz1K5dG39/f/r164eTk5Pa5eWqR48e0a5dO86ePcuBAweopuLUNEugKAoXL15MufEfOnSIc+fOoSgKBQoUSLnpv/LKKzRo0OC5lQYzctdopNLRo0QlJmZ+U8wiHVDSyYmzDRviqvKOnxIGhBBWSVEUdu3axYwZM9i2bRtFihRh7NixjBkzhmLPfPuyRUajkR49ehAUFMSePXvscvGmhw8fcvz48ZSb/5EjR7h79y4ajYZq1aqluvlXrFjxpVuQ1kVG0sOMs1w0QHDt2jSzgF0+JQwIIaze33//zcyZM1m8eDGJiYn069cPPz8/6tatq3ZpOSI5OZmhQ4eycuVKNm3aRLt27dQuKccpisKVK1dS+vkPHTrEmTNnSEpKwsPDg8aNG6fc+Bs1akTevHlzpI63L13im/Bws5zry3LleKd0abOc62VJGBBC2IyoqCgWLVrErFmzuHr1Ks2aNcPf359u3brl+MIxuUVRFN58800CAgJYtmwZ/fr1U7ukHBEfH8/JkydTNfnfetwXX6lSpVTf+r29vXPt71dRFN4JDeXrsLCs3SCfocW0wNDnZcvynzJlzF9gNkkYEELYnMTERDZs2MCMGTM4ePAgZcqUYeLEiYwYMYJ8FtAk+zKmT5/OBx98wJw5cxg7dqza5ZhNeHg4hw8fTrn5//777xiNRlxcXGjYsGHKzb9x48YUKlRI7XJZGxnJG3//zYPExMwHFT6mBTwdHfnJ25vW+fPnZHkvTMKAEMKmnTx5koCAAFasWIGjoyOvv/46kyZNopKKm8Jk1/z58xkzZgzTpk3jo48+UrucbDMajZw+fTpVk39YWBgAXl5eqb7116xZ02IXT7pjMDAjPJx5N25wNzERHamXFtZiuokmYQoB40uUYFKJErhb4OeRMCCEsAsRERHMmzePuXPnEhkZSceOHfH396dNmzZWMTVx1apV9O3bl4kTJzJjxgyrqPmJ27dvp5rXf/z4ceLj43FycqJevXopN/8mTZrgqeI2vtllSE4mKCqK4zEx/P7wIZEGAxqgqKMj9dzdaejhQYt8+dBZ8N+ZhAEhhF2Jj49nxYoVzJgxgz/++IOqVavi5+fHoEGDcHFxUbu8NO3atYtOnTrRp08ffv75Z7Ray10cNikpib/++iuln//w4cOEhIQAULx48VSL+tSpU8fupoNaKgkDQgi7pCgK+/fvZ8aMGWzYsIH8+fMzatQoxo8fT0kzLwCjKAonY2LY/+ABJ2NiuBQXh0FRcNfpqOnqSj13d9oXKIBnGjfGo0eP0rp1a1q0aMG6dessbpvnqKgojhw5knLjP3r0KA8fPsTBwYE6deqkWtSnVKlSVtWiYU8kDAgh7F5oaCizZs1i0aJFxMbG0qtXL/z9/V967n6yovDzzZt8Gx7On7GxqfqQn9BrNBgVBR3wWqFCvFu6NPU9PAA4d+4czZo1w9vbm507d6recpGcnMyFCxdSNfmfP38eMO0r8XRzf/369VWvV2SdhAEhhHgsJiaGxYsXExAQQEhICI0aNcLPz49evXq98DfyS48eMfTCBQ5FR6dMJ8uMA6agMKVUKYZrtbRu1oyCBQsSHBysyiyImJiYVEv5HjlyhPv376PVaqlRo0bKt/5XXnmFcuXKybd+KyZhQAghnpGcnMzWrVuZMWMGe/bsoUSJEowbN45Ro0ZlaVpbUFQUnf/8E2NyMtnZ+FYDOF67RrFvvuHw9u25MqhOURRCQkJSjfD/66+/SE5OJl++fCnf+F955RUaNmyIu4XvsidejIQBIYTIwJ9//snMmTNZunQpAIMGDcLPz4/q1aunefyB+/dp88cfJCpKlloD0pWURGVnZ441aoRHDkxFe/ToESdOnEjV5B8ZGQmAt7d3qul9lStXtuhBi+LlSRgQQogsuHPnDgsWLGD27NncuHGDNm3a4OfnR8eOHVNulFFGI5WPHeOu0fhyQeAxHTCoaFEWe3u/1HkURSEsLCzVt/7Tp0+TmJiIm5sbjRo1SrWUb4ECBcxQvbAmEgaEEOIFGAwG1qxZw4wZMzh27BgVK1Zk4sSJvP7664wPD2fZrVtpr0h3+TL89BNcvAj37oGTE3h5Qd++8MorGV5zS40adCxYMMs1JiQkcOrUqVQ3/xs3bgBQvnz5VN/6q1evbjNLNYvskzAghBDZdOTIEWbMmMHq1atxrlyZ2NmzMzoY1q6FatWgYEGIj4cDB+DMGXjzTejSJc23aQFvFxf+bNAg3QF6ERERqZr7T548SUJCAs7OzjRo0CDVKP8iRYqY4ZMLWyNhQAghXlJYWBivBQVxsmRJeJFv2UlJMHo0GAzw888ZHnqoTh2a5M1LYmIiZ86cSfWt/8qVKwCULl061bz+WrVq4ejo+BKfTNiLzMKA5S2gLIQQFqZkyZKElC8PiS84d0CngyJF4MKFjA9TFCZs2IDHjz9y7NgxHj16hF6vp27durz22mspN/8SJUq8xKcQIn0SBoQQIhNX4+O5n9UgEBdnagl4+BAOHYKjR6FlywzfkqTRcMZopEv+/EydOpUmTZpQr149nJ2dzVC9EJmTMCCEEJk4/fBh1g+eOxc2bTL9WauFZs3Azy/Tt2krVGD1mjVoZWEfoQIJA0IIkYl7L9I90KsX+PjAnTuwbx8kJ4PRmOnbDIpCfHIyLjLyX6hAVpkQQohMvNB39dKloV49aNcOPv/c1G3wwQegZDpW+8WuI4QZSRgQQohMFH2ZEfvNm5sGEIaFZXiYi1aLs6wCKFQi//KEECITdd3csv/mhATTz9jYTK8hGwEJtUgYEEKITBRzcqJ4Zq0DUVHPP5eYCDt3/rsaYTocNBpeyZv35YoU4iXIAEIhhMiCEZ6eTL96Ne2liAG+/db07b9WLShUyLQk8e7dcO0ajB0LefKke+5ERWFIsWI5UrcQWSErEAohRBaEx8dT5siR9DcoCgqCrVshNBSio8HFBSpVgtdeg1dfTfe8OqCxhwcH69bNibKFAGQ5YiGEMJt3Q0L4OizMLDsWPqEFjtStSwMPDzOeVYjUMgsDMmZACCGyaKqXF+Xz5MFcKwFogXdLl5YgIFQnYUAIIbLIWadjU40aeDg4vHQg0ALtChRgagYDC4XILRIGhBDiBVR2ceG3OnUo4uj4UoGgW6FCrK1WDb2sLSAsgPwrFEKIF+Tt6sq5Bg0YVLQoYJoamBU6wFWrZWHlyqypVg1nWXpYWAgZQCiEEC/htwcPCAwPZ3VkJEk8DgaKggJoNBqSHv+5oIMDY0uUYGzx4hR3clK5amFvZDaBEELkgjsGA0djYjgRE8OV+HiMycm46nRUd3Wlnrs79d3dcZQuAaESCQNCCCGEnZOphUIIIYTIkIQBIYQQws5JGBBCCCHsnIQBIYQQws5JGBBCCCHsnIQBIYQQws5JGBBCCCHsnIQBIYQQws5JGBBCCCHsnIQBIYQQws5JGBBCCCHsnIQBIYQQws5JGBBCCCHsnIQBIYQQws5JGBBCCCHsnIQBIYQQws5JGBBCCCHsnIQBIYQQws5lKQy4u7vndB1CCCGEyCGZ3cc1gJKVExUvXpyYmBhz1CSEEEKIXOLu7s6NGzcyPCbLYUAIIYQQtknGDAghhBB2TsKAEEIIYeckDAghhBB2TsKAEEIIYeckDAghhBB2TsKAEEIIYeckDAghhBB27v8B98J5yxe9uVkAAAAASUVORK5CYII=", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], "source": [ - "# use vvag to get the losses and gradients with different random circuit instances\n", - "QAOA_vvag = K.jit(tc.backend.vvag(QAOAansatz, argnums=0, vectorized_argnums=0))" + "colors = [\"r\" if states[0][i] == \"0\" else \"c\" for i in range(nnodes)]\n", + "nx.draw_networkx(example_graph, with_labels=True, node_color=colors, pos=pos)\n", + "ax = plt.gca()\n", + "ax.set_facecolor(\"w\")" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "4ae99ab9", + "metadata": {}, + "source": [ + "## Classical Method" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "720dd1a4", + "metadata": {}, + "source": [ + "In classial method, all situation will be checked one-by-one (brutal force). " + ] + }, + { + "cell_type": "code", + "execution_count": 110, + "id": "2115bb6d", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "bit string: ['01010101', '10101010'] \n", + "max cut: 10.0\n" + ] + } ], + "source": [ + "def classical_solver(graph):\n", + " num_nodes = len(graph)\n", + " max_cut = [0]\n", + " best_case = [0] # \"01\" series with max cut\n", + " for i in range(2**num_nodes):\n", + " case = f\"{bin(i)[2:]:0>{num_nodes}}\"\n", + " cat1, cat2 = [], []\n", + " for j in range(num_nodes):\n", + " if str(case)[j] == \"0\":\n", + " cat1.append(j)\n", + " else:\n", + " cat2.append(j)\n", + "\n", + " # calculate the cost function\n", + " cost = 0\n", + " for node1 in cat1:\n", + " for node2 in cat2:\n", + " if graph[node1].get(node2):\n", + " cost += graph[node1][node2][\"weight\"]\n", + " cost = round(cost, 4) # elimate minor error\n", + " if max_cut[-1] <= cost:\n", + " max_cut.append(cost)\n", + " best_case.append(case)\n", + "\n", + " # optimal cases maybe more than 1, but they are all at the end\n", + " index = max_cut.index(max_cut[-1])\n", + "\n", + " return max_cut[-1], best_case[index:]\n", + "\n", + "\n", + "max_cut, best_case = classical_solver(example_graph_dict)\n", + "print(\"bit string:\", best_case, \"\\nmax cut:\", max_cut)" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "e9b8619b", + "metadata": {}, + "source": [ + "## Weighted Max-Cut Problem" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "9f8cb1da", + "metadata": {}, + "source": [ + "When an edge is cut, the total cost increase a certain number instead of 1, we say that this edge has a \"weight\".\n", + "\n", + "Let's define a graph with random weights:" + ] + }, + { + "cell_type": "code", + "execution_count": 111, + "id": "b0a1f778", + "metadata": {}, "outputs": [], - "metadata": {} + "source": [ + "weighted_graph_dict = {\n", + " 0: {1: {\"weight\": 0.1756}, 7: {\"weight\": 2.5664}, 3: {\"weight\": 1.8383}},\n", + " 1: {0: {\"weight\": 0.1756}, 2: {\"weight\": 2.2142}, 3: {\"weight\": 4.7169}},\n", + " 2: {1: {\"weight\": 2.2142}, 3: {\"weight\": 2.0984}, 5: {\"weight\": 0.1699}},\n", + " 3: {1: {\"weight\": 4.7169}, 2: {\"weight\": 2.0984}, 0: {\"weight\": 1.8383}},\n", + " 4: {7: {\"weight\": 0.9870}, 6: {\"weight\": 0.0480}, 5: {\"weight\": 4.2509}},\n", + " 6: {7: {\"weight\": 4.7528}, 4: {\"weight\": 0.0480}, 5: {\"weight\": 2.2879}},\n", + " 5: {6: {\"weight\": 2.2879}, 4: {\"weight\": 4.2509}, 2: {\"weight\": 0.1699}},\n", + " 7: {4: {\"weight\": 0.9870}, 6: {\"weight\": 4.7528}, 0: {\"weight\": 2.5664}},\n", + "}\n", + "\n", + "weighted_graph = nx.to_networkx_graph(weighted_graph_dict)" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "264289c1", + "metadata": {}, + "source": [ + "The classical algorithm is used to find the corresponding maximum cut" + ] + }, + { + "cell_type": "code", + "execution_count": 112, + "id": "f9f5ce41", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "bit string: ['00010101', '11101010'] \n", + "max cut: 23.6685\n" + ] + } + ], + "source": [ + "max_cut, best_case = classical_solver(weighted_graph_dict)\n", + "print(\"bit string:\", best_case, \"\\nmax cut:\", max_cut)" + ] }, { "cell_type": "code", - "execution_count": 5, + "execution_count": 113, + "id": "35dea0fb", + "metadata": {}, + "outputs": [ + { + "data": { + "image/png": "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", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], "source": [ + "colors = [\"r\" if best_case[0][i] == \"0\" else \"c\" for i in range(nnodes)]\n", + "weighted_graph = nx.to_networkx_graph(weighted_graph_dict)\n", + "nx.draw_networkx(weighted_graph, with_labels=True, node_color=colors, pos=pos)\n", + "ax = plt.gca()\n", + "ax.set_facecolor(\"w\")" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "d62faa79", + "metadata": {}, + "source": [ + "The quantum algorithm is then used, and the same result is supposed to be found." + ] + }, + { + "cell_type": "code", + "execution_count": 114, + "id": "22db40be", + "metadata": {}, + "outputs": [ + { + "data": { + "image/png": "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", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "QAOA_vvag = K.jit(\n", + " tc.backend.vvag(QAOAansatz, argnums=0, vectorized_argnums=0), static_argnums=(1, 2)\n", + ")\n", "params = K.implicit_randn(\n", " shape=[ncircuits, 2 * nlayers], stddev=0.1\n", ") # initial parameters\n", "opt = K.optimizer(tf.keras.optimizers.Adam(1e-2))\n", "\n", - "for i in range(50):\n", - " loss, grads = QAOA_vvag(params, example_graph)\n", - " print(K.numpy(loss))\n", - " params = opt.update(grads, params) # gradient descent" - ], + "list_of_loss = [[] for i in range(ncircuits)]\n", + "\n", + "for i in range(300):\n", + " loss, grads = QAOA_vvag(params, weighted_graph)\n", + " params = opt.update(grads, params) # gradient descent\n", + "\n", + " # visualise the progress\n", + " clear_output(wait=True)\n", + " list_of_loss = np.hstack((list_of_loss, K.numpy(loss)[:, np.newaxis]))\n", + " plt.xlabel(\"Iteration\")\n", + " plt.ylabel(\"Cost\")\n", + " for index in range(ncircuits):\n", + " plt.plot(range(i + 1), list_of_loss[index])\n", + " legend = [\"circuit %d\" % leg for leg in range(ncircuits)]\n", + " plt.legend(legend)\n", + " plt.show()" + ] + }, + { + "cell_type": "code", + "execution_count": 115, + "id": "d2cf1b57", + "metadata": {}, "outputs": [ { + "name": "stdout", "output_type": "stream", + "text": [ + "Circuit #0\n", + "cost: -15.938664 \n", + "bit strings: ['00110101', '11001010'] \n", + "\n", + "Circuit #1\n", + "cost: -17.461643 \n", + "bit strings: ['00010101', '11101010'] \n", + "\n", + "Circuit #2\n", + "cost: -16.610685 \n", + "bit strings: ['00010101', '11101010'] \n", + "\n", + "Circuit #3\n", + "cost: -16.61402 \n", + "bit strings: ['00010101', '11101010'] \n", + "\n", + "Circuit #4\n", + "cost: -14.814039 \n", + "bit strings: ['00010101', '11101010'] \n", + "\n", + "Circuit #5\n", + "cost: -14.7950735 \n", + "bit strings: ['00010101', '11101010'] \n", + "\n" + ] + } + ], + "source": [ + "# print all results\n", + "for num_circuit in range(ncircuits):\n", + " c = QAOAansatz(params=params[num_circuit], g=weighted_graph, return_circuit=True)\n", + " loss = QAOAansatz(params=params[num_circuit], g=weighted_graph)\n", + "\n", + " # find the states with max probabilities\n", + " probs = K.numpy(c.probability()).round(decimals=4)\n", + " index = np.where(probs == max(probs))[0]\n", + " states = []\n", + " for i in index:\n", + " states.append(f\"{bin(i)[2:]:0>{c._nqubits}}\")\n", + " print(\"Circuit #%d\" % num_circuit)\n", + " print(\"cost:\", K.numpy(loss), \"\\nbit strings:\", states, \"\\n\")" + ] + }, + { + "cell_type": "code", + "execution_count": 116, + "id": "492d215f", + "metadata": {}, + "outputs": [ + { "name": "stdout", + "output_type": "stream", "text": [ - "[-0.23837963 -1.1651934 ]\n", - "[-0.5175445 -1.4539642]\n", - "[-0.7306818 -1.6646069]\n", - "[-0.91530037 -1.8384367 ]\n", - "[-1.0832287 -1.9884492]\n", - "[-1.2398103 -2.120449 ]\n", - "[-1.3878661 -2.2374902]\n", - "[-1.5290209 -2.341291 ]\n", - "[-1.6642232 -2.4328852]\n", - "[-1.7940071 -2.5128942]\n", - "[-1.9186544 -2.5888019]\n", - "[-2.0382538 -2.6627793]\n", - "[-2.152771 -2.735217]\n", - "[-2.2620971 -2.8060198]\n", - "[-2.3657765 -2.8749723]\n", - "[-2.4635859 -2.942443 ]\n", - "[-2.5571456 -3.0074604]\n", - "[-2.6474872 -3.071116 ]\n", - "[-2.7343643 -3.1320357]\n", - "[-2.8174913 -3.1904984]\n", - "[-2.896546 -3.2464304]\n", - "[-2.971222 -3.298626]\n", - "[-3.0411685 -3.3485155]\n", - "[-3.1060221 -3.3945203]\n", - "[-3.1671162 -3.4365993]\n", - "[-3.2244647 -3.47741 ]\n", - "[-3.2800133 -3.51378 ]\n", - "[-3.328074 -3.5467668]\n", - "[-3.3779154 -3.5716858]\n", - "[-3.42378 -3.6026983]\n", - "[-3.4665916 -3.6264663]\n", - "[-3.5065007 -3.6452012]\n", - "[-3.5436964 -3.6676104]\n", - "[-3.5783873 -3.6827888]\n", - "[-3.6107998 -3.696251 ]\n", - "[-3.6411772 -3.710956 ]\n", - "[-3.6697989 -3.725151 ]\n", - "[-3.6969085 -3.739223 ]\n", - "[-3.7227716 -3.753837 ]\n", - "[-3.747642 -3.7637105]\n", - "[-3.7717733 -3.7778597]\n", - "[-3.7953677 -3.7897499]\n", - "[-3.8185773 -3.8026254]\n", - "[-3.8415692 -3.8186839]\n", - "[-3.864397 -3.8288355]\n", - "[-3.887118 -3.8470592]\n", - "[-3.9089546 -3.8578553]\n", - "[-3.9298224 -3.8789082]\n", - "[-3.9531326 -3.898365 ]\n", - "[-3.9759274 -3.9132624]\n" + "OS info: macOS-13.3.1-arm64-arm-64bit\n", + "Python version: 3.10.10\n", + "Numpy version: 1.23.2\n", + "Scipy version: 1.10.1\n", + "Pandas version: 2.0.1\n", + "TensorNetwork version: 0.4.6\n", + "Cotengra is not installed\n", + "TensorFlow version: 2.12.0\n", + "TensorFlow GPU: [PhysicalDevice(name='/physical_device:GPU:0', device_type='GPU')]\n", + "TensorFlow CUDA infos: {'is_cuda_build': False, 'is_rocm_build': False, 'is_tensorrt_build': False}\n", + "Jax is not installed\n", + "JaxLib is not installed\n", + "PyTorch is not installed\n", + "Cupy is not installed\n", + "Qiskit version: 0.23.3\n", + "Cirq is not installed\n" ] } ], - "metadata": {} + "source": [ + "tc.about()" + ] } ], "metadata": { @@ -259,9 +785,9 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.7.11" + "version": "3.10.10" } }, "nbformat": 4, "nbformat_minor": 5 -} \ No newline at end of file +} diff --git a/docs/source/tutorials/qaoa_bo.ipynb b/docs/source/tutorials/qaoa_bo.ipynb new file mode 100644 index 00000000..240bf374 --- /dev/null +++ b/docs/source/tutorials/qaoa_bo.ipynb @@ -0,0 +1,943 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "source": [ + "# Optimizing QAOA by Bayesian Optimization (BO)" + ], + "metadata": {} + }, + { + "cell_type": "markdown", + "source": [ + "## Overview" + ], + "metadata": { + "collapsed": false + } + }, + { + "cell_type": "markdown", + "source": [ + "In this tutorial, we show how to use Bayesian optimization to optimize QAOA. For the introduction of QAOA, please refer to the [previous tutorial](qaoa.ipynb). Bayesian optimization in this tutorial is based on $\\text{ODBO}$, please refer to [Cheng, Yang, Hsieh, Liao and Zhang](https://doi.org/10.48550/arXiv.2205.09548) for details and this [repository](https://github.com/tencent-quantum-lab/ODBO) for source code and [installation](https://github.com/tencent-quantum-lab/ODBO#installation). In this tutorial, the updated modules of BO, TuRBO and DARBO are packaged." + ], + "metadata": {} + }, + { + "cell_type": "markdown", + "source": [ + "## Setup" + ], + "metadata": { + "collapsed": false + } + }, + { + "cell_type": "code", + "execution_count": null, + "source": [ + "import tensorcircuit as tc\n", + "from jax import numpy as jnp\n", + "import optax\n", + "import torch\n", + "import networkx as nx\n", + "import matplotlib.pyplot as plt\n", + "import numpy as np\n", + "import cotengra as ctg\n", + "from typing import Union\n", + "import time\n", + "import odbo\n", + "from IPython.display import clear_output\n", + "\n", + "K = tc.set_backend(\"jax\")\n", + "tc.set_dtype(\"complex128\")\n", + "dtype = torch.float64\n", + "\n", + "# cotengra package to speed up the calculation\n", + "opt_ctg = ctg.ReusableHyperOptimizer(\n", + " methods=[\"greedy\", \"kahypar\"],\n", + " parallel=True,\n", + " minimize=\"combo\",\n", + " max_time=20,\n", + " max_repeats=128,\n", + " progbar=True,\n", + ")\n", + "\n", + "tc.set_contractor(\"custom\", optimizer=opt_ctg, preprocessing=True)\n", + "\n", + "nlayers = 10\n", + "device = torch.device(\"cuda\" if torch.cuda.is_available() else \"cpu\")\n", + "acqfn = \"ucb\"" + ], + "outputs": [], + "metadata": {} + }, + { + "cell_type": "markdown", + "source": [ + "## MAX-CUT Hamiltonian" + ], + "metadata": {} + }, + { + "cell_type": "markdown", + "source": [ + "### Define the Graph" + ], + "metadata": { + "collapsed": false + } + }, + { + "cell_type": "code", + "execution_count": 3, + "source": [ + "# a graph instance\n", + "graph_dict = {\n", + " 0: {1: {\"weight\": 1.0}, 7: {\"weight\": 1.0}, 3: {\"weight\": 1.0}},\n", + " 1: {0: {\"weight\": 1.0}, 2: {\"weight\": 1.0}, 3: {\"weight\": 1.0}},\n", + " 2: {1: {\"weight\": 1.0}, 3: {\"weight\": 1.0}, 5: {\"weight\": 1.0}},\n", + " 3: {1: {\"weight\": 1.0}, 2: {\"weight\": 1.0}, 0: {\"weight\": 1.0}},\n", + " 4: {7: {\"weight\": 1.0}, 6: {\"weight\": 1.0}, 5: {\"weight\": 1.0}},\n", + " 5: {6: {\"weight\": 1.0}, 4: {\"weight\": 1.0}, 2: {\"weight\": 1.0}},\n", + " 6: {7: {\"weight\": 1.0}, 4: {\"weight\": 1.0}, 5: {\"weight\": 1.0}},\n", + " 7: {4: {\"weight\": 1.0}, 6: {\"weight\": 1.0}, 0: {\"weight\": 1.0}},\n", + "}\n", + "\n", + "graph = nx.to_networkx_graph(graph_dict)\n", + "pos = nx.spring_layout(graph)\n", + "nx.draw_networkx(graph, with_labels=True, pos=pos)\n", + "ax = plt.gca()\n", + "ax.set_facecolor(\"w\")" + ], + "outputs": [ + { + "data": { + "text/plain": "
", + "image/png": "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" + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "metadata": { + "ExecuteTime": { + "end_time": "2023-07-15T07:42:26.571397400Z", + "start_time": "2023-07-15T07:42:26.444088200Z" + } + } + }, + { + "cell_type": "markdown", + "source": [ + "### Brutal Force Result" + ], + "metadata": { + "collapsed": false + } + }, + { + "cell_type": "code", + "execution_count": 4, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "bit string: ['01010101', '10101010'] \n", + "max cut: 10.0\n" + ] + }, + { + "data": { + "text/plain": "
", + "image/png": "iVBORw0KGgoAAAANSUhEUgAAAgMAAAGFCAYAAABg2vAPAAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjcuMSwgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy/bCgiHAAAACXBIWXMAAA9hAAAPYQGoP6dpAABmpklEQVR4nO3deVxUZfvH8c8AAoqIG+67ueWCigOoKGhqaVquM8/z/OpxS9ueditNTU3TSssWK7Pc2mdcMjVT0wJxV1DczX3FBUX2bWbO74/JKUpWgTPDud6vFy9sODNzocT5nvu+z3XrFEVREEIIIYRmualdgBBCCCHUJWFACCGE0DgJA0IIIYTGSRgQQgghNE7CgBBCCKFxEgaEEEIIjZMwIIQQQmicR0EOstlsXL58GV9fX3Q6XUnXJIQQQohioCgKycnJ1KlTBze33K//CxQGLl++TP369YutOCGEEEKUngsXLlCvXr1cv16gMODr6+t4sUqVKhVPZUIIIYQoUUlJSdSvX99xHs9NgcLA7amBSpUqSRgQQgghXEx+U/yygFAIIYTQOAkDQgghhMZJGBBCCCE0TsKAEEIIoXESBoQQQgiNkzAghBBCaJyEASGEEELjJAwIIYQQGidhQAghhNA4CQNCCCGExkkYEEIIITROwoAQQgihcRIGhBBCCI2TMCCEEEJonIQBIYQQQuMkDAghhBAa56F2AUIIUZJuZmdzIj2dTJsNLzc3mpcvT5Vy5dQuSwinImFACFHm7EtO5pPLl1l/8yYXMzP/8fX6Xl70q1qVJ+rUob2vrwoVCuFcJAwIIcqM39PSeOz4caISE/HQ6bAoyh2Pu5CZycIrV/gsLo4wPz8+b9GCZhUqlHK1QjgPWTMghCgTPr50ibZ79rAjMREg1yBw2+2vb0tMpO2ePXx66VKJ1yiEs5IwIIRweZPPnOF/J06QpShYCvlcC5CpKDx14gRTz5wpifKEcHoSBkSubIqCLZ+rKyHU9smlS8w4d65YXmvauXPMlxECoUGyZkA4nEpP56srV9ielMTe5GQSLPZrrApubnSoWJGQSpX4V40adKpUSeVKhbA7kZbGiydP3vmLZ87A0qXw++9w8yZ4eUGjRmA0Qpcuub7mC6dO0btqVZqWL18yRQvhhHSKkv+lX1JSEn5+fiQmJlJJTgRlzoGUFF4+dYqNCQm4AzbgTj8UtxdkdaxYkVlNmtCnatVSrlSInHrs28fWxMQ7Tw3s3AkrV0Lr1lCtGmRkQFQUHDgAL74IAwbc8TU9dDq6+/mxuX37kixdiFJR0PO3hAENsyoKs86dY9q5cyiKgrWAz3PDHhhG16rF3HvuwddDBphE6TuQkkLA3r2Fe5LVCo8/DllZ8OWXeR56SK+ntY/PXVQohPoKev6WNQMalW2zYTx8mNfPnsVSiCAA9iAAsOTKFcL27+dmdnZJlChEnuZfvoyHTle4J7m7Q40akJKS52EeOh3zL1++i+qEcC0SBjRIURRGHjvGyvj4O04HFJQV+9XZ/bGxZFgLEyeEuHvrb97M9/ZBANLTITERLl2CZctg1y7o2DHPp1gUhfU3bhRTpUI4Pxnf1aBvr13jm2vXcj8gPR2+/x6OHoVjxyA5GV59FR544B+HWoGYlBSmnj3LW02bllzRQvxFksXCmYyMgh386aewZo39z25u0K0bPPdcvk87lZFBisVCRZkGExogP+Uacz0ri6d+/x0dd14kCNivor78EmrWhKZNYf/+PF/TBrxz4QJD/f3lTgNRKk6mpxf84KFDISwM4uMhIgJsNijA1JaCPRAEVKxY5DqFcBUSBjRm/uXLpFiteU8PVK0KK1bYPx8/Dk88ke/rugNvnz/PsjZtiqtU4SIURSEzM5PMzEwyMjIK9bmoz0moU8c+WlUQDRrYPwDuvx9efhkmToRPPoF81hxk2mx5fl2IskLCgIZYbDY+vnSJfH+9eXrag0BhXhv4IT6ey5mZ1PHyKmqJohCsVmuJnGgL+5ysrKwi1e/u7o63tzdeXl65fr795ypVquT42i1/f74u6l9c9+7w3ntw4cKfISEX3m6yrEpog4QBDdmXksLVElz5bwV+vnmT0bVrl9h7qE1RFCwWS6leBef2NYulsI137Tw9PfM88f71s5+fX4FO1gX5/Pc/e9zFXHyKxcI3W7cWbQHs7V0MU1PzPMwNuEcaDwmNkDCgIdHJyXmvFbhL5XQ6opOTSyQMKIpCVlaW6lfBGRkZFKA1xz/odLoCn0wrVqxItWrV7urEm9tzPD09cSsDV7s+7u7U1em4mNe/RUICVKmS8zGLBTZu/LMbYR6alS9PBXf3uy9WCBcgYUBDDqWm4qHTkV1C+w1kKwqbzpxhYVRUkU+0uZ2kM++wJ31B3GkoOreTZ+XKlUvkBOzl5UW5cuXQFfaeeJGDoihER0djNpsxm81c7N8fBg2y9w64k/fes1/9BwRA9er2lsSbNsH58/Dkk5DHVb870K9atZL5RoRwQhIGNCTFai3SVW1hnLh0iccee8wxFF2Qk2elSpWKZej5Tl+7m6FooT5FUYiNjcVkMmE2mzl9+jT+/v4MHTqUkKFDGZ7Xk3v0gHXr4McfISkJKlSA5s1h7Fjo2jXP97UCT9SpU5zfihBOTX5TaoiHTmdfPV2CgSCgTRtirNYyMRQt1KEoCocOHXIEgBMnTlCtWjUGDx7MggULCAsLc4S87w4cYFNCwp2bD/Xsaf8oJJ3NRo+KFWleocLdfitCuAz5ja0hDb29S3Rk4PaCKwkCoiiOHDnC1KlTuffee2nXrh2ffPIJ3bp1Y/369cTFxbFgwQLuu+++HKM985s3p1xxTr8oCmRlsec//2Hx4sUlPpImhLOQkQENCfT1LdQeBIVls1r56b33GBAfT1hYGGFhYXTo0EGG6kWujh8/7lgDcOjQIfz8/Bg4cCDvvvsuvXr1wtPTM8/nN/T25tPmzRlx7FjxFKTT8UmzZuwMDWXUqFGYzWY+//xz6tWrVzyvL4STkl0LNeRGdjY1t20rWCD44Qf7Zi7x8bB6tb2Fa7Nm9q8NGgS5dGV7LDaWM2vWsH37dtLT0/H19SU0NJSwsDDCw8Pp2LEj5cqVK7bvSbiekydPOgJAbGwsvr6+PPzwwxgMBvr06YNXEfpUzD5/nldOn77r2uY0bcpL9esD8NNPPzF27FhSUlKYO3cuI0eOlEWgwuXIFsbijoyHD7MyPj7/DV7+9S+4evXOX/vuO6hVK8dDOqB5+fIcDQpCp9ORlZXF3r17iYiIIDIykm3btpGamoqPjw9du3YlPDycsLAwOnXqlO/Vn3B9Z86ccQSAmJgYfHx8GDBgAEajkQceeABvb++7fo8vr1zhqd9/J9NmozAdGDwALzc35jdvziN/+7m+desWL7zwAkuWLOH+++/n888/p/4fYUEIVyBhQNzRtsREQvftK/bX1QGfNGvGE3Xr3vHr2dnZxMTEOMLB1q1bSU5OpkKFCnTp0sUxrRAUFFSkK0PhfM6fP+8IAHv27KF8+fL0798fo9FI3759qVACC/QuZGTw1IkTrL1xA3fIcxTMHfu+GgOqVePjZs2ol0cgWbduHWPHjiUpKYn33nuP0aNHyyiBcAkSBkSuRhw9ytdXrxbb+gF3IKBiRXZ17IhHARcPWiwW9u3bR2RkJJGRkWzZsoWkpCS8vb3p3LmzY1ohODi4WK4aRem4ePEiy5Ytw2w2s3PnTry9venXrx8Gg4H+/fvj4+NTKnWcTEvjs7g4fr5xg2NpaTl+1t2BVj4+9K1alcfr1KFpAbsM3rp1i5deeolFixbRp08fPv/8cxrk085YCLVJGBC5SsjO5t7du7mamYlylyv/ddg7D8Z06kTru/hFb7VaiY2NJTIykoiICKKiokhISMDLy4vg4GDHtEJISEiJXFGKort8+TIrVqzAZDKxbds2PD096du3LwaDgQEDBuDr66tqfRlWK2cyMsi02fByc6Oxtzfed9FZcP369YwZM4bExETeffddHnvsMRklEE5LwoDIlcVi4eHnnmNd3764+fhgK+IvMh3grtOxqk0bHizmbm02m42DBw86phW2bNnCjRs3KFeuHMHBwY5phS5dupTa1ab409WrVx0BICoqCg8PD+6//34MBgMPPfQQfn5+apdYohITE3nppZdYuHAhvXv35vPPP6dhw4ZqlyXEP0gYEHdksVj473//i9ls5t3ly/mgVi3OZWTkv5Ph37gDvu7umFu3pnchdzgsCpvNxuHDhx3TCpGRkVy/fh0PDw/0er0jHHTt2lX1K9Gy6vr166xcuRKTyURkZCRubm707t0bg8HAww8/TJW/7wOgARs2bOCxxx4jMTGROXPmMGbMGBklEE5FwoD4B4vFwvDhwzGZTJhMJoYMGUKa1crkM2eYe/EibuS94ArsK68twKDq1ZnfvDk1VLoTQFEUjh496phWiIyM5OrVq7i7uxMYGOhYcxAaGio/s3fhxo0b/PDDD5jNZn799VcAevbsidFoZODAgVST/v0kJiYybtw4vvjiC3r16sUXX3whowTCaUgYEDlYrVb++9//YjKZ+P777xk6dGiOr59JT+ezy5f5Ii6OG39sjXv7+ub2D0gFNzf+U7MmT9WpQwcnu/pWFIXff/89Rzi4fPkybm5udOjQwbHmoFu3blSuXFntcp1aQkICq1atwmw2s2nTJmw2G+Hh4RiNRgYNGoS/v7/aJTqlDRs2MGbMGBISEpgzZw5jx46VUQKhOgkDwsFqtTJ8+HC+//57vvvuO4YNG5brsYqicD4zk5jkZK5mZaEAVTw8aP9Hr3Y3F/nlpigKp06dcgSDiIgILl68iE6no3379o5phe7du1O1FKY5nF1iYiKrV6/GZDKxceNGLBYL3bt3x2AwMGTIEGrWrKl2iS4hMTGRl19+mc8//5z77ruPL774gkb5bJUsREmSMCAAexAYMWIE3333Hd9++y0Gg0HtklShKApnzpxxrDeIiIjg3Llz6HQ62rZt65hW6N69O9WrV1e73FKRnJzMmjVrMJlMrF+/nqysLEJDQzEYDAwdOpTatWurXaLL2rhxI4899hgJCQnMnj2bsWPHyp4dQhUSBgRWq5WRI0fy7bff8s0332A0GtUuyamcO3cux7TC6T/a2bZu3doxrRAWFkaNGjVUrrT4pKamsnbtWsxmM+vWrSMjI4POnTs7AoD04C8+SUlJvPzyyyxYsICePXvyxRdf0LhxY7XLEhojYUDjrFYro0aN4uuvv+bbb7+VIFAAFy5cyHG3wokTJwBo1aqVIxiEhYW53BVzWloa69atw2w2s3btWtLT0wkKCnIEAFnsVrI2bdrE6NGjuXHjBu+88w5PPPGEjBKIUiNhQMOsViujR4/mq6++4ptvvuFf//qX2iW5pMuXL+eYVjh+/DgAzZs3d0wrhIWFUTeXFsxqysjI4Oeff8ZsNrNmzRpSU1Pp2LEjRqORYcOGyRVqKUtOTuaVV15h/vz59OjRg4ULF8q/gSgVEgY0ymq18thjj/Hll1/y9ddf8+9//1vtksqMK1eusGXLFkc4OHLkCABNmzbNMa2gVovazMxMNmzYgNlsZvXq1SQnJxMQEOAIAPfcc48qdYk/bd68mdGjRxMfH8/bb7/Nk08+KaMEokRJGNAgm83GY489xtKlS/nqq6/4z3/+o3ZJZdr169fZsmWLY83BwYMHAWjcuLEjGISHh5foavKsrCw2bdqEyWRi1apVJCUl0aZNGwwGAwaDgRYtWpTYe4uiSU5O5tVXX+XTTz8lLCyMRYsW0aRJE7XLEmWUhAGN+WsQ+PLLL/m///s/tUvSnPj4eKKiohxTC7GxsSiKQoMGDXKEgyZNmtzV/efZ2dn8+uuvmEwmfvjhB27dukXLli0xGo0YDAbuvffeYvyuREn59ddfGT16NNeuXePtt9/mqaeeklECUewkDGjIX4PA0qVLeeSRR9QuSWBv3nM7HERERLB//35sNht169bNseagWbNm+YYDi8VCREQEZrOZlStXcuPGDZo1a+YIAG3atJEGNy4oJSWFV199lU8++YTu3buzaNEimjZtqnZZogyRMKARNpuNMWPGsGTJEgkCTu7WrVts3brVMXIQHR2NzWajdu3aOe5WaNmyJTqdDqvVypYtWzCbzaxYsYLr16/TpEkTRwAICAiQAFBG/Pbbb4waNYpr167x1ltv8fTTT8sogSgWEgY0wGazMXbsWBYtWsSXX34pQcDFJCUlsW3bNkc42LNnD1arlcqVK1OtWjWuXbtGcnIyDRs2xGAwYDQa6dixowSAMiolJYXx48fz8ccf0717dxYuXCiLPsVdK+j5W6Kni7LZbDz++OMsWrRIRgRcVKVKlejbty8zZ87k3XffZezYsVStWpVbt25x4cIFUlNTAftJ4tSpU2zfvp2DBw9isxV2j0nhCipWrMi8efP47bffuHDhAu3atePDDz+Uf29RKiQMuCCbzcYTTzzBwoULWbJkCY8++qjaJYlCUhSFXbt28eKLL9KwYUO6du3Kjz/+yKOPPsr27dtJT08nOTmZTZs28eSTT3L9+nXGjRtHQEAA/v7+DBo0iPfff599+/Zhtea316RwJeHh4Rw4cIDRo0fz3HPPER4ezsmTJ9UuS5RxMk3gYmw2G08++SSff/45ixcvZvjw4WqXJApIURSio6MxmUwsW7aMc+fOUatWLYYOHYrBYKBr1655zhOnp6ezc+dOx7TCjh07yMzMpHLlynTr1s2x5qB9+/Z4eHiU4ncmSkpkZCSjRo0iLi6OmTNn8uyzz8paAlEosmagDLLZbDz11FMsWLCARYsWMWLECLVLEvlQFIX9+/djNpsxm82cPn0af39/RwDo1q0b7u7uRXrtjIwMdu/e7bhbYceOHaSnp1OpUiVCQ0Mddyx07NhRwoELS01N5bXXXuPDDz8kNDSURYsW0axZM7XLEi5CwkAZY7PZePrpp/nss89YuHAhI0eOVLskkQtFUTh48KAjAJw4cYJq1aoxZMgQDAYDYWFhJXJyzsrKYs+ePY4mSNu2bSMtLY2KFSvStWtXx62MnTp1oly5csX+/qJkbdmyhVGjRnHp0iXHKEFRg6TQDgkDZYiiKDz99NPMnz+fL774glGjRqldkriDw4cPOwLAsWPHqFKlCoMGDcJoNNKjR49SPwFnZ2cTHR3tCAdbt24lJSWFChUq0LVrV8e0gl6vx8vLq1RrE0WTmprKxIkT+fDDD+ncuTOLFy+mefPmapclnJiEgTLidhD49NNPWbhwoQQBJ3P8+HFMJhNms5nDhw/j5+fHwIEDMRqN3HfffXh6eqpdooPFYiEmJsax5iAqKoqkpCTKly9P586dHdMKQUFBeHt7q12uyENUVBSjRo3i4sWLvPnmmzz33HMySiDuSMJAGaAoCv/73//45JNP+OKLLxg9erTaJQng5MmTjgBw4MABfH19efjhhzEYDPTp08dlrrKtViv79+93rDmIiori1q1beHl5ERIS4phWCAkJoXz58mqXK/4mLS2NiRMn8sEHHxASEsLixYtlLwrxDxIGXNxfg8Dnn3/OY489pnZJmnb69GmWLVuGyWRi3759+Pj48NBDD2EwGHjggQfKxJW01Wrl4MGDjmmFLVu2cPPmTTw9PQkKCnKEg86dO+Pj46N2ueIPW7duZeTIkVy8eJEZM2bw/PPPyyiBcJAw4MIUReGZZ57h448/ZsGCBYwZM0btkjTp3LlzjgCwd+9eKlSoQP/+/TEYDPTt25cKFSqoXWKJstlsHDp0yDGtEBkZSXx8PB4eHgQFBTnWHHTt2pWKFSuqXa6mpaWlMWnSJN5//32Cg4NZvHgxLVu2VLss4QQkDLgoRVF49tlnmTdvngQBFVy4cIHly5djMpnYtWsX3t7ePPjggxgMBh588EFNXxErisKRI0cc0wqRkZFcu3YNd3d3OnXq5Fhz0LVrV/k9oZJt27YxcuRIzp8/z4wZM3jhhRdklEDjJAy4IEVReO655/joo4/47LPPGDt2rNolacLly5dZvnw5ZrOZbdu24enpSd++fTEajfTv3x9fX1+1S3RKiqJw/PjxHOEgLi4ONzc3Onbs6JhWCA0NpXLlymqXqxlpaWlMnjyZuXPnyiiBkDDgahRF4fnnn+fDDz9k/vz5PP7442qXVKZdvXrVEQCioqLw8PDg/vvvx2g0MmDAAPz8/NQu0eUoisLJkycdwSAiIoJLly6h0+no0KGDY1qhW7duVK1aVe1yy7zt27czcuRIzp07xxtvvMFLL70kowQaJGHAhSiKwgsvvMAHH3zAp59+yhNPPKF2SWXS9evXWbFiBWazmcjISNzc3OjduzcGg4GBAwfK1WsxUxSF06dPO9YbREREcP78eXQ6He3atXNMK3Tv3p1q1aqpXW6ZlJ6ezuuvv867775LUFAQixcvplWrVmqXJUqRhAEXoSgKL774Iu+//z6ffPIJTz75pNollSk3btzghx9+wGQy8dtvvwFw3333YTAYGDRokFyhlrKzZ8/mmFY4c+YMAG3atHFMK3Tv3p0aNWqoXGnZsmPHDkaOHMnZs2eZNm0aL730krSo1ggJAy5AURReeukl5s6dy8cff8xTTz2ldkllQkJCAqtWrcJsNrNp0yZsNhvh4eEYjUYGDRqEv7+/2iWKP5w/fz7H3Qq3d+e79957HdMKYWFh1KpVS+VKXV96ejpTpkzh3XffJTAwkCVLlnDvvfeqXZYoYRIGnJyiKIwbN4733nuPefPm8fTTT6tdkktLTEzkxx9/xGw2s3HjRiwWC927d8doNDJ48GBq1qypdomiAC5dupRjWuH3338HoEWLFo5phbCwMOrUqaNypa5r586djBw5ktOnTzNt2jTGjRsnowRlmIQBJ6YoCi+//DLvvvuuBIG7kJyczOrVqzGbzaxfv56srCxCQ0MxGo0MGTKE2rVrq12iuEtxcXFs2bLFEQ6OHj0KwD333OMIBmFhYdSvX1/lSl1LRkYGU6ZMYc6cOXTs2JElS5bQunVrtcsSJUDCgJNSFIVXXnmFOXPm8NFHH/G///1P7ZJcSkpKCmvXrsVsNrNu3ToyMzPp3LkzBoOBYcOGUbduXbVLFCXo2rVrbNmyxbHm4NChQwA0adIkx7RCo0aN1C3URezatYuRI0dy6tQppkyZwiuvvCKjBGWMhAEnpCgKr776KrNnz+bDDz/kmWeeUbskl5CWlsa6deswmUz89NNPpKenExQU5AgADRo0ULtEoZL4+PgcIwcHDhwAoGHDho5gEB4eTuPGjdHpdCpX65wyMjKYOnUqs2fPpkOHDixZsoQ2bdqoXZYoJhIGnMxfg8AHH3zAs88+q3ZJTi09PZ3169djMplYs2YNaWlpBAYGOgJA48aN1S5ROKGbN28SFRXlGDnYv38/iqJQr169HGsO7rnnHgkHf7N7925GjBghowRljIQBJ6IoCuPHj+edd96RIJCHzMxMNmzYgMlkYvXq1aSkpNC+fXtHALjnnnvULlG4mFu3bhEVFeVYlBgTE4PNZqN27do51hy0aNFCwgH2UYJp06bxzjvv0KFDBxYvXkzbtm3VLkvcBQkDTkJRFCZMmMDbb7/N+++/z3PPPad2SU4lKyuLX375BbPZzKpVq0hKSqJNmzYYjUaGDRsmW7KKYpWYmMi2bdsc4WDv3r1YrVZq1qyZY1qhVatWmg4He/bsYcSIEZw4cYLXX3+dV199lXLlyqldligCCQNOQFEUXnvtNd566y3mzp3L888/r3ZJTiE7O5vNmzdjNpv54YcfuHXrFi1btsRoNGIwGOTeZ1FqkpOT2b59u2PNwZ49e7BYLPj7+9O9e3fH6EHr1q1xc3NTu9xSlZmZyRtvvMHbb79NQEAAixcvpl27dmqXJQpJwoDKFEVh4sSJzJo1i/fee48XXnhB7ZJUZbFY+O233zCbzaxcuZKbN2/SrFkzjEYjRqOR1q1ba/pKTDiH1NRUduzY4VhzsGvXLrKzs6lWrRrdu3d3jB60a9dOM+Fg7969jBgxgt9//53Jkyczfvx4GSVwIRIGVKQoCpMmTWLmzJm8++67vPjii2qXpAqr1cqWLVswmUysWLGC+Ph4mjRp4hgBCAgIkAAgnFpaWho7d+50TCvs3LmTzMxMKleunCMctG/fvkxvApSZmcn06dN56623aNeuHYsXLyYgIEDtskQBSBhQiaIoTJ48mTfffJM5c+bw0ksvqV1SqbJarWzbts0RAK5evUqjRo0wGAwYDAY6duwoAUC4rIyMDHbt2uWYVtixYwcZGRn4+fkRGhrqWHPQoUOHMrkSPzo6mhEjRnDs2DEmT57MhAkTZJTAyUkYUIGiKLz++uvMmDGD2bNnM27cOLVLKhU2m40dO3ZgNptZtmwZcXFx1K9f3xEA9Hq9BABRJmVmZrJnzx7HtML27dtJS0vD19eXrl27OtYcBAYGlpmTZmZmJjNmzGDWrFm0bduWxYsX0759e7XLErmQMFDKFEVhypQpTJ8+nXfeeYeXX35Z7ZJKlKIo7Nq1yxEALl68SN26dRk2bBgGg4Hg4GDNzKkKcVtWVhbR0dGOcLB161ZSU1Px8fGha9eujmkFvV6Pp6en2uXelZiYGEaMGMHRo0eZOHEir732mst/T2WRhIFSNmXKFMfK21deeUXtckqEoijs3bsXs9mM2Wzm/Pnz1KpVi6FDh2I0GunSpYsEACH+Ijs7m5iYGMeag6ioKJKTkylfvjxdunRxTCsEBQXh5eWldrmFlpWVxYwZM5g5cyZt2rRhyZIlMkrgZCQMlKKpU6cybdo03nrrLV599VW1yylWiqKwb98+RwA4c+YM/v7+jgAQGhpaphdOCVGcLBYL+/fvd6w5iIqKIjExEW9vb0JCQhzTCiEhIXh7e6tdboHFxMQwcuRIjhw5wmuvvcbEiRNllMBJSBgoJbeDwKxZsxg/frza5RQLRVE4ePAgJpMJs9nMyZMnqVatGkOGDMFgMBAWFlYmF0cJUdqsVisHDhxwTCts2bKFhIQEPD09CQ4OdoSDzp07U6FCBbXLzVNWVhZvvvkmM2fO5N5772XJkiV06NBB7bI0T8JAKZg2bRpTp04tM0Hg8OHDjgBw/PhxqlSpwuDBgzEYDPTo0aPMLIASwlnZbDYOHjzomFaIjIzkxo0blCtXjqCgIMeagy5dulCxYkW1y72jffv2MXLkSA4fPsyECROYNGmSjBKoSMJACXvjjTeYMmUKM2fOZMKECWqXU2THjh3DbDZjMpk4cuQIfn5+DBo0CIPBwH333Sf/EwuhIpvNxpEjRxzTCpGRkVy/fh0PDw86derkWHPQtWtXfH191S7XISsri1mzZjFjxgxatWrFkiVL6Nixo9plaZKEgRI0ffp0Xn/9dd58801ee+01tcsptBMnTjgCwMGDB/H19eXhhx/GaDTSu3dvl1zIJIQWKIrCsWPHcoSDK1eu4O7uTseOHR3TCqGhofj5+aldLvv372fEiBEcOnTIMUogv19Kl4SBEjJjxgwmT57MjBkzmDhxotrlFNjp06cdiwD37duHj48PDz30EEajkfvvv9+lFisJIewUReHEiROOYBAZGcmlS5dwc3OjQ4cOjmmFbt26UaVKFVVqzM7OZtasWUyfPp2WLVuyZMkSAgMDValFiyQMlIA333yTSZMmMX36dCZNmqR2Ofk6d+6cIwDs3buXChUq0L9/f4xGI3379qV8+fJqlyiEKEaKonDq1ClHMIiIiODChQvodDoCAgIc0wrdunWjWrVqpVpbbGwsI0eO5MCBA4wfP57JkyfLKEEpkDBQzGbOnMnEiRN54403mDx5strl5OrChQssW7YMs9nMrl278Pb25sEHH8RgMPDggw/i4+OjdolCiFKiKApnz57NMa1w9uxZANq2beuYVujevTv+/v4lXk92djZvvfUW06dPp3nz5ixZsoROnToV7cUUBU6fhosXwWKBihWhdWv7Z+FQ4PO3UgCJiYkKoCQmJhbk8DJn5syZCqBMmzZN7VLu6NKlS8oHH3ygdOnSRQEULy8vZeDAgcq3336rJCcnq12eEMKJnD17Vlm6dKkyatQopWnTpgqgAErr1q2Vp556SjGZTMqVK1dKtIbY2FilY8eOiru7uzJhwgQlIyOjYE/MzlaUH35QlL59FcXXV1HskeDPD51OUe65R1Fee01Rzp0r0e/BVRT0/C1hIB+zZs1SAGXq1Klql5JDXFycMm/ePKVbt26KTqdTypUrpwwYMED56quvNPnvJIQomgsXLihff/21MmbMGKVZs2aOcNCyZUvl8ccfV7777jvl8uXLxf6+WVlZyowZM5Ry5cop9957r7J79+68n7BmjaLUqWM/6bu7/zMI/PXD3d0eDEaMUJSbN4u9dlciYaAYvPXWWwqgTJkyRe1SFEVRlKtXryqffvqpEh4eruh0OsXDw0Pp27evsmTJEiUhIUHt8oQQZcClS5eU7777TnniiSeUli1bOsJBs2bNlDFjxihff/21cuHChWJ7vwMHDigdO3ZU3NzclPHjx/9zlCAtTVH++1/7Sd7NLe8QcKdQUKOGomzeXGz1uhoJA3fpdhB4/fXXVa3j+vXryoIFC5RevXopbm5uiru7u9KnTx9l4cKFyo0bN1StTQhR9l25ckUxm83KU089pbRu3doRDpo2baqMGjVKWbp0qXL27Nm7eo+/jxLs2rXL/oXUVEXp3r3wIeCvH25uiuLhoSirVxfD34brKej5WxYQ3sE777zDq6++yuTJk5k2bVqpb7+bkJDADz/8gNlsZtOmTSiKQo8ePTAYDAwePJjq1auXaj1CCHHb9evX2bJli2NR4sGDBwFo1KiR41bG8PBwGjVqVOjfnYcOHWLEiBHs27ePl8eN482jR3H/6Sew2e6uaJ0OPDxg2zbQ6+/utVyM3E1QRLNnz+aVV14p9SCQmJjIqlWrMJvN/PLLL1gsFsLCwjAYDAwZMoQaNWqUSh1CCFEYN27cICoqynG3QmxsLIqiUL9+fUcwCAsLo2nTpgX6fWqxWJg9eza/T57MYqs11+MygdeBr4AEoB0wA+id2xPc3aFpUzhwADR0S6OEgSKYM2cOL7/8MpMmTeKNN94o8SCQlJTEmjVrMJlMbNiwgezsbEJDQx0BoHbt2iX6/kIIUdwSEhKIiopy9DrYt28fNpuNOnXqOIJBWFgYzZs3z/137K1bWOvVQ5eaSm6bov8bWA48DzQDlgB7gN+A0NyK0+lg2jRw4tvDi5uEgUJ69913GTduHBMnTmT69OklFgRSUlJYu3YtJpOJn3/+mczMTDp37ozRaGTo0KHUrVu3RN5XCCHUkJiYyNatWx3hIDo6GqvVSq1atXJMK7Rs2fLP37sffgjPP2+f9b+D3UAwMBsY98djGUAboAawPa+CqlWDy5dBI/uuSBgohPfee4+XXnqJ1157jRkzZhR7EEhNTWXdunWYTCZ++uknMjIyCAoKcgSABg0aFOv7CSGEs0pOTmbbtm2ONQd79+7FYrFQo0YNunfvTnh4OI+98w6eFy6gy+X09ArwHnAT+OsZaRbwGnAeqJ9XESYTGAzF8w05Oe2EAUWBQ4dg927Ytw+uXbM/Vq0adOhgXyzSoYN9eOgO5s6dy4svvsiECRN48803iy0IpKen8/PPP2M2m1mzZg1paWkEBgZiMBgwGAw0atSoWN5HCCFcWUpKCjt27HCsObiwaxfnLJY8n9MbuAQc+dvjm4FewGpgQG5P9vCA0aNh/vy7rNw1FPT87VGKNRWvrCxYutQ+nHTokP2xcuXsbSnB/g9usdiDwT33wLPP2n8AKlRwvMTtIDB+/PhiCQIZGRls2LABs9nM6tWrSUlJoX379kyaNAmDwUDTpk3v6vWFEKKsqVixIr1796Z3b/vSv8zly2HYsDyfEwfcaUXV7ccu5/VkiwV27SpCpWWba4aBmBh45BE4ehTc/rK8JDv7zn8+dQqeew7eew++/BK6deP999/nxRdf5NVXX2XmzJlFDgJZWVls3LgRs9nMjz/+SFJSEm3btuXVV1/FYDDQvHnzIn6TQgihPV4XLth/r+dxO2E6cKf7Abz/8vU8nTxZtOLKMNcLAwsXwtixfw77F+T+09szIRcuQFgYUQMG8MLq1bzyyivMmjWr0EEgOzubzZs3YzKZWLVqFbdu3aJVq1a8+OKLGAwGWrVqVchvSgghBACZmfmGgfLYby38u4y/fD1PWVlFq60Mc60wsHAhPPZY0Z//xz2r3Vav5qfu3en71lsFDgIWi4XffvsNs9nMypUruXnzJs2bN+eZZ57BYDDQunXrUm9OJIQQZU758vle5NXGvmbg7+L++Fwnv/fQUJ+BgnKdMBATYx8RKCb9tmyBX3+F++7L9Rir1UpkZCRms5kVK1YQHx9P06ZNeeKJJzAYDLRr104CgBBCFKcWLfINA+2x9xNIIufdBLv+8vU8tWxZtNrKMNcIA1lZ9jUCuZx49wBLsf9wnAWqASHYu1HlOmPv5gbDh9vXHfj6Oh62Wq1s3boVs9nM8uXLuXbtGo0aNWLUqFEYjUY6dOggAUAIIUpKYGC+hwwF5gAL+LPPQCawGHv/gTxvKyxXDoKD767GMsg1wsCXX9pP2rl4G9gGDMPekvIKMA/oCOzE3ojiH2w2iIuDjz7CNn4827dvdwSAuLg46tevz6OPPorRaKRTp04SAIQQojT4+0O7dva7xHIZIQjG/vt+AnANuAf7BeFZYGF+r5+dDX36FFu5ZYXz9xlQFPsPxpEjuf5gbAc6AX/tJ3UCaIs9QX6d20sDST4+tPXz48Lly9StW5dhw4ZhMBgIDg7GzS23RphCCCFKzBdfwJgxeR6SAUzG/vv99t4E04H783vtOnXg/Hn7XgUaUHaaDh06BG3bFumptwebovM57qOBA+nw0kt06dJFAoAQQqgtNRUaN4YbN+5+x8K/mzvX3upYIwp6/nb+M18Rm0MowFUgv81+FXd3ngkOJjQ0VIKAEEI4Ax8f+91jxRkEPDygUyd45pnie80yxPnPfjEx9gUfhfQN9ltPjPkcp1MU+3sIIYRwHgMG2G8lL471Wm5u9tsJv/xSM9MDheX8YeD69T9bDBfQMeBpoDMwPL+DbTa4cqVotQkhhCg5n34KgwbdXSBwdwdvb1i/HqQhXK6cPwzkv6QhhyvAg4Af9r2uC5QBC/keQgghSoGHh32HwVdftQeCwl7Vu7lBkyawdSuEhpZMjWWE84eBqlXtPxAFkAj0BW4B6ylAFyqw/7D4+xe1OiGEECXJwwNmzYIdO6BNmz8fy4UC9uDg7W0PEQcO2HeuFXly/jDQoUOBpgkysG9Z+TuwFri3oK+v00HHjkUuTwghRCkIDrZvU79zp71h3D33/GP6IBk437ixfTfbq1dh5kx7KBD5cv6mQ3p9vsP4VuwLBXcAP2JfK1BgVqt9hakQQgjnptPZQ8HtDoIpKXDpkv33eMWKPPTf/1K1WjVW/O9/6tbpgpw/DHTsCE2bwunTuYaCl4DV2EcGbvLPJkOP5PX61apBz57FUakQQojSVLGifS+DP+iDgvj+++9VLMh1Of80gU4Hzz6b5yH7//i8Bnj0Dh+5cneHJ58ET8+8jhJCCOECgoKCuHDhAlfkDrFCc/4wADB6NDRokOtK0gjsi0Zy+7gjnQ4qV9ZUJyohhCjL9Ho9AHv27FG5EtfjGmHAx8feLMJqLb7XVBR7/+tq1YrvNYUQQqimQYMG+Pv7SxgoAtcIAwDdu8Pbbxff640bBwMHFt/rCSGEUJVOpyMoKEjCQBG4ThgAeOUV+/2mYO8PUFi3b0MZNw7eeaf46hJCCOEU9Ho9u3fvpgB78Im/cK0wADB+PPzyC9SqVbhA4O4OVarAypUwe3bx9LsWQgjhVPR6PTdv3uTMmTNql+JSXC8MAPTqBUePwvTpULu2/TF39xzhQHFzI/v2f1SrBhMmwO+/2/tcCyGEKJNkEWHR6JQCjKUUdD9kVVgssGUL7N4N0dH2rlM2G0r16rz58880+89/MH72mdw+KIQQGtG4cWMGDx7Mu+++q3Ypqivo+dv5mw7lx8PD3jTob42DdEBEr17sTUjAKEFACCE0Q6/Xy8hAIbnmNEEBhYSEsHPnTllIIoQQGqLX64mJicFanLejl3FlPgxcvXqVc+fOqV2KEEKIUhIUFERqaipHjx5VuxSXUabDQPAfm1ns2rVL5UqEEEKUlo4dO6LT6di9e7fapbiMMh0G/P39adq0KTt37lS7FCGEEKXE19eXVq1aybqBQijTYQDsowMSBoQQQlukE2HhlPkwEBISQkxMDJmZmWqXIoQQopTo9XpiY2PJyMhQuxSXoIkwkJWVxf79+9UuRQghRCnR6/VYLBZiY2PVLsUllPkwEBAQgJeXl0wVCCGEhrRr1w5PT0+ZKiigMh8GPD09CQwMlDAghBAa4uXlRUBAgNxRUEBlPgzAn82HhBBCaId0Iiw4zYSBs2fPcvXqVbVLEUIIUUr0ej3Hjx8nKSlJ7VKcnmbCAEjzISGE0JKgoCAURSE6OlrtUpyeJsJAvXr1qF27tkwVCCGEhrRo0YKKFSvKuoEC0EQY0Ol0sm5ACCE0xt3dncDAQFk3UACaCANgnyrYvXu37GIlhBAaIp0IC0ZTYSA1NZXDhw+rXYoQQohSotfrOX/+vCwgz4dmwkBgYCDu7u4yVSCEEBqi1+sBZHQgH5oJAz4+PrRr107CgBBCaEjDhg3x9/eXMJAPzYQBkOZDQgihNTqdDr1eL3cU5ENzYeDo0aPcunVL7VKEEEKUktudCBVFUbsUp6WpMBAcHAzI3JEQQmhJUFAQN27c4OzZs2qX4rQ0FQaaNWtGlSpVZKpACCE0RBYR5k9TYcDNzY3g4GAJA0IIoSH+/v40bNhQ1g3kQVNhAP5cRChzR0IIoR2yg2HeNBkGbt68ycmTJ9UuRQghRCkJCgoiOjpautDmQnNhICgoCECmCoQQQkP0ej2pqakcPXpU7VKckubCQJUqVWjZsqWEASGE0JDAwEB0Op1MFeRCc2EApPmQEEJoja+vL61atZIwkAtNhoHg4GAOHDhAWlqa2qUIIYQoJdKJMHeaDAMhISFYLBZiYmLULkUIIUQp0ev1HDhwgMzMTLVLcTqaDANt2rShQoUKMlUghBAaEhQURHZ2NrGxsWqX4nQ0GQY8PDzQ6/USBoQQQkPatWtHuXLlZKrgDjQZBkAWEQohhNZ4eXkREBAgiwjvQNNh4NKlS1y8eFHtUoQQQpQS6UR4Z5oNA7d3MJTRASGE0I6goCCOHTtGUlKS2qU4Fc2Ggdq1a9OgQQMJA0IIoSF6vR5FUYiOjla7FKei2TAA9qmCXbt2qV2GEEKIUtKyZUt8fHxkquBvNB8G9u7dS3Z2ttqlCCGEKAXu7u506tRJwsDfaD4MZGRkcODAAbVLEUIIUUqkE+E/aToMdOjQgXLlysm6ASGE0BC9Xs/58+e5du2a2qU4DU2HAW9vbzp06CBhQAghNOT2VvYyVfAnTYcBkOZDQgihNQ0bNqR69eoyVfAXEgZCQjh58iTx8fFqlyKEEKIU6HQ6aT70N5oPA7ebD8kthkIIoR23w4CiKGqX4hQ0HwYaN26Mv7+/hAEhhNCQoKAg4uPjOXfunNqlOAXNhwGdTifrBoQQQmP0ej2ArBv4g+bDAPzZidBms6ldihBCiFJQo0YNGjRoIOsG/iBhAHsYSEpK4tixY2qXIoQQopQEBQVJGPiDhAHsw0U6nU6mCoQQQkP0ej179+7FarWqXYrqJAwAvr6+tGnTRsKAEEJoiF6vJzU1VUaFkTDgIIsIhRBCWwIDA9HpdDJVgIQBh+DgYA4dOkRycrLapQghhCgFlSpVomXLlnJHARIGHEJCQlAURRKiEEJoiHQitJMw8IdWrVpRqVIlaT4khBAaEhQURGxsLJmZmWqXoioJA39wc3MjKChI1g0IIYSG6PV6srOziY2NVbsUVUkY+IvbiwilV7UQQmhDQEAA5cqV0/xUgYSBvwgJCeHatWucPXtW7VKEEEKUAi8vL9q1aydhQO0CnMntHQxlqkAIIbRDOhFKGMihevXq3HPPPRIGhBBCQ/R6PUePHtX0reUSBv4mODhYwoAQQmiIXq9HURSio6PVLkU1Egb+JiQkhH379pGRkaF2KUIIIUpBq1at8PHx0fRUgYSBvwkJCSE7O5v9+/erXYoQQohS4O7uTmBgoKY7EUoY+Jt27drh7e0tUwVCCKEhWu9EKGHgbzw9PQkMDJQwIIQQGhIUFMS5c+e4du2a2qWoQsLAHcgOhkIIoS16vR5As6MDEgbuICQkhHPnzhEXF6d2KUIIIUpBo0aNqFatmoQB8aeQkBAA2bRICCE0QqfTaXrdgISBO6hXrx516tSRqQIhhNCQ250Itbg/jYSBXMi6ASGE0Ba9Xs/169c5d+6c2qWUOgkDuQgJCWHPnj1YLBa1SxFCCFEKtLyIUMJALkJCQkhLS+Pw4cNqlyKEEKIU1KxZkwYNGkgYEH8KDAzE3d1dpgqEEEJD9Hq9JjsRShjIRYUKFQgICJAwIIQQGqLX64mOjsZqtapdSqmSMJAHWUQohBDaEhQUREpKCsePH1e7lFIlYSAPISEhHDt2jISEBLVLEUIIUQoCAwPR6XSamyqQMJCH4OBgAM39UAghhFZVqlSJFi1aaG4RoYSBPDRr1owqVarIVIEQQmiIFjsRShjIg06nk3UDQgihMUFBQcTGxpKZmal2KaVGwkA+QkJC2LVrlybbUwohhBbp9XqysrI4cOCA2qWUGgkD+QgJCSEhIYETJ06oXYoQQohSEBAQgIeHh6amCiQM5CMoKAhApgqEEEIjvL29CQgIkDAg/lS5cmVatWolYUAIITREa50IJQwUgCwiFEIIbdHr9Rw9epTk5GS1SykVEgYKIDg4mAMHDpCamqp2KUIIIUpBUFAQiqIQExOjdimlQsJAAYSEhGC1WomOjla7FCGEEKWgVatW+Pj4aGaqQMJAAbRu3RofHx+ZKhBCCI1wd3enY8eOmllEKGGgADw8PNDr9ezatUvtUoQQQpSSoKAgCQMip5CQEHbs2CHNh4QQQiP0ej1nz57l+vXrapdS4iQMFFBISAhxcXFcvHhR7VKEEEKUAr1eD6CJ0QEJAwV0ewdDWTcghBDa0LhxY6pVqyZhQPypVq1aNGrUSMKAEEJohE6n08wOhhIGCiE4OFjCgBBCaMjtToRlfb2YhIFCCAkJITo6mqysLLVLEUIIUQr0ej3Xr1/n/PnzapdSoiQMFEJISAiZmZnExsaqXYoQQohSoJVFhBIGCqFDhw54enrKVIEQQmhErVq1qF+/fpnvRChhoBC8vLzo0KGDNB8SQggN0cIiQgkDhSQ7GAohhLYEBQURHR2N1WpVu5QSI2GgkEJCQjh16pQmOlIJIYSwjwwkJydz/PhxtUspMRIGCikkJARApgqEEEIjAgMDgbK9iFDCQCE1bNiQGjVqyFSBEEJohJ+fHy1atJAwIP6k0+lk3YAQQmhMWd/BUMJAEYSEhLB79+4yvZhECCHEnzrp9ey7fJllcXEsjItjUVwca+LjuZSZWSa6E3qoXYArCgkJITk5maNHj9KmTRu1yxFCCFFCjqel8emlSyxq25bsr77CcIdFhDXLlWNk7do8Xrs2jcqXV6HKuycjA0XQqVMn3NzcZBGhEEKUUbeysxl57Bgtd+/m40uXSM7j2KvZ2cw+f54mu3bx/IkTpLngqLGEgSLw9fWlTZs2sm5ACCHKoB2JibTcvZuvrlwBwFKA51gBBfjo0iVa79nDoZSUkiyx2EkYKCJZRCiEEGVP1K1b9IyN5Xp2NkW5vrcBFzIy6LpvH7EuFAgkDBRRSEgIhw8fJikpSe1ShBBCFIOz6en0PXCALJsN2128jhVItVrpFRtLvIvscisLCIsoODgYRVHYs2cP9913n9rlCCGEuAuKojDy+HEycwsCb70FGzbk/gJmM/j7O/7TCiRkZ/O/Eyf4vnXr4i632EkYKKKWLVtSqVIldu7cKWFACCFc3PfXrhFx61buBwwYAH90InRQFJg7F2rWzBEEbrMCpuvXGZOQwH1VqhRrvcVNwkARubm5ERwcLOsGhBCiDJh78SJukPv0QOvW9o+/OngQMjKgV69cX9cD+ODiRQkDZVlISAifLFzI9lu3uGGxoADVy5WjnY8PFT3kr1YIIVzBwZQU9iTndfNgLjZtAp0uzzBgAdbeuMHlzEzqeHkVvcgSJmesIriRnc3iuDiWhIdzIzycrvv35/i6DmhevjzDa9VidO3a1PD0VKVOIYQQ+duSmIgO+62BBWaxQESEfbSgVq08D1WA7YmJDK1Ro+hFljC5m6AQsm02pp89S53t23n19GkuuLmB2z//ChXgeHo6k86coe6OHUw8fZpM292sTRVCCFFSopOTcdfpCvekPXsgKSnPUYHbyul07C3KyEMpkjBQQKfS0wmMjmbK2bNkKUqBbjuxARZFYdb58wTs2cPR1NSSLlMIIUQhnU5Px1LY/QU2bQIPDwgPz/dQq6JwLjOzaMWVEgkDBfB7WhohMTEcTUsr3DDSHxTgZHo6XWJiOOhCTSiEEEILsgobBNLTYft20OvBzy/fwxXsI8vOTMJAPm5lZ9Nz/34SsrMLnxz/wgokW632zlYu0oRCCCG0wNfdvXBP2Lo137sI/sod8Cnse5QyWUCYj+dPnuRKVtad21Lu3w8vvHDnJ378Mdx7b46HbjeheOr331kmux0KIYRTaOvjw2+3bpFd0Au+TZugfHno0qVAh9uANj4+RS+wFEgYyMOWW7dYevVq/gcOHgwtW+Z8rG7dOx5qBZbHx/PzjRv0rVbt7osUQghRZIqiUPXGjYIHgVu3IDoaevYEb+8CPcUGBPr6FrnG0iBhIA9zL17EQ6fLf3qgXTsICyvw67oD7128KGFACCFUoCgKBw8exGQyYTabOXntGqxYYV8QmJ/ffgOrtcBTBAB+7u50rlTpLiouebJmIBdxmZmsjo8v+DqBtDT7D0gBWIFNCQmcTk8veoFCCCEK5fDhw0yZMoVWrVoREBDAp59+SlhYGBuXL+eR2rXxKMjthZs2QZUq/2xNnAt3YGydOpSXNQOuaWtiYsF3rXr7bfvqUjc3+yjBE09Aixb5Pi3y1i2alC9/V3UKIYTI3fHjxzGbzZhMJg4fPoyfnx+DBg1i7ty59OrVi3LlygFQKyWF765fz/8FP/64UO9fzs2N/+UybexMJAzkIjo5Of8pAg8P6N4dgoPtt5ecOwcmEzz7LMybB82a5frUcjod0cnJjKxduwSqF0II7Tp16pRjCiA2NhZfX18efvhhZs2aRZ8+ffC6Q1vgthUrMrFhQ6afO1ekW8hzM6dpUxoUcG2BmiQM5OJkejrW/KYI2rSxf9zWtat97cDo0fD55/DOO7k+NVtROCnTBEIIUSzOnj2L2WzGbDYTHR2Nj48PAwYMYOrUqTzwwAN4F+CEPLFhQ368dInYzEy4y2F9N+CBqlV5sk6du3qd0iJhIBdZilK0dFi3rj0UREXZ1xDk8QOV4eRNKIQQwplduHCBZcuWYTKZ2L17N+XLl+fBBx9k/Pjx9OvXjwoVKhTq9c6cOEHc8OGUf/11Mhs2LPhU8d+4AT0rV2Z569a4FbbNsUokDOSigptb3ttZ5sXfH7Kz7U0p8ri31NmbUAghhLO5fPkyy5cvx2QysX37dry8vOjbty/fffcd/fv3p2LFikV63RMnTtCjRw+qV6nCzvvvZ1ZiIp/HxRXqPOD+x7Ev1a/P9MaN8brD3jXOSsJALlr5+OAWH4+tKF0H4+LA09PelCIXbjYbVZKSyM7OdixgEUII8U9Xr15lxYoVmEwmoqKi8PDw4P777+err77ioYceotJd3rZ36tQpevTogZ+fH7/++is1a9ZkQa1aDPP35+VTp4hNTc1zDdntrwVXqsScpk3pXIAWxc5GwkAuAitWzP+2wlu3oHLlnI+dPGnvWR0UdMcdDW+zubnxzeuvs2r3bkJDQwkLCyM8PJxOnTpJOBBCaF58fDwrV67EZDIRERGBm5sbvXv3ZtGiRQwcOJDKf//dW0RnzpyhR48e+Pj4OILAbb2rVmV/1arsSUrim6tX2ZmURGxqqmOKt4KbGx0rVqSznx+P1qxJ2yKOSjgDnaLkf+mblJSEn58fiYmJd53AXEWixUKt7dvzntd/8UX7CECbNvZQcO4crF1rXyfw8cfQsGGuT/XQ6Vjt6cmBLVuIiIhg69atpKSkUKFCBbp27eoIB3q9Hk9Pz+L/BoUQwsncvHmTH374AbPZzObNmwHo2bMnBoOBQYMGUa2YG7WdO3eO8PBw3N3diYyMpG4BbgFUFIUMmw03nQ5PnQ6dk68JKOj5W8JAHh4/fpxFV67kPkKwYoW9AcXly5Caag8EHTvC8OG5tiMG+3CMoUYNvvnL3gUWi4WYmBgiIyOJiIggKiqK5ORkypcvT5cuXRzhICgo6I63xQghhCtKTExk1apVmM1mNm7ciNVqJTw8HIPBwJAhQ/D39y+R971w4QLh4eEoikJkZCT169cvkfdRm4SBYnAkNZV2e/bceZOiu6BTFHYHBtIpj79Li8XC/v37c4SDxMREvL296dy5syMcBAcHF+iWGSGEcBbJycmsXr0as9nM+vXryc7OJjQ0FIPBwNChQ6lVq1aJvv+lS5cIDw8nOzubyMhIGuYxiuvqJAwUk2lnzzLt7Nnia0KhKGAy0f/SJRYsWEDtAjYdslqtxMbGOsLBli1buHXrFl5eXoSEhBAeHk5YWBghISGUl66GQggnk5qaytq1azGZTKxbt47MzEw6d+6M0Whk6NChBRqiLw5xcXGEh4eTnp5OZGQkjRs3LpX3VYuEgWKSbbMRum8f0cnJdz1C4IH9LoXX4+L439ixZGVlMW/ePP79738Xet7JarVy8ODBHOHg5s2beHp6Ehwc7AgHnTt3LvS9tkIIURzS09NZt24dJpOJtWvXkp6ejl6vx2g0MmzYMBo0aFCq9Vy9epXw8HCSk5OJjIykadOmpfr+apAwUIwSsrPpGRvLwZSUIgcCd6B5hQpEtG9PDU9Pbty4wTPPPMN3333HoEGDmD9/PjVq1ChyjTabjUOHDjnCQWRkJDdu3KBcuXIEBQU5wkGXLl3wcfJ9tYUQrisjI4MNGzZgMplYvXo1qampdOjQAaPRiMFgUO1K/Nq1a/To0YOEhAQiIyNplke7+LJEwkAxS7JYeOr33/nm2jV0UOBpg9vHDq5enc9btKDq324bXLFiBU888QQAn3zyCcOGDSuWem02G0eOHMkRDq5fv46Hhwd6vd4RDrp27VrkJh1CCAGQlZXFxo0bMZvN/PjjjyQlJdGuXTsMBgMGg0H1E298fDw9e/bk2rVrREZG0qIAG8mVFRIGSsjq+HhePHmSUxkZeACWXI5zx75VcQMvL2Y3bYohj6v+a9eu8dRTT7FixQqMRiPz5s2jevXqxVq3oigcPXo0Rzi4evUq7u7udOrUybEgsWvXrpr/NxZC5C87O5vNmzdjNpv54YcfuHXrFq1atXKMALRq1UrtEgH77Yo9e/YkLi6OiIgIp6mrtEgYKEGKovDrrVt8deUK2xITOZWRkWOkoLG3N10qVeLRWrXoXaVKgXpTK4qCyWTi6aefxsPDg88++4yBAweW6Pdw/PjxHOEgLi4ONzc3AgMDHeEgNDQUPxfspiWEKH4Wi4WIiAjMZjMrV67kxo0bNGvWDKPRiNFopM1fN25zAgkJCfTq1Yvz588TERFB69at1S6p1EkYKEWpVisJ2dkoQBUPDyp6FL2x45UrV3j88cdZvXo1jzzyCB9++CFVqlQpvmJzoSgKJ06cyBEOLl26hJubGx06dHCEg27duhVb5y8hhPOzWq1ERUVhMplYsWIF169fp0mTJo4RgICAAKdsvHPr1i169+7NmTNn+PXXX2nXrp3aJalCwoALUxSFr7/+mmeeeYYKFSrw+eef8+CDD5Z6DadOnXKEg4iICC5evIhOp6N9+/Y5wkHVqlVLtTYhRMmy2Wxs374dk8nE8uXLuXLlCg0bNnSsAQgMDHTKAHBbUlISffr04ffff+fXX3+lffv2apekGgkDZcClS5cYM2YMP//8MyNHjmTu3LmqDdkrisKZM2dyhIPz58+j0+lo166dIxx079692FuGCiFKnqIo7Nq1C5PJxLJly7h06RJ169Z1BIDg4GCnDgC3JScn88ADD3DkyBE2b95Mx44d1S5JVRIGyghFUVi8eDHPP/88fn5+LFy4kD59+qhdFgBnz57NMa1w5swZANq2bZsjHJRUO1EhxN1RFIW9e/diNpsxm82cP3+eWrVqMWzYMIxGI507d8bNhbbhTUlJoW/fvhw4cIBNmzah1+vVLkl1EgbKmPPnzzN69Gg2bdrE2LFjmTNnDr6+vmqXlcP58+dzhINTp04B0Lp1a0c4CAsLu6t+CkKIu6MoCvv373cEgNOnT+Pv78/QoUMxGo2Ehobi7u6udpmFlpqaSr9+/di3bx8bN24kJCRE7ZKcgoSBMkhRFBYsWMBLL71E9erVWbRoET179lS7rFxdvHgxRzg4ceIEAK1atcoRDkq6D7kQWqcoCocOHcJsNmMymThx4gTVqlVj8ODBGI1GwsLC8LiLhc9qS0tLo3///uzZs4cNGzbQpUsXtUtyGhIGyrAzZ84watQoIiIiePrpp3n77bddoqvg5cuXc4SD48ePA9CiRYsc4aBOnToqVypE2XD06FFHADh69CiVK1dm0KBBGI1GevbsSbm/NUFzRenp6Tz00ENs376d9evX061bN7VLcioSBso4m83Gxx9/zKuvvkrt2rVZsmSJy/1PEBcXx5YtWxzh4OjRowA0a9YsRzioV6+eypUK4TpOnDiByWTCbDZz8OBBKlWqxMCBAzEYDPTu3RtPT0+1Syw2GRkZDBo0iMjISNatW0d4eLjaJTkdCQMacfLkSUaMGMH27dt5/vnnefPNN11218KrV6/mCAeHDx8GoGnTpjnCQWlvbiKEszt9+rRjDcC+ffuoWLEiDz30EAaDgfvvv79MbnOemZnJkCFD2Lx5M2vXruW+++5TuySnJGFAQ6xWKx988AGvvfYaDRs2ZOnSpWVi8cz169dzhIODBw8C0Lhx4xzhoFGjRuoWKoQKzp8/75gC2Lt3LxUqVKB///4YjUb69u3rshcFBZGVlcWwYcPYsGEDq1evdpo7rJyRhAENOnbsGMOHD2fv3r2MGzeOadOmlakrgvj4eKKiohzhIDY2FoCGDRs6gkF4eDiNGjVyifuhhSisS5cusWzZMkwmEzt37sTb25t+/fphNBp58MEHXWLt0N3Kzs7GaDTy008/sWrVKvr27at2SU5NwoBGWSwW5syZw5QpU7jnnntYunQpnTp1UrusEnHz5s0c4WD//v0oikL9+vVzhIMmTZpIOBAuKy4ujhUrVmAymdi6dSuenp488MADGI1GBgwY4HS3GJcki8XCv//9b3788UdWrlxJ//791S7J6UkY0LhDhw4xfPhwYmNjmTBhApMnTy5TC4fuJCEhga1btzrCwb59+7DZbNStWzfHtEKzZs0kHAindu3aNVauXInJZCIyMhJ3d3f69OmD0WjkoYce0uT+IBaLhUcffZTly5ezfPlyHn74YbVLcgkSBgTZ2dm89dZbvPHGG9x7770sXbpUUz26ExMTc4SD6OhobDYbtWvXzhEOWrRoIeFAqO7GjRusXLkSs9nMr7/+ik6no1evXhgMBgYOHKjpPUCsVivDhw/n+++/x2w2M3jwYLVLchkSBoTD/v37GT58OEeOHGHy5MlMmDChTNxfXFhJSUls27bNEQ727t2L1WqlZs2aOcJBq1atJByIUpGQkMCqVaswm81s2rQJm81Gjx49MBgMDB48mOrVq6tdouqsViujRo3im2++4bvvvmPYsGFql+RSJAyIHLKyspgxYwYzZ84kICCApUuXOt3e46UtOTmZ7du3O8LBnj17sFgs1KhRg+7duzvCwb333utS/dmFc0tKSuLHH3/EZDKxceNGLBYL3bt3x2AwMGTIEGrWrKl2iU7DZrMxZswYlixZwtdff82///1vtUtyORIGxB3t3buX4cOHc/LkSaZOncrLL7/s0m1Ii1NqamqOcLB7926ys7OpXr16jnDQpk0bCQeiUFJSUlizZg0mk4n169eTmZlJ165dMRqNDBkyRLpu3oHNZuOJJ57giy++4Msvv+SRRx5RuySXJGFA5CojI4Np06bxzjvv0KlTJ5YsWUKrVq3ULsvppKWlsWPHDkc42LVrF1lZWVStWjVHOGjXrp2EA/EPqamprFu3DpPJxE8//URGRgbBwcEYjUaGDh1K/fr11S7R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" + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "def classical_solver(graph):\n", + " num_nodes = len(graph)\n", + " max_cut = [0]\n", + " best_case = [0] # \"01\" series with max cut\n", + " for i in range(2**num_nodes):\n", + " case = f\"{bin(i)[2:]:0>{num_nodes}}\"\n", + " cat1, cat2 = [], []\n", + " for j in range(num_nodes):\n", + " if str(case)[j] == \"0\":\n", + " cat1.append(j)\n", + " else:\n", + " cat2.append(j)\n", + "\n", + " # calculate the cost function\n", + " cost = 0\n", + " for node1 in cat1:\n", + " for node2 in cat2:\n", + " if graph[node1].get(node2):\n", + " cost += graph[node1][node2][\"weight\"]\n", + " cost = round(cost, 4) # elimate minor error\n", + " if max_cut[-1] <= cost:\n", + " max_cut.append(cost)\n", + " best_case.append(case)\n", + "\n", + " # optimal cases maybe more than 1, but they are all at the end\n", + " index = max_cut.index(max_cut[-1])\n", + "\n", + " return max_cut[-1], best_case[index:]\n", + "\n", + "\n", + "max_cut, best_case = classical_solver(graph_dict)\n", + "print(\"bit string:\", best_case, \"\\nmax cut:\", max_cut)\n", + "\n", + "colors = [\"r\" if best_case[0][i] == \"0\" else \"c\" for i in graph.nodes]\n", + "weighted_graph = nx.to_networkx_graph(graph_dict)\n", + "nx.draw_networkx(weighted_graph, with_labels=True, node_color=colors, pos=pos)\n", + "ax = plt.gca()\n", + "ax.set_facecolor(\"w\")" + ], + "metadata": { + "collapsed": false, + "ExecuteTime": { + "end_time": "2023-07-15T07:42:29.489809800Z", + "start_time": "2023-07-15T07:42:29.368921400Z" + } + } + }, + { + "cell_type": "markdown", + "source": [ + "## QAOA Ansatz" + ], + "metadata": { + "collapsed": false + } + }, + { + "cell_type": "code", + "execution_count": 46, + "outputs": [], + "source": [ + "def QAOAansatz(params, each=1, return_circuit=False):\n", + " n = graph.number_of_nodes() # the number of nodes\n", + "\n", + " # PQC loop\n", + " def pqc_loop(s_, params_):\n", + " c_ = tc.Circuit(n, inputs=s_)\n", + " for j in range(each):\n", + " # driving layer\n", + " for a, b in graph.edges:\n", + " c_.RZZ(a, b, theta=graph[a][b][\"weight\"] * params_[2 * j])\n", + " # mixing layer\n", + " for i in range(n):\n", + " c_.RX(i, theta=params_[2 * j + 1])\n", + " s_ = c_.state()\n", + " return s_\n", + "\n", + " c0 = tc.Circuit(n)\n", + " for i in range(n):\n", + " c0.H(i)\n", + " s0 = c0.state()\n", + " s = K.scan(pqc_loop, K.reshape(params, [nlayers // each, 2 * each]), s0)\n", + " c = tc.Circuit(n, inputs=s)\n", + "\n", + " # whether to return the circuit\n", + " if return_circuit is True:\n", + " return c\n", + "\n", + " # calculate the loss function\n", + " loss = 0.0\n", + " for a, b in graph.edges:\n", + " loss += c.expectation_ps(z=[a, b]) * graph[a][b][\"weight\"]\n", + "\n", + " return K.real(loss)" + ], + "metadata": { + "collapsed": false, + "ExecuteTime": { + "end_time": "2023-07-15T05:12:41.534488800Z", + "start_time": "2023-07-15T05:12:41.483853500Z" + } + } + }, + { + "cell_type": "code", + "execution_count": 47, + "outputs": [], + "source": [ + "QAOA_vag = K.jit(\n", + " tc.backend.value_and_grad(QAOAansatz, argnums=0), static_argnums=(1, 2)\n", + ")\n", + "QAOA_nograd = K.jit(QAOAansatz, static_argnums=(1, 2))\n", + "\n", + "\n", + "def eval_objective(x):\n", + " \"\"\"This is a helper function we use to unnormalize and evalaute a point\"\"\"\n", + " return -torch.from_numpy(np.asarray(QAOA_nograd(jnp.asarray(x.ravel()))))" + ], + "metadata": { + "collapsed": false, + "ExecuteTime": { + "end_time": "2023-07-15T05:12:41.541255500Z", + "start_time": "2023-07-15T05:12:41.484406100Z" + } + } + }, + { + "cell_type": "markdown", + "source": [ + "## Adam Optimizer" + ], + "metadata": { + "collapsed": false + } + }, + { + "cell_type": "code", + "execution_count": 48, + "outputs": [], + "source": [ + "opt = K.optimizer(optax.adam(1e-2))" + ], + "metadata": { + "collapsed": false, + "ExecuteTime": { + "end_time": "2023-07-15T05:12:41.603802800Z", + "start_time": "2023-07-15T05:12:41.585764800Z" + } + } + }, + { + "cell_type": "markdown", + "source": [ + "## BO Optimizer" + ], + "metadata": {} + }, + { + "cell_type": "code", + "execution_count": 49, + "source": [ + "class BO_optimizer:\n", + " def __init__(self, eval_func, batch_size: int = 1, device=\"cpu\"):\n", + " self.eval_func = eval_func\n", + " self.device = device\n", + " self.batch_size = batch_size\n", + " self.best_dict = {\"X\": None, \"Y\": torch.tensor(-float(\"inf\"))}\n", + "\n", + " def compute_Y(self, X):\n", + " return torch.tensor(\n", + " [self.eval_func(x) for x in X], dtype=X.dtype, device=self.device\n", + " ).unsqueeze(-1)\n", + "\n", + " def update_best(self, X_next, Y_next):\n", + " new_Y, new_idx = torch.max(Y_next, dim=0)\n", + " new_Y = new_Y.squeeze()\n", + " if new_Y > self.best_dict[\"Y\"]:\n", + " self.best_dict[\"Y\"] = new_Y\n", + " self.best_dict[\"X\"] = X_next[new_idx]\n", + "\n", + " def update(\n", + " self,\n", + " X,\n", + " Y=None,\n", + " acqfn: str = \"ucb\",\n", + " normalize: bool = False,\n", + " verbose: bool = False,\n", + " ):\n", + " if Y is None:\n", + " Y = self.compute_Y(X)\n", + " X_next = odbo.run_exp.bo_design(\n", + " X=X,\n", + " Y=Y,\n", + " batch_size=self.batch_size,\n", + " acqfn=acqfn,\n", + " normalize=normalize,\n", + " verbose=verbose,\n", + " )[0].reshape(self.batch_size, X.shape[-1])\n", + " Y_next = self.compute_Y(X_next)\n", + "\n", + " self.update_best(X_next, Y_next)\n", + "\n", + " # Update training set\n", + " X = torch.cat((X, X_next), dim=0)\n", + " Y = torch.cat((Y, Y_next), dim=0)\n", + "\n", + " return X, Y, Y_next.mean()" + ], + "outputs": [], + "metadata": { + "scrolled": false, + "ExecuteTime": { + "end_time": "2023-07-15T05:12:41.603802800Z", + "start_time": "2023-07-15T05:12:41.586291Z" + } + } + }, + { + "cell_type": "code", + "execution_count": 50, + "outputs": [], + "source": [ + "batch_size = 1\n", + "\n", + "bo_opt = BO_optimizer(eval_objective, batch_size, device)" + ], + "metadata": { + "collapsed": false, + "ExecuteTime": { + "end_time": "2023-07-15T05:12:41.603802800Z", + "start_time": "2023-07-15T05:12:41.586291Z" + } + } + }, + { + "cell_type": "markdown", + "source": [ + "## TuRBO Optimizer" + ], + "metadata": { + "collapsed": false + } + }, + { + "cell_type": "code", + "execution_count": 51, + "outputs": [], + "source": [ + "class TuRBO_optimizer(BO_optimizer):\n", + " def __init__(\n", + " self, eval_func, num_params, tr_length, failure_tolerance, device=\"cpu\"\n", + " ):\n", + " super(TuRBO_optimizer, self).__init__(eval_func, device=device)\n", + " self.batch_size = 1 # There is a bug in odbo.run_exp.turbo_design, batch_size can only be 1 right now.\n", + " self.tr_length = tr_length\n", + " self.state = odbo.turbo.TurboState(\n", + " dim=num_params,\n", + " batch_size=batch_size,\n", + " length=tr_length,\n", + " n_trust_regions=len(tr_length),\n", + " failure_tolerance=failure_tolerance,\n", + " )\n", + "\n", + " def inverse_transform(self, X):\n", + " \"\"\"\n", + " Note TuRBO is working on only [0,1] parameter range\n", + " We need to transform parameters from [0,1] to [-pi,pi] before using eval_func\n", + " \"\"\"\n", + " return X * 2 * np.pi - np.pi\n", + "\n", + " def transform(self, X):\n", + " \"\"\"\n", + " Note TuRBO is working on only [0,1] parameter range\n", + " We need to transform parameters from [-pi,pi] to [0,1] before using odbo.run_exp.turbo_design\n", + " \"\"\"\n", + " return X / 2 / np.pi + 0.5\n", + "\n", + " def compute_Y(self, X, transformed_input: bool = True):\n", + " if transformed_input:\n", + " X = self.inverse_transform(X)\n", + " return torch.tensor(\n", + " [self.eval_func(x) for x in X], dtype=X.dtype, device=self.device\n", + " ).unsqueeze(-1)\n", + "\n", + " def get_next(self, X, Y, acqfn, normalize, verbose):\n", + " X_next = odbo.run_exp.turbo_design(\n", + " state=self.state,\n", + " X=X,\n", + " Y=Y,\n", + " n_trust_regions=len(self.tr_length),\n", + " batch_size=self.batch_size,\n", + " acqfn=acqfn,\n", + " normalize=normalize,\n", + " verbose=verbose,\n", + " )[0].reshape(len(self.tr_length) * self.batch_size, X.shape[-1])\n", + " Y_next = self.compute_Y(X_next)\n", + " return X_next, Y_next\n", + "\n", + " def update_state(self, Y_next):\n", + " self.state = odbo.turbo.update_state(\n", + " state=self.state,\n", + " Y_next=Y_next.reshape(len(self.tr_length), self.batch_size, 1),\n", + " )\n", + "\n", + " def preprocess(self, X, Y, transformed_input):\n", + " if not transformed_input:\n", + " X = self.transform(X)\n", + " if Y is None:\n", + " Y = self.compute_Y(X)\n", + " best_Y, best_idx = torch.max(Y, dim=0)\n", + " best_Y = best_Y.squeeze()\n", + " if best_Y > self.best_dict[\"Y\"]:\n", + " self.state.best_value = best_Y.item()\n", + " self.best_dict[\"Y\"] = best_Y\n", + " self.best_dict[\"X\"] = X[best_idx]\n", + " return X, Y\n", + "\n", + " def postprocess(self, X, Y, X_next, Y_next, transformed_output):\n", + " X = torch.cat((X, X_next), dim=0)\n", + " Y = torch.cat((Y, Y_next), dim=0)\n", + " if not transformed_output:\n", + " X = self.inverse_transform(X)\n", + " return X, Y\n", + "\n", + " def update(\n", + " self,\n", + " X,\n", + " Y=None,\n", + " acqfn: str = \"ucb\",\n", + " normalize: bool = False,\n", + " verbose: bool = False,\n", + " transformed_input: bool = False,\n", + " transformed_output: bool = False,\n", + " ):\n", + " X, Y = self.preprocess(X, Y, transformed_input)\n", + "\n", + " X_next, Y_next = self.get_next(X, Y, acqfn, normalize, verbose)\n", + "\n", + " self.update_best(X_next, Y_next)\n", + "\n", + " self.update_state(Y_next)\n", + "\n", + " X, Y = self.postprocess(X, Y, X_next, Y_next, transformed_output)\n", + "\n", + " return X, Y, Y_next.mean()" + ], + "metadata": { + "collapsed": false, + "ExecuteTime": { + "end_time": "2023-07-15T05:12:41.603802800Z", + "start_time": "2023-07-15T05:12:41.586798100Z" + } + } + }, + { + "cell_type": "code", + "execution_count": 52, + "outputs": [], + "source": [ + "failure_tolerance = 10\n", + "tr_length = [1.6]\n", + "\n", + "turbo_opt = TuRBO_optimizer(\n", + " eval_objective, 2 * nlayers, tr_length, failure_tolerance, device\n", + ")" + ], + "metadata": { + "collapsed": false, + "ExecuteTime": { + "end_time": "2023-07-15T05:12:41.645737200Z", + "start_time": "2023-07-15T05:12:41.586798100Z" + } + } + }, + { + "cell_type": "markdown", + "source": [ + "## DARBO Optimizer" + ], + "metadata": { + "collapsed": false + } + }, + { + "cell_type": "markdown", + "source": [ + "Please refer to [Cheng, Chen, Zhang and Zhang](https://doi.org/10.48550/arXiv.2303.14877) for details." + ], + "metadata": { + "collapsed": false + } + }, + { + "cell_type": "code", + "execution_count": 53, + "outputs": [], + "source": [ + "class DARBO_optimizer(TuRBO_optimizer):\n", + " def __init__(\n", + " self,\n", + " eval_func,\n", + " num_params,\n", + " tr_length,\n", + " failure_tolerance,\n", + " mode: Union[bool, str] = True,\n", + " device=\"cpu\",\n", + " ):\n", + " super(DARBO_optimizer, self).__init__(\n", + " eval_func, num_params, tr_length, failure_tolerance, device\n", + " )\n", + " self.switch_counter = 0\n", + " if mode == True or mode == \"large\":\n", + " self.mode = True\n", + " else:\n", + " self.mode = False\n", + " self.best_dict = {\n", + " \"X\": None,\n", + " \"Y\": torch.tensor(self.state.best_value),\n", + " \"mode\": self.mode,\n", + " }\n", + "\n", + " def get_mode(self):\n", + " return \"large\" if self.mode else \"small\"\n", + "\n", + " def compute_Y(self, X, transformed_input: bool = True, mode=None):\n", + " if transformed_input:\n", + " X = self.inverse_transform(X, mode)\n", + " return torch.tensor(\n", + " [self.eval_func(x) for x in X], dtype=X.dtype, device=self.device\n", + " ).unsqueeze(-1)\n", + "\n", + " def inverse_transform(self, X, mode=None):\n", + " \"\"\"\n", + " Note TuRBO is working on only [0,1] parameter range\n", + " We need to transform parameters from [0, 1] to [-pi, pi] or [-pi / 2, pi / 2] before using eval_func\n", + " \"\"\"\n", + " if mode is None:\n", + " mode = self.mode\n", + " if mode: # [0, 1] to [-pi, pi]\n", + " return X * 2 * np.pi - np.pi\n", + " else: # [0, 1] to [-pi / 2, pi / 2]\n", + " return X * np.pi - np.pi / 2\n", + "\n", + " def transform(self, X, mode=None):\n", + " \"\"\"\n", + " Note TuRBO is working on only [0,1] parameter range\n", + " We need to transform parameters from [-pi,pi] or [-pi / 2, pi / 2] to [0,1] before using odbo.run_exp.turbo_design\n", + " \"\"\"\n", + " if mode is None:\n", + " mode = self.mode\n", + " if mode: # [-pi, pi] to [0, 1]\n", + " return X / 2 / np.pi + 0.5\n", + " else: # [-pi / 2, pi / 2] to [0, 1]\n", + " return X / np.pi + 0.5\n", + "\n", + " def update_best(self, X_next, Y_next):\n", + " new_Y, new_idx = torch.max(Y_next, dim=0)\n", + " new_Y = new_Y.squeeze()\n", + " if new_Y > self.best_dict[\"Y\"]:\n", + " self.best_dict[\"Y\"] = new_Y\n", + " self.best_dict[\"X\"] = X_next[new_idx]\n", + " self.best_dict[\"mode\"] = self.mode\n", + " else:\n", + " self.switch_counter += 1\n", + "\n", + " def update(\n", + " self,\n", + " X,\n", + " Y=None,\n", + " acqfn: str = \"ucb\",\n", + " normalize: bool = False,\n", + " verbose: bool = False,\n", + " transformed_input: bool = False,\n", + " transformed_output: bool = False,\n", + " frequency: int = 4,\n", + " ):\n", + " X, Y = self.preprocess(X, Y, transformed_input)\n", + "\n", + " # check if we need to switch the searching parameter range.\n", + " if self.switch_counter >= frequency:\n", + " if self.mode:\n", + " X = X * 2 - 0.5\n", + " self.mode = False # small\n", + " else:\n", + " X = X / 2 + 0.25\n", + " self.mode = True # large\n", + " self.switch_counter = 0\n", + "\n", + " X_next, Y_next = self.get_next(X, Y, acqfn, normalize, verbose)\n", + "\n", + " self.update_best(X_next, Y_next)\n", + "\n", + " self.update_state(Y_next)\n", + "\n", + " X, Y = self.postprocess(X, Y, X_next, Y_next, transformed_output)\n", + "\n", + " return X, Y, Y_next.mean()" + ], + "metadata": { + "collapsed": false, + "ExecuteTime": { + "end_time": "2023-07-15T05:12:41.645737200Z", + "start_time": "2023-07-15T05:12:41.627215300Z" + } + } + }, + { + "cell_type": "code", + "execution_count": 54, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "initial mode: small\n" + ] + } + ], + "source": [ + "mode = \"small\"\n", + "\n", + "darbo_opt = DARBO_optimizer(\n", + " eval_objective, 2 * nlayers, tr_length, failure_tolerance, mode, device\n", + ")\n", + "print(f\"initial mode: {darbo_opt.get_mode()}\")" + ], + "metadata": { + "collapsed": false, + "ExecuteTime": { + "end_time": "2023-07-15T05:12:41.645737200Z", + "start_time": "2023-07-15T05:12:41.627726200Z" + } + } + }, + { + "cell_type": "markdown", + "source": [ + "## Optimization" + ], + "metadata": { + "collapsed": false + } + }, + { + "cell_type": "code", + "execution_count": 55, + "outputs": [ + { + "data": { + "text/plain": "
", + "image/png": "iVBORw0KGgoAAAANSUhEUgAAAjUAAAGwCAYAAABRgJRuAAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjcuMSwgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy/bCgiHAAAACXBIWXMAAA9hAAAPYQGoP6dpAABxfklEQVR4nO3dd3hUVeLG8e+U9B4SEgKhdykqIIKNpmBb3XXFrqhrhbWsZcWua1srrnXdVVnL6toVfoIiTaVIkypFOkJCAiG9z9zfH4dMCCQhZSaTTN7P89znztx7594zl0Bezjn3HJtlWRYiIiIiLZzd3wUQERER8QaFGhEREQkICjUiIiISEBRqREREJCAo1IiIiEhAUKgRERGRgKBQIyIiIgHB6e8CNCW3282ePXuIiorCZrP5uzgiIiJSB5ZlkZeXR0pKCnZ7zfUxrSrU7Nmzh9TUVH8XQ0RERBpg165ddOjQocb9rSrUREVFAeamREdH+7k0IiIiUhe5ubmkpqZ6fo/XpFWFmoomp+joaIUaERGRFuZoXUfUUVhEREQCgkKNiIiIBASFGhEREQkIrapPjYiISHVcLhdlZWX+LkarFRQUhMPhaPR5FGpERKTVsiyL9PR0srOz/V2UVi82Npbk5ORGjSOnUCMiIq1WRaBp27Yt4eHhGpjVDyzLorCwkIyMDADatWvX4HMp1IiISKvkcrk8gaZNmzb+Lk6rFhYWBkBGRgZt27ZtcFOUOgqLiEirVNGHJjw83M8lEaj8c2hM3yaFGhERadXU5NQ8eOPPocWEmieffJIhQ4YQFRVF27ZtOf/889m4caO/iyUiIiLNRIsJNfPnz2fixIksXryYWbNmUVZWxhlnnEFBQYG/iyYiIiLNQIvpKDxz5swq76dOnUrbtm1Zvnw5p556qp9KJSIi0jI8/PDDfPHFF6xcudLfRfGZFlNTc7icnBwA4uPjazympKSE3NzcKotPZGfDli3gq/OLiIhUY9GiRTgcDs4++2x/F6VZaJGhxu12c9ttt3HSSSfRr1+/Go978skniYmJ8Sypqam+KdB550H37jBjhm/OLyIiUo0333yTP//5z3z//ffs2bPH38XxuxYZaiZOnMjatWv58MMPaz1u8uTJ5OTkeJZdu3b5pkBxcWZ94IBvzi8iIk3DsqCgoOkXy6p3UfPz8/nf//7HTTfdxNlnn83UqVOr7H/qqadISkoiKiqKa6+9luLi4ir7ly5dyumnn05CQgIxMTGcdtpprFixosoxNpuNf/7zn5xzzjmEh4fTp08fFi1axObNmxkxYgQREREMHz6cLVu21Lv8vtDiQs2kSZOYPn06c+fOpUOHDrUeGxISQnR0dJXFJxRqREQCQ2EhREY2/VJYWO+ifvTRR/Tu3ZtevXpx+eWX89Zbb2EdDEcfffQRDz/8ME888QTLli2jXbt2vPrqq1U+n5eXx1VXXcWPP/7I4sWL6dGjB2eddRZ5eXlVjvvb3/7GlVdeycqVK+nduzeXXnopN9xwA5MnT2bZsmVYlsWkSZMafs+9yWoh3G63NXHiRCslJcXatGlTg86Rk5NjAVZOTo53C3fbbZYFlnX33d49r4iI+ExRUZH1yy+/WEVFRZUb8/PNv+dNveTn17v8w4cPt6ZMmWJZlmWVlZVZCQkJ1ty5cy3Lsqxhw4ZZN998c5Xjhw4dag0cOLDG87lcLisqKsqaNm2aZxtg3X///Z73ixYtsgDrzTff9Gz74IMPrNDQ0HqX/3DV/nkcVNff3y3m6aeJEyfy3//+ly+//JKoqCjS09MBiImJ8Qyv7DeqqRERCQzh4ZCf75/r1sPGjRtZsmQJn3/+OQBOp5OLLrqIN998kxEjRrB+/XpuvPHGKp8ZNmwYc+fO9bzfu3cv999/P/PmzSMjIwOXy0VhYSE7d+6s8rkBAwZ4XiclJQHQv3//KtuKi4vJzc31XYtIHbWYUPPaa68BMGLEiCrb3377bSZMmND0BTqUQo2ISGCw2SAiwt+lOKo333yT8vJyUlJSPNssyyIkJISXX365Tue46qqr2L9/Py+++CKdOnUiJCSEYcOGUVpaWuW4oKAgz+uKUX+r2+Z2uxv8fbylxYQaqwGdqJrKvmgnr58K92YfaHmdlEREpEUpLy/nnXfe4bnnnuOMM86osu/888/ngw8+oE+fPvz0009ceeWVnn2LFy+ucuyCBQt49dVXOeusswDYtWsX+/bt8/0X8KEWE2qaqzJXGcPTHuPXURD0ywb+6u8CiYhIQJs+fToHDhzg2muvJSYmpsq+Cy64gDfffJM777yTCRMmMHjwYE466STef/991q1bR9euXT3H9ujRg3fffZfBgweTm5vLXXfd5f/uHI2kioVGCnIEcVfXywG4t/du5m6be5RPiIiINNybb77JmDFjjgg0YELNsmXL6NOnDw888AB33303gwYNYseOHdx0001HnOfAgQMcf/zxXHHFFdxyyy20bdu2qb6GT9is5tyu42W5ubnExMSQk5Pj1c5M1rp1XH1/P/5zLPRv25/VN6322rlFRMQ3iouL2bZtG126dCE0NNTfxWn1avvzqOvvb9XUeIEtLo7HZ5vXv2T+QqmrtPYPiIiIiNcp1HhDXBwpeRBRCi7LxbYD2/xdIhERkVZHocYbwsKwhYTQc795u2n/Jv+WR0REpBVSqPGWuDh6KNSIiIj4jUKNt8TGqqZGRETEjxRqvCUurjLUZCnUiIiINDWFGm85NNSopkZERKTJKdR4S2wsPbLMyz15e8gv9cOEaCIiIq2YQo0X7NwJf1l3LTFFdhIsM9Pqr/t/9XOpREREWheFmkYqLYURI+CFVaO4g+foWW5GOlQTlIiI+MqECROw2WyepU2bNowbN47VqytHtHe5XLzwwgv079+f0NBQ4uLiOPPMM1mwYIEfS+5bCjWNFBwMTz9tXr/IbWxN6w5AZm6eH0slIiKBbty4caSlpZGWlsbs2bNxOp2cc845AFiWxcUXX8yjjz7Krbfeyvr165k3bx6pqamMGDGCL774wr+F9xHN0u0Ff/wjPPGHZdz72WDSC9sAsPgnF5OG+7lgIiISsEJCQkhOTgYgOTmZe+65h1NOOYXMzEzmzJnDJ598wldffcW5557r+cwbb7zB/v37+dOf/sTpp59ORESEv4rvE6qp8ZJ7LtvFl/yOREcOAL/tdvu5RCIiUl+WBQUFTb80dmrp/Px83nvvPbp3706bNm3473//S8+ePasEmgp33HEH+/fvZ9asWY27aDOkmhovscXH8Tum0dnei0xgd7rL30USEZF6KiyEyMimv25+PtS30mT69OlEHixsQUEB7dq1Y/r06djtdjZt2kSfPn2q/VzF9k2bAq/vp2pqvCU+HoDY8mwA0tIUakRExHdGjhzJypUrWblyJUuWLGHs2LGceeaZ7NixAzD9alob1dR4S0ICAHGlZgS+giIXGRnQtq0/CyUiIvURHm5qTfxx3fqKiIige/funvf//ve/iYmJ4V//+hc9e/Zk/fr11X6uYnvPnj0bVNbmTKHGW9qYDsIh7nLz3uZi3TqFGhGRlsRmq38zUHNhs9mw2+0UFRVx8cUXc+mllzJt2rQj+tU899xztGnThtNPP91PJfUdhRpvCQmBqCjs1sFHue0m1Iwc6d9iiYhIYCopKSE9PR2AAwcO8PLLL5Ofn8+5557Laaedxscff8xVV13FM888w+jRo8nNzeWVV17hq6++4uOPPw64J59Aoca7EhNxuA+GGpubdev8WxwREQlcM2fOpF27dgBERUXRu3dvPv74Y0aMGAHARx99xJQpU3jhhRe4+eabCQ0NZdiwYcybN4+TTjrJjyX3HYUab0pIwGFtNa9tLtau9W9xREQkME2dOpWpU6fWeozT6eTOO+/kzjvvbJpCNQN6+smbEhJwVAxPY3cRgE/LiYiINFsKNd6UkICj4gk6m3n6qbTUryUSERFpNRRqvOmQmhpHkBmnZs8eP5ZHRESkFVGo8aaEBOwHa2oio0y62b3bj+URERFpRRRqvCkx0dP8FBllamoUakRERJqGQo03HdL8FKFQIyIi0qQUarzpkI7CCjUiIiJNS6HGmw6pqQmPMKHmt9/8WB4REZFWRKHGmw6pqQkPLwNUUyMiItJUFGq8KS4OOzYAwpzFgEKNiIhIU1Go8SaHA0dIGACh9gLAhBrLqu1DIiIidWOz2WpdHn744Vo///DDD3uOdTgcpKamcv3115OVlVXluM6dO1c5LiUlhWuvvZYDBw5UOS4rK4vbbruNTp06ERwcTEpKCtdccw07d+709levE4UaL3OEm1lPQzGhprQU9u3zZ4lERCRQpKWleZYpU6YQHR1dZVtd5nk65phjSEtLY+fOnbz99tvMnDmTm2666YjjHn30Uc9x77//Pt9//z233HKLZ39WVhYnnngi3333Ha+//jqbN2/mww8/ZPPmzQwZMoStW7d69bvXhUKNl1WEGqu0kLZtzTY1QYmIiDckJyd7lpiYGGw2m+f966+/zsknn1zl+ClTptC5c+cq25xOJ8nJybRv354xY8Zw4YUXMmvWrCOuFRUV5Tlu5MiRXHXVVaxYscKz/7777mPPnj189913nHnmmXTs2JFTTz2Vb775hqCgICZOnOiTe1AbzdLtZY7wSABcRYW0bw8ZGSbUHHusf8slIiJHZ1kWhWWFTX7d8KBwbDZbk193+/btfPPNNwQHB9d63O7du5k2bRpDhw4FwO128+GHH3LZZZeRnJxc5diwsDBuvvlm7r//frKysoiPj/dZ+Q+nUONljphYAFxFBaSmws8/w7Zt/i2TiIjUTWFZIZFPRjb5dfMn5xMRHNEk11qzZg2RkZG4XC6Ki81DLc8///wRx/31r3/l/vvv9xw3dOhQz3GZmZlkZ2fTp0+faq/Rp08fLMti8+bNnHDCCb77ModR85OX2aNjAHAXF3HccWbbTz/5sUAiIiKH6NWrFytXrmTp0qX89a9/ZezYsfz5z38+4ri77rqLlStXsnr1ambPng3A2Wefjcvl8hxjNbMnYVRT42WO6FjIBldxESeOMtsWL/ZniUREpK7Cg8LJn5zvl+s2lt1uPyJklJWVHXFccHAw3bt3B+Cpp57i7LPP5pFHHuFvf/tbleMSEhI8x/Xo0YMpU6YwbNgw5s6dy6hRo4iNjWX9+vXVlmX9+vXYbDbP55uKQo2XOWLjTagpKeJg0yObN0NmJiQm+rVoIiJyFDabrcmagbwtMTGR9PR0LMvy9M9ZuXLlUT93//33M2rUKG666SZSUlJqPM7hcABQVFSE3W5n/PjxvP/++zz66KNV+tUUFRXx6quvMnbs2CbtTwNqfvI6R2wcAK6SYuLioKK5UbU1IiLiSyNGjCAzM5Onn36aLVu28MorrzBjxoyjfm7YsGEMGDCAJ554osr2vLw80tPTSUtLY8mSJdx1110kJiYyfPhwAJ544gmSk5M5/fTTmTFjBrt27eL7779n7NixlJWV8corr/jke9ZGocbLHHEmlbrKS6GsjGHDzHaFGhER8aU+ffrw6quv8sorrzBw4ECWLFlSp3FrAG6//Xb+/e9/s2vXLs+2Bx98kHbt2pGSksI555xDREQE3377LW3atAGgTZs2LF68mJEjR3LDDTfQrVs3xo8fT7du3Vi6dCldu3b1yfesjc1qbr18fCg3N5eYmBhycnKIjo72yTX+8/PbTPjqGsZuhplP7ORfM1O5/noYMQLmzvXJJUVEpAGKi4vZtm0bXbp0ITQ01N/FafVq+/Oo6+9v1dR4mcMRBIDLBqSlceqpZvv8+Qo1IiIivqRQ42UOm+lI5bIDaWn06gXXXWfmf7rySpg2DdasgUNq+ERERMQLFGq8zGE/GGpswJ49ADz/PHTvDr/9Br/7HQwYAB07moAjIiIi3qFQ42WH19QAREbCrFlwww3QuzdERZlj333XT4UUEREJQAo1XlalpuZgqAHo3Blefx3WrzcBB2DmTDOLt4iIiDSeQo2X2W3mlroPCzWHGjIEkpIgLw++/74JCyciIhLAFGq8rErz08E+NYez2+Hss81r9asRERHxDoUaL6vS/LR7d43HnXuuWX/9dRMUSkREpBVQqPGyKjU1GRlQVFTtcUOGmPW2beZxbxEREWkchRov89TUOA7e2p07qz0uIcGsXS7IyWmKkomIiAQ2hRov89TUBB+cAH379mqPCwkxj3oD7NvXBAUTEZGAMWHCBGw2GzabjaCgIJKSkjj99NN56623cLvdRxw/duxYHA4HS5cuPeq5unTpwt13301xcXGV4yqOsdlsOJ1OOnbsyF/+8hdKSkqqHLdr1y6uueYaUlJSCA4OplOnTtx6663s37/fuzehGgo1XuZ5+inYTJfAjh01HltRW6NQIyIi9TVu3DjS0tLYvn07M2bMYOTIkdx6662cc845lJeXe47buXMnCxcuZNKkSbz11lu1nmvr1q288MIL/POf/+Shhx464ri3336btLQ0tm3bxquvvsq7777LY4895tm/detWBg8ezK+//soHH3zA5s2bef3115k9ezbDhg0jKyvL+zfiEE6fnr0V8jQ/BZl1TTU1YELN9u0KNSIiUn8hISEkJycD0L59e44//nhOPPFERo8ezdSpU/nTn/4EmCByzjnncNNNN3HiiSfy/PPPExYWVuO5UlNTGTNmDLNmzeLvf/97leNiY2OrHHfeeeexYsUKz/6JEycSHBzMt99+67lGx44dOe644+jWrRv33Xcfr732mm9uCKqp8TpP85OzbqEGFGpERJoLy7JwuQqafLG89MTIqFGjGDhwIJ999pnn+7z99ttcfvnl9O7dm+7du/PJJ5/Ueo61a9eycOFCgoODaz1u06ZNzJkzh6FDhwKQlZXFN998w80333xEaEpOTuayyy7jf//7n9e+a3VUU+NlR3QUrkPzUxM0M4qISB243YX88ENkk1/3lFPycTgivHKu3r17s3r1agC+++47CgsLGTt2LACXX345b775JldccUWVz0yfPp3IyEjKy8spKSnBbrfz8ssvH3HuSy65BIfD4TnunHPOYfLkyQD8+uuvWJZFnz59qi1Xnz59OHDgAJmZmbRt29Yr3/VwqqnxMk9NjcNmNqimRkREmpBlWdhs5nfQW2+9xUUXXYTTaeowLrnkEhYsWMCWLVuqfGbkyJGsXLmSn376iauuuoqrr76aCy644Ihzv/DCC6xcuZJVq1Yxffp0Nm3adERA8mVNzNGopsbLPB2F7QdDTVoalJSYx50Oo1AjItK82O3hnHJKvl+u6y3r16+nS5cuZGVl8fnnn1NWVlalH4vL5eKtt97i8ccf92yLiIige/fugAlCAwcO5M033+Taa6+tcu7k5GTPcb169SIvL49LLrmExx57jO7du2Oz2Vi/fj2///3vqy1XXFwciYmJXvuuh1NNjZdVjihsQViYGVlv165qj23TxqwVakREmgebzYbDEdHkS0XNSmPNmTOHNWvWcMEFF/D+++/ToUMHVq1axcqVKz3Lc889x9SpU3G5XNWew263c++993L//fdTVMMAshUcDvM7r6ioiDZt2nD66afz6quvHvG59PR03n//fS666CKvfddqy+6zM7dSnuYnt8tMzQ019qtRTY2IiDRUSUkJ6enp7N69mxUrVvDEE09w3nnncc4553DllVfy5ptv8sc//pF+/fpVWa699lr27dvHzJkzazz3hRdeiMPh4JVXXqmyPTs7m/T0dPbs2cP8+fN59NFH6dmzp6cfzcsvv0xJSQljx47l+++/Z9euXcycOZPTTz+d9u3bV6kd8gWFGi/z1NRYh4SabduqPVahRkREGmrmzJm0a9eOzp07M27cOObOncs//vEPvvzyS0+/l+r6xcTExDB69GjefPPNGs/tdDqZNGkSTz/9NAUFBZ7tV199Ne3ataNDhw5ccsklHHPMMcyYMcPTZ6dHjx4sW7aMrl27Mn78eLp168b111/PyJEjWbRoEfHx8d6/EYewWf7s0dMAr7zyCs888wzp6ekMHDiQl156iRNOOKFOn83NzSUmJoacnByio6N9Ur4tWVvo/lJ3IoIiyE+/Bl56Ce66C55++ohj166F/v1NuMnM9ElxRESkBsXFxWzbto0uXboQGhrq7+K0erX9edT193eLqqn53//+x1/+8hceeughVqxYwcCBAxk7diwZGRn+LppHlZqa3r3Nxg0bqj22oqYmKwuqGdVaRERE6qFFhZrnn3+e6667jquvvpq+ffvy+uuvEx4eXuOwz/7gefrJckOvXmbjxo3VHlvRUdjthuzsJiiciIhIAGsxoaa0tJTly5czZswYzza73c6YMWNYtGhRtZ8pKSkhNze3yuJrVToKV4SarVuhrOyIY4OCICbGvFa/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KMnJl5OUdDlBQfG4XAV+K4f61IiIiEij9ejxCi5XASEh/nuyTaFGREREGs3pjMbp9O8YcGp+EhERkYCgUCMiIiIBQaFGREREAoJCjYiIiAQEhRoREREJCAo1IiIiEhAUakRERCQgKNSIiIhIQFCoERERkYCgUCMiIiIBQaFGREREAoJCjYiIiAQEhRoREREJCK1qlm7LsgDIzc31c0lERESkrip+b1f8Hq9Jqwo1eXl5AKSmpvq5JCIiIlJfeXl5xMTE1LjfZh0t9gQQt9vNnj17iIqKwmazee28ubm5pKamsmvXLqKjo7123kCl+1V3ulf1o/tVd7pXdad7VT++uF+WZZGXl0dKSgp2e809Z1pVTY3dbqdDhw4+O390dLR+4OtB96vudK/qR/er7nSv6k73qn68fb9qq6GpoI7CIiIiEhAUakRERCQgKNR4QUhICA899BAhISH+LkqLoPtVd7pX9aP7VXe6V3Wne1U//rxfraqjsIiIiAQu1dSIiIhIQFCoERERkYCgUCMiIiIBQaFGREREAoJCjRe88sordO7cmdDQUIYOHcqSJUv8XSS/e/jhh7HZbFWW3r17e/YXFxczceJE2rRpQ2RkJBdccAF79+71Y4mb1vfff8+5555LSkoKNpuNL774osp+y7J48MEHadeuHWFhYYwZM4Zff/21yjFZWVlcdtllREdHExsby7XXXkt+fn4TfoumcbR7NWHChCN+1saNG1flmNZyr5588kmGDBlCVFQUbdu25fzzz2fjxo1VjqnL372dO3dy9tlnEx4eTtu2bbnrrrsoLy9vyq/ic3W5VyNGjDjiZ+vGG2+sckxruFcAr732GgMGDPAMqDds2DBmzJjh2d9cfq4Uahrpf//7H3/5y1946KGHWLFiBQMHDmTs2LFkZGT4u2h+d8wxx5CWluZZfvzxR8++22+/nWnTpvHxxx8zf/589uzZwx/+8Ac/lrZpFRQUMHDgQF555ZVq9z/99NP84x//4PXXX+enn34iIiKCsWPHUlxc7DnmsssuY926dcyaNYvp06fz/fffc/311zfVV2gyR7tXAOPGjavys/bBBx9U2d9a7tX8+fOZOHEiixcvZtasWZSVlXHGGWdQUFDgOeZof/dcLhdnn302paWlLFy4kP/85z9MnTqVBx980B9fyWfqcq8Arrvuuio/W08//bRnX2u5VwAdOnTgqaeeYvny5SxbtoxRo0Zx3nnnsW7dOqAZ/VxZ0ignnHCCNXHiRM97l8tlpaSkWE8++aQfS+V/Dz30kDVw4MBq92VnZ1tBQUHWxx9/7Nm2fv16C7AWLVrURCVsPgDr888/97x3u91WcnKy9cwzz3i2ZWdnWyEhIdYHH3xgWZZl/fLLLxZgLV261HPMjBkzLJvNZu3evbvJyt7UDr9XlmVZV111lXXeeefV+JnWeq8sy7IyMjIswJo/f75lWXX7u/f1119bdrvdSk9P9xzz2muvWdHR0VZJSUnTfoEmdPi9sizLOu2006xbb721xs+01ntVIS4uzvr3v//drH6uVFPTCKWlpSxfvpwxY8Z4ttntdsaMGcOiRYv8WLLm4ddffyUlJYWuXbty2WWXsXPnTgCWL19OWVlZlfvWu3dvOnbsqPsGbNu2jfT09Cr3JyYmhqFDh3ruz6JFi4iNjWXw4MGeY8aMGYPdbuenn35q8jL727x582jbti29evXipptuYv/+/Z59rfle5eTkABAfHw/U7e/eokWL6N+/P0lJSZ5jxo4dS25urud/5YHo8HtV4f333ychIYF+/foxefJkCgsLPfta671yuVx8+OGHFBQUMGzYsGb1c9WqJrT0tn379uFyuar8IQEkJSWxYcMGP5WqeRg6dChTp06lV69epKWl8cgjj3DKKaewdu1a0tPTCQ4OJjY2tspnkpKSSE9P90+Bm5GKe1Ddz1XFvvT0dNq2bVtlv9PpJD4+vtXdw3HjxvGHP/yBLl26sGXLFu69917OPPNMFi1ahMPhaLX3yu12c9ttt3HSSSfRr18/gDr93UtPT6/2Z69iXyCq7l4BXHrppXTq1ImUlBRWr17NX//6VzZu3Mhnn30GtL57tWbNGoYNG0ZxcTGRkZF8/vnn9O3bl5UrVzabnyuFGvGJM8880/N6wIABDB06lE6dOvHRRx8RFhbmx5JJoLn44os9r/v378+AAQPo1q0b8+bNY/To0X4smX9NnDiRtWvXVunLJtWr6V4d2u+qf//+tGvXjtGjR7Nlyxa6devW1MX0u169erFy5UpycnL45JNPuOqqq5g/f76/i1WFmp8aISEhAYfDcUQP771795KcnOynUjVPsbGx9OzZk82bN5OcnExpaSnZ2dlVjtF9MyruQW0/V8nJyUd0Ri8vLycrK6vV38OuXbuSkJDA5s2bgdZ5ryZNmsT06dOZO3cuHTp08Gyvy9+95OTkan/2KvYFmpruVXWGDh0KUOVnqzXdq+DgYLp3786gQYN48sknGThwIC+++GKz+rlSqGmE4OBgBg0axOzZsz3b3G43s2fPZtiwYX4sWfOTn5/Pli1baNeuHYMGDSIoKKjKfdu4cSM7d+7UfQO6dOlCcnJylfuTm5vLTz/95Lk/w4YNIzs7m+XLl3uOmTNnDm632/MPb2v122+/sX//ftq1awe0rntlWRaTJk3i888/Z86cOXTp0qXK/rr83Rs2bBhr1qypEgRnzZpFdHQ0ffv2bZov0gSOdq+qs3LlSoAqP1ut4V7VxO12U1JS0rx+rrzW5biV+vDDD62QkBBr6tSp1i+//GJdf/31VmxsbJUe3q3RHXfcYc2bN8/atm2btWDBAmvMmDFWQkKClZGRYVmWZd14441Wx44drTlz5ljLli2zhg0bZg0bNszPpW46eXl51s8//2z9/PPPFmA9//zz1s8//2zt2LHDsizLeuqpp6zY2Fjryy+/tFavXm2dd955VpcuXayioiLPOcaNG2cdd9xx1k8//WT9+OOPVo8ePaxLLrnEX1/JZ2q7V3l5edadd95pLVq0yNq2bZv13XffWccff7zVo0cPq7i42HOO1nKvbrrpJismJsaaN2+elZaW5lkKCws9xxzt7155ebnVr18/64wzzrBWrlxpzZw500pMTLQmT57sj6/kM0e7V5s3b7YeffRRa9myZda2bdusL7/80uratat16qmnes7RWu6VZVnWPffcY82fP9/atm2btXr1auuee+6xbDab9e2331qW1Xx+rhRqvOCll16yOnbsaAUHB1snnHCCtXjxYn8Xye8uuugiq127dlZwcLDVvn1766KLLrI2b97s2V9UVGTdfPPNVlxcnBUeHm79/ve/t9LS0vxY4qY1d+5cCzhiueqqqyzLMo91P/DAA1ZSUpIVEhJijR492tq4cWOVc+zfv9+65JJLrMjISCs6Otq6+uqrrby8PD98G9+q7V4VFhZaZ5xxhpWYmGgFBQVZnTp1sq677roj/lPRWu5VdfcJsN5++23PMXX5u7d9+3brzDPPtMLCwqyEhATrjjvusMrKypr42/jW0e7Vzp07rVNPPdWKj4+3QkJCrO7du1t33XWXlZOTU+U8reFeWZZlXXPNNVanTp2s4OBgKzEx0Ro9erQn0FhW8/m5slmWZXmv3kdERETEP9SnRkRERAKCQo2IiIgEBIUaERERCQgKNSIiIhIQFGpEREQkICjUiIiISEBQqBEREZGAoFAjIiIiAUGhRkRalc6dOzNlyhR/F0NEfEChRkR8ZsKECZx//vkAjBgxgttuu63Jrj116lRiY2OP2L506VKuv/76JiuHiDQdp78LICJSH6WlpQQHBzf484mJiV4sjYg0J6qpERGfmzBhAvPnz+fFF1/EZrNhs9nYvn07AGvXruXMM88kMjKSpKQkrrjiCvbt2+f57IgRI5g0aRK33XYbCQkJjB07FoDnn3+e/v37ExERQWpqKjfffDP5+fkAzJs3j6uvvpqcnBzP9R5++GHgyOannTt3ct555xEZGUl0dDTjx49n7969nv0PP/wwxx57LO+++y6dO3cmJiaGiy++mLy8PN/eNBGpN4UaEfG5F198kWHDhnHdddeRlpZGWloaqampZGdnM2rUKI477jiWLVvGzJkz2bt3L+PHj6/y+f/85z8EBwezYMECXn/9dQDsdjv/+Mc/WLduHf/5z3+YM2cOd999NwDDhw9nypQpREdHe6535513HlEut9vNeeedR1ZWFvPnz2fWrFls3bqViy66qMpxW7Zs4YsvvmD69OlMnz6d+fPn89RTT/nobolIQ6n5SUR8LiYmhuDgYMLDw0lOTvZsf/nllznuuON44oknPNveeustUlNT2bRpEz179gSgR48ePP3001XOeWj/nM6dO/PYY49x44038uqrrxIcHExMTAw2m63K9Q43e/Zs1qxZw7Zt20hNTQXgnXfe4ZhjjmHp0qUMGTIEMOFn6tSpREVFAXDFFVcwe/ZsHn/88cbdGBHxKtXUiIjfrFq1irlz5xIZGelZevfuDZjakQqDBg064rPfffcdo0ePpn379kRFRXHFFVewf/9+CgsL63z99evXk5qa6gk0AH379iU2Npb169d7tnXu3NkTaADatWtHRkZGvb6riPieampExG/y8/M599xz+fvf/37Evnbt2nleR0REVNm3fft2zjnnHG666SYef/xx4uPj+fHHH7n22mspLS0lPDzcq+UMCgqq8t5ms+F2u716DRFpPIUaEWkSwcHBuFyuKtuOP/54Pv30Uzp37ozTWfd/jpYvX47b7ea5557DbjcVzh999NFRr3e4Pn36sGvXLnbt2uWprfnll1/Izs6mb9++dS6PiDQPan4SkSbRuXNnfvrpJ7Zv386+fftwu91MnDiRrKwsLrnkEpYuXcqWLVv45ptvuPrqq2sNJN27d6esrIyXXnqJrVu38u6773o6EB96vfz8fGbPns2+ffuqbZYaM2YM/fv357LLLmPFihUsWbKEK6+8ktNOO43Bgwd7/R6IiG8p1IhIk7jzzjtxOBz07duXxMREdu7cSUpKCgsWLMDlcnHGGWfQv39/brvtNmJjYz01MNUZOHAgzz//PH//+9/p168f77//Pk8++WSVY4YPH86NN97IRRddRGJi4hEdjcE0I3355ZfExcVx6qmnMmbMGLp27cr//vc/r39/EfE9m2VZlr8LISIiItJYqqkRERGRgKBQIyIiIgFBoUZEREQCgkKNiIiIBASFGhEREQkICjUiIiISEBRqREREJCAo1IiIiEhAUKgRERGRgKBQIyIiIgFBoUZEREQCwv8DMUDZP+gMBJwAAAAASUVORK5CYII=" + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Epoch 299 min loss: -5.837215998677346\t-6.87628698348999\t-6.8708058821631575\t-7.000515157123274\n", + "Epoch 299 time: 13.83302903175354\tTotal time: 2123.8649847507477\n" + ] + } + ], + "source": [ + "# initial parameters\n", + "params = K.implicit_randu(shape=(2 * nlayers,))\n", + "initial_X = torch.from_numpy(np.asarray(params)).type(dtype)\n", + "\n", + "# First point by BO is actually just a random selection, to have a better search, we pick the most distant point\n", + "X_new = []\n", + "for i in initial_X:\n", + " if i <= 0.5:\n", + " X_new.append(i + 0.5)\n", + " else:\n", + " X_new.append(i - 0.5)\n", + "X_new = torch.tensor(X_new)\n", + "\n", + "X_bo = torch.stack((initial_X, X_new), dim=0)\n", + "Y_bo = None\n", + "X_turbo = X_bo.clone()\n", + "Y_turbo = None\n", + "X_darbo = X_bo.clone()\n", + "Y_darbo = None\n", + "\n", + "losses, losses_bo, losses_turbo, losses_darbo = [], [], [], []\n", + "t0 = ts = time.time()\n", + "\n", + "for i in range(300):\n", + " loss, grads = QAOA_vag(params)\n", + " params = opt.update(grads, params) # gradient descent\n", + " losses.append(loss)\n", + "\n", + " X_bo, Y_bo, _ = bo_opt.update(X_bo, Y_bo, acqfn)\n", + " losses_bo.append(-bo_opt.best_dict[\"Y\"].item())\n", + "\n", + " X_turbo, Y_turbo, _ = turbo_opt.update(\n", + " X_turbo,\n", + " Y_turbo,\n", + " acqfn,\n", + " transformed_input=False if i == 0 else True,\n", + " transformed_output=True,\n", + " )\n", + " losses_turbo.append(-turbo_opt.best_dict[\"Y\"].item())\n", + "\n", + " X_darbo, Y_darbo, _ = darbo_opt.update(\n", + " X_darbo,\n", + " Y_darbo,\n", + " acqfn,\n", + " transformed_input=False if i == 0 else True,\n", + " transformed_output=True,\n", + " )\n", + " losses_darbo.append(-darbo_opt.best_dict[\"Y\"].item())\n", + "\n", + " # visualise the progress\n", + " clear_output(wait=True)\n", + " plt.figure()\n", + " plt.xlabel(\"Iteration\")\n", + " plt.ylabel(\"Cost\")\n", + " plt.plot(range(i + 1), losses, c=\"r\", label=\"Adam\")\n", + " plt.plot(range(i + 1), losses_bo, c=\"b\", label=\"BO\")\n", + " plt.plot(range(i + 1), losses_turbo, c=\"g\", label=\"TuRBO\")\n", + " plt.plot(range(i + 1), losses_darbo, c=\"y\", label=\"DARBO\")\n", + " plt.legend()\n", + " plt.show()\n", + "\n", + " print(\n", + " f\"Epoch {i} min loss: {min(losses)}\\t{losses_bo[-1]}\\t{losses_turbo[-1]}\\t{losses_darbo[-1]}\"\n", + " )\n", + "\n", + " te = time.time()\n", + " print(f\"Epoch {i} time: {te - ts}\\tTotal time: {te - t0}\")\n", + " ts = te" + ], + "metadata": { + "collapsed": false + } + }, + { + "cell_type": "markdown", + "source": [ + "## Results" + ], + "metadata": { + "collapsed": false + } + }, + { + "cell_type": "markdown", + "source": [ + "After inputting the optimized parameters back to the ansatz circuit, we can perform the projective measurement on the output quantum state to get the solution. Here we directly use the bit string with the maximum probability as the solution since we know all information of the probability distribution of the output quantum state, but which is not feasible in the experiment." + ], + "metadata": { + "collapsed": false + } + }, + { + "cell_type": "code", + "execution_count": 56, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Adam\n", + "loss: -5.837558827215271\tprob: 0.318607790021735\tbit strings: ['10101010']\n", + "\n", + "BO\n", + "loss: -6.876287191047742\tprob: 0.3434109359024678\tbit strings: ['10101010']\n", + "\n", + "TuRBO\n", + "loss: -6.8708058821631575\tprob: 0.3339171040448526\tbit strings: ['10101010']\n", + "\n", + "DARBO\n", + "loss: -7.000515157123274\tprob: 0.36778546095898973\tbit strings: ['01010101']\n" + ] + } + ], + "source": [ + "params_bo = jnp.asarray(bo_opt.best_dict[\"X\"])\n", + "params_turbo = jnp.asarray(turbo_opt.inverse_transform(turbo_opt.best_dict[\"X\"]))\n", + "params_darbo = jnp.asarray(\n", + " darbo_opt.inverse_transform(\n", + " darbo_opt.best_dict[\"X\"], mode=darbo_opt.best_dict[\"mode\"]\n", + " )\n", + ")\n", + "\n", + "\n", + "# find the states with max probabilities\n", + "def find_max(params):\n", + " loss = QAOA_nograd(params)\n", + " c = QAOAansatz(params, return_circuit=True)\n", + " probs = K.numpy(c.probability())\n", + " max_prob = max(probs)\n", + " index = np.where(probs == max_prob)[0]\n", + " states = []\n", + " for i in index:\n", + " states.append(f\"{bin(i)[2:]:0>{graph.number_of_nodes()}}\")\n", + " return loss, max_prob, states\n", + "\n", + "\n", + "loss, prob, states = find_max(params)\n", + "loss_bo, prob_bo, states_bo = find_max(params_bo)\n", + "loss_turbo, prob_turbo, states_turbo = find_max(params_turbo)\n", + "loss_darbo, prob_darbo, states_darbo = find_max(params_darbo)\n", + "print(f\"Adam\\nloss: {loss}\\tprob: {prob}\\tbit strings: {states}\\n\")\n", + "print(f\"BO\\nloss: {loss_bo}\\tprob: {prob_bo}\\tbit strings: {states_bo}\\n\")\n", + "print(f\"TuRBO\\nloss: {loss_turbo}\\tprob: {prob_turbo}\\tbit strings: {states_turbo}\\n\")\n", + "print(f\"DARBO\\nloss: {loss_darbo}\\tprob: {prob_darbo}\\tbit strings: {states_darbo}\")" + ], + "metadata": { + "collapsed": false, + "ExecuteTime": { + "end_time": "2023-07-15T05:48:09.478379400Z", + "start_time": "2023-07-15T05:48:05.566717500Z" + } + } + }, + { + "cell_type": "code", + "execution_count": 57, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "OS info: Linux-5.4.119-1-tlinux4-0010.2-x86_64-with-glibc2.28\n", + "Python version: 3.10.11\n", + "Numpy version: 1.23.5\n", + "Scipy version: 1.11.0\n", + "Pandas version: 2.0.2\n", + "TensorNetwork version: 0.4.6\n", + "Cotengra version: 0.2.1.dev15+g120379e\n", + "TensorFlow version: 2.12.0\n", + "TensorFlow GPU: []\n", + "TensorFlow CUDA infos: {'cpu_compiler': '/dt9/usr/bin/gcc', 'cuda_compute_capabilities': ['sm_35', 'sm_50', 'sm_60', 'sm_70', 'sm_75', 'compute_80'], 'cuda_version': '11.8', 'cudnn_version': '8', 'is_cuda_build': True, 'is_rocm_build': False, 'is_tensorrt_build': True}\n", + "Jax version: 0.4.13\n", + "Jax installation doesn't support GPU\n", + "JaxLib version: 0.4.13\n", + "PyTorch version: 2.0.1\n", + "PyTorch GPU support: False\n", + "PyTorch GPUs: []\n", + "Cupy is not installed\n", + "Qiskit version: 0.24.1\n", + "Cirq version: 1.1.0\n", + "TensorCircuit version 0.10.0\n" + ] + } + ], + "source": [ + "tc.about()" + ], + "metadata": { + "collapsed": false, + "ExecuteTime": { + "end_time": "2023-07-15T05:48:09.478379400Z", + "start_time": "2023-07-15T05:48:09.477371900Z" + } + } + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "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.9.12" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/docs/source/tutorials/qaoa_nae3sat.ipynb b/docs/source/tutorials/qaoa_nae3sat.ipynb new file mode 100644 index 00000000..51937c93 --- /dev/null +++ b/docs/source/tutorials/qaoa_nae3sat.ipynb @@ -0,0 +1,1102 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "dc0db886", + "metadata": {}, + "source": [ + "# Quantum Approximation Optimization Algorithm (QAOA) for Not-all-equal 3-satisfiability (NAE3SAT)" + ] + }, + { + "cell_type": "markdown", + "id": "aecf6615", + "metadata": {}, + "source": [ + "## Overview" + ] + }, + { + "cell_type": "markdown", + "id": "b533d43e", + "metadata": {}, + "source": [ + "Quantum Approximation Optimization Algorithm (QAOA) is a hybrid classical-quantum algorithm used for solving the combinatorial optimization problem, which is proposed by [Farhi, Goldstone, and Gutmann (2014)](https://arxiv.org/abs/1411.4028). In QAOA, the parameterized quantum circuit is regarded as an oracle, we sample the circuit to obtain the gradient of the parameters, and update them through the classical optimizer. Before this tutorial, there was already a tutorial of [QAOA for Max-Cut](qaoa.ipynb). In this tutorial, we will focus on another combinatorial optimization problem - Not-all-equal 3-satisfiability (NAE3SAT), and discuss the performance of QAOA in different hardness cases." + ] + }, + { + "cell_type": "markdown", + "id": "16ad937f", + "metadata": {}, + "source": [ + "## Not-all-equal 3-satisfiability (NAE3SAT)" + ] + }, + { + "cell_type": "markdown", + "id": "c2321b00", + "metadata": {}, + "source": [ + "[Not-all-equal 3-satisfiability (NAE3SAT)](https://en.wikipedia.org/wiki/Not-all-equal_3-satisfiability) is a variant of 3-satisfiability (3-SAT) and 3-SAT is a subset of [Boolean satisfiability problem (SAT)](https://en.wikipedia.org/wiki/Boolean_satisfiability_problem). SAT is, given a Boolean expression, to check whether it is satisfiable, where the Boolean expression is a disjunction of clauses (or a single clause) and each clause is a disjunction of literals (or a single literal). Here is an example of Boolean expression of SAT,\n", + "$$\n", + "(x_1\\lor x_2\\lor\\cdots\\lor x_m)\\land(\\lnot x_5\\lor x_9\\lor\\cdots\\lor x_m)\\land\\cdots\\land(x_m\\lor \\lnot x_{m+3}\\lor\\cdots\\lor \\lnot x_n),\n", + "$$\n", + "where $(x_i\\lor x_j\\lor\\cdots\\lor x_k)$ is a clause and $x_i$ is a literal. SAT with $k$ literals in each clause is called $k$-SAT, thus in 3-SAT, there are only three literals in each clause, for example\n", + "$$\n", + "(x_1\\lor x_2\\lor x_m)\\land(\\lnot x_5\\lor x_9\\lor x_m)\\land\\cdots\\land(x_m\\lor \\lnot x_{m+3}\\lor \\lnot x_n).\n", + "$$\n", + "When $k$ is not less than 3, SAT is NP-complete. On the other hand, NAE3SAT requires the three literals in each clause are not all equal to each other, in other words, at least one is true, and at least one is false. It is different from 3-SAT, which requires at least one literal is true in each clause. However, NAE3SAT is still NP-complete, [which can be proven by a reduction from 3-SAT](https://en.wikipedia.org/wiki/Not-all-equal_3-satisfiability)." + ] + }, + { + "cell_type": "markdown", + "id": "4d28f719", + "metadata": {}, + "source": [ + "Now we use the spin model to represent a NAE3SAT. Let the set of clauses in the NAE3SAT be $\\mathcal{C}$. In each clause, there are three literals and each literal is represented by a spin. Spins up ($s=1$, $\\text{bit}=0$) and down ($s=-1$, $\\text{bit}=1$) represent false and true respectively. For the clause $(s_i,\\ s_j,\\ s_k)\\in\\mathcal{C}$, $s_i,\\ s_j,\\ s_k$ cannot be 1 or -1 at the same time. The Hamiltonian of the NAE3SAT is as follows\n", + "$$\n", + "\\begin{split}\n", + " \\hat{H}_C&=\\sum_{(i,j,k)\\in\\mathcal{C}}\\left[(s_i+s_j+s_k)^2-1\\right]/2\\\\\n", + " &=\\sum_{(i,j,k)\\in\\mathcal{C}}(s_i s_j+s_j s_k+s_k s_i)+|\\mathcal{C}|,\n", + "\\end{split}\n", + "$$\n", + "where $|\\mathcal{C}|$ is the number of clauses in $\\mathcal{C}$. When all clauses are true, $\\hat{H}_C$ takes the minimum value 0, and the corresponding bit string is the solution of the NAE3SAT." + ] + }, + { + "cell_type": "markdown", + "id": "871bff2e", + "metadata": {}, + "source": [ + "## QAOA for NAE3SAT" + ] + }, + { + "cell_type": "markdown", + "id": "598dc69e", + "metadata": {}, + "source": [ + "QAOA utilizes a parameterized quantum circuit ([PQC](https://tensorcircuit.readthedocs.io/en/latest/textbook/chap5.html?highlight=变分)) to generate a quantum state that represents a potential solution. The initial state, denoted as $|s\\rangle$, is a uniform superposition over computational basis states.\n", + "$$\n", + "|s\\rangle=\\frac{1}{\\sqrt{2^n}}\\sum_z|z\\rangle\n", + "$$\n", + "This state is then evolved by a unitary operator that consists of $p$ layers, denoted as\n", + "$$\n", + "U(\\boldsymbol{\\beta}, \\boldsymbol{\\gamma}) = V_{p}U_{p} \\cdots V_{1}U_{1},\n", + "$$\n", + "where $U_{j}= e^{-\\text{i}\\gamma_{j}\\hat{H}_{C}}$ is the driving layer and $V_{j}= e^{-\\text{i}\\beta_{j} \\hat{H}_m}$ is the mixing layer. $\\hat{H}_C$ is the driving and cost Hamiltonian introduced in previous section and the mixing Hamiltonian $\\hat{H}_m=\\sum_{j=1}^{n}\\sigma_j^x$ is used to mix the quantum state to explore different solutions. The unitary operator is parameterized by $2p$ angle parameters $\\gamma_1, \\gamma_2, \\dots, \\gamma_p$ and $\\beta_1, \\beta_2, \\dots ,\\beta_p$ and each $\\gamma$ and $\\beta$ are restricted to lie between $0$ and $2\\pi$." + ] + }, + { + "cell_type": "markdown", + "id": "949be36e", + "metadata": {}, + "source": [ + "Begin with a set of initial $\\boldsymbol{\\gamma}$ and $\\boldsymbol{\\beta}$, the quantum state is obtained from the PQC and then the expectation value of $\\hat{H}_C$ is calculated. A classical optimizer is then used to vary the parameters until a lower expectation value is found. This process is iterated a certain number of times until the expectation value of $\\hat{H}_C$ is approximated to 0. Then we perform projective measurement on the quantum state output by PQC, and obtain a bit string, which is very likely to be the solution of NAE3SAT. Since NAE3SAT is an NP-complete problem, we can verify whether the solution is correct in polynomial time on classical computer. Even if this bit string is not the correct solution, we can repeat the projective measurement and verify the obtained solution until we get the correct solution.\n", + "\n", + "For other details of QAOA, such as the selection of $p$ and the overall algorithm loop, please refer to [Farhi, Goldstone, and Gutmann (2014)](https://arxiv.org/abs/1411.4028) or the tutorial of [QAOA for Max-Cut](qaoa.ipynb)." + ] + }, + { + "cell_type": "markdown", + "id": "940fa22b", + "metadata": {}, + "source": [ + "### Setup" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "id": "b0def04d", + "metadata": { + "ExecuteTime": { + "end_time": "2023-07-03T11:21:55.043565200Z", + "start_time": "2023-07-03T11:21:54.946406500Z" + } + }, + "outputs": [], + "source": [ + "import tensorcircuit as tc\n", + "import optax\n", + "import tensorflow as tf\n", + "import networkx as nx\n", + "import matplotlib.pyplot as plt\n", + "import numpy as np\n", + "from IPython.display import clear_output\n", + "import random\n", + "\n", + "K = tc.set_backend(\"jax\")\n", + "\n", + "nlayers = 30 # the number of layers\n", + "ncircuits = 6 # six circuits with different initial parameters are going to be optimized at the same time" + ] + }, + { + "cell_type": "markdown", + "id": "6e407437", + "metadata": {}, + "source": [ + "### Define the Graph" + ] + }, + { + "cell_type": "markdown", + "id": "c82972a4", + "metadata": {}, + "source": [ + "The graph of NAE3SAT is constructed by the set $\\mathcal{C}$ of clauses. When a clause is violated, the energy will increase by 4, so the upper bound of $\\hat{H}_C$ will not exceed $4|\\mathcal{C}|$. In practice, we multiply $\\hat{H}_C$ by a normalization factor $1/(4|\\mathcal{C}|)$." + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "id": "f1532831", + "metadata": { + "ExecuteTime": { + "end_time": "2023-07-03T11:21:55.148550800Z", + "start_time": "2023-07-03T11:21:54.965499300Z" + } + }, + "outputs": [ + { + "data": { + "text/plain": "
", + "image/png": "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" + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "# an easy graph instance\n", + "easy_clauses = [\n", + " [4, 7, 6],\n", + " [0, 5, 9],\n", + " [2, 6, 9],\n", + " [2, 6, 7],\n", + " [3, 1, 9],\n", + " [5, 9, 11],\n", + " [4, 8, 9],\n", + " [5, 1, 9],\n", + " [3, 8, 6],\n", + " [2, 8, 10],\n", + " [5, 6, 8],\n", + " [2, 9, 6],\n", + " [2, 6, 8],\n", + " [5, 3, 9],\n", + " [4, 11, 7],\n", + " [3, 11, 10],\n", + " [5, 10, 7],\n", + " [3, 9, 8],\n", + " [3, 6, 9],\n", + " [2, 4, 7],\n", + " [4, 0, 6],\n", + " [3, 4, 6],\n", + " [3, 11, 6],\n", + " [4, 5, 6],\n", + " [4, 0, 10],\n", + " [5, 4, 10],\n", + " [3, 7, 9],\n", + " [0, 11, 6],\n", + " [5, 11, 9],\n", + " [3, 5, 9],\n", + " [3, 4, 7],\n", + " [3, 4, 7],\n", + " [3, 0, 7],\n", + " [1, 7, 8],\n", + " [0, 3, 10],\n", + " [0, 8, 9],\n", + " [5, 7, 8],\n", + " [2, 9, 6],\n", + " [0, 8, 6],\n", + " [4, 6, 8],\n", + " [3, 2, 9],\n", + " [4, 3, 8],\n", + " [0, 2, 8],\n", + " [4, 5, 10],\n", + " [2, 4, 8],\n", + " [5, 8, 9],\n", + " [4, 8, 9],\n", + " [3, 5, 11],\n", + " [5, 4, 10],\n", + " [2, 7, 9],\n", + " [3, 0, 7],\n", + " [2, 8, 6],\n", + " [5, 3, 6],\n", + " [0, 6, 10],\n", + " [3, 2, 8],\n", + " [4, 6, 9],\n", + " [3, 2, 6],\n", + " [1, 5, 6],\n", + " [2, 8, 11],\n", + " [2, 10, 8],\n", + " [2, 0, 6],\n", + " [2, 6, 9],\n", + " [0, 8, 7],\n", + " [0, 10, 8],\n", + " [3, 5, 7],\n", + " [2, 10, 8],\n", + " [5, 7, 9],\n", + " [0, 1, 6],\n", + " [0, 3, 8],\n", + " [0, 6, 9],\n", + " [0, 5, 11],\n", + " [1, 2, 10],\n", + "]\n", + "factor = 1 / len(easy_clauses) / 4\n", + "\n", + "# convert to a NetworkX graph\n", + "easy_graph = nx.Graph()\n", + "for i, j, k in easy_clauses:\n", + " easy_graph.add_edge(i, j, weight=0)\n", + " easy_graph.add_edge(j, k, weight=0)\n", + " easy_graph.add_edge(k, i, weight=0)\n", + "for i, j, k in easy_clauses:\n", + " easy_graph[i][j][\"weight\"] += 1\n", + " easy_graph[j][k][\"weight\"] += 1\n", + " easy_graph[k][i][\"weight\"] += 1\n", + "pos_easy = nx.spring_layout(easy_graph)\n", + "nx.draw_networkx(easy_graph, with_labels=True, pos=pos_easy)\n", + "ax = plt.gca()\n", + "ax.set_facecolor(\"w\")" + ] + }, + { + "cell_type": "markdown", + "id": "c944f6dd", + "metadata": {}, + "source": [ + "### Parameterized Quantum Circuit (PQC)" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "id": "055d1257", + "metadata": { + "ExecuteTime": { + "end_time": "2023-07-03T11:21:55.167476700Z", + "start_time": "2023-07-03T11:21:55.166422100Z" + } + }, + "outputs": [], + "source": [ + "def QAOAansatz(params, g, each=1, return_circuit=False):\n", + " n = g.number_of_nodes() # the number of nodes\n", + "\n", + " # PQC loop\n", + " def pqc_loop(s_, params_):\n", + " c_ = tc.Circuit(n, inputs=s_)\n", + " for j in range(each):\n", + " # driving layer\n", + " for a, b in g.edges:\n", + " c_.RZZ(a, b, theta=g[a][b][\"weight\"] * params_[2 * j] * factor)\n", + " # mixing layer\n", + " for i in range(n):\n", + " c_.RX(i, theta=params_[2 * j + 1])\n", + " s_ = c_.state()\n", + " return s_\n", + "\n", + " c0 = tc.Circuit(n)\n", + " for i in range(n):\n", + " c0.H(i)\n", + " s0 = c0.state()\n", + " s = K.scan(pqc_loop, K.reshape(params, [nlayers // each, 2 * each]), s0)\n", + " c = tc.Circuit(n, inputs=s)\n", + "\n", + " # whether to return the circuit\n", + " if return_circuit is True:\n", + " return c\n", + "\n", + " # calculate the loss function\n", + " loss = 0.25\n", + " for a, b in g.edges:\n", + " loss += c.expectation_ps(z=[a, b]) * g[a][b][\"weight\"] * factor\n", + "\n", + " return K.real(loss)" + ] + }, + { + "cell_type": "markdown", + "id": "5b159920", + "metadata": {}, + "source": [ + "### Optimization" + ] + }, + { + "cell_type": "markdown", + "id": "6d07ee61", + "metadata": {}, + "source": [ + "Here, several circuits with different initial parameters are optimized/trained at the same time.\n", + "\n", + "Optimizers are used to find the minimum value." + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "id": "690b8b67", + "metadata": { + "collapsed": false, + "jupyter": { + "outputs_hidden": false + } + }, + "outputs": [ + { + "data": { + "text/plain": "
", + "image/png": "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" + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "# use vvag to get the losses and gradients with different random circuit instances\n", + "QAOA_vvag = K.jit(\n", + " K.vvag(QAOAansatz, argnums=0, vectorized_argnums=0), static_argnums=(1, 2, 3)\n", + ")\n", + "\n", + "params_easy = K.implicit_randn(\n", + " shape=[ncircuits, 2 * nlayers], stddev=0.1\n", + ") # initial parameters\n", + "if type(K).__name__ == \"JaxBackend\":\n", + " opt = K.optimizer(optax.adam(1e-2))\n", + "else:\n", + " opt = K.optimizer(tf.keras.optimizers.Adam(1e-2))\n", + "\n", + "list_of_loss = [[] for i in range(ncircuits)]\n", + "\n", + "for i in range(2000):\n", + " loss, grads = QAOA_vvag(params_easy, easy_graph)\n", + " params_easy = opt.update(grads, params_easy) # gradient descent\n", + "\n", + " # visualise the progress\n", + " clear_output(wait=True)\n", + " list_of_loss = np.hstack((list_of_loss, K.numpy(loss)[:, np.newaxis]))\n", + " plt.xlabel(\"Iteration\")\n", + " plt.ylabel(\"Cost\")\n", + " for index in range(ncircuits):\n", + " plt.plot(range(i + 1), list_of_loss[index])\n", + " legend = [f\"circuit {leg}\" for leg in range(ncircuits)]\n", + " plt.legend(legend)\n", + " plt.show()" + ] + }, + { + "cell_type": "markdown", + "id": "98a6b152", + "metadata": {}, + "source": [ + "### Results" + ] + }, + { + "cell_type": "markdown", + "id": "fc1c257d", + "metadata": {}, + "source": [ + "After inputting the optimized parameters back to the ansatz circuit, we can perform the projective measurement on the output quantum state to get the solution. Here we directly use the bit string with the maximum probability as the solution since we know all information of the probability distribution of the output quantum state, but which is not feasible in the experiment." + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "id": "8c5df93e", + "metadata": { + "collapsed": false, + "jupyter": { + "outputs_hidden": false + }, + "ExecuteTime": { + "end_time": "2023-07-03T11:48:26.692908100Z", + "start_time": "2023-07-03T11:47:45.326665300Z" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Circuit #0\n", + "cost: 0.014918745495378971\n", + "max prob: 0.2869008183479309\n", + "bit strings: ['111111000000']\n", + "\n", + "Circuit #1\n", + "cost: 0.030193958431482315\n", + "max prob: 0.21499991416931152\n", + "bit strings: ['111111000000']\n", + "\n", + "Circuit #2\n", + "cost: 0.021412445232272148\n", + "max prob: 0.24743150174617767\n", + "bit strings: ['000000111111']\n", + "\n", + "Circuit #3\n", + "cost: 0.013799840584397316\n", + "max prob: 0.2941778600215912\n", + "bit strings: ['000000111111']\n", + "\n", + "Circuit #4\n", + "cost: 0.014260157942771912\n", + "max prob: 0.29104718565940857\n", + "bit strings: ['111111000000']\n", + "\n", + "Circuit #5\n", + "cost: 0.013322753831744194\n", + "max prob: 0.2968374788761139\n", + "bit strings: ['111111000000']\n", + "\n" + ] + } + ], + "source": [ + "# print QAOA results\n", + "for num_circuit in range(ncircuits):\n", + " print(f\"Circuit #{num_circuit}\")\n", + " c = QAOAansatz(params=params_easy[num_circuit], g=easy_graph, return_circuit=True)\n", + " loss = QAOAansatz(params=params_easy[num_circuit], g=easy_graph)\n", + "\n", + " # find the states with max probabilities\n", + " probs = K.numpy(c.probability())\n", + " max_prob = max(probs)\n", + " index = np.where(probs == max_prob)[0]\n", + " states = []\n", + " for i in index:\n", + " states.append(f\"{bin(i)[2:]:0>{c._nqubits}}\")\n", + "\n", + " print(f\"cost: {K.numpy(loss)}\\nmax prob: {max_prob}\\nbit strings: {states}\\n\")" + ] + }, + { + "cell_type": "markdown", + "id": "4ae99ab9", + "metadata": {}, + "source": [ + "## Classical Method" + ] + }, + { + "cell_type": "markdown", + "id": "720dd1a4", + "metadata": {}, + "source": [ + "Here we use two classical methods. The first is the brutal force method (BF), which is to check all bit string one-by-one and need exponential time, thus the obtained solution is guaranteed to be correct." + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "id": "2115bb6d", + "metadata": { + "ExecuteTime": { + "end_time": "2023-07-03T11:48:26.693910800Z", + "start_time": "2023-07-03T11:48:26.692908100Z" + } + }, + "outputs": [], + "source": [ + "def b2s(bit):\n", + " return 1 - 2 * int(bit)\n", + "\n", + "\n", + "def energy(cfg, graph, normalize=True):\n", + " E = 0.25\n", + " for a, b in graph.edges:\n", + " E += cfg[a] * cfg[b] * graph[a][b][\"weight\"] * factor\n", + " return E if normalize else E / factor\n", + "\n", + "\n", + "def brutal_force(graph):\n", + " num_nodes = graph.number_of_nodes()\n", + " min_cost, best_case = 1.0, []\n", + " for i in range(2**num_nodes):\n", + " case = f\"{bin(i)[2:]:0>{num_nodes}}\"\n", + "\n", + " cost = energy(list(map(b2s, case)), graph)\n", + "\n", + " gap = min_cost - cost\n", + " if gap > 1e-6:\n", + " min_cost = cost\n", + " best_case = [case]\n", + " elif abs(gap) < 1e-6:\n", + " best_case.append(case)\n", + "\n", + " return best_case, min_cost" + ] + }, + { + "cell_type": "code", + "execution_count": 11, + "id": "b0cdc04f", + "metadata": { + "collapsed": false, + "jupyter": { + "outputs_hidden": false + }, + "ExecuteTime": { + "end_time": "2023-07-03T11:48:27.165348700Z", + "start_time": "2023-07-03T11:48:26.692908100Z" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "cost: 0.000\n", + "bit string: ['000000111111', '111111000000']\n" + ] + }, + { + "data": { + "text/plain": "
", + "image/png": "iVBORw0KGgoAAAANSUhEUgAAAgMAAAGFCAYAAABg2vAPAAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjcuMSwgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy/bCgiHAAAACXBIWXMAAA9hAAAPYQGoP6dpAAEAAElEQVR4nOyddVhU29fH1yRDh4SEEiqiomKgoIIoFjZXsQsVxcYA9doY11Zssbu7E7u7rt1gKxISw5zv+weX84oMMAMzqD/353nmudc55+y9zwxz9tprr/VdAgAgBoPBYDAYfyzCnz0ABoPBYDAYPxdmDDAYDAaD8YfDjAEGg8FgMP5wmDHAYDAYDMYfDjMGGAwGg8H4w2HGAIPBYDAYfzjMGGAwGAwG4w9HrMpJHMdRTEwMGRoakkAg0PaYGAwGg8FgaAAAFB8fTzY2NiQUZr/+V8kYiImJoSJFimhscAwGg8FgMAqOV69ekZ2dXbbHVTIGDA0N+caMjIw0MzIGg8FgMBhaJS4ujooUKcLP49mhkjGQsTVgZGTEjAEGg8FgMH4zctviZwGEDAaDwWD84TBjgMFgMBiMPxxmDDAYDAaD8YfDjAEGg8FgMP5wmDHAYDAYDMYfDjMGGAwGg8H4w2HGAIPBYDAYfzjMGGAwGAwG4w+HGQMMBoPBYPzhMGOAwWAwGIw/HGYMMBgMBoPxh8OMAQaDwWAw/nCYMcBgMBgMxh8OMwYYDAaDwfjDYcYAg8FgMBh/OMwYYDAYDAbjD0f8swfwyyGXEz14QPTlC5FAQGRlRVSsGJGQ2U0MBoPB+N+EGQNERLGxRKtXE61ZQ3TrFlFqaubj+vpE7u5E3boRtWxJJJP9lGEyGAwGg6EN/uzlbmoq0bhxRNbWRCEhRFevZjUEiIgSE4lOnSLq2JHIxoZo2TIioMCHy2AwGAyGNvhzjYEHD4gqVkw3BpKT0yf3nCZ4jkv/75cvRN27EzVoQPTxY8GMlcFgMBgMLfJnGgO3bxN5ehLdv5/3Ff6xY0TVqxO9f6/ZsTEYDAaDUcD8ecbA27dEtWsTxcURKRR5b0ehIHr6lKhuXeVbCwwGg8Fg/Cb8WcYAQNSjR7qrX4khkEBEY4ioARGZEZGAiFbm1F5aWrqXYfx4LQyWwWAwGIyC4c8yBnbuJNqzJ1uPwEciCieif4movKptAkSTJhH9+69GhshgMBgMRkHzZxkD06fnqBdgTURviOgFEU1Tp12hkGjevPyNjcFgMBiMn8SfYwzcvUt07tz/ZwUoQYeICuel7bQ0ohUriBIS8jo6BoPBYDB+Gn+OMXDsmHZVBJOSiC5f1l77DAaDwWBoiT/HGLh6VbvGgEhEdOWK9tpnMBgMBkNL/DnGwL176e58bSEQED18qL32GQwGg8HQEn+OMZCcrP0+mN4Ag8FgMH5D/hxjQFdXu+0LBKyAEYPBYDB+S/4cY8DVlUisxSKNCgVRyZLaa5/BYDAYDC3x5xgDlSrlT344NziOqHJl7bXPYDAYDIaW0OJS+Rejbl2VihLNI6JYIor57997iOj1f//fj4iMs7kuRSKhVyYmVDyfw2QwGAwGo6D5czwDzs5EPj7pKYA5MJ2IRhHRwv/+vf2/f48ioi/ZXJNGREuJqET58lS3bl3avn07pWkzc4HBYDAYDA3y5xgDRERhYbluFTwnImTzcsjmGhDRLLmcKlSoQO/fv6cWLVqQvb09jR07ll6/fp3NVQwGg8Fg/Br8WcaAnx9Rmza5egfUAUS00smJnhDRw4cP6datW9SwYUOqWbMmTZ8+nRwcHMjf358OHz5MXA5SyAwGg8Fg/CwEQO4b6XFxcWRsbExfv34lIyOjghiX9vj0iahcOaL37/MtQpRGRFcEAnq9fj3JARo0aBB9+fKFpFIpffv2jbp06ULFihWj9evX0507d6hYsWLUs2dPCgwMJHNzc83cD4PBYDAY2aDq/P1neQaIiAoVIoqKSv9vPlIN04joiUxGzcRiCmjbll6/fk3//vsv9ezZkxITE8nc3Jw2bdpE48ePp4YNG9L+/fvJ09OTRo4cSba2ttShQwc6e/YsqWCLMRgMBoOhVf48Y4AoPZjwwoX0dEM1gUBARET7dXSob9my5FChAuno6FBYWBgNGzaMZsyYQVeuXCEHBwdKSEigEiVK0Lx586ht27bk4uJCDx48oAkTJtCFCxeoRo0aVL58eVqwYAHFxcVp+i4ZDAaDwVCJP9MYICJycCA6e5Zo5kwi4/8SBnMoZIT/4gw+S6VEW7aQ2dGjdOL6dapQoQJVqlSJZDIZLV26lBo3bkzFihWjc+fOUWRkJL169YokEglVrFiRxo0bR1WrViWZTEa3b9+mQ4cOUbFixahfv35kY2NDPXv2pBs3bmj/3hkMBoPB+I4/1xggSg8kHDiQ6M0bopUriWrXJs7QMMtpsTo6JGjalI7270+WKSn0b5kyVKNGDYqIiKDFixdTly5dqEqVKiSVSun06dPk5eVF0dHRFBQURA8ePKAWLVpQVFQUlS5dmqpWrUohISFUqlQpiomJoa1bt9KLFy9oyJAhtHfvXqpQoQJ5eHjQqlWrKCkpqeA/EwaDwWD8eUAFvn79CiLC169fVTn9t+bSxYuwI8K/GzcCt25hRPfuKFmyJAAgOTkZ1tbW6NatGwCA4zh06dIFMpkMZ86cQe3ataGrqwtLS0vY2Njg2rVrfLtnzpxB2bJlIRAI0K5dOzRp0gREhFKlSmHbtm3gOA6pqanYvn076tatCyKCqakpBg4ciAcPHvyUz4LBYDAYvzeqzt/MGPiB7du3g4jw/v17AEBkZCSEQiGSkpIAAP/88w+kUinevHkDAEhKSkLlypVRtGhRvHjxAvXq1YNMJkOJEiWgr6+PPXv28G3L5XLMnDkTBgYGsLS0xLhx4/iJv3Llyjh8+DA4jgMAPHz4EEOGDIGZmRmICL6+vtiyZQtSU1ML+BNhMBgMxu+KqvP3n71NoITXr1+TVCrlU/9cXV2J4zh68OABEREFBweTVCqluXPnEhGRTCajbdu20bdv36hr1660bds2ql27Nr18+ZLKli1LzZo1o3nz5hERkVgspoEDB9L9+/fJx8eHxowZQ6mpqbRixQoSi8VUr1498vX1pQsXLlCJEiVo2rRpFB0dTWvWrKGkpCQKCAgge3t7Gj16NL169ernfEAMBoPB+N9Dk5bF/wJhYWFwdHTk/51x72vXruXfGzhwIExNTREfH8+/d/z4cYhEIgwZMgTJyclo2rQppFIpmjZtCiJCSEgI0tLSMvV16NAhFC9eHGKxGGFhYdi8eTPKli0LIkLTpk1x+/btTOffvHkTvXr1goGBAYRCIZo2bYoDBw5AoVBo6dNgMBgMxu8M2ybII+3bt4eXl1em94oUKYJhw4bx/37+/DlEIhEiIiIynTdz5kwQETZu3IiUlBT89ddfEIvFCAoKglAoRLNmzZCQkJDpmqSkJISHh0NHRwdFixbFtm3bsGbNGjg5OUEgEKBDhw548uRJpmvi4uKwcOFClCtXDkQER0dHTJ48md/aYDAYDAYDYMZAnqlZsybatm2b6T0/Pz80btw403vt2rWDg4MD5HI5/x7HcWjXrh309PRw8+ZNpKamolWrVhCJRAgLC4O+vj4qV67Mxxt8z+PHj+Hn5wciQqNGjXD//n0sXLgQ1tbWEIvF6NWrF2JiYjJdw3Eczp07h06dOkFHRwdSqRRt27bFqVOn+NgDBoPBYPy5MGMgjxQrVgyhoaGZ3gsNDc20dQAA165d470A35OYmIjy5cvDyckJnz59glwuR/v27SEUCjFhwgRYW1ujaNGiWbYAgPTJffv27bCzs4NMJsP48ePx+fNnTJkyBaamptDV1cXQoUPx6dOnLNd+/PgR06dPR/HixUFEKFOmDObOnYvY2FgNfCoMBoPB+B1hxkAe4DgOOjo6Wdz/q1atAhFlihEAAF9fX1SqVCnLKvzp06cwMzNDgwYNkJaWhrS0NHTp0gVCoRAzZ85E2bJlYWRkhCNHjigdR3x8PMLCwiAWi+Hs7IwjR47gy5cvGDlyJPT19WFsbIwJEyZkGQ8AKBQKHDlyBH/99RdEIhH09PTQvXt3XL16NZ+fDoPBYDB+N5gxkAc+fPgAIsK2bdsyvX/lyhUQES5evJjp/QMHDoCIEBUVlaWtQ4cOQSgUYsSIEQDSJ+nu3btDIBBg7ty5aNCgAcRiMZYtW5bteO7cuQNvb28QEVq3bo3o6Gi8ffsW/fv3h1QqhaWlJebMmYPk5GSl10dHR2PcuHGwtbUFEaFKlSpYvnw5EhMT1fxkGAwGg/E7woyBPHD9+nWlk35iYiIEAkGWiZvjOLi6uqJhw4ZK25s8eTKICNu3bweQbhD06tULRIR58+ahZ8+eICIMHz4824wAjuOwevVqWFhYwNDQELNnz4ZcLsfz588RGBgIoVAIe3t7rFixIku2QgZyuRw7d+5E/fr1QUQwMTHBgAED8O+//6r7ETEYDAbjN4IZA3lgz549ICJER0dnOVa8eHEMGjQoy/srV64EEeHOnTtZjnEch5YtW8LAwAD37t3j3+vfvz+ICHPmzMG0adP4lX+GsJEyPn/+jN69e0MgEKB8+fI4d+4cAODevXto0aIFr2a4devWHIMHHz9+jLCwMJibm4OIUKtWLWzevBkpKSm5fj4MBoPB+L1gxkAeWLhwIUQikdIVdvPmzVGvXr0s76ekpMDGxgaBgYFK24yPj0eZMmXg7OzMB/NxHIfBgweDiDBz5kxs3boVMpkM1apVw4cPH3Ic46VLl1CpUiUQEbp3746PHz8CAC5fvox69eopVTNURnJyMtauXYsaNWqAiGBlZYURI0bgxYsXOfbPYDAYjN8HZgzkgZEjR8LOzk7psREjRsDGxkbpsSlTpkAikWRJ/cvg4cOHMDY2RtOmTfntAI7jMHz4cBARJk+ejPPnz8PCwgLFihXLtRZBWloa5s+fD2NjYxQqVAhLly7l242KioKHhweICD4+Pjh//nyu933r1i306dMHhoaGEAqFaNy4Mfbt25fttgODwWAwfg+YMZAHunTpAg8PD6XHNmzYACLC58+fsxyLjY2FoaFhJmGiH9m7dy8EAgHGjRvHv8dxHEaPHg0iwvjx4/H06VO4uLjAzMwMp06dynW8b9++RceOHUFE8PT0xI0bN/h2d+/enaOaoTLi4+OxePFiuLm5gYjg4OCASZMm4e3bt7ley2AwGIxfD2YM5IE6deqgZcuWSo/dvn0bRITTp08rPT548GAYGxsjLi4u2/bHjRsHgUCQqXgRAISHh4OIMHr0aHz69Ak+Pj6QSqWZJJBz4sSJEyhdujSEQiFCQkL47yktLQ1r167NUc1QGRzH4cKFC+jcuTNkMhkkEglat26NEydOMDEjBoPB+I1gxkAecHFxQUhIiNJjKSkpEIvFWLhwodLjL1++hFgsxqxZs7JtX6FQoGnTpjA2NsbDhw8zHcvIPBg+fDiSk5PRqVMnEBHCw8NVmoBTUlIwZcoU6OnpwdraGhs3buSvS01NzVXNMDs+ffqEmTNnwtnZmQ9SjIiIwJcvX1S6nsFgMBg/D2YMqAnHcdDX18f06dOzPad06dLo06dPtsc7dOiAokWL5lhmODY2Fs7OzihdunQWL8KMGTNARBgyZAgUCgXGjRsHIkKXLl1UjvZ/8eIF/P39QUSoU6dOpviDxMREldQMlcFxHI4dO4aWLVtCLBZDV1cXXbt2xeXLl1W6nsFgMBgFDzMG1CQ2NlapvPD3tGrVCjVr1sz2+I0bN0BEWL9+fY593bt3DwYGBmjZsmWWVf+cOXNARBgwYAA4jsOaNWsglUpRu3ZttVbje/fuhaOjI6RSKUaOHIlv377xx1RVM8yOmJgYjB8/HkWKFOGzF5YuXcrEjBgMBuMXgxkDanLnzh0QEc6cOZPtOeHh4ShUqFCObvu6deuiQoUKubr2t2/fzmcS/MjChQtBROjduzcUCgVOnjwJU1NTlCpVCk+fPlX5nr59+4ZRo0ZBKpXC0dERe/fuzXT8RzXDiIiIbNUMlSGXy7F79274+flBIBDA2NgY/fv35zUVGAwGg/FzYcaAmhw8eBBEhOfPn2d7TsYEnlN0/aFDh0BEOHbsWK59jhgxAkKhEIcOHcpybOnSpRAIBAgKCoJCocD9+/fh5OQES0tLXLhwQbWb+o8HDx6gTp06ICI0b948i5aAOmqG2fH06VMMGzYMFhYWICLUrFmTL+XMYDAYjJ8DMwbUZOnSpSCiHCevBw8egIhw9OjRbM/hOA7lypVDgwYNcu0zLS0NDRo0gKmpqdIo/5UrV0IgEKBLly5IS0vD+/fvUa1aNchksiz1E3KD4zhs2rQJ1tbW0NPTw+TJk7Pcq7pqhspITk7G+vXr+ZoKlpaWGD58OJ49e6ZWOwwGg8HIP8wYUJNx48bBysoqx3PS0tKgo6OD2bNn53je6tWrQUQq5fZ//vwZTk5OKF++vNI993Xr1kEoFKJDhw6Qy+VISkpCq1atIBAIMH36dLUn669fv2LgwIEQiUQoVaqU0iJL6qoZZsedO3fQt29fGBkZQSAQoGHDhtizZw8TM2IwGIwCghkDahIUFIRKlSrlel6FChUQFBSU4zmpqamws7ND586dVer71q1b0NPTQ7t27ZROups2bYJIJEKbNm2QmpoKhUKBYcOGgYgQHBwMuVyuUj/fc+PGDVSrVg1EhA4dOijd+siLmqEyEhISsGTJElSsWBFEhKJFi2LChAl48+ZNntpjMBgMhmowY0BN/Pz80KxZs1zP69ixIzw9PXM9b/r06ZBIJHj9+rVK/W/cuJGvVaCMbdu2QSwWo0WLFrx7PzIyEiKRCH5+fjmKHWWHQqHAsmXLUKhQIRgbG2PevHlZVu3K1Axv3bqldl8ZXLp0CYGBgdDV1YVYLEZAQACOHz/OxIwYDAZDCzBjQE3Kli2bo4ZABpMnT4ahoWGuk9fXr19hZGSEsLAwlccwZMgQiEQiHD9+XOnxXbt2QSKRoFmzZnzU/+HDh2FkZIRy5crh1atXKvf1PR8/fkRQUBCICJUqVcKlS5eynPOjmmH79u1VUjPMjs+fP2P27NlwcXEBEaFkyZKYNWuWUrlnBoPBYOQNZgyoiampKf75559cz9u7dy+ISKXqfqGhoTAyMlL5c5PL5fD19YW5uXm27e/btw86Ojpo2LAhX/L49u3bKFKkCGxsbHD9+nWV+lLG+fPn4ebmBoFAgF69eimdmJWpGSor+awqHMchKioKrVq14sWMAgMDcfHiReYtYDAYjHzCjAE1SEhIABFhzZo1uZ77/PlzEBH279+f67mvXr2CWCzGjBkzVB7Lhw8fYG9vj0qVKmUSCvqeQ4cOQSaToV69evw5MTExqFSpEvT19bFv3z6V+/sRuVyOiIgIGBoawsLCAqtWrVI6Kf+oZhgWFqaymmF2vHnzBhMnTkTRokVBRKhYsSKWLFmChISEfLXLYDAYfyrMGFCDjJRBZZH1P8JxHAwMDDB16lSV2u7UqROKFCmSo0Txj1y9ehUymQxdunTJdnV87Ngx6OnpoXbt2vxkmZCQgKZNm0IoFGL+/Pkq96eMmJgYtG3bFkQELy+vbDMj8qtmqIy0tDTs2bMHjRo1gkAggJGREfr27Ys7d+7kq10Gg8H402DGgBocO3YMRIRHjx6pdH7VqlXRqVMnlc69efMmiEjlCoQZZKQn5jSpnzx5Evr6+vD29uYn4LS0NAwYMABEhEGDBuU7je/o0aMoWbIkxGIxQkNDs53o86tmmB3Pnj3D33//DUtLS94wWb9+vUbaZjAYjP91mDGgBhkTb3Zu+R/p1q0bKlasqHL79evXR/ny5dXeA+/fvz/EYnG2ZZMB4OzZszA0NET16tUzfT9z5syBUCiEv79/vmsGJCcnY+LEiZDJZLCzs8tRjEgTaobKSElJwcaNG+Hj4wMigoWFBYYOHaqWPDODwWD8aTBjQA0mTZoEMzMzlc+fNWsWdHV1VZ7kjhw5AiLCkSNH1BpXamoqvL29YWVllWOK4oULF2BsbAwPD49MxYx2794NPT09uLu7aySn/+nTp2jcuDGICA0aNMjRk6IJNcOc2u7fvz+MjY0hEAjQoEED7Nq1i4kZMRgMxg8wY0ANevfujXLlyql8fsbkruq2AsdxcHNzQ7169dQe29u3b2FrawsPD48cXeNXrlyBqakpKleunCmQ7+rVq7C2toa9vb3G9tx37dqFokWLQkdHB2PHjuWzGpShKTVDZSQmJmLZsmVwd3cHEaFIkSIIDw9HTEyMRtpnMBiM3x1mDKhB06ZN0bBhQ5XPj4mJARFhx44dKl+zdu1aEBFu3ryp9vguXrwIqVSKnj175nje9evXUahQIbi5ueHjx4/8+y9evEDZsmVhbGycY10FdUhISMDw4cMhkUhQrFgxHDx4MMfzNaVmmB1XrlxBt27deDGjli1b4ujRoyw9kcFg/NEwY0ANKlasiB49eqh8PsdxMDMzw/jx41W+JjU1FUWKFEHHjh3zMkS+kNKSJUtyPO/WrVuwsLBA2bJl8e7dO/79r1+/ol69ehCLxVi2bFmexqCMe/fuoVatWiAitGzZMkfhI02rGSrjy5cvmDNnDkqVKgUigrOzM2bMmJHvtEcGg8H4HWHGgBpYWloiPDxcrWu8vb3Rpk0bta6ZOXMmxGIxXr58qdZ1GfTs2RNSqTTXEsZ3796FlZUVSpcunSlWIDU1lVcaHDFihMZWzRzHYd26dbCysoKBgQFmzJiRYyqlptUMsxvTyZMn0aZNG0gkEshkMnTu3Bnnz59n3gIGg/HHwIwBFUlOTgYRYfny5Wpd17t3b7i6uqp1TVxcHIyNjTFkyBC1rssgOTkZnp6esLW1VVpY6Hvu378PGxsblCxZMpNCIMdxmDJlCogIbdu2zXG/X12+fPmCfv36QSgUomzZsjlmQQCaVzPMjrdv3+Kff/6Bg4MDiAhubm5YvHhxvvUQGAwG41eHGQMq8vTpUxARDh8+rNZ18+fPh1gs5osGqcrQoUNhaGiI2NhYta7LIDo6GoULF4aXl1euQkaPHj1CkSJFULx48SzeiM2bN0NHRwc1atTAhw8f8jSW7Lh69SqqVKkCIkJgYCDev3+f4/naUDNURlpaGvbv348mTZpAKBTC0NAQvXv31vhWBYPBYPwqMGNARU6dOgUiwr1799S67uTJkyAitSP0o6OjIZFIMG3aNLWu+54zZ85ALBajX79+uZ779OlT2Nvbw9HREc+fP8907Ny5c7CwsEDx4sXx8OHDPI9HGQqFAosWLYKpqSlMTU2xePFiKBSKHK/Rhpphdrx48QIjRoyAlZUViAjVq1fH2rVrNeopYTAYjJ8NMwZUZMOGDXm6t48fP4KIsGnTJrX77NKlC2xtbdX2KnzP/PnzQURYtWpVruc+f/4cTk5OsLe3z7I3/+TJE5QsWRJmZma5uvXzwrt379ClSxcQEapUqYKrV6/meo221AyVkZqais2bN6N27dogIpibmyM0NBSPHz/WSn8MBoNRkDBjQEWmTZsGQ0PDPF1rbW2NUaNGqX3d7du3QURYvXp1nvoF0vf+AwMDIZPJVJpgX716hRIlSsDOzi6LF+DTp0+oWbMmpFIp1q9fn+cx5cSpU6fg6uoKoVCIfv36qbRNokzNUC6Xa2V8QHqcRUhICExMTEBEqF+/Pnbs2KHVPhkMBkObMGNARQYMGIBSpUrl6do6derA398/T9f6+fmhbNmy+YpsT0pKQuXKlVG0aFGV9v2jo6Ph4uICa2tr3L9/P9OxlJQUdOzYEUSECRMmaCXiPjU1FdOnT4e+vj6srKywbt06lfr5Xs3QxcVFo2qGyvj27RtWrFjBxz3Y2tpi7NixOapAMhgMxq8IMwZUpEWLFqhbt26erg0JCUGJEiXydG1GcaRDhw7l6foMXr58CQsLC9SuXVulFezbt29RpkwZWFlZ4e7du5mOcRyHsWPH8oF/+dnGyIlXr16hZcuWICLUqlVL5XgNbaoZZsfVq1cRFBQEPT09iEQi/PXXXzh8+HCu8Q8MBoPxK8CMARWpWrUqAgMD83Tt0qVLIRQKVS5w9D0cx6FixYqoU6dOnvr+nqioKIhEIpVTFt+/f49y5crBwsJCqSLi6tWrIZFIULt27Uy1DjTNgQMHUKxYMUgkEgwfPlzlgkraVjNURmxsLObNm4cyZcqAiFC8eHFMnz49k9Ijg8Fg/GowY0BFbG1t87TvD6QXCCIiXLt2LU/XZwQvXr9+PU/Xf8+sWbNARNiwYYNK53/8+BEVKlRAoUKFlI4/KioKJiYmKF26NJ49e5bv8WVHUlISxo4dCx0dHdjb22PXrl0qXVcQaobZ9Xv69Gm0a9cOUqkUOjo66NixI86ePcvEjBgMxi8HMwZUQC6XQygUYvHixXm6Pi4uLl+BgHK5HPb29mjfvn2erv8ejuPQrl076OnpqVz/4PPnz3B3d4eJiQkuX76c5fi///4LJycnWFlZ4dKlS/keY048evQI9evXBxGhSZMmKhsgytQMCyoT4P3795gyZQocHR1BRChXrhwWLlyIuLi4AumfwWAwcoMZAyrw6tUrEBH27duX5zbs7e0RFhaW5+tnz54NkUiEFy9e5LmNDBITE1G+fHk4OTmpLNoTGxsLDw8PGBsbK5U5fv/+PTw8PKCrq4vt27fne4w5wXEctm7dCltbW+jq6mLixIkqpxT+qGYYHBysFTVDZSgUChw4cADNmjWDUCiEgYEBgoOD81SUisFgMDQJMwZUIMPNn5+HdqNGjdCoUaM8Xx8fHw8TExMMGjQoz218z9OnT2FmZoYGDRogLS1NpWu+fv2KGjVqwNDQEGfOnMly/Nu3bwgICIBAIMCMGTO07g6Pi4vDkCFDIBKJULJkSbUqLRaUmmF2vHz5EqNHj4a1tTWICJ6enli9ejUTM2IwfnNSFQpcj4vD8pgYTHvxAtNfvsTqN29wOz4e8l84oJgZAyqwdetWEFG+gsCGDh0Ke3v7fI1j+PDhMDAw0Fiw3uHDhyEUCvH333+rfE18fDx8fHygr6+PEydOZDmuUCgwdOhQEBF69+5dILn3t27dQo0aNfg6CjExMSpfW5BqhspITU3F1q1b4evrCyKCmZkZBg8erHGlRwaDoT04jsOFr1/R8d496Jw4AYqKAkVFQfTfK+PfeidPouf9+7j5C9Y7YcaACsyePRsymSxfK93Vq1fn+7OJiYmBVCrFlClT8tzGj0yePBlEhG3btql8TWJiInx9faGrq4tjx44pPScyMhIikQgNGzYskL1xjuOwcuVKmJubw8jICBEREWoZIgWpZpgdDx48wKBBg2BqagoiQp06dbBt2zYmZsRg/MK8SU5Gs1u3QFFREH9nCGT3yjin0717+JJL3ZiCRNX5W0h/MK9fvyY7OzsSCAR5bsPV1ZWIiO7du5fnNqytralDhw4UERFBqampeW7ne8LCwqhly5bUuXNnlcemp6dHe/bsIW9vb2rUqBEdPnw4yzlBQUG0b98+On36NHl7e1N0dLRGxpsdAoGAOnfuTA8ePKB27dpRSEgIubu704ULF1S63srKiiIiIujhw4fUqFEjGjhwIJUsWZJWrlxJaWlpWh17Bs7OzjRjxgyKjo6mVatWUUJCArVo0YLs7e1p7Nix9Pr16wIZB4PBUI2jnz+Ty6VLtO/TJyIiSgNyvSbjnHXv3lHJS5foUlycVseoaZgxYGeXrzZcXFxIKBTS3bt389XO4MGDKSYmhjZs2JCvdjIQCAS0YsUKsre3J39/f/r69atK1+nq6tLOnTvJ19eXmjRpQvv3789yTv369enMmTP08eNHqlq1Kt28eVMjY84JMzMzWrhwIV24cIGEQiF5enpSjx496NN/P9bcsLe3p+XLl9OdO3eocuXKFBgYSGXLlqVt27YRVPih50pqKtGFC0Tz5xP17UvUtStRcDDR1KlEx44RxcWRrq4uderUic6fP0/Xr1+nJk2a0PTp08nBwYH8/f3p0KFDxHFc/sfCYDDyzIFPn8jv9m2KUygoL8sFBRF9lMvJ58YNOqfic/dXgBkD+TQGdHV1qXjx4nTnzp18tVO6dGlq1KgRTZ8+XTOTExEZGBjQjh076N27d9SxY0eVJxqZTEbbt28nPz8/at68Oe3evTvLOeXKlaOLFy+SpaUl1ahRgw4cOKCRMedGlSpV6NKlSzRv3jzatGkTubi40IoVK1S+t1KlStHWrVvp8uXLVLRoUWrZsiW5u7vT4cOH8/a5R0cTjRhBZG1N5OlJ1K8fUWQk0Zo1RMuXEw0fTlSnDpGlJVH37kT/GU5ubm60aNEiiomJoblz59KTJ0+oQYMG5OzsTNOmTaOPHz+qPxYGg5EvHnz7Rv537pACoPw8hTkiSuE4anjrFsWkpGhqeNpFk3sOvxsODg4YNmxYvtvx9/fXmJIgEeHAgQP5but79u7dC4FAgHHjxql1XWpqKlq2bAmxWIytW7cqPSc+Ph5NmjSBSCTCwoULNTFclXnz5g06dOjAlyDOS1ZIntUMFQpgwQJAVxcQiQCi3F9iMSAQAIMGAT+oLXIch7Nnz6JDhw6QSqWQSqVo3749zpw5w8SMGIwCII3jUOXKlfS9//37QZ06gdzdQYaGICLQ0KFZYwUWLAA1bQoqUQIkEqWf930cQVQUGt28+VN/wyyAMBcUCgUkEgnmzZuX77ZGjRqFwoUL57sdjuNQuXJl1K5dO99t/Uh4eDiICHv27FHrOrlcjjZt2kAkEmHjxo1Kz0lLS0O/fv1ARBg8eHCB6/YfP34cLi4uEIlEGDRokNqBjWqrGSYmAo0aqWYAKHsJhUCJEkA22hIfPnzAtGnTUKxYMRARypYti/nz5/9P/f4YjF+N5TEx/z+R/6cOS1ZWIDe37I2Bzp1BYjHI2RlUpEgWYyDjtfcnypYzYyAX3r59CyLCzp07893Wpk2b8p2i+GNbqpQlVgeFQoGmTZvCyMgIDx48UOtauVyOjh07QigUYs2aNdmeFxERAYFAgL/++kvlOgOaIiUlBf/88w90dXVhY2ODzZs3q22Nq6RmmJQE+PikT+h5NQYyvAS2tkAOlRAVCgUOHToEf39/iEQi6Ovro0ePHhqRr2YwGP8Px3FwvXQJwowJ/NAh0LZt6f+/aFH2xsC2baCDB9P/v3lzpcaAKCoKdW/c+Gn3xrIJciEjCj6/MQNE/59RkN8gQiKiv/76ixwcHGj69On5but7hEIhrV69mqytrcnf35/i4+NVvlYsFtOKFSuoc+fO1KlTJ1q5cqXS8/r37087d+6kgwcPUq1atejdu3caGn3uSKVSGjZsGP3777/k7u5OrVq1ogYNGtCjR49UbkMkElH79u3p/v37tGDBAjp+/Di5uLhQr169KCYmJv2ksDCiU6eI8hvol5ZG9O4dkb8/kUKh9BShUEj16tWj7du304sXLyg0NJT27t1LFSpUIA8PD1q1ahUlJSXlbxwMBoOuJyTQncRE4n/VUimRmVnuF5qZEeno5HiKgoiOfPlCL5OT8ztMrfLHGgMZ6Vy2trb5bqtEiRIkkUjyHURIlD7xDho0iDZv3kwvXrzId3vfY2xsTDt27KCXL19SYGCgWgFzIpGIli5dSkFBQdS1a1daunSp0vOaNm1Kp06dopcvX5KHh0e+Ui7zgr29Pe3cuZN2795NDx8+JFdXVxozZoxak6ZEIqHg4GB6/PgxTZw4kTZt2kTFixenRW3bEs2dm60hkEJEQ4nIhoh0iagqER3JqaO0NKLLl4lmz851TLa2tjRmzBh68eIFbd++nYyMjKhLly5ka2tLgwYNoocPH6p8fwwGIzNnvn6lvCeYq8b5XzzV8I82BsRiMVlaWua7LYlEQiVLltSIMUBEFBgYSEZGRjRbhUlCXUqVKkWrV6+mbdu20ZQpU9S6VigU0sKFC6l3794UFBRECxcuVHpepUqV6OLFi6Svr0/VqlWj48ePa2LoatGkSRO6e/cuhYaG0j///EOurq5qZzzo6elRWFgYPX36lAYPGkQ1Nm3KMdWoCxHNJKL2RBRBRCIiakhEZ3LraORIothYlcYkFovJ39+fDh8+TI8ePaJu3brR6tWrqWTJklSnTh3aunUryeVyldpiMBjpXI2PJ1E+9GZyQyIQ0BU1vLE/gz/aGLC1tSWhUDMfgaurq8aMAQMDA+rduzctWbKEvnz5opE2v8ff359GjBhBf//9Nx06dEita4VCIc2dO5dCQkKod+/eNGfOHKXnFS1alM6ePUtVqlSh+vXrZ7u1oE309PRowoQJdPv2bXJ0dKSGDRtSixYt6NWrV2q1Y2JiQuMbNyZXgMTZnHOJiDYS0T9ENI2IehDRcSKyJ6Kw3DpISSFatUqtMRERFS9enKZNm0avX7+mNWvWUFJSEgUEBJC9vT2NHj1a7ftkMH5XAJBCoaCUlBRKTEykr1+/0qdPn+jdu3cUHR1NL168oCdPntCDBw/o3r17dOvWLbp27RpdunSJzp07RzffvVNJWCivKAB69YtvE2T3bPufRxMaA9/j6urK56rnR9Ewg759+9K0adNo8eLFNGzYMA2MMDPjxo2ja9euUdu2benKlSvk5OSk8rUCgYBmzpxJEomEBgwYQHK5nAYPHpzlPGNjY9q3bx/17t2bAgMD6cmTJxQeHq6Rz0cdSpYsSUeOHKHNmzfTwIEDqVSpUjRmzBgKCQkhiUSiWiMrVxKJxemufSVspXRPQI/v3pMRUTci+puIXhFRkZzaX7qUaMAA1cbyAzKZjDp06EAdOnSgW7du0aJFi2jWrFk0ceJEaty4MfXq1Yvq1aunMcOX8evBcRylpaVRWloaKRQK/v+18fpV288Xc+cS/Rf7pQ1AqqkY/kyYMaAhXF1d6fPnz/T27VuytrbOd3uFCxemTp06UUREBA0cOJB0cglSUReRSETr1q0jd3d38vf3p3PnzpG+vr7K1wsEApoyZQpJJBIaMmQIyeVypUaLRCKhyMhIKlasGA0fPpyePn1Ky5cv1/j9qDLe1q1bk5+fH40ZM4aGDRtGq1atogULFpC3t3fuDZw5k60hQER0nYicicjoh/er/PffG5SDMQAQ3btH9O0bkZ5e7mPJgXLlytGCBQtoypQptH79elq4cCH5+fmRo6Mj9ezZkwIDAzWyNfYrkbEq/NmT0c9sX534n/wgFos19pJIJKSrq0tisZhEIpFG2/7xlVv7PWNj6XRyMpGWFioigYAMxb/2dPtrj06LvH79mipWrKix9sqUKUNERHfu3NGIMUBENGjQIFq6dCmtX7+eAgMDNdLm95iamtKOHTvIw8ODgoKCaN26dWqt2gUCAU2YMIGkUikNHz6cUlNTafTo0UrPGzZsGDk5OVGnTp3o1atXtGPHDipUqJAmb0cljIyMaNasWdS5c2fq3bs31axZkzp16kTTpk3LfpJMSSG6fz/Hdt8QkbJvPeO9mNwGxnHp6oSenrmdmQkAmVaF378aN25M9evXp8uXL9O6deto9OjRNHLkSPL19SV/f39ydXXV2sRXkBOqIptsDE2TnwlL2bUZE2FBTHaaaF8oFBa4V08bcBxHT548oYsXL9KhQ4fowoUL9MTXl6hly3Tvnzb6BKisGoutn8EfaQwA0LhnwNHRkXR1denOnTtUt25djbRZqlQpXr++c+fOWnHzli1blpYvX05t2rQhd3d3GjhwoFrXCwQCGjNmDInFYho5ciSlpaXRuHHjlD40WrVqRXZ2dtSsWTPy9PSk/fv3U/HixTV1K2rh5uZGZ86coRUrVlBYWBjt3r2bJk2aRD169CCRSJT55M+fs03/yyCJiJT5OmTfHc+NwR070jEDA7UnU3U5dOhQnmJFNDkh6ejokL6+/i834eV07H9hIvzTAEBPnjyhq1ev0unTp+nUqVP08OFDSvlOIlggEJDIwYHStLhy54iokqGh1trXBH+kMRAbG0tJSUkaNQZEIhGVLl1aI1oD3xMaGkre3t508OBBatiwoUbbzqB169Z05coVCg0NJTc3N6pVq5babYwYMYKkUimFhYWRXC6nSZMmKX14VqtWjS5cuEANGzYkDw8P2rVrF1WvXl0Tt6E2QqGQunXrRs2aNaNhw4ZR7969afny5bRw4UKqXLny/5+oggtWl9JTC38k+bvjuVGiWDFKKVFC65OdUCika9eu0datW+nYsWMklUqpWbNm1KVLF6pYsWK2q0IG41cGAD179oyuXLlCly9fplOnTtHt27ezpBXr6uqSjY0NJScn0+fPn0kqlVJNIyM6znGU9v3f+Y4dRAkJRBl1Qs6dI/rwIf3//f2JDAyI3r4lOvJfAvGDB+n/XbMm/b9WVkT16hERkZlYTJ5GP24i/mJoUsHod+HWrVsgIpw7d06j7Xbu3BlVq1bVaJscx6FKlSrw8fHRaLs/IpfL4evrC3Nzc7zIRiZXFWbOnAkiwqBBg3JUAPz06RO8vb2ho6OTrcxxQXP27FmUK1cOAoEAvXv3xpcvX9IPJCTkqihYhwillLx/ND12CLtVUSWMiirwe46Ojsa4ceNgZ2cHIoK7uzuWL19e4AqSDIY6cByHp0+fYsuWLRg6dCh8fHxgYGCQrgBIBIFAACKCUCiEo6MjvL294eHhAcP/6gxYW1sjKCgIu3fvRkJCAu7evQvH+fNBR478v3qglRXfXpbXhg3p58yalf055cvzCoQjnjz5aZ8VkyPOgf3794OI8PLlS422O3XqVBgYGGhcm3/Lli0gIly+fFmj7f7Ihw8fYG9vj0qVKuHbt295bmfevHkgIvTr1y9HgyA5OZkvNDRx4sRfoiCPXC7HrFmzYGBgAEtLS6xevTp9XA4OOU7kQ4ggIsLXH96f+N+D4aUKxkDE2LG4ePEi5HL5T7nvnTt3okGDBhAIBDAxMcGAAQPw77//FvhYGIzv4TgOz58/x9atWzF8+HDUqVMHxsbG/KQrFov5/zc0NESdOnUwYMAA9OjRA56enhAKhSAiVKpUCWPHjsWVK1f4Z/SDBw/Qvn17CAQCyEqUAB0+rLS2QF5fgqgoGJw6hZjk5J/2+ak6fwuA3H2gcXFxZGxsTF+/fiWjX93VoQJLliyh4OBgSk5OVj21TAUOHDhADRs2pGfPnpGDg4PG2lUoFOTs7Ezu7u60ceNGjbWrjGvXrlH16tWpdevWtGLFijzvk0ZGRlLPnj0pODiY5s+fn62bGQCNHTuWwsPDqVu3brRw4UKNfid5JTo6mgYPHkybNm2imjVr0k4DAzI5eDDb2IGLRORB6RoDQ/57L4WIXImoEBFdyKW/dzIZOQmF9O3bNzIwMKBq1apRzZo1qWbNmuTu7k5SqVRDd5Y7T58+pcWLF9Py5cvp48ePVKtWLerVqxc1a9asQMfB+PMAQK9evaIrV67Q1atX6erVq3T58mX6/PkzERHp6OgQAEpNTSWi9LRhb29vcnd3J7FYTNevX6d9+/bR06dPSVdXl+rWrUuNGzemRo0akY2NDd/Ps2fPKDw8nNasWUMGBgaUnJxMBgYGJOnYkd42barRrIIVJUtSFw0FlecFledvTVoWvwujR4+GjY2Nxtt9+fIliAh79+7VeNvz5s2DUCjE06dPNd72j6xevRpElO+KjsuWLYNAIED37t1z9ZasXLkSEokEvr6+/++e/wU4fPgwSpQogWYqFCYKIIKYCKFEWEyEav/9+2RuXgGRCBgxAikpKTh37hwmT54MPz8/3qWpq6uL2rVrY+zYsYiKisqX10YdkpOTsW7dOtSoUQNEBCsrK4wYMQLPnz8vkP4Z/9twHIeXL19ix44dGDFiBBo0aIBChQrxq3w9PT0YGxvzK3sDAwPUqVMHo0ePxsGDB/Ho0SOsXLkSLVu25H8rdnZ2CA4Oxr59+5T+Tl6+fIkePXpALBbDzMwMhQsXhkAgQIsWLWBrawszCwu4HT0KwfHj+fYKCKOi0ObOnZ/u8WTbBDnQtWtXVKlSRePtchwHIyMjTJ48WeNtJyQkwMzMDP3799d428ro378/xGIxTp8+na92Vq9eDaFQiM6dOyMtLS3Hc48fPw4TExOULl36l5pwkpOTETZ4MF4TQZHDpJ7033ZBYSLoEMGdCAdViRUQCJSWM5bL5bh8+TJmzJiBpk2bwtTUFEQEqVQKLy8vjBgxAocPH0ZCQoLWP4Pbt2+jT58+MDQ0hFAoROPGjbFv375cv1MGA0h/Nr5+/Ro7d+7EqFGj4OfnB0tLS37iNzAwQOHChflJnYhQvHhxdOrUCYsWLcLNmzchl8tx69YtTJo0CZ6enhAIBBAIBKhatSrGjx+P69evZzvxxsTEoG/fvpBKpTA1NUXFihVBRKhatSoGDRoEsVgMT09PvHz5EguWLwfNnAk6dixf2wP+t28jpYDLuSuDGQM5UK9ePfz1119aadvT0xMdOnTQStujRo2Cnp4ePn36pJX2vyc1NRXe3t6wsrLC6xzK7KrC+vXrIRKJ0K5du1z3w+/duwdHR0dYWVlpPUZCFb5+/YoRI0ZAV1cXwXp6uU/sar7SiPCqaVOVxqJQKHDjxg1ERESgRYsWMDc35/dMPTw8MHToUOzbt0+rv9P4+HhERkaiQoUKICI4ODhg0qRJePv2rdb6ZPx+REdHY/fu3Rg9ejQaNWoEq++C8YyMjODo6Ag7OztIJBIQEWQyGby9vTF06FDs2rUL7969AwAkJSXhwIED6NOnD+zt7UFE0NfXh7+/P5YvX57r3927d+8waNAgyGQymJiYoFGjRjA0NEShQoUQERGBZs2agYgwePBgpKam4vDhwxAIBNA3MUG/27ch+C8AUFUjQBwVBfGJE5j0/DnSfoEYKIAZAzlSunRpra2wg4KC4ObmppW23759Cx0dHUycOFEr7Svrz9bWFh4eHkjOZwDM5s2bIRaL0apVK6SmpuZ47rt371C1alXo6upi586d+eo3r6SkpGDu3LkwNzeHrq4uRowYga+xseD8/KBQYctAlRcnFOKdjg70iTBgwAC13f8cx+Hu3btYsGABWrdujcKFC4MoPYK6UqVKGDRoEHbu3KkV45HjOFy4cAFdunSBTCaDRCJB69atceLEiZ/uFmUULDExMdizZw/GjBmDxo0bw9ramp/4TUxMUKpUKZQpUyaTQVCkSBG0bt0aERERuHTpElJSUvj23rx5g2XLlqF58+bQ19cHEcHe3h59+/bFwYMHVXoWffz4EcOGDYO+vj4MDQ0RGBgIV1dXCAQC9OzZE0ePHoWjoyNMTEywa9cuAOneL7FYDIlEwgfOXvz6FaUPHuSzAgTZbAdkHGt08yZux8dr54POI8wYyAEjIyNMnTpVK21HRERAR0dHa+7THj16wMrKCklJSVpp/0cuXrwIqVSKHj165Lut7du3QyKR4K+//sr041fGt2/f0KJFCwgEAsyaNavAJhiO47BlyxYUL14cQqEQXbt2zewZefsWcHAAJxLl2yPAyWRQXLiAWbNmQUdHBy4uLvnyhnAch4cPH2LJkiXo0KEDihQpAqL0NKty5cqhX79+2LJlC7/q0hSfPn3CrFmzULJkSRARSpUqhYiIiF8q9oOhGd6+fYu9e/di3LhxaNKkCWxsbPgJPsP9Xq1aNbi6ukJPTw9EBIlEgqpVq2LgwIHYvHkzXr16lalNjuNw/fp1hIeHo0qVKvzfbLVq1TBp0iTcvn1b5d//ly9fMHr0aBgaGkJPTw/9+vVD27ZtQUSoXLkyLl68iHnz5kEqlcLd3R3Pnj0DkG6A6OvrQyAQ4Pjx45na9PLygkfLlhj77Bka3rwJizNnIDlxAtITJ1D47Fk0vXULE58/x/MCeiarCzMGsiEuLg5EhPXr12ul/WPHjoGI8ODBA620f//+fQgEAixdulQr7Stj6dKlICJERkbmu609e/ZAKpWiSZMmuVr4CoUCoaGhICL07dtX6yl3p0+fhoeHB4gIDRs2xK1bt5Sf+PIlUKxYeuBfXjwCIhHiiRBWrRpvNN69excVK1aESCTC2LFjc/WeqMqzZ8+wcuVKBAYGwsnJiX9wlypVCsHBwVi/fj2io6M10hfHcTh27BgCAgIgFouhq6uLrl27/hLbPQz1effuHfbv34/w8HA0a9YMtra2mSZ+T09P+Pn5oVatWpn+tqysrNC8eXNMnToVZ86cUerx+vbtG/bu3Yvg4GBe48LQ0BABAQFYtWoV3r9/r9ZY4+LiMGHCBJiYmEAmk2HgwIGYPHkyTExMYGpqikWLFuHz588ICAgAEaF///78giQhIYGPX1i5cmWmdu/cuQMiwubNm/P+Qf5kmDGQDf/++y+ICCdPntRK+2/fvgURYdu2bVppHwCaNWsGFxcXjesZ5ETPnj0hlUpx4cKFfLd14MAB6OjowM/PTyUPx6JFiyASidCoUSPEa8EF9++///J7hxUrVsSxY8dyvyg2FujWDSCCXA1vAIjAeXnhxIoVEIlE6NOnD7/qSU1NxejRoyESieDu7q6VHP9Xr15h3bp16NGjB7+SzwjW6tatG1atWqWR4M2YmBiMHz+e905UrlwZS5cuZWJGvyjv37/HgQMHMGHCBDRv3pz/3jImfh8fH7Ru3RqtWrWCt7c3n+cvFArh5uaG3r17Y82aNXjy5Em2q/jo6GhERkaiSZMm0NXVBRHByckJAwYMwNGjR3P1FiojISEBU6dORaFChSCVStGvXz/s3buXDxDs3r07Pnz4gBs3bqB48eIwMjLCli1b+Ovlcjn/OxgzZkyW9vv16wcrK6s8je1XgRkD2XDkyBEQEZ5oSRGK4ziYm5tj3LhxWmkfAM6cOQMiwu7du7XWx48kJyfD09MTtra2ePPmTb7bO3LkCHR1dVG3bl2VJogDBw7AwMAAFSpU0NhK9s2bNwgODoZIJIKDgwPWr1+vtoEV2aYNjohE4Cg900AuEGQyAOREUPz3XpyjIzoRYe9/31tkZCSICNOnT8/U5sWLF+Hs7AyZTIaIiAitGn1v377F5s2b0adPH7i6uvITgL29PTp16oRly5bh0aNHed6mSUtLw+7du9GwYUMIBAIYGxujf//+uHv3robvhKEqHz58wMGDBzFx4kT4+/ujaNGimfb4a9eujaCgIPTp0wft2rXjVTkzDIOGDRti/PjxOHbsGOLi4rLtR6FQ4PLlyxgzZgwqVarEGw9eXl6YOnUq7t27l+e/q6SkJMyaNQtWVlYQi8Xo2bMnbty4ge7du4OIUKFCBZw/fx4cxyEyMhI6Ojpwc3PDo0eP+DY4jkPNmjVBROjYsWOWPhISEmBsbIy///47T2P8VWDGQDasWLECRKTVPXcfHx+0atVKa+1zHAcPDw94e3trrQ9lREdHo3DhwvDy8tKIGzsqKgp6enqoVauWSulxN27cgK2tLezs7HDjxo089xsfH4+xY8dCX18fpqammDFjRp4CJL98+QJDQ0OEhYWhnIEBhhoaAl27IqFUKTyVyXCdCNulUqwpVw64dAmcQgFvb2+4ubnxE/zw4cNBRJlWKwCQmJiI/v37g4hQu3btfElEq8OHDx+wY8cOhISEoEKFCvwkYGNjg7Zt22LRokV5fog/ffoUw4YNg4WFBYgINWvWxIYNGwpu1RUfD5w7B+zfDxw4AFy/DvzGKz5V+PTpEw4fPoxJkyahRYsWfEQ+EcHY2Bi1atVCSEgIxowZg9DQUDRt2pT/fjK2k7p164Zly5bh3r17uRqmCQkJ2LVrF7p3784HEhobG6NNmzZYu3ZtvoNZk5OTMX/+fNjY2EAkEiEwMBCPHj3C4sWLYWZmBhMTE8yfPx9paWmIj49H+/btQUQIDg7O8szv2LEjiAje3t5K/54zdFIy4gp+V5gxkA3jx4+HhYWFVvvo27cvSpcurdU+tm3bBiLCxYsXtdrPj5w5cwZisRj9+vXTSHunTp2CgYEBvLy8clxlZPD69WtUqFABBgYGOHDggFp9yeVyLFq0CFZWVtDR0UFoaCg+f/6c16Fj0qRJ0NHR4T0136eUpqWlYeHChZBKpRCJRFiyZAkUCgVOnTqVaQ9SoVCgbdu20NHRwdmzZ7P0ceTIEdjZ2cHIyOj/pZELkC9fvmDv3r0IDQ1FlSpVIBKJQESwtLREy5YtMXfuXNy8eVMt70VycjI2bNgAb29vvq3hw4dr56EbEwOEhwMlSqTrOfy4fSMWA+7uQGRkeg2K35jPnz/jyJEjmDx5Mlq2bAlHR0d+UjcyMoKPjw+GDBmCefPmYdasWejXrx/c3d15OV8DAwP4+vpi1KhR2L9/v8q/jZcvX2LBggVo2LAhZDIZiAjOzs4YPHgwoqKiNLJwSE1NxZIlS1C0aFEIBAK0b98eDx8+xOXLl+Hu7g4iQpcuXfjg2Nu3b8PFxQX6+vpK48NGjRoFIkKJEiWyHZ+7uzsaNmyY77H/bJgxkA09e/ZEhQoVtNrHwoULIRaL852OlxNpaWkoXrw4AgICtNZHdsyfPx9EhFWrVmmkvXPnzsHIyAjVqlVT6W8sPj4ejRo1gkgkwqJFi3I9n+M47Nq1Cy4uLrxLML/74t++fYOlpSV69uyJvn37ZmuYTZkyhV9de3h44Pr166hfvz5cXFz44MHk5GR4eXmhUKFCmdyYGXz58oVfxfz1119qB1dpkri4OBw8eBB///03qlevzueJm5mZoVmzZpg5cyauXr2qcjbN3bt30a9fPxgZGUEgEKBhw4bYs2dP/rNxEhOBkJD0IM/cUkEzjAQDA2D+fOAXEIrJjS9fvuDo0aOYMmUKWrVqlSmAz8DAADVr1sSgQYOwevVqbN68GTNnzkRAQECmIMBixYqhQ4cOWLBgAW7cuKHyZ65QKHDhwgWMHDkS5cuXBxFBJBKhVq1amDFjhkaDp9PS0rBq1SoUK1YMRISAgADcvXsXnz59QnBwMJ8pc+bMGf6alStXQldXF66urkrjbpYtWwYigrm5ebbPmytXrhT4Vqy2YMZANjRq1AhNmjTRah+nT58GEWUfja4hFixYAKFQqLX4h+zgOA6BgYGQyWS4cuWKRtq8ePEiTExMUKVKFZVS0uRyOfr06QMiQmhoaLYr0wsXLsDLywtEBF9fX1y7dk0j450/fz6EQiEeP34MBwcHyGQypav28+fPg4iwZMkSlClTBkKhEG3atMliTH369AklS5ZE8eLF8eHDB6V9bt26FYUKFYKlpSWfG/2zSUxMxLFjxzB69Gj4+PhAR0eHX4k2bNgQU6ZMwYULF3JdHSYkJGDp0qX83nLRokUxYcKEvMWnXL0K2NvnbgRk96pZE9Bw+mV+iI2NxfHjxzF16lS0bt2anxgzJn4vLy8MHDgQa9euxZkzZ7Bt2zaEhoaiRo0a/EpdR0cHNWrUQFhYGHbs2KG2SFRcXBy2bduGwMBAPvLe1NQU7du3x8aNGzWeRqpQKLBhwwY+uK9Zs2a4ceMGFAoFli5dikKFCsHIyAgRERF8llFiYiICAwNBROjatavSWKQMUSE9Pb0sKY7fExQUhCJFivxPKGwyYyAbypcvj169emm1j8+fP4OIsGHDBq32k5iYCHNzc/Tt21er/SgjKSkJlStXRtGiRTW2Ur169SrMzMxQqVIllfYWOY7DrFmzeG3x71OYHj16xKcRlS1bFgcPHtSYi10ul8PBwQFt2rRBYmIinxOtjISEBAgEAixfvhypqamYOnUq9PT0IJPJYGFhkWm//MmTJ7CwsEC1atWyFSB68+YNGjduzD/wfrXfZHJyMk6dOoXx48ejbt26fK65vr4+6tatiwkTJuD06dM5es0uXbqErl27QldXF2KxGAEBATh+/Lhq39+5c4CeXp7TPvmtAyen9C2GAubr16+IiorC9OnT0aZNG5QoUYKf+PX19VGjRg2EhIRgzZo1uHXrFq5cuYL58+ejQ4cOmbwDtra2aNWqFWbNmoWLFy/mKS7j2bNnmDt3LurVqwepVMrHEISFheHUqVNaSfXlOA7btm3jg1n9/Pz41NSrV6/yqb8dO3bMZCj++++/cHV1ha6ubpb0wAxu3rwJsVgMkUiE69evZzuG2NhY6OvrIzw8XKP39rNgxkA2mJubF4iCn62tLUaMGKH1fsaMGQM9PT18/PhR6339yMuXL2FhYYHatWtr7MFw48YNmJubo3z58tmukH9kx44d0NXVRdWqVXHv3j30798fEokEdnZ2WLlypcat+zVr1oCIcOPGDSxcuBBEhMWLF2d7vrOzcybFy5cvX8LX1xdEBBcXF9y/f58/dvHiRejq6iIgICBbbwfHcVi6dCkMDAzg4OCAEydOaO7mNExqairOnz+fpfiSTCaDj48PxowZg+PHjys1fj5//ozZs2fz2zslS5bErFmzst/LfvYMMDTMu0fgR4PA1RXQ4lZfXFwcTp48iRkzZqBdu3ZwdnbmJ3M9PT1Ur14d/fv3x+rVq3H37l18+PAB+/fvx8iRI1G7dm0YGBiAKF2O2t3dHQMGDMCmTZvyXJo9LS0NZ8+exbBhw/jJWCKRoE6dOpg9ezYeP36s4U/g/+E4Dnv27OFlruvUqYNz584BSP876NOnD4RCIcqUKZMlLXzdunXQ19eHi4sLbt++rbT9169f86JCuRWSmz9/PkQikcayln42zBhQQlJSkkb3unOiXr16aNasmdb7ef/+PWQyGcaPH6/1vpQRFRUFkUiEwYMHa6zN27dvw9LSEmXLllVZLe/06dMwMDCAQCCAgYEBJk+erJXqfgqFAmXKlIGfnx8A8AVTcuorIzf7R2rWrAmRSASJRIIRI0bwbs2dO3dCIBAgNDQ0x7E8efIEXl5eEAgEGDx4cIGpUuYHuVyOK1euZCm+JJFIUL16dfz99984dOhQJj0JjuMQFRWF1q1bQyKRQFdXF4GBgbh48eL/ewsUinT3vlisdHKP+m+SVfY6n1MsgYYM+vj4eJw6dQozZ85E+/btUbJkST6WRFdXF56enujXrx9WrlyJO3fuIDU1Fffu3cPSpUvRrVs3lCpVih+vhYUFmjVrhsmTJ+PUqVP5+juPjY3F5s2b0alTJ77Whbm5OTp37owtW7Zo/ZnPcRwOHTqEqlWrgojg5eXFG7cKhQIrV66EhYUFDAwMMHPmzEzbTUlJSejZsycygnez0yCJjY3ltzZyq8TKcRzKli0Lf39/zd3kT4YZA0p4/PgxiAhHjx7Vel+DBg1CsWLFtN4PAAQHB8PS0vKnTQazZs3S+LbIvXv3ULhwYZQqVQoxObhr09LSsHz5ctja2kIikcDMzAxGRkZZJEU1xe7du0FEOHXqFDiOg46ODhwcHHK8ZtKkSTA2Ns7i5n748CGEQiHvhnVwcMCePXsApMtaExEWLFiQY9tpaWmYNm0apFIpSpcurbGYiIJCoVDg5s2bmDNnDlq0aMGntYlEIlStWhWhoaHYu3cvYmNjAaTrIkycOJFPkatYsSIiIyORHBmZ40o/wxjoT4Q1P7w+5OQhEAqBe/fUuqf4+HicPn0as2fPRocOHVCqVCl+4pfJZPDw8EDfvn2xYsUK3L59G3K5HHFxcTh69CjGjx8PPz8/mJiYICMvv3z58ggODsbq1avx+PHjfG93PX78GLNmzYKvry+fSVC2bFkMHz4c586dK7B98qioKL48toeHB44cOcLf240bN1C9enUQEdq2bZtllf7o0SO4ublBR0cHS5YsyfYzSUlJ4eMOBg0alOuYzp07ByLCoUOH8n+DvwjMGFDCiRMnQESZ3LLaYvny5RAIBAWiuPbw4UMIBAKNyAXnBY7j0K5dO+jq6uLmzZsaa/fBgwewtbWFs7NzlsqJHMfhwIEDKFu2LIgIrVu3xuPHjxEbG4s6depAIpFku3eYVziOg6enJ6pXrw4AOHv2LIgo1zTL/fv3g4iUps5169YNFhYWuH79OurVq8cHSz1//hwDBgyAUCjkDYScuH37Ntzc3CAWizFhwgStSzdrC47jcO/ePSxcuBBt2rThc9WFQiEqVqyIkJAQ7NixA+/evcPevXvRqFEjCIhwWyjMsbx0hjGwJS/bBX36ZDvexMREnD17FhEREejUqRNKly4NoVCIjKC9qlWronfv3li+fDlfhpfjODx58gRr1qxBr1694Obmxl9jbGyMBg0aIDw8HEeOHNHIM1cul+PkyZMIDQ3lt1ykUinq16+PefPmFXge/ZkzZ1C7dm0QESpVqoR9+/bxk3lsbCz/d1+qVCmlRv2WLVtgaGiI4sWL57j3r1AoeFEhf39/lYyoTp06wcnJqUDVXbUNMwaUsHbtWhCRViRtf+TSpUsgIo1F2+eGv78/SpYs+dP+iBMTE1G+fHk4OTlptEre48ePUbRoURQrVowX3rl69Sq/5+7t7Z0lpS81NRVdu3YFEWH06NEaCxw8efIkiIifnNu1awei3OtQxMTEgIiwY8eOLMeeP38OiUSCSZMmgeM4bN68GTY2NtDV1cXEiRPRrFkz6OnpqfR3lJKSghEjRkAoFKJq1apaq49RkHAch0ePHmHp0qXo2LFjJrW8smXLok+fPtgSFpbrpP69MRBHqktIgyg9IDExEYmJiTh37hzmzJmDzp0789khGRO/u7s7evXqhaVLl+LGjRu8S/vbt284ffo0pkyZgubNm/Mu64yYkcDAQCxZsgR3797V2O/38+fP2LBhA9q1a8dvxVhZWaFr167Yvn17gTwDf+TSpUuoX78+/93t2LGD/21yHIc1a9bAysoK+vr6mDp1apagx+TkZPTr1w9EhFatWuU6H3Xo0AFEBHd3d5W8HZ8+fYKOjg6mTJmS95v8BWHGgBIyClcUBPHx8SDKWvhCW2S4t35WyV8gXWHOzMwM9evX16ir8dmzZ3BwcICdnR1fQ6BUqVLYvXt3thM9x3GYNGkSiAjt27fXiOZDgwYN4OrqyvdpaWkJQ0PDXK/jOA6WlpZKtc+BdJEqExMTPj0rLi4OgwYNgkgkQsmSJeHi4oLChQurrI1w7tw5FC9eHLq6upg3b97/XEnhZ8+eYdWqVejatSuKFSuGMBUm9wxjwOC//4qI4EOEyyoaBJ2dnHjBJalUisqVKyM4OBhLlizB9evXM+1lv3r1Cps3b0ZISAiqVKnCazHo6+ujVq1aGDFiBPbt26fx0tIPHjzA9OnT4ePjw4/Vzc0NI0eOxMWLF3/aQuH69eto0qQJ/7vdtGlTprHcvn2bF6Bq1aqV0pS/p0+fwt3dHVKpFPPnz8/1b3rEiBEgSpfVVtU7O3PmTEgkkp+q46ENmDGghL59+8LV1bXA+nN0dMSQIUMKrL9q1aqhRo0aBdafMg4fPgyhUKhRPe/Pnz+jR48evLtYHTf4hg0boKOjA29v73w9fK9fvw4iwtq1awGkV3QjIjRo0ECl63MKKI2JiYGuri5GjRqV6f2bN2+iWrVq/ETi7Oyscj53QkICevfuDSJC3bp1c8yp/t1JbNwYilwyCM4SoQURlhFhFxH+IUIhIsiIcC0XQ0BBhE2enoiMjMTVq1czrVhTU1Nx6dIlzJ49G61bt85U4MfR0RHt27fHvHnzcO3aNY1v3aSmpuL48eMYNGgQn4Iok8nQqFEjLFy4MM9ZBZrizp07aNGiBYjSC2GtWbMm0yLhe6PX2dkZhw8fVtrOzp07YWJiAkdHR5UqYC5ZsgRE6XUWVJ3YOY6Ds7Mz2rZtq9rN/UYwY0AJzZs3V/nhrQmaNGnCR50XBDt27AAR4fz58wXWpzImT54MovxXbkxKSsL06dNhamoKAwMDDBkyBCVKlICtrS0ePnyocjtnzpxBoUKF4OzsnOf0qNatW8PR0ZF/oI8bN06tewwLC4O9vX22x0NDQ2FgYJDl4aVQKLB8+XI+oKxEiRJqRY8fPHgQNjY2MDExwbp16/7nvAQA0lMA1XH7//d6RARdItTP7VyJBOjdG0C6Ebhz504MHToUXl5emUR9qlWrhiFDhmD79u0aKealjI8fP2LNmjVo3bo1XznQ2toaQUFB2L17t0o1PrTN/fv30bZtWwgEAjg4OGDZsmWZDCGO47BhwwZ+O2zSpElKPXepqakYPHgwiAjNmzdXyRA+cOAABAIBdHR01NomO378OIi0V832Z8KMASVUrlwZ3bt3L7D+hg8fjiJFihRYf2lpaShRogRatGhRYH0qg+M4tGzZEgYGBnmqTqdQKLB27VrY29tDJBKhd+/evGJaTEwMSpUqBWtra7VK/D569AglSpSAubm50hoAuV0rFAozRfZn7Berqru+bt06EFG2OfIfPnyAoaFhtimaHz9+5F2tZmZmapWS/vz5Mx/fEBAQ8FM0KbRKiRJ5MgZAhDZEkNL/l5dW9koTChFVvDiKFy/Or/ptbGzQsmVLzJgxA+fPn9ea9DjHcbh79y6mTJmCGjVq8DEKlSpVwtixY3HlypVfJtjtyZMn6Ny5M4RCIezs7LBo0aIs+/53795FrVq1QJQurZ1dAa6XL1/C09MTYrEYs2bNUsmIvXbtGsRiMYRCIU6fPq3W2Fu1aoVSpUr9TxrLzBhQgrW1NcaOHVtg/WUELGakRRUEixYtgkAgUKpxX5DEx8ejTJkyKFGihFr3f/ToUV54xN/fX2nmx9u3b+Hq6gpLS0vcuXNH5bY/fvwILy8v6OjoYNOmTSpf16NHD1haWvIr8pSUFIhEIrWKUd29exdEhKioqGzPGT16NGQyWY5iJ+Hh4fyE1LNnT7W2PjZt2gQzMzMULlw4V+GV34ry5fNsDIT+91l+zeGcFCJssLREv379sGHDBjx//lyrk0ZKSgqOHDmC/v3786qCurq6aNq0KSIjI385MZwXL14gKCgIYrEYhQsXxpw5c7KkOcfHxyMsLAxisRjFixfPscjY/v37UahQIRQpUkRlL+ezZ8+gr68PIsK6devUGv/bt28hFosRERGh1nW/C8wY+IHU1FQIBAIsXbq0wPq8ceMGiEjtlWh++PbtGywsLND7P7fmz+Thw4cwNjZGkyZNcl293Lp1Cw0aNAARwdPTM1PhEWV8+PAB5cuXh7m5uVrljJOTk/lV8j///JPrQz0mJgZSqRT//PMP/15GxcjsAgKVIZfLIZPJMGvWrGzPiY2Nhampaa7f3YQJE/gJwtzcHCtWrFB5coqOjoafnx+ICD169PgpUeWaJDo6Gs9q1ECasoqEKrxaUHrcQE5piZxAAOSi95Bf3r9/j5UrV6Jly5a8SqOdnR2Cg4Oxb98+rQho5Zfo6Gj06dMHUqkU5ubmmD59epZgPY7jsGXLFtjZ2fHiaNnpocjlcr6kd6NGjVT2YH38+JHP0JgwYYLa9zFp0iTIZLJ8VTD9lWHGwA+8ePECRKR22dv8kJSUBJFIVOD5/+PGjYOurq7Kcr7aZO/evRAIBNl6ZF69eoXAwEAIBAKUKFEC27ZtU3li+/TpEypWrAgzMzNcvXpV5TFxHMeXMO3evXuOrv7Q0FAYGRll8m5kGC05iSEpw93dHV26dMnxnH/++QcSiSTH3G+O4xAUFASRSMTna9eoUUPlwlgcx2Hx4sXQ19eHk5OT2i7VnwXHcXjw4AGWLl2Kzp0786vmAblM5iDCeyXv3SCChAhNVTEcNFwqnOM43Lp1C5MmTeJVLAUCAapWrYrx48fj+vXrv6zL+u3btxg4cCBkMhlMTU0xadIkpUbl/fv3UbduXRARmjZtiqdPn2bbZnR0NLy9vSESiTBlyhSVtz4SExN5UaGuXbuqfS8KhQIODg65/i5/Z5gx8AMZAjHZaVdrCxcXl0y69AXBhw8foKuri3HjxhVov9mR4dr+XjwnNjYWw4cP5wv2zJs3L091z798+YIqVarAxMQEly5dUuvaFStWQCwWo27dukq3Mj5//gwDAwMMGzYs0/uGhoYwNzdXe6xBQUFwc3PL8ZyEhARYWloiMDAwx/PkcjkaNGgAIyMjLFmyBCVLloRIJMKQIUNUXu0/fvwY1atXh0AgQFhYmFZLbueFtLQ0XL16FbNnz0aLFi1gZWUFIoJAIICbmxv69euHTZs24cjcublO5rWI0JAIE4gQSYQQIugRwZgI93IzBMzMgDwU+vmRpKQkHDhwAH369OEVFPX19eHv74/ly5erXUmwoPn48SPCwsKgp6cHIyMjjB07VunvJiEhAcOHD4dEIoGjo2OuollHjhyBhYUFbGxs1DJM5XI5r2Do6+ubp9iJAwcOgIjUisH53WDGwA9s2rQJRKTxUpu50aJFC9SuXbtA+wSA3r17w8LC4pdwLyoUCjRt2hRGRka4ffs25syZA3Nzc+jq6mLkyJH5/ruKjY2Fp6cnjIyM+OImqnLs2DEYGxvD1dU1SzDThAkToKOjkykyPGPvv3Xr1mqPc/78+ZBIJLlWkJs9ezaEQmGu0dBxcXFwc3NDkSJF8PTpU0yaNAm6urqwtbXF1q1bVVpZpqWlYfLkyZBIJChbtqxaWy6aJikpCSdPnsSECRNQv3593l0ulUpRo0YNDB8+HPv370dsbCzi4uKwYMECXoHynFSaYxBgBBGqEMGMCGIiWBOhA6VnFORoCIhE+apP8ObNGyxbtgzNmzfn97Tt7e3Rt29fHDx48JczwJTx5csXjBw5EgYGBtDX18fff/+tNFaF4zhs374dRYsWhY6ODsaMGZPj8yctLQ1jxoyBQCBAvXr1VK5DktFX69atQUQoU6ZMnqXYmzVrBjc3t1/WC6MJmDHwAzNmzIC+vn6Bf+ljxoyBpaVlgfYJpK/6BAIBFi5cWOB9K+PLly+wsbGBRCKBUChE9+7ds0gM54e4uDh4eXnBwMBAbbf33bt34eDggMKFC/NKfxnloX8sd923b18QUZ4qBWZ4p3KbcJOSkmBnZ4c2bdrk2mZ0dDTs7Ozg5uaGuLg4PHv2jM86aNCggcqBpDdu3EDZsmUhkUjwzz//FIg+fWxsLPbt24dhw4ahevXqfJlcQ0NDNGjQABMnTsSpU6cyPehv3bqFTp068Sl9pqamkEgkaJCHeAGVXjIZoIZGA8dxuH79OsLDw1GlShXek1GtWjVMmjQJt2/f/m0mnq9fvyI8PBzGxsbQ1dXFkCFDss3bf/ToER+L0rBhw1xTeN++fQtfX18IhUKMHz9e7VV9WFgYiAiFCxfOs37Iq1evIBQKsWjRojxd/7vAjIEfGDhwIEqWLFng/W7ZsgVE9FNUrVq0aIESJUoUWOGR7Dh16hT/YBSJRKhTp45WHogJCQmoVasW9PX1c4zaV8bbt29RpUoV6OnpYefOnZg7dy5EIlGWfU57e3tIJJI8faZxcXEQCAQqqVIuXrwYRKRSrYdbt27B0NAQfn5+fD73rl27YG9vDx0dHYwdO1allVNycjKGDh3KT16aLlkbExODTZs2oW/fvihfvjxfvMfKygotW7ZEREQErl27lumzTU1Nxblz5zJV1ct42dnZoW3btpg7dy6uXLmCxObNc/QO5Ok1f36u9/Xt2zfs3bsXwcHBsLOz4w2ali1bYtWqVb+dol1CQgImT54MMzMz6OjooH///tnqJiQmJmLUqFGQSqWwt7fHzp07c/1tnzhxAoULF4aVlRWOHTum9vjmz5/Pb7Hkp67CmDFjYGBggLi4uDy38TvAjIEfCAgIgK+vb4H3+++//yK3lDJtceHCBRARtm/fXuB9A+mVB5s2bYqMvOjjx49j+/btyIjk1waJiYmoW7cudHV1ceTIEbWv/euvv/gVZ7t27TId//LlC4gIVapUyfP4ihcvjpCQkFzPS01NRbFixVQug3348GGIxWL07NmTfxgnJibi77//hkQiQbFixVQOnj19+jScnJygr6+PRYsW5clwy6gpsHz5cgQGBqJYsWL8JF6sWDF06dIFy5Ytw8OHDzO1/+HDB+zevRvDhw9H1apV+ap6RAQjIyM0btwYGzduzJJed+3aNZS1scFroRAKkSj/RoBIBNSrl14aWQnR0dGIjIxEkyZNoKurCyKCk5MTBgwYgCNHjuS6FfQr8u3bN8ycOROWlpaQSCTo1atXjsqVu3fvhoODA6RSKUaOHJmr7K9CocDEiRMhFArh4+OTJ2GmjPLeYrFYJTXC7JDL5bCxsUFwcHCe2/hdYMbAD1SrVg2dO3cu8H7lcjmkUinmzp1b4H0DQI0aNVCtWrUC7fPNmzfo0aMHhEIhHB0dsWHDhkxuwBEjRkAgEODgwYNa6T8pKQl+fn6QyWRq96FQKHh3Z9u2bTOtUhcuXAgiytd32bJlS/j4+Kh07po1a0BEWQoxZcfy5ctBRJg8eXKm9//9918+66Bly5YqSRPHx8fzteIbNGiQa257Wloarl+/jjlz5iAgIACFCxfmXeTly5dH3759sWnTpkztpKWl4datW1i8eDE6d+7MS+pmKPplyOsGBATkuLWyadMm6OrqolKlSog5fRqwtk6vNphXQ0AoBGrUAL5T81MoFLh8+TLGjBmDSpUqIUMa28vLC1OnTsW9e/d+G/f/jyQnJ2PevHmwtraGSCRCt27dclxxP3nyBI0bNwYRoV69eiop/X348AENGjSAQCDAqFGj8uRZO3fuHG8c7t69W+3rvydDrfVnxsgUFMwY+IGiRYtiRD4CgfJDuXLl0LNnz5/S965du1BQWgfx8fEYM2YM9PX1YWpqipkzZyoNkEpLS4Ofnx9MTU3x5MkTrYwlOTkZTZo0gVQqVakEcAYKhQKlS5eGq6srhEIhmjRpwkfne3h4gIjyVeNgwoQJMDExUTm4r3Tp0qhXr57K7WekTG7YsCHT+xzHYf369ShcuDD09fUxffp0lbI39u3bh8KFC8PU1BQbN27k309OTsbp06cxadIk+Pn5wcjICBnBftWrV8ewYcOwb9++TAG7sbGxOHToEMaMGYN69erx14hEIpQrVw4eHh78VkDFihWxdOnSHOV1FQoFRo4cCSJCu3bt/j9Y7dUrwNtbbSMgY4shrXt34Ns3JCQkYNeuXejevTtfStnY2Bht2rTB2rVrNV5oqKBJTU3F4sWLUaRIEQiFQnTs2DHHGJOkpCSMGzcOMpkMRYoUUTkN+MyZM7Czs4O5uTkOHTqUp7E+ePAAenp6ICLMmzcvT218T/369eHh4ZHvdn4HmDHwHWlpaRCLxT8tmK5du3Y/rYCQQqFAyZIl4e/vr7U+UlNTsXDhQlhZWUFHRwdhYWG5Cnh8/vwZxYoVQ7ly5bSmp56SkgJ/f39IJBKl5YOVsXPnThARzpw5g/3798PAwAAVK1bEy5cvIZVKYWdnl68x7d27F0SUrQzrj2zduhXqaKZzHIeOHTtCKpUqDaSMjY1F//79IRQK4erqqlKw5cePH9G8eXMQEUqXLg1PT09+5W5gYID69etjwoQJOHnyJD8hZ2gCrFy5Ej169ICrqysfI2BmZoZGjRphwoQJiIiIQEBAACQSCWQyGbp06aJSimhcXByaNWsGgUCAyZMnZ52UFIp0oaBChQDKuaphhmBRkr096gmFaNq0KRo2bMgHKTo7O2PQoEGIiorKU/rrr4ZcLseKFSvg6OiIjMyYe/fu5XjNvn374OTkBIlEgmHDhqn0m+U4DtOmTYNIJEKNGjXyHDD85s0bWFhYgIgwaNCgPLXxPU+ePAFRwVWU/dkwY+A7MurJ59e1lFcmTZqk8mpQG0RGRkIgEGi8vj3HcdixYwdKliwJgUCATp06qTzJAemBb3p6emjbtq3WPpvU1FQEBARALBZjy5YtOZ7LcRyqVq0KLy8v/r0bN27A1taWfxjld4/x9evXICLs2rVLpfMVCgUqVKgALy8vlT+jlJQU+Pj4wMzMTKmcM5C+x161alUQEbp06ZIlyO3t27fYsmUL+vfvjwoVKvCa+AKBADKZDD179sSVK1f4gMXExEScOHECkyZNQuPGjVGoUCH+/DJlyiAoKAgrVqzA/fv3ERsbi/nz58PV1RVE6cWXZs6cqfJK+8mTJyhTpgwMDQ1z9/qkpKCPhQW2E0FhaZnZEBAIwBUrhkdeXvAigqODA79NUbVqVcyYMUPjv5mfSVpaGtatWwdnZ2cQpct95yZU9ezZM94Q9PX1VbkeyKdPn/islrCwsDwbUV+/fuW3j/766y+NPCeGDh0KExOTXyLtuiBgxsB3XLp0CUSEa9eu/ZT+M1z1mkylU4ekpCRYWlpqNFjm/PnzvOBH3bp1cf369Ty1k6H/MGPGDI2N7UfkcjnatWsHkUiUxX3+PVFRUSAi7Nu3L9P7r1694qsG5tdFyXEczM3N1RKE2rdvH4hIrfiHz58/o1SpUnBycso2f1uhUCAyMhKmpqYwNjZGp06dEBgYmGnv3tHREZ06dcKSJUtw//59vHr1ileVq1OnDoKDg1GpUiWIRCJkRNHXrVsXo0ePxsGDBzNtE9y4cQM9e/aEgYEBRCIR/P39ceTIEbXSyo4fPw4zMzMUL15cpSJYGcqjfCbRx4+Iv3EDBxYsQHCHDryMrUgkgp2dHVasWAEbGxs0adJE5TH96igUCmzZsgWlS5cGUbrUb26KncnJyZgwYQJ0dXVhY2ODTZs2qTwRX7x4Efb29jA1NVVri+5HUlJS4OnpyRtnmgjKTElJgYWFBQYMGJDvtn4XmDHwHRkR7D8rxSfDLaWtgDlVGD9+PGQymVrCHsp4+PAhWrZsCSJCuXLl8rwH+D2hoaEQiUR5SjNSlbS0NHTq1AlCoRBr1qxRek69evVQrlw5pQ+9QoUKQSgUQigUYvHixfkaS506ddTatuE4Dp6enqhcubJaK6Nnz57BysoKVatWzRTprVAocOPGDcydOxetWrXiJ0Si9HoHAQEB2LBhA2+8Jicn49y5c5gxYwZatGgBGxsb/nyJRAI/Pz8sWrQIN2/ezBIYlpSUhDVr1qBatWogSi+3O2bMGLUNY47jMH/+fD41VVUvwtSpU0FECAkJwdy5c1GvXj1ez6BUqVIICwvDqVOnMGfOHIhEIsTExPBbMzt37lRrjL8aHMdh165dKF++PG+0q1L459ChQyhRogTEYjGGDBmicuodx3GIiIiARCJB1apV8fz58zyPXaFQ8Jk9jo6OGiv2tnHjRhBRrtsi/0swY+A75syZA6lU+tPc9AqFAnp6elpd/ebGx48foaenp1Zxne95//49+vbtC7FYjCJFimDVqlUa0y+Qy+Xw9fWFubm5WtsM6pKWloZu3bpBIBBg+fLlmY5dvXoVRIT169dnue758+cgItSqVQu9e/fmXZ95LR07ZMgQODo6qnVNRr11VWMfMrh8+TJ0dXXh7e2NiRMnomHDhjA2NuYnck9PT4SFhWHPnj3Yv38/ypYtC4FAAD8/P/Tt2xfVqlXjJ0+ZTAZvb28MGzYMu3btwoULF+Dh4QGhUIi///4708rtyZMnCAsL4wMCfX19sXXr1jy5i1NSUtCjRw8QEQYMGMBvTeREWloazp49m0mbQCKRoE6dOpg9e3YWDYUvX75AJpPx8Qd+fn4oWrSo1uJZtAnHcThw4ADc3d1BRKhZsyZOnTqV63UvX75EixYtQETw8fFRqyJobGwsf21ISEi+V/EDBgxARoqvKtkvquLj44OaNWtqrL3fAWYMfEdYWBicnJx+6hjc3d1z1ZvXNn379kWhQoVyzQf+nsTEREycOBGGhoYwNjbGlClTtLLX9uHDB9jb26NSpUpa3ctTKBQIDg4GEWVa4QcEBMDJyUnpRDNu3DgQEdauXQuO4zBjxgwIBAK0bNkyT2PNSBlUVxrb19cXrq6uuRphcXFxOHjwIEaMGAFvb29+MpdIJKhbty7Cw8MRFRWFxMREyOVyXL16FfPmzUO7du3g8N2+uVAohIeHB2bPno3Lly8rncjlcjkmTpwIsVgMNzc3zJ07F35+fhAIBDAxMUFISEi2cQuq8P79e3h5eUEikWDZsmU5nhsbG4vNmzdnESiSSqXYvHlzrs+v9u3bw9nZGRzH4cmTJ5DJZBg6dGiex/4zOHbsGO+F8fT0xNGjR3NdBKWkpGDy5MnQ09ODtbU11q9fr9bC6erVq3BycoKxsbFGNE1mzJiBjPRSVUS3VCVD8yWnrcL/RZgx8B3t2rWDt7f3Tx1Dly5d4O7u/lPH8OTJEwiFQsxXQVUtLS0Ny5Yt4yWEQ0JCVC4pmleuXbsGmUyGzp07a9WLw3EcLys8b948PHz4MEfp5tKlS0MgEGRyl27fvh26urrw8PBQe/vp9u3bamUIZHD+/Hml3ot3795h69atGDBgACpWrMgH+5mbm8Pf3x8zZ85EaGgoiNLFnvbu3Yu///4btWrV4tO1Mly7AwcOxObNm3Hp0iUEBATwq8Sc3Kpv375Fnz59IJFIQJSuDLh06VK1jE5l3LhxA/b29rC0tMy2pPXjx48xa9Ys+Pr68jnoZcuWxfDhwzFo0CAQEbp166ZSfxnel4xVdHh4OMRisUqxCT+b06dPw8fHB0SEypUr48CBAyr9ho4ePcoXuRo4cKBaz3iO47Bw4UJIpVJUqlRJI2nCGW58gUCAo0eP5ru97wkJCYGFhcVvUQ9CkzBj4Dtq1qyZRU2uoJk+fTr09PTy7FrWFAEBAShWrFi2q0uO47Bv3z4+0rtNmzZa0wJQxurVqzWWS5wTHMdh4MCBICJ4eHigcOHCSiV7ExISIBQKUaJEiSzHLl68CEtLSzg5OakcZQ2kZzjo6OggIiJC7XE3atQIDg4OWL58Obp3786XbyUiODg4oGPHjoiMjMS///6LtLQ03LlzB5GRkQgMDISpqSl/rpWVFfz9/TF16lScOXMmW7nigwcPonjx4hCLxZlSyjiOw8mTJ9G6dWs+LbBTp07o0KEDBAIBvLy8cixZmxtbtmyBnp4en9aZgVwux8mTJxEaGgoXFxd+5V+/fn3Mmzcvk1hOhgS2qsqLCoUCTk5OvDhZcnIySpQogZo1a/6ygkIXLlxAvXr1QEQoX748du3apdJYX79+zRf68fLyUrn8dQZxcXFo06YNiAh9+vTRyAR7/PhxPhB1xYoV+W7ve759+wYTE5PfztOjCZgx8B3FihVDWFjYTx3DwYMHQUT5ekBqgozMiq1bt2Y5duXKFdSqVYvfZ1S3JLCm6N+/P8RisUr7nPmB4zg+BqBhw4ZKz9m8eTOICMOHD1d6/NmzZyhdujRMTU3VKl5UqVIlleqvKxQK3Lp1C/Pnz0ebNm0yBfu5urqiV69eWL9+PV69eoWvX7/iyJEjGDduHOrXr8/HBgiFQri5uaFXr15wd3eHjo6OWiVbM8RmdHR0YGdnh6CgID4y3dnZGbNmzcqkK3Hy5Ek4ODjAwMAAS5YsUWsiVSgUGD16NJ//npiYiM+fP2PDhg1o164db9BYWVmha9eu2L59u9KSze/fv4dAIIBUKlWrot2ECROgp6fHP+uOHDkCIsLq1atVbqMguHr1Kho1aoQM7YctW7aotNBITU3FtGnTYGBgAEtLS6xevVptQ+fmzZtwdnaGoaFhJiGq/HDz5k1e1jmvcU05sXLlShBRgS5sfhWYMfAfHMdBR0cHc+bM+anjyMgv/1laB9/j7e2NqlWr8g+Bp0+fol27dvyDZc+ePT91JZSamgpvb29YWVlpPR1z0KBBvIDOxIkTsxyvX78+iCjHfe8vX77A19cXEolE5UmjW7duqFixYpb3U1JScO7cOUyZMgWNGzfmJz+xWAwPDw+EhoaievXqsLGxwd27d7Fq1Sr07NkT5cqV40V9TE1N0bBhQ4wfPx7Hjh3LNFl++/YNnp6esLS0VMswvX79Otq0acOv3AoXLow1a9Zk+3fy9etXdOvWDUSExo0bq6RDHx8fD39/fwgEAgwcOBDTpk2Dj48P36ebmxtGjhyJixcv5jrxRUZGgohQv359le8RSP+d/pgx0qZNG1hYWOQqpFUQ3Lp1C/7+/sjQZ1i3bp3KgbxRUVEoXbo0hEIh+vfvr3bMCsdxWLZsGWQyGcqVK6cxDYYXL17wuhSdOnXSyrPHw8ND7b+F/xWYMfAfHz58ABFh27ZtP3UcHMfB2NgYkyZN+qnjAIA9e/bw+fSDBg2CVCqFtbU1lixZolKkdkHw9u1b2NraomrVqlrb4/v06RNfnz0jSHDs2LH8w4jjOOjp6cHY2FilIKzAwEB+ZZPb+XPnzoVUKsXnz59x+PBhjBo1Cj4+PvzqSE9PD76+vhg7diyOHTuG9+/f4+TJk5g8eTLvvcl4lS5dGt26dcOyZcvw77//5jpRvn//HsWKFYOLi0uOE1xSUhJWr17N53rb2NhgzJgxiIyMhJ2dHWQyGSZMmJDj97N7925YWlqiUKFCSr1RGTx8+JBXuMuQ/pXJZGjUqBEWLlyYaatAFTI+o7ykgTZs2DBTMaro6GgYGhr+1KI2//77L1q3bg2BQABHR0esWLFC5d9qTEwM2rdvzwcV5kUTJCEhAZ06dQIRISgoSGNBvp8+fYKTkxMEAgFq166tlefP9evX85SJ878CMwb+I+MPQdViL9qkevXqPz12AUjPELC0tIRYLIaBgQHGjx//S6ZQXbx4EVKpFD169NBK++Hh4Zm0FyZNmgQiwogRI8BxHK5cuQIiQvPmzVVqj+M4TJgwAUSEjh07Kp0k379/j+3bt6NVq1YgIn7VW6hQITRr1gzTp0/HxYsX8eTJE2zcuBH9+/eHu7s7HxxnYGAAX19flCtXDqampnn2nDx8+BCFChVCzZo1s4zz8ePHCA0N5VdrderUwbZt2zJlE8THxyM0NBRisRjOzs45Voh8//49nzPeoUMHfkX68eNHrFmzBrVq1eK9Gubm5ggKCsLu3bvz/Df56dMn/nPNy+ezbds2EBFu377NvxcREQGBQFDgz5FHjx6hY8eOEAqFKFKkCBYvXqxyeqZcLsesWbNgaGgIc3NzLF++PE8xS3fv3kXp0qWhp6eXrUZHXvj27RsqVaoEoVCI0qVLK93u0QTBwcGwsbH5ZRY6BQ0zBv4jYxWcW9W1gqBnz54oX778T+tfoVBgzZo1KFq0KP/wLYgCRvlh6dKlICJERkZqtN2EhAQUKlQIffr0yfT+tGnTeB2BjHgCdVXU1q9fD6lUCm9vb1y/fh2rV69GUFAQH/BGRChSpAiI0qWAr1+/jnPnzmHmzJkICAiAra0tf16xYsXQsWNHLFiwADdu3OBdwk+ePIFYLMbUqVPz/BmcPXsWOjo6aNeuHVJTU7Fr1y5+W8TU1BQDBw7M1RV8+/ZteHl58cGm2f3OOI7D6tWr+RTVjEJQGffp6OiIo0ePaiTAdsWKFSAilClTJk/XZ6jUfV9qWi6Xw83NDRUrVtSYvkZOPH/+HN26dYNIJIK1tTXmzp2rlofs9OnTvGZE796987zFsWbNGujp6aF06dIazapIS0tDo0aNIBAIULhwYcTExGis7e+Ji4uDgYEBRo8erZX2fweYMfAfCxcuhEgkKpAfcG5kuIZ/hoV65MgRuLm5gShd4/vmzZuwsrLS2qpbkwQHB0MqlaqknqYqEREREIlESku1zp49m1+FC4VClV2iCoUCt2/fxoIFC1CnTp1Mk13p0qXRs2dPrF27FleuXMGOHTtgbGwMGxubTOV6a9SogbCwMOzcuRNv377Nsb+ePXuiUKFC+fpdZuytZ1QQrFKlClasWKGWG5jjOKxatQoWFhYwNDTE7Nmz+b/xlJQUHDlyBP3794eTkxMf0EhEvNHTr18/jRYAatSoEcRiMUaOHJnnNgYPHoxChQplmoAvXLgAgUCg1XLkr1+/Rq9evSCRSGBhYYEZM2ao9V28ffuWd+dXqVIFV65cydM4vn37hu7du4OI0LlzZ416DjmOQ1BQEAQCAfT19bWqBrho0SIIhUK1t5n+l2DGwH+MGDECRYoU+dnDAPD/eczqpKHllxs3bvCrvWrVqmXyBEycOBE6Ojq5Tjo/mwyNchsbG5UC0VRpr0iRIujQoUO25/zzzz+8+z67/f/U1FScP38eU6dORZMmTWBmZsYH+1WpUgVdu3aFlZUVjIyMMHjwYHTo0IGfEDMmf0tLS8yaNQsXL15UW7Xt1atX0NHRUavOAZD+MD5x4gRatWoFsVjM6wPkZ/IE0ush9OrVCwKBAEWKFEGtWrVgaGjIaw8EBwdj3759ePbsGYoXLw4igqWlpUZd71+/fuXvJz/G471790BE2Lx5c6b3e/ToASMjI42vZN+8eYMBAwZAR0cHZmZmmDx5slpuc7lcjrlz58LY2BiFChXCkiVL8uxlefDgAcqVKweZTJZFqVMTjB8/nt8iU1drQx04jkOFChXQtGlTrfXxO8CMgf/o0qULPD09f/YwAKTvnRJRrtXzNMHLly/RuXNnCAQClChRAtu3b88yqWUE0OV3EigIoqOjUbhwYdSoUSPfUqcZbuTv94R/ZO7cubz4Sc+ePaFQKJCQkICjR49i9OjRqFWrVqZgv9q1a2PMmDHYvn07tm3bhpEjR6J27dq8qE+Gy3/AgAHYtGkTXr58ifDwcJiZmeUrejokJARGRkYqafXHxsZizpw5fFpgyZIlMXv2bHz69Am9evWCSCTKU/0MjuNw69YtTJo0iQ82zHhVqlQJUVFR/D3evHkTDg4OsLCwwNq1a+Hu7g6RSIRRo0ZpxDuwbt06EKWXSc6vN9DT0zNLBPqnT59gbm6usdif9+/fIzQ0FLq6ujA2NkZ4eLjaz9mzZ8/Czc0NAoEAPXr0yJc42MaNG2FgYABnZ2eNqv9lsHz5cv5vQ9tKgBcvXgQRYf/+/Vrt51eHGQP/UadOHQQEBPzsYfBYWlpqJY82g9jYWAwbNgwymQwWFhaYP39+jg/Z/v37w8zM7JcMIPyRM2fOQCwWo2/fvnluQ6FQwMXFJdeqdBmCNZ07d+YD2zKC0szMzNC0aVNMmTIFGzZswOLFi9G1a1eUKlWKf9BZWFigWbNmmDx5Mo4cOcKr+U2ZMoWfGHfv3g0iypcL8927d9DX18ewYcOyPefatWsICgqCnp4exGIxWrZsiWPHjmUyQuRyORo1agQDAwPcuHEj136TkpJw4MAB9OnTB/b29iAi6Ovrw9/fH8uXL0d0dDTmz5/Pr1SXLl2KLVu2QF9fH25ubnwNCrlcjnHjxkEsFqNixYr53pf29/eHrq4uOnXqlK92gPR4FYFAkKVeRoYxmR+FvM+fP+Pvv/+GgYEBDAwMMHLkSLX39d+/f89nsFSqVClfHpbk5GQ+RqZt27YqFydSh3379vFbRFOmTNF4+z8SGBgIBweHX2KL+GfCjIH/cHFxwcCBA3/2MHhq166Nli1barzdlJQUzJ49G4UKFYKenh5GjRql0g/62bNnEAqFWt0H1SQLFiwAEWHlypV5uj6jguW5c+eyHHvx4gXWrl3L75VmvMzMzCAQCFC5cmVERkYiPDwcfn5+fFljoVCI8uXLIzg4GKtXr8bjx4+zrPYVCgVGjBgBIkKPHj2QmpqKly9f5ilA8Uf+/vtv6OnpZdpCSUpKwqpVq1C1alV+f37cuHE5BtLGx8ejYsWKsLW1VVoc5s2bN1i2bBmaN28OfX19EBHs7e3Rt29fHDx4UGmA29u3b9GhQwf+s6xbt65Sw/Py5csoVaoUdHR0MHPmzDy5uOPj4/n4ix/d+3khLi4O+vr6WbZhOI6Dl5cXSpYsqXbaa2xsLMaOHQsjIyPo6ekhLCwMHz58UKuNtLQ0LFiwACYmJjA1NcXChQvzNeE9efIEFStWhFQqxaJFi7SS53/x4kX+u+nVq5fWdUw+f/4MXV3dXyKV+2fDjAGk/2j19fV/arXAH+nfvz9cXFw01h7Hcdi0aROcnJwgFAoRFBSkduZE69at4ejo+FtY0BzHITAwEDo6OmoHR3EchypVqvDysnfv3sWiRYvQvn17FC1aNEukf+XKlTFr1iz06tUrUwEfY2NjNGjQAOHh4Thy5Ihav4vly5dDLBajXr16iI2NhZmZGcaPH6/ux5CJz58/w9jYGAMGDMCjR48wePBgPn6hbt262LFjh1o56UWLFkW5cuUQGxuL69evIzw8nPeUCAQCVKtWDZMmTcLt27dzfagnJCTw1ewsLCwgEokQEhKi9DP79u0bLxHt4+OjdgncDLVIkUiksZK3Xbt2hYODQxbj5Pbt2xCLxZgwYYJK7cTHx2PSpEkwNTWFjo4OQkJC8hSrc/HiRVSqVAlE6TUX8luWffv27TA2NoaTkxOuXr2ar7ay4+HDhzAxMYFQKESjRo0K5DkTEREBsVj8y8dDFQTMGEC6FU5EGpPM1ASLFy+GSCRSSyI1O06cOMGXKW3cuLFaJUe/5/LlyxpbTRUESUlJqFy5MooWLarywzA1NRXz58/nhVcycuhFIhHc3d3Rr18/TJw4EaNGjcqU2kdEcHFxQWBgIIKDgyESidCiRYt87W8fPXoURkZGKFu2LKpVq4YWLVrkuS0g3dXerl27TAqEgwcPxsOHD9Vu69u3b5g/fz6kUim/kjM0NETLli2xatUqtSaf58+fo3z58tDX18f27duRkpKCKVOmQE9PDzY2Nti0aZNSY+L48eMoWrQoDA0NsWLFCpVXka1bt4aRkRFq1aql8hhz49y5cyAipToKoaGhkMlkOSo5JiYmYvr06bCwsIBEIkHv3r3zpH3w8eNHPgLfzc1NqWdLHVJSUhASEgIiQosWLTRmPP3I27dvUaRIEYhEIlSsWDHfxatUgeM4lCpVCq1atdJ6X78DzBgAcOfOHRBRthXPfgZnz54FEam0L5sdd+/eRZMmTfjVa1RUVL7H5ePjA3d391+2IMuPvHz5EhYWFtmqliUmJuLYsWMYO3YsfH19+UA+gUCAWrVqISQkBCNHjkSfPn1QpUoVPgJdX18fYrEYQqEQu3btyhKYt3PnTkgkEvj7++crkPHOnTuwt7eHvr4+7Ozs8tRGTEwMwsPDYWdnx2cx+Pj4qK0OFx0djcjISDRp0oQPirS2toZQKESDBg3ypAB56tQpWFhYwNHRMUsRnBcvXvCSunXr1lWqZRAbG4suXbqAiNCsWTNeGCo7vn37xn9306dPV3u82ZExsbRp0ybLsfj4eBQpUgSNGjXK8rtJTk7GnDlzULhwYYhEInTv3l1tTweQvr0UGRkJMzMzGBsbY968efleWT9//hxVq1aFRCLBnDlztPabj4uLQ7ly5SASidQy3PPLyZMnQUQ4fvx4gfT3q8OMAfx/caC8/Ai1RYa3Yt26dWpfGxMTg6CgIAiFQjg6OmLjxo0aq4K4b98+EKlfVvdnEhUVBZFIhMGDB+PTp0/YtWsXhgwZgqpVq/KKfRk6/RmKfx4eHvw2QIbYTfv27TFv3jxcu3YNN2/eBBGhevXq2fa7d+9eSKVSNG7cOF9SyW/evIGjo6Na3iuO43D8+HEEBARALBZDT08P3bt3x9WrVzF9+nSIxWI8fvw4xzYUCgUuX76MMWPG8C5noVAILy8vTJ06Fffu3eO1A4hIZVd4BpGRkbxhktN++N69e+Ho6AipVIpRo0YpNWJ27NgBCwsLWFhY5Cgnu3PnTv471XTq7vTp06Gjo6M0YyMjBiVjbCkpKVi0aBHs7OwgFArRuXPnXL+P7Lh8+XKmQFZNuLz37NkDU1NT2Nvba1VNMSUlBXXq1IFIJIKJiUmevFR5pW3btnB2dv5tFjbahhkD+P9o4PymommaIkWKZFsFTxlxcXEYPXo09PT0YGZmhtmzZ2tcr1+hUKB06dK5Rtn/Krx8+RLr1q1DtWrVMrn0bW1t0bx5c/To0QNdu3aFl5cXZDJZpv3uIUOGYPv27Uo1CzIq5uWmaX/w4EHIZDI0aNAgXzrtFy5c4CfjnIppffnyBREREXzGgouLCyIiIjIVm/n27Rusra2V6ickJCRg165d6N69O6/9b2xsjDZt2mDt2rXZpiaOHTsWRIS1a9fmei+pqano06cPiAi9e/dWaSslMTERI0eOhFQqhaOjI/bt25flnHfv3qFZs2b8pKjMpd2hQweYmpqiWLFiGp8E3r17B7FYrPT74TgOjRo1gp2dHRYsWAAHBwcIBAK0bds2x+JWOfHp0ycEBwdDIBCgXLlyOH36dH5vAampqQgLCwMRoUmTJiqlouYVjuP4UtYSiUSjYmG58e7dO0gkEsycObPA+vzVYcYA0h9khQsX/tnDyEKDBg1UmnRTU1OxYMECWFpaQkdHB0OHDlW70pg6ZOQAa1MRLC9wHId79+5h8eLF6NChA5/KRpReQjdjFVa7dm1ezIYovbBOy5YtMWzYMBAR5s2bl2tfJUuWBBGpJCpz9OhR6Orqok6dOnneC01JSYFEIuEL6/Tv3z+TG/jq1avo1q0bnxYYEBCQKW//R+bPnw+BQIC7d+/i5cuXWLBgARo2bMgbRM7Ozhg0aBCioqJUmqw5jkOXLl0gkUhyLNH84cMH1KpVC2KxGIsWLVL7c7h//z7q1KkDIoK/v3+WdD6O47BixQoYGhqiaNGimVzAycnJMDY2hpGREfr3769236rw119/oXz58lk+97S0NMycOZOP12jRokWO+hU5oVAosGzZMpibm2dRcswPr1+/Ro0aNSASiTB9+nStr5iHDh3K/wa3b9+u1b5+ZMqUKdl6cf5UmDEAICgoCJUrV/7Zw8jCkCFD4OjomO1xjuOwfft2ODs7QyAQoHPnzlkejtogOTkZ1tbW6N69u9b7ygm5XI5Lly5hxowZaN68OczNzTOl8DVv3hwBAQHw9vbmFe6ICFKpFEFBQdiwYQNevHjBP/S6du0Ka2vrXL0pnz59gkAgQNGiRVUe64kTJ6Cvrw8fH588F1qpUKECunXrhvnz5/MR14sXL+ZdxHZ2dggPD8/VQFEoFDh9+jSMjY1hbGzMB0j6+PhgxowZeS45m5KSAl9fX5iYmCg1FG/fvg1HR0eYm5vna5uJ4zhs3LgR1tbW0NPTw5QpU7J49Z4/fw4fHx8QEUJCQvDt2zd+i4uIcPjw4Tz3nxMZfWRksCgUCmzevDmTp0YkEuXZELh27Rov2NShQweNKRwePHgQ5ubmsLOzK5A6JHPmzOG/i4IuG69QKODk5ISOHTsWaL+/OswYAODn56dyxbmCZOXKlSAipfnWZ8+e5V3f9evXz1egYV74559/IJVKNSL7qyqJiYk4fvw4xo0bhzp16vA57Do6OnB3d0fjxo3RoEGDTIV+zM3N0aRJE/zzzz84efIk7t69CzMzM9SvXz/TyvrVq1eQSCSYNm1aruNYs2YNiAgDBgxQa/xnzpyBoaEhatSokSexlsDAQFSuXBkPHz5E8+bN+XusWbMmdu7cmePqMC4uDtu2bUNgYCAsLS1BRHyw5KRJkzTmSYqNjYWrqyscHBwy7V3v3LkTBgYGKFeunNI6D3nh69evCAkJ4avZ/eiRUCgUmDVrFnR0dFCqVCk0bdqU19fQVrnrtLQ02NraIjg4GDt27EC5cuX43+jFixeRnJyMkiVLwsvLS62V95cvX9C3b18IhUKUKVMmR++LuuMdOXIkBAIBGjRooLaWQV7YvHkz7yEZPHiw1vv7kcOHD4Po1y++VtAwYwBA2bJl86VWpy0ySuNeunSJf+/Bgwd8Pnb58uW1tsLJjS9fvsDAwAB///231vr4/Pkzdu/ejdDQUHh4ePCR/MbGxqhatSrq1auHatWq8bnyAoEAZcuWRc+ePbFy5Uo8fPhQ6QP38OHDEAqFmeIxBg4cCBMTE5Um6bp164Iob5r258+fh5GRETw9PdVK05LL5ejatSv/EDUzM0OnTp1gaWmJokWLKl1pPnv2DHPnzkW9evUglUpBRChVqhTCwsJw6tQpJCUlwdnZGQ0bNlT7PnLixYsXsLa2RuXKlREfH89rzLdo0UIr5WevX7/Or5Y7duyYJYDu7t27qFChAp/+2KxZM42PIQOO49C6dWtehbJWrVpZspSOHj0KItUEsTICNC0tLWFgYIDp06drrFhTTEwMfHx8IBQKMWnSJI0FGefEiRMnIJFIIBQKERAQUCB9/shff/2FsmXLssDBH2DGAABTU1NMnjz5Zw8jC4mJiRAIBFi+fDnevXuHPn36QCwWo0iRIli9evVP+SF9T0hICExNTTX2gH/9+jU2bNiA3r17w9XVlV/5WlpaomrVqvD29ubdrETpFfTq1auHsWPH4vDhw2pNrlOmTAERYevWrfj48aPKtRfkcjlkMhl0dXXznLp1+fJlmJiYwN3dPVdp2ejoaIwbNy6TpsHEiRP5YMRXr16hfPnyMDIywv79+3H27FkMGzaM//wkEgnq1KmD2bNnK41W37Bhg1ZWSdeuXeN1AogIY8eO1erfq0KhwNKlS/nUuvnz52f6fg4cOMB/fg4ODnkO2ssOjuNw5MiRTDUXcgr+bdu2LczNzXPcs7558yZq1KgBovSyz3nRHciOY8eOwcrKCoULF9aYlyE3bt26BUNDQ0gkElSvXl0jGirqEh0dDZFIhPnz5xd43786f7wxkJCQACLCmjVrfvZQlOLk5IRq1arBwMAAxsbGmDJlyk/5ESnj+fPnEIlEiIiIUPtajuNw//59LFmyBJ06deJT54jSlf2qVKmCSpUqwcLCIlMQYJcuXRAZGYnbt2/na3LhOA4BAQEwMDBAnz59oKurmym/+UtqKo58+oR/nj9HnwcP0PP+fQx+9AjDT5wAOTigfoMGee4bSJ8szczMULFixSwFYziOw7Fjx9CyZUs+LTAoKIjPi/4+Yj82NharVq3iJ92MrZFOnTphy5Ytuf4WFQoFypYtq1EBHiDdO1CsWDEQEfz8/ApsFfbhwwdeJrpSpUq8V61nz558TImTkxNkMhnmzJmjEQPl5MmT8Pb2BhHB3d0dBw8ehI+PD3x8fLK9JiYmBkZGRujZs2eWYxnbHyKRCC4uLjh27Fi+x5hBWloawsPDIRQK4evrW2DKey9fvoS1tTWkUilKlCjx0wL3wsPDoa+v/1vNUQXFH28MPHjwAESkEUEeTZKWloalS5dCJpNBIBBg4MCB+aoypi3atm0LBweHXKOZ5XI5rly5glmzZuGvv/7i960FAgEcHR1RoUIFODs78+5sPT09+Pj4YPjw4dizZ49W9jLj4+Ph4uICoVCIHj16AADOx8ai3d27EJ84AYqKgigqCpITJ/gXRUWBoqJgeeQIZr18idh8RHHfvHkT5ubmKF++PN6/f48vX75g9uzZfKZCqVKlMHfu3EweDwcHB3Tv3h2zZs2Cr68vr5NQpkwZ3hUeFham1iSXkXufn4I633P69GlYWlrC3t4eI0eOBBEVeArXuXPnUL58eQgEAgQHB8Pc3BwlS5ZEpUqVkJiYiP79+4OI4Ovrm+cCUOfPn+czG9zc3LB7927e6Fm7di2IKEftgDlz5kAgEODChQsA0o3AdevWoXDhwtkGRuaHd+/eoW7duhAIBBg7dmyByYp/+vQJLi4ukEqlMDc311jMiLrI5XLY2dkhKCjop/T/q/PHGwPHjh0DEeHRo0c/eygA0h8Ie/fuRZkyZfiH/K+Y9pjB1atXlYrhfPv2DSdOnMD48eNRr149GBgY8G5rJycnlC5dmjcIMly3bdu2xdy5c3HlyhWN7Yvmxt9//w0igpefHzrduweKiuINgZxegv9eFmfOYE8+DJU7d+6gUKFCMDExgUwmg1gsRqtWrXDixAl+YpHL5Th58iRCQ0P5z1EqlaJ+/fqYO3cu/3DlOA7Tp0+HQCBAq1atVNY1yKjF4OHhke8V/JIlSyCRSODt7c17WoYOHQqBQICtW7fmq211kcvlmD17Nh8oqaOjg9GjR/PHjxw5Ajs7OxgbG/8fe2cdFsX2//H3Ft0pLSCIioodiN1YqNhixxULE7u7uwu7uwO7vXZhKyqKgYTk7vv3B3fnJ1ILLOq9X17PM48yc+bMmdndOZ/zSQYFBal879evX2f9+vWF3+fOnTtTCV/fv3+noaFhhj41SUlJLFGiBEuUKMFbt26xSpUqBMDmzZvnqEJlWpw9e5bW1ta0sLBIM2VybhEbG0tPT09qaGhQW1s7y3VC1Imy+ufvHMOfzP+8MKDMnpaThDDq4tq1a0I4VNWqVXnt2jVu2rSJALJctvRXUr16dXp4eHD//v0cOnQoK1asKDj7aWtr08nJiY6OjkIOew0NDVaoUIEDBw7kjh07slwwSV3Ex8fT1taWZVu1InbsoOjUqUyFgJ838T//9g4JoTwLE2lMTAxXr14t1IwQi8U0NzcXokK+fPnCzZs3s02bNjQ2NhZ8J0qUKEF9ff0Mf2M7duyglpYWK1SooHJqV6WHdXYrIyYmJrJPnz4EwJ49e6ZY0crlcrZs2ZJaWlo5zpWfHZQFqwCwRIkSKWpzfP36le3btycANm3aNMPndfv2bSGKo2DBgty8eXOGGphevXrR2to6Q63ZqVOnhM/f1dWVR48ezd5NpoNcLufUqVMpkUhYuXLlX/pbS0pKYtOmTYW03WklivqV1K9fn2XKlPmtY/iT+Z8XBiZPnkxTU9PfOobnz5+zVatWwkrj4MGDwirlzp07BKCW7GLq5O3bt9yyZQv9/f1T2PsNDAzo4OCQwtafL18+Nm3alDNnzuTFixdzLawrq6xatYrIn596p09nSxD4eev66FGmq8vHjx8zICBAmODr1q3LvXv38sGDB8yXLx/NzMxYvnx5wUnSw8ODI0eO5JUrVyiXywWVfmbOZJcvX6aFhQWdnJxUcpZTKBSsXLkyPTw8smxH//Tpk2CyWLx4cZptYmNjWalSJZqZmWU77W52kMvltLa2ZsmSJWlkZERXV1dKpVIOHjw4hePrjh07aGpqSktLS+7bty9FHw8ePBDSVDs5OXHdunUqJflRas0OHDiQ6pgyV4K1tTWlUik1NDTUrj7/9OmToMEYNmyYWhITqYpCoaC/v78Q/bJ8+fJfdu20ePHiBUUiEVetWvVbx/En8z8vDPTq1YvFixf/Ldf+9OkTAwICKJPJaGVlxZUrV6b6wcbHx2c7W5u6UCgUfPz4MVeuXMkOHTrQyclJmOiNjY1pbW0t/OiVVcd69+7NjRs38sWLF39kCE9SUhJdihWj9t69lORQCPhxW5jGJJ2YmMidO3cK9mVTU1MOHjyYDx8+5KlTpzhgwAC6uLgIz1RHR4eTJk1KU1X88uVLAlBplfX8+XMWKlSIxsbGKiX5OXv2LIGsVaW8d+8enZ2daWpqmqnfzadPn+jq6koXF5df5v+irCbo4ODATp06MS4ujhMnTqSWlhZtbW25c+dO4fv5/v17NmjQgEBy2d+///6b7dq1o1gspr29PVesWJFl85WHhwd9fHxS7Hv48CFr1KhBIDmL4u3bt2lubp5mkaPscunSJdrZ2dHU1JSHDh1SW7+qMnnyZOH7PGLEiF9+/Z8ZPnw4DQwM0szZkkcy//PCQKNGjejt7f1Lr/n9+3dOmzaNhoaG1NfX58SJEzP8khYuXPiX5kFISkrijRs3OHfuXDZr1iyFbd/U1JSmpqYp4t0bNGgg5D64du3aLxtnTtixYwfRt6+g5k93W7aMqFiR0NcnNDWJ/PmJ3r3Tba915gyf/WNyevv2LceOHSt4+leoUIGLFy/m6tWr2bJlSyH7n5WVFbt16yZoCBwdHZk/f/40S94qFAoaGRlx0qRJKt3nly9fWK1aNWpoaKhUN6BOnTp0c3NTybls37591NPTo7u7e4bleX/k6dOnNDMz+2WhZQMGDBCiCHbu3Cnsf/78Ob29vYVoB6W2QqFQcOrUqYJjppmZGRctWpRtbdaCBQsolUoZFhbGqKgoDh06lDKZjM7OzikmaaW5Mqf2fIVCwdmzZ1MqlbJChQpq9z1QBWWyNLFYzPbt2//2xUB8fDwtLS3/yFwyfxL/88JAyZIl0wzvyQ3kcjnXrVtHOzs7SqVS+vv7Z1pylSR9fX0zDFPKKbGxsTxz5gwnTpzIOnXqCE5qEomExsbGQrlakUjEIkWKsFu3blyzZg0f/aAWj4+Pp7W1NTt16pRr41QXCoWC7vXqZb7Snz6dkMmIQoWIXr2IgQOJ1q2Jli3TPUcaHMwqJ0+yWbNmlEgk1NHRoa+vL/v168dKlSpRLBYLYW9jxozh9evXU6nlX79+zQIFCtDOzi5Nx9aqVavS19dX5fuNj48XyvyOGzcuw5fztWvXCIDr1q3L8PlNnjyZIpGITZo0yXI2xUuXLlFLS4stWrTI1dwDCoWCDg4OrFSpEmUyWapxKhQK7tmzh/b29tTS0uLAgQPZtWtXSqVSmpqaChqwgQMHZltw+fLlCzU0NNi+fXva2tpSS0uL48ePT9Wf0kzj6uqabcHjy5cvgk/DwIEDf5kT7o8cPnyYEomEGhoarF69+h9R/G3btm0EkO0U0P8r/M8LAxYWFpwwYUKuX+fYsWMsXrw4geRMbFnJ/z5u3DiamZmpbSwRERE8dOgQhw0bRk9PT8HZTyaT0cDAQJiw9PX1WatWLY4ZM4ZHjhzJNGXttGnTKJPJ1JYvPbc4fvw40asXxRn5CRw4QBgbE15exMmTWTMXnDhBm6JFWbVqVebPn19wpGzUqBGXL1+ukhNXaGgoCxYsSGtr61Tflf79+9PFxSVL96xQKIRMgB06dMjwJd2kSRM6Ojqm2SYmJkbwbxk9enS2J/OdO3dSJBJx6NCh2TpfFZSCTenSpVmzZs102z19+jRFieYuXbowOjqaSUlJnDFjBjU0NFikSBH+/fffWR7D48ePmS9fPgLJVQCfPXuWbtt79+5RKpVm63107do1Ojo60sjIiHv37s3y+erg6tWr1NHRoba2Nt3d3bOUBCw3qVGjBitVqvS7h/HH8z8tDMTFxREAV69enWvXuHXrFmvXrk0A9PT0zJY39c6dOwlAJS1CWrx//57btm1jnz59hFzpylArZZU6AHRxcaGfnx+XLl3KO3fuZDkOOSIigvr6+gwMDMzWOH8V1apXp/jw4Ywn9ICA5Oeydm3y34cOqS4UnDxJ+PrS1taWPXv25MGDB7MVrfL+/XsWLlyY+fLlS1H4Z82aNRSJRNmqb7BhwwZqaGiwatWq6Uao3L17lyKRKJWfyuvXr1myZEnq6Ohkya8gPWbPnk0AueYPExgYSBMTE8pkMs6ZMyfV8Y8fP3LgwIHU1tamkZER+/TpI2T8a9GiheCkeffuXXp4eFAmk3HSpEkqOeLFxMRw+PDh1NDQEISBn9MSp8XQoUOppaWVodDwIwqFggsXLqSGhgbLlCnz22L4nzx5QjMzM+ro6NDKyopv3rz5LeP4GWUeGVVMZP/r/E8LA8+fPyeQOxXMXr16RT8/P4pEIrq6unL37t3Ztp0pv9CqZCJTKBR88uQJV69ezU6dOgkrUwDU0tISVv1aWlqsXLkyAwMDuXfvXpVD0DJjwIABKuf4/x1cuXKFsLPLfEKvXJnQ1SVmzkxuDxBaWkSjRsTRoxnnIDh1ijXOnVOLrfTDhw8sWrQozc3NBTXnzZs3CWQ/hfCZM2doYmJCNze3dCedNm3a0MbGRlBnX7hwgZaWlrS3t+fNmzezdd2fUSgU7NOnT66EnSkUChYoUEBw2vzR3PL582cOGzaMurq61NfX56hRowStl0Kh4IYNG2hpaUk9PT3Onj2biYmJjI+P54gRIygWi1m+fHmGhISke93du3fT3t5eyGsQHR1NR0dHlUxo0dHRtLe3Vylr47dv3+jr60sguaT171LJf/jwgc7OztTR0aGenh5v3779W8aRFgMHDqSpqekfk7X1T+Z/Uhj4nJDAxzEx3HDmDGFomGa51ezy9etXDhkyhJqamrSwsODixYtzbLtLSkqipqZmmml/k5KSePPmTc6fP5/NmjWjqampMPkrs/kBoI2NDVu1asV58+bx2rVruWZPfP36NaVSaZorsT8BHx8fWrZtm7kw4OSUPPlraRE+PsS4ccn/AkT16pmeb6vGePrw8HB6eHjQ1NSUt27dYnx8PGUyWY7yqz9+/JjOzs40NzdPs+BSSEgIJRIJ58yZw1WrVlFDQ4OVKlXKtnYqPZKSktioUSPq6uryxo0bauv39u3bQuimq6sryWTN1ZgxY2hgYEAdHR0GBgamG9XwY5XAYsWKCav6ixcvskCBAtTR0eGiRYtSTNhPnz4VQvnq1auXQgBRpsFVRUhWho/+6PD4Mzdv3mSBAgVoYGDA7du3q/RMcoOoqCiWLl2a2tralEgkastiqQ5iY2NpYmLCQYMG/e6h/Cv4nxAGkhQKHvj0ib737tHmwoVUL27zc+fY5O5dbv/wgQnZtIHGxcVxzpw5NDExoY6ODkePHq3W1bGHhwe7devGuLg4njt3jpMnT2aNGjVSOPcpV/1SqZSlS5dmQEAAt2/frtYCJ6rQtm1b2tvb/9K4ZlV48OABAdB3+/bMwwmVuf4bNUq5v2HD5P3r12d4vpYKoXxZ4fPnzyxVqhSNjY15/fp1Fi9ePMdpVcPDw1mxYkVqaWmlOaF06tRJ+H517do111ae0dHRLF26NPPly8dXr16ppc9Ro0bR0NCQVlZW7N27NydNmkRjY2NqaWlxwIABKgs1169fFxJDde7cmeHh4YyOjmavXr0IgLVr1+aTJ084evRoampq0t7ePk0t4OvXrykSibhixYpMr6lQKNigQQPa2tqmKgKmUCi4fPlyampq0sPD47dmTk1ISGCdOnWEhE4ZOZ3+DpSlxtPT4uSRkv+8MLD9wwfaXrxIBAdnOAEoj1mcP89V796prOKVy+XcvHkzHR0dhRz36nSg+/btGw8fPswiRYpQV1dXCHlShvYpY/0bNWrE6dOn8/z5879dJaZUY2/atOm3jiM6OpovXrzg1atXefDgQVaqVIkGBgasvHAhRZnZ/5XmlblzU+6fOzd5f2BghufLcqES3NevX1muXDkaGhqqLZtabGwsW7ZsSQCcPn268L3//Pkzvby8CIANGjTI9fCwsLAw5s+fn0WKFFGL41nhwoVZt25dAsklrzU0NNi7d+9sZeBLSkri0qVLaWRkRBMTEy5btoxyuZxHjhyhiYkJxWIxpVIphw8fzpiYmHT7qVu3LsuXL6/SNZ8/f05tbe0Uq9qoqCi2bdtWyPL4O3/nCoWCfn5+QnKsX+GEnVU8PT0zdBzNIyWqzt9S/MuISExE95AQbA8Ph+ifffIM2iuPfUxMRJfHj7H140cEFSoESw2NdM85ffo0hgwZgmvXrqFhw4Y4cOAAChcunKNxf/jwAefOncOJEydw4sQJPH/+HCSF4yKRCK6urqhevToqVaqEChUqwNHRESKRKINefy0eHh6oWbMmZs6ciVatWqllbCQRFRWF8PDwTLePHz8iPDwcsbGxqfrR1NTEg2vXwMw+JzMz4OVLwNg45X7l31FRGZ4u+v4dY8aMgZubG9zc3ODq6gpdXd0s3HFqjIyMcOzYMdSrVw+nTp2CQqFAUlISpNLs/zy1tLSwadMmODs7Y8iQIXj27Bn8/f3RtGlTfPnyBU2aNMHp06fx7ds3GBkZ5Wj8GWFpaYlDhw6hYsWKaNasGQ4dOgSNDH57GXHr1i08ePAAz58/BwA0b94co0ePhr29fbb6k0gk6NGjB3x8fDB06FD06NEDS5YsgYGBAb58+YJ8+fIhLCwMT58+RWxsLHR0dNLsp0uXLvD19cWDBw8yfU84Ojpi5MiRGD16NPz8/CASieDr64s3b95g06ZNaN26dbbuRV2MGDECQUFBEIvF6Nq1K0aMGPFbx/Mzd+/exYULF7Bjx47fPZT/HCL+OCOlQ2RkJAwNDfHt2zcYGBj8inGlyefERFS9dQsPY2IyFAAyQgrAVksL5zw8YKulleLY/fv3ERgYiAMHDqBMmTKYMWMGqlSpkuVrkMTz589x9uxZHDp0COfOncOHDx9StNHR0UGZMmVga2uLjRs34uHDh3Bzc8vmXf06jh07hjp16uDkyZOoXr16quMKhQIREREqTe7KLSEhIVU/RkZGMDc3z3RbvHgxdu3ahVevXuERibJ//53xDaxYAWzaBMyaBZQs+f/7//4bGDgQGDECqFkz7XNJGD97Bs1RoxAWFibsdnBwEIQDNzc3FCpUCG5ubrCwsMiSwBQdHQ1PT0/cuXMHQUFBaN++vcrnZsSqVavQvXt3QeA8cOAAtLW14ezsjIEDB2LChAlquU5GnDlzBrVq1UKbNm2wZs2aLD2XhIQErFq1CoGBgYiMjISZmRlKly6Nw4cPq218cXFx8Pf3x5o1a0AS9erVw6ZNm3Ds2DH89ddf0NDQwKpVq1C/fv00x2djYwM/Pz/MmjVLpfspXrw45HI5QkND4ezsjO3bt//23//ChQvRp08faGpqonr16ti3b1+OBNLcoHfv3ti5cydev34NmUz2u4fzr0DV+fvP+qQzIEGhQN3bt3MkCABAEoDQuDhUv30bf5cqBT2pFO/evcOYMWOwevVq5M+fH1u3boWvr6/KLyyFQoG7d+/i5MmTOHDgAK5fv46on1aYNjY28PLyQq1atVCxYkW4urpCLBbj5cuX2LhxI168ePHbXwZpIZfL8eXLF2Hi/vbtG2xsbNCzZ0/UqVMnxYo9PDwcnz59glye8hMSiUQwNTVNMYk7OzunO8GbmZmp9EP/9OkTNm/ejEGDBkFfXx/FFArIRCIkZiTfVq2aLAwcOpRSGDh4EJBIAA+PdE+VisXoVb06JnbtioiICDx+/BiPHj3Cw4cP8ejRIxw5cgQLFy4U7t/IyCiVgODm5gYnJ6c0X7J6eno4ePAg7Ozs0K1bN9ja2qJatWqZPoeMIIlPnz6BJMRiMcRiMWQyGaysrNC7d2/MnTsXffv2hbm5eY6ukxlVqlTBmjVr0K5dOzg6OmLMmDGZnpOYmIigoCBMmDABr1+/hqGhIWrWrImTJ0+iVatWahvb4cOH0adPH7x69QoDBw6EsbExJk+ejEKFCmHWrFm4c+cOunXrBm9vb3Tv3h2zZs2Cnp6ecL6Ghgbat2+PoKAgTJkyJVPNR1JSEvLnz48jR46gUqVKOHr0aLpah1/Fzp070bdvX+jq6qJgwYLYtm3bHycIREdHIygoCH379s0TBHKBf41mYOyLFxj/6hUYGwts2QI8fAg8epSs1h06FKhbN/VJr14BixYBd+8CMhlQvjzQqxdgZAQxgC5mZrDcuhWzZs2Cjo4ORo0ahZ49e0JTUzPDscTHx+P69evYv38/jh49igcPHqRY3cpkMhQuXBi1atVC9erVUb58eRj/rJb+B4VCAUNDQ4wZMwaDBg3KySNSicTERHz69EnlVfvnz5/x81dEJBKBJAoUKAA7OztYWFhkuHo3MTGBRCJR+72MGTMGM2fOxKtXr2BmZgYAaHH/PnaHhyMpoxOnTwcOH04WDIoXB27dAs6cAdq0Abp1y/Cad0qXRtEfJoKfSUhIwPPnzwUBQbk9fPhQEBBlMhkKFCiQQkBQbvr6+rCzs4NUKsWHDx+wd+9e1KpVK2sP5h9iY2PRtWtXbNq0CSNGjECLFi3QsGFDJCUl4cCBA7C3t4ejoyO6d++OmTNnZusaWWXSpEkYOXIk1q5diw4dOqTZRi6XY9OmTRg3bhyePXsGX19fdOrUCfXr14e/vz8WL16MsLAwWFhY5Ggsr169QkBAAHbv3o3q1atj4cKFKFSoEAAgNDQUAQEB2LFjh3Ds3LlzGDBgACwtLREUFARPT0+hr/v378Pd3R07duxAs2bN0r3mo0eP4Ovri2fPnsHDwwMhISF4/PgxTE1Nc3QvOeHcuXOoVasWNDU1YWRkhCtXriBfvny/bTzpsXLlSnTv3h0vXryAg4PD7x7OvwZV5+9/hTDwICYGxa5dS9YIhIUBrVsDlpaAlVXyizwtYSA8PPnFrqsLNG0KxMYC27YBFhbAkiXJwgEAjYEDMaB2bQwdOjRd22lUVBTOnDmDnTt34uzZs3j58iUUCoVw3MTEBKVKlYK3tzeqVq0Kd3f3LE1+5cuXh5ubG9auXZul5wIkCyaq2NmVW0RERKo+NDQ0VFLJKzddXV04OzujZs2a2RqzOoiKioKDgwP8/Pwwd+5cYf+ZiAhUvXUr45OTkoCNG5MFgs+fk79LTZoAzZunf45CgXIGBrhcunS2xksS79+/TyEcKP8fGhoqtLOxsUF8fDy0tLRgYGCAJ0+eYNWqVWjXrl2WVOtv375FkyZNcP/+faxZswYtW7YEALx//x4NGzbEo0ePsGXLFly7dg3Tp0/Hs2fPYG1tna17ywok0b17d6xduxZHjx5NYWpSKBTYvn07xo4di0ePHqFx48YYN24cihcvjmnTpmHcuHGoU6cO3r17hytXrmR7DPHx8Zg1axYmTpwIY2NjzJ49Gy1atEjz+R45cgS9e/fG69evMXjwYLRu3Ro9e/bExYsXMWTIEIwbN05YPJQvXx4mJiY4dOhQmtfdtGkTunfvDjs7O2zfvh1mZmZwc3NDixYtsHz58mzfT064f/8+KlWqJPx98eJFQSD60yhTpgwsLS1x4MCB3z2UfxX/KWGg5+PHWBUWhiQSSEgAoqMBExPg8WOgZ8+0hYE5c4CjR4F165Jf9gBw4wYwaBAwYADQsCFECgVq6+nhSNmyKU79+PEj9u/fjz179uDq1av4+PGjcEwsFsPBwQFeXl5o3LgxPD09YansP5t06dIFt2/fxvXr1xETE5Mle/vP5ggg2R8hK5O7vr5+lp0BZ86cieHDh+PFixewsbHJ0f1nh9mzZ2Po0KF49uxZCgcykqhx+zbOffuW/H1RI7IhQzCzXTv07dtXrf1GRUUhJCREEBB27dqFkJAQiEQiJCUl6zi0tLTg7u6eSptQoECBVGrpy5cvw8fHB1KpFHv37kXJH80hAGJiYtC2bVvs378fU6dOxZQpU9C6dWssWrRIrfeVHomJiWjQoAGuXLmCCxcuoHDhwtizZw/GjBmDu3fvol69ehg/fjxK/yB4lS1bFra2tjh58iQGDRqEUaNGZevax48fR+/evfH8+XP0798fo0ePhr6+fobnxMXFYerUqZg6dSry5cuHuXPn4vHjxxg1ahQKFSqE9evXo1ixYlixYgV69uyJV69ewdbWNsX5/fv3x7Jly9CuXTssWbJEMDMsWrQIvXv3xqVLl1C+fPls3VN2CQ0NRYUKFRAdHY2YmBicPHkSXl5ev3QMqnL9+nWUKVMG+/fvR4MGDX73cP5V/GeEgcikJFhevIi4H1biAhkJA02bAsWKAWPHptzv5weYmyc7kAEQAzidLx9Ob9+OI0eO4N69e4iMjBSa6+jooHDhwqhZsyaaNGmCEiVKqOwNnZmnvHLVfu/ePbx9+xaampqIi4tL1Y++vn6mE/qPqvpfYX+MjIyEnZ0devbsiWnTpuX69X4kPj4eTk5OqF27NtasWZPq+Ku4OBS6ehVxCgXUIQ6IAdT8/h0XW7ZEdHQ0nJycsHfvXri7u6uh99Ts3r0bTZs2xevXrxEZGYlu3brh6tWrqFKlCmJjY/Hw4UNBwyORSODs7CwIB58/f0ZQUBBKlCiBffv2pSuoyuVyDBo0CHPnzoWnpyeuXr2KkJAQ5M+fP1fu6WciIyNRqVIlhIWFIV++fLh79y5q1KiB8ePHo2LFiinavnr1Cvnz58eIESMwadIk3LhxI5WAkxmhoaEYMGAAtm/fjipVqmDRokUoUqRIlvp48uQJ+vTpg6NHj6JRo0bo2bMnAgMD8fDhQ0yYMAHdu3eHra0thg0bhpEjRwIAnj59Cl9fXzx8+BALFy5Ely5dUgjecrkc5cqVQ1JSEq5fv/7L7PQRERHw8vISvmNbtmwRtEd/It26dcPRo0fx4sWLXDE5/pf5zzgQXvj2LW1BICPCw4GvX4GCBVMfc3MDflAxKgBU7tcPOHYMAGBhYYGKFSuiUaNGaNCgAezs7P6/rZo85Y2NjVNM5EWLFkVoaCgGDhyIQoUKpXKm0/op6uFPwMDAAN27d8fSpUsxYsSIXyokrl+/Hu/fv8eQIUPSPO6gpYX1hQrB9/79HF9LAqCEnh52eXlB8+tXdO3aFUFBQShatChatmyJoKCgbIfKpYfHP06M9+7dQ7169XDmzBm0a9cOO3fuxObNm9G8eXOEh4enMDc8ePAAS5cuFQTZq1evonjx4mk6MNrZ2UEikWDOnDlwdnZG3759oaGhgdGjRyMoKEit95IWJHHlyhVIpVIhVPTw4cOom5bfD4Bdu3ZBU1MTERERsLa2RokSJVS+VkJCAubOnYvx48dDX18fGzZsQJs2bbIVFuvi4oLDhw9j586d6N+/P5o1a4Zhw4YhKioKw4cPx/79+1G3bl2sXr0aw4cPx65du9C5c2dYWlri8uXLwuf6IxKJBEuXLkXZsmWxaNEi9OvXL8vjyipxcXFo3Lgxnj9/ju/fv2PGjBl/tCDw7ds3bNq0CcOGDcsTBHKRP14zMPHlS4x9+TLtCIL0NAPK/cOGAbVrpzxn6VJg69ZkE4KGBpCUBKe7d9EtPh52dnaIjIxM09auiqd8Zo505ubmMDU1TeUJ+/79e1hbW2PPnj1o3Lixeh7cLyA0NBSOjo6YNm0aBgwY8EuuKZfLUahQIbi7u2PXrl0Ztt384QPaP3yYfF42riUGUEpfH0eLFYPxD59ZSEgIGjVqhMePH0NHRweLFi1Cx44ds3GFtCEJIyMjDBs2DIGBgQCSPdA7duyILVu2YP369Sni0b9+/YrWrVvjxIkTmDp1KmrUqCFEOigFhpCQEEHrpK2tjYIFCwoCQkxMDObMmYPExEScO3cuhQ1Z3Zw5cwajRo3CuXPnUK5cOXTs2BGDBw9GlSpVsGfPnjRXxp6enjA1NUVISAi8vLywYsUKla516tQp9O7dGyEhIejTpw/Gjh0LQ0NDtdxHVFQUxo0bh7lz58LFxQV//fUX5s2bh/fv3yM2NhZNmjTBnj170KJFC6xYsSLT96a/vz/Wr1+PR48e5arvhlwuR6tWrbBv3z4kJibC398f8+fP/6PymfyMUkh68+YNrKysfvdw/nX8ZzQDj75/z/pJ8fHJ/6YVfqJcxSUkJP9fIsFzuRzDhg0DAEilUpiZmaVQvxcpUiRXPeXz5csHY2Nj3Lt3718lDNja2qJ169aYO3cu+vTp80vCfXbt2oUnT55gw4YNmbZtbWkJZ21t1Dl9GhF6eoBYrNI1xCQoEmGQnR3G5c8PrZ8+X1dXVzx69AjLly9Hv3790KlTJ8yYMQP79++Hk5NTtu7rR0QiETw8PHDrB0dIqVSKdevWQSaToV27dkhMTISfnx8eP36MRo0aITw8HEeOHEHNf3Ik/Lx6lsvleP36dSoHxhMnTiA8PFxoV7lyZVSqVAllypRJoVFQRmtkl4sXL2L06NE4efIkSpYsiQMHDqB+/foQiURwdHSEt7c3+vbti0WLFqWYmN6+fYuLFy9i+vTp2L9/v0omqbdv32LQoEHYsmULKlWqhL///hvFihXL0fh/Rl9fHzNnzkSHDh3w119/oV+/fmjRogXi4+Oxd+9e7NmzB5MnT0ZgYKBKE+2kSZOwY8cODBgwAFu2bFHrWJWQREBAAHbt2gWpVIrGjRtj7ty5f7QgQBJLly5FkyZN8gSBXOaPFwZiFQpk0UgAKEMDExNTH1Oq7ZVCgUgEc3t7dBg0CPny5YOFhQUMDAygr6+fatPT04NYxQklK4hEIri7u+PevXtq7zu3GTRoENavX49t27ahbdu2uXotkpg6dSqqV6+Osj85faaH1suX+NaiBRqsXIkL+fLhq1QKkUIBiMUp/AmkIhHkJKhQQOvmTZzs0gXlM8nM1717d/j5+aFNmzbYvXs3ChQogC5dumDp0qU5FhA9PDxw9OjRFPskEglWrVoFqVSKjh074ubNm1i9ejVsbGxw9epVFChQIN3+JBIJHB0d4ejoiHr16qU49vnzZzx69Ahz5szBzp07cfHiRTx79gxhYWFC1IypqWkqc4Obmxvy58+f4b1ev34do0aNwpEjR1C0aFHs2rULTZo0STEB1alTB0uXLkW3bt3g6OiIwYMHC8d2794NqVSKpKQkaGhooEaNGuleKzExEQsWLMCYMWOgo6ODdevWoX379rk62RUtWhRnz55FUFAQ+vXrh8jISGhoaCAhIQEzZsyAs7MzWrRokWk/RkZGmDVrFtq3b48uXbpkO5w0I6ZPn44FCxZAX18fRYoUwcaNG/94tfvFixdx7949zJkz53cP5T/PHy8MaIrFEAFZcwQzMUn+9/Pn1Me+fAEMDP5fGAAQGR6OlStXIioqKpUZ4Gd0dXXTFBSys2lrawsvKnd3d5w/fz4rd/lHUKxYMdSuXRszZ87Mti1WVY4fP46///4bx48fV6k9SfTv3x+u+fNjZ4sWqFWnDs5GR6PznDl4b2CA53FxSFQoYCSVooS+Pkrp6cHmzRs0GjQIH11cgEaNMr2GlpYWdu3ahZs3b6JJkyZYuXIltm3bhjVr1qBp06bZvtfixYtjwYIFiImJSZHuWCwWY+nSpbh//z7mzp0Ld3d3XLhwIUfmO1NTU3h6eqJs2bIoWLAgYmJi8PHjRyxevBienp4ptAl///03Nm3ahO//aOw0NTXh6uqaKgNjfHw8pkyZgn379sHNzQ1bt25F8+bN0xWmu3btihcvXmDIkCFwcHAQJtCdO3eiZs2aOHXqFKpVq5Yi2c+PnDlzBv7+/nj48CF69eqFCRMm5Gqa5R+Ry+W4e/cuIiMjkT9/frx69QoAYG5ujpYtW2LPnj1YuHAhTJTvpXRo27YtVq1ahV69euHu3btq9RVav349AgMDYWJiAhMTE+zbt++3JzpShaVLl6JAgQJpZjvNQ7388cJAAW1tiEUiKLISJmZuDhgZJfsO/MyjR4Czs/CnTCRCp1q1sMTfHyQRFxeHqKioLG/v379HSEhIin3R0dEZDlMikUBPTw/6+vpISkrChw8fUKNGjXQ1E5lt6nZkU5XBgwejVq1aOHnypKCmzg2mTp2K0qVLZ7g6/JFdu3YhODhYiPu+fPEi9DQ0sLxixfQ1PDY2KFeuHBYsWIBGKggDSkqUKIFXr15hxowZGDFiBJo1a4bSpUtj79692bIBe3h4gCTu3r2bIuQsLi4O3bp1w6VLl1CqVCncuHEDQUFB6N27d5av8TMymQzjx49H+/bt4ePjg549e2LYsGGYOHFiiuelUCjw9u3bVPkSVq5ciffv3wvtpFIp3N3dUbVqVXz58gVnz56Fm5sbLC0t0xQaJ06ciJcvX8LPzw82NjZwcXHB2bNnMW/ePAwYMCDNVL9hYWEYNGgQNm7ciAoVKuD69etZcjDMKW/evEHLli1x7do1zJkzB/369cPVq1dRp04dhISEoHr16jh48CDOnDmDNWvWoPbPPkw/IBKJsHjxYhQrVgzTp0/H6NGj1TLGo0ePonPnzrCwsIBcLsfhw4dzPeOkOvj06RO2b9+OCRMm5IpGNo+U/PHCQCl9/ezFi1eunOwk+PFjcqIhIDnPwJs3KZLLJCoUMPn0CXRxgUgkgra2NrS1tXOc3QxIfmnGxMSoJEw8fPgQO3bsgIaGBuLj4/Hp06dUbdIKO/wRDQ0NtWkt9PT0VFYh1qhRAx4eHpgxY0auCQNXrlxBcHAwduzYoZL2ITY2FgMHDoS3tzfq1auHEydOICEhAQ0aNMj0xdKnTx+0a9dOpcIzPzN48GD07NkTzZo1w/Hjx2Fvb4/+/ftjxowZWdKaFC5cGFKpFLdu3RKEgXfv3sHHxwd37tzBpk2b0KpVKwwePBh9+vRBYmIiAgICsjTWtGjdujWmTJmC6OhoTJ8+HUOGDMHz58+xdu1aYaUqFothZ2cHOzs7QZ0dEhKCcePGYfPmzbC0tET9+vVhZmaGkJAQHD9+HEuWLBG0boaGhmmaHJycnLB69Wq8ffsWjRs3Rr9+/SASiaCnp4fExER4e3sL40xKSsKiRYswevRooW5Ax44df+mkcfjwYbRv3x46Ojo4d+6c8DmVK1cO69atQ5MmTXD16lVoaGjA2NgYderUQa9evTB9+vR0i1sVKlQIgwYNwuTJk9GmTZuUph+FAnj2DAgNBeTyZA1nkSLJidXS4caNG2jWrBnMzMwQERGB4ODgDM1JfxLr1q0DSbU65+aRPn98NMGnhARYXbqUUiDYvTs58dCnT8C+fYCXF+DiknzMxwfQ00sWArp1S/5/s2bJGQi3bk3WGixZksJMgLZtYSsWo0mTJvDx8UHlypV/eV7uT58+wdzcHFu3bk3XxpiYmIjo6OhsaS7S2pQJbdJDR0dHZeHhwYMHWL16NebPn49ixYqlOq6jo5MjE0KTJk2EEDpVXvgTJ07E+PHjce/ePbi6uqJDhw4ICgrCvn370LBhwwzPTUhIgIODA3x8fLB48eJsj/ns2bNo0aIFPnz4ADMzM2zevDlLwlKxYsVQsWJFLF26FFevXkWTJk0gFouxZ88eISEPSYwYMQJTpkzBtGnT0g23zAo7d+5E8+bNcfr0aYSHh6N9+/YoWbIk9u7dm8qR8MWLFxg/fjyCgoJgZWWFkSNHonPnzqm0VMo0zT9rE9JK0+zs7IxLly4hMjISRYoUgbu7O27cuIEHDx4AAM6fPw9/f3/cvXsXPXv2xMSJEzNVwauTpKQkjBo1ClOnToW3tzfWrVuXKp1wUlIS7O3tUadOHSQmJmLjxo1wcnLC27dvYWdnh6CgIFSoUCHN/r9//47ChQvDzc0Nh/ftg2jvXmD5cuDSJSAmJmVjsRgoUABo1Sr5ffdDsqNnz56h4j9asLCwMOzevRtNmjRR9+PIFUiiYMGCKFOmDDZu3Pi7h/Ov5j+TdAgA2j54gG3h4f8vELRqBfxUBVBg82ZAmVf7xQtg8WLg3j1AKk2uTfDXX//vU5CUBJM3bzA6MhLPnz/H7t278ebNG5iamqJhw4Zo2rQpatWq9cvi/K2srNC9e3eMGzcu169FEvHx8WoTLNLKhPgjyhVedrQUnz59gp+fH6ZOnYrOnTtDX18fmpqa6QoXoaGhKFiwIHr16oUZM2aAJMzNzREREYGoqChoa2tn+nzGjh2LGTNm4O3btzmyPZMUJg65XI7KlStj9+7dKk1efn5+CAkJgb+/P7p164YSJUpg165dqbyqSWLs2LEYP348JkyYICS8ycmYS5UqBV1dXZw9exZXrlxBo0aNYGBggEOHDsHV1RVv3rzBxIkTsXr1apiammL48OHo3r17ln8rP6dpVm537txJUelTT08PHh4e+PTpEx49egQ3NzfMnDlTiEj4Vbx79w6tW7fGhQsXMHnyZAwaNChd4XT48OFYsmQJ3r17h8uXL6NXr1548uQJLCwsEBYWhmHDhmHMmDFpmvf27duHdY0bY6OhIbS+fUsuopWRP5NEApBAly7AzJn4GBcHT09PfPv2DeHh4ViwYIFaTEm/ilOnTqFGjRo4e/bsH5sV8d/Cf0oYuBoZiXKZlabNJvmXLMHLbdtQqVIlDB8+HGZmZtizZw927dqFR48eQVdXF/Xr14ePjw+8vb1z9f5r1aoFAwMD7Ny5M9eukVsoFApMmzZNCB/T1dXNkWARGxub4fWkUmm6wsPNmzcRFhaGv/76C2ZmZvj+/TsmTpwIFxcXLFq0KM1zftYEvX//Hg4ODpg2bZpa1O/h4eFo3LgxLl26BKlUilGjRmHUqFEZTmQzZ87EsGHDhBwDS5cuzbCI1oQJEzB69GiMGTMGY8aMydEkeejQIXh7e+PIkSOoU6cOnj9/Dm9vb4SFhaF69eo4cOAADAwMMHToUPTq1Uvtzmhr1qxBly5dIJFIkJSUhNKlS+P27dtCdIPS5KCnp5eq2JObmxtcXFzU7kNz4sQJtGnTBjKZDFu3bs00H8PTp0/h4uKCjRs3ok2bNkhISMDs2bMxfvx4SKVSxMTEoGjRotiwYUPKbJbR0cmT+rZtUCA534XKSCRQmJujh5ERdoSFISIiAoMGDcKMGTOyc8u/jRYtWuD+/fu4d+/eHx36+G/gPyUMAIB/SAiWvnuX9TDD9JDLIb14ESvs7GBiYoLJkyfjypUrKFmyJIYPHw4fHx88fvwYu3fvxu7du3H9+nUhtMnHxweNGzdWi1/Bj/Tv3x+HDx/G47QcH/8FREVFCeV3c/rySUpKQnR0NB4+fAgvLy/07t0bDRo0yFSIeP36Na5duwYbGxtIpVJERUUhIiIiRWGptNDS0kolIDx79gyRkZFo2bJllpw6dXV1010tHjp0CO3atcPXr19hbW2NXbt2oVy5cqnaRUREoE6dOrh69SoCAwMxefJklV6K06ZNQ2BgIIYPH46JEydm+0VKEpUqVUJCQgKuXr2K8PBwjBs3DkuXLoVCoYCvry9WrVqVaV7/7NKgQQMhTO/kyZMAklPSTp48GUZGRnj58mWalSG/fv0KINk518nJKVWUg5ubW7oVRNNDLpdjwoQJGD9+PGrVqoX169er/NuvWrUqJBKJcA8A8PLlS/Tv3x979+6Frq4uEhISMHnyZAQEBEDy/TtQqxZw/XrGmoCMxovkUu1NpVLoN2uGTZs2/asc8MLCwmBnZ4fZs2ejT58+v3s4/3r+c8JAdFISil+/jtdxcRmXp1UBCQATqRTVgoKwbcUK+Pj4YOnSpbh79y4mTZqE4OBgFCpUCMOGDUOrVq0gk8nw+vVrQWNw7tw5AMmZ0Zo2bQofHx+1lNRcuXIlevTogZiYmD8yBbEqDB06FEuWLMGbN2/Uku2tb9++2LhxI169epVuWJkSuVyOMmXKQCKR4MqVK8ILsFChQoK/gba2tsraidevX+PMmTNwdXWFRCJJcSwz4SIjk4ienh6uXr2K27dvgySKFi2KgQMHCkWjvnz5goCAAMGJdPPmzWjVqpXKz2z27NkYOHAgBg0ahOnTp2dbIAgODkb16tXh4+ODo0ePQiqVom/fvnj69Cm2bNmCCRMmYMSIEWpfuX379g1mZmYoVaoUrly5Am1tbcTGxmLhwoXw9/dP9zySQprmn5MrvXr1SijFbWFhkaYDo729fapJ88OHD2jbti2Cg4Mxbtw4DB8+PEsT6/r16+Hn54dnz56lSki1f/9+9OnTB6GhoZDL5fCsWBHHxGLoXLqUbUFAiRyAXCQCr16FZjYrbf4uJk+ejIkTJ+Ldu3e/LDz0v8x/ThgAkgvQeP79Nz4kJGRbIJAAMJRKcbZECRTR1cXOnTvRo0cPIaFLgwYNcOnSJUyePBkHDhxA/vz5MXToUHTs2FGYoMPDw7F//37s2rULx48fR0JCAkqWLAkfHx80bdoUhQoVytYL8vLly6hQoQJu3ryZZh7zfwNv376Fo6OjYE/NCeHh4XBwcMDQoUMxZsyYTNuvXLkS3bp1w8WLFwXnrIiICJiYmCB//vx4/vx5lsdQtmxZGBsbp0gARBKxsbE59rGIiIjA58+fMxUsRCIRDA0Ns+Rnce7cOaxevRotWrTAyJEjYWBgIAgoqqjPIyIiMHv2bEyePBkkMXjwYAwZMgQmJiYgiQkTJmDMmDHo2LEjli1bpjaVvFwuR7du3bBmzRro6+sjKioK69atw82bNzF//nzs2bMnUwfQtPj+/TuePHmSSpvw+PHjVGmalcKBQqHA4sWLIRaLsWXLFlSrVi1b17WyskLfvn0xYcKENI9PnjwZ06ZNQ3eFAovS+S7cBzAWwA0AYQB0ABQGMBhAek+DEglEhQsnR1H9guyg6kAul8PZ2Rk1atTAqlWrfvdw/hOoPH9TBb59+0YA/PbtmyrNc5U3sbEsd/06ERyc9e3UKRa5coUhMTEp+nz//j29vb0JgF27dmVkZCRJ8tatW2zRogVFIhGtrKw4a9YsRkdHpzj327dv3LJlC1u2bEk9PT0CoKurK4cOHcorV65QLperfG/K57x+/fqcP6jfSIcOHWhjY8P4+Pgc9TNy5Ejq6ury06dPmbb9+vUrzc3N2a5duxT7g4KCCIBDhw7N1hiU5z98+DBb56vChg0bqKOjQyTn1qKHhwf37t3LPXv20MPDg4ULF+a0adM4cuRI9uvXj507d6avry/r1q1LT09PFitWjI6OjjQzM6OmpqbQT3qbhoYGTU1NmT9/fhYtWpQVK1ZknTp12Lx5c7Zr147ly5enlpYWZTIZS5cuTQAMCAjgiRMneOXKFT548IBv3rzhsmXLKJVKWb16dX79+jXHz+Hq1avC9czNzTl37lyKRCKGh4czKSmJPj4+1NHR4bVr13L+0P9BLpfz+fPnPHToEGfPns3u3buzUqVKKT4PAHR0dGS9evUYEBDA5cuX8+zZs/z48SMVCkWm1+jZsydtbW2ZlJSUbpuQCxcYKxZTkewGmGo7CLAOwLEAlwOcC9Drn7EtS+ccAqRIRM6cqbbnldscPHiQAHj16tXfPZT/DKrO3/8qzYASOYn5oaEY+eIFYv+RpNO7CeX6XCyXQ7R5M0LGjIHjD5UIlZDEypUrERAQAAsLCwQFBQkOQiEhIZg6dSrWr18PQ0ND9OvXD3369EmlwoqLi8PJkyexa9cu7Nu3D58+fYKNjQ18fHxUDll0cHBA69atMXXq1Kw8kj+Ke/fuoWjRoggKCkL79u2z1UdkZCQcHBzQqVMnzJ49O9P2AwYMwPLly/H48WPY2NgI+6tXr47g4GDcv38/yzkDgORyyfb29vD19cXChQuzfL4qxMXFoXv37li/fr2wr0WLFggKCsLEiRNTJfPJjMTEREH7sG7dOowZMwb16tVDp06d0s178fXrVzx8+BAvX76EXC4X/B5iYmIyzcoJJNvobW1tYWJikuVokaSkJCxYsAAbNmxA0aJF8fjxY0yaNAkXL17Ehw8fcOHCBQDJq+jq1avj5cuXuHz5cq6UW/706RPat2+Po0ePYsCAAWjcuHEqjcLz588FbY6JiUkqc4ObmxscHR2FPB3Xr19HmTJlcOjQoVSpoAWmTgVHjEhOla0icgClAMQBeJRRQ2tr4PXr5IiDP5xGjRrh7du3uH79ep7joJr4z2oGfiQyMZFLQkNZ5vp1yk6fTqUJkJ4+zeJXr3L269d8+fkzLSwsUq0cf+bp06f09PSkSCTikCFDGBcXJxx7+fIl/f39qampSX19fQYGBvLDhw9p9pOYmMjTp0+zb9++tLOzIwCamJiwY8eO3LdvH2NjY9M8r379+vT29s7+Q/lDqFevHosVK6bSyiktZsyYQZlMxjdv3mTa9uHDh5RKpZw0aVKK/UlJSdTU1KSRkVG2x0GSo0aNoq6uLiMiIrLdR3q8e/eO5cqVo6amJtevX8+HDx/SxcWFAKijo8NevXoRAN+/f5/ta6xdu5YikYgdO3ZMtTqNjY3lnDlzaGFhQZlMxp49e6Z45gqFgpcuXSIATp48mTdv3uTZs2d58OBBbtmyhStWrGBgYCCNjY2po6NDHx8ftmnThg0bNmTVqlVZqlQpurq60srKStCcZbSJRCICoKWlJUUiEW1sbFi9enU2btyY7dq1Y8eOHWloaEhzc3NOnjyZq1at4rZt23j48GGeP3+et2/f5vPnzxkeHp7it6sK58+fp42NDc3MzHj06NF028XFxfHu3bvcvn07J0yYwLZt27JUqVLU1dVNoX1xd3dn8+bNOWLECNrZ2bF69eqMiopK3aFCQdrYpL+6z2BrANBSlbb792fpWfwOXr16RbFYzOXLl//uofynUHX+/lcLAz+SIJfzdlQUT3z5wuOfP/PvyEjG/aSiX7FiBQHw8uXLGfaVlJTEKVOmUCaTsVixYrx9+3aK4+/fv+eQIUOop6dHLS0t9unTh69fv063P4VCwWvXrnH48OF0c3MjAOrq6rJ58+bctGlTiklmyJAhzJ8/fzaewJ/FyZMnCSDDl2p6xMXF0crKip07d860rUKhYN26denk5JRKwDp79iwBsFWrVlkew4+8ffuWUqmUc+fOzVE/P3P16lVaW1vT2tqaV65cSXFs8eLF1NLSEiaX1atX5+haGzdupFgsZrt27ZiYmMi4uDguWrSI1tbWlEgk7Ny5M1+8eJHu+b6+vrS3t093gv348SMrVKhALS0t7ty5M91+5HI5IyMjefjwYRYrVowAWLt2ba5bt44bNmxgyZIlaWNjw7Zt2xIAfXx82KJFC9arV4+VKlVi8eLFaWdnR5FIRLFYnKlwIZPJaGJiQgcHB7q7u7NChQqsXbs2mzVrxo4dO7JPnz4cNmwY69WrR5FIRBcXF65YsYLHjx/n5cuXef/+fb5+/Zpfv35lYmJihs9YLpfz9evXPHbsGOfPn8+//vqL1apVo5WVVYox2dnZsVatWuzbty8XL17Mi+vXqzz5RwMMB/gU4GyAEoBtMjtPJiMDAlT6nvxORo0aRX19/bQFpjyyzf+cMKAKSUlJLF68OMuXL6/SSvHmzZt0d3enhoYGp02blmpV9fnzZ44bN44mJiaUyWTs0qULnzx5kmm/Dx484KRJkwT7qEwmY7169bh8+XIuXLiQAAS/hX8rCoWCJUuWZM2aNbN87rJlyygSifjo0aNM2+7fv58AuHv37lTHOnToQAA8ceJElsfwM61atWKBAgWy5AOSERs3bqSWlhbLli3Lt2/fptnm+/fvbNKkiTCJdO3aNdMJKSO2bt1KsVjMMmXK0N7eniKRiO3atWNISEim5z548IBisZgLFixIt83379/p6+tLkUjEmTNnpvkb+/LlC3v16kWRSMSiRYvy7NmzKc7X1dXlpEmT2K9fP9ra2qb7Oz179iw1NDTYpk0bfv78ma9eveK9e/d46dIlHjt2jDt37uTatWu5YMECTp48mcOGDWPv3r3ZoUMHNm3alLVq1WL58uXp5uZGbW1tAlBJuNDW1qaFhQWdnZ3p4eFBLy8v1q9fny1btmTXrl0ZEBDA0aNHc8aMGVy6dCk3btzIffv2cdOmTZRIJKxfvz79/f3p7e3NggULUiqVskUWNAE9fhiLGGBzgF9UOdfTU4VvyO8jISGBVlZW/Ouvv373UP5z5AkD6XDq1CkC4KZNm1RqHxsby0GDBlEkEtHLy4vPnz9P1SYyMpIzZsxgvnz5KBaL2apVK965c0el/l+9esV58+axatWqKV5G/fr1y3Cl9m9g06ZNBMCbN2+qfE5SUhKdnZ3ZrFmzTNvGx8ezQIECrFmzZpqThrm5OTU0NJiQkJCVYafJxYsXCYAHDx7MUT9JSUkcOnQoAbB9+/bpmot+pHjx4oJDm4GBAXfs2JHl6yYmJnLdunW0tLQkANra2qbSeGWGn58f8+XLx5ifHHB/RC6XMzAwkADYs2dPQXiRy+Vcs2YNzc3Nqa+vzzlz5qQSbHbv3k0AfPToEZ2dndmzZ88Mx7NlyxYC4MiRI7N0H0quXLlCBwcHGhsbc//+/VQoFIyNjeXHjx/5/Plz3r59m+fPn+fhw4e5bds2rlq1inPnzuWECRM4ZMgQ/vXXX2zXrh0bN27M6tWrs0yZMnRzc6ONjQ0NDAwEk0dGJhE9PT1O0tFhgorCwEOAxwGuA+gN0AdgmCrnmpll6xn9Knbu3EkAvHXr1u8eyn+OPGEgA5o0aUI7Ozt+//5d5XPOnDlDBwcH6unpceXKlWlOPrGxsVy0aBEdHBwIgI0aNcrUJPEjHz9+5JIlSwiAEomEAFiiRAlOmDCB9+7dy5Hd+3eQmJhIe3t7tm3bVuVzlC94VTzGp0+fTolEwnv37qU69uLFCwKgl5dXlsacHgqFgqVKlWKdOnWy3UdERAS9vb0pFovTXTmnRe/evenm5sapU6dSJpMRAEuVKsXQ0NBMz5XL5dy0aRMLFixIAGzSpAnnzZtHDQ0NNm7cOEt29WfPnlEqlXL69OmZtl2+fDklEgnr1avH8+fPs2LFigTANm3a8N27d2me07ZtW7q7u/PRo0cEwP0q2LmnTp1KAFy1apXK96FQKDhv3jzKZDKWK1eOL1++VPncrKBQKBgdHc33798zJCSEixYtIgDOmDGDmzZt4rJlyzhz5kyerlyZiWJxtnwGagEsA6QbhSBsBga5co/qonbt2qxQocLvHsZ/kjxhIAOePHlCmUzGCRMmZOm8b9++sXPnzgTAhg0bMiwsLM12CQkJXLt2rfACrlGjBk+dOqXyy9/FxYX+/v7phixevnxZberq3GbOnDmUSCQZ+lQoUSgU9PDwUMm08P79e+rr67NPnz5pHh8/fjwBcM2aNVkdcrqsW7dOWLlmlZCQELq5udHQ0JCHDx/O0rkrVqygWCxmTEwMv379yho1aghq7YCAgDS/C3K5nDt27GCRIkUIgPXr1+f169eF44cOHaKmpia9vb1V0k4o6dGjB01MTFR6F+zevZsaGhrCdzc4ODjdtnFxcTQwMODYsWM5c+ZMamlpZaiBUKJQKNijRw9KpVIeO3Ys0/YRERFs1qwZAbB///45Dn/NCnK5nA4ODuzatWvKA5Mnk1JptoSBZf9oGR5l1tbC4pfdZ1Z5+vQpAXDdunW/eyj/SfKEgUwYOHAgdXR00rXXZsSePXtobm5OMzMz7tq1K912SUlJ3LZtGz08PAiAFSpU4IEDBzIVCnx8fFirVi3h79jYWB44cIBdunShmZkZAdDGxob+/v48efJkjuzIuU1kZCSNjIw4YMCATNsePnyYAHjy5MlM23bs2JGmpqb88uVLmscLFSpEkUik1giAuLg4mpubs3fv3lk679ixYzQyMqKrq2u2BIlr164RQAonw+DgYFpYWBAATU1NBUdNhULBffv2Cd+5WrVq8eLFi2n2e/ToUWppabFOnToqa8nevHlDTU1Njhs3Lt02CoWCQUFBtLS0pI6ODg0NDWllZZWhuejAgQMEwLt377JatWqsX7++SuMhkzVQ9erVo76+foamjxs3btDJyYmGhoYZ/m5zk7Fjx1JPT48RERG8c+cOV61axSW1amVLECCS8w0A4JXM2lat+lvuVxWGDBlCY2PjLGlq81CdPGEgE75+/UozMzN26NAhW+d/+PCBjRs3JgB27Ngxw2ejUCh48OBBQVVavHhxbt26Nd0kJCNHjqSVlVWax5Qhi/369UsVsrh3794/8gcVGBhIPT29TBPTVKlShWXLls1UWLp69SoBcMmSJWkej4qKokgkoqura3aHnC4jR46knp6eSr8FhULBOXPmUCwWs06dOtlOzPP9+3dKJBIuW7YsxX6lfV5pUnJ3d2fJkiUJgJUrV+aZM2cy7fvkyZPU0dFhjRo1VFqJk2RAQAANDAz4+fPnVMfu3LlDLy8vAmDLli0ZGhrKd+/esWTJktTV1U3X56Jjx44sWLAgv379SqlUykWLFqk0FiWRkZEsUaIEbWxsUplPFAoFlyxZQg0NDZYqVYrPnj3LUt85RaFQ8MWLF9y2bRu7d+9OAEJyKJFIxGrOzplO+h/S2JcAsCRAbYBRGZ0vlZJDhvzSe1aVuLg4mpmZMeBfEO3wbyVPGFABpX3+R/VpVlAoFFy9ejX19fXp4OCQoRpU2T44OJi1atUiABYsWJBr1qxJ5eCmtJtnlnlPoVDw+vXrHD58OAsVKsQfQxY3btyYK3Hx2eHdu3eUyWScNm1aum2UDnqZrdjkcjnLly/PYsWKpStMbdy4kQA4evToHI07LUJDQymVSjl//vwM28XFxbFTp04EwIEDB2aYfU4VihQpkq6n9Y4dO6ivr0+lY5qfn1+WzEhnzpyhrq4uq1SpolJY14cPH6irq5siq+O3b98YEBBAiURCNze3VBEc0dHRbNiwIcVicaqJPiEhgcbGxhw+fDi3bdtGANmy4799+5Z2dnYsXry4EI0TGRnJVq1aEQD9/f2znHsgO4SHh/PQoUMcN24cvb29aW5uLnw2Dg4OtLS0pJ2dHQMDA1m7dm1KxGKGAJRnMKE3AVgdyRkIVwCcANDtnz5nqaJFUENETW6gdDLOjsYsD9XIEwZUIDExke7u7qxUqVKOnPNevHjBypUrUyQSccCAASrZYK9cuSJoFuzt7blw4UJhVX/v3j0CUGll9yMPHz7k5MmTU4Qs1q1bl8uXL083OdKvolOnTrS2tk7XRtuwYUO6ubllOokp0wOfPn063TbVq1fP9oSiCi1atKCLi0u6Y33//j0rVKhADQ0NtdlB27Ztm8rB6vz586xWrRqVDoWjR4+moaEhAdDa2poXLlxQuf8LFy5QX1+fnp6eKv3Ohw8fTm1tbb57944bN25kvnz5qKOjw6lTp6b7GSclJbFv374EwAEDBggC0tGjRwmAf//9N/38/Oju7q7yuH/mzp07NDAwYN26dXn9+nW6urpSX1+fW7ZsyXafGRETE8Nz585x1qxZbNWqFZ2cnISJ38TEhHXr1uWoUaO4f/9+Pn78mGvXrhW0N0rhHQBHGhhk6AS4GWBNJCcYkgI0/ufvvZkJASIR6eycnNjoD6Ry5cqsVq3a7x7Gf5o8YUBFjh07RgDctm1bjvpJSkrijBkzqKGhwSJFivDvv/9W6bw7d+6wTZs2FIvFtLS05PTp0/np0yfKZDIuXrw42+P5OWRRGRo5e/bs3xKyeP/+fQLg2rVrUx27e/cuVXH2i4yMpJWVFX19fdNtI5fLqa2tTXNz85wOOV3Onz9PAGk6Al6/fp22trbMly8fL126pLZrzpgxg7q6upTL5bxy5Qrr1KlDACxWrBj37NkjCLOJiYns1auXEKZar149lXNWXL58mYaGhixfvnymWqUvX75QT0+PNjY2BMBmzZqp5CRKkvPmzaNYLKaPjw9jYmLYvXt3Ojo6MjExkebm5gwMDFSpn/Q4duwYxWIxJRIJixUrxsePH+eoPyWJiYm8desWly9fzq5du7J48eKCiUZLS4uenp4MCAjgpk2b+PTpUyoUCkZERHDdunVs0KCBEAmi9PUQiURs0KAB9+/fz6TPn0lj4+TJW5WVfla2PzSjn/KdsHXr1t89lP80ecJAFmjQoAHz58+fJa/q9Lhz5w6LFy9OmUzGSZMmqezc9+TJE3bt2pUymYzGxsY0NzdXKQOfKnz8+JGrVq1igwYNBO/uEiVKcPz48b80ZNHb25vu7u6prteuXTva2dll6tk9bNgwamlpZbjiv3z5MpUx/LmFQqFgiRIlWK9evRT7N2/eTC0tLZYuXVqlsL+scPz4cQIQtB6FChXitm3b0tVOPH/+nEWLFiWQnBp39uzZKn3O169fp7GxMUuXLp2uc2ZkZCQHDRokCBzZKay1b98+6ujosHTp0jQ1NeXgwYOFtMfnzp3Lcn9KoqOj6efnJ6y8M3J0zAiFQsFnz55x8+bNDAgIoKenZ4rkRMWKFWPXrl25fPly3rx5M4WpLyIigkFBQSl+b87OzoKPj42NDcuVK0dTU9OUJsKtW9UrBEilpJcX+YdGHvXt25cWFha/NKLjf5E8YSALPHr0iFKplFOmTFFLf3FxcQwMDKRYLGaFChVUykqo5PXr1+zbty8lEgklEgkHDx6co7z0PxMZGcmtW7emCFl0cXH5JSGLwcHBqVbUL168oEQiyTTV79OnT6mhoZGpH0DHjh0JQK2r8rRYs2YNATAkJIRyuZzDhw8nALZt21btTpz37t1jgwYNCIBWVlbcsGGDyj4IQUFBgio6f/78KiV1uXnzJk1NTenh4ZHCb0WhUHDr1q20sbGhtrY2R48eTVNTU3br1i1b96UUPABw8+bNHDlyJE1MTLIdHXP//n0WLlyYOjo6XL9+PUePHk0A3LhxY6bnfvjwgQcOHODo0aNZt25dmpqaCgKFo6MjW7ZsyVmzZvHs2bOpKpeSye/I9evXs2HDhoIAUKxYMVasWJF6enoUiUT09vbm3r17mZiYyDt37jCVj4xCQbZpQ0U2cw6k2CQS0tCQ/MXOkqoSExNDQ0PDHGuB8sicPGEgi/Tv3596enpqnXjPnz9PJycn6urqcunSpVlagQ8dOpRaWlo0MDCgpqYme/XqpXYb+K8OWVQoFCxdujSrV68u7PP396epqWmaL9gfady4Me3s7DL1eLewsKCWllau52GIjY2lmZkZe/TowYYNG1IkEnHatGlq1bI8evSIrVu3pkgkoqOjI42MjLL18oyLi2O7du2EjHi+vr6ZPsc7d+7Q3NycRYsW5cePH/nw4UPWrFmTysRFSlPTzJkzKZFIsiTw/oifnx9lMhkNDAxYoEABtmnTJlv9BAUFUUdHh4ULF+b9+/dJJn/f2rdvTw0NjRT+N1FRUTxz5gxnzJhBX19fIUkYAJqZmbF+/focO3YsDx06xI8fP6Z7TaUA0KhRI0EAKFu2LFu2bMnixYsLvhujRo3iq1evUp1fpkyZ1EXJ4uN539ExQ2dClQQBfX3yDy4DvHr1aopEol8e2fG/SJ4wkEW+fPlCExMTdunSRa39RkVFsVu3blQmfkkv+9rP/JiadcKECTQ1NaVUKmXHjh1zxfP2V4UsKiMlbty4wbCwMGppaXH8+PEZnqP068jMCSw0NJQAfplD0l9//UWxWEwDA4Mcpyn+kadPn9LPz49isZi2trZctmwZ4+PjWb9+/SzF3//MvXv36OzsTCC5IuKKFSsybH///n1aWFjQ1NSUMpmMTk5OPHDgQIo2379/p5WVVabVQNNCLpfTysqKvXr1YpUqVQiA3bt3z1If379/Z9euXQmAHTp0SCVURkdHs3Tp0tTR0WHTpk3p7u4umDd0dHTo5eXFgQMHcuvWrXzx4kWmwty3b9+4YcMGNmrUSAgPLF++PAcOHEg/Pz8hDXG9evW4e/fuDAXqpUuXUiwWpzApLVq0iFKAS3R0KAeokEiyJgiIRGSRImQaWTn/JMqWLcu6dev+7mH8T5AnDGSDBQsWUCQSqez8lxUOHDhAS0tLmpqacvv27Zm2f/LkCQHw+PHjJJNfarNnz6a1tTVFIhF9fX2zlPM/K+RmyGJiYiLz58/P1q1bc/jw4dTT00szXl1JQkICCxcuTC8vr0xf1OPGjSOget2JnHDixAnBc3/EiBFq6fPly5fs2rUrpVIpraysuGDBghShcMOHD6e1tXWOrqFQKLhw4UJhIitUqFCaDnYKhYI7d+4UKu6Zmpqmu4pbtGgRRSJRmmmhM0LpiHnu3DkuXrxYWJ2PGDFCJQ3L48ePWaxYMWppaXH16tVUKBQMCQnhxo0b2a9fP6GKorJfDQ0Ntm/fnitXruSdO3dU1nxFRkZy48aNbNy4cQoBYMqUKZwxYwbLlStHAMyXLx9HjBihsoNuREQEtbW1hdLbBw4cEARAY2NjvtywgXR3/3/7fzoCgEIkShYCdHXJsWPJP9wG//fffxMA9+zZ87uH8j9BnjCQDRISElioUCFWqVIlV5zqwsPDhVSobdu2zTAJTVJSErW0tDhnzpwU++Pi4rhs2TI6OjoSAL29vdPNMKculCGLZcqU4c8hi+mlZM6IefPmUSKRUF9fnwMHDsy0raoCWuHChSkWi9XiCJoeyrz2EomEtWrVYuPGjVmwYMEcmSVCQ0PZq1cvymQympubc9asWWlqYpQx+OoIE42MjGSjRo2o9Grv3Lmz4MgVEhIiRCt4e3vz5MmTtLOzo4uLC9+8eZOqr/j4eDo4OKhUXOpHAgICaGVlRblczkaNGrFSpUqcNm0aAbB169YZfo5btmyhjo4Ora2t2a1bN9auXVvwPwDAAgUKsHXr1pwzZw4vXLjAhw8fMl++fCxbtqxKyZWUAkCTJk0EAaBcuXKcNWsWjx49Sn9/fxoaGlIkErFOnTrctWtXtgpi+fn50dnZmdeuXaOuri5tbW2poaHx/9UcFQrywgWyU6fkEMGfog0iAEaVK0cuW0b+S0r/9ujRgzY2Nn905tT/EnnCQDZRpsTNrXSlylStBgYGtLW1zbC8bsmSJdM1WyQmJnL9+vUsXLgwAbBq1ao8fvx4rkcGvH79mvPnz89RyGJUVBS1tbVTqUh/5uPHjzQyMlJJdRwbG0uxWMzChQureitZJi4ujl26dCEABgQEMDExkWfPniUAHjlyJMv9hYWFsX///tTU1KSJiQmnTJmSYdKfkJAQAlApB7+qXL58WQgP1NPTY9OmTamhocH8+fNz3759Qrvnz5/TwcGBTk5OafqurF69WjD/qIJCoaC9vT179erF2NhYIUcBmSz0aGpqslKlSgwPDyeZ/A46deoUJ02alCKWXxmq16BBA44fP55HjhxJN1nX9evXqaOjwyZNmqTpgBkZGclNmzalEADKli3LmTNn8uHDh1yzZg0rVKhAALS0tOTw4cPTrGKaFc6cOSOY5JRamAxD7SIjycePyfv3+e2f0LzsRHP8LiIjI6mnp8exY8f+7qH8z5AnDOSAunXr0snJKVezlb169UpIGNOvX780V4J+fn4sX758hv3I5XLu2rWLpUqVEl5ee/fu/SWFjLIbshgbG0tdXV1KpdJ0w9dIsmfPnjQ0NMzQiUvJ5s2bCSDLxadUJSwsjJ6entTQ0ODq1auF/criSqkcwTIgPDycQ4YMoY6ODg0MDDhu3DiVfltyuZy6uroqVQ3MCnK5nG3bthUm13z58qXpl/Ly5Us6OTnRwcEhlckgMTGRrq6uKvs0KFNKnzp1ShDAlWaG+Ph4rlq1irq6utTX12eBAgUE50elAFqrVi1u27aNr169ypIAvH//forFYvbr149ksmC6efNm+vj4CCaFsmXLcsaMGXzx4gXv3bvHPn360MjIiEByrYcdO3aopSw2mWwq0NDQEH4/M2fOzNL5Tk5OmWrX/iSWLFlCiUSi9tDbPNInTxjIAffv36dEIlH7S/dn5HI558yZQ01NTbq5uaUq2zt9+nTq6emp9LJTKBQ8cuSIkBe+aNGi3LRpU47T4KqKMmSxVatWQmrc9EIWlWmgZTJZuuGct27dolgsTmUmSQ9l/H1uZFr8+++/aWdnR0tLyzRNMqtWraJIJMrUo/7Lly9CbQM9PT2OGDEiQ2EoLSpWrJilktCZ8fTpU3p7exNIrq6ptH+LxWL2798/lSr3zZs3dHFxoa2tbar7VQpkysyHCXI5b0ZGctW7d5z66hWnvHzJ5W/f8tq3bxwQGEgzMzPGx8ezbdu2NDU1pb+/P8uVKydMjFKplJqamtTU1GTTpk2pp6dHR0dHlbUP6TFr1iwqQ/+UAkCZMmUEAeD79+9ct26dUEvEwsKCgYGBavd8T0hIYO3atYVkRN27d8+yZq9p06asUaOGWseVWygUChYvXpxNmjT53UP5nyJPGMghvXv3poGBwS9J43v//n2WLFmSUqmU48aNE17ABw8eJJD1tLpnz55l3bp1BdvpypUrf2lij7i4OB48eDDNkMWjR4/SycmJvr6+7Nq1K62srFJpYBQKBStXrsxChQqptAJTKBTU1tZmvnz51H4vW7dupba2NkuVKpWmvZxM9mg3NTVl//790zz+7ds3jh8/noaGhtTW1ubgwYMF9XdW6dWrF4sUKZKtc3/k+/fvHDNmDDU1NWlvb89du3YJE9Hx48eFz83ExISHDh1Kce7bt2/p5uZGa2vrFBoEuVzOYsWKsXTr1uzy8CG1zpwhgoOJ4GBKT5+m9PRpiv75G8ePU2viROr+I3wAyWWO27Vrx/nz5/PSpUuMjY3l+/fvBTNG2bJls+28GhUVxS1btrBp06YpnAr9/PwEVf/9+/fZr18/wfegRo0a3LZtW678dhQKheAsqqGhQZFIxIULF2a5n/Hjx9PU1PSXJQ7LCcqkUtkxqeWRffKEgRzy6dMnGhkZsUePHr/kevHx8Rw5ciTFYjHLli3Lx48f89WrVwSQKpxLVa5fv86mTZsSAG1tbTlv3jyVK9Opi7RCFgGwQYMGXLhwIQGkULuTyRNwVl4ayhK/6srYSCZPbCNHjiQAtmrVKtPnFhgYSAMDgxQ2/+joaE6dOpUmJibU1NRk//79c5zHYtmyZZRIJDkK9Txw4ACdnJwok8k4fPjwNHM8JCUlcfDgwUIYXqVKlVIIxmFhYSxSpAgtLS2FuP5PCQn0OnaMCA6m5NQpQRBIdztxIvnfKVO4JI0Im5cvX7Js2bKUyWSC8+rEiRNVnviio6O5detWNmvWTMgeWLp0aU6fPp1Pnz6lr68vNTU1OWbMGFaqVIkAaG5uziFDhmQ7b4KqTJ48mQCor6/P8uXLs379+ixVqlSW+9m3bx8BpCuo/kl06NCBjo6Ov8SEmcf/kycMqAFl+dmMaqSrm0uXLrFAgQLU1tbmggULqK+vn2G1P1W4f/8+27dvT4lEQnNzc06ZMuW3fJZyuZwuLi50cnISQhYlEgkNDAy4fv16RkREMCYmhvb29mzYsKHK/Xbo0IEAVMqupwqRkZFs3LgxRSIRp0yZotLk8+rVK6Ei3/fv3zlr1iyam5tTJpOxV69earORXrlyhQBSmZRU4cWLF0JxrFq1aqmUryI0NFSYiKVSKUeNGiW8zD9+/MhixYrR3Nycq65coWFwMEWqCAE/b8ePU+/MGR7+wfFv//79NDY2poODA69cuUKFQsGxY8cSADt16pTuaj06Oprbtm1j8+bNBQGgVKlSnDZtWgo1/8OHD9m7d29KpVICYIUKFbhly5ZfUtVQaU4xMTFhgQIF+PHjR+7duzdb3+HXr18TAPfv359Lo1UPnz9/ppaWltqyvOahOnnCgBqIj4+nq6sra9So8UvVcNHR0fzrr78IgIaGhlkO2UqPZ8+esUePHtTQ0KChoSFHjhyZbXV1dlCaPZSlnh8+fCgkZFL6EBQoUIBSqTRL6YQtLS2pq6urls/o2bNnLFKkCPX19VN406tCkyZNaGlpSSsrK0okEnbt2lXtWSNjYmIoFoszTRj0I7GxsZwwYQK1tLRoY2PD7du3Z/lZ7dy5kwYGBoInfVBQENeuXcvOnTtTs0wZ4siR/1/pZ2MT/2NK2B0WxsGDBxMAGzZsmCoHRVBQEGUyGatXry6E5ioFAF9fX+ro6AgCwNSpU1MIALGxsdy4cSMrV65MIDnboL+/P+3t7VmwYMEM812oi3PnzlFDQ4Pm5uY0NTUVNBCJiYnMly8f+/Tpk6X+FAoFTUxMcs1xVl3MmTOHMpksW6HIeeSMPGFATezfv58AsjwxqIPDhw9TW1ubEomEmzdvVlu/oaGhHDBgAHV0dKijo8MBAwbw7du3aus/Pby8vFiuXLkUE5FCoWDZsmVZoUIFjh07VlBLi0QiVqpUKdOQxQ8fPggr3Zxy6tQpmpiY0NnZWVB9q0JCQgKXLVsmVKOrWbMmnz59muPxpEehQoXo7++vUtvDhw8LAtaQIUMyDF1MC4VCwdevX3PHjh0cNGgQra2tU4T1OZYtS+nRozkSBJSbKDiYouPHKXZ05MyZM9MVWIKDg2loaEhbW1t6e3sLAkDJkiU5derUVM/+0aNHHDBggFBvoGrVqty8ebOgBXjy5AlNTU3p5eWVqzkqQkJCaGJiQnNzc2ppafHy5cspjg8dOpTGxsZZHkP16tXVtmDIDRQKBQsWLMiWLVv+7qH8T5InDKgJhULBWrVq0cXF5bdU15o0aZJQJrVVq1ZqXb2Eh4dz5MiRNDQ0pIaGBnv06JHjuOn0UGabSyvrmDKZTq1atZgvXz6+ePGCq1evZoMGDYR47/RCFpVZB3OSF0KZlU8ikbBGjRoqP+PExESuWbOGjo6OFIlEbNWqFV1dXbNk4sgOrVu3pqenZ4ZtXr16JfiLVKtWjQ8ePFCp7y9fvvDo0aOcOHEiGzVqxHz58gkTv62tLX18fDho0CAhn79oxgyKMzINLF1KlClD6OgQ2tpE6dLEihUZ+hG4nT7NpDQEgZiYGO7YsYMtWrQQTABSqZT+/v6pBIC4uDhu2rSJVatWJZCcQXHgwIHpmkYuXLhATU1NtmrVKlds2h8/fqSzszONjY0pEonS/B08fvyYALIs+A8YMIDOzs7qGqraOX36dAqNYB6/ljxhQI3cvXuXYrGYs2fP/uXXPnHiBAFw1qxZNDIyorW1tdq9cb99+8YpU6bQ3NycEomE7du3V3nyUBVvb28WLlw4zRdtUlKSkHBl7dq1KY6lF7I4ZMgQXr58mYULF6ZEIsl2NrP4+Hh2796dANi3b1+V+klKSuLGjRvp6upKAGzatCnv3r1LklyxYkWuF2CZNm0a9fT00nyW8fHxnDJlCnV0dGhlZcXNmzenu8L+/v07L168yLlz57Jt27Z0cXERJn5DQ0PWqlWLw4cP5549e9LUHPXdvDnj1f6yZYSGBmFrS/z1F9GjB5EvH6GrS6xbl+G5K/+p4aEUAFq2bClUXyxRogQnT57My5cvs3z58tTW1haEwZCQEA4aNEiIhqhcuTI3btyo0mp7+/btBMBhw4Zl5ePIlNjYWKF6IYAMowa8vLxYs2bNLPUfFBT0R7+fW7VqxYIFC/4rIh7+i+QJA2pGmQDnV9rYyWSvbeXK982bN6xVqxYB0N/fX+2RATExMZw3bx5tbW0pEonYrFmzHMd0k+Tt27cJgOvWrUvzeFJSkhA+ltEkqgxZ7Nq1q/CyV676Tpw4keVEMB8+fKCXlxdlMhlXrlyZaXu5XM7t27cLWR8bNGiQKk1yTEwMTUxMOGDAgCyNJSscPXqUAFKtho8fP86CBQtSIpFwwIABKX6vSUlJvHv3LletWsUePXoIoawAqKmpyfLly7Nv375cv349Hz9+rNLquMbNm5RkJAyUK0fo6xN79vz/vh07kjUEXl4Z+g/YHz/Olq1aCQKAh4cHJ0+enMrL//v374IGpECBAoJjXkBAQLYE2hkzZhAAly1bluVz00Iul7NFixZC7oTBgwdn2H7t2rUEoHI2T5JCOeRz587lcLTq58OHD5TJZCrnC8lD/eQJA2rm48ePNDAwUNlWqy4UCgVNTU2Fyn5yuZwLFiygtrY2XV1dU9kd1UF8fDxXrFghVLirW7dujl40bdq0ob29fbqT9dKlSwmABgYG7Nu3r0p9JiUlcdSoUQQgZIczNjZmhw4duGfPnkxD727evEl7e3taWFjw/PnzGbZVKBTcu3evUJa2du3aGT73IUOG0NDQMMv2eVVRCog7duwgmZwIyNfXV1gJ37lzhy9fvuS2bds4aNAgVqlSRZhURSIRixQpwk6dOnHJkiW8fv16tsxfL2NjM/cD0NEhqlRJvb98eUImIw4dyvB8F29vTpo0iSEhIWmO4cmTJxwyZAjNzc0FwbBGjRo5eu4KhYK9evWiRCJJlV8hOwwdOpQikYhSqVQlE0R0dDT19fU5evRola+RkJBADQ0NLliwIKfDVTtTp06llpbWL3HOzCNt8oSBXEBZuz2r1dlySpUqVdiiRYsU+x49esQyZcpQIpFw1KhRakuP+iOJiYnctGkT3d3dCYBeXl48cuRIltR9z549o1gs5vz589M8/uXLF5qamtLPz48jR46krq6uyi8OZdbBr1+/8vr16xwxYoSwatfR0WGzZs3SrLK4fft26ujosESJEmnWmVeiUCh4+PBhIbSuatWqKglFL1++pFgs5pIlS1S6j+yQL18+Dhs2jNOnT6eOjg6NjIzYtGlTent7C46MAGhvb8/mzZtz+vTpDA4OZmRkpFquvyksLHNhQCYjatdOvf+fcsVYtChDZ8J56RRF2rZtG2vUqCEIgv369eP9+/eFHAz169fP0X0mJiayQYMG1NPTy1Fl0GXLlgnfxcqVK6sctti9e3fa2dllKXtoRnVMfhdyuZyOjo7s0KHD7x7K/zR5wkAuEB8fzwIFCrB27dq/1P7l7++fZta5hIQEjh07lhKJhKVKlVK7nV+JXC7n3r17WbZsWSFsa9euXSqpkv/66y+am5una9Lo168f9fT0+O7dO4aFhVFTU1Mo6ZoZOjo6tLGxSbX/0aNHnDJlSqoqi0uWLOGAAQMIgC1atMjQzHLy5EkhHW3FihV58uRJlcakxMfHh4ULF1b79yQmJobnz5+nnZ2doOZXbsbGxqxTpw5HjRrF/fv352oY18AnTyg7fTpjYcDJibCzSxlpcOwYYWmZPOaxY9M9V3r6NP1++D4/ffqUQ4cOFQQdT09PBgUFpdIAHTlyhPr6+vTw8MhRbofo6GiWKlWK1tbWfP36dZbPP3z4sJBDw83NLUtpp5W5JLLiG9S5c+dsJS3KTY4cOUIAWQoTzkP95AkDucTu3bsJgAcPHvxl11yyZAmlUmm66tyrV6+yYMGC1NLS4ty5c3Mtw5dCoeDx48cFD+3ChQtz/fr16TrdvX//npqampw4cWKax5U1IJTV6sjkVZGlpWWmDl83btwggEwrGiqrLCprNgCgg4MDZ86cmWbkxNmzZ4X7K1OmTJY1IUqCg4MJIMOqlJmRmJjI27dvc8WKFezWrRuLFy8uRJYoVf5t27blpk2b+OTJk18qoLa4d4/izDQDAQHJY61Th1izhli9mqhWjVAKMcOHZ3i+140b3L59u+AnY2hoyD59+gjOmulx+/Zt2tra0sbGJkeJqN6/f08HBwe6u7tnKQ3yrVu3qKenR0NDQ1paWmY514RCoaC7uzt9fX1VPmf+/PnU1NTMFQ1hdmnSpAmLFy+e5zj4m8kTBnIJhULBatWqsWDBgr/sh6csk5vRSzAmJoZ9+vQR7KbZWc1khQsXLggFbpycnLhs2bJUatDAwEDq6emluSpShmw6OzunOO/Ro0cUiUSCQ1+8XM4bkZHcHBbGte/fc8uHD7wVFcV2/2QdfPjwYaZjff78OYsWLUpdXV326dMnRciih4cHx48fz40bNwqTjoeHB/ft25ejl5jyhd64cWOV2z979oxbtmzhgAEDWKlSJSF+XiwW093dneXKlaOmpiaNjY2FCIhf7dCqpOndu/9fZyCjrW3b/5/8AaJgQaJdu+T/T5iQ4bmyf3xJKlSowLVr12bJYfbt27csUaIE9fT0cmT7v3fvnhBZocrvPTQ0lDY2NjQ0NKSOjk62HXCVSXpU/XxVeUf8St68eUOJRMLFixf/7qH8z5MnDOQit27dokgkStcOrm4+f/6scvzxsWPHhJfR+vXrc10qv3nzJn19fSkSiWhtbc3Zs2czOjqaERERNDAwSNd7Wpl+de/evamONfTxoXXr1qz699+UpqeKPn6cklmzuC88PM2YdCXBwcE0NTWlo6NjihdlZGQkt23bxtq1awurbQ0NDTZu3JgXL15Ui3Zl+fLlFIlEaWogPn78yAMHDnDMmDGsV6+ekBAHAB0dHdmiRQvOnDmTZ8+e5ZEjR+ju7k6xWEx/f39++fKFjx49yrHmISOioqJ4//59Hj58mMuWLeOIESPYvn17VqlSJTmvQmAgcfx45sJAcDCxbx8xfz6xalXy323aJN/rmjXpn3PyJO03b+adO3dydA8NGjSgRCLJkf/GqVOnKJPJ2KlTpwx/T5GRkSxevDj19PQoFot5+PDhbF8zPDw8S174ERERBMD169dn+5rqZOzYsdTV1c2bM/4AVJ2/RSSJTIiMjIShoSG+ffsGAwODzJr/T9C9e3fs2LEDT58+hYmJSa5fz8bGBp07d8aECRMybfv161f07t0bmzZtQvPmzbF06VKYmprm6vgeP36MqVOnYsOGDTAyMkLJkiURHByMly9fwtraOkXb+Ph4FClSBM7Ozjhy5AhEIpFw7NTXr2h9+zY+AhCTUPxw7GdECgUoFsNZSwtBhQqhoqFhiuNLlixB37594eXlhe3bt6d4Bnfv3sWYMWOwe/duuLi4oEmTJvj8+TP279+P8PBwWFtbo0mTJmjatCkqV64MmUyW5Wfy/ft32Nraon379mjWrBmuXbuGq1ev4urVq3j58iUAwMzMDGXLlkWZMmWEf83NzQEAYWFhGDx4MDZs2IDy5ctj0aJFKFmyJABALpfDwMAA48ePx8CBA7M0LrlcjrCwMLx+/Trd7cuXL0J7sVgMGxsb2NvbC9sTDw/strQEM/h80uWvv4DPn4EtWwCxOM0mUgCD7O0xxckp6/3/gFwuR//+/bFw4UIMHDgQ06dPhzida2bE+vXr4efnh/Hjx2PUqFGpjiclJaFRo0Y4deoU4uPjsXLlSnTp0iVHY2/RogUePnyIO3fupPiNpIezszN8fHwwc+bMHF03pyQlJSF//vzw9vbGsmXLfutY8sjC/K1OyeJ/ibCwMOrr66scCpdTateuneU64Fu3bqWJiQnz5cv3y3wcXr58KaiwZTIZhw0bxo8fP6ZoM2XKFEql0hQOj0kKBfuGhAhx5iqtOP/ZJP94nw9/9oxyhYIJCQns2bMnAbB3794p1LsPHz5ky5YtKRKJ6OTkxLVr16bweUhKSuKZM2fYr18/2tvbZzlkMSEhgX///TeXLl2aooQz/vEq9/Ly4oABA7hlyxY+f/48zZVmYmIi582bRwMDA5qZmXHVqlVpairKly/Pdu3apdofGRnJ+/fv89ChQ1y6dCmHDx/Odu3asXLlysyfP38qx0N9fX26u7uzfv367NmzJydPnswNGzbw7NmzfPnyZZo+IWe+fs3SZyRs/4SD4q+/Mm27XY3lw+fOnUuRSMSmTZtmOz/H+PHj01x9KxQK9uzZU9AwjRo1Sh1DFhzwrly5olL7pk2bskaNGmq5dk7Ys2cPAaTKwZHH7yHPTPALmDp1KiUSiUp265wSEBDAAgUKZPm8t2/fsm7dugTAHj165Frs+48sWrSIIpGIXbt2pZ6eHrW1tdmvXz++efOGb9++pa6uLvv37y+0T1Io2OrePdVs0Jls7W/eZOUqVSiVSlMkjnny5Anbt29PsVhMOzs7Ll++PFMbsEKh4I0bN9INWfz69SufPHnCjRs3sl+/fqxYsSK1tLQIJFdj9PDwYOvWrSkSiThq1CiVshueP3+exYsXp0gkYs+ePVOFWSYlJfHNmze8cOECa9asyXz58tHf358NGzZk8eLFaWxsnGKiV96vp6cn27Rpw8DAQC5evJgHDhzgnTt3suQY9yOv37+n9j/litPd5s4lSpYkuncnBg0ivL0JsZgoWzbTWgZaZ84wIptZJdNjz5491NHRYdmyZbMVaaFQKNixY0fKZDKeOnVK2D99+nThM+/QoYPaTHNJSUm0s7PL1ElWyfjx42lqavrbHfbq1avHsmXL/tYx5PH/5JkJfgFxcXEoXLgwChcujAMHDuTqtVavXo2uXbsiOjoaOjo6WTqXJJYtW4aBAwfCysoK69evR4UKFXJlnElJSXBxcUG5cuWwZcsWfPnyBfPnz8f8+fMRHR0NBwcHfP78Gc+fP4eRkREAIPDZM0x/8waZfhFVRGfzZhxp1w5eXl549eoVJkyYgLVr18LCwgIjRoxA165doampmeV+z58/jxUrVuD48eN4//59imP58+dHhQoVBFV/iRIlhM+pSZMmePr0Ke7evZuuuvfjx48YOnQo1q5diyJFiqBr167Q0tJKpb4PDQ2FXC5Pca67uzscHBxSqPGVm7W1NaRSaZbvNT1u3ryJefPmYfPmzVB07Ah5y5Zgemr3t2+BuXOBJ0+A798BKyugTh3A1xfIwOwiAdDVygpLCxZU27iVXL9+HQ0bNoSWlhYOHjyIwoULZ+n8xMRE1K9fH9euXcPFixdx//59tGjRApqamvDy8sLBgwehoaGhtvGOGTMGc+bMwfv376Grq5th2/3796NRo0Z48+YNbG1t1TaGrPDixQs4Oztj1apV6NSp028ZQx4pUXX+zhMGcsjOnTvRvHlzHDlyBHXq1Mm161y9ehXlypXDjRs3BLtxVnny5An8/Pxw9epVBAYGYsyYMWp9cQHAxo0b0a5dO9y8eRMeHh7C/qioKAwfPhwLFy6ESCRC69atMWzYMETZ2cHz5s30BYHQUGD1auDuXSAqCrCwAGrUAFq2BLS00jxFAuCwvT12z5yJlStXwtjYGIGBgejZsye0tbVVuo+oqCjcuHFDsPFfvXoVb968AQBYWFigaNGi0NTURGhoKO7duweS8PT0hI+PD3x8fODo6Cj0derUKdSoUQPHjx9HwYIFU0zuL1++xPnz5/Hw4UMoFAr8+HOUSCSpbPX29vbCxB8WFoZatWrl6DuhCnK5HPv27cPcuXNx9uxZ2NnZoU+fPmjYoQPKPn6MaLlcbYIcACAxEbU3b8bO+fOhp6enzp4BAK9evYK3tzdCQ0Oxa9cuVK9ePUvnf/v2DV5eXggPD8eXL18glUrh7OyM8+fPq/39+PLlSzg5OWHNmjXo0KFDhm3fvHkDe3t77N+/Hw0aNFDrOFRl+PDhWLx4Md69e5flRUseuUOeMPCLIImqVavi06dPuH37tlpXYT8SHR0NfX19rFu3Dn5+ftnuJykpCdOmTcPYsWPh7u6O9evXw93dXS1jJIlixYrB1tYWhw8fTnFMoVCgfPnyiI+PR9euXTFz5ky8fv0aejt24LupKRRpdfjxI9ClC6CrCzRqBOjrAw8eAEeOABUrApMmpTkOkUIBPH4Mk1GjMGTIEPj7+2e4qkpISMDdu3dTTPwPHz4ESejq6qJ06dIoW7assNnZ2aVY4X/69An79+/Htm3bcPLkSSQmJsLW1haOjo7Q19dHZGQkrly5gqSkpBSTvZ6eHuRyOWJjY1GoUCE0b94chQoVEiZ9KyurDL9PMTEx0NfXx8qVK9G5c+d022WXiIgIrF69GgsWLMDLly/h6emJ/v37o0mTJsK4gsLC0OHRI7Ve13TbNnxesgTa2tqYN28eunbtqpIDXVb49u0bfH19ERwcjBUrVqBjx45ZOv/cuXOoUqUKRCIRLC0tce3aNdjY2Kh1jEpq1aqFuLg4nDt3LsN2JGFmZoaAgACMHDkyV8aSEQkJCbCzs0OrVq0wb968X379PNJG1fk7d2au/yFEIhFmz56NMmXKYPny5ejVq1euXEdPTw/58+fHvXv3ctSPVCrFiBEjUK9ePbRv3x6lS5fGpEmTEBAQkC0v6x85ePAg7t27h0WLFqU6tn79ely7dg1nz56Fl5cXevbsiVE7d2JaRlEOx44B0dHA/PmAcqXdsCGgUCQfi4pKFhB+gmIxUKgQdt27h8r58qU4plAo8OTJkxSe/bdu3UJ8fDykUimKFSuGypUrY9CgQShbtizc3NwgkUiQlJSEd+/e4fXr1zh//nyaHvjfvn0TrvP27Vu8ffsWJKGnpwdbW1u8ePECK1euhKurK1auXImgoCCULFkSixYtQvny5bP8vHV1deHq6opbt25l+dyMCAkJwYIFC7BmzRokJCSgZcuW2L59O0qXLp2qbXtLS+wNDcWuqCgghxO2BEBNY2McWLgQi93cMGTIEHTv3h1z5szBnj174OrqmqP+f8TQ0BAHDx5Er1690KlTJzx79gzjx49XSej4/PkzunTpAk1NTcTFxcHFxQX5fvqeqZMuXbqgdevWePz4MQpmYDoRiUTw8PBQ+/dBVfbs2YOPHz+iR48ev+X6eeSMPM2AmujcuTP27duHp0+fCrZwddOwYUPI5XIcOnRILf3FxcVh+PDhmDNnDqpUqYJ169bBwcEh2/1VqlQJCoUCFy5cSPFSjYqKgqurK6pUqYItW7YI+1vdv4+d4eFISq/D5cuBzZuBPXuAH8MGly8Htm4FDhwA0lH7S0UidMmXD6P19VNM/NeuXRMmbRcXF2G1X7hwYRgZGeHDhw949epVqon+7du3UCj+X39hZGSUQmX/82ZlZYWkpCScPHkSu3fvxp49e/Dp0yfBV0Emk2Hy5Mno1asXJBJJtp43ALRq1Qrv3r3D2bNns90HkLyqPHHiBObOnYtDhw7B3NwcPXv2xF9//QUrK6tU7eVyOY4cOYLly5dj/+HDEI8cCbmXV7YFAhGAGkZG2Fu0KHT+eR4RERFo164dDh48CJFIhK5du2LhwoVqNW2RxLRp0zBs2DC0jhh4vQAAV+tJREFUadMGq1evztCfJD4+HrVq1cLVq1chl8uFUMOePXtiwYIFatdgAMm/U2tra3Tr1g3Tpk3LsO3AgQOxd+9ePH36VO3jyIzq1atDLpfjzJkzv/zaeaRPXmjhL+bdu3fU1dXN1dK1gYGBtLe3V3u/p06dop2dHfX19blmzZpseSMrM6Dt27cv1bEhQ4ZQW1s7VVZE8/PnM/ZGnzYt2Su+YkVixQpi69bk0DRdXaJ588xDDjdvFrzqzc3N6enpSV9fX3bq1ImdOnVi/fr16e7uTgMDgxQe+FKplPnz52flypXZrl07Dh8+nEuXLuWhQ4d47969bP0OLl26RBMTEyEiAVkMWUyPKVOmUF9fP9tJkmJiYrhs2TIhWqJ48eJcs2ZNuumgQ0NDOX78eCHs0sPDg0uWLOGXiAhOe/WKstOnKc1iWKg4OJgjnj1jfDr3cP78eVpZWQkpiXft2pWte82ILVu2UFNTk15eXvz06VOabeRyOVu3bi2EECpDDJUFiWbOnKn2cSnp06cPLS0tM42ACQoK+i3v6ocPHxIAN23a9Euvm0fm5IUW/gYmTpxIqVTKx48f50r/GzZsyLXPISIign5+fgRAHx+fVLkBMqNevXp0d3dPNSmFhIRQJpNx3LhxKfa/i4tTbcLo3Jn4J3WwsLVrp9q5p07RxMaGIpEoVUGf4sWLs2HDhvT39+e0adO4ZcsWXrx4kaGhoVmqFpcZnz59Yo8ePSgSiVioUCEC4LJly9INWdywYQO/fv2qcv+HDx8mAD579ixL43rz5g0DAwNpYmJCkUjEJk2a8PTp02kKgklJSTx06BAbN25MiURCHR0ddunShVevXk3V/kF0NJvcvUtxcDBFJ08SJ0+m+lzEP+SGqHPrFq+rUGFQLpdz9OjRQo6EsmXL8t27d1m658w4f/48TU1N6eLiwidPnqQ6PmLECOE79HMxrcDAQALg9u3b1TomJbdu3SIA7tmzJ8N2d+7cIYAclRzPDgEBATQzM1O5MmMev448YeA38P37d9rb27NRo0a50v/NmzcJgBcvXsyV/kly586dNDU1pYWFRZqr/LRQvqjSSoXasGFD2tvbp0r0ciEiQrUJffhwokwZYuBAYtw4ol49QiQi+vZV6fzukydz2bJlPHz4MO/fv6+2Er6ZIZfLuXLlSpqamtLAwIDz589nYmIiGzVqxKJFi6aYRJVVFpVVIWUyGevUqcOlS5fy/fv3GV7n/fv3BKDSalmhUPDixYts2bKlUFEvICAgXUHi7du3nDBhAh0cHAStweLFi1XKTfAmNpbGPXuywIYNtLlwgTh8mNITJ2h14QIb3bnDiS9f8mk2tCFhYWH09PQU4vqHDRumVuHtyZMndHV1pampKc+fPy/sX7VqlVAcqnv37qmEILlczlatWlFTU5MXLlxQ23h+pFSpUmzYsGGGbRISEqihocEFCxbkyhjS4vv37zQ2Nk439Xgev5c8YeA3sWXLllzLGR8bG0uxWMzly5erve8fef/+PRs0aEAA7NKlS6YTaKtWrZg/f/5USXWUGdS2bduW6pzTqmSwGzUqWSuwbVvK/XXrElpaxJ49mfZx8xdN/j9y48YNli9fngDYvn37FBP68ePHCYCnT59O89w3b95wwYIFrFatGiUSCUUiET09PTlr1qw0axyQpIWFBUePHp3ueOLj47lx40ZB2ChQoADnz5+f5ucql8t55MgR+vj4UCKRUFtbm506deLly5ezZD569uyZUHvizJkzBMCrV6+qfH5mHDhwQEiwZGFhke7zzA6fPn2il5cXNTU1uWXLFh47doxSqZRSqZT169dPN3lUbGwsK1WqRDMzszQ1Czll8eLFlEgkmWpESpYsyS5duqj9+umxbt06AsiVe84j5+QJA78JhULBihUrsmjRompdsSgpWLAg+/Xrp/Z+f0ahUHDFihXU1dWlo6Mjz549m2a7p0+fUiwWc+HChSn2JyQk0M3NjVWqVElzErn27VvmwkCxYkSRIqn3/5MWFjNnZtpHSDZTz2aHL1++0N/fX6gyeObMmVRtFAoFCxUqxKZNm2baX3h4OFevXs2GDRumqLI4btw43r17V3iutWvXTlMb9fHjR06cOFGwt9esWZMHDhxI07/g/fv3nDRpEvPnz08ALFq0KBcuXJjtDIXLly+nWCzm169fOWTIEFpaWqq9tHZCQgL9/f0FM1CdOnWyZGLJiLi4OLZt21YoYKWhocGSJUtmmsHz06dPdHV1ZYECBdReUfLr16/U0tLilClTMmzXuXNnlipVSq3XzogKFSqwVq1av+x6eWSNPGHgN3L16lXBNqxumjVr9kvzjz99+pSenp4UiUQcMmRIKptgjx49aG5unsoBbs6cORSLxenWk49KTMw8/bCdHVGoUOr9o0cnCwPTpmV4vvjECS5etox3795V+0T0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" + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "# print BF results\n", + "bf_best_cases, bf_best = brutal_force(easy_graph)\n", + "print(f\"cost: {bf_best:.3f}\\nbit string: {bf_best_cases}\")\n", + "\n", + "# plot NetworkX graph\n", + "colors = [\"r\" if bf_best_cases[0][i] == \"0\" else \"c\" for i in easy_graph.nodes]\n", + "nx.draw_networkx(easy_graph, with_labels=True, node_color=colors, pos=pos_easy)\n", + "ax = plt.gca()\n", + "ax.set_facecolor(\"w\")" + ] + }, + { + "cell_type": "markdown", + "id": "20324164", + "metadata": {}, + "source": [ + "Another method is the simulated annealing method (SA), which is an approximation method that can be done in polynomial time, so the obtained solution has only a certain probability of being correct." + ] + }, + { + "cell_type": "code", + "execution_count": 12, + "id": "e1551fb4", + "metadata": { + "collapsed": false, + "jupyter": { + "outputs_hidden": false + }, + "ExecuteTime": { + "end_time": "2023-07-03T11:48:27.165348700Z", + "start_time": "2023-07-03T11:48:27.164345100Z" + } + }, + "outputs": [], + "source": [ + "def sim_annealing(graph, t_max: int, T: float):\n", + " num_nodes = graph.number_of_nodes()\n", + " state = np.random.randint(0, 2, num_nodes)\n", + " next_state = state.copy()\n", + " E = energy(1 - 2 * state, graph, normalize=False)\n", + " t = 0\n", + " while t < t_max:\n", + " temper = (1 - t / t_max) * T\n", + " flip_idx = np.random.randint(num_nodes)\n", + " next_state[flip_idx] = 1 - next_state[flip_idx]\n", + " next_E = energy(1 - 2 * next_state, graph, normalize=False)\n", + " if next_E <= E or np.exp(-(next_E - E) / temper) > np.random.rand():\n", + " state[flip_idx] = 1 - state[flip_idx]\n", + " E = next_E\n", + " else:\n", + " next_state[flip_idx] = 1 - next_state[flip_idx]\n", + " t += 1\n", + " return \"\".join(map(str, state.tolist())), E" + ] + }, + { + "cell_type": "code", + "execution_count": 13, + "id": "04596ec1", + "metadata": { + "collapsed": false, + "jupyter": { + "outputs_hidden": false + }, + "ExecuteTime": { + "end_time": "2023-07-03T11:48:31.015863400Z", + "start_time": "2023-07-03T11:48:27.165348700Z" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "cost: 0.000\n", + "prob: 0.910\n", + "bit string: ['000000111111', '111111000000']\n" + ] + } + ], + "source": [ + "# print SA results\n", + "sa_best_cases, sa_best, n_exp = [], float(\"inf\"), 100\n", + "for _ in range(n_exp):\n", + " sa_case, sa_cost = sim_annealing(easy_graph, 200, 1)\n", + " gap = sa_best - sa_cost\n", + " if gap > 1e-6:\n", + " sa_best = sa_cost\n", + " sa_best_cases = [sa_case]\n", + " elif abs(gap) < 1e-6:\n", + " sa_best_cases.append(sa_case)\n", + "sa_prob = len(sa_best_cases) / n_exp\n", + "sa_best_cases = list(set(sa_best_cases))\n", + "print(f\"cost: {sa_best:.3f}\\nprob: {sa_prob:.3f}\\nbit string: {sa_best_cases}\")" + ] + }, + { + "cell_type": "markdown", + "id": "e9b8619b", + "metadata": {}, + "source": [ + "## Hard Problem" + ] + }, + { + "cell_type": "markdown", + "id": "9f8cb1da", + "metadata": {}, + "source": [ + "We call the above problem an easy problem, because the classical simulated annealing method has a high probability to obtain the correct solution. Now let's define another relatively hard problem." + ] + }, + { + "cell_type": "code", + "execution_count": 14, + "id": "b0a1f778", + "metadata": { + "ExecuteTime": { + "end_time": "2023-07-03T11:48:31.522735600Z", + "start_time": "2023-07-03T11:48:30.976530700Z" + } + }, + "outputs": [ + { + "data": { + "text/plain": "
", + "image/png": "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" + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "# a hard graph instance\n", + "hard_clauses = [\n", + " [4, 1, 7],\n", + " [5, 11, 8],\n", + " [4, 1, 8],\n", + " [4, 11, 8],\n", + " [4, 1, 10],\n", + " [5, 11, 8],\n", + " [4, 1, 8],\n", + " [1, 11, 8],\n", + " [4, 1, 7],\n", + " [0, 11, 8],\n", + " [4, 1, 10],\n", + " [4, 11, 8],\n", + " [5, 0, 10],\n", + " [0, 6, 7],\n", + " [5, 0, 11],\n", + " [0, 6, 7],\n", + " [5, 0, 9],\n", + " [3, 6, 7],\n", + " [5, 0, 8],\n", + " [5, 6, 7],\n", + " [5, 0, 10],\n", + " [3, 6, 7],\n", + " [5, 0, 10],\n", + " [1, 6, 7],\n", + " [2, 4, 6],\n", + " [1, 8, 11],\n", + " [2, 4, 6],\n", + " [2, 8, 11],\n", + " [2, 4, 9],\n", + " [5, 8, 11],\n", + " [2, 4, 10],\n", + " [2, 8, 11],\n", + " [2, 4, 10],\n", + " [4, 8, 11],\n", + " [2, 4, 8],\n", + " [4, 8, 11],\n", + " [3, 0, 9],\n", + " [5, 11, 7],\n", + " [3, 0, 10],\n", + " [2, 11, 7],\n", + " [3, 0, 9],\n", + " [0, 11, 7],\n", + " [3, 0, 9],\n", + " [5, 11, 7],\n", + " [3, 0, 10],\n", + " [3, 11, 7],\n", + " [3, 0, 7],\n", + " [4, 11, 7],\n", + " [5, 0, 10],\n", + " [4, 0, 10],\n", + " [2, 5, 6],\n", + " [2, 11, 10],\n", + " [2, 6, 10],\n", + " [2, 4, 9],\n", + " [0, 9, 10],\n", + " [3, 0, 7],\n", + " [2, 5, 6],\n", + " [1, 10, 9],\n", + " [1, 4, 11],\n", + " [5, 10, 11],\n", + " [0, 4, 8],\n", + " [0, 9, 8],\n", + " [2, 11, 10],\n", + " [2, 8, 6],\n", + " [3, 6, 7],\n", + " [0, 8, 10],\n", + " [4, 0, 9],\n", + " [3, 5, 8],\n", + " [5, 11, 10],\n", + " [2, 11, 10],\n", + " [4, 11, 8],\n", + " [1, 3, 11],\n", + "]\n", + "factor = 1 / len(hard_clauses) / 4\n", + "\n", + "# convert to a NetworkX graph\n", + "hard_graph = nx.Graph()\n", + "for i, j, k in hard_clauses:\n", + " hard_graph.add_edge(i, j, weight=0)\n", + " hard_graph.add_edge(j, k, weight=0)\n", + " hard_graph.add_edge(k, i, weight=0)\n", + "for i, j, k in hard_clauses:\n", + " hard_graph[i][j][\"weight\"] += 1\n", + " hard_graph[j][k][\"weight\"] += 1\n", + " hard_graph[k][i][\"weight\"] += 1\n", + "pos_hard = nx.spring_layout(hard_graph)\n", + "nx.draw_networkx(hard_graph, with_labels=True, pos=pos_hard)\n", + "ax = plt.gca()\n", + "ax.set_facecolor(\"w\")" + ] + }, + { + "cell_type": "markdown", + "id": "d37be898", + "metadata": {}, + "source": [ + "We first solve this problem by two classical methods." + ] + }, + { + "cell_type": "code", + "execution_count": 15, + "id": "f22b5956", + "metadata": { + "collapsed": false, + "jupyter": { + "outputs_hidden": false + }, + "ExecuteTime": { + "end_time": "2023-07-03T11:48:31.523763100Z", + "start_time": "2023-07-03T11:48:31.070473700Z" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "cost: 0.000\n", + "bit string: ['000000111111', '111111000000']\n" + ] + }, + { + "data": { + "text/plain": "
", + "image/png": "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" + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "# print BF results\n", + "bf_best_cases, bf_best = brutal_force(hard_graph)\n", + "print(f\"cost: {bf_best:.3f}\\nbit string: {bf_best_cases}\")\n", + "\n", + "# plot NetworkX graph\n", + "colors = [\"r\" if bf_best_cases[0][i] == \"0\" else \"c\" for i in hard_graph.nodes]\n", + "nx.draw_networkx(hard_graph, with_labels=True, node_color=colors, pos=pos_hard)\n", + "ax = plt.gca()\n", + "ax.set_facecolor(\"w\")" + ] + }, + { + "cell_type": "code", + "execution_count": 16, + "id": "9df75fbc", + "metadata": { + "collapsed": false, + "jupyter": { + "outputs_hidden": false + }, + "ExecuteTime": { + "end_time": "2023-07-03T11:48:35.050434800Z", + "start_time": "2023-07-03T11:48:31.518734800Z" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "cost: 0.000\n", + "prob: 0.070\n", + "bit string: ['000000111111', '111111000000']\n" + ] + } + ], + "source": [ + "# print SA results\n", + "sa_best_cases, sa_best, n_exp = [], float(\"inf\"), 100\n", + "for _ in range(n_exp):\n", + " sa_case, sa_cost = sim_annealing(hard_graph, 200, 1)\n", + " gap = sa_best - sa_cost\n", + " if gap > 1e-6:\n", + " sa_best = sa_cost\n", + " sa_best_cases = [sa_case]\n", + " elif abs(gap) < 1e-6:\n", + " sa_best_cases.append(sa_case)\n", + "sa_prob = len(sa_best_cases) / n_exp\n", + "sa_best_cases = list(set(sa_best_cases))\n", + "print(f\"cost: {sa_best:.3f}\\nprob: {sa_prob:.3f}\\nbit string: {sa_best_cases}\")" + ] + }, + { + "cell_type": "markdown", + "id": "c4f3a812", + "metadata": {}, + "source": [ + "We found that the probability of SA getting the correct solution on the hard problem is much lower than that on the easy problem. This is because the energy landscape is different for the easy and hard problem, as shown in the following figure.\n", + "\n", + "\n", + "\n", + "The global minimum is located in a large and smooth neighborhood for a simpler problem and a narrow region for a harder problem. It is worth noting that when the system size is relatively small, most of the randomly generated problems are easy, and hard problems need to be constructed with special methods, please refer to [Wang, Zheng, Wu, and Zhang (2023)](https://journals.aps.org/prresearch/abstract/10.1103/PhysRevResearch.5.023171)." + ] + }, + { + "cell_type": "markdown", + "id": "99ee9bf8", + "metadata": {}, + "source": [ + "Now we use QAOA to solve this hard problem." + ] + }, + { + "cell_type": "code", + "execution_count": 20, + "id": "1f6ba479", + "metadata": { + "collapsed": false, + "jupyter": { + "outputs_hidden": false + } + }, + "outputs": [ + { + "data": { + "text/plain": "
", + "image/png": "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upnVaqx/FaSiriaqdRFCCCH6KglA3cwaE56zQKkAVd7qKNdGCCGE6JskAHUzq6PhhnMBqjwSgIQQQohokADUzWxOe+R1da2MARJCCNG18vLy0DTtlDcv7ahVq1YRFxfXpfvoTBKAupk17tjU3J5aaQESQgjRtTIyMigoKGD06NFdup958+axZ8+eyPtFixYxfvz4U2737LPPcs455xAfH098fDyzZs3iiy++6MKahkkA6mbmhDggfCm8v642qnURQghx+tPr9aSlpWEwGJr8XClFINDxOxNYrVZSUlLavN369eu54oor+PDDD9m0aRMZGRlcfPHFHD16tMN1aokEoG5mcDhBC18JFqiri3JthBBCnA5CoRAPPfQQ2dnZmM1mBg4cyNKlS4GTu8DWr1+Ppmm88847TJo0CbPZzMaNG1sso2GbysrKyD5zcnLQNI28vDygcRfYqlWrWLx4MVu3bkXTNDRNY9WqVU3W/Z///Cc333wz48ePZ/jw4fzlL38hFAqxbt26rvhRRTQdB0WX0cXGoWFAAarOHe3qCCGEaIFSirpA9/9n1Wqwomlaq9dfsGABzz77LI899hgzZsygoKCAXbt2tbjNvffey4oVK8jKyiI+Pr5dZTRn3rx5bNu2jTVr1rB27VoAnE5nq7Z1u934/X4SEhLate/WkgDUzXSOxEgA0rvlVhhCCNGT1QXqmPbitG7f7+c/+xyb0daqdaurq3n88cd58sknmT9/PgBDhgxhxowZLW63ZMkSLrroog6V0Ryr1YrdbsdgMJCWltambe+55x769evHrFmz2rXv1pIA1N2ssWiEZ4M21kkAEkII0TE7d+7E6/Uyc+bMNm03efLkDpfR2ZYvX85LL73E+vXrsVgsXbovCUDdTDPZIgHI5O34oDMhhBBdx2qw8vnPPo/Kflu9rrX16x4vJiam1WXodOEhw0qpyDK/39+u/TZnxYoVLF++nLVr1zJ27NhOLbspEoC6m06Prn7sucEvAUgIIXoyTdNa3RUVLUOHDsVqtbJu3Tquu+66LikjOTkZgIKCAuLj4wFOOa+QyWQiGAy2av8PPfQQS5cu5d13323UMtWVJABFgVYfgIyBUJRrIoQQorezWCzcc8893H333ZhMJs4++2xKSkrYvn071157baeUkZ2dTUZGBosWLWLp0qXs2bOHRx55pMUyMzMzyc3N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nW+++YZzzjkHi8VCRkYGDz30UFcfWkRLx+j3+7nnnnsYM2YMMTEx9OvXj6uuuor8/PxGZTR13pcvX95onWgd46nO4dVXX31S3efMmdNonZ58Dk91fE39e9Q0jYcffjiyTk8+f635Xuisv53r169n4sSJmM1msrOzWbVqVecchBLd4qWXXlImk0k999xzavv27er6669XcXFxqqioKNpVO6XZs2er559/Xm3btk3l5OSo73znO2rgwIGqpqYmss55552nrr/+elVQUBB5VFVVRT4PBAJq9OjRatasWerrr79Wb7/9tkpKSlILFiyIxiE1cv/996tRo0Y1qntJSUnk8xtvvFFlZGSodevWqa+++kqdeeaZ6qyzzop83pOPrUFxcXGj43v//fcVoD788EOlVO87f2+//bb6zW9+o1577TUFqNdff73R58uXL1dOp1O98cYbauvWreoHP/iBGjx4sKqrq4usM2fOHDVu3Dj12WefqY8//lhlZ2erK664IvJ5VVWVSk1NVVdeeaXatm2bWr16tbJarepPf/pT1I+xsrJSzZo1S7388stq165datOmTWrq1Klq0qRJjcoYNGiQWrJkSaPzevy/22ge46nO4fz589WcOXMa1b28vLzROj35HJ7q+I4/roKCAvXcc88pTdPU/v37I+v05PPXmu+FzvjbeeDAAWWz2dTtt9+uduzYoZ544gml1+vVmjVrOnwMEoC6ydSpU9Utt9wSeR8MBlW/fv3UsmXLolir9ikuLlaA2rBhQ2TZeeedp2677bZmt3n77beVTqdThYWFkWVPP/20cjgcyuv1dmV1T+n+++9X48aNa/KzyspKZTQa1SuvvBJZtnPnTgWoTZs2KaV69rE157bbblNDhgxRoVBIKdW7z9+JXy6hUEilpaWphx9+OLKssrJSmc1mtXr1aqWUUjt27FCA+vLLLyPrvPPOO0rTNHX06FGllFJ//OMfVXx8fKPju+eee9SwYcO6+IhO1tQX6Im++OILBaiDBw9Glg0aNEg99thjzW7TU46xuQB06aWXNrtNbzqHrTl/l156qbrwwgsbLest50+pk78XOutv5913361GjRrVaF/z5s1Ts2fP7nCdpQusG/h8PjZv3sysWbMiy3Q6HbNmzWLTpk1RrFn7VFVVAZCQkNBo+T//+U+SkpIYPXo0CxYswO12Rz7btGkTY8aMITU1NbJs9uzZuFwutm/f3j0Vb8HevXvp168fWVlZXHnllRw6dAiAzZs34/f7G5274cOHM3DgwMi56+nHdiKfz8c//vEPfvGLXzS62W9vPn/Hy83NpbCwsNE5czqdTJs2rdE5i4uLY/LkyZF1Zs2ahU6n4/PPP4+sc+6552IymSLrzJ49m927d1NRUdFNR9N6VVVVaJpGXFxco+XLly8nMTGRCRMm8PDDDzfqXujpx7h+/XpSUlIYNmwYN910E2VlZZHPTqdzWFRUxH/+8x+uvfbakz7rLefvxO+FzvrbuWnTpkZlNKzTGd+dcjPUblBaWkowGGx0kgFSU1PZtWtXlGrVPqFQiF//+tecffbZjB49OrL8Zz/7GYMGDaJfv35888033HPPPezevZvXXnsNgMLCwiaPv+GzaJo2bRqrVq1i2LBhFBQUsHjxYs455xy2bdtGYWEhJpPppC+V1NTUSL178rE15Y033qCyspKrr746sqw3n78TNdSnqfoef85SUlIafW4wGEhISGi0zuDBg08qo+Gz+Pj4Lql/e3g8Hu655x6uuOKKRjeW/O///m8mTpxIQkICn376KQsWLKCgoIBHH30U6NnHOGfOHH70ox8xePBg9u/fz//8z/9wySWXsGnTJvR6/Wl1Dl944QViY2P50Y9+1Gh5bzl/TX0vdNbfzubWcblc1NXVYbVa211vCUCiTW655Ra2bdvGxo0bGy2/4YYbIq/HjBlDeno6M2fOZP/+/QwZMqS7q9kml1xySeT12LFjmTZtGoMGDeJf//pXh/5x9VR//etfueSSS+jXr19kWW8+f32d3+/npz/9KUopnn766Uaf3X777ZHXY8eOxWQy8ctf/pJly5b1+NssXH755ZHXY8aMYezYsQwZMoT169czc+bMKNas8z333HNceeWVWCyWRst7y/lr7nuhp5MusG6QlJSEXq8/afR7UVERaWlpUapV291666289dZbfPjhhwwYMKDFdadNmwbAvn37AEhLS2vy+Bs+60ni4uI444wz2LdvH2lpafh8PiorKxutc/y5603HdvDgQdauXct1113X4nq9+fw11Kelf29paWkUFxc3+jwQCFBeXt6rzmtD+Dl48CDvv/9+o9afpkybNo1AIEBeXh7QO46xQVZWFklJSY1+J0+Hc/jxxx+ze/fuU/6bhJ55/pr7Xuisv53NreNwODr8H1QJQN3AZDIxadIk1q1bF1kWCoVYt24d06dPj2LNWkcpxa233srrr7/OBx98cFKTa1NycnIASE9PB2D69Ol8++23jf5gNfzBHjlyZJfUu71qamrYv38/6enpTJo0CaPR2Ojc7d69m0OHDkXOXW86tueff56UlBS++93vtrhebz5/gwcPJi0trdE5c7lcfP75543OWWVlJZs3b46s88EHHxAKhSLhb/r06Xz00Uf4/f7IOu+//z7Dhg3rEV0nDeFn7969rF27lsTExFNuk5OTg06ni3Qd9fRjPN6RI0coKytr9DvZ288hhFtkJ02axLhx4065bk86f6f6Xuisv53Tp09vVEbDOp3y3dnhYdSiVV566SVlNpvVqlWr1I4dO9QNN9yg4uLiGo1+76luuukm5XQ61fr16xtdjul2u5VSSu3bt08tWbJEffXVVyo3N1e9+eabKisrS5177rmRMhoud7z44otVTk6OWrNmjUpOTu4Rl4rfcccdav369So3N1d98sknatasWSopKUkVFxcrpcKXcg4cOFB98MEH6quvvlLTp09X06dPj2zfk4/teMFgUA0cOFDdc889jZb3xvNXXV2tvv76a/X1118rQD366KPq66+/jlwBtXz5chUXF6fefPNN9c0336hLL720ycvgJ0yYoD7//HO1ceNGNXTo0EaXUFdWVqrU1FT185//XG3btk299NJLymazddtl8C0do8/nUz/4wQ/UgAEDVE5OTqN/lw1Xz3z66afqscceUzk5OWr//v3qH//4h0pOTlZXXXVVjzjGlo6vurpa3XnnnWrTpk0qNzdXrV27Vk2cOFENHTpUeTyeSBk9+Rye6ndUqfBl7DabTT399NMnbd/Tz9+pvheU6py/nQ2Xwd91111q586d6qmnnpLL4HujJ554Qg0cOFCZTCY1depU9dlnn0W7Sq0CNPl4/vnnlVJKHTp0SJ177rkqISFBmc1mlZ2dre66665G88gopVReXp665JJLlNVqVUlJSeqOO+5Qfr8/CkfU2Lx581R6eroymUyqf//+at68eWrfvn2Rz+vq6tTNN9+s4uPjlc1mUz/84Q9VQUFBozJ66rEd791331WA2r17d6PlvfH8ffjhh03+Ts6fP18pFb4U/r777lOpqanKbDarmTNnnnTcZWVl6oorrlB2u105HA51zTXXqOrq6kbrbN26Vc2YMUOZzWbVv39/tXz58u46xBaPMTc3t9l/lw1zO23evFlNmzZNOZ1OZbFY1IgRI9Tvf//7RgEimsfY0vG53W518cUXq+TkZGU0GtWgQYPU9ddff9J/GHvyOTzV76hSSv3pT39SVqtVVVZWnrR9Tz9/p/peUKrz/nZ++OGHavz48cpkMqmsrKxG++gIrf5AhBBCCCH6DBkDJIQQQog+RwKQEEIIIfocCUBCCCGE6HMkAAkhhBCiz5EAJIQQQog+RwKQEEIIIfocCUBCCCGE6HMkAAkhhBCiz5EAJIQQTcjMzGTlypXRroYQootIABJCRN3VV1/NZZddBsD555/Pr3/9627b96pVq4iLiztp+ZdffskNN9zQbfUQQnQvQ7QrIIQQXcHn82Eymdq9fXJycifWRgjR00gLkBCix7j66qvZsGEDjz/+OJqmoWkaeXl5AGzbto1LLrkEu91OamoqP//5zyktLY1se/7553Prrbfy61//mqSkJGbPng3Ao48+ypgxY4iJiSEjI4Obb76ZmpoaANavX88111xDVVVVZH+LFi0CTu4CO3ToEJdeeil2ux2Hw8FPf/pTioqKIp8vWrSI8ePH8/e//53MzEycTieXX3451dXVXftDE0K0iwQgIUSP8fjjjzN9+nSuv/56CgoKKCgoICMjg8rKSi688EImTJjAV199xZo1aygqKuKnP/1po+1feOEFTCYTn3zyCc888wwAOp2OP/zhD2zfvp0XXniBDz74gLvvvhuAs846i5UrV+JwOCL7u/POO0+qVygU4tJLL6W8vJwNGzbw/vvvc+DAAebNm9dovf379/PGG2/w1ltv8dZbb7FhwwaWL1/eRT8tIURHSBeYEKLHcDqdmEwmbDYbaWlpkeVPPvkkEyZM4Pe//31k2XPPPUdGRgZ79uzhjDPOAGDo0KE89NBDjco8fjxRZmYmDzzwADfeeCN//OMfMZlMOJ1ONE1rtL8TrVu3jm+//Zbc3FwyMjIA+Nvf/saoUaP48ssvmTJlChAOSqtWrSI2NhaAn//856xbt46lS5d27AcjhOh00gIkhOjxtm7dyocffojdbo88hg8fDoRbXRpMmjTppG3Xrl3LzJkz6d+/P7Gxsfz85z+nrKwMt9vd6v3v3LmTjIyMSPgBGDlyJHFxcezcuTOyLDMzMxJ+ANLT0ykuLm7TsQohuoe0AAkheryamhq+//3v8+CDD570WXp6euR1TExMo8/y8vL43ve+x0033cTSpUtJSEhg48aNXHvttfh8Pmw2W6fW02g0NnqvaRqhUKhT9yGE6BwSgIQQPYrJZCIYDDZaNnHiRP7973+TmZmJwdD6P1ubN28mFArxyCOPoNOFG7z/9a9/nXJ/JxoxYgSHDx/m8OHDkVagHTt2UFlZyciRI1tdHyFEzyFdYEKIHiUzM5PPP/+cvLw8SktLCYVC3HLLLZSXl3PFFVfw5Zdfsn//ft59912uueaaFsNLdnY2fr+fJ554ggMHDvD3v/89Mjj6+P3V1NSwbt06SktLm+wamzVrFmPGjOHKK69ky5YtfPHFF1x11VWcd955TJ48udN/BkKIricBSAjRo9x5553o9XpGjhxJcnIyhw4dol+/fnzyyScEg0EuvvhixowZw69//Wvi4uIiLTtNGTduHI8++igPPvggo0eP5p///CfLli1rtM5ZZ53FjTfeyLx580hOTj5pEDWEu7LefPNN4uPjOffcc5k1axZZWVm8/PLLnX78QojuoSmlVLQrIYQQQgjRnaQFSAghhBB9jgQgIYQQQvQ5EoCEEEII0edIABJCCCFEnyMBSAghhBB9jgQgIYQQQvQ5EoCEEEII0edIABJCCCFEnyMBSAghhBB9jgQgIYQQQvQ5EoCEEEII0ef8f9CiVOYDEPM5AAAAAElFTkSuQmCC" + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "# use vvag to get the losses and gradients with different random circuit instances\n", + "QAOA_vvag = K.jit(\n", + " K.vvag(QAOAansatz, argnums=0, vectorized_argnums=0), static_argnums=(1, 2, 3)\n", + ")\n", + "\n", + "params_hard = K.implicit_randn(\n", + " shape=[ncircuits, 2 * nlayers], stddev=0.1\n", + ") # initial parameters\n", + "if type(K).__name__ == \"JaxBackend\":\n", + " opt = K.optimizer(optax.adam(1e-2))\n", + "else:\n", + " opt = K.optimizer(tf.keras.optimizers.Adam(1e-2))\n", + "\n", + "list_of_loss = [[] for i in range(ncircuits)]\n", + "\n", + "for i in range(2000):\n", + " loss, grads = QAOA_vvag(params_hard, hard_graph)\n", + " params_hard = opt.update(grads, params_hard) # gradient descent\n", + "\n", + " # visualise the progress\n", + " clear_output(wait=True)\n", + " list_of_loss = np.hstack((list_of_loss, K.numpy(loss)[:, np.newaxis]))\n", + " plt.xlabel(\"Iteration\")\n", + " plt.ylabel(\"Cost\")\n", + " for index in range(ncircuits):\n", + " plt.plot(range(i + 1), list_of_loss[index])\n", + " legend = [f\"circuit {leg}\" for leg in range(ncircuits)]\n", + " plt.legend(legend)\n", + " plt.show()" + ] + }, + { + "cell_type": "code", + "execution_count": 21, + "id": "a6b7606f", + "metadata": { + "collapsed": false, + "jupyter": { + "outputs_hidden": false + }, + "ExecuteTime": { + "end_time": "2023-07-03T14:01:09.731783100Z", + "start_time": "2023-07-03T14:00:36.495805100Z" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Circuit #0\n", + "cost: 0.02998761646449566\n", + "max prob: 0.04241819679737091\n", + "bit strings: ['111111000000']\n", + "\n", + "Circuit #1\n", + "cost: 0.034460458904504776\n", + "max prob: 0.03702807426452637\n", + "bit strings: ['000000111111']\n", + "\n", + "Circuit #2\n", + "cost: 0.04517427086830139\n", + "max prob: 0.027316443622112274\n", + "bit strings: ['111111000000']\n", + "\n", + "Circuit #3\n", + "cost: 0.02961093559861183\n", + "max prob: 0.04281751438975334\n", + "bit strings: ['111111000000']\n", + "\n", + "Circuit #4\n", + "cost: 0.030255526304244995\n", + "max prob: 0.042135994881391525\n", + "bit strings: ['111111000000']\n", + "\n", + "Circuit #5\n", + "cost: 0.029639022424817085\n", + "max prob: 0.04278436675667763\n", + "bit strings: ['000000111111']\n", + "\n" + ] + } + ], + "source": [ + "# print QAOA results\n", + "for num_circuit in range(ncircuits):\n", + " print(f\"Circuit #{num_circuit}\")\n", + " c = QAOAansatz(params=params_hard[num_circuit], g=hard_graph, return_circuit=True)\n", + " loss = QAOAansatz(params=params_hard[num_circuit], g=hard_graph)\n", + "\n", + " # find the states with max probabilities\n", + " probs = K.numpy(c.probability())\n", + " max_prob = max(probs)\n", + " index = np.where(probs == max_prob)[0]\n", + " states = []\n", + " for i in index:\n", + " states.append(f\"{bin(i)[2:]:0>{c._nqubits}}\")\n", + " print(f\"cost: {K.numpy(loss)}\\nmax prob: {max_prob}\\nbit strings: {states}\\n\")" + ] + }, + { + "cell_type": "markdown", + "id": "565dd4a7", + "metadata": {}, + "source": [ + "The probability of QAOA getting the correct solution is also very low. A very simple trick can be adopted to improve the performance of QAOA, namely quantum dropout, please refer to [following tutorials](qaoa_quantum_dropout.ipynb) or [Wang, Zheng, Wu, and Zhang (2023)](https://journals.aps.org/prresearch/abstract/10.1103/PhysRevResearch.5.023171)." + ] + }, + { + "cell_type": "code", + "execution_count": 22, + "id": "89ed16e3", + "metadata": { + "collapsed": false, + "jupyter": { + "outputs_hidden": false + }, + "ExecuteTime": { + "end_time": "2023-07-03T14:02:46.556219900Z", + "start_time": "2023-07-03T14:02:46.519177800Z" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "OS info: Linux-5.4.119-1-tlinux4-0010.2-x86_64-with-glibc2.28\n", + "Python version: 3.10.11\n", + "Numpy version: 1.23.5\n", + "Scipy version: 1.11.0\n", + "Pandas version: 2.0.2\n", + "TensorNetwork version: 0.4.6\n", + "Cotengra is not installed\n", + "TensorFlow version: 2.12.0\n", + "TensorFlow GPU: []\n", + "TensorFlow CUDA infos: {'cpu_compiler': '/dt9/usr/bin/gcc', 'cuda_compute_capabilities': ['sm_35', 'sm_50', 'sm_60', 'sm_70', 'sm_75', 'compute_80'], 'cuda_version': '11.8', 'cudnn_version': '8', 'is_cuda_build': True, 'is_rocm_build': False, 'is_tensorrt_build': True}\n", + "Jax version: 0.4.13\n", + "Jax installation doesn't support GPU\n", + "JaxLib version: 0.4.13\n", + "PyTorch version: 2.0.1\n", + "PyTorch GPU support: False\n", + "PyTorch GPUs: []\n", + "Cupy is not installed\n", + "Qiskit version: 0.24.1\n", + "Cirq version: 1.1.0\n", + "TensorCircuit version 0.10.0\n" + ] + } + ], + "source": [ + "tc.about()" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "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.10.9" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/docs/source/tutorials/qaoa_quantum_dropout.ipynb b/docs/source/tutorials/qaoa_quantum_dropout.ipynb new file mode 100644 index 00000000..e856d151 --- /dev/null +++ b/docs/source/tutorials/qaoa_quantum_dropout.ipynb @@ -0,0 +1,1064 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "source": [ + "# Quantum Dropout for QAOA" + ], + "metadata": { + "collapsed": false + } + }, + { + "cell_type": "markdown", + "source": [ + "## Overview" + ], + "metadata": { + "collapsed": false + } + }, + { + "cell_type": "markdown", + "source": [ + "Quantum Approximation Optimization Algorithm (QAOA) is a hybrid classical-quantum algorithm used for solving the combinatorial optimization problem, which is proposed by [Farhi, Goldstone, and Gutmann (2014)](https://arxiv.org/abs/1411.4028). In the [previous tutorial](qaoa_nae3sat.ipynb), we introduced solving the [Not-all-equal 3-satisfiability (NAE3SAT)](https://en.wikipedia.org/wiki/Not-all-equal_3-satisfiability) by QAOA and the dilemma of QAOA on the hard problem. In this tutorial, we will introduce a simple trick to alleviate this dilemma, namely quantum dropout, please refer to [Wang, Zheng, Wu, and Zhang (2023)](https://journals.aps.org/prresearch/abstract/10.1103/PhysRevResearch.5.023171) for more details." + ], + "metadata": { + "collapsed": false + } + }, + { + "cell_type": "markdown", + "source": [ + "## NAE3SAT and Hard Problem" + ], + "metadata": { + "collapsed": false + } + }, + { + "cell_type": "markdown", + "source": [ + "Let's briefly review the NAE3SAT and the difference between the easy and hard problem.\n", + "\n", + "Let the set of clauses in the NAE3SAT be $\\mathcal{C}$. In each clause, there are three literals and each literal is represented by a spin. Spins up ($s=1$, $\\text{bit}=0$) and down ($s=-1$, $\\text{bit}=1$) represent false and true respectively. NAE3SAT requires the three spins in each clause are not all equal to each other, in other words, at least one is up, and at least one is down. The Hamiltonian of the NAE3SAT is as follows\n", + "$$\n", + "\\begin{split}\n", + " \\hat{H}_C&=\\sum_{(i,j,k)\\in\\mathcal{C}}\\left[(s_i+s_j+s_k)^2-1\\right]/2\\\\\n", + " &=\\sum_{(i,j,k)\\in\\mathcal{C}}(s_i s_j+s_j s_k+s_k s_i)+|\\mathcal{C}|,\n", + "\\end{split}\n", + "$$\n", + "where $|\\mathcal{C}|$ is the number of clauses in $\\mathcal{C}$. When all clauses are true, $\\hat{H}_C$ takes the minimum value 0, and the corresponding bit string is the solution of the NAE3SAT.\n", + "\n", + "The difference between the easy and hard problem is the energy landscape, as shown in the following figure.\n", + "\n", + "\n", + "\n", + "The global minimum is located in a large and smooth neighborhood for a simpler problem and a narrow region for a harder problem. It is worth noting that when the system size is relatively small, most of the randomly generated problems are easy, and hard problems need to be constructed with special methods, please refer to [Wang, Zheng, Wu, and Zhang (2023)](https://journals.aps.org/prresearch/abstract/10.1103/PhysRevResearch.5.023171)." + ], + "metadata": { + "collapsed": false + } + }, + { + "cell_type": "markdown", + "source": [ + "## Quantum Dropout" + ], + "metadata": { + "collapsed": false + } + }, + { + "cell_type": "markdown", + "source": [ + "The algorithm is shown in the following figure.\n", + "\n", + "\n", + "\n", + "We first implement the classical algorithm that can be done in polynomial time, such as simulated annealing method (SA). If the result is satisfactory, we stop the procedure since there is no point in a quantum solver. Otherwise, these failed classical results, typically low-lying excited states (local minima), offer insights as we prepare quantum dropout for QAOA: whether a clause should be kept or available for quantum dropout to underweight the distracting local minima and enhance the chances to locate the true ground state. Specifically, the clauses violated by low-lying excited states should be all kept, and the other clauses can be randomly discarded at the ratio $R$.\n", + "\n", + "Finally, we optimize the PQC with respect to the original cost function $H_C$ with a complete set of clauses to ensure the uniqueness of the global minimum. The current procedure does not incur obvious overhead to the conventional QAOA since the preliminary approaches and the quantum-dropout controls are both inexpensive on a classical computer." + ], + "metadata": { + "collapsed": false + } + }, + { + "cell_type": "markdown", + "source": [ + "### Setup" + ], + "metadata": { + "collapsed": false + } + }, + { + "cell_type": "code", + "execution_count": null, + "outputs": [], + "source": [ + "import tensorcircuit as tc\n", + "import optax\n", + "import jax.numpy as jnp\n", + "import tensorflow as tf\n", + "import networkx as nx\n", + "import matplotlib.pyplot as plt\n", + "import numpy as np\n", + "from functools import partial\n", + "from IPython.display import clear_output\n", + "import random\n", + "\n", + "K = tc.set_backend(\"jax\")\n", + "\n", + "nlayers = 30 # the number of layers\n", + "ncircuits = 6 # six circuits with different initial parameters are going to be optimized at the same time\n", + "R = 0.5 # dropout ratio, 0 means no dropout, 1 means all dropout" + ], + "metadata": { + "collapsed": false + } + }, + { + "cell_type": "markdown", + "source": [ + "We use the same NAE3SAT as the [previous tutorial](qaoa_nae3sat.ipynb)." + ], + "metadata": { + "collapsed": false + } + }, + { + "cell_type": "code", + "execution_count": 2, + "outputs": [], + "source": [ + "# a hard graph instance\n", + "hard_clauses = [\n", + " [4, 1, 7],\n", + " [5, 11, 8],\n", + " [4, 1, 8],\n", + " [4, 11, 8],\n", + " [4, 1, 10],\n", + " [5, 11, 8],\n", + " [4, 1, 8],\n", + " [1, 11, 8],\n", + " [4, 1, 7],\n", + " [0, 11, 8],\n", + " [4, 1, 10],\n", + " [4, 11, 8],\n", + " [5, 0, 10],\n", + " [0, 6, 7],\n", + " [5, 0, 11],\n", + " [0, 6, 7],\n", + " [5, 0, 9],\n", + " [3, 6, 7],\n", + " [5, 0, 8],\n", + " [5, 6, 7],\n", + " [5, 0, 10],\n", + " [3, 6, 7],\n", + " [5, 0, 10],\n", + " [1, 6, 7],\n", + " [2, 4, 6],\n", + " [1, 8, 11],\n", + " [2, 4, 6],\n", + " [2, 8, 11],\n", + " [2, 4, 9],\n", + " [5, 8, 11],\n", + " [2, 4, 10],\n", + " [2, 8, 11],\n", + " [2, 4, 10],\n", + " [4, 8, 11],\n", + " [2, 4, 8],\n", + " [4, 8, 11],\n", + " [3, 0, 9],\n", + " [5, 11, 7],\n", + " [3, 0, 10],\n", + " [2, 11, 7],\n", + " [3, 0, 9],\n", + " [0, 11, 7],\n", + " [3, 0, 9],\n", + " [5, 11, 7],\n", + " [3, 0, 10],\n", + " [3, 11, 7],\n", + " [3, 0, 7],\n", + " [4, 11, 7],\n", + " [5, 0, 10],\n", + " [4, 0, 10],\n", + " [2, 5, 6],\n", + " [2, 11, 10],\n", + " [2, 6, 10],\n", + " [2, 4, 9],\n", + " [0, 9, 10],\n", + " [3, 0, 7],\n", + " [2, 5, 6],\n", + " [1, 10, 9],\n", + " [1, 4, 11],\n", + " [5, 10, 11],\n", + " [0, 4, 8],\n", + " [0, 9, 8],\n", + " [2, 11, 10],\n", + " [2, 8, 6],\n", + " [3, 6, 7],\n", + " [0, 8, 10],\n", + " [4, 0, 9],\n", + " [3, 5, 8],\n", + " [5, 11, 10],\n", + " [2, 11, 10],\n", + " [4, 11, 8],\n", + " [1, 3, 11],\n", + "]\n", + "cost_factor = 1 / len(hard_clauses) / 4" + ], + "metadata": { + "collapsed": false, + "ExecuteTime": { + "end_time": "2023-07-03T11:45:08.202605500Z", + "start_time": "2023-07-03T11:45:08.202029100Z" + } + } + }, + { + "cell_type": "code", + "execution_count": 3, + "outputs": [], + "source": [ + "# convert to a NetworkX graph\n", + "def construct_graph(clauses):\n", + " graph = nx.Graph()\n", + " for i, j, k in clauses:\n", + " graph.add_edge(i, j, weight=0)\n", + " graph.add_edge(j, k, weight=0)\n", + " graph.add_edge(k, i, weight=0)\n", + " for i, j, k in clauses:\n", + " graph[i][j][\"weight\"] += 1\n", + " graph[j][k][\"weight\"] += 1\n", + " graph[k][i][\"weight\"] += 1\n", + " return graph" + ], + "metadata": { + "collapsed": false, + "ExecuteTime": { + "end_time": "2023-07-03T11:45:08.206621400Z", + "start_time": "2023-07-03T11:45:08.203172500Z" + } + } + }, + { + "cell_type": "code", + "execution_count": 4, + "outputs": [ + { + "data": { + "text/plain": "
", + "image/png": "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" + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "# plot original hard graph\n", + "hard_graph = construct_graph(hard_clauses)\n", + "pos = nx.spring_layout(hard_graph)\n", + "nx.draw_networkx(hard_graph, with_labels=True, pos=pos)\n", + "ax = plt.gca()\n", + "ax.set_facecolor(\"w\")" + ], + "metadata": { + "collapsed": false, + "ExecuteTime": { + "end_time": "2023-07-03T11:45:08.390560600Z", + "start_time": "2023-07-03T11:45:08.210412300Z" + } + } + }, + { + "cell_type": "markdown", + "source": [ + "We first use the brutal force method (BF) to obtain the true solution." + ], + "metadata": { + "collapsed": false + } + }, + { + "cell_type": "code", + "execution_count": 5, + "outputs": [], + "source": [ + "def b2s(bit):\n", + " return 1 - 2 * int(bit)\n", + "\n", + "\n", + "def energy(cfg, graph, n_cls, normalize=True):\n", + " factor = 1 / n_cls / 4\n", + " E = 0.25\n", + " for a, b in graph.edges:\n", + " E += cfg[a] * cfg[b] * graph[a][b][\"weight\"] * factor\n", + " return E if normalize else E / factor\n", + "\n", + "\n", + "def brutal_force(graph):\n", + " num_nodes, n_cls = graph.number_of_nodes(), len(hard_clauses)\n", + " min_cost, best_case = 1.0, []\n", + " for i in range(2**num_nodes):\n", + " case = f\"{bin(i)[2:]:0>{num_nodes}}\"\n", + "\n", + " cost = energy(list(map(b2s, case)), graph, n_cls)\n", + "\n", + " gap = min_cost - cost\n", + " if gap > 1e-6:\n", + " min_cost = cost\n", + " best_case = [case]\n", + " elif abs(gap) < 1e-6:\n", + " best_case.append(case)\n", + "\n", + " return best_case, min_cost" + ], + "metadata": { + "collapsed": false, + "ExecuteTime": { + "end_time": "2023-07-03T11:45:08.390560600Z", + "start_time": "2023-07-03T11:45:08.390046300Z" + } + } + }, + { + "cell_type": "code", + "execution_count": 6, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "cost: 0.000\n", + "bit string: ['000000111111', '111111000000']\n" + ] + }, + { + "data": { + "text/plain": "
", + "image/png": "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" + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "# print BF results\n", + "bf_best_cases, bf_best = brutal_force(hard_graph)\n", + "print(f\"cost: {bf_best:.3f}\\nbit string: {bf_best_cases}\")\n", + "\n", + "# plot NetworkX graph\n", + "colors = [\"r\" if bf_best_cases[0][i] == \"0\" else \"c\" for i in hard_graph.nodes]\n", + "nx.draw_networkx(hard_graph, with_labels=True, node_color=colors, pos=pos)\n", + "ax = plt.gca()\n", + "ax.set_facecolor(\"w\")" + ], + "metadata": { + "collapsed": false, + "ExecuteTime": { + "end_time": "2023-07-03T11:45:08.708112400Z", + "start_time": "2023-07-03T11:45:08.390560600Z" + } + } + }, + { + "cell_type": "markdown", + "source": [ + "Then we start to execute the algorithm loop shown in the figure above. The algorithm starts from the classical method - SA." + ], + "metadata": { + "collapsed": false + } + }, + { + "cell_type": "code", + "execution_count": 7, + "outputs": [], + "source": [ + "def sim_annealing(graph, t_max: int, T: float):\n", + " num_nodes, num_cls = graph.number_of_nodes(), len(hard_clauses)\n", + " state = np.random.randint(0, 2, num_nodes)\n", + " next_state = state.copy()\n", + " E = energy(1 - 2 * state, graph, num_cls, normalize=False)\n", + " t = 0\n", + " while t < t_max:\n", + " temper = (1 - t / t_max) * T\n", + " flip_idx = np.random.randint(num_nodes)\n", + " next_state[flip_idx] = 1 - next_state[flip_idx]\n", + " next_E = energy(1 - 2 * next_state, graph, num_cls, normalize=False)\n", + " if next_E <= E or np.exp(-(next_E - E) / temper) > np.random.rand():\n", + " state[flip_idx] = 1 - state[flip_idx]\n", + " E = next_E\n", + " else:\n", + " next_state[flip_idx] = 1 - next_state[flip_idx]\n", + " t += 1\n", + " return tuple(state), E" + ], + "metadata": { + "collapsed": false, + "ExecuteTime": { + "end_time": "2023-07-03T11:45:08.711814200Z", + "start_time": "2023-07-03T11:45:08.709702400Z" + } + } + }, + { + "cell_type": "code", + "execution_count": 8, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "number of low-lying excited states: 23\n" + ] + } + ], + "source": [ + "# obtain the low-lying excited states\n", + "ll_excited, n_exp = set(), 100\n", + "for _ in range(n_exp):\n", + " sa_case, sa_cost = sim_annealing(hard_graph, 200, 1)\n", + " if sa_cost > 1e-6:\n", + " ll_excited.add(sa_case)\n", + "print(f\"number of low-lying excited states: {len(ll_excited)}\")" + ], + "metadata": { + "collapsed": false, + "ExecuteTime": { + "end_time": "2023-07-03T11:45:12.123684300Z", + "start_time": "2023-07-03T11:45:08.709702400Z" + } + } + }, + { + "cell_type": "code", + "execution_count": 9, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "number of clauses should be kept: 12\n", + "number of clauses can be discarded: 60\n", + "number of clauses after dropout: 42\n" + ] + } + ], + "source": [ + "# obtain the clauses violated by low-lying excited states and the clauses can be discarded\n", + "def get_clauses(ll_exc_st, clauses):\n", + " kept, drop = [], []\n", + " for cls in clauses:\n", + " violated = False\n", + " for state in ll_exc_st:\n", + " if sum(state[i] for i in cls) in [0, 3]:\n", + " kept.append(cls)\n", + " violated = True\n", + " break\n", + " if not violated:\n", + " drop.append(cls)\n", + " return kept, drop\n", + "\n", + "\n", + "kept_clauses, drop_clauses = get_clauses(ll_excited, hard_clauses)\n", + "num_selected = int((1 - R) * len(drop_clauses))\n", + "num_after_drop = len(kept_clauses) + num_selected\n", + "driving_factor = 1 / num_after_drop / 4\n", + "print(f\"number of clauses should be kept: {len(kept_clauses)}\")\n", + "print(f\"number of clauses can be discarded: {len(drop_clauses)}\")\n", + "print(f\"number of clauses after dropout: {num_after_drop}\")" + ], + "metadata": { + "collapsed": false, + "ExecuteTime": { + "end_time": "2023-07-03T11:45:12.128858800Z", + "start_time": "2023-07-03T11:45:12.128083Z" + } + } + }, + { + "cell_type": "markdown", + "source": [ + "There are two ways of quantum dropout. One isotropic or uniform, i.e., $\\hat H_{C_1} = \\hat H_{C_2} = \\cdots = \\hat H_{C_p}$. The other is random or different, i.e., $\\hat H_{C_i}\\neq\\hat H_{C_j}$ if $i\\neq j$." + ], + "metadata": { + "collapsed": false + } + }, + { + "cell_type": "markdown", + "source": [ + "### Isotropic Quantum Dropout" + ], + "metadata": { + "collapsed": false + } + }, + { + "cell_type": "code", + "execution_count": 10, + "outputs": [ + { + "data": { + "text/plain": "
", + "image/png": "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" + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "# get the graph after dropout\n", + "iso_clauses = kept_clauses + random.sample(drop_clauses, num_selected)\n", + "iso_graph = construct_graph(iso_clauses)\n", + "nx.draw_networkx(iso_graph, with_labels=True, pos=pos)\n", + "ax = plt.gca()\n", + "ax.set_facecolor(\"w\")" + ], + "metadata": { + "collapsed": false, + "ExecuteTime": { + "end_time": "2023-07-03T11:45:12.461569500Z", + "start_time": "2023-07-03T11:45:12.132507800Z" + } + } + }, + { + "cell_type": "markdown", + "source": [ + "The PQC is similar to the regular QAOA, but the cost Hamiltonian is the original Hamiltonian but the driving Hamiltonian is the one after dropout." + ], + "metadata": { + "collapsed": false + } + }, + { + "cell_type": "code", + "execution_count": 11, + "outputs": [], + "source": [ + "def QAOAansatz_iso(params, g, each=1, return_circuit=False):\n", + " n = g.number_of_nodes() # the number of nodes\n", + "\n", + " # PQC loop\n", + " def pqc_loop(s_, params_):\n", + " c_ = tc.Circuit(n, inputs=s_)\n", + " for j in range(each):\n", + " # driving layer\n", + " for a, b in g.edges:\n", + " c_.RZZ(a, b, theta=g[a][b][\"weight\"] * params_[2 * j] * driving_factor)\n", + " # mixing layer\n", + " for i in range(n):\n", + " c_.RX(i, theta=params_[2 * j + 1])\n", + " s_ = c_.state()\n", + " return s_\n", + "\n", + " c0 = tc.Circuit(n)\n", + " for i in range(n):\n", + " c0.H(i)\n", + " s0 = c0.state()\n", + " s = K.scan(pqc_loop, K.reshape(params, [nlayers // each, 2 * each]), s0)\n", + " c = tc.Circuit(n, inputs=s)\n", + "\n", + " # whether to return the circuit\n", + " if return_circuit is True:\n", + " return c\n", + "\n", + " # calculate the loss function\n", + " loss = 0.25\n", + " for a, b in hard_graph.edges:\n", + " loss += c.expectation_ps(z=[a, b]) * hard_graph[a][b][\"weight\"] * cost_factor\n", + "\n", + " return K.real(loss)" + ], + "metadata": { + "collapsed": false, + "ExecuteTime": { + "end_time": "2023-07-03T11:45:12.464821600Z", + "start_time": "2023-07-03T11:45:12.462095700Z" + } + } + }, + { + "cell_type": "markdown", + "source": [ + "Here, several circuits with different initial parameters are optimized/trained at the same time." + ], + "metadata": { + "collapsed": false + } + }, + { + "cell_type": "code", + "execution_count": 12, + "outputs": [ + { + "data": { + "text/plain": "
", + "image/png": "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" + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "# use vvag to get the losses and gradients with different random circuit instances\n", + "QAOA_vvag = K.jit(\n", + " K.vvag(QAOAansatz_iso, argnums=0, vectorized_argnums=0), static_argnums=(1, 2, 3)\n", + ")\n", + "\n", + "params_iso = K.implicit_randn(\n", + " shape=[ncircuits, 2 * nlayers], stddev=0.1\n", + ") # initial parameters\n", + "if type(K).__name__ == \"JaxBackend\":\n", + " opt = K.optimizer(optax.adam(1e-2))\n", + "else:\n", + " opt = K.optimizer(tf.keras.optimizers.Adam(1e-2))\n", + "\n", + "list_of_loss = [[] for i in range(ncircuits)]\n", + "\n", + "for i in range(2000):\n", + " loss, grads = QAOA_vvag(params_iso, iso_graph)\n", + " params_iso = opt.update(grads, params_iso) # gradient descent\n", + "\n", + " # visualise the progress\n", + " clear_output(wait=True)\n", + " list_of_loss = np.hstack((list_of_loss, K.numpy(loss)[:, np.newaxis]))\n", + " plt.xlabel(\"Iteration\")\n", + " plt.ylabel(\"Cost\")\n", + " for index in range(ncircuits):\n", + " plt.plot(range(i + 1), list_of_loss[index])\n", + " legend = [f\"circuit {leg}\" for leg in range(ncircuits)]\n", + " plt.legend(legend)\n", + " plt.show()" + ], + "metadata": { + "collapsed": false + } + }, + { + "cell_type": "markdown", + "source": [ + "After inputting the optimized parameters back to the ansatz circuit, we can perform the projective measurement on the output quantum state to get the solution. Here we directly use the bit string with the maximum probability as the solution since we know all information of the probability distribution of the output quantum state, but which is not feasible in the experiment." + ], + "metadata": { + "collapsed": false + } + }, + { + "cell_type": "code", + "execution_count": 13, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Circuit #0\n", + "cost: 0.042537812143564224\n", + "max prob: 0.047906018793582916\n", + "bit strings: ['000000111111']\n", + "\n", + "Circuit #1\n", + "cost: 0.033970173448324203\n", + "max prob: 0.054868899285793304\n", + "bit strings: ['000000111111']\n", + "\n", + "Circuit #2\n", + "cost: 0.033648744225502014\n", + "max prob: 0.055139534175395966\n", + "bit strings: ['111111000000']\n", + "\n", + "Circuit #3\n", + "cost: 0.03602508455514908\n", + "max prob: 0.05475332960486412\n", + "bit strings: ['111111000000']\n", + "\n", + "Circuit #4\n", + "cost: 0.03601868823170662\n", + "max prob: 0.05406792089343071\n", + "bit strings: ['111111000000']\n", + "\n", + "Circuit #5\n", + "cost: 0.03338256850838661\n", + "max prob: 0.05537496879696846\n", + "bit strings: ['000000111111']\n", + "\n" + ] + } + ], + "source": [ + "# print QAOA results\n", + "for num_circuit in range(ncircuits):\n", + " print(f\"Circuit #{num_circuit}\")\n", + " c = QAOAansatz_iso(params=params_iso[num_circuit], g=iso_graph, return_circuit=True)\n", + " loss = QAOAansatz_iso(params=params_iso[num_circuit], g=iso_graph)\n", + "\n", + " # find the states with max probabilities\n", + " probs = K.numpy(c.probability())\n", + " max_prob = max(probs)\n", + " index = np.where(probs == max_prob)[0]\n", + " states = []\n", + " for i in index:\n", + " states.append(f\"{bin(i)[2:]:0>{c._nqubits}}\")\n", + " print(f\"cost: {K.numpy(loss)}\\nmax prob: {max_prob}\\nbit strings: {states}\\n\")" + ], + "metadata": { + "collapsed": false, + "ExecuteTime": { + "end_time": "2023-07-03T12:07:03.904854400Z", + "start_time": "2023-07-03T12:06:31.721659700Z" + } + } + }, + { + "cell_type": "markdown", + "source": [ + "On average, QAOA with isotropic quantum dropout improves the probability of correct solution (max prob) by nearly 0.015 compared to regular QAOA.\n", + "\n", + "It should be noted that isotropic quantum dropout will lead to more ground state degeneracy, so it does not necessarily lead to better results than conventional QAOA. However, it has a high upper limit, which means that it is possible to get much better results, please refer to [Wang, Zheng, Wu, and Zhang (2023)](https://journals.aps.org/prresearch/abstract/10.1103/PhysRevResearch.5.023171) for more analysis and details." + ], + "metadata": { + "collapsed": false + } + }, + { + "cell_type": "markdown", + "source": [ + "### Random Quantum Dropout" + ], + "metadata": { + "collapsed": false + } + }, + { + "cell_type": "markdown", + "source": [ + "Because the dropout of each layer is different, we need to generate $\\text{nlayers}$ graphs after dropout. In order to perform just-in-time (JIT) compilation more conveniently, here we only save the weights in the order of the edges of the original hard graph, instead of saving each graph after dropout." + ], + "metadata": { + "collapsed": false + } + }, + { + "cell_type": "code", + "execution_count": 21, + "outputs": [], + "source": [ + "def graph_weights(graph):\n", + " gw = []\n", + " for a, b in hard_graph.edges:\n", + " gw.append(graph[a].get(b, default={\"weight\": 0})[\"weight\"])\n", + " return jnp.asarray(gw)" + ], + "metadata": { + "collapsed": false, + "ExecuteTime": { + "end_time": "2023-07-03T13:23:26.871885Z", + "start_time": "2023-07-03T13:23:26.858216600Z" + } + } + }, + { + "cell_type": "code", + "execution_count": 22, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Here is the graph of the H_C of the last driving layer:\n" + ] + }, + { + "data": { + "text/plain": "
", + "image/png": "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" + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "# get the graph after dropout\n", + "rnd_graphs_w = []\n", + "for _ in range(nlayers):\n", + " rnd_clauses = kept_clauses + random.sample(drop_clauses, num_selected)\n", + " rnd_graph = construct_graph(rnd_clauses)\n", + " rnd_graphs_w.append(graph_weights(rnd_graph))\n", + "print(\"Here is the graph of the H_C of the last driving layer:\")\n", + "rnd_graphs_w = jnp.stack(rnd_graphs_w, axis=0)\n", + "nx.draw_networkx(rnd_graph, with_labels=True, pos=pos)\n", + "ax = plt.gca()\n", + "ax.set_facecolor(\"w\")" + ], + "metadata": { + "collapsed": false, + "ExecuteTime": { + "end_time": "2023-07-03T13:23:28.437985100Z", + "start_time": "2023-07-03T13:23:28.262064700Z" + } + } + }, + { + "cell_type": "markdown", + "source": [ + "The ansatz needs to accept $\\text{nlayers}$ weights of graphs as inputs." + ], + "metadata": { + "collapsed": false + } + }, + { + "cell_type": "code", + "execution_count": 23, + "outputs": [], + "source": [ + "def QAOAansatz_rnd(params, g, each=1, return_circuit=False):\n", + " n = hard_graph.number_of_nodes() # the number of nodes\n", + " rep = nlayers // each\n", + " g = g.reshape(rep, each, g.shape[-1]) * driving_factor\n", + "\n", + " # PQC loop\n", + " def pqc_loop(s_, pkg):\n", + " params_, g_ = pkg\n", + " c_ = tc.Circuit(n, inputs=s_)\n", + " for j in range(each):\n", + " # driving layer\n", + " for i, (a, b) in enumerate(hard_graph.edges):\n", + " c_.RZZ(a, b, theta=g_[j][i] * params_[2 * j])\n", + " # mixing layer\n", + " for i in range(n):\n", + " c_.RX(i, theta=params_[2 * j + 1])\n", + " s_ = c_.state()\n", + " return s_\n", + "\n", + " c0 = tc.Circuit(n)\n", + " for i in range(n):\n", + " c0.H(i)\n", + " s0 = c0.state()\n", + " s = K.scan(pqc_loop, [K.reshape(params, [rep, 2 * each]), g], s0)\n", + " c = tc.Circuit(n, inputs=s)\n", + "\n", + " # whether to return the circuit\n", + " if return_circuit is True:\n", + " return c\n", + "\n", + " # calculate the loss function\n", + " loss = 0.25\n", + " for a, b in hard_graph.edges:\n", + " loss += c.expectation_ps(z=[a, b]) * hard_graph[a][b][\"weight\"] * cost_factor\n", + "\n", + " return K.real(loss)" + ], + "metadata": { + "collapsed": false, + "ExecuteTime": { + "end_time": "2023-07-03T13:23:41.855222600Z", + "start_time": "2023-07-03T13:23:41.802298100Z" + } + } + }, + { + "cell_type": "markdown", + "source": [ + "Then, we perform the optimization step, and also optimize several circuits in parallel. Since graph weights Jax arrays are non-hashable static arguments and not supported when using vmap, we use $\\text{partial}$ to wrap the ansatz and accept the graph weights input." + ], + "metadata": { + "collapsed": false + } + }, + { + "cell_type": "code", + "execution_count": 28, + "outputs": [ + { + "data": { + "text/plain": "
", + "image/png": "iVBORw0KGgoAAAANSUhEUgAAAkAAAAGwCAYAAABB4NqyAAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjcuMSwgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy/bCgiHAAAACXBIWXMAAA9hAAAPYQGoP6dpAACFb0lEQVR4nOzdeXxU1f3/8dedfcm+JxBIwr7IIiCi4FJAUNuqpS21/urytdpq6/LFVqStCKgFEbdWq5ZWsa27dfnWBSsIKsqiaED2LWHLvk4ms8+9vz8mGQhZSMgyWT7Px2MeuXPn3HPPzcXM23PPPVfRNE1DCCGEEKIP0UW6AUIIIYQQXU0CkBBCCCH6HAlAQgghhOhzJAAJIYQQos+RACSEEEKIPkcCkBBCCCH6HAlAQgghhOhzDJFuQHekqioFBQVER0ejKEqkmyOEEEKIVtA0jZqaGjIyMtDpWu7jkQDUhIKCAjIzMyPdDCGEEEKcgaNHj9K/f/8Wy0gAakJ0dDQQ+gXGxMREuDVCCCGEaA2Hw0FmZmb4e7wlEoCaUH/ZKyYmRgKQEEII0cO0ZviKDIIWQgghRJ8jAUgIIYQQfY4EICGEEEL0OTIGSAghhDhFMBjE7/dHuhniFEajEb1e3yF1SQASQggh6miaRlFREVVVVZFuimhGXFwcaWlp7Z6nTwKQEEIIUac+/KSkpGCz2WQy3G5E0zRcLhclJSUApKent6s+CUBCCCEEocte9eEnMTEx0s0RTbBarQCUlJSQkpLSrsthMghaCCGEgPCYH5vNFuGWiJbUn5/2jtGSACSEEEKcRC57dW8ddX4kAAkhhBCiz5EAJIQQQog+RwKQEEII0Yvl5+ejKAq5ubmdup9Vq1YRFxfXqfvoSBKAulCtN8CxShdlTm+kmyKEEKKPyMzMpLCwkNGjR3fqfubOncu+ffvC7xctWsS4ceNate3rr7/O8OHDsVgsnHXWWbz//vud1MoTJAB1ob9vyGPqQ+t45L97I90UIYQQfYReryctLQ2DoemZbzRNIxAItHs/VquVlJSUNm/3xRdfcPXVV3PjjTfyzTffcOWVV3LllVeyY8eOdrepJRKAupDFGPp1e/1qhFsihBCiNTRNw+ULdPlL07Q2tVNVVZYvX87gwYMxm80MGDCABx98EGh8CWz9+vUoisIHH3zAhAkTMJvNbNiwocU66rc5eYbs3NxcFEUhPz8faHgJbNWqVSxevJht27ahKAqKorBq1aom2/7EE08we/Zsfvvb3zJixAjuv/9+zj77bJ588sk2/Q7aSiZC7EIWY2jCJk8gGOGWCCGEaA23P8jIhR92+X53LZmFzdT6r+gFCxawcuVKHnvsMaZOnUphYSF79uxpcZt77rmHFStWkJOTQ3x8/BnV0Zy5c+eyY8cOVq9ezZo1awCIjY1tsuzGjRuZN29eg3WzZs3i7bffPqN9t5YEoC5kNoR6gDzSAySEEKKD1NTU8MQTT/Dkk09y3XXXATBo0CCmTp3a4nZLlixh5syZ7aqjOVarlaioKAwGA2lpaS2WLSoqIjU1tcG61NRUioqKzmjfrSUBqAuFe4D80gMkhBA9gdWoZ9eSWRHZb2vt3r0br9fL9OnT27SPiRMntruOnkwCUBcyG0L/oL0B6QESQoieQFGUNl2KioT652O1ld1ub3UdOl3oCsbJY5Pa+yiKemlpaRQXFzdYV1xcfNqeo/aSQdBdqH4QtPQACSGE6ChDhgzBarWydu3aTqsjOTkZgMLCwvC6080rZDKZCAZP/303ZcqURvv96KOPmDJlymm3bY9uEYCeeuopsrKysFgsTJ48mS1btjRbduXKlUybNo34+Hji4+OZMWNGo/LXX399eNR5/Wv27NmdfRinVd8DJAFICCFER7FYLMyfP5+7776bf/zjHxw8eJBNmzbx97//vcPqGDx4MJmZmSxatIj9+/fz3nvv8cgjj7RYZ1ZWFnl5eeTm5lJWVobX2/QceHfccQerV6/mkUceYc+ePSxatIivvvqKX//6163/JZyBiAegV199lXnz5nHffffx9ddfM3bsWGbNmkVJSUmT5devX8/VV1/NunXr2LhxI5mZmVxyySUcP368QbnZs2dTWFgYfr388stdcTgtOtEDJJfAhBBCdJx7772Xu+66i4ULFzJixAjmzp3b7PfomdRhNBp5+eWX2bNnD2PGjOGhhx7igQceaLG+OXPmMHv2bC6++GKSk5Ob/R4+77zzeOmll/jrX//K2LFjeeONN3j77bc7feJGRWvrZAMdbPLkyUyaNCl8v7+qqmRmZnLbbbdxzz33nHb7YDBIfHw8Tz75JNdeey0Q6gGqqqpq9S10Xq+3QTJ1OBxkZmZSXV1NTExM2w+qGbsLHVz6xGckRZn56g8zOqxeIYQQ7efxeMjLyyM7OxuLxRLp5ohmtHSeHA4HsbGxrfr+jmgPkM/nY+vWrcyYcSIM6HQ6ZsyYwcaNG1tVh8vlwu/3k5CQ0GD9+vXrSUlJYdiwYdxyyy2Ul5c3W8fSpUuJjY0NvzIzM8/sgE6j/i4wr1wCE0IIISIqogGorKyMYDDYrvv/58+fT0ZGRoMQNXv2bP7xj3+wdu1aHnroIT755BMuvfTSZgdjLViwgOrq6vDr6NGjZ35QLah+9R8s3vpnrtr5VqfUL4QQQojW6d739p3GsmXLeOWVV1i/fn2DbrCf/OQn4eWzzjqLMWPGMGjQINavX9/kHAdmsxmz2dzp7S0+coCyBANxnhKCqoZep3T6PoUQQgjRWER7gJKSktDr9Wd0//+KFStYtmwZ//3vfxkzZkyLZXNyckhKSuLAgQPtbnN7mC2heRZ0iopXHochhBBCRExEA5DJZGLChAkN7v9XVZW1a9e2eP//8uXLuf/++1m9enWDmSybc+zYMcrLy0lPT++Qdp8pq90GgI4gLp8EICGEECJSIn4b/Lx581i5ciUvvPACu3fv5pZbbqG2tpYbbrgBgGuvvZYFCxaEyz/00EPce++9PPfcc2RlZVFUVERRURFOpxMAp9PJb3/7WzZt2kR+fj5r167liiuuYPDgwcya1fXTmZ9sf03duCY91HoDEW2LEEII0ZdFfAzQ3LlzKS0tZeHChRQVFTFu3DhWr14dHhh95MiR8BTcAE8//TQ+n48f/vCHDeq57777WLRoEXq9nu3bt/PCCy9QVVVFRkYGl1xyCffff3+XjPNpicebAhxCNVhxSgASQgghIibiAQjg17/+dbMzPq5fv77B+/z8/BbrslqtfPjhhx3Uso6ltxgB0AhS65VLYEIIIUSkRPwSWF9irLtTTUOVS2BCCCG6RH5+PoqinPbZXe21atUq4uLiOnUfHUkCUBcy2OsDUIAaT8c8RVcIIYRoSWZmJoWFhZ3+aIm5c+eyb9++8PtFixYxbty40263c+dO5syZQ1ZWFoqi8Pjjj3deI08iAagLmaNsdUsaTpcnom0RQgjRN+j1etLS0jAYmh71omkagUD7r0pYrVZSUlLavJ3L5SInJ4dly5addgqcjiQBqAuZTSf+gTlrnBFsiRBCiN5EVVWWL1/O4MGDMZvNDBgwgAcffBBofAls/fr1KIrCBx98wIQJEzCbzWzYsKHFOuq3qaqqCu8zNzcXRVHCY3NPvgS2atUqFi9ezLZt21AUBUVRWLVqVZNtnzRpEg8//DA/+clPuvRmpW4xCLqvsNTmAUbAT61TApAQQnR7mgZ+V9fv12gDpfVPC1iwYAErV67kscceY+rUqRQWFrJnz54Wt7nnnntYsWIFOTk5xMfHn1EdzZk7dy47duxg9erVrFmzBoDY2NgzqquzSADqQiabERQTaH7cEoCEEKL787vgjxldv9/fFYDJ3qqiNTU1PPHEEzz55JNcd911AAwaNIipU6e2uN2SJUuYOXNmu+pojtVqJSoqCoPB0KWXtdpCLoF1IYvdjKKYAPDVOCLcGiGEEL3B7t278Xq9TT7rsiUnP0nhTOvoyaQHqAtZo22hHiAg4KyJcGuEEEKcltEW6o2JxH5byWq1ntEu7PYTPUynq6N+QmJN08Lr/P6efTez9AB1IZvNFu4BCkoAEkKI7k9RQpeiuvrVhvE/Q4YMwWq1NniuZludro7k5GQACgsLw+tON6+QyWQiGOy+k/5KD1AXstus4R4gzSVjgIQQQrSfxWJh/vz53H333ZhMJs4//3xKS0vZuXMnN954Y4fUMXjwYDIzM1m0aBEPPvgg+/bt45FHHmmxzqysLPLy8sjNzaV///5ER0c3eZeXz+dj165d4eXjx4+Tm5tLVFQUgwcPbvsvpJWkB6gL2azRQCgA4YnAXQVCCCF6pXvvvZe77rqLhQsXMmLECObOnUtJSUmH1WE0Gnn55ZfZs2cPY8aM4aGHHuKBBx5osb45c+Ywe/ZsLr74YpKTk3n55ZebLFdQUMD48eMZP348hYWFrFixgvHjx/Pzn/+8Te1vK0U7+YKeAMDhcBAbG0t1dTUxMTEdVm/g0HqeWriagH8HZTFDWbry0Q6rWwghRPt4PB7y8vLIzs7GUvfoItH9tHSe2vL9LT1AXchgikKp+5Xr/N4It0YIIYTouyQAdSWDGYXQwDZ90BfhxgghhBB9lwSgrmSwhkf261Q/gaAa4QYJIYQQfZMEoK5ktKDUDbkyakFqfd339kAhhBCiN5MA1JUMFhQlFIAMWoBab/ufviuEEEKItpMA1JUMFqgLQDpNlQAkhBBCRIgEoK50Ug+QTlNxSgASQgghIkICUFfSG1B0oXE/Ciq1XhkDJIQQQkSCBKAupujr553UcHp79oPkhBBCiJ5KAlAX0+vqb31XcUoPkBBCiE6Wn5+PoiinfXhpe61atYq4uLhO3UdHkgDUxfTGUA+QhgyCFkII0fkyMzMpLCxk9OjRnbqfuXPnsm/fvvD7RYsWMW7cuNNut3LlSqZNm0Z8fDzx8fHMmDGDLVu2dGJLQyQAdTF93bNQ0VRqPHIJTAghROfS6/WkpaVhMBia/FzTNAKB9v8PudVqJSUlpc3brV+/nquvvpp169axceNGMjMzueSSSzh+/Hi729QSCUBdzGSuW1DA5fJEtC1CCCF6B1VVWb58OYMHD8ZsNjNgwAAefPBBoPElsPXr16MoCh988AETJkzAbDazYcOGFuuo36aqqiq8z9zcXBRFIT8/H2h4CWzVqlUsXryYbdu2oSgKiqKwatWqJtv+4osvcuuttzJu3DiGDx/O3/72N1RVZe3atZ3xqwprOg6KTmO26sPLLpcrgi0RQghxOpqm4Q64u3y/VoMVpe7RSa2xYMECVq5cyWOPPcbUqVMpLCxkz549LW5zzz33sGLFCnJycoiPjz+jOpozd+5cduzYwerVq1mzZg0AsbGxrdrW5XLh9/tJSEg4o323lgSgLmaxGQE9EKS2VgKQEEJ0Z+6Am8kvTe7y/W7+6WZsRlurytbU1PDEE0/w5JNPct111wEwaNAgpk6d2uJ2S5YsYebMme2qozlWq5WoqCgMBgNpaWlt2nb+/PlkZGQwY8aMM9p3a0kA6mIWuxEUA2hBfM6aSDdHCCFED7d79268Xi/Tp09v03YTJ05sdx0dbdmyZbzyyiusX78ei8XSqfuSANTFrFEWwAh4CdY4I90cIYQQLbAarGz+6eaI7LfVZa2tL3syu93e6jp0utCQYU3Twuv8/o69kWfFihUsW7aMNWvWMGbMmA6tuykSgLpYlNWMohjQNNCcjkg3RwghRAsURWn1pahIGTJkCFarlbVr1/Lzn/+8U+pITk4GoLCwkPj4eIDTzitkMpkIBls3393y5ct58MEH+fDDDxv0THUmCUBdLMpqA8UIgOaWS2BCCCHax2KxMH/+fO6++25MJhPnn38+paWl7Ny5kxtvvLFD6hg8eDCZmZksWrSIBx98kH379vHII4+0WGdWVhZ5eXnk5ubSv39/oqOjMZvNjco99NBDLFy4kJdeeomsrCyKiooAiIqKIioqqu2/kFaS2+C7WLTVRugSGCge6QESQgjRfvfeey933XUXCxcuZMSIEcydO5eSkpIOq8NoNPLyyy+zZ88exowZw0MPPcQDDzzQYn1z5sxh9uzZXHzxxSQnJ/Pyyy83We7pp5/G5/Pxwx/+kPT09PBrxYoVbWp/WynayRf0BAAOh4PY2Fiqq6uJiYnp0Lpdm//Ns39aixo4Qr59CH9+7rEOrV8IIcSZ8Xg85OXlkZ2d3ekDcMWZa+k8teX7W3qAupjBag1fAtMH3Ej+FEIIIbqeBKAuZrBaqB96ZQ768PjVljcQQgghRIeTANTFdJYoFCU0G7RZC+KUB6IKIYQQXU4CUFcz2qj/tRvVAC6fBCAhhBCiq0kA6mrGE893MWoqtd7WzZEghBBCiI4jAairmezUP97OoKnSAySEEEJEgASgrma0UZ+AdGjU+qQHSAghhOhqEoC6mtGGooRufddrGi4ZBC2EEEJ0OQlAXU1vCAcgpAdICCGEiAgJQBGg6EIBSEGTMUBCCCE6VX5+PoqinPbhpe21atUq4uLiOnUfHUkCUATo9KHJDzWQu8CEEEJ0qszMTAoLCxk9enSn7mfu3Lns27cv/H7RokWMGzfutNu9+eabTJw4kbi4OOx2O+PGjeOf//xnJ7Y0RJ4GHwF6w4lLYNIDJIQQojPp9XrS0tKa/VzTNILBIAZD+yKB1WrFarW2ebuEhAR+//vfM3z4cEwmE++++y433HADKSkpzJo1q11taon0AEWAzhQKQKoi8wAJIYRoP1VVWb58OYMHD8ZsNjNgwAAefPBBoPElsPXr16MoCh988AETJkzAbDazYcOGFuuo36aqqiq8z9zcXBRFIT8/H2h4CWzVqlUsXryYbdu2oSgKiqKwatWqJtt+0UUXcdVVVzFixAgGDRrEHXfcwZgxY9iwYUNn/KrCpAcoAoym0E8NlVq5C0wIIbotTdPQ3O4u369iPTFpbmssWLCAlStX8thjjzF16lQKCwvZs2dPi9vcc889rFixgpycHOLj48+ojubMnTuXHTt2sHr1atasWQNAbGzsabfTNI2PP/6YvXv38tBDD53RvltLAlAEGC2hnxoqLrcnso0RQgjRLM3tZu/ZE7p8v8O+3opis7WqbE1NDU888QRPPvkk1113HQCDBg1i6tSpLW63ZMkSZs6c2a46mmO1WomKisJgMLR4+a1edXU1/fr1w+v1otfr+ctf/hJuW2eRABQBZmvoYagoEHQ4ItsYIYQQPdru3bvxer1Mnz69TdtNnDix3XV0lOjoaHJzc3E6naxdu5Z58+aRk5PDRRdd1Gn7lAAUAVb7iV+76qiKXEOEEEK0SLFaGfb11ojst7XOZOAxgN1ub3UdOl1oyLCmaeF1fr//jPbbXP2DBw8GYNy4cezevZulS5d2agCSQdARYDMbgbpeIKf0AAkhRHelKAo6m63LX20Z/zNkyBCsVitr16494+M8XR3JyckAFBYWhtedbl4hk8lEMHhmN/qoqorX6z2jbVtLeoAiwG41gmIELYhSKwFICCHEmbNYLMyfP5+7774bk8nE+eefT2lpKTt37uTGG2/skDoGDx5MZmYmixYt4sEHH2Tfvn088sgjLdaZlZVFXl4eubm59O/fn+joaMxmc6NyS5cuZeLEiQwaNAiv18v777/PP//5T55++ukz+n20lgSgCIi2WAEj4EFxOyPdHCGEED3cvffei8FgYOHChRQUFJCens4vf/nLDqvDaDTy8ssvc8sttzBmzBgmTZrEAw88wI9+9KNm65szZw5vvvkmF198MVVVVTz//PNcf/31jcrV1tZy6623cuzYMaxWK8OHD+df//oXc+fObVP720rRTr6gJwBwOBzExsZSXV1NTExMh9df8N7TvPKvjWhqBYctI/nTC8s7fB9CCCHaxuPxkJeXR3Z2NhaLJdLNEc1o6Ty15ftbxgBFQJTdHroEBhj8TiSDCiGEEF1LAlAEmOx2UEJXH62BWrwBNcItEkIIIfoWCUARYLTZUQj1AFlVPy6fPA5DCCGE6EoSgCJAZ7WFe4AsakAehyGEEEJ0MQlAEaBYoqn/1Rs1TXqAhBBCiC4mASgSTFHh37wBjVqf9AAJIYQQXUkCUCSYosKzfOo1DZdXeoCEEEKIriQBKBJMdtCFbn1XUHDKGCAhhBCiS0kAigSTHUUXuvVdQcEll8CEEEKILiUBKBJ0ehRDKABpikKtDIIWQgjRSfLz81EU5bQPL22vVatWERcX16n76EjdIgA99dRTZGVlYbFYmDx5Mlu2bGm27MqVK5k2bRrx8fHEx8czY8aMRuU1TWPhwoWkp6djtVqZMWMG+/fv7+zDaBO9IXQJTFXA7XRHuDVCCCF6q8zMTAoLCxk9enSn7mfu3Lns27cv/H7RokWMGzeuTXW88sorKIrClVde2bGNa0LEA9Crr77KvHnzuO+++/j6668ZO3Yss2bNoqSkpMny69ev5+qrr2bdunVs3LiRzMxMLrnkEo4fPx4us3z5cv70pz/xzDPPsHnzZux2O7NmzcLj8XTVYZ2W3hjq9VF14HPIE+GFEEJ0Dr1eT1paGgZD088/1zSNQKD9QzGsVispKSlnvH1+fj6/+c1vmDZtWrvb0hoRD0CPPvooN910EzfccAMjR47kmWeewWaz8dxzzzVZ/sUXX+TWW29l3LhxDB8+nL/97W+oqsratWuB0Il8/PHH+cMf/sAVV1zBmDFj+Mc//kFBQQFvv/12Fx5Zy0ym0M+gAoFqCUBCCCHOnKqqLF++nMGDB2M2mxkwYAAPPvgg0PgS2Pr161EUhQ8++IAJEyZgNpvZsGFDi3XUb1NVVRXeZ25uLoqikJ+fDzS8BLZq1SoWL17Mtm3bUBQFRVFYtWpVs+0PBoNcc801LF68mJycnI7+9TSp6TjYRXw+H1u3bmXBggXhdTqdjhkzZrBx48ZW1eFyufD7/SQkJACQl5dHUVERM2bMCJeJjY1l8uTJbNy4kZ/85CeN6vB6vXi93vB7Rxf0yFhModvgVSVIoKam0/cnhBCi7TRNI+Dr+uc1Gky68HQprbFgwQJWrlzJY489xtSpUyksLGTPnj0tbnPPPfewYsUKcnJyiI+PP6M6mjN37lx27NjB6tWrWbNmDRD6Lm7OkiVLSElJ4cYbb+Szzz47o322VUQDUFlZGcFgkNTU1AbrU1NTW/1Lnz9/PhkZGeHAU1RUFK7j1DrrPzvV0qVLWbx4cVub3y42S10AQkWVACSEEN1SwKfy1zs+6fL93vzEhRjN+laVramp4YknnuDJJ5/kuuuuA2DQoEFMnTq1xe2WLFnCzJkz21VHc6xWK1FRURgMBtLS0losu2HDBv7+9793+iDtU0X8Elh7LFu2jFdeeYW33noLi8VyxvUsWLCA6urq8Ovo0aMd2MqmRVlD/7A1JYAmY4CEEEKcod27d+P1epk+fXqbtps4cWK762ivmpoafvazn7Fy5UqSkpK6dN8R7QFKSkpCr9dTXFzcYH1xcfFpE+OKFStYtmwZa9asYcyYMeH19dsVFxeTnp7eoM7mRqObzWbMZvMZHsWZibXVDQJCQ6uVACSEEN2RwaTj5icujMh+W8tqtZ7RPux2e6vr0OlC7dE0LbzO7/ef0X5PdvDgQfLz8/ne974XXqeqoUuOBoOBvXv3MmjQoHbvpykR7QEymUxMmDAhPIAZCA9onjJlSrPbLV++nPvvv5/Vq1c3SLAA2dnZpKWlNajT4XCwefPmFuvsanHRJ3qsJAAJIUT3pCgKRrO+y19tGf8zZMgQrFZrg++9tjpdHcnJyQAUFhaG153ukpXJZCIYbHmeu+HDh/Ptt9+Sm5sbfn3/+9/n4osvJjc3l8zMzLYdSBtEtAcIYN68eVx33XVMnDiRc845h8cff5za2lpuuOEGAK699lr69evH0qVLAXjooYdYuHAhL730EllZWeFxPVFRUURFhZ6xdeedd/LAAw8wZMgQsrOzuffee8nIyOiSeQVaK85uBYyAH72rKsKtEUII0VNZLBbmz5/P3Xffjclk4vzzz6e0tJSdO3dy4403dkgdgwcPJjMzk0WLFvHggw+yb98+HnnkkRbrzMrKIi8vj9zcXPr37090dHSjqy0Wi6XR/ET1d5J19rxFEQ9Ac+fOpbS0lIULF1JUVMS4ceNYvXp1eBDzkSNHwl1vAE8//TQ+n48f/vCHDeq57777WLRoEQB33303tbW13HzzzVRVVTF16lRWr17drnFCHc1ut4JiAs2P3uuMdHOEEEL0YPfeey8Gg4GFCxdSUFBAeno6v/zlLzusDqPRyMsvv8wtt9zCmDFjmDRpEg888AA/+tGPmq1vzpw5vPnmm1x88cVUVVXx/PPPc/3117fnMDuUop18QU8AoUtmsbGxVFdXExMT0yn7CH7xLE/8eQOaWonRZee2/3ulTV2eQgghOpbH4yEvL4/s7Oxu9T/MoqGWzlNbvr979F1gPZneGgWKEQCTGsTtl+eBCSGEEF1FAlCkmOyghK5AGtGo8cgT4YUQQoiuIgEoUkxRoIR+/XqgxtP+2wmFEEII0ToSgCLFHAV1g7t1KDikB0gIIYToMhKAIsVkD3X9AKDglAAkhBBCdBkJQJFiigoHIE2nUOP0RLY9QgghRB8iAShSTFEo+tB03wG9DldlVWTbI4QQQvQhEoAixWRHpw/d+h7QKXgqqiLbHiGEEKIPkQAUKUYrhroAFFTAV1UV2fYIIYQQfYgEoEhRFMyGUABSdRr+ankgqhBCiI6Xn5+PoiinfXhpe61atSr8HK+eQAJQBJkNoaeQBBWNoEMCkBBCiI6XmZlJYWFhpz9cdO7cuezbty/8ftGiRYwbN+60261atQpFURq8uuJRJBF/GGpfFmWGEkBVgmgSgIQQQnQCvV5PWlpas59rmkYwGMRgaF8ksFqtWK3WM9o2JiaGvXv3ht93xbMxpQcogqLNoV+/RhDFWRPh1gghhDiVpmn4PZ4uf7X1OeWqqrJ8+XIGDx6M2WxmwIABPPjgg0DjS2Dr169HURQ++OADJkyYgNlsZsOGDS3WUb9N1UnjVXNzc1EUhfz8fKDhJbBVq1axePFitm3bFu7VWbVqVbPtVxSFtLS08Cs1NbVNx38mpAcoguLsJgA0AlArAUgIIbqbgNfLn677YZfv9/YX3sDYhstACxYsYOXKlTz22GNMnTqVwsJC9uzZ0+I299xzDytWrCAnJ4f4+PgzqqM5c+fOZceOHaxevZo1a9YAEBsb22x5p9PJwIEDUVWVs88+mz/+8Y+MGjXqjPbdWhKAIigxxl63pGF0VUe0LUIIIXqmmpoannjiCZ588kmuu+46AAYNGsTUqVNb3G7JkiXMnDmzXXU0x2q1EhUVhcFgaPHyG8CwYcN47rnnGDNmDNXV1axYsYLzzjuPnTt30r9//zPaf2tIAIqgxFhbeNnokQAkhBDdjcFs5vYX3ojIfltr9+7deL1epk+f3qZ9TJw4sd11dIQpU6YwZcqU8PvzzjuPESNG8Oyzz3L//fd32n4lAEWQLS4KMAE+jAFfpJsjhBDiFIqitOlSVCSc6cBju90eXj5dHbq6h3efPDbJ7/ef0X5Px2g0Mn78eA4cONAp9deTQdARZIiKBcUIgFkDbyAY4RYJIYToaYYMGYLVamXt2rWdVkdycjIAhYWF4XWnm1fIZDIRDLb9ey0YDPLtt9+Snp7e5m3bQnqAIskaBzoDBMGgKVS7/aRE60+7mRBCCFHPYrEwf/587r77bkwmE+effz6lpaXs3LmTG2+8sUPqGDx4MJmZmSxatIgHH3yQffv28cgjj7RYZ1ZWFnl5eeTm5tK/f3+io6MxN3Fpb8mSJZx77rkMHjyYqqoqHn74YQ4fPszPf/7zM/p9tJYEoEiyxIESCjw6xUBVrY+U6O7d1SqEEKL7uffeezEYDCxcuJCCggLS09P55S9/2WF1GI1GXn75ZW655RbGjBnDpEmTeOCBB/jRj37UbH1z5szhzTff5OKLL6aqqornn3+e66+/vlG5yspKbrrpJoqKioiPj2fChAl88cUXjBw5sk3tbytFa+tkA32Aw+EgNjaW6upqYmJiOm9Hh7/gkT/8FXxlZFVaGfDEY0wa0Xkj3oUQQjTP4/GQl5dHdnZ2l8xELM5MS+epLd/fMgYokixxYAjNdukzGKkpLo9se4QQQog+QgJQJFnj0OlCHXB+g4HaUglAQgghRFeQABRJljgMehUAv16Hp6Iywg0SQggh+gYJQJFktGI0hG4RDOgUfBKAhBBCiC4hASiSFAWzMdQDFNBDsEoCkBBCRJrcG9S9ddT5kQAUYVF1UyIEFRW1Sh6HIYQQkWI0hiamdblcEW6JaEn9+ak/X2dK5gGKsDiLwhFAVYLoahyRbo4QQvRZer2euLg4SkpKALDZbCiKEuFWiXqapuFyuSgpKSEuLg69vn0TB0sAirDk6NApUPGjd1ZFtjFCCNHH1T+5vD4Eie4nLi7utE+Ybw0JQBGWEhuaxEnTfBhd3gi3Rggh+jZFUUhPTyclJaXTHvYpzpzRaGx3z089CUARFptY/zReP2avGtG2CCGECNHr9R32RSu6JxkEHWHW2MTwsgkdQVXuPhBCCCE6mwSgCNPZ40EJdcTpFSPVTk+EWySEEEL0fhKAIs0SB7pQAPIZLVQWlUa2PUIIIUQfIAEo0qxxoAudBq/JQtWxosi2RwghhOgDJABFmi0JvT40z4THaMJRUBzhBgkhhBC9nwSgSItKxmAI3f3lM+hxFcvcE0IIIURnkwAUafYULPoAEHoivK9ExgAJIYQQnU0CUKSZo7Ab6wOQRrC8PMINEkIIIXo/CUDdQJwlNPdPQAmiVFZEuDVCCCFE7ycBqBtIjg490VZVAhgdZRFujRBCCNH7SQDqBpLi658H5sbidEa4NUIIIUTvJwGoG4hOiAVAU91E+YJomjwOQwghhOhMEoC6AVtiWt2SD/Q2HKUyDkgIIYToTBKAugFLQjoQmgzRaY2lZF9eZBskhBBC9HISgLoBJTYj/DwwlyWKsoOHI9wiIYQQoneTANQdxPZHV/c4DLfZhvPIsQg3SAghhOjdJAB1B7GZmOoeh+ExmfAdL4hwg4QQQojeTQJQd2BLxGYIzQbtMxhRiuWJ8EIIIURnkgDUHSgKsdbQok8PpnIJQEIIIURnkgDUTSRFmQAI6lSiaioj3BohhBCid5MA1E3EJUUBoKm1mFQj3hqZEVoIIYToLBKAuonopNBkiJrmxGuJ5/gemQtICCGE6CwSgLqJqNRMADTViceSQPHu/RFukRBCCNF7SQDqJqL6DQotaG5qLYk49h+MbIOEEEKIXkwCUDdh6T+q/mkYOK0xBPJlNmghhBCis0gA6iaU2ExMoadh4LJYMRYejWyDhBBCiF5MAlB3odMRbQ51AXmMRmLLZTZoIYQQorNIAOpG4mPMAPj1KnavF29lVWQbJIQQQvRSEoC6kbikeAA01YHbmsSR7Xsj3CIhhBCid5IA1I3EpvcH6gKQJZmibTsj3CIhhBCid5IA1I3EDhgKgBaswm1LpnbXngi3SAghhOidJAB1I7GDzwZCPUA1tjT0h2QyRCGEEKIznFEAWrJkCS6Xq9F6t9vNkiVL2t2oviqmX07dkh+nLYmEosNomhbRNgkhhBC90RkFoMWLF+N0Nn5Yp8vlYvHixe1uVF9lMBqxWfQAOC1mrD4PjnyZD0gIIYToaGcUgDRNQ1GURuu3bdtGQkJCm+p66qmnyMrKwmKxMHnyZLZs2dJs2Z07dzJnzhyysrJQFIXHH3+8UZlFixahKEqD1/Dhw9vUpkiKT4gGQFXceCwJ7Pt0c4RbJIQQQvQ+bQpA8fHxJCQkoCgKQ4cOJSEhIfyKjY1l5syZ/PjHP251fa+++irz5s3jvvvu4+uvv2bs2LHMmjWLkpKSJsu7XC5ycnJYtmwZaWlpzdY7atQoCgsLw68NGza05TAjKi6tHwBasJpaezoVX34d4RYJIYQQvY+hLYUff/xxNE3jf/7nf1i8eDGxsbHhz0wmE1lZWUyZMqXV9T366KPcdNNN3HDDDQA888wzvPfeezz33HPcc889jcpPmjSJSZMmATT5eT2DwdBiQDqV1+vF6/WG3zscjlZv29FiBwyDr3eiqdU47ekYd8ut8EIIIURHa1MAuu666wDIzs7m/PPPx2Bo0+YN+Hw+tm7dyoIFC8LrdDodM2bMYOPGjWdcL8D+/fvJyMjAYrEwZcoUli5dyoABA5otv3Tp0m4zdiluQOip8GqwglpbDkP2rUP1etGZzRFumRBCCNF7nNEYoOjoaHbv3h1+/84773DllVfyu9/9Dp/P16o6ysrKCAaDpKamNlifmppKUVHRmTQLgMmTJ7Nq1SpWr17N008/TV5eHtOmTaOmpqbZbRYsWEB1dXX4dfRo5AYeJ/bLBEBTK6iOSsekBjiw8ZuItUcIIYTojc4oAP3iF79g3759ABw6dIi5c+dis9l4/fXXufvuuzu0gW116aWX8qMf/YgxY8Ywa9Ys3n//faqqqnjttdea3cZsNhMTE9PgFSnxGaExQGge3NY4gjojeWs+jVh7hBBCiN7ojALQvn37GDduHACvv/46F154IS+99BKrVq3i3//+d6vqSEpKQq/XU1xc3GB9cXFxm8bvnE5cXBxDhw7lwIEDHVZnZzKazMQmJQKgqVU4o/qhftX8nXFCCCGEaLszvg1eVVUA1qxZw2WXXQZAZmYmZWVlrarDZDIxYcIE1q5dG16nqipr165t00Dq03E6nRw8eJD09PQOq7OzJWZmA6CqFdREDyTjyF7cztoIt0oIIYToPc4oAE2cOJEHHniAf/7zn3zyySdcfvnlAOTl5TUa09OSefPmsXLlSl544QV2797NLbfcQm1tbfiusGuvvbbBIGmfz0dubi65ubn4fD6OHz9Obm5ug96d3/zmN3zyySfk5+fzxRdfcNVVV6HX67n66qvP5FAjIqF/aMC2FiyjND4Tkxpg2/ufRLhVQgghRO9xRrdxPf7441xzzTW8/fbb/P73v2fw4MEAvPHGG5x33nmtrmfu3LmUlpaycOFCioqKGDduHKtXrw6HqCNHjqDTnchoBQUFjB8/Pvx+xYoVrFixggsvvJD169cDcOzYMa6++mrKy8tJTk5m6tSpbNq0ieTk5DM51IhIza67EyxQTGXcRQAc+2g9/PiyyDVKCCGE6EUUrQMfNuXxeNDr9RiNxo6qMiIcDgexsbFUV1dHZEB0ZVEBz91xM6DHFHcrF392NyX2OC7YtA6DXp5fK4QQQjSlLd/fZz6RD7B169bw7fAjR47k7LPPbk91ok5cajpmmx2vqxaClZTHDyS9fD9bP97C5JnnRrp5QgghRI93Rt0JJSUlXHzxxUyaNInbb7+d22+/nYkTJzJ9+nRKS0s7uo19jqIopOaELiuqwWL2DgxdEjv87/+LZLOEEEKIXuOMAtBtt92G0+lk586dVFRUUFFRwY4dO3A4HNx+++0d3cY+KXXQEAC0QBHlCaEAlPjVBlzeQCSbJYQQQvQKZxSAVq9ezV/+8hdGjBgRXjdy5EieeuopPvjggw5rXF+WPmQYAGrgOGYtC69BIcNZyrr/fBbhlgkhhBA93xkFIFVVmxzobDQaw/MDifbJHHEWKAqaWoHJr/LliAwASl9/I8ItE0IIIXq+MwpA3/nOd7jjjjsoKCgIrzt+/Dj/+7//y/Tp0zuscX2ZJSqKlIE5AKiBo+zODo0JGrXzC/YelnFWQgghRHucUQB68skncTgcZGVlMWjQIAYNGkR2djYOh4M///nPHd3GPitz1FkAqP6jqMYcSmIhKuBh/bOvRLhlQgghRM92RrfBZ2Zm8vXXX7NmzRr27NkDwIgRI5gxY0aHNq6vG3DWWLa+9zZB/2Eyq89h7TgdV3+ikrz+PYqqbyIt1hLpJgohhBA9Upt6gD7++GNGjhyJw+FAURRmzpzJbbfdxm233cakSZMYNWoUn30mg3Q7SuaoMRjMZtBqMHm8bB2aTkAHIyoO8/aLqyPdPCGEEKLHalMAevzxx7npppuanF0xNjaWX/ziFzz66KMd1ri+zmgykzUmNLlk0H+QeP9QPhulhD578xXKnN5INk8IIYTosdoUgLZt28bs2bOb/fySSy5h69at7W6UOGHwpNDMz6pvP+OcY3l3cuiUTT62nX/8+/NINk0IIYTosdoUgIqLi1t8zpfBYJCZoDtYzoRz0OkNaGo51opYjiXpyc0BPRraay9RVO2JdBOFEEKIHqdNAahfv37s2LGj2c+3b99Oenp6uxslTrBGRTP4nCkA+J3fcoHvLN45N3TaZuZt4u9vb4pk84QQQogeqU0B6LLLLuPee+/F42nc6+B2u7nvvvv47ne/22GNEyFnfecSAIK+PVxgvZqdAxT2ZIJRDWJ55Z8cKnVGuIVCCCFEz9KmAPSHP/yBiooKhg4dyvLly3nnnXd45513eOihhxg2bBgVFRX8/ve/76y29lkDR4/FEpUAmhfvnlIGmxN5+QI9AJfkb+LPL8mdd0IIIURbtCkApaam8sUXXzB69GgWLFjAVVddxVVXXcXvfvc7Ro8ezYYNG0hNTe2stvZZik7HiGmhGbbL8r/gujF3snuAwp4BYNBUMt9/lU/3ydgrIYQQorXaPBP0wIEDef/99ykrK2Pz5s1s2rSJsrIy3n//fbKzszujjQKY9L3LQdGjBgpIP5jIQGsyL15Y1wt0ZAt//dfHBILyHDYhhBCiNc7oURgA8fHxTJo0iXPOOYf4+PiObJNoQnRiEskDJwGw/b/v8MuJ89jbX+GbwaDXNKZ/+iovbzkS4VYKIYQQPcMZByDR9SZ8dw6g4Cjdy7jAIHKiMnnhO3o0RePcot2s/td7VLv8kW6mEEII0e1JAOpBhk0eisEyAoBPXljFL8++jYJEhXXjQrNDX/PVG6xYvTuSTRRCCCF6BAlAPYjBpCdz9GWAnqIDO8kpiWZY/FBevECP36iRXV1Ewb/fZsfx6kg3VQghhOjWJAD1MIMnDkJvmQjAp/98njvH3EGNTeGN80Kn8uad7/DA61+hqlokmymEEEJ0axKAepgBoxIxWM5B0UXhKC3GsLWA8zPO591zdDhiNGI8LkavfY1/f30s0k0VQgghui0JQD1MXIqN+LRYDNYLANj05iv8PP0nBI16nr4kdFv8VQc+Y9Wrn1LtlgHRQgghRFMkAPVA2WOT0BmHEZ00gmAgwK4X3uCKgd9j62CFg1kqOk3j55v/wWP/3RvppgohhBDdkgSgHihnXDKKoqDqLsYaHUPpkXzOy0vHarTxxCwjmk5jeMkRDrz5H3YVOCLdXCGEEKLbkQDUA6VmxWCLNRH0Wxg3+3oAdq5ezf+L/i5FCQprzwndFn/nt69x/5tfo2kyIFoIIYQ4mQSgHkjRKWSPTQbA6xnAqItmgKZh+TCPdF0SL5yvx2dXsbs8jPvoX7z1zfEIt1gIIYToXiQA9VA545IAyNtWyoU/u4m41HRqykq5YvcQfAaFv880AvC9/Z+z8pXPcHhkQLQQQghRTwJQD9VvaDwmqwF3jZ+qIj9X/Ob3GMxmPAcLmHEkh3XDobSfiqLCbV+u5LGP9kW6yUIIIUS3IQGoh9IbdGSNSQTgwNYSkgZkMfuWOwHotytIVpGd5ZeZQNHILiwk76232VMkA6KFEEIIkADUow2ZmArA/q0lqEGVYVOmMen7cwC4YHsKtQYT284OzQ105/ZXWfxGrgyIFkIIIZAA1KNljkzAYjfidvg4vrcKgKlXX0v2+Inoghozvkrh2YlGVIuK2ennvHV/5Z3cgsg2WgghhOgGJAD1YHq9jsETUgDYt6UIAJ1Oz3fvnE9K9iAsPj3Tvk3l7e9EAzBj71f87fVPqJEB0UIIIfo4CUA93JBzQpfBDuaWEvAFATBZrFw1/z7siYnE1hpx1MbhTFMgoHDXl0/xp7X7I9lkIYQQIuIkAPVw6TmxRCdY8HuC5H9bHl4fFZ/ADxcsQbEYSamy8O6wfmhopB2toPCdlzlY6oxgq4UQQojIkgDUwyk6hSGTQr1A9ZfB6iVlDuQH8xcR1GvEVJnZMi4dDbhl27958J3crm+sEEII0U1IAOoFhtZdBju8sxxPbcPxPVkjx5L9k8vQ0CjX7OzrF4+uWuPij1bw8Z7iSDRXCCGEiDgJQL1AYr8oEvvZUQMah74pbfT5D793K8fPiwLgYFICeUmxTNq1m3+8+gHeQLCrmyuEEEJEnASgXmLoOWkA7N1c1OgzRVG48Zp7+XpoJQC7+yVxzB7Drzb9iec35HVpO4UQQojuQAJQLzFkUiooULC/iqoSV6PPRyeNZsDMaezKCs0GvX1ACq4yKH/jT5Q4PF3dXCGEECKiJAD1EtEJFgaOCj0aY/fnTU92eMeEO9k22sXBDCeaovB1VhrTd37Co+/ldmFLhRBCiMiTANSLjJyaAcDuLwoJBtRGn6fZ07hu9HVsGFNOVbKKqtOxOSGDUe8/yDdHKru6uUIIIUTESADqRQaelYgt1oS7xk/etrImy/zP6P8hwZbIf84+hs0EAYMeh8vP3//1b1RVnhMmhBCib5AA1Ivo9TpGnJcOwK4Nx5ssYzfauW38bQT1Gm9dUEm0z4fHYGBE7r957bNdXdlcIYQQImIkAPUyI8/PAAWO7q7EUeZussyVg69kSPwQCk0OLOPjsfr8eBQ9eX9bQkWVo4tbLIQQQnQ9CUC9TEySlcwRCQDs2tD0YGi9Ts9vJ/4WgMez9jLZ6cDkD2Ly1fLcfX8g4PN1WXuFEEKISJAA1AuNOnkwdLDxYGiAKRlTODf9XPxakA0/GsGkvAIMQRWl6BBvPLIMVZUJEoUQQvReEoB6oayxSVhjTLgcPg5vL2+23B1n3wHAc5atxA1PY0JeITpN5XjuFtasfApNk0HRQggheicJQL2QXq9jxJTQYOgdnx5rttzopNFMHzAdVVN56YpMEt0exh0uATS+/fi/fP7qP7uoxUIIIUTXkgDUS42aloFSNxi6oqC22XK3jb8NnaLjLe9m1EvPIa26lnFlocdpbH7rNba+905XNVkIIYToMhKAeqmYJCvZY5MB2L7uaLPlBsUN4rs53wXg6amgs+jJOO4iWxe6G2z9P1ay69OPO7/BQgghRBeSANSLjZ3eH4C9m4rwOP3Nlrt13K0YdAY+cXxN7TWzARi1uwhXfAoAq59+nENff9n5DRZCCCG6iASgXix9cBxJmVEE/Co7m5kYEaBfVD9+PPTHADw69BjGpChUj565JZ9Dzjg0VeU/jy2jYN/urmq6EEII0akkAPViiqIw9juZAHy7/nizt8QD3DTmJqwGK9uqdlJ881UAqHvB4DtO+ujxBHxe3npoCRUFzQ+qFkIIIXoKCUC93JCJqVijjdRWeTn0dWmz5ZKsSfy/Ef8PgIejN2MZlokW1HHlzk/YnnEeaYOG4HHW8O8/LsRZ0fyt9UIIIURPIAGol9MbdYy+MDQW6Ov/Hm5xbp/rR19PjCmGg45D7LvpewC48k2c+83jDLz6DuLS0nGUlvDvPy7E7azpkvYLIYQQnUECUB9w1kX9MJh0lB11cmRnRbPlYkwx3HjWjQA85n2PqIvOARTGb9/Pax+t4wcLlmCPT6Ds6GHeemgxfo+ni45ACCGE6FgSgPoAa5SJ0Rf0A+Cr9/Nb7AW6evjVJFuTOe48zuarJ6MYFFwlZq7b9WfWHPUz53dLMNvtFO7bw/89tpRgoPm7y4QQQojuSgJQHzFu5gD0Bh1Fh6op2FfVbDmrwcovx/4SgCeLXiN67g8AiNvu4Ov3n8Oa2p+r5i/CYDKTn7uV1X95HE1tfnC1EEII0R1JAOoj7LFmRpwfejzGVx/kt1j2qsFX0T+qP+WeclbPTEUfZcbnMHLTvhdZuXYH/YaN4PvzFqDT69nz+Sd8vOqv8twwIYQQPYoEoD5k/CUD0OkUju2p5Pi+ymbLGfVGfj3+1wD87dBL2G65GYDADgX9p09wtMJF9viJzL71fwHI/fBdNr7xcucfgBBCCNFBJAD1ITGJVkZOzQDgizcPtthrc2n2pYxIGIHT7+SlEdWYMpIIevXM2buOp//zGQAjpl7Exdf/AoCNb7zENx++2/kHIYQQQnQACUB9zKTvZmMw6ynJd3CwhXmBdIqOOyfcCcDLB19DN+82AGr2Wpi8/VG+zA/dTXb2pd9jyg+vBuDj559l9+efdO4BCCGEEB0g4gHoqaeeIisrC4vFwuTJk9myZUuzZXfu3MmcOXPIyspCURQef/zxdtfZ19hiTIyfEZodetPbBwkGmh/AfF7GeZyXcR4BNcDT0V9iGzsCTVWYuGMPr7z5Jqoa6kGa8sOfMm7W5aBprH7qUfJyt3bJsQghhBBnKqIB6NVXX2XevHncd999fP3114wdO5ZZs2ZRUlLSZHmXy0VOTg7Lli0jLS2tQ+rsi8bNHIA1xkR1qZvtH7f8aIv/nfC/KCh8kL8a5+03gAKOwzb+38En+ffW0FPmFUXhO9f/guHnX4gaDPJ/j/xRnhsmhBCiW4toAHr00Ue56aabuOGGGxg5ciTPPPMMNpuN5557rsnykyZN4uGHH+YnP/kJZrO5Q+rsi0wWA+dekQPAlncPUVPR/ISGwxOGc3nO5QA85nyHmMsuASDumxq+fX8ltd4AAIpOx+xb7yRr3ITQc8OWLabsSH7nHogQQghxhiIWgHw+H1u3bmXGjBknGqPTMWPGDDZu3NildXq9XhwOR4NXbzdiSjrpg2MJ+FQ+fWVfi2VvG38bRp2RzUWbyb9+FjqLEU+Fif/Z9zJ/W7sjXE5vMPL9/11A+tDheGqdvPHHhVSXFHX2oQghhBBtFrEAVFZWRjAYJDU1tcH61NRUiorO7EvzTOtcunQpsbGx4VdmZuYZ7b8nUXQKF/50GDqdQv72Mg5sbf4SYUZUBj8d/lMAVhxaSeIddwDg/VaPZf0jHKt0hcsaLRaumn8fif0HUFtZwRsP3kttVfO33AshhBCREPFB0N3BggULqK6uDr+OHj0a6SZ1icSMKM6ePRCA9S/tobbK22zZm8bcRJw5jgNVB/hwkglzdjqqT8dl337Gs//X8M4va1Q0c36/hJjkVKqKCvn30vvwumo79ViEEEKItohYAEpKSkKv11NcXNxgfXFxcbMDnDurTrPZTExMTINXXzHxsiySB0TjrQ2w9h+70dSm5waKNcdy2/jQrfBPffs09oX3AeDMs3Lh5qXh2+LrRSck8cPfL8EWG0dp/iHeXLoIn8fduQcjhBBCtFLEApDJZGLChAmsXbs2vE5VVdauXcuUKVO6TZ29nd6gY8YNI9EbdRzdVcH2dc3fFTZnyByGxQ+jxlfDX/mU2EsvAiDn6wLef3UlvlNuqY9P7xd+eGrBvt288/D9+H3N9zIJIYQQXSWil8DmzZvHypUreeGFF9i9eze33HILtbW13HDDDQBce+21LFiwIFze5/ORm5tLbm4uPp+P48ePk5uby4EDB1pdp2gsId3O+XMGA/DFvw9QcKCqyXJ6nZ57zrkHgNf3vU71r69HZzPhrTJy7VcrWfXxtkbbpGTlMGfBEowWK0d2bOc/j/yRgF+eIC+EECKyIhqA5s6dy4oVK1i4cCHjxo0jNzeX1atXhwcxHzlyhMLCwnD5goICxo8fz/jx4yksLGTFihWMHz+en//8562uUzRt9IX9GDwxBVXV+PCvO6itbrqnZmLaRGZnzUZDY9m+v5D6+z8A4N1pIPG9hRwocTbaJn3IMH4w/z4MJjN5uVt574nlqMFgpx6PEEII0RJFk8d4N+JwOIiNjaW6urpPjQfyeQL8e/lWKgpqSR8cyxV3jkdvaJyRi2qL+N5b38MT9PDAefczfvG/qP1qJ9ZEH8//6G7uu/1mdDql0Xb527/h7eVLCPr9DD//Qi799Tx0On1XHJoQQog+oC3f33IXmAgzWQxc+ouzMFn0FB6oZv2Le5p8YGqaPY1fjv0lAA9vXYHl/iUoZj3uchPXbHqMVzbub7L+rDHj+d7/LkCn17Pn80/46K9PoqnNP4pDCCGE6CwSgEQDcak2Lvn5aBQF9mws4usPDzdZ7tpR1zIsfhjV3moePfYPUu+aB4D6rYb65h84Uu5qcrtBE87h8tt/i6Lo2LHuIz5e9WyLT6UXQgghOoMEINHIwNGJTJs7FIBNbx9i/1fFjcoYdUYWnbcIBYX3Dr3HrouHYB2dgxbUcf6mrfztH/8g2Mwt9UPPncrsX/0vKAq5H77Hpy8+LyFICCFEl5IAJJp01kX9GfOd/gCsXbWbokPVjcqMThrNNSOuAeD+LQ+S8MifUCx6POUmrtvwKM+tzW22/pHTLmbmz38FwFf/eZONb7zU8QchhBBCNEMCkGjW+T8cQtaYJIIBlXef3EZFQePZnG8bfxvp9nSOO4/zl+LXSV+0GADfLh2Zb/6WHccbB6d6Y2bM5uLrbwZg4xsvs+WdNzrnQIQQQohTSAASzdLpFC65cRSp2TF4XQH+8+fcRk+OtxltLJyyEIAXd7/IznPSiZlxHmgKg7cc5t3nHsHta/6W97Mv/T7Tfno9AJ+9tIqvP/i/TjseIYQQop4EINEio1nPd381lvg0G85KL//5Uy4eZ8OJDKf2m8rcYXMBuHfDvdgW348h0Y6/1sDcT17k8ddWt7iPc674IefOuRqAdav+yva1LZcXQggh2ksCkDgtS5SR790+jqh4M5VFLt59aht+b8Nenbsm3kVWTBYl7hL+uOMx+v3pGdCB55iJ7//f3fx7U9O3xtc770c/ZeL3fgDARyufYtdn6zrteIQQQggJQKJVohMsfO+2cZhtBorzHHzw7LcE/Sfm8LEarCydthS9omd1/mrWxhWS+pvbAdB9G8S66lZ2FTiarV9RFC645gbGXnI5aBqrn3qMfZs2dPpxCSGE6JskAIlWS8iw891fj8VgCj04dfXKHQSDJ0LQ6KTR/GLsLwC4f9P9VFw1k5jvTA6NB9p4mDVP3YfD0/xzwBRFYfoNv2D0xTPRNJX3/vQw+7d80enHJYQQou+RACTaJC0nlstuHYPeoCN/exlrnt+FetJ8PzefdTOT0yfjDriZ98k8YpY9grFfAkGvnu+u+w9P/PUfBILNz/6s6HTMvPnXjJh6EWowyLuPP8T+zRKChBBCdCwJQKLNMocnMPsXo9HpFQ58VcK6f+1BqwtBep2eh6Y9RIothbzqPBZ/s5TMv/0LxaLDW2Hkp/+3jD//e22L9et0emb/6n8Zfv6FoRD0hIQgIYQQHUsCkDgjWWclMfN/RoUemfFFIZ+9tj88m3OiNZFHLnwEg2Lgw/wPed39OQP+8hfQge+Ygdkv3sGrG3a2WL9Op+fSX8870RP0xEPs2/x5VxyaEEKIPkACkDhjgyekMP36kaDAt+uP8fm/D4RD0LiUcfxm0m8AWPHVCnKzjGTce3dow70aWU/fwCd7Clusv74nqD4EvffEcglBQgghOoQEINEuwyancdFPhwGwbc1RPn/9RAj66fCf8r2c7xHUgty1/i5KZ08j6bo5AMR87cD70P9ja35Fi/WfGoLefVx6goQQQrSfBCDRbqOm9eOia+pC0MdH2VB3OUxRFBadt4izU87G6Xfyq7W/gjvuIGbGOaApZG46ztE/XsvuguYflwEnhaBpF6OpqoQgIYQQ7SYBSHSIUdP6cfH/Gw4KbF93jM9e2YemaZj0Jp64+AkGxgykoLaA2z++ndgVf8Z+7gjQFIZ/foDcB2/mcHnj54ydTKfTM/vWOxuGIJknSAghxBmSACQ6zMipGeEQ9O0nx1n/0l5UVSPOEsdT058izhzHjvId3PnJPFKe+QfWMVloQYXxn+Ty6X2/5Ei5q8X660PQyPoQ9MRyCUFCCCHOiAQg0aFGnp/B9GtHgAK7Pivgo+d2EgyoDIwZyNMznsZutLO5aDO//XwB6S+8jnlYOlpAx6R1X/LF72/gUKmzxfp1Oj2zTglBezdKCBJCCNE2EoBEhxs+JZ1LbhwVnifo/b9sx+8NMjppNH/+zp8x682sP7ae322+j34vv4NldD+0oMLYT7eRu+D/sb+o+UdmQOMQ9N4Ty9mxfk0XHZ0QQojeQAKQ6BRDJqZy+a1jMJh0HNlVwf898Q2eWj+T0ibx6EWPYtAZ+O/h//KbTb8j/V9vY5uQDarCiM/3sP+3P+brw+Ut1l8fguofm/Hh04+z9b13uujohBBC9HQSgESnGTAqkSvuHI/ZZqDokIN/L99KdamLC/pfwBMXP4FJZ2L90fXc8dldJD/3Gvbzh4OmkP3lYbx3XMZHuXkt1q/T6bnkF7cz4fIrAVj/j5V8/tq/wrfhCyGEEM2RACQ6VVpOLFfddTZR8Waqil28sWwrBQequKD/BTw14ymsBitfFHzBret+TcxTzxN31TQA4vY4SP3t93n1oy0t1q8oChf+7EbOn/szADb9+xXWPvcMqhrs9GMTQgjRc0kAEp0usV8UP7xnIskDovHU+nnn8W/Yu7mIc9PP5dmZz2I32tlavJXrVl+H+vv7SL3zWlA0jEcDjL3vWp559kV8gRYeoKoonPuDuUy/8VZQFLb99z3eefgBvK6W7yoTQgjRdymaXC9oxOFwEBsbS3V1NTExMZFuTq/h9wZZ8/wuDuWWAjD+kgGce0UO+6v3c+vaWylxlZBgSeCp6U+R9eVOjs2/D82noDOrbJjxXb5334OkxFha3Me+TRv44MlHCfh9JGUO5Kr59xGTnNIVhyeEECLC2vL9LT1AossYzXpm3zya8ZcMAOCb/x7hncdz6a/P4sXLXmRY/DAqPBXcsPoGPhkWRc5rL2NINqB6dZz3/nt8fdMVbD1Y3OI+hp47lbmLlmGPi6fs6GFe/P08Cvbt6YrDE0II0YNIABJdStEpnPeDwcy6aTRGs56C/VW89uCXBI+beeHSF5jabyqeoId7PruHxxz/ZcD7n2Cb0A80hYHbjmD4n5n865X3CarNd1ymDR7KTx98lOSB2biqq3htyQL2fP5JFx6lEEKI7k4ugTVBLoF1jcqiWlb/dQcVBbUoOoVJl2cx9pL+PPPt06z8diUAE1InsPyC5Sh/eZjy5/+DFlRQTBpfXjiD79z/MP3irM3W7/O4ef/PKzj41WYAJl/1Y8770TXo9PouOT4hhBBdqy3f3xKAmiABqOv4fUE+eWkvezcVAZAyMJrp14/kG/8mfr/h99T6a4kzx7HovEWcXxTk2Lz/xV8WGhBdNSCGqj88y+xpY1EUpcn6VTXIpy+uYuu7bwHQf8RoLr/9t0QlJHbNAQohhOgyEoDaSQJQ19I0jf1fFfPpy/vwugLojTrOvSKH2AlB5m+Yz+6K3QD8cOgP+c3oX1Fxz404P94LmgJWjS0z53DpH+5tcYD0ns8/4b9/fRK/x401JpZLfzWP7HETuuoQhRBCdAEJQO0kASgynJVe1v1zN0d2VQCQkhXD1LmDeLnieVbtWIWGRlZMFkvOX8KQL7dRuHgZAUeo56e6XzSl81bw3cumNdsbVFFwnHcfX0bp4dAEi2MvuZwLr7kBo6XlO8uEEEL0DBKA2kkCUORomsbOzwrY+OYBfJ4gigJnXdQf/eQK7t3ye0rcJSgozB02l9uz51L5+1uo/fxoqDfIoLF74iSG3vcoo7OTm6zf7/Py6b+eJ/fDdwGIS0tn9i3/S7/hI7vyMIUQQnQCCUDtJAEo8mqrvXz++n72f1UCgDXGxJhL0/g/wz9569CbAKTaUrn33HuZtO84BYvvxx8qSiBKT+53f8ald91BUnTTvTuHt+ey+pnHcZaXATBm+mym/fR6LFFRnX9wQgghOoUEoHaSANR9HN1VwSev7KW6xA1AQoadhIsCPF7yAMdqjwFwYf8L+e3Y2zA/+SCOtzcR9ITu8qpOjeb4jb/j+9d8H6O+8YwPnlonn/zz7+xY9xEAttg4LvzZjYw4/0IUncwQIYQQPY0EoHaSANS9BAMqOz45zpfv5+GtDQCQNjiaI8O+4R+VTxPQAhh1Rq4bdR03xk6mYvFduL4qR1NDY4EKszLw37GEmbPOQ6drPD7o2K4dfPS3p6g4fhSAlOxBXPDTGxg4ZlyXHaMQQoj2kwDUThKAuidPrZ+tqw+zfd1R1EDon21CjoUv+7/PR/53AEi0JHLL2Fu4rMhP+fKluA/VPUNM0TgydBDWeQ8w7YJxjQZKBwN+vvrPW2x553V87lBv08Ax45l29XWk5gzuuoMUQghxxiQAtZMEoO7NWenh69WH2fl5QTgIWTPhs8S32Gr5BE3RGBA9gNvH/YrJm76i8u//wnO8bvJDBY4OG4LtzkWcf+H4RkHI5ahm85uvkvvf91GDod6mYeddwDlX/JCUrJwuPU4hhBBtIwGonSQA9Qw1FR6+/vAwu04KQrq4AF+mfMg3cesI6P2MSBjBL0bdwLiP1uJ4+R08RQYgdNPYsUE56G+5h4svndro0lh1SRGfv/Yiuzesh7r/RPoNH8nUq6+j//BRXXqcQgghWkcCUDtJAOpZnJVevl1/jJ2fHcfrCvXaYA6yPeUTtid/itNcyZD4Idw8/P8xYc06al//AHehMbx9aUYKnmt+xUXX/gCT0dCg7pL8Q2x+6zUOfLkRNRgEIHPUGM6+9PvkTJiETieP1RBCiO5CAlA7SQDqmXyeAHs2FrJt7VEcZZ7w+qLYQ+xK/oIDiV8zIC6Tm4ZdzXmfb6L29Q9wHdWHuoMAZ4ydwst/yrTbfk58QsPz7qwo54s3XmLHuo/Q1NC4ouikZEZdOINRF04nLjWt6w5UCCFEkyQAtZMEoJ5NVTXytpXy7frjHN9XCXX/wl0mB7uTN7I3eQtRSSZ+OmQOs/cdwf/Sa9TuV1ADoVvf/WY9RyZewLDbbmfIuOEN6naUlbLtv++xfe2HeJw14fX9R45m9EUzGTL5PEyW5h/QKoQQovNIAGonCUC9R02Fh72bitjx6XFqq7zh9UXRh9ib/CWFKXu4bNhMflSjw/biS7h2ePHX1o0TAkoHZBL88c85//9didliCm/v93k58OUmdq5fw+Fvc8PjhIxmC0OnTGX0hTPoN2JUs4/lEEII0fEkALWTBKDeJ+hXydtexu7PCzi6u6I+rxBUAuTHf8v+5K8YOCqRa2KGM/DNt/BvOYSr6MQs0h6rmYJzpjPixmvJOWdsg7odZaXs+vRjdn6yhqqiwvD62NQ0hp07lSHnnEfqoCEShoQQopNJAGonCUC9W221l31bitmzqZCK47Xh9W5DDQeSvsaZXcD0EcO56NvdmN/7GOdBA0HvicHOlckpBC+dw4Qbr8aWeuKZY5qmcXzvLnauX8vejZ/h97jDn0UnJjP4nHMZMmkK/YaPQqeXwdNCCNHRJAC1kwSgvqPsWA17Nhaxe3MBPmcwvN5hLic/cTvxI2BWTIAR771PYFc1NQUWqJthWlWgbOhoEn98NSPmXIb+pKfK+z0eDmzdzIEtG8n75iv83hODsi32KAaOGU/WuAlkjT2bqPiErjtgIYToxSQAtZMEoL5HDaoc2VXBzo3HyN9eDoETl6sc5nKKU/czaBhcUrWLhHWf4TxkwFNxYkyQz2SifOI0Bv34KgZMvwDFeOI2e7/Py5Fvc9m/eSMHt25uMHgaIHlgNlnjJpA99mwyho1AbzAihBCi7SQAtZMEoL7N7w1yeEc5uZsOUrTbiRI4cbnKaarE3a+EUZkuzj3wLubc/VTnWwm4Tswf5DObqZ00jUE/+j7JF12AzmwOf6YGgxQe2Ef+tq3k526l6NCB8ABqAIPZTMbQEWSOGE3/UWeRNmgoBqMEIiGEaA0JQO0kAUjU8/uCHNpezKbPd1K9T0UfPBFGfHoPwYxyhiYcZcLuV9EdqqTmmCX8NHoAv8mEZ+L5ZM/5HonfuQidteEt8i5HNYe3f0N+7lbyt3+Dq7qqwecGo4mMYcPpP+Is+o8cTfrgYRhMJoQQQjQmAaidJACJpgT8QbZ/c5CNG3fgOWjA4osKf6YqQdTkMnJiDjA+/00MR8qoOWol4D4RhoIGA54JUxhw5eUkXnwh+ri4BvVrmkb5sSMc27WDo7u+5eiub3E7qhuU0ekNJA/MJn3IUNIGDSV9yDDi0zJQdLpOPXYhhOgJJAC1kwQgcTrBYJBPv/6SrZv34TtkJtaV3OBzLbaajKg8RpZ8SMzRfbiOGsLzCwFoioJryEhSL5lO8szpmIc2vk1e0zQqjh/j6K5vOVYXiE7tIQIw2+2kDaoPREOJTUkjPr0feoOhUVkhhOjNJAC1kwQg0Rb+oJ/1O75gy6bdeA+aSHEMQOFEj4ymDxAVfYzhzs9IKt6BcqQSX3XDcT3ehGSiL7qQlJnfwX7uuY0ulUEoEDlKiyk8sI+iA3spPLCfkkMHCPh9jcraYuPoN3wk/YePIiV7ECnZg2SGaiFErycBqJ0kAIkz5fK7+Hjvp2z68ltq8yC9cjB2f2yDMnqzg4HqVjIc24k6fgB/YRAteKL3J2g0oT97Aqkzv4P9/PMxZWU1O4liMBCg7OjhukC0j+KD+6kuLWkwBxGAouhI7J9JSvYgUrMHkZI1iOSsHMw2W8f/EoQQIkIkALWTBCDREdwBNxuPb+Sz7Vs4truKpPKBpDsGoddOvjSlEm88xABfLvGlezDmHyfoajieJ5CUQsy084mfNhXblCkY4uNb3G8wEODozu0UHzpAwf49lOQdxFlR3mTZ2NQ0kgdkkTQgm+SBWSQPyCIuNV3GFAkheiQJQO0kAUh0tKAaJLc0l48PrePb7YewFiXRv2o4Ce6GT5E36Fz0ZztJNbuIKdiLUliDpp7o/dEUBWXwUBIumob93HOxnX12k5fLTuWsrKDo4H5K8g5Skn+QkrxD1JSXNlnWYDaTlDmQ5AFZJA/MJnlANkkDsrBERTVZXgghugsJQO0kAUh0Jk3TOFB1gI+PfMzn+7bgztORWT2cflVDsQTtDcrG6I+TEfiWhMo9WI8dRqtoON5H0xswjzmLmClTsE2ejHXc2AbzDrXE5aim7Mhhyo7kUXokn9LD+ZQfPdzkmCIIPc4jeWAWSZkDSeiXSWK/TBL69cdklctoQojuQQJQO0kAEl2pqLaIdUfXse7wevIPFJFeNZjMqhGk1gxER8NnhsXrjpDk20ds5UGsx/MwVjWcVVozmrCfPR7bOZOwTZiIdeyYVvUQ1VPVIFVFhZQezm8QjBylxc1uE5WYFA5Dif0ySew3gIT+mdhiYpvdRgghOoMEoHaSACQixeFz8Nmxz1h3dB1fHfma6NJ0MquHk+EYRPwpl8sAopRSkvx7ia08iK0gH0tlCQ2GSxsMWEePxjZxAtYJE7CdfTb62LYHE6+rlrIjhyk9kk/Z0cNUHD9KxfGj1FZVNruNJTqGxH79T+otyiSxfybRicnNDuoWQoj2kADUThKARHcQVIPsLN/JFwVfsLFgI3sLDpBcnUV6TQ7pjkEk1fZr1ENkxkGyfx+xjgPYCvOxlx9Hp6knCigK5iFDQoFo3DgsZ53V4l1mp+NxOqkoOEr58aOUHzsaDkbVpSUNHvFxMqPZQkKDYBRajktNl7mLhBDtIgGonSQAie6oxlfDlqItfHH8Cz4v+Jzi6lLSarJJc+SQXpNDqjMLg9rwMRkGPCQFDxDrPIiltICokqOYfafMLh0bi/Wss7COOQvLmDFYx4zBkNC+J9T7vR4qCwsoP36UimNHQj+PH6OysAA1GGhyG53eQFxaOon964JRRn/i0jKIS8/AGhXdrvYIIfoGCUDtJAFI9ARHHEf4ouALvij4gi1FW3B7PSTV9ifdMYj0mhwyagZhCjQeoGzVKon1HsZWfQxb6TFiHYcx+xwNyhj798c6ZgyWMWdhHTMWy8gR6CyWdrc5GAhQXVJUF4yO1gWjUDjyez3NbmexRxGXlh4KRGkZxKelh99bo2PkkpoQApAA1G4SgERP41f9fFv6LV8WfcmXRV+SW5qLN+Aj3p1GuiOHFOdA0l0DialNaTBLdT2rWkm05zCWqgLiyg8TXXOkYSgyGLAMHRoORNaxYzBlZ3fYfEGaqlJTUVYXio6FQlHhMaqKCpudw6ie2WYPhaHUUCCKT8+oW07HFhsn4UiIPkQCUDtJABI9nS/oY3vp9lAgKv6SbSXb8Kk+DEEjia5+JDsz6efKJt05AIsrEZoIRRa1EpvrGFGVx4irzCOm5ggm/4m7znRRUVjOGh0KRGPOCl06S05uVE97+b0eqouLqCwqoKqoMPQqLqCysLDZuYzqGc0WkjIHEpuaFg5JsSmpxKamERWXIBM+CtHLSABqJwlAorfxBr1sL93OlqItfFn0JTvKduANegEwBE0kujJIr81ikHswCdWp6N1JNBWKzMFKrLXHia08TEzNMey1hVjdZSiE/owY0tOxjhkTDkTmESPRR9kb1dNR/D4vjpJiKosKqSo8TlVxYWi5qICasjK0kweAn0KnNxCdlERMYjIxySlEJyUTk3TyzySMptbNqSSE6B4kALWTBCDR2/mDfnZV7OKb4m/4piT0qvSeuKXdEDSRVNuP4d7hDKjOILo6haAvhaZCkaL6sXpKiK4pwF5bGHq56oKRAqYBA7CMGol5xAgsI0diGTnytI/z6AjBgJ+qokLKjx2hqriI6uIiqkqKqC4pwlFagqY2H47q2WLjiE5MJiY5mZj6YJScEg5NlqhoucQmRDciAaidJACJvkbTNA47DofD0Dcl35DvyG9QxhA0Mdg7lFHObFIrErDWxuPxZRCk6V4SRfXXBaIi7K6C0M/aQqyecozpaVjCgSj005Ca2mVhQg0GcVaW4ygrpaa0BEdZKY6yEmrKSkPLpSUtDsquZzRb6nqMTuo9OikgRSUkotPrT1uPEKJj9LgA9NRTT/Hwww9TVFTE2LFj+fOf/8w555zTbPnXX3+de++9l/z8fIYMGcJDDz3EZZddFv78+uuv54UXXmiwzaxZs1i9enWr2iMBSAgod5eTW5ob6iUq/YZd5bsIqA1vYTdhZIyWzdCqJPqVW4mpjcPr609loF+zwUgX9GF3FYV7i2zuEqzuUuyWIFHDB4cC0YgRmEeMwDRgAEoEAoSmaXhqnThK60NRyYmwVB4KSK7qqtPWoyg6ohISwyHJHhcfftni4rHHxmGPiw/dySbjkYRotx4VgF599VWuvfZannnmGSZPnszjjz/O66+/zt69e0lJSWlU/osvvuCCCy5g6dKlfPe73+Wll17ioYce4uuvv2b06NFAKAAVFxfz/PPPh7czm83Et7LbXQKQEI15Ah52lO3gm5Jv2Fa6je2l2xtcNquXYLAzUolmWLmZ7HIDibVJuPz9qQhkUhXoRxBTE7WHGH0OrO4yrJ5yrO5SrEEHsUkW4rKSiBuejWX4MMxDh3TJJbTTCfh81JSX4igtxVFegqO0NByW6nuSmpvz6FSKToctNg57bDy2uNBPe1woHNnqQpKtLjiZbXa57CZEM3pUAJo8eTKTJk3iySefBEBVVTIzM7ntttu45557GpWfO3cutbW1vPvuu+F15557LuPGjeOZZ54BQgGoqqqKt99+u1Vt8Hq9eL3e8HuHw0FmZqYEICFaoGkax5zH2F66ne2l2/m27Ft2V+xu1EsE0M8Ux9CgiWEOJyMrAqSGQ9EAqgNpVAfT8GotT3aoC/qweMqxesqw4yI63khcvxjih2SQOHYI9iHZKKbmw1VX01SV2qrKE5fXysuorarEVVVJbXVV6GdVJe4ax+krO4neYAiHIXtc/CmhqWHPkrED5m4SoidpSwCK6LzzPp+PrVu3smDBgvA6nU7HjBkz2LhxY5PbbNy4kXnz5jVYN2vWrEZhZ/369aSkpBAfH893vvMdHnjgARITE5usc+nSpSxevLh9ByNEH6MoCpnRmWRGZ3J5zuVA6G6zPRV72F66nR1lO9hZvpPDjsMc91VxHFhnB+ygUEi2ycOQQB7DayqZWF1AjkeHN5BGdTAVRyCV6kAa5b5+VAXT8RKPqjfhsqfjsqcTnhmosO716XFM3p3YqSXKDrEpVuKykkkcOZD4IenYYkxd3mui6EKXv6ISEskYOrzZcsFAALejmtqqSmqrK+tCUlXdchWu+p9VlXhdtQQDAWrKQr1Np2O0WLHHxWE7qUcpHJjCy6FeJoPR2JGHL0S3F9EAVFZWRjAYJDU1tcH61NRU9uzZ0+Q2RUVFTZYvKioKv589ezY/+MEPyM7O5uDBg/zud7/j0ksvZePGjeibGE+wYMGCBqGqvgdICNE2Zr2ZscljGZs8Nryu2lvNrvJd7Czfyc6ynewo30FRbRGHfBUcAj60A/YUDIqeQaZYhvl9DHZ8y1k1HzLR6yFa0whqepzBJBzBNMq96Rx1DaLSn4GPBPy6OFS9BZ85Dh9xVAbgaAFQEIQvDgGH0Gt+7CYfMXFGYjNiSRicRmxGDLFJVqITLOiNkRt/ozcYwkHpdAI+H67qqlBYqns1eF9d976ykoDPi9/jpqrITVVR4WnrttijGvQshS+9xcZhjY7BEhWNNToaS1Q0FnuUDO4WPV6vfPLgT37yk/DyWWedxZgxYxg0aBDr169n+vTpjcqbzWbMZpnvQ4jOEGuOZUrGFKZkTAmvK3OXsat8FzvKdoR7iio8Fez1lrMXIAqICk2qmGGMJUczkVXrYqTjKKO9OzkrZnX4MbCaBh41mv3OoRxxDqbWn4GqxhPQovHqY/Ca4wgqRhx+I45SOFbqhm15J7VQw2qBqDgzUSnRRCVaiYozY48zExUfetnjzBiMkf/CN5hMobvMkhuPjzyZpmn4Pe66UHTictuJHqXQcm11qLdJDQbw1Drx1DqpOH60VW0x2+xYoqOx2E8KRnWv+vfW+nX1n9vsMthbdBsRDUBJSUno9XqKi4sbrC8uLiYtLa3JbdLS0tpUHiAnJ4ekpCQOHDjQZAASQnStJGsSF/S/gAv6XwCEvrCLaovYUb6DXeW72Fe5j70Veyl2FVPgr6YA2GAGkqOAKCw6I1m6KAZ6gwxzVjLOVcYQ4zeMid3aYD8Ov4W91QMors0h6EtF8Uej+k14ddG4rcl4rEkE9WbcHnAX+Sgtav6xGxa7EXu8ORSO6n7Wh6OoOAtR8WZM1u7x/5SKomCy2jBZbcSn92uxbP0db64Goahh75LHWYPbWYPHWYO3thYAr6sWr6uWaoparP+UhmGxR4UCkv1EMDo1KFlPCVImq00GfosOF9H/Wk0mExMmTGDt2rVceeWVQGgQ9Nq1a/n1r3/d5DZTpkxh7dq13HnnneF1H330EVOmTGmyPMCxY8coLy8nPT29I5svhOggiqKQHpVOelQ6MwfODK+v9lazr3If+yr3sb9yf/inJ+hhj1rJHj18GKuH2NBl8WS9nYFBA4NcLsbUljDS62GccR/GpH3hOoOawgF3OkerUnHXxmDwxmDyGNHXBvErFrzmeLzmuPDLY45D1Zvx1Prx1PopP+Zs9jiMZn04FNnjzNhjTdhizNhiTQ2WTZbuEZQg9Lu31oWOxP4DTlteDQZDvUV1gchdUxNern9fH5Y8NTV4akPr/B43aFq4XJvaqNM1CkrWJnqcwi97FGa7HbPVJj1OolkRvwvs1Vdf5brrruPZZ5/lnHPO4fHHH+e1115jz549pKamcu2119KvXz+WLl0KhG6Dv/DCC1m2bBmXX345r7zyCn/84x/Dt8E7nU4WL17MnDlzSEtL4+DBg9x9993U1NTw7bfftupSl9wGL0T3FVSDHHMeY2/F3nA42le5j+PO402WNyp6Big2cjwBhrsqGe12MNTnI+mUmaCrVRuHXGkUVScQcNkwu/VEO73Yy8vAG6wLRHXhyBQKRl5zHD5LPF5rAn5d6++4Mpj12GNM2OpDUYwJW4wRa7QJa7QJW4wJa3TovdGs7xW9H8GAH4/TWReSHHicTtzO0E9PE+/dtU48NTUEfN7TV94cRcFstYXCkM1e9zMKyynvzfbQssVmx2Sr+8xmw2yzy1inHqZH3QYP8OSTT4YnQhw3bhx/+tOfmDx5MgAXXXQRWVlZrFq1Klz+9ddf5w9/+EN4IsTly5eHJ0J0u91ceeWVfPPNN1RVVZGRkcEll1zC/fff32jwdHMkAAnR8zh9Tg5UHQgHovqA5Aq4miyfoLOQHdQzxFXLaFc5w3w+cnz+RrMUBTQdhz0pHHMk4ayNQe81Y3dr2B1OLGUlKIHQbf9BnfGkXqN4vOZY/LZE/LGp+KwJ+AxReDQTgWDbeiQMRl1dMDKGglGMqS4whcKTNdqINcqENcaIxWZE0fX8sHQyv89b12vUOCjVBylPg/ehy3QBv69D9m80WzDbbHXBKBSK6pdNVtuJdS0sG7rR9Ay9XY8LQN2NBCAhegdVUylwFjToKdpfuZ/DjsNoNP7Tp0fHQEM0AwIKA90uhjjLGeJzkuUPYGviT2VZMJo8ZxrlNXGovigsHgN2lx9bVRWG8tLQCO1TBPRmfKYYfJY4gqlZ+BP7EYhKxG+Nw2ew49XMeP163B6NgO/0zys7maKAJcqIJcqELTr0MxSQQsuWKAMWeygwme2h5d7Sw3SqgM8XHqfkra3FW+vEU7/sCr33umrxnPTeU1uLz1WL1+VqX8/TKfQGQ2hMls2GyWINBySTte69zYbJasVstWGs+2kKv06UN1os6HTSI9USCUDtJAFIiN7N5XdxsOpgg2C0r3IfDl/zkxKmGqLorxrp5/WRVVvNME8FA/0BMgIBTp1Bx6fpyQukcqQ2hVpPAkrAjsVnIMrtJ6qyHFNJEbTiC1aLiiWYMQg1pT/++HQCUUn4rXF4DVF4seDx6XC7gnicfryu1s06fSqdQcFqN4aCkz30MkcZG60LL0cZMVsNva6n6VTBQACvqxafy1UXpFx43Sfe+1wuvO4Tn/ncDdf7XLX43O4Ob5fRbKkLTDbMVismqxWjxVoXrJpatoSDltFiCQcqk8WKwWzudeFXAlA7SQASou/RNI1iVzH7KveRV50XfuU78qnwVDS7nR6FVJ2NtAD093jI9lSS7ffS3x8gM9C456hCi+KomkyhOwGXOxYlYMOkWrD5FeweH9bqylDvkaO6Ve3WxcRgTE1Fl56OmjyAYGI6gZhkAtY4fKZo/HobbreK1+kPD+R21y2rgTP7868oYLY1DEWWuh4ls82I2Waoe52ybDVEdM6lrqaqQXxuN766gOTzuOsCkjv0vu6n1+1q8N7ndoW28bhDZV21qMFgxzdQUUIByVIXlqyh8GSs+3li2RLuuWr0PrxsxWi2RHzQuQSgdpIAJIQ4WbW3OhyGjjiOkO/I57DjMEccR/AEW35qfAJGMgKQ6XWT7XPSPxAgPRAkPRAgJRBs1HtUosVxTEvimD8Jpy8eTY3BGLRgDRiI8gWIqa3GUlmGvqwEzdn8HWkn08fFYUhPx5iWhiEtFWNaOoa0NEhKJRidSMAcgzeoDwUkZyAclDzOU37W+vF72vdFbDDqMNUFIovNULd8IiA1Dk4n3vfWy3Wno2kaQb//pADlOhGU3G78Hs+JgFX/3hMKUyeW3fg97tCyx9Pk5dmOYDRb6nqfLJgstnCvU7hn6qT3GUOG03/k6A7dvwSgdpIAJIRoDVVTKXWVcrTmaPh1rOYYR2uOcqTmSIuX1AB0QKJmINUfpJ/fTT+/l4y6cFQfkqJO+hMd1BSKSOCYlswxLZlifwKBQBwm7ESpemICGnFuJ9GOCsxVZehKS9BcTQ8CP5VitWJITMSQmIg+KQlDUlLdciKGxCQMyaH3xCbgw4i3tumg5HUH8LkCeFwBvC4/PncAryuA1x2giWFXbaLolJNCUuhlshpD45lsBkzWpnudzHYDZqsBnb7v9D61RFNV/D7vieB0SmjyeepDlbtu2R1eDgep8GehcprWtvFqAOdc8UOm/fT6Dj02CUDtJAFICNERqr3V4UBU/ypwFlBYW0hhbSF+1X/aOuyaQlpAI8PvpV/AR3ogQEYgSFpdSEoOBjn5a92h2SjW4inS4inR4qgKxqEEozAFrdiDBuwBiPJ4iXJUYKoog/JStDaOVVEslpMCUuinISnpRFhKqnufmITOHprEUFM1fJ66MFQXjuqDkbe27r27mc9dZ3657mRGs75xD1N9aLKHApSl7vP6nqnQZT1Dt5gJvLvSNK3u0SsnQtPJ4chf1+sU7oFyu/F7PQw6+xyGTD6vQ9siAaidJAAJITqbqqlUeCoodBZSUFtAUW1Rg3BUWFtItff044AMGiSrkBLwk+r3kRIMhaKUQDC8nBoINhiLpGoKZcRSpMVTrMVTqcbjV2NAjcKgWbBoJqxBhSivD5vLgbmmCn1VJWpFGZqr7WFJnxCPIT4BfXx83XJ8aDk+AX18HIaEus/i49HHxqKcMveOpmkE/Gpdz5If38khqT401Qbwuv0nBagTn7f3sh2AwaQLXbaz1wUkqwGTVY/ZasRk1Yd6n6yGBj9N1vpeKgMGo65PXr7rahKA2kkCkBCiO3D5XeEwVOCsC0m1BRQ6Q+tKXCUEtdZ9uds0SAqopAVCISklECAlGCQpECQ5qJIUDJIUbBiUPJqRYi2eYkJBqUxJwK1LAC0aI3YsmokoVSHa7yPGU4O1pgpTTRW6qkrU8nLUVl5+a0BR0MfGog+HorjQGKa40M/wKzYWXUwM+uhodDEx6GzNz/qsBtUGvUv1QcrrCtRdovPjqVsf7nk6KUR1xLekTqeEQpGtPiDpMVlOBKRmA9RJP/vSAPIzJQGonSQACSF6goAaoNRVSrGrmBJXCaXu0HKpqzS8vtRdSq2/ttV1WlVIDAZJDvpJDKokBIMkBlUSg8GTXqH3Nk1DAbyagUqiqdSiKdeiqSSaaiUWny4GXdCKAStGzYRF02MNKlgDKjZPLebaGoxOBzpHFWpVJWp1y2OmWqTToYuOrgtE0eijY9DHRKOLjkEfE9PEuoY/6y/VnUpTNXzeIN7aEwPB64OTzx3E6/bjcwdDQcodaPCz/tVR37J6g+5EgLLoG/QwmU8Z/3RqkDJZ9BgtBnS9fPoCCUDtJAFICNGb1PprQwHppFBUv1zmLgu/3IG2Xd4yqxpJwSCxapC4oEqsGnqFl4PB0PuT1kWrKjqgWrNRrsVQSTQVWgxVSjQuXSwQhaJZMahmLJoRs6rDoirY/D6s7lrMrhoMTgdKrRO1pga1uhrNf/qxVKel16OPikIXG9sgRDUdnKJDoSo6OtRbFR2NYrU2HaA0Db83GB4MHg5Inroep7ogdWpwOjlMdcQlvHpGS6jnqT5AmereG09aNlkM4ct6Jouhbhs9RnPdenP37Y2SANROEoCEEH2Ry++i1F1KmbuMcnc55Z7y8M8Kd0WD920NS/UUTSOmLhTFBk8OTMG60HTyZ8HwskE11IWluhcx1OhicBvjCehiQGdHr9gw6yyYMWFBT1TAh83vxup1Yfa6MLpr0btqUWprUB01BGtqCDoc0BEBymBo3Ptkt6Oz2dFFRYWWT35F2dHZbKFXfTl76L1iMjUIU6qqnehR8gROCVNBfG4/XncwdNddeOD4yeWDBANtv0urJTq9EgpG5pMCksWAyazHeMpyfeAy1oepum1sMSYs9lMngmgfCUDtJAFICCFa5vK7woHI4XNQ5a2i2lsd/nnqcrWvuk2X4k5l0LQTgUkNElMflE4OUCetiwloKKoZjxpFNXaqNXv4pwM7HmMsfmMMqikGnTEKM2ZMihEbBmwoRKt+ovwerD4XFq8LkycUoHT1PU8OB0GHg2BNDQTObBbuZun1J8LRqS+7DaXBOvspn58IUjqrFcVqRWe1ohnM+AOEe5TC4ai5ZXcQf9260Dah7QL+jgtS42YO4Pw5gzusPmjb97ehQ/cshBCiT7AZbdiMNjKjM1u9jT/op9rXOBydHJ6aClPeoJeAolBu0FOOHhpNH9k8s6oSq3qIVV3EBYvDvUyxapCUumW7VyVa04hSVaLqLtMRNOHTbDg1Gw5sFJvt1JitOGLtePRR+I2p+I3RBI3RKDobRoyYNCMm9JhVBbsaxBbwYg14Mfu9mH1ujF4PBo8LvceN4nahuVyoJ700T92kmsFgKGTV1LTxrJyG0YjOYkFnsYSDkdliwWK11q2zoLPaTixbrOhi6sparOisFjSThaDRQkBnJqg3E8SIHyNB9PhVHX6/gt8bCk5+T12I8p4IU35vEF9dmDLbIhtBJAAJIYToEka9kSRrEknWpDZt5wl4GgemuiBV/77KW4XD6zipXBUBLYhXp6NEp6PkDNpr0DTsqkqU6iRadYQDUlRdWIque0UFNWx16+2qil1VsaqAakJVrfixUmuyUmG04oyy4tQs1GLFq0vEbxhA0GhHNUaBwYZeZ8GomDDpjJgVI2adHhsKNtWPNejDUheoTH4vRp8Hg8eN4nGhutwnhanaUKCqdaG63Sdmffb7Uf3+jg9WJzEaDJhPDlAWC4rNemK5Lmzp4qzYTecDWZ3WltORACSEEKJbsxgspBnSSLOntXobTdNwBVzhcBS+FNfEpTmnz4nT78Tpc1Ljr6HWV4uKSkBRqNbrqW7nHIg6TcOuurBrTuyqik3VsGkaVlXFqml171WsqoYt2PBzW93nimpApxpQNCNq0EytZsKHGZfJjNdsxh9vJaC3EDREo+pTCBqsaAYbGK0oOhN6xYxBMWBQTBjQYUKPAR1G9Jg0MKlBTEEfRp8Xg9+Lwe9D7/OieTyobjeqx43m9qB6PGhud926E8uodZfGAgFUpxOcTk43dFsXFU309Ont++W2gwQgIYQQvY6iKNiNduxGO/2i+rVp2/rwVB+Manw1DQLSyYHJ6XdS669t8HL6anD5a6kNuNHQUBWFGr1CDR1551QQi+rEptVgVTWsmtowSGmhXimrv2HA0msaelXFpGlYNQ2LqmHRNHSqDkUzoKlm/KqJWsy4MeM1mfFZLPh1FgL6upClj0UzWFGNoYCF0YZiMGPAhFExYsCAUdFjUgwYFB0mTcGkahiDfkwBLwafD4PPi33ypA78fbSdBCAhhBDiJCeHp1RSz7geVVPxBDzhkOTyu3D6nbj8LtwBN66Aq9GyK3Divdvvwu2vxeWvxeV34wq4cQc9qHUPVfPodHgAOuEpHQZNxaK5MKu1WDQNi6Zi1kJhyaxpmOuCk9mvYfHVrTspUIXL1a0zahqoRtAMaKoRTTNRvbuI706Z0vGNb+0xRmzPQgghRC+mU3ThweIdRdM0vEHviaBUF5qaC1Vuv7tBWXfQjSfgwRPw4A648dS93AEPnqAXrS5cBRQFp6Lg7MTpfmYFt/Pdzqv+tCQACSGEED2EoihYDBYsBkuH161pGj7Vh9vvxhMMhSRv0Isn6MEbqPsZ9IYC1CnrTv08vJ3fgyfgwhNw4w2v9+IJ+skcck6HH0NbSAASQgghBIqiYNabMevNkW5Kl+iec1kLIYQQQnQiCUBCCCGE6HMkAAkhhBCiz5EAJIQQQog+RwKQEEIIIfocCUBCCCGE6HMkAAkhhBCiz5EAJIQQQog+RwKQEEIIIfocCUBCCCGE6HMkAAkhhBCiz5EAJIQQQog+RwKQEEIIIfocCUBCCCGE6HMMkW5Ad6RpGgAOhyPCLRFCCCFEa9V/b9d/j7dEAlATampqAMjMzIxwS4QQQgjRVjU1NcTGxrZYRtFaE5P6GFVVKSgoIDo6GkVROrRuh8NBZmYmR48eJSYmpkPr7g7k+Hq+3n6Mvf34oPcfoxxfz9dZx6hpGjU1NWRkZKDTtTzKR3qAmqDT6ejfv3+n7iMmJqbX/sMGOb7eoLcfY28/Puj9xyjH1/N1xjGeruenngyCFkIIIUSfIwFICCGEEH2OBKAuZjabue+++zCbzZFuSqeQ4+v5evsx9vbjg95/jHJ8PV93OEYZBC2EEEKIPkd6gIQQQgjR50gAEkIIIUSfIwFICCGEEH2OBCAhhBBC9DkSgLrQU089RVZWFhaLhcmTJ7Nly5ZIN6lVli5dyqRJk4iOjiYlJYUrr7ySvXv3Nihz0UUXoShKg9cvf/nLBmWOHDnC5Zdfjs1mIyUlhd/+9rcEAoGuPJQmLVq0qFHbhw8fHv7c4/Hwq1/9isTERKKiopgzZw7FxcUN6uiux1YvKyur0TEqisKvfvUroOedv08//ZTvfe97ZGRkoCgKb7/9doPPNU1j4cKFpKenY7VamTFjBvv3729QpqKigmuuuYaYmBji4uK48cYbcTqdDcps376dadOmYbFYyMzMZPny5Z19aGEtHaPf72f+/PmcddZZ2O12MjIyuPbaaykoKGhQR1PnfdmyZQ3KROoYT3cOr7/++kZtnz17doMy3fkcnu74mvrvUVEUHn744XCZ7nz+WvO90FF/O9evX8/ZZ5+N2Wxm8ODBrFq1qmMOQhNd4pVXXtFMJpP23HPPaTt37tRuuukmLS4uTisuLo50005r1qxZ2vPPP6/t2LFDy83N1S677DJtwIABmtPpDJe58MILtZtuukkrLCwMv6qrq8OfBwIBbfTo0dqMGTO0b775Rnv//fe1pKQkbcGCBZE4pAbuu+8+bdSoUQ3aXlpaGv78l7/8pZaZmamtXbtW++qrr7Rzzz1XO++888Kfd+djq1dSUtLg+D766CMN0NatW6dpWs87f++//772+9//XnvzzTc1QHvrrbcafL5s2TItNjZWe/vtt7Vt27Zp3//+97Xs7GzN7XaHy8yePVsbO3astmnTJu2zzz7TBg8erF199dXhz6urq7XU1FTtmmuu0Xbs2KG9/PLLmtVq1Z599tmIH2NVVZU2Y8YM7dVXX9X27Nmjbdy4UTvnnHO0CRMmNKhj4MCB2pIlSxqc15P/u43kMZ7uHF533XXa7NmzG7S9oqKiQZnufA5Pd3wnH1dhYaH23HPPaYqiaAcPHgyX6c7nrzXfCx3xt/PQoUOazWbT5s2bp+3atUv785//rOn1em316tXtPgYJQF3knHPO0X71q1+F3weDQS0jI0NbunRpBFt1ZkpKSjRA++STT8LrLrzwQu2OO+5odpv3339f0+l0WlFRUXjd008/rcXExGher7czm3ta9913nzZ27NgmP6uqqtKMRqP2+uuvh9ft3r1bA7SNGzdqmta9j605d9xxhzZo0CBNVVVN03r2+Tv1y0VVVS0tLU17+OGHw+uqqqo0s9msvfzyy5qmadquXbs0QPvyyy/DZT744ANNURTt+PHjmqZp2l/+8hctPj6+wfHNnz9fGzZsWCcfUWNNfYGeasuWLRqgHT58OLxu4MCB2mOPPdbsNt3lGJsLQFdccUWz2/Skc9ia83fFFVdo3/nOdxqs6ynnT9Mafy901N/Ou+++Wxs1alSDfc2dO1ebNWtWu9ssl8C6gM/nY+vWrcyYMSO8TqfTMWPGDDZu3BjBlp2Z6upqABISEhqsf/HFF0lKSmL06NEsWLAAl8sV/mzjxo2cddZZpKamhtfNmjULh8PBzp07u6bhLdi/fz8ZGRnk5ORwzTXXcOTIEQC2bt2K3+9vcO6GDx/OgAEDwueuux/bqXw+H//617/4n//5nwYP++3J5+9keXl5FBUVNThnsbGxTJ48ucE5i4uLY+LEieEyM2bMQKfTsXnz5nCZCy64AJPJFC4za9Ys9u7dS2VlZRcdTetVV1ejKApxcXEN1i9btozExETGjx/Pww8/3ODyQnc/xvXr15OSksKwYcO45ZZbKC8vD3/Wm85hcXEx7733HjfeeGOjz3rK+Tv1e6Gj/nZu3LixQR31ZTriu1MehtoFysrKCAaDDU4yQGpqKnv27IlQq86MqqrceeednH/++YwePTq8/qc//SkDBw4kIyOD7du3M3/+fPbu3cubb74JQFFRUZPHX/9ZJE2ePJlVq1YxbNgwCgsLWbx4MdOmTWPHjh0UFRVhMpkafamkpqaG292dj60pb7/9NlVVVVx//fXhdT35/J2qvj1Ntffkc5aSktLgc4PBQEJCQoMy2dnZjeqo/yw+Pr5T2n8mPB4P8+fP5+qrr27wYMnbb7+ds88+m4SEBL744gsWLFhAYWEhjz76KNC9j3H27Nn84Ac/IDs7m4MHD/K73/2OSy+9lI0bN6LX63vVOXzhhReIjo7mBz/4QYP1PeX8NfW90FF/O5sr43A4cLvdWK3WM263BCDRJr/61a/YsWMHGzZsaLD+5ptvDi+fddZZpKenM336dA4ePMigQYO6upltcumll4aXx4wZw+TJkxk4cCCvvfZau/7j6q7+/ve/c+mll5KRkRFe15PPX1/n9/v58Y9/jKZpPP300w0+mzdvXnh5zJgxmEwmfvGLX7B06dJu/5iFn/zkJ+Hls846izFjxjBo0CDWr1/P9OnTI9iyjvfcc89xzTXXYLFYGqzvKeevue+F7k4ugXWBpKQk9Hp9o9HvxcXFpKWlRahVbffrX/+ad999l3Xr1tG/f/8Wy06ePBmAAwcOAJCWltbk8dd/1p3ExcUxdOhQDhw4QFpaGj6fj6qqqgZlTj53PenYDh8+zJo1a/j5z3/eYrmefP7q29PSf29paWmUlJQ0+DwQCFBRUdGjzmt9+Dl8+DAfffRRg96fpkyePJlAIEB+fj7QM46xXk5ODklJSQ3+TfaGc/jZZ5+xd+/e0/43Cd3z/DX3vdBRfzubKxMTE9Pu/0GVANQFTCYTEyZMYO3ateF1qqqydu1apkyZEsGWtY6mafz617/mrbfe4uOPP27U5dqU3NxcANLT0wGYMmUK3377bYM/WPV/sEeOHNkp7T5TTqeTgwcPkp6ezoQJEzAajQ3O3d69ezly5Ej43PWkY3v++edJSUnh8ssvb7FcTz5/2dnZpKWlNThnDoeDzZs3NzhnVVVVbN26NVzm448/RlXVcPibMmUKn376KX6/P1zmo48+YtiwYd3i0kl9+Nm/fz9r1qwhMTHxtNvk5uai0+nCl466+zGe7NixY5SXlzf4N9nTzyGEemQnTJjA2LFjT1u2O52/030vdNTfzilTpjSoo75Mh3x3tnsYtWiVV155RTObzdqqVau0Xbt2aTfffLMWFxfXYPR7d3XLLbdosbGx2vr16xvcjulyuTRN07QDBw5oS5Ys0b766istLy9Pe+edd7ScnBztggsuCNdRf7vjJZdcouXm5mqrV6/WkpOTu8Wt4nfddZe2fv16LS8vT/v888+1GTNmaElJSVpJSYmmaaFbOQcMGKB9/PHH2ldffaVNmTJFmzJlSnj77nxsJwsGg9qAAQO0+fPnN1jfE89fTU2N9s0332jffPONBmiPPvqo9s0334TvgFq2bJkWFxenvfPOO9r27du1K664osnb4MePH69t3rxZ27BhgzZkyJAGt1BXVVVpqamp2s9+9jNtx44d2iuvvKLZbLYuuw2+pWP0+Xza97//fa1///5abm5ug/8u6++e+eKLL7THHntMy83N1Q4ePKj961//0pKTk7Vrr722WxxjS8dXU1Oj/eY3v9E2btyo5eXlaWvWrNHOPvtsbciQIZrH4wnX0Z3P4en+jWpa6DZ2m82mPf3004227+7n73TfC5rWMX8762+D/+1vf6vt3r1be+qpp+Q2+J7oz3/+szZgwADNZDJp55xzjrZp06ZIN6lVgCZfzz//vKZpmnbkyBHtggsu0BISEjSz2awNHjxY++1vf9tgHhlN07T8/Hzt0ksv1axWq5aUlKTdddddmt/vj8ARNTR37lwtPT1dM5lMWr9+/bS5c+dqBw4cCH/udru1W2+9VYuPj9dsNpt21VVXaYWFhQ3q6K7HdrIPP/xQA7S9e/c2WN8Tz9+6deua/Dd53XXXaZoWuhX+3nvv1VJTUzWz2axNnz690XGXl5drV199tRYVFaXFxMRoN9xwg1ZTU9OgzLZt27SpU6dqZrNZ69evn7Zs2bKuOsQWjzEvL6/Z/y7r53baunWrNnnyZC02NlazWCzaiBEjtD/+8Y8NAkQkj7Gl43O5XNoll1yiJScna0ajURs4cKB20003Nfofxu58Dk/3b1TTNO3ZZ5/VrFarVlVV1Wj77n7+Tve9oGkd97dz3bp12rhx4zSTyaTl5OQ02Ed7KHUHIoQQQgjRZ8gYICGEEEL0ORKAhBBCCNHnSAASQgghRJ8jAUgIIYQQfY4EICGEEEL0ORKAhBBCCNHnSAASQgghRJ8jAUgIIYQQfY4EICGEaEJWVhaPP/54pJshhOgkEoCEEBF3/fXXc+WVVwJw0UUXceedd3bZvletWkVcXFyj9V9++SU333xzl7VDCNG1DJFugBBCdAafz4fJZDrj7ZOTkzuwNUKI7kZ6gIQQ3cb111/PJ598whNPPIGiKCiKQn5+PgA7duzg0ksvJSoqitTUVH72s//frt2ERLXGcRz/jtUsfBsTBS0OHKJCJwdfyoWzqKghN4U7LWIECcHUhQtx7cK3CRp8iWgVmbQoCBRmkdRUQi7UCkJqQFAHXcjEJIZDi8SxhdxDk3Gv92bdgfl9YGDmOS//5zyL4cf/OV6i0ah17dmzZ2ltbaWtrY28vDyqq6sB8Pv9uFwuMjIyMAyD5uZmYrEYAC9fvqShoYHPnz9b9To7O4GdW2BLS0vU1NSQmZlJdnY2tbW1RCIR63hnZydlZWWMjIxgmiYOh4PLly+zvr7+exdNRP4TBSARSRoDAwNUVVXR2NjIysoKKysrGIbB2toa586do7y8nNevX/PkyRMikQi1tbUJ1w8PD2O325mcnOTOnTsApKWlMTg4yPv37xkeHub58+d0dHQA4Ha76e/vJzs726rX3t6+Y17xeJyamhpWV1eZmJjg6dOnLCwsUFdXl3De/Pw8o6OjBAIBAoEAExMT9PX1/abVEpFfoS0wEUkaDocDu91Oeno6BQUF1vitW7coLy+np6fHGrt79y6GYTA3N8fx48cBOHbsGDdu3Ei45/fvE5mmSVdXF01NTdy+fRu73Y7D4cBmsyXU+1EwGGR2dpbFxUUMwwDg/v37nDhxgpmZGSorK4HtoHTv3j2ysrIA8Hq9BINBuru7f21hRGTPqQMkIknv3bt3vHjxgszMTOtTVFQEbHdd/nLy5Mkd1z579ozz589z+PBhsrKy8Hq9fPr0iS9fvuy6figUwjAMK/wAOJ1OcnJyCIVC1phpmlb4ASgsLOTjx4//6llF5M9QB0hEkl4sFuPSpUv4fL4dxwoLC63vGRkZCcfC4TAXL17k+vXrdHd3k5uby6tXr7h27Rpfv34lPT19T+d54MCBhN82m414PL6nNURkbygAiUhSsdvtbG5uJoxVVFTw+PFjTNNk//7d/229efOGeDzOzZs3SUvbbng/evToH+v9qLi4mOXlZZaXl60u0IcPH1hbW8PpdO56PiKSPLQFJiJJxTRNpqamCIfDRKNR4vE4LS0trK6ucuXKFWZmZpifn2d8fJyGhoa/DS9Hjx5lY2ODoaEhFhYWGBkZsV6O/r5eLBYjGAwSjUZ/ujXm8XhwuVxcvXqVt2/fMj09TX19PWfOnOHUqVN7vgYi8vspAIlIUmlvb2ffvn04nU7y8/NZWlri0KFDTE5Osrm5yYULF3C5XLS1tZGTk2N1dn6mtLQUv9+Pz+ejpKSEBw8e0Nvbm3CO2+2mqamJuro68vPzd7xEDdtbWWNjYxw8eJDTp0/j8Xg4cuQIDx8+3PPnF5E/w7a1tbX1f09CRERE5E9SB0hERERSjgKQiIiIpBwFIBEREUk5CkAiIiKSchSAREREJOUoAImIiEjKUQASERGRlKMAJCIiIilHAUhERERSjgKQiIiIpBwFIBEREUk53wAO8vrL3kNSCgAAAABJRU5ErkJggg==" + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "# use vvag to get the losses and gradients with different random circuit instances\n", + "QAOA_vvag = K.jit(\n", + " K.vvag(partial(QAOAansatz_rnd, g=rnd_graphs_w), argnums=0, vectorized_argnums=0)\n", + ")\n", + "\n", + "params_rnd = K.implicit_randn(\n", + " shape=[ncircuits, 2 * nlayers], stddev=0.1\n", + ") # initial parameters\n", + "if type(K).__name__ == \"JaxBackend\":\n", + " opt = K.optimizer(optax.adam(1e-2))\n", + "else:\n", + " opt = K.optimizer(tf.keras.optimizers.Adam(1e-2))\n", + "\n", + "list_of_loss = [[] for i in range(ncircuits)]\n", + "\n", + "for i in range(2000):\n", + " loss, grads = QAOA_vvag(params_rnd)\n", + " params_rnd = opt.update(grads, params_rnd) # gradient descent\n", + "\n", + " # visualise the progress\n", + " clear_output(wait=True)\n", + " list_of_loss = np.hstack((list_of_loss, K.numpy(loss)[:, np.newaxis]))\n", + " plt.xlabel(\"Iteration\")\n", + " plt.ylabel(\"Cost\")\n", + " for index in range(ncircuits):\n", + " plt.plot(range(i + 1), list_of_loss[index])\n", + " legend = [f\"circuit {leg}\" for leg in range(ncircuits)]\n", + " plt.legend(legend)\n", + " plt.show()" + ], + "metadata": { + "collapsed": false + } + }, + { + "cell_type": "code", + "execution_count": 29, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Circuit #0\n", + "cost: 0.034298673272132874\n", + "max prob: 0.061373475939035416\n", + "bit strings: ['000000111111']\n", + "\n", + "Circuit #1\n", + "cost: 0.034172173589468\n", + "max prob: 0.06166590005159378\n", + "bit strings: ['000000111111', '111111000000']\n", + "\n", + "Circuit #2\n", + "cost: 0.03370116278529167\n", + "max prob: 0.06229700148105621\n", + "bit strings: ['000000111111']\n", + "\n", + "Circuit #3\n", + "cost: 0.035995520651340485\n", + "max prob: 0.0600101463496685\n", + "bit strings: ['111111000000']\n", + "\n", + "Circuit #4\n", + "cost: 0.03770057111978531\n", + "max prob: 0.05639055743813515\n", + "bit strings: ['000000111111']\n", + "\n", + "Circuit #5\n", + "cost: 0.042536795139312744\n", + "max prob: 0.047668762505054474\n", + "bit strings: ['111111000000']\n", + "\n" + ] + } + ], + "source": [ + "# print QAOA results\n", + "for num_circuit in range(ncircuits):\n", + " print(f\"Circuit #{num_circuit}\")\n", + " c = QAOAansatz_rnd(\n", + " params=params_rnd[num_circuit], g=rnd_graphs_w, return_circuit=True\n", + " )\n", + " loss = QAOAansatz_rnd(params=params_rnd[num_circuit], g=rnd_graphs_w)\n", + "\n", + " # find the states with max probabilities\n", + " probs = K.numpy(c.probability())\n", + " max_prob = max(probs)\n", + " index = np.where(probs == max_prob)[0]\n", + " states = []\n", + " for i in index:\n", + " states.append(f\"{bin(i)[2:]:0>{c._nqubits}}\")\n", + " print(f\"cost: {K.numpy(loss)}\\nmax prob: {max_prob}\\nbit strings: {states}\\n\")" + ], + "metadata": { + "collapsed": false, + "ExecuteTime": { + "end_time": "2023-07-04T01:38:27.853435600Z", + "start_time": "2023-07-04T01:37:48.381823700Z" + } + } + }, + { + "cell_type": "markdown", + "source": [ + "On average, QAOA with random quantum dropout improves the probability of correct solution (max prob) by nearly 0.02 compared to regular QAOA.\n", + "\n", + "Compared with isotropic quantum dropout, the standard deviation of the probability of correct solution obtained by random quantum dropout is smaller, but the upper limit is lower. From the physical picture, QAOA after random quantum dropout works like a quantum interferometer. QAOA circuits with different dropouts over driving layers may work through a focusing effect on the true ground state: different clause sets lead to different energy landscapes and minima, whose configurations receive constructive interference and enhanced amplitudes. Being the only common minimum of all $\\hat{H}_{C_i}$ irrespective of the dropouts, the true ground state remains stand-out through all driving layers. Please refer to [Wang, Zheng, Wu, and Zhang (2023)](https://journals.aps.org/prresearch/abstract/10.1103/PhysRevResearch.5.023171) for more analysis and details." + ], + "metadata": { + "collapsed": false + } + }, + { + "cell_type": "code", + "execution_count": 30, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "OS info: Linux-5.4.119-1-tlinux4-0010.2-x86_64-with-glibc2.28\n", + "Python version: 3.10.11\n", + "Numpy version: 1.23.5\n", + "Scipy version: 1.11.0\n", + "Pandas version: 2.0.2\n", + "TensorNetwork version: 0.4.6\n", + "Cotengra is not installed\n", + "TensorFlow version: 2.12.0\n", + "TensorFlow GPU: []\n", + "TensorFlow CUDA infos: {'cpu_compiler': '/dt9/usr/bin/gcc', 'cuda_compute_capabilities': ['sm_35', 'sm_50', 'sm_60', 'sm_70', 'sm_75', 'compute_80'], 'cuda_version': '11.8', 'cudnn_version': '8', 'is_cuda_build': True, 'is_rocm_build': False, 'is_tensorrt_build': True}\n", + "Jax version: 0.4.13\n", + "Jax installation doesn't support GPU\n", + "JaxLib version: 0.4.13\n", + "PyTorch version: 2.0.1\n", + "PyTorch GPU support: False\n", + "PyTorch GPUs: []\n", + "Cupy is not installed\n", + "Qiskit version: 0.24.1\n", + "Cirq version: 1.1.0\n", + "TensorCircuit version 0.10.0\n" + ] + } + ], + "source": [ + "tc.about()" + ], + "metadata": { + "collapsed": false, + "ExecuteTime": { + "end_time": "2023-07-04T01:40:31.531945400Z", + "start_time": "2023-07-04T01:40:31.478214100Z" + } + } + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "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.10.10" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/docs/source/tutorials/qcloud_sdk.ipynb b/docs/source/tutorials/qcloud_sdk.ipynb new file mode 100644 index 00000000..3080c416 --- /dev/null +++ b/docs/source/tutorials/qcloud_sdk.ipynb @@ -0,0 +1,1315 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "38c73e8c", + "metadata": {}, + "source": [ + "# tensorcircuit SDK for QCloud(230220 ver)" + ] + }, + { + "cell_type": "markdown", + "id": "f77a17c8", + "metadata": {}, + "source": [ + "## import the package\n", + "\n", + "``apis`` is temporarily as the entry point submodule for qcloud" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "8595b0b0", + "metadata": {}, + "outputs": [], + "source": [ + "import tensorcircuit as tc\n", + "from tensorcircuit.cloud import apis\n", + "from tensorcircuit.cloud.wrapper import batch_expectation_ps\n", + "from tensorcircuit.compiler.qiskit_compiler import qiskit_compile\n", + "import numpy as np" + ] + }, + { + "cell_type": "markdown", + "id": "faece4fa", + "metadata": {}, + "source": [ + "## setup the token\n", + "\n", + "The users need an API token from tQuK to connect to the server and submit tasks, the token only need be set once and it is then written to the local computer" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "06e38047", + "metadata": {}, + "outputs": [], + "source": [ + "apis.set_token(\"foobar\")\n", + "# only required running once for a given laptop" + ] + }, + { + "cell_type": "markdown", + "id": "f3e53835", + "metadata": {}, + "source": [ + "## list providers/devices/properties\n", + "\n", + "Get basic info of devices and device information" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "59dc5c6f", + "metadata": {}, + "outputs": [], + "source": [ + "apis.list_providers()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d231e5c3", + "metadata": {}, + "outputs": [], + "source": [ + "apis.list_devices(\"tencent\")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "be41de9d", + "metadata": {}, + "outputs": [], + "source": [ + "apis.list_devices(\"tencent\", state=\"on\")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "e2ab9a2c", + "metadata": { + "scrolled": false + }, + "outputs": [], + "source": [ + "apis.list_properties(device=\"9gmon\")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "aaec43c3", + "metadata": {}, + "outputs": [], + "source": [ + "d = apis.get_device(\"9gmon\")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "f57b5239", + "metadata": {}, + "outputs": [], + "source": [ + "d.list_properties()[\"bits\"][8]" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "f6470346", + "metadata": {}, + "outputs": [], + "source": [ + "d.topology()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "1823e172", + "metadata": {}, + "outputs": [], + "source": [ + "d.native_gates()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "e407e5c7", + "metadata": {}, + "outputs": [], + "source": [ + "d.topology_graph(visualize=True)" + ] + }, + { + "cell_type": "markdown", + "id": "148b9b40", + "metadata": {}, + "source": [ + "## Task submit and the results" + ] + }, + { + "cell_type": "markdown", + "id": "dc7645ab", + "metadata": {}, + "source": [ + "Basic task submission syntax below, here we use a simulator backend on tQuK `simulator:tc`" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "94404d7a", + "metadata": {}, + "outputs": [], + "source": [ + "c = tc.Circuit(1)\n", + "c.H(0)\n", + "\n", + "t = apis.submit_task(device=\"simulator:tc\", circuit=c, shots=1024)\n", + "print(t.details())\n", + "t.results(blocked=True)" + ] + }, + { + "cell_type": "markdown", + "id": "fab152bb", + "metadata": {}, + "source": [ + "``blocked=True`` can wait until the task is finished or failed (rasing an error)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "617cbd84", + "metadata": {}, + "outputs": [], + "source": [ + "t.status()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "a6e686be", + "metadata": {}, + "outputs": [], + "source": [ + "t.get_device()" + ] + }, + { + "cell_type": "markdown", + "id": "7e550c80", + "metadata": {}, + "source": [ + "resubmit a job with the same source (device/shots) and command (circuit)\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "77dddd16", + "metadata": {}, + "outputs": [], + "source": [ + "t1 = t.resubmit()\n", + "t1.details(blocked=True, prettify=True)" + ] + }, + { + "cell_type": "markdown", + "id": "e227f48a", + "metadata": {}, + "source": [ + "``t.details`` can also permit the ``blocked=True`` option, which waits until the task is finished or failed (no error raised).\n", + "\n", + "Also note by using ``prettfiy=True`` option, we have python datatime object for the timestamp which is easy to read but hard for io (not directly json serializable anymore) " + ] + }, + { + "cell_type": "markdown", + "id": "8c795e68", + "metadata": {}, + "source": [ + "## local provider enable quick debugging and testing\n", + "\n", + "TC comes with a local provider which behaves as a simple cloud provider but run the circuit locally" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "570159fe", + "metadata": {}, + "outputs": [], + "source": [ + "apis.set_provider(\"local\")\n", + "# using tc simulator on local device: your own laptop is your server\n", + "apis.list_devices()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "fd99ee01", + "metadata": {}, + "outputs": [], + "source": [ + "c = tc.Circuit(2)\n", + "c.h(0)\n", + "c.cx(0, 1)\n", + "\n", + "# exactly the same API as tQuK\n", + "t = apis.submit_task(circuit=c, device=\"testing\", shots=8192)\n", + "t.results(blocked=True)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "bb62ed45", + "metadata": {}, + "outputs": [], + "source": [ + "tl = apis.list_tasks()\n", + "tl" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "045a19ef", + "metadata": {}, + "outputs": [], + "source": [ + "id_ = tl[0].__str__()\n", + "print(id_)\n", + "t = apis.get_task(id_)\n", + "t.details()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "510f8d82", + "metadata": {}, + "outputs": [], + "source": [ + "id_ = tl[0].__str__()\n", + "print(id_.split(\"~~\")[1])\n", + "t = apis.get_task(id_)\n", + "t.details()" + ] + }, + { + "cell_type": "markdown", + "id": "2e359725", + "metadata": {}, + "source": [ + "As shown above, the task can be indexed either with device information or not (as long as we use ``set_provider``)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "307eeb42", + "metadata": {}, + "outputs": [], + "source": [ + "# back to tencent server for demonstration below\n", + "apis.set_provider(\"tencent\")" + ] + }, + { + "cell_type": "markdown", + "id": "9bf0d5ca", + "metadata": {}, + "source": [ + "## GHZ state on real device and readout mitigation" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "2cc7c82c", + "metadata": {}, + "outputs": [], + "source": [ + "nqubit = 9\n", + "shots = 4096\n", + "c = tc.Circuit(nqubit)\n", + "c.H(8)\n", + "c.cnot(8, 4)\n", + "c.cnot(4, 0)\n", + "c.cnot(0, 2)\n", + "c.cnot(2, 6)\n", + "\n", + "# above we dirct assign physical qubits\n", + "\n", + "t = apis.submit_task(\n", + " circuit=c, shots=shots, device=\"9gmon\", enable_qos_qubit_mapping=False\n", + ")\n", + "raw_count = t.results(blocked=True)\n", + "# blocked = True will block the process until the result is returned\n", + "# the default behavior is blocked=False, where only one query is made and raise error when the task is incomplete\n", + "\n", + "# note we explicitly turn off qubit mapping from qos, which gurantee our logical circuit are identical to the physical one.\n", + "# but one should ensure the topology link in the logical circuit is compatible with the target device" + ] + }, + { + "cell_type": "markdown", + "id": "80b617b4", + "metadata": {}, + "source": [ + "In the below, we use tensorcircuit builtin powerful tool for readout mitigation: ``tc.results.rem.ReadoutMit``, it supports various method for calibriation and mitigation" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "f2ed2419", + "metadata": {}, + "outputs": [], + "source": [ + "mit = tc.results.rem.ReadoutMit(\"9gmon?o=0\")\n", + "# here o=0 is a short for disable qubit mapping and gate decomposition at the backend server\n", + "mit.cals_from_system(nqubit, shots, method=\"local\")\n", + "# local calibriation\n", + "miti_count = mit.apply_correction(raw_count, nqubit, method=\"constrained_least_square\")" + ] + }, + { + "cell_type": "markdown", + "id": "07cacbed", + "metadata": {}, + "source": [ + "By attaching ``?o=0`` after the device string, we have the same effect of setting ``enable_qos_qubit_mapping=False`` (o=1)\n", + "and ``enable_qos_gate_decomposition=False`` (o=2), and both of them are by default True (o=3).\n", + "\n", + "We can define the REM class by using more customizable function." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "09a498b1", + "metadata": {}, + "outputs": [], + "source": [ + "def run(cs, shots):\n", + " \"\"\"batch mode\"\"\"\n", + " ts = apis.submit_task(\n", + " circuit=cs, shots=shots, device=\"9gmon\", enable_qos_qubit_mapping=False\n", + " )\n", + " return [t.results(blocked=True) for t in ts]\n", + "\n", + "\n", + "mit = tc.results.rem.ReadoutMit(run)\n", + "mit.cals_from_system(nqubit, shots, method=\"local\")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "139700b3", + "metadata": {}, + "outputs": [], + "source": [ + "raw_count = tc.results.counts.marginal_count(raw_count, [8, 4, 0, 2, 6])\n", + "miti_count = tc.results.counts.marginal_count(miti_count, [8, 4, 0, 2, 6])\n", + "# only keep the result for qubit 8, 4, 0, 2, 6 and in that exact order" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "093ec74c", + "metadata": {}, + "outputs": [], + "source": [ + "tc.results.counts.plot_histogram([raw_count, miti_count])" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "e4bd6569", + "metadata": {}, + "outputs": [], + "source": [ + "ideal_count = tc.results.counts.vec2count(c.probability(), prune=True)\n", + "# we can obtain analytic count results by ``c.probability()`` method, and ``vec2count`` with transform the vector as a dict\n", + "\n", + "ideal_count = tc.results.counts.marginal_count(ideal_count, [8, 4, 0, 2, 6])\n", + "tc.results.counts.kl_divergence(\n", + " ideal_count, raw_count\n", + "), tc.results.counts.kl_divergence(ideal_count, miti_count)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "91720f2c", + "metadata": {}, + "outputs": [], + "source": [ + "# we can directly check local readout matrix on each qubit\n", + "print(\"readout matrix\")\n", + "for i, m in enumerate(mit.single_qubit_cals):\n", + " print(\"qubit %s:\" % i)\n", + " print(m)" + ] + }, + { + "cell_type": "markdown", + "id": "1c88101c", + "metadata": {}, + "source": [ + "Apart from calibriation from real experiments, we can access the readout error matrix from API (which is fast but may be not that up to date)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "ecca1aab", + "metadata": {}, + "outputs": [], + "source": [ + "mit = tc.results.rem.ReadoutMit(\"9gmon?o=0\")\n", + "mit.cals_from_api(nqubit)\n", + "mit.single_qubit_cals[0]" + ] + }, + { + "cell_type": "markdown", + "id": "422d0a1b", + "metadata": {}, + "source": [ + "## Abstraction of three layers of qubits and the mappings\n", + "\n", + "In the above example, the circuit is not compiled by the frontend: tc or backend: qos, in the follows, we will introduce circuit compiling and the new abstraction on different level of qubits.\n", + "\n", + "New abstraction on qubits: positional qubits, logical qubits, physical qubits, we need two more mappings: ``positional_logical_mapping`` and ``logical_physical_mapping``.\n", + "\n", + "The separation between positional and logical qubits is due to partial measurement, while the seperation between logical and physical qubits are from circuit compiling onto hardware, including swap inserting (where the last swap is omitted, current qos behavior), qubit routing (i.e. initial mapping).\n", + "\n", + "Now we do the GHZ preparation on another chip, but use mapping and partial measurement abstraction this time" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "4a12dd32", + "metadata": {}, + "outputs": [], + "source": [ + "# logical circuit for GHZ-5\n", + "\n", + "c = tc.Circuit(5)\n", + "c.h(0)\n", + "for i in range(4):\n", + " c.cx(i, i + 1)\n", + "for i in range(5):\n", + " c.measure_instruction(i)\n", + "\n", + "# We map the circuit on the physical qubits by hand\n", + "\n", + "c1 = c.initial_mapping({0: 8, 1: 4, 2: 0, 3: 2, 4: 6}, n=9)\n", + "positional_logical_mapping = c1.get_positional_logical_mapping()\n", + "positional_logical_mapping" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "fb7e4b4b", + "metadata": {}, + "outputs": [], + "source": [ + "c1.draw() # circuit after mapping" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "39d0c16c", + "metadata": {}, + "outputs": [], + "source": [ + "t = apis.submit_task(\n", + " circuit=c1, shots=shots, device=\"9gmon\", enable_qos_qubit_mapping=False\n", + ")\n", + "raw_count = t.results(blocked=True)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "c2bbecbd", + "metadata": {}, + "outputs": [], + "source": [ + "logical_physical_mapping = t.details()[\"optimization\"][\"pairs\"]\n", + "logical_physical_mapping\n", + "# this mapping is identical since we disable qos qubit mapping above" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d59fa9b0", + "metadata": {}, + "outputs": [], + "source": [ + "mit = tc.results.rem.ReadoutMit(\"9gmon?o=0\")\n", + "mit.cals_from_system(9, shots, method=\"local\")\n", + "miti_count = mit.apply_correction(\n", + " raw_count,\n", + " [8, 4, 0, 2, 6],\n", + " positional_logical_mapping=positional_logical_mapping,\n", + " logical_physical_mapping=logical_physical_mapping,\n", + " method=\"square\",\n", + ")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "07d7acfd", + "metadata": {}, + "outputs": [], + "source": [ + "plot_histogram([raw_count, miti_count])" + ] + }, + { + "cell_type": "markdown", + "id": "03d5c6cf", + "metadata": {}, + "source": [ + "We can have another way to understand logical qubits: we could treat 0-4 in the original circuit as logical qubits, then we will have the following convention and the circuit after initial mapping as the physical one (abstraction reference shift)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "ad81141a", + "metadata": {}, + "outputs": [], + "source": [ + "miti_count = mit.apply_correction(\n", + " raw_count,\n", + " [0, 1, 2, 3, 4],\n", + " positional_logical_mapping=None,\n", + " logical_physical_mapping={0: 8, 1: 4, 2: 0, 3: 2, 4: 6},\n", + " method=\"square\",\n", + ")\n", + "# note how the None by default implies an identity mapping" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "f564d224", + "metadata": {}, + "outputs": [], + "source": [ + "plot_histogram([raw_count, miti_count])\n", + "# the results should be exactly the same, since they are just the same thing using different reference system" + ] + }, + { + "cell_type": "markdown", + "id": "32d753ff", + "metadata": {}, + "source": [ + "The above abstraction is rather low level where the compiling is done by hand and we recommend the following api for users (**the highly recommended way**).\n", + "\n", + "The recommended approach heavily depends on the frontend compiling via qiskit (builtin support in tc)." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "2711fdb3", + "metadata": {}, + "outputs": [], + "source": [ + "# 0. acquire readout mitigation class\n", + "\n", + "mit = tc.results.rem.ReadoutMit(\"20xmon?o=0\")\n", + "mit.cals_from_system(20)\n", + "\n", + "# 1. define the logical circuit\n", + "\n", + "n = 5\n", + "c = tc.Circuit(n)\n", + "c.h(0)\n", + "for i in range(n - 1):\n", + " c.cx(i, i + 1)\n", + "for i in reversed(range(n)):\n", + " c.measure_instruction(i)\n", + "\n", + "# 2. compile the circuit\n", + "\n", + "d = apis.get_device(\"20xmon\")\n", + "\n", + "c1, info = qiskit_compile(\n", + " c,\n", + " compiled_options={\n", + " \"basis_gates\": d.native_gates(),\n", + " \"optimization_level\": 3,\n", + " \"coupling_map\": d.topology(),\n", + " },\n", + ")\n", + "\n", + "\n", + "# 3. submit the job and get the raw result\n", + "\n", + "t = apis.submit_task(\n", + " circuit=c1,\n", + " shots=8192,\n", + " device=d,\n", + " enable_qos_qubit_mapping=False,\n", + " enable_qos_gate_decomposition=False,\n", + ")\n", + "raw_count = t.results(blocked=True)\n", + "\n", + "# 4. obtain the mitigated result in terms of distribution or expectation\n", + "\n", + "print(\"distribution\", mit.apply_correction(raw_count, n, method=\"square\", **info))\n", + "print(\"\", mit.expectation(raw_count, [0, 1], **info))" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "84b26c27", + "metadata": {}, + "outputs": [], + "source": [ + "info # compiling info and the qubit mapping are recorded automatically" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "f5b63e46", + "metadata": {}, + "outputs": [], + "source": [ + "tc.results.counts.plot_histogram(\n", + " [raw_count, mit.apply_correction(raw_count, n, method=\"square\", **info)]\n", + ")" + ] + }, + { + "cell_type": "markdown", + "id": "e2b28dbe", + "metadata": {}, + "source": [ + "And the **all-in-one API**: ``batch_expectation_ps`` with circuit generating, grouping, compiling, optimization and error mitigation support is as shown below, the API is also consistent with numerical simulations, basically the API capture all the workflow shown in above cell with extra enhancement" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "35ba52b4", + "metadata": {}, + "outputs": [], + "source": [ + "c = tc.Circuit(2)\n", + "c.h(0)\n", + "c.cz(0, 1)\n", + "c.x(1)\n", + "print(\"numerical results: [, ]\", batch_expectation_ps(c, [[1, 0], [1, 3]]))\n", + "print(\n", + " \"hardware results: [, ]\",\n", + " batch_expectation_ps(c, [[1, 0], [1, 3]], \"20xmon\"),\n", + ")\n", + "print(\n", + " \"numerical results: + 0.5* \",\n", + " batch_expectation_ps(c, [[1, 0], [1, 3]], ws=[1, 0.5]),\n", + ")\n", + "print(\n", + " \"hardware results: + 0.5* \",\n", + " batch_expectation_ps(c, [[1, 0], [1, 3]], \"20xmon\", ws=[1, 0.5]),\n", + ")" + ] + }, + { + "cell_type": "markdown", + "id": "f13baa43", + "metadata": {}, + "source": [ + "batch submission is possible with multiple circuits in a list and the return is a list of task, respectively. The batch mechanism are supported both on real chips and simulators." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "db3e59b5", + "metadata": {}, + "outputs": [], + "source": [ + "# we can also do a batch submission for the real hardware chip, simply by provide a circuit list\n", + "\n", + "c = tc.Circuit(2)\n", + "c.h(0)\n", + "\n", + "c1 = tc.Circuit(2)\n", + "c1.h(1)\n", + "\n", + "ts = apis.submit_task(device=\"20xmon\", circuit=[c, c1], shots=1024)\n", + "\n", + "for t in ts:\n", + " print(t.results(blocked=True))" + ] + }, + { + "cell_type": "markdown", + "id": "4d2c56b6", + "metadata": {}, + "source": [ + "## measure on partial of the qubits\n", + "\n", + "Note the return order should ideally follow the measure order in the instructions" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "9ac0b2fe", + "metadata": {}, + "outputs": [], + "source": [ + "# directly partial measure via qiskit\n", + "\n", + "from qiskit.circuit import QuantumCircuit\n", + "\n", + "qc = QuantumCircuit(9, 9)\n", + "qc.x(8)\n", + "qc.x(6)\n", + "qc.measure(8, 8)\n", + "qc.measure(2, 2)\n", + "qc.measure(6, 6)\n", + "\n", + "t = apis.submit_task(circuit=qc, shots=shots, device=\"9gmon?o=0\")\n", + "print(t.results(blocked=True))" + ] + }, + { + "cell_type": "markdown", + "id": "7ea3ea0f", + "metadata": {}, + "source": [ + " The above case also indicates that tc ``submit_task`` API directly support Qiskit ``QuantumCircuit`` object" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "5ce371e9", + "metadata": {}, + "outputs": [], + "source": [ + "# directly partial measure on tc\n", + "\n", + "# recommended approach\n", + "\n", + "nqubit = 9\n", + "shots = 4096\n", + "c = tc.Circuit(nqubit)\n", + "c.x(8)\n", + "c.x(6)\n", + "c.measure_instruction(8)\n", + "c.measure_instruction(2)\n", + "c.measure_instruction(6)\n", + "\n", + "t = apis.submit_task(circuit=c, shots=shots, device=\"9gmon?o=0\")\n", + "print(t.results(blocked=True))\n", + "print(c.get_positional_logical_mapping())" + ] + }, + { + "cell_type": "markdown", + "id": "e431f862", + "metadata": {}, + "source": [ + "partial measurment also supported via the simulator on the cloud" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "c6150317", + "metadata": {}, + "outputs": [], + "source": [ + "nqubit = 9\n", + "shots = 4096\n", + "c = tc.Circuit(nqubit)\n", + "c.x(8)\n", + "c.x(6)\n", + "c.measure_instruction(8)\n", + "c.measure_instruction(2)\n", + "c.measure_instruction(6)\n", + "\n", + "t = apis.submit_task(circuit=c, shots=shots, device=\"simulator:tc\")\n", + "print(t.results(blocked=True))" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "40bdf80d", + "metadata": {}, + "outputs": [], + "source": [ + "nqubit = 9\n", + "shots = 4096\n", + "c = tc.Circuit(nqubit)\n", + "c.x(8)\n", + "c.x(6)\n", + "c.measure_instruction(8)\n", + "c.measure_instruction(2)\n", + "c.measure_instruction(6)\n", + "\n", + "t = apis.submit_task(circuit=c, shots=shots, device=\"simulator:aer\")\n", + "print(t.results(blocked=True))" + ] + }, + { + "cell_type": "markdown", + "id": "20b8b1d5", + "metadata": {}, + "source": [ + "## two level compiling system\n", + "\n", + "We provide compiling support at frond end (via tc-qiskit pipeline) and at back end (in qos).\n", + "The front end option is enabled by ``compiled=True`` (default to False) and also with an optional dict for ``qiskit.transpile`` arguments called ``compiled_options``. For advanced users, we recommand you to separately deal with the circuit compiling and submission as we discussed above as the recommended approach. The backend qos compiling is controlled by ``enable_qos_qubit_mapping`` and ``enable_qos_gate_decomposition`` (all default to True). The ``?o=int`` str after the device name can overide qos compiling options. We strongly recommend the users only use one part of the compiling in case confusing and conflicts. For front end compiling, though the built-in compiling via ``compiled`` switch in ``submit_task`` is handy, we recommend the advanced user to use standalone compiling module as shown above, i.e. explicitly call ``qiskit_compile``, the advantage for the latter is we can obtain qubit mapping information at the same time for further error mitigation pipelines." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "03aed751", + "metadata": {}, + "outputs": [], + "source": [ + "# directly use built-in mitigation with expectation evaluation + front-end (tc/qiskit) compiling system\n", + "\n", + "nqubit = 3\n", + "shots = 8192\n", + "c = tc.Circuit(nqubit)\n", + "c.h(0)\n", + "c.rz(0, theta=0.4)\n", + "c.x(0)\n", + "c.y(0)\n", + "c.h(1)\n", + "c.rx(2, theta=0.7)\n", + "c.ry(1, theta=-1.2)\n", + "c.cnot(0, 1)\n", + "c.cnot(2, 0)\n", + "c.h(1)\n", + "c.x(2)\n", + "\n", + "print(\"exact: \", [np.real(c.expectation_ps(z=[i])).tolist() for i in range(nqubit)])\n", + "\n", + "t = apis.submit_task(\n", + " circuit=c,\n", + " shots=shots,\n", + " device=\"9gmon\",\n", + " compiled=True,\n", + " enable_qos_qubit_mapping=False,\n", + " enable_qos_gate_decomposition=False,\n", + ")\n", + "\n", + "ct = t.results(blocked=True)\n", + "\n", + "mit = tc.results.readout_mitigation.ReadoutMit(\"9gmon?o=0\")\n", + "mit.cals_from_system(3, shots=8192, method=\"local\")\n", + "\n", + "print(\n", + " \"experiments (mitigated directly via expectation): \",\n", + " [mit.expectation(ct, [i]) for i in range(nqubit)],\n", + ")\n", + "\n", + "# no need to provider mapping in mit as there is no mapping in this case,\n", + "# compiled=True itself doesn't enable front end qubit routing\n", + "\n", + "print(\n", + " \"experiments (mitigated using lstm): \",\n", + " [\n", + " tc.results.counts.expectation(mit.apply_correction(ct, 3, method=\"square\"), [i])\n", + " for i in range(nqubit)\n", + " ],\n", + ")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "60e9d6d0", + "metadata": {}, + "outputs": [], + "source": [ + "c.draw() # target circuit: mimic a VQA case" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "a45fa6f5", + "metadata": {}, + "outputs": [], + "source": [ + "# use backend compiling system enabled by qos and the very handy built-in auto mitigation\n", + "# (only works without qubit mapping at front end)\n", + "\n", + "nqubit = 3\n", + "shots = 8192\n", + "c = tc.Circuit(nqubit)\n", + "c.h(0)\n", + "c.rz(0, theta=0.4)\n", + "c.x(0)\n", + "c.y(0)\n", + "c.h(1)\n", + "c.rx(2, theta=0.7)\n", + "c.ry(1, theta=-1.2)\n", + "c.cnot(0, 1)\n", + "c.cnot(2, 0)\n", + "c.h(1)\n", + "c.x(2)\n", + "\n", + "print(\"exact: \", [np.real(c.expectation_ps(z=[i])) for i in range(nqubit)])\n", + "\n", + "t = apis.submit_task(\n", + " circuit=c,\n", + " shots=shots,\n", + " device=\"9gmon\",\n", + " compiled=False,\n", + " enable_qos_qubit_mapping=True,\n", + " enable_qos_gate_decomposition=True,\n", + ")\n", + "\n", + "ct = t.results(blocked=True, mitigated=True)\n", + "# auto mitigation with backend qubit mapping considered\n", + "\n", + "\n", + "print(\n", + " \"experiments (mitigated): \",\n", + " [tc.results.counts.expectation(ct, [i]) for i in range(nqubit)],\n", + ")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "cf2d2690", + "metadata": {}, + "outputs": [], + "source": [ + "# inspect compiling results from the tc and qos for the task, we can directly get the circuit objects from prettified details\n", + "\n", + "c_complied_before_qos = t.details(prettify=True)[\"frontend\"]\n", + "c_complied_after_qos = t.details(prettify=True)[\"backend\"]" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b2b4b1e9", + "metadata": {}, + "outputs": [], + "source": [ + "c_complied_before_qos.draw()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "a1af33b6", + "metadata": {}, + "outputs": [], + "source": [ + "c_complied_after_qos.draw(output=\"mpl\")" + ] + }, + { + "cell_type": "markdown", + "id": "1ad85e14", + "metadata": {}, + "source": [ + "dry run mode to query compiled circuit only from qos (not really sending the circuit to chips), we can use ``qos_dry_run=True`` option\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "7fee9a15", + "metadata": {}, + "outputs": [], + "source": [ + "nqubit = 3\n", + "shots = 8192\n", + "c = tc.Circuit(nqubit)\n", + "c.h(0)\n", + "c.h(1)\n", + "c.rx(2, theta=0.7)\n", + "c.ry(1, theta=-1.2)\n", + "c.cnot(0, 1)\n", + "c.cnot(2, 0)\n", + "c.h(1)\n", + "\n", + "\n", + "t = apis.submit_task(\n", + " circuit=c,\n", + " shots=shots,\n", + " device=\"9gmon\",\n", + " compiled=True,\n", + " enable_qos_qubit_mapping=True,\n", + " enable_qos_gate_decomposition=True,\n", + " qos_dry_run=True,\n", + ")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "e262a682", + "metadata": { + "scrolled": false + }, + "outputs": [], + "source": [ + "t.details(prettify=True, blocked=True)[\"backend\"].draw()" + ] + }, + { + "cell_type": "markdown", + "id": "ea015c48", + "metadata": {}, + "source": [ + "## scalable readout simulation and mitigation\n", + "\n", + "Via TensorCircuit, we provide the capability to do scalable (20+ qubits) readout error simulation and mitigation" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "2b509155", + "metadata": {}, + "outputs": [], + "source": [ + "# scalable readout error simulation on tQuK with tensorcircuit backend using tensor network approach\n", + "\n", + "c = tc.Circuit(3)\n", + "t = apis.submit_task(circuit=c, device=\"simulator:tcn1\", shots=8192)\n", + "t.results(blocked=True)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "71f634ff", + "metadata": {}, + "outputs": [], + "source": [ + "t.results(mitigated=True)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "7533330d", + "metadata": { + "scrolled": false + }, + "outputs": [], + "source": [ + "c = tc.Circuit(25)\n", + "t = apis.submit_task(circuit=c, device=\"simulator:tcn1\", shots=8192)\n", + "t.results(blocked=True)" + ] + }, + { + "cell_type": "markdown", + "id": "06281585", + "metadata": {}, + "source": [ + "Simulator device also support batch submission" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "cdfb639c", + "metadata": {}, + "outputs": [], + "source": [ + "# batch submission to the simulator\n", + "cs = []\n", + "for i in range(15):\n", + " c = tc.Circuit(15)\n", + " c.x(i)\n", + " cs.append(c)\n", + "ts = apis.submit_task(circuit=cs, device=\"simulator:tcn1\", shots=8192)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "f8d8217e", + "metadata": {}, + "outputs": [], + "source": [ + "# mitigated with m3 scalable on count dict\n", + "c = tc.Circuit(15)\n", + "c.x(0)\n", + "t = apis.submit_task(circuit=c, device=\"simulator:tcn1\", shots=8192)\n", + "\n", + "mit = tc.results.readout_mitigation.ReadoutMit(\"simulator:tcn1\")\n", + "mit.cals_from_system(15)\n", + "\n", + "raw_count = t.results(blocked=True)\n", + "mit.apply_correction(raw_count, 15, method=\"M3_auto\")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "292f9b11", + "metadata": {}, + "outputs": [], + "source": [ + "# mitigated scalable directly on expectation: not a wrapper for count but a new algorithm!\n", + "# see eq 6 in https://arxiv.org/pdf/2006.14044.pdf\n", + "\n", + "mit.expectation(raw_count, [0])" + ] + }, + { + "cell_type": "markdown", + "id": "bd454737", + "metadata": {}, + "source": [ + "## list task and get previous task\n", + "\n", + "get history tasks and their details, so that your experimental data are always accessible with detailed meta data on the cloud" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "2b80411b", + "metadata": {}, + "outputs": [], + "source": [ + "apis.list_tasks()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "8dffbcfd", + "metadata": {}, + "outputs": [], + "source": [ + "apis.list_tasks(device=\"9gmon\")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "5e29190d", + "metadata": {}, + "outputs": [], + "source": [ + "t = apis.get_task(\"d77bec2f-ab07-4dbc-a273-caa8b23a921c\")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d24ddc18", + "metadata": { + "scrolled": false + }, + "outputs": [], + "source": [ + "t.details()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "706c9c08", + "metadata": {}, + "outputs": [], + "source": [ + "t = apis.get_task(\"tencent::9gmon~~e32bb488-5ee9-4b07-8217-1e78ceb4bde3\")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "8ad61cb9", + "metadata": {}, + "outputs": [], + "source": [ + "t.details(prettify=True)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "a5426c97", + "metadata": {}, + "outputs": [], + "source": [ + "t.results()" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "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.8.0" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/docs/source/tutorials/qcloud_sdk_demo.ipynb b/docs/source/tutorials/qcloud_sdk_demo.ipynb new file mode 100644 index 00000000..8f0a97a5 --- /dev/null +++ b/docs/source/tutorials/qcloud_sdk_demo.ipynb @@ -0,0 +1,1648 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "a246d497", + "metadata": {}, + "source": [ + "# Demo on TensorCircuit SDK for Tencent Quantum Cloud\n", + "\n", + "This notebook is not served as a full user manual for TC SDK for QCLOUD. Instead,it only highlighted a limited subset of features that TC enabled, mainly for live demo and tutorials.\n", + "\n", + "## Import and Setup" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "id": "ff5cc3c8", + "metadata": {}, + "outputs": [], + "source": [ + "import tensorcircuit as tc" + ] + }, + { + "cell_type": "markdown", + "id": "e66850fe", + "metadata": {}, + "source": [ + "The following two line are by default and no need to run explicitly, \n", + "unless you activate tencent cloud service for the first time when you have to set up the token copied from the web" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "id": "4352cc25", + "metadata": {}, + "outputs": [], + "source": [ + "# tc.cloud.apis.set_token(\"123456isnotgoodpassword\")\n", + "# tc.cloud.apis.set_provider(\"tencent\")" + ] + }, + { + "cell_type": "markdown", + "id": "2aa760be", + "metadata": {}, + "source": [ + "## Devices and properties" + ] + }, + { + "cell_type": "markdown", + "id": "735c2d63", + "metadata": {}, + "source": [ + "**Provider agnostic**: The SDK architecture is designed to be provider agnostic so that we have the potential to support multiple QPU providers in the future. And from the user's pespective, no code will change to deploy the quantum program on different QPU providers. We also support some third party and local providers now internally, and the list will be expanding...\n" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "id": "72106e97", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "[tencent, local]" + ] + }, + "execution_count": 7, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "tc.cloud.apis.list_providers()" + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "id": "e35e1518", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "[tencent::simulator:tc,\n", + " tencent::simulator:aer,\n", + " tencent::simulator:tcn1,\n", + " tencent::tianshu_s1,\n", + " tencent::tianxuan_s1]" + ] + }, + "execution_count": 8, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "tc.cloud.apis.list_devices()" + ] + }, + { + "cell_type": "markdown", + "id": "4f5ce14e", + "metadata": {}, + "source": [ + "list only devices online that are currently available with `state` argument" + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "id": "b8456acc", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "[tencent::simulator:tc,\n", + " tencent::simulator:aer,\n", + " tencent::tianshu_s1,\n", + " tencent::tianxuan_s1]" + ] + }, + "execution_count": 9, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "tc.cloud.apis.list_devices(\"tencent\", state=\"on\")" + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "id": "7ced9cc6", + "metadata": {}, + "outputs": [], + "source": [ + "device_name = \"tianxuan_s1\" # 9 qubits chip\n", + "\n", + "# get the device object\n", + "\n", + "d = tc.cloud.apis.get_device(device_name)" + ] + }, + { + "cell_type": "code", + "execution_count": 46, + "id": "0e0633c5", + "metadata": { + "scrolled": false + }, + "outputs": [ + { + "data": { + "image/png": "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\n", + "text/plain": [ + "" + ] + }, + "execution_count": 46, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "from IPython.display import Image\n", + "\n", + "Image(filename=\"../statics/tianxuan_s1.png\")" + ] + }, + { + "cell_type": "code", + "execution_count": 11, + "id": "0461fc24", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "{'id': 'tianxuan_s1',\n", + " 'type': 'CHIP',\n", + " 'qubits': 9,\n", + " 'T1': 30.593555450439453,\n", + " 'T2': 12.94344425201416,\n", + " 'Err': {'SQ': 0.0008366666666666665,\n", + " 'CZ': 0.01615125,\n", + " 'Readout': {'F0': 0.0209, 'F1': 0.0849111111111111}},\n", + " 'report': {'at': 1683908208,\n", + " 'consumed': 181292436838,\n", + " 'done': 32164,\n", + " 'total': 32175,\n", + " 'waiting': 5039},\n", + " 'at': 1685496713,\n", + " 'state': 'on',\n", + " 'links': {(0, 1): {'A': 0, 'B': 1, 'CZErrRate': 0.0135, 'GateLenInNs': 75.56},\n", + " (0, 2): {'A': 0, 'B': 2, 'CZErrRate': 0.02358, 'GateLenInNs': 78.74},\n", + " (0, 3): {'A': 0, 'B': 3, 'CZErrRate': 0.01899, 'GateLenInNs': 79.94},\n", + " (0, 4): {'A': 0, 'B': 4, 'CZErrRate': 0.03357, 'GateLenInNs': 79.93},\n", + " (1, 5): {'A': 1, 'B': 5, 'CZErrRate': 0.00552, 'GateLenInNs': 74.94},\n", + " (2, 6): {'A': 2, 'B': 6, 'CZErrRate': 0.01951, 'GateLenInNs': 79.71},\n", + " (3, 7): {'A': 3, 'B': 7, 'CZErrRate': 0.00422, 'GateLenInNs': 65.04},\n", + " (4, 8): {'A': 4, 'B': 8, 'CZErrRate': 0.01032, 'GateLenInNs': 76.07}},\n", + " 'bits': {0: {'Freqency': 3974.78,\n", + " 'Qubit': 0,\n", + " 'ReadoutF0Err': 0.035,\n", + " 'ReadoutF1Err': 0.0876,\n", + " 'SingleQubitErrRate': 0.00079,\n", + " 'SingleQubitGateLenInNs': 40,\n", + " 'T1': 34.524,\n", + " 'T2': 9.62},\n", + " 1: {'Freqency': 4180.96,\n", + " 'Qubit': 1,\n", + " 'ReadoutF0Err': 0.0308,\n", + " 'ReadoutF1Err': 0.0732,\n", + " 'SingleQubitErrRate': 0.00064,\n", + " 'SingleQubitGateLenInNs': 40,\n", + " 'T1': 34.459,\n", + " 'T2': 8.321},\n", + " 2: {'Freqency': 4106.44,\n", + " 'Qubit': 2,\n", + " 'ReadoutF0Err': 0.0192,\n", + " 'ReadoutF1Err': 0.0728,\n", + " 'SingleQubitErrRate': 0.00085,\n", + " 'SingleQubitGateLenInNs': 40,\n", + " 'T1': 18.917,\n", + " 'T2': 5.222},\n", + " 3: {'Freqency': 4657.5,\n", + " 'Qubit': 3,\n", + " 'ReadoutF0Err': 0.0073,\n", + " 'ReadoutF1Err': 0.1156,\n", + " 'SingleQubitErrRate': 0.00124,\n", + " 'SingleQubitGateLenInNs': 40,\n", + " 'T1': 31.079,\n", + " 'T2': 42.79},\n", + " 4: {'Freqency': 4405.96,\n", + " 'Qubit': 4,\n", + " 'ReadoutF0Err': 0.0117,\n", + " 'ReadoutF1Err': 0.0483,\n", + " 'SingleQubitErrRate': 0.0006,\n", + " 'SingleQubitGateLenInNs': 40,\n", + " 'T1': 32.353,\n", + " 'T2': 9.3},\n", + " 5: {'Freqency': 4371.74,\n", + " 'Qubit': 5,\n", + " 'ReadoutF0Err': 0.0125,\n", + " 'ReadoutF1Err': 0.0604,\n", + " 'SingleQubitErrRate': 0.00058,\n", + " 'SingleQubitGateLenInNs': 40,\n", + " 'T1': 30.132,\n", + " 'T2': 6.954},\n", + " 6: {'Freqency': 4247.08,\n", + " 'Qubit': 6,\n", + " 'ReadoutF0Err': 0.0465,\n", + " 'ReadoutF1Err': 0.1351,\n", + " 'SingleQubitErrRate': 0.00102,\n", + " 'SingleQubitGateLenInNs': 40,\n", + " 'T1': 34.827,\n", + " 'T2': 4.462},\n", + " 7: {'Freqency': 4462.26,\n", + " 'Qubit': 7,\n", + " 'ReadoutF0Err': 0.0096,\n", + " 'ReadoutF1Err': 0.0525,\n", + " 'SingleQubitErrRate': 0.0006,\n", + " 'SingleQubitGateLenInNs': 40,\n", + " 'T1': 28.269,\n", + " 'T2': 13.429},\n", + " 8: {'Freqency': 4335.34,\n", + " 'Qubit': 8,\n", + " 'ReadoutF0Err': 0.0155,\n", + " 'ReadoutF1Err': 0.1187,\n", + " 'SingleQubitErrRate': 0.00121,\n", + " 'SingleQubitGateLenInNs': 40,\n", + " 'T1': 30.782,\n", + " 'T2': 16.393}},\n", + " 'langs': ['tQASM', 'eQASM'],\n", + " 'memo': 'tQLab 9Gmon',\n", + " 'usage': '9gmon?o=0 \\nthe o(ptimized) to specify optimization level bits: both = 3 (bits 11) = gate decomposition = 2 (bit 10) | qubit mapping = 1 (bit 01)',\n", + " 'native_gates': ['h', 'rz', 'x', 'y', 'z', 'cz', 'cx']}" + ] + }, + "execution_count": 11, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "d.list_properties()" + ] + }, + { + "cell_type": "code", + "execution_count": 12, + "id": "b44f1844", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "{'Freqency': 4106.44,\n", + " 'Qubit': 2,\n", + " 'ReadoutF0Err': 0.0192,\n", + " 'ReadoutF1Err': 0.0728,\n", + " 'SingleQubitErrRate': 0.00085,\n", + " 'SingleQubitGateLenInNs': 40,\n", + " 'T1': 18.917,\n", + " 'T2': 5.222}" + ] + }, + "execution_count": 12, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "d.list_properties()[\"bits\"][2]" + ] + }, + { + "cell_type": "code", + "execution_count": 13, + "id": "ae4798dc", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "(['h', 'rz', 'x', 'y', 'z', 'cz', 'cx'],\n", + " 'tianxuan_s1',\n", + " tencent,\n", + " [[0, 1],\n", + " [6, 2],\n", + " [4, 0],\n", + " [0, 4],\n", + " [8, 4],\n", + " [1, 5],\n", + " [3, 7],\n", + " [0, 3],\n", + " [2, 0],\n", + " [5, 1],\n", + " [3, 0],\n", + " [7, 3],\n", + " [0, 2],\n", + " [2, 6],\n", + " [4, 8],\n", + " [1, 0]])" + ] + }, + "execution_count": 13, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# some meta data for the device\n", + "\n", + "d.native_gates(), d.name, d.provider, d.topology()" + ] + }, + { + "cell_type": "markdown", + "id": "ccd597ae", + "metadata": {}, + "source": [ + "The native gate set and the coupling map information is essential for TC to transpile the circuits so that they conform the standard of the corresponding devices." + ] + }, + { + "cell_type": "markdown", + "id": "dcea5cfe", + "metadata": {}, + "source": [ + "## Tasks" + ] + }, + { + "cell_type": "markdown", + "id": "3ad38549", + "metadata": {}, + "source": [ + "Submit a simple two-qubit task.\n", + "\n", + "Note that there is no need to explicitly add any measurement operations to the circuit. By default, t.results will return the number of (Z-basis) measurement outcomes for all (in this case 2) qubits in the specified circuit" + ] + }, + { + "cell_type": "code", + "execution_count": 14, + "id": "01a0fb92", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "{'00': 494, '11': 391, '10': 80, '01': 59}" + ] + }, + "execution_count": 14, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "c = tc.Circuit(2)\n", + "c.H(0)\n", + "c.cx(0, 1)\n", + "\n", + "t = tc.cloud.apis.submit_task(device=d, circuit=c, shots=1024)\n", + "\n", + "t.results() # this will wait until the result is return" + ] + }, + { + "cell_type": "code", + "execution_count": 15, + "id": "33b9eb5a", + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "
     ┌───┐     \n",
+       "q_0: ┤ H ├──■──\n",
+       "     └───┘┌─┴─┐\n",
+       "q_1: ─────┤ X ├\n",
+       "          └───┘
" + ], + "text/plain": [ + " ┌───┐ \n", + "q_0: ┤ H ├──■──\n", + " └───┘┌─┴─┐\n", + "q_1: ─────┤ X ├\n", + " └───┘" + ] + }, + "execution_count": 15, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "c.draw()" + ] + }, + { + "cell_type": "code", + "execution_count": 16, + "id": "88cfc4ad", + "metadata": {}, + "outputs": [ + { + "data": { + "image/png": "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4oUXXmDNmjVMmTKFE044gQsvvJAXX3yx7DLVSxiQknqE9evXc/3119PU1MTy5ctpa2tj9uzZXHnllVxxxRX85je/YdCgQXzzm98su1T1EgakpB6jtbWVHTt20NrayvPPP88xxxzDvHnzuOCCCwCYMWMGt956a7lFqtcwICX1CCNGjOCTn/wkxx57LMcccwyHH344kyZN4ogjjqC2thaAkSNHsn79+pIrVW9hQErqEbZs2cJtt93GmjVreOqpp3juuee48847yy5LvVht2QVI0v645557OP744zn66KMBeNe73sUDDzzAs88+S2trK7W1taxbt44RI0aUXKl6C0eQknqEY489ll/84hc8//zzZCY///nPGTduHGeddRZz5swB4Oabb+Yd73hHyZWqtzAgJfUIU6ZM4YILLqC+vp5TTz2VnTt3cumll3L11Vfzla98hRNOOIHNmzfz4Q9/uOxS1Us4xSqpx7jqqqu46qqrXrZszJgxPPTQQyVVpN7MEaQkSQUMSEmSCjjFKu3F6NGjGTBgADU1NdTW1tLU1MSyZcu47LLL2L59O6NHj+Z73/seAwcOLLvUbumSWWVX0Hlumll2BSqDI0hpH+69914efvhhmpqaALj44ov58pe/zCOPPML555/PtddeW3KFkg4WA1LqgFWrVjFt2jQApk+fzi233FJyRZIOFgNS2ouI4JxzzmHSpEnceOONAIwfP57bbrsNgB/+8IesXbu2zBIlHUQGpLQXCxcuZOnSpfz0pz/la1/7GgsWLOBb3/oWX//615k0aRLbtm3jsMMOK7tMSQeJASntxa5Llg0ZMoTzzz+fhx56iJNPPpm77rqLJUuWcNFFF/G6172u5ColHSwGpFTgueeeY9u2bbu/vuuuu5gwYQLNzc0A7Ny5ky984QtcdtllZZYp6SAyIKUCmzZtYurUqUycOJHJkyfztre9jbe+9a18//vfZ+zYsZx88skMHz6cD33oQ2WXKukg8TxIqcCYMWNYtmzZHyz/xCc+wSc+8YkSKpLU1RxBSpJUwICUJKmAU6zqFbysmaTO5ghSkqQCBqQkSQUMSEmSChiQkiQVMCAlSSpgQEqSVMCAlCSpgAEpSVIBA1KSpAIGpCRJBQxISZIKGJCSJBUwICVJKmBASpJUwICUJKmAASlJUgEDUpKkAgakJEkFDEhJkgoYkJIkFTAg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\n", + "text/plain": [ + "
" + ] + }, + "execution_count": 16, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "tc.results.counts.plot_histogram(t.results())" + ] + }, + { + "cell_type": "markdown", + "id": "0596e9f1", + "metadata": {}, + "source": [ + "Check with the analytical exact result is easy, just use tensorcircuit's sota tensornetwork based simulation engine. The answer is a quantum state as $\\vert 00\\rangle + \\vert 11\\rangle$" + ] + }, + { + "cell_type": "code", + "execution_count": 17, + "id": "dd798dd7", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "[0.49999997 0. 0. 0.49999997]\n", + "{'00': 0.4999999701976776, '11': 0.4999999701976776}\n" + ] + } + ], + "source": [ + "p = c.probability()\n", + "print(p)\n", + "exact_result = tc.results.counts.vec2count(p, prune=True)\n", + "print(exact_result)" + ] + }, + { + "cell_type": "code", + "execution_count": 18, + "id": "0d5ca58d", + "metadata": {}, + "outputs": [ + { + "data": { + "image/png": "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\n", + "text/plain": [ + "
" + ] + }, + "execution_count": 18, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "tc.results.counts.plot_histogram([t.results(), exact_result])\n", + "# experiment vs exact" + ] + }, + { + "cell_type": "markdown", + "id": "d48cb1c1", + "metadata": {}, + "source": [ + "Let us further investigate the Task object ``t`` returned by ``submit_task``, it contains enriched information on manager, compiling, etc." + ] + }, + { + "cell_type": "code", + "execution_count": 19, + "id": "598448da", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "{'id': '3a8840fa-1831-48c3-85fd-67a66523ed8f',\n", + " 'queue': 'txq.low',\n", + " 'device': 'tianxuan_s1?o=3',\n", + " 'qubits': 2,\n", + " 'depth': 3,\n", + " 'state': 'completed',\n", + " 'shots': 1024,\n", + " 'prior': 1,\n", + " 'at': datetime.datetime(2023, 5, 31, 11, 45, 7, 695261),\n", + " 'ts': {'completed': datetime.datetime(2023, 5, 31, 11, 45, 7, 695261),\n", + " 'pending': datetime.datetime(2023, 5, 31, 11, 45, 6, 364959),\n", + " 'scheduled': datetime.datetime(2023, 5, 31, 11, 45, 6, 359719)},\n", + " 'md5': '9cb407b41938a256ec15dfec163dca1d',\n", + " 'runAt': 1685504730279662,\n", + " 'runDur': 1016276,\n", + " 'source': 'OPENQASM 2.0;\\ninclude \"qelib1.inc\";\\nqreg q[2];\\nh q[0];\\ncx q[0],q[1];',\n", + " 'version': '1',\n", + " 'lang': 'OPENQASM',\n", + " 'result': {'00': 494, '01': 59, '10': 80, '11': 391},\n", + " 'optimization': {'progs': [{'code': 'Tencent Quantum Program\\nversion 1.0\\nqubit involved: q0,q1,q2,q3,q4,q5,q6,q7,q8\\n# section: eqasm\\n# section lines 3\\neqasm program\\nbs 1 H q0\\nbs 1 CX (q0, q1)\\nMEASZ q0,q1\\n# section: end\\n',\n", + " 'lang': 'QEXE'},\n", + " {'code': 'Tencent Quantum Program\\nversion 1.0\\nqubit involved: q0,q1,q2,q3,q4,q5,q6,q7,q8\\n# section: eqasm\\n# section lines 3\\neqasm program\\nbs 1 H q0\\nbs 1 CX (q0, q1)\\nMEASZ q0,q1\\n# section: end\\n',\n", + " 'lang': 'QEXE_COMPACT'}],\n", + " 'pairs': {0: 0, 1: 1, 2: 2, 3: 3, 4: 4, 5: 5, 6: 6, 7: 7, 8: 8}},\n", + " 'results': {'00': 494, '01': 59, '10': 80, '11': 391},\n", + " 'frontend': ,\n", + " 'backend': }" + ] + }, + "execution_count": 19, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "t.details(prettify=True)" + ] + }, + { + "cell_type": "code", + "execution_count": 20, + "id": "c6a539ab", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "'completed'" + ] + }, + "execution_count": 20, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "t.status()" + ] + }, + { + "cell_type": "code", + "execution_count": 21, + "id": "b3fcd4ec", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "'3a8840fa-1831-48c3-85fd-67a66523ed8f'" + ] + }, + "execution_count": 21, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "t.id_" + ] + }, + { + "cell_type": "markdown", + "id": "f951f18c", + "metadata": {}, + "source": [ + "The task can be retrieved from cloud with the id without task object `t`" + ] + }, + { + "cell_type": "code", + "execution_count": 22, + "id": "00e6c83d", + "metadata": {}, + "outputs": [], + "source": [ + "t1 = tc.cloud.apis.get_task(t.id_)" + ] + }, + { + "cell_type": "code", + "execution_count": 23, + "id": "19a5e66b", + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "
     ┌───┐     \n",
+       "q_0: ┤ H ├──■──\n",
+       "     └───┘┌─┴─┐\n",
+       "q_1: ─────┤ X ├\n",
+       "          └───┘
" + ], + "text/plain": [ + " ┌───┐ \n", + "q_0: ┤ H ├──■──\n", + " └───┘┌─┴─┐\n", + "q_1: ─────┤ X ├\n", + " └───┘" + ] + }, + "execution_count": 23, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "t1.details(prettify=True)[\"frontend\"].draw()\n", + "# exactly the task we submitted" + ] + }, + { + "cell_type": "code", + "execution_count": 24, + "id": "1e7520af", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "{'00': 494, '11': 391, '10': 80, '01': 59}" + ] + }, + "execution_count": 24, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "t1.results()" + ] + }, + { + "cell_type": "markdown", + "id": "303cf6c3", + "metadata": {}, + "source": [ + "Task group management is also possible but not shown here. Try using ``group`` argument when ``submit_task`` and ``list_task`` when retrieving." + ] + }, + { + "cell_type": "markdown", + "id": "ce9f8313", + "metadata": {}, + "source": [ + "## Cloud simulator\n", + "\n", + "We can also submit tasks to run on tc simulators on the cloud, the only thing you need to change is the device name, and now the result becomes exact." + ] + }, + { + "cell_type": "code", + "execution_count": 25, + "id": "adadefd1", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "{'11': 546, '00': 478}" + ] + }, + "execution_count": 25, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "c = tc.Circuit(2)\n", + "c.H(0)\n", + "c.cx(0, 1)\n", + "\n", + "t = tc.cloud.apis.submit_task(device=\"simulator:tc\", circuit=c, shots=1024)\n", + "\n", + "t.results() # this will wait until the result is return\n", + "# instead, using wait=False for t.results(wait=False), the task objects can be returned in async mode" + ] + }, + { + "cell_type": "markdown", + "id": "c00c7dde", + "metadata": {}, + "source": [ + "**Batch submission:** Tasks can also submitted in batch, either on real devices or on simulators, a list of task object is returned by ``submit_task``, if the circuit submitted is in a list. In this way, the joint tasks are executed on the QPU at the same time so that the noise profile remains consistent." + ] + }, + { + "cell_type": "code", + "execution_count": 26, + "id": "7f57f4e2", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "{'10': 524, '00': 500}\n", + "{'00': 519, '01': 505}\n" + ] + } + ], + "source": [ + "c1 = tc.Circuit(2)\n", + "c1.h(0)\n", + "\n", + "c2 = tc.Circuit(2)\n", + "c2.h(1)\n", + "\n", + "ts = tc.cloud.apis.submit_task(device=\"simulator:tc\", circuit=[c1, c2], shots=1024)\n", + "for t in ts:\n", + " print(t.results())" + ] + }, + { + "cell_type": "markdown", + "id": "1e72f1ab", + "metadata": {}, + "source": [ + "## Compling: gate decomposition and qubit mapping" + ] + }, + { + "cell_type": "markdown", + "id": "7bea5aba", + "metadata": {}, + "source": [ + "Say we want to simulate the following logic circuit, however, the gate set and the coupling for two-qubit gates are both incompatible with our real device" + ] + }, + { + "cell_type": "code", + "execution_count": 27, + "id": "2fc080c9", + "metadata": {}, + "outputs": [ + { + "data": { + "image/png": "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\n", + "text/plain": [ + "
" + ] + }, + "execution_count": 27, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "c = tc.Circuit(5)\n", + "c.rx(0, theta=1.5708)\n", + "for i in range(4):\n", + " c.cx(i, i + 1)\n", + "c.measure_instruction(*range(5))\n", + "# note for tasks involving qubit mapping, we recommend you add the measure instruction explicitly\n", + "c.draw(output=\"mpl\")" + ] + }, + { + "cell_type": "code", + "execution_count": 28, + "id": "ea2b3692", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "{'00000': 4014, '11111': 4178}" + ] + }, + "execution_count": 28, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# the ideal answer with shot noise\n", + "c.sample(allow_state=True, batch=8192, format=\"count_dict_bin\")" + ] + }, + { + "cell_type": "markdown", + "id": "79349e8e", + "metadata": {}, + "source": [ + "The target state we prepare is the so called GHZ state (here is GHZ-5), which is also famuous as Schordinger cat state, as it is a superposition of two very different (macroscopic) quantum states: $\\vert 00000\\rangle + \\vert 11111\\rangle$. GHZ state is also a great measure to determine the quality of the quantum hardware.\n", + "\n", + "\n", + "By default the **backend compiler** options are both enabled which we write expicitly below" + ] + }, + { + "cell_type": "code", + "execution_count": 29, + "id": "f381a9f7", + "metadata": {}, + "outputs": [], + "source": [ + "t = tc.cloud.apis.submit_task(\n", + " circuit=c,\n", + " shots=8192,\n", + " device=d,\n", + " enable_qos_gate_decomposition=True,\n", + " enable_qos_qubit_mapping=True,\n", + ")" + ] + }, + { + "cell_type": "code", + "execution_count": 30, + "id": "0704b37f", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "{'00000': 3794,\n", + " '11111': 862,\n", + " '00010': 584,\n", + " '10000': 311,\n", + " '00011': 258,\n", + " '01111': 254,\n", + " '00110': 221,\n", + " '10111': 188,\n", + " '00111': 176,\n", + " '11101': 169,\n", + " '11000': 128,\n", + " '00100': 113,\n", + " '11100': 104,\n", + " '10010': 102,\n", + " '01010': 89,\n", + " '00001': 87,\n", + " '10100': 84,\n", + " '11110': 79,\n", + " '11011': 78,\n", + " '01011': 71,\n", + " '01000': 66,\n", + " '01101': 63,\n", + " '01110': 63,\n", + " '10110': 49,\n", + " '01100': 43,\n", + " '10011': 42,\n", + " '00101': 26,\n", + " '10101': 25,\n", + " '10001': 18,\n", + " '01001': 15,\n", + " '11001': 15,\n", + " '11010': 15}" + ] + }, + "execution_count": 30, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "rb = t.results()\n", + "rb" + ] + }, + { + "cell_type": "markdown", + "id": "9847ccaa", + "metadata": {}, + "source": [ + "We can inspect the circuit compiled after the backend server compiling:" + ] + }, + { + "cell_type": "code", + "execution_count": 31, + "id": "24748fe7", + "metadata": {}, + "outputs": [ + { + "data": { + "image/png": "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\n", + "text/plain": [ + "
" + ] + }, + "execution_count": 31, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "t.details(prettify=True)[\"backend\"].draw(idle_wires=False, output=\"mpl\")" + ] + }, + { + "cell_type": "code", + "execution_count": 29, + "id": "5f0ad718", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "{0: 1, 1: 3, 2: 2, 3: 0, 4: 4, 5: 5, 6: 6, 7: 7, 8: 8}" + ] + }, + "execution_count": 29, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "t.get_logical_physical_mapping() # the logical qubit - physical qubit mapping is also returned by the server" + ] + }, + { + "cell_type": "markdown", + "id": "98b2b053", + "metadata": {}, + "source": [ + "To better customize and use the advanced compiling system, we strongly recommend the users to compile the circuit before task submission as **frontend compiling**" + ] + }, + { + "cell_type": "code", + "execution_count": 32, + "id": "4debc9ad", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "{'logical_physical_mapping': {0: 6, 1: 2, 2: 0, 3: 4, 4: 8}, 'positional_logical_mapping': {0: 0, 1: 1, 2: 2, 3: 3, 4: 4}}\n" + ] + }, + { + "data": { + "image/png": "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\n", + "text/plain": [ + "
" + ] + }, + "execution_count": 32, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "c1, info = tc.compiler.default_compile(\n", + " c, compiled_options={\"coupling_map\": d.topology()}\n", + ")\n", + "print(info)\n", + "c1.draw(idle_wires=False, output=\"mpl\")" + ] + }, + { + "cell_type": "markdown", + "id": "258236df", + "metadata": {}, + "source": [ + "We now submit the compiled circuit ``c1`` for the qcloud, with now the ``logical_physical_mapping`` in ``info``, the result is improved with tc built in compiler" + ] + }, + { + "cell_type": "code", + "execution_count": 33, + "id": "3a165a8c", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "{'00000': 3506,\n", + " '11111': 1962,\n", + " '01111': 523,\n", + " '11011': 279,\n", + " '10000': 262,\n", + " '11110': 257,\n", + " '10111': 174,\n", + " '11000': 167,\n", + " '00111': 148,\n", + " '11101': 109,\n", + " '01000': 92,\n", + " '01011': 90,\n", + " '00001': 87,\n", + " '01110': 85,\n", + " '00011': 80,\n", + " '11100': 72,\n", + " '11010': 45,\n", + " '00100': 42,\n", + " '10011': 31,\n", + " '00110': 24,\n", + " '01101': 24,\n", + " '00010': 22,\n", + " '10110': 18,\n", + " '11001': 17,\n", + " '01100': 16,\n", + " '01010': 15,\n", + " '10100': 11,\n", + " '00101': 10,\n", + " '10001': 10,\n", + " '10101': 8,\n", + " '01001': 4,\n", + " '10010': 2}" + ] + }, + "execution_count": 33, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "t = tc.cloud.apis.submit_task(\n", + " circuit=c1,\n", + " shots=8192,\n", + " device=d,\n", + " enable_qos_gate_decomposition=False,\n", + " enable_qos_qubit_mapping=False,\n", + ")\n", + "rf = t.results()\n", + "rf" + ] + }, + { + "cell_type": "code", + "execution_count": 34, + "id": "22d46c41", + "metadata": {}, + "outputs": [ + { + "data": { + "image/png": "iVBORw0KGgoAAAANSUhEUgAAAdEAAAFTCAYAAABmuzZIAAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjUuMSwgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy/YYfK9AAAACXBIWXMAAAsTAAALEwEAmpwYAAAybElEQVR4nO3deXwV5dn/8c8FgSxCBFQIAWRfwxqjNDaCyw+xLn1cqlal7mL1UYs7LlXE+qitUitqpVo3HmvrUvcNN5ZoHjSgKIugsmiQTdmChGDI9ftjJukhBiFjOCfL9/16nRcnM/eZXOOJ53vmnnvuMXdHREREaq5JogsQERGprxSiIiIiESlERUREIlKIioiIRKQQFRERiUghKiIiElFSoguoS/bee2/v0qVLossQEZE6ZNasWd+4+z7VrVOIxujSpQuFhYWJLkNEROoQM1u2o3XqzhUREYlIIZpA69evp0WLFqSmppKSksLw4cMB2HPPPUlNTSU1NZWmTZvSvn17AJYsWUJmZiapqam0aNGCZ599drvtbd26lbS0NNq1axf3fRERaYwUogmUnp7O559/TklJCWvXrmXWrFk8+OCDbNiwgZKSEkpKSsjIyOCYY44B4KSTTqJ3796UlJTw5JNPcs4552y3vRNPPJG2bdsmYldERBolhWgCNWnShIyMDAA2b95MeXk5Zla5vqioiBUrVnDDDTcAsGzZMk444QQAjjzySIqLi5k7dy4AH3zwATNmzODiiy+O816IiDReCtEE27p1K6mpqeyzzz4MGDBgu6PL8ePHk5mZSceOHQHo0aMHDz30EAAPP/wwZWVlfPTRRwAcd9xx3H333TRt2jTu+yAi0lglLETN7BozczO7J2aZmdk4M/vazErMbKqZZVV5XWszm2xmG8LHZDNrVaXNADObFm5juZndYLGHeHVI8+bNKSkpYenSpSxatGi785zPPvssp5xySuXPTz75JMXFxaSmpnLzzTeTlpZGs2bNuOGGG2jVqhWjRo1KxC6IiDRalohboZnZz4AngI3ADHe/KFx+NXA9cCawELgByAN6u3tx2OZVYF/g3HBzDwKL3f2YcH06sAiYDowH+gAPA+Pc/c4fqysnJ8cTeYnLoYceSlpaGi+99BILFy6kb9++rF27llatWv2gbXl5Oc2bN2fp0qWceOKJvP/++5gZ7k55eTldunRhyZIl8d8JEZEGxsxmuXtOdevifiRqZnsCjwNnA+tilhswBrjN3Z9x97nAGUBL4NSwTV/gCGC0uxe4ewFwPnC0mfUON3UakAac4e5z3f1p4Hbgsrp2NLpgwQKWLQsuP6oYWJSdnQ3ATTfdRNeuXbcL0GXLlrFp0yYAzjzzTDp37kzHjh0pKChg27ZtlJWVceedd9K2bVsFqIhIHCSiO/dvwNPu/k6V5V2BDGBKxQJ3LyE4ojwwXJQLbALei3ndu8B3VdrMCF9b4XUgE+hSO7tQO+bNm0ffvn1JTU0lMzOT/fbbj/HjxwPwyiuvcNZZZ23X/vXXX6dNmzY0b96cN998kzfffDMRZYuISCiuMxaZ2XlAD6C6k3cZ4b+rqixfBXSIabPGY/qg3d3NbHXM6zOAomq2UbFuu0M0MxsNjAbIzMxk6tSpAHTr1o2WLVsyZ84cAPbaay+ysrKYPn06AElJSeTl5TF79mw2btwIQE5ODqtWreKrr74CoGfPniQnJ1eOoG3bti29evUiPz8fgA4dOrB582YKCwsrjzBLSkooKiriueeeA2DFihU0bdqU+fPn06tXLz7++GO6du1KQUEBy5YtY/Xq1QwdOpSZM2dSUlLC4MGD+fLLL/n0009ZuXIlAP369WPbtm0sXLiw8vd27NiRmTNnAtCiRQtycnIoKCigtLQUgLy8PBYtWsTq1asB6N+/P6WlpXz22WcAdOrUiXbt2lXO8JSenk52djb5+fmUlZUBMGzYMObNm8e3334LwKBBgyguLmbx4sVAMENUmzZtmD17NgCtW7dm0KBBTJs2DXfHzBg+fDhz5sxh3bqg0yI7O5u1a9eydOnSuL1PycnJ5Obmbvc+DR06lKKiIpYvXw5A7969K98ngIyMjMr3CSA1NXW79wkgNzeXJUuW6H3S+6T3qY6/Tz8mbudEw+7WfCDP3ReGy6YCc939IjM7kOCosrO7fxnzuoeADu4+0syuBc51925Vtr0YeMDdbzWzKUCRu58ds35fYBlwYNgFXK1EnxMVEZG6p66cE80F9gbmmVmZmZUBw4ELw+ffhu2qTrfTDlgZPl8J7BN7bjN83rZKm+q2QUwbERGRnyyeIfocMAAYHPMoBP4ZPl9EEHIjKl5gZinAQfznHGgB0IIgkCvkAntUaXNQ+NoKI4CvgaW1tC8iIiLxOyfq7uuB9bHLzOw7YG04Ehczuwu41sw+JQjV6wkGEv0j3MYCM3sNmBSeywSYBLxU0UUctr0ReMTM/gD0AsYCN3m8+q5FRKRRqGu3QvsjkArcC7QGZgKHV1wjGjoVmEgw4hbgBeCiipXuvsHMRoTbKCS4jOZOYMJur15ERBqVhEy2UFdpYJGIiFRVVwYWiYiINCgKURERkYgUoiIiddz69etp0aIFqamppKSkMHz4cCC4s1OzZs1ITU0lNTWVf/3rXwBce+21lcv22GMP7rvvvsptFRQUsNdee5GcnExycnLlJAgSjUJURKSOS09P5/PPP6ekpKRynu0HH3wQgEsuuYSSkhJKSko4+eSTK5d99913lJSU8Oijj3LppZdWbusXv/gFV155JaWlpSxbtow+ffokZJ8aCoWo1MiWLVs44IADGDRoEFlZWdx4441AMCF+165dGTx4MIMHD668z6m7c8kll9CjRw8GDhxYOSUZwJdffsnhhx9O37596devX+XUYyKyvSZNmpCREcxsunnzZsrLy/mx+2lkZGTQpEnw8b527drK5S+88ALl5eWMHTu2st3ee++9GytvBNxdj/Cx3377ufy48vJyLy4udnf3rVu3+gEHHOAFBQV+xhln+FNPPfWD9i+//LIfccQRXl5e7gUFBX7AAQdUrhs+fLhPmTLF3d2Li4v9u+++i89OiNRDpaWlnpKS4kDl/0fdu3f3Zs2aeUpKig8ZMsQ3bNhQ2f6qq67y5s2bu5n5pEmT3N197Nix3rZtW2/fvr2npqZ6Tk6Ol5aWJmR/6hOg0HeQG3XtOtEG4by7El1BzTwwZtfbmhktWrQA4Pvvv+f777//0W/Ezz//PKeffjpmxs9+9jPWr1/PihUrWLduHWVlZYwYEUxQVbFNEale8+bNKSkpYdmyZQwePJhnn32Wp59+moEDB7Jp0yaGDBnCsccey9tvvw3A7bffzu23387EiRMZO3Yso0ePZuvWraxevZp33nmHn/3sZ3Tv3p3Ro0fzyCOPJHbn6jF150qNbdu2jcGDB9O2bVtGjBjB0KFDAbjuuusYOHAgl156aeWdD5YvX06nTp0qX9uxY0eWL1/OokWLaNWqFccffzxDhgzhyiuvZNu2bQnZH5H6pHPnzgwZMoS///3vDB48mCZNmpCens7FF1/MvHnzftD+4osvpri4mIULF9K3b1/23HNPDj74YFJSUjjmmGOYNWtWAvai4VCISo01bdqUjz76iKKiIt5//33mzp3LrbfeyqeffsoHH3zA2rVruf322390G2VlZcyYMYM77riDDz74gMWLF+vbsMgOLFiwgGXLlgFUDizKzs6uHHtQXl7Oo48+SteuXQF46623KC8vB+Dxxx+nvLycnj17cvrpp1NaWsqCBQsq2/Xt2zf+O9SAKEQlslatWnHIIYfw2muv0b59e8yM5ORkzjrrLN5//30guIdfxX0GAYqKiirv6zd48GC6detGUlISxx577HaDjkTkP+bNm0ffvn1JTU0lMzOT/fbbj/Hjx3PIIYeQkpJCWloa69evr7wP8R133EFaWhqpqan89re/ZeLEiTRp0oTmzZtz4403kp2dTUpKCu7OQw89lNidq+cUolIja9asYf369UBwA/E33niDPn36sGLFCiAYqPbcc8/Rv39/AH75y1/y2GOP4e783//9H3vuuSft27dn//33Z/369axZswaAt99+m379+iVkn0Tqul/96lds3ryZkpIStmzZUnnec926dWzZsoUtW7awZMmSyhG8r776Klu2bKGkpITi4mIuvPDCym2NHTu2cjuff/65xiP8RBpYJDWyYsUKzjjjDLZt20Z5eTknnXQSRx99NIceeihr1qzB3Rk8eDD3338/AEceeSSvvPIKPXr0IC0tjYcffhgIuoTvuOMODjvssIqR0Zx33nmJ3DURkRrTBPQxamsC+oY8OldEpLHRBPQiIiK7gUJUREQkIoWoiIhIRApRERGRiBSiIiIiESlERUREIlKIioiIRKQQFRERiUghKiIiEpFCVEREJCLNnSsikgD1bXpQ0BSh1dGRqIiISEQ6EhW+6D0u0SXUWPeF4xJdgoiIjkRFRESiUoiKiIhEpBAVERGJSCEqIiISkUJUREQkIoWoiIhIRApRERGRiBSiIiIiESlERUREIlKIioiIRKQQFRERiUghKiIiEpFCVEREJCKFqIiISEQKURERkYgUoiIiIhEpREVERCJSiIqIiESkEBUREYlIISoiIhKRQlRERCQihaiIiEhEClEREZGIFKIiIiIRKURFREQiUoiKiIhEpBAVERGJSCEqIiISUdxC1Mz+28w+NrON4aPAzI6KWW9mNs7MvjazEjObamZZVbbR2swmm9mG8DHZzFpVaTPAzKaF21huZjeYmcVpN0VEpBGJ55FoEXA1kA3kAG8Dz5nZwHD9VcDlwMXA/sBq4A0zaxmzjX+Erz8ifGQDkytWmlk68AawKtzG74Argct2216JiEijlRSvX+Tuz1dZdJ2ZXQDkmtknwBjgNnd/BsDMziAI0lOBSWbWlyA489y9IGxzPjDDzHq7+0LgNCANOMPdS4C5ZtYHuMzMJri77/49FRGRxiIh50TNrKmZ/RpoAbwHdAUygCkVbcIQnA4cGC7KBTaF7Su8C3xXpc2M8LUVXgcygS61viMiItKoxe1IFILzlUABkEIQiMe5+ydmVhGCq6q8ZBXQIXyeAayJPZp0dzez1eG6ijZF1WyjYt2SamoaDYwGyMzMZOrUqQB069aNli1bMmfOHAD22msvsrKymD59OgBJSUnk5eUxe/ZsNm7cCEBOTg6rVq0Cuu/afxCJrDbep6+++gqAnj17kpyczNy5cwFo27YtvXr1Ij8/H4Dk5GRyc3MpLCxk06ZNAAwdOpSioiKWL18OQO/evWnatCnz588HICMjg65du1JQUABAamoqQ4cOZebMmZSUBN/xcnNzWbJkCStXrgSgX79+bNu2jYULFwLQoUMHOnbsyMyZMwFo0aIFOTk5FBQUUFpaCkBeXh6LFi1i9erVAPTv35/S0lI+++wzADp16kS7du0oLCwEID09nezsbPLz8ykrKwNg2LBhzJs3j2+//RaAQYMGUVxczOLFiwHo0qULbdq0Yfbs2QC0bt2aQYMGMW3aNNwdM2P48OHMmTOHdevWAZCdnc3atWtZunSp3qcdvE+wN/VNfn7+Lr1P33zzDWeeeSbr1q0jKSmJCy+8kOzsbG666SaWL19OWloaq1evJi0tjQcffJBNmzYxduxYNm/eTFJSErfccgvDhw/f7n264oormDdvHg8//HDc/3/6MRbPHk4zaw7sC+wJ/Ao4DzgYSCc4quzs7l/GtH8I6ODuI83sWuBcd+9WZZuLgQfc/VYzmwIUufvZMev3BZYBB1Z0A+9ITk6OV3zY/BTn3fWTNxFXY/86LtEl1Fj3heMSXYLIT1LfPicAHhiza+1WrFjBihUryM7Opri4mP3224/nnnuOfv36Vba5/PLL2XPPPbnhhhv48MMPadeuHZmZmcydO5eRI0dWfvEB+Pe//83TTz/Nxx9/XPklKp7MbJa751S3Lq7due6+1d0/d/dZ7n4N8BFwKbAybNKuykvaxaxbCewTO9I2fN62SpvqtkFMGxER2Y3at29PdnY2AC1btqRv377bhaK78+STT3LKKacAMGTIEDIzMwHIysqipKSk8ghw06ZNTJgwgeuvvz7Oe7FrEn2daBMgmaCbdSUwomKFmaUAB/Gfc6AFBOdQc2NenwvsUaXNQeFrK4wAvgaW1n75IiLyY5YuXcqHH37I0KFDK5fNmDGDdu3a0bNnzx+0f+aZZ8jOziY5ORmA3//+91x++eWkpaXFreaaiOd1oreZ2UFm1iW8lvNWgq7cx8PznHcBV5vZ8WbWH3iE4LzpPwDcfQHwGsFI3VwzywUmAS+FI3MJ224GHjGz/mZ2PDAW0MhcEZE427RpEyeccAJ33XUX6enplcufeOKJyqPQWPPmzePqq69m0qRJAHz00Ud88cUXHHfccXGruabiObAoA/jf8N8NwMfAL9z99XD9H4FU4F6gNTATONzdi2O2cSowkWDELcALwEUVK919g5mNCLdRCKwD7gQm7KZ9EhGRanz//feccMIJnHbaaRx//PGVy8vKyvj3v//NrFmztmtfVFTEcccdx2OPPUb37sHgzIKCAgoLC+nSpQtlZWWsXr2agw8+uHJgYV0Qz+tEz9zJegfGhY8dtVkHjNrJdj4BhtW4QBERqRXuzjnnnEPfvn257LLt57p588036dOnDx07dqxctn79eo466ihuu+02fv7zn1cuv+CCC7jggguAoFv46KOPrlMBCok/JyoiIg3Mu+++y+TJk3n77bcZPHgwgwcP5pVXXgHgn//85w+6cu+55x4+//xzxo8fX9m+4jKTui6u14mKiEjDl5eXx46GoTzyyCM/WHb99dfvdPRtly5dEnJ5y87oSFRERCQihaiIiEhEClEREZGIFKIiIiIRKURFREQiUoiKiIhEpBAVERGJSCEqIiISkUJUREQkIs1YJCIiu+SL3uMSXUKNdF84brf/Dh2JioiIRKQQFRERiUghKiIiEpFCVEREJCKFqIiISEQKURERkYgUoiIiIhEpREVERCJSiIqIiESkEBUREYlIISoiIhKRQlRERCQihaiIiEhEClEREZGIFKIiIiIRKURFREQiUoiKiIhEpBAVERGJSCEqIiISkUJUREQkIoWoiIhIRApRERGRiHY5RM1smJklVbM8ycyG1W5ZIiIidV9NjkTfAdpUs3zP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VUyaNIkNGzZUvm7ChAlcc801zJ07l549e0b63V/0HldLexEf9e1vor59TkD9+6yorb8JM5vl7jnVratL3bkrw3/bVVneLmbdSmCf2JG24fO2VdpUt43Y3yFSL0ycOJF99tmHvn37kpaWxjHHHMOMGTNo3rw5V155JQC33XZb5VE2wNNPP83VV1/NCy+8EDlARWTX1KUQXUIQciMqFphZCnAQ/zkHWkAwwjc35nW5wB5V2hwUvrbCCOBrYOnuKFxkd8nKyuLLL7/km2++oby8nLfeeos+ffowZMgQHn74YQDuvvtu0tLSgKBL85RTTuGee+5h5MiRiSxdpFGI6zlRM2sB9Ah/bALsa2aDgbXu/qWZ3QVca2afAouA6wkGEv0DwN0XmNlrBCN1R4fbmQS8FI7MJWx7I/CImf0B6AWMBW7SyFypb8455xwee+wxOnTogJmRmZnJQw89xLp169h///1JSUmhWbNmlYF65plnsm3bNsaMGcOYMWNo0qQJ3333XYL3QqThivfAohzgnZifbwofjwJnAn8EUoF7gdbATOBwd48d5XAqMJFgxC3AC8BFFSvdfYOZjQi3UQisA+4EJtT+7ojsftOmTfvBsvT09MrBD7EqBlGISHzE+zrRqcAOZw4KjxTHhY8dtVkHjNrJ7/kEGBalRhERkV1Vl86JioiI1CsKURERkYjq0mQLIg1WvbwmMNEFiNQDOhIVERGJSCEqIiISkUJUREQkIoWoiIhIRApRERGRiBSiIiIiESlERUREIlKIioiIRKQQFRERiUghKiIiEpFCVEREJCKFqIiISEQKURERkYgUoiIiIhEpREVERCJSiIqIiESkEBUREYlIISoiIhKRQlRERCQihaiIiEhEClEREZGIFKIiIiIRKURFREQiUoiKiIhEpBAVERGJSCEqIiISkUJUREQkIoWoiIhIRApRERGRiBSiIiIiESlERUREIlKIioiIRKQQFRERiUghKiIiEpFCVEREJCKFqIiISEQKURERkYgUoiIiIhEpREVERCJSiIqIiESkEBUREYlIISoiIhKRQlRERCQihaiIiEhEClEREZGIFKIiIiIRKURFREQiUoiKiIhEpBAVERGJSCEqIiISkUJUREQkogYbomZ2oZktMbMtZjbLzA5KdE0iItKwNMgQNbOTgb8A/wMMAd4DXjWzfRNamIiINCgN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\n", + "text/plain": [ + "
" + ] + }, + "execution_count": 34, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "tc.results.counts.plot_histogram([rb, rf], number_to_keep=2)\n", + "# backend compiling vs frontend compiling" + ] + }, + { + "cell_type": "markdown", + "id": "60ba2314", + "metadata": {}, + "source": [ + "## Readout Error Mitigation" + ] + }, + { + "cell_type": "markdown", + "id": "acc9f9ab", + "metadata": {}, + "source": [ + "The above results can be further improved via readout error mitigation (a classical algorithmic postprocessing on the measurement results)" + ] + }, + { + "cell_type": "code", + "execution_count": 35, + "id": "da287ca9", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "{'00000': 3931.5654653719635,\n", + " '11111': 3736.9302177814375,\n", + " '11000': 156.2388037789295,\n", + " '00111': 139.79207327296174,\n", + " '10000': 82.70413218088015,\n", + " '10111': 62.54914892519676,\n", + " '11100': 40.2309011836783,\n", + " '00011': 34.01063384376864,\n", + " '01111': 5.923071025515406,\n", + " '00001': 2.0555526356691276}" + ] + }, + "execution_count": 35, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "mit = tc.results.rem.ReadoutMit(d.name + \"?o=0\")\n", + "mit.cals_from_system(9)\n", + "mr = mit.apply_correction(t.results(), qubits=5, **info)\n", + "mr" + ] + }, + { + "cell_type": "code", + "execution_count": 36, + "id": "1cbf19de", + "metadata": {}, + "outputs": [ + { + "data": { + "image/png": "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\n", + "text/plain": [ + "
" + ] + }, + "execution_count": 36, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "tc.results.counts.plot_histogram([t.results(), mr], number_to_keep=2)\n", + "# raw vs mitigated" + ] + }, + { + "cell_type": "markdown", + "id": "73a6c287", + "metadata": {}, + "source": [ + "We can also collect the readout calibriation from the API, but the results can be wrose since it is not up to date" + ] + }, + { + "cell_type": "code", + "execution_count": 37, + "id": "43ea80ec", + "metadata": {}, + "outputs": [ + { + "data": { + "image/png": "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\n", + "text/plain": [ + "
" + ] + }, + "execution_count": 37, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "mit1 = tc.results.rem.ReadoutMit(d.name + \"?o=0\")\n", + "mit1.cals_from_api(9)\n", + "mr1 = mit1.apply_correction(t.results(), qubits=5, **info)\n", + "tc.results.counts.plot_histogram([mr, mr1], number_to_keep=2)\n", + "\n", + "# mitigated via real time calibriation vs. mitigated via api calibriation data" + ] + }, + { + "cell_type": "markdown", + "id": "8ec8d9c8", + "metadata": {}, + "source": [ + "Readout error mitigation in tc supports many other options for subset measurement, scalable mitigation for hundereds of qubits, customized calibriation in local and global mode and native error mitigated expectations, please refer to the API documentation for more interesting usages. For example, we can directly compute the expectation $$ (ideal value should be 1) as" + ] + }, + { + "cell_type": "code", + "execution_count": 38, + "id": "77e7d8e1", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "(array(1.+0.j, dtype=complex64), 0.9584156264735034)" + ] + }, + "execution_count": 38, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "c.expectation_ps(z=[0, 1]), mit.expectation(t.results(), [0, 1])" + ] + }, + { + "cell_type": "markdown", + "id": "7b156c6f", + "metadata": {}, + "source": [ + "## High level API\n", + "\n", + "Ultimately, for near term quantum computing tasks, the users only want to evaluate some given observable expectation for a circuit without worrying too much details above: compilation, error mitigation, subset measruement, positional/logical/physical mapping etc. Therefore, for most of the applications, `batch_expectation_ps` method is all you need." + ] + }, + { + "cell_type": "code", + "execution_count": 39, + "id": "f07070b5", + "metadata": {}, + "outputs": [], + "source": [ + "import logging\n", + "\n", + "logger = logging.getLogger(\"tensorcircuit.cloud\")\n", + "logger.setLevel(logging.INFO)\n", + "# ch = logging.StreamHandler()\n", + "# ch.setLevel(logging.INFO)\n", + "# logger.addHandler(ch)\n", + "\n", + "# we enable log for the high level API to see what happen behind the scene" + ] + }, + { + "cell_type": "markdown", + "id": "4f50a8f1", + "metadata": {}, + "source": [ + "$\\langle Z_0Z_1\\rangle$" + ] + }, + { + "cell_type": "code", + "execution_count": 40, + "id": "2100ff5d", + "metadata": {}, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "INFO:tensorcircuit.cloud.wrapper:submit task on tencent::tianxuan_s1 for 1 circuits\n", + "INFO:tensorcircuit.cloud.wrapper:finished collecting count results of 1 tasks in 4.873 seconds\n", + "INFO:tensorcircuit.cloud.wrapper:submit task on tencent::tianxuan_s1 for 2 circuits\n", + "INFO:tensorcircuit.cloud.wrapper:finished collecting count results of 2 tasks in 7.6044 seconds\n" + ] + }, + { + "data": { + "text/plain": [ + "array([0.82589585])" + ] + }, + "execution_count": 40, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "tc.cloud.wrapper.batch_expectation_ps(c, pss=[[3, 3, 0, 0, 0]], device=d)\n", + "# compute Z0Z1" + ] + }, + { + "cell_type": "markdown", + "id": "630b8791", + "metadata": {}, + "source": [ + "$\\langle Z_0Z_1\\rangle+0.5\\langle Z_1Z_2\\rangle$" + ] + }, + { + "cell_type": "code", + "execution_count": 41, + "id": "c405123f", + "metadata": {}, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "INFO:tensorcircuit.cloud.wrapper:submit task on tencent::tianxuan_s1 for 1 circuits\n", + "INFO:tensorcircuit.cloud.wrapper:finished collecting count results of 1 tasks in 4.9699 seconds\n" + ] + }, + { + "data": { + "text/plain": [ + "array(1.54442738)" + ] + }, + "execution_count": 41, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "tc.cloud.wrapper.batch_expectation_ps(\n", + " c, pss=[[3, 3, 0, 0, 0], [0, 3, 3, 0, 0]], device=d, ws=[1, 0.5]\n", + ")\n", + "# compute Z0Z1 + 0.5*Z1Z2" + ] + }, + { + "cell_type": "markdown", + "id": "0965380b", + "metadata": {}, + "source": [ + "The interface is also unifying the numerical simulation (exact) interface with QPU experiments, by spcifying the device as ``None``, we can obtain the expected result from tc simulator" + ] + }, + { + "cell_type": "code", + "execution_count": 42, + "id": "5744293b", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "array(1.5+0.j)" + ] + }, + "execution_count": 42, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "tc.cloud.wrapper.batch_expectation_ps(\n", + " c, pss=[[3, 3, 0, 0, 0], [0, 3, 3, 0, 0]], device=None, ws=[1, 0.5]\n", + ")" + ] + }, + { + "cell_type": "markdown", + "id": "300cff3a", + "metadata": {}, + "source": [ + "The results with readout error mitigation disabled can become worse. Note how we cache the readout error calibriation within tc, so that REM is effcient to use." + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "id": "0e49467d", + "metadata": {}, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "INFO:tensorcircuit.cloud.wrapper:submit task on tencent::tianxuan_s1 for 1 circuits\n", + "INFO:tensorcircuit.cloud.wrapper:finished collecting count results of 1 tasks in 3.6587 seconds\n" + ] + }, + { + "data": { + "text/plain": [ + "array(0.8828125)" + ] + }, + "execution_count": 8, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "tc.cloud.wrapper.batch_expectation_ps(\n", + " c,\n", + " pss=[[3, 3, 0, 0, 0], [0, 3, 3, 0, 0]],\n", + " device=d,\n", + " ws=[1, 0.5],\n", + " with_rem=False,\n", + " shots=1024,\n", + ")" + ] + }, + { + "cell_type": "markdown", + "id": "da002ef1", + "metadata": {}, + "source": [ + "**QPU support for tf/torch ML:** Above this API, we also have corresponding keras and torch layers for hybrid deployment" + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "id": "718d7215", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Z_i: [[3, 0, 0, 0, 0], [0, 3, 0, 0, 0], [0, 0, 3, 0, 0], [0, 0, 0, 3, 0], [0, 0, 0, 0, 3]]\n" + ] + }, + { + "data": { + "text/plain": [ + "" + ] + }, + "execution_count": 9, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "import tensorflow as tf\n", + "from functools import partial\n", + "\n", + "tc.set_backend(\"tensorflow\")\n", + "\n", + "pss = []\n", + "for i in range(5):\n", + " ps = [0 for _ in range(5)]\n", + " ps[i] = 3 # Z_i\n", + " pss.append(ps)\n", + "print(\"Z_i:\", pss)\n", + "\n", + "\n", + "def quantum_func(inputs, weights, device=None):\n", + " c = tc.Circuit(5)\n", + " for i in range(5):\n", + " c.rx(i, theta=inputs[i])\n", + " for i in range(5):\n", + " c.rz(i, theta=weights[0, i])\n", + " for i in range(5):\n", + " c.rx(i, theta=weights[1, i])\n", + " return tc.cloud.wrapper.batch_expectation_ps(c, pss=pss, device=device)\n", + "\n", + "\n", + "qlayer = tc.KerasLayer(quantum_func, [2, 5])\n", + "model = tf.keras.Sequential([qlayer, tf.keras.layers.Dense(1)])\n", + "inputs = tf.stack([0.1 * tf.ones([5]), 0.2 * tf.ones([5])])\n", + "model(inputs)" + ] + }, + { + "cell_type": "markdown", + "id": "f2b675fa", + "metadata": {}, + "source": [ + "``model`` is a model with quantum layer simulated using CPU/GPU and a classical layer on CPU/GPU while ``model1`` shares exactly the same architecture but is with quantum layer on real QPU while classical layer still live on CPU/GPU, namely, TC is powerful enough to handle these quantum-classical hybrid tasks with an interface familiar to any ML engineers." + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "id": "0ec02dac", + "metadata": { + "collapsed": true + }, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "INFO:tensorcircuit.cloud.wrapper:submit task on tencent::tianxuan_s1 for 1 circuits\n", + "INFO:tensorcircuit.cloud.wrapper:finished collecting count results of 1 tasks in 5.2411 seconds\n", + "INFO:tensorcircuit.cloud.wrapper:submit task on tencent::tianxuan_s1 for 1 circuits\n", + "INFO:tensorcircuit.cloud.wrapper:finished collecting count results of 1 tasks in 5.3121 seconds\n" + ] + }, + { + "data": { + "text/plain": [ + "" + ] + }, + "execution_count": 10, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "qlayer1 = tc.KerasHardwareLayer(partial(quantum_func, device=d), [2, 5])\n", + "model1 = tf.keras.Sequential([qlayer1, tf.keras.layers.Dense(1)])\n", + "model1(inputs)" + ] + }, + { + "cell_type": "code", + "execution_count": 11, + "id": "51620507", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Model: \"sequential\"\n", + "_________________________________________________________________\n", + " Layer (type) Output Shape Param # \n", + "=================================================================\n", + " quantum_layer (QuantumLayer (2, 5) 10 \n", + " ) \n", + " \n", + " dense (Dense) (2, 1) 6 \n", + " \n", + "=================================================================\n", + "Total params: 16\n", + "Trainable params: 16\n", + "Non-trainable params: 0\n", + "_________________________________________________________________\n", + "\n", + "\n", + "Model: \"sequential_1\"\n", + "_________________________________________________________________\n", + " Layer (type) Output Shape Param # \n", + "=================================================================\n", + " hardware_layer (HardwareLay multiple 10 \n", + " er) \n", + " \n", + " dense_1 (Dense) multiple 6 \n", + " \n", + "=================================================================\n", + "Total params: 16\n", + "Trainable params: 16\n", + "Non-trainable params: 0\n", + "_________________________________________________________________\n" + ] + } + ], + "source": [ + "model.summary()\n", + "print(\"\\n\")\n", + "model1.summary()" + ] + }, + { + "cell_type": "markdown", + "id": "3b5423ca", + "metadata": {}, + "source": [ + "we align the weights between the two models (numerical one vs hybrid one)" + ] + }, + { + "cell_type": "code", + "execution_count": 12, + "id": "ab5ca8a5", + "metadata": {}, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "INFO:tensorcircuit.cloud.wrapper:submit task on tencent::tianxuan_s1 for 1 circuits\n", + "INFO:tensorcircuit.cloud.wrapper:finished collecting count results of 1 tasks in 5.5113 seconds\n", + "INFO:tensorcircuit.cloud.wrapper:submit task on tencent::tianxuan_s1 for 1 circuits\n", + "INFO:tensorcircuit.cloud.wrapper:finished collecting count results of 1 tasks in 5.2209 seconds\n" + ] + }, + { + "data": { + "text/plain": [ + "" + ] + }, + "execution_count": 12, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "model1.set_weights(model.get_weights())\n", + "model1(inputs)" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "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.8.0" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/docs/source/tutorials/qubo_problem.ipynb b/docs/source/tutorials/qubo_problem.ipynb new file mode 100644 index 00000000..a4779366 --- /dev/null +++ b/docs/source/tutorials/qubo_problem.ipynb @@ -0,0 +1,580 @@ +{ + "cells": [ + { + "attachments": {}, + "cell_type": "markdown", + "id": "6ddb8a88-779a-43f7-ae14-115463bd87f5", + "metadata": {}, + "source": [ + "# Solving QUBO Problem using QAOA\n", + "\n", + "## Overview\n", + "\n", + "In this tutorial, we will demonstrate how to solve quadratic unconstrained binary optimization (QUBO) problems using QAOA. There is a specific application for portfolio optimization and we will introduce it in another [tutorial](https://tensorcircuit.readthedocs.io/en/latest/tutorials/portfolio_optimization.html).\n", + "\n", + "## QUBO problem\n", + "\n", + "### What is QUBO?\n", + "\n", + "Quadratic unconstrained binary optimization (QUBO) is a type of problem that aims to optimize a quadratic objective function using binary variables. The primary goal of a QUBO problem is to determine the assignments of binary variables that minimize or maximize the quadratic objective function. These variables represent choices or decision variables that can be either selected (1) or not selected (0). The objective function captures the associated costs, benefits, or constraints related to these decisions.\n", + "\n", + "From a computational perspective, solving a QUBO problem is NP-hard. This classification implies that solving the optimal solution to a QUBO instance is believed to be computationally challenging, and no known polynomial-time algorithm that can efficiently solve all QUBO problems.\n", + "\n", + "However, a promising approach called Quantum Approximate Optimization Algorithm (QAOA), introduced in [this tutorial](https://tensorcircuit.readthedocs.io/en/latest/tutorials/qaoa.html), has the potential to offer significant advantages when applied to QUBO problem-solving. QAOA leverages inherent quantum parallelism and interference effects to explore the solution space more efficiently compared to classical methods. This efficiency can lead to faster and more optimal solutions. In QAOA, each qubit represents a binary variable, and the objective function is calculated as the expected value of a quantum state generated by the ansatz (a quantum circuit with parameters to be decided). The parameters in the ansatz are iteratively optimized by a classical algorithm to improve the solution quality.\n", + "\n", + "### General Case\n", + "\n", + "For the general QUBO case, we wish to minimize a cost function of the form\n", + "\n", + "$$ \n", + "x^T Q x\n", + "$$\n", + "\n", + "where $x\\in\\{0,1\\}^n$ and $Q\\in\\mathbb{R}^{n\\times n}$ is a real symmetric matrix.\n", + "\n", + "This function maps to an Ising Hamiltonian \n", + "\n", + "$$\n", + "\\frac{1}{2}\\left(\\sum_{i=1}^n C_{ii} + \\sum_{i{n_qubits}}\"\n", + " states.append(a)\n", + "\n", + " # Calculate the probabilities of each state using the circuit's probability method\n", + " probs = K.numpy(c.probability()).round(decimals=4)\n", + "\n", + " # Sort the states and probabilities in descending order based on the probabilities\n", + " sorted_indices = np.argsort(probs)[::-1]\n", + " if reverse == True:\n", + " sorted_indices = sorted_indices[::-1]\n", + " state_sorted = np.array(states)[sorted_indices]\n", + " prob_sorted = np.array(probs)[sorted_indices]\n", + "\n", + " print(\"\\n-------------------------------------\")\n", + " print(\" selection\\t |\\tprobability\")\n", + " print(\"-------------------------------------\")\n", + " if wrap == False:\n", + " for i in range(len(states)):\n", + " print(\"%10s\\t |\\t %.4f\" % (state_sorted[i], prob_sorted[i]))\n", + " # Print the sorted states and their corresponding probabilities\n", + " elif wrap == True:\n", + " for i in range(4):\n", + " print(\"%10s\\t |\\t %.4f\" % (state_sorted[i], prob_sorted[i]))\n", + " print(\" ... ...\")\n", + " for i in range(-4, -1):\n", + " print(\"%10s\\t |\\t %.4f\" % (state_sorted[i], prob_sorted[i]))\n", + " print(\"-------------------------------------\")" + ] + }, + { + "cell_type": "code", + "execution_count": 83, + "id": "da315228", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "\n", + "-------------------------------------\n", + " selection\t |\tprobability\n", + "-------------------------------------\n", + " 10\t |\t 0.8848\n", + " 11\t |\t 0.1051\n", + " 01\t |\t 0.0093\n", + " 00\t |\t 0.0008\n", + "-------------------------------------\n" + ] + } + ], + "source": [ + "c = QAOA_ansatz_for_Ising(final_params, nlayers, pauli_terms, weights)\n", + "\n", + "print_result_prob(c)" + ] + }, + { + "cell_type": "code", + "execution_count": 84, + "id": "294ea9ce-5064-4176-94d0-8dbb7d1707f8", + "metadata": {}, + "outputs": [], + "source": [ + "def print_output(c):\n", + " n = c._nqubits\n", + " N = 2**n\n", + "\n", + " # Generate labels for the x-axis representing the binary states\n", + " x_label = r\"$\\left|{0:0\" + str(n) + r\"b}\\right>$\"\n", + " labels = [x_label.format(i) for i in range(N)]\n", + "\n", + " # Create a bar plot with the probabilities of each state\n", + " plt.bar(range(N), c.probability())\n", + "\n", + " # Set the x-axis ticks to the generated labels and rotate them for better visibility\n", + " plt.xticks(range(N), labels, rotation=70)" + ] + }, + { + "cell_type": "code", + "execution_count": 85, + "id": "fc1353ab-7a7a-4cdc-931c-3b90417c4961", + "metadata": { + "tags": [] + }, + "outputs": [ + { + "data": { + "image/png": "iVBORw0KGgoAAAANSUhEUgAAAiMAAAGuCAYAAABC7AYqAAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjcuMiwgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy8pXeV/AAAACXBIWXMAAA9hAAAPYQGoP6dpAAAcuklEQVR4nO3dcXDW9X3A8U8iwRMIOgQTiYK2KrjWlcLBTLde1XQb1XU3rwy33rW7Ye0YtS3F0RbKVECl9DQ7Z3t2cx0y3dFxenO6tmOmerPagIKDoUXXVqg2KQGKDlbAQPjuj9bcMgLmAemHhNfr7nOlv/x+PF/49jne/T1PnlRFRAkAgCTV2QsAAE5uYgQASCVGAIBUYgQASCVGAIBUYgQASCVGAIBUYgQASDUoewF9NXr06Ni9e3f2MgCACtTW1kZ7e/sRz+kXMTJ69Ohoa2vLXgYAcBQaGhqOGCT9IkbeuCPS0NDg7ggA9BO1tbXR1tb2pv9294sYecPu3bvFCAAMMN7ACgCkEiMAQCoxAgCkEiMAQCoxAgCkEiMAQCoxAgCkEiMAQCoxAgCkEiMAQCoxAgCkEiMAQCoxAgCkEiMAQKpB2QsAqNQdG1uzl3DSuuGSxuwlMAC5MwIApBIjAEAqMQIApBIjAEAqMQIApBIjAEAqMQIApBIjAEAqMQIApBIjAEAqMQIApBIjAEAqMQIApBIjAEAqMQIApBIjAEAqMQIApBIjAEAqMQIApBIjAEAqMQIApBIjAEAqMQIApBIjAEAqMQIApBIjAEAqMQIApBIjAEAqMQIApDqqGJk1a1Zs3rw59u7dG6tXr47Jkycf8fxPf/rT8cILL8SePXvi5Zdfjubm5jj11FOPasEAwMBScYxMnz49mpubY+HChTFx4sTYsGFDrFq1KkaNGtXr+X/0R38UX/ziF2PhwoVx8cUXx7XXXhvXXHNN3Hbbbce8eACg/6s4RubMmRP33HNP3HvvvbFp06aYOXNm7NmzJ2bMmNHr+e95z3viqaeeihUrVsSPfvSjePTRR2PFihUxZcqUY148AND/VRQjNTU1MWnSpGhpaek+VkqJlpaWaGxs7PWa7373uzFp0qTul3LOP//8uPLKK+Ob3/zmYR9n8ODBUVtb22MAgIFpUCUnjxw5MgYNGhQdHR09jnd0dMT48eN7vWbFihUxcuTIePLJJ6Oqqipqamri7rvvjiVLlhz2cebNmxc333xzJUsDAPqp4/7dNO973/ti/vz5MWvWrJg4cWJcffXVcdVVV8WCBQsOe82SJUti+PDh3dPQ0HC8lwkAJKnozsiOHTviwIEDUVdX1+N4XV1dbN26tddrFi9eHPfdd1987Wtfi4iI5557LoYOHRp/8zd/E7feemuUUg65prOzMzo7OytZGgDQT1V0Z2T//v2xbt26aGpq6j5WVVUVTU1N0dra2us1Q4YMiYMHD/Y41tXV1X0tAHByq+jOSEREc3NzLF++PNauXRtPP/10zJ49O4YOHRrLli2LiIjly5dHW1tbzJ8/PyIiHnnkkZgzZ078x3/8R6xZsyYuuOCCWLx4cTzyyCOHRAoAcPKpOEZWrlwZo0aNikWLFkV9fX2sX78+pk6dGtu2bYuIiDFjxvSIjFtuuSVKKXHLLbdEQ0NDbN++PR555JH4whe+8Nb9KQCAfqsqIg5908YJpra2Nnbt2hXDhw+P3bt3Zy8HSHbHxt5fFub4u+GS3j/GAXrT13+//WwaACCVGAEAUokRACCVGAEAUokRACCVGAEAUokRACCVGAEAUokRACCVGAEAUokRACCVGAEAUokRACCVGAEAUokRACCVGAEAUokRACCVGAEAUokRACCVGAEAUokRACCVGAEAUokRACCVGAEAUokRACCVGAEAUokRACCVGAEAUokRACCVGAEAUokRACCVGAEAUokRACCVGAEAUokRACCVGAEAUokRACCVGAEAUokRACCVGAEAUokRACCVGAEAUokRACCVGAEAUokRACCVGAEAUokRACCVGAEAUokRACCVGAEAUokRACCVGAEAUokRACCVGAEAUokRACCVGAEAUokRACCVGAEAUokRACCVGAEAUokRACCVGAEAUokRACCVGAEAUokRACCVGAEAUokRACCVGAEAUh1VjMyaNSs2b94ce/fujdWrV8fkyZOPeP7pp58eX/7yl6O9vT327dsXL774YnzgAx84qgUDAAPLoEovmD59ejQ3N8fMmTNjzZo1MXv27Fi1alWMGzcutm/ffsj5NTU18eijj8a2bdti2rRp0dbWFmPHjo3XXnvtrVg/ANDPVRwjc+bMiXvuuSfuvffeiIiYOXNmXHXVVTFjxoxYunTpIefPmDEjRowYEe95z3viwIEDERHxox/96NhWDQAMGBW9TFNTUxOTJk2KlpaW7mOllGhpaYnGxsZer/m93/u9aG1tja985SuxdevW2LhxY8ybNy+qqw//0IMHD47a2toeAwAMTBXFyMiRI2PQoEHR0dHR43hHR0fU19f3es3b3va2mDZtWpxyyilx5ZVXxuLFi+OGG26IBQsWHPZx5s2bF7t27eqetra2SpYJAPQjx/27aaqrq2Pbtm3x8Y9/PJ599tlYuXJl3HrrrTFz5szDXrNkyZIYPnx49zQ0NBzvZQIASSp6z8iOHTviwIEDUVdX1+N4XV1dbN26tddrfvKTn8T+/fvj4MGD3cc2bdoUZ599dtTU1MT+/fsPuaazszM6OzsrWRoA0E9VdGdk//79sW7dumhqauo+VlVVFU1NTdHa2trrNU899VRccMEFUVVV1X3soosuivb29l5DBAA4uVT8Mk1zc3Ncd9118dGPfjTGjx8fd999dwwdOjSWLVsWERHLly+P2267rfv8u+++O0aMGBF33nlnXHjhhXHllVfG/Pnz4ytf+cpb96cAAPqtir+1d+XKlTFq1KhYtGhR1NfXx/r162Pq1Kmxbdu2iIgYM2ZMj5dkfvzjH8fv/M7vxF/+5V/Gf/7nf0ZbW1vceeedvX4bMABw8qmKiJK9iDdTW1sbu3btiuHDh8fu3buzlwMku2Nj7y8Lc/zdcEnvH+MAvenrv99+Ng0AkEqMAACpxAgAkEqMAACpxAgAkEqMAACpxAgAkEqMAACpxAgAkEqMAACpxAgAkEqMAACpxAgAkEqMAACpxAgAkEqMAACpxAgAkEqMAACpxAgAkEqMAACpxAgAkEqMAACpxAgAkEqMAACpxAgAkEqMAACpxAgAkEqMAACpxAgAkEqMAACpxAgAkEqMAACpxAgAkEqMAACpxAgAkEqMAACpxAgAkEqMAACpxAgAkEqMAACpxAgAkEqMAACpxAgAkEqMAACpxAgAkEqMAACpxAgAkEqMAACpxAgAkEqMAACpxAgAkEqMAACpxAgAkEqMAACpxAgAkEqMAACpxAgAkEqMAACpxAgAkEqMAACpxAgAkEqMAACpxAgAkEqMAACpxAgAkEqMAACpxAgAkEqMAACpxAgAkOqoYmTWrFmxefPm2Lt3b6xevTomT57cp+uuueaaKKXEP/3TPx3NwwIAA1DFMTJ9+vRobm6OhQsXxsSJE2PDhg2xatWqGDVq1BGvGzt2bNx+++3xxBNPHPViAYCBp+IYmTNnTtxzzz1x7733xqZNm2LmzJmxZ8+emDFjxuEfpLo6/uEf/iFuuummeOmll45pwQDAwFJRjNTU1MSkSZOipaWl+1gpJVpaWqKxsfGw1914442xbdu2+Lu/+7s+Pc7gwYOjtra2xwAAA1NFMTJy5MgYNGhQdHR09Dje0dER9fX1vV7zG7/xG3HttdfGdddd1+fHmTdvXuzatat72traKlkmANCPHNfvphk2bFjcd999cd1118VPf/rTPl+3ZMmSGD58ePc0NDQcx1UCAJkGVXLyjh074sCBA1FXV9fjeF1dXWzduvWQ89/+9rfH+eefH4888kj3serqn/fP/v37Y9y4cb2+h6SzszM6OzsrWRoA0E9VdGdk//79sW7dumhqauo+VlVVFU1NTdHa2nrI+S+88EK8853vjAkTJnTPww8/HI8//nhMmDAhXnnllWP/EwAA/VpFd0YiIpqbm2P58uWxdu3aePrpp2P27NkxdOjQWLZsWURELF++PNra2mL+/Pnx+uuvx/PPP9/j+tdeey0i4pDjAMDJqeIYWblyZYwaNSoWLVoU9fX1sX79+pg6dWps27YtIiLGjBkTBw8efMsXCgAMTFURUbIX8WZqa2tj165dMXz48Ni9e3f2coBkd2w89GVhfjluuOTwH+MA/19f//32s2kAgFRiBABIJUYAgFRiBABIJUYAgFRiBABIJUYAgFRiBABIJUYAgFRiBABIJUYAgFRiBABIJUYAgFRiBABIJUYAgFRiBABIJUYAgFRiBABIJUYAgFRiBABIJUYAgFRiBABIJUYAgFRiBABIJUYAgFRiBABIJUYAgFRiBABIJUYAgFRiBABIJUYAgFRiBABIJUYAgFRiBABIJUYAgFRiBABIJUYAgFRiBABIJUYAgFRiBABIJUYAgFRiBABIJUYAgFRiBABIJUYAgFRiBABIJUYAgFRiBABIJUYAgFRiBABIJUYAgFRiBABIJUYAgFRiBABIJUYAgFRiBABIJUYAgFRiBABIJUYAgFRiBABIJUYAgFRiBABIJUYAgFRiBABIJUYAgFRiBABIJUYAgFRiBABIJUYAgFRHFSOzZs2KzZs3x969e2P16tUxefLkw577sY99LJ544onYuXNn7Ny5Mx599NEjng8AnFwqjpHp06dHc3NzLFy4MCZOnBgbNmyIVatWxahRo3o9/7LLLosVK1bE5ZdfHo2NjfHKK6/Ev/3bv8Xo0aOPefEAQP9XFRGlkgtWr14dzzzzTHzyk5/8+W9QVRWvvPJK3HXXXbF06dI3vb66ujpeffXVuP766+O+++7r02PW1tbGrl27Yvjw4bF79+5KlgsMQHdsbM1ewknrhksas5dAP9LXf78rujNSU1MTkyZNipaWlu5jpZRoaWmJxsa+/Q90yJAhUVNTEzt37jzsOYMHD47a2toeAwAMTBXFyMiRI2PQoEHR0dHR43hHR0fU19f36fdYunRptLe39wia/2/evHmxa9eu7mlra6tkmQBAP/JL/W6az33uc/GHf/iHcfXVV8frr79+2POWLFkSw4cP756GhoZf4ioBgF+mQZWcvGPHjjhw4EDU1dX1OF5XVxdbt2494rU33HBDfP7zn4/3v//9sXHjxiOe29nZGZ2dnZUsDQDopyq6M7J///5Yt25dNDU1dR+rqqqKpqamaG09/BvK5s6dG3/xF38RU6dOjXXr1h39agGAAaeiOyMREc3NzbF8+fJYu3ZtPP300zF79uwYOnRoLFu2LCIili9fHm1tbTF//vyIiPjsZz8bixYtig9/+MOxZcuW7rsq//M//xM/+9nP3sI/CgDQH1UcIytXroxRo0bFokWLor6+PtavXx9Tp06Nbdu2RUTEmDFj4uDBg93n/9mf/Vmceuqp8eCDD/b4fW6++eZYuHDhMS4fAOjvKv6ckQw+ZwT4v3zOSB6fM0IljsvnjAAAvNXECACQSowAAKnECACQSowAAKnECACQSowAAKnECACQSowAAKnECACQSowAAKnECACQSowAAKnECACQSowAAKnECACQSowAAKnECACQSowAAKnECACQSowAAKnECACQSowAAKnECACQSowAAKnECACQSowAAKnECACQSowAAKnECACQalD2AgAgIuKOja3ZSzhp3XBJY+rjuzMCAKQSIwBAKjECAKQSIwBAKjECAKQSIwBAKjECAKQSIwBAKjECAKQSIwBAKjECAKQSIwBAKjECAKQSIwBAKjECAKQSIwBAKjECAKQSIwBAKjECAKQSIwBAKjECAKQSIwBAKjECAKQSIwBAKjECAKQSIwBAKjECAKQSIwBAKjECAKQSIwBAKjECAKQSIwBAKjECAKQSIwBAKjECAKQSIwBAKjECAKQadDQXzZo1K+bOnRv19fWxYcOG+OQnPxnPPPPMYc+fNm1aLF68OM4777z4/ve/H5/73OfiW9/61lEvGvrijo2t2Us4ad1wSWP2EoB+pOI7I9OnT4/m5uZYuHBhTJw4MTZs2BCrVq2KUaNG9Xp+Y2NjrFixIr72ta/Fu9/97njooYfioYceine84x3HvHgAoP+rOEbmzJkT99xzT9x7772xadOmmDlzZuzZsydmzJjR6/mf/vSn41//9V/j9ttvjxdeeCFuvPHGePbZZ+P6668/5sUDAP1fRS/T1NTUxKRJk2LJkiXdx0op0dLSEo2Nvd+WbWxsjObm5h7HVq1aFb//+79/2McZPHhwnHrqqd3/vba2tsd/Ql8Mrj4lewknreP9XLW3eY7n3trXPMdrX/v6+1YUIyNHjoxBgwZFR0dHj+MdHR0xfvz4Xq+pr6/v9fz6+vrDPs68efPi5ptvPuR4W1tbJcsFkly/a1f2EjhO7O3AdLz3tba2Nnbv3n3Yrx/VG1iPtyVLlhxyN2XEiBGxc+fOpBWdmGpra6OtrS0aGhqOuMn0L/Z14LK3A5e9Pbza2tpob28/4jkVxciOHTviwIEDUVdX1+N4XV1dbN26tddrtm7dWtH5ERGdnZ3R2dnZ45jNPbzdu3f7+xmA7OvAZW8HLnt7qL78fVT0Btb9+/fHunXroqmpqftYVVVVNDU1RWtr799G2dra2uP8iIjf+q3fOuz5AMDJp1Qy06dPL3v37i0f/ehHy/jx48tXv/rVsnPnznLWWWeViCjLly8vt912W/f5jY2NpbOzs8yZM6eMGzeu3HTTTeX1118v73jHOyp6XHPo1NbWllJKqa2tTV+Lsa/G3p7MY2+PeSq/6BOf+ETZsmVL2bdvX1m9enWZMmVK99cef/zxsmzZsh7nT5s2rbzwwgtl3759ZePGjeUDH/hA9h96QMzgwYPLTTfdVAYPHpy+FmNfjb09mcfeHttU/eIXAAAp/GwaACCVGAEAUokRACCVGAEAUokRACCVGAEAUokRACDVCfmD8ji8c889Ny688MLYsmVLtLe3xznnnBOvvfZa7NixI3tpHCN7OzDZ14HL3r51xEg/Mnr06Pj4xz8ekydPjne+851RSok1a9bEeeedF2eddVbcddddcdddd8W+ffuyl0qF7O3AZF8HLnv71vIJrP3MmDFj4qyzzorvf//7MW7cuGhoaIiXX3453vWud8XHPvaxePbZZ+NLX/pSvPzyy9lLpUL2dmCyrwOXvX1rpX8mvXlr5tJLLy3//M//XJ544oly+eWXp6/H2FtjX0/WsbcVT/oCzFs4tbW15Y477ijPPPNMmTNnThkyZEj6mkzfpqqqyt6ehGNf++94zr6Ff5e/+AX9wBlnnBF/8id/EmeeeWacdtppsXnz5vjmN78ZL7300iHnXnvttfHhD384mpqaElbK8WRv+w/PWSLsbV+IkX7i3HPPjfvvvz/Gjh0b27dvjx07dsSIESNiyJAh0draGn/7t38bTz/9dFRVVUUpP9/ShoaGaGtr63GME8+wYcNiwoQJ8b3vfS9effXVPu2VvT3xec4OXJ6zx0f67RlzhFtXv7gNeOedd5bHH3+8nH322SUiyujRo8sVV1xRvvCFL5THHnusPPbYY+Vtb3tb+npN36e6urpERLn11ltLV1dX+cY3vlH+9E//tFx88cVl6NChPc6tr68v119/fTn11FPT122OPJ6zA3c8Z4/rpC/A9GG+853vlOuvv77Xr1188cVl7dq15dvf/nb3k8X0n3n22WfLsmXLyoMPPlj27dtXdu7cWVasWFGmTZtWxo4dW0455ZSyYMGC8swzz6Sv1fR9PGcH7njOHpdJX4B5k6muri5f/OIXy/PPP1/Gjh3b6zmNjY3l+eefL7/2a7+Wvl7T92loaCiPPvpomTZtWvexj3zkI+W73/1u6erqKlu2bCl33nln6ejoKHPnzi0RUU455ZT0dZsjj+fswB3P2eM26QswfZhx48aV1atXl/vvv79MmTKl1NTU9Pj6r/7qr5Y9e/aUYcOGpa/V9H3OOOOM8qEPfag0NjYe8rURI0aUz372s+XHP/5x6erqOuQ2sDmxZ9y4caW1tbX8/d//vefsAJo3nrO//uu/fsjXPGePfryBtZ+oqqqKK664IpYuXRrvfve744knnogHH3wwXnrppZgwYUK8973vjQMHDsQHP/jBqK6ujoMHD2YvmT465ZRToqurKyIiqquro6amJrq6uuLAgQMREXHHHXfExIkT4/LLL7e3/cxVV10VS5cujYsuuij+/d//PR566KHYvHmz52w/V1398x/r9saeVVdXR1VVVffz2HP26KQXkTnynHbaaWXIkCHlnHPOKTU1NWXSpEnly1/+ctm6dWvZsmVLee6558rtt9/efTvYa9D9Z4YMGVKGDRtWJk2aVM4888weX6uuri6nn356+cEPflD++I//2N7243n/+99fvv71r5ef/vSn5Yc//KHn7ACe008/vfzwhz/0nK180hdgepk33pH/27/926WlpaX893//d1m/fn1pbW0tf/3Xf13e+973logo559/fqmvr09frzn6vX311VfL2rVry1NPPVWWL19ePvjBD3bf0h88eHCZMmVK+ppN3+fNXnYZP378IeFp+sf09SW1yZMnp6+1v42XaU5gp59+erz44ovx4IMPxje+8Y0455xzYuzYsTFhwoQYPXp0PPXUUzF37tzYu3dv9lKpUG97O2bMmJgwYULU1dXFmjVr4sYbb4ydO3dmL5UKDBkyJP7qr/4qHnvssVi9enW0t7f3+oPSRo8eHe3t7Qkr5Gj1dW/r6+tj69atCSvs/9KLyPQ+n/nMZ8ratWsPuc130UUXldmzZ5eXX365/Mu//Is3SfXD6cvePvzww97c2E/mjbtdn/rUp0pXV1fZu3dvaW9vL1/96lfL1KlTy+jRo8ugQYNKRJSRI0eWlpYWnzHST6aSvT3zzDPLt7/9bXt7dJO+AHOY+fM///OyatWqctppp/X69UsvvbT84Ac/KJdeemn6Wo29NVG+/vWvlyVLlpRRo0aVT3ziE+W5554rXV1d5fnnny+LFy8uU6ZMKXPnzi2vvvpq+lqNvT3BJn0B5jBzySWXlB07dpSbb765jBw58pCvV1dXl3Xr1pVPfepT6Ws19vZkn8GDB5fPf/7zZcGCBT2On3feeWXp0qWlvb297Nmzp3R1dZXbbrutRPj8if4y9vaXMukLMEeYWbNmlfb29rJq1aryoQ99qJx99tmltra2nHbaaWXKlCllz5495e1vf3v6Oo29NVHOOeeccsEFF5SIKDU1NYf8VNdrrrmmdHV1lXPPPTd9rcbenmCTvgDzJvObv/mb5Vvf+lbZt29f2b59e3n44YfL6tWry5YtW8pdd91VIt78R1mbE3Ps7cCdN/atqqqq+71BCxYsKD/5yU/saz8fe3sc/k5/8QtOMHV1dXHFFVfEAw88EPv374+IiLPOOiuuuuqquOyyy+LFF1+MNWvWRGtra+zZs8dPguxH7O3AVF9fH5dddlk88MAD3R9YFxE9PvTqM5/5TGzfvj3uv//+Hh92x4nN3h5/YuQEdeGFF8Ytt9wSZ5xxRvzXf/1XrFy5Mr7zne90f90/UP2XvR2Y/u++btq0KR544IF48skne5wzdOjQ+NnPfpa0Qo6WvT3+xMgJbtiwYXH11VfH7/7u78av/MqvRGtra/zjP/5jfO9738teGsfI3g5M/3dfzzjjjHjyySdj5cqV8eKLL2YvjWNkb48fMXIC+///D3nMmDHxB3/wB/G+970vhg8fHh/5yEfilVdeSVwhR8veDkz2deCyt8df+htXTOXzrne9K30Nxt4a+2rs7Vsx7owAAKmqsxcAAJzcxAgAkEqMAACpxAgAkEqMAACpxAgAkEqMAACpxAgAkEqMAACpxAgAkEqMAACp/hfD9CqCsTZO9wAAAABJRU5ErkJggg==", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "print_output(c)" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "c48e4a38", + "metadata": {}, + "source": [ + "## Improve the performance with CVaR\n", + "\n", + "Conditional Value-at-Risk (CVaR) is a risk measure that quantifies the potential loss beyond a certain threshold (alpha), considering the tail end of the distribution. As proposed by [Barkoutsos et al. (2020)](https://arxiv.org/abs/1907.04769), incorporating CVaR as an objective function in QAOA allows for addressing risk-averse optimization problems effectively. By optimizing for CVaR, the algorithm focuses on minimizing the expected value of the worst-case scenario, rather than solely optimizing for the mean or expected value, which usually leads to faster convergence to a more accurate result.\n", + "\n", + "To showcase the performance of CVaR, a more complicated QUBO problem is used. This QUBO problem is described as a randomly generated symmetric Q matrix. The Q matrix is:" + ] + }, + { + "cell_type": "code", + "execution_count": 86, + "id": "02ec55b6", + "metadata": {}, + "outputs": [], + "source": [ + "Q = np.array(\n", + " [\n", + " [-60.3657, 11.68835, 12.23445, 11.7274, 11.9959, 11.80955],\n", + " [11.68835, -59.7527, 11.6231, 13.23295, 11.96335, 12.44725],\n", + " [12.23445, 11.6231, -59.69535, 11.29525, 12.00035, 11.78495],\n", + " [11.7274, 13.23295, 11.29525, -59.12165, 12.1006, 12.5461],\n", + " [11.9959, 11.96335, 12.00035, 12.1006, -60.45515, 12.07545],\n", + " [11.80955, 12.44725, 11.78495, 12.5461, 12.07545, -59.9126],\n", + " ]\n", + ")\n", + "pauli_terms, weights, offset = QUBO_to_Ising(Q)" + ] + }, + { + "cell_type": "markdown", + "id": "76879a55", + "metadata": {}, + "source": [ + "Then let's define a function to classically brute-force calculate all feasible combinations of stocks and their associated cost. The results are printed below." + ] + }, + { + "cell_type": "code", + "execution_count": 87, + "id": "46e9cbd9", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "\n", + "-------------------------------------\n", + " selection\t |\t cost\n", + "-------------------------------------\n", + " 110010\t |\t-109.2784\n", + " 100011\t |\t-108.9717\n", + " 011010\t |\t-108.7296\n", + " 111000\t |\t-108.7219\n", + " 101100\t |\t-108.6685\n", + " 001110\t |\t-108.4798\n", + " 001011\t |\t-108.3416\n", + " 101001\t |\t-108.3157\n", + " ...\t |\t ...\n", + "-------------------------------------\n" + ] + } + ], + "source": [ + "def print_Q_cost(Q, wrap=False, reverse=False):\n", + " n_stocks = len(Q)\n", + " states = []\n", + " for i in range(2**n_stocks):\n", + " a = f\"{bin(i)[2:]:0>{n_stocks}}\"\n", + " n_ones = 0\n", + " for j in a:\n", + " if j == \"1\":\n", + " n_ones += 1\n", + " states.append(a)\n", + "\n", + " cost_dict = {}\n", + " for selection in states:\n", + " x = np.array([int(bit) for bit in selection])\n", + " cost_dict[selection] = np.dot(x, np.dot(Q, x))\n", + " cost_sorted = dict(sorted(cost_dict.items(), key=lambda item: item[1]))\n", + " if reverse == True:\n", + " cost_sorted = dict(\n", + " sorted(cost_dict.items(), key=lambda item: item[1], reverse=True)\n", + " )\n", + " num = 0\n", + " print(\"\\n-------------------------------------\")\n", + " print(\" selection\\t |\\t cost\")\n", + " print(\"-------------------------------------\")\n", + " for k, v in cost_sorted.items():\n", + " print(\"%10s\\t |\\t%.4f\" % (k, v))\n", + " num += 1\n", + " if (num >= 8) & (wrap == True):\n", + " break\n", + " print(\" ...\\t |\\t ...\")\n", + " print(\"-------------------------------------\")\n", + "\n", + "\n", + "print_Q_cost(Q, wrap=True)" + ] + }, + { + "cell_type": "markdown", + "id": "04d4ea38", + "metadata": {}, + "source": [ + "The QAOA with CVaR and three different alpha (1, 0.25, 0.1) will be run and a callback function will be used to record the parameters during the solving procedure. When alpha is $1$, the complete measurement results are accepted and the model changes to the standard QAOA." + ] + }, + { + "cell_type": "code", + "execution_count": 88, + "id": "d3b386d6", + "metadata": {}, + "outputs": [], + "source": [ + "# Set the number of layers to 2\n", + "nlayers = 2\n", + "\n", + "# Define a list of alpha values\n", + "alpha_list = [0.1, 0.25, 1]\n", + "\n", + "\n", + "# Define the callback function to record parameter values\n", + "def record_param(xk):\n", + " xk_list.append(xk)\n", + "\n", + "\n", + "# Generate initial parameters randomly for all alpha\n", + "init_params = K.implicit_randn(shape=[2 * nlayers], stddev=0.5)\n", + "\n", + "# Create an empty list to store parameter values for each alpha\n", + "params_list = []\n", + "\n", + "# Iterate over each alpha value\n", + "for alpha in alpha_list:\n", + " # Create a new empty list for callback function\n", + " xk_list = []\n", + "\n", + " # Run the QUBO_QAOA_cvar function with the specified parameters\n", + " final_params = QUBO_QAOA_cvar(\n", + " Q,\n", + " nlayers,\n", + " alpha=alpha,\n", + " callback=record_param,\n", + " maxiter=100,\n", + " init_params=init_params,\n", + " )\n", + "\n", + " # Append the parameter values for the current alpha to the params_list\n", + " params_list.append(xk_list)" + ] + }, + { + "cell_type": "code", + "execution_count": 89, + "id": "b3da7c48", + "metadata": {}, + "outputs": [], + "source": [ + "best = 50 # Represents the binary number 110010\n", + "prob_list = [] # Create an empty list to store probabilities\n", + "loss_list = [] # Create an empty list to store loss values\n", + "\n", + "# Iterate three times\n", + "for i in range(3):\n", + " c = QAOA_ansatz_for_Ising(init_params, nlayers, pauli_terms, weights)\n", + " loss = [cvar_loss(nlayers, Q, 1000, alpha_list[i], True, init_params)]\n", + " prob = [c.probability()[best].numpy()]\n", + "\n", + " # Iterate 100 times\n", + " for j in range(100):\n", + " if j < len(params_list[i]) - 1:\n", + " params = params_list[i][j]\n", + " else:\n", + " pass\n", + " c = QAOA_ansatz_for_Ising(params, nlayers, pauli_terms, weights)\n", + " loss.append(cvar_loss(nlayers, Q, 1000, alpha_list[i], True, params))\n", + " prob.append(c.probability()[best].numpy())\n", + "\n", + " loss_list.append(loss) # Append the loss values to the loss_list\n", + " prob_list.append(prob) # Append the probability values to the prob_list" + ] + }, + { + "cell_type": "code", + "execution_count": 92, + "id": "d1f375ce", + "metadata": {}, + "outputs": [ + { + "data": { + "image/png": "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u3dvceLECfHNN99UVpu9/9Ju9LJr1y7x0UcfyZ8VCoW4ePGiePnll73etupSateuLYQQ4uabbxYARGhoqCgsLBT33nuvXKdly5ZCCCF69Ojh9fb6WwkODhaJiYli0KBBYuvWrXJwxPdccWX27Nli27ZtLutcvnxZTJo0Sf4cGhoqDAaDeOCBB7zefn8pq1evFl988YXdsZ9++kksW7aM77mCiqPgqLR32qpVKyGEEF26dJHr3HrrrcJsNou6detWeBvZreZlGo0GXbp0webNm+VjQghs3rwZcXFxXmxZ9RIWFgYAuHr1KgCgS5cu0Gq1du89MTERSUlJfO9lsHDhQqxduxZbtmyxO873XHHuuusu7NmzBz/88ANSUlKwb98+PPHEE/L5m266CXXr1rV719nZ2di9ezfftQf++usvDBo0CM2bNwcAdOjQAX369MG6desA8D1XBnfeaVxcHDIyMrB37165zubNm2GxWNCjR48KbxM3nvWy2rVrQ61WIyUlxe54SkoKWrVq5aVWVS8KhQIffPABduzYgSNHjgAAoqOjUVhYiKysLLu6KSkpiI6O9kYz/dYDDzyAzp07Oxwnx/dccZo0aYKnnnoK8+bNw9tvv41u3brhww8/hNFoxNKlS+X36ei/JXzX7nvnnXcQGhqK48ePw2w2Q6VS4dVXX8W3334LAHzPlcCddxodHY3U1FS782azGVevXq2U987giKq9hQsXol27dujTp4+3m1LtNGjQAAsWLMDgwYO5pUIlUyqV2LNnD1599VUAwIEDB9CuXTtMnDgRS5cu9XLrqo+RI0dizJgxGD16NI4cOYKOHTvigw8+wOXLl/mebyDsVvMyvV6PoqIiREVF2R2PiopCcnKyl1pVfXz00UcYNmwYBgwYgEuXLsnHk5OTERAQIHe3SfjePdOlSxdERUVh3759MJlMMJlM6N+/P5577jmYTCakpKTwPVeQK1eu4OjRo3bHjh07hpiYGACQ3yf/W1I+c+bMwTvvvIPvv/8e//77L5YvX4758+dj6tSpAPieK4M77zQ5ObnELFeVSoWaNWtWyntncORlJpMJe/fuxaBBg+RjCoUCgwYNQkJCghdb5v8++ugjjBgxAgMHDsS5c+fszu3duxdGo9Huvbdo0QKNGjXie/fAli1b0K5dO3Ts2FEu//zzD7755ht07NgRe/bs4XuuIDt37kTLli3tjrVo0QJJSUkAgLNnz+LKlSt271qn06FHjx581x4ICgqCxWKxO2Y2m6FUWn9d8j1XPHfeaUJCAiIiItC5c2e5zsCBA6FUKrF79+5KaZfXR67f6GXkyJHCYDCIhx9+WLRq1Up89tln4urVq6JOnTpeb5u/loULF4qMjAzRt29fERUVJZcaNWrIdT755BNx7tw50b9/f9G5c2exc+dOsXPnTq+33d+L7Ww1vueKK127dhVGo1FMnTpVNG3aVDz44IMiNzdXjB49Wq7z0ksviatXr4o777xTtGvXTqxcuZJTzD0sX331lbhw4YI8lf/uu+8Wqamp4p133uF7LkcJDg4WsbGxIjY2VgghxPPPPy9iY2NFw4YN3X6nv/32m9i7d6/o1q2b6NWrl0hMTORU/upennnmGXHu3DlRUFAgdu3aJbp37+71NvlzcWbcuHFynYCAAPHxxx+L9PR0kZubK37++WcRFRXl9bb7e7k+OOJ7rrhyxx13iEOHDgmDwSCOHj0qnnjiiRJ1ZsyYIa5cuSIMBoPYtGmTaN68udfb7U8lJCREzJ8/X5w7d07k5+eLU6dOiTfffFNoNBq+53KUfv36Ofxv8ldffeX2O42IiBDffPONyM7OFpmZmWLx4sUiODi4UtqruPYDEREREYFjjoiIiIjsMDgiIiIissHgiIiIiMgGgyMiIiIiGwyOiIiIiGwwOCIiIiKyweCIiIiIyAaDIyIiIiIbDI6IyOds3boV8+fP93Yz7AghMHz4cG83g4iqiNeXFWdhYWGxLRERESIkJEQAEGfPnhXx8fFV9uw33nhD7N+/v8TxqKgoodVqvf5uWFhYKr+oQUTkYzIyMir8nhqNBiaTqczXp6SkVGBriMjXeT1CY2FhYbEt0ua1W7duLbFRpVSnd+/eYtu2bSI/P1+cP39eLFiwQAQFBcnnz549K1577TWxZMkSkZWVJW9w+c4774jExESRl5cnTp8+LWbOnCnUarUAIMaNG+d0s2IhhBg+fLh8/3bt2oktW7aI/Px8odfrxaJFi+w2wfzqq6/EypUrxaRJk8Tly5eFXq8XH3/8sfwsAOKpp54SJ06cEAaDQSQnJ4sff/zR6++ehYUFAj7QABYWFha7IgVHERER4vz58+K1114TUVFRIioqSgAQTZo0ETk5OSI+Pl40a9ZMxMXFib1794ovv/xSvsfZs2dFZmameOGFF0STJk1EkyZNBADx6quviri4ONGoUSMxbNgwceXKFTF58mQBQNSoUUPMmTNHHD58WH5ejRo1BGAfHAUFBYlLly6Jn376SbRt21YMGDBAnD592m6H8a+++kpkZmaKTz75RLRs2VLccccdIjc3VzzxxBMCgOjSpYswmUxi1KhRIiYmRnTs2FE8++yzXn/3LCwsEPCBBrCwsLDYFSk4AhyPOfr888/FZ599Znesd+/eoqioSAQEBMjX/fLLL6U+a9KkSeKff/6RPzsbc2QbHD3xxBMiPT3dLlN12223iaKiIlGnTh0BWIOjs2fPCqVSKdf5/vvvxYoVKwQAMWLECJGZmSmPrWJhYfGdwjFHROR3YmNj0aFDB4wZM0Y+plAooFKpcNNNN+H48eMAgD179pS4duTIkXjuuefQtGlThISEQK1WIzs726Pnt27dGgcPHkR+fr58bOfOnVCpVGjZsiVSU1MBAEeOHIHFYpHrXLlyBe3btwcAbNq0CUlJSThz5gzWr1+P9evXY+XKlTAYDB61hYgqHqfyE5HfCQkJwaJFi9CxY0e5xMbGolmzZjh9+rRcLy8vz+66nj174ptvvsFvv/2GYcOGoVOnTpg1axa0Wm2ltPP6AeBCCCiV1v/s5ubmonPnznjwwQdx5coVzJw5EwcPHkRYWFiltIWI3MfMERH5NKPRCJVKZXds3759aNOmjV0g5I5evXohKSkJb7/9tnysUaNGpT7veseOHcMjjzyCoKAgOXvUu3dvmM1mJCYmut0es9mMLVu2YMuWLZgxYwYyMzMxcOBArFy50oNvRUQVjZkjIvJp586dQ9++fVGvXj3UqlULAPDuu++iV69e+Oijj+SM0V133YWPPvrI5b1OnjyJmJgYPPDAA2jSpAmeffZZjBgxosTzbrrpJsTGxqJWrVoOs0rffPMNCgoKsGTJErRt2xb9+/fHRx99hGXLlsldaqW544478OyzzyI2NhYxMTF4+OGHoVQqPQquiKhyMDgiIp82bdo0NG7cGKdPn4ZerwcAHD58GP369UOLFi2wfft27N+/HzNnzsTly5dd3mv16tWYP38+Pv74Yxw4cAC9evXCm2++aVfn559/xvr167F161bo9Xo8+OCDJe5jMBhw6623ombNmvjnn3/w008/YcuWLfi///s/t79XZmYm7rnnHvz+++84duwYJk6ciAcffBBHjx51+x5EVDkUsI7MJiIiIiIwc0RERERkh8ERERERkQ0GR0REREQ2GBwRERER2WBwRERERGSDwRERERGRDQZHRERERDYYHBERERHZYHBEREREZIPBEREREZENBkdERERENv4flMrniyoj9T4AAAAASUVORK5CYII=", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "for loss in loss_list:\n", + " plt.plot(loss)\n", + "plt.legend([\"alpha = 0.1\", \"alpha = 0.25\", \"alpha = 1\"])\n", + "plt.xlabel(\"iterations\")\n", + "p = plt.ylabel(\"cost\")" + ] + }, + { + "cell_type": "markdown", + "id": "6a88dda3", + "metadata": {}, + "source": [ + "The depicted figure illustrates that utilizing CVaR results in quicker convergence towards a lower loss." + ] + }, + { + "cell_type": "code", + "execution_count": 93, + "id": "f81fdeae", + "metadata": {}, + "outputs": [ + { + "data": { + "image/png": "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/vSnP8He3h5//vOfMWjQIJw7dw4zZ87sF88uISIiop6hejf3rAXPADEYDIbpQqvVSnJysnh5eameC+PZDy8vL0lOThatVtvhte78/lb9m6CJiMi8VVZWAgB8fHxUzoT6gof/TvR6/VPtR/VLYEREZN4aGhpw5swZREZGAgBu3LihPBWd6CFra2v4+PggMjISZ86ceeIXPz5xfz2UFxER0Y+WmJgIAIiKilI5E3rWnTlzRvn38jQs8OBaGH2PRqNBbW0tHB0d+Z1AREQmZGdnB2dn5371hXvUM0QEer2+0zM/3fn9zTNARET0zGhsbMSdO3fUToPMABdBExERkdlhA0RERERmhw0QERERmR2uAeqERqNROwUiIiLqou783mYD9AgPC1haWqpyJkRERNRdGo3miXeB8Tb4x3B3d++VW+A1Gg1KS0vxwgsv8Bb7XsQ6mwbrbBqss2mwzqbR23XWaDQoKyt74jyeAXqMrhTvadTV1fE/mAmwzqbBOpsG62warLNp9Fadu7pPLoImIiIis8MGiIiIiMwOGyATa25uxh/+8Ac0NzernUq/xjqbButsGqyzabDOpvGs1JmLoImIiMjs8AwQERERmR02QERERGR22AARERGR2WEDRERERGaHDZAJrVixAkVFRTAYDDh//jyCgoLUTqlPe/vtt/H111+jtrYWOp0OqampGDVqlNEcW1tb7Nq1C3q9HnV1dTh8+DBcXFxUyrh/WL9+PUQE8fHxyhjr3DPc3d1x4MAB6PV6NDY24ptvvkFgYKDRnJiYGJSVlaGxsRGnTp3CiBEjVMq2b7K0tERsbCxu376NxsZG3Lp1C5s2beowj3XuvpdffhmfffYZSktLISKIiIjoMOdJdXVycsInn3yCe/fuobq6Gvv374e9vX2v5SyM3o/IyEhpamqSt956S0aPHi379u2TqqoqGTJkiOq59dU4fvy4LF68WMaMGSPjx4+Xf/3rX1JcXCx2dnbKnD179khJSYlMnTpVAgIC5KuvvpJz586pnntfjZdeeklu374teXl5Eh8fzzr3YAwaNEiKiork448/lqCgIBk2bJhMnz5dPD09lTnr1q2T6upqmTt3rowbN06OHj0qhYWFYmtrq3r+fSU2bNgg//nPf+S1114TDw8Pef3116W2tlaio6NZ56eMmTNnyubNm2XevHkiIhIREWH0elfq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", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "for prob in prob_list:\n", + " plt.plot(prob)\n", + "plt.legend([\"alpha = 0.1\", \"alpha = 0.25\", \"alpha = 1\"])\n", + "plt.xlabel(\"iterations\")\n", + "p = plt.ylabel(\"probability of the best answer\")" + ] + }, + { + "cell_type": "markdown", + "id": "0ae45348", + "metadata": {}, + "source": [ + "The data presented in this figure indicates that QAOA with CVaR typically shows a higher probability of obtaining the correct measurement outcome." + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "tc_dev", + "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.11.4" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/docs/source/tutorials/sklearn_svc.ipynb b/docs/source/tutorials/sklearn_svc.ipynb new file mode 100644 index 00000000..5226eb08 --- /dev/null +++ b/docs/source/tutorials/sklearn_svc.ipynb @@ -0,0 +1,299 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Support Vector Classification with SKLearn\n", + "\n", + "Authored by [_Mark (Zixuan) Song_](https://marksong.tech)\n", + "\n", + "We use the `SKLearn` library to implement `SVC` in the following tutorial." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Overview\n", + "\n", + "The aim of this tutorial is to implant a quantum machine learning (QML) transformer into SVC pipeline. And this is a general introduction to connect `tensorcircuit` with `scikit-learn`." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Setup\n", + "\n", + "Install `scikit-learn` and `requests`. The data that is going to be used is [German Credit Data by UCI](http://home.cse.ust.hk/~qyang/221/Assignments/German/GermanData.csv)\n", + "\n", + "```bash\n", + "pip install scikit-learn requests\n", + "```" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "metadata": {}, + "outputs": [], + "source": [ + "import tensorcircuit as tc\n", + "import tensorflow as tf\n", + "from sklearn.svm import SVC\n", + "from sklearn import metrics\n", + "from time import time\n", + "import requests\n", + "\n", + "K = tc.set_backend(\"tensorflow\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Data Preprocessing\n", + "\n", + "The data has 20 variables and each is a integer value. In order for the model to use the data, we need to normalize the data to between 0 and 1." + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "metadata": {}, + "outputs": [], + "source": [ + "def load_GCN_data():\n", + " link2gcn = \"http://home.cse.ust.hk/~qyang/221/Assignments/German/GermanData.csv\"\n", + " data = requests.get(link2gcn)\n", + " data = data.text\n", + " data = data.split(\"\\n\")[:-1]\n", + " x = None\n", + " y = None\n", + "\n", + " def destring(string):\n", + " string = string.split(\",\")\n", + " return_array = []\n", + " for i, v in enumerate(string):\n", + " if v[0] == \"A\":\n", + " return_array.append(int(v[1 + len(str(i)) :]))\n", + " else:\n", + " return_array.append(int(v))\n", + " return K.cast([return_array[:-1]], dtype=\"float32\"), K.cast(\n", + " [return_array[-1] - 1], dtype=\"int32\"\n", + " )\n", + "\n", + " for i in data:\n", + " if x is None:\n", + " temp_x, temp_y = destring(i)\n", + " x = K.cast(temp_x, dtype=\"float32\")\n", + " y = K.cast(temp_y, dtype=\"int32\")\n", + " else:\n", + " temp_x, temp_y = destring(i)\n", + " x = K.concat([x, temp_x], axis=0)\n", + " y = K.concat([y, temp_y], axis=0)\n", + " x = K.transpose(x)\n", + " nx = None\n", + " for i in x:\n", + " max_i = K.cast(K.max(i), dtype=\"float32\")\n", + " temp_nx = [K.divide(i, max_i)]\n", + " nx = K.concat([nx, temp_nx], axis=0) if nx is not None else temp_nx\n", + " x = K.transpose(nx)\n", + " return (x[:800], y[:800]), (x[800:], y[800:])\n", + "\n", + "\n", + "(x_train, y_train), (x_test, y_test) = load_GCN_data()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Quantum Model\n", + "\n", + "This quantum model takes in 1x20 matrices as input and output the state of 5 qbits. The model is shown below:" + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "metadata": {}, + "outputs": [], + "source": [ + "def quantumTran(inputs):\n", + " c = tc.Circuit(5)\n", + " for i in range(4):\n", + " if i % 2 == 0:\n", + " for j in range(5):\n", + " c.rx(j, theta=(0 if i * 5 + j >= 20 else inputs[i * 5 + j]))\n", + " else:\n", + " for j in range(5):\n", + " c.rz(j, theta=(0 if i * 5 + j >= 20 else inputs[i * 5 + j]))\n", + " for j in range(4):\n", + " c.cnot(j, j + 1)\n", + " return c.state()\n", + "\n", + "\n", + "func_qt = tc.interfaces.tensorflow_interface(quantumTran, ydtype=tf.complex64, jit=True)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Wrapping Quantum Model into a SVC\n", + "\n", + "Convert quantum model into svc that can be trained." + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "metadata": {}, + "outputs": [], + "source": [ + "def quantum_kernel(quantumTran, data_x, data_y):\n", + " def kernel(x, y):\n", + " x = K.convert_to_tensor(x)\n", + " y = K.convert_to_tensor(y)\n", + " x_qt = None\n", + " for i, x1 in enumerate(x):\n", + " if i == 0:\n", + " x_qt = K.convert_to_tensor([quantumTran(x1)])\n", + " else:\n", + " x_qt = K.concat([x_qt, [quantumTran(x1)]], 0)\n", + " y_qt = None\n", + " for i, x1 in enumerate(y):\n", + " if i == 0:\n", + " y_qt = K.convert_to_tensor([quantumTran(x1)])\n", + " else:\n", + " y_qt = K.concat([y_qt, [quantumTran(x1)]], 0)\n", + " data_ret = K.cast(K.power(K.abs(x_qt @ K.transpose(y_qt)), 2), \"float32\")\n", + " return data_ret\n", + "\n", + " clf = SVC(kernel=kernel)\n", + " clf.fit(data_x, data_y)\n", + " return clf" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Create Traditional SVC" + ] + }, + { + "cell_type": "code", + "execution_count": 11, + "metadata": {}, + "outputs": [], + "source": [ + "def standard_kernel(data_x, data_y, method):\n", + " methods = [\"linear\", \"poly\", \"rbf\", \"sigmoid\"]\n", + " if method not in methods:\n", + " raise ValueError(\"method must be one of %r.\" % methods)\n", + " clf = SVC(kernel=method)\n", + " clf.fit(data_x, data_y)\n", + " return clf" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Test\n", + "\n", + "Test the accuracy of the quantum model SVC with the test data and compare it with traditional SVC." + ] + }, + { + "cell_type": "code", + "execution_count": 12, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "\n", + "Accuracy:(linear as kernel) 0.78\n", + "time: 0.007764101028442383 seconds\n", + "\n", + "Accuracy:(poly as kernel) 0.75\n", + "time: 0.024492979049682617 seconds\n", + "\n", + "Accuracy:(rbf as kernel) 0.765\n", + "time: 0.011505126953125 seconds\n", + "\n", + "Accuracy:(sigmoid as kernel) 0.695\n", + "time: 0.010205984115600586 seconds\n", + "\n", + "Accuracy:(qml as kernel) 0.66\n", + "time: 3.0243749618530273 seconds\n" + ] + } + ], + "source": [ + "methods = [\"linear\", \"poly\", \"rbf\", \"sigmoid\"]\n", + "\n", + "for method in methods:\n", + " print()\n", + " t = time()\n", + "\n", + " k = standard_kernel(data_x=x_train, data_y=y_train, method=method)\n", + " y_pred = k.predict(x_test)\n", + " print(\"Accuracy:(%s as kernel)\" % method, metrics.accuracy_score(y_test, y_pred))\n", + "\n", + " print(\"time:\", time() - t, \"seconds\")\n", + "\n", + "print()\n", + "t = time()\n", + "\n", + "k = quantum_kernel(quantumTran=func_qt, data_x=x_train, data_y=y_train)\n", + "y_pred = k.predict(x_test)\n", + "print(\"Accuracy:(qml as kernel)\", metrics.accuracy_score(y_test, y_pred))\n", + "\n", + "print(\"time:\", time() - t, \"seconds\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Issue with `SKLearn`\n", + "\n", + "Due to the limitation of `SKLearn`, `SKLearn`'s `SVC` is not fully compatible with quantum machine model (QML). \n", + "\n", + "This is because QML outputs a result as complex number (coordinate on the bloch sphere) whereas SKLearn only accept float. This is causing the result output by QML must be converted into float before it can be used in SVC, leading to a potential loss of accuracy.\n", + "\n", + "## Conclusion\n", + "\n", + "Due to the present limitation of SKLearn, quantum SVC is worse than traditional SVC in both accuracy and speed. However, if the limitation is removed, quantum SVC might be able to outperform traditional SVC in both accuracy." + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "tc2", + "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.10.11" + }, + "orig_nbformat": 4 + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/docs/source/tutorials/sklearn_svc_cn.ipynb b/docs/source/tutorials/sklearn_svc_cn.ipynb new file mode 100644 index 00000000..c737dc72 --- /dev/null +++ b/docs/source/tutorials/sklearn_svc_cn.ipynb @@ -0,0 +1,299 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# 结合SKLearn实现的支持向量分类\n", + "\n", + "[_Mark (Zixuan) Song_](https://marksong.tech) 撰写\n", + "\n", + "本示例结合了`sklearn`库中的`SVC`类,实现了支持向量分类。" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## 概述\n", + "\n", + "本示例的目的是将量子机器学习(QML)转换器嵌入到SVC管道中并且介绍`tensorcircuit`与`scikit-learn`的一种连接方式。" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## 设置\n", + "\n", + "安装 `scikit-learn` 和 `requests`. 本模型测试数据为 [德国信用]The data that is going to be used is [German Credit Data by UCI](http://home.cse.ust.hk/~qyang/221/Assignments/German/GermanData.csv)\n", + "\n", + "```bash\n", + "pip install scikit-learn requests\n", + "```" + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "metadata": {}, + "outputs": [], + "source": [ + "import tensorcircuit as tc\n", + "import tensorflow as tf\n", + "from sklearn.svm import SVC\n", + "from sklearn import metrics\n", + "from time import time\n", + "import requests\n", + "\n", + "K = tc.set_backend(\"tensorflow\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## 数据处理\n", + "\n", + "数据集包含20个变量,每个变量都是整数值。为了使模型能够使用数据,我们需要将数据归一化为0到1之间。" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": {}, + "outputs": [], + "source": [ + "def load_GCN_data():\n", + " link2gcn = \"http://home.cse.ust.hk/~qyang/221/Assignments/German/GermanData.csv\"\n", + " data = requests.get(link2gcn)\n", + " data = data.text\n", + " data = data.split(\"\\n\")[:-1]\n", + " x = None\n", + " y = None\n", + "\n", + " def destring(string):\n", + " string = string.split(\",\")\n", + " return_array = []\n", + " for i, v in enumerate(string):\n", + " if v[0] == \"A\":\n", + " return_array.append(int(v[1 + len(str(i)) :]))\n", + " else:\n", + " return_array.append(int(v))\n", + " return K.cast([return_array[:-1]], dtype=\"float32\"), K.cast(\n", + " [return_array[-1] - 1], dtype=\"int32\"\n", + " )\n", + "\n", + " for i in data:\n", + " if x is None:\n", + " temp_x, temp_y = destring(i)\n", + " x = K.cast(temp_x, dtype=\"float32\")\n", + " y = K.cast(temp_y, dtype=\"int32\")\n", + " else:\n", + " temp_x, temp_y = destring(i)\n", + " x = K.concat([x, temp_x], axis=0)\n", + " y = K.concat([y, temp_y], axis=0)\n", + " x = K.transpose(x)\n", + " nx = None\n", + " for i in x:\n", + " max_i = K.cast(K.max(i), dtype=\"float32\")\n", + " temp_nx = [K.divide(i, max_i)]\n", + " nx = K.concat([nx, temp_nx], axis=0) if nx is not None else temp_nx\n", + " x = K.transpose(nx)\n", + " return (x[:800], y[:800]), (x[800:], y[800:])\n", + "\n", + "\n", + "(x_train, y_train), (x_test, y_test) = load_GCN_data()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## 量子模型\n", + "\n", + "这个量子模型是输入为1x20的矩阵,并输出为5个量子比特的状态。模型如下所示:" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": {}, + "outputs": [], + "source": [ + "def quantumTran(inputs):\n", + " c = tc.Circuit(5)\n", + " for i in range(4):\n", + " if i % 2 == 0:\n", + " for j in range(5):\n", + " c.rx(j, theta=(0 if i * 5 + j >= 20 else inputs[i * 5 + j]))\n", + " else:\n", + " for j in range(5):\n", + " c.rz(j, theta=(0 if i * 5 + j >= 20 else inputs[i * 5 + j]))\n", + " for j in range(4):\n", + " c.cnot(j, j + 1)\n", + " return c.state()\n", + "\n", + "\n", + "func_qt = tc.interfaces.tensorflow_interface(quantumTran, ydtype=tf.complex64, jit=True)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## 将量子模型打包成SVC\n", + "\n", + "将量子模型打包成`SKLearn`能使用的SVC模型。" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": {}, + "outputs": [], + "source": [ + "def quantum_kernel(quantumTran, data_x, data_y):\n", + " def kernel(x, y):\n", + " x = K.convert_to_tensor(x)\n", + " y = K.convert_to_tensor(y)\n", + " x_qt = None\n", + " for i, x1 in enumerate(x):\n", + " if i == 0:\n", + " x_qt = K.convert_to_tensor([quantumTran(x1)])\n", + " else:\n", + " x_qt = K.concat([x_qt, [quantumTran(x1)]], 0)\n", + " y_qt = None\n", + " for i, x1 in enumerate(y):\n", + " if i == 0:\n", + " y_qt = K.convert_to_tensor([quantumTran(x1)])\n", + " else:\n", + " y_qt = K.concat([y_qt, [quantumTran(x1)]], 0)\n", + " data_ret = K.cast(K.power(K.abs(x_qt @ K.transpose(y_qt)), 2), \"float32\")\n", + " return data_ret\n", + "\n", + " clf = SVC(kernel=kernel)\n", + " clf.fit(data_x, data_y)\n", + " return clf" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## 创建传统SVC模型" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "metadata": {}, + "outputs": [], + "source": [ + "def standard_kernel(data_x, data_y, method):\n", + " methods = [\"linear\", \"poly\", \"rbf\", \"sigmoid\"]\n", + " if method not in methods:\n", + " raise ValueError(\"method must be one of %r.\" % methods)\n", + " clf = SVC(kernel=method)\n", + " clf.fit(data_x, data_y)\n", + " return clf" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## 测试对比\n", + "\n", + "测试量子SVC模型并于传统SVC模型进行对比。" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "\n", + "Accuracy:(linear as kernel) 0.78\n", + "time: 0.00810384750366211 seconds\n", + "\n", + "Accuracy:(poly as kernel) 0.75\n", + "time: 0.024804115295410156 seconds\n", + "\n", + "Accuracy:(rbf as kernel) 0.765\n", + "time: 0.011444091796875 seconds\n", + "\n", + "Accuracy:(sigmoid as kernel) 0.695\n", + "time: 0.010396003723144531 seconds\n", + "\n", + "Accuracy:(qml as kernel) 0.66\n", + "time: 6.472219228744507 seconds\n" + ] + } + ], + "source": [ + "methods = [\"linear\", \"poly\", \"rbf\", \"sigmoid\"]\n", + "\n", + "for method in methods:\n", + " print()\n", + " t = time()\n", + "\n", + " k = standard_kernel(data_x=x_train, data_y=y_train, method=method)\n", + " y_pred = k.predict(x_test)\n", + " print(\"Accuracy:(%s as kernel)\" % method, metrics.accuracy_score(y_test, y_pred))\n", + "\n", + " print(\"time:\", time() - t, \"seconds\")\n", + "\n", + "print()\n", + "t = time()\n", + "\n", + "k = quantum_kernel(quantumTran=func_qt, data_x=x_train, data_y=y_train)\n", + "y_pred = k.predict(x_test)\n", + "print(\"Accuracy:(qml as kernel)\", metrics.accuracy_score(y_test, y_pred))\n", + "\n", + "print(\"time:\", time() - t, \"seconds\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## `SKLearn`的局限性\n", + "\n", + "因为`SKLearn`的局限性,`SKLearn`的`SVC`并不完全兼容量子机器学习(QML)。\n", + "\n", + "这是因为QML输出的为复数(布洛赫球上的坐标),而`SKLearn`只接受浮点数。这导致QML输出的结果必须在使用SVC之前转换为浮点数,从而可能导致精度损失。\n", + "\n", + "## 结论\n", + "\n", + "由于`SKLearn`的局限性,量子SVC在准确性和速度上都不如传统SVC。但是,如果这种局限性被消除,量子SVC可能会在准确性上都优于传统SVC。" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "tc2", + "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.10.11" + }, + "orig_nbformat": 4 + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/docs/source/tutorials/tfim_vqe.ipynb b/docs/source/tutorials/tfim_vqe.ipynb index d515d288..31170fe5 100644 --- a/docs/source/tutorials/tfim_vqe.ipynb +++ b/docs/source/tutorials/tfim_vqe.ipynb @@ -443,7 +443,6 @@ "\n", "\n", "def batched_train_step_jax(batch, n, nlayers, maxiter=10000):\n", - "\n", " key = jax.random.PRNGKey(42)\n", " param = jax.random.normal(key, shape=[batch, nlayers * 2, n]) * 0.1\n", " opt_init, opt_update, get_params = optimizers.adam(step_size=1e-2)\n", diff --git a/docs/source/tutorials/tfim_vqe_cn.ipynb b/docs/source/tutorials/tfim_vqe_cn.ipynb index 42132ad6..fbb293a1 100644 --- a/docs/source/tutorials/tfim_vqe_cn.ipynb +++ b/docs/source/tutorials/tfim_vqe_cn.ipynb @@ -440,7 +440,6 @@ "\n", "\n", "def batched_train_step_jax(batch, n, nlayers, maxiter=10000):\n", - "\n", " key = jax.random.PRNGKey(42)\n", " param = jax.random.normal(key, shape=[batch, nlayers * 2, n]) * 0.1\n", " opt_init, opt_update, get_params = optimizers.adam(step_size=1e-2)\n", diff --git a/docs/source/tutorials/tfim_vqe_diffreph_cn.ipynb b/docs/source/tutorials/tfim_vqe_diffreph_cn.ipynb index eca8c1ad..14a5cbea 100644 --- a/docs/source/tutorials/tfim_vqe_diffreph_cn.ipynb +++ b/docs/source/tutorials/tfim_vqe_diffreph_cn.ipynb @@ -420,7 +420,9 @@ "\n", "Jx = np.array([1.0 for _ in range(n - 1)]) # xx 相互作用的强度 (OBC)\n", "Bz = np.array([1.0 for _ in range(n)]) # 横向场强\n", - "hamiltonian_mpo = tn.matrixproductstates.mpo.FiniteTFI(Jx, Bz, dtype=dtype) # 矩阵乘积算子\n", + "hamiltonian_mpo = tn.matrixproductstates.mpo.FiniteTFI(\n", + " Jx, Bz, dtype=dtype\n", + ") # 矩阵乘积算子\n", "hamiltonian_mpo = quoperator_mpo(hamiltonian_mpo) # 从 mpo 生成 QuOperator" ] }, diff --git a/docs/source/whitepaper/5-density-matrix.ipynb b/docs/source/whitepaper/5-density-matrix.ipynb index df67b438..023e6644 100644 --- a/docs/source/whitepaper/5-density-matrix.ipynb +++ b/docs/source/whitepaper/5-density-matrix.ipynb @@ -364,9 +364,7 @@ "metadata": {}, "source": [ "In this framework though, the output of a channel acting on $\\vert{\\psi}\\rangle$ , i.e.\n", - "$\n", - "\\mathcal{E} ( \\vert{\\psi}\\rangle\\langle{\\psi}\\vert) = \\sum_i K_i \\vert{\\psi}\\rangle\\langle{\\psi}\\vert K_i^ \\dagger\n", - "$\n", + "$\\mathcal{E} ( \\vert{\\psi}\\rangle\\langle{\\psi}\\vert) = \\sum_i K_i \\vert{\\psi}\\rangle\\langle{\\psi}\\vert K_i^ \\dagger$\n", "is viewed as an ensemble of states $\\frac{K_i\\vert{\\psi}\\rangle}{\\sqrt{\\langle{\\psi}\\vert K_i^\\dagger K_i \\vert{\\psi}\\rangle}}$ that each occur with probability $p_i = \\langle{\\psi}\\vert K_i^\\dagger K_i \\vert{\\psi}\\rangle$. Thus, the code above stochastically produces the output of a single qubit initialized in state $\\vert{\\psi}\\rangle=\\frac{\\vert{0}\\rangle+\\vert{1}\\rangle}{\\sqrt{2}}$ being passed through a phase damping channel with parameter $\\gamma=0.5$. \n", "\n", "The Monte Carlo simulation of channels where the Kraus operators are all unitary matrices (up to a constant factor) can be handled with additional efficiency by using ``unitary_kraus`` instead of ``general_kraus``." @@ -502,4 +500,4 @@ }, "nbformat": 4, "nbformat_minor": 5 -} \ No newline at end of file +} diff --git a/docs/source/whitepaper/5-density-matrix_cn.ipynb b/docs/source/whitepaper/5-density-matrix_cn.ipynb index b6ea47c0..a1f8cd1e 100644 --- a/docs/source/whitepaper/5-density-matrix_cn.ipynb +++ b/docs/source/whitepaper/5-density-matrix_cn.ipynb @@ -368,9 +368,7 @@ "metadata": {}, "source": [ "不过,在这个框架中,作用于 $\\vert{\\psi}\\rangle$ 的通道的输出,即\n", - "$\n", - "\\mathcal{E} ( \\vert{\\psi}\\rangle\\langle{\\psi}\\vert) = \\sum_i K_i \\vert{\\psi}\\rangle\\langle{\\psi}\\vert K_i^ \\dagger\n", - "$\n", + "$\\mathcal{E} ( \\vert{\\psi}\\rangle\\langle{\\psi}\\vert) = \\sum_i K_i \\vert{\\psi}\\rangle\\langle{\\psi}\\vert K_i^ \\dagger$\n", "被视为状态的集合 $\\frac{K_i\\vert{\\psi}\\rangle}{\\sqrt{\\langle{\\psi}\\vert K_i^\\dagger K_i \\vert{\\psi}\\rangle}}$\n", "每个发生的概率为 $p_i = \\langle{\\psi}\\vert K_i^\\dagger K_i \\vert{\\psi}\\rangle$.\n", "因此,上面的代码随机产生在状态 $\\vert{\\psi}\\rangle=\\frac{\\vert{0}\\rangle+\\vert{1}\\rangle}{\\sqrt{2} }$\n", @@ -513,4 +511,4 @@ }, "nbformat": 4, "nbformat_minor": 5 -} \ No newline at end of file +} diff --git a/examples/adiabatic_vqnhe.py b/examples/adiabatic_vqnhe.py index 6e52b652..a1b093ca 100644 --- a/examples/adiabatic_vqnhe.py +++ b/examples/adiabatic_vqnhe.py @@ -1,6 +1,7 @@ """ Calculate the molecule dissociation curve using VQNHE. """ + from functools import partial import sys diff --git a/examples/analog_evolution_interface.py b/examples/analog_evolution_interface.py new file mode 100644 index 00000000..9cc00a9b --- /dev/null +++ b/examples/analog_evolution_interface.py @@ -0,0 +1,40 @@ +""" +jax backend is required, experimental built-in interface for parameterized hamiltonian evolution +""" + +import optax +import tensorcircuit as tc +from tensorcircuit.experimental import evol_global, evol_local + +K = tc.set_backend("jax") + + +def h_fun(t, b): + return b * tc.gates.x().tensor + + +hy = tc.quantum.PauliStringSum2COO([[2, 0]]) + + +def h_fun2(t, b): + return b[2] * K.cos(b[0] * t + b[1]) * hy + + +@K.jit +@K.value_and_grad +def hybrid_evol(params): + c = tc.Circuit(2) + c.x([0, 1]) + c = evol_local(c, [1], h_fun, 1.0, params[0]) + c.cx(1, 0) + c.h(0) + c = evol_global(c, h_fun2, 1.0, params[1:]) + return K.real(c.expectation_ps(z=[0, 1])) + + +opt = K.optimizer(optax.adam(0.1)) +b = K.implicit_randn([4]) +for _ in range(50): + v, gs = hybrid_evol(b) + b = opt.update(gs, b) + print(v, b) diff --git a/examples/analog_evolution_jax.py b/examples/analog_evolution_jax.py new file mode 100644 index 00000000..f5e92ca0 --- /dev/null +++ b/examples/analog_evolution_jax.py @@ -0,0 +1,42 @@ +""" +Parameterized Hamiltonian (Pulse control/Analog simulation) with AD/JIT support using jax ode solver +""" + +import optax +from jax.experimental.ode import odeint +import tensorcircuit as tc + +K = tc.set_backend("jax") +tc.set_dtype("complex128") + +hx = tc.quantum.PauliStringSum2COO([[1]]) +hz = tc.quantum.PauliStringSum2COO([[3]]) + + +# psi = -i H psi +# we want to optimize the final z expectation over parameters params +# a single qubit example below + + +def final_z(b): + def f(y, t, b): + h = b[3] * K.sin(b[0] * t + b[1]) * hx + K.cos(b[2]) * hz + return -1.0j * K.sparse_dense_matmul(h, y) + + y0 = tc.array_to_tensor([1, 0]) + y0 = K.reshape(y0, [-1, 1]) + t = tc.array_to_tensor([0.0, 10.0], dtype=tc.rdtypestr) + yf = odeint(f, y0, t, b) + c = tc.Circuit(1, inputs=K.reshape(yf[-1], [-1])) + return K.real(c.expectation_ps(z=[0])) + + +vgf = K.jit(K.value_and_grad(final_z)) + + +opt = K.optimizer(optax.adam(0.1)) +b = K.implicit_randn([4]) +for _ in range(50): + v, gs = vgf(b) + b = opt.update(gs, b) + print(v, b) diff --git a/examples/analog_evolution_mint.py b/examples/analog_evolution_mint.py new file mode 100644 index 00000000..0af4fc04 --- /dev/null +++ b/examples/analog_evolution_mint.py @@ -0,0 +1,36 @@ +""" +jax backend analog evolution targeting at minimizing evolution time +""" + +import optax +import tensorcircuit as tc +from tensorcircuit.experimental import evol_global + +K = tc.set_backend("jax") + +hx = tc.quantum.PauliStringSum2COO([[1]]) + + +def h_fun(t, b): + return K.sin(b) * hx + + +def fast_evol(t, b): + lbd = 0.08 + c = tc.Circuit(1) + c = evol_global(c, h_fun, t, b) + loss = K.real(c.expectation_ps(z=[0])) + return loss + lbd * t**2, loss + # l2 regularization to minimize t while target z=-1 + + +vgf = K.jit(K.value_and_grad(fast_evol, argnums=(0, 1), has_aux=True)) + +opt = K.optimizer(optax.adam(0.05)) +b, t = tc.array_to_tensor(0.5, 1.0, dtype=tc.rdtypestr) + +for i in range(500): + (v, loss), gs = vgf(b, t) + b, t = opt.update(gs, (b, t)) + if i % 20 == 0: + print(v, loss, b, t) diff --git a/examples/bp_benchmark.py b/examples/bp_benchmark.py index 734ddfb6..66a12ea6 100644 --- a/examples/bp_benchmark.py +++ b/examples/bp_benchmark.py @@ -1,6 +1,7 @@ """ benchmark on barren plateau using tfq and tc """ + import os os.environ["TF_FORCE_GPU_ALLOW_GROWTH"] = "true" @@ -9,6 +10,7 @@ import cirq import sympy import numpy as np +import pennylane as qml import tensorflow_quantum as tfq import tensorcircuit as tc @@ -53,7 +55,6 @@ def generate_random_qnn(qubits, symbol, depth): return circuit def process_batch(circuits, symbol, op): - # Setup a simple layer to batch compute the expectation gradients. expectation = tfq.layers.Expectation() @@ -123,8 +124,42 @@ def op_expectation(params, seed, n_qubits, depth): ) -def tc_approach(n_qubits=10, depth=10, n_circuits=100): +def pennylane_approach(n_qubits=10, depth=10, n_circuits=100): + dev = qml.device("lightning.qubit", wires=n_qubits) + gate_set = [qml.RX, qml.RY, qml.RZ] + + @qml.qnode(dev) + def rand_circuit(params, status): + for i in range(n_qubits): + qml.RY(np.pi / 4, wires=i) + + for j in range(depth): + for i in range(n_qubits): + gate_set[status[i, j]](params[j, i], wires=i) + + for i in range(n_qubits - 1): + qml.CZ(wires=[i, i + 1]) + + return qml.expval(qml.Hamiltonian([1.0], [qml.PauliZ(0) @ qml.PauliZ(1)], True)) + + gf = qml.grad(rand_circuit, argnum=0) + params = np.random.uniform(0, 2 * np.pi, size=[n_circuits, depth, n_qubits]) + status = np.random.choice(3, size=[n_circuits, depth, n_qubits]) + g_results = [] + + for i in range(n_circuits): + g_results.append(gf(params[i], status[i])) + + g_results = np.stack(g_results) + + return np.std(g_results[:, 0, 0]) + + +benchmark(pennylane_approach) + + +def tc_approach(n_qubits=10, depth=10, n_circuits=100): seed = tc.array_to_tensor( np.random.uniform(low=0.0, high=1.0, size=[n_circuits, n_qubits, depth]), dtype="float32", diff --git a/examples/bp_validation.py b/examples/bp_validation.py index dce80299..f620ee43 100644 --- a/examples/bp_validation.py +++ b/examples/bp_validation.py @@ -2,6 +2,7 @@ Compute the circuit gradient with single qubit random Haar averaged to demonstrate the gradient vanishing, aka, barren plateau. """ + import numpy as np from tqdm import tqdm import tensorcircuit as tc diff --git a/examples/chaotic_behavior.py b/examples/chaotic_behavior.py index f1039e86..6a37f4c7 100644 --- a/examples/chaotic_behavior.py +++ b/examples/chaotic_behavior.py @@ -1,6 +1,7 @@ """ Some chaotic properties calculations from the circuit state. """ + from functools import partial import sys diff --git a/examples/circuit_compiler.py b/examples/circuit_compiler.py new file mode 100644 index 00000000..349eac9a --- /dev/null +++ b/examples/circuit_compiler.py @@ -0,0 +1,77 @@ +""" +compilation utilities in tensorcircuit +""" + +import tensorcircuit as tc + + +c = tc.Circuit(3) +c.rx(0, theta=0.2) +c.rz(0, theta=-0.3) +c.ry(1, theta=0.1) +c.h(2) +c.cx(0, 1) +c.cz(2, 1) +c.x(0) +c.y(0) +c.rxx(1, 2, theta=1.7) + + +c0, _ = tc.compiler.qiskit_compiler.qiskit_compile( + c, + compiled_options={"optimization_level": 0, "basis_gates": ["cx", "cz", "h", "rz"]}, +) + +c1, _ = tc.compiler.qiskit_compiler.qiskit_compile( + c, + compiled_options={"optimization_level": 1, "basis_gates": ["cx", "cz", "h", "rz"]}, +) + + +c2, _ = tc.compiler.qiskit_compiler.qiskit_compile( + c, + compiled_options={"optimization_level": 2, "basis_gates": ["cx", "cz", "h", "rz"]}, +) + + +c3, _ = tc.compiler.qiskit_compiler.qiskit_compile( + c, + compiled_options={"optimization_level": 3, "basis_gates": ["cx", "cz", "h", "rz"]}, +) + +print( + "qiskit can become worse with higher level optimization when the target gate is not U3 but rz" +) +print("level 0:\n") +print(c0.draw()) +print("level 1:\n") +print(c1.draw()) +print("level 2:\n") +print(c2.draw()) +print("level 3:\n") +print(c3.draw()) + + +compiler_wo_mapping = tc.compiler.DefaultCompiler() +c4, _ = compiler_wo_mapping(c) +print( + "compiled with tc default compiler: combining the good from qiskit and our tc own" +) +# we always uuggest using DefaultCompiler for tasks on qcloud +# internally we run optimized compiling using U3 basis with qiskit which has good performance +# and we unroll u3 with rz and apply replace/prune/merge loop developed in tc to further optimize the circuit +print(c4.draw()) + +print("gate number comparison (last ours vs before qiskit (0, 1, 2, 3))") +for c in [c0, c1, c2, c3, c4]: + print(c.gate_count()) + +# if we want to apply routing/qubit mapping + +compiler_w_mapping = tc.compiler.DefaultCompiler( + {"coupling_map": [[0, 2], [2, 0], [1, 0], [0, 1]]} +) +c5, info = compiler_w_mapping(c) +print("circuit with qubit mapping") +print(c5.draw()) +print(info) diff --git a/examples/cotengra_setting_bench.py b/examples/cotengra_setting_bench.py new file mode 100644 index 00000000..d1b3f1b7 --- /dev/null +++ b/examples/cotengra_setting_bench.py @@ -0,0 +1,162 @@ +""" +Optimization for performance comparison with different cotengra settings. +""" + +import itertools +import sys +import warnings + +import cotengra as ctg +import networkx as nx +import numpy as np + +sys.path.insert(0, "../") +import tensorcircuit as tc + +try: + import kahypar +except ImportError: + print("kahypar not installed, please install it to run this script.") + exit() + + +# suppress the warning from cotengra +warnings.filterwarnings( + "ignore", + message="The inputs or output of this tree are not ordered." + "Costs will be accurate but actually contracting requires " + "ordered indices corresponding to array axes.", +) + +K = tc.set_backend("jax") + + +def generate_circuit(param, g, n, nlayers): + # construct the circuit ansatz + c = tc.Circuit(n) + for i in range(n): + c.H(i) + for j in range(nlayers): + c = tc.templates.blocks.QAOA_block(c, g, param[j, 0], param[j, 1]) + return c + + +def trigger_cotengra_optimization(n, nlayers, graph): + + # define the loss function + def loss_f(params, n, nlayers): + + c = generate_circuit(params, graph, n, nlayers) + + loss = c.expectation_ps(z=[0, 1, 2], reuse=False) + + return K.real(loss) + + params = K.implicit_randn(shape=[nlayers, 2]) + + # run only once to trigger the compilation + K.jit( + loss_f, + static_argnums=(1, 2), + )(params, n, nlayers) + + +# define the benchmark parameters +n = 12 +nlayers = 12 + +# define the cotengra optimizer parameters +graph_args = { + "1D lattice": nx.convert_node_labels_to_integers( + nx.grid_graph((n, 1)) + ), # 1D lattice + "2D lattice": nx.convert_node_labels_to_integers( + nx.grid_graph((n // 5, n // (n // 5))) + ), # 2D lattice + "all-to-all connected": nx.convert_node_labels_to_integers( + nx.complete_graph(n) + ), # all-to-all connected +} + +methods_args = [ # https://cotengra.readthedocs.io/en/latest/advanced.html#drivers + "greedy", + "kahypar", + # "labels", + # "spinglass", # requires igraph + # "labelprop", # requires igraph + # "betweenness", # requires igraph + # "walktrap", # requires igraph + # "quickbb", # requires https://github.com/dechterlab/quickbb + # "flowcutter", # requires https://github.com/kit-algo/flow-cutter-pace17 +] + +optlib_args = [ # https://cotengra.readthedocs.io/en/latest/advanced.html#optimization-library + "optuna", # pip install optuna + # "random", # default when no library is installed + # "baytune", # pip install baytune + # "nevergrad", # pip install nevergrad + # "chocolate", # pip install git+https://github.com/AIworx-Labs/chocolate@master + # "skopt", # pip install scikit-optimize +] + +post_processing_args = [ # https://cotengra.readthedocs.io/en/latest/advanced.html#slicing-and-subtree-reconfiguration + (None, None), + # ("slicing_opts", {"target_size": 2**28}), + # ("slicing_reconf_opts", {"target_size": 2**28}), + ("reconf_opts", {}), + ("simulated_annealing_opts", {}), +] + +minimize_args = [ # https://cotengra.readthedocs.io/en/main/advanced.html#objective + # "flops", # minimize the total number of scalar operations + # "size", # minimize the size of the largest intermediate tensor + # "write", # minimize the sum of sizes of all intermediate tensors + "combo", # minimize the sum of FLOPS + α * WRITE where α is 64 +] + + +def get_optimizer(method, optlib, post_processing, minimize): + if post_processing[0] is None: + return ctg.HyperOptimizer( + methods=method, + optlib=optlib, + minimize=minimize, + parallel=True, + max_time=60, + max_repeats=128, + progbar=True, + ) + else: + return ctg.HyperOptimizer( + methods=method, + optlib=optlib, + minimize=minimize, + parallel=True, + max_time=60, + max_repeats=128, + progbar=True, + **{post_processing[0]: post_processing[1]}, + ) + + +if __name__ == "__main__": + for graph, method, optlib, post_processing, minimize in itertools.product( + graph_args.keys(), + methods_args, + optlib_args, + post_processing_args, + minimize_args, + ): + print( + f"graph: {graph}, method: {method}, optlib: {optlib}, " + f"post_processing: {post_processing}, minimize: {minimize}" + ) + tc.set_contractor( + "custom", + optimizer=get_optimizer(method, optlib, post_processing, minimize), + contraction_info=True, + preprocessing=True, + debug_level=2, # no computation + ) + trigger_cotengra_optimization(n, nlayers, graph_args[graph]) + print("-------------------------") diff --git a/examples/ghz_dqas.py b/examples/ghz_dqas.py index a3bfc325..5022f441 100644 --- a/examples/ghz_dqas.py +++ b/examples/ghz_dqas.py @@ -1,6 +1,7 @@ """ DQAS for GHZ state preparation circuit, deprecated DQAS implementation """ + import sys sys.path.insert(0, "../") diff --git a/examples/hamiltonian_building.py b/examples/hamiltonian_building.py index 055faed7..80385265 100644 --- a/examples/hamiltonian_building.py +++ b/examples/hamiltonian_building.py @@ -1,37 +1,103 @@ """ benchmark sparse hamiltonian building """ + import time + +import jax import numpy as np import quimb +import scipy +import tensorflow as tf + import tensorcircuit as tc +tc.set_dtype("complex128") nwires = 20 -# tc outperforms quimb in large nwires +print("--------------------") + +# 1.1 tc approach for TFIM (numpy backend) -# 1. tc approach for TFIM +tc.set_backend("numpy") +print("hamiltonian building with tc (numpy backend)") +print("numpy version: ", np.__version__) +print("scipy version: ", scipy.__version__) g = tc.templates.graphs.Line1D(nwires, pbc=False) -time0 = time.time() -h1 = tc.quantum.heisenberg_hamiltonian( +time0 = time.perf_counter() +h11 = tc.quantum.heisenberg_hamiltonian( g, hzz=1, hxx=0, hyy=0, hz=0, hx=-1, hy=0, sparse=True, numpy=True ) -time1 = time.time() +time1 = time.perf_counter() + +print("tc (numpy) time: ", time1 - time0) +print("--------------------") + +# 1.2 tc approach for TFIM (jax backend) + +tc.set_backend("jax") +print("hamiltonian building with tc (jax backend)") +print("jax version: ", jax.__version__) + +g = tc.templates.graphs.Line1D(nwires, pbc=False) +time0 = time.perf_counter() +h12 = tc.quantum.heisenberg_hamiltonian( + g, hzz=1, hxx=0, hyy=0, hz=0, hx=-1, hy=0, sparse=True +) +time1 = time.perf_counter() + +print("tc (jax) time: ", time1 - time0) + +time0 = time.perf_counter() +h12 = tc.quantum.heisenberg_hamiltonian( + g, hzz=1, hxx=0, hyy=0, hz=0, hx=-1, hy=0, sparse=True +) +time1 = time.perf_counter() + +print("tc (jax) time (after jit): ", time1 - time0) +print("--------------------") + +# 1.3 tc approach for TFIM (tensorflow backend) + +tc.set_backend("tensorflow") +print("hamiltonian building with tc (tensorflow backend)") +print("tensorflow version: ", tf.__version__) + +g = tc.templates.graphs.Line1D(nwires, pbc=False) +time0 = time.perf_counter() +h13 = tc.quantum.heisenberg_hamiltonian( + g, hzz=1, hxx=0, hyy=0, hz=0, hx=-1, hy=0, sparse=True +) +time1 = time.perf_counter() + +print("tc (tensorflow) time: ", time1 - time0) + +time0 = time.perf_counter() +h13 = tc.quantum.heisenberg_hamiltonian( + g, hzz=1, hxx=0, hyy=0, hz=0, hx=-1, hy=0, sparse=True +) +time1 = time.perf_counter() + +print("tc (tensorflow) time (after jit): ", time1 - time0) + +print("--------------------") -print("tc time: ", time1 - time0) # 2. quimb approach for TFIM -builder = quimb.tensor.tensor_gen.SpinHam() +print("hamiltonian building with quimb") +print("quimb version: ", quimb.__version__) + +builder = quimb.tensor.SpinHam1D() # spin operator instead of Pauli matrix builder += 4, "Z", "Z" builder += -2, "X" -time0 = time.time() +time0 = time.perf_counter() h2 = builder.build_sparse(nwires) h2 = h2.tocoo() -time1 = time.time() +time1 = time.perf_counter() print("quimb time: ", time1 - time0) @@ -42,4 +108,25 @@ def assert_equal(h1, h2): np.testing.assert_allclose(h1.data, h2.data, atol=1e-5) -assert_equal(h1, h2) +# numpy +assert_equal(h11, h2) + +# jax +scipy_coo = scipy.sparse.coo_matrix( + ( + h12.data, + (h12.indices[:, 0], h12.indices[:, 1]), + ), + shape=h12.shape, +) +assert_equal(scipy_coo, h2) + +# tensorflow +scipy_coo = scipy.sparse.coo_matrix( + ( + h13.values, + (h13.indices[:, 0], h13.indices[:, 1]), + ), + shape=h13.shape, +) +assert_equal(scipy_coo, h2) diff --git a/examples/hea_scan_jit_acc.py b/examples/hea_scan_jit_acc.py new file mode 100644 index 00000000..94e19032 --- /dev/null +++ b/examples/hea_scan_jit_acc.py @@ -0,0 +1,75 @@ +""" +reducing jit compiling time by general scan magic +""" + +import numpy as np +import tensorcircuit as tc + +n = 10 +nlayers = 16 +param_np = np.random.normal(size=[nlayers, n, 2]) + +for backend in ["tensorflow", "jax"]: + with tc.runtime_backend(backend) as K: + print("running %s" % K.name) + + def energy_reference(param, n, nlayers): + c = tc.Circuit(n) + for i in range(n): + c.h(i) + for i in range(nlayers): + for j in range(n - 1): + c.rzz(j, j + 1, theta=param[i, j, 0]) + for j in range(n): + c.rx(j, theta=param[i, j, 1]) + return K.real(c.expectation_ps(z=[0, 1]) + c.expectation_ps(x=[2])) + + vg_reference = K.jit( + K.value_and_grad(energy_reference, argnums=0), static_argnums=(1, 2) + ) + + # a jit efficient way to utilize scan + + def energy(param, n, nlayers, each): + def loop_f(s_, param_): + c_ = tc.Circuit(n, inputs=s_) + for i in range(each): + for j in range(n - 1): + c_.rzz(j, j + 1, theta=param_[i, j, 0]) + for j in range(n): + c_.rx(j, theta=param_[i, j, 1]) + s_ = c_.state() + return s_ + + c = tc.Circuit(n) + for i in range(n): + c.h(i) + s = c.state() + s1 = K.scan(loop_f, K.reshape(param, [nlayers // each, each, n, 2]), s) + c1 = tc.Circuit(n, inputs=s1) + return K.real(c1.expectation_ps(z=[0, 1]) + c1.expectation_ps(x=[2])) + + vg = K.jit( + K.value_and_grad(energy, argnums=0), + static_argnums=(1, 2, 3), + jit_compile=True, + ) + # set to False can improve compile time for tf + + param = K.convert_to_tensor(param_np) + + for each in [1, 2, 4]: + print(" scan impl with each=%s" % str(each)) + r1 = tc.utils.benchmark(vg, param, n, nlayers, each) + print(r1[0][0]) + + print(" plain impl") + r0 = tc.utils.benchmark(vg_reference, param, n, nlayers) # too slow + np.testing.assert_allclose(r0[0][0], r1[0][0], atol=1e-5) + np.testing.assert_allclose(r0[0][1], r1[0][1], atol=1e-5) + # correctness check + + +# jit_compile=True icrease runtime while degrades jit time for tensorflow +# and in general jax improves better with scan methodology, +# both compile time and running time can outperform tf diff --git a/examples/incremental_twoqubit.py b/examples/incremental_twoqubit.py index 8b8b0057..26fdb454 100644 --- a/examples/incremental_twoqubit.py +++ b/examples/incremental_twoqubit.py @@ -2,6 +2,7 @@ Optimizing the parameterized circuit with progressively dense two-qubit gates, as a potential approach to alleviate barren plateau. """ + import sys sys.path.insert(0, "../") diff --git a/examples/jax_scan_jit_acc.py b/examples/jax_scan_jit_acc.py new file mode 100644 index 00000000..34626680 --- /dev/null +++ b/examples/jax_scan_jit_acc.py @@ -0,0 +1,95 @@ +""" +reducing jax jit compiling time by some magic: +for backend agnostic but similar approach, +see `hea_scan_jit_acc.py` +""" + +import numpy as np +import jax +import tensorcircuit as tc + +K = tc.set_backend("jax") +tc.set_dtype("complex128") + + +def energy_reference(param, n, nlayers): + c = tc.Circuit(n) + for i in range(n): + c.h(i) + for i in range(nlayers): + for j in range(n - 1): + c.rzz(j, j + 1, theta=param[i, j, 0]) + for j in range(n): + c.rx(j, theta=param[i, j, 1]) + return K.real(c.expectation_ps(z=[0, 1])) + + +vg_reference = K.jit( + K.value_and_grad(energy_reference, argnums=0), static_argnums=(1, 2) +) + +with tc.runtime_backend("tensorflow") as tfk: + + def energy_reference_tf(param, n, nlayers): + c = tc.Circuit(n) + for i in range(n): + c.h(i) + for i in range(nlayers): + for j in range(n - 1): + c.rzz(j, j + 1, theta=param[i, j, 0]) + for j in range(n): + c.rx(j, theta=param[i, j, 1]) + return tfk.real(c.expectation_ps(z=[0, 1])) + + vg_reference_tf = tfk.jit( + tfk.value_and_grad(energy_reference_tf, argnums=0), static_argnums=(1, 2) + ) + +# a jit efficient way to utilize jax scan + + +def energy(param, n, nlayers, each): + def loop_f(s_, param_): + c_ = tc.Circuit(n, inputs=s_) + for i in range(each): + for j in range(n - 1): + c_.rzz(j, j + 1, theta=param_[i, j, 0]) + for j in range(n): + c_.rx(j, theta=param_[i, j, 1]) + s_ = c_.state() + return s_, s_ + + c = tc.Circuit(n) + for i in range(n): + c.h(i) + s = c.state() + s1, _ = jax.lax.scan(loop_f, s, K.reshape(param, [nlayers // each, each, n, 2])) + c1 = tc.Circuit(n, inputs=s1) + return K.real(c1.expectation_ps(z=[0, 1])) + + +vg = K.jit(K.value_and_grad(energy, argnums=0), static_argnums=(1, 2, 3)) + +if __name__ == "__main__": + n = 10 + nlayers = 32 + param = K.implicit_randn([nlayers, n, 2]) + + r1 = tc.utils.benchmark(vg, param, n, nlayers, 1) + print(r1[0][0]) + r1 = tc.utils.benchmark(vg, param, n, nlayers, 2) + print(r1[0][0]) + r1 = tc.utils.benchmark(vg, param, n, nlayers, 4) + print(r1[0][0]) + + with tc.runtime_backend("tensorflow"): + print("tf plain impl") + param_tf = tc.array_to_tensor(param, dtype="float32") + r0 = tc.utils.benchmark(vg_reference_tf, param_tf, n, nlayers) + + np.testing.assert_allclose(r0[0][0], r1[0][0], atol=1e-5) + np.testing.assert_allclose(r0[0][1], r1[0][1], atol=1e-5) + # correctness check + + print("jax plain impl (may be super slow for deeper system)") + r0 = tc.utils.benchmark(vg_reference, param, n, nlayers) # too slow diff --git a/examples/jsonio.py b/examples/jsonio.py index 7626cb06..565c12b0 100644 --- a/examples/jsonio.py +++ b/examples/jsonio.py @@ -2,6 +2,7 @@ example showcasing how circuit can be load from and dump to json: useful for storage or restful api """ + import numpy as np import tensorcircuit as tc diff --git a/examples/keras3_tc_integration.py b/examples/keras3_tc_integration.py new file mode 100644 index 00000000..ba45363c --- /dev/null +++ b/examples/keras3_tc_integration.py @@ -0,0 +1,95 @@ +""" +keras3 is excellent to use together with tc, we will have unique features including: +1. turn OO paradigm to functional paradigm, i.e. reuse keras layer function in functional programming +2. batch on neural network weights +""" + +import os + +os.environ["KERAS_BACKEND"] = "jax" +import keras_core as keras +import numpy as np +import optax +import tensorcircuit as tc + +K = tc.set_backend("jax") + +batch = 8 +n = 6 +layer = keras.layers.Dense(1, activation="sigmoid") +layer.build([batch, n]) + +data_x = np.random.choice([0, 1], size=batch * n).reshape([batch, n]) +# data_y = np.sum(data_x, axis=-1) % 2 +data_y = data_x[:, 0] +data_y = data_y.reshape([batch, 1]) +data_x = data_x.astype(np.float32) +data_y = data_y.astype(np.float32) + + +print("data", data_x, data_y) + + +def loss(xs, ys, params, weights): + c = tc.Circuit(n) + c.rx(range(n), theta=xs) + c.cx(range(n - 1), range(1, n)) + c.rz(range(n), theta=params) + outputs = K.stack([K.real(c.expectation_ps(z=[i])) for i in range(n)]) + ypred, _ = layer.stateless_call(weights, [], outputs) + return keras.losses.binary_crossentropy(ypred, ys), ypred + + +# common data batch practice +vgf = K.jit( + K.vectorized_value_and_grad( + loss, argnums=(2, 3), vectorized_argnums=(0, 1), has_aux=True + ) +) + +params = K.implicit_randn(shape=[n]) +w = K.implicit_randn(shape=[n, 1]) +b = K.implicit_randn(shape=[1]) +opt = K.optimizer(optax.adam(1e-2)) +# seems that currently keras3'optimizer doesn't support nested list of variables + +for i in range(100): + (v, yp), gs = vgf(data_x, data_y, params, [w, b]) + params, [w, b] = opt.update(gs, (params, [w, b])) + if i % 10 == 0: + print(K.mean(v)) + +m = keras.metrics.BinaryAccuracy() +m.update_state(data_y, yp[:, None]) +print("acc", m.result()) + + +# data batch with batched and quantum neural weights + +vgf2 = K.jit( + K.vmap( + K.vectorized_value_and_grad( + loss, argnums=(2, 3), vectorized_argnums=(0, 1), has_aux=True + ), + vectorized_argnums=(2, 3), + ) +) + +wbatch = 4 +params = K.implicit_randn(shape=[wbatch, n]) +w = K.implicit_randn(shape=[wbatch, n, 1]) +b = K.implicit_randn(shape=[wbatch, 1]) +opt = K.optimizer(optax.adam(1e-2)) +# seems that currently keras3'optimizer doesn't support nested list of variables + +for i in range(100): + (v, yp), gs = vgf2(data_x, data_y, params, [w, b]) + params, [w, b] = opt.update(gs, (params, [w, b])) + if i % 10 == 0: + print(K.mean(v, axis=-1)) + +for i in range(wbatch): + m = keras.metrics.BinaryAccuracy() + m.update_state(data_y, yp[0, :, None]) + print("acc", m.result()) + m.reset_state() diff --git a/examples/matprod_vmap.py b/examples/matprod_vmap.py new file mode 100644 index 00000000..bbf8f137 --- /dev/null +++ b/examples/matprod_vmap.py @@ -0,0 +1,42 @@ +""" +matrix product: a new twist +rewrite matrix product in a vmap style +""" + +from functools import partial + +import numpy as np +import tensorcircuit as tc + +for bk in ["jax", "tensorflow"]: + with tc.runtime_backend(bk) as K: + print("~~~~~~~~~~~~~~~~~~~~~") + print(f"using {K.name} backend") + + @partial(K.jit, jit_compile=True) + def mul(a, b): + return a @ b + + def ij(i, j): + """ + Inner product + """ + return K.tensordot(i, j, 1) + + vij = K.vmap(ij, vectorized_argnums=1) + vvij = K.vmap(vij, vectorized_argnums=0) + + @partial(K.jit, jit_compile=True) + def mul2(a, b): + b = K.transpose(b) + return vvij(a, b) + + for shape in [(256, 4096), (4096, 256), (2048, 2048)]: + print(shape) + a = K.implicit_randn(shape) + b = K.implicit_randn([shape[1], shape[0]]) + print("plain matprod") + r1, _, _ = tc.utils.benchmark(mul, a, b, tries=10) + print("vmap matprod") + r2, _, _ = tc.utils.benchmark(mul2, a, b, tries=10) + np.testing.assert_allclose(r1, r2, atol=1e-5) diff --git a/examples/mcnoise_boost_v2.py b/examples/mcnoise_boost_v2.py new file mode 100644 index 00000000..19b8e25a --- /dev/null +++ b/examples/mcnoise_boost_v2.py @@ -0,0 +1,84 @@ +""" +Boost the Monte Carlo noise simulation (specifically the staging time) +on general error with circuit layerwise slicing: new paradigm, +essentially the same as v1, but much simpler +""" + +import time +import sys + +sys.path.insert(0, "../") +import tensorcircuit as tc + +tc.set_backend("jax") + +n = 6 # 10 +nlayer = 5 # 4 + + +def precompute(c): + s = c.state() + return tc.Circuit(c._nqubits, inputs=s) + + +def f1(key, param, n, nlayer): + if key is not None: + tc.backend.set_random_state(key) + c = tc.Circuit(n) + for i in range(n): + c.H(i) + for j in range(nlayer): + for i in range(n - 1): + c.cnot(i, i + 1) + c.apply_general_kraus(tc.channels.phasedampingchannel(0.15), i) + c.apply_general_kraus(tc.channels.phasedampingchannel(0.15), i + 1) + for i in range(n): + c.rx(i, theta=param[j, i]) + return tc.backend.real(c.expectation((tc.gates.z(), [int(n / 2)]))) + + +def f2(key, param, n, nlayer): + c = tc.Circuit(n) + for i in range(n): + c.H(i) + for j in range(nlayer): + for i in range(n - 1): + c.cnot(i, i + 1) + c = precompute(c) + c.apply_general_kraus(tc.channels.phasedampingchannel(0.15), i) + c = precompute(c) + c.apply_general_kraus(tc.channels.phasedampingchannel(0.15), i + 1) + for i in range(n): + c.rx(i, theta=param[j, i]) + return tc.backend.real(c.expectation((tc.gates.z(), [int(n / 2)]))) + + +vagf1 = tc.backend.jit(tc.backend.value_and_grad(f1, argnums=1), static_argnums=(2, 3)) +vagf2 = tc.backend.jit(tc.backend.value_and_grad(f2, argnums=1), static_argnums=(2, 3)) + +param = tc.backend.ones([nlayer, n]) + + +def benchmark(f, tries=3): + time0 = time.time() + key = tc.backend.get_random_state(42) + print(f(key, param, n, nlayer)[0]) + time1 = time.time() + for _ in range(tries): + print(f(key, param, n, nlayer)[0]) + time2 = time.time() + print( + "staging time: ", + time1 - time0, + "running time: ", + (time2 - time1) / tries, + ) + + +print("without layerwise slicing jit") +benchmark(vagf1) +print("=============================") +print("with layerwise slicing jit") +benchmark(vagf2) + +# mac16 intel cpu: (6*5, jax) 1015, 0.0035; 31.68, 0.00082 diff --git a/examples/mcnoise_check.py b/examples/mcnoise_check.py index 79e292f1..de7a4c32 100644 --- a/examples/mcnoise_check.py +++ b/examples/mcnoise_check.py @@ -1,6 +1,7 @@ """ Cross check the correctness of the density matrix simulator and the Monte Carlo trajectory state simulator. """ + import os os.environ["CUDA_VISIBLE_DEVICES"] = "-1" diff --git a/examples/mera_extra_mpo.py b/examples/mera_extra_mpo.py new file mode 100644 index 00000000..c0b0d152 --- /dev/null +++ b/examples/mera_extra_mpo.py @@ -0,0 +1,121 @@ +""" +MERA VQE example with Hamiltonian expectation in MPO representation +""" + +import time +import logging +import numpy as np +import tensornetwork as tn +import optax +import cotengra +import tensorflow as tf +import tensorcircuit as tc + +logger = logging.getLogger("tensorcircuit") +logger.setLevel(logging.INFO) +ch = logging.StreamHandler() +ch.setLevel(logging.DEBUG) +logger.addHandler(ch) + + +K = tc.set_backend("tensorflow") +tc.set_dtype("complex128") + + +optc = cotengra.ReusableHyperOptimizer( + methods=["greedy", "kahypar"], + parallel="ray", + minimize="combo", + max_time=90, + max_repeats=1024, + progbar=True, +) + +tc.set_contractor("custom", optimizer=optc, preprocessing=True) + + +def MERA_circuit(params, n, d): + c = tc.Circuit(n) + + idx = 0 + + for i in range(n): + c.rx(i, theta=params[2 * i]) + c.rz(i, theta=params[2 * i + 1]) + idx += 2 * n + + for n_layer in range(1, int(np.log2(n)) + 1): + n_qubit = 2**n_layer + step = int(n / n_qubit) + + for _ in range(d): + # even + for i in range(step, n - step, 2 * step): + c.exp1(i, i + step, theta=params[idx], unitary=tc.gates._xx_matrix) + c.exp1(i, i + step, theta=params[idx + 1], unitary=tc.gates._zz_matrix) + idx += 2 + + # odd + for i in range(0, n, 2 * step): + c.exp1(i, i + step, theta=params[idx], unitary=tc.gates._xx_matrix) + c.exp1(i, i + step, theta=params[idx + 1], unitary=tc.gates._zz_matrix) + idx += 2 + + for i in range(0, n, step): + c.rx(i, theta=params[idx]) + c.rz(i, theta=params[idx + 1]) + idx += 2 + + return c, idx + + +def MERA(params, n, d, hamiltonian_mpo): + c, _ = MERA_circuit(params, n, d) + return tc.templates.measurements.mpo_expectation(c, hamiltonian_mpo) + + +MERA_vvag = K.jit(K.vectorized_value_and_grad(MERA), static_argnums=(1, 2, 3)) + + +def train(opt, j, b, n, d, batch, maxiter): + Jx = j * np.ones([n - 1]) # strength of xx interaction (OBC) + Bz = -b * np.ones([n]) # strength of transverse field + hamiltonian_mpo = tn.matrixproductstates.mpo.FiniteTFI(Jx, Bz, dtype=np.complex128) + # matrix product operator + hamiltonian_mpo = tc.quantum.tn2qop(hamiltonian_mpo) + _, idx = MERA_circuit(K.ones([int(1e6)]), n, d) + params = K.implicit_randn([batch, idx], 0, 0.05) + times = [] + times.append(time.time()) + for i in range(maxiter): + e, g = MERA_vvag(params, n, d, hamiltonian_mpo) + params = opt.update(g, params) + times.append(time.time()) + if i % 100 == 99: + print("energy: ", e) + print( + "time analysis: ", + times[1] - times[0], + (times[-1] - times[1]) / (len(times) - 2), + ) + return K.min(e) + + +if __name__ == "__main__": + if K.name == "jax": + exponential_decay_scheduler = optax.exponential_decay( + init_value=1e-2, transition_steps=500, decay_rate=0.9 + ) + opt = K.optimizer(optax.adam(exponential_decay_scheduler)) + elif K.name == "tensorflow": + exponential_decay_scheduler = tf.keras.optimizers.schedules.ExponentialDecay( + initial_learning_rate=1e-2, decay_steps=500, decay_rate=0.9 + ) + opt = K.optimizer(tf.keras.optimizers.Adam(exponential_decay_scheduler)) + e = train(opt, 1, -1, 64, 2, 2, 5000) + print("optimized energy:", e) + +# backend: n, d, batch: compiling time, running time +# jax: 16, 2, 8: 730s, 0.033s +# tf: 16, 2, 8: 140s, 0.043s +# tf: 32, 2, 2: 251s, 0.9s diff --git a/examples/mipt.py b/examples/mipt.py new file mode 100644 index 00000000..ec703cb9 --- /dev/null +++ b/examples/mipt.py @@ -0,0 +1,84 @@ +""" +demo example of mipt in tc style +""" + +from functools import partial +import time +import numpy as np +from scipy import stats +import tensorcircuit as tc + +K = tc.set_backend("jax") +# tf backend is slow (at least on cpu) + + +@partial(K.jit, static_argnums=(2, 3, 4)) +def circuit_output(random_matrix, status, n, d, p): + """ + mipt circuit + + :param random_matrix: a float or complex tensor containing 4*4 random haar matrix wth size [d*n, 4, 4] + :type random_matrix: _type_ + :param status: a int tensor with element in 0 or 1 or 2 (no meausrement) with size d*n + :type status: _type_ + :param n: number of qubits + :type n: _type_ + :param d: number of depth + :type d: _type_ + :param p: measurement ratio + :type p: float + :return: output state + """ + random_matrix = K.reshape(random_matrix, [d, n, 4, 4]) + status = K.reshape(status, [d, n]) + inputs = None + for j in range(d): + if inputs is None: + c = tc.Circuit(n) + else: + c = tc.Circuit(n, inputs=inputs) + for i in range(0, n, 2): + c.unitary(i, (i + 1) % n, unitary=random_matrix[j, i]) + for i in range(1, n, 2): + c.unitary(i, (i + 1) % n, unitary=random_matrix[j, i]) + inputs = c.state() + c = tc.Circuit(n, inputs=inputs) + for i in range(n): + c.general_kraus( + [ + np.sqrt(p) * np.array([[1.0, 0], [0, 0]]), + np.sqrt(p) * np.array([[0, 0], [0, 1.0]]), + np.sqrt(1 - p) * np.eye(2), + ], + i, + status=status[j, i], + ) + inputs = c.state() + c = tc.Circuit(n, inputs=inputs) + inputs = c.state() + inputs /= K.norm(inputs) + return inputs + + +@partial(K.jit, static_argnums=(2, 3, 4)) +def cals(random_matrix, status, n, d, p): + state = circuit_output(random_matrix, status, n, d, p) + rho = tc.quantum.reduced_density_matrix(state, cut=[i for i in range(n // 2)]) + return tc.quantum.entropy(rho), tc.quantum.renyi_entropy(rho, k=2) + + +if __name__ == "__main__": + n = 12 + d = 12 + st = np.random.uniform(size=[d * n]) + ## assume all X gate instead + rm = [stats.unitary_group.rvs(4) for _ in range(d * n)] + rm = [r / np.linalg.det(r) for r in rm] + rm = np.stack(rm) + time0 = time.time() + print(cals(rm, st, n, d, 0.1)) + time1 = time.time() + st = np.random.uniform(size=[d * n]) + print(cals(rm, st, n, d, 0.1)) + time2 = time.time() + print(f"compiling time {time1-time0}, running time {time2-time1}") diff --git a/examples/mipt_pideal.py b/examples/mipt_pideal.py new file mode 100644 index 00000000..c419446b --- /dev/null +++ b/examples/mipt_pideal.py @@ -0,0 +1,112 @@ +""" +demo example of mipt in tc style, with ideal p for each history trajectory +p is also jittable now, change parameter p doesn't trigger recompiling +""" + +from functools import partial +import time +import numpy as np +from scipy import stats +import tensorcircuit as tc + +K = tc.set_backend("jax") +tc.set_dtype("complex128") +# tf backend is slow (at least on cpu) +tc.set_contractor("cotengra-16-64") + + +def delete2(pick, plist): + # pick = 0, 1 : return plist[pick]/(plist[0]+plist[1]) + # pick = 2: return 1 + indicator = (K.sign(1.5 - pick) + 1) / 2 # 0,1 : 1, 2: 0 + p = 0 + p += 1 - indicator + p += indicator / (plist[0] + plist[1]) * (plist[0] * (1 - pick) + plist[1] * pick) + return p + + +@partial(K.jit, static_argnums=(2, 3)) +def circuit_output(random_matrix, status, n, d, p): + """ + mipt circuit + + :param random_matrix: a float or complex tensor containing 4*4 random haar matrix wth size [d*n, 4, 4] + :type random_matrix: _type_ + :param status: a int tensor with element in 0 or 1 or 2 (no meausrement) with size d*n + :type status: _type_ + :param n: number of qubits + :type n: _type_ + :param d: number of depth + :type d: _type_ + :param p: measurement ratio + :type p: float + :return: output state + """ + random_matrix = K.reshape(random_matrix, [d, n, 4, 4]) + status = K.reshape(status, [d, n]) + inputs = None + bs_history = [] + prob_history = [] + for j in range(d): + if inputs is None: + c = tc.Circuit(n) + else: + c = tc.Circuit(n, inputs=inputs) + for i in range(0, n, 2): + c.unitary(i, (i + 1) % n, unitary=random_matrix[j, i]) + for i in range(1, n, 2): + c.unitary(i, (i + 1) % n, unitary=random_matrix[j, i]) + inputs = c.state() + c = tc.Circuit(n, inputs=inputs) + for i in range(n): + pick, plist = c.general_kraus( + [ + K.sqrt(p) * K.convert_to_tensor(np.array([[1.0, 0], [0, 0]])), + K.sqrt(p) * K.convert_to_tensor(np.array([[0, 0], [0, 1.0]])), + K.sqrt(1 - p) * K.eye(2), + ], + i, + status=status[j, i], + with_prob=True, + ) + bs_history.append(pick) + prob_history.append(delete2(pick, plist)) + inputs = c.state() + c = tc.Circuit(n, inputs=inputs) + inputs = c.state() + inputs /= K.norm(inputs) + bs_history = K.stack(bs_history) + prob_history = K.stack(prob_history) + return inputs, bs_history, prob_history, K.sum(K.log(prob_history + 1e-11)) + + +@partial(K.jit, static_argnums=(2, 3)) +def cals(random_matrix, status, n, d, p): + state, bs_history, prob_history, prob = circuit_output( + random_matrix, status, n, d, p + ) + rho = tc.quantum.reduced_density_matrix(state, cut=[i for i in range(n // 2)]) + return ( + tc.quantum.entropy(rho), + tc.quantum.renyi_entropy(rho, k=2), + bs_history, + prob_history, + prob, + ) + + +if __name__ == "__main__": + n = 12 + d = 12 + st = np.random.uniform(size=[d * n]) + ## assume all X gate instead + rm = [stats.unitary_group.rvs(4) for _ in range(d * n)] + rm = [r / np.linalg.det(r) for r in rm] + rm = np.stack(rm) + time0 = time.time() + print(cals(rm, st, n, d, 0.6)) + time1 = time.time() + st = np.random.uniform(size=[d * n]) + print(cals(rm, st, n, d, 0.1)) + time2 = time.time() + print(f"compiling time {time1-time0}, running time {time2-time1}") diff --git a/examples/mpsvsexact.py b/examples/mpsvsexact.py index 4a4f5cf1..0ea1cfe3 100644 --- a/examples/mpsvsexact.py +++ b/examples/mpsvsexact.py @@ -1,6 +1,7 @@ """ A simple script to benchmark the approximation power of the MPS simulator. """ + import sys sys.path.insert(0, "../") @@ -8,6 +9,7 @@ import tensorcircuit as tc tc.set_backend("tensorflow") +tc.set_dtype("complex128") def tfi_energy(c, n, j=1.0, h=-1.0): @@ -19,23 +21,12 @@ def tfi_energy(c, n, j=1.0, h=-1.0): return e -def tfi_energy_mps(c, n, j=1.0, h=-1.0): - e = 0.0 - for i in range(n): - e += h * c.expectation_single_gate(tc.gates.x(), i) - for i in range(n - 1): - e += j * c.expectation_two_gates_product( - tc.gates.z(), tc.gates.z(), i, (i + 1) % n - ) - return e - - def energy(param, mpsd=None): if mpsd is None: c = tc.Circuit(n) else: c = tc.MPSCircuit(n) - c.set_truncation_rule(max_singular_values=mpsd) + c.set_split_rules({"max_singular_values": mpsd}) for i in range(n): c.H(i) @@ -49,11 +40,8 @@ def energy(param, mpsd=None): ) for i in range(n): c.rx(i, theta=param[2 * j + 1, i]) - if mpsd is None: - e = tfi_energy(c, n) - else: - e = tfi_energy_mps(c, n) + e = tfi_energy(c, n) e = tc.backend.real(e) if mpsd is not None: fidelity = c._fidelity diff --git a/examples/noise_calibration.py b/examples/noise_calibration.py new file mode 100644 index 00000000..c88392dc --- /dev/null +++ b/examples/noise_calibration.py @@ -0,0 +1,224 @@ +""" +1. Add readout error and mitigate readout error with two methods. +2. Add thermalrelaxition error and calibrate the thermalrelaxition error. +""" + +import numpy as np +from scipy.optimize import minimize, curve_fit +import tensorcircuit as tc + + +# Add readout error and mitigate readout error with two methods. +def miti_readout_circ(nqubit): + miticirc = [] + for i in range(2**nqubit): + name = "{:0" + str(nqubit) + "b}" + lisbs = [int(x) for x in name.format(i)] + c = tc.Circuit(nqubit) + for k in range(nqubit): + if lisbs[k] == 1: + c.X(k) + miticirc.append(c) + return miticirc + + +def probability_bs(bs): + nqubit = len(list(bs.keys())[0]) + probability = [0] * 2**nqubit + shots = sum([bs[s] for s in bs]) + for s in bs: + probability[int(s, 2)] = bs[s] / shots + return probability + + +def mitigate_probability(probability_noise, calmatrix, method="inverse"): + if method == "inverse": + X = np.linalg.inv(calmatrix) + Y = probability_noise + probability_cali = X @ Y + else: # method="square" + + def fun(x): + return sum((probability_noise - calmatrix @ x) ** 2) + + x0 = np.random.rand(len(probability_noise)) + cons = {"type": "eq", "fun": lambda x: 1 - sum(x)} + bnds = tuple((0, 1) for x in x0) + res = minimize(fun, x0, method="SLSQP", constraints=cons, bounds=bnds, tol=1e-6) + probability_cali = res.x + return probability_cali + + +def mitigate_readout(nqubit, circ, readout_error): + K = tc.set_backend("tensorflow") + + key = K.get_random_state(42) + keys = [] + for _ in range(2**nqubit): + key, subkey = tc.backend.random_split(key) + keys.append(subkey) + + # calibration matrix + miticirc = miti_readout_circ(nqubit) + shots = 100000 + calmatrix = np.zeros((2**nqubit, 2**nqubit)) + for i in range(2**nqubit): + c = miticirc[i] + bs = c.sample( + batch=shots, + allow_state=True, + readout_error=readout_error, + format_="count_dict_bin", + random_generator=keys[i], + ) + for s in bs: + calmatrix[int(s, 2)][i] = bs[s] / shots + + key, subkey = tc.backend.random_split(key) + bs = circ.sample( + batch=shots, allow_state=True, format_="count_dict_bin", random_generator=subkey + ) + probability_perfect = probability_bs(bs) + print("probability_without_readouterror", probability_perfect) + + key, subkey = tc.backend.random_split(key) + bs = circ.sample( + batch=shots, + allow_state=True, + readout_error=readout_error, + format_="count_dict_bin", + random_generator=subkey, + ) + probability_noise = probability_bs(bs) + print("probability_with_readouterror", probability_noise) + + probability_miti = mitigate_probability( + probability_noise, calmatrix, method="inverse" + ) + print("mitigate_readouterror_method1", probability_miti) + + probability_miti = mitigate_probability( + probability_noise, calmatrix, method="square" + ) + print("mitigate_readouterror_method2", probability_miti) + + +def example_readout_mitigate(): + nqubit = 3 + c = tc.Circuit(nqubit) + c.H(0) + c.cnot(0, 1) + c.X(2) + + readout_error = [] + readout_error.append([0.9, 0.75]) # readout error of qubit 0 + readout_error.append([0.4, 0.7]) # readout error of qubit 1 + readout_error.append([0.7, 0.9]) # readout error of qubit 2 + + mitigate_readout(nqubit, c, readout_error) + + +# Thermalrelaxition calibration +def fit_function(x_values, y_values, function, init_params): + fitparams, _ = curve_fit(function, x_values, y_values, init_params) + return fitparams + + +def T1_cali(t1, t2, time, method, excitedstatepopulation): + # calibrating experiments + nstep = int(4 * t1 / time) + pex = [] + for i in range(nstep): + dmc = tc.DMCircuit(1) + dmc.x(0) + for _ in range(i): + dmc.i(0) + dmc.thermalrelaxation( + 0, + t1=t1, + t2=t2, + time=time, + method=method, + excitedstatepopulation=excitedstatepopulation, + ) + + val = dmc.expectation_ps(z=[0]) + p = (1 - val) / 2.0 + pex.append(p) + + timelist = np.array([i * time for i in range(nstep)]) + measurement = np.array(np.real(pex)) + + return measurement, timelist + + +def T2_cali(t1, t2, time, method, excitedstatepopulation): + # calibrating experiments + nstep = int(4 * t2 / time) + pex = [] + for i in range(nstep): + dmc = tc.DMCircuit(1) + dmc.h(0) + for _ in range(0, i): + dmc.i(0) + dmc.thermalrelaxation( + 0, + t1=t1, + t2=t2, + time=time, + method=method, + excitedstatepopulation=excitedstatepopulation, + ) + # dmc.rz(0,theta = i*np.pi/1.5) + dmc.h(0) + + val = dmc.expectation_ps(z=[0]) + p = (1 - val) / 2.0 + pex.append(p) + + timelist = np.array([i * time for i in range(nstep)]) + measurement = np.array(np.real(pex)) + + return measurement, timelist + + +def example_T1_cali(): + t1 = 300 + t2 = 100 + time = 100 + method = "AUTO" + excitedstatepopulation = 0 + measurement, timelist = T1_cali(t1, t2, time, method, excitedstatepopulation) + + fit_params = fit_function( + timelist, measurement, lambda x, A, C, T: (A * np.exp(-x / T) + C), [-3, 0, 100] + ) + + _, _, T = fit_params + + print("realistic_T1", t1) + print("calibrated_T1", T) + + +def example_T2_cali(): + t1 = 300 + t2 = 280 + time = 50 + method = "AUTO" + excitedstatepopulation = 0 + measurement, timelist = T2_cali(t1, t2, time, method, excitedstatepopulation) + + fit_params = fit_function( + timelist, measurement, lambda x, A, C, T: (A * np.exp(-x / T) + C), [-3, 0, 100] + ) + + _, _, T = fit_params + + print("realistic_T2", t2) + print("calibrated_T2", T) + + +if __name__ == "__main__": + example_readout_mitigate() + example_T1_cali() + example_T2_cali() diff --git a/examples/omeinsum_julia/README.md b/examples/omeinsum_julia/README.md new file mode 100644 index 00000000..eadad801 --- /dev/null +++ b/examples/omeinsum_julia/README.md @@ -0,0 +1,125 @@ +# Use OMEinsum in TensorCircuit + +This example introduces how to use OMEinsum, a julia-based einsum package, to contract a circuit in TensorCircuit. + +We provide two solutions: + +* use subprocess to call a stand-alone julia script (**recommended**) +* use juliacall to integrate julia script into python (seems to be more elegant, but not recommended) + +We highly recommend using the first solution based on subprocess, not only due to its compatibility with julia's multi-threading but also because the experimental KaHyPar-based initialization is developed based on it. + +## Experiments + +We test contractors from OMEinsum on Google random circuits ([available online](https://datadryad.org/stash/dataset/doi:10.5061/dryad.k6t1rj8)) and compare with the cotengra contractor. +We choose the one with the largest seed for circuits that only differ in PRNG seed number (which means with the same tensor network structure but different tensor entries). For example, we benchmark `circuit_n12_m14_s9_e6_pEFGH.qsim` but skip +circuits like `circuit_n12_m14_s0_e6_pEFGH.qsim`. +We list experimental results in [benchmark_results.csv](benchmark_results.csv). +All experiments are done with +1. a 32GB CPU machine with 16 cores +2. TensorCircuit with TensorFlow backend +3. without using jit + + +Specifically, we test the following three methods: + +* **cotengra**: the HyperOptimizer from cotengra. More formally: +```python +opt = ctg.ReusableHyperOptimizer( + methods=["greedy", "kahypar"], + parallel=True, + minimize="flops", + max_repeats=1024, + progbar=False, +) +``` +* **treesa_greedy**: TreeSA contractor with greedy initialization. More formally: +```python +opt = OMEinsumTreeSAOptimizerSubprocess( + sc_target=60, sc_weight=0.0, rw_weight=0.0, ntrials=16, niters=64 +) +``` +* **treesa_kahypar**: TreeSA contractor with kahypar initialization, where the betas hyperparameters (initial temperature for SA) are [suggested by the author of OMEimsum](https://github.com/TensorBFS/OMEinsumContractionOrders.jl/issues/35#issuecomment-1397117653). More formally: +```python +opt = OMEinsumTreeSAOptimizerSubprocess( + sc_target=60, + sc_weight=0.0, + rw_weight=0.0, + kahypar_init=True, + ntrials=16, + niters=64, + betas=(10, 0.01, 40), +) +``` + +The above three `opt` are passed to TensorCircuit for contraction, respectively: +```python +tc.set_contractor( + "custom", + optimizer=opt, + preprocessing=True, + contraction_info=True, + debug_level=2, +) +``` + +We compute +```python +c.expectation_ps(z=[0], reuse=False) +``` + +Both OMEimsum and cotengra are able to optimize a weighted average of `log10[FLOPs]`, `log2[SIZE]` and `log2[WRITE]`. +However, OMEimsum and cotengra have different weight coefficients, which makes fair comparison difficult. +Thus we force each method to purely optimized `FLOPs`, but we do collect all contraction information in the table, including +`log10[FLOPs]`, `log2[SIZE]`, `log2[WRITE]`, `PathFindingTime`, `WallClockTime`. + +For circuits with `PathFindingTime` but empty `WallClockTime`, it means we meet OOM when computing with a 32GB CPU machine. + +For three circuits, namely `circuit_patch_n46_m14_s19_e21_pEFGH`, `circuit_patch_n44_m14_s19_e21_pEFGH` and `circuit_n42_m14_s9_e0_pEFGH`, we meet [errors in OMEinsum](https://github.com/TensorBFS/OMEinsumContractionOrders.jl/issues/35#issuecomment-1405236778), and there results are set to empty in [benchmark_results.csv](benchmark_results.csv). + + +## Details about subprocess and JuliaCall solutions +### Subprocess solution (Recommended) + +This solution calls a stand-alone julia script [omeinsum.jl](omeinsum.jl) for tensor network contraction. + +#### Setup + +* Step 1: install julia, see https://julialang.org/download/. Please install julia >= 1.8.5, the 1.6.7 LTS version raises: `Error in python: free(): invalid pointer` +* Step 2: add julia path to the PATH env variable so that we can find it +* Step 3: install julia package `OMEinsum`, `ArgParse`, `JSON` and `KaHyPar`, this example was tested with OMEinsum v0.7.2, ArgParse v1.1.4, JSON v0.21.3 and KaHyPar v0.3.0. See https://docs.julialang.org/en/v1/stdlib/Pkg/ for more details on julia's package manager + +#### How to run + +Run +`JULIA_NUM_THREADS=N python omeinsum_contractor_subprocess.py`. The env variable `JULIA_NUM_THREADS=N` will be passed to the julia script, so that you can enjoy the acceleration brought by julia multi-threading. + + +#### KaHyPar initialization + +The choice of initial status plays an important role in simulated annealing. +In a [discussion with the author of OMEinsum](https://github.com/TensorBFS/OMEinsumContractionOrders.jl/issues/35), we +found that there was a way to run TreeSA with initializer other than greedy or random. We demo how KaHyPar can produce the initial status of simulated annealing. Although we have not seen significant improvement by using KaHyPar initialization, we believe it is an interesting topic to explore. + +### JuliaCall solution (Not Recommended) + +JuliaCall seems to be a more elegant solution because all related code are integrated into a single python script. +However, in order to use julia multi-threading in juliacall, we have to turn off julia GC at the risk of OOM. See [this issue](https://github.com/cjdoris/PythonCall.jl/issues/219) for more details. + + +#### Setup + +* Step 1: install julia (say, from [here](https://julialang.org/download/)). Please install julia >= 1.8.5, the 1.6.7 LTS version raises: `Error in python: free(): invalid pointer` +* Step 2: add julia path to the PATH env variable so that juliacall can find it +* Step 3: install juliacall via `pip install juliacall`, this example was tested with juliacall 0.9.9 +* Step 4: install julia package `OMEinsum`, this example was tested with OMEinsum v0.7.2, see [here](https://docs.julialang.org/en/v1/stdlib/Pkg/) for more details on julia's package manager +* Step 5: for julia multi-threading, set env variable `PYTHON_JULIACALL_THREADS=`. + +#### How to run + +Run +`PYTHON_JULIACALL_THREADS= python omeinsum_contractor_juliacall.py`. + + + + diff --git a/examples/omeinsum_julia/benchmark_results.csv b/examples/omeinsum_julia/benchmark_results.csv new file mode 100644 index 00000000..ddbb9955 --- /dev/null +++ b/examples/omeinsum_julia/benchmark_results.csv @@ -0,0 +1,295 @@ +Circuit,Method,log10[FLOPs],log2[SIZE],log2[WRITE],PathFindingTime,WallClockTime +circuit_patch_n53_m20_s19_e35_pABCDCDAB,cotengra,11.647,27.0,35.691,398.803,904.630027267456 +circuit_patch_n53_m20_s19_e35_pABCDCDAB,treesa_greedy,11.5,27.0,34.627,580.954,470.45179032897954 +circuit_patch_n53_m20_s19_e35_pABCDCDAB,treesa_kahypar,11.495,27.0,34.529,475.356,437.65489906692505 +circuit_n53_m20_s9_e22_pABCDCDAB,cotengra,17.937,41.0,43.775,610.416, +circuit_n53_m20_s9_e22_pABCDCDAB,treesa_greedy,16.568,42.0,46.278,743.266, +circuit_n53_m20_s9_e22_pABCDCDAB,treesa_kahypar,16.861,40.0,45.633,605.181, +circuit_n53_m20_s9_e0_pABCDCDAB,cotengra,19.557,53.0,61.963,532.239, +circuit_n53_m20_s9_e0_pABCDCDAB,treesa_greedy,19.446,53.0,61.279,1560.847, +circuit_n53_m20_s9_e0_pABCDCDAB,treesa_kahypar,19.421,53.0,61.117,2265.351, +circuit_patch_n53_m18_s19_e31_pABCDCDAB,cotengra,11.575,27.0,35.454,330.965, +circuit_patch_n53_m18_s19_e31_pABCDCDAB,treesa_greedy,11.406,27.0,34.29,536.233,374.6099431915284 +circuit_patch_n53_m18_s19_e31_pABCDCDAB,treesa_kahypar,11.401,27.0,34.102,439.004, +circuit_n53_m18_s9_e19_pABCDCDAB,cotengra,14.172,34.0,37.063,1423.764, +circuit_n53_m18_s9_e19_pABCDCDAB,treesa_greedy,14.163,34.0,38.185,592.027, +circuit_n53_m18_s9_e19_pABCDCDAB,treesa_kahypar,14.163,34.0,38.382,401.272, +circuit_n53_m18_s9_e0_pABCDCDAB,cotengra,19.475,53.0,61.696,332.417, +circuit_n53_m18_s9_e0_pABCDCDAB,treesa_greedy,19.324,53.0,60.87,1414.159, +circuit_n53_m18_s9_e0_pABCDCDAB,treesa_kahypar,19.312,53.0,60.65,1886.625, +circuit_patch_n53_m16_s19_e28_pABCDCDAB,cotengra,11.504,27.0,35.211,334.772, +circuit_patch_n53_m16_s19_e28_pABCDCDAB,treesa_greedy,11.344,27.0,34.117,478.65,331.2237154006958 +circuit_patch_n53_m16_s19_e28_pABCDCDAB,treesa_kahypar,11.34,27.0,34.077,427.951,321.19098875427244 +circuit_n53_m16_s9_e15_pABCDCDAB,cotengra,17.566,42.0,44.29,928.736, +circuit_n53_m16_s9_e15_pABCDCDAB,treesa_greedy,16.08,46.0,49.819,686.65, +circuit_n53_m16_s9_e15_pABCDCDAB,treesa_kahypar,16.592,44.0,47.877,631.217, +circuit_n53_m16_s9_e0_pABCDCDAB,cotengra,19.383,53.0,61.388,377.769, +circuit_n53_m16_s9_e0_pABCDCDAB,treesa_greedy,19.219,53.0,60.47,1220.764, +circuit_n53_m16_s9_e0_pABCDCDAB,treesa_kahypar,19.24,53.0,60.536,1766.683, +circuit_patch_n53_m14_s19_e25_pABCDCDAB,cotengra,11.399,27.0,34.863,188.49,509.74672747612 +circuit_patch_n53_m14_s19_e25_pABCDCDAB,treesa_greedy,11.203,27.0,33.567,406.24,228.8411741352081 +circuit_patch_n53_m14_s19_e25_pABCDCDAB,treesa_kahypar,11.196,27.0,33.447,382.167,211.26469307708743 +circuit_n53_m14_s9_e12_pABCDCDAB,cotengra,15.148,36.0,38.137,659.913, +circuit_n53_m14_s9_e12_pABCDCDAB,treesa_greedy,15.073,36.0,40.602,429.041, +circuit_n53_m14_s9_e12_pABCDCDAB,treesa_kahypar,15.072,36.0,40.432,346.216, +circuit_n53_m14_s9_e11_pEFGH,cotengra,16.04,40.0,41.073,450.93, +circuit_n53_m14_s9_e11_pEFGH,treesa_greedy,15.752,36.0,40.182,482.829, +circuit_n53_m14_s9_e11_pEFGH,treesa_kahypar,15.956,38.0,40.972,339.366, +circuit_n53_m14_s9_e0_pEFGH,cotengra,19.209,55.0,60.822,215.841, +circuit_n53_m14_s9_e0_pEFGH,treesa_greedy,19.038,53.0,59.909,992.68, +circuit_n53_m14_s9_e0_pEFGH,treesa_kahypar,20.484,53.0,59.617,1099.601, +circuit_n53_m14_s9_e0_pABCDCDAB,cotengra,19.229,53.0,60.874,285.173, +circuit_n53_m14_s9_e0_pABCDCDAB,treesa_greedy,19.034,53.0,59.847,966.856, +circuit_n53_m14_s9_e0_pABCDCDAB,treesa_kahypar,19.036,53.0,59.678,1400.73, +circuit_patch_n53_m12_s19_e21_pABCDCDAB,cotengra,11.275,27.0,34.452,131.188,383.0614959831238 +circuit_patch_n53_m12_s19_e21_pABCDCDAB,treesa_greedy,11.097,27.0,33.278,356.329,184.52528404808044 +circuit_patch_n53_m12_s19_e21_pABCDCDAB,treesa_kahypar,11.098,27.0,33.305,360.083,187.92855328750608 +circuit_n53_m12_s9_e8_pABCDCDAB,cotengra,18.012,49.0,49.337,228.721, +circuit_n53_m12_s9_e8_pABCDCDAB,treesa_greedy,16.568,42.0,46.236,496.834, +circuit_n53_m12_s9_e8_pABCDCDAB,treesa_kahypar,16.861,40.0,45.511,468.701, +circuit_n53_m12_s9_e0_pABCDCDAB,cotengra,19.07,53.0,60.348,204.507, +circuit_n53_m12_s9_e0_pABCDCDAB,treesa_greedy,18.873,53.0,59.324,867.543, +circuit_n53_m12_s9_e0_pABCDCDAB,treesa_kahypar,18.926,53.0,59.575,1188.158, +circuit_patch_n51_m14_s19_e25_pEFGH,cotengra,11.118,26.0,33.936,176.226,265.7081673851013 +circuit_patch_n51_m14_s19_e25_pEFGH,treesa_greedy,10.967,26.0,33.088,410.717,162.59800005722047 +circuit_patch_n51_m14_s19_e25_pEFGH,treesa_kahypar,10.974,26.0,33.023,364.369,156.77375979995725 +circuit_n51_m14_s9_e11_pEFGH,cotengra,16.265,42.0,44.935,986.076, +circuit_n51_m14_s9_e11_pEFGH,treesa_greedy,15.356,38.0,40.518,479.331, +circuit_n51_m14_s9_e11_pEFGH,treesa_kahypar,15.753,38.0,40.943,340.365, +circuit_n51_m14_s9_e0_pEFGH,cotengra,18.627,53.0,58.878,250.292, +circuit_n51_m14_s9_e0_pEFGH,treesa_greedy,18.46,51.0,58.05,925.77, +circuit_n51_m14_s9_e0_pEFGH,treesa_kahypar,18.456,51.0,58.009,1000.855, +circuit_patch_n50_m14_s19_e25_pEFGH,cotengra,10.896,26.0,33.194,189.555,161.8636086654663 +circuit_patch_n50_m14_s19_e25_pEFGH,treesa_greedy,10.77,25.0,32.356,401.736,101.84471446418765 +circuit_patch_n50_m14_s19_e25_pEFGH,treesa_kahypar,10.783,25.0,32.41,359.563,106.30724927139283 +circuit_n50_m14_s9_e6_pEFGH,cotengra,15.473,36.0,37.622,559.121, +circuit_n50_m14_s9_e6_pEFGH,treesa_greedy,15.357,35.0,38.348,468.989, +circuit_n50_m14_s9_e6_pEFGH,treesa_kahypar,15.357,35.0,38.431,304.096, +circuit_n50_m14_s9_e0_pEFGH,cotengra,18.32,50.0,57.861,291.025, +circuit_n50_m14_s9_e0_pEFGH,treesa_greedy,18.123,50.0,56.899,905.059, +circuit_n50_m14_s9_e0_pEFGH,treesa_kahypar,18.2,50.0,57.005,1064.76, +circuit_patch_n49_m14_s19_e25_pEFGH,cotengra,10.784,25.0,32.822,234.339,126.944433675766 +circuit_patch_n49_m14_s19_e25_pEFGH,treesa_greedy,10.656,25.0,31.946,396.791,79.66520441436768 +circuit_patch_n49_m14_s19_e25_pEFGH,treesa_kahypar,10.671,25.0,32.005,362.81,83.06759184837341 +circuit_n49_m14_s9_e6_pEFGH,cotengra,15.426,40.0,43.488,977.093, +circuit_n49_m14_s9_e6_pEFGH,treesa_greedy,15.38,35.0,38.721,435.335, +circuit_n49_m14_s9_e6_pEFGH,treesa_kahypar,15.381,37.0,39.817,315.995, +circuit_n49_m14_s9_e0_pEFGH,cotengra,18.0,49.0,56.796,202.637, +circuit_n49_m14_s9_e0_pEFGH,treesa_greedy,17.812,49.0,55.846,865.665, +circuit_n49_m14_s9_e0_pEFGH,treesa_kahypar,17.902,49.0,55.917,1097.266, +circuit_patch_n48_m14_s19_e25_pEFGH,cotengra,10.593,25.0,32.194,141.984,84.87485381698608 +circuit_patch_n48_m14_s19_e25_pEFGH,treesa_greedy,10.461,24.0,31.434,385.411,58.61000372314453 +circuit_patch_n48_m14_s19_e25_pEFGH,treesa_kahypar,10.468,24.0,31.311,370.662,54.62013644027712 +circuit_n48_m14_s9_e6_pEFGH,cotengra,15.39,39.0,42.954,356.865, +circuit_n48_m14_s9_e6_pEFGH,treesa_greedy,15.075,39.0,42.456,418.301, +circuit_n48_m14_s9_e6_pEFGH,treesa_kahypar,15.353,35.0,37.725,327.35, +circuit_n48_m14_s9_e0_pEFGH,cotengra,17.699,48.0,55.797,247.7, +circuit_n48_m14_s9_e0_pEFGH,treesa_greedy,17.542,48.0,54.951,867.798, +circuit_n48_m14_s9_e0_pEFGH,treesa_kahypar,17.561,48.0,55.12,1023.973, +circuit_patch_n47_m14_s19_e21_pEFGH,cotengra,10.47,24.0,31.78,139.135,66.35265134811402 +circuit_patch_n47_m14_s19_e21_pEFGH,treesa_greedy,10.376,24.0,31.211,385.959,52.02424632644653 +circuit_patch_n47_m14_s19_e21_pEFGH,treesa_kahypar,10.372,24.0,31.07,361.883,47.92822636795046 +circuit_n47_m14_s9_e6_pEFGH,cotengra,15.392,36.0,38.739,717.684, +circuit_n47_m14_s9_e6_pEFGH,treesa_greedy,15.079,34.0,38.001,421.445, +circuit_n47_m14_s9_e6_pEFGH,treesa_kahypar,15.08,36.0,38.969,321.19, +circuit_n47_m14_s9_e0_pEFGH,cotengra,17.404,47.0,54.818,188.359, +circuit_n47_m14_s9_e0_pEFGH,treesa_greedy,17.238,47.0,53.996,806.703, +circuit_n47_m14_s9_e0_pEFGH,treesa_kahypar,17.232,47.0,53.929,1011.005, +circuit_patch_n46_m14_s19_e21_pEFGH,cotengra,10.291,23.0,31.19,132.961,47.43699904823302 +circuit_patch_n46_m14_s19_e21_pEFGH,treesa_greedy,10.195,23.0,30.649,372.6,38.02652993202207 +circuit_patch_n46_m14_s19_e21_pEFGH,treesa_kahypar,,,,, +circuit_n46_m14_s9_e6_pEFGH,cotengra,13.634,36.0,39.078,520.681, +circuit_n46_m14_s9_e6_pEFGH,treesa_greedy,13.61,36.0,38.42,365.466, +circuit_n46_m14_s9_e6_pEFGH,treesa_kahypar,13.849,30.0,34.175,297.738, +circuit_n46_m14_s9_e0_pEFGH,cotengra,17.069,46.0,53.705,234.515, +circuit_n46_m14_s9_e0_pEFGH,treesa_greedy,16.904,46.0,52.783,803.606, +circuit_n46_m14_s9_e0_pEFGH,treesa_kahypar,16.977,46.0,52.974,825.773, +circuit_patch_n45_m14_s19_e21_pEFGH,cotengra,10.151,23.0,30.722,140.734,36.95154472923278 +circuit_patch_n45_m14_s19_e21_pEFGH,treesa_greedy,10.064,23.0,30.134,377.491,29.460883554458636 +circuit_patch_n45_m14_s19_e21_pEFGH,treesa_kahypar,10.065,23.0,29.992,393.157,27.99106628227236 +circuit_n45_m14_s9_e6_pEFGH,cotengra,14.805,35.0,37.31,562.626, +circuit_n45_m14_s9_e6_pEFGH,treesa_greedy,14.778,33.0,37.072,468.155, +circuit_n45_m14_s9_e6_pEFGH,treesa_kahypar,14.802,33.0,36.592,312.201, +circuit_n45_m14_s9_e0_pEFGH,cotengra,16.77,45.0,52.711,182.538, +circuit_n45_m14_s9_e0_pEFGH,treesa_greedy,16.588,45.0,51.792,758.175, +circuit_n45_m14_s9_e0_pEFGH,treesa_kahypar,16.909,45.0,52.135,845.952, +circuit_patch_n44_m14_s19_e21_pEFGH,cotengra,9.973,22.0,30.134,139.612,27.20677493858338 +circuit_patch_n44_m14_s19_e21_pEFGH,treesa_greedy,9.885,22.0,29.572,359.367,23.439527376174908 +circuit_patch_n44_m14_s19_e21_pEFGH,treesa_kahypar,,,,, +circuit_n44_m14_s9_e6_pEFGH,cotengra,13.26,30.0,32.518,501.754, +circuit_n44_m14_s9_e6_pEFGH,treesa_greedy,13.249,30.0,33.105,362.069, +circuit_n44_m14_s9_e6_pEFGH,treesa_kahypar,13.25,31.0,33.689,317.296, +circuit_n44_m14_s9_e0_pEFGH,cotengra,16.472,44.0,51.716,166.682, +circuit_n44_m14_s9_e0_pEFGH,treesa_greedy,16.323,44.0,50.795,725.629, +circuit_n44_m14_s9_e0_pEFGH,treesa_kahypar,16.342,44.0,51.098,836.718, +circuit_patch_n43_m14_s19_e21_pEFGH,cotengra,9.841,23.0,29.701,124.817,22.214245708465583 +circuit_patch_n43_m14_s19_e21_pEFGH,treesa_greedy,9.739,22.0,28.984,348.046,18.258648876190197 +circuit_patch_n43_m14_s19_e21_pEFGH,treesa_kahypar,9.739,22.0,28.932,359.195,18.26624458312989 +circuit_n43_m14_s9_e6_pEFGH,cotengra,14.506,34.0,37.565,457.91, +circuit_n43_m14_s9_e6_pEFGH,treesa_greedy,14.477,32.0,36.386,417.651, +circuit_n43_m14_s9_e6_pEFGH,treesa_kahypar,14.478,34.0,37.138,325.975, +circuit_n43_m14_s9_e0_pEFGH,cotengra,16.164,43.0,50.7,188.734, +circuit_n43_m14_s9_e0_pEFGH,treesa_greedy,16.016,43.0,49.954,703.888, +circuit_n43_m14_s9_e0_pEFGH,treesa_kahypar,16.196,43.0,50.008,810.97, +circuit_patch_n42_m14_s19_e21_pEFGH,cotengra,9.628,22.0,28.986,136.774,16.64340107536316 +circuit_patch_n42_m14_s19_e21_pEFGH,treesa_greedy,9.551,21.0,28.359,337.304,14.703207632064846 +circuit_patch_n42_m14_s19_e21_pEFGH,treesa_kahypar,9.548,21.0,28.267,349.973,14.334425737380968 +circuit_n42_m14_s9_e6_pEFGH,cotengra,13.077,34.0,37.894,580.035, +circuit_n42_m14_s9_e6_pEFGH,treesa_greedy,12.949,30.0,33.037,349.173, +circuit_n42_m14_s9_e6_pEFGH,treesa_kahypar,12.949,30.0,32.338,300.511, +circuit_n42_m14_s9_e0_pEFGH,cotengra,15.853,42.0,49.668,148.982, +circuit_n42_m14_s9_e0_pEFGH,treesa_greedy,15.691,42.0,48.799,647.25, +circuit_n42_m14_s9_e0_pEFGH,treesa_kahypar,,,,, +circuit_patch_n41_m14_s19_e21_pEFGH,cotengra,9.509,21.0,28.596,127.293,14.677715522766109 +circuit_patch_n41_m14_s19_e21_pEFGH,treesa_greedy,9.42,21.0,27.908,335.6,12.917063379287695 +circuit_patch_n41_m14_s19_e21_pEFGH,treesa_kahypar,9.419,21.0,27.914,365.479,12.920412155151382 +circuit_n41_m14_s9_e6_pEFGH,cotengra,15.104,33.0,36.033,193.758, +circuit_n41_m14_s9_e6_pEFGH,treesa_greedy,14.177,32.0,36.003,377.052, +circuit_n41_m14_s9_e6_pEFGH,treesa_kahypar,14.177,32.0,35.905,309.514, +circuit_n41_m14_s9_e0_pEFGH,cotengra,15.537,41.0,48.619,143.36, +circuit_n41_m14_s9_e0_pEFGH,treesa_greedy,15.377,41.0,47.724,483.166, +circuit_n41_m14_s9_e0_pEFGH,treesa_kahypar,15.374,41.0,47.552,511.412, +circuit_patch_n40_m14_s19_e21_pEFGH,cotengra,9.339,20.0,28.02,114.253,12.69566180419922 +circuit_patch_n40_m14_s19_e21_pEFGH,treesa_greedy,9.243,20.0,27.29,321.336,11.173633779525744 +circuit_patch_n40_m14_s19_e21_pEFGH,treesa_kahypar,9.242,20.0,27.336,380.531,11.552189010620111 +circuit_n40_m14_s9_e6_pEFGH,cotengra,11.782,26.0,30.112,429.931, +circuit_n40_m14_s9_e6_pEFGH,treesa_greedy,11.553,29.0,32.558,317.519, +circuit_n40_m14_s9_e6_pEFGH,treesa_kahypar,11.754,26.0,30.017,307.609,32.396990266799946 +circuit_n40_m14_s9_e0_pEFGH,cotengra,15.279,40.0,47.758,167.751, +circuit_n40_m14_s9_e0_pEFGH,treesa_greedy,15.127,40.0,46.888,485.121, +circuit_n40_m14_s9_e0_pEFGH,treesa_kahypar,15.122,40.0,46.827,570.709, +circuit_patch_n39_m14_s19_e21_pEFGH,cotengra,9.183,20.0,27.503,117.214,11.140823112487794 +circuit_patch_n39_m14_s19_e21_pEFGH,treesa_greedy,9.114,20.0,26.894,320.019,10.45490222549438 +circuit_patch_n39_m14_s19_e21_pEFGH,treesa_kahypar,9.117,20.0,26.907,359.031,10.429051774978632 +circuit_n39_m14_s9_e6_pEFGH,cotengra,14.982,39.0,46.773,155.635, +circuit_n39_m14_s9_e6_pEFGH,treesa_greedy,13.605,32.0,35.819,359.551, +circuit_n39_m14_s9_e6_pEFGH,treesa_kahypar,13.606,32.0,35.772,304.695, +circuit_n39_m14_s9_e0_pEFGH,cotengra,14.946,39.0,46.654,154.522, +circuit_n39_m14_s9_e0_pEFGH,treesa_greedy,14.83,39.0,46.045,452.056, +circuit_n39_m14_s9_e0_pEFGH,treesa_kahypar,14.839,39.0,46.029,451.003, +circuit_patch_n38_m14_s19_e18_pEFGH,cotengra,9.02,20.0,26.976,113.789,10.02211407279968 +circuit_patch_n38_m14_s19_e18_pEFGH,treesa_greedy,8.929,19.0,26.184,313.756,9.229728740692169 +circuit_patch_n38_m14_s19_e18_pEFGH,treesa_kahypar,8.927,19.0,26.211,367.953,9.526923963546778 +circuit_n38_m14_s9_e6_pEFGH,cotengra,13.44,33.0,35.424,444.322, +circuit_n38_m14_s9_e6_pEFGH,treesa_greedy,13.354,32.0,35.714,353.183, +circuit_n38_m14_s9_e6_pEFGH,treesa_kahypar,13.352,32.0,35.259,298.964, +circuit_n38_m14_s9_e0_pEFGH,cotengra,14.633,38.0,45.615,168.515, +circuit_n38_m14_s9_e0_pEFGH,treesa_greedy,14.514,38.0,44.77,426.414, +circuit_n38_m14_s9_e0_pEFGH,treesa_kahypar,14.512,38.0,44.717,465.762, +circuit_patch_n36_m14_s19_e18_pEFGH,cotengra,8.693,20.0,25.88,111.161,8.696066631317137 +circuit_patch_n36_m14_s19_e18_pEFGH,treesa_greedy,8.595,18.0,25.128,295.207,7.998978631973273 +circuit_patch_n36_m14_s19_e18_pEFGH,treesa_kahypar,8.595,18.0,25.218,352.907,8.22156841087343 +circuit_n36_m14_s9_e6_pEFGH,cotengra,13.31,30.0,33.416,399.867, +circuit_n36_m14_s9_e6_pEFGH,treesa_greedy,12.987,32.0,34.57,313.595, +circuit_n36_m14_s9_e6_pEFGH,treesa_kahypar,12.982,30.0,34.073,306.227, +circuit_n36_m14_s9_e0_pEFGH,cotengra,14.047,36.0,43.666,112.642, +circuit_n36_m14_s9_e0_pEFGH,treesa_greedy,13.899,36.0,42.852,334.293, +circuit_n36_m14_s9_e0_pEFGH,treesa_kahypar,13.897,36.0,42.839,342.502, +circuit_patch_n34_m14_s19_e18_pEFGH,cotengra,8.362,17.0,24.789,92.887,7.233053529739379 +circuit_patch_n34_m14_s19_e18_pEFGH,treesa_greedy,8.279,17.0,24.067,280.897,6.78341157722474 +circuit_patch_n34_m14_s19_e18_pEFGH,treesa_kahypar,8.278,17.0,24.093,351.171,7.07559204483033 +circuit_n34_m14_s9_e6_pEFGH,cotengra,12.977,30.0,32.099,186.683, +circuit_n34_m14_s9_e6_pEFGH,treesa_greedy,12.417,32.0,34.306,290.161, +circuit_n34_m14_s9_e6_pEFGH,treesa_kahypar,12.409,31.0,34.133,301.459, +circuit_n34_m14_s9_e0_pEFGH,cotengra,13.366,34.0,41.184,723.872, +circuit_n34_m14_s9_e0_pEFGH,treesa_greedy,13.235,34.0,40.647,321.03, +circuit_n34_m14_s9_e0_pEFGH,treesa_kahypar,13.304,34.0,40.84,313.386, +circuit_patch_n32_m14_s19_e18_pEFGH,cotengra,8.14,17.0,24.05,82.169,6.364380270004276 +circuit_patch_n32_m14_s19_e18_pEFGH,treesa_greedy,8.06,17.0,23.345,260.718,6.2471072025298895 +circuit_patch_n32_m14_s19_e18_pEFGH,treesa_kahypar,8.06,17.0,23.263,336.565,5.982450304031374 +circuit_n32_m14_s9_e6_pEFGH,cotengra,12.455,30.0,31.599,274.974, +circuit_n32_m14_s9_e6_pEFGH,treesa_greedy,12.387,32.0,37.673,282.553, +circuit_n32_m14_s9_e6_pEFGH,treesa_kahypar,11.927,30.0,33.364,287.065, +circuit_n32_m14_s9_e0_pEFGH,cotengra,12.767,32.0,39.409,102.515, +circuit_n32_m14_s9_e0_pEFGH,treesa_greedy,12.613,32.0,38.534,301.8, +circuit_n32_m14_s9_e0_pEFGH,treesa_kahypar,12.616,32.0,38.531,315.007, +circuit_patch_n30_m14_s19_e18_pEFGH,cotengra,7.722,16.0,22.654,81.24,5.3438632583618215 +circuit_patch_n30_m14_s19_e18_pEFGH,treesa_greedy,7.627,15.0,21.867,243.191,5.291374429702756 +circuit_patch_n30_m14_s19_e18_pEFGH,treesa_kahypar,7.627,15.0,22.003,321.618,5.321541578292852 +circuit_n30_m14_s9_e6_pEFGH,cotengra,11.93,30.0,36.445,126.822, +circuit_n30_m14_s9_e6_pEFGH,treesa_greedy,11.767,30.0,35.616,266.514, +circuit_n30_m14_s9_e6_pEFGH,treesa_kahypar,11.755,30.0,35.362,294.707, +circuit_n30_m14_s9_e0_pEFGH,cotengra,12.108,30.0,37.106,589.94, +circuit_n30_m14_s9_e0_pEFGH,treesa_greedy,12.008,30.0,36.456,281.111, +circuit_n30_m14_s9_e0_pEFGH,treesa_kahypar,12.004,30.0,36.513,336.693, +circuit_patch_n28_m14_s19_e18_pEFGH,cotengra,7.39,15.0,21.544,67.828,4.449433633804318 +circuit_patch_n28_m14_s19_e18_pEFGH,treesa_greedy,7.295,14.0,20.685,225.298,4.4308215160369855 +circuit_patch_n28_m14_s19_e18_pEFGH,treesa_kahypar,7.296,14.0,20.815,327.638,4.614945423126244 +circuit_n28_m14_s9_e6_pEFGH,cotengra,11.337,28.0,34.601,127.216, +circuit_n28_m14_s9_e6_pEFGH,treesa_greedy,11.165,28.0,33.674,246.72, +circuit_n28_m14_s9_e6_pEFGH,treesa_kahypar,11.159,28.0,33.527,312.592, +circuit_n28_m14_s9_e0_pEFGH,cotengra,11.535,28.0,35.321,94.159, +circuit_n28_m14_s9_e0_pEFGH,treesa_greedy,11.42,28.0,34.62,264.422, +circuit_n28_m14_s9_e0_pEFGH,treesa_kahypar,11.423,28.0,34.546,339.157, +circuit_patch_n26_m14_s19_e18_pEFGH,cotengra,7.148,14.0,20.659,81.953,3.9409566612243614 +circuit_patch_n26_m14_s19_e18_pEFGH,treesa_greedy,7.079,14.0,20.113,213.555,3.86619908332824 +circuit_patch_n26_m14_s19_e18_pEFGH,treesa_kahypar,7.079,14.0,20.016,303.826,4.038710569381692 +circuit_n26_m14_s9_e6_pEFGH,cotengra,10.806,26.0,32.895,356.549,128.8634298095703 +circuit_n26_m14_s9_e6_pEFGH,treesa_greedy,10.563,26.0,31.556,236.575,56.48603467941285 +circuit_n26_m14_s9_e6_pEFGH,treesa_kahypar,10.557,26.0,31.361,300.496,49.58447723770144 +circuit_n26_m14_s9_e0_pEFGH,cotengra,10.959,26.0,33.408,88.53,181.06059762954712 +circuit_n26_m14_s9_e0_pEFGH,treesa_greedy,10.827,26.0,32.479,250.678,103.2807061405182 +circuit_n26_m14_s9_e0_pEFGH,treesa_kahypar,10.826,26.0,32.687,351.967,120.00889325904848 +circuit_patch_n24_m14_s19_e18_pEFGH,cotengra,6.729,12.0,19.348,61.661,3.498441232681273 +circuit_patch_n24_m14_s19_e18_pEFGH,treesa_greedy,6.646,12.0,18.699,198.9,3.5054071903228703 +circuit_patch_n24_m14_s19_e18_pEFGH,treesa_kahypar,6.646,12.0,18.673,300.658,3.372755758285507 +circuit_n24_m14_s9_e6_pEFGH,cotengra,10.171,24.0,30.782,311.853,30.42965380859374 +circuit_n24_m14_s9_e6_pEFGH,treesa_greedy,9.962,24.0,29.59,226.844,16.06061111068726 +circuit_n24_m14_s9_e6_pEFGH,treesa_kahypar,9.957,24.0,29.503,294.211,15.658095802307116 +circuit_n24_m14_s9_e0_pEFGH,cotengra,10.299,24.0,31.136,404.642,37.84940930175782 +circuit_n24_m14_s9_e0_pEFGH,treesa_greedy,10.227,24.0,30.55,236.778,28.07969152641297 +circuit_n24_m14_s9_e0_pEFGH,treesa_kahypar,10.219,24.0,30.526,337.676,27.57617652320863 +circuit_patch_n22_m14_s19_e18_pEFGH,cotengra,6.358,11.0,18.104,54.359,3.0047275695800764 +circuit_patch_n22_m14_s19_e18_pEFGH,treesa_greedy,6.279,11.0,17.488,178.999,2.7667670974731493 +circuit_patch_n22_m14_s19_e18_pEFGH,treesa_kahypar,6.282,11.0,17.514,261.383,2.939791814804096 +circuit_n22_m14_s9_e6_pEFGH,cotengra,9.637,22.0,29.023,63.475,10.559750938415526 +circuit_n22_m14_s9_e6_pEFGH,treesa_greedy,9.331,22.0,27.236,202.455,5.451742572784411 +circuit_n22_m14_s9_e6_pEFGH,treesa_kahypar,9.335,22.0,27.534,270.648,6.229142667770361 +circuit_n22_m14_s9_e0_pEFGH,cotengra,9.664,22.0,29.101,324.958,11.2127053184509 +circuit_n22_m14_s9_e0_pEFGH,treesa_greedy,9.577,22.0,28.5,216.074,8.796073556900012 +circuit_n22_m14_s9_e0_pEFGH,treesa_kahypar,9.577,22.0,28.517,303.148,8.924938919067358 +circuit_patch_n20_m14_s19_e18_pEFGH,cotengra,6.117,11.0,17.306,50.205,2.314639015197756 +circuit_patch_n20_m14_s19_e18_pEFGH,treesa_greedy,6.053,11.0,16.765,168.795,2.467866020202649 +circuit_patch_n20_m14_s19_e18_pEFGH,treesa_kahypar,6.052,11.0,16.821,247.906,2.282943862915033 +circuit_n20_m14_s9_e6_pEFGH,cotengra,8.855,20.0,26.352,69.377,3.870749090194707 +circuit_n20_m14_s9_e6_pEFGH,treesa_greedy,8.7,20.0,25.226,188.972,3.292818906784049 +circuit_n20_m14_s9_e6_pEFGH,treesa_kahypar,8.7,20.0,25.255,249.616,3.154874023437486 +circuit_n20_m14_s9_e0_pEFGH,cotengra,9.025,20.0,26.99,61.497,4.605196216583252 +circuit_n20_m14_s9_e0_pEFGH,treesa_greedy,8.921,20.0,26.163,200.363,3.766950284957886 +circuit_n20_m14_s9_e0_pEFGH,treesa_kahypar,8.922,20.0,26.111,269.689,3.91698325729368 +circuit_patch_n18_m14_s19_e18_pEFGH,cotengra,5.633,9.0,14.972,46.211,1.9544469966888445 +circuit_patch_n18_m14_s19_e18_pEFGH,treesa_greedy,5.58,9.0,15.293,149.954,1.8456432781219403 +circuit_patch_n18_m14_s19_e18_pEFGH,treesa_kahypar,5.578,9.0,15.371,237.544,1.9948498306274305 +circuit_n18_m14_s9_e6_pEFGH,cotengra,8.189,18.0,24.108,157.027,2.411690185546888 +circuit_n18_m14_s9_e6_pEFGH,treesa_greedy,8.084,18.0,23.467,176.467,2.463656433105456 +circuit_n18_m14_s9_e6_pEFGH,treesa_kahypar,8.084,18.0,23.472,247.014,2.3280464992523093 +circuit_n18_m14_s9_e0_pEFGH,cotengra,8.369,18.0,24.769,60.902,2.8730332355499257 +circuit_n18_m14_s9_e0_pEFGH,treesa_greedy,8.298,18.0,24.253,185.16,2.7633098030090366 +circuit_n18_m14_s9_e0_pEFGH,treesa_kahypar,8.296,18.0,24.191,275.009,2.5868957672118995 +circuit_patch_n16_m14_s19_e18_pEFGH,cotengra,5.244,8.0,14.229,41.156,1.5808226051330578 +circuit_patch_n16_m14_s19_e18_pEFGH,treesa_greedy,5.223,8.0,14.19,116.484,1.542439189910894 +circuit_patch_n16_m14_s19_e18_pEFGH,treesa_kahypar,5.221,8.0,14.219,177.855,1.4062686157226665 +circuit_n16_m14_s9_e6_pEFGH,cotengra,7.661,16.0,22.435,46.802,1.917652652740479 +circuit_n16_m14_s9_e6_pEFGH,treesa_greedy,7.461,16.0,21.268,152.908,1.6896731777191292 +circuit_n16_m14_s9_e6_pEFGH,treesa_kahypar,7.461,16.0,21.475,233.981,1.976748413085943 +circuit_n16_m14_s9_e0_pEFGH,cotengra,7.707,16.0,22.591,49.002,1.8988505344390845 +circuit_n16_m14_s9_e0_pEFGH,treesa_greedy,7.658,16.0,22.015,164.476,1.8667677154541025 +circuit_n16_m14_s9_e0_pEFGH,treesa_kahypar,7.657,16.0,22.145,263.176,2.03469669723512 +circuit_patch_n14_m14_s19_e18_pEFGH,cotengra,5.033,8.0,13.664,37.481,1.0769466819763167 +circuit_patch_n14_m14_s19_e18_pEFGH,treesa_greedy,5.026,8.0,13.612,99.889,1.2105106697082562 +circuit_patch_n14_m14_s19_e18_pEFGH,treesa_kahypar,5.026,8.0,13.612,149.045,1.068460779190076 +circuit_n14_m14_s9_e6_pEFGH,cotengra,6.9,14.0,19.893,41.197,1.3414757518768283 +circuit_n14_m14_s9_e6_pEFGH,treesa_greedy,6.881,14.0,19.725,135.419,1.4815486965179332 +circuit_n14_m14_s9_e6_pEFGH,treesa_kahypar,6.881,14.0,19.67,203.684,1.3270058784484888 +circuit_n14_m14_s9_e0_pEFGH,cotengra,7.055,14.0,20.397,44.708,1.4755951805114762 +circuit_n14_m14_s9_e0_pEFGH,treesa_greedy,7.037,14.0,20.166,150.216,1.6235471572875897 +circuit_n14_m14_s9_e0_pEFGH,treesa_kahypar,7.037,14.0,20.224,250.565,1.4627597904205345 +circuit_patch_n12_m14_s19_e18_pEFGH,cotengra,4.6,6.0,12.3,34.107,0.799267166137696 +circuit_patch_n12_m14_s19_e18_pEFGH,treesa_greedy,4.599,6.0,12.293,80.596,0.7942165889739954 +circuit_patch_n12_m14_s19_e18_pEFGH,treesa_kahypar,4.599,6.0,12.293,129.781,0.9455541553497256 +circuit_n12_m14_s9_e6_pEFGH,cotengra,6.24,12.0,17.684,39.775,1.0273772239685073 +circuit_n12_m14_s9_e6_pEFGH,treesa_greedy,6.197,12.0,17.363,118.493,1.0141830749511769 +circuit_n12_m14_s9_e6_pEFGH,treesa_kahypar,6.197,12.0,17.494,182.711,1.0144590988159052 +circuit_n12_m14_s9_e0_pEFGH,cotengra,6.409,12.0,18.255,53.6,1.1409694194793687 +circuit_n12_m14_s9_e0_pEFGH,treesa_greedy,6.387,12.0,18.034,132.08,1.27682678222655 +circuit_n12_m14_s9_e0_pEFGH,treesa_kahypar,6.387,12.0,18.077,214.627,1.1322091560363674 diff --git a/examples/omeinsum_julia/circuit_n12_m14_s0_e0_pEFGH.qsim b/examples/omeinsum_julia/circuit_n12_m14_s0_e0_pEFGH.qsim new file mode 100644 index 00000000..2be8b29b --- /dev/null +++ b/examples/omeinsum_julia/circuit_n12_m14_s0_e0_pEFGH.qsim @@ -0,0 +1,481 @@ +12 +0 hz_1_2 0 +0 hz_1_2 1 +0 hz_1_2 2 +0 y_1_2 3 +0 hz_1_2 4 +0 y_1_2 5 +0 x_1_2 6 +0 x_1_2 7 +0 x_1_2 8 +0 x_1_2 9 +0 y_1_2 10 +0 hz_1_2 11 +1 rz 1 0.8693959871027742 +1 rz 2 -0.5809728937821895 +1 rz 5 -3.1455232066056915 +1 rz 6 3.5262953599473446 +1 rz 9 -1.0402901334038208 +1 rz 10 1.2704822022121596 +2 fs 1 2 1.5862983338115253 0.5200148508319427 +2 fs 5 6 1.5289739216684795 0.5055240639761313 +2 fs 9 10 1.5346175385256955 0.5131039467233695 +3 rz 1 -2.1118243782923773 +3 rz 2 2.4002474716129623 +3 rz 5 2.510370594218442 +3 rz 6 -2.1295984408767894 +3 rz 9 0.15501230573908462 +3 rz 10 0.07517976306925431 +4 y_1_2 0 +4 x_1_2 1 +4 y_1_2 2 +4 hz_1_2 3 +4 y_1_2 4 +4 hz_1_2 5 +4 y_1_2 6 +4 y_1_2 7 +4 y_1_2 8 +4 hz_1_2 9 +4 hz_1_2 10 +4 y_1_2 11 +5 rz 0 7.95878242287787 +5 rz 1 -7.774843741231972 +5 rz 2 -14.076559546984539 +5 rz 3 14.218497333398785 +5 rz 4 6.7102990377713985 +5 rz 5 -6.8557924692300185 +5 rz 6 -11.869086784143517 +5 rz 7 11.992514358506712 +5 rz 8 2.4540157696942893 +5 rz 9 -2.130087599403273 +5 rz 10 5.854589755013336 +5 rz 11 -6.755719773321365 +6 fs 0 1 1.2947043217999283 0.4859467238431821 +6 fs 2 3 1.541977006124425 0.6073798124875975 +6 fs 4 5 1.5138652502397498 0.47710618607286504 +6 fs 6 7 1.5849169442855044 0.54346233613361 +6 fs 8 9 1.5398075246432927 0.5174515645943538 +6 fs 10 11 1.4593314109380113 0.5230636172671492 +7 rz 0 -7.370403665363804 +7 rz 1 7.554342347009701 +7 rz 2 15.859387461711556 +7 rz 3 -15.71744967529731 +7 rz 4 -8.338843337248123 +7 rz 5 8.193349905789502 +7 rz 6 12.26637631755088 +7 rz 7 -12.142948743187686 +7 rz 8 -4.874679037269875 +7 rz 9 5.198607207560892 +7 rz 10 -5.948000038116973 +7 rz 11 5.046870019808944 +8 x_1_2 0 +8 y_1_2 1 +8 x_1_2 2 +8 y_1_2 3 +8 x_1_2 4 +8 x_1_2 5 +8 x_1_2 6 +8 hz_1_2 7 +8 x_1_2 8 +8 x_1_2 9 +8 x_1_2 10 +8 x_1_2 11 +9 rz 4 -10.300317334985465 +9 rz 8 10.489798112305557 +9 rz 5 -16.935559040199575 +9 rz 9 17.018814476659895 +9 rz 6 -17.65620553132214 +9 rz 10 17.688640528946085 +9 rz 7 13.72138435193195 +9 rz 11 -12.285441868370123 +10 fs 4 8 1.589821065740506 0.5045391214115686 +10 fs 5 9 1.5472406430590444 0.5216932173558055 +10 fs 6 10 1.5124128267683938 0.5133142626030278 +10 fs 7 11 1.5707871303628709 0.5176678491729374 +11 rz 4 9.244163795666879 +11 rz 8 -9.054683018346786 +11 rz 5 14.03202226075948 +11 rz 9 -13.94876682429916 +11 rz 6 14.094665893757096 +11 rz 10 -14.062230896133155 +11 rz 7 -15.384431193528544 +11 rz 11 16.82037367709037 +12 hz_1_2 0 +12 hz_1_2 1 +12 hz_1_2 2 +12 hz_1_2 3 +12 hz_1_2 4 +12 hz_1_2 5 +12 y_1_2 6 +12 y_1_2 7 +12 hz_1_2 8 +12 hz_1_2 9 +12 y_1_2 10 +12 hz_1_2 11 +13 rz 0 39.909529911684075 +13 rz 4 -40.171428733227174 +13 rz 1 38.277959915149545 +13 rz 5 -38.040241149679936 +13 rz 2 11.883270567757798 +13 rz 6 -12.169262658895612 +13 rz 3 14.993690269805821 +13 rz 7 -14.989527131529702 +14 fs 0 4 1.4668587973263782 0.4976074601121169 +14 fs 1 5 1.47511091993527 0.538612093835262 +14 fs 2 6 1.603651215218248 0.46649538437100246 +14 fs 3 7 1.6160334279232749 0.4353897326147861 +15 rz 0 -39.198437626672444 +15 rz 4 38.936538805129345 +15 rz 1 -35.53420813044422 +15 rz 5 35.77192689591383 +15 rz 2 -17.246915283138684 +15 rz 6 16.96092319200087 +15 rz 3 -18.421895502761547 +15 rz 7 18.426058641037667 +16 x_1_2 0 +16 x_1_2 1 +16 y_1_2 2 +16 x_1_2 3 +16 x_1_2 4 +16 x_1_2 5 +16 hz_1_2 6 +16 hz_1_2 7 +16 y_1_2 8 +16 x_1_2 9 +16 x_1_2 10 +16 y_1_2 11 +17 rz 1 16.21293450723508 +17 rz 2 -15.924511413914495 +17 rz 5 -14.76941602488768 +17 rz 6 15.150188178229332 +17 rz 9 -11.269315813491886 +17 rz 10 11.499507882300225 +18 fs 1 2 1.5862983338115253 0.5200148508319427 +18 fs 5 6 1.5289739216684795 0.5055240639761313 +18 fs 9 10 1.5346175385256955 0.5131039467233695 +19 rz 1 -17.455362898424685 +19 rz 2 17.743785991745266 +19 rz 5 14.134263412500427 +19 rz 6 -13.753491259158777 +19 rz 9 10.384037985827149 +19 rz 10 -10.15384591701881 +20 hz_1_2 0 +20 hz_1_2 1 +20 x_1_2 2 +20 y_1_2 3 +20 y_1_2 4 +20 y_1_2 5 +20 y_1_2 6 +20 x_1_2 7 +20 hz_1_2 8 +20 y_1_2 9 +20 y_1_2 10 +20 hz_1_2 11 +21 rz 0 23.76727665574203 +21 rz 1 -23.583337974096132 +21 rz 2 -48.01832657636844 +21 rz 3 48.160264362782684 +21 rz 4 22.053837557903705 +21 rz 5 -22.199330989362323 +21 rz 6 -43.48607524987143 +21 rz 7 43.609502824234625 +21 rz 8 9.42835146066348 +21 rz 9 -9.104423290372464 +21 rz 10 17.47848257329532 +21 rz 11 -18.37961259160335 +22 fs 0 1 1.2947043217999283 0.4859467238431821 +22 fs 2 3 1.541977006124425 0.6073798124875975 +22 fs 4 5 1.5138652502397498 0.47710618607286504 +22 fs 6 7 1.5849169442855044 0.54346233613361 +22 fs 8 9 1.5398075246432927 0.5174515645943538 +22 fs 10 11 1.4593314109380113 0.5230636172671492 +23 rz 0 -23.178897898227966 +23 rz 1 23.362836579873864 +23 rz 2 49.801154491095446 +23 rz 3 -49.6592167046812 +23 rz 4 -23.682381857380427 +23 rz 5 23.53688842592181 +23 rz 6 43.88336478327879 +23 rz 7 -43.7599372089156 +23 rz 8 -11.849014728239068 +23 rz 9 12.172942898530083 +23 rz 10 -17.571892856398957 +23 rz 11 16.670762838090926 +24 x_1_2 0 +24 x_1_2 1 +24 hz_1_2 2 +24 x_1_2 3 +24 x_1_2 4 +24 hz_1_2 5 +24 x_1_2 6 +24 y_1_2 7 +24 x_1_2 8 +24 x_1_2 9 +24 hz_1_2 10 +24 x_1_2 11 +25 rz 4 -25.643855855118186 +25 rz 8 25.833336632438275 +25 rz 5 -40.648300389495404 +25 rz 9 40.73155582595572 +25 rz 6 -39.97407974242405 +25 rz 10 40.00651474004799 +25 rz 7 34.64439142483994 +25 rz 11 -33.20844894127811 +26 fs 4 8 1.589821065740506 0.5045391214115686 +26 fs 5 9 1.5472406430590444 0.5216932173558055 +26 fs 6 10 1.5124128267683938 0.5133142626030278 +26 fs 7 11 1.5707871303628709 0.5176678491729374 +27 rz 4 24.587702315799596 +27 rz 8 -24.398221538479504 +27 rz 5 37.744763610055315 +27 rz 9 -37.661508173594996 +27 rz 6 36.412540104859005 +27 rz 10 -36.38010510723507 +27 rz 7 -36.307438266436534 +27 rz 11 37.743380749998366 +28 hz_1_2 0 +28 hz_1_2 1 +28 y_1_2 2 +28 hz_1_2 3 +28 hz_1_2 4 +28 x_1_2 5 +28 y_1_2 6 +28 x_1_2 7 +28 y_1_2 8 +28 hz_1_2 9 +28 x_1_2 10 +28 hz_1_2 11 +29 rz 0 81.75554405750046 +29 rz 4 -82.01744287904356 +29 rz 1 79.65901834823408 +29 rz 5 -79.42129958276446 +29 rz 2 26.296897662428034 +29 rz 6 -26.58288975356585 +29 rz 3 31.732095928132054 +29 rz 7 -31.727932789855927 +30 fs 0 4 1.4668587973263782 0.4976074601121169 +30 fs 1 5 1.47511091993527 0.538612093835262 +30 fs 2 6 1.603651215218248 0.46649538437100246 +30 fs 3 7 1.6160334279232749 0.4353897326147861 +31 rz 0 -81.04445177248883 +31 rz 4 80.78255295094573 +31 rz 1 -76.91526656352876 +31 rz 5 77.15298532899838 +31 rz 2 -31.660542377808927 +31 rz 6 31.37455028667111 +31 rz 3 -35.16030116108778 +31 rz 7 35.16446429936389 +32 x_1_2 0 +32 x_1_2 1 +32 x_1_2 2 +32 y_1_2 3 +32 y_1_2 4 +32 hz_1_2 5 +32 hz_1_2 6 +32 hz_1_2 7 +32 hz_1_2 8 +32 x_1_2 9 +32 hz_1_2 10 +32 y_1_2 11 +33 rz 1 31.556473027367385 +33 rz 2 -31.268049934046807 +33 rz 5 -26.393308843169667 +33 rz 6 26.774080996511316 +33 rz 9 -21.49834149357995 +33 rz 10 21.728533562388286 +34 fs 1 2 1.5862983338115253 0.5200148508319427 +34 fs 5 6 1.5289739216684795 0.5055240639761313 +34 fs 9 10 1.5346175385256955 0.5131039467233695 +35 rz 1 -32.798901418556994 +35 rz 2 33.08732451187758 +35 rz 5 25.75815623078241 +35 rz 6 -25.37738407744076 +35 rz 9 20.613063665915213 +35 rz 10 -20.382871597106877 +36 y_1_2 0 +36 y_1_2 1 +36 y_1_2 2 +36 x_1_2 3 +36 x_1_2 4 +36 y_1_2 5 +36 y_1_2 6 +36 x_1_2 7 +36 x_1_2 8 +36 hz_1_2 9 +36 y_1_2 10 +36 hz_1_2 11 +37 rz 0 39.57577088860619 +37 rz 1 -39.391832206960295 +37 rz 2 -81.96009360575232 +37 rz 3 82.10203139216657 +37 rz 4 37.39737607803601 +37 rz 5 -37.54286950949463 +37 rz 6 -75.10306371559935 +37 rz 7 75.22649128996255 +37 rz 8 16.402687151632673 +37 rz 9 -16.078758981341654 +37 rz 10 29.102375391577308 +37 rz 11 -30.00350540988534 +38 fs 0 1 1.2947043217999283 0.4859467238431821 +38 fs 2 3 1.541977006124425 0.6073798124875975 +38 fs 4 5 1.5138652502397498 0.47710618607286504 +38 fs 6 7 1.5849169442855044 0.54346233613361 +38 fs 8 9 1.5398075246432927 0.5174515645943538 +38 fs 10 11 1.4593314109380113 0.5230636172671492 +39 rz 0 -38.98739213109213 +39 rz 1 39.17133081273803 +39 rz 2 83.74292152047934 +39 rz 3 -83.6009837340651 +39 rz 4 -39.025920377512726 +39 rz 5 38.88042694605411 +39 rz 6 75.50035324900671 +39 rz 7 -75.3769256746435 +39 rz 8 -18.823350419208257 +39 rz 9 19.147278589499276 +39 rz 10 -29.19578567468094 +39 rz 11 28.294655656372914 +40 hz_1_2 0 +40 x_1_2 1 +40 x_1_2 2 +40 hz_1_2 3 +40 hz_1_2 4 +40 x_1_2 5 +40 x_1_2 6 +40 hz_1_2 7 +40 y_1_2 8 +40 y_1_2 9 +40 x_1_2 10 +40 x_1_2 11 +41 rz 4 -40.9873943752509 +41 rz 8 41.176875152571 +41 rz 5 -64.36104173879124 +41 rz 9 64.44429717525156 +41 rz 6 -62.29195395352596 +41 rz 10 62.3243889511499 +41 rz 7 55.56739849774794 +41 rz 11 -54.13145601418609 +42 fs 4 8 1.589821065740506 0.5045391214115686 +42 fs 5 9 1.5472406430590444 0.5216932173558055 +42 fs 6 10 1.5124128267683938 0.5133142626030278 +42 fs 7 11 1.5707871303628709 0.5176678491729374 +43 rz 4 39.931240835932314 +43 rz 8 -39.741760058612215 +43 rz 5 61.45750495935115 +43 rz 9 -61.37424952289083 +43 rz 6 58.730414315960914 +43 rz 10 -58.69797931833698 +43 rz 7 -57.23044533934453 +43 rz 11 58.666387822906366 +44 y_1_2 0 +44 y_1_2 1 +44 y_1_2 2 +44 y_1_2 3 +44 x_1_2 4 +44 y_1_2 5 +44 hz_1_2 6 +44 y_1_2 7 +44 x_1_2 8 +44 hz_1_2 9 +44 y_1_2 10 +44 y_1_2 11 +45 rz 0 123.60155820331686 +45 rz 4 -123.86345702485994 +45 rz 1 121.04007678131862 +45 rz 5 -120.80235801584898 +45 rz 2 40.71052475709828 +45 rz 6 -40.99651684823609 +45 rz 3 48.47050158645827 +45 rz 7 -48.46633844818216 +46 fs 0 4 1.4668587973263782 0.4976074601121169 +46 fs 1 5 1.47511091993527 0.538612093835262 +46 fs 2 6 1.603651215218248 0.46649538437100246 +46 fs 3 7 1.6160334279232749 0.4353897326147861 +47 rz 0 -122.89046591830522 +47 rz 4 122.62856709676213 +47 rz 1 -118.29632499661328 +47 rz 5 118.5340437620829 +47 rz 2 -46.07416947247916 +47 rz 6 45.78817738134135 +47 rz 3 -51.89870681941401 +47 rz 7 51.902869957690115 +48 x_1_2 0 +48 x_1_2 1 +48 hz_1_2 2 +48 x_1_2 3 +48 hz_1_2 4 +48 x_1_2 5 +48 x_1_2 6 +48 x_1_2 7 +48 y_1_2 8 +48 y_1_2 9 +48 hz_1_2 10 +48 x_1_2 11 +49 rz 1 46.900011547499695 +49 rz 2 -46.611588454179106 +49 rz 5 -38.017201661451644 +49 rz 6 38.397973814793296 +49 rz 9 -31.727367173668014 +49 rz 10 31.957559242476353 +50 fs 1 2 1.5862983338115253 0.5200148508319427 +50 fs 5 6 1.5289739216684795 0.5055240639761313 +50 fs 9 10 1.5346175385256955 0.5131039467233695 +51 rz 1 -48.1424399386893 +51 rz 2 48.43086303200989 +51 rz 5 37.3820490490644 +51 rz 6 -37.00127689572275 +51 rz 9 30.842089346003284 +51 rz 10 -30.611897277194945 +52 y_1_2 0 +52 y_1_2 1 +52 y_1_2 2 +52 hz_1_2 3 +52 y_1_2 4 +52 y_1_2 5 +52 hz_1_2 6 +52 hz_1_2 7 +52 x_1_2 8 +52 hz_1_2 9 +52 y_1_2 10 +52 y_1_2 11 +53 rz 0 55.38426512147036 +53 rz 1 -55.20032643982446 +53 rz 2 -115.90186063513623 +53 rz 3 116.04379842155048 +53 rz 4 52.740914598168324 +53 rz 5 -52.88640802962693 +53 rz 6 -106.72005218132728 +53 rz 7 106.84347975569048 +53 rz 8 23.377022842601868 +53 rz 9 -23.05309467231085 +53 rz 10 40.72626820985929 +53 rz 11 -41.627398228167316 +54 fs 0 1 1.2947043217999283 0.4859467238431821 +54 fs 2 3 1.541977006124425 0.6073798124875975 +54 fs 4 5 1.5138652502397498 0.47710618607286504 +54 fs 6 7 1.5849169442855044 0.54346233613361 +54 fs 8 9 1.5398075246432927 0.5174515645943538 +54 fs 10 11 1.4593314109380113 0.5230636172671492 +55 rz 0 -54.795886363956285 +55 rz 1 54.97982504560218 +55 rz 2 117.68468854986324 +55 rz 3 -117.54275076344899 +55 rz 4 -54.36945889764503 +55 rz 5 54.22396546618641 +55 rz 6 107.11734171473461 +55 rz 7 -106.9939141403714 +55 rz 8 -25.797686110177455 +55 rz 9 26.12161428046847 +55 rz 10 -40.81967849296293 +55 rz 11 39.918548474654905 +56 hz_1_2 0 +56 x_1_2 1 +56 x_1_2 2 +56 x_1_2 3 +56 x_1_2 4 +56 hz_1_2 5 +56 x_1_2 6 +56 y_1_2 7 +56 y_1_2 8 +56 y_1_2 9 +56 hz_1_2 10 +56 x_1_2 11 diff --git a/examples/omeinsum_julia/omeinsum.jl b/examples/omeinsum_julia/omeinsum.jl new file mode 100644 index 00000000..627bffaa --- /dev/null +++ b/examples/omeinsum_julia/omeinsum.jl @@ -0,0 +1,87 @@ +import OMEinsum +import ArgParse +import JSON +using KaHyPar + +function parse_commandline() + s = ArgParse.ArgParseSettings() + + @ArgParse.add_arg_table s begin + "--einsum_json" + arg_type = String + default = "einsum.json" + "--result_json" + arg_type = String + default = "opteinsum.json" + "--sc_target" + arg_type = Int + default = 20 + "--beta_start" + arg_type = Float64 + default = 0.01 + "--beta_step" + arg_type = Float64 + default = 0.01 + "--beta_stop" + arg_type = Float64 + default = 15.0 + "--ntrials" + arg_type = Int + default = 10 + "--niters" + arg_type = Int + default = 50 + "--sc_weight" + arg_type = Float64 + default = 1.0 + "--rw_weight" + arg_type = Float64 + default = 0.2 + "--kahypar_init" + action = :store_true + end + + return ArgParse.parse_args(s) +end + +function main() + parsed_args = parse_commandline() + # println("Parsed args:") + # for (arg,val) in parsed_args + # println(" $arg => $val") + # end + # println(Threads.nthreads()) + contraction_args = JSON.parsefile(parsed_args["einsum_json"]) + + inputs = map(Tuple, contraction_args["inputs"]) + output = contraction_args["output"] + + eincode = OMEinsum.EinCode(Tuple(inputs), Tuple(output)) + + size_dict = OMEinsum.uniformsize(eincode, 2) + for (k, v) in contraction_args["size"] + size_dict[k] = v + end + + if parsed_args["kahypar_init"] + eincode = OMEinsum.optimize_code(eincode, size_dict, OMEinsum.KaHyParBipartite( + sc_target=parsed_args["sc_target"], + max_group_size=50)) + end + + algorithm = OMEinsum.TreeSA( + sc_target=parsed_args["sc_target"], + βs=parsed_args["beta_start"]:parsed_args["beta_step"]:parsed_args["beta_stop"], + ntrials=parsed_args["ntrials"], + niters=parsed_args["niters"], + sc_weight=parsed_args["sc_weight"], + rw_weight=parsed_args["rw_weight"], + initializer=parsed_args["kahypar_init"] ? :specified : :greedy + ) + # println(parsed_args["beta_start"]:parsed_args["beta_step"]:parsed_args["beta_stop"]) + # println(algorithm) + optcode = OMEinsum.optimize_code(eincode, size_dict, algorithm) + OMEinsum.writejson(parsed_args["result_json"], optcode) +end + +main() diff --git a/examples/omeinsum_julia/omeinsum_contractor_juliacall.py b/examples/omeinsum_julia/omeinsum_contractor_juliacall.py new file mode 100644 index 00000000..377e871c --- /dev/null +++ b/examples/omeinsum_julia/omeinsum_contractor_juliacall.py @@ -0,0 +1,140 @@ +import os +import json +import time +from typing import List, Set, Dict, Tuple +import tempfile +import warnings +import cotengra as ctg + +# Prerequisites for running this example: +# Step 1: install julia, see https://julialang.org/download/. +# Please install julia >= 1.8.5, the 1.6.7 LTS version raises: +# `Error in python: free(): invalid pointer` +# Step 2: add julia path to the PATH env variable so that juliacall can find it +# Step 3: install juliacall via `pip install juliacall`, this example was tested with juliacall 0.9.9 +# Step 4: install julia package `OMEinsum`, this example was tested with OMEinsum v0.7.2, +# see https://docs.julialang.org/en/v1/stdlib/Pkg/ for more details on julia's package manager +# Step 5: for julia multi-threading, set env variable `PYTHON_JULIACALL_THREADS=`. +# However, in order to use julia multi-threading in juliacall, +# we have to turn off julia GC at the risk of OOM. +# See see https://github.com/cjdoris/PythonCall.jl/issues/219 for more details. +from juliacall import Main as jl + +jl.seval("using OMEinsum") + +from omeinsum_treesa_optimizer import OMEinsumTreeSAOptimizer +import tensorcircuit as tc + +tc.set_backend("tensorflow") + + +class OMEinsumTreeSAOptimizerJuliaCall(OMEinsumTreeSAOptimizer): + def __init__( + self, + sc_target: int = 20, + betas: Tuple[float, float, float] = (0.01, 0.01, 15), + ntrials: int = 10, + niters: int = 50, + sc_weight: float = 1.0, + rw_weight: float = 0.2, + ): + super().__init__(sc_target, betas, ntrials, niters, sc_weight, rw_weight) + + def __call__( + self, + inputs: List[Set[str]], + output: Set[str], + size: Dict[str, int], + memory_limit=None, + ) -> List[Tuple[int, int]]: + inputs_omeinsum = tuple(map(tuple, inputs)) + output_omeinsum = tuple(output) + + eincode = jl.OMEinsum.EinCode(inputs_omeinsum, output_omeinsum) + + size_dict = jl.OMEinsum.uniformsize(eincode, 2) + for k, v in size.items(): + size_dict[k] = v + + algorithm = jl.OMEinsum.TreeSA( + sc_target=self.sc_target, + βs=jl.range(self.betas[0], step=self.betas[1], stop=self.betas[2]), + ntrials=self.ntrials, + niters=self.niters, + sc_weight=self.sc_weight, + rw_weight=self.rw_weight, + ) + + nthreads = jl.Threads.nthreads() + if nthreads > 1: + warnings.warn( + "Julia receives Threads.nthreads()={0}. " + "However, in order to use julia multi-threading in juliacall, " + "we have to turn off julia GC at the risk of OOM. " + "That means you may need a large memory machine. " + "Please see https://github.com/cjdoris/PythonCall.jl/issues/219 " + "for more details.".format(nthreads) + ) + jl.GC.enable(False) + t0 = time.time() + optcode = jl.OMEinsum.optimize_code(eincode, size_dict, algorithm) + running_time = time.time() - t0 + print(f"running_time: {running_time}") + if nthreads > 1: + jl.GC.enable(True) + # jl.println("time and space complexity computed by OMEinsum: ", + # jl.OMEinsum.timespace_complexity(optcode, size_dict)) + + fp = tempfile.NamedTemporaryFile(suffix=".json", delete=False) + fp.close() + jl.OMEinsum.writejson(fp.name, optcode) + with open(fp.name, "r") as f: + contraction_tree = json.load(f) + os.unlink(fp.name) + + num_tensors = len(contraction_tree["inputs"]) + assert num_tensors == len( + inputs + ), "should have the same number of input tensors" + queue = list(range(num_tensors)) + path = [] + self._contraction_tree_to_contraction_path( + contraction_tree["tree"], queue, path, num_tensors + ) + return path + + +if __name__ == "__main__": + # For more random circuits, please refer to + # https://datadryad.org/stash/dataset/doi:10.5061/dryad.k6t1rj8 + c = tc.Circuit.from_qsim_file("circuit_n12_m14_s0_e0_pEFGH.qsim") + + opt = ctg.ReusableHyperOptimizer( + methods=["greedy", "kahypar"], + parallel=True, + minimize="flops", + max_repeats=1024, + progbar=False, + ) + print("cotengra contractor") + tc.set_contractor( + "custom", + optimizer=opt, + preprocessing=True, + contraction_info=True, + debug_level=2, + ) + c.expectation_ps(z=[0], reuse=False) + + print("OMEinsum contractor") + opt_treesa = OMEinsumTreeSAOptimizerJuliaCall( + sc_target=30, sc_weight=0.0, rw_weight=0.0 + ) + tc.set_contractor( + "custom", + optimizer=opt_treesa, + preprocessing=True, + contraction_info=True, + debug_level=2, + ) + c.expectation_ps(z=[0], reuse=False) diff --git a/examples/omeinsum_julia/omeinsum_contractor_subprocess.py b/examples/omeinsum_julia/omeinsum_contractor_subprocess.py new file mode 100644 index 00000000..a204ce63 --- /dev/null +++ b/examples/omeinsum_julia/omeinsum_contractor_subprocess.py @@ -0,0 +1,160 @@ +# Prerequisites for running this example: +# Step 1: install julia, see https://julialang.org/download/. +# Please install julia >= 1.8.5, the 1.6.7 LTS version raises: +# `Error in python: free(): invalid pointer` +# Step 2: add julia path to the PATH env variable so that we can find it +# Step 3: install julia package `OMEinsum`, `ArgParse` and `JSON`, this example was tested with OMEinsum v0.7.2, +# see https://docs.julialang.org/en/v1/stdlib/Pkg/ for more details on julia's package manager + +import os +import sys +import json +import time +from typing import List, Set, Dict, Tuple +import tempfile +import subprocess +import cotengra as ctg +from omeinsum_treesa_optimizer import OMEinsumTreeSAOptimizer +import tensorcircuit as tc + +sys.setrecursionlimit(10000) +tc.set_backend("tensorflow") + + +class OMEinsumTreeSAOptimizerSubprocess(OMEinsumTreeSAOptimizer): + def __init__( + self, + sc_target: int = 20, + betas: Tuple[float, float, float] = (0.01, 0.01, 15), + ntrials: int = 10, + niters: int = 50, + sc_weight: float = 1.0, + rw_weight: float = 0.2, + kahypar_init: bool = False, + ): + super().__init__(sc_target, betas, ntrials, niters, sc_weight, rw_weight) + self.kahypar_init = kahypar_init + + def __call__( + self, + inputs: List[Set[str]], + output: Set[str], + size: Dict[str, int], + memory_limit=None, + ) -> List[Tuple[int, int]]: + inputs_omeinsum = list(map(list, inputs)) + output_omeinsum = list(output) + + fin = tempfile.NamedTemporaryFile(suffix=".json", delete=False) + fout = tempfile.NamedTemporaryFile(suffix=".json", delete=False) + fin.close() + fout.close() + einsum_json = { + "inputs": inputs_omeinsum, + "output": output_omeinsum, + "size": list(size.items()), + } + with open(fin.name, "w") as fp: + json.dump(einsum_json, fp) + + cmd = ( + "julia omeinsum.jl --einsum_json {0} " + "--result_json {1} --sc_target {2} " + "--beta_start {3} --beta_step {4} " + "--beta_stop {5} --ntrials {6} " + "--niters {7} --sc_weight {8} " + "--rw_weight {9}".format( + fin.name, + fout.name, + self.sc_target, + self.betas[0], + self.betas[1], + self.betas[2], + self.ntrials, + self.niters, + self.sc_weight, + self.rw_weight, + ) + ) + + if self.kahypar_init: + cmd += " --kahypar_init" + + print(cmd) + p = subprocess.Popen( + cmd.split(), stdout=subprocess.PIPE, stderr=subprocess.PIPE + ) + t0 = time.time() + _, stderr = p.communicate() + running_time = time.time() - t0 + print(f"running_time: {running_time}") + retcode = p.wait() + if retcode: + os.unlink(fin.name) + os.unlink(fout.name) + raise Exception("julia failed\nstderr:\n%s\n" % stderr.decode("utf-8")) + + with open(fout.name, "r") as f: + contraction_tree = json.load(f) + os.unlink(fin.name) + os.unlink(fout.name) + + num_tensors = len(contraction_tree["inputs"]) + assert num_tensors == len( + inputs + ), "should have the same number of input tensors" + queue = list(range(num_tensors)) + path = [] + self._contraction_tree_to_contraction_path( + contraction_tree["tree"], queue, path, num_tensors + ) + return path + + +if __name__ == "__main__": + # For more random circuits, please refer to + # https://datadryad.org/stash/dataset/doi:10.5061/dryad.k6t1rj8 + c = tc.Circuit.from_qsim_file("circuit_n12_m14_s0_e0_pEFGH.qsim") + + opt = ctg.ReusableHyperOptimizer( + methods=["greedy", "kahypar"], + parallel=True, + minimize="flops", + max_repeats=1024, + progbar=False, + ) + print("cotengra contractor") + tc.set_contractor( + "custom", + optimizer=opt, + preprocessing=True, + contraction_info=True, + debug_level=2, + ) + c.expectation_ps(z=[0], reuse=False) + + print("OMEinsum contractor") + opt_treesa = OMEinsumTreeSAOptimizerSubprocess( + sc_target=30, sc_weight=0.0, rw_weight=0.0 + ) + tc.set_contractor( + "custom", + optimizer=opt_treesa, + preprocessing=True, + contraction_info=True, + debug_level=2, + ) + c.expectation_ps(z=[0], reuse=False) + + print("OMEinsum contractor with kahypar init") + opt_treesa = OMEinsumTreeSAOptimizerSubprocess( + sc_target=30, sc_weight=0.0, rw_weight=0.0, kahypar_init=True + ) + tc.set_contractor( + "custom", + optimizer=opt_treesa, + preprocessing=True, + contraction_info=True, + debug_level=2, + ) + c.expectation_ps(z=[0], reuse=False) diff --git a/examples/omeinsum_julia/omeinsum_treesa_optimizer.py b/examples/omeinsum_julia/omeinsum_treesa_optimizer.py new file mode 100644 index 00000000..5637e2ea --- /dev/null +++ b/examples/omeinsum_julia/omeinsum_treesa_optimizer.py @@ -0,0 +1,50 @@ +from typing import List, Set, Dict, Tuple + + +class OMEinsumTreeSAOptimizer(object): + def __init__( + self, + sc_target: int = 20, + betas: Tuple[float, float, float] = (0.01, 0.01, 15), + ntrials: int = 10, + niters: int = 50, + sc_weight: float = 1.0, + rw_weight: float = 0.2, + ): + self.sc_target = sc_target + self.betas = betas + self.ntrials = ntrials + self.niters = niters + self.sc_weight = sc_weight + self.rw_weight = rw_weight + + def _contraction_tree_to_contraction_path(self, ei, queue, path, idx): + if ei["isleaf"]: + # OMEinsum provide 1-based index + # but in contraction path we want 0-based index + ei["tensorindex"] -= 1 + return idx + assert len(ei["args"]) == 2, "must be a binary tree" + for child in ei["args"]: + idx = self._contraction_tree_to_contraction_path(child, queue, path, idx) + assert "tensorindex" in child + + lhs_args = sorted( + [queue.index(child["tensorindex"]) for child in ei["args"]], reverse=True + ) + for arg in lhs_args: + queue.pop(arg) + + ei["tensorindex"] = idx + path.append(lhs_args) + queue.append(idx) + return idx + 1 + + def __call__( + self, + inputs: List[Set[str]], + output: Set[str], + size: Dict[str, int], + memory_limit=None, + ) -> List[Tuple[int, int]]: + raise NotImplementedError diff --git a/examples/optperformance_comparison.py b/examples/optperformance_comparison.py index 7011210c..4a23c47e 100644 --- a/examples/optperformance_comparison.py +++ b/examples/optperformance_comparison.py @@ -2,6 +2,7 @@ Optimization for performance comparison for different densities of two-qubit gates (random layouts averaged). """ + import sys sys.path.insert(0, "../") @@ -59,7 +60,6 @@ def energy_p(params, p, seed, n, nlayers): if __name__ == "__main__": - n = 12 nlayers = 12 nsteps = 250 diff --git a/examples/parameter_shift.py b/examples/parameter_shift.py index b5fa966f..9ccaceb5 100644 --- a/examples/parameter_shift.py +++ b/examples/parameter_shift.py @@ -2,7 +2,11 @@ Demonstration on the correctness and efficiency of parameter shift gradient implementation """ +import sys import numpy as np + +sys.path.insert(0, "../") + import tensorcircuit as tc from tensorcircuit import experimental as E diff --git a/examples/qaoa_parallel_opt.py b/examples/qaoa_parallel_opt.py index 8ab0734f..fd694ec1 100644 --- a/examples/qaoa_parallel_opt.py +++ b/examples/qaoa_parallel_opt.py @@ -1,3 +1,7 @@ +""" +Depracated, using ``vectorized_value_and_grad`` instead for batched optimization +""" + import sys sys.path.insert(0, "../") diff --git a/examples/qaoa_shot_noise.py b/examples/qaoa_shot_noise.py new file mode 100644 index 00000000..65d93020 --- /dev/null +++ b/examples/qaoa_shot_noise.py @@ -0,0 +1,216 @@ +""" +QAOA with finite measurement shot noise +""" + +from functools import partial +import numpy as np +from scipy import optimize +import networkx as nx +import optax +import cotengra as ctg +import tensorcircuit as tc +from tensorcircuit import experimental as E +from tensorcircuit.applications.graphdata import maxcut_solution_bruteforce + +K = tc.set_backend("jax") +# note this script only supports jax backend + +opt_ctg = ctg.ReusableHyperOptimizer( + methods=["greedy", "kahypar"], + parallel="ray", + minimize="combo", + max_time=10, + max_repeats=128, + progbar=True, +) + +tc.set_contractor("custom", optimizer=opt_ctg, preprocessing=True) + + +def get_graph(n, d, weights=None): + g = nx.random_regular_graph(d, n) + if weights is not None: + i = 0 + for e in g.edges: + g[e[0]][e[1]]["weight"] = weights[i] + i += 1 + return g + + +def get_exact_maxcut_loss(g): + cut, _ = maxcut_solution_bruteforce(g) + totalw = 0 + for e in g.edges: + totalw += g[e[0]][e[1]].get("weight", 1) + loss = totalw - 2 * cut + return loss + + +def get_pauli_string(g): + n = len(g.nodes) + pss = [] + ws = [] + for e in g.edges: + l = [0 for _ in range(n)] + l[e[0]] = 3 + l[e[1]] = 3 + pss.append(l) + ws.append(g[e[0]][e[1]].get("weight", 1)) + return pss, ws + + +def generate_circuit(param, g, n, nlayers): + # construct the circuit ansatz + c = tc.Circuit(n) + for i in range(n): + c.H(i) + for j in range(nlayers): + c = tc.templates.blocks.QAOA_block(c, g, param[j, 0], param[j, 1]) + return c + + +def ps2z(psi): + # ps2xyz([1, 2, 2, 0]) = {"x": [0], "y": [1, 2], "z": []} + zs = [] # no x or y for QUBO problem + for i, j in enumerate(psi): + if j == 3: + zs.append(i) + return zs + + +rkey = K.get_random_state(42) + + +def main_benchmark_suite(n, nlayers, d=3, init=None): + g = get_graph(n, d, weights=np.random.uniform(size=[int(d * n / 2)])) + loss_exact = get_exact_maxcut_loss(g) + print("exact minimal loss by max cut bruteforce: ", loss_exact) + pss, ws = get_pauli_string(g) + if init is None: + init = np.random.normal(scale=0.1, size=[nlayers, 2]) + + @partial(K.jit, static_argnums=(2)) + def exp_val(param, key, shots=10000): + # expectation with shot noise + # ps, w: H = \sum_i w_i ps_i + # describing the system Hamiltonian as a weighted sum of Pauli string + c = generate_circuit(param, g, n, nlayers) + loss = 0 + s = c.state() + mc = tc.quantum.measurement_counts( + s, + counts=shots, + format="sample_bin", + random_generator=key, + jittable=True, + is_prob=False, + ) + for ps, w in zip(pss, ws): + loss += w * tc.quantum.correlation_from_samples(ps2z(ps), mc, c._nqubits) + return K.real(loss) + + @K.jit + def exp_val_analytical(param): + c = generate_circuit(param, g, n, nlayers) + loss = 0 + for ps, w in zip(pss, ws): + loss += w * c.expectation_ps(z=ps2z(ps)) + return K.real(loss) + + # 0. Exact result double check + + hm = tc.quantum.PauliStringSum2COO( + K.convert_to_tensor(pss), K.convert_to_tensor(ws), numpy=True + ) + hm = K.to_dense(hm) + e, _ = np.linalg.eigh(hm) + print("exact minimal loss via eigenstate: ", e[0]) + + # 1.1 QAOA with numerically exact expectation: gradient free + + print("QAOA without shot noise") + + exp_val_analytical_sp = tc.interfaces.scipy_interface( + exp_val_analytical, shape=[nlayers, 2], gradient=False + ) + + r = optimize.minimize( + exp_val_analytical_sp, + init, + method="Nelder-Mead", + options={"maxiter": 5000}, + ) + print(r) + print("double check the value?: ", exp_val_analytical_sp(r["x"])) + # cobyla seems to have issue to given consistent x and cobyla + + # 1.2 QAOA with numerically exact expectation: gradient based + + exponential_decay_scheduler = optax.exponential_decay( + init_value=1e-2, transition_steps=500, decay_rate=0.9 + ) + opt = K.optimizer(optax.adam(exponential_decay_scheduler)) + param = init # zeros stall the gradient + param = tc.array_to_tensor(init, dtype=tc.rdtypestr) + exp_val_grad_analytical = K.jit(K.value_and_grad(exp_val_analytical)) + for i in range(1000): + e, gs = exp_val_grad_analytical(param) + param = opt.update(gs, param) + if i % 100 == 99: + print(e) + print("QAOA energy after gradient descent:", e) + + # 2.1 QAOA with finite shot noise: gradient free + + print("QAOA with shot noise") + + def exp_val_wrapper(param): + global rkey + rkey, skey = K.random_split(rkey) + # maintain stateless randomness in scipy optimize interface + return exp_val(param, skey) + + exp_val_sp = tc.interfaces.scipy_interface( + exp_val_wrapper, shape=[nlayers, 2], gradient=False + ) + + r = optimize.minimize( + exp_val_sp, + init, + method="Nelder-Mead", + options={"maxiter": 5000}, + ) + print(r) + + # the real energy position after optimization + + print("converged as: ", exp_val_analytical_sp(r["x"])) + + # 2.2 QAOA with finite shot noise: gradient based + + exponential_decay_scheduler = optax.exponential_decay( + init_value=1e-2, transition_steps=500, decay_rate=0.9 + ) + opt = K.optimizer(optax.adam(exponential_decay_scheduler)) + param = tc.array_to_tensor(init, dtype=tc.rdtypestr) + exp_grad = E.parameter_shift_grad_v2( + exp_val, argnums=0, random_argnums=1, shifts=(0.001, 0.002) + ) + # parameter shift doesn't directly apply in QAOA case + rkey = K.get_random_state(42) + + for i in range(1000): + rkey, skey = K.random_split(rkey) + gs = exp_grad(param, skey) + param = opt.update(gs, param) + if i % 100 == 99: + rkey, skey = K.random_split(rkey) + print(exp_val(param, skey)) + + # the real energy position after optimization + + print("converged as:", exp_val_analytical(param)) + + +if __name__ == "__main__": + main_benchmark_suite(8, 4) diff --git a/examples/qem_dqas.py b/examples/qem_dqas.py index 44acf8f9..e5f6e8cd 100644 --- a/examples/qem_dqas.py +++ b/examples/qem_dqas.py @@ -1,6 +1,7 @@ """ DQAS for QFT QEM circuit design, deprecated DQAS implementation """ + import sys sys.path.insert(0, "../") diff --git a/examples/readout_mitigation.py b/examples/readout_mitigation.py new file mode 100644 index 00000000..215313ed --- /dev/null +++ b/examples/readout_mitigation.py @@ -0,0 +1,95 @@ +""" +Demonstration on readout error mitigation usage +""" + +import numpy as np + +import tensorcircuit as tc +from tensorcircuit.results.readout_mitigation import ReadoutMit + + +def partial_sample(c, batch, readout_error=None): + measure_index = [] + for inst in c._extra_qir: + if inst["name"] == "measure": + measure_index.append(inst["index"][0]) + if len(measure_index) == 0: + measure_index = list(range(c._nqubits)) + + ct = c.sample( + allow_state=True, + batch=batch, + readout_error=readout_error, + format="count_dict_bin", + ) + return tc.results.counts.marginal_count(ct, measure_index) + + +def simulator(c, shots, logical_physical_mapping=None): + # with readout_error noise + nqubit = c._nqubits + if logical_physical_mapping is None: + logical_physical_mapping = {i: i for i in range(nqubit)} + + gg = [] + for i in range(200): + gg.append(np.sin(i) * 0.02 + 0.978) + # mocked readout error + readout_error = np.reshape(gg[0 : nqubit * 2], (nqubit, 2)) + mapped_readout_error = [[1, 1]] * nqubit + for lq, phyq in logical_physical_mapping.items(): + mapped_readout_error[lq] = readout_error[phyq] + return partial_sample(c, shots, mapped_readout_error) + + +def run(cs, shots): + # customized backend for mitigation test + # a simulator with readout error and qubit mapping supporting batch submission + ts = [] + for c in cs: + count = simulator(c, shots) + ts.append(count) + return ts + + +def apply_readout_mitigation(): + nqubit = 4 + c = tc.Circuit(nqubit) + c.H(0) + c.cnot(0, 1) + c.x(3) + + shots = 10000 + + raw_count = run([c], shots)[0] + + cal_qubits = [0, 1, 2, 3] + use_qubits = [0, 1] + + # idea_value = c.expectation_ps(z=[0,1]) + idea_count = partial_sample(c, batch=shots) + idea_count = tc.results.counts.marginal_count(idea_count, use_qubits) + idea_value = tc.results.counts.expectation(idea_count, z=[0, 1]) + + # use case 1 + mit = ReadoutMit(execute=run) + mit.cals_from_system(cal_qubits, shots=shots, method="local") + mit_count = mit.apply_correction(raw_count, use_qubits, method="inverse") + mit_value1 = tc.results.counts.expectation(mit_count, z=[0, 1]) + + # use case 2: directly mitigation on expectation + mit = ReadoutMit(execute=run) + mit.cals_from_system(cal_qubits, shots=shots, method="local") + mit_value2 = mit.expectation(raw_count, z=[0, 1]) + + # use case 3: global calibriation + mit = ReadoutMit(execute=run) + mit.cals_from_system(cal_qubits, shots=shots, method="global") + mit_value3 = mit.expectation(raw_count, z=[0, 1], method="square") + + print("idea expectation value:", idea_value) + print("mitigated expectation value:", mit_value1, mit_value2, mit_value3) + + +if __name__ == "__main__": + apply_readout_mitigation() diff --git a/examples/rem_super_large_scale.py b/examples/rem_super_large_scale.py new file mode 100644 index 00000000..a10e7290 --- /dev/null +++ b/examples/rem_super_large_scale.py @@ -0,0 +1,54 @@ +""" +Demonstrate the failure of rem when qubit number is much larger than 1/p +""" + +from functools import partial +import numpy as np +import tensorcircuit as tc + + +def simulate_engine(p, ans): + fans = "" + for a in ans: + if p > np.random.uniform(): + if a == "0": + fans += "1" + else: + fans += "0" + else: + fans += a + return fans + + +def run(cs, shots, p=0.1): + # we assume only all 0 and all 1 results for simplicity + rds = [] + for c in cs: + if len(c.to_qir()) < 2: + ans = "0" * c._nqubits + else: + ans = "1" * c._nqubits + rd = {} + for _ in range(shots): + r = simulate_engine(p, ans) + rd[r] = rd.get(r, 0) + 1 + rds.append(rd) + return rds + + +if __name__ == "__main__": + for p in [0.1, 0.05, 0.02]: + print(p) + n = int(3 / p) + c = tc.Circuit(n) + c.x(range(n)) + runp = partial(run, p=p) + r = runp([c], 8192)[0] + mit = tc.results.rem.ReadoutMit(runp) + mit.cals_from_system(n) + for i in range(n): + print(i, "\n", mit.single_qubit_cals[i]) + rs = [] + for i in range(n): + rs.append([i, np.abs(mit.expectation(r, list(range(i))))]) + print(rs[i]) diff --git a/examples/sample_value_gradient.py b/examples/sample_value_gradient.py new file mode 100644 index 00000000..b3726b30 --- /dev/null +++ b/examples/sample_value_gradient.py @@ -0,0 +1,109 @@ +""" +Evaluate expectation and gradient on Pauli string sum with finite measurement shots +""" + +from functools import partial +import numpy as np +import tensorcircuit as tc +from tensorcircuit import experimental as E + +K = tc.set_backend("jax") +# note this script only supports jax backend + +n = 5 +nlayers = 4 +ps = [] +for i in range(n): + l = [0 for _ in range(n)] + l[i] = 1 + ps.append(l) +for i in range(n - 1): + l = [0 for _ in range(n)] + l[i] = 3 + l[i + 1] = 3 + ps.append(l) + + +w = [-1.0 for _ in range(n)] + [1.0 for _ in range(n - 1)] + + +def generate_circuit(param): + c = tc.Circuit(n) + for i in range(n): + c.H(i) + for j in range(nlayers): + for i in range(n - 1): + c.rzz(i, i + 1, theta=param[i, j, 0]) + for i in range(n): + c.rx(i, theta=param[i, j, 1]) + return c + + +def sample_exp(c, ps, w, shots, key): + if isinstance(shots, int): + shots = [shots for _ in range(len(ps))] + loss = 0 + for psi, wi, shot in zip(ps, w, shots): + key, subkey = K.random_split(key) + xyz = {"x": [], "y": [], "z": []} + for i, j in enumerate(psi): + if j == 1: + xyz["x"].append(i) + if j == 2: + xyz["y"].append(i) + if j == 3: + xyz["z"].append(i) + loss += wi * c.sample_expectation_ps(**xyz, shots=shot, random_generator=subkey) + return loss + + +@K.jit +def exp_val_analytical(param): + c = generate_circuit(param) + loss = 0 + for psi, wi in zip(ps, w): + xyz = {"x": [], "y": [], "z": []} + for i, j in enumerate(psi): + if j == 1: + xyz["x"].append(i) + if j == 2: + xyz["y"].append(i) + if j == 3: + xyz["z"].append(i) + loss += wi * c.expectation_ps(**xyz) + return K.real(loss) + + +@partial(K.jit, static_argnums=2) +def exp_val(param, key, shots=4096): + # from circuit parameter to expectation + c = generate_circuit(param) + return sample_exp(c, ps, w, shots, key=key) + + +print("benchmarking sample expectation") +tc.utils.benchmark( + exp_val, K.ones([n, nlayers, 2], dtype="float32"), K.get_random_state(42) +) +print("benchmarking analytical expectation") +tc.utils.benchmark(exp_val_analytical, K.ones([n, nlayers, 2], dtype="float32")) +r1 = exp_val(K.ones([n, nlayers, 2], dtype="float32"), K.get_random_state(42)) +r2 = exp_val_analytical(K.ones([n, nlayers, 2], dtype="float32")) +np.testing.assert_allclose(r1, r2, atol=0.05, rtol=0.01) +print("correctness check passed for expectation value") +gradf1 = E.parameter_shift_grad_v2(exp_val, argnums=0, random_argnums=1) +gradf2 = K.jit(K.grad(exp_val_analytical)) +print("benchmarking sample gradient") +tc.utils.benchmark( + gradf1, K.ones([n, nlayers, 2], dtype="float32"), K.get_random_state(42) +) +# n=12, nlayers=4, 276s + 0.75s, mac CPU +print("benchmarking analytical gradient") +tc.utils.benchmark(gradf2, K.ones([n, nlayers, 2], dtype="float32")) +r1 = gradf1(K.ones([n, nlayers, 2], dtype="float32"), K.get_random_state(42)) +r2 = gradf2(K.ones([n, nlayers, 2], dtype="float32")) +print("gradient with measurement shot and parameter shift") +print(r1) +print(r2) +np.testing.assert_allclose(r1, r2, atol=0.2, rtol=0.01) +print("correctness check passed for gradients") diff --git a/examples/shvqe.py b/examples/shvqe.py new file mode 100644 index 00000000..4083fa6d --- /dev/null +++ b/examples/shvqe.py @@ -0,0 +1,246 @@ +""" +Schrodinger-Heisenberg quantum variational eigensolver (SHVQE) with DQAS-style optimization. + +DQAS part is modified from: examples/clifford_optimization.py +""" + +import sys + +sys.path.insert(0, "../") + +import numpy as np +import tensorflow as tf + +import tensorcircuit as tc +from tensorcircuit.applications.vqes import construct_matrix_v3 + +ctype, rtype = tc.set_dtype("complex64") +K = tc.set_backend("tensorflow") + +n = 10 # the number of qubits (must be even for consistency later) +ncz = 2 # number of cz layers in Schrodinger circuit +nlayersq = ncz + 1 # Schrodinger parameter layers + +# training setup +epochs = 1000 +batch = 1000 + +# Hamiltonian +h6h = np.load("./h6_hamiltonian.npy") # reported in 0.99 A +hamiltonian = construct_matrix_v3(h6h.tolist()) + + +def hybrid_ansatz(structure, paramq, preprocess="direct", train=True): + """_summary_ + + Parameters + ---------- + structure : K.Tensor, (n//2, 2) + parameters to decide graph structure of Clifford circuits + paramq : K.Tensor, (nlayersq, n, 3) + parameters in quantum variational circuits, the last layer for Heisenberg circuits + preprocess : str, optional + preprocess, by default "direct" + + Returns + ------- + K.Tensor, [1,] + loss value + """ + c = tc.Circuit(n) + if preprocess == "softmax": + structure = K.softmax(structure, axis=-1) + elif preprocess == "most": + structure = K.onehot(K.argmax(structure, axis=-1), num=2) + elif preprocess == "direct": + pass + + structure = K.cast(structure, ctype) + structure = tf.reshape(structure, shape=[n // 2, 2]) + + # quantum variational in Schrodinger part, first consider a ring topol + for j in range(nlayersq): + if j != 0 and j != nlayersq - 1: + for i in range(j % 2, n, 2): + c.cz(i, (i + 1) % n) + for i in range(n): + c.rx(i, theta=paramq[j, i, 0]) + c.ry(i, theta=paramq[j, i, 1]) + c.rz(i, theta=paramq[j, i, 2]) + + # Clifford part, which is actually virtual + if train: + for j in range(0, n // 2 - 1): + dis = j + 1 + for i in range(0, n): + c.unitary( + i, + (i + dis) % n, + unitary=structure[j, 0] * tc.gates.ii().tensor + + structure[j, 1] * tc.gates.cz().tensor, + ) + + for i in range(0, n // 2): + c.unitary( + i, + i + n // 2, + unitary=structure[n // 2 - 1, 0] * tc.gates.ii().tensor + + structure[n // 2 - 1, 1] * tc.gates.cz().tensor, + ) + else: # if not for training, we just put nontrivial gates + for j in range(0, n // 2 - 1): + dis = j + 1 + for i in range(0, n): + if structure[j, 1] == 1: + c.cz(i, (i + dis) % n) + + for i in range(0, n // 2): + if structure[j, 1] == 1: + c.cz(i, i + n // 2) + + return c + + +def hybrid_vqe(structure, paramq, preprocess="direct"): + """_summary_ + + Parameters + ---------- + structure : K.Tensor, (n//2, 2) + parameters to decide graph structure of Clifford circuits + paramq : K.Tensor, (nlayersq, n, 3) + parameters in quantum variational circuits, the last layer for Heisenberg circuits + preprocess : str, optional + preprocess, by default "direct" + + Returns + ------- + K.Tensor, [1,] + loss value + """ + c = hybrid_ansatz(structure, paramq, preprocess) + return tc.templates.measurements.operator_expectation(c, hamiltonian) + + +def sampling_from_structure(structures, batch=1): + ch = structures.shape[-1] + prob = K.softmax(K.real(structures), axis=-1) + prob = K.reshape(prob, [-1, ch]) + p = prob.shape[0] + r = np.stack( + np.array( + [np.random.choice(ch, p=K.numpy(prob[i]), size=[batch]) for i in range(p)] + ) + ) + return r.transpose() + + +@K.jit +def best_from_structure(structures): + return K.argmax(structures, axis=-1) + + +def nmf_gradient(structures, oh): + """compute the Monte Carlo gradient with respect of naive mean-field probabilistic model + + Parameters + ---------- + structures : K.Tensor, (n//2, ch) + structure parameter for single- or two-qubit gates + oh : K.Tensor, (n//2, ch), onehot + a given structure sampled via strcuture parameters (in main function) + + Returns + ------- + K.Tensor, (n//2 * 2, ch) == (n, ch) + MC gradients + """ + choice = K.argmax(oh, axis=-1) + prob = K.softmax(K.real(structures), axis=-1) + indices = K.transpose( + K.stack([K.cast(tf.range(structures.shape[0]), "int64"), choice]) + ) + prob = tf.gather_nd(prob, indices) + prob = K.reshape(prob, [-1, 1]) + prob = K.tile(prob, [1, structures.shape[-1]]) + + return K.real( + tf.tensor_scatter_nd_add( + tf.cast(-prob, dtype=ctype), + indices, + tf.ones([structures.shape[0]], dtype=ctype), + ) + ) # in oh : 1-p, not in oh : -p + + +# vmap for a batch of structures +nmf_gradient_vmap = K.jit(K.vmap(nmf_gradient, vectorized_argnums=1)) + +# vvag for a batch of structures +vvag_hybrid = K.jit( + K.vectorized_value_and_grad(hybrid_vqe, vectorized_argnums=(0,), argnums=(1,)), + static_argnums=(2,), +) + + +def train_hybrid( + stddev=0.05, lr=None, epochs=2000, debug_step=50, batch=256, verbose=False +): + # params = K.implicit_randn([n//2, 2], stddev=stddev) + params = K.ones([n // 2, 2], dtype=float) + paramq = K.implicit_randn([nlayersq, n, 3], stddev=stddev) * 2 * np.pi + if lr is None: + lr = tf.keras.optimizers.schedules.ExponentialDecay(0.6, 100, 0.8) + structure_opt = K.optimizer(tf.keras.optimizers.Adam(lr)) + + avcost = 0 + avcost2 = 0 + loss_history = [] + for epoch in range(epochs): # iteration to update strcuture param + # random sample some structures + batched_stucture = K.onehot( + sampling_from_structure(params, batch=batch), + num=params.shape[-1], + ) + vs, gq = vvag_hybrid(batched_stucture, paramq, "direct") + loss_history.append(np.min(vs)) + gq = gq[0] + avcost = K.mean(vs) # average cost of the batch + gs = nmf_gradient_vmap(params, batched_stucture) # \nabla lnp + gs = K.mean(K.reshape(vs - avcost2, [-1, 1, 1]) * gs, axis=0) + # avcost2 is averaged cost in the last epoch + avcost2 = avcost + + [params, paramq] = structure_opt.update([gs, gq], [params, paramq]) + if epoch % debug_step == 0 or epoch == epochs - 1: + print("----------epoch %s-----------" % epoch) + print( + "batched average loss: ", + np.mean(vs), + "minimum candidate loss: ", + np.min(vs), + ) + + # max over choices, min over layers and qubits + minp = tf.math.reduce_min( + tf.math.reduce_max(tf.math.softmax(params), axis=-1) + ) + if minp > 0.5: + print("probability converged") + + if verbose: + print("strcuture parameter: \n", params.numpy()) + + cand_preset = best_from_structure(params) + print(cand_preset) + print("current recommendation loss: ", hybrid_vqe(params, paramq, "most")) + + loss_history = np.array(loss_history) + return hybrid_vqe(params, paramq, "most"), params, paramq, loss_history + + +print("Train hybrid.") +ee, params, paramq, loss_history = train_hybrid( + epochs=epochs, batch=batch, verbose=True +) +print("Energy:", ee) diff --git a/examples/simple_qaoa.py b/examples/simple_qaoa.py index 133b7d6e..a1fbfaa0 100644 --- a/examples/simple_qaoa.py +++ b/examples/simple_qaoa.py @@ -1,6 +1,7 @@ """ A plain QAOA optimization example with given graphs using networkx. """ + import sys sys.path.insert(0, "../") diff --git a/examples/slicing_wavefunction_vqa.py b/examples/slicing_wavefunction_vqa.py new file mode 100644 index 00000000..b31a2184 --- /dev/null +++ b/examples/slicing_wavefunction_vqa.py @@ -0,0 +1,199 @@ +""" +slicing the output wavefunction to save the memory in VQA context +""" + +from itertools import product +import numpy as np +import tensorcircuit as tc + +K = tc.set_backend("jax") + + +def circuit(param, n, nlayers): + c = tc.Circuit(n) + for i in range(n): + c.h(i) + c = tc.templates.blocks.example_block(c, param, nlayers) + return c + + +def sliced_state(c, cut, mask): + # mask = Tensor([0, 1, 0]) + # cut = [0, 1, 2] + n = c._nqubits + ncut = len(cut) + end0 = tc.array_to_tensor(np.array([1.0, 0.0])) + end1 = tc.array_to_tensor(np.array([0.0, 1.0])) + ends = [tc.Gate(mask[i] * end1 + (1 - mask[i]) * end0) for i in range(ncut)] + nodes, front = c._copy() + for j, i in enumerate(cut): + front[i] ^ ends[j][0] + oeo = [] + for i in range(n): + if i not in cut: + oeo.append(front[i]) + ss = tc.contractor(nodes + ends, output_edge_order=oeo) + return ss + + +def sliced_op(ps, cut, mask1, mask2): + # ps: Tensor([0, 0, 1, 1]) + n = K.shape_tuple(ps)[-1] + ncut = len(cut) + end0 = tc.array_to_tensor(np.array([1.0, 0.0])) + end1 = tc.array_to_tensor(np.array([0.0, 1.0])) + endsr = [tc.Gate(mask1[i] * end1 + (1 - mask1[i]) * end0) for i in range(ncut)] + endsl = [tc.Gate(mask2[i] * end1 + (1 - mask2[i]) * end0) for i in range(ncut)] + + structuresc = K.cast(ps, dtype="int32") + structuresc = K.onehot(structuresc, num=4) + structuresc = K.cast(structuresc, dtype=tc.dtypestr) + obs = [] + for i in range(n): + obs.append( + tc.Gate( + sum( + [ + structuresc[i, k] * g.tensor + for k, g in enumerate(tc.gates.pauli_gates) + ] + ) + ) + ) + for j, i in enumerate(cut): + obs[i][0] ^ endsl[j][0] + obs[i][1] ^ endsr[j][0] + oeo = [] + for i in range(n): + if i not in cut: + oeo.append(obs[i][0]) + for i in range(n): + if i not in cut: + oeo.append(obs[i][1]) + return obs + endsl + endsr, oeo + + +def sliced_core(param, n, nlayers, ps, cut, mask1, mask2): + # param, ps, mask1, mask2 are all tensor + c = circuit(param, n, nlayers) + ss = sliced_state(c, cut, mask1) + ssc = sliced_state(c, cut, mask2) + ssc, _ = tc.Circuit.copy([ssc], conj=True) + op_nodes, op_edges = sliced_op(ps, cut, mask1, mask2) + nodes = [ss] + ssc + op_nodes + ssc = ssc[0] + n = c._nqubits + nleft = n - len(cut) + for i in range(nleft): + op_edges[i + nleft] ^ ss[i] + op_edges[i] ^ ssc[i] + scalar = tc.contractor(nodes) + return K.real(scalar.tensor) + + +sliced_core_vvg = K.jit( + K.vectorized_value_and_grad(sliced_core, argnums=0, vectorized_argnums=(5, 6)), + static_argnums=(1, 2, 4), +) # vmap version if memory is enough + +sliced_core_vg = K.jit( + K.value_and_grad(sliced_core, argnums=0), + static_argnums=(1, 2, 4), +) # nonvmap version is memory is tight and distrubution workload may be enabled + + +def sliced_expectation_and_grad(param, n, nlayers, ps, cut, is_vmap=True): + pst = tc.array_to_tensor(ps) + res = 0.0 + mask1s = [] + mask2s = [] + for mask1 in product(*[(0, 1) for _ in cut]): + mask1t = tc.array_to_tensor(np.array(mask1)) + mask1s.append(mask1t) + mask2 = list(mask1) + for j, i in enumerate(cut): + if ps[i] in [1, 2]: + mask2[j] = 1 - mask1[j] + mask2t = tc.array_to_tensor(np.array(mask2)) + mask2s.append(mask2t) + if is_vmap: + mask1s = K.stack(mask1s) + mask2s = K.stack(mask2s) + res = sliced_core_vvg(param, n, nlayers, pst, cut, mask1s, mask2s) + res = list(res) + res[0] = K.sum(res[0]) + res = tuple(res) + else: + # memory bounded + # can modified to adpative pmap + vs = 0.0 + gs = 0.0 + for i in range(len(mask1s)): + mask1t = mask1s[i] + mask2t = mask2s[i] + v, g = sliced_core_vg(param, n, nlayers, pst, cut, mask1t, mask2t) + vs += v + gs += g + res = (vs, gs) + return res + + +def sliced_expectation_ref(c, ps, cut): + """ + reference implementation + """ + # ps: [0, 2, 1] + res = 0.0 + for mask1 in product(*[(0, 1) for _ in cut]): + mask1t = tc.array_to_tensor(np.array(mask1)) + ss = sliced_state(c, cut, mask1t) + mask2 = list(mask1) + for j, i in enumerate(cut): + if ps[i] in [1, 2]: + mask2[j] = 1 - mask1[j] + mask2t = tc.array_to_tensor(np.array(mask2)) + ssc = sliced_state(c, cut, mask2t) + ssc, _ = tc.Circuit.copy([ssc], conj=True) + ps = tc.array_to_tensor(ps) + op_nodes, op_edges = sliced_op(ps, cut, mask1t, mask2t) + nodes = [ss] + ssc + op_nodes + ssc = ssc[0] + n = c._nqubits + nleft = n - len(cut) + for i in range(nleft): + op_edges[i + nleft] ^ ss[i] + op_edges[i] ^ ssc[i] + scalar = tc.contractor(nodes) + res += scalar.tensor + return res + + +if __name__ == "__main__": + n = 10 + nlayers = 5 + param = K.ones([n, 2 * nlayers], dtype="float32") + cut = (0, 2, 5, 9) + ops = [2, 0, 3, 1, 0, 0, 1, 2, 0, 1] + ops_dict = tc.quantum.ps2xyz(ops) + + def trivial_core(param, n, nlayers): + c = circuit(param, n, nlayers) + return K.real(c.expectation_ps(**ops_dict)) + + trivial_vg = K.jit(K.value_and_grad(trivial_core, argnums=0), static_argnums=(1, 2)) + + print("reference impl") + r0 = tc.utils.benchmark(trivial_vg, param, n, nlayers) + print("vmapped slice") + r1 = tc.utils.benchmark( + sliced_expectation_and_grad, param, n, nlayers, ops, cut, True + ) + print("naive for slice") + r2 = tc.utils.benchmark( + sliced_expectation_and_grad, param, n, nlayers, ops, cut, False + ) + + np.testing.assert_allclose(r0[0][0], r1[0][0], atol=1e-5) + np.testing.assert_allclose(r2[0][0], r1[0][0], atol=1e-5) + np.testing.assert_allclose(r0[0][1], r1[0][1], atol=1e-5) + np.testing.assert_allclose(r2[0][1], r1[0][1], atol=1e-5) diff --git a/examples/stabilizer_simulation.py b/examples/stabilizer_simulation.py new file mode 100644 index 00000000..699983bf --- /dev/null +++ b/examples/stabilizer_simulation.py @@ -0,0 +1,151 @@ +import numpy as np +import stim + +import tensorcircuit as tc + +np.random.seed(0) + +tc.set_dtype("complex128") + +clifford_one_qubit_gates = ["H", "X", "Y", "Z", "S"] +clifford_two_qubit_gates = ["CNOT"] +clifford_gates = clifford_one_qubit_gates + clifford_two_qubit_gates + + +def genpair(num_qubits, count): + choice = list(range(num_qubits)) + for _ in range(count): + np.random.shuffle(choice) + x, y = choice[:2] + yield (x, y) + + +def random_clifford_circuit_with_mid_measurement(num_qubits, depth): + c = tc.Circuit(num_qubits) + operation_list = [] + for _ in range(depth): + for j, k in genpair(num_qubits, 2): + c.cnot(j, k) + operation_list.append(("CNOT", (j, k))) + for j in range(num_qubits): + gate_name = np.random.choice(clifford_one_qubit_gates) + getattr(c, gate_name)(j) + operation_list.append((gate_name, (j,))) + if np.random.uniform() < 0.2: + measured_qubit = np.random.randint(0, num_qubits - 1) + sample, p = c.measure_reference(measured_qubit, with_prob=True) + # Check if there is a non-zero probability to measure "0" for post-selection + if (sample == "0" and not np.isclose(p, 0.0)) or ( + sample == "1" and not np.isclose(p, 1.0) + ): + c.mid_measurement(measured_qubit, keep=0) + operation_list.append(("M", (measured_qubit,))) + return c, operation_list + + +def convert_operation_list_to_stim_circuit(operation_list): + stim_circuit = stim.Circuit() + for instruction in operation_list: + gate_name = instruction[0] + qubits = instruction[1] + stim_circuit.append(gate_name, qubits) + return stim_circuit + + +# ref: https://quantumcomputing.stackexchange.com/questions/16718/measuring-entanglement-entropy-using-a-stabilizer-circuit-simulator +def get_binary_matrix(z_stabilizers): + N = len(z_stabilizers) + binary_matrix = np.zeros((N, 2 * N)) + for row_idx, row in enumerate(z_stabilizers): + for col_idx, col in enumerate(row): + if col == 3: # Pauli Z + binary_matrix[row_idx, N + col_idx] = 1 + if col == 2: # Pauli Y + binary_matrix[row_idx, N + col_idx] = 1 + binary_matrix[row_idx, col_idx] = 1 + if col == 1: # Pauli X + binary_matrix[row_idx, col_idx] = 1 + return binary_matrix + + +def get_cut_binary_matrix(binary_matrix, cut): + N = len(binary_matrix) + new_indices = [i for i in range(N) if i in set(cut)] + [ + i + N for i in range(N) if i in set(cut) + ] + return binary_matrix[:, new_indices] + + +# ref: https://gist.github.com/StuartGordonReid/eb59113cb29e529b8105?permalink_comment_id=3268301#gistcomment-3268301 +def gf2_rank(matrix): + n = len(matrix[0]) + rank = 0 + for col in range(n): + j = 0 + rows = [] + while j < len(matrix): + if matrix[j][col] == 1: + rows += [j] + j += 1 + if len(rows) >= 1: + for c in range(1, len(rows)): + for k in range(n): + matrix[rows[c]][k] = (matrix[rows[c]][k] + matrix[rows[0]][k]) % 2 + matrix.pop(rows[0]) + rank += 1 + for row in matrix: + if sum(row) > 0: + rank += 1 + return rank + + +# ref: https://quantumcomputing.stackexchange.com/questions/27795/exact-probabilities-of-outcomes-for-clifford-circuits-with-mid-circuit-measureme +def simulate_stim_circuit_with_mid_measurement(stim_circuit): + simulator = stim.TableauSimulator() + + for instruction in stim_circuit.flattened(): + if instruction.name == "M": + for t in instruction.targets_copy(): + expectaction_value = simulator.peek_z(t.value) # 1, 0, -1 + # there is a non-zero probability to measure "0" if expectaction_value is not -1 + if expectaction_value != -1: + simulator.postselect_z(t.value, desired_value=0) + else: + simulator.do(instruction) + + return simulator.current_inverse_tableau() ** -1 + + +if __name__ == "__main__": + # Number of qubits + num_qubits = 12 + # Depth of the circuit + depth = 24 + # index list that is traced out to calculate the entanglement entropy + cut = [i for i in range(num_qubits // 3)] + + tc_circuit, op_list = random_clifford_circuit_with_mid_measurement( + num_qubits, depth + ) + print(tc_circuit.draw(output="text")) + + stim_circuit = convert_operation_list_to_stim_circuit(op_list) + + # Entanglement entropy calculation using stabilizer formalism + stabilizer_tableau = simulate_stim_circuit_with_mid_measurement(stim_circuit) + zs = [stabilizer_tableau.z_output(k) for k in range(len(stabilizer_tableau))] + binary_matrix = get_binary_matrix(zs) + cur_matrix = get_cut_binary_matrix(binary_matrix, cut) + stim_entropy = (gf2_rank(cur_matrix.tolist()) - len(cut)) * np.log(2) + print("Stim Entanglement Entropy:", stim_entropy) + + # Entanglement entropy calculation using TensorCircuit + state_vector = tc_circuit.wavefunction() + assert np.linalg.norm(state_vector) > 0 + # Normalize the state vector because mid-measurement operation is not unitary + state_vector /= np.linalg.norm(state_vector) + tc_entropy = tc.quantum.entanglement_entropy(state_vector, cut) + print("TensorCircuit Entanglement Entropy:", tc_entropy) + + # Check if the entanglement entropies are close + np.testing.assert_allclose(stim_entropy, tc_entropy, atol=1e-8) diff --git a/examples/tcgates.inc b/examples/tcgates.inc index 86a971dd..0f3e0f57 100644 --- a/examples/tcgates.inc +++ b/examples/tcgates.inc @@ -88,7 +88,8 @@ gate fredkin a, b, c {cswap a, b, c; } gate cnot a, b {cx a, b; } gate sd a, b {sdg a, b; } gate td a, b {tdg a, b; } -gate phase(λ) a { ctrl @ gphase(λ) a; } +// gate phase(λ) a { ctrl @ gphase(λ) a; } +// double check whether phase and p is consistent // Neg control gates @@ -111,3 +112,5 @@ gate iswap(θ) a, b { cx a, b; h a; cx b, a; phase(θ*π/2) a; cx b, a; phase(- // wroot gate wroot a { U(π/2, -π/4, π/4) a; } + +// qiskit ref: https://github.com/Qiskit/qiskit-terra/blob/main/qiskit/qasm/libs/qelib1.inc diff --git a/examples/time_evolution.py b/examples/time_evolution.py new file mode 100644 index 00000000..c1d0e797 --- /dev/null +++ b/examples/time_evolution.py @@ -0,0 +1,71 @@ +""" +A simple static Hamiltonian evolution benchmark +""" + +import time +from functools import partial +import numpy as np +from scipy.integrate import solve_ivp +import tensorcircuit as tc +from tensorcircuit.experimental import hamiltonian_evol + +K = tc.set_backend("jax") +tc.set_dtype("complex128") + + +@partial(K.jit, static_argnums=1) +def total_z(psi, N): + return K.real( + K.sum(K.stack([tc.expectation([tc.gates.z(), i], ket=psi) for i in range(N)])) + ) + + +@K.jit +def naive_evol(t, h, psi0): + return K.reshape(K.expm(-1j * t * h) @ K.reshape(psi0, [-1, 1]), [-1]) + + +@K.jit +def hpsi(h, y): + return K.reshape(-1.0j * h @ K.reshape(y, [-1, 1]), [-1]) + + +def main(N): + psi0 = np.zeros([2**N]) + psi0[0] = 1 + psi0 = tc.array_to_tensor(psi0) + g = tc.templates.graphs.Line1D(N, pbc=False) + h = tc.quantum.heisenberg_hamiltonian(g, hzz=1, hxx=0, hyy=0, hx=1, sparse=False) + tlist = K.arange(0, 3, 0.1) + time0 = time.time() + for t in tlist: + psit = naive_evol(t, h, psi0) + psit /= K.norm(psit) + print(total_z(psit, N)) + time1 = time.time() + r = hamiltonian_evol(1.0j * tlist, h, psi0, callback=partial(total_z, N=N)) + print(r) + time2 = time.time() + + def fun(t, y): + y = tc.array_to_tensor(y) + return K.numpy(hpsi(h, y)) + + r = solve_ivp( + fun, (0, 3), psi0, method="DOP853", t_eval=K.numpy(tlist), rtol=1e-6, atol=1e-6 + ) + for psit in r.y.T: + print(total_z(psit, N)) + time3 = time.time() + print( + "matrix exponential:", + time1 - time0, + "tc fast implementation", + time2 - time1, + "scipy ode", + time3 - time2, + ) + + +if __name__ == "__main__": + main(10) diff --git a/examples/timeevolution_trotter.py b/examples/timeevolution_trotter.py new file mode 100644 index 00000000..91ea7f0f --- /dev/null +++ b/examples/timeevolution_trotter.py @@ -0,0 +1,47 @@ +""" +Time evolution of Heisenberg model realized by Trotter decomposition +""" + +import numpy as np +import tensorcircuit as tc + +K = tc.set_backend("tensorflow") +tc.set_dtype("complex128") + +xx = tc.gates._xx_matrix +yy = tc.gates._yy_matrix +zz = tc.gates._zz_matrix + +nqubit = 4 +t = 1.0 +tau = 0.1 + + +def Trotter_step_unitary(input_state, tau, nqubit): + c = tc.Circuit(nqubit, inputs=input_state) + for i in range(nqubit - 1): ### U_zz + c.exp1(i, i + 1, theta=tau, unitary=zz) + for i in range(nqubit - 1): ### U_yy + c.exp1(i, i + 1, theta=tau, unitary=yy) + for i in range(nqubit - 1): ### U_xx + c.exp1(i, i + 1, theta=tau, unitary=xx) + TSUstate = c.state() ### return state U(τ)|ψ_i> + z0 = c.expectation_ps(z=[0]) + return TSUstate, z0 + + +TSU_vmap = tc.backend.jit( + tc.backend.vmap( + Trotter_step_unitary, + vectorized_argnums=0, + ) +) + +ninput = 2 +input_state = np.zeros((ninput, 2**nqubit)) +input_state[0, 0] = 1.0 +input_state[1, -1] = 1.0 + +for _ in range(int(t / tau)): + input_state, z0 = TSU_vmap(input_state, tau, nqubit) + print("z: ", z0) diff --git a/examples/training_deep_tunable_structures.py b/examples/training_deep_tunable_structures.py new file mode 100644 index 00000000..9f084870 --- /dev/null +++ b/examples/training_deep_tunable_structures.py @@ -0,0 +1,172 @@ +""" +An integrated script demonstrating: +1. shortcut setup of cotengra contractor (with correct interplay with multiprocessing); +2. jit scan acceleration for deep structured circuit with multiple variables; +3. tensor controlled tunable circuit structures all in one jit; +4. batched trainable parameters via vmap/vvag; +and yet anonther demonstration of infras for training with incremental random activation +""" + +import time +import numpy as np +import jax +import optax +import tensorcircuit as tc + + +def main(): + tc.set_contractor("cotengra-40-64") + K = tc.set_backend("jax") + tc.set_dtype("complex128") + + ii = tc.gates._ii_matrix + xx = tc.gates._xx_matrix + yy = tc.gates._yy_matrix + zz = tc.gates._zz_matrix + + n = 12 + nlayers = 7 + g = tc.templates.graphs.Line1D(n) + ncircuits = 10 + heih = tc.quantum.heisenberg_hamiltonian( + g, hzz=1.0, hyy=1.0, hxx=1.0, hx=0, hy=0, hz=0 + ) + + def energy(params, structures, n, nlayers): + def one_layer(state, others): + params, structures = others + # print(state.shape, params.shape, structures.shape) + l = 0 + c = tc.Circuit(n, inputs=state) + for i in range(1, n, 2): + matrix = structures[3 * l, i] * ii + (1.0 - structures[3 * l, i]) * ( + K.cos(params[3 * l, i]) * ii + 1.0j * K.sin(params[3 * l, i]) * zz + ) + c.any( + i, + (i + 1) % n, + unitary=matrix, + ) + + ### YY + for i in range(1, n, 2): + matrix = structures[3 * l + 1, i] * ii + ( + 1.0 - structures[3 * l + 1, i] + ) * ( + K.cos(params[3 * l + 1, i]) * ii + + 1.0j * K.sin(params[3 * l + 1, i]) * yy + ) + c.any( + i, + (i + 1) % n, + unitary=matrix, + ) + + ### XX + for i in range(1, n, 2): + matrix = structures[3 * l + 2, i] * ii + ( + 1.0 - structures[3 * l + 2, i] + ) * ( + K.cos(params[3 * l + 2, i]) * ii + + 1.0j * K.sin(params[3 * l + 2, i]) * xx + ) + c.any( + i, + (i + 1) % n, + unitary=matrix, + ) + + ### Even layer + ### ZZ + for i in range(0, n, 2): + matrix = structures[3 * l, i] * ii + (1.0 - structures[3 * l, i]) * ( + K.cos(params[3 * l, i]) * ii + 1.0j * K.sin(params[3 * l, i]) * zz + ) + c.any( + i, + (i + 1) % n, + unitary=matrix, + ) + ### YY + + for i in range(0, n, 2): + matrix = structures[3 * l + 1, i] * ii + ( + 1.0 - structures[3 * l + 1, i] + ) * ( + K.cos(params[3 * l + 1, i]) * ii + + 1.0j * K.sin(params[3 * l + 1, i]) * yy + ) + c.any( + i, + (i + 1) % n, + unitary=matrix, + ) + + ### XX + for i in range(0, n, 2): + matrix = structures[3 * l + 2, i] * ii + ( + 1.0 - structures[3 * l + 2, i] + ) * ( + K.cos(params[3 * l + 2, i]) * ii + + 1.0j * K.sin(params[3 * l + 2, i]) * xx + ) + c.any( + i, + (i + 1) % n, + unitary=matrix, + ) + s = c.state() + return s, s + + params = K.cast(K.real(params), dtype="complex128") + structures = (K.sign(structures) + 1) / 2 # 0 or 1 + structures = K.cast(structures, dtype="complex128") + + c = tc.Circuit(n) + + for i in range(n): + c.x(i) + for i in range(0, n, 2): + c.H(i) + for i in range(0, n, 2): + c.cnot(i, i + 1) + s = c.state() + s, _ = jax.lax.scan( + one_layer, + s, + ( + K.reshape(params, [nlayers, 3, n]), + K.reshape(structures, [nlayers, 3, n]), + ), + ) + c = tc.Circuit(n, inputs=s) + # e = tc.templates.measurements.heisenberg_measurements( + # c, g, hzz=1, hxx=1, hyy=1, hx=0, hy=0, hz=0 + # ) + e = tc.templates.measurements.operator_expectation(c, heih) + return K.real(e) + + vagf = K.jit(K.vvag(energy, argnums=0, vectorized_argnums=0), static_argnums=(2, 3)) + + structures = tc.array_to_tensor( + np.random.uniform(low=0.0, high=1.0, size=[3 * nlayers, n]), dtype="complex128" + ) + structures -= 1.0 * K.ones([3 * nlayers, n]) + params = tc.array_to_tensor( + np.random.uniform(low=-0.1, high=0.1, size=[ncircuits, 3 * nlayers, n]), + dtype="float64", + ) + + # opt = K.optimizer(tf.keras.optimizers.Adam(1e-2)) + opt = K.optimizer(optax.adam(1e-2)) + + for _ in range(50): + time0 = time.time() + e, grads = vagf(params, structures, n, nlayers) + time1 = time.time() + params = opt.update(grads, params) + print(K.numpy(e), time1 - time0) + + +if __name__ == "__main__": + main() diff --git a/examples/universal_lr.py b/examples/universal_lr.py index d98e62ca..2aa547c8 100644 --- a/examples/universal_lr.py +++ b/examples/universal_lr.py @@ -27,6 +27,7 @@ def lr(xs, ys): """ fully ML backend agnostic linear regression implementation """ + # construct the loss def loss_pointwise(x, y, param): k, b = param["k"], param["b"] @@ -74,7 +75,6 @@ def loss(xs, ys, param): if __name__ == "__main__": - for n in ["tensorflow", "jax"]: with tc.runtime_backend(n): # runtime backend switch with context manager print("~~~~~~~~ using %s backend ~~~~~~~~" % n) diff --git a/examples/variational_dynamics.py b/examples/variational_dynamics.py index a6e8fc88..39015fbd 100644 --- a/examples/variational_dynamics.py +++ b/examples/variational_dynamics.py @@ -46,6 +46,8 @@ def lhs_matrix(theta, psi0): vij = tc.backend.vmap(ij, vectorized_argnums=0) vvij = tc.backend.vmap(vij, vectorized_argnums=1) jacobian = ppsioverptheta(theta, psi0=psi0) + # fim = tc.backend.adjoint(jacobian)@jacobian is also ok + # speed comparison? jacobian = tc.backend.transpose(jacobian) fim = vvij(jacobian, jacobian) fim = tc.backend.real(fim) @@ -62,12 +64,16 @@ def energy(theta, psi0): wl = tc.backend.reshape(wl, [1, -1]) wr = tc.backend.reshape(wr, [-1, 1]) e = wl @ h @ wr + # use sparse matrix if required return tc.backend.real(e)[0, 0] eg = tc.backend.grad(energy, argnums=0) rhs = eg(theta, psi0) rhs = tc.backend.imag(rhs) return rhs + # for ITE, imag is replace with real + # a simpler way to get rhs in ITE case is to directly evaluate + # 0.5*\nabla @tc.backend.jit diff --git a/examples/variational_dynamics_circuit.py b/examples/variational_dynamics_circuit.py new file mode 100644 index 00000000..a7098c12 --- /dev/null +++ b/examples/variational_dynamics_circuit.py @@ -0,0 +1,323 @@ +""" +Variational quantum simulation by directly contruct circuit for matrix elements +""" + +import math +import numpy as np +import matplotlib.pyplot as plt +import tensorflow as tf +import tensorcircuit as tc + +tc.set_backend("tensorflow") +tc.set_dtype("complex128") + + +# realize R gates in paper +def R_gate(k, c, ODE_theta): + if door[k][0] == 0: + c.rx(door[k][1] + 1, theta=ODE_theta[k]) + if door[k][0] == 1: + c.ry(door[k][1] + 1, theta=ODE_theta[k]) + if door[k][0] == 2: + c.rz(door[k][1] + 1, theta=ODE_theta[k]) + if door[k][0] == 3: + c.rxx(door[k][1] + 1, door[k][2] + 1, theta=ODE_theta[k]) + if door[k][0] == 4: + c.ryy(door[k][1] + 1, door[k][2] + 1, theta=ODE_theta[k]) + if door[k][0] == 5: + c.rzz(door[k][1] + 1, door[k][2] + 1, theta=ODE_theta[k]) + if door[k][0] == 6: + c.crx(door[k][1] + 1, door[k][2] + 1, theta=ODE_theta[k]) + if door[k][0] == 7: + c.cry(door[k][1] + 1, door[k][2] + 1, theta=ODE_theta[k]) + if door[k][0] == 8: + c.crz(door[k][1] + 1, door[k][2] + 1, theta=ODE_theta[k]) + + +# realize U and H gates in paper +def U_H_gate(k, UHgate): + if UHgate[k][0] == 0: + gate_now = tc.gates.multicontrol_gate( + np.kron(tc.gates._x_matrix, np.eye(2)), [1] + ) + if UHgate[k][0] == 1: + gate_now = tc.gates.multicontrol_gate( + np.kron(tc.gates._y_matrix, np.eye(2)), [1] + ) + if UHgate[k][0] == 2: + gate_now = tc.gates.multicontrol_gate( + np.kron(tc.gates._z_matrix, np.eye(2)), [1] + ) + if UHgate[k][0] == 3: + gate_now = tc.gates.multicontrol_gate(tc.gates._xx_matrix, [1]) + if UHgate[k][0] == 4: + gate_now = tc.gates.multicontrol_gate(tc.gates._yy_matrix, [1]) + if UHgate[k][0] == 5: + gate_now = tc.gates.multicontrol_gate(tc.gates._zz_matrix, [1]) + if UHgate[k][0] == 6: + gate_now = tc.gates.multicontrol_gate(tc.gates._x_matrix, [1, 1]) + if UHgate[k][0] == 7: + gate_now = tc.gates.multicontrol_gate(tc.gates._y_matrix, [1, 1]) + if UHgate[k][0] == 8: + gate_now = tc.gates.multicontrol_gate(tc.gates._z_matrix, [1, 1]) + return gate_now.eval_matrix() + + +# use quantum circuit to calculate coefficient of variation A and C in paper +def Calculation_A(theta_x, is_k, is_q, ODE_theta): + # mod: a in paper; theta_x: theta in paper; k, q: A[k, q] or C[k] qth term(k <= q) + c = tc.Circuit(N + 1, inputs=np.kron([1, 1] / np.sqrt(2), state)) + c.rz(0, theta=-theta_x) + for i in range(len(door)): + c.conditional_gate(is_k[i], [np.eye(2), tc.gates._x_matrix], 0) + c.conditional_gate( + is_k[i], + [np.eye(8), U_H_gate(i, door)], + 0, + door[i][1] + 1, + door[i][2] + 1, + ) + c.conditional_gate(is_k[i], [np.eye(2), tc.gates._x_matrix], 0) + c.conditional_gate( + is_q[i], + [np.eye(8), U_H_gate(i, door)], + 0, + door[i][1] + 1, + door[i][2] + 1, + ) + R_gate(i, c, ODE_theta) + pstar = c.expectation([np.array([[1, 1], [1, 1]]) / 2, [0]]) + return 2 * pstar - 1 + + +Calculation_A_vmap = tc.backend.jit( + tc.backend.vmap(Calculation_A, vectorized_argnums=[0, 1, 2]) +) + + +def Calculation_C(theta_x, is_k, is_q, ODE_theta): + # mod: a in paper; theta_x: theta in paper; k, q: A[k, q] or C[k] qth term + c = tc.Circuit(N + 1, inputs=np.kron([1, 1] / np.sqrt(2), state)) + c.rz(0, theta=-theta_x) + for i in range(len(door)): + c.conditional_gate(is_k[i], [np.eye(2), tc.gates._x_matrix], 0) + c.conditional_gate( + is_k[i], + [np.eye(8), U_H_gate(i, door)], + 0, + door[i][1] + 1, + door[i][2] + 1, + ) + c.conditional_gate(is_k[i], [np.eye(2), tc.gates._x_matrix], 0) + R_gate(i, c, ODE_theta) + for i in range(len(h_door)): + c.conditional_gate( + is_q[i], + [np.eye(8), U_H_gate(i, h_door)], + 0, + h_door[i][1] + 1, + h_door[i][2] + 1, + ) + pstar = c.expectation([np.array([[1, 1], [1, 1]]) / 2, [0]]) + return 2 * pstar - 1 + + +Calculation_C_vmap = tc.backend.jit( + tc.backend.vmap(Calculation_C, vectorized_argnums=[0, 1, 2]) +) + + +# use original quantum circuit simulate with c +@tc.backend.jit +def simulation(ODE_theta): + c = tc.Circuit(N, inputs=state) + for k in range(len(door)): + if door[k][0] == 0: + c.rx(door[k][1], theta=ODE_theta[k]) + if door[k][0] == 1: + c.ry(door[k][1], theta=ODE_theta[k]) + if door[k][0] == 2: + c.rz(door[k][1], theta=ODE_theta[k]) + if door[k][0] == 3: + c.rxx(door[k][1], door[k][2], theta=ODE_theta[k]) + if door[k][0] == 4: + c.ryy(door[k][1], door[k][2], theta=ODE_theta[k]) + if door[k][0] == 5: + c.rzz(door[k][1], door[k][2], theta=ODE_theta[k]) + if door[k][0] == 6: + c.crx(door[k][1], door[k][2], theta=ODE_theta[k]) + if door[k][0] == 7: + c.cry(door[k][1], door[k][2], theta=ODE_theta[k]) + if door[k][0] == 8: + c.crz(door[k][1], door[k][2], theta=ODE_theta[k]) + return tc.backend.real(c.expectation([tc.gates.x(), [1]])) + + +def numdiff(i): + return (i + 1) % N + + +if __name__ == "__main__": + # l: layers; h and J: coefficient of Hamiltonian; + # L_var and L_num: results of variation method and numerical method + N = 3 + l = 4 + J = 1 / 4 + dt = 0.005 + t = 1 + h = [] + L_var = [] + L_num = [] + x_value = [] + + how_variation = 0 # 0:McLachlan; 1:time-dependent + + # the priciple correspond with all gates + # the first term: 0rx,1ry,2rz,3rxx,4ryy,5rzz,6crx,7cry,8crz; + # the second and the third term: num/ctrl+num + # f: coefficient with simulation gates in paper + door = [] + h_door = [] + f = [] + for k in range(l): + for i in range(N): + f.append(-0.5j) + door.append([0, i, numdiff(i)]) + for i in range(N - 1): + f.append(-0.5j) + door.append([5, i, i + 1]) + for i in range(N - 1): + f.append(-0.5j) + door.append([3, i, i + 1]) + for i in range(N): + h.append(1) + h_door.append([0, i, numdiff(i)]) + for i in range(N - 1): + h.append(J) + h_door.append([5, i, i + 1]) + f = tf.constant(f, dtype="complex64") + h = tf.constant(h, dtype="float32") + + # initial state + state = np.zeros(1 << N) + state[0] = 1 + + # numerical realize H + ls = [] + weight = [] + for q in range(len(h_door)): + if h_door[q][0] == 0: + r = [0 for _ in range(N)] + r[h_door[q][1]] = 1 + if h_door[q][0] == 1: + r = [0 for _ in range(N)] + r[h_door[q][1]] = 2 + if h_door[q][0] == 2: + r = [0 for _ in range(N)] + r[h_door[q][1]] = 3 + if h_door[q][0] == 3: + r = [0 for _ in range(N)] + r[h_door[q][1]] = 1 + r[h_door[q][2]] = 1 + if h_door[q][0] == 4: + r = [0 for _ in range(N)] + r[h_door[q][1]] = 2 + r[h_door[q][2]] = 2 + if h_door[q][0] == 5: + r = [0 for _ in range(N)] + r[h_door[q][1]] = 3 + r[h_door[q][2]] = 3 + ls.append(r) + weight.append(h[q]) + ls = tc.array_to_tensor(ls) + weight = tc.array_to_tensor(weight) + H = tc.quantum.PauliStringSum2Dense(ls, weight, numpy=False) + + # variation realize + ODE_theta = tf.zeros(len(door), dtype="float64") + + a_batch_theta = [] + a_batch_is_k = [] + a_batch_is_q = [] + for k in range(len(door)): + for q in range(len(door)): + is_k = [0 for _ in range(len(door))] + is_k[k] = 1 + is_q = [0 for _ in range(len(door))] + is_q[q] = 1 + if how_variation == 0: + a_batch_theta.append(np.angle(f[q]) - np.angle(f[k])) + else: + a_batch_theta.append(np.angle(f[q]) - np.angle(f[k]) - math.pi / 2) + a_batch_is_k.append(is_k) + a_batch_is_q.append(is_q) + a_batch_theta = tc.array_to_tensor(a_batch_theta) + a_batch_is_k = tf.constant(a_batch_is_k) + a_batch_is_q = tf.constant(a_batch_is_q) + + c_batch_theta = [] + c_batch_is_k = [] + c_batch_is_q = [] + for k in range(len(door)): + for q in range(len(h_door)): + is_k = [0 for _ in range(len(door))] + is_k[k] = 1 + is_q = [0 for _ in range(len(door))] + is_q[q] = 1 + c_batch_is_k.append(is_k) + c_batch_is_q.append(is_q) + if how_variation == 0: + c_batch_theta.append(np.angle(h[q]) - np.angle(f[k]) - math.pi / 2) + else: + c_batch_theta.append(np.angle(h[q]) - np.angle(f[k]) + math.pi) + c_batch_theta = tc.array_to_tensor(c_batch_theta) + c_batch_is_k = tf.constant(c_batch_is_k) + c_batch_is_q = tf.constant(c_batch_is_q) + + for T in range(int(t / dt)): + # calculate coefficient in paper + + vmap_result = Calculation_A_vmap( + a_batch_theta, a_batch_is_k, a_batch_is_q, ODE_theta + ) + A = tf.cast( + tf.tensordot(tf.abs(f), tf.abs(f), 0), dtype="float64" + ) * tf.reshape( + tc.backend.cast(vmap_result, dtype="float64"), [len(door), len(door)] + ) + + vmap_result = Calculation_C_vmap( + c_batch_theta, c_batch_is_k, c_batch_is_q, ODE_theta + ) + C = tf.reduce_sum( + tf.cast(tf.tensordot(tf.abs(f), tf.abs(h), 0), dtype="float64") + * tf.reshape( + tc.backend.cast(vmap_result, dtype="float64"), [len(door), len(h_door)] + ), + 1, + ) + + # calculate parameter and its derivative + A += np.eye(len(door)) * 1e-7 + ODE_dtheta = tc.backend.solve(A, C) + ODE_theta += ODE_dtheta * dt + + # numerical results + ep = np.array(tc.backend.expm(-1j * H * (T + 1) * dt)) @ state + L_num.append( + np.array( + tc.backend.real( + tc.expectation([tc.gates.x(), [1]], ket=ep.astype("complex128")) + ) + ) + ) + + # variation results + L_var.append(tc.backend.numpy(simulation(ODE_theta)).tolist()) + + x_value.append(round((T + 1) * dt, 3)) + print("Now time:", x_value[T], "Loss:", L_num[T] - L_var[T]) + + plt.plot(x_value, L_var, color="green") + plt.plot(x_value, L_num, color="red") + plt.show() diff --git a/examples/vqe_noisyopt.py b/examples/vqe_noisyopt.py new file mode 100644 index 00000000..8bd18948 --- /dev/null +++ b/examples/vqe_noisyopt.py @@ -0,0 +1,222 @@ +""" +VQE with finite measurement shot noise +""" + +from functools import partial +import numpy as np +import optax +from noisyopt import minimizeCompass, minimizeSPSA +from tabulate import tabulate # pip install tabulate +import tensorcircuit as tc +from tensorcircuit import experimental as E + +seed = 42 +np.random.seed(seed) + +K = tc.set_backend("jax") +# note this script only supports jax backend + +n = 6 +nlayers = 4 + +# initial value of the parameters +initial_value = np.random.uniform(size=[n * nlayers * 2]) + +result = { + "Algorithm / Optimization": ["Without Shot Noise", "With Shot Noise"], + "SPSA (Gradient Free)": [], + "Compass Search (Gradient Free)": [], + "Adam (Gradient based)": [], +} + +# We use OBC 1D TFIM Hamiltonian in this script + +ps = [] +for i in range(n): + l = [0 for _ in range(n)] + l[i] = 1 + ps.append(l) + # X_i +for i in range(n - 1): + l = [0 for _ in range(n)] + l[i] = 3 + l[i + 1] = 3 + ps.append(l) + # Z_i Z_i+1 +w = [-1.0 for _ in range(n)] + [1.0 for _ in range(n - 1)] + + +def generate_circuit(param): + # construct the circuit ansatz + c = tc.Circuit(n) + for i in range(n): + c.H(i) + for j in range(nlayers): + for i in range(n - 1): + c.rzz(i, i + 1, theta=param[i, j, 0]) + for i in range(n): + c.rx(i, theta=param[i, j, 1]) + return c + + +def ps2xyz(psi): + # ps2xyz([1, 2, 2, 0]) = {"x": [0], "y": [1, 2], "z": []} + xyz = {"x": [], "y": [], "z": []} + for i, j in enumerate(psi): + if j == 1: + xyz["x"].append(i) + if j == 2: + xyz["y"].append(i) + if j == 3: + xyz["z"].append(i) + return xyz + + +@partial(K.jit, static_argnums=(2)) +def exp_val(param, key, shots=1024): + # expectation with shot noise + # ps, w: H = \sum_i w_i ps_i + # describing the system Hamiltonian as a weighted sum of Pauli string + c = generate_circuit(param) + if isinstance(shots, int): + shots = [shots for _ in range(len(ps))] + loss = 0 + for psi, wi, shot in zip(ps, w, shots): + key, subkey = K.random_split(key) + xyz = ps2xyz(psi) + loss += wi * c.sample_expectation_ps(**xyz, shots=shot, random_generator=subkey) + return K.real(loss) + + +@K.jit +def exp_val_analytical(param): + param = param.reshape(n, nlayers, 2) + c = generate_circuit(param) + loss = 0 + for psi, wi in zip(ps, w): + xyz = ps2xyz(psi) + loss += wi * c.expectation_ps(**xyz) + return K.real(loss) + + +# 0. Exact result + +hm = tc.quantum.PauliStringSum2COO(ps, w, numpy=True) +hm = K.to_dense(hm) +e, v = np.linalg.eigh(hm) +exact_gs_energy = e[0] +print("==================================================================") +print("Exact ground state energy: ", exact_gs_energy) +print("==================================================================") + +# 1.1 VQE with numerically exact expectation: gradient free + +print(">>> VQE without shot noise") + + +r = minimizeSPSA( + func=exp_val_analytical, + x0=initial_value, + niter=6000, + paired=False, +) + +print(r) +print(">> SPSA converged as:", exp_val_analytical(r.x)) +result["SPSA (Gradient Free)"].append(exp_val_analytical(r.x)) + +r = minimizeCompass( + func=exp_val_analytical, + x0=initial_value, + deltatol=0.1, + feps=1e-3, + paired=False, +) + +print(r) +print(">> Compass converged as:", exp_val_analytical(r.x)) +result["Compass Search (Gradient Free)"].append(exp_val_analytical(r.x)) + +# 1.2 VQE with numerically exact expectation: gradient based + +exponential_decay_scheduler = optax.exponential_decay( + init_value=1e-2, transition_steps=500, decay_rate=0.9 +) +opt = K.optimizer(optax.adam(exponential_decay_scheduler)) +param = initial_value.reshape((n, nlayers, 2)) # zeros stall the gradient +exp_val_grad_analytical = K.jit(K.value_and_grad(exp_val_analytical)) +for i in range(1000): + e, g = exp_val_grad_analytical(param) + param = opt.update(g, param) + if i % 100 == 99: + print(f"Expectation value at iteration {i}: {e}") + +print(">> Adam converged as:", exp_val_grad_analytical(param)[0]) +result["Adam (Gradient based)"].append(exp_val_grad_analytical(param)[0]) + +# 2.1 VQE with finite shot noise: gradient free + +print("==================================================================") +print(">>> VQE with shot noise") + + +rkey = K.get_random_state(seed) + + +def exp_val_wrapper(param): + param = param.reshape(n, nlayers, 2) + global rkey + rkey, skey = K.random_split(rkey) + # maintain stateless randomness in scipy optimize interface + return exp_val(param, skey, shots=1024) + + +r = minimizeSPSA( + func=exp_val_wrapper, + x0=initial_value, + niter=6000, + paired=False, +) +print(r) +print(">> SPSA converged as:", exp_val_wrapper(r["x"])) +result["SPSA (Gradient Free)"].append(exp_val_wrapper(r["x"])) + +r = minimizeCompass( + func=exp_val_wrapper, + x0=initial_value, + deltatol=0.1, + feps=1e-2, + paired=False, +) + +print(r) +print(">> Compass converged as:", exp_val_wrapper(r["x"])) +result["Compass Search (Gradient Free)"].append(exp_val_wrapper(r["x"])) + + +# 2.2 VQE with finite shot noise: gradient based + +exponential_decay_scheduler = optax.exponential_decay( + init_value=1e-2, transition_steps=500, decay_rate=0.9 +) +opt = K.optimizer(optax.adam(exponential_decay_scheduler)) +param = initial_value.reshape((n, nlayers, 2)) # zeros stall the gradient +exp_grad = E.parameter_shift_grad_v2(exp_val, argnums=0, random_argnums=1) +rkey = K.get_random_state(seed) + +for i in range(1000): + rkey, skey = K.random_split(rkey) + g = exp_grad(param, skey) + param = opt.update(g, param) + if i % 100 == 99: + rkey, skey = K.random_split(rkey) + print(f"Expectation value at iteration {i}: {exp_val(param, skey)}") + +# the real energy position after optimization +print(">> Adam converged as:", exp_val_analytical(param)) +result["Adam (Gradient based)"].append(exp_val_analytical(param)) + +print("==================================================================") +print(">>> Benchmark") +print(">> Exact ground state energy: ", exact_gs_energy) +print(tabulate(result, headers="keys", tablefmt="github")) diff --git a/examples/vqe_parallel_pmap.py b/examples/vqe_parallel_pmap.py new file mode 100644 index 00000000..54ea6922 --- /dev/null +++ b/examples/vqe_parallel_pmap.py @@ -0,0 +1,68 @@ +""" +jax pmap paradigm for vqe on multiple gpus +""" + +import os + +os.environ["XLA_FLAGS"] = "--xla_force_host_platform_device_count=8" +from functools import partial +import jax +import optax +import tensorcircuit as tc + +K = tc.set_backend("jax") +tc.set_contractor("cotengra") + + +def vqef(param, measure, n, nlayers): + c = tc.Circuit(n) + c.h(range(n)) + for i in range(nlayers): + c.rzz(range(n - 1), range(1, n), theta=param[i, 0]) + c.rx(range(n), theta=param[i, 1]) + return K.real( + tc.templates.measurements.parameterized_measurements(c, measure, onehot=True) + ) + + +def get_tfim_ps(n): + tfim_ps = [] + for i in range(n): + tfim_ps.append(tc.quantum.xyz2ps({"x": [i]}, n=n)) + for i in range(n): + tfim_ps.append(tc.quantum.xyz2ps({"z": [i, (i + 1) % n]}, n=n)) + return K.convert_to_tensor(tfim_ps) + + +vqg_vgf = jax.vmap(K.value_and_grad(vqef), in_axes=(None, 0, None, None)) + + +@partial( + jax.pmap, + axis_name="pmap", + in_axes=(0, 0, None, None), + static_broadcasted_argnums=(2, 3), +) +def update(param, measure, n, nlayers): + # Compute the gradients on the given minibatch (individually on each device). + loss, grads = vqg_vgf(param, measure, n, nlayers) + grads = K.sum(grads, axis=0) + grads = jax.lax.psum(grads, axis_name="pmap") + loss = K.sum(loss, axis=0) + loss = jax.lax.psum(loss, axis_name="pmap") + param = opt.update(grads, param) + return param, loss + + +if __name__ == "__main__": + n = 8 + nlayers = 4 + ndevices = 8 + m = get_tfim_ps(n) + m = K.reshape(m, [ndevices, m.shape[0] // ndevices] + list(m.shape[1:])) + param = K.stateful_randn(jax.random.PRNGKey(43), shape=[nlayers, 2, n], stddev=0.1) + param = K.stack([param] * ndevices) + opt = K.optimizer(optax.adam(1e-2)) + for _ in range(100): + param, loss = update(param, m, n, nlayers) + print(loss[0]) diff --git a/examples/vqe_shot_noise.py b/examples/vqe_shot_noise.py new file mode 100644 index 00000000..26a6b28b --- /dev/null +++ b/examples/vqe_shot_noise.py @@ -0,0 +1,178 @@ +""" +VQE with finite measurement shot noise +""" + +from functools import partial +import numpy as np +from scipy import optimize +import optax +import tensorcircuit as tc +from tensorcircuit import experimental as E + +K = tc.set_backend("jax") +# note this script only supports jax backend + +n = 6 +nlayers = 4 + +# We use OBC 1D TFIM Hamiltonian in this script + +ps = [] +for i in range(n): + l = [0 for _ in range(n)] + l[i] = 1 + ps.append(l) + # X_i +for i in range(n - 1): + l = [0 for _ in range(n)] + l[i] = 3 + l[i + 1] = 3 + ps.append(l) + # Z_i Z_i+1 +w = [-1.0 for _ in range(n)] + [1.0 for _ in range(n - 1)] + + +def generate_circuit(param): + # construct the circuit ansatz + c = tc.Circuit(n) + for i in range(n): + c.H(i) + for j in range(nlayers): + for i in range(n - 1): + c.rzz(i, i + 1, theta=param[i, j, 0]) + for i in range(n): + c.rx(i, theta=param[i, j, 1]) + return c + + +def ps2xyz(psi): + # ps2xyz([1, 2, 2, 0]) = {"x": [0], "y": [1, 2], "z": []} + xyz = {"x": [], "y": [], "z": []} + for i, j in enumerate(psi): + if j == 1: + xyz["x"].append(i) + if j == 2: + xyz["y"].append(i) + if j == 3: + xyz["z"].append(i) + return xyz + + +@partial(K.jit, static_argnums=(2)) +def exp_val(param, key, shots=1024): + # expectation with shot noise + # ps, w: H = \sum_i w_i ps_i + # describing the system Hamiltonian as a weighted sum of Pauli string + c = generate_circuit(param) + if isinstance(shots, int): + shots = [shots for _ in range(len(ps))] + loss = 0 + for psi, wi, shot in zip(ps, w, shots): + key, subkey = K.random_split(key) + xyz = ps2xyz(psi) + loss += wi * c.sample_expectation_ps(**xyz, shots=shot, random_generator=subkey) + return K.real(loss) + + +@K.jit +def exp_val_analytical(param): + c = generate_circuit(param) + loss = 0 + for psi, wi in zip(ps, w): + xyz = ps2xyz(psi) + loss += wi * c.expectation_ps(**xyz) + return K.real(loss) + + +# 0. Exact result + +hm = tc.quantum.PauliStringSum2COO(ps, w, numpy=True) +hm = K.to_dense(hm) +e, v = np.linalg.eigh(hm) +print("exact ground state energy: ", e[0]) + +# 1.1 VQE with numerically exact expectation: gradient free + +print("VQE without shot noise") + +exp_val_analytical_sp = tc.interfaces.scipy_interface( + exp_val_analytical, shape=[n, nlayers, 2], gradient=False +) + +r = optimize.minimize( + exp_val_analytical_sp, + np.zeros([n * nlayers * 2]), + method="COBYLA", + options={"maxiter": 5000}, +) +print(r) + + +# 1.2 VQE with numerically exact expectation: gradient based + +exponential_decay_scheduler = optax.exponential_decay( + init_value=1e-2, transition_steps=500, decay_rate=0.9 +) +opt = K.optimizer(optax.adam(exponential_decay_scheduler)) +param = K.implicit_randn([n, nlayers, 2], stddev=0.1) # zeros stall the gradient +exp_val_grad_analytical = K.jit(K.value_and_grad(exp_val_analytical)) +for i in range(1000): + e, g = exp_val_grad_analytical(param) + param = opt.update(g, param) + if i % 100 == 99: + print(e) + + +# 2.1 VQE with finite shot noise: gradient free + +print("VQE with shot noise") + + +rkey = K.get_random_state(42) + + +def exp_val_wrapper(param): + global rkey + rkey, skey = K.random_split(rkey) + # maintain stateless randomness in scipy optimize interface + return exp_val(param, skey, shots=1024) + + +exp_val_sp = tc.interfaces.scipy_interface( + exp_val_wrapper, shape=[n, nlayers, 2], gradient=False +) + +r = optimize.minimize( + exp_val_sp, + np.random.normal(scale=0.1, size=[n * nlayers * 2]), + method="COBYLA", + options={"maxiter": 5000}, +) +print(r) + +# the real energy position after optimization + +print("converged as: ", exp_val_analytical_sp(r["x"])) + + +# 2.2 VQE with finite shot noise: gradient based + +exponential_decay_scheduler = optax.exponential_decay( + init_value=1e-2, transition_steps=500, decay_rate=0.9 +) +opt = K.optimizer(optax.adam(exponential_decay_scheduler)) +param = K.implicit_randn([n, nlayers, 2], stddev=0.1) # zeros stall the gradient +exp_grad = E.parameter_shift_grad_v2(exp_val, argnums=0, random_argnums=1) +rkey = K.get_random_state(42) + +for i in range(1000): + rkey, skey = K.random_split(rkey) + g = exp_grad(param, skey) + param = opt.update(g, param) + if i % 100 == 99: + rkey, skey = K.random_split(rkey) + print(exp_val(param, skey)) + +# the real energy position after optimization + +print("converged as:", exp_val_analytical(param)) diff --git a/examples/vqetfim_benchmark.py b/examples/vqetfim_benchmark.py index 9b17c27a..343fb05d 100644 --- a/examples/vqetfim_benchmark.py +++ b/examples/vqetfim_benchmark.py @@ -119,13 +119,11 @@ def vqe_template(param, op): ) if enable_dense is True: - hamiltonian_dense = K.to_dense(hamiltonian_sparse) vqe3 = partial(vqe_template, op=hamiltonian_dense) else: - vqe3 = vqe1 # 4. sparse matrix diff --git a/examples/vqnhe_h6.py b/examples/vqnhe_h6.py index 5bc86719..1498c5d4 100644 --- a/examples/vqnhe_h6.py +++ b/examples/vqnhe_h6.py @@ -1,6 +1,7 @@ """ H6 molecule VQNHE with code from tc.application """ + import sys sys.path.insert(0, "../") diff --git a/mypy.ini b/mypy.ini index 6b9982e9..deb964dc 100644 --- a/mypy.ini +++ b/mypy.ini @@ -5,14 +5,16 @@ strict = True warn_unused_ignores = False disallow_untyped_calls = False local_partial_types = False +implicit_reexport = True [mypy-tensorcircuit.backends.pytorch_ops] ;;mypy simply cannot ignore files with wildcard patterns... ;;only module level * works... ignore_errors = True -;;[mypy-numpy.*] -;;ignore_errors = True + +[mypy-cirq.*] +ignore_errors = True ;; doesn't work due to https://github.com/python/mypy/issues/10757 ;; mypy + numpy is currently a disaster, never use mypy in your next project diff --git a/pytest.ini b/pytest.ini new file mode 100644 index 00000000..a8f7d7ea --- /dev/null +++ b/pytest.ini @@ -0,0 +1,4 @@ +[pytest] +filterwarnings = + ignore::DeprecationWarning + ignore:Explicitly requested dtype*:UserWarning \ No newline at end of file diff --git a/requirements/requirements-dev.txt b/requirements/requirements-dev.txt index 877fc936..e6b3e293 100644 --- a/requirements/requirements-dev.txt +++ b/requirements/requirements-dev.txt @@ -1,13 +1,14 @@ -mypy==0.782 +mypy==1.5.1 pytest==6.2.4 pytest-cov pytest-benchmark pytest-xdist -black==22.3.0 +black[jupyter] sphinx>=4.0 pytest-lazy-fixture -pylint==2.11.1 -numpy==1.21.5 +pylint==2.17.5 furo sphinx-copybutton -nbsphinx \ No newline at end of file +nbsphinx +myst-parser +sphinx-design \ No newline at end of file diff --git a/requirements/requirements-docker-v2.txt b/requirements/requirements-docker-v2.txt new file mode 100644 index 00000000..5a99a02d --- /dev/null +++ b/requirements/requirements-docker-v2.txt @@ -0,0 +1,47 @@ +torch>2.0 # 2.0.1 +jax[cuda11_pip]==0.4.7 +tensorflow==2.11 +# tf 2.12 can make torch 2 hangs with runtime error +# check with hybrid_gpu_pipeline.py example +cupy-cuda11x==12.0.0 +tensornetwork==0.4.6 +graphviz +numpy==1.23.5 +scipy==1.10.1 +sympy==1.12 +cirq==1.1.0 +qiskit +matplotlib +jupyter +cotengra +networkx +optax==0.1.5 +kahypar +optuna +baytune +nevergrad +scikit-learn==1.2.2 +scikit-optimize +openfermion +quimb +openfermionpyscf +pennylane==0.30.0 +mthree==1.1.0 +mitiq==0.26.0 +# below is for development +mypy==1.3.0 +pytest +pytest-cov +pytest-benchmark +pytest-xdist +pytest-lazy-fixture +black==23.3.0 +sphinx>=4.0 +sphinx-intl +sphinx-copybutton +nbsphinx +furo +myst-parser +pylint +sphinx-design +# made in 202306 diff --git a/requirements/requirements-docker.txt b/requirements/requirements-docker.txt index 04def897..6f686f79 100644 --- a/requirements/requirements-docker.txt +++ b/requirements/requirements-docker.txt @@ -21,19 +21,22 @@ qiskit openfermion quimb openfermionpyscf +pennylane +# tensorflow_quantum==0.6.1 +mthree +mitiq # below is for development -mypy==0.782 +mypy==1.2.0 pytest pytest-cov pytest-benchmark pytest-xdist pytest-lazy-fixture -black==22.3.0 +black==23.3.0 sphinx>=4.0 sphinx-intl sphinx-copybutton nbsphinx furo +myst-parser pylint -pennylane -tensorflow_quantum==0.6.1 \ No newline at end of file diff --git a/requirements/requirements-extra.txt b/requirements/requirements-extra.txt index 74b78e9e..63e1f992 100644 --- a/requirements/requirements-extra.txt +++ b/requirements/requirements-extra.txt @@ -1,3 +1,10 @@ # extra dependencies for ci -qiskit -torch \ No newline at end of file +qiskit<1.0 +qiskit-aer<1.0 +qiskit-nature +mitiq +cirq +torch==2.2.2 +# jupyter +mthree==1.1.0 +openfermion diff --git a/requirements/requirements-rtd.txt b/requirements/requirements-rtd.txt index c58fa3b4..8e93c419 100644 --- a/requirements/requirements-rtd.txt +++ b/requirements/requirements-rtd.txt @@ -12,4 +12,7 @@ sphinx==4.3.2 ipykernel furo==2022.4.7 sphinx-copybutton -nbsphinx \ No newline at end of file +nbsphinx +myst-parser +urllib3==1.26.15 +sphinx-design \ No newline at end of file diff --git a/requirements/requirements-types.txt b/requirements/requirements-types.txt new file mode 100644 index 00000000..6626e99d --- /dev/null +++ b/requirements/requirements-types.txt @@ -0,0 +1,8 @@ +types-urllib3 +types-typed-ast +types-toml +types-setuptools +types-requests +types-pytz +types-protobuf +types-docutils \ No newline at end of file diff --git a/requirements/requirements.txt b/requirements/requirements.txt index dae1d490..97de418f 100644 --- a/requirements/requirements.txt +++ b/requirements/requirements.txt @@ -1,8 +1,7 @@ numpy scipy -cirq==0.11 -tensorflow==2.7 -tensornetwork +tensorflow<2.16 # tf 2.16 with integration of keras 3 seems a disaster... +tensornetwork-ng graphviz jax jaxlib diff --git a/setup.py b/setup.py index 1c5dfcd1..8e176a49 100644 --- a/setup.py +++ b/setup.py @@ -2,7 +2,7 @@ from tensorcircuit import __version__, __author__ -with open("README.md", "r") as fh: +with open("README.md", "r", encoding="utf-8") as fh: long_description = fh.read() @@ -11,18 +11,19 @@ version=__version__, author=__author__, author_email="shixinzhang@tencent.com", - description="Quantum circuits on top of tensor network", + description="High performance unified quantum computing framework for the NISQ era", long_description=long_description, long_description_content_type="text/markdown", url="https://github.com/tencent-quantum-lab/tensorcircuit", packages=setuptools.find_packages(), include_package_data=True, - install_requires=["numpy", "scipy", "tensornetwork", "networkx"], + install_requires=["numpy", "scipy", "tensornetwork-ng", "networkx"], extras_require={ - "tensorflow": ["tensorflow"], + "tensorflow": ["tensorflow<2.16"], "jax": ["jax", "jaxlib"], "torch": ["torch"], - "qiskit": ["qiskit"], + "qiskit": ["qiskit<1.0"], + "cloud": ["qiskit<1.0", "mthree"], }, classifiers=[ "Programming Language :: Python :: 3", diff --git a/tensorcircuit/__init__.py b/tensorcircuit/__init__.py index a0e33d25..fb1c2e40 100644 --- a/tensorcircuit/__init__.py +++ b/tensorcircuit/__init__.py @@ -1,8 +1,14 @@ -__version__ = "0.4.1" +__version__ = "0.12.0" __author__ = "TensorCircuit Authors" __creator__ = "refraction-ray" +from .utils import gpu_memory_share + +gpu_memory_share() + +from .about import about from .cons import ( + backend, set_backend, set_dtype, set_contractor, @@ -29,20 +35,36 @@ from .vis import qir2tex, render_pdf from . import interfaces from . import templates +from . import results from . import quantum from .quantum import QuOperator, QuVector, QuAdjointVector, QuScalar +from . import compiler +from . import cloud +from . import fgs +from .fgs import FGSSimulator try: from . import keras - from .keras import QuantumLayer as KerasLayer + from .keras import KerasLayer, KerasHardwareLayer except ModuleNotFoundError: pass # in case tf is not installed try: from . import torchnn - from .torchnn import QuantumNet as TorchLayer + from .torchnn import TorchLayer, TorchHardwareLayer except ModuleNotFoundError: pass # in case torch is not installed +try: + import qiskit + + qiskit.QuantumCircuit.cnot = qiskit.QuantumCircuit.cx + qiskit.QuantumCircuit.toffoli = qiskit.QuantumCircuit.ccx + qiskit.QuantumCircuit.fredkin = qiskit.QuantumCircuit.cswap + + # amazing qiskit 1.0 nonsense... +except ModuleNotFoundError: + pass + # just for fun from .asciiart import set_ascii diff --git a/tensorcircuit/about.py b/tensorcircuit/about.py new file mode 100644 index 00000000..d071c1dd --- /dev/null +++ b/tensorcircuit/about.py @@ -0,0 +1,119 @@ +""" +Prints the information for tensorcircuit installation and environment. +""" + +import platform +import sys +import numpy + + +def about() -> None: + """ + Prints the information for tensorcircuit installation and environment. + """ + print(f"OS info: {platform.platform(aliased=True)}") + print( + f"Python version: {sys.version_info[0]}.{sys.version_info[1]}.{sys.version_info[2]}" + ) + print(f"Numpy version: {numpy.__version__}") + + try: + import scipy + + print(f"Scipy version: {scipy.__version__}") + except ModuleNotFoundError: + print(f"Scipy is not installed") + + try: + import pandas + + print(f"Pandas version: {pandas.__version__}") + except ModuleNotFoundError: + print(f"Pandas is not installed") + + try: + import tensornetwork as tn + + print(f"TensorNetwork version: {tn.__version__}") + except ModuleNotFoundError: + print(f"TensorNetwork is not installed") + + try: + import cotengra + + try: + print(f"Cotengra version: {cotengra.__version__}") + except AttributeError: + print(f"Cotengra: installed") + except ModuleNotFoundError: + print(f"Cotengra is not installed") + + try: + import tensorflow as tf + + print(f"TensorFlow version: {tf.__version__}") + print(f"TensorFlow GPU: {tf.config.list_physical_devices('GPU')}") + print(f"TensorFlow CUDA infos: {dict(tf.sysconfig.get_build_info())}") + except ModuleNotFoundError: + print(f"TensorFlow is not installed") + + try: + import jax + + print(f"Jax version: {jax.__version__}") + try: + device = jax.devices("gpu") + print(f"Jax GPU: {device}") + except RuntimeError: + print(f"Jax installation doesn't support GPU") + except ModuleNotFoundError: + print(f"Jax is not installed") + + try: + import jaxlib + + print(f"JaxLib version: {jaxlib.__version__}") + except ModuleNotFoundError: + print(f"JaxLib is not installed") + + try: + import torch + + print(f"PyTorch version: {torch.__version__}") + print(f"PyTorch GPU support: {torch.cuda.is_available()}") + print( + f"PyTorch GPUs: {[torch.cuda.device(i) for i in range(torch.cuda.device_count())]}" + ) + if torch.version.cuda is not None: + print(f"Pytorch cuda version: {torch.version.cuda}") + except ModuleNotFoundError: + print(f"PyTorch is not installed") + + try: + import cupy + + print(f"Cupy version: {cupy.__version__}") + except ModuleNotFoundError: + print(f"Cupy is not installed") + + try: + import qiskit + + print(f"Qiskit version: {qiskit.__version__}") + except ModuleNotFoundError: + print(f"Qiskit is not installed") + + try: + import cirq + + print(f"Cirq version: {cirq.__version__}") + except ModuleNotFoundError: + print(f"Cirq is not installed") + + from tensorcircuit import __version__ + + print(f"TensorCircuit version {__version__}") + + +if __name__ == "__main__": + about() diff --git a/tensorcircuit/abstractcircuit.py b/tensorcircuit/abstractcircuit.py index a1970c3e..2334aa39 100644 --- a/tensorcircuit/abstractcircuit.py +++ b/tensorcircuit/abstractcircuit.py @@ -1,21 +1,24 @@ """ Methods for abstract circuits independent of nodes, edges and contractions """ + # pylint: disable=invalid-name -from typing import Any, Callable, Dict, List, Optional, Sequence, Union, Tuple -from functools import reduce -from operator import add import json import logging +from copy import deepcopy +from functools import reduce +from operator import add +from typing import Any, Callable, Dict, List, Optional, Sequence, Tuple, Union import numpy as np import tensornetwork as tn from . import gates from .cons import backend, dtypestr -from .vis import qir2tex from .quantum import QuOperator +from .utils import is_sequence +from .vis import qir2tex logger = logging.getLogger(__name__) @@ -66,6 +69,7 @@ class AbstractCircuit: _nqubits: int _qir: List[Dict[str, Any]] + _extra_qir: List[Dict[str, Any]] inputs: Tensor circuit_param: Dict[str, Any] is_mps: bool @@ -124,9 +128,27 @@ def apply(self: "AbstractCircuit", *index: int, **vars: Any) -> None: split=split, mpo=mpo, ir_dict=gate_dict, - ) # type: ignore + ) - return apply + def apply_list(self: "AbstractCircuit", *index: int, **vars: Any) -> None: + if isinstance(index[0], int): + apply(self, *index, **vars) + elif is_sequence(index[0]) or isinstance(index[0], range): + for i, ind in enumerate(zip(*index)): + nvars = {} + for k, v in vars.items(): + try: + nvars[k] = v[i] + except Exception: # pylint: disable=W0703 + # (TypeError, IndexError) is not enough, + # tensorflow.python.framework.errors_impl.InvalidArgumentError + # not ideally captured here + nvars[k] = v + apply(self, *ind, **nvars) + else: + raise ValueError("Illegal index specification") + + return apply_list @staticmethod def apply_general_gate_delayed( @@ -150,7 +172,7 @@ def apply( if name is not None: localname = name else: - localname = defaultname # type: ignore + localname = defaultname # split = None gate = gatef() @@ -165,7 +187,16 @@ def apply( ir_dict=gate_dict, ) - return apply + def apply_list(self: "AbstractCircuit", *index: int, **kws: Any) -> None: + if isinstance(index[0], int): + apply(self, *index, **kws) + elif is_sequence(index[0]) or isinstance(index[0], range): + for ind in zip(*index): + apply(self, *ind, **kws) + else: + raise ValueError("Illegal index specification") + + return apply_list @classmethod def _meta_apply(cls) -> None: @@ -348,17 +379,18 @@ def from_qir( :return: The circuit have same gates in the qir. :rtype: Circuit """ + # TODO(@refraction-ray): add extra_qir support if circuit_params is None: circuit_params = {} if "nqubits" not in circuit_params: nqubits = 0 for d in qir: - if max(d["index"]) > nqubits: # type: ignore - nqubits = max(d["index"]) # type: ignore + if max(d["index"]) > nqubits: + nqubits = max(d["index"]) nqubits += 1 circuit_params["nqubits"] = nqubits - c = cls(**circuit_params) # type: ignore + c = cls(**circuit_params) c = cls._apply_qir(c, qir) return c @@ -368,13 +400,13 @@ def _apply_qir( ) -> "AbstractCircuit": for d in qir: if "parameters" not in d: - c.apply_general_gate_delayed(d["gatef"], d["name"], mpo=d["mpo"])( # type: ignore - c, *d["index"], split=d["split"] # type: ignore + c.apply_general_gate_delayed(d["gatef"], d["name"], mpo=d["mpo"])( + c, *d["index"], split=d["split"] ) else: - c.apply_general_variable_gate_delayed(d["gatef"], d["name"], mpo=d["mpo"])( # type: ignore - c, *d["index"], **d["parameters"], split=d["split"] # type: ignore - ) + c.apply_general_variable_gate_delayed( + d["gatef"], d["name"], mpo=d["mpo"] + )(c, *d["index"], **d["parameters"], split=d["split"]) return c def inverse( @@ -400,16 +432,61 @@ def inverse( if "nqubits" not in circuit_params: circuit_params["nqubits"] = self._nqubits - c = type(self)(**circuit_params) # type: ignore + c = type(self)(**circuit_params) for d in reversed(self._qir): if "parameters" not in d: - self.apply_general_gate_delayed(d["gatef"].adjoint(), d["name"], mpo=d["mpo"])( # type: ignore - c, *d["index"], split=d["split"] # type: ignore - ) + if d["gatef"].n in self.sgates and d["gatef"].n not in [ + "wroot", + "sd", + "td", + ]: + self.apply_general_gate_delayed( + d["gatef"], d["name"], mpo=d["mpo"] + )(c, *d["index"], split=d["split"]) + elif d["gatef"].n in ["sd", "td"]: + self.apply_general_gate_delayed( + getattr(gates, d["gatef"].n[:-1]), d["name"], mpo=d["mpo"] + )(c, *d["index"], split=d["split"]) + else: + self.apply_general_gate_delayed( + d["gatef"].adjoint(), d["name"], mpo=d["mpo"] + )(c, *d["index"], split=d["split"]) else: - self.apply_general_variable_gate_delayed(d["gatef"].adjoint(), d["name"], mpo=d["mpo"])( # type: ignore - c, *d["index"], **d["parameters"], split=d["split"] # type: ignore - ) + if d["gatef"].n in ["r", "cr"]: + params = {k: v for k, v in d["parameters"].items()} + if "theta" in params: + params["theta"] = -params.get("theta", 0.0) + self.apply_general_variable_gate_delayed( + d["gatef"], d["name"], mpo=d["mpo"] + )(c, *d["index"], **params, split=d["split"]) + elif d["gatef"].n in ["u", "cu"]: + params = {k: v for k, v in d["parameters"].items()} + # deepcopy fail with tf+jit + params["lbd"] = -d["parameters"].get("phi", 0) + np.pi + params["phi"] = -d["parameters"].get("lbd", 0) + np.pi + self.apply_general_variable_gate_delayed( + d["gatef"], d["name"], mpo=d["mpo"] + )(c, *d["index"], **params, split=d["split"]) + elif d["gatef"].n in self.vgates and d["gatef"].n not in [ + "any", + "exp", + "exp1", + ]: + params = {k: v for k, v in d["parameters"].items()} + df = 0.0 + if d["gatef"].n == "iswap": + df = 1.0 + params["theta"] = -params.get("theta", df) + self.apply_general_variable_gate_delayed( + d["gatef"], d["name"], mpo=d["mpo"] + )(c, *d["index"], **params, split=d["split"]) + + # TODO(@refraction-ray): multi control gate? + + else: + self.apply_general_variable_gate_delayed( + d["gatef"].adjoint(), d["name"], mpo=d["mpo"] + )(c, *d["index"], **d["parameters"], split=d["split"]) return c @@ -437,6 +514,75 @@ def append_from_qir(self, qir: List[Dict[str, Any]]) -> None: """ self._apply_qir(self, qir) + def initial_mapping( + self, + logical_physical_mapping: Dict[int, int], + n: Optional[int] = None, + circuit_params: Optional[Dict[str, Any]] = None, + ) -> "AbstractCircuit": + """ + generate a new circuit with the qubit mapping given by ``logical_physical_mapping`` + + :param logical_physical_mapping: how to map logical qubits to the physical qubits on the new circuit + :type logical_physical_mapping: Dict[int, int] + :param n: number of qubit of the new circuit, can be different from the original one, defaults to None + :type n: Optional[int], optional + :param circuit_params: _description_, defaults to None + :type circuit_params: Optional[Dict[str, Any]], optional + :return: _description_ + :rtype: AbstractCircuit + """ + if circuit_params is None: + circuit_params = {} + if "nqubits" not in circuit_params: + if n is not None: + circuit_params["nqubits"] = n + else: + circuit_params["nqubits"] = self._nqubits + + c = type(self)(**circuit_params) + + for d in self.to_qir(): + mapped_index = [logical_physical_mapping[i] for i in d["index"]] + + if "parameters" not in d: + c.apply_general_gate_delayed(d["gatef"], d["name"], mpo=d["mpo"])( + c, *mapped_index, split=d["split"] + ) + else: + c.apply_general_variable_gate_delayed( + d["gatef"], d["name"], mpo=d["mpo"] + )(c, *mapped_index, **d["parameters"], split=d["split"]) + for d in self._extra_qir: + mapped_index = [logical_physical_mapping[i] for i in d["index"]] + dc = deepcopy(d) + dc["index"] = mapped_index + c._extra_qir.append(dc) + + return c + + def get_positional_logical_mapping(self) -> Dict[int, int]: + """ + Get positional logical mapping dict based on measure instruction. + This function is useful when we only measure part of the qubits in the circuit, + to process the count result from partial measurement, we must be aware of the mapping, + i.e. for each position in the count bitstring, what is the corresponding qubits (logical) + defined on the circuit + + :return: ``positional_logical_mapping`` + :rtype: Dict[int, int] + """ + id_mapping = {i: i for i in range(self._nqubits)} + if len(self._extra_qir) == 0: + return id_mapping + measure_index = [] + for inst in self._extra_qir: + if inst["name"] == "measure": + measure_index.append(inst["index"][0]) + if len(measure_index) == 0: + return id_mapping + return {i: j for i, j in enumerate(measure_index)} + @staticmethod def standardize_gate(name: str) -> str: """ @@ -453,10 +599,12 @@ def standardize_gate(name: str) -> str: name = g1 break if name not in sgates + vgates + mpogates: - logger.warning("gate name not in the common gate set that tc supported") + logger.warning( + "gate name %s not in the common gate set that tc supported" % name + ) return name - def gate_count(self, gate_list: Optional[Sequence[str]] = None) -> int: + def gate_count(self, gate_list: Optional[Union[str, Sequence[str]]] = None) -> int: """ count the gate number of the circuit @@ -471,7 +619,7 @@ def gate_count(self, gate_list: Optional[Sequence[str]] = None) -> int: >>> c.gate_count(["multicontrol", "toffoli"]) 2 - :param gate_list: gate name list to be counted, defaults to None (counting all gates) + :param gate_list: gate name or gate name list to be counted, defaults to None (counting all gates) :type gate_list: Optional[Sequence[str]], optional :return: the total number of all gates or gates in the ``gate_list`` :rtype: int @@ -479,13 +627,43 @@ def gate_count(self, gate_list: Optional[Sequence[str]] = None) -> int: if gate_list is None: return len(self._qir) else: + if isinstance(gate_list, str): + gate_list = [gate_list] gate_list = [self.standardize_gate(g) for g in gate_list] c = 0 for d in self._qir: - if d["name"] in gate_list: + if d["gatef"].n in gate_list: c += 1 return c + def gate_count_by_condition( + self, cond_func: Callable[[Dict[str, Any]], bool] + ) -> int: + """ + count the number of gates that satisfy certain condition + + :Example: + + >>> c = tc.Circuit(3) + >>> c.x(0) + >>> c.h(0) + >>> c.multicontrol(0, 1, 2, ctrl=[0, 1], unitary=tc.gates._x_matrix) + >>> c.gate_count_by_condition(lambda qir: qir["index"] == (0, )) + 2 + >>> c.gate_count_by_condition(lambda qir: qir["mpo"]) + 1 + + :param cond_func: the condition for counting the gate + :type cond_func: Callable[[Dict[str, Any]], bool] + :return: the total number of all gates which satisfy the ``condition`` + :rtype: int + """ + count = 0 + for d in self._qir: + if cond_func(d): + count += 1 + return count + def gate_summary(self) -> Dict[str, int]: """ return the summary dictionary on gate type - gate count pair @@ -498,22 +676,159 @@ def gate_summary(self) -> Dict[str, int]: summary[d["name"]] = summary.get(d["name"], 0) + 1 return summary - def to_qiskit(self) -> Any: + def measure_instruction(self, *index: int) -> None: + """ + add a measurement instruction flag, no effect on numerical simulation + + :param index: the corresponding qubits + :type index: int + """ + l = len(self._qir) + for ind in index: + d = { + "index": [ind], + "name": "measure", + "gatef": "measure", + "instruction": True, + "pos": l, + } + self._extra_qir.append(d) + + def reset_instruction(self, *index: int) -> None: + """ + add a reset instruction flag, no effect on numerical simulation + + :param index: the corresponding qubits + :type index: int + """ + l = len(self._qir) + for ind in index: + d = { + "index": [ind], + "name": "reset", + "gatef": "reset", + "instruction": True, + "pos": l, + } + self._extra_qir.append(d) + + def barrier_instruction(self, *index: List[int]) -> None: + """ + add a barrier instruction flag, no effect on numerical simulation + + :param index: the corresponding qubits + :type index: List[int] + """ + l = len(self._qir) + d = { + "index": index, + "name": "barrier", + "gatef": "barrier", + "instruction": True, + "pos": l, + } + self._extra_qir.append(d) + + def to_cirq(self, enable_instruction: bool = False) -> Any: + """ + Translate ``tc.Circuit`` to a cirq circuit object. + + :param enable_instruction: whether also export measurement and reset instructions + :type enable_instruction: bool, defaults to False + :return: A cirq circuit of this circuit. + """ + from .translation import qir2cirq + + qir = self.to_qir() + if enable_instruction is False: + return qir2cirq(qir, n=self._nqubits) + return qir2cirq(qir, n=self._nqubits, extra_qir=self._extra_qir) + + def to_qiskit( + self, enable_instruction: bool = False, enable_inputs: bool = False + ) -> Any: """ Translate ``tc.Circuit`` to a qiskit QuantumCircuit object. + :param enable_instruction: whether also export measurement and reset instructions + :type enable_instruction: bool, defaults to False + :param enable_inputs: whether also export the inputs + :type enable_inputs: bool, defaults to False :return: A qiskit object of this circuit. """ - from .translation import qir2qiskit + from .translation import perm_matrix, qir2qiskit qir = self.to_qir() - return qir2qiskit(qir, n=self._nqubits) + if enable_instruction: + extra_qir = self._extra_qir + else: + extra_qir = None + if enable_inputs and self.circuit_param.get("inputs") is not None: + initialization = perm_matrix(self._nqubits).T @ self.circuit_param["inputs"] + else: + initialization = None + return qir2qiskit( + qir, + n=self._nqubits, + extra_qir=extra_qir, + initialization=initialization, + ) + + def to_openqasm(self, **kws: Any) -> str: + """ + transform circuit to openqasm via qiskit circuit, + see https://qiskit.org/documentation/stubs/qiskit.circuit.QuantumCircuit.qasm.html + for usage on possible options for ``kws`` + + :return: circuit representation in openqasm format + :rtype: str + """ + return self.to_qiskit(enable_instruction=True).qasm(**kws) # type: ignore + + @classmethod + def from_openqasm( + cls, + qasmstr: str, + circuit_params: Optional[Dict[str, Any]] = None, + keep_measure_order: bool = False, + ) -> "AbstractCircuit": + from qiskit.circuit import QuantumCircuit + + if keep_measure_order is True: + from .translation import qiskit_from_qasm_str_ordered_measure + + qiskit_circ = qiskit_from_qasm_str_ordered_measure(qasmstr) + else: + qiskit_circ = QuantumCircuit.from_qasm_str(qasmstr) + c = cls.from_qiskit(qiskit_circ, circuit_params=circuit_params) + return c + + @classmethod + def from_openqasm_file( + cls, + file: str, + circuit_params: Optional[Dict[str, Any]] = None, + keep_measure_order: bool = False, + ) -> "AbstractCircuit": + from qiskit.circuit import QuantumCircuit + + if keep_measure_order is True: + from .translation import qiskit_from_qasm_str_ordered_measure + + with open(file) as f: + qasmstr = f.read() + qiskit_circ = qiskit_from_qasm_str_ordered_measure(qasmstr) + else: + qiskit_circ = QuantumCircuit.from_qasm_file(file) + c = cls.from_qiskit(qiskit_circ, circuit_params=circuit_params) + return c def draw(self, **kws: Any) -> Any: """ Visualise the circuit. This method recevies the keywords as same as qiskit.circuit.QuantumCircuit.draw. More details can be found here: https://qiskit.org/documentation/stubs/qiskit.circuit.QuantumCircuit.draw.html. + Interesting kws options include: ``idle_wires``(bool) :Example: >>> c = tc.Circuit(3) @@ -528,11 +843,16 @@ def draw(self, **kws: Any) -> Any: q_2: ┤ X ├───── └───┘ """ - return self.to_qiskit().draw(**kws) + return self.to_qiskit(enable_instruction=True).draw(**kws) @classmethod def from_qiskit( - cls, qc: Any, n: Optional[int] = None, inputs: Optional[List[float]] = None + cls, + qc: Any, + n: Optional[int] = None, + inputs: Optional[List[float]] = None, + circuit_params: Optional[Dict[str, Any]] = None, + binding_params: Optional[Union[Sequence[float], Dict[Any, float]]] = None, ) -> "AbstractCircuit": """ Import Qiskit QuantumCircuit object as a ``tc.Circuit`` object. @@ -552,6 +872,12 @@ def from_qiskit( :type n: int :param inputs: possible input wavefunction for ``tc.Circuit``, defaults to None :type inputs: Optional[List[float]], optional + :param circuit_params: kwargs given in Circuit.__init__ construction function, default to None. + :type circuit_params: Optional[Dict[str, Any]] + :param binding_params: (variational) parameters for the circuit. + Could be either a sequence or dictionary depending on the type of parameters in the Qiskit circuit. + For ``ParameterVectorElement`` use sequence. For ``Parameter`` use dictionary + :type binding_params: Optional[Union[Sequence[float], Dict[Any, float]]] :return: The same circuit but as tensorcircuit object :rtype: Circuit """ @@ -560,7 +886,14 @@ def from_qiskit( if n is None: n = qc.num_qubits - return qiskit2tc(qc.data, n, inputs, is_dm=cls.is_dm) # type: ignore + return qiskit2tc( # type: ignore + qc, + n, + inputs, + circuit_constructor=cls, + circuit_params=circuit_params, + binding_params=binding_params, + ) def vis_tex(self, **kws: Any) -> str: """ @@ -571,7 +904,7 @@ def vis_tex(self, **kws: Any) -> str: """ if (not self.is_mps) and (self.inputs is not None): init = ["" for _ in range(self._nqubits)] - init[self._nqubits // 2] = "\psi" + init[self._nqubits // 2] = r"\psi" okws = {"init": init} else: okws = {"init": None} # type: ignore @@ -600,6 +933,90 @@ def to_json(self, file: Optional[str] = None, simplified: bool = False) -> Any: json.dump(tcqasm, f) return json.dumps(tcqasm) + @classmethod + def from_qsim_file( + cls, file: str, circuit_params: Optional[Dict[str, Any]] = None + ) -> "AbstractCircuit": + with open(file, "r") as f: + lines = f.readlines() + if circuit_params is None: + circuit_params = {} + if "nqubits" not in circuit_params: + circuit_params["nqubits"] = int(lines[0]) + + c = cls(**circuit_params) + c = cls._apply_qsim(c, lines) + return c + + @staticmethod + def _apply_qsim(c: "AbstractCircuit", qsim_str: List[str]) -> "AbstractCircuit": + def _convert_ints_and_floats(x: str) -> Union[str, int, float]: + try: + return int(x) + except ValueError: + pass + + try: + return float(x) + except ValueError: + pass + + return x.lower() + + qsim_gates = [ + tuple(map(_convert_ints_and_floats, line.strip().split(" "))) + for line in qsim_str[1:] + if line + ] + # https://github.com/quantumlib/qsim/blob/master/docs/input_format.md + # https://github.com/jcmgray/quimb/blob/master/quimb/tensor/circuit.py#L241 + for gate in qsim_gates: + if gate[1] == "h": + getattr(c, "H")(gate[2]) + elif gate[1] == "x": + getattr(c, "X")(gate[2]) + elif gate[1] == "y": + getattr(c, "Y")(gate[2]) + elif gate[1] == "z": + getattr(c, "Z")(gate[2]) + elif gate[1] == "s": + getattr(c, "PHASE")(gate[2], theta=np.pi / 2) + elif gate[1] == "t": + getattr(c, "PHASE")(gate[2], theta=np.pi / 4) + elif gate[1] == "x_1_2": + getattr(c, "RX")(gate[2], theta=np.pi / 2) + elif gate[1] == "y_1_2": + getattr(c, "RY")(gate[2], theta=np.pi / 2) + elif gate[1] == "z_1_2": + getattr(c, "RZ")(gate[2], theta=np.pi / 2) + elif gate[1] == "w_1_2": + getattr(c, "U")(gate[2], theta=np.pi / 2, phi=-np.pi / 4, lbd=np.pi / 4) + elif gate[1] == "hz_1_2": + getattr(c, "WROOT")(gate[2]) + elif gate[1] == "cnot": + getattr(c, "CNOT")(gate[2], gate[3]) + elif gate[1] == "cx": + getattr(c, "CX")(gate[2], gate[3]) + elif gate[1] == "cy": + getattr(c, "CY")(gate[2], gate[3]) + elif gate[1] == "cz": + getattr(c, "CZ")(gate[2], gate[3]) + elif gate[1] == "is" or gate[1] == "iswap": + getattr(c, "ISWAP")(gate[2], gate[3]) + elif gate[1] == "rx": + getattr(c, "RX")(gate[2], theta=gate[3]) + elif gate[1] == "ry": + getattr(c, "RY")(gate[2], theta=gate[3]) + elif gate[1] == "rz": + getattr(c, "RZ")(gate[2], theta=gate[3]) + elif gate[1] == "fs" or gate[1] == "fsim": + i, j, theta, phi = gate[2:] + getattr(c, "ISWAP")(i, j, theta=-theta) # type: ignore + getattr(c, "CPHASE")(i, j, theta=-phi) # type: ignore + else: + raise NotImplementedError + return c + @classmethod def from_json( cls, jsonstr: str, circuit_params: Optional[Dict[str, Any]] = None @@ -690,7 +1107,9 @@ def cond_measurement(self, index: int) -> Tensor: :rtype: Tensor """ return self.general_kraus( # type: ignore - [np.array([[1.0, 0], [0, 0]]), np.array([[0, 0], [0, 1]])], index, name="measure" # type: ignore + [np.array([[1.0, 0], [0, 0]]), np.array([[0, 0], [0, 1]])], + index, + name="measure", ) cond_measure = cond_measurement @@ -710,7 +1129,9 @@ def prepend(self, c: "AbstractCircuit") -> "AbstractCircuit": self.__dict__.update(newc.__dict__) return self - def append(self, c: "AbstractCircuit") -> "AbstractCircuit": + def append( + self, c: "AbstractCircuit", indices: Optional[List[int]] = None + ) -> "AbstractCircuit": """ append circuit ``c`` before @@ -732,19 +1153,38 @@ def append(self, c: "AbstractCircuit") -> "AbstractCircuit": :param c: The other circuit to be appended :type c: BaseCircuit + :param indices: the qubit indices to which ``c`` is appended on. + Defaults to None, which means plain concatenation. + :type indices: Optional[List[int]], optional :return: The composed circuit :rtype: BaseCircuit """ qir1 = self.to_qir() qir2 = c.to_qir() + if indices is not None: + qir2_old = qir2 + qir2 = [] + for d in qir2_old: + # avoid modifying the original circuit + d = d.copy() + d["index"] = [indices[i] for i in d["index"]] + qir2.append(d) newc = type(self).from_qir(qir1 + qir2, self.circuit_param) self.__dict__.update(newc.__dict__) return self + def copy(self) -> "AbstractCircuit": + qir = self.to_qir() + c = type(self).from_qir(qir, self.circuit_param) + return c + def expectation( self, *ops: Tuple[tn.Node, List[int]], reuse: bool = True, + noise_conf: Optional[Any] = None, + nmc: int = 1000, + status: Optional[Tensor] = None, **kws: Any, ) -> Tensor: raise NotImplementedError @@ -754,7 +1194,11 @@ def expectation_ps( x: Optional[Sequence[int]] = None, y: Optional[Sequence[int]] = None, z: Optional[Sequence[int]] = None, + ps: Optional[Sequence[int]] = None, reuse: bool = True, + noise_conf: Optional[Any] = None, + nmc: int = 1000, + status: Optional[Tensor] = None, **kws: Any, ) -> Tensor: """ @@ -769,18 +1213,51 @@ def expectation_ps( >>> c.expectation_ps(x=[1], z=[0]) array(-0.99999994+0.j, dtype=complex64) + >>> c = tc.Circuit(2) + >>> c.cnot(0, 1) + >>> c.rx(0, theta=0.4) + >>> c.rx(1, theta=0.8) + >>> c.h(0) + >>> c.h(1) + >>> error1 = tc.channels.generaldepolarizingchannel(0.1, 1) + >>> error2 = tc.channels.generaldepolarizingchannel(0.06, 2) + >>> noise_conf = NoiseConf() + >>> noise_conf.add_noise("rx", error1) + >>> noise_conf.add_noise("cnot", [error2], [[0, 1]]) + >>> c.expectation_ps(x=[0], noise_conf=noise_conf, nmc=10000) + (0.46274087-3.764033e-09j) + :param x: sites to apply X gate, defaults to None :type x: Optional[Sequence[int]], optional :param y: sites to apply Y gate, defaults to None :type y: Optional[Sequence[int]], optional :param z: sites to apply Z gate, defaults to None :type z: Optional[Sequence[int]], optional + :param ps: or one can apply a ps structures instead of ``x``, ``y``, ``z``, + e.g. [0, 1, 3, 0, 2, 2] for X_1Z_2Y_4Y_5 + defaults to None, ``ps`` can overwrite ``x``, ``y`` and ``z`` + :type ps: Optional[Sequence[int]], optional :param reuse: whether to cache and reuse the wavefunction, defaults to True :type reuse: bool, optional + :param noise_conf: Noise Configuration, defaults to None + :type noise_conf: Optional[NoiseConf], optional + :param nmc: repetition time for Monte Carlo sampling for noisfy calculation, defaults to 1000 + :type nmc: int, optional + :param status: external randomness given by tensor uniformly from [0, 1], defaults to None, + used for noisfy circuit sampling + :type status: Optional[Tensor], optional :return: Expectation value :rtype: Tensor """ obs = [] + if ps is not None: + from .quantum import ps2xyz + + d = ps2xyz(ps) # type: ignore + x = d.get("x", None) + y = d.get("y", None) + z = d.get("z", None) + if x is not None: for i in x: obs.append([gates.x(), [i]]) # type: ignore @@ -790,4 +1267,6 @@ def expectation_ps( if z is not None: for i in z: obs.append([gates.z(), [i]]) # type: ignore - return self.expectation(*obs, reuse=reuse, **kws) # type: ignore + return self.expectation( + *obs, reuse=reuse, noise_conf=noise_conf, nmc=nmc, status=status, **kws # type: ignore + ) diff --git a/tensorcircuit/applications/README.md b/tensorcircuit/applications/README.md index c63ef6cf..202e06cf 100644 --- a/tensorcircuit/applications/README.md +++ b/tensorcircuit/applications/README.md @@ -1,3 +1,6 @@ +Code implementation in this submodule `applications` are not very well maintained and extensively tested. There are indeed many interesting pieces, but try on your own risk. + + ## Differentiable Quantum Architecture Search In `applications`, framework and relevant applications of DQAS are implemented, mainly in `vags.py` and `dqas.py`. diff --git a/tensorcircuit/applications/__init__.py b/tensorcircuit/applications/__init__.py index 99d7c732..70d128b0 100644 --- a/tensorcircuit/applications/__init__.py +++ b/tensorcircuit/applications/__init__.py @@ -1,5 +1,5 @@ """ The application codebase is related to research and previous version of tensorcircuit, the code inside is subject to change, be caution to use. -Most of the useful code is refactored and copied to other parts of tensorcircuit. +Most of the useful code is and will be refactored and copied to other parts of tensorcircuit. """ diff --git a/tensorcircuit/applications/ai/__init__.py b/tensorcircuit/applications/ai/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/tensorcircuit/applications/ai/ensemble.py b/tensorcircuit/applications/ai/ensemble.py new file mode 100644 index 00000000..a9d6b0d7 --- /dev/null +++ b/tensorcircuit/applications/ai/ensemble.py @@ -0,0 +1,163 @@ +""" +Useful utilities for ensemble +""" + +from typing import Any, List, Optional +import tensorflow as tf +import numpy as np + +NDArray = Any +kwargus = Any +model_type = Any + + +class bagging: # A.K.A. voting + def __init__(self) -> None: + self.models: List[model_type] = [] + self.model_trained: List[bool] = [] + self.count = 0 + self.need_confidence = True # Help in reducing numbers of get_confidence runs + self.permit_train = False + + def append(self, model: model_type, model_trained: bool) -> None: + """ + Add model to the voting method + """ + self.models.append(model) + self.model_trained.append(model_trained) + self.count += 1 + + def __train_model(self, i: int, **kwargs: kwargus) -> None: + """ + Train a model if it isn't trained already + """ + if not self.model_trained[i]: + self.need_confidence = True + self.model_trained[i] = True + self.models[i].fit(**kwargs) + + def train(self, **kwargs: kwargus) -> None: + """ + Train all models in the class, **kwargs expect to receive the argus that can be directly sent to tf.fit + Expected to be run after finishing compile + """ + if not self.permit_train: + raise ValueError("Models needed to be compiled before training") + for i in range(self.count): + if "verbose" in kwargs: + if kwargs["verbose"] == 1: + print("Model ", i + 1, "/", self.count, " is training...") + else: + print("Model ", i + 1, "/", self.count, " is training...") + self.__train_model(i, **kwargs) + + def compile(self, **kwargs: kwargus) -> None: + self.permit_train = True + + for i in range(self.count): + if not self.model_trained[i]: + dict_kwargs = kwargs.copy() + # TODO(@refraction-ray): still not compatible with new optimizer + # https://github.com/tensorflow/tensorflow/issues/58973 + self.models[i].compile(**dict_kwargs) + + def __get_confidence(self, model_index: int, input: NDArray) -> NDArray: + """ + Get the confidence value that is needed by voting. + Number of calling this function is reduced by self.need_confidence + """ + self.need_confidence = False + prediction = self.models[model_index].predict(input, verbose=0) + prediction_returns = np.zeros(len(prediction)) + for i in range(len(prediction)): + prediction_returns[i] = prediction[i][0] + return prediction_returns + + """ + Voting strategies begin + More voting strategies can be added beneath, a single function, and a if function in self.predict + """ + + def __voting_weight(self, array: NDArray) -> NDArray: + result = [] + for i in array: + result.append(self.__voting_weight_single(i)) + return np.array(result) + + def __voting_average(self, array: NDArray) -> NDArray: + result = np.mean(array, axis=1) + return result + + def __voting_weight_single(self, array: NDArray) -> float: + opp_array = np.ones(len(array)) - array + weight = np.absolute(opp_array - array) + weight_sum = np.sum(weight) + weight = weight / weight_sum + result = array * weight + return float(np.sum(result)) + + def __voting_most(self, array: NDArray) -> NDArray: + return_result = [] + for items in array: + items_ = items > 0.5 + result = 0 + for i in items_: + result += 1 if i else -1 + return_result.append(1 if result > 0 else 0) + return np.array(return_result) + + def predict( + self, input_data: NDArray, voting_policy: Optional[str] = None + ) -> NDArray: + """ + Input data is expected to be a 2D array that the first layer is different input data (into the trained models) + """ + if self.need_confidence: + predictions = [] + for i in range(self.count): + predictions.append(np.array(self.__get_confidence(i, input_data))) + self.predictions = np.transpose(np.array(predictions)) + if voting_policy == "weight": + return self.__voting_weight(self.predictions) + elif voting_policy == "most": + return self.__voting_most(self.predictions) + elif voting_policy == "average": + return self.__voting_average(self.predictions) + elif voting_policy is None: + return self.predictions + else: + raise ValueError("voting_policy must be none, weight, most, or average") + + def __acc_binarify(self, array: NDArray) -> NDArray: + """ + Needed for ACC test + """ + result = [] + for i in array: + result.append(1 if (i > 0.5) else 0) + return result + + def __eval_accuracy(self, input_data: NDArray) -> float: + input_data[1] = self.__acc_binarify(input_data[1]) + algo = tf.keras.metrics.Accuracy() + algo.reset_state() + algo.update_state(input_data[0], input_data[1]) + return float(algo.result().numpy()) + + def __eval_auc(self, input_data: NDArray) -> float: + algo = tf.keras.metrics.AUC() + algo.reset_state() + algo.update_state(input_data[0], input_data[1]) + return float(algo.result().numpy()) + + def eval(self, input_data: List[NDArray], evaluation_method: str = "acc") -> float: + """ + Expect input data to be a 2D array + which a 1D array of yTrue followed by a 1D array of yPred is expected to be the components of the 2D array + """ + if evaluation_method == "acc": + return self.__eval_accuracy(input_data) + elif evaluation_method == "auc": + return self.__eval_auc(input_data) + else: + raise ValueError("evaluation_method must be acc or auc") diff --git a/tensorcircuit/applications/dqas.py b/tensorcircuit/applications/dqas.py index c6bf7a22..161a2c73 100644 --- a/tensorcircuit/applications/dqas.py +++ b/tensorcircuit/applications/dqas.py @@ -1,6 +1,7 @@ """ Modules for DQAS framework """ + # possibly deprecated, multiprocessing is not the recommended way to do DQAS task now, using vmap! import sys @@ -486,9 +487,9 @@ def qaoa_simple_train( if "prob_model_func" in kws: pmf = kws["prob_model_func"] del kws["prob_model_func"] - kws[ - "prob_model" - ] = pmf() # in case keras model cannot pickled for multiprocessing map + kws["prob_model"] = ( + pmf() + ) # in case keras model cannot pickled for multiprocessing map if isinstance(graph, list): def graph_generator() -> Iterator[Graph]: @@ -778,7 +779,6 @@ def DQAS_search_pmb( try: for epoch in range(epochs): # iteration to update strcuture param - print("----------new epoch %s-----------" % epoch) deri_stp = [] diff --git a/tensorcircuit/applications/finance/__init__.py b/tensorcircuit/applications/finance/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/tensorcircuit/applications/finance/portfolio.py b/tensorcircuit/applications/finance/portfolio.py new file mode 100644 index 00000000..a9097241 --- /dev/null +++ b/tensorcircuit/applications/finance/portfolio.py @@ -0,0 +1,89 @@ +""" +Supplementary functions for portfolio optimization +""" + +from typing import Any + +import numpy as np + +Array = Any +Tensor = Any + + +def QUBO_from_portfolio(cov: Array, mean: Array, q: float, B: int, t: float) -> Tensor: + """ + convert portfolio parameters to a Q matrix + :param cov: n-by-n covariance numpy array + :param mean: numpy array of means + :param q: the risk preference of investor + :param B: budget + :param t: penalty factor + :return Q: n-by-n symmetric Q matrix + """ + n = cov.shape[0] + R = np.diag(mean) + S = np.ones((n, n)) - 2 * B * np.diag(np.ones(n)) + + Q = q * cov - R + t * S + return Q + + +class StockData: + """ + A class for converting real-world stock data to an annualized covariance matrix and annualized return. + + Attributes: + - data: A list of continuous stock data in the same time span. + - n_stocks: The number of stocks in the data. + - n_days: The number of trading days in the data. + + Methods: + - __init__(self, data): Initializes the StockData object. + - get_return(self, decimals=5): Calculates the annualized return. + - get_covariance(self, decimals=5): Calculates the annualized covariance matrix. + """ + + def __init__(self, data: Tensor): + """ + Initializes the StockData object. + + :param data: A list of continuous stock data in the same time span. + """ + self.data = data + self.n_stocks = len(data) + + # Check the number of days + n_days = [len(i) for i in data] + if max(n_days) != (sum(n_days) / len(n_days)): + raise Exception("Timespan of stocks should be the same") + self.n_days = len(data[1]) + + # Calculate the daily percentage price change + self.daily_change = [ + np.diff(data[i][:]) / data[i][:-1] for i in range(self.n_stocks) + ] + + def get_return(self, decimals: int = 5) -> Any: + """ + Calculates the annualized return (mu). + + :param decimals: Number of decimal places to round the result to (default: 5). + :return: The annualized return as an array rounded to the specified number of decimals. + """ + change = [[j + 1 for j in i] for i in self.daily_change] + ret = np.prod(change, axis=1) ** (252 / self.n_days) + return ret.round(decimals) + + def get_covariance(self, decimals: int = 5) -> Tensor: + """ + Calculates the annualized covariance matrix (sigma). + + :param decimals: Number of decimal places to round the result to (default: 5). + :return: The annualized covariance matrix rounded to the specified number of decimals. + """ + mean = np.mean(self.daily_change, axis=1) + relative_change = [ + [j - mean[i] for j in self.daily_change[i]] for i in range(6) + ] + cov = 252 / self.n_days * np.dot(relative_change, np.transpose(relative_change)) + return cov.round(decimals) diff --git a/tensorcircuit/applications/graphdata.py b/tensorcircuit/applications/graphdata.py index 1a53b24e..d5b694dd 100644 --- a/tensorcircuit/applications/graphdata.py +++ b/tensorcircuit/applications/graphdata.py @@ -290,7 +290,7 @@ def regular_graph_generator(d: int, n: int, weights: bool = False) -> Iterator[G def _maxcut(g: Graph, values: Sequence[int]) -> float: - """ + r""" To get the max-cut for the graph g by given values $$\pm 1$$ on each vertex as a list. :param g: The graph @@ -322,7 +322,7 @@ def ensemble_maxcut_solution(g: Graph, samples: int = 100) -> Tuple[float, float r = [] for _ in range(samples): r.append(maxcut_solution_bruteforce(g.send(None))[0]) - return np.mean(r), np.std(r) / np.sqrt(len(r)) + return np.mean(r), np.std(r) / np.sqrt(len(r)) # type: ignore def reduce_edges(g: Graph, m: int = 1) -> Sequence[Graph]: @@ -387,7 +387,7 @@ def split_ansatz(g: Graph, split: int = 2) -> Sequence[Graph]: """ edges = np.array(g.edges) ne = len(edges) - np.random.shuffle(edges) # type: ignore + np.random.shuffle(edges) gs = [nx.Graph() for _ in range(split)] for i in range(split): for j, k in edges[int(i * ne / split) : int((i + 1) * ne / split)]: diff --git a/tensorcircuit/applications/layers.py b/tensorcircuit/applications/layers.py index 7101ef8d..64eef546 100644 --- a/tensorcircuit/applications/layers.py +++ b/tensorcircuit/applications/layers.py @@ -1,6 +1,7 @@ """ Module for functions adding layers of circuits """ + import itertools import logging import sys @@ -81,7 +82,7 @@ def f( def generate_gate_layer(gate: str) -> None: - """ + r""" $$e^{-i\theta \sigma}$$ :param gate: @@ -108,7 +109,7 @@ def f( def generate_any_gate_layer(gate: str) -> None: - """ + r""" $$e^{-i\theta_i \sigma}$$ :param gate: @@ -392,7 +393,7 @@ def cirqcnotgate( return circuit def generate_cirq_gate_layer(gate: str) -> None: - """ + r""" $$e^{-i\theta \sigma}$$ :param gate: @@ -422,7 +423,7 @@ def f( setattr(thismodule, "cirq" + gate + "layer", f) def generate_cirq_any_gate_layer(gate: str) -> None: - """ + r""" $$e^{-i\theta \sigma}$$ :param gate: diff --git a/tensorcircuit/applications/optimization.py b/tensorcircuit/applications/optimization.py new file mode 100644 index 00000000..28c5c81a --- /dev/null +++ b/tensorcircuit/applications/optimization.py @@ -0,0 +1,364 @@ +""" +modules for QUBO problems in QAOA +""" + +from typing import List, Callable, Any, Optional, Tuple +from functools import partial + +import tensorflow as tf +import scipy.optimize as optimize + +from ..cons import backend +from ..quantum import measurement_results +from ..interfaces import scipy_interface +from ..templates.ansatz import QAOA_ansatz_for_Ising +from ..templates.conversions import QUBO_to_Ising + +Circuit = Any +Tensor = Any +Array = Any + + +def Ising_loss(c: Circuit, pauli_terms: Tensor, weights: List[float]) -> Any: + """ + computes the loss function for the Ising model based on a given quantum circuit, + a list of Pauli terms, and corresponding weights. + The offset is ignored. + + :param c: A quantum circuit object generating the state. + :param pauli_terms: A list of Pauli terms, where each term is represented as a list of 0/1 series. + :param weights: A list of weights corresponding to each Pauli term. + :return loss: A real number representing the computed loss value. + """ + loss = 0.0 + for k in range(len(pauli_terms)): + term = pauli_terms[k] + index_of_ones = [] + + for l in range(len(term)): + if term[l] == 1: + index_of_ones.append(l) + + # Compute expectation value based on the number of qubits involved in the Pauli term + if len(index_of_ones) == 1: + delta_loss = weights[k] * c.expectation_ps(z=[index_of_ones[0]]) + # Compute expectation value for a single-qubit Pauli term + else: + delta_loss = weights[k] * c.expectation_ps( + z=[index_of_ones[0], index_of_ones[1]] + ) + # Compute expectation value for a two-qubit Pauli term + + loss += delta_loss + + return backend.real(loss) + + +def QAOA_loss( + nlayers: int, + pauli_terms: Tensor, + weights: List[float], + params: List[float], + full_coupling: bool = False, + mixer: str = "X", +) -> Any: + """ + computes the loss function for the Quantum Approximate Optimization Algorithm (QAOA) applied to the Ising model. + + :param nlayers: The number of layers in the QAOA ansatz. + :param pauli_terms: A list of Pauli terms, where each term is represented as a list of 0/1 series. + :param weights: A list of weights corresponding to each Pauli term. + :param params: A list of parameter values used in the QAOA ansatz. + :param full_coupling (optional): A flag indicating whether to use all-to-all coupling in mixers. Default is False. + :paran mixer (optional): The mixer operator to use. Default is "X". The other options are "XY" and "ZZ". + :return: The computed loss value. + """ + c = QAOA_ansatz_for_Ising( + params, nlayers, pauli_terms, weights, mixer=mixer, full_coupling=full_coupling + ) + # Obtain the quantum circuit using QAOA_from_Ising function + + return Ising_loss(c, pauli_terms, weights) + # Compute the Ising loss using Ising_loss function on the obtained circuit + + +def QUBO_QAOA( + Q: Tensor, + nlayers: int, + iterations: int, + vvag: bool = False, + ncircuits: int = 10, + init_params: Optional[List[float]] = None, + mixer: str = "X", + learning_rate: float = 1e-2, + callback: Optional[Optional[Callable[[List[float], float], None]]] = None, + full_coupling: bool = False, +) -> Array: + """ + Performs the QAOA on a given QUBO problem. + Adam optimizer from TensorFlow is used. + + :param Q: The n-by-n square and symmetric Q-matrix representing the QUBO problem. + :param nlayers: The number of layers (depth) in the QAOA ansatz. + :param iterations: The number of iterations to run the optimization. + :param vvag (optional): A flag indicating whether to use vectorized variational adjoint gradient. Default is False. + :param ncircuits (optional): The number of circuits when using vectorized variational adjoint gradient. + Default is 10. + :param init_params (optional): The initial parameters for the ansatz circuit. + Default is None, which initializes the parameters randomly. + :paran mixer (optional): The mixer operator to use. Default is "X". The other options are "XY" and "ZZ". + :param learning_rate (optional): The learning rate for the Adam optimizer. Default is 1e-2. + :param callback (optional): A callback function that is executed during each iteration. Default is None. + :param full_coupling (optional): A flag indicating whether to use all-to-all coupling in mixers. Default is False. + :return params: The optimized parameters for the ansatz circuit. + """ + + pauli_terms, weights, _ = QUBO_to_Ising(Q) + + loss_val_grad = backend.value_and_grad( + partial( + QAOA_loss, + nlayers, + pauli_terms, + weights, + mixer=mixer, + full_coupling=full_coupling, + ) + ) + loss_val_grad = backend.jit(loss_val_grad, static_argnums=(1, 2)) + # Define the loss and gradients function using value_and_grad, which calculates both the loss value and gradients. + + if init_params is None: + params = backend.implicit_randn(shape=[2 * nlayers], stddev=0.5) + if vvag is True: + loss_val_grad = backend.vvag(loss_val_grad, argnums=0, vectorized_argnums=0) + params = backend.implicit_randn(shape=[ncircuits, 2 * nlayers], stddev=0.1) + # If init_params is not provided, initialize the parameters randomly. + # If vvag flag is set to True, use vectorized variational adjoint gradient (vvag) with multiple circuits. + else: + params = init_params + # If init_params is provided, use the provided parameters. + # Initialize the parameters for the ansatz circuit. + + # This can improve the performance by pre-compiling the loss and gradients function. + + opt = backend.optimizer(tf.keras.optimizers.Adam(learning_rate)) + # Define the optimizer (Adam optimizer) with the specified learning rate. + + for _ in range(iterations): + loss, grads = loss_val_grad(params) + # Calculate the loss and gradients using the loss_val_grad_jit function. + + params = opt.update(grads, params) + # Update the parameters using the optimizer and gradients. + + if callback is not None: + callback(loss, params) + # Execute the callback function with the current loss and parameters. + + return params + # Return the optimized parameters for the ansatz circuit. + + +def cvar_value(r: List[float], p: List[float], percent: float) -> Any: + """ + Compute the Conditional Value at Risk (CVaR) based on the measurement results. + + :param r: The observed outcomes after measurements. + :param p: Probabilities associated with each observed outcome. + :param percent: The cut-off percentage for CVaR computation. + :return: The calculated CVaR value. + """ + sorted_indices = tf.argsort(r) + p_sorted = tf.cast(tf.gather(p, sorted_indices), dtype=tf.float32) + r_sorted = tf.cast(tf.gather(r, sorted_indices), dtype=tf.float32) + + # Calculate the cumulative sum of sorted probabilities. + cumsum_p = tf.math.cumsum(p_sorted) + + # Create a tensor that evaluates to 1 if the condition is met, otherwise 0. + mask = tf.cast(tf.math.less(cumsum_p, percent), dtype=tf.float32) + + # Use mask to filter and sum the required elements for CVaR. + cvar_numerator = tf.reduce_sum(mask * p_sorted * r_sorted) + + # Compute the last remaining portion that exceeds the cut-off percentage. + last_portion_index = tf.math.argmax(tf.math.greater_equal(cumsum_p, percent)) + last_portion = (percent - cumsum_p[last_portion_index - 1]) * r_sorted[ + last_portion_index + ] + + # Calculate the final CVaR. + cvar_result = (cvar_numerator + last_portion) / percent + + return cvar_result + + +def cvar_from_circuit(circuit: Circuit, nsamples: int, Q: Tensor, alpha: float) -> Any: + """ + Directly calculate the Conditional Value at Risk (CVaR) from a circuit. + The CVaR depends on a bunch of measurements. + + :param circuit: The quantum circuit used to prepare the state. + :param nsamples: The number of samples to take for measurements. + :param Q: The Q-matrix representing the Quadratic Unconstrained Binary Optimization (QUBO) problem. + :param alpha: The cut-off percentage for CVaR. + :return: The calculated CVaR value. + """ + # Obtain and normalize measurement results + measurement_data = circuit.state() + results = measurement_results( + measurement_data, counts=nsamples, format="sample_int", jittable=True + ) + n_counts = tf.shape(results)[0] + + # Determine the number of qubits in the circuit and generate all possible states + n_qubits = len(Q) + all_states = tf.constant([format(i, f"0{n_qubits}b") for i in range(2**n_qubits)]) + all_binary = tf.reshape( + tf.strings.to_number(tf.strings.bytes_split(all_states), tf.float32), + (2**n_qubits, n_qubits), + ) + all_decimal = tf.range(2**n_qubits, dtype=tf.int32) + + # Convert the Q matrix to a TensorFlow tensor + Q_tensor = tf.convert_to_tensor(Q, dtype=tf.float32) + + # calculate cost values + values = tf.reduce_sum(all_binary * tf.matmul(all_binary, Q_tensor), axis=1) + + # Count the occurrences of each state and calculate probabilities + state_counts = tf.reduce_sum( + tf.cast(tf.equal(tf.reshape(results, [-1, 1]), all_decimal), tf.int32), axis=0 + ) + probabilities = tf.cast(state_counts, dtype=tf.float32) / tf.cast( + n_counts, dtype=tf.float32 + ) + + # Calculate CVaR + cvar_result = cvar_value(values, probabilities, alpha) + + return cvar_result + + +def cvar_from_expectation(circuit: Circuit, Q: Tensor, alpha: float) -> Any: + """ + Calculate the Conditional Value at Risk (CVaR) from the expectation values of a quantum circuit. + + :param circuit: The quantum circuit. + :param Q: The Q-matrix representing the Quadratic Unconstrained Binary Optimization (QUBO) problem. + :param alpha: The cut-off percentage for CVaR. + :return: The calculated CVaR value. + """ + + # Calculate the probability amplitudes for quantum circuit outcomes. + prob = tf.convert_to_tensor(circuit.probability(), dtype=tf.float32) + + # Generate all possible binary states for the given Q-matrix. + n_qubits = len(Q) + all_states = tf.constant( + [format(i, "0" + str(n_qubits) + "b") for i in range(2 ** len(Q))] + ) + all_binary = tf.reshape( + tf.strings.to_number(tf.strings.bytes_split(all_states), tf.float32), + (2**n_qubits, n_qubits), + ) + + # Convert the Q-matrix to a TensorFlow tensor. + Q_tensor = tf.convert_to_tensor(Q, dtype=tf.float32) + + # calculate cost values + elementwise_product = tf.multiply(all_binary, tf.matmul(all_binary, Q_tensor)) + values = tf.reduce_sum(elementwise_product, axis=1) + + # Calculate the CVaR value using the computed values and the probability distribution. + cvar_result = cvar_value(values, prob, alpha) + + return cvar_result + + +def cvar_loss( + nlayers: int, + Q: Tensor, + nsamples: int, + alpha: float, + expectation_based: bool, + params: List[float], +) -> Any: + """ + Calculate the CVaR loss for a given QUBO problem using the QAOA ansatz. + + :param nlayers: The number of layers (depth) in the QAOA ansatz. + :param Q: The Q-matrix representing the Quadratic Unconstrained Binary Optimization (QUBO) problem. + :param nsamples: The number of samples to take for measurements in the CVaR calculation. + :param alpha: The cut-off percentage for CVaR. + :param expectation_based: A flag indicating the type of CVaR ansatz (measurement-based or expectation-based). + :param params: The parameters for the QAOA ansatz circuit. + :return: The calculated CVaR loss. + """ + + pauli_terms, weights, _ = QUBO_to_Ising(Q) + + c = QAOA_ansatz_for_Ising(params, nlayers, pauli_terms, weights) + # Generate the QAOA ansatz circuit for the given parameters. + + if expectation_based is False: + return cvar_from_circuit(c, nsamples, Q, alpha) + # Calculate CVaR using circuit-based measurement results. + elif expectation_based is True: + return cvar_from_expectation(c, Q, alpha) + # Calculate CVaR using expectation values of the circuit. + else: + raise ValueError("Invalid CVaR ansatz type.") + # Raise an error if an invalid CVaR ansatz type is provided. + + +def QUBO_QAOA_cvar( + Q: Tensor, + nlayers: int, + alpha: float, + nsamples: int = 1000, + callback: Optional[Callable[[List[float], float], None]] = None, + expectation_based: bool = False, + maxiter: int = 1000, + init_params: Optional[Tuple[float,]] = None, +) -> Array: + """ + Perform the QUBO QAOA optimization with CVaR as the loss function. + + :param Q: The n-by-n square and symmetric Q-matrix representing the QUBO problem. + :param ansatz: The ansatz function to be used for QAOA. + :param nlayers: The number of layers (depth) in the QAOA ansatz. + :param alpha: The cut-off percentage for CVaR. + :param nsamples: The number of samples for measurements in the CVaR calculation. Default is 1000. + :param callback: A callback function to be called after each iteration. Default is None. + :param expectation_based: A flag indicating the type of CVaR ansatz (measurement-based or expectation-based). + Default is False. + :param maxiter: The maximum number of iterations for the optimization. Default is 1000. + :return: The optimized parameters for the ansatz circuit. + """ + loss = partial(cvar_loss, nlayers, Q, nsamples, alpha, expectation_based) + + f_scipy = scipy_interface(loss, shape=(2 * nlayers,), jit=True, gradient=False) + + if init_params is None: + params = backend.implicit_randn(shape=[2 * nlayers], stddev=0.5) + # If init_params is not provided, initialize the parameters randomly. + else: + params = init_params + # If init_params is provided, use the provided parameters. + + # Initialize the parameters for the ansatz circuit. + params = backend.implicit_randn(shape=[2 * nlayers], stddev=0.5) + + r = optimize.minimize( + f_scipy, + params, + method="COBYLA", + callback=callback, + options={"maxiter": maxiter}, + ) + # Perform the optimization using the COBYLA method from scipy.optimize. + + return r.x + # Return the optimized parameters for the ansatz circuit. diff --git a/tensorcircuit/applications/physics/__init__.py b/tensorcircuit/applications/physics/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/tensorcircuit/applications/physics/baseline.py b/tensorcircuit/applications/physics/baseline.py new file mode 100644 index 00000000..9105abaf --- /dev/null +++ b/tensorcircuit/applications/physics/baseline.py @@ -0,0 +1,57 @@ +""" +baseline calculators for physical systems +""" + +import numpy as np + + +def TFIM1Denergy( + L: int, Jzz: float = 1.0, Jx: float = 1.0, Pauli: bool = True +) -> float: + # PBC + # nice tutorial: https://arxiv.org/pdf/2009.09208.pdf + # further investigation on 1 TFIM solution structure is required + # will fail on AFM phase Jzz>Jx and Jzz>0 and odd sites (frustration in boundary) + e = 0 + if Pauli: + Jx *= 2 + Jzz *= 4 + for i in range(L): + q = np.pi * (2 * i - (1 + (-1) ** L) / 2) / L + e -= np.abs(Jx) / 2 * np.sqrt(1 + Jzz**2 / 4 / Jx**2 - Jzz / Jx * np.cos(q)) + return e + + +def Heisenberg1Denergy(L: int, Pauli: bool = True, maxiters: int = 1000) -> float: + # PBC + error = 1e-15 + eps = 1e-20 # avoid zero division + phi = np.zeros([L // 2, L // 2]) + phi2 = np.zeros([L // 2, L // 2]) + lamb = np.array([2 * i + 1 for i in range(L // 2)]) + for _ in range(maxiters): + k = 1 / L * (2 * np.pi * lamb + np.sum(phi, axis=-1) - np.diag(phi)) + for i in range(L // 2): + for j in range(L // 2): + phi2[i, j] = ( + np.arctan( + 2 + / ( + 1 / (np.tan(k[i] / 2) + eps) + - 1 / (np.tan(k[j] / 2) + eps) + + eps + ) + ) + * 2 + ) + if np.allclose(phi, phi2, rtol=error): # converged + break + phi = phi2.copy() + else: + raise ValueError( + "the maxiters %s is too small for bethe ansatz to converge" % maxiters + ) + e = -np.sum(1 - np.cos(k)) + L / 4 + if Pauli is True: + e *= 4 + return e # type: ignore diff --git a/tensorcircuit/applications/physics/fss.py b/tensorcircuit/applications/physics/fss.py new file mode 100644 index 00000000..f0490509 --- /dev/null +++ b/tensorcircuit/applications/physics/fss.py @@ -0,0 +1,110 @@ +""" +finite size scaling tools +""" + +from typing import List, Tuple, Optional + +import numpy as np + + +def data_collapse( + n: List[int], + p: List[float], + obs: List[List[float]], + pc: float, + nu: float, + beta: float = 0, + obs_type: int = 1, + fit_type: int = 0, + dobs: Optional[List[List[float]]] = None, +) -> Tuple[List[float], List[List[float]], List[List[float]], float]: + xL: List[List[float]] = [] # x=(p-pc)L^(1/\nv) + yL: List[List[float]] = [] # y=S(p,L)-S(pc,L) or S(p,L) + pc_list = [] + if not isinstance(p[0], list): + p = [p for _ in n] # type: ignore + for n0 in range(len(n)): + xL.append([]) + yL.append([]) + for p0 in range(len(p[n0])): # type: ignore + xL[n0].append((p[n0][p0] - pc) * (n[n0] ** (1 / nu))) # type: ignore + pc_L = pc_linear_interpolation(p[n0], obs[n0], pc) # type: ignore + if obs_type == 0: + yL[n0].append((obs[n0][p0] - pc_L) * n[n0] ** beta) + # entanglement with only collapse and no crossing + else: + yL[n0].append(obs[n0][p0] * n[n0] ** beta) + # tripartite mutual information + pc_list.append(pc_L) + if fit_type == 0: + xL_all = [] + for i in range(len(xL)): + xL_all.extend(xL[i]) + yL_ave = [] + loss = [] + for x0 in range(len(xL_all)): + ybar_list = [] + for n0 in range(len(n)): + if xL_all[x0] >= xL[n0][0] and xL_all[x0] <= xL[n0][-1]: + yinsert = pc_linear_interpolation(xL[n0], yL[n0], xL_all[x0]) + ybar_list.append(yinsert) + + ybar = np.mean(ybar_list) + mean_squared = [(ybar_list[i] - ybar) ** 2 for i in range(len(ybar_list))] + loss.append(np.sum(mean_squared)) + yL_ave.append(ybar) + loss = np.sum(loss) + + return pc_list, xL, yL, loss # type: ignore + + # fit_type == 1 + if dobs is None: + raise ValueError("uncertainty of each y has to be specified in `dobs`") + + datas = [] + for n0 in range(len(n)): + for i in range(len(xL[n0])): + datas.append([xL[n0][i], yL[n0][i], dobs[n0][i]]) + datas = sorted(datas, key=lambda x: x[0]) + loss = _quality_objective_v2(datas) # type: ignore + return pc_list, xL, yL, loss # type: ignore + + +def _quality_objective_v2(datas: List[List[float]]) -> float: + # https://journals.aps.org/prb/supplemental/10.1103/PhysRevB.101.060301/Supplement.pdf + loss = [] + for i in range(len(datas) - 2): + # i, i+1, i+2 + x, y, d = datas[i + 1] + x1, y1, d1 = datas[i] + x2, y2, d2 = datas[i + 2] + if np.abs(x - x1) < 1e-4 or np.abs(x - x2) < 1e-4: + continue + ybar = ((x2 - x) * y1 - (x1 - x) * y2) / (x2 - x1) + delta = ( + d**2 + + d1**2 * (x2 - x) ** 2 / (x2 - x1) ** 2 + + d2**2 * (x1 - x) ** 2 / (x2 - x1) ** 2 + ) + w = (y - ybar) ** 2 / delta + loss.append(w) + return np.mean(loss) # type: ignore + + +def pc_linear_interpolation(p: List[float], SA: List[float], pc_input: float) -> float: + if pc_input in p: + pc_index = p.index(pc_input) + pc = SA[pc_index] + else: + pr_index = 0 + for p0 in range(len(p)): + if p[p0] > pc_input: + pr_index = p0 + break + + x = [p[pr_index - 1], p[pr_index]] + y = [SA[pr_index - 1], SA[pr_index]] + linear_model = np.polyfit(x, y, 1) + linear_model_fn = np.poly1d(linear_model) + pc = linear_model_fn(pc_input) + return pc diff --git a/tensorcircuit/applications/utils.py b/tensorcircuit/applications/utils.py index 9ca73f0f..3ca54ff2 100644 --- a/tensorcircuit/applications/utils.py +++ b/tensorcircuit/applications/utils.py @@ -2,6 +2,7 @@ A collection of useful function snippets that irrelevant with the main modules or await for further refactor """ + import logging from typing import Any, Callable, Iterator, Optional, Sequence, Tuple @@ -349,7 +350,7 @@ def color_svg(circuit: cirq.Circuit, *coords: Tuple[int, int]) -> Any: :return: """ import xml - from cirq.contrib.svg import SVGCircuit # type: ignore + from cirq.contrib.svg import SVGCircuit svg_str = SVGCircuit(circuit)._repr_svg_() DOMTree = xml.dom.minidom.parseString(svg_str) # type: ignore @@ -388,6 +389,8 @@ def repr2array(inputs: str) -> Array: return np.array(outputs) +# duplicated, will remove later +# use the version in applications.physics.baseline.py def TFIM1Denergy( L: int, Jzz: float = 1.0, Jx: float = 1.0, Pauli: bool = True ) -> float: @@ -413,7 +416,7 @@ def Heisenberg1Denergy(L: int, Pauli: bool = True, maxiters: int = 1000) -> floa phi2 = np.zeros([L // 2, L // 2]) lamb = np.array([2 * i + 1 for i in range(L // 2)]) for _ in range(maxiters): - k = 1 / L * (2 * np.pi * lamb + np.sum(phi, axis=-1) - np.diag(phi)) # type: ignore + k = 1 / L * (2 * np.pi * lamb + np.sum(phi, axis=-1) - np.diag(phi)) for i in range(L // 2): for j in range(L // 2): phi2[i, j] = ( @@ -427,7 +430,7 @@ def Heisenberg1Denergy(L: int, Pauli: bool = True, maxiters: int = 1000) -> floa ) * 2 ) - if np.allclose(phi, phi2, rtol=error): # type: ignore # converged + if np.allclose(phi, phi2, rtol=error): # converged break phi = phi2.copy() else: diff --git a/tensorcircuit/applications/vags.py b/tensorcircuit/applications/vags.py index 352a3759..b2b5a612 100644 --- a/tensorcircuit/applications/vags.py +++ b/tensorcircuit/applications/vags.py @@ -1,6 +1,7 @@ """ DQAS application kernels as vag functions """ + from functools import lru_cache, partial, reduce import logging import operator @@ -514,7 +515,7 @@ def qaoa_noise_vag( pnnp = array_to_tensor(np.array(pnnp)) # complex with tf.GradientTape() as t: t.watch(pnnp) - loss = forward_func(pnnp, preset, gdata, measure_func, **kws) # type: ignore + loss = forward_func(pnnp, preset, gdata, measure_func, **kws) loss = cons.backend.real(loss) gr = t.gradient(loss, pnnp) if gr is None: @@ -681,6 +682,7 @@ def gatewise_vqe_vag( try: v = sympy.symbols("v_{0:64}") vv = sympy.symbols(["v_" + str(i) + "_0:32" for i in range(32)]) + # symbol pool def double_qubits_initial() -> Iterator[Sequence[Any]]: while True: @@ -750,7 +752,6 @@ def q(i: int) -> cirq.LineQubit: @lru_cache() def qft_circuit(n: int) -> cirq.Circuit: - circuit = cirq.Circuit() for i in reversed(range(n)): circuit.append(cirq.H(q(i))) diff --git a/tensorcircuit/applications/vqes.py b/tensorcircuit/applications/vqes.py index 83d4c4db..a542268b 100644 --- a/tensorcircuit/applications/vqes.py +++ b/tensorcircuit/applications/vqes.py @@ -1,6 +1,7 @@ """ VQNHE application """ + from functools import lru_cache from itertools import product import time @@ -28,6 +29,9 @@ dtype = np.complex128 # only guarantee compatible with float64 mode # only support tf backend +# i.e. use the following setup in the code +# tc.set_backend("tensorflow") +# tc.set_dtype("complex128") x = np.array([[0, 1.0], [1.0, 0]], dtype=dtype) y = np.array([[0, -1j], [1j, 0]], dtype=dtype) @@ -282,7 +286,6 @@ def create_real_model( width: int = 2, **kws: Any, ) -> Model: - model = tf.keras.Sequential() for _ in range(depth): model.add(tf.keras.layers.Dense(width * self.n, activation="relu")) @@ -377,7 +380,11 @@ def create_circuit( return self.create_hea2_circuit(**kws) if choose == "hn": return self.create_hn_circuit(**kws) - raise ValueError("no such choose option: %s" % choose) + return self.create_functional_circuit(**kws) # type: ignore + + def create_functional_circuit(self, **kws: Any) -> Callable[[Tensor], Tensor]: + func = kws.get("func") + return func # type: ignore def create_hn_circuit(self, **kws: Any) -> Callable[[Tensor], Tensor]: def circuit(a: Tensor) -> Tensor: @@ -485,7 +492,6 @@ def plain_evaluation(self, cv: Tensor) -> Tensor: :rtype: Tensor """ with tf.GradientTape() as tape: - c = self.circuit(cv) if not self.shortcut: loss = tf.math.real(vqe_energy(c, self.hamiltonian)) @@ -666,6 +672,6 @@ def multi_training( if rs: es = [r["energy"] for r in rs] ind = np.argmin(es) - self.assign(rs[ind]["model_weights"], rs[ind]["circuit_weights"]) # type: ignore - self.history = rs[ind]["history"] # type: ignore + self.assign(rs[ind]["model_weights"], rs[ind]["circuit_weights"]) + self.history = rs[ind]["history"] return rs diff --git a/tensorcircuit/asciiart.py b/tensorcircuit/asciiart.py index f4a635aa..b5c9e3b4 100644 --- a/tensorcircuit/asciiart.py +++ b/tensorcircuit/asciiart.py @@ -1,6 +1,7 @@ """ Some ascii art from https://www.asciiart.eu/, have fun! """ + # pylint: disable=invalid-name import hashlib @@ -38,7 +39,7 @@ def __str__(self) -> str: (*)`(*) """ -__bigbike__ = """ +__bigbike__ = r""" $" *. d$$$$$$$P" $ J ^$. 4r " @@ -58,7 +59,7 @@ def __str__(self) -> str: "*==*"" ^"*==*"" Gilo94' """ -__moon__ = """ +__moon__ = r""" ___---___ .-- --. ./ () .-. \. @@ -77,7 +78,7 @@ def __str__(self) -> str: """ -__cat__ = """ +__cat__ = r""" ,_ _ |\ _,-~/ / _ _ | ,--. @@ -90,7 +91,7 @@ def __str__(self) -> str: ((_/`(____,-' """ -__moonlanding__ = """ +__moonlanding__ = r""" _ _ ____________.--. |\|_|//_.-"" .' \ /| | |.-"-"-.| / \_/ | | @@ -124,7 +125,7 @@ def __str__(self) -> str: """ -__moonshot__ = """ +__moonshot__ = r""" .-. ( ( `-' diff --git a/tensorcircuit/backends/abstract_backend.py b/tensorcircuit/backends/abstract_backend.py index d032997f..4a7ccecd 100644 --- a/tensorcircuit/backends/abstract_backend.py +++ b/tensorcircuit/backends/abstract_backend.py @@ -1,6 +1,7 @@ """ Backend magic inherited from tensornetwork: abstract backend """ + # pylint: disable=invalid-name # pylint: disable=unused-variable @@ -25,7 +26,7 @@ def copy(self: Any, a: Tensor) -> Tensor: :param a: tensor in matrix form :type a: Tensor - :return: matrix exponential of matrix ``a`` + :return: the copy tensor of ``a`` :rtype: Tensor """ raise NotImplementedError( @@ -345,6 +346,34 @@ def adjoint(self: Any, a: Tensor) -> Tensor: """ return self.conj(self.transpose(a)) + def det(self: Any, a: Tensor) -> Tensor: + """ + Return the determinant scalar of a tensor ``a`` + + :param a: Input tensor + :type a: Tensor + :return: determinant of ``a`` + :rtype: Tensor + """ + raise NotImplementedError( + "Backend '{}' has not implemented `det`.".format(self.name) + ) + + def schur(self: Any, a: Tensor, output: str = "real") -> Tuple[Tensor, Tensor]: + """ + Compute Schur decomposition of a matrix. + + :param a: _description_ + :type a: Tensor + :param output: _description_, defaults to "real" + :type output: str, optional + :return: T, Z so that ZTZ^H = a + :rtype: Tuple[Tensor, Tensor] + """ + raise NotImplementedError( + "Backend '{}' has not implemented `schur`.".format(self.name) + ) + def i(self: Any, dtype: str) -> Tensor: """ Return 1.j in as a tensor compatible with the backend. @@ -462,6 +491,31 @@ def mean( "Backend '{}' has not implemented `mean`.".format(self.name) ) + def std( + self: Any, + a: Tensor, + axis: Optional[Sequence[int]] = None, + keepdims: bool = False, + ) -> Tensor: + """ + Compute the standard deviation along the specified axis. + + :param a: _description_ + :type a: Tensor + :param axis: Axis or axes along which the standard deviation is computed, + defaults to None, implying all axis + :type axis: Optional[Sequence[int]], optional + :param keepdims: If this is set to True, + the axes which are reduced are left in the result as dimensions with size one, + defaults to False + :type keepdims: bool, optional + :return: _description_ + :rtype: Tensor + """ + raise NotImplementedError( + "Backend '{}' has not implemented `std`.".format(self.name) + ) + def min(self: Any, a: Tensor, axis: Optional[int] = None) -> Tensor: """ Return the minimum of an array or minimum along an axis. @@ -746,6 +800,26 @@ def solve(self: Any, A: Tensor, b: Tensor, **kws: Any) -> Tensor: "Backend '{}' has not implemented `solve`.".format(self.name) ) + def searchsorted(self: Any, a: Tensor, v: Tensor, side: str = "left") -> Tensor: + """ + Find indices where elements should be inserted to maintain order. + + :param a: input array sorted in ascending order + :type a: Tensor + :param v: value to inserted + :type v: Tensor + :param side: If ‘left’, the index of the first suitable location found is given. + If ‘right’, return the last such index. + If there is no suitable index, return either 0 or N (where N is the length of a), + defaults to "left" + :type side: str, optional + :return: Array of insertion points with the same shape as v, or an integer if v is a scalar. + :rtype: Tensor + """ + raise NotImplementedError( + "Backend '{}' has not implemented `searchsorted`.".format(self.name) + ) + def tree_map(self: Any, f: Callable[..., Any], *pytrees: Any) -> Any: """ Return the new tree map with multiple arg function ``f`` through pytrees. @@ -1047,6 +1121,41 @@ def stateful_randc( "Backend '{}' has not implemented `stateful_randc`.".format(self.name) ) + def probability_sample( + self: Any, shots: int, p: Tensor, status: Optional[Tensor] = None, g: Any = None + ) -> Tensor: + """ + Drawn ``shots`` samples from probability distribution p, given the external randomness + determined by uniform distributed ``status`` tensor or backend random generator ``g``. + This method is similar with ``stateful_randc``, but it supports ``status`` beyond ``g``, + which is convenient when jit or vmap + + :param shots: Number of samples to draw with replacement + :type shots: int + :param p: prbability vector + :type p: Tensor + :param status: external randomness as a tensor with each element drawn uniformly from [0, 1], + defaults to None + :type status: Optional[Tensor], optional + :param g: backend random genrator, defaults to None + :type g: Any, optional + :return: The drawn sample as an int tensor + :rtype: Tensor + """ + if status is not None: + status = self.convert_to_tensor(status) + elif g is not None: + status = self.stateful_randu(g, shape=[shots]) + else: + status = self.implicit_randu(shape=[shots]) + p = p / self.sum(p) + p_cuml = self.cumsum(p) + r = p_cuml[-1] * (1 - self.cast(status, p.dtype)) + ind = self.searchsorted(p_cuml, r) + a = self.arange(self.shape_tuple(p)[0]) + res = self.gather1d(a, ind) + return res + def gather1d(self: Any, operand: Tensor, indices: Tensor) -> Tensor: """ Return ``operand[indices]``, both ``operand`` and ``indices`` are rank-1 tensor. @@ -1254,6 +1363,28 @@ def switch( "Backend '{}' has not implemented `switch`.".format(self.name) ) + def scan( + self: Any, f: Callable[[Tensor, Tensor], Tensor], xs: Tensor, init: Tensor + ) -> Tensor: + # use restricted f as tensorflow + """ + This API follows ``tf.scan`` covention, + i.e. no ys supported as jax + + :param f: _description_ + :type f: Callable[Tuple[Tensor, Tensor], Tensor] + :param xs: _description_ + :type xs: Tensor + :param init: _description_ + :type init: Tensor + :return: _description_ + :rtype: Tensor + """ + carry = init + for x in xs: + carry = f(carry, x) + return carry + def stop_gradient(self: Any, a: Tensor) -> Tensor: """ Stop backpropagation from ``a``. @@ -1409,16 +1540,19 @@ def wrapper(*args: Any, **kws: Any) -> Any: args, tuple( [ - self.reshape( - self.eye(self.sizen(arg), dtype=arg.dtype), - [-1] + list(self.shape_tuple(arg)), - ) - if i == argnum - else self.reshape( - self.zeros( - [self.sizen(arg), self.sizen(arg)], dtype=arg.dtype - ), - [-1] + list(self.shape_tuple(arg)), + ( + self.reshape( + self.eye(self.sizen(arg), dtype=arg.dtype), + [-1] + list(self.shape_tuple(arg)), + ) + if i == argnum + else self.reshape( + self.zeros( + [self.sizen(arg), self.sizen(arg)], + dtype=arg.dtype, + ), + [-1] + list(self.shape_tuple(arg)), + ) ) for i, arg in enumerate(args) ] @@ -1471,22 +1605,24 @@ def _first(x: Sequence[Any]) -> Any: collect = _first # type: ignore jjs = [] - for k in range(len(values)): # type: ignore + for k in range(len(values)): jj = self.vmap(vjp1, vectorized_argnums=2)( pf, args, collect( [ - self.reshape( - self.eye(self.sizen(v), dtype=v.dtype), - [-1] + list(self.shape_tuple(v)), - ) - if i == k - else self.reshape( - self.zeros( - [self.sizen(v), self.sizen(v)], dtype=v.dtype - ), - [-1] + list(self.shape_tuple(v)), + ( + self.reshape( + self.eye(self.sizen(v), dtype=v.dtype), + [-1] + list(self.shape_tuple(v)), + ) + if i == k + else self.reshape( + self.zeros( + [self.sizen(v), self.sizen(v)], dtype=v.dtype + ), + [-1] + list(self.shape_tuple(v)), + ) ) for i, v in enumerate(values) ] @@ -1526,6 +1662,7 @@ def jit( f: Callable[..., Any], static_argnums: Optional[Union[int, Sequence[int]]] = None, jit_compile: Optional[bool] = None, + **kws: Any ) -> Callable[..., Any]: """ Return the jitted version of function ``f``. diff --git a/tensorcircuit/backends/backend_factory.py b/tensorcircuit/backends/backend_factory.py index 10cf82bf..c665a76c 100644 --- a/tensorcircuit/backends/backend_factory.py +++ b/tensorcircuit/backends/backend_factory.py @@ -4,6 +4,8 @@ from typing import Any, Dict, Text, Union +import tensornetwork as tn + try: # old version tn compatiblity from tensornetwork.backends import base_backend @@ -18,6 +20,7 @@ from .jax_backend import JaxBackend from .tensorflow_backend import TensorFlowBackend from .pytorch_backend import PyTorchBackend +from .cupy_backend import CuPyBackend bk = Any # tnbackend @@ -26,8 +29,11 @@ "jax": JaxBackend, "tensorflow": TensorFlowBackend, "pytorch": PyTorchBackend, # no intention to fully maintain this one + "cupy": CuPyBackend, # no intention to fully maintain this one } +tn.backends.backend_factory._BACKENDS["cupy"] = CuPyBackend + _INSTANTIATED_BACKENDS: Dict[str, bk] = dict() @@ -43,6 +49,7 @@ def get_backend(backend: Union[Text, bk]) -> bk: """ if isinstance(backend, tnbackend): return backend + backend = backend.lower() if backend not in _BACKENDS: raise ValueError("Backend '{}' does not exist".format(backend)) if backend in _INSTANTIATED_BACKENDS: diff --git a/tensorcircuit/backends/cupy_backend.py b/tensorcircuit/backends/cupy_backend.py new file mode 100644 index 00000000..11260d19 --- /dev/null +++ b/tensorcircuit/backends/cupy_backend.py @@ -0,0 +1,483 @@ +""" +CuPy backend. Not in the tensornetwork package and highly experimental. +""" + +# pylint: disable=invalid-name + +import logging +import warnings +from typing import Any, Callable, Optional, Sequence, Tuple, Union + +import numpy as np +import scipy + +try: # old version tn compatiblity + from tensornetwork.backends import base_backend + + tnbackend = base_backend.BaseBackend + +except ImportError: + from tensornetwork.backends import abstract_backend + + tnbackend = abstract_backend.AbstractBackend + + +from .abstract_backend import ExtendedBackend + +logger = logging.getLogger(__name__) + +dtypestr: str +Tensor = Any + +cp: Any +cpx: Any + + +class CuPyBackend(tnbackend, ExtendedBackend): # type: ignore + def __init__(self) -> None: + super().__init__() + try: + import cupy + import cupyx + except ImportError: + raise ImportError( + "CuPy not installed, please switch to a different " + "backend or install CuPy." + ) + global cp + global cpx + cp = cupy + cpx = cupyx + self.name = "cupy" + + def convert_to_tensor(self, a: Tensor) -> Tensor: + if not isinstance(a, cp.ndarray) and not cp.isscalar(a): + a = cp.array(a) + a = cp.asarray(a) + return a + + def sum( + self: Any, + a: Tensor, + axis: Optional[Sequence[int]] = None, + keepdims: bool = False, + ) -> Tensor: + return cp.sum(a, axis=axis, keepdims=keepdims) + + def conj(self, tensor: Tensor) -> Tensor: + return tensor.conj() + + def sign(self, tensor: Tensor) -> Tensor: + return cp.sign(tensor) + + def multiply(self, tensor1: Tensor, tensor2: Tensor) -> Tensor: + return tensor1 * tensor2 + + def norm(self, tensor: Tensor) -> Tensor: + return cp.linalg.norm(tensor) + + def shape_tuple(self, tensor: Tensor) -> Tuple[int]: + return tensor.shape # type:ignore + + def tensordot( + self, a: Tensor, b: Tensor, axes: Union[int, Sequence[Sequence[int]]] + ) -> Tensor: + return cp.tensordot(a, b, axes) + + def outer_product(self, tensor1: Tensor, tensor2: Tensor) -> Tensor: + return cp.tensordot(tensor1, tensor2, 0) + + def transpose(self, tensor: Tensor, perm: Optional[Sequence[int]] = None) -> Tensor: + return cp.transpose(tensor, perm) + + def reshape(self, tensor: Tensor, shape: Tensor) -> Tensor: + return cp.reshape(tensor, np.asarray(shape).astype(np.int32)) + + def eye( + self, N: int, dtype: Optional[str] = None, M: Optional[int] = None + ) -> Tensor: + if dtype is None: + dtype = dtypestr + return cp.eye(N, M=M, dtype=dtype) + + def ones(self, shape: Sequence[int], dtype: Optional[str] = None) -> Tensor: + if dtype is None: + dtype = dtypestr + return cp.ones(shape, dtype=dtype) + + def zeros(self, shape: Sequence[int], dtype: Optional[str] = None) -> Tensor: + if dtype is None: + dtype = dtypestr + return cp.zeros(shape, dtype=dtype) + + def copy(self, a: Tensor) -> Tensor: + return a.copy() + + def expm(self, a: Tensor) -> Tensor: + return self.convert_to_tensor(scipy.linalg.expm(self.numpy(a))) + + def abs(self, a: Tensor) -> Tensor: + return cp.abs(a) + + def sin(self, a: Tensor) -> Tensor: + return cp.sin(a) + + def cos(self, a: Tensor) -> Tensor: + return cp.cos(a) + + # acos acosh asin asinh atan atan2 atanh cosh (cos) tan tanh sinh (sin) + def acos(self, a: Tensor) -> Tensor: + return cp.arccos(a) + + def acosh(self, a: Tensor) -> Tensor: + return cp.arccosh(a) + + def asin(self, a: Tensor) -> Tensor: + return cp.arcsin(a) + + def asinh(self, a: Tensor) -> Tensor: + return cp.arcsinh(a) + + def atan(self, a: Tensor) -> Tensor: + return cp.arctan(a) + + def atan2(self, y: Tensor, x: Tensor) -> Tensor: + return cp.arctan2(y, x) + + def atanh(self, a: Tensor) -> Tensor: + return cp.arctanh(a) + + def cosh(self, a: Tensor) -> Tensor: + return cp.cosh(a) + + def tan(self, a: Tensor) -> Tensor: + return cp.tan(a) + + def tanh(self, a: Tensor) -> Tensor: + return cp.tanh(a) + + def sinh(self, a: Tensor) -> Tensor: + return cp.sinh(a) + + def size(self, a: Tensor) -> Tensor: + return a.size + + def eigvalsh(self, a: Tensor) -> Tensor: + return cp.linalg.eigvalsh(a) + + def kron(self, a: Tensor, b: Tensor) -> Tensor: + return cp.kron(a, b) + + def dtype(self, a: Tensor) -> str: + return a.dtype.__str__() # type: ignore + + def numpy(self, a: Tensor) -> Tensor: + if isinstance(a, cp.ndarray): + return a.get() + else: + return np.array(a) + + def i(self, dtype: Any = None) -> Tensor: + if not dtype: + dtype = npdtype # type: ignore + if isinstance(dtype, str): + dtype = getattr(np, dtype) + return cp.array(1j, dtype=dtype) + + def stack(self, a: Sequence[Tensor], axis: int = 0) -> Tensor: + return cp.stack(a, axis=axis) + + def concat(self, a: Sequence[Tensor], axis: int = 0) -> Tensor: + return cp.concatenate(a, axis=axis) + + def tile(self, a: Tensor, rep: Tensor) -> Tensor: + return cp.tile(a, rep) + + def mean( + self, + a: Tensor, + axis: Optional[Sequence[int]] = None, + keepdims: bool = False, + ) -> Tensor: + return cp.mean(a, axis=axis, keepdims=keepdims) + + def std( + self, a: Tensor, axis: Optional[Sequence[int]] = None, keepdims: bool = False + ) -> Tensor: + return cp.std(a, axis=axis, keepdims=keepdims) + + def unique_with_counts(self, a: Tensor, **kws: Any) -> Tuple[Tensor, Tensor]: + return cp.unique(a, return_counts=True) # type: ignore + + def min(self, a: Tensor, axis: Optional[int] = None) -> Tensor: + return cp.min(a, axis=axis) + + def max(self, a: Tensor, axis: Optional[int] = None) -> Tensor: + return cp.max(a, axis=axis) + + def argmax(self, a: Tensor, axis: int = 0) -> Tensor: + return cp.argmax(a, axis=axis) + + def argmin(self, a: Tensor, axis: int = 0) -> Tensor: + return cp.argmin(a, axis=axis) + + def sigmoid(self, a: Tensor) -> Tensor: + return cpx.scipy.special.expit(a) + + def relu(self, a: Tensor) -> Tensor: + return (abs(a) + a) / 2 + # this impl seems to be the fastest + # see https://stackoverflow.com/questions/32109319/how-to-implement-the-relu-function-in-numpy + + def softmax(self, a: Sequence[Tensor], axis: Optional[int] = None) -> Tensor: + return cpx.scipy.special.softmax(a, axis=axis) + + def onehot(self, a: Tensor, num: int) -> Tensor: + res = cp.eye(num)[a.reshape([-1])] + return res.reshape(list(a.shape) + [num]) + # https://stackoverflow.com/questions/38592324/one-hot-encoding-using-numpy + + def cumsum(self, a: Tensor, axis: Optional[int] = None) -> Tensor: + return cp.cumsum(a, axis) + + def is_tensor(self, a: Any) -> bool: + if isinstance(a, cp.ndarray): + return True + return False + + def real(self, a: Tensor) -> Tensor: + return cp.real(a) + + def imag(self, a: Tensor) -> Tensor: + return cp.imag(a) + + def cast(self, a: Tensor, dtype: str) -> Tensor: + with warnings.catch_warnings(): + warnings.simplefilter("ignore", np.ComplexWarning) + if isinstance(dtype, str): + return a.astype(getattr(np, dtype)) + return a.astype(dtype) + + def arange(self, start: int, stop: Optional[int] = None, step: int = 1) -> Tensor: + if stop is None: + return cp.arange(start=0, stop=start, step=step) + return cp.arange(start=start, stop=stop, step=step) + + def mod(self, x: Tensor, y: Tensor) -> Tensor: + return cp.mod(x, y) + + def right_shift(self, x: Tensor, y: Tensor) -> Tensor: + return cp.right_shift(x, y) + + def left_shift(self, x: Tensor, y: Tensor) -> Tensor: + return cp.left_shift(x, y) + + def solve(self, A: Tensor, b: Tensor, assume_a: str = "gen") -> Tensor: # type: ignore + raise NotImplementedError + + def searchsorted(self, a: Tensor, v: Tensor, side: str = "left") -> Tensor: + return cp.searchsorted(a, v, side=side) + + def set_random_state( + self, seed: Optional[int] = None, get_only: bool = False + ) -> Any: + g = cp.random.default_rng(seed) # None auto supported + if get_only is False: + self.g = g + return g + + def stateful_randn( + self, + g: "cp.random.Generator", + shape: Union[int, Sequence[int]] = 1, + mean: float = 0, + stddev: float = 1, + dtype: str = "32", + ) -> Tensor: + if isinstance(dtype, str): + dtype = dtype[-2:] + if isinstance(shape, int): + shape = (shape,) + r = g.normal(loc=mean, scale=stddev, size=shape) + if dtype == "32": + r = r.astype(np.float32) + elif dtype == "64": + r = r.astype(np.float64) + elif not isinstance(dtype, str): + r = r.astype(dtype) + else: + raise ValueError("unspported `dtype` %s" % dtype) + return r + + def stateful_randu( + self, + g: "cp.random.Generator", + shape: Union[int, Sequence[int]] = 1, + low: float = 0, + high: float = 1, + dtype: str = "32", + ) -> Tensor: + if isinstance(dtype, str): + dtype = dtype[-2:] + if isinstance(shape, int): + shape = (shape,) + r = g.random(shape) * (high - low) + low + if dtype == "32": + r = r.astype(np.float32) + elif dtype == "64": + r = r.astype(np.float64) + elif not isinstance(dtype, str): + r = r.astype(dtype) + else: + raise ValueError("unspported `dtype` %s" % dtype) + return r + + def stateful_randc( + self, + g: "cp.random.Generator", + a: Union[int, Sequence[int], Tensor], + shape: Union[int, Sequence[int]], + p: Optional[Union[Sequence[float], Tensor]] = None, + ) -> Tensor: + if isinstance(shape, int): + shape = (shape,) + return g.choice(a, size=shape, replace=True, p=p) + + def scatter(self, operand: Tensor, indices: Tensor, updates: Tensor) -> Tensor: + operand_new = cp.copy(operand) + operand_new[tuple([indices[:, i] for i in range(indices.shape[1])])] = updates + return operand_new + + def coo_sparse_matrix( + self, indices: Tensor, values: Tensor, shape: Tensor + ) -> Tensor: + values = self.convert_to_tensor(values) + indices = self.convert_to_tensor(indices).T + return cp.sparse.coo_matrix((values, indices), shape=shape) + + def sparse_dense_matmul( + self, + sp_a: Tensor, + b: Tensor, + ) -> Tensor: + return sp_a @ b + + def to_dense(self, sp_a: Tensor) -> Tensor: + return sp_a.todense() + + def is_sparse(self, a: Tensor) -> bool: + return cpx.scipy.sparse.issparse(a) # type: ignore + + def cond( + self, + pred: bool, + true_fun: Callable[[], Tensor], + false_fun: Callable[[], Tensor], + ) -> Tensor: + if pred: + return true_fun() + return false_fun() + + def switch(self, index: Tensor, branches: Sequence[Callable[[], Tensor]]) -> Tensor: + return branches[index]() + + def device(self, a: Tensor) -> str: + return self._dev2str(a.device) + + def device_move(self, a: Tensor, dev: Any) -> Tensor: + if isinstance(dev, str): + dev = self._str2dev(dev) + with dev: + return cp.asarray(a) + + def _dev2str(self, dev: Any) -> str: + return f"gpu:{dev.id}" + + def _str2dev(self, str_: str) -> Any: + if str_ == "cpu": + raise ValueError("CuPy backend only support GPU device") + else: + return cp.cuda.Device(int(str_.split(":")[-1])) + + def stop_gradient(self, a: Tensor) -> Tensor: + raise NotImplementedError("CuPy backend doesn't support AD") + + def grad( + self, + f: Callable[..., Any], + argnums: Union[int, Sequence[int]] = 0, + has_aux: bool = False, + ) -> Callable[..., Any]: + raise NotImplementedError("CuPy backend doesn't support AD") + + def value_and_grad( + self, + f: Callable[..., Any], + argnums: Union[int, Sequence[int]] = 0, + has_aux: bool = False, + ) -> Callable[..., Tuple[Any, Any]]: + raise NotImplementedError("CuPy backend doesn't support AD") + + def jit( + self, + f: Callable[..., Any], + static_argnums: Optional[Union[int, Sequence[int]]] = None, + jit_compile: Optional[bool] = None, + **kws: Any, + ) -> Callable[..., Any]: + logger.warning("CuPy backend has no jit interface, just do nothing") + return f + # raise NotImplementedError("numpy backend doesn't support jit compiling") + + def vmap( + self, f: Callable[..., Any], vectorized_argnums: Union[int, Sequence[int]] = 0 + ) -> Any: + logger.warning( + "CuPy backend has no intrinsic vmap like interface" + ", use vectorize instead (plain for loop)" + ) + if isinstance(vectorized_argnums, int): + vectorized_argnums = (vectorized_argnums,) + + def wrapper(*args: Any, **kws: Any) -> Tensor: + results = [] + for barg in zip(*[args[i] for i in vectorized_argnums]): # type: ignore + narg = [] + j = 0 + for k in range(len(args)): + if k in vectorized_argnums: # type: ignore + narg.append(barg[j]) + j += 1 + else: + narg.append(args[k]) + results.append(f(*narg, **kws)) + return cp.array(results) + + return wrapper + + def vectorized_value_and_grad( + self, + f: Callable[..., Any], + argnums: Union[int, Sequence[int]] = 0, + vectorized_argnums: Union[int, Sequence[int]] = 0, + has_aux: bool = False, + ) -> Callable[..., Tuple[Any, Any]]: + raise NotImplementedError("CuPy backend doesn't support AD") + + vvag = vectorized_value_and_grad + + def vjp( + self, + f: Callable[..., Any], + inputs: Union[Tensor, Sequence[Tensor]], + v: Union[Tensor, Sequence[Tensor]], + ) -> Tuple[Union[Tensor, Sequence[Tensor]], Union[Tensor, Sequence[Tensor]]]: + raise NotImplementedError("CuPy backend doesn't support AD") + + def jvp( + self, + f: Callable[..., Any], + inputs: Union[Tensor, Sequence[Tensor]], + v: Union[Tensor, Sequence[Tensor]], + ) -> Tuple[Union[Tensor, Sequence[Tensor]], Union[Tensor, Sequence[Tensor]]]: + raise NotImplementedError("CuPy backend doesn't support AD") diff --git a/tensorcircuit/backends/jax_backend.py b/tensorcircuit/backends/jax_backend.py index 965a26f6..33be28fd 100644 --- a/tensorcircuit/backends/jax_backend.py +++ b/tensorcircuit/backends/jax_backend.py @@ -1,16 +1,19 @@ """ Backend magic inherited from tensornetwork: jax backend """ + # pylint: disable=invalid-name -from functools import partial import logging +import warnings +from functools import partial from typing import Any, Callable, Optional, Sequence, Tuple, Union import numpy as np -from scipy.sparse import coo_matrix import tensornetwork +from scipy.sparse import coo_matrix from tensornetwork.backends.jax import jax_backend + from .abstract_backend import ExtendedBackend logger = logging.getLogger(__name__) @@ -155,12 +158,19 @@ def _rq_jax( return r, q +def _eigh_jax(self: Any, tensor: Tensor) -> Tensor: + from .jax_ops import adaware_eigh_jit as adaware_eigh + + return adaware_eigh(tensor) + + tensornetwork.backends.jax.jax_backend.JaxBackend.convert_to_tensor = ( _convert_to_tensor_jax ) tensornetwork.backends.jax.jax_backend.JaxBackend.svd = _svd_jax tensornetwork.backends.jax.jax_backend.JaxBackend.qr = _qr_jax tensornetwork.backends.jax.jax_backend.JaxBackend.rq = _rq_jax +tensornetwork.backends.jax.jax_backend.JaxBackend.eigh = _eigh_jax class JaxBackend(jax_backend.JaxBackend, ExtendedBackend): # type: ignore @@ -187,8 +197,8 @@ def __init__(self) -> None: "Jax not installed, please switch to a different " "backend or install Jax." ) - from jax.experimental import sparse import jax.scipy + from jax.experimental import sparse try: import optax @@ -294,7 +304,13 @@ def i(self, dtype: Any = None) -> Tensor: dtype = npdtype # type: ignore if isinstance(dtype, str): dtype = getattr(jnp, dtype) - return np.array(1j, dtype=dtype) + return jnp.array(1j, dtype=dtype) + + def det(self, a: Tensor) -> Tensor: + return jnp.linalg.det(a) + + def schur(self, a: Tensor, output: str = "real") -> Tuple[Tensor, Tensor]: + return jsp.linalg.schur(a, output=output) # type: ignore def real(self, a: Tensor) -> Tensor: return jnp.real(a) @@ -306,9 +322,11 @@ def dtype(self, a: Tensor) -> str: return a.dtype.__str__() # type: ignore def cast(self, a: Tensor, dtype: str) -> Tensor: - if isinstance(dtype, str): - return a.astype(getattr(jnp, dtype)) - return a.astype(dtype) + with warnings.catch_warnings(): + warnings.simplefilter("ignore", np.ComplexWarning) + if isinstance(dtype, str): + return a.astype(getattr(jnp, dtype)) + return a.astype(dtype) def arange(self, start: int, stop: Optional[int] = None, step: int = 1) -> Tensor: if stop is None: @@ -346,6 +364,11 @@ def mean( ) -> Tensor: return jnp.mean(a, axis=axis, keepdims=keepdims) + def std( + self, a: Tensor, axis: Optional[Sequence[int]] = None, keepdims: bool = False + ) -> Tensor: + return jnp.std(a, axis=axis, keepdims=keepdims) + def min(self, a: Tensor, axis: Optional[int] = None) -> Tensor: return jnp.min(a, axis=axis) @@ -387,26 +410,33 @@ def is_tensor(self, a: Any) -> bool: return False def solve(self, A: Tensor, b: Tensor, assume_a: str = "gen") -> Tensor: # type: ignore - return jsp.linalg.solve(A, b, assume_a) + return jsp.linalg.solve(A, b, assume_a=assume_a) + + def searchsorted(self, a: Tensor, v: Tensor, side: str = "left") -> Tensor: + if not self.is_tensor(a): + a = self.convert_to_tensor(a) + if not self.is_tensor(v): + v = self.convert_to_tensor(v) + return jnp.searchsorted(a, v, side) def tree_map(self, f: Callable[..., Any], *pytrees: Any) -> Any: - return libjax.tree_map(f, *pytrees) + return libjax.tree_util.tree_map(f, *pytrees) def tree_flatten(self, pytree: Any) -> Tuple[Any, Any]: - return libjax.tree_flatten(pytree) # type: ignore + return libjax.tree_util.tree_flatten(pytree) # type: ignore def tree_unflatten(self, treedef: Any, leaves: Any) -> Any: - return libjax.tree_unflatten(treedef, leaves) + return libjax.tree_util.tree_unflatten(treedef, leaves) def from_dlpack(self, a: Any) -> Tensor: import jax.dlpack - return jax.dlpack.from_dlpack(a) # type: ignore + return jax.dlpack.from_dlpack(a) def to_dlpack(self, a: Tensor) -> Any: import jax.dlpack - return jax.dlpack.to_dlpack(a) # type: ignore + return jax.dlpack.to_dlpack(a) def set_random_state( self, seed: Optional[Union[int, PRNGKeyArray]] = None, get_only: bool = False @@ -440,7 +470,7 @@ def implicit_randn( g = getattr(self, "g", None) try: key, subkey = libjax.random.split(g) - except libjax._src.errors.UnexpectedTracerError: + except libjax.errors.UnexpectedTracerError: self.set_random_state() g = getattr(self, "g", None) key, subkey = libjax.random.split(g) @@ -462,7 +492,7 @@ def implicit_randu( g = getattr(self, "g", None) try: key, subkey = libjax.random.split(g) - except libjax._src.errors.UnexpectedTracerError: + except libjax.errors.UnexpectedTracerError: self.set_random_state() g = getattr(self, "g", None) key, subkey = libjax.random.split(g) @@ -482,7 +512,7 @@ def implicit_randc( g = getattr(self, "g", None) try: key, subkey = libjax.random.split(g) - except libjax._src.errors.UnexpectedTracerError: + except libjax.errors.UnexpectedTracerError: self.set_random_state() g = getattr(self, "g", None) key, subkey = libjax.random.split(g) @@ -562,6 +592,16 @@ def switch(self, index: Tensor, branches: Sequence[Callable[[], Tensor]]) -> Ten branches_null = [lambda _, f=b: f() for b in branches] return libjax.lax.switch(index, branches_null, None) + def scan( + self, f: Callable[[Tensor, Tensor], Tensor], xs: Tensor, init: Tensor + ) -> Tensor: + def f_jax(*args: Any, **kws: Any) -> Any: + r = f(*args, **kws) + return r, None + + carry, _ = libjax.lax.scan(f_jax, init, xs) + return carry + def scatter(self, operand: Tensor, indices: Tensor, updates: Tensor) -> Tensor: rank = len(operand.shape) dnums = libjax.lax.ScatterDimensionNumbers( @@ -591,7 +631,7 @@ def is_sparse(self, a: Tensor) -> bool: return isinstance(a, sparse.BCOO) # type: ignore def device(self, a: Tensor) -> str: - dev = a.device() + (dev,) = a.devices() return self._dev2str(dev) def device_move(self, a: Tensor, dev: Any) -> Tensor: @@ -671,6 +711,7 @@ def jit( f: Callable[..., Any], static_argnums: Optional[Union[int, Sequence[int]]] = None, jit_compile: Optional[bool] = None, + **kws: Any, ) -> Any: return libjax.jit(f, static_argnums=static_argnums) @@ -717,7 +758,7 @@ def wrapper( gs = list(gs) for i, (j, g) in enumerate(zip(argnums_list, gs)): if j not in vectorized_argnums: # type: ignore - gs[i] = libjax.tree_map(partial(jnp.sum, axis=0), g) + gs[i] = libjax.tree_util.tree_map(partial(jnp.sum, axis=0), g) if isinstance(argnums, int): gs = gs[0] else: diff --git a/tensorcircuit/backends/jax_ops.py b/tensorcircuit/backends/jax_ops.py index d9a9ced0..2eaf2f1b 100644 --- a/tensorcircuit/backends/jax_ops.py +++ b/tensorcircuit/backends/jax_ops.py @@ -1,6 +1,7 @@ """ Customized ops for ML framework """ + # pylint: disable=invalid-name from typing import Any, Tuple, Sequence @@ -11,9 +12,10 @@ Array = Any # jnp.array -@jax.custom_vjp # type: ignore +@jax.custom_vjp def adaware_svd(A: Array) -> Any: - return jnp.linalg.svd(A, full_matrices=False) + u, s, v = jnp.linalg.svd(A, full_matrices=False) + return (u, s, v) def _safe_reciprocal(x: Array, epsilon: float = 1e-15) -> Array: @@ -66,7 +68,7 @@ def jaxsvd_bwd(r: Sequence[Array], tangents: Sequence[Array]) -> Tuple[Array]: return (grad_a,) -adaware_svd.defvjp(jaxsvd_fwd, jaxsvd_bwd) +adaware_svd.defvjp(jaxsvd_fwd, jaxsvd_bwd) # type: ignore adaware_svd_jit = jax.jit(adaware_svd) @@ -74,10 +76,10 @@ def jaxsvd_bwd(r: Sequence[Array], tangents: Sequence[Array]) -> Tuple[Array]: qr_epsilon = 1e-8 -@jax.custom_vjp # type: ignore +@jax.custom_vjp def adaware_qr(A: Array) -> Any: - # q, r = jnp.linalg.qr(A) - return jnp.linalg.qr(A) + q, r = jnp.linalg.qr(A) + return (q, r) def jaxqr_fwd(A: Array) -> Any: @@ -142,6 +144,32 @@ def _QrGradSquareAndDeepMatrices(q: Array, r: Array, dq: Array, dr: Array) -> Ar return (result,) -adaware_qr.defvjp(jaxqr_fwd, jaxqr_bwd) +adaware_qr.defvjp(jaxqr_fwd, jaxqr_bwd) # type: ignore adaware_qr_jit = jax.jit(adaware_qr) + + +@jax.custom_vjp +def adaware_eigh(A: Array) -> Array: + return jnp.linalg.eigh(A) + + +def jaxeigh_fwd(A: Array) -> Array: + e, v = jnp.linalg.eigh(A) + return (e, v), (A, e, v) + + +def jaxeigh_bwd(r: Array, tangents: Array) -> Array: + a, e, v = r + de, dv = tangents + eye_n = jnp.eye(a.shape[-1], dtype=a.dtype) + f = _safe_reciprocal(e[..., jnp.newaxis, :] - e[..., jnp.newaxis] + eye_n) - eye_n + middle = jnp.diag(de) + jnp.multiply(f, (v.T @ dv)) + grad_a = jnp.conj(v) @ middle @ v.T + return (grad_a,) + + +# denegerate eigev values lead nan in eigh gradients, while tf has fixed that long ago + +adaware_eigh.defvjp(jaxeigh_fwd, jaxeigh_bwd) +adaware_eigh_jit = jax.jit(adaware_eigh) diff --git a/tensorcircuit/backends/numpy_backend.py b/tensorcircuit/backends/numpy_backend.py index bbc86ae9..df3dc043 100644 --- a/tensorcircuit/backends/numpy_backend.py +++ b/tensorcircuit/backends/numpy_backend.py @@ -1,14 +1,16 @@ """ Backend magic inherited from tensornetwork: numpy backend """ + # pylint: disable=invalid-name import logging +import warnings from typing import Any, Callable, Optional, Sequence, Tuple, Union import numpy as np import tensornetwork -from scipy.linalg import expm, solve +from scipy.linalg import expm, solve, schur from scipy.special import softmax, expit from scipy.sparse import coo_matrix, issparse from tensornetwork.backends.numpy import numpy_backend @@ -130,6 +132,12 @@ def dtype(self, a: Tensor) -> str: def numpy(self, a: Tensor) -> Tensor: return a + def det(self, a: Tensor) -> Tensor: + return np.linalg.det(a) + + def schur(self, a: Tensor, output: str = "real") -> Tuple[Tensor, Tensor]: + return schur(a, output=output) # type: ignore + def i(self, dtype: Any = None) -> Tensor: if not dtype: dtype = npdtype # type: ignore @@ -154,6 +162,11 @@ def mean( ) -> Tensor: return np.mean(a, axis=axis, keepdims=keepdims) + def std( + self, a: Tensor, axis: Optional[Sequence[int]] = None, keepdims: bool = False + ) -> Tensor: + return np.std(a, axis=axis, keepdims=keepdims) + def unique_with_counts(self, a: Tensor, **kws: Any) -> Tuple[Tensor, Tensor]: return np.unique(a, return_counts=True) # type: ignore @@ -200,9 +213,11 @@ def imag(self, a: Tensor) -> Tensor: return np.imag(a) def cast(self, a: Tensor, dtype: str) -> Tensor: - if isinstance(dtype, str): - return a.astype(getattr(np, dtype)) - return a.astype(dtype) + with warnings.catch_warnings(): + warnings.simplefilter("ignore", np.ComplexWarning) + if isinstance(dtype, str): + return a.astype(getattr(np, dtype)) + return a.astype(dtype) def arange(self, start: int, stop: Optional[int] = None, step: int = 1) -> Tensor: if stop is None: @@ -221,7 +236,10 @@ def left_shift(self, x: Tensor, y: Tensor) -> Tensor: def solve(self, A: Tensor, b: Tensor, assume_a: str = "gen") -> Tensor: # type: ignore # gen, sym, her, pos # https://stackoverflow.com/questions/44672029/difference-between-numpy-linalg-solve-and-numpy-linalg-lu-solve/44710451 - return solve(A, b, assume_a) + return solve(A, b, assume_a=assume_a) + + def searchsorted(self, a: Tensor, v: Tensor, side: str = "left") -> Tensor: + return np.searchsorted(a, v, side=side) # type: ignore def set_random_state( self, seed: Optional[int] = None, get_only: bool = False @@ -366,6 +384,7 @@ def jit( f: Callable[..., Any], static_argnums: Optional[Union[int, Sequence[int]]] = None, jit_compile: Optional[bool] = None, + **kws: Any ) -> Callable[..., Any]: logger.warning("numpy backend has no jit interface, just do nothing") return f diff --git a/tensorcircuit/backends/pytorch_backend.py b/tensorcircuit/backends/pytorch_backend.py index 5234a6be..3214c72c 100644 --- a/tensorcircuit/backends/pytorch_backend.py +++ b/tensorcircuit/backends/pytorch_backend.py @@ -1,12 +1,13 @@ """ Backend magic inherited from tensornetwork: pytorch backend """ + # pylint: disable=invalid-name import logging from typing import Any, Callable, Optional, Sequence, Tuple, Union from operator import mul -from functools import reduce +from functools import reduce, partial import tensornetwork from tensornetwork.backends.pytorch import pytorch_backend @@ -26,6 +27,24 @@ # To be added once pytorch backend is ready +class torch_jit_func: + """ + Delay the tracing of torch jit to the first run time: + consistent with tf and jax mechanism + """ + + def __init__(self, f: Callable[..., Any]): + self.compiled = False + self.f = f + + def __call__(self, *args: Any, **kws: Any) -> Any: + if self.compiled is False: + self.f = torchlib.jit.trace(self.f, example_inputs=args) + self.compiled = True + + return self.f(*args, **kws) + + class torch_optimizer: def __init__(self, optimizer: Any) -> None: self.optimizer = optimizer @@ -283,11 +302,14 @@ def numpy(self, a: Tensor) -> Tensor: def i(self, dtype: Any = None) -> Tensor: if not dtype: - dtype = getattr(torchlib, dtypestr) # type: ignore + dtype = getattr(torchlib, dtypestr) if isinstance(dtype, str): dtype = getattr(torchlib, dtype) return torchlib.tensor(1j, dtype=dtype) + def det(self, a: Tensor) -> Tensor: + return torchlib.linalg.det(a) + def real(self, a: Tensor) -> Tensor: try: a = torchlib.real(a) @@ -324,6 +346,13 @@ def mean( axis = tuple([i for i in range(len(a.shape))]) return torchlib.mean(a, dim=axis, keepdim=keepdims) + def std( + self, a: Tensor, axis: Optional[Sequence[int]] = None, keepdims: bool = False + ) -> Tensor: + if axis is None: + axis = tuple([i for i in range(len(a.shape))]) + return torchlib.std(a, dim=axis, unbiased=False, keepdim=keepdims) + def min(self, a: Tensor, axis: Optional[int] = None) -> Tensor: if axis is None: return torchlib.min(a) @@ -344,10 +373,10 @@ def unique_with_counts(self, a: Tensor, **kws: Any) -> Tuple[Tensor, Tensor]: return torchlib.unique(a, return_counts=True) # type: ignore def sigmoid(self, a: Tensor) -> Tensor: - return torchlib.nn.Sigmoid(a) + return torchlib.sigmoid(a) def relu(self, a: Tensor) -> Tensor: - return torchlib.nn.ReLU(a) + return torchlib.relu(a) def softmax(self, a: Sequence[Tensor], axis: Optional[int] = None) -> Tensor: return torchlib.nn.Softmax(a, dim=axis) @@ -389,6 +418,13 @@ def left_shift(self, x: Tensor, y: Tensor) -> Tensor: def solve(self, A: Tensor, b: Tensor, **kws: Any) -> Tensor: return torchlib.linalg.solve(A, b) + def searchsorted(self, a: Tensor, v: Tensor, side: str = "left") -> Tensor: + if not self.is_tensor(a): + a = self.convert_to_tensor(a) + if not self.is_tensor(v): + v = self.convert_to_tensor(v) + return torchlib.searchsorted(a, v, side=side) + def reverse(self, a: Tensor) -> Tensor: return torchlib.flip(a, dims=(-1,)) @@ -500,37 +536,43 @@ def value_and_grad( argnums: Union[int, Sequence[int]] = 0, has_aux: bool = False, ) -> Callable[..., Tuple[Any, Any]]: - def ask_require(t: Tensor) -> Any: - t.requires_grad_(True) - return t - - def get_grad(t: Tensor) -> Tensor: - return t.grad - def wrapper(*args: Any, **kws: Any) -> Any: - x = [] - if isinstance(argnums, int): - argnumsl = [argnums] - # if you also call lhs as argnums, something weird may happen - # the reason is that python then take it as local vars - else: - argnumsl = argnums # type: ignore - for i, arg in enumerate(args): - if i in argnumsl: - x.append(self.tree_map(ask_require, arg)) - else: - x.append(arg) - y = f(*x, **kws) - if has_aux: - y[0].backward() - else: - y.backward() - gs = [self.tree_map(get_grad, x[i]) for i in argnumsl] - if len(gs) == 1: - gs = gs[0] - return y, gs + gavf = torchlib.func.grad_and_value(f, argnums=argnums, has_aux=has_aux) + g, v = gavf(*args, **kws) + return v, g return wrapper + # def ask_require(t: Tensor) -> Any: + # t.requires_grad_(True) + # return t + + # def get_grad(t: Tensor) -> Tensor: + # return t.grad + + # def wrapper(*args: Any, **kws: Any) -> Any: + # # x = [] + # if isinstance(argnums, int): + # argnumsl = [argnums] + # # if you also call lhs as argnums, something weird may happen + # # the reason is that python then take it as local vars + # else: + # argnumsl = argnums # type: ignore + # args = list(args) + # for i, arg in enumerate(args): + # if i in argnumsl: + # args[i] = self.tree_map(ask_require, arg) + # args = tuple(args) + # y = f(*args, **kws) + # if has_aux: + # y[0].backward() + # else: + # y.backward() + # gs = [self.tree_map(get_grad, x[i]) for i in argnumsl] + # if len(gs) == 1: + # gs = gs[0] + # return y, gs + + # return wrapper def vjp( self, @@ -563,33 +605,43 @@ def jvp( def vmap( self, f: Callable[..., Any], - vectorized_argnums: Optional[Union[int, Sequence[int]]] = None, + vectorized_argnums: Union[int, Sequence[int]] = 0, ) -> Any: - logger.warning( - "pytorch backend has no intrinsic vmap like interface" - ", use plain for loop for compatibility" - ) - # the vmap support is vey limited, f must return one tensor - # nested list of tensor as return is not supported if isinstance(vectorized_argnums, int): vectorized_argnums = (vectorized_argnums,) def wrapper(*args: Any, **kws: Any) -> Tensor: - results = [] - for barg in zip(*[args[i] for i in vectorized_argnums]): # type: ignore - narg = [] - j = 0 - for k in range(len(args)): - if k in vectorized_argnums: # type: ignore - narg.append(barg[j]) - j += 1 - else: - narg.append(args[k]) - results.append(f(*narg, **kws)) - return torchlib.stack(results) + in_axes = tuple([0 if i in vectorized_argnums else None for i in range(len(args))]) # type: ignore + return torchlib.vmap(f, in_axes, 0)(*args, **kws) return wrapper + # v3 + # logger.warning( + # "pytorch backend has no intrinsic vmap like interface" + # ", use plain for loop for compatibility" + # ) + # # the vmap support is vey limited, f must return one tensor + # # nested list of tensor as return is not supported + # if isinstance(vectorized_argnums, int): + # vectorized_argnums = (vectorized_argnums,) + + # def wrapper(*args: Any, **kws: Any) -> Tensor: + # results = [] + # for barg in zip(*[args[i] for i in vectorized_argnums]): # type: ignore + # narg = [] + # j = 0 + # for k in range(len(args)): + # if k in vectorized_argnums: # type: ignore + # narg.append(barg[j]) + # j += 1 + # else: + # narg.append(args[k]) + # results.append(f(*narg, **kws)) + # return torchlib.stack(results) + # return wrapper + + # v2 # def vmapf(*args: Tensor, **kws: Any) -> Tensor: # r = [] # for i in range(args[0].shape[0]): @@ -599,6 +651,7 @@ def wrapper(*args: Any, **kws: Any) -> Tensor: # return vmapf + # v1 # raise NotImplementedError("pytorch backend doesn't support vmap") # There seems to be no map like architecture in pytorch for now # see https://discuss.pytorch.org/t/fast-way-to-use-map-in-pytorch/70814 @@ -608,8 +661,13 @@ def jit( f: Callable[..., Any], static_argnums: Optional[Union[int, Sequence[int]]] = None, jit_compile: Optional[bool] = None, + **kws: Any ) -> Any: - return f # do nothing here until I figure out what torch.jit is for and how does it work + if jit_compile is True: + # experimental feature reusing the jit_compile flag for tf + return torch_jit_func(f) + return f + # return f # do nothing here until I figure out what torch.jit is for and how does it work # see https://github.com/pytorch/pytorch/issues/36910 def vectorized_value_and_grad( @@ -620,10 +678,33 @@ def vectorized_value_and_grad( has_aux: bool = False, ) -> Callable[..., Tuple[Any, Any]]: # [WIP], not a consistent impl compared to tf and jax backend, but pytorch backend is not fully supported anyway - f = self.value_and_grad(f, argnums=argnums, has_aux=has_aux) - f = self.vmap(f, vectorized_argnums=vectorized_argnums) - # f = self.jit(f) - return f + if isinstance(vectorized_argnums, int): + vectorized_argnums = (vectorized_argnums,) + + def wrapper( + *args: Any, **kws: Any + ) -> Tuple[Tensor, Union[Tensor, Tuple[Tensor, ...]]]: + jf = self.value_and_grad(f, argnums=argnums, has_aux=has_aux) + jf = self.vmap(jf, vectorized_argnums=vectorized_argnums) + vs, gs = jf(*args, **kws) + + if isinstance(argnums, int): + argnums_list = [argnums] + gs = [gs] + else: + argnums_list = argnums # type: ignore + gs = list(gs) + for i, (j, g) in enumerate(zip(argnums_list, gs)): + if j not in vectorized_argnums: # type: ignore + gs[i] = self.tree_map(partial(torchlib.sum, dim=0), g) + if isinstance(argnums, int): + gs = gs[0] + else: + gs = tuple(gs) + + return vs, gs + + return wrapper vvag = vectorized_value_and_grad diff --git a/tensorcircuit/backends/pytorch_ops.py b/tensorcircuit/backends/pytorch_ops.py index 49f10d2a..e801a4ca 100644 --- a/tensorcircuit/backends/pytorch_ops.py +++ b/tensorcircuit/backends/pytorch_ops.py @@ -1,6 +1,7 @@ """ Customized ops for ML framework """ + # pylint: disable=invalid-name from typing import Any @@ -90,11 +91,25 @@ class torchqr(torch.autograd.Function): """ @staticmethod - def forward(ctx, a: Array) -> Any: + def forward(a: Array) -> Any: q, r = torch.linalg.qr(a, mode="reduced") - ctx.save_for_backward(a, q, r) + # ctx.save_for_backward(a, q, r) return q, r + # setup_context is responsible for calling methods and/or assigning to + # the ctx object. Please do not do additional compute (e.g. add + # Tensors together) in setup_context. + # https://pytorch.org/docs/master/notes/extending.func.html + @staticmethod + def setup_context(ctx, inputs, output): + (a,) = inputs + q, r = output + # Tensors must be saved via ctx.save_for_backward. Please do not + # assign them directly onto the ctx object. + ctx.save_for_backward(a, q, r) + # Non-tensors may be saved by assigning them as attributes on the ctx object. + # ctx.dim = dim + @staticmethod def backward(ctx, dq: Array, dr: Array) -> Any: a, q, r = ctx.saved_tensors diff --git a/tensorcircuit/backends/tensorflow_backend.py b/tensorcircuit/backends/tensorflow_backend.py index 70636395..365bd521 100644 --- a/tensorcircuit/backends/tensorflow_backend.py +++ b/tensorcircuit/backends/tensorflow_backend.py @@ -1,8 +1,11 @@ """ Backend magic inherited from tensornetwork: tensorflow backend """ + # pylint: disable=invalid-name +import os +import re from functools import reduce, partial from operator import mul from typing import Any, Callable, Optional, Sequence, Tuple, Union @@ -23,23 +26,49 @@ class keras_optimizer: def __init__(self, optimizer: Any) -> None: self.optimizer = optimizer - self.is_variable = True - - def _c2v(self, v: Tensor) -> Tensor: - if not isinstance(v, tf.Variable): - v = tf.Variable(v) - self.is_variable = False - return v + self.is_initialized = False - def _apply_gradients(self, grads: Tensor, params: Tensor) -> None: - self.optimizer.apply_gradients([(grads, params)]) + # def _apply_gradients(self, grads: Tensor, params: Tensor) -> None: + # self.optimizer.apply_gradients([(grads, params)]) def update(self, grads: pytree, params: pytree) -> pytree: - params = TensorFlowBackend.tree_map(None, self._c2v, params) + # if not self.is_initialized: + # l, treedef = TensorFlowBackend.tree_flatten(None, params) + # # https://github.com/tensorflow/tensorflow/issues/58973 + # # still breaks tf2.11 + # ol = [deepcopy(self.optimizer) for _ in l] + # self.optimizer = TensorFlowBackend.tree_unflatten(None, treedef, ol) + # self.is_initialized = True + # params = TensorFlowBackend.tree_map(None, self._c2v, params) # don't do the () initialization since cache is in upper level of backend_factory - TensorFlowBackend.tree_map(None, self._apply_gradients, grads, params) - if not self.is_variable: - return TensorFlowBackend.tree_map(None, tf.convert_to_tensor, params) + grads_l, _ = TensorFlowBackend.tree_flatten(None, grads) + params_l, params_def = TensorFlowBackend.tree_flatten(None, params) + if not self.is_initialized: + self.params_v = [] + self.is_variable = [] + for p in params_l: + if not isinstance(p, tf.Variable): + self.params_v.append(tf.Variable(p)) + self.is_variable.append(False) + else: + self.params_v.append(p) + self.is_variable.append(True) + self.is_initialized = True + else: + for i, p in enumerate(params_l): + if not isinstance(p, tf.Variable): + self.params_v[i] = self.params_v[i].assign(p) + else: + self.params_v[i] = self.params_v[i].assign(p.value()) + + self.optimizer.apply_gradients(zip(grads_l, self.params_v)) + nparams_l = [] + for p, flag in zip(self.params_v, self.is_variable): + if flag is True: + nparams_l.append(p) + else: + nparams_l.append(p.value()) + params = TensorFlowBackend.tree_unflatten(None, params_def, nparams_l) return params @@ -199,6 +228,120 @@ def _rq_tf( return r, q +def _svd_tf( + self: Any, + tensor: Tensor, + pivot_axis: int = -1, + max_singular_values: Optional[int] = None, + max_truncation_error: Optional[float] = None, + relative: Optional[bool] = False, +) -> Tuple[Tensor, Tensor, Tensor, Tensor]: + """Computes the singular value decomposition (SVD) of a tensor. + + The SVD is performed by treating the tensor as a matrix, with an effective + left (row) index resulting from combining the axes `tensor.shape[:pivot_axis]` + and an effective right (column) index resulting from combining the axes + `tensor.shape[pivot_axis:]`. + + For example, if `tensor` had a shape (2, 3, 4, 5) and `pivot_axis` was 2, then + `u` would have shape (2, 3, 6), `s` would have shape (6), and `vh` would + have shape (6, 4, 5). + + If `max_singular_values` is set to an integer, the SVD is truncated to keep + at most this many singular values. + + If `max_truncation_error > 0`, as many singular values will be truncated as + possible, so that the truncation error (the norm of discarded singular + values) is at most `max_truncation_error`. + If `relative` is set `True` then `max_truncation_err` is understood + relative to the largest singular value. + + If both `max_singular_values` snd `max_truncation_error` are specified, the + number of retained singular values will be + `min(max_singular_values, nsv_auto_trunc)`, where `nsv_auto_trunc` is the + number of singular values that must be kept to maintain a truncation error + smaller than `max_truncation_error`. + + The output consists of three tensors `u, s, vh` such that: + ```python + u[i1,...,iN, j] * s[j] * vh[j, k1,...,kM] == tensor[i1,...,iN, k1,...,kM] + ``` + Note that the output ordering matches numpy.linalg.svd rather than tf.svd. + + Args: + tf: The tensorflow module. + tensor: A tensor to be decomposed. + pivot_axis: Where to split the tensor's axes before flattening into a + matrix. + max_singular_values: The number of singular values to keep, or `None` to + keep them all. + max_truncation_error: The maximum allowed truncation error or `None` to not + do any truncation. + relative: Multiply `max_truncation_err` with the largest singular value. + + Returns: + u: Left tensor factor. + s: Vector of ordered singular values from largest to smallest. + vh: Right tensor factor. + s_rest: Vector of discarded singular values (length zero if no + truncation). + """ + left_dims = tf.shape(tensor)[:pivot_axis] + right_dims = tf.shape(tensor)[pivot_axis:] + + tensor = tf.reshape(tensor, [tf.reduce_prod(left_dims), tf.reduce_prod(right_dims)]) + + eps = os.environ.get("TC_BACKENDS_TENSORFLOW_BACKEND__SVD_TF_EPS") + if eps is not None: + eps = 10 ** (-int(eps)) + tensor += eps * tf.ones(tensor.shape, dtype=tensor.dtype) + # for numerical stability at least in tf+cpu + s, u, v = tf.linalg.svd(tensor) + + if max_singular_values is None: + max_singular_values = tf.size(s, out_type=tf.int64) + else: + max_singular_values = tf.constant(max_singular_values, dtype=tf.int64) + + if max_truncation_error is not None: + # Cumulative norms of singular values in ascending order. + trunc_errs = tf.sqrt(tf.cumsum(tf.square(s), reverse=True)) + # If relative is true, rescale max_truncation error with the largest + # singular value to yield the absolute maximal truncation error. + if relative: + abs_max_truncation_error = max_truncation_error * s[0] + else: + abs_max_truncation_error = max_truncation_error + # We must keep at least this many singular values to ensure the + # truncation error is <= abs_max_truncation_error. + num_sing_vals_err = tf.math.count_nonzero( + tf.cast(trunc_errs > abs_max_truncation_error, dtype=tf.int32) + ) + else: + num_sing_vals_err = max_singular_values + + num_sing_vals_keep = tf.minimum(max_singular_values, num_sing_vals_err) + + # tf.svd() always returns the singular values as a vector of float{32,64}. + # since tf.math_ops.real is automatically applied to s. This causes + # s to possibly not be the same dtype as the original tensor, which can cause + # issues for later contractions. To fix it, we recast to the original dtype. + s = tf.cast(s, tensor.dtype) + + s_rest = s[num_sing_vals_keep:] + s = s[:num_sing_vals_keep] + u = u[:, :num_sing_vals_keep] + v = v[:, :num_sing_vals_keep] + + vh = tf.linalg.adjoint(v) + + dim_s = tf.shape(s)[0] # must use tf.shape (not s.shape) to compile + u = tf.reshape(u, tf.concat([left_dims, [dim_s]], axis=-1)) + vh = tf.reshape(vh, tf.concat([[dim_s], right_dims], axis=-1)) + + return u, s, vh, s_rest + + # temporary hot replace until new version of tensorflow is released, # see issue: https://github.com/google/TensorNetwork/issues/940 # avoid buggy tensordot2 in tensornetwork @@ -214,6 +357,7 @@ def _rq_tf( ) tensornetwork.backends.tensorflow.tensorflow_backend.TensorFlowBackend.qr = _qr_tf tensornetwork.backends.tensorflow.tensorflow_backend.TensorFlowBackend.rq = _rq_tf +tensornetwork.backends.tensorflow.tensorflow_backend.TensorFlowBackend.svd = _svd_tf class TensorFlowBackend(tensorflow_backend.TensorFlowBackend, ExtendedBackend): # type: ignore @@ -236,6 +380,9 @@ def __init__(self) -> None: tf.sparse.SparseTensor.__add__ = tf.sparse.add self.minor = int(tf.__version__.split(".")[1]) self.name = "tensorflow" + logger = tf.get_logger() # .setLevel('ERROR') + logger.addFilter(lambda s: not re.match(".*You are casting.*", s.getMessage())) + # ignore casting warning by logger def eye( self, N: int, dtype: Optional[str] = None, M: Optional[int] = None @@ -329,11 +476,14 @@ def numpy(self, a: Tensor) -> Tensor: def i(self, dtype: Any = None) -> Tensor: if not dtype: - dtype = getattr(tf, dtypestr) # type: ignore + dtype = getattr(tf, dtypestr) if isinstance(dtype, str): dtype = getattr(tf, dtype) return tf.constant(1j, dtype=dtype) + def det(self, a: Tensor) -> Tensor: + return tf.linalg.det(a) + def min(self, a: Tensor, axis: Optional[int] = None) -> Tensor: return tf.reduce_min(a, axis=axis) @@ -368,6 +518,11 @@ def mean( ) -> Tensor: return tf.math.reduce_mean(a, axis=axis, keepdims=keepdims) + def std( + self, a: Tensor, axis: Optional[Sequence[int]] = None, keepdims: bool = False + ) -> Tensor: + return tf.math.reduce_std(a, axis=axis, keepdims=keepdims) + def sigmoid(self, a: Tensor) -> Tensor: return tf.nn.sigmoid(a) @@ -434,6 +589,9 @@ def solve(self, A: Tensor, b: Tensor, **kws: Any) -> Tensor: return self.reshape(x, x.shape[:-1]) return x + def searchsorted(self, a: Tensor, v: Tensor, side: str = "left") -> Tensor: + return tf.searchsorted(a, v, side) + def from_dlpack(self, a: Any) -> Tensor: return tf.experimental.dlpack.from_dlpack(a) @@ -548,11 +706,14 @@ def cond( ) -> Tensor: return tf.cond(pred, true_fun, false_fun) - def switch( - self: Any, index: Tensor, branches: Sequence[Callable[[], Tensor]] - ) -> Tensor: + def switch(self, index: Tensor, branches: Sequence[Callable[[], Tensor]]) -> Tensor: return tf.switch_case(index, branches) + def scan( + self, f: Callable[[Tensor, Tensor], Tensor], xs: Tensor, init: Tensor + ) -> Tensor: + return tf.scan(f, xs, init)[-1] + def device(self, a: Tensor) -> str: dev = a.device return self._dev2str(dev) @@ -594,7 +755,7 @@ def wrapper(*args: Any, **kws: Any) -> Any: else: args = tuple( [ - tf.identity(arg) if i in argnums else arg # type: ignore + tf.identity(arg) if i in argnums else arg for i, arg in enumerate(args) ] ) @@ -625,7 +786,7 @@ def wrapper(*args: Any, **kws: Any) -> Any: else: args = tuple( [ - tf.identity(arg) if i in argnums else arg # type: ignore + tf.identity(arg) if i in argnums else arg for i, arg in enumerate(args) ] ) @@ -684,6 +845,16 @@ def vjp( t.watch(inputs) y = f(*inputs) g = t.gradient(y, inputs, v) + g = list(g) + for i, gi in enumerate(g): + if gi is None: + g[i] = tf.zeros_like(inputs[i]) + if isinstance(gi, tf.IndexedSlices): + # gradient can return sth weird + # TODO(@refraction-ray): check whether other AD tf methods have such issues + # shape is still unkown, dense_shape attr doesn't work? + g[i] = tf.convert_to_tensor(gi) + g = tuple(g) if one_input: g = g[0] return y, g @@ -693,6 +864,7 @@ def jit( f: Callable[..., Any], static_argnums: Optional[Union[int, Sequence[int]]] = None, jit_compile: Optional[bool] = None, + **kws: Any ) -> Any: # static_argnums not supported in tf case, this is only for a consistent interface # for more on static_argnums in tf.function, see issue: https://github.com/tensorflow/tensorflow/issues/52193 @@ -782,7 +954,6 @@ def pf(x: Tensor) -> Tensor: return tf.vectorized_map(pf, args[0]) else: - # @self.jit # otherwise, vectorized_map claim on retracing def wrapper(*args: Any, **kws: Any) -> Tensor: # @self.jit diff --git a/tensorcircuit/backends/tf_ops.py b/tensorcircuit/backends/tf_ops.py index 0dd8a53a..d7b6d380 100644 --- a/tensorcircuit/backends/tf_ops.py +++ b/tensorcircuit/backends/tf_ops.py @@ -1,11 +1,12 @@ """ Customized ops for ML framework """ + # pylint: disable=invalid-name from typing import Any -import tensorflow as tf # type: ignore +import tensorflow as tf Array = Any # tensorflow Tensor diff --git a/tensorcircuit/basecircuit.py b/tensorcircuit/basecircuit.py index fdeac033..468c6a1b 100644 --- a/tensorcircuit/basecircuit.py +++ b/tensorcircuit/basecircuit.py @@ -1,6 +1,7 @@ """ Quantum circuit: common methods for all circuit classes as MixIn """ + # pylint: disable=invalid-name from typing import Any, Dict, List, Optional, Sequence, Tuple, Union @@ -14,6 +15,7 @@ from .quantum import ( QuOperator, QuVector, + correlation_from_samples, correlation_from_counts, measurement_counts, sample_int2bin, @@ -25,6 +27,7 @@ from .simplify import _split_two_qubit_gate from .utils import arg_alias + Gate = gates.Gate Tensor = Any @@ -79,7 +82,7 @@ def coloring_copied_nodes( node.id = getattr(n0, "id", id(n0)) @staticmethod - def copy( + def copy_nodes( nodes: Sequence[tn.Node], dangling: Optional[Sequence[tn.Edge]] = None, conj: Optional[bool] = False, @@ -109,7 +112,7 @@ def copy( def _copy( self, conj: Optional[bool] = False ) -> Tuple[List[tn.Node], List[tn.Edge]]: - return self.copy(self._nodes, self._front, conj) + return self.copy_nodes(self._nodes, self._front, conj) def apply_general_gate( self, @@ -135,7 +138,7 @@ def apply_general_gate( ir_dict = gate_dict self._qir.append(ir_dict) assert len(index) == len(set(index)) - index = tuple([i if i >= 0 else self._nqubits + i for i in index]) # type: ignore + index = tuple([i if i >= 0 else self._nqubits + i for i in index]) noe = len(index) nq = self._nqubits applied = False @@ -169,7 +172,7 @@ def apply_general_gate( self._front[index[0]] = n1[0] self._front[index[1]] = n2[1] if self.is_dm: - [n1l, n2l], _ = self.copy([n1, n2], conj=True) + [n1l, n2l], _ = self.copy_nodes([n1, n2], conj=True) n1l[1] ^ self._front[index[0] + nq] n2l[2] ^ self._front[index[1] + nq] self._nodes.append(n1l) @@ -184,7 +187,7 @@ def apply_general_gate( self._front[index[0]] = n1[0] self._front[index[1]] = n2[1] if self.is_dm: - [n1l, n2l], _ = self.copy([n1, n2], conj=True) + [n1l, n2l], _ = self.copy_nodes([n1, n2], conj=True) n2l[1] ^ self._front[index[0] + nq] n1l[2] ^ self._front[index[1] + nq] self._nodes.append(n1l) @@ -201,7 +204,7 @@ def apply_general_gate( # gate.id = id(gate) self._nodes.append(gate) if self.is_dm: - lgates, _ = self.copy([gate], conj=True) + lgates, _ = self.copy_nodes([gate], conj=True) lgate = lgates[0] self._nodes.append(lgate) for i, ind in enumerate(index): @@ -256,7 +259,7 @@ def _copy_state_tensor( for e in t.edges: newfront.append(edict[e]) return newnodes, newfront - return self._copy(conj) # type: ignore + return self._copy(conj) def expectation_before( self, @@ -310,7 +313,7 @@ def expectation_before( for j in range(nq): if j not in occupied: # edge1[j].is_dangling invalid here! newdang[j] ^ newdang[j + nq] - return nodes # type: ignore + return nodes def to_qir(self) -> List[Dict[str, Any]]: """ @@ -501,13 +504,30 @@ def amplitude(self, l: Union[str, Tensor]) -> Tensor: no.extend(msconj) return contractor(no).tensor + def probability(self) -> Tensor: + """ + get the 2^n length probability vector over computational basis + + :return: probability vector + :rtype: Tensor + """ + s = self.state() # type: ignore + if self.is_dm is False: + p = backend.abs(s) ** 2 + + else: + p = backend.abs(backend.diagonal(s)) + return p + @partial(arg_alias, alias_dict={"format": ["format_"]}) def sample( self, batch: Optional[int] = None, allow_state: bool = False, + readout_error: Optional[Sequence[Any]] = None, format: Optional[str] = None, random_generator: Optional[Any] = None, + status: Optional[Tensor] = None, ) -> Any: """ batched sampling from state or circuit tensor network directly @@ -515,15 +535,20 @@ def sample( :param batch: number of samples, defaults to None :type batch: Optional[int], optional :param allow_state: if true, we sample from the final state - if memory allsows, True is prefered, defaults to False + if memory allows, True is preferred, defaults to False :type allow_state: bool, optional + :param readout_error: readout_error, defaults to None + :type readout_error: Optional[Sequence[Any]]. Tensor, List, Tuple :param format: sample format, defaults to None as backward compatibility check the doc in :py:meth:`tensorcircuit.quantum.measurement_results` :type format: Optional[str] :param random_generator: random generator, defaults to None :type random_generator: Optional[Any], optional - :return: List (if batch) of tuple (binary configuration tensor and correponding probability) - if the format is None, and consitent with format when given + :param status: external randomness given by tensor uniformly from [0, 1], + if set, can overwrite random_generator + :type status: Optional[Tensor] + :return: List (if batch) of tuple (binary configuration tensor and corresponding probability) + if the format is None, and consistent with format when given :rtype: Any """ # allow_state = False is compatibility issue @@ -539,7 +564,7 @@ def sample( r = [r] # type: ignore else: - @backend.jit # type: ignore + @backend.jit def perfect_sampling(key: Any) -> Any: backend.set_random_state(key) return self.perfect_sampling() @@ -560,25 +585,25 @@ def perfect_sampling(key: Any) -> Any: nbatch = 1 else: nbatch = batch - s = self.state() # type: ignore - if self.is_dm is False: - p = backend.abs(s) ** 2 - else: - p = backend.abs(backend.diagonal(s)) - a_range = backend.arange(2**self._nqubits) - if random_generator is None: - ch = backend.implicit_randc(a=a_range, shape=[nbatch], p=p) - else: - ch = backend.stateful_randc( - random_generator, a=a_range, shape=[nbatch], p=p - ) + p = self.probability() + + # readout error + if readout_error is not None: + p = self.readouterror_bs(readout_error, p) + ch = backend.probability_sample(nbatch, p, status, random_generator) + # if random_generator is None: + # ch = backend.implicit_randc(a=a_range, shape=[nbatch], p=p) + # else: + # ch = backend.stateful_randc( + # random_generator, a=a_range, shape=[nbatch], p=p + # ) # confg = backend.mod( # backend.right_shift( # ch[..., None], backend.reverse(backend.arange(self._nqubits)) # ), # 2, # ) - if format is None: + if format is None: # for backward compatibility confg = sample_int2bin(ch, self._nqubits) prob = backend.gather1d(p, ch) r = list(zip(confg, prob)) # type: ignore @@ -594,6 +619,11 @@ def sample_expectation_ps( z: Optional[Sequence[int]] = None, shots: Optional[int] = None, random_generator: Optional[Any] = None, + status: Optional[Tensor] = None, + readout_error: Optional[Sequence[Any]] = None, + noise_conf: Optional[Any] = None, + nmc: int = 1000, + statusc: Optional[Tensor] = None, **kws: Any, ) -> Tensor: """ @@ -606,6 +636,26 @@ def sample_expectation_ps( >>> c.rx(1, theta=np.pi/2) >>> c.sample_expectation_ps(x=[0], y=[1]) -0.99999976 + >>> readout_error = [] + >>> readout_error.append([0.9,0.75]) + >>> readout_error.append([0.4,0.7]) + >>> c.sample_expectation_ps(x=[0], y=[1],readout_error = readout_error) + + >>> c = tc.Circuit(2) + >>> c.cnot(0, 1) + >>> c.rx(0, theta=0.4) + >>> c.rx(1, theta=0.8) + >>> c.h(0) + >>> c.h(1) + >>> error1 = tc.channels.generaldepolarizingchannel(0.1, 1) + >>> error2 = tc.channels.generaldepolarizingchannel(0.06, 2) + >>> readout_error = [[0.9, 0.75],[0.4, 0.7]] + >>> noise_conf = NoiseConf() + >>> noise_conf.add_noise("rx", error1) + >>> noise_conf.add_noise("cnot", [error2], [[0, 1]]) + >>> noise_conf.add_noise("readout", readout_error) + >>> c.sample_expectation_ps(x=[0], noise_conf=noise_conf, nmc=10000) + 0.44766843 :param x: index for Pauli X, defaults to None :type x: Optional[Sequence[int]], optional @@ -616,47 +666,139 @@ def sample_expectation_ps( :param shots: number of measurement shots, defaults to None, indicating analytical result :type shots: Optional[int], optional :param random_generator: random_generator, defaults to None - :type random_general: Optional[Any] + :type random_generator: Optional[Any] + :param status: external randomness given by tensor uniformly from [0, 1], + if set, can overwrite random_generator + :type status: Optional[Tensor] + :param readout_error: readout_error, defaults to None. Overrided if noise_conf is provided. + :type readout_error: Optional[Sequence[Any]]. Tensor, List, Tuple + :param noise_conf: Noise Configuration, defaults to None + :type noise_conf: Optional[NoiseConf], optional + :param nmc: repetition time for Monte Carlo sampling for noisfy calculation, defaults to 1000 + :type nmc: int, optional + :param statusc: external randomness given by tensor uniformly from [0, 1], defaults to None, + used for noisfy circuit sampling + :type statusc: Optional[Tensor], optional :return: [description] :rtype: Tensor """ - if self.is_dm is False: - c = type(self)(self._nqubits, mps_inputs=self.quvector()) # type: ignore - else: - c = type(self)(self._nqubits, mpo_dminputs=self.get_dm_as_quoperator()) # type: ignore - if x is None: - x = [] - if y is None: - y = [] - if z is None: - z = [] - for i in x: - c.H(i) # type: ignore - for i in y: - c.rx(i, theta=np.pi / 2) # type: ignore - s = c.state() # type: ignore - if c.is_dm is False: - p = backend.abs(s) ** 2 + from .noisemodel import sample_expectation_ps_noisfy + + if noise_conf is None: + inputs_nodes, _ = self._copy_state_tensor() + inputs = inputs_nodes[0].tensor + if self.is_dm is False: + c = type(self)(self._nqubits, inputs=inputs) # type: ignore + else: + c = type(self)(self._nqubits, dminputs=inputs) # type: ignore + if x is None: + x = [] + if y is None: + y = [] + if z is None: + z = [] + for i in x: + c.H(i) # type: ignore + for i in y: + c.rx(i, theta=np.pi / 2) # type: ignore + s = c.state() # type: ignore + if self.is_dm is False: + p = backend.abs(s) ** 2 + else: + p = backend.abs(backend.diagonal(s)) + + # readout error + if readout_error is not None: + p = self.readouterror_bs(readout_error, p) + + x = list(x) + y = list(y) + z = list(z) + if shots is None: + mc = measurement_counts( + p, + counts=shots, + format="count_vector", + random_generator=random_generator, + status=status, + jittable=True, + is_prob=True, + ) + r = correlation_from_counts(x + y + z, mc) + else: + mc = measurement_counts( + p, + counts=shots, + format="sample_bin", + random_generator=random_generator, + status=status, + jittable=True, + is_prob=True, + ) + r = correlation_from_samples(x + y + z, mc, self._nqubits) + # TODO(@refraction-ray): analytical standard deviation + return r else: - p = backend.abs(backend.diagonal(s)) - # readout error can be processed here later - mc = measurement_counts( - p, - counts=shots, - format="count_vector", - random_generator=random_generator, - jittable=True, - is_prob=True, - ) - x = list(x) - y = list(y) - z = list(z) - r = correlation_from_counts(x + y + z, mc) - # TODO(@refraction-ray): analytical standard deviation - return r + return sample_expectation_ps_noisfy( + c=self, + x=x, + y=y, + z=z, + noise_conf=noise_conf, + nmc=nmc, + shots=shots, + statusc=statusc, + status=status, + **kws, + ) sexpps = sample_expectation_ps + def readouterror_bs( + self, readout_error: Optional[Sequence[Any]] = None, p: Optional[Any] = None + ) -> Tensor: + """Apply readout error to original probabilities of bit string and return the noisy probabilities. + + :Example: + + >>> readout_error = [] + >>> readout_error.append([0.9,0.75]) # readout error for qubit 0, [p0|0,p1|1] + >>> readout_error.append([0.4,0.7]) # readout error for qubit 1, [p0|0,p1|1] + + + :param readout_error: list of readout error for each qubits. + :type readout_error: Optional[Sequence[Any]]. Tensor, List, Tuple + :param p: probabilities of bit string + :type p: Optional[Any] + :rtype: Tensor + """ + # if isinstance(readout_error, tuple): + # readout_error = list[readout_error] # type: ignore + + nqubit = len(readout_error) # type: ignore + readoutlist = [] + for i in range(nqubit): + readoutlist.append( + [ + [readout_error[i][0], 1 - readout_error[i][1]], # type: ignore + [1 - readout_error[i][0], readout_error[i][1]], # type: ignore + ] + ) + readoutlist = backend.cast( + backend.convert_to_tensor(readoutlist), dtype=dtypestr + ) + + ms = [Gate(readoutlist[i]) for i in range(nqubit)] + p = backend.cast(p, dtypestr) + p = Gate(backend.reshape2(p)) + for i in range(nqubit): + ms[i][1] ^ p[i] + nodes = ms + [p] + r = contractor(nodes, output_edge_order=[m[0] for m in ms]).tensor + p = backend.reshape(r, [-1]) + + return backend.real(p) + def replace_inputs(self, inputs: Tensor) -> None: """ Replace the input state with the circuit structure unchanged. @@ -676,7 +818,7 @@ def replace_inputs(self, inputs: Tensor) -> None: else: # TODO(@refraction-ray) replace several start as inputs raise NotImplementedError("not support replace with no inputs") - def cond_measurement(self, index: int) -> Tensor: + def cond_measurement(self, index: int, status: Optional[float] = None) -> Tensor: """ Measurement on z basis at ``index`` qubit based on quantum amplitude (not post-selection). The highlight is that this method can return the @@ -705,7 +847,10 @@ def cond_measurement(self, index: int) -> Tensor: :rtype: Tensor """ return self.general_kraus( # type: ignore - [np.array([[1.0, 0], [0, 0]]), np.array([[0, 0], [0, 1]])], index, name="measure" # type: ignore + [np.array([[1.0, 0], [0, 0]]), np.array([[0, 0], [0, 1]])], + index, + status=status, + name="measure", ) cond_measure = cond_measurement diff --git a/tensorcircuit/channels.py b/tensorcircuit/channels.py index 9ce944cc..6b2fd959 100644 --- a/tensorcircuit/channels.py +++ b/tensorcircuit/channels.py @@ -4,8 +4,6 @@ import sys from typing import Any, Sequence, Union, Optional, Dict -from operator import and_ -from functools import reduce from functools import partial import numpy as np @@ -24,6 +22,13 @@ Matrix = Any +class KrausList(list): # type: ignore + def __init__(self, iterable, name, is_unitary): # type: ignore + super().__init__(iterable) + self.name = name + self.is_unitary = is_unitary + + def _sqrt(a: Tensor) -> Tensor: r""" Return the square root of Tensor with default global dtype @@ -93,108 +98,120 @@ def depolarizingchannel(px: float, py: float, pz: float) -> Sequence[Gate]: x = Gate(_sqrt(px) * gates.x().tensor) # type: ignore y = Gate(_sqrt(py) * gates.y().tensor) # type: ignore z = Gate(_sqrt(pz) * gates.z().tensor) # type: ignore - return [i, x, y, z] + return KrausList([i, x, y, z], name="depolarizing", is_unitary=True) -def generaldepolarizingchannel( - p: Union[float, Sequence[Any]], num_qubits: int = 1 -) -> Sequence[Gate]: +def isotropicdepolarizingchannel(p: float, num_qubits: int = 1) -> Sequence[Gate]: r""" - Return a Depolarizing Channel for 1 qubit or 2 qubits + Return an isotropic depolarizing channel. + + .. math:: + + \mathcal{E}(\rho) = (1 - p)\rho + p/(4^n-1)\sum_j P_j \rho P_j + + where $n$ is the number of qubits and $P_j$ are $n$-qubit Pauli strings except $I$. + Or alternatively + + .. math:: + + \mathcal{E}(\rho) = \frac{4^n}{4^n-1}p \frac{I}{2} + (1 - \frac{4^n}{4^n-1}p) \rho + + .. note:: + + The definition of ``p`` in this method is different from :func:`generaldepolarizingchannel`. :Example: - >>> cs = tc.channels.generaldepolarizingchannel([0.1,0.1,0.1],1) - >>> tc.channels.kraus_identity_check(cs) - >>> cs = tc.channels.generaldepolarizingchannel(0.02,2) + >>> cs = tc.channels.isotropicdepolarizingchannel(0.30,2) >>> tc.channels.kraus_identity_check(cs) - :param p: parameter for each Pauli channel - :type p: Union[float, Sequence] - :param num_qubits: number of qubits, 1 and 2 are avaliable, defaults 1 + :param p: error probability + :type p: float + :param num_qubits: number of qubits, defaults 1 :type num_qubits: int, optional :return: Sequences of Gates :rtype: Sequence[Gate] """ + real_p = p / (4**num_qubits - 1) + return generaldepolarizingchannel(real_p, num_qubits) - if num_qubits == 1: - if isinstance(p, float): +def generaldepolarizingchannel( + p: Union[float, Sequence[float]], num_qubits: int = 1 +) -> Sequence[Gate]: + r""" + Return a depolarizing channel. + If :math:`p` is a float number, the one qubit channel is - assert p > 0 and p < 1 / 3, "p should be >0 and <1/3" - probs = [1 - 3 * p] + 3 * [p] + .. math:: - elif isinstance(p, list): + \mathcal{E}(\rho) = (1 - 3p)\rho + p(X\rho X + Y\rho Y + Z\rho Z) - assert reduce( - and_, [pi > 0 and pi < 1 for pi in p] - ), "p should be >0 and <1" - probs = [1 - sum(p)] + p # type: ignore + Or alternatively - elif isinstance(p, tuple): + .. math:: - p = list[p] # type: ignore - assert reduce( - and_, [pi > 0 and pi < 1 for pi in p] - ), "p should be >0 and <1" - probs = [1 - sum(p)] + p # type: ignore + \mathcal{E}(\rho) = 4p \frac{I}{2} + (1 - 4p) \rho - else: - raise ValueError("p should be float or list") + .. note:: - elif num_qubits == 2: + The definition of ``p`` in this method is different from :func:`isotropicdepolarizingchannel`. - if isinstance(p, float): - assert p > 0 and p < 1, "p should be >0 and <1/15" - probs = [1 - 15 * p] + 15 * [p] + And if :math:`p` is a sequence, the one qubit channel is - elif isinstance(p, list): + .. math:: - assert reduce( - and_, [pi > 0 and pi < 1 for pi in p] - ), "p should be >0 and <1" - probs = [1 - sum(p)] + p # type: ignore + \mathcal{E}(\rho) = (1 - \sum_i p_i) \rho + p_1 X\rho X + p_2 Y\rho Y + p_3 \rho Z - elif isinstance(p, tuple): + The logic for two-qubit or more-qubit channel follows similarly. - p = list[p] # type: ignore - assert reduce( - and_, [pi > 0 and pi < 1 for pi in p] - ), "p should be >0 and <1" - probs = [1 - sum(p)] + p # type: ignore + :Example: - else: - raise ValueError("p should be float or list") + >>> cs = tc.channels.generaldepolarizingchannel([0.1,0.1,0.1],1) + >>> tc.channels.kraus_identity_check(cs) + >>> cs = tc.channels.generaldepolarizingchannel(0.02,2) + >>> tc.channels.kraus_identity_check(cs) - if num_qubits == 1: - tup = [gates.i().tensor, gates.x().tensor, gates.y().tensor, gates.z().tensor] # type: ignore - if num_qubits == 2: - tup = [ - gates.ii().tensor, # type: ignore - gates.ix().tensor, # type: ignore - gates.iy().tensor, # type: ignore - gates.iz().tensor, # type: ignore - gates.xi().tensor, # type: ignore - gates.xx().tensor, # type: ignore - gates.xy().tensor, # type: ignore - gates.xz().tensor, # type: ignore - gates.yi().tensor, # type: ignore - gates.yx().tensor, # type: ignore - gates.yy().tensor, # type: ignore - gates.yz().tensor, # type: ignore - gates.zi().tensor, # type: ignore - gates.zx().tensor, # type: ignore - gates.zy().tensor, # type: ignore - gates.zz().tensor, # type: ignore - ] + + :param p: parameter for each Pauli channel + :type p: Union[float, Sequence] + :param num_qubits: number of qubits, defaults 1 + :type num_qubits: int, optional + :return: Sequences of Gates + :rtype: Sequence[Gate] + """ + m = 4**num_qubits - 1 + if isinstance(p, float): + probs = [1 - m * p] + m * [p] + elif isinstance(p, Sequence): + if not len(p) == m: + raise ValueError(f"Invalid probability input {p}") + probs = [1 - sum(p)] + list(p) + else: + raise ValueError("p should be float or list") + + if not np.all(np.array(probs) >= 0): + raise ValueError(f"Invalid probability input {p}") + + paulis = [gates.i().tensor, gates.x().tensor, gates.y().tensor, gates.z().tensor] # type: ignore + tup = paulis + for _ in range(num_qubits - 1): + old_tup = tup + tup = [] + for pauli in paulis: + for term in old_tup: + mat = np.kron(pauli, term).reshape([2, 2] * num_qubits) + tup.append(mat) + + assert len(tup) == len(probs) Gkarus = [] for pro, paugate in zip(probs, tup): Gkarus.append(Gate(_sqrt(pro) * paugate)) - return Gkarus + return KrausList(Gkarus, name="depolarizing", is_unitary=True) def amplitudedampingchannel(gamma: float, p: float) -> Sequence[Gate]: @@ -247,7 +264,7 @@ def amplitudedampingchannel(gamma: float, p: float) -> Sequence[Gate]: m1 = _sqrt(p) * (_sqrt(gamma) * g01) m2 = _sqrt(1 - p) * (_sqrt(1 - gamma) * g00 + g11) m3 = _sqrt(1 - p) * (_sqrt(gamma) * g10) - return [m0, m1, m2, m3] + return KrausList([m0, m1, m2, m3], name="amplitude_damping", is_unitary=False) def resetchannel() -> Sequence[Gate]: @@ -274,7 +291,7 @@ def resetchannel() -> Sequence[Gate]: """ m0 = Gate(np.array([[1, 0], [0, 0]], dtype=cons.npdtype)) m1 = Gate(np.array([[0, 1], [0, 0]], dtype=cons.npdtype)) - return [m0, m1] + return KrausList([m0, m1], name="reset", is_unitary=False) def phasedampingchannel(gamma: float) -> Sequence[Gate]: @@ -305,7 +322,7 @@ def phasedampingchannel(gamma: float) -> Sequence[Gate]: g11 = Gate(np.array([[0, 0], [0, 1]], dtype=cons.npdtype)) m0 = 1.0 * (g00 + _sqrt(1 - gamma) * g11) # 1* ensure gate m1 = _sqrt(gamma) * g11 - return [m0, m1] + return KrausList([m0, m1], name="phase_damping", is_unitary=False) def thermalrelaxationchannel( @@ -394,7 +411,7 @@ def thermalrelaxationchannel( Gkraus = [] for pro, paugate in zip(probs, tup): Gkraus.append(Gate(_sqrt(pro) * paugate)) - return Gkraus + return KrausList(Gkraus, name="thermal_relaxation", is_unitary=False) elif method == "ByChoi" or ( method == "AUTO" and backend.real(t2) >= backend.real(t1) @@ -439,7 +456,8 @@ def thermalrelaxationchannel( nmax = 3 listKraus = choi_to_kraus(choi, truncation_rules={"max_singular_values": nmax}) - return [Gate(i) for i in listKraus] + Gatelist = [Gate(i) for i in listKraus] + return KrausList(Gatelist, name="thermal_relaxation", is_unitary=False) else: raise ValueError("No valid method is provided") @@ -513,6 +531,8 @@ def kraus_to_super_gate(kraus_list: Sequence[Gate]) -> Tensor: :rtype: Tensor """ kraus_tensor_list = [k.tensor for k in kraus_list] + kraus_tensor_list = [backend.reshapem(k) for k in kraus_tensor_list] + k = kraus_tensor_list[0] u = backend.kron(k, backend.conj(k)) for k in kraus_tensor_list[1:]: @@ -521,7 +541,7 @@ def kraus_to_super_gate(kraus_list: Sequence[Gate]) -> Tensor: @partial( - interfaces.args_to_tensor, # type: ignore + interfaces.args_to_tensor, argnums=[0], gate_to_tensor=True, ) @@ -562,7 +582,7 @@ def kraus_to_super(kraus_list: Sequence[Matrix]) -> Matrix: @partial( - interfaces.args_to_tensor, # type: ignore + interfaces.args_to_tensor, argnums=[0], gate_to_tensor=True, ) @@ -603,7 +623,7 @@ def super_to_choi(superop: Matrix) -> Matrix: @partial( - interfaces.args_to_tensor, # type: ignore + interfaces.args_to_tensor, argnums=[0], gate_to_tensor=True, ) @@ -630,7 +650,7 @@ def reshuffle(op: Matrix, order: Sequence[int]) -> Matrix: @partial( - interfaces.args_to_tensor, # type: ignore + interfaces.args_to_tensor, argnums=[0], gate_to_tensor=True, ) @@ -685,7 +705,6 @@ def choi_to_kraus( truncation_rules = {} if truncation_rules.get("max_singular_values", None) is not None: - nkraus = truncation_rules["max_singular_values"] for i in range(nkraus): k = backend.sqrt(backend.cast(e[-(i + 1)], dtypestr)) * backend.transpose( @@ -714,7 +733,7 @@ def choi_to_kraus( @partial( - interfaces.args_to_tensor, # type: ignore + interfaces.args_to_tensor, argnums=[0], gate_to_tensor=True, ) @@ -732,7 +751,7 @@ def kraus_to_choi(kraus_list: Sequence[Matrix]) -> Matrix: @partial( - interfaces.args_to_tensor, # type: ignore + interfaces.args_to_tensor, argnums=[0], gate_to_tensor=True, ) @@ -749,7 +768,7 @@ def choi_to_super(choi: Matrix) -> Matrix: @partial( - interfaces.args_to_tensor, # type: ignore + interfaces.args_to_tensor, argnums=[0], gate_to_tensor=True, ) @@ -828,7 +847,7 @@ def krausmatrix_to_krausgate(kraus_list: Sequence[Matrix]) -> Sequence[Gate]: @partial( - interfaces.args_to_tensor, # type: ignore + interfaces.args_to_tensor, argnums=[0, 1], gate_to_tensor=True, ) @@ -862,7 +881,7 @@ def evol_kraus(density_matrix: Matrix, kraus_list: Sequence[Matrix]) -> Matrix: @partial( - interfaces.args_to_tensor, # type: ignore + interfaces.args_to_tensor, argnums=[0, 1], gate_to_tensor=True, ) @@ -952,3 +971,26 @@ def check_rep_transformation( print("test evolution identity of kraus and superop") density_matrix3 = evol_superop(density_matrix, superop) np.testing.assert_allclose(density_matrix1, density_matrix3, atol=1e-5) + + +def composedkraus(kraus1: KrausList, kraus2: KrausList) -> KrausList: + """ + Compose the noise channels + + :param kraus1: One noise channel + :type kraus1: KrausList + :param kraus2: Another noise channel + :type kraus2: KrausList + :return: Composed nosie channel + :rtype: KrausList + """ + new_kraus = [] + for i in kraus1: + for j in kraus2: + k = Gate(backend.reshapem(i.tensor) @ backend.reshapem(j.tensor)) + new_kraus.append(k) + return KrausList( + new_kraus, + name=kraus1.name + "_" + kraus2.name, + is_unitary=kraus1.is_unitary and kraus2.is_unitary, + ) diff --git a/tensorcircuit/circuit.py b/tensorcircuit/circuit.py index d33f764e..a293e3df 100644 --- a/tensorcircuit/circuit.py +++ b/tensorcircuit/circuit.py @@ -1,6 +1,7 @@ """ Quantum circuit: the state simulator """ + # pylint: disable=invalid-name from typing import Any, Callable, Dict, List, Optional, Sequence, Tuple @@ -100,7 +101,7 @@ def __init__( self._front = new_front self.coloring_nodes(nodes) - self._nodes = nodes # type: ignore + self._nodes = nodes self._start_index = len(nodes) # self._start = nodes @@ -109,6 +110,7 @@ def __init__( # self._qcode = "" # deprecated # self._qcode += str(self._nqubits) + "\n" self._qir: List[Dict[str, Any]] = [] + self._extra_qir: List[Dict[str, Any]] = [] def replace_mps_inputs(self, mps_inputs: QuOperator) -> None: """ @@ -393,6 +395,7 @@ def _unitary_kraus_template( sites = len(index) kraus = [k.tensor if isinstance(k, tn.Node) else k for k in kraus] kraus = [gates.array_to_tensor(k) for k in kraus] + kraus = [backend.reshapem(k) for k in kraus] if prob is None: prob = [ backend.real(backend.trace(backend.adjoint(k) @ k) / k.shape[0]) @@ -407,9 +410,14 @@ def _unitary_kraus_template( l = int(prob.shape[0]) # type: ignore def step_function(x: Tensor) -> Tensor: - r = backend.sum( - backend.stack([backend.sign(x - prob_cumsum[i]) for i in range(l - 1)]) - ) + if l == 1: + r = backend.convert_to_tensor(0.0) + else: + r = backend.sum( + backend.stack( + [backend.sign(x - prob_cumsum[i]) for i in range(l - 1)] + ) + ) r = backend.cast(r / 2.0 + (l - 1) / 2.0, dtype="int32") # [0: kraus[0], 1: kraus[1]...] return r @@ -500,6 +508,7 @@ def _general_kraus_2( kraus: Sequence[Gate], *index: int, status: Optional[float] = None, + with_prob: bool = False, name: Optional[str] = None, ) -> Tensor: # the graph building time is frustratingly slow, several minutes @@ -542,20 +551,25 @@ def calculate_kraus_p(i: int) -> Tensor: return backend.real(norm_square) prob = [calculate_kraus_p(i) for i in range(len(kraus))] + eps = 1e-10 new_kraus = [ - k / backend.cast(backend.sqrt(w), dtypestr) + k / backend.cast(backend.sqrt(w) + eps, dtypestr) for w, k in zip(prob, kraus_tensor) ] - - return self.unitary_kraus( + pick = self.unitary_kraus( new_kraus, *index, prob=prob, status=status, name=name ) + if with_prob is False: + return pick + else: + return pick, prob def general_kraus( self, kraus: Sequence[Gate], *index: int, status: Optional[float] = None, + with_prob: bool = False, name: Optional[str] = None, ) -> Tensor: """ @@ -575,7 +589,9 @@ def general_kraus( when the random number will be generated automatically :type status: Optional[float], optional """ - return self._general_kraus_2(kraus, *index, status=status, name=name) + return self._general_kraus_2( + kraus, *index, status=status, with_prob=with_prob, name=name + ) apply_general_kraus = general_kraus @@ -766,11 +782,15 @@ def measure_reference( # TODO(@refraction-ray): more _before function like state_before? and better API? - def expectation( # type: ignore + def expectation( self, *ops: Tuple[tn.Node, List[int]], reuse: bool = True, enable_lightcone: bool = False, + noise_conf: Optional[Any] = None, + nmc: int = 1000, + status: Optional[Tensor] = None, + **kws: Any, ) -> Tensor: """ Compute the expectation of corresponding operators. @@ -782,6 +802,20 @@ def expectation( # type: ignore >>> c.expectation((tc.gates.z(), [0])) array(0.+0.j, dtype=complex64) + >>> c = tc.Circuit(2) + >>> c.cnot(0, 1) + >>> c.rx(0, theta=0.4) + >>> c.rx(1, theta=0.8) + >>> c.h(0) + >>> c.h(1) + >>> error1 = tc.channels.generaldepolarizingchannel(0.1, 1) + >>> error2 = tc.channels.generaldepolarizingchannel(0.06, 2) + >>> noise_conf = NoiseConf() + >>> noise_conf.add_noise("rx", error1) + >>> noise_conf.add_noise("cnot", [error2], [[0, 1]]) + >>> c.expectation((tc.gates.x(), [0]), noise_conf=noise_conf, nmc=10000) + (0.46274087-3.764033e-09j) + :param ops: Operator and its position on the circuit, eg. ``(tc.gates.z(), [1, ]), (tc.gates.x(), [2, ])`` is for operator :math:`Z_1X_2`. :type ops: Tuple[tn.Node, List[int]] @@ -790,22 +824,41 @@ def expectation( # type: ignore :type reuse: bool, optional :param enable_lightcone: whether enable light cone simplification, defaults to False :type enable_lightcone: bool, optional + :param noise_conf: Noise Configuration, defaults to None + :type noise_conf: Optional[NoiseConf], optional + :param nmc: repetition time for Monte Carlo sampling for noisfy calculation, defaults to 1000 + :type nmc: int, optional + :param status: external randomness given by tensor uniformly from [0, 1], defaults to None, + used for noisfy circuit sampling + :type status: Optional[Tensor], optional :raises ValueError: "Cannot measure two operators in one index" :return: Tensor with one element :rtype: Tensor """ - # if not reuse: - # nodes1, edge1 = self._copy() - # nodes2, edge2 = self._copy(conj=True) - # else: # reuse - - # self._nodes = nodes1 - if enable_lightcone: - reuse = False - nodes1 = self.expectation_before(*ops, reuse=reuse) - if enable_lightcone: - nodes1 = _full_light_cone_cancel(nodes1) - return contractor(nodes1).tensor + from .noisemodel import expectation_noisfy + + if noise_conf is None: + # if not reuse: + # nodes1, edge1 = self._copy() + # nodes2, edge2 = self._copy(conj=True) + # else: # reuse + + # self._nodes = nodes1 + if enable_lightcone: + reuse = False + nodes1 = self.expectation_before(*ops, reuse=reuse) + if enable_lightcone: + nodes1 = _full_light_cone_cancel(nodes1) + return contractor(nodes1).tensor + else: + return expectation_noisfy( + self, + *ops, + noise_conf=noise_conf, + nmc=nmc, + status=status, + **kws, + ) Circuit._meta_apply() diff --git a/tensorcircuit/cloud/__init__.py b/tensorcircuit/cloud/__init__.py new file mode 100644 index 00000000..56d5a724 --- /dev/null +++ b/tensorcircuit/cloud/__init__.py @@ -0,0 +1,5 @@ +from . import apis +from . import abstraction +from . import wrapper +from .wrapper import batch_expectation_ps +from .apis import submit_task diff --git a/tensorcircuit/cloud/abstraction.py b/tensorcircuit/cloud/abstraction.py new file mode 100644 index 00000000..b94e2a69 --- /dev/null +++ b/tensorcircuit/cloud/abstraction.py @@ -0,0 +1,472 @@ +""" +Abstraction for Provider, Device and Task +""" + +from typing import Any, Dict, List, Optional, Union, Tuple +from functools import partial +import time + +import networkx as nx + +from ..results import readout_mitigation as rem +from ..results import counts +from ..utils import arg_alias + + +class TCException(BaseException): + pass + + +class TaskException(TCException): + pass + + +class TaskUnfinished(TaskException): + def __init__(self, taskid: str, state: str): + self.taskid = taskid + self.state = state + super().__init__( + "Task %s is not completed yet, now in %s state" % (self.taskid, self.state) + ) + + +class TaskFailed(TaskException): + def __init__(self, taskid: str, state: str, message: str): + self.taskid = taskid + self.state = state + self.message = message + super().__init__( + "Task %s is in %s state with err message %s" + % (self.taskid, self.state, self.message) + ) + + +class Provider: + """ + Provider abstraction for cloud connection, eg. "tencent", "local" + """ + + activated_providers: Dict[str, "Provider"] = {} + + def __init__(self, name: str, lower: bool = True): + if lower is True: + name = name.lower() + self.name = name + + def __str__(self) -> str: + return self.name + + __repr__ = __str__ + + @classmethod + def from_name(cls, provider: Union[str, "Provider"] = "tencent") -> "Provider": + if provider is None: + provider = "tencent" + if isinstance(provider, cls): + p = provider + elif isinstance(provider, str): + if provider in cls.activated_providers: + return cls.activated_providers[provider] + else: + p = cls(provider) + cls.activated_providers[provider] = p + else: + raise ValueError( + "Unsupported format for `provider` argument: %s" % provider + ) + return p + + def set_token(self, token: str, cached: bool = True) -> Any: + from .apis import set_token + + return set_token(token, self, cached=cached) + + def get_token(self) -> str: + from .apis import get_token + + return get_token(self) # type: ignore + + def list_devices(self, **kws: Any) -> Any: + from .apis import list_devices + + return list_devices(self, **kws) + + def get_device(self, device: Optional[Union[str, "Device"]]) -> "Device": + from .apis import get_device + + return get_device(self, device) + + def list_tasks(self, **filter_kws: Any) -> List["Task"]: + from .apis import list_tasks + + return list_tasks(self, **filter_kws) + + +sep = "::" + + +class Device: + """ + Device abstraction for cloud connection, eg. quantum chips + """ + + activated_devices: Dict[str, "Device"] = {} + + def __init__( + self, + name: str, + provider: Optional[Union[str, Provider]] = None, + lower: bool = False, + ): + if lower is True: + name = name.lower() + if provider is not None: + self.provider = Provider.from_name(provider) + if len(name.split(sep)) > 1: + self.name = name.split(sep)[1] + else: + self.name = name + else: # no explicit provider + if len(name.split(sep)) > 1: + self.name = name.split(sep)[1] + self.provider = Provider.from_name(name.split(sep)[0]) + else: + from .apis import get_provider + + self.name = name + self.provider = get_provider() + self.readout_mit: Any = None + + def __str__(self) -> str: + return self.provider.name + sep + self.name + + __repr__ = __str__ + + @classmethod + def from_name( + cls, + device: Union[str, "Device"], + provider: Optional[Union[str, Provider]] = None, + ) -> "Device": + # if device is None: + # raise ValueError("Must specify on device instead of default ``None``") + if isinstance(device, cls): + d = device + elif isinstance(device, str): + if len(device.split(sep)) > 1: + provider = device.split(sep)[0] + device = device.split(sep)[1] + if provider is None: + pn = "" + elif isinstance(provider, str): + pn = provider + else: + pn = provider.name + if pn + sep + device in cls.activated_devices: + return cls.activated_devices[pn + sep + device] + else: + d = cls(device, provider) + + cls.activated_devices[pn + sep + device] = d + else: + raise ValueError("Unsupported format for `provider` argument: %s" % device) + + return d + + def set_token(self, token: str, cached: bool = True) -> Any: + from .apis import set_token + + return set_token(token, provider=self.provider, device=self, cached=cached) + + def get_token(self) -> Optional[str]: + from .apis import get_token + + s = get_token(provider=self.provider, device=self) + if s is not None: + return s + # fallback to provider default + return get_token(provider=self.provider) + + def list_properties(self) -> Dict[str, Any]: + """ + List all device properties in as dict + + :return: [description] + :rtype: Dict[str, Any] + """ + from .apis import list_properties + + return list_properties(self.provider, self) + + def native_gates(self) -> List[str]: + """ + List native gates supported for the device, str conforms qiskit convention + + :return: _description_ + :rtype: List[str] + """ + properties = self.list_properties() + + if "native_gates" in properties: + return properties["native_gates"] # type: ignore + return [] + + def topology(self) -> List[Tuple[int, int]]: + """ + Get the bidirectional topology link list of the device + + :return: [description] + :rtype: List[Tuple[int, int]] + """ + properties = self.list_properties() + if "links" not in properties: + return # type: ignore + links = [] + for link in properties["links"]: + links.append((link[0], link[1])) + links.append((link[1], link[0])) + links = list(set(links)) + links = [list(link) for link in links] # type: ignore + # compatible with coupling_map in qiskit + return links + + def topology_graph(self, visualize: bool = False) -> nx.Graph: + """ + Get the qubit topology in ``nx.Graph`` or directly visualize it + + :param visualize: [description], defaults to False + :type visualize: bool, optional + :return: [description] + :rtype: nx.Graph + """ + pro = self.list_properties() + if not ("links" in pro and "bits" in pro): + return # type: ignore + g = nx.Graph() + node_color = [] + edge_color = [] + for i in pro["bits"]: + g.add_node(i) + node_color.append(pro["bits"][i]["T1"]) + for e1, e2 in pro["links"]: + g.add_edge(e1, e2) + edge_color.append(pro["links"][(e1, e2)]["CZErrRate"]) + if visualize is False: + return g + from matplotlib import colormaps + + # pos1 = nx.planar_layout(g) + # pos2 = nx.spring_layout(g, pos=pos1, k=2) + pos = nx.kamada_kawai_layout(g) + return nx.draw( + g, + pos=pos, + with_labels=True, + node_size=600, + node_color=node_color, + cmap=colormaps["Wistia"], + vmin=max(min(node_color) - 5, 0), + width=2.5, + edge_color=edge_color, + edge_cmap=colormaps["gray"], + edge_vmin=0, + edge_vmax=max(edge_color) * 1.2, + ) + + def get_task(self, taskid: str) -> "Task": + from .apis import get_task + + return get_task(taskid, device=self) + + def submit_task(self, **task_kws: Any) -> List["Task"]: + from .apis import submit_task + + return submit_task(provider=self.provider, device=self, **task_kws) + + def list_tasks(self, **filter_kws: Any) -> List["Task"]: + from .apis import list_tasks + + return list_tasks(self.provider, self, **filter_kws) + + +sep2 = "~~" + + +class Task: + """ + Task abstraction for quantum jobs on the cloud + """ + + def __init__(self, id_: str, device: Optional[Device] = None): + self.id_ = id_ + self.device = device + self.more_details: Dict[str, Any] = {} + + def __repr__(self) -> str: + return self.device.__repr__() + sep2 + self.id_ + + __str__ = __repr__ + + def get_device(self) -> Device: + """ + Query which device the task is run on + + :return: _description_ + :rtype: Device + """ + if self.device is None: + return Device.from_name(self.details()["device"]) + else: + return Device.from_name(self.device) + + @partial(arg_alias, alias_dict={"blocked": ["wait"]}) + def details(self, blocked: bool = False, **kws: Any) -> Dict[str, Any]: + """ + Get the current task details + + + :param blocked: whether return until task is finished, defaults to False + :type blocked: bool + :return: _description_ + :rtype: Dict[str, Any] + """ + from .apis import get_task_details + + if blocked is False: + dt = get_task_details(self, **kws) + dt.update(self.more_details) + return dt + s = self.state() + tries = 0 + while s == "pending": + time.sleep(0.5 + tries / 10) + tries += 1 + s = self.state() + return self.details(**kws) # type: ignore + + def get_logical_physical_mapping(self) -> Optional[Dict[int, int]]: + d = self.details() + try: + mp = d["optimization"]["pairs"] + except KeyError: + if "qubits" in d and isinstance(d["qubits"], int): + mp = {i: i for i in range(d["qubits"])} + else: + mp = None + return mp # type: ignore + + def add_details(self, **kws: Any) -> None: + self.more_details.update(kws) + + def state(self) -> str: + """ + Query the current task status + + :return: _description_ + :rtype: str + """ + r = self.details() + return r["state"] # type: ignore + + status = state + + def resubmit(self) -> "Task": + """ + resubmit the task + + :return: the resubmitted task + :rtype: Task + """ + from .apis import resubmit_task + + return resubmit_task(self) + + @partial(arg_alias, alias_dict={"format": ["format_"], "blocked": ["wait"]}) + def results( + self, + format: Optional[str] = None, + blocked: bool = True, + mitigated: bool = False, + calibriation_options: Optional[Dict[str, Any]] = None, + readout_mit: Optional[rem.ReadoutMit] = None, + mitigation_options: Optional[Dict[str, Any]] = None, + ) -> counts.ct: + """ + get task results of the qjob + + :param format: unsupported now, defaults to None, which is "count_dict_bin" + :type format: Optional[str], optional + :param blocked: whether blocked to wait until the result is returned, defaults to False, + which raise error when the task is unfinished + :type blocked: bool, optional + :param mitigated: whether enable readout error mitigation, defaults to False + :type mitigated: bool, optional + :param calibriation_options: option dict for ``ReadoutMit.cals_from_system``, + defaults to None + :type calibriation_options: Optional[Dict[str, Any]], optional + :param readout_mit: if given, directly use the calibriation info on ``readout_mit``, + defaults to None + :type readout_mit: Optional[rem.ReadoutMit], optional + :param mitigation_options: option dict for ``ReadoutMit.apply_correction``, defaults to None + :type mitigation_options: Optional[Dict[str, Any]], optional + :return: count dict results + :rtype: Any + """ + if not blocked: + s = self.state() + if s != "completed": + raise TaskUnfinished(self.id_, s) + r = self.details()["results"] + r = counts.sort_count(r) # type: ignore + else: + s = self.state() + tries = 0 + while s != "completed": + if s in ["failed"]: + err = self.details().get("err", "") + raise TaskFailed(self.id_, s, err) + time.sleep(0.5 + tries / 10) + tries += 1 + s = self.state() + r = self.results(format=format, blocked=False, mitigated=False) + if mitigated is False: + return r # type: ignore + nqubit = len(list(r.keys())[0]) + + # mitigated is True: + device = self.get_device() + if device.provider.name != "tencent": + raise ValueError("Only tencent provider supports auto readout mitigation") + if readout_mit is None and getattr(device, "readout_mit", None) is None: + + def run(cs: Any, shots: Any) -> Any: + """ + current workaround for batch + """ + from .apis import submit_task + + ts = submit_task(circuit=cs, shots=shots, device=device.name + "?o=0") + return [t.results(blocked=True) for t in ts] # type: ignore + + shots = self.details()["shots"] + readout_mit = rem.ReadoutMit(run) + if calibriation_options is None: + calibriation_options = {} + readout_mit.cals_from_system( + list(range(nqubit)), shots, **calibriation_options + ) + device.readout_mit = readout_mit + elif readout_mit is None: + readout_mit = device.readout_mit + + if mitigation_options is None: + try: + mitigation_options = { + "logical_physical_mapping": self.details()["optimization"]["pairs"] + } + except KeyError: + mitigation_options = {} + miti_count = readout_mit.apply_correction( + r, list(range(nqubit)), **mitigation_options + ) + return counts.sort_count(miti_count) diff --git a/tensorcircuit/cloud/apis.py b/tensorcircuit/cloud/apis.py new file mode 100644 index 00000000..0e4f6eb6 --- /dev/null +++ b/tensorcircuit/cloud/apis.py @@ -0,0 +1,541 @@ +""" +main entrypoints of cloud module +""" + +from typing import Any, List, Optional, Dict, Union, Tuple +from base64 import b64decode, b64encode +from functools import partial +import json +import os +import sys +import logging + +from .abstraction import Provider, Device, Task, sep, sep2 + +logger = logging.getLogger(__name__) + + +try: + from . import tencent # type: ignore +except (ImportError, ModuleNotFoundError): + logger.warning("fail to load cloud provider module: tencent") + +try: + from . import local +except (ImportError, ModuleNotFoundError): + logger.warning("fail to load cloud provider module: local") + +try: + from . import quafu_provider +except (ImportError, ModuleNotFoundError): + pass + # logger.warning("fail to load cloud provider module: quafu") + +package_name = "tensorcircuit" +thismodule = sys.modules[__name__] + + +default_provider = Provider.from_name("tencent") +avail_providers = ["tencent", "local"] + + +def list_providers() -> List[Provider]: + """ + list all cloud providers that tensorcircuit supports + + :return: _description_ + :rtype: List[Provider] + """ + return [get_provider(s) for s in avail_providers] + + +def set_provider( + provider: Optional[Union[str, Provider]] = None, set_global: bool = True +) -> Provider: + """ + set default provider for the program + + :param provider: _description_, defaults to None + :type provider: Optional[Union[str, Provider]], optional + :param set_global: whether set, defaults to True, + if False, equivalent to ``get_provider`` + :type set_global: bool, optional + :return: _description_ + :rtype: Provider + """ + if provider is None: + provider = default_provider + provider = Provider.from_name(provider) + if set_global: + for module in sys.modules: + if module.startswith(package_name): + setattr(sys.modules[module], "default_provider", provider) + return provider + + +set_provider() +get_provider = partial(set_provider, set_global=False) + +default_device = Device.from_name("tencent::simulator:tc") + + +def set_device( + provider: Optional[Union[str, Provider]] = None, + device: Optional[Union[str, Device]] = None, + set_global: bool = True, +) -> Device: + """ + set the default device + + :param provider: provider of the device, defaults to None + :type provider: Optional[Union[str, Provider]], optional + :param device: the device, defaults to None + :type device: Optional[Union[str, Device]], optional + :param set_global: whether set, defaults to True, + if False, equivalent to ``get_device``, defaults to True + :type set_global: bool, optional + :return: _description_ + :rtype: Device + """ + if provider is not None and device is None: + provider, device = None, provider + if device is None and provider is not None: + raise ValueError("Please specify the device apart from the provider") + if device is None: + device = default_device + + if isinstance(device, str): + if len(device.split(sep)) > 1: + provider, device = device.split(sep) + provider = Provider.from_name(provider) + device = Device.from_name(device, provider) + else: + if provider is None: + provider = get_provider() + provider = Provider.from_name(provider) + device = Device.from_name(device, provider) + else: + if provider is None: + provider = get_provider() + provider = Provider.from_name(provider) + device = Device.from_name(device, provider) + + if set_global: + for module in sys.modules: + if module.startswith(package_name): + setattr(sys.modules[module], "default_device", device) + return device + + +set_device() +get_device = partial(set_device, set_global=False) + + +def b64encode_s(s: str) -> str: + return b64encode(s.encode("utf-8")).decode("utf-8") + + +def b64decode_s(s: str) -> str: + return b64decode(s.encode("utf-8")).decode("utf-8") + + +saved_token: Dict[str, Any] = {} + + +def _preprocess( + provider: Optional[Union[str, Provider]] = None, + device: Optional[Union[str, Device]] = None, +) -> Tuple[Provider, Device]: + """ + Smartly determine the provider and device based on the input + + :param provider: _description_, defaults to None + :type provider: Optional[Union[str, Provider]], optional + :param device: _description_, defaults to None + :type device: Optional[Union[str, Device]], optional + :return: a pair of provider and device after preprocessing + :rtype: Tuple[Provider, Device] + """ + if provider is not None and device is None: + provider, device = None, provider + if device is None: + device = get_device() + if isinstance(device, str): + if len(device.split(sep)) > 1: + device = Device.from_name(device, provider) + else: + if provider is None: + provider = get_provider() + device = Device.from_name(device, provider) + if provider is None: + provider = device.provider + if isinstance(provider, str): + provider = Provider.from_name(provider) + return provider, device # type: ignore + + +def set_token( + token: Optional[str] = None, + provider: Optional[Union[str, Provider]] = None, + device: Optional[Union[str, Device]] = None, + cached: bool = True, + clear: bool = False, +) -> Dict[str, Any]: + """ + Set API token for given provider or specifically to given device + + :param token: the API token, defaults to None + :type token: Optional[str], optional + :param provider: _description_, defaults to None + :type provider: Optional[Union[str, Provider]], optional + :param device: _description_, defaults to None + :type device: Optional[Union[str, Device]], optional + :param cached: whether save on the disk, defaults to True + :type cached: bool, optional + :param clear: if True, clear all token saved, defaults to False + :type clear: bool, optional + :return: _description_ + :rtype: Dict[str, Any] + """ + global saved_token + homedir = os.path.expanduser("~") + authpath = os.path.join(homedir, ".tc.auth.json") + # provider, device = _preprocess(provider, device) + if clear is True: + saved_token = {} + if token is None: + if cached and os.path.exists(authpath): + try: + with open(authpath, "r") as f: + file_token = json.load(f) + file_token = {k: b64decode_s(v) for k, v in file_token.items()} + # file_token = backend.tree_map(b64decode_s, file_token) + except json.JSONDecodeError: + logger.warning("token file loading failure, set empty token instead") + # TODO(@refraction-ray): better conflict solve with multiprocessing + file_token = {} + else: + file_token = {} + file_token.update(saved_token) + saved_token = file_token + else: # with token + if isinstance(provider, str): + provider = Provider.from_name(provider) + if device is None: + if provider is None: + provider = default_provider + added_token = {provider.name + sep: token} + else: + device = Device.from_name(device) + if provider is None: + provider = device.provider # type: ignore + if provider is None: + provider = default_provider + added_token = {provider.name + sep + device.name: token} # type: ignore + saved_token.update(added_token) + + if cached: + # file_token = backend.tree_map(b64encode_s, saved_token) + file_token = {k: b64encode_s(v) for k, v in saved_token.items()} + if file_token: + with open(authpath, "w") as f: + json.dump(file_token, f) + + return saved_token + + +set_token() + + +def get_token( + provider: Optional[Union[str, Provider]] = None, + device: Optional[Union[str, Device]] = None, +) -> Optional[str]: + """ + Get API token setted for given provider or device, + if no device token saved, the corresponding provider tken is returned + + :param provider: _description_, defaults to None + :type provider: Optional[Union[str, Provider]], optional + :param device: _description_, defaults to None + :type device: Optional[Union[str, Device]], optional + :return: _description_ + :rtype: Optional[str] + """ + if provider is None: + provider = get_provider() + provider = Provider.from_name(provider) + target = provider.name + sep + if device is not None: + device = Device.from_name(device, provider) + target = target + device.name + for k, v in saved_token.items(): + if k == target: + return v # type: ignore + return None + + +# token json structure +# {"tencent::": token1, "tencent::20xmon": token2} + + +def list_devices( + provider: Optional[Union[str, Provider]] = None, + token: Optional[str] = None, + **kws: Any, +) -> List[Device]: + """ + List all devices under a provider + + :param provider: _description_, defaults to None + :type provider: Optional[Union[str, Provider]], optional + :param token: _description_, defaults to None + :type token: Optional[str], optional + :return: _description_ + :rtype: Any + """ + if provider is None: + provider = default_provider + provider = Provider.from_name(provider) + if token is None: + token = provider.get_token() + if provider.name == "tencent": + return tencent.list_devices(token, **kws) # type: ignore + elif provider.name == "local": + return local.list_devices(token, **kws) + else: + raise ValueError("Unsupported provider: %s" % provider.name) + + +def list_properties( + provider: Optional[Union[str, Provider]] = None, + device: Optional[Union[str, Device]] = None, + token: Optional[str] = None, +) -> Dict[str, Any]: + """ + List properties of a given device + + :param provider: _description_, defaults to None + :type provider: Optional[Union[str, Provider]], optional + :param device: _description_, defaults to None + :type device: Optional[Union[str, Device]], optional + :param token: _description_, defaults to None + :type token: Optional[str], optional + :return: Propeties dict + :rtype: Dict[str, Any] + """ + # if provider is not None and device is None: + # provider, device = None, provider + # if device is None: + # device = default_device + # device = Device.from_name(device, provider) + # if provider is None: + # provider = device.provider + provider, device = _preprocess(provider, device) + + if token is None: + token = device.get_token() # type: ignore + if provider.name == "tencent": # type: ignore + return tencent.list_properties(device, token) # type: ignore + elif provider.name == "local": + raise ValueError("Unsupported method for local backend") + else: + raise ValueError("Unsupported provider: %s" % provider.name) # type: ignore + + +def get_task( + taskid: str, + provider: Optional[Union[str, Provider]] = None, + device: Optional[Union[str, Device]] = None, +) -> Task: + """ + Get ``Task`` object from task string, the binding device can also be provided + + :param taskid: _description_ + :type taskid: str + :param provider: _description_, defaults to None + :type provider: Optional[Union[str, Provider]], optional + :param device: _description_, defaults to None + :type device: Optional[Union[str, Device]], optional + :return: _description_ + :rtype: Task + """ + if provider is not None and device is None: + provider, device = None, provider + if device is not None: # device can be None for identify tasks + device = Device.from_name(device, provider) + elif len(taskid.split(sep2)) > 1: + device = Device(taskid.split(sep2)[0]) + taskid = taskid.split(sep2)[1] + return Task(taskid, device=device) + + +def get_task_details( + taskid: Union[str, Task], token: Optional[str] = None, prettify: bool = False +) -> Dict[str, Any]: + """ + Get task details dict given task id + + :param taskid: _description_ + :type taskid: Union[str, Task] + :param token: _description_, defaults to None + :type token: Optional[str], optional + :param prettify: whether make the returned dict more readable and more phythonic, + defaults to False + :type prettify: bool + :return: _description_ + :rtype: Dict[str, Any] + """ + if isinstance(taskid, str): + task = Task(taskid) + else: + task = taskid + if task.device is not None: + device = task.device + else: + device = default_device + if token is None: + token = device.get_token() + provider = device.provider + + if provider.name == "tencent": + return tencent.get_task_details(task, device, token, prettify) # type: ignore + elif provider.name == "local": + return local.get_task_details(task, device, token, prettify) # type: ignore + elif provider.name == "quafu": + return quafu_provider.get_task_details(task, device, token, prettify) # type: ignore + + else: + raise ValueError("Unsupported provider: %s" % provider.name) # type: ignore + + +def submit_task( + provider: Optional[Union[str, Provider]] = None, + device: Optional[Union[str, Device]] = None, + token: Optional[str] = None, + **task_kws: Any, +) -> List[Task]: + """ + submit task to the cloud platform, batch submission default enabled + + .. seealso:: + + :py:meth:`tensorcircuit.cloud.tencent.submit_task` + + :param provider: _description_, defaults to None + :type provider: Optional[Union[str, Provider]], optional + :param device: _description_, defaults to None + :type device: Optional[Union[str, Device]], optional + :param token: _description_, defaults to None + :type token: Optional[str], optional + :param task_kws: all necessary keywords arguments for task submission, + see detailed API in each provider backend: + 1. tencent - :py:meth:`tensorcircuit.cloud.tencent.submit_task` + :type task_kws: Any + :return: The task object + :rtype: List[Task] + """ + # if device is None: + # device = get_device() + # if isinstance(device, str): + # if len(device.split(sep)) > 1: + # device = Device(device, provider) + # else: + # if provider is None: + # provider = get_provider() + # device = Device(device, provider) + # if provider is None: + # provider = device.provider + provider, device = _preprocess(provider, device) + + if token is None: + token = device.get_token() # type: ignore + if provider.name == "tencent": # type: ignore + return tencent.submit_task(device, token, **task_kws) # type: ignore + elif provider.name == "local": # type: ignore + return local.submit_task(device, token, **task_kws) # type: ignore + elif provider.name == "quafu": # type: ignore + return quafu_provider.submit_task(device, token, **task_kws) # type: ignore + else: + raise ValueError("Unsupported provider: %s" % provider.name) # type: ignore + + +def resubmit_task( + task: Optional[Union[str, Task]], + token: Optional[str] = None, +) -> Task: + """ + Rerun the given task + + :param task: _description_ + :type task: Optional[Union[str, Task]] + :param token: _description_, defaults to None + :type token: Optional[str], optional + :return: _description_ + :rtype: Task + """ + if isinstance(task, str): + task = Task(task) + device = task.get_device() # type: ignore + if token is None: + token = device.get_token() + provider = device.provider + + if provider.name == "tencent": # type: ignore + return tencent.resubmit_task(task, token) # type: ignore + elif provider.name == "local": + raise ValueError("Unsupported method for local backend") + else: + raise ValueError("Unsupported provider: %s" % provider.name) # type: ignore + + +def remove_task( + task: Optional[Union[str, Task]], + token: Optional[str] = None, +) -> Task: + if isinstance(task, str): + task = Task(task) + device = task.get_device() # type: ignore + if token is None: + token = device.get_token() + provider = device.provider + + if provider.name == "tencent": # type: ignore + return tencent.remove_task(task, token) # type: ignore + elif provider.name == "local": + raise ValueError("Unsupported method for local backend") + else: + raise ValueError("Unsupported provider: %s" % provider.name) # type: ignore + + +def list_tasks( + provider: Optional[Union[str, Provider]] = None, + device: Optional[Union[str, Device]] = None, + token: Optional[str] = None, + **filter_kws: Any, +) -> List[Task]: + """ + List tasks based on given filters + + :param provider: _description_, defaults to None + :type provider: Optional[Union[str, Provider]], optional + :param device: _description_, defaults to None + :type device: Optional[Union[str, Device]], optional + :param token: _description_, defaults to None + :type token: Optional[str], optional + :return: list of task object that satisfy these filter criteria + :rtype: List[Task] + """ + if provider is None: + provider = default_provider + provider = Provider.from_name(provider) + if token is None: + token = provider.get_token() # type: ignore + if device is not None: + device = Device.from_name(device) + if provider.name == "tencent": # type: ignore + return tencent.list_tasks(device, token, **filter_kws) # type: ignore + elif provider.name == "local": # type: ignore + return local.list_tasks(device, token, **filter_kws) # type: ignore + else: + raise ValueError("Unsupported provider: %s" % provider.name) # type: ignore diff --git a/tensorcircuit/cloud/config.py b/tensorcircuit/cloud/config.py new file mode 100644 index 00000000..5ad31e81 --- /dev/null +++ b/tensorcircuit/cloud/config.py @@ -0,0 +1 @@ +tencent_base_url = "https://quantum.tencent.com/cloud/quk/" diff --git a/tensorcircuit/cloud/local.py b/tensorcircuit/cloud/local.py new file mode 100644 index 00000000..6d48f8e6 --- /dev/null +++ b/tensorcircuit/cloud/local.py @@ -0,0 +1,75 @@ +""" +Cloud provider from local machine +""" + +from typing import Any, Dict, List, Optional, Union, Sequence +from uuid import uuid4 +import time + +from .abstraction import Device, sep, Task +from ..utils import is_sequence +from ..abstractcircuit import AbstractCircuit + +local_devices = ["testing"] + +task_list: Dict[str, Any] = {} # memory only task cache + + +def list_devices(token: Optional[str] = None, **kws: Any) -> List[Device]: + rs = [] + for d in local_devices: + rs.append(Device.from_name("local" + sep + d)) + return rs + + +def get_task_details( + task: Task, device: Device, token: str, prettify: bool +) -> Dict[str, Any]: + if task.id_ in task_list: + return task_list[task.id_] # type: ignore + raise ValueError("no task with id: %s" % task.id_) + + +def submit_task( + device: Device, + token: str, + shots: Union[int, Sequence[int]] = 1024, + version: str = "1", + circuit: Optional[Union[AbstractCircuit, Sequence[AbstractCircuit]]] = None, + **kws: Any +) -> List[Task]: + def _circuit2result(c: AbstractCircuit) -> Dict[str, Any]: + if device.name in ["testing", "default"]: + count = c.sample(batch=shots, allow_state=True, format="count_dict_bin") # type: ignore + else: + raise ValueError("Unsupported device from local provider: %s" % device.name) + d = { + "id": str(uuid4()), + "state": "completed", + "at": time.time() * 1e6, + "shots": shots, + "device": device.name, + "results": count, + } + return d + + if is_sequence(circuit): + tl = [] + for c in circuit: # type: ignore + d = _circuit2result(c) + task_list[d["id"]] = d + tl.append(Task(id_=d["id"], device=device)) + return tl + else: + d = _circuit2result(circuit) # type: ignore + task_list[d["id"]] = d + + return Task(id_=d["id"], device=device) # type: ignore + + +def list_tasks(device: Device, token: str, **filter_kws: Any) -> List[Task]: + r = [] + for t, v in task_list.items(): + if (device is not None and v["device"] == device.name) or device is None: + r.append(Task(id_=t, device=Device.from_name("local" + sep + v["device"]))) + return r diff --git a/tensorcircuit/cloud/quafu_provider.py b/tensorcircuit/cloud/quafu_provider.py new file mode 100644 index 00000000..f1d1f9bf --- /dev/null +++ b/tensorcircuit/cloud/quafu_provider.py @@ -0,0 +1,95 @@ +""" +Cloud provider from QuaFu: http://quafu.baqis.ac.cn/ +""" + +from typing import Any, Dict, List, Optional, Sequence, Union +import logging + +from quafu import User, QuantumCircuit +from quafu import Task as Task_ + +from .abstraction import Device, sep, Task +from ..abstractcircuit import AbstractCircuit +from ..utils import is_sequence + +logger = logging.getLogger(__name__) + + +def list_devices(token: Optional[str] = None, **kws: Any) -> List[Device]: + raise NotImplementedError + + +def list_properties(device: Device, token: Optional[str] = None) -> Dict[str, Any]: + raise NotImplementedError + + +def submit_task( + device: Device, + token: str, + shots: Union[int, Sequence[int]] = 1024, + circuit: Optional[Union[AbstractCircuit, Sequence[AbstractCircuit]]] = None, + source: Optional[Union[str, Sequence[str]]] = None, + compile: bool = True, + **kws: Any +) -> Task: + if source is None: + + def c2qasm(c: Any) -> str: + from qiskit.circuit import QuantumCircuit + + if isinstance(c, QuantumCircuit): + s = c.qasm() + # nq = c.num_qubits + else: + s = c.to_openqasm() + return s # type: ignore + + if not is_sequence(circuit): + source = c2qasm(circuit) + else: + source = [c2qasm(c) for c in circuit] # type: ignore + user = User() + user.save_apitoken(token) + + def c2task(source: str) -> Task: + nq = int(source.split("\n")[2].split("[")[1].split("]")[0]) # type: ignore + qc = QuantumCircuit(nq) + qc.from_openqasm(source) + task = Task_() + device_name = device.name.split(sep)[-1] + task.config(backend=device_name, shots=shots, compile=compile) + res = task.send(qc, wait=False) + wrapper = Task(res.taskid, device=device) + return wrapper + + if not is_sequence(source): + return c2task(source) # type: ignore + else: + return [c2task(s) for s in source] # type: ignore + + +def resubmit_task(task: Task, token: str) -> Task: + raise NotImplementedError + + +def remove_task(task: Task, token: str) -> Any: + raise NotImplementedError + + +def list_tasks(device: Device, token: str, **filter_kws: Any) -> List[Task]: + raise NotImplementedError + + +def get_task_details( + task: Task, device: Device, token: str, prettify: bool +) -> Dict[str, Any]: + # id results + r = {} + r["id"] = task.id_ + t = Task_() + r["results"] = dict(t.retrieve(task.id_).counts) # type: ignore + if r["results"]: + r["state"] = "completed" + else: + r["state"] = "pending" + return r diff --git a/tensorcircuit/cloud/tencent.py b/tensorcircuit/cloud/tencent.py new file mode 100644 index 00000000..fc37460a --- /dev/null +++ b/tensorcircuit/cloud/tencent.py @@ -0,0 +1,414 @@ +""" +Cloud provider from Tencent +""" + +from typing import Any, Dict, List, Optional, Sequence, Union +from datetime import datetime +from json import dumps +import logging +from functools import partial + +from .config import tencent_base_url +from .utils import rpost_json +from .abstraction import Device, sep, Task +from ..abstractcircuit import AbstractCircuit +from ..utils import is_sequence, arg_alias +from ..circuit import Circuit +from ..translation import eqasm2tc + +logger = logging.getLogger(__name__) + + +def tencent_headers(token: Optional[str] = None) -> Dict[str, str]: + if token is None: + token = "ANY;0" + headers = {"Authorization": "Bearer " + token} + return headers + + +def error_handling(r: Dict[str, Any]) -> Dict[str, Any]: + if not isinstance(r, dict): + raise ValueError("failed to get legal response from the server") + if "err" in r: + raise ValueError(r["err"]) + return r + + +def list_devices(token: Optional[str] = None, **kws: Any) -> List[Device]: + json: Dict[Any, Any] = kws + r = rpost_json( + tencent_base_url + "device/find", json=json, headers=tencent_headers(token) + ) + r = error_handling(r) + ds = r["devices"] + rs = [] + for d in ds: + rs.append(Device.from_name("tencent" + sep + d["id"])) + return rs + + +def list_properties(device: Device, token: Optional[str] = None) -> Dict[str, Any]: + json = {"id": device.name} + r = rpost_json( + tencent_base_url + "device/detail", json=json, headers=tencent_headers(token) + ) + r = error_handling(r) + if "device" in r: + r = r["device"] + if "links" in r: + links_dict = {} + for link in r["links"]: + links_dict[(link["A"], link["B"])] = link + r["links"] = links_dict + if "bits" in r: + bits_dict = {} + for bit in r["bits"]: + bits_dict[bit["Qubit"]] = bit + r["bits"] = bits_dict + r["native_gates"] = ["h", "rz", "x", "y", "z", "cz", "cx"] # handcoded + return r # type: ignore + else: + raise ValueError("No device with the name: %s" % device) + + # list properties should at least contain the following items + """ + {'id': '20xmon', + 'type': 'CHIP', + 'state': 'on', + 'native_gates': ["rz"] + 'links': {(0, 1): { 'CZErrRate': 0.03, 'at': 1673605888}, + ...}, + 'bits': {0: { 'At': 1673605888, + 'Freqency': 4420, + 'ReadoutF0Err': 0.0415, + 'ReadoutF1Err': 0.1006, + 'SingleQubitErrRate': 0.00095, + 'SingleQubitGateLenInNs': 30, + 'T1': 35.5, + 'T2': 5.4}, + ...,}, + 'langs': ['tQASM', 'eQASM']} + """ + + +def _replace_rz_to_st(qasm: str) -> str: + nqasm = [] + for line in qasm.split("\n"): + if line.startswith("rz(pi/2)") or line.startswith("rz(5*pi/2)"): + line = " ".join(["s"] + line.split(" ")[1:]) + elif line.startswith("rz(-pi/2)") or line.startswith("rz(3*pi/2)"): + line = " ".join(["sdg"] + line.split(" ")[1:]) + elif line.startswith("rz(pi/4)"): + line = " ".join(["t"] + line.split(" ")[1:]) + elif line.startswith("rz(-pi/4)"): + line = " ".join(["tdg"] + line.split(" ")[1:]) + + nqasm.append(line) + return "\n".join(nqasm) + + +@partial( + arg_alias, + alias_dict={ + "compiling": ["compiled"], + "compiled_options": ["qiskit_compiled_options"], + }, +) +def submit_task( + device: Device, + token: str, + lang: str = "OPENQASM", + shots: Union[int, Sequence[int]] = 1024, + version: str = "1", + prior: int = 1, + circuit: Optional[Union[AbstractCircuit, Sequence[AbstractCircuit]]] = None, + source: Optional[Union[str, Sequence[str]]] = None, + remarks: Optional[str] = None, + group: Optional[str] = None, + compiling: bool = False, + compiled_options: Optional[Dict[str, Any]] = None, + enable_qiskit_initial_mapping: bool = False, + # measure: Optional[Sequence[int]] = None, + enable_qos_qubit_mapping: bool = True, + enable_qos_gate_decomposition: bool = True, + enable_qos_initial_mapping: bool = False, + qos_dry_run: bool = False, + **kws: Any +) -> List[Task]: + """ + Submit task via tencent provider, we suggest to enable one of the compiling functionality: + either in tc: frontend or in qos: backend. If both are enabled, try on your own risk, some + qubit mapping may fail silently. If the user directly provide ``source`` or qiskit Circuit in ``circuit``, + the qubit mapping should be taken care of by the users. + + :param device: [description] + :type device: Device + :param token: [description] + :type token: str + :param lang: language choice for ``source``, defaults to "OPENQASM" + :type lang: str, optional + :param shots: number of measurement shots, defaults to 1024 + :type shots: Union[int, Sequence[int]], optional + :param version: submit task protocol version, defaults to "1" + :type version: str, optional + :param prior: priority for the task queue, defaults to 1 + :type prior: int, optional + :param circuit: tensorcircuit or qiskit circuit object, defaults to None + :type circuit: Optional[Union[AbstractCircuit, Sequence[AbstractCircuit]]], optional + :param source: directly given circuit representation, defaults to None + :type source: Optional[Union[str, Sequence[str]]], optional + :param remarks: remarks on the task, defaults to None + :type remarks: Optional[str], optional + :param compiling: whether compiling in tc via qiskit compiling system, + defaults to False + :type compiling: bool, optional + :param compiled_options: compiling options for qiskit ``transpile`` method, + defaults to None + :type compiled_options: Optional[Dict[str, Any]], optional + :param enable_qos_qubit_mapping: whether to insert swap if necessary in qos, defaults to True + :type enable_qos_qubit_mapping: bool, optional + :param enable_qos_gate_decomposition: whether to compile the gate in qos, defaults to True + :type enable_qos_gate_decomposition: bool, optional + :param enable_qos_initial_mapping: whether to run an initial qubit mapping in qos, + defaults to False + :type enable_qos_initial_mapping: bool, optional + :param qos_dry_run: when dry run, only compiled circuit is returned (no real circuit execution), + defaults to False + :type qos_dry_run: bool, optional + :return: Task object or List of Task for batch submission + :rtype: List[Task] + """ + # :param measure: [deprecated] which group of qubit to measure, + # defaults to None, the measure result is in the order of qubit index + # instead of the ``measure`` list + # :type measure: Optional[Sequence[int]], optional + if source is None: + if compiled_options is None and compiling is True: + links = device.topology() + if ( + enable_qiskit_initial_mapping is True + and isinstance(links, list) + and len(links) > 1 + and isinstance(links[0], list) + ): + coupling_map = links + else: + coupling_map = None + compiled_options = { + "basis_gates": ["h", "x", "y", "z", "s", "t", "rz", "cx", "cz"], + "optimization_level": 2, + "coupling_map": coupling_map, + } + + def c2qasm(c: Any, compiling: bool) -> str: + from ..compiler.qiskit_compiler import qiskit_compile + from qiskit.circuit import QuantumCircuit + + if compiling is True: + s, _ = qiskit_compile( + c, output="qasm", info=None, compiled_options=compiled_options + ) + else: + if isinstance(c, QuantumCircuit): + s = c.qasm() + # nq = c.num_qubits + else: + s = c.to_openqasm() + # nq = c._nqubits + # s = _free_pi(s) # tQuk translation now supports this + # if measure is not None: # ad hoc partial measurement + # slist = s.split("\n")[:-1] + # if len(slist) > 3 and not slist[3].startswith("creg"): + # slist.insert(3, "creg c[%s];" % nq) + # for m in measure: + # slist.append("measure q[%s]->c[%s];" % (m, m)) + # slist.append("") + # s = "\n".join(slist) + s = _replace_rz_to_st(s) + return s # type: ignore + + if is_sequence(circuit): + source = [c2qasm(c, compiling) for c in circuit] # type: ignore + else: + source = c2qasm(circuit, compiling) + lang = "OPENQASM" + + if len(device.name.split("?")) > 1: + device_str = device.name + else: + oint = 0 + if enable_qos_qubit_mapping: + oint += 1 + if enable_qos_gate_decomposition: + oint += 2 + if enable_qos_initial_mapping: + oint += 4 + device_str = device.name + "?o=" + str(oint) + if qos_dry_run: + device_str += "&dry" + + if is_sequence(source): + # batched mode + json = [] + if not is_sequence(shots): + shots = [shots for _ in source] # type: ignore + for sc, sh in zip(source, shots): # type: ignore + json.append( + { + "device": device_str, + "shots": sh, + "source": sc, + "version": version, + "lang": lang, + "prior": prior, + "remarks": remarks, + "group": group, + } + ) + + else: + json = { # type: ignore + "device": device_str, + "shots": shots, + "source": source, + "version": version, + "lang": lang, + "prior": prior, + "remarks": remarks, + "group": group, + } + r = rpost_json( + tencent_base_url + "task/submit", json=json, headers=tencent_headers(token) + ) + r = error_handling(r) + rtn = [] + for t in r["tasks"]: + if "err" in t or "id" not in t: + if "err" in t: + logger.warning(t["err"]) + else: + logger.warning("unsuccessful submission of the task:\n" + dumps(r)) + else: + ti = Task(id_=t["id"], device=device) + rtn.append(ti) + if not is_sequence(source): + return rtn[0] # type: ignore + elif len(rtn) == 0: + raise ValueError("All tasks submitted failed") + else: + return rtn + + +def resubmit_task(task: Task, token: str) -> Task: + # TODO(@refraction-ray): batch resubmit + json = {"id": task.id_} + r = rpost_json( + tencent_base_url + "task/start", json=json, headers=tencent_headers(token) + ) + r = error_handling(r) + try: + return Task(id_=r["tasks"][0]["id"]) + + except KeyError: + raise ValueError(dumps(r)) + + +def remove_task(task: Task, token: str) -> Any: + # TODO(@refraction-ray): batch cancel + json = {"id": task.id_} + r = rpost_json( + tencent_base_url + "task/remove", json=json, headers=tencent_headers(token) + ) + r = error_handling(r) + return r + + +def list_tasks(device: Device, token: str, **filter_kws: Any) -> List[Task]: + json = filter_kws + if device is not None: + json["device"] = device.name + r = rpost_json( + tencent_base_url + "task/find?pn=1&npp=50", + json=json, + headers=tencent_headers(token), + ) + r = error_handling(r) + try: + rtn = [] + for t in r["tasks"]: + rtn.append( + Task( + id_=t["id"], + device=Device.from_name("tencent" + sep + t["device"]), + ) + ) + return rtn + except KeyError: + raise ValueError(dumps(r)) + + +def get_task_details( + task: Task, device: Device, token: str, prettify: bool +) -> Dict[str, Any]: + json = {"id": task.id_} + r = rpost_json( + tencent_base_url + "task/detail", json=json, headers=tencent_headers(token) + ) + r = error_handling(r) + try: + if "result" in r["task"]: + if "counts" in r["task"]["result"]: + r["task"]["results"] = r["task"]["result"]["counts"] + else: + r["task"]["results"] = r["task"]["result"] + if "optimization" in r["task"]: + if ( + "pairs" in r["task"]["optimization"] + and r["task"]["optimization"]["pairs"] is not None + ): + r["task"]["optimization"]["pairs"] = { + int(k): int(v) + for k, v in r["task"]["optimization"]["pairs"].items() + } + # r["logical_physical_mapping"] = r["task"]["optimization"]["pairs"] + + # if not "logical_physical_mapping" in r["task"]: + # r["task"]["logical_physical_mapping"] = None + + if prettify is False: + return r["task"] # type: ignore + # make the results more readable + r = r["task"] + if "at" in r: + r["at"] = datetime.fromtimestamp(r["at"] / 1e6) + if "ts" in r: + for k in r["ts"]: + r["ts"][k] = datetime.fromtimestamp(r["ts"][k] / 1e6) + if "source" in r: + r["frontend"] = Circuit.from_openqasm(r["source"]) + if "optimization" in r and r["state"] == "completed": + try: + r["backend"] = eqasm2tc(r["optimization"]["progs"][0]["code"]) + except KeyError: + pass + return r # type: ignore + except KeyError: + raise ValueError(dumps(r)) + + # make the return at least contain the following terms across different providers + """ + 'id': 'd947cd76-a961-4c22-b295-76287c9fdaa3', + 'state': 'completed', + 'at': 1666752095915849, + 'shots': 1024, + 'source': 'OPENQASM 2.0;\ninclude "qelib1.inc";\nqreg q[3];\nh q[0];\nh q[1];\nh q[2];\n', + 'device': 'simulator:aer', + 'results': {'000': 123, + '001': 126, + '010': 131, + '011': 128, + '100': 122, + '101': 135, + '110': 128, + '111': 131} + """ diff --git a/tensorcircuit/cloud/utils.py b/tensorcircuit/cloud/utils.py new file mode 100644 index 00000000..fe15a56f --- /dev/null +++ b/tensorcircuit/cloud/utils.py @@ -0,0 +1,121 @@ +""" +utility functions for cloud connection +""" + +from typing import Any, Callable, Optional +from functools import wraps +import inspect +import logging +import os +import sys +import time + +import requests + +# from simplejson.errors import JSONDecodeError + +logger = logging.getLogger(__name__) +thismodule = sys.modules[__name__] + + +class HttpStatusError(Exception): + """ + Used when the return request has http code beyond 200 + """ + + pass + + +# TODO(@refraction-ray): whether an exception hierarchy for tc is necessary? +connection_errors = ( + ConnectionResetError, + HttpStatusError, + requests.exceptions.RequestException, + requests.exceptions.ConnectionError, + requests.exceptions.SSLError, + ValueError, + # JSONDecodeError, +) + + +def set_proxy(proxy: Optional[str] = None) -> None: + """ + :param proxy: str. format as "http://user:passwd@host:port" user passwd part can be omitted if not set. + None for turning off the proxy. + :return: + """ + if proxy: + os.environ["http_proxy"] = proxy + os.environ["https_proxy"] = proxy + setattr(thismodule, "proxy", proxy) + else: + os.environ["http_proxy"] = "" + os.environ["https_proxy"] = "" + setattr(thismodule, "proxy", None) + + +def reconnect(tries: int = 5, timeout: int = 12) -> Callable[..., Any]: + # wrapper originally designed in xalpha by @refraction-ray + # https://github.com/refraction-ray/xalpha + def robustify(f: Callable[..., Any]) -> Callable[..., Any]: + @wraps(f) + def wrapper(*args: Any, **kws: Any) -> Any: + if getattr(thismodule, "proxy", None): + kws["proxies"] = { + "http": getattr(thismodule, "proxy"), + "https": getattr(thismodule, "proxy"), + } + logger.debug("Using proxy %s" % getattr(thismodule, "proxy")) + kws["timeout"] = timeout + if args: + url = args[0] + else: + url = kws.get("url", "") + headers = kws.get("headers", {}) + if (not headers.get("user-agent", None)) and ( + not headers.get("User-Agent", None) + ): + headers["user-agent"] = "Mozilla/5.0" + kws["headers"] = headers + for count in range(tries): + try: + logger.debug( + "Fetching url: %s . Inside function `%s`" + % (url, inspect.stack()[1].function) + ) + r = f(*args, **kws) + if ( + getattr(r, "status_code", 200) != 200 + ): # in case r is a json dict + raise HttpStatusError + return r + except connection_errors as e: + logger.warning("Fails at fetching url: %s. Try again." % url) + if count == tries - 1: + logger.error( + "Still wrong at fetching url: %s. after %s tries." + % (url, tries) + ) + logger.error("Fails due to %s" % e.args[0]) + raise e + time.sleep(0.5 * count) + + return wrapper + + return robustify + + +rget = reconnect()(requests.get) +rpost = reconnect()(requests.post) + + +@reconnect() +def rget_json(*args: Any, **kws: Any) -> Any: + r = requests.get(*args, **kws) + return r.json() + + +@reconnect() +def rpost_json(*args: Any, **kws: Any) -> Any: + r = requests.post(*args, **kws) + return r.json() diff --git a/tensorcircuit/cloud/wrapper.py b/tensorcircuit/cloud/wrapper.py new file mode 100644 index 00000000..f43f9ea9 --- /dev/null +++ b/tensorcircuit/cloud/wrapper.py @@ -0,0 +1,321 @@ +""" +higher level wrapper shortcut for submit_task +""" + +from typing import Any, Callable, Dict, List, Optional, Sequence, Tuple, Union +import logging +import time + +import numpy as np + +from ..circuit import Circuit +from ..results import counts +from ..results.readout_mitigation import ReadoutMit +from ..utils import is_sequence +from ..cons import backend +from ..quantum import ps2xyz +from ..compiler import DefaultCompiler +from ..compiler.simple_compiler import simple_compile +from .apis import submit_task, get_device +from .abstraction import Device + + +logger = logging.getLogger(__name__) +Tensor = Any + + +def batch_submit_template( + device: str, batch_limit: int = 64, **kws: Any +) -> Callable[..., List[counts.ct]]: + def run( + cs: Union[Circuit, Sequence[Circuit]], shots: int = 8192, **nkws: Any + ) -> List[counts.ct]: + """ + batch circuit running alternative + """ + single = False + if not is_sequence(cs): + cs = [cs] # type: ignore + single = True + # for c in cs: # type: ignore + # ts.append(submit_task(circuit=c, shots=shots, device=device)) + # time.sleep(0.3) + kws.update(nkws) + if len(cs) <= batch_limit: # type: ignore + css = [cs] + else: + ntimes = len(cs) // batch_limit # type: ignore + if ntimes * batch_limit == len(cs): # type: ignore + css = [ + cs[i * batch_limit : (i + 1) * batch_limit] for i in range(ntimes) # type: ignore + ] + else: + css = [ + cs[i * batch_limit : (i + 1) * batch_limit] for i in range(ntimes) # type: ignore + ] + [ + cs[ntimes * batch_limit :] # type: ignore + ] + tss = [] + logger.info(f"submit task on {device} for {len(cs)} circuits") # type: ignore + time0 = time.time() + for i, cssi in enumerate(css): + tss += submit_task(circuit=cssi, shots=shots, device=device, **kws) + if i < len(css) - 1: + time.sleep(1.5) + # TODO(@refraction-ray) whether the sleep time is enough for tquk? + # incase duplicae request error protection + l = [t.results() for t in tss] + time1 = time.time() + logger.info( + f"finished collecting count results of {len(cs)} tasks in {round(time1-time0, 4)} seconds" # type: ignore + ) + if single is False: + return l + return l[0] # type: ignore + + return run + + +def sample_expectation_ps( + c: Circuit, + x: Optional[Sequence[int]] = None, + y: Optional[Sequence[int]] = None, + z: Optional[Sequence[int]] = None, + shots: int = 1024, + device: Optional[Device] = None, + **kws: Any, +) -> float: + """ + Deprecated, please use :py:meth:`tensorcircuit.cloud.wrapper.batch_expectation_ps`. + """ + logger.warning( + "This method is deprecated and not maintained, \ + please use `tensorcircuit.cloud.wrapper.batch_expectation_ps` instead" + ) + c1 = Circuit.from_qir(c.to_qir()) + if x is None: + x = [] + if y is None: + y = [] + if z is None: + z = [] + for i in x: + c1.H(i) # type: ignore + for i in y: + c1.rx(i, theta=np.pi / 2) # type: ignore + if device is None: + device = get_device() + t = submit_task(circuit=c1, device=device, shots=shots) + raw_counts = t.results(blocked=True) # type: ignore + x, y, z = list(x), list(y), list(z) + return counts.expectation(raw_counts, x + y + z) + + +def reduce_and_evaluate( + cs: List[Circuit], shots: int, run: Callable[..., Any] +) -> List[counts.ct]: + reduced_cs = [] + reduced_dict = {} + recover_dict = {} + # merge the same circuit + for j, c in enumerate(cs): + key = hash(c.to_openqasm()) + if key not in reduced_dict: + reduced_dict[key] = [j] + reduced_cs.append(c) + recover_dict[key] = len(reduced_cs) - 1 + else: + reduced_dict[key].append(j) + + # for j, ps in enumerate(pss): + # ps = [i if i in [1, 2] else 0 for i in ps] + # if tuple(ps) not in reduced_dict: + # reduced_dict[tuple(ps)] = [j] + # reduced_cs.append(cs[j]) + # recover_dict[tuple(ps)] = len(reduced_cs) - 1 + # else: + # reduced_dict[tuple(ps)].append(j) + + reduced_raw_counts = run(reduced_cs, shots) + raw_counts: List[Dict[str, int]] = [None] * len(cs) # type: ignore + for i, c in enumerate(cs): + key = hash(c.to_openqasm()) + raw_counts[i] = reduced_raw_counts[recover_dict[key]] + return raw_counts + + +def batch_expectation_ps( + c: Circuit, + pss: List[List[int]], + device: Any = None, + ws: Optional[List[float]] = None, + shots: int = 8192, + with_rem: bool = True, + compile_func: Optional[Callable[[Circuit], Tuple[Circuit, Dict[str, Any]]]] = None, + batch_limit: int = 64, + batch_submit_func: Optional[Callable[..., List[counts.ct]]] = None, +) -> Union[Any, List[Any]]: + """ + Unified interface to compute the Pauli string expectation lists or sums via simulation or on real qpu. + Error mitigation, circuit compilation and Pauli string grouping are all built-in. + + One line access to unlock the whole power or real quantum hardware on quantum cloud. + + :Example: + + .. code-block:: python + + c = tc.Circuit(2) + c.h(0) + c.x(1) + tc.cloud.wrapper.batch_expectation_ps(c, [[1, 0], [0, 3]], device=None) + # array([ 0.99999994, -0.99999994], dtype=float32) + tc.cloud.wrapper.batch_expectation_ps(c, [[1, 0], [0, 3]], device="tencent::9gmon") + # array([ 1.03093477, -1.11715944]) + + :param c: The target circuit to compute expectation + :type c: Circuit + :param pss: List of Pauli string list, eg. [[0, 1, 0], [2, 3, 3]] represents [X1, Y0Z1Z2]. + :type pss: List[List[int]] + :param device: The device str or object for quantum cloud module, + defaults to None, None is for analytical exact simulation + :type device: Any, optional + :param ws: List of float to indicate the final return is the weighted sum of Pauli string expectations, + e.g. [2., -0.3] represents the final results is 2* ``pss`` [0]-0.3* ``pss`` [1] + defaults to None, None indicate the list of expectations for ``pss`` are all returned + :type ws: Optional[List[float]], optional + :param shots: measurement shots for each expectation estimation, defaults to 8192 + :type shots: int, optional + :param with_rem: whether enable readout error mitigation for the result, defaults to True + :type with_rem: bool, optional + :return: List of Pauli string expectation or a weighted sum float for Pauli strings, depending on ``ws`` + :rtype: Union[Any, List[Any]] + """ + if device is None: + results = [] + for ps in pss: + results.append(c.expectation_ps(**ps2xyz(ps))) # type: ignore + if ws is None: + return backend.real(backend.stack(results)) + else: + sumr = sum([w * r for w, r in zip(ws, results)]) + return backend.convert_to_tensor(sumr) + cs = [] + infos = [] + exps = [] + if isinstance(device, str): + device = get_device(device) + + if compile_func is None: + try: + coupling_map = device.topology() + compile_func = DefaultCompiler( + { + "coupling_map": coupling_map, + } + ) + except (AttributeError, ValueError): + compile_func = DefaultCompiler() + c1, info = compile_func(c) # type: ignore + if not info.get("logical_physical_mapping", None): + info["logical_physical_mapping"] = {i: i for i in range(c._nqubits)} + for ps in pss: + # TODO(@refraction-ray): Pauli string grouping + # https://docs.pennylane.ai/en/stable/_modules/pennylane/pauli/grouping/group_observables.html + c2 = Circuit.from_qir(c1.to_qir()) + exp = [] + for j, i in enumerate(ps): + if i == 1: + c2.H(info["logical_physical_mapping"][j]) # type: ignore + c2, _ = simple_compile(c2) + exp.append(j) + + elif i == 2: + c2.rx(info["logical_physical_mapping"][j], theta=np.pi / 2) # type: ignore + c2, _ = simple_compile(c2) + exp.append(j) + elif i == 3: + exp.append(j) + for i in range(c._nqubits): + c2.measure_instruction(info["logical_physical_mapping"][i]) + # c1, info = compile_func(c1) # type: ignore + # TODO(@refraction-ray): two steps compiling with pre compilation: + # basically done, require some fine tuning for performance + cs.append(c2) + infos.append(info) + exps.append(exp) + + # reduced_cs = [] + # reduced_dict = {} + # recover_dict = {} + # # merge the same circuit + # for j, ps in enumerate(pss): + # ps = [i if i in [1, 2] else 0 for i in ps] + # if tuple(ps) not in reduced_dict: + # reduced_dict[tuple(ps)] = [j] + # reduced_cs.append(cs[j]) + # recover_dict[tuple(ps)] = len(reduced_cs) - 1 + # else: + # reduced_dict[tuple(ps)].append(j) + + if batch_submit_func is None: + run = batch_submit_template( + device, + batch_limit, + enable_qos_qubit_mapping=False, + enable_qos_gate_decomposition=False, + ) + else: + run = batch_submit_func + + raw_counts = reduce_and_evaluate(cs, shots, run) # type: ignore + + # def run(cs: List[Any], shots: int) -> List[Dict[str, int]]: + # logger.info(f"submit task on {device.name} for {len(cs)} circuits") + # time0 = time.time() + # ts = submit_task( + # circuit=cs, + # device=device, + # shots=shots, + # enable_qos_qubit_mapping=False, + # enable_qos_gate_decomposition=False, + # ) + # if not is_sequence(ts): + # ts = [ts] # type: ignore + # raw_counts = [t.results(blocked=True) for t in ts] + # time1 = time.time() + # logger.info( + # f"finished collecting count results of {len(cs)} tasks in {round(time1-time0, 4)} seconds" + # ) + # return raw_counts + + # reduced_raw_counts = run(reduced_cs, shots) + # raw_counts: List[Dict[str, int]] = [None] * len(cs) # type: ignore + # for i in range(len(cs)): + # ps = [i if i in [1, 2] else 0 for i in pss[i]] + # raw_counts[i] = reduced_raw_counts[recover_dict[tuple(ps)]] + + if with_rem: + if getattr(device, "readout_mit", None) is None: + mit = ReadoutMit(run) + # TODO(@refraction-ray) only work for tencent provider + nq = device.list_properties().get("qubits", None) + if nq is None: + nq = c._nqubits + mit.cals_from_system(nq, shots=shots) + device.readout_mit = mit + else: + mit = device.readout_mit + + results = [ + mit.expectation(raw_counts[i], exps[i], **infos[i]) + for i in range(len(raw_counts)) + ] + else: + results = [ + counts.expectation(raw_counts[i], exps[i]) for i in range(len(raw_counts)) + ] + if ws is not None: + sumr = sum([w * r for w, r in zip(ws, results)]) + return backend.convert_to_tensor(sumr) + return backend.stack(results) diff --git a/tensorcircuit/compiler/__init__.py b/tensorcircuit/compiler/__init__.py new file mode 100644 index 00000000..d5f812fe --- /dev/null +++ b/tensorcircuit/compiler/__init__.py @@ -0,0 +1,8 @@ +""" +Experimental module, no software agnostic unified interface for now, +only reserve for internal use +""" + +from .composed_compiler import Compiler, DefaultCompiler, default_compile +from . import simple_compiler +from . import qiskit_compiler diff --git a/tensorcircuit/compiler/composed_compiler.py b/tensorcircuit/compiler/composed_compiler.py new file mode 100644 index 00000000..d49c4fe4 --- /dev/null +++ b/tensorcircuit/compiler/composed_compiler.py @@ -0,0 +1,84 @@ +""" +object oriented compiler pipeline +""" + +from typing import Any, Callable, Dict, List, Optional, Tuple, Union + +from ..utils import is_sequence +from ..abstractcircuit import AbstractCircuit +from .qiskit_compiler import qiskit_compile +from .simple_compiler import simple_compile + + +class Compiler: + def __init__( + self, + compile_funcs: Union[Callable[..., Any], List[Callable[..., Any]]], + compiled_options: Optional[List[Dict[str, Any]]] = None, + ): + if not is_sequence(compile_funcs): + self.compile_funcs = [compile_funcs] + else: + self.compile_funcs = list(compile_funcs) # type: ignore + self.add_options(compiled_options) + + def add_options( + self, compiled_options: Optional[List[Dict[str, Any]]] = None + ) -> None: + if compiled_options is None: + self.compiled_options = [{} for _ in range(len(self.compile_funcs))] # type: ignore + elif not is_sequence(compiled_options): + self.compiled_options = [compiled_options for _ in self.compile_funcs] # type: ignore + else: + assert len(compiled_options) == len( # type: ignore + self.compile_funcs + ), "`compiled_options` must have the same list length as `compile_funcs`" + self.compiled_options = list(compiled_options) # type: ignore + for i, c in enumerate(self.compiled_options): + if c is None: + self.compiled_options[i] = {} + + def __call__( + self, circuit: AbstractCircuit, info: Optional[Dict[str, Any]] = None + ) -> Any: + for f, d in zip(self.compile_funcs, self.compiled_options): + result = f(circuit, info, compiled_options=d) # type: ignore + if not isinstance(result, tuple): + result = (result, info) + circuit, info = result + return circuit, info + + +class DefaultCompiler(Compiler): + def __init__(self, qiskit_compiled_options: Optional[Dict[str, Any]] = None): + """ + A fallback choice to compile circuit running on tencent quantum cloud with rz as native gate + + :param qiskit_compiled_options: qiskit compiled options to be added + options documented in `qiskit.transpile` method, + to use tencent quantum cloud, `{"coupling_map": d.topology()}` is in general enough, + where d is a device object, + defaults to None, i.e. no qubit mapping is applied + :type qiskit_compiled_options: Optional[Dict[str, Any]], optional + """ + compiled_options = { + "optimization_level": 3, + "basis_gates": ["u3", "h", "cx", "cz"], + } + # rz target is bad for qiskit + if qiskit_compiled_options: + compiled_options.update(qiskit_compiled_options) + super().__init__( + [qiskit_compile, simple_compile], + [compiled_options, None], # type: ignore + ) + + +def default_compile( + circuit: AbstractCircuit, + info: Optional[Dict[str, Any]] = None, + compiled_options: Optional[Dict[str, Any]] = None, +) -> Tuple[AbstractCircuit, Dict[str, Any]]: + dc = DefaultCompiler(compiled_options) + c, info = dc(circuit, info) + return c, info # type: ignore diff --git a/tensorcircuit/compiler/qiskit_compiler.py b/tensorcircuit/compiler/qiskit_compiler.py new file mode 100644 index 00000000..1f946359 --- /dev/null +++ b/tensorcircuit/compiler/qiskit_compiler.py @@ -0,0 +1,191 @@ +""" +compiler interface via qiskit +""" + +import re +from typing import Any, Dict, Optional + +from ..abstractcircuit import AbstractCircuit +from ..circuit import Circuit +from ..translation import qiskit_from_qasm_str_ordered_measure + + +def _free_pi(s: str) -> str: + # dirty trick to get rid of pi in openqasm from qiskit + rs = [] + pistr = "3.141592653589793" + s = s.replace("pi", pistr) + for r in s.split("\n"): + inc = re.search(r"\(.*\)", r) + if inc is None: + rs.append(r) + else: + v = r[inc.start() : inc.end()] + v = eval(v) + if not isinstance(v, tuple): + r = r[: inc.start()] + "(" + str(v) + ")" + r[inc.end() :] + else: # u gate case + r = r[: inc.start()] + str(v) + r[inc.end() :] + rs.append(r) + return "\n".join(rs) + + +def _comment_qasm(s: str) -> str: + """ + return the qasm str in comment format + + :param s: _description_ + :type s: str + :return: _description_ + :rtype: str + """ + nslist = [] + nslist.append("//circuit begins") + for line in s.split("\n"): + nslist.append("//" + line) + nslist.append("//circuit ends") + return "\n".join(nslist) + + +def _comment_dict(d: Dict[int, int], name: str = "logical_physical_mapping") -> str: + """ + save a dict in commented qasm + + :param d: _description_ + :type d: Dict[int, int] + :param name: _description_, defaults to "logical_physical_mapping" + :type name: str, optional + :return: _description_ + :rtype: str + """ + nslist = [] + nslist.append("//%s begins" % name) + for k, v in d.items(): + nslist.append("// " + str(k) + " : " + str(v)) + nslist.append("//%s ends" % name) + return "\n".join(nslist) + + +def _get_positional_logical_mapping_from_qiskit(qc: Any) -> Dict[int, int]: + i = 0 + positional_logical_mapping = {} + for inst in qc.data: + if inst[0].name == "measure": + positional_logical_mapping[i] = qc.find_bit(inst[1][0]).index + i += 1 + + return positional_logical_mapping + + +def _get_logical_physical_mapping_from_qiskit( + qc_after: Any, qc_before: Any = None +) -> Dict[int, int]: + """ + get ``logical_physical_mapping`` from qiskit Circuit by comparing the circuit after and before compiling + + :param qc_after: qiskit ``QuantumCircuit`` after compiling + :type qc_after: Any + :param qc_before: qiskit ``QuantumCircuit`` before compiling, + if None, measure(q, q) is assumed + :type qc_before: Any + :return: logical_physical_mapping + :rtype: Dict[int, int] + """ + logical_physical_mapping = {} + for inst in qc_after.data: + if inst[0].name == "measure": + if qc_before is None: + logical_q = qc_after.find_bit(inst[2][0]).index + else: + for instb in qc_before.data: + if ( + instb[0].name == "measure" + and qc_before.find_bit(instb[2][0]).index + == qc_after.find_bit(inst[2][0]).index + ): + logical_q = qc_before.find_bit(instb[1][0]).index + break + logical_physical_mapping[logical_q] = qc_after.find_bit(inst[1][0]).index + return logical_physical_mapping + + +def _add_measure_all_if_none(qc: Any) -> Any: + for inst in qc.data: + if inst[0].name == "measure": + break + else: + qc.measure_all() + return qc + + +def qiskit_compile( + circuit: Any, + info: Optional[Dict[str, Any]] = None, + output: str = "tc", + compiled_options: Optional[Dict[str, Any]] = None, +) -> Any: + """ + compile the circuit using ``qiskit.transpile`` method with some tricks and hacks + + :param circuit: circuit in ``tc.Circuit`` or ``qiskit.QuantumCircuit`` form + :type circuit: Any + :param info: info for qubit mappings, defaults to None + :type info: Optional[Dict[str, Any]], optional + :param output: output circuit format, defaults to "tc" + :type output: str, optional + :param compiled_options: ``qiskit.transpile`` options in a dict, defaults to None + :type compiled_options: Optional[Dict[str, Any]], optional + :return: Tuple containing the output circuit and the qubit mapping info dict + :rtype: Any + """ + from qiskit.compiler import transpile + from qiskit.transpiler.passes import RemoveBarriers + + if isinstance(circuit, AbstractCircuit): + circuit = circuit.to_qiskit(enable_instruction=True) + elif isinstance(circuit, str): + circuit = qiskit_from_qasm_str_ordered_measure(circuit) + # else qiskit circuit + circuit = _add_measure_all_if_none(circuit) + if compiled_options is None: + compiled_options = { + "basis_gates": ["h", "rz", "cx"], + "optimization_level": 2, # 3 can induce bugs... + } + ncircuit = transpile(circuit, **compiled_options) + ncircuit = RemoveBarriers()(ncircuit) + + if output.lower() in ["qasm", "openqasm"]: + r0 = ncircuit.qasm() + + elif output.lower() in ["qiskit", "ibm"]: + r0 = ncircuit + + elif output.lower() in ["tc", "tensorcircuit"]: + s = _free_pi(ncircuit.qasm()) + r0 = Circuit.from_openqasm( + s, + keep_measure_order=True, + ) + + else: + raise ValueError("Unknown output format: %s" % output) + + r1 = {} + nlpm = _get_logical_physical_mapping_from_qiskit(ncircuit, circuit) + # new_logical_physical_mapping + if info is not None and "logical_physical_mapping" in info: + r1["logical_physical_mapping"] = { + k: nlpm[v] for k, v in info["logical_physical_mapping"].items() + } + else: + r1["logical_physical_mapping"] = nlpm + if info is not None and "positional_logical_mapping" in info: + r1["positional_logical_mapping"] = info["positional_logical_mapping"] + else: # info is none, assume circuit is the logical circuit + r1["positional_logical_mapping"] = _get_positional_logical_mapping_from_qiskit( + circuit + ) + # TODO(@refraction-ray): more info to be added into r1 dict + + return (r0, r1) diff --git a/tensorcircuit/compiler/simple_compiler.py b/tensorcircuit/compiler/simple_compiler.py new file mode 100644 index 00000000..868a678e --- /dev/null +++ b/tensorcircuit/compiler/simple_compiler.py @@ -0,0 +1,311 @@ +""" +Very simple transformations that qiskit may even fail or hard to control +""" + +from typing import Any, Dict, List, Optional, Tuple, Union +from copy import copy + +import numpy as np + +from ..abstractcircuit import AbstractCircuit +from ..cons import backend +from ..quantum import QuOperator +from .. import gates +from ..utils import is_sequence + + +def replace_r(circuit: AbstractCircuit, **kws: Any) -> AbstractCircuit: + qir = circuit.to_qir() + c: Any = type(circuit)(**circuit.circuit_param) + for d in qir: + if "parameters" not in d: + c.apply_general_gate_delayed(d["gatef"], d["name"], mpo=d["mpo"])( + c, *d["index"], split=d["split"] + ) + else: + if d["gatef"].n == "rx": + c.h(*d["index"]) + c.rz(*d["index"], theta=d["parameters"].get("theta", 0.0)) + c.h(*d["index"]) + + elif d["gatef"].n == "ry": + c.sd(*d["index"]) + c.h(*d["index"]) + c.rz(*d["index"], theta=d["parameters"].get("theta", 0.0)) + c.h(*d["index"]) + c.s(*d["index"]) + + elif d["gatef"].n == "rzz": + c.cx(*d["index"]) + c.rz(d["index"][1], theta=d["parameters"].get("theta", 0.0)) + c.cx(*d["index"]) + + elif d["gatef"].n == "rxx": + c.h(d["index"][0]) + c.h(d["index"][1]) + c.cx(*d["index"]) + c.rz(d["index"][1], theta=d["parameters"].get("theta", 0.0)) + c.cx(*d["index"]) + c.h(d["index"][0]) + c.h(d["index"][1]) + + elif d["gatef"].n == "ryy": + c.sd(d["index"][0]) + c.sd(d["index"][1]) + c.h(d["index"][0]) + c.h(d["index"][1]) + c.cx(*d["index"]) + c.rz(d["index"][1], theta=d["parameters"].get("theta", 0.0)) + c.cx(*d["index"]) + c.h(d["index"][0]) + c.h(d["index"][1]) + c.s(d["index"][0]) + c.s(d["index"][1]) + + else: + c.apply_general_variable_gate_delayed( + d["gatef"], d["name"], mpo=d["mpo"] + )(c, *d["index"], **d["parameters"], split=d["split"]) + + return c # type: ignore + + +def replace_u(circuit: AbstractCircuit, **kws: Any) -> AbstractCircuit: + qir = circuit.to_qir() + c: Any = type(circuit)(**circuit.circuit_param) + for d in qir: + if "parameters" not in d: + c.apply_general_gate_delayed(d["gatef"], d["name"], mpo=d["mpo"])( + c, *d["index"], split=d["split"] + ) + else: + if d["gatef"].n == "u": + c.rz(*d["index"], theta=d["parameters"].get("lbd", 0) - np.pi / 2) + c.h(*d["index"]) + c.rz(*d["index"], theta=d["parameters"].get("theta", 0)) + c.h(*d["index"]) + c.rz(*d["index"], theta=np.pi / 2 + d["parameters"].get("phi", 0)) + else: + c.apply_general_variable_gate_delayed( + d["gatef"], d["name"], mpo=d["mpo"] + )(c, *d["index"], **d["parameters"], split=d["split"]) + + return c # type: ignore + + +def _get_matrix(qir_item: Dict[str, Any]) -> Any: + if "gate" in qir_item: + op = qir_item["gate"] + else: + op = qir_item["gatef"](**qir_item["parameters"]) + if isinstance(op, QuOperator): + m = backend.numpy(op.eval_matrix()) + else: + m = backend.numpy(backend.reshapem(op.tensor)) + return m + + +def prune( + circuit: Union[AbstractCircuit, List[Dict[str, Any]]], + rtol: float = 1e-3, + atol: float = 1e-3, + **kws: Any +) -> Any: + if isinstance(circuit, list): + qir = circuit + output = "qir" + else: + qir = circuit.to_qir() + output = "tc" + if output in ["tc", "circuit"]: + c: Any = type(circuit)(**circuit.circuit_param) # type: ignore + for d in qir: + m = _get_matrix(d) + + if not np.allclose( + m / (m[0, 0] + 1e-8), np.eye(m.shape[0]), rtol=rtol, atol=atol + ): + # upto a phase + if "parameters" not in d: + c.apply_general_gate_delayed(d["gatef"], d["name"], mpo=d["mpo"])( + c, *d["index"], split=d["split"] + ) + else: + c.apply_general_variable_gate_delayed( + d["gatef"], d["name"], mpo=d["mpo"] + )(c, *d["index"], **d["parameters"], split=d["split"]) + return c + + elif output in ["qir"]: + nqir = [] + for d in qir: + m = _get_matrix(d) + + if not np.allclose( + m / (m[0, 0] + 1e-8), np.eye(m.shape[0]), rtol=rtol, atol=atol + ): + nqir.append(d) + # upto a phase + + return nqir + + +# upto global phase +default_merge_rules = { + ("s", "s"): "z", + ("sd", "sd"): "z", + ("t", "t"): "s", + ("td", "td"): "sd", + ("x", "y"): "z", + ("y", "x"): "z", + ("x", "z"): "y", + ("z", "x"): "y", + ("z", "y"): "x", + ("y", "z"): "x", + ("x", "x"): "i", + ("y", "y"): "i", + ("z", "z"): "i", + ("h", "h"): "i", + ("rz", "rz"): "rz", + ("rx", "rx"): "rx", + ("ry", "ry"): "rx", + ("rzz", "rzz"): "rzz", + ("rxx", "rxx"): "rxx", + ("ryy", "ryy"): "ryy", + ("crz", "crz"): "crz", + ("crx", "crx"): "crx", + ("cry", "cry"): "crx", + ("cnot", "cnot"): "i", + ("cz", "cz"): "i", + ("cy", "cy"): "i", +} + + +def _find_next(qir: List[Dict[str, Any]], i: int) -> Optional[int]: + # if qir[i] is None: + # return None + index = qir[i]["index"] + for j, item in enumerate(qir[i + 1 :]): + # if item is not None: + if item["index"] == index: + return j + i + 1 + for ind in item["index"]: + if ind in index: + return None + return None + + +def _get_theta(qir_item: Dict[str, Any]) -> float: + theta = qir_item["parameters"].get("theta", 0.0) + if is_sequence(theta) and len(theta) == 1: + return theta[0] # type: ignore + return theta # type: ignore + + +def _merge( + qir: List[Dict[str, Any]], rules: Dict[Tuple[str, ...], str] +) -> Tuple[List[Dict[str, Any]], bool]: + i = 0 + flg = False + while i < len(qir) - 1: + j = _find_next(qir, i) + + if j is not None: + if (qir[i]["gatef"].n, qir[j]["gatef"].n) in rules: + nn = rules[(qir[i]["gatef"].n, qir[j]["gatef"].n)] + if nn == "i": + del qir[i] + del qir[j - 1] + # qir[i] = None + # qir[j] = None + else: + param = {} + if nn.startswith("r") or nn.startswith("cr"): + param = {"theta": _get_theta(qir[i]) + _get_theta(qir[j])} + qir[i] = { + "gatef": getattr(gates, nn), + "name": nn, + "mpo": False, + "split": False, + "parameters": param, + "index": qir[i]["index"], + } + del qir[j] + # qir[j] = None + flg = True + # return qir, flg + elif ( + qir[i]["gatef"].n == qir[j]["gatef"].n + "d" + or qir[i]["gatef"].n + "d" == qir[j]["gatef"].n + ): + del qir[i] + del qir[j - 1] + # qir[i] = None + # qir[j] = None + flg = True + # return qir, True + i += 1 + return qir, flg + + +def merge( + circuit: Union[AbstractCircuit, List[Dict[str, Any]]], + rules: Optional[Dict[Tuple[str, ...], str]] = None, + **kws: Any +) -> Any: + merge_rules = copy(default_merge_rules) + if rules is not None: + merge_rules.update(rules) # type: ignore + if isinstance(circuit, list): + qir = circuit + output = "qir" + else: + qir = circuit.to_qir() + output = "tc" + flg = True + while flg: + qir, flg = _merge(qir, merge_rules) # type: ignore + if output in ["qir"]: + return qir + elif output in ["tc", "circuit"]: + c: Any = type(circuit).from_qir(qir, circuit.circuit_param) # type: ignore + return c + + +def simple_compile( + circuit: Any, + info: Optional[Dict[str, Any]] = None, + output: str = "tc", + compiled_options: Optional[Dict[str, Any]] = None, +) -> Any: + if compiled_options is None: + compiled_options = {} + len0 = len(circuit.to_qir()) + for d in circuit._extra_qir: + if d["pos"] < len0 - 1: + raise ValueError( + "TC's simple compiler doesn't support measurement/reset instructions \ + in the middle of the circuit" + ) + + c = replace_r(circuit, **compiled_options) + c = replace_u(c, **compiled_options) + qir = c.to_qir() + len0 = len(qir) + qir = merge(qir, **compiled_options) + qir = prune(qir, **compiled_options) + len1 = len(qir) + while len1 != len0: + len0 = len1 + qir = merge(qir, **compiled_options) + if len(qir) == len0: + break + qir = prune(qir, **compiled_options) + len1 = len(qir) + + c = type(circuit).from_qir(qir, circuit.circuit_param) + + for d in circuit._extra_qir: + d["pos"] = len1 + c._extra_qir.append(d) + return (c, info) diff --git a/tensorcircuit/cons.py b/tensorcircuit/cons.py index 3435af02..c2ffa8f8 100644 --- a/tensorcircuit/cons.py +++ b/tensorcircuit/cons.py @@ -1,10 +1,12 @@ """ Constants and setups """ + # pylint: disable=invalid-name import logging import sys +import time from contextlib import contextmanager from functools import partial, reduce, wraps from operator import mul @@ -16,7 +18,7 @@ from tensornetwork.backend_contextmanager import get_default_backend from .backends.numpy_backend import NumpyBackend -from .backends import get_backend # type: ignore +from .backends import get_backend from .simplify import _multi_remove logger = logging.getLogger(__name__) @@ -128,24 +130,39 @@ def set_dtype(dtype: Optional[str] = None, set_global: bool = True) -> Tuple[str """ Set the global runtime numerical dtype of tensors. - :param dtype: "complex64" or "complex128", defaults to None, which is equivalent to "complex64". + :param dtype: "complex64"/"float32" or "complex128"/"float64", + defaults to None, which is equivalent to "complex64". :type dtype: Optional[str], optional :return: complex dtype str and the corresponding real dtype str :rtype: Tuple[str, str] """ if not dtype: dtype = "complex64" + if dtype == "complex64": rdtype = "float32" - else: + elif dtype == "complex128": + rdtype = "float64" + elif dtype == "float32": + dtype = "complex64" + rdtype = "float32" + elif dtype == "float64": + dtype = "complex128" rdtype = "float64" - if backend.name == "jax": - from jax.config import config # type: ignore + else: + raise ValueError(f"Unsupported data type: {dtype}") + + try: + from jax import config + except ImportError: + config = None # type: ignore + if config is not None: if dtype == "complex128": config.update("jax_enable_x64", True) elif dtype == "complex64": config.update("jax_enable_x64", False) + if set_global: npdtype = getattr(np, dtype) for module in sys.modules: @@ -245,7 +262,7 @@ def _merge_single_gates( njs = [i for i, n in enumerate(nodes) if id(n) in [id(e0.node1), id(e0.node2)]] qjs = [i for i, n in enumerate(queue) if id(n) in [id(e0.node1), id(e0.node2)]] new_node = tn.contract(e0) - total_size += _sizen(new_node) # type: ignore + total_size += _sizen(new_node) logger.debug( _sizen(new_node, is_log=True), @@ -259,7 +276,7 @@ def _merge_single_gates( if len(new_node.tensor.shape) <= 2: # queue.append(new_node) queue.insert(0, new_node) - return nodes, total_size # type: ignore + return nodes, total_size def experimental_contractor( @@ -431,7 +448,7 @@ def _get_path( output_set = set([id(e) for e in tn.get_subgraph_dangling(nodes)]) size_dict = {id(edge): edge.dimension for edge in tn.get_all_edges(nodes)} - return algorithm(input_sets, output_set, size_dict), nodes # type: ignore + return algorithm(input_sets, output_set, size_dict), nodes def _get_path_cache_friendly( @@ -462,7 +479,7 @@ def _get_path_cache_friendly( logger.debug("output_set: %s" % output_set) logger.debug("size_dict: %s" % size_dict) logger.debug("path finder algorithm: %s" % algorithm) - return algorithm(input_sets, output_set, size_dict), nodes_new # type: ignore + return algorithm(input_sets, output_set, size_dict), nodes_new # directly get input_sets, output_set and size_dict by using identity function as algorithm @@ -603,8 +620,8 @@ def _base( nodes = _multi_remove(nodes, [a, b]) logger.debug(_sizen(new_node, is_log=True)) - total_size += _sizen(new_node) # type: ignore - logger.info("----- WRITE: %s --------\n" % np.log2(total_size)) # type: ignore + total_size += _sizen(new_node) + logger.info("----- WRITE: %s --------\n" % np.log2(total_size)) # if the final node has more than one edge, # output_edge_order has to be specified @@ -620,7 +637,7 @@ def custom( memory_limit: Optional[int] = None, output_edge_order: Optional[List[Any]] = None, ignore_edge_order: bool = False, - **kws: Any + **kws: Any, ) -> Any: if len(nodes) < 5: alg = opt_einsum.paths.optimal @@ -653,7 +670,7 @@ def custom_stateful( opt_conf: Optional[Dict[str, Any]] = None, output_edge_order: Optional[List[Any]] = None, ignore_edge_order: bool = False, - **kws: Any + **kws: Any, ) -> Any: if len(nodes) < 5: alg = opt_einsum.paths.optimal @@ -690,18 +707,22 @@ def new_algorithm( input_sets: Dict[Any, int], output_set: Dict[Any, int], size_dict: Dict[Any, int], - **kws: Any + **kws: Any, ) -> Any: + t0 = time.time() path = algorithm(input_sets, output_set, size_dict, **kws) + path_finding_time = time.time() - t0 tree = ContractionTree.from_path(input_sets, output_set, size_dict, path=path) print("------ contraction cost summary ------") print( "log10[FLOPs]: ", - "%.3f" % np.log10(tree.total_flops()), + "%.3f" % np.log10(float(tree.total_flops())), " log2[SIZE]: ", "%.0f" % tree.contraction_width(), " log2[WRITE]: ", - "%.3f" % np.log2(tree.total_write()), + "%.3f" % np.log2(float(tree.total_write())), + " PathFindingTime: ", + "%.3f" % path_finding_time, ) return path @@ -716,7 +737,7 @@ def set_contractor( set_global: bool = True, contraction_info: bool = False, debug_level: int = 0, - **kws: Any + **kws: Any, ) -> Callable[..., Any]: """ To set runtime contractor of the tensornetwork for a better contraction path. @@ -738,6 +759,32 @@ def set_contractor( method = "greedy" # auto for small size fallbacks to dp, which has bug for now # see: https://github.com/dgasmith/opt_einsum/issues/172 + if method.startswith("cotengra"): + # cotengra shortcut + import cotengra + + if method == "cotengra": + method = "custom" + optimizer = cotengra.ReusableHyperOptimizer( + methods=["greedy", "kahypar"], + parallel=True, + minimize="combo", + max_time=30, + max_repeats=64, + progbar=True, + ) + else: # "cotengra-30-64" + _, mt, mr = method.split("-") + method = "custom" + optimizer = cotengra.ReusableHyperOptimizer( + methods=["greedy", "kahypar"], + parallel=True, + minimize="combo", + max_time=int(mt), + max_repeats=int(mr), + progbar=True, + ) + if method == "plain": cf = plain_contractor elif method == "plain-experimental": @@ -757,7 +804,7 @@ def set_contractor( opt_conf=opt_conf, contraction_info=contraction_info, debug_level=debug_level, - **kws + **kws, ) else: @@ -774,7 +821,7 @@ def set_contractor( optimizer=optimizer, memory_limit=memory_limit, debug_level=debug_level, - **kws + **kws, ) if set_global: for module in sys.modules: diff --git a/tensorcircuit/densitymatrix.py b/tensorcircuit/densitymatrix.py index 2ea1ba2e..dc52fe4a 100644 --- a/tensorcircuit/densitymatrix.py +++ b/tensorcircuit/densitymatrix.py @@ -1,6 +1,7 @@ """ Quantum circuit class but with density matrix simulator """ + # pylint: disable=invalid-name from functools import reduce @@ -62,7 +63,7 @@ def __init__( and (mpo_dminputs is None) ): # Get nodes on the interior - self._nodes = self.all_zero_nodes(nqubits) # type: ignore + self._nodes = self.all_zero_nodes(nqubits) self._front = [n.get_edge(0) for n in self._nodes] self.coloring_nodes(self._nodes) self._double_nodes_front() @@ -75,18 +76,18 @@ def __init__( n = int(np.log(N) / np.log(2)) assert n == nqubits inputs = backend.reshape(inputs, [2 for _ in range(n)]) - inputs = Gate(inputs) - self._nodes = [inputs] + inputs_gate = Gate(inputs) + self._nodes = [inputs_gate] self.coloring_nodes(self._nodes) - self._front = [inputs.get_edge(i) for i in range(n)] + self._front = [inputs_gate.get_edge(i) for i in range(n)] self._double_nodes_front() elif mps_inputs is not None: - mps_nodes = list(mps_inputs.nodes) # type: ignore + mps_nodes = list(mps_inputs.nodes) for i, n in enumerate(mps_nodes): mps_nodes[i].tensor = backend.cast(n.tensor, dtypestr) # type: ignore - mps_edges = mps_inputs.out_edges + mps_inputs.in_edges # type: ignore - self._nodes, self._front = self.copy(mps_nodes, mps_edges) + mps_edges = mps_inputs.out_edges + mps_inputs.in_edges + self._nodes, self._front = self.copy_nodes(mps_nodes, mps_edges) self.coloring_nodes(self._nodes) self._double_nodes_front() @@ -94,9 +95,9 @@ def __init__( dminputs = backend.convert_to_tensor(dminputs) dminputs = backend.cast(dminputs, dtype=dtypestr) dminputs = backend.reshape(dminputs, [2 for _ in range(2 * nqubits)]) - dminputs = Gate(dminputs) - nodes = [dminputs] - self._front = [dminputs.get_edge(i) for i in range(2 * nqubits)] + dminputs_gate = Gate(dminputs) + nodes = [dminputs_gate] + self._front = [dminputs_gate.get_edge(i) for i in range(2 * nqubits)] self._nodes = nodes self.coloring_nodes(self._nodes) @@ -128,9 +129,10 @@ def __init__( } self._qir: List[Dict[str, Any]] = [] + self._extra_qir: List[Dict[str, Any]] = [] def _double_nodes_front(self) -> None: - lnodes, lfront = self.copy(self._nodes, self._front, conj=True) + lnodes, lfront = self.copy_nodes(self._nodes, self._front, conj=True) self._front.extend(lfront) self._nodes.extend(lnodes) @@ -265,7 +267,12 @@ def get_dm_as_quoperator(self) -> QuOperator: return QuOperator(edges[: self._nqubits], edges[self._nqubits :]) def expectation( - self, *ops: Tuple[tn.Node, List[int]], reuse: bool = True, **kws: Any + self, + *ops: Tuple[tn.Node, List[int]], + reuse: bool = True, + noise_conf: Optional[Any] = None, + status: Optional[Tensor] = None, + **kws: Any ) -> tn.Node.tensor: """ Compute the expectation of corresponding operators. @@ -275,11 +282,27 @@ def expectation( :type ops: Tuple[tn.Node, List[int]] :param reuse: whether contract the density matrix in advance, defaults to True :type reuse: bool + :param noise_conf: Noise Configuration, defaults to None + :type noise_conf: Optional[NoiseConf], optional + :param status: external randomness given by tensor uniformly from [0, 1], defaults to None, + used for noisfy circuit sampling + :type status: Optional[Tensor], optional :return: Tensor with one element :rtype: Tensor """ - nodes = self.expectation_before(*ops, reuse=reuse) - return contractor(nodes).tensor + from .noisemodel import expectation_noisfy + + if noise_conf is None: + nodes = self.expectation_before(*ops, reuse=reuse) + return contractor(nodes).tensor + else: + return expectation_noisfy( + self, + *ops, + noise_conf=noise_conf, + status=status, + **kws, + ) @staticmethod def check_density_matrix(dm: Tensor) -> None: @@ -306,12 +329,16 @@ def to_circuit(self, circuit_params: Optional[Dict[str, Any]] = None) -> Circuit class DMCircuit2(DMCircuit): - def apply_general_kraus(self, kraus: Sequence[Gate], *index: int, **kws: Any) -> None: # type: ignore + def apply_general_kraus( + self, kraus: Sequence[Gate], *index: int, **kws: Any + ) -> None: # incompatible API for now kraus = [ - k - if isinstance(k, tn.Node) - else Gate(backend.cast(backend.convert_to_tensor(k), dtypestr)) + ( + k + if isinstance(k, tn.Node) + else Gate(backend.cast(backend.convert_to_tensor(k), dtypestr)) + ) for k in kraus ] self.check_kraus(kraus) @@ -336,7 +363,7 @@ def apply_general_kraus(self, kraus: Sequence[Gate], *index: int, **kws: Any) -> self._nodes.append(super_op) setattr(self, "state_tensor", None) - general_kraus = apply_general_kraus # type: ignore + general_kraus = apply_general_kraus @staticmethod def apply_general_kraus_delayed( diff --git a/tensorcircuit/experimental.py b/tensorcircuit/experimental.py index 137e81a1..8eba9a43 100644 --- a/tensorcircuit/experimental.py +++ b/tensorcircuit/experimental.py @@ -3,18 +3,21 @@ """ from functools import partial -from typing import Any, Callable, Optional, Sequence, Union +from typing import Any, Callable, Optional, Tuple, Sequence, Union import numpy as np -from .cons import backend, dtypestr +from .cons import backend, dtypestr, contractor, rdtypestr +from .gates import Gate Tensor = Any +Circuit = Any def adaptive_vmap( f: Callable[..., Any], vectorized_argnums: Union[int, Sequence[int]] = 0, + static_argnums: Optional[Union[int, Sequence[int]]] = None, chunk_size: Optional[int] = None, ) -> Callable[..., Any]: if chunk_size is None: @@ -44,7 +47,10 @@ def wrapper(*args: Any, **kws: Any) -> Tensor: reshape_args.append(arg) if s2 != 0: rest_args.append(arg_rest) - _vmap = backend.vmap(f, vectorized_argnums) + _vmap = backend.jit( + backend.vmap(f, vectorized_argnums=vectorized_argnums), + static_argnums=static_argnums, + ) r = [] for i in range(s1): # currently using naive python loop for simplicity @@ -53,16 +59,16 @@ def wrapper(*args: Any, **kws: Any) -> Tensor: for j, a in enumerate(reshape_args) ] r.append(_vmap(*nreshape_args, **kws)) - r = backend.stack(r) - rshape = list(backend.shape_tuple(r)) - if len(rshape) == 2: - nshape = [rshape[0] * rshape[1]] - else: - nshape = [rshape[0] * rshape[1], -1] - r = backend.reshape(r, nshape) + r = backend.tree_map(lambda *x: backend.concat(x), *r) + # rshape = list(backend.shape_tuple(r)) + # if len(rshape) == 2: + # nshape = [rshape[0] * rshape[1]] + # else: + # nshape = [rshape[0] * rshape[1], -1] + # r = backend.reshape(r, nshape) if s2 != 0: rest_r = _vmap(*rest_args, **kws) - return backend.concat([r, rest_r]) + return backend.tree_map(lambda *x: backend.concat(x), r, rest_r) return r return wrapper @@ -210,6 +216,7 @@ def parameter_shift_grad( f: Callable[..., Tensor], argnums: Union[int, Sequence[int]] = 0, jit: bool = False, + shifts: Tuple[float, float] = (np.pi / 2, 2), ) -> Callable[..., Tensor]: """ similar to `grad` function but using parameter shift internally instead of AD, @@ -223,10 +230,12 @@ def parameter_shift_grad( :param jit: whether jit the original function `f` at the beginning, defaults to False :type jit: bool, optional + :param shifts: two floats for the delta shift on the numerator and dominator, + defaults to (pi/2, 2) for parameter shift + :type shifts: Tuple[float, float] :return: the grad function :rtype: Callable[..., Tensor] """ - # TODO(@refraction-ray): finite shot sample_expectation_ps not supported well for now if jit is True: f = backend.jit(f) @@ -243,14 +252,95 @@ def grad_f(*args: Any, **kws: Any) -> Any: onehot = backend.eye(size) onehot = backend.cast(onehot, args[i].dtype) onehot = backend.reshape(onehot, [size] + list(shape)) - onehot = np.pi / 2 * onehot + onehot = shifts[0] * onehot + nargs = list(args) + arg = backend.reshape(args[i], [1] + list(shape)) + batched_arg = backend.tile(arg, [size] + [1 for _ in shape]) + nargs[i] = batched_arg + onehot + nargs2 = list(args) + nargs2[i] = batched_arg - onehot + r = (vfs[i](*nargs, **kws) - vfs[i](*nargs2, **kws)) / shifts[1] + r = backend.reshape(r, shape) + grad_values.append(r) + if len(argnums) > 1: # type: ignore + return tuple(grad_values) + return grad_values[0] + + return grad_f + + +def parameter_shift_grad_v2( + f: Callable[..., Tensor], + argnums: Union[int, Sequence[int]] = 0, + jit: bool = False, + random_argnums: Optional[Sequence[int]] = None, + shifts: Tuple[float, float] = (np.pi / 2, 2), +) -> Callable[..., Tensor]: + """ + similar to `grad` function but using parameter shift internally instead of AD, + vmap is utilized for evaluation, v2 also supports random generator for finite + measurememt shot, only jax backend is supported, since no vmap randomness is + available in tensorflow + + :param f: quantum function with weights in and expectation out + :type f: Callable[..., Tensor] + :param argnums: label which args should be differentiated, + defaults to 0 + :type argnums: Union[int, Sequence[int]], optional + :param jit: whether jit the original function `f` at the beginning, + defaults to False + :type jit: bool, optional + :param shifts: two floats for the delta shift on the numerator and dominator, + defaults to (pi/2, 2) for parameter shift + :type shifts: Tuple[float, float] + :return: the grad function + :rtype: Callable[..., Tensor] + """ + # TODO(@refraction-ray): replace with new status support for the sample API + if jit is True: + f = backend.jit(f) + + if isinstance(argnums, int): + argnums = [argnums] + + if random_argnums is None: + vfs = [backend.vmap(f, vectorized_argnums=i) for i in argnums] + else: + if isinstance(random_argnums, int): + random_argnums = [random_argnums] + vfs = [ + backend.vmap(f, vectorized_argnums=[i] + random_argnums) for i in argnums # type: ignore + ] + + def grad_f(*args: Any, **kws: Any) -> Any: + grad_values = [] + for i in argnums: # type: ignore + shape = backend.shape_tuple(args[i]) + size = backend.sizen(args[i]) + onehot = backend.eye(size) + onehot = backend.cast(onehot, args[i].dtype) + onehot = backend.reshape(onehot, [size] + list(shape)) + onehot = shifts[0] * onehot nargs = list(args) arg = backend.reshape(args[i], [1] + list(shape)) batched_arg = backend.tile(arg, [size] + [1 for _ in shape]) nargs[i] = batched_arg + onehot nargs2 = list(args) nargs2[i] = batched_arg - onehot - r = (vfs[i](*nargs, **kws) - vfs[i](*nargs2, **kws)) / 2.0 + if random_argnums is not None: + for j in random_argnums: + keys = [] + key = args[j] + for _ in range(size): + key, subkey = backend.random_split(key) + keys.append(subkey) + nargs[j] = backend.stack(keys) + keys = [] + for _ in range(size): + key, subkey = backend.random_split(key) + keys.append(subkey) + nargs2[j] = backend.stack(keys) + r = (vfs[i](*nargs, **kws) - vfs[i](*nargs2, **kws)) / shifts[1] r = backend.reshape(r, shape) grad_values.append(r) if len(argnums) > 1: # type: ignore @@ -258,3 +348,180 @@ def grad_f(*args: Any, **kws: Any) -> Any: return grad_values[0] return grad_f + + +# TODO(@refraction-ray): add SPSA gradient wrapper similar to parameter shift +# -- using noisyopt package instead + + +def finite_difference_differentiator( + f: Callable[..., Any], + argnums: Tuple[int, ...] = (0,), + shifts: Tuple[float, float] = (0.001, 0.002), +) -> Callable[..., Any]: + # \bar{x}_j = \sum_i \bar{y}_i \frac{\Delta y_i}{\Delta x_j} + # tf only now and designed for hardware, since we dont do batch evaluation + import tensorflow as tf + + @tf.custom_gradient # type: ignore + def tf_function(*args: Any, **kwargs: Any) -> Any: + y = f(*args, **kwargs) + + def grad(ybar: Any) -> Any: + # only support one output + delta_ms = [] + for argnum in argnums: + delta_m = [] + xi = tf.reshape(args[argnum], [-1]) + xi_size = xi.shape[0] + onehot = tf.one_hot(tf.range(xi_size), xi_size) + for j in range(xi_size): + xi_plus = xi + tf.cast(shifts[0] * onehot[j], xi.dtype) + xi_minus = xi - tf.cast(shifts[0] * onehot[j], xi.dtype) + args_plus = list(args) + args_plus[argnum] = tf.reshape(xi_plus, args[argnum].shape) + args_minus = list(args) + args_minus[argnum] = tf.reshape(xi_minus, args[argnum].shape) + dy = f(*args_plus, **kwargs) - f(*args_minus, **kwargs) + dy /= shifts[-1] + delta_m.append(tf.reshape(dy, [-1])) + delta_m = tf.stack(delta_m) + delta_m = tf.transpose(delta_m) + delta_ms.append(delta_m) + dxs = [tf.zeros_like(arg) for arg in args] + ybar_flatten = tf.reshape(ybar, [1, -1]) + for i, argnum in enumerate(argnums): + dxs[argnum] = tf.cast( + tf.reshape(ybar_flatten @ delta_ms[i], args[argnum].shape), + args[argnum].dtype, + ) + + return tuple(dxs) + + return y, grad + + return tf_function # type: ignore + + +def hamiltonian_evol( + tlist: Tensor, + h: Tensor, + psi0: Tensor, + callback: Optional[Callable[..., Any]] = None, +) -> Tensor: + """ + Fast implementation of static full Hamiltonian evolution + (default as imaginary time) + + :param tlist: _description_ + :type tlist: Tensor + :param h: _description_ + :type h: Tensor + :param psi0: _description_ + :type psi0: Tensor + :param callback: _description_, defaults to None + :type callback: Optional[Callable[..., Any]], optional + :return: Tensor + :rtype: result dynamics on ``tlist`` + """ + es, u = backend.eigh(h) + utpsi0 = backend.reshape( + backend.transpose(u) @ backend.reshape(psi0, [-1, 1]), [-1] + ) + + @backend.jit + def _evol(t: Tensor) -> Tensor: + ebetah_utpsi0 = backend.exp(-t * es) * utpsi0 + psi_exact = backend.conj(u) @ backend.reshape(ebetah_utpsi0, [-1, 1]) + psi_exact = backend.reshape(psi_exact, [-1]) + psi_exact = psi_exact / backend.norm(psi_exact) + if callback is None: + return psi_exact + return callback(psi_exact) + + return backend.stack([_evol(t) for t in tlist]) + + +def evol_local( + c: Circuit, + index: Sequence[int], + h_fun: Callable[..., Tensor], + t: float, + *args: Any, + **solver_kws: Any +) -> Circuit: + """ + ode evolution of time dependent Hamiltonian on circuit of given indices + [only jax backend support for now] + + :param c: _description_ + :type c: Circuit + :param index: _description_ + :type index: Sequence[int] + :param h_fun: h_fun should return a dense Hamiltonian matrix + with input arguments time and *args + :type h_fun: Callable[..., Tensor] + :param t: evolution time + :type t: float + :return: _description_ + :rtype: Circuit + """ + from jax.experimental.ode import odeint + + s = c.state() + n = c._nqubits + l = len(index) + + def f(y: Tensor, t: Tensor, *args: Any) -> Tensor: + y = backend.reshape2(y) + y = Gate(y) + h = -1.0j * h_fun(t, *args) + h = backend.reshape2(h) + h = Gate(h) + edges = [] + for i in range(n): + if i not in index: + edges.append(y[i]) + else: + j = index.index(i) + edges.append(h[j]) + h[j + l] ^ y[i] + y = contractor([y, h], output_edge_order=edges) + return backend.reshape(y.tensor, [-1]) + + ts = backend.stack([0.0, t]) + ts = backend.cast(ts, dtype=rdtypestr) + s1 = odeint(f, s, ts, *args, **solver_kws) + return type(c)(n, inputs=s1[-1]) + + +def evol_global( + c: Circuit, h_fun: Callable[..., Tensor], t: float, *args: Any, **solver_kws: Any +) -> Circuit: + """ + ode evolution of time dependent Hamiltonian on circuit of all qubits + [only jax backend support for now] + + :param c: _description_ + :type c: Circuit + :param h_fun: h_fun should return a **SPARSE** Hamiltonian matrix + with input arguments time and *args + :type h_fun: Callable[..., Tensor] + :param t: _description_ + :type t: float + :return: _description_ + :rtype: Circuit + """ + from jax.experimental.ode import odeint + + s = c.state() + n = c._nqubits + + def f(y: Tensor, t: Tensor, *args: Any) -> Tensor: + h = -1.0j * h_fun(t, *args) + return backend.sparse_dense_matmul(h, y) + + ts = backend.stack([0.0, t]) + ts = backend.cast(ts, dtype=rdtypestr) + s1 = odeint(f, s, ts, *args, **solver_kws) + return type(c)(n, inputs=s1[-1]) diff --git a/tensorcircuit/fgs.py b/tensorcircuit/fgs.py new file mode 100644 index 00000000..a7012701 --- /dev/null +++ b/tensorcircuit/fgs.py @@ -0,0 +1,931 @@ +""" +Fermion Gaussian state simulator +""" + +from typing import Any, Dict, List, Optional, Tuple + +import numpy as np + +try: + import openfermion +except ModuleNotFoundError: + pass + +from .cons import backend, dtypestr, rdtypestr, get_backend +from .circuit import Circuit +from . import quantum + +Tensor = Any + + +def onehot_matrix(i: int, j: int, N: int) -> Tensor: + m = np.zeros([N, N]) + m[i, j] = 1 + m = backend.convert_to_tensor(m) + m = backend.cast(m, dtypestr) + return m + + +# TODO(@refraction-ray): efficiency benchmark with jit +# TODO(@refraction-ray): FGS mixed state support? +# TODO(@refraction-ray): overlap? + + +class FGSSimulator: + r""" + main refs: https://arxiv.org/pdf/2306.16595.pdf, + https://arxiv.org/abs/2209.06945, + https://scipost.org/SciPostPhysLectNotes.54/pdf + + convention: + for Hamiltonian (c^dagger, c)H(c, c^\dagger) + for correlation <(c, c^\dagger)(c^\dagger, c)> + c' = \alpha^\dagger (c, c^\dagger) + """ + + def __init__( + self, + L: int, + filled: Optional[List[int]] = None, + alpha: Optional[Tensor] = None, + hc: Optional[Tensor] = None, + cmatrix: Optional[Tensor] = None, + ): + """ + _summary_ + + :param L: system size + :type L: int + :param filled: the fermion site that is fully occupied, defaults to None + :type filled: Optional[List[int]], optional + :param alpha: directly specify the alpha tensor as the input + :type alpha: Optional[Tensor], optional + :param hc: the input is given as the ground state of quadratic Hamiltonian ``hc`` + :type hc: Optional[Tensor], optional + :param cmatrix: only used for debug, defaults to None + :type cmatrix: Optional[Tensor], optional + """ + if filled is None: + filled = [] + self.L = L + if alpha is None: + if hc is None: + self.alpha = self.init_alpha(filled, L) + else: + _, _, self.alpha = self.fermion_diagonalization(hc, L) + else: + self.alpha = alpha + self.alpha0 = self.alpha + self.wtransform = self.wmatrix(L) + self.cmatrix = cmatrix + self.otcmatrix: Dict[Tuple[int, int], Tensor] = {} + + @classmethod + def fermion_diagonalization( + cls, hc: Tensor, L: int + ) -> Tuple[Tensor, Tensor, Tensor]: + es, u = backend.eigh(hc) + es = es[::-1] + u = u[:, ::-1] + alpha = u[:, :L] + return es, u, alpha + + @classmethod + def fermion_diagonalization_2( + cls, hc: Tensor, L: int + ) -> Tuple[Tensor, Tensor, Tensor]: + w = cls.wmatrix(L) + hm = 0.25 * w @ hc @ backend.adjoint(w) + hm = backend.real((-1.0j) * hm) + hd, om = backend.schur(hm, output="real") + # order not kept + # eps = 1e-10 + # idm = backend.convert_to_tensor(np.array([[1.0, 0], [0, 1.0]])) + # idm = backend.cast(idm, dtypestr) + # xm = backend.convert_to_tensor(np.array([[0, 1.0], [1.0, 0]])) + # xm = backend.cast(xm, dtypestr) + # for i in range(0, 2 * L, 2): + # (backend.sign(hd[i, i + 1] + eps) + 1) / 2 * idm - ( + # backend.sign(hd[i, i + 1] + eps) - 1 + # ) / 2 * xm + # print(hd) + + es = backend.adjoint(w) @ (1.0j * hd) @ w + u = 0.5 * backend.adjoint(w) @ backend.transpose(om) @ w + alpha = backend.adjoint(u)[:, :L] + # c' = u@c + # e_k (c^\dagger_k c_k - c_k c^\dagger_k) + return es, u, alpha + + @staticmethod + def wmatrix(L: int) -> Tensor: + w = np.zeros([2 * L, 2 * L], dtype=complex) + for i in range(2 * L): + if i % 2 == 1: + w[i, (i - 1) // 2] = 1.0j + w[i, (i - 1) // 2 + L] = -1.0j + else: + w[i, i // 2] = 1 + w[i, i // 2 + L] = 1 + return backend.convert_to_tensor(w) + + @staticmethod + def init_alpha(filled: List[int], L: int) -> Tensor: + alpha = np.zeros([2 * L, L]) + for i in range(L): + if i not in filled: + alpha[i, i] = 1 + else: + alpha[i + L, i] = 1 + alpha = backend.convert_to_tensor(alpha) + alpha = backend.cast(alpha, dtypestr) + return alpha + + def get_alpha(self) -> Tensor: + return self.alpha + + def get_cmatrix(self, now_i: bool = True, now_j: bool = True) -> Tensor: + # otoc in FGS language, see: https://arxiv.org/pdf/1908.03292.pdf + # https://journals.aps.org/prb/pdf/10.1103/PhysRevB.99.054205 + # otoc for non=hermitian system is more subtle due to the undefined normalization + # of operator and not considered here, see: https://arxiv.org/pdf/2305.12054.pdf + if self.cmatrix is not None and now_i is True and now_j is True: + return self.cmatrix + elif now_i is True and now_j is True: + cmatrix = self.alpha @ backend.adjoint(self.alpha) + self.cmatrix = cmatrix + return cmatrix + elif now_i is True: # new i old j + if (1, 0) in self.otcmatrix: + return self.otcmatrix[(1, 0)] + cmatrix = self.alpha @ backend.adjoint(self.alpha0) + self.otcmatrix[(1, 0)] = cmatrix + return cmatrix + elif now_j is True: # old i new j + if (0, 1) in self.otcmatrix: + return self.otcmatrix[(0, 1)] + cmatrix = self.alpha0 @ backend.adjoint(self.alpha) + self.otcmatrix[(0, 1)] = cmatrix + return cmatrix + else: # initial cmatrix + if (0, 0) in self.otcmatrix: + return self.otcmatrix[(0, 0)] + cmatrix = self.alpha0 @ backend.adjoint(self.alpha0) + self.otcmatrix[(0, 0)] = cmatrix + return cmatrix + + def get_reduced_cmatrix(self, subsystems_to_trace_out: List[int]) -> Tensor: + m = self.get_cmatrix() + if subsystems_to_trace_out is None: + subsystems_to_trace_out = [] + keep = [i for i in range(self.L) if i not in subsystems_to_trace_out] + keep += [i + self.L for i in range(self.L) if i not in subsystems_to_trace_out] + keep = backend.convert_to_tensor(keep) + + def slice_(a: Tensor) -> Tensor: + return backend.gather1d(a, keep) + + slice_ = backend.vmap(slice_) + m = backend.gather1d(slice_(m), keep) + return m + + def renyi_entropy(self, n: int, subsystems_to_trace_out: List[int]) -> Tensor: + """ + compute renyi_entropy of order ``n`` for the fermion state + + :param n: _description_ + :type n: int + :param subsystems_to_trace_out: system sites to be traced out + :type subsystems_to_trace_out: List[int] + :return: _description_ + :rtype: Tensor + """ + m = self.get_reduced_cmatrix(subsystems_to_trace_out) + lbd, _ = backend.eigh(m) + lbd = backend.real(lbd) + lbd = backend.relu(lbd) + eps = 1e-6 + + entropy = backend.sum(backend.log(lbd**n + (1 - lbd) ** n + eps)) + s = 1 / (2 * (1 - n)) * entropy + return s + + def entropy(self, subsystems_to_trace_out: Optional[List[int]] = None) -> Tensor: + """ + compute von Neumann entropy for the fermion state + + :param subsystems_to_trace_out: _description_, defaults to None + :type subsystems_to_trace_out: Optional[List[int]], optional + :return: _description_ + :rtype: Tensor + """ + m = self.get_reduced_cmatrix(subsystems_to_trace_out) # type: ignore + lbd, _ = backend.eigh(m) + lbd = backend.real(lbd) + lbd = backend.relu(lbd) + # lbd /= backend.sum(lbd) + eps = 1e-6 + entropy = -backend.sum( + lbd * backend.log(lbd + eps) + (1 - lbd) * backend.log(1 - lbd + eps) + ) + return entropy / 2 + + def evol_hamiltonian(self, h: Tensor) -> None: + r""" + Evolve as :math:`e^{-i/2 \hat{h}}` + + :param h: _description_ + :type h: Tensor + """ + # e^{-i/2 H} + h = backend.cast(h, dtype=dtypestr) + self.alpha = backend.expm(-1.0j * h) @ self.alpha + self.cmatrix = None + self.otcmatrix = {} + + def evol_ihamiltonian(self, h: Tensor) -> None: + r""" + Evolve as :math:`e^{-1/2 \hat{h}}` + + :param h: _description_ + :type h: Tensor + """ + # e^{-1/2 H} + h = backend.cast(h, dtype=dtypestr) + self.alpha = backend.expm(h) @ self.alpha + self.orthogonal() + self.cmatrix = None + self.otcmatrix = {} + + def evol_ghamiltonian(self, h: Tensor) -> None: + r""" + Evolve as :math:`e^{-1/2 i \hat{h}}` with h generally non-Hermitian + + :param h: _description_ + :type h: Tensor + """ + # e^{-1/2 H} + h = backend.cast(h, dtype=dtypestr) + self.alpha = backend.expm(-1.0j * backend.adjoint(h)) @ self.alpha + self.orthogonal() + self.cmatrix = None + self.otcmatrix = {} + + def orthogonal(self) -> None: + q, _ = backend.qr(self.alpha) + self.alpha = q + + @staticmethod + def hopping(chi: Tensor, i: int, j: int, L: int) -> Tensor: + # chi * ci dagger cj + hc. + chi = backend.convert_to_tensor(chi) + chi = backend.cast(chi, dtypestr) + m = chi / 2 * onehot_matrix(i, j, 2 * L) + m += -chi / 2 * onehot_matrix(j + L, i + L, 2 * L) + m += backend.adjoint(m) + return m + + def evol_hp(self, i: int, j: int, chi: Tensor = 0) -> None: + r""" + The evolve Hamiltonian is :math:`\chi c_i^\dagger c_j +h.c.` + + :param i: _description_ + :type i: int + :param j: _description_ + :type j: int + :param chi: _description_, defaults to 0 + :type chi: Tensor, optional + """ + self.evol_hamiltonian(self.hopping(chi, i, j, self.L)) + + @staticmethod + def chemical_potential(chi: Tensor, i: int, L: int) -> Tensor: + chi = backend.convert_to_tensor(chi) + chi = backend.cast(chi, dtypestr) + m = chi / 2 * onehot_matrix(i, i, 2 * L) + m += -chi / 2 * onehot_matrix(i + L, i + L, 2 * L) + return m + + @staticmethod + def sc_pairing(chi: Tensor, i: int, j: int, L: int) -> Tensor: + chi = backend.convert_to_tensor(chi) + chi = backend.cast(chi, dtypestr) + m = chi / 2 * onehot_matrix(i, j + L, 2 * L) + m += -chi / 2 * onehot_matrix(j, i + L, 2 * L) + m += backend.adjoint(m) + return m + + def evol_sp(self, i: int, j: int, chi: Tensor = 0) -> None: + r""" + The evolve Hamiltonian is :math:`chi c_i^\dagger c_j^\dagger +h.c.` + + + :param i: _description_ + :type i: int + :param j: _description_ + :type j: int + :param chi: _description_, defaults to 0 + :type chi: Tensor, optional + """ + self.evol_hamiltonian(self.sc_pairing(chi, i, j, self.L)) + + def evol_cp(self, i: int, chi: Tensor = 0) -> None: + r""" + The evolve Hamiltonian is :math:`chi c_i^\dagger c_i` + + :param i: _description_ + :type i: int + :param chi: _description_, defaults to 0 + :type chi: Tensor, optional + """ + self.evol_hamiltonian(self.chemical_potential(chi, i, self.L)) + + def evol_icp(self, i: int, chi: Tensor = 0) -> None: + r""" + The evolve Hamiltonian is :math:`chi c_i^\dagger c_i` with :math:`\exp^{-H/2}` + + :param i: _description_ + :type i: int + :param chi: _description_, defaults to 0 + :type chi: Tensor, optional + """ + self.evol_ihamiltonian(self.chemical_potential(chi, i, self.L)) + + def get_bogoliubov_uv(self) -> Tuple[Tensor, Tensor]: + return ( + backend.gather1d( + self.alpha, backend.convert_to_tensor([i for i in range(self.L)]) + ), + backend.gather1d( + self.alpha, + backend.convert_to_tensor([i + self.L for i in range(self.L)]), + ), + ) + + def get_cmatrix_majorana(self) -> Tensor: + r""" + correlation matrix defined in majorana basis + convention: :math:`gamma_0 = c_0 + c_0^\dagger` + :math:`gamma_1 = i(c_0 - c_0^\dagger)` + + :return: _description_ + :rtype: Tensor + """ + c = self.get_cmatrix() + return self.wtransform @ c @ backend.adjoint(self.wtransform) + + def get_covariance_matrix(self) -> Tensor: + m = self.get_cmatrix_majorana() + return -1.0j * (2 * m - backend.eye(self.L * 2)) + + def expectation_2body( + self, i: int, j: int, now_i: bool = True, now_j: bool = True + ) -> Tensor: + """ + expectation of two fermion terms + convention: (c, c^\dagger) + for i>L, c_{i-L}^\dagger is assumed + + :param i: _description_ + :type i: int + :param j: _description_ + :type j: int + :return: _description_ + :rtype: Tensor + """ + return self.get_cmatrix(now_i, now_j)[i][(j + self.L) % (2 * self.L)] + + def expectation_4body(self, i: int, j: int, k: int, l: int) -> Tensor: + """ + expectation of four fermion terms using Wick Thm + convention: (c, c^\dagger) + for i>L, c_{i-L}^\dagger is assumed + + :param i: _description_ + :type i: int + :param j: _description_ + :type j: int + :param k: _description_ + :type k: int + :param l: _description_ + :type l: int + :return: _description_ + :rtype: Tensor + """ + e = ( + self.expectation_2body(i, j) * self.expectation_2body(k, l) + - self.expectation_2body(i, k) * self.expectation_2body(j, l) + + self.expectation_2body(i, l) * self.expectation_2body(j, k) + ) + return e + + def post_select(self, i: int, keep: int = 1) -> None: + """ + post select (project) the fermion state to occupation eigenstate + = ``keep`` + + :param i: _description_ + :type i: int + :param keep: _description_, defaults to 1 + :type keep: int, optional + """ + # i is not jittable, keep is jittable + L = backend.convert_to_tensor(self.L) + i = backend.convert_to_tensor(i) + L = backend.cast(L, "int32") + i = backend.cast(i, "int32") + keep = backend.convert_to_tensor(keep) + keep = backend.cast(keep, "int32") + alpha = self.alpha + # if keep == 0: + i = i + L * (1 - keep) + i0 = backend.argmax(backend.abs(alpha[(i + L) % (2 * L), :])) + i0 = backend.cast(i0, "int32") + alpha1 = alpha - backend.reshape(alpha[:, i0], [-1, 1]) @ backend.reshape( + alpha[(i + L) % (2 * L), :] / alpha[(i + L) % (2 * L), i0], [1, -1] + ) + mask1 = backend.onehot(i0, alpha.shape[1]) + mask1 = backend.cast(mask1, dtypestr) + mask0 = backend.ones(alpha.shape[1]) - mask1 + mask12d = backend.tile(mask1[None, :], [alpha.shape[0], 1]) + mask02d = backend.tile(mask0[None, :], [alpha.shape[0], 1]) + alpha1 = mask02d * alpha1 + mask12d * alpha + r = [] + for j in range(2 * self.L): + indicator = ( + backend.sign(backend.cast((i - j), rdtypestr) ** 2 - 0.5) + 1 + ) / 2 + # i=j indicator = 0, i!=j indicator = 1 + indicator = backend.cast(indicator, dtypestr) + r.append( + backend.ones([self.L]) * indicator + + backend.ones([self.L]) * mask1 * (1 - indicator) + ) + # if j != i: + # r.append(backend.ones([L])) + # else: + # r.append(backend.ones([L]) * mask1) + mask2 = backend.stack(r) + alpha1 = alpha1 * mask2 + r = [] + for j in range(2 * self.L): + indicator = ( + backend.sign( + (backend.cast((i + L) % (2 * L) - j, rdtypestr)) ** 2 - 0.5 + ) + + 1 + ) / 2 + r.append(1 - indicator) + newcol = backend.stack(r) + # newcol = np.zeros([2 * self.L]) + # newcol[(i + L) % (2 * L)] = 1 + # newcol = backend.convert_to_tensor(newcol) + newcol = backend.cast(newcol, dtypestr) + alpha1 = alpha1 * mask02d + backend.tile(newcol[:, None], [1, self.L]) * mask12d + q, _ = backend.qr(alpha1) + self.alpha = q + + def cond_measure(self, ind: int, status: float, with_prob: bool = False) -> Tensor: + p0 = backend.real(self.get_cmatrix()[ind, ind]) + prob = backend.convert_to_tensor([p0, 1 - p0]) + status = backend.convert_to_tensor(status) + status = backend.cast(status, rdtypestr) + eps = 1e-12 + keep = (backend.sign(status - p0 + eps) + 1) / 2 + self.post_select(ind, keep) + if with_prob is False: + return keep + else: + return keep, prob + + # def product(self, other): + # # self@other + # gamma1 = self.get_covariance_matrix() + # gamma2 = other.get_covariance_matrix() + # den = backend.inv(1 + gamma1 @ gamma2) + # idm = backend.eye(2 * self.L) + # covm = idm - (idm - gamma2) @ den @ (idm - gamma1) + # cm = (1.0j * covm + idm) / 2 + # cmatrix = backend.adjoint(self.wtransform) @ cm @ self.wtransform * 0.25 + # return type(self)(self.L, cmatrix=cmatrix) + + def overlap(self, other: "FGSSimulator") -> Tensor: + """ + overlap upto a U(1) phase + + :param other: _description_ + :type other: FGSSimulator + :return: _description_ + :rtype: _type_ + """ + u, v = self.get_bogoliubov_uv() + u1, v1 = other.get_bogoliubov_uv() + return backend.sqrt( + backend.abs(backend.det(backend.adjoint(u1) @ u + backend.adjoint(v1) @ v)) + ) + + +npb = get_backend("numpy") + + +class FGSTestSimulator: + """ + Never use, only for correctness testing + stick to numpy backend and no jit/ad/vmap is available + """ + + def __init__( + self, + L: int, + filled: Optional[List[int]] = None, + state: Optional[Tensor] = None, + hc: Optional[Tensor] = None, + ): + if filled is None: + filled = [] + self.L = L + if state is not None: + self.state = state + elif hc is not None: + self.state = self.fermion_diagonalization(hc, L) + else: + self.state = self.init_state(filled, L) + self.state0 = self.state + + @staticmethod + def init_state(filled: List[int], L: int) -> Tensor: + c = Circuit(L) + for i in filled: + c.x(i) # type: ignore + return c.state() + + @classmethod + def fermion_diagonalization(cls, hc: Tensor, L: int) -> Tensor: + h = cls.get_hmatrix(hc, L) + _, u = np.linalg.eigh(h) + return u[:, 0] + + @staticmethod + def get_hmatrix(hc: Tensor, L: int) -> Tensor: + hm = np.zeros([2**L, 2**L], dtype=complex) + for i in range(L): + for j in range(L): + op = openfermion.FermionOperator(f"{str(i)}^ {str(j)}") + hm += ( + hc[i, j] * openfermion.get_sparse_operator(op, n_qubits=L).todense() + ) + + for i in range(L, 2 * L): + for j in range(L): + op = openfermion.FermionOperator(f"{str(i-L)} {str(j)}") + hm += ( + hc[i, j] * openfermion.get_sparse_operator(op, n_qubits=L).todense() + ) + + for i in range(L): + for j in range(L, 2 * L): + op = openfermion.FermionOperator(f"{str(i)}^ {str(j-L)}^") + hm += ( + hc[i, j] * openfermion.get_sparse_operator(op, n_qubits=L).todense() + ) + + for i in range(L, 2 * L): + for j in range(L, 2 * L): + op = openfermion.FermionOperator(f"{str(i-L)} {str(j-L)}^") + hm += ( + hc[i, j] * openfermion.get_sparse_operator(op, n_qubits=L).todense() + ) + + return hm + + @staticmethod + def hopping_jw(chi: Tensor, i: int, j: int, L: int) -> Tensor: + op = chi * openfermion.FermionOperator(f"{str(i)}^ {str(j)}") + np.conj( + chi + ) * openfermion.FermionOperator(f"{str(j)}^ {str(i)}") + sop = openfermion.transforms.jordan_wigner(op) + m = openfermion.get_sparse_operator(sop, n_qubits=L).todense() + return m + + @staticmethod + def chemical_potential_jw(chi: Tensor, i: int, L: int) -> Tensor: + op = chi * openfermion.FermionOperator(f"{str(i)}^ {str(i)}") + sop = openfermion.transforms.jordan_wigner(op) + m = openfermion.get_sparse_operator(sop, n_qubits=L).todense() + return m + + def evol_hamiltonian(self, h: Tensor) -> None: + self.state = npb.expm(-1 / 2 * 1.0j * h) @ npb.reshape(self.state, [-1, 1]) + self.state = npb.reshape(self.state, [-1]) + + def evol_ihamiltonian(self, h: Tensor) -> None: + self.state = npb.expm(-1 / 2 * h) @ npb.reshape(self.state, [-1, 1]) + self.state = npb.reshape(self.state, [-1]) + self.orthogonal() + + def evol_ghamiltonian(self, h: Tensor) -> None: + self.state = npb.expm(-1 / 2 * 1.0j * h) @ npb.reshape(self.state, [-1, 1]) + self.state = npb.reshape(self.state, [-1]) + self.orthogonal() + + def evol_hp(self, i: int, j: int, chi: Tensor = 0) -> None: + self.evol_hamiltonian(self.hopping_jw(chi, i, j, self.L)) + + def evol_cp(self, i: int, chi: Tensor = 0) -> None: + self.evol_hamiltonian(self.chemical_potential_jw(chi, i, self.L)) + + def evol_icp(self, i: int, chi: Tensor = 0) -> None: + self.evol_ihamiltonian(self.chemical_potential_jw(chi, i, self.L)) + + @staticmethod + def sc_pairing_jw(chi: Tensor, i: int, j: int, L: int) -> Tensor: + op = chi * openfermion.FermionOperator(f"{str(i)}^ {str(j)}^") + np.conj( + chi + ) * openfermion.FermionOperator(f"{str(j)} {str(i)}") + sop = openfermion.transforms.jordan_wigner(op) + m = openfermion.get_sparse_operator(sop, n_qubits=L).todense() + return m + + def evol_sp(self, i: int, j: int, chi: Tensor = 0) -> None: + self.evol_hamiltonian(self.sc_pairing_jw(chi, i, j, self.L)) + + def orthogonal(self) -> None: + self.state /= backend.norm(self.state) + + def get_ot_cmatrix(self, h: Tensor, now_i: bool = True) -> Tensor: + alpha1_jw = self.state + cmatrix = np.zeros([2 * self.L, 2 * self.L], dtype=complex) + for i in range(self.L): + for j in range(self.L): + op1 = openfermion.FermionOperator(f"{str(i)}") + op2 = openfermion.FermionOperator(f"{str(j)}^") + + m1 = openfermion.get_sparse_operator(op1, n_qubits=self.L).todense() + m2 = openfermion.get_sparse_operator(op2, n_qubits=self.L).todense() + eh = npb.expm(-1 / 2 * 1.0j * h) + eh1 = npb.expm(1 / 2 * 1.0j * h) + if now_i is True: + cmatrix[i, j] = backend.item( + ( + backend.reshape(backend.adjoint(alpha1_jw), [1, -1]) + @ eh1 + @ m1 + @ eh + @ m2 + @ backend.reshape(alpha1_jw, [-1, 1]) + )[0, 0] + ) + else: + cmatrix[i, j] = backend.item( + ( + backend.reshape(backend.adjoint(alpha1_jw), [1, -1]) + @ m1 + @ eh1 + @ m2 + @ eh + @ backend.reshape(alpha1_jw, [-1, 1]) + )[0, 0] + ) + for i in range(self.L, 2 * self.L): + for j in range(self.L): + op1 = openfermion.FermionOperator(f"{str(i-self.L)}^") + op2 = openfermion.FermionOperator(f"{str(j)}^") + + m1 = openfermion.get_sparse_operator(op1, n_qubits=self.L).todense() + m2 = openfermion.get_sparse_operator(op2, n_qubits=self.L).todense() + eh = npb.expm(-1 / 2 * 1.0j * h) + eh1 = npb.expm(1 / 2 * 1.0j * h) + + if now_i is True: + cmatrix[i, j] = backend.item( + ( + backend.reshape(backend.adjoint(alpha1_jw), [1, -1]) + @ eh1 + @ m1 + @ eh + @ m2 + @ backend.reshape(alpha1_jw, [-1, 1]) + )[0, 0] + ) + else: + cmatrix[i, j] = backend.item( + ( + backend.reshape(backend.adjoint(alpha1_jw), [1, -1]) + @ m1 + @ eh1 + @ m2 + @ eh + @ backend.reshape(alpha1_jw, [-1, 1]) + )[0, 0] + ) + + for i in range(self.L): + for j in range(self.L, 2 * self.L): + op1 = openfermion.FermionOperator(f"{str(i)}") + op2 = openfermion.FermionOperator(f"{str(j-self.L)}") + + m1 = openfermion.get_sparse_operator(op1, n_qubits=self.L).todense() + m2 = openfermion.get_sparse_operator(op2, n_qubits=self.L).todense() + eh = npb.expm(-1 / 2 * 1.0j * h) + eh1 = npb.expm(1 / 2 * 1.0j * h) + + if now_i is True: + cmatrix[i, j] = backend.item( + ( + backend.reshape(backend.adjoint(alpha1_jw), [1, -1]) + @ eh1 + @ m1 + @ eh + @ m2 + @ backend.reshape(alpha1_jw, [-1, 1]) + )[0, 0] + ) + else: + cmatrix[i, j] = backend.item( + ( + backend.reshape(backend.adjoint(alpha1_jw), [1, -1]) + @ m1 + @ eh1 + @ m2 + @ eh + @ backend.reshape(alpha1_jw, [-1, 1]) + )[0, 0] + ) + for i in range(self.L, 2 * self.L): + for j in range(self.L, 2 * self.L): + op1 = openfermion.FermionOperator(f"{str(i-self.L)}^ ") + op2 = openfermion.FermionOperator(f"{str(j-self.L)}") + + m1 = openfermion.get_sparse_operator(op1, n_qubits=self.L).todense() + m2 = openfermion.get_sparse_operator(op2, n_qubits=self.L).todense() + eh = npb.expm(-1 / 2 * 1.0j * h) + eh1 = npb.expm(1 / 2 * 1.0j * h) + + if now_i is True: + cmatrix[i, j] = backend.item( + ( + backend.reshape(backend.adjoint(alpha1_jw), [1, -1]) + @ eh1 + @ m1 + @ eh + @ m2 + @ backend.reshape(alpha1_jw, [-1, 1]) + )[0, 0] + ) + else: + cmatrix[i, j] = backend.item( + ( + backend.reshape(backend.adjoint(alpha1_jw), [1, -1]) + @ m1 + @ eh1 + @ m2 + @ eh + @ backend.reshape(alpha1_jw, [-1, 1]) + )[0, 0] + ) + + return cmatrix + + def get_cmatrix(self) -> Tensor: + alpha1_jw = self.state + cmatrix = np.zeros([2 * self.L, 2 * self.L], dtype=complex) + for i in range(self.L): + for j in range(self.L): + op = openfermion.FermionOperator(f"{str(i)} {str(j)}^") + m = openfermion.get_sparse_operator(op, n_qubits=self.L).todense() + cmatrix[i, j] = backend.item( + ( + backend.reshape(backend.adjoint(alpha1_jw), [1, -1]) + @ m + @ backend.reshape(alpha1_jw, [-1, 1]) + )[0, 0] + ) + for i in range(self.L, 2 * self.L): + for j in range(self.L): + op = openfermion.FermionOperator(f"{str(i-self.L)}^ {str(j)}^") + m = openfermion.get_sparse_operator(op, n_qubits=self.L).todense() + cmatrix[i, j] = backend.item( + ( + backend.reshape(backend.adjoint(alpha1_jw), [1, -1]) + @ m + @ backend.reshape(alpha1_jw, [-1, 1]) + )[0, 0] + ) + for i in range(self.L): + for j in range(self.L, 2 * self.L): + op = openfermion.FermionOperator(f"{str(i)} {str(j-self.L)}") + m = openfermion.get_sparse_operator(op, n_qubits=self.L).todense() + cmatrix[i, j] = backend.item( + ( + backend.reshape(backend.adjoint(alpha1_jw), [1, -1]) + @ m + @ backend.reshape(alpha1_jw, [-1, 1]) + )[0, 0] + ) + for i in range(self.L, 2 * self.L): + for j in range(self.L, 2 * self.L): + op = openfermion.FermionOperator(f"{str(i-self.L)}^ {str(j-self.L)}") + m = openfermion.get_sparse_operator(op, n_qubits=self.L).todense() + cmatrix[i, j] = backend.item( + ( + backend.reshape(backend.adjoint(alpha1_jw), [1, -1]) + @ m + @ backend.reshape(alpha1_jw, [-1, 1]) + )[0, 0] + ) + return cmatrix + + def get_cmatrix_majorana(self) -> Tensor: + alpha1_jw = self.state + cmatrix = np.zeros([2 * self.L, 2 * self.L], dtype=complex) + for i in range(2 * self.L): + for j in range(2 * self.L): + op = openfermion.MajoranaOperator((i,)) * openfermion.MajoranaOperator( + (j,) + ) + if j % 2 + i % 2 == 1: + op *= -1 + # convention diff in jordan wigner + # c\dagger = X\pm iY + + op = openfermion.jordan_wigner(op) + m = openfermion.get_sparse_operator(op, n_qubits=self.L).todense() + cmatrix[i, j] = backend.item( + ( + backend.reshape(backend.adjoint(alpha1_jw), [1, -1]) + @ m + @ backend.reshape(alpha1_jw, [-1, 1]) + )[0, 0] + ) + + return cmatrix + + def expectation_4body(self, i: int, j: int, k: int, l: int) -> Tensor: + s = "" + if i < self.L: + s += str(i) + "" + else: + s += str(i - self.L) + "^ " + if j < self.L: + s += str(j) + "" + else: + s += str(j - self.L) + "^ " + if k < self.L: + s += str(k) + "" + else: + s += str(k - self.L) + "^ " + if l < self.L: + s += str(l) + "" + else: + s += str(l - self.L) + "^ " + op = openfermion.FermionOperator(s) + m = openfermion.get_sparse_operator(op, n_qubits=self.L).todense() + return ( + npb.reshape(npb.adjoint(self.state), [1, -1]) + @ m + @ npb.reshape(self.state, [-1, 1]) + )[0, 0] + + def entropy(self, subsystems_to_trace_out: Optional[List[int]] = None) -> Tensor: + rm = quantum.reduced_density_matrix(self.state, subsystems_to_trace_out) # type: ignore + return quantum.entropy(rm) + + def renyi_entropy(self, n: int, subsystems_to_trace_out: List[int]) -> Tensor: + rm = quantum.reduced_density_matrix(self.state, subsystems_to_trace_out) + return quantum.renyi_entropy(rm, n) + + def overlap(self, other: "FGSTestSimulator") -> Tensor: + return backend.tensordot(backend.conj(self.state), other.state, 1) + + def get_dm(self) -> Tensor: + s = backend.reshape(self.state, [-1, 1]) + return s @ backend.adjoint(s) + + def product(self, other: "FGSTestSimulator") -> Tensor: + rho1 = self.get_dm() + rho2 = other.get_dm() + rho = rho1 @ rho2 + rho /= backend.trace(rho) + return rho + + def post_select(self, i: int, keep: int = 1) -> None: + c = Circuit(self.L, inputs=self.state) + c.post_select(i, keep) + s = c.state() + s /= backend.norm(s) + self.state = s + + def cond_measure(self, ind: int, status: float, with_prob: bool = False) -> Tensor: + p0 = self.get_cmatrix()[ind, ind] + prob = [p0, 1 - p0] + if status < p0: + self.post_select(ind, 0) + keep = 0 + else: + self.post_select(ind, 1) + keep = 1 + + if with_prob is False: + return keep + else: + return keep, prob diff --git a/tensorcircuit/gates.py b/tensorcircuit/gates.py index 87003112..412230e2 100644 --- a/tensorcircuit/gates.py +++ b/tensorcircuit/gates.py @@ -7,13 +7,13 @@ from functools import reduce, partial from typing import Any, Callable, Optional, Sequence, List, Union, Tuple from operator import mul +import warnings import numpy as np import tensornetwork as tn from scipy.stats import unitary_group -from .cons import backend, dtypestr, npdtype -from .backends import get_backend # type: ignore +from .cons import backend, dtypestr, npdtype, runtime_backend from .utils import arg_alias thismodule = sys.modules[__name__] @@ -256,22 +256,18 @@ def __init__( self.ctrl = ctrl def __call__(self, *args: Any, **kws: Any) -> Gate: - # m = array_to_tensor(self.m) + m1 = array_to_tensor(self.m) # m = backend.cast(m, dtypestr) - m = self.m.astype(npdtype) - return Gate(deepcopy(m), name=self.n) + m1 = backend.cast(m1, dtypestr) + return Gate(m1, name=self.n) def adjoint(self) -> "GateF": m = self.__call__() - npb = get_backend("numpy") - shape0 = npb.shape_tuple(m.tensor) - m0 = npb.reshapem(m.tensor) - ma = npb.adjoint(m0) - if np.allclose(m0, ma, atol=1e-5): - name = self.n - else: - name = self.n + "d" - ma = npb.reshape(ma, shape0) + shape0 = backend.shape_tuple(m.tensor) + m0 = backend.reshapem(m.tensor) + ma = backend.adjoint(m0) + name = self.n + "d" + ma = backend.reshape(ma, shape0) return GateF(ma, name, self.ctrl) # TODO(@refraction-ray): adjoint gate convention finally determined @@ -359,15 +355,14 @@ def __call__(self, *args: Any, **kws: Any) -> Gate: def adjoint(self) -> "GateVF": def f(*args: Any, **kws: Any) -> Gate: m = self.__call__(*args, **kws) - npb = get_backend("numpy") - shape0 = npb.shape_tuple(m.tensor) - m0 = npb.reshapem(m.tensor) - ma = npb.adjoint(m0) - if np.allclose(m0, ma, atol=1e-5): - name = self.n - else: - name = self.n + "d" - ma = npb.reshape(ma, shape0) + shape0 = backend.shape_tuple(m.tensor) + m0 = backend.reshapem(m.tensor) + ma = backend.adjoint(m0) + # if np.allclose(m0, ma, atol=1e-5): + # name = self.n + # else: + name = self.n + "d" + ma = backend.reshape(ma, shape0) return Gate(ma, name) return GateVF(f, self.n + "d", self.ctrl) @@ -394,6 +389,9 @@ def meta_gate() -> None: setattr(thismodule, n + "_gate", temp) setattr(thismodule, n, temp) + with runtime_backend("numpy"): # backward compatibility + setattr(thismodule, "pauli_gates", [i(), x(), y(), z()]) # type: ignore + meta_gate() @@ -478,6 +476,7 @@ def phase_gate(theta: float = 0) -> Gate: :return: phase gate :rtype: Gate """ + theta = array_to_tensor(theta) i00, i11 = array_to_tensor(np.array([[1, 0], [0, 0]]), np.array([[0, 0], [0, 1]])) unitary = i00 + backend.exp(1.0j * theta) * i11 return Gate(unitary) @@ -527,6 +526,7 @@ def u_gate(theta: float = 0, phi: float = 0, lbd: float = 0) -> Gate: :return: _description_ :rtype: Gate """ + theta, phi, lbd = array_to_tensor(theta, phi, lbd) i00, i01, i10, i11 = array_to_tensor( np.array([[1, 0], [0, 0]]), np.array([[0, 1], [0, 0]]), @@ -547,7 +547,7 @@ def r_gate(theta: float = 0, alpha: float = 0, phi: float = 0) -> Gate: General single qubit rotation gate .. math:: - R(\theta, \alpha, \phi) = j \cos(\theta) I + R(\theta, \alpha, \phi) = \cos(\theta) I - j \cos(\phi) \sin(\alpha) \sin(\theta) X - j \sin(\phi) \sin(\alpha) \sin(\theta) Y - j \sin(\theta) \cos(\alpha) Z @@ -675,9 +675,8 @@ def random_single_qubit_gate() -> Gate: :rtype: Gate """ # Get the random parameters - theta, alpha, phi = np.random.rand(3) * 2 * np.pi # type: ignore - - return r_gate(theta, alpha, phi) # type: ignore + theta, alpha, phi = np.random.rand(3) * 2 * np.pi + return r_gate(theta, alpha, phi) # rs = random_single_qubit_gate # deprecated @@ -811,7 +810,7 @@ def exponential_gate(unitary: Tensor, theta: float, name: str = "none") -> Gate: :return: Exponential Gate :rtype: Gate """ - theta = num_to_tensor(theta) + theta, unitary = num_to_tensor(theta, unitary) mat = backend.expm(-backend.i() * theta * unitary) dimension = reduce(mul, mat.shape) nolegs = int(np.log(dimension) / np.log(2)) @@ -893,7 +892,10 @@ def multicontrol_gate(unitary: Tensor, ctrl: Union[int, Sequence[int]] = 1) -> O eps = 1e-5 if isinstance(ctrl, int): ctrl = [ctrl] - if int(ctrl[0] + eps) == 1: + with warnings.catch_warnings(): + warnings.simplefilter("ignore", np.ComplexWarning) + ctrl0_int = int(ctrl[0] + eps) + if ctrl0_int == 1: leftend = np.zeros([2, 2, 2]) leftend[1, 1, 0] = 1 leftend[0, 0, 1] = 1 diff --git a/tensorcircuit/interfaces/__init__.py b/tensorcircuit/interfaces/__init__.py index b9d23a09..1f8cc608 100644 --- a/tensorcircuit/interfaces/__init__.py +++ b/tensorcircuit/interfaces/__init__.py @@ -12,5 +12,8 @@ ) from .numpy import numpy_interface, np_interface from .scipy import scipy_interface, scipy_optimize_interface -from .torch import torch_interface, pytorch_interface +from .torch import torch_interface, pytorch_interface, torch_interface_kws from .tensorflow import tensorflow_interface, tf_interface + + +# TODO(@refraction-ray): jax interface using puer_callback and custom_vjp diff --git a/tensorcircuit/interfaces/scipy.py b/tensorcircuit/interfaces/scipy.py index dbebd431..fe45a1dd 100644 --- a/tensorcircuit/interfaces/scipy.py +++ b/tensorcircuit/interfaces/scipy.py @@ -80,8 +80,8 @@ def scipy_vg(*args: Any, **kws: Any) -> Tuple[Tensor, Tensor]: scipy_vs = general_args_to_numpy(vs) gs = backend.reshape(gs, [-1]) scipy_gs = general_args_to_numpy(gs) - scipy_vs = scipy_vs.astype(np.float64) - scipy_gs = scipy_gs.astype(np.float64) + scipy_vs = scipy_vs.real.astype(np.float64) + scipy_gs = scipy_gs.real.astype(np.float64) return scipy_vs, scipy_gs return scipy_vg @@ -97,7 +97,7 @@ def scipy_v(*args: Any, **kws: Any) -> Tensor: scipy_args = tuple(scipy_args) vs = fun(*scipy_args, **kws) scipy_vs = general_args_to_numpy(vs) - scipy_vs = scipy_vs.astype(np.float64) + scipy_vs = scipy_vs.real.astype(np.float64) return scipy_vs return scipy_v diff --git a/tensorcircuit/interfaces/tensorflow.py b/tensorcircuit/interfaces/tensorflow.py index 3febabc3..7166e4e7 100644 --- a/tensorcircuit/interfaces/tensorflow.py +++ b/tensorcircuit/interfaces/tensorflow.py @@ -58,7 +58,7 @@ def f(params): f = tc.interfaces.tf_interface(f, ydtype=tf.float32, jit=True) tfb = tc.get_backend("tensorflow") - grads = tfb.jit(tfb.grad(f))(tc.get_backend("tensorflow").ones([2])) + grads = tfb.jit(tfb.grad(f))(tfb.ones([2])) :param fun: The quantum function with tensor in and tensor out :type fun: Callable[..., Any] @@ -92,7 +92,7 @@ def vjp_fun(*xv: Tensor) -> Tuple[Tensor, Tensor]: x = x[0] if len(v) == 1: v = v[0] - return backend.vjp(fun, x, v) # type: ignore + return backend.vjp(fun, x, v) if jit is True: vjp_fun = backend.jit(vjp_fun) diff --git a/tensorcircuit/interfaces/tensortrans.py b/tensorcircuit/interfaces/tensortrans.py index efd987c7..c258c481 100644 --- a/tensorcircuit/interfaces/tensortrans.py +++ b/tensorcircuit/interfaces/tensortrans.py @@ -8,7 +8,7 @@ from ..cons import backend, dtypestr from ..gates import Gate from ..quantum import QuOperator -from ..backends import get_backend # type: ignore +from ..backends import get_backend Tensor = Any Array = Any @@ -44,14 +44,18 @@ def which_backend(a: Tensor, return_backend: bool = True) -> Any: def tensor_to_numpy(t: Tensor) -> Array: if isinstance(t, int) or isinstance(t, float): return t + if t is None: + return return which_backend(t).numpy(t) def tensor_to_backend_jittable(t: Tensor) -> Tensor: - if which_backend(t, return_backend=False) == backend.name: - return t if isinstance(t, int) or isinstance(t, float): return t + if isinstance(t, QuOperator): + return t + if which_backend(t, return_backend=False) == backend.name: + return t return backend.convert_to_tensor(which_backend(t).numpy(t)) @@ -250,6 +254,8 @@ def f(a, b, c, d): :return: The wrapped function :rtype: Callable[..., Any] """ + from ..channels import KrausList + if isinstance(argnums, int): argnumslist = [argnums] else: @@ -260,20 +266,42 @@ def wrapper(*args: Any, **kws: Any) -> Any: nargs = [] for i, arg in enumerate(args): if i in argnumslist: + if isinstance(arg, KrausList): + is_krauslist = True + name = arg.name + is_unitary = arg.is_unitary + arg = list(arg) + else: + is_krauslist = False + if gate_to_tensor: arg = backend.tree_map( partial(gate_to_matrix, is_reshapem=gate_as_matrix), arg ) - if qop_to_matrix: + if qop_as_matrix: arg = backend.tree_map( partial(qop_to_matrix, is_reshapem=qop_as_matrix), arg ) arg = backend.tree_map(tensor_to_backend_jittable, arg) + # arg = backend.tree_map(backend.convert_to_tensor, arg) + def _cast(a: Tensor, dtype: str) -> Tensor: + if isinstance(a, QuOperator): + return a + return backend.cast(a, dtype) + + def _reshapem(a: Tensor) -> Tensor: + if isinstance(a, QuOperator): + return a + return backend.reshapem(a) + if cast_dtype: - arg = backend.tree_map(partial(backend.cast, dtype=dtypestr), arg) + arg = backend.tree_map(partial(_cast, dtype=dtypestr), arg) if tensor_as_matrix: arg = backend.tree_map(backend.reshapem, arg) + + if is_krauslist is True: + arg = KrausList(arg, name, is_unitary) nargs.append(arg) return f(*nargs, **kws) diff --git a/tensorcircuit/interfaces/torch.py b/tensorcircuit/interfaces/torch.py index 698f4990..8fcdb2e2 100644 --- a/tensorcircuit/interfaces/torch.py +++ b/tensorcircuit/interfaces/torch.py @@ -2,7 +2,8 @@ Interface wraps quantum function as a torch function """ -from typing import Any, Callable, Tuple +from typing import Any, Callable, Dict, Tuple +from functools import partial from ..cons import backend from ..utils import is_sequence @@ -11,6 +12,8 @@ Tensor = Any +# TODO(@refraction-ray): new paradigm compatible with torch functional trasnformation + def torch_interface( fun: Callable[..., Any], jit: bool = False, enable_dlpack: bool = False @@ -56,13 +59,13 @@ def f(params): import torch def vjp_fun(x: Tensor, v: Tensor) -> Tuple[Tensor, Tensor]: - return backend.vjp(fun, x, v) # type: ignore + return backend.vjp(fun, x, v) if jit is True: fun = backend.jit(fun) vjp_fun = backend.jit(vjp_fun) - class Fun(torch.autograd.Function): # type: ignore + class Fun(torch.autograd.Function): @staticmethod def forward(ctx: Any, *x: Any) -> Any: # type: ignore # ctx.xdtype = [xi.dtype for xi in x] @@ -108,7 +111,55 @@ def backward(ctx: Any, *grad_y: Any) -> Any: return r # currently, memory transparent dlpack in these ML framework has broken support on complex dtypes - return Fun.apply # type: ignore + return Fun.apply pytorch_interface = torch_interface + + +def torch_interface_kws( + f: Callable[..., Any], jit: bool = True, enable_dlpack: bool = False +) -> Callable[..., Any]: + """ + similar to py:meth:`tensorcircuit.interfaces.torch.torch_interface`, + but now the interface support static arguments for function ``f``, + which is not a tensor and can be used with keyword arguments + + :Example: + + .. code-block:: python + + tc.set_backend("tensorflow") + + def f(tensor, integer): + r = 0. + for i in range(integer): + r += tensor + return r + + fnew = tc.interfaces.torch_interface_kws(f) + + print(fnew(torch.ones([2]), integer=3)) + print(fnew(torch.ones([2]), integer=4)) + + :param f: _description_ + :type f: Callable[..., Any] + :param jit: _description_, defaults to True + :type jit: bool, optional + :param enable_dlpack: _description_, defaults to False + :type enable_dlpack: bool, optional + :return: _description_ + :rtype: Callable[..., Any] + """ + cache_dict: Dict[Tuple[Any, ...], Callable[..., Any]] = {} + + def wrapper(*args: Any, **kws: Any) -> Any: + key = tuple([(k, v) for k, v in kws.items()]) + if key not in cache_dict: + fnew = torch_interface( + partial(f, **kws), jit=jit, enable_dlpack=enable_dlpack + ) + cache_dict[key] = fnew + return cache_dict[key](*args) + + return wrapper diff --git a/tensorcircuit/keras.py b/tensorcircuit/keras.py index e09ba532..ed947b8c 100644 --- a/tensorcircuit/keras.py +++ b/tensorcircuit/keras.py @@ -7,9 +7,9 @@ import numpy as np import tensorflow as tf from tensorflow.keras.layers import Layer -from tensorflow.keras import initializers, constraints +from tensorflow.keras import initializers, constraints, regularizers -from .cons import rdtypestr, backend # type: ignore +from .cons import rdtypestr, backend # @tf.keras.utils.register_keras_serializable( # package="tensorcircuit" @@ -20,9 +20,10 @@ class QuantumLayer(Layer): # type: ignore def __init__( self, f: Callable[..., Any], - weights_shape: Sequence[Tuple[int, ...]], + weights_shape: Optional[Sequence[Tuple[int, ...]]] = None, initializer: Union[Text, Sequence[Text]] = "glorot_uniform", constraint: Optional[Union[Text, Sequence[Text]]] = None, + regularizer: Optional[Union[Text, Sequence[Text]]] = None, **kwargs: Any ) -> None: """ @@ -38,12 +39,17 @@ def __init__( :type initializer: Union[Text, Sequence[Text]], optional :param constraint: [description], defaults to None :type constraint: Optional[Union[Text, Sequence[Text]]], optional + :param initializer: The regularizer of the weights, defaults to None + :type initializer: Union[Text, Sequence[Text]], optional """ super().__init__(**kwargs) - if isinstance(weights_shape[0], int): + if weights_shape is not None and isinstance(weights_shape[0], int): weights_shape = [weights_shape] - self.number_weights = len(weights_shape) + if weights_shape is not None: + self.number_weights = len(weights_shape) + else: + self.number_weights = 0 self.f = f self.weights_shape = weights_shape if not (isinstance(initializer, list) or isinstance(initializer, tuple)): @@ -58,25 +64,38 @@ def __init__( constraints.get(item) if isinstance(item, str) else item for item in constraint ] + if not (isinstance(regularizer, list) or isinstance(regularizer, tuple)): + regularizer = [regularizer for _ in range(self.number_weights)] # type: ignore + self.regularizer = [ + regularizers.get(item) if isinstance(item, str) else item + for item in regularizer + ] def build(self, input_shape: Optional[List[int]] = None) -> None: if input_shape is None: input_shape = [1, 1] super().build(input_shape) self.pqc_weights = [] - for i, (shape, init, cst) in enumerate( - zip(self.weights_shape, self.initializer, self.constraint) - ): - self.pqc_weights.append( - self.add_weight( - name="PQCweights%s" % i, - shape=shape, - dtype=getattr(np, rdtypestr), # type: ignore - trainable=True, - initializer=init, - constraint=cst, + if self.weights_shape is not None: + for i, (shape, init, cst, reg) in enumerate( + zip( + self.weights_shape, + self.initializer, + self.constraint, + self.regularizer, + ) + ): + self.pqc_weights.append( + self.add_weight( + name="PQCweights%s" % i, + shape=shape, + dtype=getattr(np, rdtypestr), + trainable=True, + initializer=init, + constraint=cst, + regularizer=reg, + ) ) - ) @tf.function # type: ignore def call( @@ -123,6 +142,42 @@ def call( KerasLayer = QuantumLayer +class HardwareLayer(QuantumLayer): + """ + Keras Layer wrapping quantum function with cloud qpu access + (using :py:mod:`tensorcircuit.cloud` module) + """ + + @tf.autograph.experimental.do_not_convert # type: ignore + def call( + self, + inputs: tf.Tensor, + training: Optional[bool] = None, + mask: Optional[tf.Tensor] = None, + **kwargs: Any + ) -> tf.Tensor: + if inputs is None: # not possible + result = self.f(*self.pqc_weights, **kwargs) + elif ( + len( + backend.tree_map(backend.shape_tuple, backend.tree_flatten(inputs))[0][ + 0 + ] + ) + == 1 + ): + result = self.f(inputs, *self.pqc_weights, **kwargs) + else: + result = [] + for inp in inputs: + result.append(self.f(inp, *self.pqc_weights, **kwargs)) + result = tf.stack(result) + return result + + +KerasHardwareLayer = HardwareLayer + + def output_asis_loss(y_true: tf.Tensor, y_pred: tf.Tensor) -> tf.Tensor: """ The keras loss function that directly taking the model output as the loss. diff --git a/tensorcircuit/mps_base.py b/tensorcircuit/mps_base.py index 146493a7..d8234c2b 100644 --- a/tensorcircuit/mps_base.py +++ b/tensorcircuit/mps_base.py @@ -1,12 +1,21 @@ """ FiniteMPS from tensornetwork with bug fixed """ + # pylint: disable=invalid-name from typing import Any, Optional, List, Sequence import numpy as np -from tensornetwork.linalg.node_linalg import conj + +try: + from tensornetwork.linalg.node_linalg import conj + + # google tn +except ModuleNotFoundError: + # tc tn + from tensornetwork.matrixproductstates.utils import conj + import tensornetwork as tn import tensornetwork.ncon_interface as ncon from tensornetwork.network_components import Node @@ -19,6 +28,7 @@ class FiniteMPS(tn.FiniteMPS): # type: ignore center_position: Optional[int] + # TODO(@SUSYUSTC): Maybe more functions can be put here to disentangle with circuits def apply_two_site_gate( self, @@ -126,7 +136,6 @@ def set_center_position(site: int) -> None: if center_position is None: center_position = site1 - if use_svd: U, S, V, tw = self.backend.svd( tensor, @@ -135,6 +144,7 @@ def set_center_position(site: int) -> None: max_truncation_error=max_truncation_err, relative=relative, ) + # Note: fix the center position bug here if center_position == site2: left_tensor = U @@ -264,7 +274,6 @@ def measure_two_body_correlator( c = [] if len(left_sites) > 0: - A = Node(self.tensors[site1], backend=self.backend) O1 = Node(op1, backend=self.backend) conj_A = conj(A) diff --git a/tensorcircuit/mpscircuit.py b/tensorcircuit/mpscircuit.py index 0c6bee10..7fc4052e 100644 --- a/tensorcircuit/mpscircuit.py +++ b/tensorcircuit/mpscircuit.py @@ -1,6 +1,7 @@ """ Quantum circuit: MPS state simulator """ + # pylint: disable=invalid-name from functools import reduce @@ -52,6 +53,11 @@ def split_tensor( return backend.qr(tensor) # type: ignore +# AD + MPS can lead to numerical stability issue +# E ./tensorflow/core/kernels/linalg/svd_op_impl.h:110] Eigen::BDCSVD failed with error code 3 +# this is now solved by setting os.environ["TC_BACKENDS_TENSORFLOW_BACKEND__SVD_TF_EPS"]="10" + + class MPSCircuit(AbstractCircuit): """ ``MPSCircuit`` class. @@ -139,12 +145,12 @@ def __init__( self._nqubits = nqubits self._fidelity = 1.0 self._qir: List[Dict[str, Any]] = [] + self._extra_qir: List[Dict[str, Any]] = [] # `MPSCircuit` does not has `replace_inputs` like `Circuit` # because the gates are immediately absorted into the MPS when applied, # so it is impossible to remember the initial structure - # @property def get_bond_dimensions(self) -> Tensor: """ Get the MPS bond dimensions @@ -154,7 +160,6 @@ def get_bond_dimensions(self) -> Tensor: """ return self._mps.bond_dimensions - # @property def get_tensors(self) -> List[Tensor]: """ Get the MPS tensors @@ -164,7 +169,6 @@ def get_tensors(self) -> List[Tensor]: """ return self._mps.tensors # type: ignore - # @property def get_center_position(self) -> Optional[int]: """ Get the center position of the MPS @@ -206,15 +210,7 @@ def apply_single_gate(self, gate: Gate, index: int) -> None: :param index: Qubit index of the gate :type index: int """ - # TODO(@SUSYUSTC): make it jittable - tensor = backend.numpy(gate.tensor) - prod = tensor.dot(tensor.T.conj()) - I = np.eye(prod.shape[0]) - err_max = np.max(np.abs(prod - I)) - # TODO(@SUSYUSTC): change this number to 1e-12 after the dtype bug fixed - is_unitary = err_max < 1e-6 - if not is_unitary: - self.position(index) + self.position(index) self._mps.apply_one_site_gate(gate.tensor, index) def apply_adjacent_double_gate( @@ -299,6 +295,11 @@ def apply_double_gate( :param index2: The second qubit index of the gate :type index2: int """ + assert index1 != index2 + if index1 > index2: + newgate = Gate(backend.transpose(gate.tensor, [1, 0, 3, 2])) + self.apply_double_gate(newgate, index2, index1) + return if split is None: split = self.split # apply N SWAP gates, the required gate, N SWAP gates sequentially on adjacent gates @@ -338,16 +339,19 @@ def gate_to_MPO( # --i--I--j-- = \delta_{i,j} \delta_{a,b} # | # b + + # index must be ordered + assert np.all(np.diff(index) > 0) index_left = np.min(index) if isinstance(gate, tn.Node): gate = backend.copy(gate.tensor) index = np.array(index) - index_left nindex = len(index) # transform gate from (in1, in2, ..., out1, out2 ...) to + # (in1, out1, in2, out2, ...) order = tuple(np.arange(2 * nindex).reshape((2, nindex)).T.flatten()) shape = (4,) * nindex gate = backend.reshape(backend.transpose(gate, order), shape) - # (in1, out1, in2, out2, ...) argsort = np.argsort(index) # reorder the gate according to the site positions gate = backend.transpose(gate, tuple(argsort)) @@ -517,9 +521,19 @@ def apply_nqubit_gate( *index: int, split: Optional[Dict[str, Any]] = None, ) -> None: + # TODO(@SUSYUSTC): jax autograd is wrong on this function """ Apply a n-qubit gate by transforming the gate to MPO """ + ordered = np.all(np.diff(index) > 0) + if not ordered: + order = np.argsort(index) + order2 = order + len(index) + order_all = order.tolist() + order2.tolist() + newgate = backend.transpose(gate.tensor, order_all) + index = np.sort(index).tolist() + self.apply_nqubit_gate(newgate, *index, split=split) + return if split is None: split = self.split MPO, index_left = self.gate_to_MPO(gate, *index) @@ -588,10 +602,14 @@ def mid_measurement(self, index: int, keep: int = 0) -> None: """ # normalization not guaranteed assert keep in [0, 1] - discard = 1 - keep - self.position(index) - # TODO(@SUSYUSTC): make it compatible with other backends - self._mps.tensors[index][:, discard, :] = 0 + gate = backend.zeros((2, 2), dtype=dtypestr) + gate = backend.scatter( + gate, + backend.convert_to_tensor([[keep, keep]]), + backend.convert_to_tensor(np.array([1.0], dtype=dtypestr)), + ) + gate = Gate(gate) + self.apply_single_gate(gate, index) def is_valid(self) -> bool: """ @@ -802,7 +820,7 @@ def slice(self, begin: int, end: int) -> "MPSCircuit": ) return mps - def expectation( # type: ignore + def expectation( self, *ops: Tuple[Gate, List[int]], reuse: bool = True, @@ -810,6 +828,7 @@ def expectation( # type: ignore conj: bool = True, normalize: bool = False, split: Optional[Dict[str, Any]] = None, + **kws: Any, ) -> Tensor: """ Compute the expectation of corresponding operators in the form of tensor. @@ -911,6 +930,7 @@ def measure( :return: The sample output and probability (optional) of the quantum line. :rtype: Tuple[Tensor, Tensor] """ + """ is_sorted = np.all(np.sort(index) == np.array(index)) if not is_sorted: order = backend.convert_to_tensor(np.argsort(index).tolist()) @@ -919,6 +939,7 @@ def measure( ) return backend.convert_to_tensor([sample[i] for i in order]), p # set the center to the left side, then gradually move to the right and do measurement at sites + """ mps = self.copy() up = backend.convert_to_tensor(np.array([1, 0]).astype(dtypestr)) down = backend.convert_to_tensor(np.array([0, 1]).astype(dtypestr)) diff --git a/tensorcircuit/noisemodel.py b/tensorcircuit/noisemodel.py new file mode 100644 index 00000000..9a1d85ac --- /dev/null +++ b/tensorcircuit/noisemodel.py @@ -0,0 +1,356 @@ +""" +General Noise Model Construction. +""" + +import logging +from functools import partial +from typing import Any, Sequence, Optional, List, Dict, Tuple, Callable, Union + +import tensornetwork as tn + +from .abstractcircuit import AbstractCircuit +from . import gates +from . import Circuit, DMCircuit +from .cons import backend +from .channels import KrausList + +Gate = gates.Gate +Tensor = Any +logger = logging.getLogger(__name__) + + +class NoiseConf: + """ + ``Noise Configuration`` class. + + .. code-block:: python + + error1 = tc.channels.generaldepolarizingchannel(0.1, 1) + error2 = tc.channels.thermalrelaxationchannel(300, 400, 100, "ByChoi", 0) + readout_error = [[0.9, 0.75], [0.4, 0.7]] + + noise_conf = NoiseConf() + noise_conf.add_noise("x", error1) + noise_conf.add_noise("h", [error1, error2], [[0], [1]]) + noise_conf.add_noise("readout", readout_error) + """ + + def __init__(self) -> None: + """ + Establish a noise configuration. + """ + # description, condition and Kraus operators + self.nc: List[Tuple[Any, Callable[[Dict[str, Any]], bool], KrausList]] = [] + self.has_quantum = False + self.readout_error = None + + def add_noise( + self, + gate_name: str, + kraus: Union[KrausList, Sequence[KrausList]], + qubit: Optional[Sequence[Any]] = None, + ) -> None: + """ + Add noise channels on specific gates and specific qubits in form of Kraus operators. + + :param gate_name: noisy gate + :type gate_name: str + :param kraus: noise channel + :type kraus: Sequence[Gate] + :param qubit: the list of noisy qubit, defaults to None, indicating applying the noise channel on all qubits + :type qubit: Optional[Sequence[Any]], optional + """ + if gate_name == "readout": + assert qubit is None + self.readout_error = kraus # type:ignore + return + + gate_name = AbstractCircuit.standardize_gate(gate_name) + description: Any + + if qubit is None: + description = gate_name + + def condition(d: Dict[str, Any]) -> bool: + return str(d["gatef"].n) == gate_name + + if not isinstance(kraus, KrausList): + assert len(kraus) == 1 + krauslist = kraus[0] + else: + krauslist = kraus + self.nc.append((description, condition, krauslist)) + else: + for idx, krauslist in zip(qubit, kraus): + description = (gate_name, idx) + + # https://stackoverflow.com/questions/1107210/python-create-function-in-a-loop-capturing-the-loop-variable + def condition( # type:ignore + d: Dict[str, Any], _idx: Sequence[Any] + ) -> bool: + # avoid bad black style because the long is too long + b1 = d["gatef"].n == gate_name + b2 = tuple(d["index"]) == tuple(_idx) + return b1 and b2 + + condition = partial(condition, _idx=idx) + self.nc.append((description, condition, krauslist)) + + self.has_quantum = True + + def add_noise_by_condition( + self, + condition: Callable[[Dict[str, Any]], bool], + kraus: KrausList, + name: Optional[Any] = "custom", + ) -> None: + """ + Add noise based on specified condition + + :param condition: a function to decide if the noise should be added to the qir. + :type condition: Callable[[Dict[str, Any]], bool] + :param kraus: the error channel + :type kraus: KrausList + :param name: the name of the condition. A metadata that does not affect the numerics. + :type name: Any + """ + self.nc.append((name, condition, kraus)) + + def channel_count(self, c: Circuit) -> int: + """ + Count the total number of channels in a given circuit + + :param c: the circuit to be counted + :type c: Circuit + :return: the count + :rtype: int + """ + count = 0 + for d in c.to_qir(): + for _, condition, _ in self.nc: + if condition(d): + count += 1 + return count + + +def apply_qir_with_noise( + c: Any, + qir: List[Dict[str, Any]], + noise_conf: NoiseConf, + status: Optional[Tensor] = None, +) -> Any: + """ + + :param c: A newly defined circuit + :type c: AbstractCircuit + :param qir: The qir of the clean circuit + :type qir: List[Dict[str, Any]] + :param noise_conf: Noise Configuration + :type noise_conf: NoiseConf + :param status: The status for Monte Carlo sampling, defaults to None + :type status: 1D Tensor, optional + :return: A newly constructed circuit with noise + :rtype: AbstractCircuit + """ + quantum_index = 0 + for d in qir: + d["name"] = AbstractCircuit.standardize_gate(d["name"]) + + if "parameters" not in d: # paramized gate + c.apply_general_gate_delayed(d["gatef"], d["name"], d["mpo"])( + c, *d["index"] + ) + else: + c.apply_general_variable_gate_delayed(d["gatef"], d["name"], d["mpo"])( + c, *d["index"], **d["parameters"] + ) + + if isinstance(c, DMCircuit): + for _, condition, krauslist in noise_conf.nc: + if condition(d): + c.general_kraus(krauslist, *d["index"]) + else: + for _, condition, krauslist in noise_conf.nc: + if condition(d): + if krauslist.is_unitary: + c.unitary_kraus( + krauslist, + *d["index"], + status=status[quantum_index], # type: ignore + ) + else: + c.general_kraus( + krauslist, + *d["index"], + status=status[quantum_index], # type: ignore + ) + quantum_index += 1 + + return c + + +def circuit_with_noise( + c: AbstractCircuit, noise_conf: NoiseConf, status: Optional[Tensor] = None +) -> Any: + """Noisify a clean circuit. + + :param c: A clean circuit + :type c: AbstractCircuit + :param noise_conf: Noise Configuration + :type noise_conf: NoiseConf + :param status: The status for Monte Carlo sampling, defaults to None + :type status: 1D Tensor, optional + :return: A newly constructed circuit with noise + :rtype: AbstractCircuit + """ + qir = c.to_qir() + cnew: AbstractCircuit + if isinstance(c, DMCircuit): + cnew = DMCircuit(**c.circuit_param) + else: + cnew = Circuit(**c.circuit_param) + cnew = apply_qir_with_noise(cnew, qir, noise_conf, status) + return cnew + + +def sample_expectation_ps_noisfy( + c: Any, + x: Optional[Sequence[int]] = None, + y: Optional[Sequence[int]] = None, + z: Optional[Sequence[int]] = None, + noise_conf: Optional[NoiseConf] = None, + nmc: int = 1000, + shots: Optional[int] = None, + statusc: Optional[Tensor] = None, + status: Optional[Tensor] = None, + **kws: Any, +) -> Tensor: + """ + Calculate sample_expectation_ps with noise configuration. + + :param c: The clean circuit + :type c: Any + :param x: sites to apply X gate, defaults to None + :type x: Optional[Sequence[int]], optional + :param y: sites to apply Y gate, defaults to None + :type y: Optional[Sequence[int]], optional + :param z: sites to apply Z gate, defaults to None + :type z: Optional[Sequence[int]], optional + :param noise_conf: Noise Configuration, defaults to None + :type noise_conf: Optional[NoiseConf], optional + :param nmc: repetition time for Monte Carlo sampling for noisfy calculation, defaults to 1000 + :type nmc: int, optional + :param shots: number of measurement shots, defaults to None, indicating analytical result + :type shots: Optional[int], optional + :param statusc: external randomness given by tensor uniformly from [0, 1], defaults to None, + used for noisfy circuit sampling + :type statusc: Optional[Tensor], optional + :param status: external randomness given by tensor uniformly from [0, 1], defaults to None, + used for measurement sampling + :type status: Optional[Tensor], optional + :return: sample expectation value with noise + :rtype: Tensor + """ + + if noise_conf is None: + noise_conf = NoiseConf() + + readout_error = noise_conf.readout_error + + if noise_conf.has_quantum: + # density matrix + if isinstance(c, DMCircuit): + cnoise = circuit_with_noise(c, noise_conf) + return cnoise.sample_expectation_ps( + x=x, y=y, z=z, shots=shots, status=status, readout_error=readout_error + ) + + # monte carlo + else: + + def mcsim(statusc: Optional[Tensor], status: Optional[Tensor]) -> Tensor: + cnoise = circuit_with_noise(c, noise_conf, statusc) # type: ignore + return cnoise.sample_expectation_ps( + x=x, + y=y, + z=z, + shots=shots, + status=status, + readout_error=readout_error, + ) + + mcsim_vmap = backend.vmap(mcsim, vectorized_argnums=(0, 1)) + if statusc is None: + num_quantum = noise_conf.channel_count(c) + statusc = backend.implicit_randu([nmc, num_quantum]) + + if status is None: + if shots is None: + status = backend.implicit_randu([nmc, 1]) + else: + status = backend.implicit_randu([nmc, shots]) + + value = backend.mean(mcsim_vmap(statusc, status)) + return value + + else: + value = c.sample_expectation_ps( + x=x, y=y, z=z, shots=shots, status=status, readout_error=readout_error + ) + return value + + +def expectation_noisfy( + c: Any, + *ops: Tuple[tn.Node, List[int]], + noise_conf: Optional[NoiseConf] = None, + nmc: int = 1000, + status: Optional[Tensor] = None, + **kws: Any, +) -> Tensor: + """ + Calculate expectation value with noise configuration. + + :param c: The clean circuit + :type c: Any + :param noise_conf: Noise Configuration, defaults to None + :type noise_conf: Optional[NoiseConf], optional + :param nmc: repetition time for Monte Carlo sampling for noisfy calculation, defaults to 1000 + :type nmc: int, optional + :param status: external randomness given by tensor uniformly from [0, 1], defaults to None, + used for noisfy circuit sampling + :type status: Optional[Tensor], optional + :return: expectation value with noise + :rtype: Tensor + """ + + if noise_conf is None: + noise_conf = NoiseConf() + + if noise_conf.readout_error is not None: + logger.warning("expectation_ps_noisfy can't support readout error.") + + if noise_conf.has_quantum: + # density matrix + if isinstance(c, DMCircuit): + cnoise = circuit_with_noise(c, noise_conf) + return cnoise.expectation(*ops, **kws) + + # monte carlo + else: + + def mcsim(status: Optional[Tensor]) -> Tensor: + cnoise = circuit_with_noise(c, noise_conf, status) # type: ignore + return cnoise.expectation(*ops, **kws) + + mcsim_vmap = backend.vmap(mcsim, vectorized_argnums=0) + if status is None: + num_quantum = noise_conf.channel_count(c) + status = backend.implicit_randu([nmc, num_quantum]) + + value = backend.mean(mcsim_vmap(status)) + + return value + + else: + return c.expectation(*ops, **kws) diff --git a/tensorcircuit/quantum.py b/tensorcircuit/quantum.py index 47d7736b..23bb926c 100644 --- a/tensorcircuit/quantum.py +++ b/tensorcircuit/quantum.py @@ -7,11 +7,13 @@ import tensorcircuit.quantum as qu """ + # pylint: disable=invalid-name -from functools import reduce, partial import logging -from operator import or_, mul, matmul +import os +from functools import partial, reduce +from operator import matmul, mul, or_ from typing import ( Any, Callable, @@ -26,19 +28,18 @@ ) import numpy as np -from tensornetwork.network_components import AbstractNode, Node, Edge, connect -from tensornetwork.network_components import CopyNode -from tensornetwork.network_operations import get_all_nodes, copy, reachable -from tensornetwork.network_operations import get_subgraph_dangling, remove_node - -try: - import tensorflow as tf -except ImportError: - pass +from tensornetwork.network_components import AbstractNode, CopyNode, Edge, Node, connect +from tensornetwork.network_operations import ( + copy, + get_all_nodes, + get_subgraph_dangling, + reachable, + remove_node, +) from .cons import backend, contractor, dtypestr, npdtype, rdtypestr -from .backends import get_backend # type: ignore -from .utils import is_m1mac, arg_alias +from .gates import Gate, num_to_tensor +from .utils import arg_alias Tensor = Any Graph = Any @@ -195,7 +196,7 @@ def check_spaces(edges_1: Sequence[Edge], edges_2: Sequence[Edge]) -> None: "with {} subsystems.".format(len(edges_1), len(edges_2)) ) - for (i, (e1, e2)) in enumerate(zip(edges_1, edges_2)): + for i, (e1, e2) in enumerate(zip(edges_1, edges_2)): if e1.dimension != e2.dimension: raise ValueError( "Hilbert-space mismatch on subsystems {}: Input " @@ -429,10 +430,10 @@ def check_network(self) -> None: there are no other dangling edges (except any specified in `ignore_edges`). If not, an exception is raised. """ - for (i, e) in enumerate(self.out_edges): + for i, e in enumerate(self.out_edges): if not e.is_dangling(): raise ValueError("Output edge {} is not dangling!".format(i)) - for (i, e) in enumerate(self.in_edges): + for i, e in enumerate(self.in_edges): if not e.is_dangling(): raise ValueError("Input edge {} is not dangling!".format(i)) for e in self.ignore_edges: @@ -511,7 +512,7 @@ def partial_trace(self, subsystems_to_trace_out: Collection[int]) -> "QuOperator check_spaces(in_edges_trace, out_edges_trace) nodes_dict, edge_dict = copy(self.nodes, False) - for (e1, e2) in zip(out_edges_trace, in_edges_trace): + for e1, e2 in zip(out_edges_trace, in_edges_trace): edge_dict[e1] = edge_dict[e1] ^ edge_dict[e2] # get leftover edges in the original order @@ -544,7 +545,7 @@ def __matmul__(self, other: Union["QuOperator", Tensor]) -> "QuOperator": nodes_dict2, edges_dict2 = copy(other.nodes, False) # connect edges to create network for the result - for (e1, e2) in zip(self.in_edges, other.out_edges): + for e1, e2 in zip(self.in_edges, other.out_edges): _ = edges_dict1[e1] ^ edges_dict2[e2] in_edges = [edges_dict2[e] for e in other.in_edges] @@ -711,7 +712,7 @@ def eval( return list(nodes)[0].tensor def eval_matrix(self, final_edge_order: Optional[Sequence[Edge]] = None) -> Tensor: - """ + r""" Contracts the tensor network in place and returns the final tensor in two dimentional matrix. The default ordering for the axes of the final tensor is: @@ -1021,6 +1022,52 @@ def show_attributes(op): return cls(set([n])) +def ps2xyz(ps: List[int]) -> Dict[str, List[int]]: + """ + pauli string list to xyz dict + + # ps2xyz([1, 2, 2, 0]) = {"x": [0], "y": [1, 2], "z": []} + + :param ps: _description_ + :type ps: List[int] + :return: _description_ + :rtype: Dict[str, List[int]] + """ + xyz: Dict[str, List[int]] = {"x": [], "y": [], "z": []} + for i, j in enumerate(ps): + if j == 1: + xyz["x"].append(i) + if j == 2: + xyz["y"].append(i) + if j == 3: + xyz["z"].append(i) + return xyz + + +def xyz2ps(xyz: Dict[str, List[int]], n: Optional[int] = None) -> List[int]: + """ + xyz dict to pauli string list + + :param xyz: _description_ + :type xyz: Dict[str, List[int]] + :param n: _description_, defaults to None + :type n: Optional[int], optional + :return: _description_ + :rtype: List[int] + """ + if n is None: + n = max(xyz.get("x", []) + xyz.get("y", []) + xyz.get("z", [])) + 1 + ps = [0 for _ in range(n)] + for i in range(n): + if i in xyz.get("x", []): + ps[i] = 1 + elif i in xyz.get("y", []): + ps[i] = 2 + elif i in xyz.get("z", []): + ps[i] = 3 + return ps + + def generate_local_hamiltonian( *hlist: Sequence[Tensor], matrix_form: bool = True ) -> Union[QuOperator, Tensor]: @@ -1085,311 +1132,355 @@ def quimb2qop(qb_mpo: Any) -> QuOperator: nwires = len(qb_mpo) assert nwires >= 3, "number of tensors must be larger than 2" mpo = [] + edges = [] for i in range(nwires): mpo.append(Node(qb_mpo[i].data)) - pbc = len(qb_mpo[0].shape) == 4 - if pbc: - for i in range(nwires): - connect(mpo[i][1], mpo[(i + 1) % nwires][0]) - else: - for i in range(1, nwires - 1): - connect(mpo[i][1], mpo[i + 1][0]) - connect(mpo[0][0], mpo[1][0]) + for j, ind in enumerate(qb_mpo[i].inds): + edges.append((i, j, ind)) + + out_edges = [] + in_edges = [] + + for i, e1 in enumerate(edges): + if e1[2].startswith("k"): + out_edges.append(mpo[e1[0]][e1[1]]) + elif e1[2].startswith("b"): + in_edges.append(mpo[e1[0]][e1[1]]) + else: + for j, e2 in enumerate(edges[i + 1 :]): + if e2[2] == e1[2]: + connect(mpo[e1[0]][e1[1]], mpo[e2[0]][e2[1]]) + qop = quantum_constructor( - [mpo[i][-2] for i in range(nwires)], # out_edges - [mpo[i][-1] for i in range(nwires)], # in_edges + out_edges, # out_edges + in_edges, # in_edges [], [], # ignore_edges ) return qop -try: +def heisenberg_hamiltonian( + g: Graph, + hzz: float = 1.0, + hxx: float = 1.0, + hyy: float = 1.0, + hz: float = 0.0, + hx: float = 0.0, + hy: float = 0.0, + sparse: bool = True, + numpy: bool = False, +) -> Tensor: + """ + Generate Heisenberg Hamiltonian with possible external fields. + Currently requires tensorflow installed - def _id(x: Any) -> Any: - return x + :Example: - if is_m1mac(): - compiled_jit = _id - else: - compiled_jit = partial(get_backend("tensorflow").jit, jit_compile=True) - - def heisenberg_hamiltonian( - g: Graph, - hzz: float = 1.0, - hxx: float = 1.0, - hyy: float = 1.0, - hz: float = 0.0, - hx: float = 0.0, - hy: float = 0.0, - sparse: bool = True, - numpy: bool = False, - ) -> Tensor: - """ - Generate Heisenberg Hamiltonian with possible external fields. + >>> g = tc.templates.graphs.Line1D(6) + >>> h = qu.heisenberg_hamiltonian(g, sparse=False) + >>> tc.backend.eigh(h)[0][:10] + array([-11.2111025, -8.4721365, -8.472136 , -8.472136 , -6. , + -5.123106 , -5.123106 , -5.1231055, -5.1231055, -5.1231055], + dtype=float32) + + :param g: input circuit graph + :type g: Graph + :param hzz: zz coupling, default is 1.0 + :type hzz: float + :param hxx: xx coupling, default is 1.0 + :type hxx: float + :param hyy: yy coupling, default is 1.0 + :type hyy: float + :param hz: External field on z direction, default is 0.0 + :type hz: float + :param hx: External field on y direction, default is 0.0 + :type hx: float + :param hy: External field on x direction, default is 0.0 + :type hy: float + :param sparse: Whether to return sparse Hamiltonian operator, default is True. + :type sparse: bool, defalts True + :param numpy: whether return the matrix in numpy or tensorflow form + :type numpy: bool, defaults False, + + :return: Hamiltonian measurements + :rtype: Tensor + """ + n = len(g.nodes) + ls = [] + weight = [] + for e in g.edges: + if hzz != 0: + r = [0 for _ in range(n)] + r[e[0]] = 3 + r[e[1]] = 3 + ls.append(r) + weight.append(hzz) + if hxx != 0: + r = [0 for _ in range(n)] + r[e[0]] = 1 + r[e[1]] = 1 + ls.append(r) + weight.append(hxx) + if hyy != 0: + r = [0 for _ in range(n)] + r[e[0]] = 2 + r[e[1]] = 2 + ls.append(r) + weight.append(hyy) + for node in g.nodes: + if hz != 0: + r = [0 for _ in range(n)] + r[node] = 3 + ls.append(r) + weight.append(hz) + if hx != 0: + r = [0 for _ in range(n)] + r[node] = 1 + ls.append(r) + weight.append(hx) + if hy != 0: + r = [0 for _ in range(n)] + r[node] = 2 + ls.append(r) + weight.append(hy) + ls = num_to_tensor(ls) + weight = num_to_tensor(weight) + if sparse: + r = PauliStringSum2COO_numpy(ls, weight) + if numpy: + return r + return backend.coo_sparse_matrix_from_numpy(r) + return PauliStringSum2Dense(ls, weight, numpy=numpy) - :Example: - >>> g = tc.templates.graphs.Line1D(6) - >>> h = qu.heisenberg_hamiltonian(g, sparse=False) - >>> tc.backend.eigh(h)[0][:10] - array([-11.2111025, -8.4721365, -8.472136 , -8.472136 , -6. , - -5.123106 , -5.123106 , -5.1231055, -5.1231055, -5.1231055], - dtype=float32) - - :param g: input circuit graph - :type g: Graph - :param hzz: zz coupling, default is 1.0 - :type hzz: float - :param hxx: xx coupling, default is 1.0 - :type hxx: float - :param hyy: yy coupling, default is 1.0 - :type hyy: float - :param hz: External field on z direction, default is 0.0 - :type hz: float - :param hx: External field on y direction, default is 0.0 - :type hx: float - :param hy: External field on x direction, default is 0.0 - :type hy: float - :param sparse: Whether to return sparse Hamiltonian operator, default is True. - :type sparse: bool, defalts True - :param numpy: whether return the matrix in numpy or tensorflow form - :type numpy: bool, defaults False, - - :return: Hamiltonian measurements - :rtype: Tensor - """ - n = len(g.nodes) - ls = [] - weight = [] - for e in g.edges: - if hzz != 0: - r = [0 for _ in range(n)] - r[e[0]] = 3 - r[e[1]] = 3 - ls.append(r) - weight.append(hzz) - if hxx != 0: - r = [0 for _ in range(n)] - r[e[0]] = 1 - r[e[1]] = 1 - ls.append(r) - weight.append(hxx) - if hyy != 0: - r = [0 for _ in range(n)] - r[e[0]] = 2 - r[e[1]] = 2 - ls.append(r) - weight.append(hyy) - for node in g.nodes: - if hz != 0: - r = [0 for _ in range(n)] - r[node] = 3 - ls.append(r) - weight.append(hz) - if hx != 0: - r = [0 for _ in range(n)] - r[node] = 1 - ls.append(r) - weight.append(hx) - if hy != 0: - r = [0 for _ in range(n)] - r[node] = 2 - ls.append(r) - weight.append(hy) - ls = tf.constant(ls) - weight = tf.constant(weight) - ls = get_backend("tensorflow").cast(ls, dtypestr) - weight = get_backend("tensorflow").cast(weight, dtypestr) - if sparse: - r = PauliStringSum2COO_numpy(ls, weight) - if numpy: - return r - return backend.coo_sparse_matrix_from_numpy(r) - return PauliStringSum2Dense(ls, weight, numpy=numpy) - - def PauliStringSum2Dense( - ls: Sequence[Sequence[int]], - weight: Optional[Sequence[float]] = None, - numpy: bool = False, - ) -> Tensor: - """ - Generate dense matrix from Pauli string sum - - :param ls: 2D Tensor, each row is for a Pauli string, - e.g. [1, 0, 0, 3, 2] is for :math:`X_0Z_3Y_4` - :type ls: Sequence[Sequence[int]] - :param weight: 1D Tensor, each element corresponds the weight for each Pauli string - defaults to None (all Pauli strings weight 1.0) - :type weight: Optional[Sequence[float]], optional - :param numpy: default False. If True, return numpy coo - else return backend compatible sparse tensor - :type numpy: bool - :return: the tensorflow dense matrix - :rtype: Tensor - """ - sparsem = PauliStringSum2COO_numpy(ls, weight) - if numpy: - return sparsem.todense() - sparsem = backend.coo_sparse_matrix_from_numpy(sparsem) - densem = backend.to_dense(sparsem) - return densem - - # already implemented as backend method - # - # def _tf2numpy_sparse(a: Tensor) -> Tensor: - # return get_backend("numpy").coo_sparse_matrix( - # indices=a.indices, - # values=a.values, - # shape=a.get_shape(), - # ) - - # def _numpy2tf_sparse(a: Tensor) -> Tensor: - # return get_backend("tensorflow").coo_sparse_matrix( - # indices=np.array([a.row, a.col]).T, - # values=a.data, - # shape=a.shape, - # ) - - def PauliStringSum2COO( - ls: Sequence[Sequence[int]], - weight: Optional[Sequence[float]] = None, - numpy: bool = False, - ) -> Tensor: - """ - Generate sparse tensor from Pauli string sum - - :param ls: 2D Tensor, each row is for a Pauli string, - e.g. [1, 0, 0, 3, 2] is for :math:`X_0Z_3Y_4` - :type ls: Sequence[Sequence[int]] - :param weight: 1D Tensor, each element corresponds the weight for each Pauli string - defaults to None (all Pauli strings weight 1.0) - :type weight: Optional[Sequence[float]], optional - :param numpy: default False. If True, return numpy coo - else return backend compatible sparse tensor - :type numpy: bool - :return: the scipy coo sparse matrix - :rtype: Tensor - """ - # numpy version is 3* faster! - - nterms = len(ls) - n = len(ls[0]) - s = 0b1 << n - if weight is None: - weight = [1.0 for _ in range(nterms)] - if not (isinstance(weight, tf.Tensor) or isinstance(weight, tf.Variable)): - weight = tf.constant(weight, dtype=getattr(tf, dtypestr)) - rsparse = get_backend("numpy").coo_sparse_matrix( - indices=np.array([[0, 0]], dtype=np.int64), - values=np.array([0.0], dtype=getattr(np, dtypestr)), # type: ignore - shape=(s, s), - ) - for i in range(nterms): - rsparse += get_backend("tensorflow").numpy(PauliString2COO(ls[i], weight[i])) # type: ignore - # auto transformed into csr format!! - rsparse = rsparse.tocoo() - if numpy: - return rsparse - return backend.coo_sparse_matrix_from_numpy(rsparse) +def PauliStringSum2Dense( + ls: Sequence[Sequence[int]], + weight: Optional[Sequence[float]] = None, + numpy: bool = False, +) -> Tensor: + """ + Generate dense matrix from Pauli string sum. + Currently requires tensorflow installed. - PauliStringSum2COO_numpy = partial(PauliStringSum2COO, numpy=True) - def PauliStringSum2COO_tf( - ls: Sequence[Sequence[int]], weight: Optional[Sequence[float]] = None - ) -> Tensor: - """ - Generate tensorflow sparse matrix from Pauli string sum - - :param ls: 2D Tensor, each row is for a Pauli string, - e.g. [1, 0, 0, 3, 2] is for :math:`X_0Z_3Y_4` - :type ls: Sequence[Sequence[int]] - :param weight: 1D Tensor, each element corresponds the weight for each Pauli string - defaults to None (all Pauli strings weight 1.0) - :type weight: Optional[Sequence[float]], optional - :return: the tensorflow coo sparse matrix - :rtype: Tensor - """ - nterms = len(ls) - n = len(ls[0]) - s = 0b1 << n - if weight is None: - weight = [1.0 for _ in range(nterms)] - if not (isinstance(weight, tf.Tensor) or isinstance(weight, tf.Variable)): - weight = tf.constant(weight, dtype=getattr(tf, dtypestr)) - rsparse = tf.SparseTensor( - indices=tf.constant([[0, 0]], dtype=tf.int64), - values=tf.constant([0.0], dtype=weight.dtype), # type: ignore - dense_shape=(s, s), + :param ls: 2D Tensor, each row is for a Pauli string, + e.g. [1, 0, 0, 3, 2] is for :math:`X_0Z_3Y_4` + :type ls: Sequence[Sequence[int]] + :param weight: 1D Tensor, each element corresponds the weight for each Pauli string + defaults to None (all Pauli strings weight 1.0) + :type weight: Optional[Sequence[float]], optional + :param numpy: default False. If True, return numpy coo + else return backend compatible sparse tensor + :type numpy: bool + :return: the tensorflow dense matrix + :rtype: Tensor + """ + sparsem = PauliStringSum2COO_numpy(ls, weight) + if numpy: + return sparsem.todense() + sparsem = backend.coo_sparse_matrix_from_numpy(sparsem) + densem = backend.to_dense(sparsem) + return densem + + +# already implemented as backend method +# +# def _tf2numpy_sparse(a: Tensor) -> Tensor: +# return get_backend("numpy").coo_sparse_matrix( +# indices=a.indices, +# values=a.values, +# shape=a.get_shape(), +# ) + +# def _numpy2tf_sparse(a: Tensor) -> Tensor: +# return get_backend("tensorflow").coo_sparse_matrix( +# indices=np.array([a.row, a.col]).T, +# values=a.data, +# shape=a.shape, +# ) + + +def PauliStringSum2COO( + ls: Sequence[Sequence[int]], + weight: Optional[Sequence[float]] = None, + numpy: bool = False, +) -> Tensor: + """ + Generate sparse tensor from Pauli string sum. + Currently requires tensorflow installed + + :param ls: 2D Tensor, each row is for a Pauli string, + e.g. [1, 0, 0, 3, 2] is for :math:`X_0Z_3Y_4` + :type ls: Sequence[Sequence[int]] + :param weight: 1D Tensor, each element corresponds the weight for each Pauli string + defaults to None (all Pauli strings weight 1.0) + :type weight: Optional[Sequence[float]], optional + :param numpy: default False. If True, return numpy coo + else return backend compatible sparse tensor + :type numpy: bool + :return: the scipy coo sparse matrix + :rtype: Tensor + """ + # numpy version is 3* faster! + + nterms = len(ls) + # n = len(ls[0]) + # s = 0b1 << n + if weight is None: + weight = [1.0 for _ in range(nterms)] + weight = num_to_tensor(weight) + ls = num_to_tensor(ls) + # rsparse = get_backend("numpy").coo_sparse_matrix( + # indices=np.array([[0, 0]], dtype=np.int64), + # values=np.array([0.0], dtype=getattr(np, dtypestr)), + # shape=(s, s), + # ) + global PauliString2COO_jit + if backend.name not in PauliString2COO_jit: + PauliString2COO_jit[backend.name] = backend.jit( + PauliString2COO, jit_compile=True ) - for i in range(nterms): - rsparse = tf.sparse.add(rsparse, PauliString2COO(ls[i], weight[i])) # type: ignore - # very slow sparse.add? + rsparses = [ + backend.numpy(PauliString2COO_jit[backend.name](ls[i], weight[i])) # type: ignore + for i in range(nterms) + ] + rsparse = _dc_sum(rsparses) + # auto transformed into csr format!! + + # for i in range(nterms): + # rsparse += get_backend("tensorflow").numpy(PauliString2COO(ls[i], weight[i])) + rsparse = rsparse.tocoo() + if numpy: return rsparse + return backend.coo_sparse_matrix_from_numpy(rsparse) - @compiled_jit - def PauliString2COO(l: Sequence[int], weight: Optional[float] = None) -> Tensor: - """ - Generate tensorflow sparse matrix from Pauli string sum - - :param l: 1D Tensor representing for a Pauli string, - e.g. [1, 0, 0, 3, 2] is for :math:`X_0Z_3Y_4` - :type l: Sequence[int] - :param weight: the weight for the Pauli string - defaults to None (all Pauli strings weight 1.0) - :type weight: Optional[float], optional - :return: the tensorflow sparse matrix - :rtype: Tensor - """ - n = len(l) - one = tf.constant(0b1, dtype=tf.int64) - idx_x = tf.constant(0b0, dtype=tf.int64) - idx_y = tf.constant(0b0, dtype=tf.int64) - idx_z = tf.constant(0b0, dtype=tf.int64) - i = tf.constant(0, dtype=tf.int64) - for j in l: - # i, j from enumerate is python, non jittable when cond using tensor - if j == 1: # xi - idx_x += tf.bitwise.left_shift(one, n - i - 1) - elif j == 2: # yi - idx_y += tf.bitwise.left_shift(one, n - i - 1) - elif j == 3: # zi - idx_z += tf.bitwise.left_shift(one, n - i - 1) - i += 1 - - if weight is None: - weight = tf.constant(1.0, dtype=tf.complex64) - return ps2coo_core(idx_x, idx_y, idx_z, weight, n) - - @compiled_jit - def ps2coo_core( - idx_x: Tensor, idx_y: Tensor, idx_z: Tensor, weight: Tensor, nqubits: int - ) -> Tuple[Tensor, Tensor]: - dtype = weight.dtype - s = 0b1 << nqubits - idx1 = tf.cast(tf.range(s), dtype=tf.int64) - idx2 = (idx1 ^ idx_x) ^ (idx_y) - indices = tf.transpose(tf.stack([idx1, idx2])) - tmp = idx1 & (idx_y | idx_z) - e = idx1 * 0 - ny = 0 - for i in range(nqubits): - # if tmp[i] is power of 2 (non zero), then e[i] = 1 - e ^= tf.bitwise.right_shift(tmp, i) & 0b1 - # how many 1 contained in idx_y - ny += tf.bitwise.right_shift(idx_y, i) & 0b1 - ny = tf.math.mod(ny, 4) - values = ( - tf.cast((1 - 2 * e), dtype) - * tf.math.pow(tf.constant(-1.0j, dtype=dtype), tf.cast(ny, dtype)) - * weight - ) - return tf.SparseTensor(indices=indices, values=values, dense_shape=(s, s)) # type: ignore -except (NameError, ImportError): - logger.warning( - "tensorflow is not installed, and sparse Hamiltonian generation utilities are disabled" +def _dc_sum(l: List[Any]) -> Any: + """ + For the sparse sum, the speed is determined by the non zero terms, + so the DC way to do the sum can indeed bring some speed advantage (several times) + + :param l: _description_ + :type l: List[Any] + :return: _description_ + :rtype: Any + """ + n = len(l) + if n > 2: + return _dc_sum(l[: n // 2]) + _dc_sum(l[n // 2 :]) + else: + return sum(l) + + +PauliStringSum2COO_numpy = partial(PauliStringSum2COO, numpy=True) + + +def PauliStringSum2COO_tf( + ls: Sequence[Sequence[int]], weight: Optional[Sequence[float]] = None +) -> Tensor: + """ + Generate tensorflow sparse matrix from Pauli string sum + [deprecated] + + :param ls: 2D Tensor, each row is for a Pauli string, + e.g. [1, 0, 0, 3, 2] is for :math:`X_0Z_3Y_4` + :type ls: Sequence[Sequence[int]] + :param weight: 1D Tensor, each element corresponds the weight for each Pauli string + defaults to None (all Pauli strings weight 1.0) + :type weight: Optional[Sequence[float]], optional + :return: the tensorflow coo sparse matrix + :rtype: Tensor + """ + import tensorflow as tf + + nterms = len(ls) + n = len(ls[0]) + s = 0b1 << n + if weight is None: + weight = [1.0 for _ in range(nterms)] + if not (isinstance(weight, tf.Tensor) or isinstance(weight, tf.Variable)): + weight = tf.constant(weight, dtype=getattr(tf, dtypestr)) + rsparse = tf.SparseTensor( + indices=tf.constant([[0, 0]], dtype=tf.int64), + values=tf.constant([0.0], dtype=weight.dtype), # type: ignore + dense_shape=(s, s), + ) + for i in range(nterms): + rsparse = tf.sparse.add(rsparse, PauliString2COO(ls[i], weight[i])) # type: ignore + # very slow sparse.add? + return rsparse + + +def PauliString2COO(l: Sequence[int], weight: Optional[float] = None) -> Tensor: + """ + Generate sparse matrix from Pauli string sum + + :param l: 1D Tensor representing for a Pauli string, + e.g. [1, 0, 0, 3, 2] is for :math:`X_0Z_3Y_4` + :type l: Sequence[int] + :param weight: the weight for the Pauli string + defaults to None (all Pauli strings weight 1.0) + :type weight: Optional[float], optional + :return: the tensorflow sparse matrix + :rtype: Tensor + """ + n = len(l) + l = num_to_tensor(l, dtype="int64") + # l = backend.cast(l, dtype="int64") + one = num_to_tensor(0b1, dtype="int64") + idx_x = num_to_tensor(0b0, dtype="int64") + idx_y = num_to_tensor(0b0, dtype="int64") + idx_z = num_to_tensor(0b0, dtype="int64") + i = num_to_tensor(0, dtype="int64") + # for j in l: + for j in range(n): + oh = backend.onehot(l[j], 4) + s = backend.left_shift(one, n - i - 1) + oh = backend.cast(oh, dtype="int64") + idx_x += oh[1] * s + idx_y += oh[2] * s + idx_z += oh[3] * s + + # if j == 1: # xi + # idx_x += backend.left_shift(one, n - i - 1) + # elif j == 2: # yi + # idx_y += backend.left_shift(one, n - i - 1) + # elif j == 3: # zi + # idx_z += backend.left_shift(one, n - i - 1) + i += 1 + + if weight is None: + weight = num_to_tensor(1.0, dtype="complex64") + return ps2coo_core(idx_x, idx_y, idx_z, weight, n) + + +PauliString2COO_jit = {"numpy": PauliString2COO} + + +def ps2coo_core( + idx_x: Tensor, idx_y: Tensor, idx_z: Tensor, weight: Tensor, nqubits: int +) -> Tuple[Tensor, Tensor]: + s = 0b1 << nqubits + idx1 = num_to_tensor(backend.arange(s), dtype="int64") + idx2 = (idx1 ^ idx_x) ^ (idx_y) + indices = backend.transpose(backend.stack([idx1, idx2])) + tmp = idx1 & (idx_y | idx_z) + e = idx1 * 0 + ny = 0 + for i in range(nqubits): + # if tmp[i] is power of 2 (non zero), then e[i] = 1 + e ^= backend.right_shift(tmp, i) & 0b1 + # how many 1 contained in idx_y + ny += backend.right_shift(idx_y, i) & 0b1 + ny = backend.mod(ny, 4) + values = ( + num_to_tensor(1 - 2 * e) + * ((num_to_tensor(-1.0j) ** num_to_tensor(ny))) + * weight ) + return backend.coo_sparse_matrix(indices=indices, values=values, shape=(s, s)) # type: ignore + # some quantum quatities below @@ -1421,7 +1512,7 @@ def op2tensor( @op2tensor -def entropy(rho: Union[Tensor, QuOperator], eps: float = 1e-12) -> Tensor: +def entropy(rho: Union[Tensor, QuOperator], eps: Optional[float] = None) -> Tensor: """ Compute the entropy from the given density matrix ``rho``. @@ -1459,8 +1550,15 @@ def entanglement2(param, n, nlayers): :return: Entropy on the given density matrix. :rtype: Tensor """ + eps_env = os.environ.get("TC_QUANTUM_ENTROPY_EPS") + if eps is None and eps_env is None: + eps = 1e-12 + elif eps is None and eps_env is not None: + eps = 10 ** (-int(eps_env)) + rho += eps * backend.cast(backend.eye(rho.shape[-1]), rho.dtype) # type: ignore lbd = backend.real(backend.eigh(rho)[0]) lbd = backend.relu(lbd) + lbd /= backend.sum(lbd) # we need the matrix anyway for AD. entropy = -backend.sum(lbd * backend.log(lbd + eps)) return backend.real(entropy) @@ -1498,6 +1596,51 @@ def trace_product(*o: Union[Tensor, QuOperator]) -> Tensor: return backend.trace(prod) +@op2tensor +def entanglement_entropy(state: Tensor, cut: Union[int, List[int]]) -> Tensor: + rho = reduced_density_matrix(state, cut) + return entropy(rho) + + +def reduced_wavefunction( + state: Tensor, cut: List[int], measure: Optional[List[int]] = None +) -> Tensor: + """ + Compute the reduced wavefunction from the quantum state ``state``. + The fixed measure result is guaranteed by users, + otherwise final normalization may required in the return + + :param state: _description_ + :type state: Tensor + :param cut: the list of position for qubit to be reduced + :type cut: List[int] + :param measure: the fixed results of given qubits in the same shape list as ``cut`` + :type measure: List[int] + :return: _description_ + :rtype: Tensor + """ + if measure is None: + measure = [0 for _ in cut] + s = backend.reshape2(state) + n = len(backend.shape_tuple(s)) + s_node = Gate(s) + end_nodes = [] + for c, m in zip(cut, measure): + rt = backend.cast(backend.convert_to_tensor(1 - m), dtypestr) * backend.cast( + backend.convert_to_tensor(np.array([1.0, 0.0])), dtypestr + ) + backend.cast(backend.convert_to_tensor(m), dtypestr) * backend.cast( + backend.convert_to_tensor(np.array([0.0, 1.0])), dtypestr + ) + end_node = Gate(rt) + end_nodes.append(end_node) + s_node[c] ^ end_node[0] + new_node = contractor( + [s_node] + end_nodes, + output_edge_order=[s_node[i] for i in range(n) if i not in cut], + ) + return backend.reshape(new_node.tensor, [-1]) + + def reduced_density_matrix( state: Union[Tensor, QuOperator], cut: Union[int, List[int]], @@ -1707,6 +1850,76 @@ def truncated_free_energy( return energy - renyi / beta +@op2tensor +def partial_transpose(rho: Tensor, transposed_sites: List[int]) -> Tensor: + """ + _summary_ + + :param rho: density matrix + :type rho: Tensor + :param transposed_sites: sites int list to be transposed + :type transposed_sites: List[int] + :return: _description_ + :rtype: Tensor + """ + rho = backend.reshape2(rho) + rho_node = Gate(rho) + n = len(rho.shape) // 2 + left_edges = [] + right_edges = [] + for i in range(n): + if i not in transposed_sites: + left_edges.append(rho_node[i]) + right_edges.append(rho_node[i + n]) + else: + left_edges.append(rho_node[i + n]) + right_edges.append(rho_node[i]) + rhot_op = QuOperator(out_edges=left_edges, in_edges=right_edges) + rhot = rhot_op.eval_matrix() + return rhot + + +@op2tensor +def entanglement_negativity(rho: Tensor, transposed_sites: List[int]) -> Tensor: + """ + _summary_ + + :param rho: _description_ + :type rho: Tensor + :param transposed_sites: _description_ + :type transposed_sites: List[int] + :return: _description_ + :rtype: Tensor + """ + rhot = partial_transpose(rho, transposed_sites) + es = backend.eigvalsh(rhot) + rhot_m = backend.sum(backend.abs(es)) + return (backend.log(rhot_m) - 1.0) / 2.0 + + +@op2tensor +def log_negativity(rho: Tensor, transposed_sites: List[int], base: str = "e") -> Tensor: + """ + _summary_ + + :param rho: _description_ + :type rho: Tensor + :param transposed_sites: _description_ + :type transposed_sites: List[int] + :param base: whether use 2 based log or e based log, defaults to "e" + :type base: str, optional + :return: _description_ + :rtype: Tensor + """ + rhot = partial_transpose(rho, transposed_sites) + es = backend.eigvalsh(rhot) + rhot_m = backend.sum(backend.abs(es)) + een = backend.log(rhot_m) + if base in ["2", 2]: + return een / backend.cast(backend.log(2.0), rdtypestr) + return een + + @partial(op2tensor, op_argnums=(0, 1)) def trace_distance(rho: Tensor, rho0: Tensor, eps: float = 1e-12) -> Tensor: """ @@ -1935,7 +2148,7 @@ def sample2count( results = backend.unique_with_counts(sample) # non-jittable else: # jax specified results = backend.unique_with_counts(sample, size=d, fill_value=-1) - return results # type: ignore + return results def count_vector2dict(count: Tensor, n: int, key: str = "bin") -> Dict[Any, int]: @@ -1951,7 +2164,10 @@ def count_vector2dict(count: Tensor, n: int, key: str = "bin") -> Dict[Any, int] :return: _description_ :rtype: _type_ """ - d = {i: backend.numpy(count[i]).item() for i in range(2**n)} + from .interfaces import which_backend + + b = which_backend(count) + d = {i: b.numpy(count[i]).item() for i in range(2**n)} if key == "int": return d else: @@ -1999,6 +2215,7 @@ def measurement_counts( format: str = "count_vector", is_prob: bool = False, random_generator: Optional[Any] = None, + status: Optional[Tensor] = None, jittable: bool = False, ) -> Any: """ @@ -2048,7 +2265,10 @@ def measurement_counts( defaults to be False :type is_prob: bool :param random_generator: random_generator, defaults to None - :type random_general: Optional[Any] + :type random_generator: Optional[Any] + :param status: external randomness given by tensor uniformly from [0, 1], + if set, can overwrite random_generator + :type status: Optional[Tensor] :param jittable: if True, jax backend try using a jittable count, defaults to False :type jittable: bool :return: The counts for each bit string measured. @@ -2066,7 +2286,6 @@ def measurement_counts( pi = backend.reshape(pi, [-1]) d = int(backend.shape_tuple(pi)[0]) n = int(np.log(d) / np.log(2) + 1e-8) - drange = backend.arange(d) if (counts is None) or counts <= 0: if format == "count_vector": return pi @@ -2081,12 +2300,15 @@ def measurement_counts( "unsupported format %s for analytical measurement" % format ) else: - if random_generator is None: - raw_counts = backend.implicit_randc(drange, shape=counts, p=pi) - else: - raw_counts = backend.stateful_randc( - random_generator, a=drange, shape=counts, p=pi - ) + raw_counts = backend.probability_sample( + counts, pi, status=status, g=random_generator + ) + # if random_generator is None: + # raw_counts = backend.implicit_randc(drange, shape=counts, p=pi) + # else: + # raw_counts = backend.stateful_randc( + # random_generator, a=drange, shape=counts, p=pi + # ) return sample2all(raw_counts, n, format=format, jittable=jittable) @@ -2170,7 +2392,7 @@ def spin_by_basis(n: int, m: int, elements: Tuple[int, int] = (1, -1)) -> Tensor def correlation_from_samples(index: Sequence[int], results: Tensor, n: int) -> Tensor: - """ + r""" Compute :math:`\prod_{i\in \\text{index}} s_i (s=\pm 1)`, Results is in the format of "sample_int" or "sample_bin" @@ -2185,15 +2407,16 @@ def correlation_from_samples(index: Sequence[int], results: Tensor, n: int) -> T """ if len(backend.shape_tuple(results)) == 1: results = sample_int2bin(results, n) - results = results * 2 - 1 + results = 1 - results * 2 r = results[:, index[0]] for i in index[1:]: r *= results[:, i] + r = backend.cast(r, rdtypestr) return backend.mean(r) def correlation_from_counts(index: Sequence[int], results: Tensor) -> Tensor: - """ + r""" Compute :math:`\prod_{i\in \\text{index}} s_i`, where the probability for each bitstring is given as a vector ``results``. Results is in the format of "count_vector" diff --git a/tensorcircuit/results/__init__.py b/tensorcircuit/results/__init__.py new file mode 100644 index 00000000..7a953ae9 --- /dev/null +++ b/tensorcircuit/results/__init__.py @@ -0,0 +1,4 @@ +from . import counts +from . import readout_mitigation + +rem = readout_mitigation # alias diff --git a/tensorcircuit/results/counts.py b/tensorcircuit/results/counts.py new file mode 100644 index 00000000..a4800cc4 --- /dev/null +++ b/tensorcircuit/results/counts.py @@ -0,0 +1,124 @@ +""" +dict related functionalities +""" + +from typing import Any, Dict, Optional, Sequence + +import numpy as np + + +Tensor = Any +ct = Dict[str, int] + +# TODO(@refraction-ray): merge_count + + +def reverse_count(count: ct) -> ct: + ncount = {} + for k, v in count.items(): + ncount[k[::-1]] = v + return ncount + + +def sort_count(count: ct) -> ct: + return {k: v for k, v in sorted(count.items(), key=lambda item: -item[1])} + + +def normalized_count(count: ct) -> Dict[str, float]: + shots = sum([v for k, v in count.items()]) + return {k: v / shots for k, v in count.items()} + + +def marginal_count(count: ct, keep_list: Sequence[int]) -> ct: + import qiskit + + count = reverse_count(count) + ncount = qiskit.result.utils.marginal_distribution(count, keep_list) + return reverse_count(ncount) + + +def count2vec(count: ct, normalization: bool = True) -> Tensor: + nqubit = len(list(count.keys())[0]) + probability = [0] * 2**nqubit + shots = sum([v for k, v in count.items()]) + for k, v in count.items(): + if normalization is True: + v /= shots # type: ignore + probability[int(k, 2)] = v + return np.array(probability) + + +def vec2count(vec: Tensor, prune: bool = False) -> ct: + from ..quantum import count_vector2dict + + if isinstance(vec, list): + vec = np.array(vec) + n = int(np.log(vec.shape[0]) / np.log(2) + 1e-9) + c = count_vector2dict(vec, n, key="bin") + if prune is True: + nc = c.copy() + for k, v in c.items(): + if np.abs(v) < 1e-8: + del nc[k] + return nc + return c + + +def kl_divergence(c1: ct, c2: ct) -> float: + eps = 1e-4 # typical value for inverse of the total shots + c1 = normalized_count(c1) # type: ignore + c2 = normalized_count(c2) # type: ignore + kl = 0 + for k, v in c1.items(): + kl += v * (np.log(v) - np.log(c2.get(k, eps))) + return kl + + +def expectation( + count: ct, z: Optional[Sequence[int]] = None, diagonal_op: Optional[Tensor] = None +) -> float: + """ + compute diagonal operator expectation value from bit string count dictionary + + :param count: count dict for bitstring histogram + :type count: ct + :param z: if defaults as None, then ``diagonal_op`` must be set + a list of qubit that we measure Z op on + :type z: Optional[Sequence[int]] + :param diagoal_op: shape [n, 2], explicitly indicate the diagonal op on each qubit + eg. [1, -1] for z [1, 1] for I, etc. + :type diagoal_op: Tensor + :return: the expectation value + :rtype: float + """ + if z is None and diagonal_op is None: + raise ValueError("One of `z` and `diagonal_op` must be set") + n = len(list(count.keys())[0]) + if z is not None: + diagonal_op = [[1, -1] if i in z else [1, 1] for i in range(n)] + r = 0 + shots = 0 + for k, v in count.items(): + cr = 1.0 + for i in range(n): + cr *= diagonal_op[i][int(k[i])] # type: ignore + r += cr * v # type: ignore + shots += v + return r / shots + + +def plot_histogram(data: Any, **kws: Any) -> Any: + """ + See ``qiskit.visualization.plot_histogram``: + https://qiskit.org/documentation/stubs/qiskit.visualization.plot_histogram.html + + interesting kw options include: ``number_to_keep`` (int) + + :param data: _description_ + :type data: Any + :return: _description_ + :rtype: Any + """ + from qiskit.visualization import plot_histogram + + return plot_histogram(data, **kws) diff --git a/tensorcircuit/results/qem/__init__.py b/tensorcircuit/results/qem/__init__.py new file mode 100644 index 00000000..c895d370 --- /dev/null +++ b/tensorcircuit/results/qem/__init__.py @@ -0,0 +1,14 @@ +from . import qem_methods + +apply_zne = qem_methods.apply_zne +zne_option = qem_methods.zne_option # type: ignore + +apply_dd = qem_methods.apply_dd +dd_option = qem_methods.dd_option # type: ignore +used_qubits = qem_methods.used_qubits +prune_ddcircuit = qem_methods.prune_ddcircuit +add_dd = qem_methods.add_dd + +apply_rc = qem_methods.apply_rc +rc_circuit = qem_methods.rc_circuit +rc_candidates = qem_methods.rc_candidates diff --git a/tensorcircuit/results/qem/benchmark_circuits.py b/tensorcircuit/results/qem/benchmark_circuits.py new file mode 100644 index 00000000..368cc828 --- /dev/null +++ b/tensorcircuit/results/qem/benchmark_circuits.py @@ -0,0 +1,107 @@ +""" +circuits for quantum chip benchmark +""" + +from typing import Any, List, Dict, Tuple +import logging + +logger = logging.getLogger(__name__) + +import networkx as nx + +try: + from mitiq.benchmarks import ( + generate_ghz_circuit, + generate_mirror_circuit, + generate_rb_circuits, + generate_w_circuit, + ) + from mitiq.interface import convert_from_mitiq +except ModuleNotFoundError: + logger.warning("mitiq is not installed, please ``pip install mitiq`` first") + + +from ... import Circuit + + +def ghz_circuit(num_qubits: int) -> Tuple[Any, Dict[str, float]]: + cirq = generate_ghz_circuit(num_qubits) + ideal = {"0" * num_qubits: 0.5, "1" * num_qubits: 0.5} + qisc = convert_from_mitiq(cirq, "qiskit") + circuit = Circuit.from_qiskit(qisc, qisc.num_qubits) + return circuit, ideal + + +def w_circuit(num_qubits: int) -> Tuple[Any, Dict[str, float]]: + # Efficient quantum algorithms for GHZ and W states https://arxiv.org/abs/1807.05572 + # Werner-state with linear complexity {'1000': 0.25, '0100': 0.25, '0010': 0.25, '0001': 0.25} + cirq = generate_w_circuit(num_qubits) + + ideal = {} + for i in range(num_qubits): + bitstring = "0" * i + "1" + "0" * (num_qubits - i - 1) + ideal[bitstring] = 1 / num_qubits + + qisc = convert_from_mitiq(cirq, "qiskit") + circuit = Circuit.from_qiskit(qisc, qisc.num_qubits) + return circuit, ideal + + +def rb_circuit(num_qubits: int, depth: int) -> Tuple[Any, Dict[str, float]]: + # num_qubits limited to 1 or 2 + cirq = generate_rb_circuits(num_qubits, depth)[0] + ideal = {"0" * num_qubits: 1.0} + qisc = convert_from_mitiq(cirq, "qiskit") + circuit = Circuit.from_qiskit(qisc, qisc.num_qubits) + return circuit, ideal + + +def mirror_circuit( + depth: int, + two_qubit_gate_prob: float, + connectivity_graph: nx.Graph, + seed: int, + two_qubit_gate_name: str = "CNOT", +) -> Tuple[Any, Dict[str, float]]: + # Measuring the Capabilities of Quantum Computers https://arxiv.org/pdf/2008.11294.pdf + cirq, bitstring_list = generate_mirror_circuit( + nlayers=depth, + two_qubit_gate_prob=two_qubit_gate_prob, + connectivity_graph=connectivity_graph, + two_qubit_gate_name=two_qubit_gate_name, + seed=seed, + ) + ideal_bitstring = "".join(map(str, bitstring_list)) + ideal = {ideal_bitstring: 1.0} + qisc = convert_from_mitiq(cirq, "qiskit") + circuit = Circuit.from_qiskit(qisc, qisc.num_qubits) + return circuit, ideal + + +def QAOA_circuit( + graph: List[Tuple[int]], weight: List[float], params: List[float] +) -> Any: # internal API don't use + nlayers = len(params) + + qlist = [] + for i in range(len(graph)): + for j in range(2): + qlist.append(graph[i][j]) + qlist = list(set(qlist)) + + nqubit = max(qlist) + 1 + + c = Circuit(nqubit) + for i in qlist: + c.h(i) # type: ignore + + for i in range(nlayers): + for e in range(len(graph)): + c.cnot(graph[e][0], graph[e][1]) # type: ignore + c.rz(graph[e][1], theta=params[i, 0] * weight[e]) # type: ignore + c.cnot(graph[e][0], graph[e][1]) # type: ignore + + for k in qlist: + c.rx(k, theta=params[i, 1] * 2) # type: ignore + + return c diff --git a/tensorcircuit/results/qem/qem_methods.py b/tensorcircuit/results/qem/qem_methods.py new file mode 100644 index 00000000..68199e49 --- /dev/null +++ b/tensorcircuit/results/qem/qem_methods.py @@ -0,0 +1,383 @@ +""" +quantum error mitigation functionalities +""" + +from typing import Any, List, Union, Optional, Dict, Tuple, Callable, Sequence +from itertools import product +import functools +from functools import partial +import collections +import operator +from random import choice +import logging + +logger = logging.getLogger(__name__) + +import numpy as np + +try: + from mitiq import zne, ddd + from mitiq.zne.scaling import fold_gates_at_random + + zne_option = zne + dd_option = ddd +except ModuleNotFoundError: + logger.warning("mitiq is not installed, please ``pip install mitiq`` first") + zne_option = None + dd_option = None + +from ... import Circuit +from ... import backend, gates +from ...compiler import simple_compiler + +Gate = gates.Gate + + +def apply_zne( + circuit: Any, + executor: Callable[[Union[Any, Sequence[Any]]], Any], + factory: Optional[Any], + scale_noise: Optional[Callable[[Any, float], Any]] = None, + num_to_average: int = 1, + **kws: Any, +) -> Any: + """ + Apply zero-noise extrapolation (ZNE) and return the mitigated results. + + :param circuit: The aim circuit. + :type circuit: Any + :param executor: A executor that executes a single circuit or a batch of circuits and return results. + :type executor: Callable[[Union[Any, Sequence[Any]]], Any] + :param factory: Determines the extropolation method. + :type factory: Optional[Factory] + :param scale_noise: The scaling function for the aim circuit, defaults to fold_gates_at_random + :type scale_noise: Callable[[Any, float], Any], optional + :param num_to_average: Number of times expectation values are computed by + the executor, average each point, defaults to 1. + :type num_to_average: int, optional + :return: Mitigated average value by ZNE. + :rtype: float + """ + if scale_noise is None: + scale_noise = fold_gates_at_random + + def executortc(c): # type: ignore + c = Circuit.from_qiskit(c, c.num_qubits) + return executor(c) + + circuit = circuit.to_qiskit(enable_instruction=True) + result = zne.execute_with_zne( + circuit=circuit, + executor=executortc, + factory=factory, + scale_noise=scale_noise, + num_to_average=num_to_average, + **kws, + ) + return result + + +def prune_ddcircuit(c: Any, qlist: List[int]) -> Any: + """ + Discard DD sequence on idle qubits and Discard identity gate + (no identity/idle gate on device now) filled in DD sequence. + + :param c: circuit + :type c: Any + :param qlist: qubit list to apply DD sequence + :type qlist: list + :return: new circuit + :rtype: Any + """ + qir = c.to_qir() + cnew = Circuit(c.circuit_param["nqubits"]) + for d in qir: + if d["index"][0] in qlist: + if_iden = np.sum(abs(np.array([[1, 0], [0, 1]]) - d["gate"].get_tensor())) + if if_iden > 1e-4: + if "parameters" not in d: + cnew.apply_general_gate_delayed(d["gatef"], d["name"])( + cnew, *d["index"] + ) + else: + cnew.apply_general_variable_gate_delayed(d["gatef"], d["name"])( + cnew, *d["index"], **d["parameters"] + ) + return cnew + + +def used_qubits(c: Any) -> List[int]: + """ + Create a qubit list that includes all qubits having gate manipulation. + + :param c: a circuit + :type c: Any + :return: qubit list + :rtype: List + """ + qir = c.to_qir() + qlist = [] + for d in qir: + for i in range(len(d["index"])): + if d["index"][i] not in qlist: + qlist.append(d["index"][i]) + return qlist + + +def add_dd(c: Any, rule: Callable[[int], Any]) -> Any: + """ + Add DD sequence to A circuit + + :param c: circuit + :type c: Any + :param rule: The rule to conduct the DD sequence + :type rule: Callable[[int], Any] + :return: new circuit + :rtype: Any + """ + nqubit = c.circuit_param["nqubits"] + input_circuit = c.to_qiskit() + circuit_dd = dd_option.insert_ddd_sequences(input_circuit, rule=rule) + circuit_dd = Circuit.from_qiskit(circuit_dd, nqubit) + return circuit_dd + + +def apply_dd( + circuit: Any, + executor: Callable[[Any], Any], + rule: Union[Callable[[int], Any], List[str]], + rule_args: Optional[Dict[str, Any]] = None, + num_trials: int = 1, + full_output: bool = False, + ignore_idle_qubit: bool = True, + fulldd: bool = False, + iscount: bool = False, +) -> Union[ + float, Tuple[float, List[Any]], Dict[str, float], Tuple[Dict[str, float], List[Any]] +]: + """ + Apply dynamic decoupling (DD) and return the mitigated results. + + + :param circuit: The aim circuit. + :type circuit: Any + :param executor: A executor that executes a circuit and return results. + :type executor: Callable[[Any], Any] + :param rule: The rule to construct DD sequence, can use default rule "dd_option.rules.xx" + or custom rule "['X','X']" + :type rule: Union[Callable[[int], Any], List[str]] + :param rule_args:An optional dictionary of keyword arguments for ``rule``, defaults to {}. + :type rule_args: Dict[str, Any], optional + :param num_trials: The number of independent experiments to average over, defaults to 1 + :type num_trials: int, optional + :param full_output: If ``False`` only the mitigated expectation value is + returned. If ``True`` a dictionary containing all DD data is + returned too, defaults to False + :type full_output: bool, optional + :param ig_idle_qubit: ignore the DD sequences that added to unused qubits, defaults to True + :type ig_idle_qubit: bool, optional + :param fulldd: dd sequence full fill the idle circuits, defaults to False + :type fulldd: bool, optional + :param iscount: whether the output is bit string, defaults to False + :type iscount: bool, optional + :return: mitigated expectation value or mitigated expectation value and DD circuit information + :rtype: Union[float, Tuple[float, Dict[str, Any]]] + """ + if rule_args is None: + rule_args = {} + + def dd_rule(slack_length: int, spacing: int = -1) -> Any: + """ + Set DD rule. + + :param slack_length: Length of idle window to fill. Automatically calculated for a circuit. + :type slack_length: int + :param spacing: How many identity spacing gates to apply between dynamical + decoupling gates, defaults to -1 + :type spacing: int, optional + """ + dd_sequence = dd_option.rules.general_rule( + slack_length=slack_length, + spacing=spacing, + gates=gates, + ) + return dd_sequence + + if isinstance(rule, list): + import cirq + + gates = [] + for i in rule: + gates.append(getattr(cirq, i)) + rule = dd_rule + + if ignore_idle_qubit is True: + qlist = used_qubits(circuit) + else: + qlist = list(range(circuit.circuit_param["nqubits"])) + + rule_partial = partial( + rule, **rule_args + ) # rule_args influence the finall DD sequence + c2 = circuit + c3 = add_dd(c2, rule_partial) + c3 = prune_ddcircuit(c3, qlist) + if fulldd is True: + while c2.to_openqasm() != c3.to_openqasm(): + c2 = c3 + c3 = add_dd(c2, rule_partial) + c3 = prune_ddcircuit(c3, qlist) + + exp = [] + for i in range(num_trials): # type: ignore + exp.append(executor(c3)) + + if iscount is True: + sumcount = dict( + functools.reduce(operator.add, map(collections.Counter, exp)) # type:ignore + ) # type:ignore + result = {k: sumcount[k] / num_trials for k in sumcount.keys()} + else: + result = np.mean(exp) # type:ignore + + if full_output is True: + result = [result, c3] # type:ignore + + # def executortc(c): + # c = Circuit.from_qiskit(c, c.num_qubits) + # c = prune_ddcircuit(c,qlist) + # return executor(c) + # circuit = circuit.to_qiskit() + # result = ddd.execute_with_ddd( + # circuit=circuit, + # executor=executortc, + # rule=rule, + # rule_args=rule_args, + # num_trials=num_trials, + # full_output=full_output, + # ) + + return result # type:ignore + + +def rc_candidates(gate: Gate) -> List[Any]: + pauli = [m.tensor for m in gates.pauli_gates] + if isinstance(gate, gates.Gate): + gate = gate.tensor + gatem = backend.reshapem(gate) + r = [] + for combo in product(*[range(4) for _ in range(4)]): + i = ( + np.kron(pauli[combo[0]], pauli[combo[1]]) + @ gatem + @ np.kron(pauli[combo[2]], pauli[combo[3]]) + ) + if np.allclose(i, gatem, atol=1e-4): + r.append(combo) + elif np.allclose(i, -gatem, atol=1e-4): + r.append(combo) + return r + + +def _apply_gate(c: Any, i: int, j: int) -> Any: + if i == 0: + c.i(j) + elif i == 1: + c.x(j) + elif i == 2: + c.y(j) + elif i == 3: + c.z(j) + return c + + +candidate_dict = {} # type: ignore + + +def rc_circuit(c: Any) -> Any: + qir = c.to_qir() + cnew = Circuit(c.circuit_param["nqubits"]) + for d in qir: + if len(d["index"]) == 2: + if d["gate"].name in candidate_dict: + rc_cand = candidate_dict[d["gate"].name] + rc_list = choice(rc_cand) + else: + rc_cand = rc_candidates(d["gate"]) + rc_list = choice(rc_cand) + candidate_dict[d["gate"].name] = rc_cand + + cnew = _apply_gate(cnew, rc_list[0], d["index"][0]) + cnew = _apply_gate(cnew, rc_list[1], d["index"][1]) + if "parameters" not in d: + cnew.apply_general_gate_delayed(d["gatef"], d["name"])( + cnew, *d["index"] + ) + else: + cnew.apply_general_variable_gate_delayed(d["gatef"], d["name"])( + cnew, *d["index"], **d["parameters"] + ) + cnew = _apply_gate(cnew, rc_list[2], d["index"][0]) + cnew = _apply_gate(cnew, rc_list[3], d["index"][1]) + else: + if "parameters" not in d: + cnew.apply_general_gate_delayed(d["gatef"], d["name"])( + cnew, *d["index"] + ) + else: + cnew.apply_general_variable_gate_delayed(d["gatef"], d["name"])( + cnew, *d["index"], **d["parameters"] + ) + return cnew + + +def apply_rc( + circuit: Any, + executor: Callable[[Any], Any], + num_to_average: int = 1, + simplify: bool = True, + iscount: bool = False, + **kws: Any, +) -> Tuple[float, List[Any]]: + """ + Apply Randomized Compiling or Pauli twirling on two-qubit gates. + + :param circuit: Input circuit + :type circuit: Any + :param executor: A executor that executes a circuit and return results. + :type executor: Callable[[Any], Any] + :param num_to_average: Number of circuits for RC, defaults to 1 + :type num_to_average: int, optional + :param simplify: Whether simplify the circuits by merging single qubit gates, defaults to True + :type simplify: bool, optional + :param iscount: whether the output is bit string, defaults to False + :type iscount: bool, optional + :return: Mitigated results by RC + :rtype: float + """ + exp = [] + circuit_list = [] + for _ in range(num_to_average): + circuit1 = rc_circuit(circuit) + + if simplify is True: + circuit1 = prune_ddcircuit( + circuit1, qlist=list(range(circuit1.circuit_param["nqubits"])) + ) + circuit1 = simple_compiler.merge(circuit1) + + exp.append(executor(circuit1)) + circuit_list.append(circuit1) + + if iscount is True: + sumcount = dict( + functools.reduce(operator.add, map(collections.Counter, exp)) # type:ignore + ) # type:ignore + result = {k: sumcount[k] / num_to_average for k in sumcount.keys()} + else: + result = np.mean(exp) # type:ignore + + return result, circuit_list # type:ignore + + +# TODO(yuqinchen) add executor with batch ability diff --git a/tensorcircuit/results/readout_mitigation.py b/tensorcircuit/results/readout_mitigation.py new file mode 100644 index 00000000..d701dcc1 --- /dev/null +++ b/tensorcircuit/results/readout_mitigation.py @@ -0,0 +1,836 @@ +""" +readout error mitigation functionalities +""" + +# Part of the code in this file is from mthree: https://github.com/Qiskit-Partners/mthree (Apache2) +# https://journals.aps.org/prxquantum/pdf/10.1103/PRXQuantum.2.040326 + +from typing import Any, Callable, List, Sequence, Optional, Union, Dict +import warnings +from time import perf_counter + +import numpy as np +import scipy.linalg as la +import scipy.sparse.linalg as spla +from scipy.optimize import minimize + +try: + from mthree.matrix import _reduced_cal_matrix + from mthree.utils import counts_to_vector, vector_to_quasiprobs + from mthree.norms import ainv_onenorm_est_lu, ainv_onenorm_est_iter + from mthree.matvec import M3MatVec + from mthree.exceptions import M3Error + from mthree.classes import QuasiCollection + + mthree_installed = True +except ImportError: + mthree_installed = False + +from .counts import count2vec, vec2count, ct, marginal_count, expectation, sort_count +from ..circuit import Circuit +from ..utils import is_sequence + + +Tensor = Any + + +class ReadoutMit: + def __init__(self, execute: Callable[..., List[ct]], iter_threshold: int = 4096): + """ + The Class for readout error mitigation + + :param execute: execute function to run the cirucit + :type execute: Callable[..., List[ct]] + :param iter_threshold: iteration threshold, defaults to 4096 + :type iter_threshold: int, optional + """ + + self.cal_qubits = None # qubit list for calibration + self.use_qubits = None # qubit list for mitigation + + self.local = None + self.single_qubit_cals = None + self.global_cals = None + + self.iter_threshold = iter_threshold + + if isinstance(execute, str): + # execute is a device name str + from ..cloud.wrapper import batch_submit_template + + self.devstr: Optional[str] = execute + self.execute_fun: Callable[..., List[ct]] = batch_submit_template(execute) + else: + self.execute_fun = execute + self.devstr = None + + def ubs(self, i: int, qubits: Optional[Sequence[Any]]) -> int: + """ + Help omit calibration results that not in used qubit list. + + :param i: index + :type i: int + :param qubits: used qubit list + :type qubits: Sequence[Any] + :return: omitation related value + :rtype: int + """ + name = "{:0" + str(len(self.cal_qubits)) + "b}" # type: ignore + lisbs = [int(x) for x in name.format(i)] + + vomit = 0 + for k in list(filter(lambda x: x not in qubits, self.cal_qubits)): # type: ignore + vomit += lisbs[self.cal_qubits.index(k)] # type: ignore + + return vomit + + def newrange(self, m: int, qubits: Optional[Sequence[Any]]) -> int: + """ + Rerange the order according to used qubit list. + + :param m: index + :type m: int + :param qubits: used qubit list + :type qubits: Sequence[Any] + :return: new index + :rtype: int + """ + # sorted_index = sorted( + # range(len(qubits)), key=lambda k: qubits[k] # type: ignore + # ) + sorted_index = [] + qubits1 = sorted(qubits) # type: ignore + for i in qubits: # type: ignore + sorted_index.append(qubits1.index(i)) + + name = "{:0" + str(len(qubits)) + "b}" # type: ignore + lisbs = [int(x) for x in name.format(m)] + lisbs2 = [lisbs[i] for i in sorted_index] + + indexstr = "" + for i in lisbs2: + indexstr += str(i) + return int(indexstr, 2) + + def get_matrix(self, qubits: Optional[Sequence[Any]] = None) -> Tensor: + """ + Calculate cal_matrix according to use qubit list. + + :param qubits: used qubit list, defaults to None + :type qubits: Sequence[Any], optional + :return: cal_matrix + :rtype: Tensor + """ + + if qubits is None: + if self.use_qubits is not None: + qubits = self.use_qubits + else: + qubits = self.cal_qubits + + if self.local is False: + lbs = [marginal_count(i, qubits) for i in self.global_cal] + calmatrix = np.zeros((2 ** len(qubits), 2 ** len(qubits))) + + m = 0 + for i in range(len(lbs)): + vv = self.ubs(i, qubits) + + if vv == 0: + for s in lbs[i]: + calmatrix[int(s, 2)][self.newrange(m, qubits)] = ( + lbs[i][s] / self.cal_shots + ) + m += 1 + self.calmatrix = calmatrix + return calmatrix + + # self.local = True + calmatrix = self.single_qubit_cals[qubits[0]] # type: ignore + for i in range(1, len(qubits)): # type: ignore + calmatrix = np.kron(calmatrix, self.single_qubit_cals[qubits[i]]) # type: ignore + self.calmatrix = calmatrix # type: ignore + return calmatrix + + def _form_cals(self, qubits): # type: ignore + qubits = np.asarray(qubits, dtype=int) + cals = np.zeros(4 * qubits.shape[0], dtype=float) + + # Reverse index qubits for easier indexing later + for kk, qubit in enumerate(qubits[::-1]): + cals[4 * kk : 4 * kk + 4] = self.single_qubit_cals[qubit].ravel() # type: ignore + return cals + + def local_miti_readout_circ(self) -> List[Circuit]: + """ + Generate circuits for local calibration. + + :return: circuit list + :rtype: List[Circuit] + """ + # TODO(@yutuer): Note on qubit mapping + miticirc = [] + c = Circuit(max(self.cal_qubits) + 1) # type: ignore + miticirc.append(c) + c = Circuit(max(self.cal_qubits) + 1) # type: ignore + for i in self.cal_qubits: # type: ignore + c.X(i) # type: ignore + miticirc.append(c) + return miticirc + + def local_miti_readout_circ_by_mask(self, bsl: List[str]) -> List[Circuit]: + cs = [] + n = max(self.cal_qubits) + 1 # type: ignore + for bs in bsl: + c = Circuit(n) + for j, i in enumerate(bs): + if i == "1": + c.X(j) # type: ignore + cs.append(c) + return cs + + def global_miti_readout_circ(self) -> List[Circuit]: + """ + Generate circuits for global calibration. + + :return: circuit list + :rtype: List[Circuit] + """ + miticirc = [] + for i in range(2 ** len(self.cal_qubits)): # type: ignore + name = "{:0" + str(len(self.cal_qubits)) + "b}" # type: ignore + lisbs = [int(x) for x in name.format(i)] + c = Circuit(max(self.cal_qubits) + 1) # type: ignore + for k in range(len(self.cal_qubits)): # type: ignore + if lisbs[k] == 1: + c.X(self.cal_qubits[k]) # type: ignore + miticirc.append(c) + return miticirc + + def cals_from_api( + self, qubits: Union[int, List[int]], device: Optional[str] = None + ) -> None: + """ + Get local calibriation matrix from cloud API from tc supported providers + + :param qubits: list of physical qubits to be calibriated + :type qubits: Union[int, List[int]] + :param device: the device str to qurey for the info, defaults to None + :type device: Optional[str], optional + """ + if device is None and self.devstr is None: + raise ValueError("device argument cannot be None") + if device is None: + device = self.devstr.split("?")[0] # type: ignore + + if isinstance(qubits, int): + qubits = list(range(qubits)) + + self.cal_qubits = qubits # type: ignore + self.local = True # type: ignore + self.single_qubit_cals = [None] * (max(self.cal_qubits) + 1) # type: ignore + + from ..cloud.apis import list_properties + + pro = list_properties(device) + + for q in qubits: + error01 = pro["bits"][q]["ReadoutF0Err"] + error10 = pro["bits"][q]["ReadoutF1Err"] + readout_single = np.array( + [ + [1 - error01, error10], + [error01, 1 - error10], + ] + ) + self.single_qubit_cals[q] = readout_single # type: ignore + # only works in zero based qubit information + + def cals_from_system( + self, + qubits: Union[int, List[int]], + shots: int = 8192, + method: str = "local", + masks: Optional[List[str]] = None, + ) -> None: + """ + Get calibrattion information from system. + + :param qubits: calibration qubit list (physical qubits on device) + :type qubits: Sequence[Any] + :param shots: shots used for runing the circuit, defaults to 8192 + :type shots: int, optional + :param method: calibration method, defaults to "local", it can also be "global" + :type method: str, optional + """ + if not is_sequence(qubits): + qubits = list(range(qubits)) # type: ignore + qubits.sort() # type: ignore + self.cal_qubits = qubits # type: ignore + self.cal_shots = shots + + if method == "local": + self.local = True # type: ignore + if masks is None: + miticirc = self.local_miti_readout_circ() + lbsall = self.execute_fun(miticirc, self.cal_shots) + lbs = [marginal_count(i, self.cal_qubits) for i in lbsall] # type: ignore + + self.single_qubit_cals = [None] * (max(self.cal_qubits) + 1) # type: ignore + for i in range(len(self.cal_qubits)): # type: ignore + error00 = 0 + for s in lbs[0]: + if s[i] == "0": + error00 = error00 + lbs[0][s] / self.cal_shots # type: ignore + + error10 = 0 + for s in lbs[1]: + if s[i] == "0": + error10 = error10 + lbs[1][s] / self.cal_shots # type: ignore + + readout_single = np.array( + [ + [error00, error10], + [1 - error00, 1 - error10], + ] + ) + self.single_qubit_cals[self.cal_qubits[i]] = readout_single # type: ignore + + else: + miticirc = self.local_miti_readout_circ_by_mask(masks) + lbsall = self.execute_fun(miticirc, self.cal_shots) + # lbs = [marginal_count(i, self.cal_qubits) for i in lbsall] # type: ignore + self.single_qubit_cals = [None] * (max(self.cal_qubits) + 1) # type: ignore + for i in self.cal_qubits: # type: ignore + error00n = 0 + error00d = 0 + error11n = 0 + error11d = 0 + for j, bs in enumerate(lbsall): + ans = masks[j][i] + if ans == "0": + error00d += self.cal_shots + else: # ans == "1" + error11d += self.cal_shots + for s in bs: + if s[i] == ans and ans == "0": + error00n += bs[s] + elif s[i] == ans and ans == "1": + error11n += bs[s] + + readout_single = np.array( + [ + [error00n / error00d, 1 - error11n / error11d], + [1 - error00n / error00d, error11n / error11d], + ] + ) + self.single_qubit_cals[i] = readout_single # type: ignore + + elif method == "global": + self.local = False # type: ignore + miticirc = self.global_miti_readout_circ() + lbsall = self.execute_fun(miticirc, self.cal_shots) + self.global_cal = lbsall + + else: + raise ValueError("Unrecognized `miti_method`: %s" % method) + + def mitigate_probability( + self, probability_noise: Tensor, method: str = "inverse" + ) -> Tensor: + """ + Get the mitigated probability. + + :param probability_noise: probability of raw count + :type probability_noise: Tensor + :param method: mitigation methods, defaults to "inverse", it can also be "square" + :type method: str, optional + :return: mitigated probability + :rtype: Tensor + """ + calmatrix = self.get_matrix() + if method == "inverse": + X = np.linalg.inv(calmatrix) + Y = probability_noise + probability_cali = X @ Y + else: # method="square" + + def fun(x: Any) -> Any: + return sum((probability_noise - calmatrix @ x) ** 2) + + x0 = np.random.rand(len(probability_noise)) + cons = {"type": "eq", "fun": lambda x: 1 - sum(x)} + bnds = tuple((0, 1) for x in x0) + res = minimize( + fun, x0, method="SLSQP", constraints=cons, bounds=bnds, tol=1e-6 + ) + probability_cali = res.x + return probability_cali + + def apply_readout_mitigation(self, raw_count: ct, method: str = "inverse") -> ct: + """ + Main readout mitigation program for method="inverse" or "square" + + :param raw_count: the raw count + :type raw_count: ct + :param method: mitigation method, defaults to "inverse" + :type method: str, optional + :return: mitigated count + :rtype: ct + """ + probability = count2vec(raw_count) + shots = sum([v for k, v in raw_count.items()]) + probability = self.mitigate_probability(probability, method=method) + probability = probability * shots + return vec2count(probability, prune=True) + + def mapping_preprocess( + self, + counts: ct, + qubits: Sequence[int], + positional_logical_mapping: Optional[Dict[int, int]] = None, + logical_physical_mapping: Optional[Dict[int, int]] = None, + ) -> ct: + """ + Preprocessing to deal with qubit mapping, including positional_logical_mapping and + logical_physical_mapping. Return self.use_qubits(physical) and corresponding counts. + + :param counts: raw_counts on positional_qubits + :type counts: ct + :param qubits: user-defined logical qubits to show final mitted results + :type qubits: Sequence[int] + :param positional_logical_mapping: positional_logical_mapping, defaults to None. + :type positional_logical_mapping: Optional[Dict[int, int]], optional + :param logical_physical_mapping: logical_physical_mapping, defaults to None + :type logical_physical_mapping: Optional[Dict[int, int]], optional + :return: counts on self.use_qubit(physical) + :rtype: ct + """ + # counts [0,1,2] / logic_qubit(circuit,meas) [3,4,6] / physic_qubit [2,8,6] + # / use_logic_qubits [4,3] / cal_qubits[0-8] + # input: counts [0,1,2], use_logical_qubits [4,3], logic_physic_mapping {3:2,4:8,6:6}, + # input: position_logical_mapping{0:3,1:4,2:6} + # self.use_qubits(physic)[8,2] + # use_position_qubits [1,0] + # counts = marginal_count(counts[0,1,2], [1,0]) corresponds to self.use_qubits(physic) + + if not is_sequence(qubits): + qubits = list(range(qubits)) # type: ignore + + if positional_logical_mapping is None: + use_position_qubits = qubits + else: + logical_positional_mapping = { + v: k for k, v in positional_logical_mapping.items() + } + use_position_qubits = [logical_positional_mapping[lq] for lq in qubits] + + if logical_physical_mapping is None: + self.use_qubits = qubits # type: ignore + else: + self.use_qubits = [logical_physical_mapping[lq] for lq in qubits] # type: ignore + + counts = marginal_count(counts, use_position_qubits) + + if not set(self.use_qubits).issubset(set(self.cal_qubits)): # type: ignore + raise ValueError( + "The qubit list used in calculation must included in the calibration qubit list." + ) + + return counts + + def apply_correction( + self, + counts: ct, + qubits: Sequence[int], + positional_logical_mapping: Optional[Dict[int, int]] = None, + logical_physical_mapping: Optional[Dict[int, int]] = None, + distance: Optional[int] = None, + method: str = "constrained_least_square", + max_iter: int = 25, + tol: float = 1e-5, + return_mitigation_overhead: bool = False, + details: bool = False, + ) -> ct: + """ + Main readout mitigation program for all methods. + + :param counts: raw count + :type counts: ct + :param qubits: user-defined logical qubits to show final mitted results + :type qubits: Sequence[int] + :param positional_logical_mapping: positional_logical_mapping, defaults to None. + :type positional_logical_mapping: Optional[Dict[int, int]], optional + :param logical_physical_mapping: logical_physical_mapping, defaults to None + :type logical_physical_mapping: Optional[Dict[int, int]], optional + :param distance: defaults to None + :type distance: int, optional + :param method: mitigation method, defaults to "square" + :type method: str, optional + :param max_iter: defaults to 25 + :type max_iter: int, optional + :param tol: defaults to 1e-5 + :type tol: float, optional + :param return_mitigation_overhead:defaults to False + :type return_mitigation_overhead: bool, optional + :param details: defaults to False + :type details: bool, optional + :return: mitigated count + :rtype: ct + """ + # if not is_sequence(qubits): + # qubits = list(range(qubits)) # type: ignore + # self.use_qubits = qubits # type: ignore + # if not set(self.use_qubits).issubset(set(self.cal_qubits)): # type: ignore + # raise ValueError( + # "The qubit list used in calculation must included in the calibration qubit list." + # ) + + # counts = marginal_count(counts, self.use_qubits) # type: ignore + + counts = self.mapping_preprocess( + counts=counts, + qubits=qubits, + positional_logical_mapping=positional_logical_mapping, + logical_physical_mapping=logical_physical_mapping, + ) + + qubits = self.use_qubits # type: ignore + + shots = sum([v for _, v in counts.items()]) + # methods for small system, "global" calibration only fit for those methods. + if method in ["inverse", "pseudo_inverse"]: + mitcounts = self.apply_readout_mitigation(counts, method="inverse") + return sort_count(mitcounts) + elif method in ["square", "constrained_least_square"]: + mitcounts = self.apply_readout_mitigation(counts, method="square") + return sort_count(mitcounts) + if mthree_installed is False: + warnings.warn( + " To use [scalable-] related methods, please pip install mthree !" + ) + + if len(counts) == 0: + raise M3Error("Input counts is any empty dict.") + given_list = False + if isinstance(counts, (list, np.ndarray)): + given_list = True + if not given_list: + counts = [counts] # type: ignore + + if isinstance(qubits, dict): + # If a mapping was given for qubits + qubits = [list(qubits)] # type: ignore + elif not any(isinstance(qq, (list, tuple, np.ndarray, dict)) for qq in qubits): + qubits = [qubits] * len(counts) # type: ignore + else: + if isinstance(qubits[0], dict): + # assuming passed a list of mappings + qubits = [list(qu) for qu in qubits] # type: ignore + + if len(qubits) != len(counts): + raise M3Error("Length of counts does not match length of qubits.") + + quasi_out = [] + for idx, cnts in enumerate(counts): + quasi_out.append( + self._apply_correction( + cnts, + qubits=qubits[idx], + distance=distance, + method=method, + max_iter=max_iter, + tol=tol, + return_mitigation_overhead=return_mitigation_overhead, + details=details, + ) + ) + + if not given_list: + r = quasi_out[0] + r = sort_count(r) + r = {k: v * shots for k, v in r.items()} + return sort_count(r) + # return quasi_out[0] # type: ignore + mitcounts = QuasiCollection(quasi_out) + return sort_count(mitcounts.nearest_probability_distribution()) # type: ignore + + def _apply_correction( # type: ignore + self, + counts, + qubits, + distance=None, + method="auto", + max_iter=25, + tol=1e-5, + return_mitigation_overhead=False, + details=False, + ): + if self.local is False: + raise ValueError("M3 methods need local calibration") + + # This is needed because counts is a Counts object in Qiskit not a dict. + counts = dict(counts) + shots = sum(counts.values()) + + # If distance is None, then assume max distance. + num_bits = len(qubits) + num_elems = len(counts) + if distance is None: + distance = num_bits + + # check if len of bitstrings does not equal number of qubits passed. + bitstring_len = len(next(iter(counts))) + if bitstring_len != num_bits: + raise M3Error( + "Bitstring length ({}) does not match".format(bitstring_len) + + " number of qubits ({})".format(num_bits) + ) + + # Check if no cals done yet + if self.single_qubit_cals is None: + warnings.warn("No calibration data. Calibrating: {}".format(qubits)) + self._grab_additional_cals(qubits, method=self.cal_method) # type: ignore + + # Check if one or more new qubits need to be calibrated. + missing_qubits = [qq for qq in qubits if self.single_qubit_cals[qq] is None] # type: ignore + if any(missing_qubits): + warnings.warn( + "Computing missing calibrations for qubits: {}".format(missing_qubits) + ) + self._grab_additional_cals(missing_qubits, method=self.cal_method) # type: ignore + + if method == "M3_auto": + import psutil + + current_free_mem = psutil.virtual_memory().available / 1024**3 + # First check if direct method can be run + if num_elems <= self.iter_threshold and ( + (num_elems**2 + num_elems) * 8 / 1024**3 < current_free_mem / 2 + ): + method = "M3_direct" + else: + method = "M3_iterative" + + if method == "M3_direct": + st = perf_counter() + mit_counts, col_norms, gamma = self._direct_solver( + counts, qubits, distance, return_mitigation_overhead + ) + dur = perf_counter() - st + mit_counts.shots = shots + if gamma is not None: + mit_counts.mitigation_overhead = gamma * gamma + if details: + info = {"method": "direct", "time": dur, "dimension": num_elems} + info["col_norms"] = col_norms + return mit_counts, info + return mit_counts + + elif method == "M3_iterative": + iter_count = np.zeros(1, dtype=int) + + def callback(_): # type: ignore + iter_count[0] += 1 + + if details: + st = perf_counter() + mit_counts, col_norms, gamma = self._matvec_solver( + counts, + qubits, + distance, + tol, + max_iter, + 1, + callback, + return_mitigation_overhead, + ) + dur = perf_counter() - st + mit_counts.shots = shots + if gamma is not None: + mit_counts.mitigation_overhead = gamma * gamma + info = {"method": "iterative", "time": dur, "dimension": num_elems} + info["iterations"] = iter_count[0] + info["col_norms"] = col_norms + return mit_counts, info + # pylint: disable=unbalanced-tuple-unpacking + mit_counts, gamma = self._matvec_solver( + counts, + qubits, + distance, + tol, + max_iter, + 0, + None, + return_mitigation_overhead, + ) + mit_counts.shots = shots + if gamma is not None: + mit_counts.mitigation_overhead = gamma * gamma + return mit_counts + + else: + raise M3Error("Invalid method: {}".format(method)) + + def reduced_cal_matrix(self, counts, qubits, distance=None): # type: ignore + counts = dict(counts) + # If distance is None, then assume max distance. + num_bits = len(qubits) + if distance is None: + distance = num_bits + + # check if len of bitstrings does not equal number of qubits passed. + bitstring_len = len(next(iter(counts))) + if bitstring_len != num_bits: + raise M3Error( + "Bitstring length ({}) does not match".format(bitstring_len) + + " number of qubits ({})".format(num_bits) + ) + + cals = self._form_cals(qubits) + A, counts, _ = _reduced_cal_matrix(counts, cals, num_bits, distance) + return A, counts + + def _direct_solver( # type: ignore + self, counts, qubits, distance=None, return_mitigation_overhead=False + ): + cals = self._form_cals(qubits) + num_bits = len(qubits) + A, sorted_counts, col_norms = _reduced_cal_matrix( + counts, cals, num_bits, distance + ) + vec = counts_to_vector(sorted_counts) + LU = la.lu_factor(A, check_finite=False) + x = la.lu_solve(LU, vec, check_finite=False) + gamma = None + if return_mitigation_overhead: + gamma = ainv_onenorm_est_lu(A, LU) + out = vector_to_quasiprobs(x, sorted_counts) + return out, col_norms, gamma + + def _matvec_solver( # type: ignore + self, + counts, + qubits, + distance, + tol=1e-5, + max_iter=25, + details=0, + callback=None, + return_mitigation_overhead=False, + ): + cals = self._form_cals(qubits) + M = M3MatVec(dict(counts), cals, distance) + L = spla.LinearOperator( + (M.num_elems, M.num_elems), matvec=M.matvec, rmatvec=M.rmatvec + ) + diags = M.get_diagonal() + + def precond_matvec(x): # type: ignore + out = x / diags + return out + + P = spla.LinearOperator((M.num_elems, M.num_elems), precond_matvec) + vec = counts_to_vector(M.sorted_counts) + out, error = spla.gmres( + L, vec, tol=tol, atol=tol, maxiter=max_iter, M=P, callback=callback + ) + if error: + raise M3Error("GMRES did not converge: {}".format(error)) + + gamma = None + if return_mitigation_overhead: + gamma = ainv_onenorm_est_iter(M, tol=tol, max_iter=max_iter) + + quasi = vector_to_quasiprobs(out, M.sorted_counts) + if details: + return quasi, M.get_col_norms(), gamma + return quasi, gamma + + def expectation( + self, + counts: ct, + z: Optional[Sequence[int]] = None, + diagonal_op: Optional[Tensor] = None, + positional_logical_mapping: Optional[Dict[int, int]] = None, + logical_physical_mapping: Optional[Dict[int, int]] = None, + method: str = "constrained_least_square", + ) -> float: + """ + Calculate expectation value after readout error mitigation + + :param counts: raw counts + :type counts: ct + :param z: if defaults as None, then ``diagonal_op`` must be set + a list of qubit that we measure Z op on + :type z: Optional[Sequence[int]] + :param diagoal_op: shape [n, 2], explicitly indicate the diagonal op on each qubit + eg. [1, -1] for z [1, 1] for I, etc. + :type diagoal_op: Tensor + :param positional_logical_mapping: positional_logical_mapping, defaults to None. + :type positional_logical_mapping: Optional[Dict[int, int]], optional + :param logical_physical_mapping: logical_physical_mapping, defaults to None + :type logical_physical_mapping: Optional[Dict[int, int]], optional + :param method: readout mitigation method, defaults to "constrained_least_square" + :type method: str, optional + :return: expectation value after readout error mitigation + :rtype: float + """ + # https://arxiv.org/pdf/2006.14044.pdf + + # count[0,1,2], logical[3,4,5], physic[6,7,8], z=[4,5](logical) + # z1=[1,2](position), z=[7,8] (physic) + # diagonal_op [i6 z7 z8 ] + + n = len(list(counts.keys())[0]) + + if positional_logical_mapping is None: + logical_qubits = list(range(n)) + else: + logical_qubits = [positional_logical_mapping[pq] for pq in range(n)] + if logical_physical_mapping is None: + physical_qubits = logical_qubits + else: + physical_qubits = [logical_physical_mapping[i] for i in logical_qubits] + + if z is None: + z1 = None + if diagonal_op is None: + raise ValueError("One of `z` and `diagonal_op` must be set") + else: + z1 = [logical_qubits.index(i) for i in z] + + if self.local is True: + inv_single_qubit_cals = [] + for i in physical_qubits: + inv_single_qubit_cals.append(np.linalg.pinv(self.single_qubit_cals[i])) + + if z is None: + diagonal_op = [ + diagonal_op[i] @ inv_single_qubit_cals[i] + for i in range(diagonal_op) + ] + else: + diagonal_op = [ + ( + [1, -1] @ inv_single_qubit_cals[i] + if i in z1 + else [1, 1] @ inv_single_qubit_cals[i] + ) + for i in range(n) + ] + + mit_value = expectation(counts, diagonal_op=diagonal_op) + + else: + mit_count = self.apply_correction( + counts, + qubits=logical_qubits, + positional_logical_mapping=positional_logical_mapping, + logical_physical_mapping=logical_physical_mapping, + method=method, + ) + + mit_value = expectation(mit_count, z1, diagonal_op) + + return mit_value diff --git a/tensorcircuit/shadows.py b/tensorcircuit/shadows.py new file mode 100644 index 00000000..63cad7f6 --- /dev/null +++ b/tensorcircuit/shadows.py @@ -0,0 +1,485 @@ +""" +Classical shadows functions +""" + +from typing import Any, Union, Optional, Sequence, Tuple, List +from string import ascii_letters as ABC + +import numpy as np + +from .cons import backend, dtypestr, rdtypestr +from .circuit import Circuit + +Tensor = Any + + +def shadow_bound( + observables: Union[Tensor, Sequence[int]], epsilon: float, delta: float = 0.01 +) -> Tuple[int, int]: + r"""Calculate the shadow bound of the Pauli observables, please refer to the Theorem S1 and Lemma S3 in + Huang, H.-Y., R. Kueng, and J. Preskill, 2020, Nat. Phys. 16, 1050. + + :param observables: shape = (nq,) or (M, nq), where nq is the number of qubits, M is the number of observables + :type: Union[Tensor, Sequence[int]] + :param epsilon: error on the estimator + :type: float + :param delta: rate of failure for the bound to hold + :type: float + + :return Nk: number of snapshots + :rtype: int + :return k: number of equal parts to split the shadow snapshot states to compute the median of means. + k=1 (default) corresponds to simply taking the mean over all shadow snapshot states. + :rtype: int + """ + count = np.sign(np.asarray(observables)) + if len(count.shape) == 1: + count = count[None, :] + M = count.shape[0] + k = np.ceil(2 * np.log(2 * M / delta)) + max_length = np.max(np.sum(count, axis=1)) + N = np.ceil((34 / epsilon**2) * 3**max_length) + return int(N * k), int(k) + + +def shadow_snapshots( + psi: Tensor, + pauli_strings: Tensor, + status: Optional[Tensor] = None, + sub: Optional[Sequence[int]] = None, + measurement_only: bool = False, +) -> Tensor: + r"""To generate the shadow snapshots from given pauli string observables on psi + + :param psi: shape = (2 ** nq,), where nq is the number of qubits + :type: Tensor + :param pauli_strings: shape = (ns, nq), where ns is the number of pauli strings + :type: Tensor + :param status: shape = None or (ns, repeat), where repeat is the times to measure on one pauli string + :type: Optional[Tensor] + :param sub: qubit indices of subsystem + :type: Optional[Sequence[int]] + :param measurement_only: return snapshots (True) or snapshot states (False), default=False + :type: bool + + :return snapshots: shape = (ns, repeat, nq) if measurement_only=True otherwise (ns, repeat, nq, 2, 2) + :rtype: Tensor + """ + pauli_strings = backend.cast(pauli_strings, dtype="int32") - 1 + ns, nq = pauli_strings.shape + if 2**nq != len(psi): + raise ValueError( + f"The number of qubits of psi and pauli_strings should be the same, " + f"but got {nq} and {int(np.log2(len(psi)))}." + ) + if status is None: + status = backend.convert_to_tensor(np.random.rand(ns, 1)) + elif status.shape[0] != ns: + raise ValueError(f"status.shape[0] should be {ns}, but got {status.shape[0]}.") + status = backend.cast(status, dtype=rdtypestr) + repeat = status.shape[1] + + angles = backend.cast( + backend.convert_to_tensor( + [ + [-np.pi / 2, np.pi / 4, 0], + [np.pi / 4, np.pi / 2, 0], + [0, 0, 0], + ] + ), + dtype=rdtypestr, + ) # (3, 3) + + def proj_measure(pauli_string: Tensor, st: Tensor) -> Tensor: + c_ = Circuit(nq, inputs=psi) + for i in range(nq): + c_.r( # type: ignore + i, + theta=backend.gather1d( + backend.gather1d(angles, backend.gather1d(pauli_string, i)), 0 + ), + alpha=backend.gather1d( + backend.gather1d(angles, backend.gather1d(pauli_string, i)), 1 + ), + phi=backend.gather1d( + backend.gather1d(angles, backend.gather1d(pauli_string, i)), 2 + ), + ) + return c_.sample(batch=repeat, format="sample_bin", allow_state=True, status=st) + + vpm = backend.vmap(proj_measure, vectorized_argnums=(0, 1)) + snapshots = vpm(pauli_strings, status) # (ns, repeat, nq) + if measurement_only: + return snapshots if sub is None else slice_sub(snapshots, sub) + else: + return local_snapshot_states(snapshots, pauli_strings + 1, sub) + + +def local_snapshot_states( + snapshots: Tensor, pauli_strings: Tensor, sub: Optional[Sequence[int]] = None +) -> Tensor: + r"""To generate the local snapshots states from snapshots and pauli strings + + :param snapshots: shape = (ns, repeat, nq) + :type: Tensor + :param pauli_strings: shape = (ns, nq) or (ns, repeat, nq) + :type: Tensor + :param sub: qubit indices of subsystem + :type: Optional[Sequence[int]] + + :return lss_states: shape = (ns, repeat, nq, 2, 2) + :rtype: Tensor + """ + pauli_strings = backend.cast(pauli_strings, dtype="int32") - 1 + if len(pauli_strings.shape) < len(snapshots.shape): + pauli_strings = backend.tile( + pauli_strings[:, None, :], (1, snapshots.shape[1], 1) + ) # (ns, repeat, nq) + + X_dm = backend.cast( + backend.convert_to_tensor([[[1, 1], [1, 1]], [[1, -1], [-1, 1]]]) / 2, + dtype=dtypestr, + ) + Y_dm = backend.cast( + backend.convert_to_tensor( + np.array([[[1, -1j], [1j, 1]], [[1, 1j], [-1j, 1]]]) / 2 + ), + dtype=dtypestr, + ) + Z_dm = backend.cast( + backend.convert_to_tensor([[[1, 0], [0, 0]], [[0, 0], [0, 1]]]), dtype=dtypestr + ) + pauli_dm = backend.stack((X_dm, Y_dm, Z_dm), axis=0) # (3, 2, 2, 2) + + def dm(p: Tensor, s: Tensor) -> Tensor: + return backend.gather1d(backend.gather1d(pauli_dm, p), s) + + v = backend.vmap(dm, vectorized_argnums=(0, 1)) + vv = backend.vmap(v, vectorized_argnums=(0, 1)) + vvv = backend.vmap(vv, vectorized_argnums=(0, 1)) + + lss_states = vvv(pauli_strings, snapshots) + if sub is not None: + lss_states = slice_sub(lss_states, sub) + return 3 * lss_states - backend.eye(2)[None, None, None, :, :] + + +def global_shadow_state( + snapshots: Tensor, + pauli_strings: Optional[Tensor] = None, + sub: Optional[Sequence[int]] = None, +) -> Tensor: + r"""To generate the global shadow state from local snapshot states or snapshots and pauli strings + + :param snapshots: shape = (ns, repeat, nq, 2, 2) or (ns, repeat, nq) + :type: Tensor + :param pauli_strings: shape = None or (ns, nq) or (ns, repeat, nq) + :type: Optional[Tensor] + :param sub: qubit indices of subsystem + :type: Optional[Sequence[int]] + + :return gsdw_state: shape = (2 ** nq, 2 ** nq) + :rtype: Tensor + """ + if pauli_strings is not None: + if len(snapshots.shape) != 3: + raise ValueError( + f"snapshots should be 3-d if pauli_strings is not None, got {len(snapshots.shape)}-d instead." + ) + lss_states = local_snapshot_states( + snapshots, pauli_strings, sub + ) # (ns, repeat, nq_sub, 2, 2) + else: + if sub is not None: + lss_states = slice_sub(snapshots, sub) + else: + lss_states = snapshots # (ns, repeat, nq, 2, 2) + + nq = lss_states.shape[2] + + def tensor_prod(dms: Tensor) -> Tensor: + res = backend.gather1d(dms, 0) + for i in range(1, nq): + res = backend.kron(res, backend.gather1d(dms, i)) + return res + + v = backend.vmap(tensor_prod, vectorized_argnums=0) + vv = backend.vmap(v, vectorized_argnums=0) + gss_states = vv(lss_states) + return backend.mean(gss_states, axis=(0, 1)) + + +def expectation_ps_shadow( + snapshots: Tensor, + pauli_strings: Optional[Tensor] = None, + x: Optional[Sequence[int]] = None, + y: Optional[Sequence[int]] = None, + z: Optional[Sequence[int]] = None, + ps: Optional[Sequence[int]] = None, + k: int = 1, +) -> List[Tensor]: + r"""To calculate the expectation value of an observable on shadow snapshot states + + :param snapshots: shape = (ns, repeat, nq, 2, 2) or (ns, repeat, nq) + :type: Tensor + :param pauli_strings: shape = None or (ns, nq) or (ns, repeat, nq) + :type: Optional[Tensor] + :param x: sites to apply X gate, defaults to None + :type: Optional[Sequence[int]] + :param y: sites to apply Y gate, defaults to None + :type: Optional[Sequence[int]] + :param z: sites to apply Z gate, defaults to None + :type: Optional[Sequence[int]] + :param ps: or one can apply a ps structures instead of x, y, z, e.g. [1, 1, 0, 2, 3, 0] for X_0X_1Y_3Z_4 + defaults to None, ps can overwrite x, y and z + :type: Optional[Sequence[int]] + :param k: Number of equal parts to split the shadow snapshot states to compute the median of means. + k=1 (default) corresponds to simply taking the mean over all shadow snapshot states. + :type: int + + :return expectation values: shape = (k,) + :rtype: List[Tensor] + """ + if pauli_strings is not None: + if len(snapshots.shape) != 3: + raise ValueError( + f"snapshots should be 3-d if pauli_strings is not None, got {len(snapshots.shape)}-d instead." + ) + lss_states = local_snapshot_states( + snapshots, pauli_strings + ) # (ns, repeat, nq, 2, 2) + else: + lss_states = snapshots # (ns, repeat, nq, 2, 2) + ns, repeat, nq, _, _ = lss_states.shape + ns *= repeat + ss_states = backend.reshape(lss_states, (ns, nq, 2, 2)) + + if ps is None: + ps = [0] * nq + if x is not None: + for i in x: + ps[i] = 1 + if y is not None: + for i in y: + ps[i] = 2 + if z is not None: + for i in z: + ps[i] = 3 + elif len(ps) != nq: + raise ValueError( + f"The number of qubits of the shadow state is {nq}, but got a {len(ps)}-qubit pauli string observable." + ) + ps = backend.cast(backend.convert_to_tensor(ps), dtype="int32") # (nq,) + + paulis = backend.convert_to_tensor( + backend.cast( + np.array( + [ + [[1, 0], [0, 1]], + [[0, 1], [1, 0]], + [[0, -1j], [1j, 0]], + [[1, 0], [0, -1]], + ] + ), + dtype=dtypestr, + ) + ) # (4, 2, 2) + + def trace_paulis_prod(dm: Tensor, idx: Tensor) -> Tensor: + return backend.real(backend.trace(backend.gather1d(paulis, idx) @ dm)) + + v = backend.vmap(trace_paulis_prod, vectorized_argnums=(0, 1)) # (nq,) + + def prod(dm: Tensor) -> Tensor: + return backend.shape_prod(v(dm, ps)) + + vv = backend.vmap(prod, vectorized_argnums=0) # (ns,) + + batch = int(np.ceil(ns / k)) + return [backend.mean(vv(ss_states[i : i + batch])) for i in range(0, ns, batch)] + + +def entropy_shadow( + snapshots: Tensor, + pauli_strings: Optional[Tensor] = None, + sub: Optional[Sequence[int]] = None, + alpha: int = 2, +) -> Tensor: + r"""To calculate the Renyi entropy of a subsystem from shadow state or shadow snapshot states + + :param snapshots: shape = (ns, repeat, nq, 2, 2) or (ns, repeat, nq) + :type: Tensor + :param pauli_strings: shape = None or (ns, nq) or (ns, repeat, nq) + :type: Optional[Tensor] + :param sub: qubit indices of subsystem + :type: Optional[Sequence[int]] + :param alpha: order of the Renyi entropy, alpha=1 corresponds to the von Neumann entropy + :type: int + + :return Renyi entropy: shape = () + :rtype: Tensor + """ + if alpha <= 0: + raise ValueError("Alpha should not be less than 1!") + + sdw_rdm = global_shadow_state(snapshots, pauli_strings, sub) # (2 ** nq, 2 ** nq) + + evs = backend.relu(backend.real(backend.eigvalsh(sdw_rdm))) + evs /= backend.sum(evs) + if alpha == 1: + return -backend.sum(evs * backend.log(evs + 1e-12)) + else: + return backend.log(backend.sum(backend.power(evs, alpha))) / (1 - alpha) + + +def renyi_entropy_2(snapshots: Tensor, sub: Optional[Sequence[int]] = None) -> Tensor: + r"""To calculate the second order Renyi entropy of a subsystem from snapshot, please refer to + Brydges, T. et al. Science 364, 260–263 (2019). This function is not jitable. + + :param snapshots: shape = (ns, repeat, nq) + :type: Tensor + :param sub: qubit indices of subsystem + :type: Optional[Sequence[int]] + + :return second order Renyi entropy: shape = () + :rtype: Tensor + """ + if sub is not None: + snapshots = slice_sub(snapshots, sub) + snapshots = backend.cast(snapshots, dtype="int32") + ns, repeat, nq = snapshots.shape + + count = {} + for i, ss in enumerate(snapshots): + for s in ss: + s = tuple(backend.numpy(s)) + if s not in count: + count[s] = np.zeros(ns) + count[s][i] += 1 + + tr = 0.0 + for x in count: + for y in count: + h = np.sum((np.asarray(x) + np.asarray(y)) % 2) + pp_mean = np.mean(count[x] * count[y]) / repeat / repeat + tr += pp_mean * (-2.0) ** (-h) + return -np.log(tr * 2**nq) + + +def global_shadow_state1( + snapshots: Tensor, + pauli_strings: Optional[Tensor] = None, + sub: Optional[Sequence[int]] = None, +) -> Tensor: + r"""To generate the global snapshots states from local snapshot states or snapshots and pauli strings + + :param snapshots: shape = (ns, repeat, nq, 2, 2) or (ns, repeat, nq) + :type: Tensor + :param pauli_strings: shape = None or (ns, nq) or (ns, repeat, nq) + :type: Optional[Tensor] + :param sub: qubit indices of subsystem + :type: Optional[Sequence[int]] + + :return gsdw_state: shape = (2 ** nq, 2 ** nq) + :rtype: Tensor + """ + if pauli_strings is not None: + if len(snapshots.shape) != 3: + raise ValueError( + f"snapshots should be 3-d if pauli_strings is not None, got {len(snapshots.shape)}-d instead." + ) + lss_states = local_snapshot_states( + snapshots, pauli_strings, sub + ) # (ns, repeat, nq_sub, 2, 2) + else: + if sub is not None: + lss_states = slice_sub(snapshots, sub) + else: + lss_states = snapshots # (ns, repeat, nq, 2, 2) + lss_states = backend.transpose( + lss_states, (2, 0, 1, 3, 4) + ) # (nq, ns, repeat, 2, 2) + nq, ns, repeat, _, _ = lss_states.shape + + old_indices = [f"ab{ABC[2 + 2 * i: 4 + 2 * i]}" for i in range(nq)] + new_indices = f"ab{ABC[2:2 * nq + 2:2]}{ABC[3:2 * nq + 2:2]}" + + gss_states = backend.reshape( + backend.einsum( + f'{",".join(old_indices)}->{new_indices}', *lss_states, optimize=True + ), + (ns, repeat, 2**nq, 2**nq), + ) + return backend.mean(gss_states, axis=(0, 1)) + + +def global_shadow_state2( + snapshots: Tensor, + pauli_strings: Optional[Tensor] = None, + sub: Optional[Sequence[int]] = None, +) -> Tensor: + r"""To generate the global snapshots states from local snapshot states or snapshots and pauli strings + + :param snapshots: shape = (ns, repeat, nq, 2, 2) or (ns, repeat, nq) + :type: Tensor + :param pauli_strings: shape = None or (ns, nq) or (ns, repeat, nq) + :type: Optional[Tensor] + :param sub: qubit indices of subsystem + :type: Optional[Sequence[int]] + + :return gsdw_state: shape = (2 ** nq, 2 ** nq) + :rtype: Tensor + """ + if pauli_strings is not None: + if len(snapshots.shape) != 3: + raise ValueError( + f"snapshots should be 3-d if pauli_strings is not None, got {len(snapshots.shape)}-d instead." + ) + lss_states = local_snapshot_states( + snapshots, pauli_strings, sub + ) # (ns, repeat, nq_sub, 2, 2) + else: + if sub is not None: + lss_states = slice_sub(snapshots, sub) + else: + lss_states = snapshots # (ns, repeat, nq, 2, 2) + nq = lss_states.shape[2] + + old_indices = [f"{ABC[2 * i: 2 + 2 * i]}" for i in range(nq)] + new_indices = f"{ABC[0:2 * nq:2]}{ABC[1:2 * nq:2]}" + + def tensor_prod(dms: Tensor) -> Tensor: + return backend.reshape( + backend.einsum( + f'{",".join(old_indices)}->{new_indices}', *dms, optimize=True + ), + (2**nq, 2**nq), + ) + + v = backend.vmap(tensor_prod, vectorized_argnums=0) + vv = backend.vmap(v, vectorized_argnums=0) + gss_states = vv(lss_states) + return backend.mean(gss_states, axis=(0, 1)) + + +def slice_sub(entirety: Tensor, sub: Sequence[int]) -> Tensor: + r"""To slice off the subsystem + + :param entirety: shape = (ns, repeat, nq, 2, 2) or (ns, repeat, nq) + :type: Tensor + :param sub: qubit indices of subsystem + :type: Sequence[int] + + :return subsystem: shape = (ns, repeat, nq_sub, 2, 2) + :rtype: Tensor + """ + if len(entirety.shape) < 3: + entirety = entirety[:, None, :] + + def slc(x: Tensor, idx: Tensor) -> Tensor: + return backend.gather1d(x, idx) + + v = backend.vmap(slc, vectorized_argnums=(1,)) + vv = backend.vmap(v, vectorized_argnums=(0,)) + vvv = backend.vmap(vv, vectorized_argnums=(0,)) + return vvv(entirety, backend.convert_to_tensor(sub)) diff --git a/tensorcircuit/simplify.py b/tensorcircuit/simplify.py index 6123c8dd..1cbcfc6a 100644 --- a/tensorcircuit/simplify.py +++ b/tensorcircuit/simplify.py @@ -1,6 +1,7 @@ """ Tensornetwork Simplification """ + # part of the implementations and ideas are inspired from # https://github.com/jcmgray/quimb/blob/a2968050eba5a8a04ced4bdaa5e43c4fb89edc33/quimb/tensor/tensor_core.py#L7309-L8293 # (Apache 2.0) diff --git a/tensorcircuit/templates/__init__.py b/tensorcircuit/templates/__init__.py index e40637e3..be2236ab 100644 --- a/tensorcircuit/templates/__init__.py +++ b/tensorcircuit/templates/__init__.py @@ -1,5 +1,9 @@ +from . import ansatz from . import blocks from . import chems from . import dataset from . import graphs from . import measurements +from . import conversions + +costfunctions = measurements diff --git a/tensorcircuit/templates/ansatz.py b/tensorcircuit/templates/ansatz.py new file mode 100644 index 00000000..0bb96258 --- /dev/null +++ b/tensorcircuit/templates/ansatz.py @@ -0,0 +1,93 @@ +""" +Shortcuts for reusable circuit ansatz +""" + +from typing import Any, List + +from ..circuit import Circuit as Circ + +Tensor = Any +Circuit = Any + + +def QAOA_ansatz_for_Ising( + params: List[float], + nlayers: int, + pauli_terms: Tensor, + weights: List[float], + full_coupling: bool = False, + mixer: str = "X", +) -> Circuit: + """ + Construct the QAOA ansatz for the Ising Model. + The number of qubits is determined by `pauli_terms`. + + :param params: A list of parameter values used in the QAOA ansatz. + :param nlayers: The number of layers in the QAOA ansatz. + :pauli_terms: A list of Pauli terms, where each term is represented as a list of 0/1 series. + :param weights: A list of weights corresponding to each Pauli term. + :param full_coupling (optional): A flag indicating whether to use all-to-all coupling in mixers. Default is False. + :paran mixer (optional): The mixer operator to use. Default is "X". The other options are "XY" and "ZZ". + :return: QAOA ansatz for Ising model. + """ + nqubits = len(pauli_terms[0]) + c: Any = Circ(nqubits) + for i in range(nqubits): + c.h(i) # Apply Hadamard gate to each qubit + + for j in range(nlayers): + # cost terms + for k, term in enumerate(pauli_terms): + index_of_ones = [] + for l in range(len(term)): + if term[l] == 1: + index_of_ones.append(l) + if len(index_of_ones) == 1: + c.rz(index_of_ones[0], theta=2 * weights[k] * params[2 * j]) + # Apply Rz gate with angle determined by weight and current parameter value + elif len(index_of_ones) == 2: + c.rzz( + index_of_ones[0], + index_of_ones[1], + theta=weights[k] * params[2 * j], + ) + # Apply exp1 gate with a custom unitary (zz_matrix) + # and angle determined by weight and current parameter value + else: + raise ValueError("Invalid number of Z terms") + + # prepare the coupling map for mixer terms + pairs = [] + if full_coupling is False: + for q0 in list(range(0, nqubits - 1, 2)) + list(range(1, nqubits - 1, 2)): + pairs.append([q0, q0 + 1]) + pairs.append([nqubits - 1, 0]) + elif full_coupling is True: + for q0 in range(nqubits - 1): + for q1 in range(q0 + 1, nqubits): + pairs.append([q0, q1]) + else: + raise ValueError("Invalid input.") + + # standard mixer + if mixer == "X": + for i in range(nqubits): + c.rx( + i, theta=params[2 * j + 1] + ) # Apply Rx gate with angle determined by current parameter value + + # XY mixer + elif mixer == "XY": + for [q0, q1] in pairs: + c.rxx(q0, q1, theta=params[2 * j + 1]) + c.ryy(q0, q1, theta=params[2 * j + 1]) + + # ZZ mixer + elif mixer == "ZZ": + for [q0, q1] in pairs: + c.rzz(q0, q1, theta=params[2 * j + 1]) + + else: + raise ValueError("Invalid mixer type.") + + return c diff --git a/tensorcircuit/templates/blocks.py b/tensorcircuit/templates/blocks.py index ea3370c4..1e376903 100644 --- a/tensorcircuit/templates/blocks.py +++ b/tensorcircuit/templates/blocks.py @@ -1,6 +1,7 @@ """ Shortcuts for measurement patterns on circuit """ + # circuit in, circuit out # pylint: disable=invalid-name @@ -136,7 +137,7 @@ def example_block( "fixed_choice": 1, } else: - split_conf = None # type: ignore + split_conf = None n = c._nqubits param = backend.reshape(param, [2 * nlayers, n]) for i in range(n): @@ -149,3 +150,54 @@ def example_block( for i in range(n): c.rx(i, theta=param[2 * j + 1, i]) return c + + +def qft( + c: Circuit, + *index: int, + do_swaps: bool = True, + inverse: bool = False, + insert_barriers: bool = False +) -> Circuit: + """ + This function applies quantum fourier transformation (QFT) to the selected circuit lines + + :param c: Circuit in + :type c: Circuit + :param *index: the indices of the circuit lines to apply QFT + :type *index: List[int] + :param do_swaps: Whether to include the final swaps in the QFT + :type do_swaps: bool + :param inverse: If True, the inverse Fourier transform is constructed + :type inverse: bool + :param insert_barriers: If True, barriers are inserted as visualization improvement + :type insert_barriers: bool + :return: Circuit c + :rtype: Circuit + """ + assert len(set(index)) == len(index), "There should not be any repetitive qubits" + if inverse: + if do_swaps: + for i in range(len(index) // 2): + c.swap(index[i], index[len(index) - 1 - i]) + for i in range(len(index) - 1, -1, -1): + rotation = -np.pi / 2 + for j in range(len(index) - 1, i, -1): + c.cphase(index[j], index[i], theta=rotation) + rotation /= 2 + c.H(index[i]) + if insert_barriers: + c.barrier_instruction(range(min(index), max(index) + 1)) + else: + for i in range(len(index)): + c.H(index[i]) + rotation = np.pi / 2 + for j in range(i + 1, len(index)): + c.cphase(index[j], index[i], theta=rotation) + rotation /= 2 + if insert_barriers: + c.barrier_instruction(range(min(index), max(index) + 1)) + if do_swaps: + for i in range(len(index) // 2): + c.swap(index[i], index[len(index) - 1 - i]) + return c diff --git a/tensorcircuit/templates/chems.py b/tensorcircuit/templates/chems.py index 0ac3a0da..bc0a2184 100644 --- a/tensorcircuit/templates/chems.py +++ b/tensorcircuit/templates/chems.py @@ -2,37 +2,6 @@ Useful utilities for quantum chemistry related task """ -from typing import Any, Tuple +from .conversions import get_ps # pylint:disable=unused-import -import numpy as np - -Tensor = Any - - -def get_ps(qo: Any, n: int) -> Tuple[Tensor, Tensor]: - """ - Get Pauli string array and weights array for a qubit Hamiltonian - as a sum of Pauli strings defined in openfermion ``QubitOperator``. - - :param qo: ``openfermion.ops.operators.qubit_operator.QubitOperator`` - :type qo: ``openfermion.ops.operators.qubit_operator.QubitOperator`` - :param n: The number of qubits - :type n: int - :return: Pauli String array and weights array - :rtype: Tuple[Tensor, Tensor] - """ - value = {"X": 1, "Y": 2, "Z": 3} - terms = qo.terms - res = [] - wts = [] - for key in terms: - bit = np.zeros(n, dtype=int) - for i in range(len(key)): - bit[key[i][0]] = value[key[i][1]] - w = terms[key] - res_t = tuple() # type: ignore - for i in range(n): - res_t = res_t + (bit[i],) - res.append(res_t) - wts.append(w) - return np.array(res), np.array(wts) +# backward compatibility for the entry point diff --git a/tensorcircuit/templates/conversions.py b/tensorcircuit/templates/conversions.py new file mode 100644 index 00000000..d848e405 --- /dev/null +++ b/tensorcircuit/templates/conversions.py @@ -0,0 +1,94 @@ +""" +helper functions for conversions +""" + +from typing import Any, Tuple, List + +import numpy as np + +Tensor = Any +Array = Any + + +def get_ps(qo: Any, n: int) -> Tuple[Tensor, Tensor]: + """ + Get Pauli string array and weights array for a qubit Hamiltonian + as a sum of Pauli strings defined in openfermion ``QubitOperator``. + + :param qo: ``openfermion.ops.operators.qubit_operator.QubitOperator`` + :type qo: ``openfermion.ops.operators.qubit_operator.QubitOperator`` + :param n: The number of qubits + :type n: int + :return: Pauli String array and weights array + :rtype: Tuple[Tensor, Tensor] + """ + value = {"X": 1, "Y": 2, "Z": 3} + terms = qo.terms + res = [] + wts = [] + for key in terms: + bit = np.zeros(n, dtype=int) + for i in range(len(key)): + bit[key[i][0]] = value[key[i][1]] + w = terms[key] + res_t = tuple() # type: ignore + for i in range(n): + res_t = res_t + (bit[i],) + res.append(res_t) + wts.append(w) + return np.array(res), np.array(wts) + + +def QUBO_to_Ising(Q: Tensor) -> Tuple[Tensor, List[float], float]: + """ + Cnvert the Q matrix into a the indication of pauli terms, the corresponding weights, and the offset. + The outputs are used to construct an Ising Hamiltonian for QAOA. + + :param Q: The n-by-n square and symmetric Q-matrix. + :return pauli_terms: A list of 0/1 series, where each element represents a Pauli term. + A value of 1 indicates the presence of a Pauli-Z operator, while a value of 0 indicates its absence. + :return weights: A list of weights corresponding to each Pauli term. + :return offset: A float representing the offset term of the Ising Hamiltonian. + """ + + # input is n-by-n symmetric numpy array corresponding to Q-matrix + # output is the components of Ising Hamiltonian + + n = Q.shape[0] + + # square matrix check + if Q[0].shape[0] != n: + raise ValueError("Matrix is not a square matrix.") + + offset = ( + np.triu(Q, 0).sum() / 2 + ) # Calculate the offset term of the Ising Hamiltonian + pauli_terms = [] # List to store the Pauli terms + weights = ( + -np.sum(Q, axis=1) / 2 + ) # Calculate the weights corresponding to each Pauli term + + for i in range(n): + term = np.zeros(n) + term[i] = 1 + pauli_terms.append( + term.tolist() + ) # Add a Pauli term corresponding to a single qubit + + for i in range(n - 1): + for j in range(i + 1, n): + term = np.zeros(n) + term[i] = 1 + term[j] = 1 + pauli_terms.append( + term.tolist() + ) # Add a Pauli term corresponding to a two-qubit interaction + + weight = ( + Q[i][j] / 2 + ) # Calculate the weight for the two-qubit interaction term + weights = np.concatenate( + (weights, weight), axis=None + ) # Add the weight to the weights list + + return pauli_terms, weights, offset diff --git a/tensorcircuit/templates/graphs.py b/tensorcircuit/templates/graphs.py index 4b2e1c9c..e382085c 100644 --- a/tensorcircuit/templates/graphs.py +++ b/tensorcircuit/templates/graphs.py @@ -1,6 +1,7 @@ """ Some common graphs and lattices """ + # pylint: disable=invalid-name from functools import partial diff --git a/tensorcircuit/templates/measurements.py b/tensorcircuit/templates/measurements.py index c72ccd9c..63f5c5b7 100644 --- a/tensorcircuit/templates/measurements.py +++ b/tensorcircuit/templates/measurements.py @@ -1,6 +1,7 @@ """ Shortcuts for measurement patterns on circuit """ + # circuit in, scalar out from typing import Any @@ -218,7 +219,7 @@ def heisenberg_measurements( hy: float = 0.0, reuse: bool = True, ) -> Tensor: - """ + r""" Evaluate Heisenberg energy expectation, whose Hamiltonian is defined on the lattice graph ``g`` as follows: (e are edges in graph ``g`` where e1 and e2 are two nodes for edge e and v are nodes in graph ``g``) diff --git a/tensorcircuit/torchnn.py b/tensorcircuit/torchnn.py index 9d0bb6fe..0cda7f0f 100644 --- a/tensorcircuit/torchnn.py +++ b/tensorcircuit/torchnn.py @@ -7,13 +7,13 @@ import torch from .cons import backend -from .interfaces import torch_interface # type: ignore +from .interfaces import torch_interface from .utils import is_sequence Tensor = Any -class QuantumNet(torch.nn.Module): # type: ignore +class QuantumNet(torch.nn.Module): def __init__( self, f: Callable[..., Any], @@ -81,7 +81,7 @@ def qpred(x, weights): if use_interface: f = torch_interface(f, jit=use_jit, enable_dlpack=enable_dlpack) self.f = f - self.q_weights = torch.nn.ParameterList() # type: ignore + self.q_weights = torch.nn.ParameterList() if isinstance(weights_shape[0], int): weights_shape = [weights_shape] if not is_sequence(initializer): @@ -89,7 +89,7 @@ def qpred(x, weights): for ws, initf in zip(weights_shape, initializer): if initf is None: initf = torch.randn - self.q_weights.append(torch.nn.Parameter(initf(ws))) # type: ignore + self.q_weights.append(torch.nn.Parameter(initf(ws))) def forward(self, *inputs: Tensor) -> Tensor: ypred = self.f(*inputs, *self.q_weights) @@ -97,3 +97,42 @@ def forward(self, *inputs: Tensor) -> Tensor: TorchLayer = QuantumNet + + +class HardwareNet(QuantumNet): + """ + PyTorch Layer wrapping quantum function with cloud qpu access + (using :py:mod:`tensorcircuit.cloud` module) + """ + + def __init__( + self, + f: Callable[..., Any], + weights_shape: Sequence[Tuple[int, ...]], + initializer: Union[Any, Sequence[Any]] = None, + use_vmap: bool = True, + ): + super().__init__( + f, + weights_shape, + initializer, + use_vmap=False, + use_interface=False, + use_jit=False, + ) + self.batch_support = use_vmap + + def forward(self, *inputs: Tensor) -> Tensor: + if self.batch_support: + ypred = [] + batch = inputs[0].shape[0] + for i in range(batch): + inp = tuple([a[i] for a in inputs]) + ypred.append(self.f(*inp, *self.q_weights)) + ypred = torch.stack(ypred) # type: ignore + else: + ypred = self.f(*inputs, *self.q_weights) + return ypred + + +TorchHardwareLayer = HardwareNet diff --git a/tensorcircuit/translation.py b/tensorcircuit/translation.py index 519a5aa8..73b973b6 100644 --- a/tensorcircuit/translation.py +++ b/tensorcircuit/translation.py @@ -2,30 +2,45 @@ Circuit object translation in different packages """ -from typing import Any, Dict, List, Optional -from copy import deepcopy import logging +from copy import deepcopy +from typing import Any, Dict, List, Optional, Sequence, Tuple, Union + import numpy as np logger = logging.getLogger(__name__) try: - from qiskit import QuantumCircuit import qiskit.quantum_info as qi - from qiskit.extensions.exceptions import ExtensionError + import symengine + import sympy + from qiskit import QuantumCircuit + from qiskit.circuit import Parameter, ParameterExpression + from qiskit.circuit.exceptions import CircuitError + from qiskit.circuit.library import HamiltonianGate, UnitaryGate, XXPlusYYGate + from qiskit.circuit.parametervector import ParameterVectorElement + from qiskit.circuit.quantumcircuitdata import CircuitInstruction except ImportError: logger.warning( - "Please first ``pip install qiskit`` to enable related functionality" + "Please first ``pip install -U qiskit`` to enable related functionality in translation module" + ) + CircuitInstruction = Any + QuantumCircuit = Any + +try: + import cirq +except ImportError: + logger.info( + "Please first ``pip install -U cirq`` to enable related functionality in translation module" ) from . import gates from .circuit import Circuit -from .densitymatrix import DMCircuit2 from .cons import backend +from .densitymatrix import DMCircuit2 from .interfaces.tensortrans import tensor_to_numpy - Tensor = Any @@ -54,7 +69,143 @@ def perm_matrix(n: int) -> Tensor: return p_mat -def qir2qiskit(qir: List[Dict[str, Any]], n: int) -> Any: +def _get_float(parameters: Any, key: str, default: int = 0) -> float: + return np.real(backend.numpy(gates.array_to_tensor(parameters.get(key, default)))).item() # type: ignore + + +def _merge_extra_qir( + qir: List[Dict[str, Any]], extra_qir: List[Dict[str, Any]] +) -> List[Dict[str, Any]]: + nqir = [] + # caution on the same pos! + inds: Dict[int, List[Dict[str, Any]]] = {} + for d in extra_qir: + if d["pos"] not in inds: + inds[d["pos"]] = [] + inds[d["pos"]].append(d) + for i, q in enumerate(qir): + if i in inds: + nqir += inds[i] + nqir.append(q) + for k in inds: + if k >= len(qir): + nqir += inds[k] + return nqir + + +def qir2cirq( + qir: List[Dict[str, Any]], n: int, extra_qir: Optional[List[Dict[str, Any]]] = None +) -> Any: + r""" + Generate a cirq circuit using the quantum intermediate + representation (qir) in tensorcircuit. + + :Example: + + >>> c = tc.Circuit(2) + >>> c.H(1) + >>> c.X(1) + >>> cisc = tc.translation.qir2cirq(c.to_qir(), 2) + >>> print(cisc) + 1: ───H───X─── + + :param qir: The quantum intermediate representation of a circuit. + :type qir: List[Dict[str, Any]] + :param n: # of qubits + :type n: int + :param extra_qir: The extra quantum IR of tc circuit including measure and reset on hardware, + defaults to None + :type extra_qir: Optional[List[Dict[str, Any]]] + :return: qiskit cirq object + :rtype: Any + + #TODO(@erertertet): + add default theta to iswap gate + add more cirq built-in gate instead of customized + add unitary test with tolerance + add support of cirq built-in ControlledGate for multiplecontroll + support more element in qir, e.g. barrier, measure... + """ + + class CustomizedCirqGate(cirq.Gate): # type: ignore + def __init__(self, uMatrix: Any, name: str, nqubit: int): + super(CustomizedCirqGate, self) + self.uMatrix = uMatrix + self.name = name + self.nqubit = nqubit + + def _num_qubits_(self) -> int: + return self.nqubit + + def _unitary_(self) -> Any: + return self.uMatrix + + def _circuit_diagram_info_( + self, *args: Optional[cirq.CircuitDiagramInfoArgs] + ) -> List[str]: + return [self.name] * self.nqubit + + if extra_qir is not None and len(extra_qir) > 0: + qir = _merge_extra_qir(qir, extra_qir) + qbits = cirq.LineQubit.range(n) + cmd = [] + for gate_info in qir: + index = [qbits[i] for i in gate_info["index"]] + gate_name = str(gate_info["gatef"]) + if "parameters" in gate_info: + parameters = gate_info["parameters"] + if gate_name in [ + "h", + "i", + "x", + "y", + "z", + "s", + "t", + "fredkin", + "toffoli", + "cnot", + "swap", + ]: + cmd.append(getattr(cirq, gate_name.upper())(*index)) + elif gate_name in ["rx", "ry", "rz"]: + cmd.append( + getattr(cirq, gate_name)(_get_float(parameters, "theta")).on(*index) + ) + elif gate_name == "iswap": + cmd.append( + cirq.ISwapPowGate( + exponent=_get_float(parameters, "theta", default=1) + ).on(*index) + ) + elif gate_name in ["mpo", "multicontrol"]: + gatem = np.reshape( + backend.numpy(gate_info["gatef"](**parameters).eval_matrix()), + [2 ** len(index), 2 ** len(index)], + ) + ci_name = gate_info["name"] + cgate = CustomizedCirqGate(gatem, ci_name, len(index)) + cmd.append(cgate.on(*index)) + else: + # Add Customized Gate if there is no match + gatem = np.reshape( + gate_info["gate"].tensor, + [2 ** len(index), 2 ** len(index)], + ) + # Note: unitary test is not working for some of the generated matrix, + # probably add tolerance unitary test later + cgate = CustomizedCirqGate(gatem, gate_name, len(index)) + cmd.append(cgate.on(*index)) + cirq_circuit = cirq.Circuit(*cmd) + return cirq_circuit + + +def qir2qiskit( + qir: List[Dict[str, Any]], + n: int, + extra_qir: Optional[List[Dict[str, Any]]] = None, + initialization: Optional[Tensor] = None, +) -> Any: r""" Generate a qiskit quantum circuit using the quantum intermediate representation (qir) in tensorcircuit. @@ -73,10 +224,22 @@ def qir2qiskit(qir: List[Dict[str, Any]], n: int) -> Any: :type qir: List[Dict[str, Any]] :param n: # of qubits :type n: int + :param extra_qir: The extra quantum IR of tc circuit including measure and reset on hardware, + defaults to None + :type extra_qir: Optional[List[Dict[str, Any]]] + :param initialization: Circuit initial state in qiskit format + :type initialization: Optional[Tensor] :return: qiskit QuantumCircuit object :rtype: Any """ - qiskit_circ = QuantumCircuit(n) + if extra_qir is not None and len(extra_qir) > 0: + qir = _merge_extra_qir(qir, extra_qir) + qiskit_circ = QuantumCircuit(n, n) + else: + qiskit_circ = QuantumCircuit(n) + if initialization is not None: + qiskit_circ.initialize(initialization) + measure_cbit = 0 for gate_info in qir: index = gate_info["index"] gate_name = str(gate_info["gatef"]) @@ -95,7 +258,6 @@ def qir2qiskit(qir: List[Dict[str, Any]], n: int) -> Any: "s", "t", "swap", - "iswap", "cnot", "toffoli", "fredkin", @@ -109,28 +271,37 @@ def qir2qiskit(qir: List[Dict[str, Any]], n: int) -> Any: getattr(qiskit_circ, gate_name + "g")(*index) elif gate_name in ["ox", "oy", "oz"]: getattr(qiskit_circ, "c" + gate_name[1:])(*index, ctrl_state=0) - elif gate_name == "wroot": - wroot_op = qi.Operator( - np.reshape( - backend.numpy(gate_info["gatef"](**parameters).tensor), - [2 ** len(index), 2 ** len(index)], - ) + elif gate_name == "u": + getattr(qiskit_circ, "u")( + _get_float(parameters, "theta"), + _get_float(parameters, "phi"), + _get_float(parameters, "lbd"), + *index, + ) + elif gate_name == "cu": + getattr(qiskit_circ, "cu")( + _get_float(parameters, "theta"), + _get_float(parameters, "phi"), + _get_float(parameters, "lbd"), + 0, # gamma + *index, ) - qiskit_circ.unitary(wroot_op, index, label=qis_name) - elif gate_name in ["rx", "ry", "rz", "crx", "cry", "crz"]: - getattr(qiskit_circ, gate_name)( - np.real( - backend.numpy(gates.array_to_tensor(parameters["theta"])) - ).item(), - *index + elif gate_name == "iswap": + qiskit_circ.append( + XXPlusYYGate(np.pi * _get_float(parameters, "theta", 1), np.pi), + index, + ) + elif gate_name == "wroot": + getattr(qiskit_circ, "u")(np.pi / 2, -np.pi / 4, np.pi / 4, *index) + elif gate_name in ["rx", "ry", "rz", "crx", "cry", "crz", "rxx", "ryy", "rzz"]: + getattr(qiskit_circ, gate_name)(_get_float(parameters, "theta"), *index) + elif gate_name in ["phase", "cphase"]: + getattr(qiskit_circ, gate_name[:-4])( + _get_float(parameters, "theta"), *index ) elif gate_name in ["orx", "ory", "orz"]: getattr(qiskit_circ, "c" + gate_name[1:])( - np.real( - backend.numpy(gates.array_to_tensor(parameters["theta"])) - ).item(), - *index, - ctrl_state=0 + _get_float(parameters, "theta"), *index, ctrl_state=0 ) elif gate_name in ["exp", "exp1"]: unitary = backend.numpy(backend.convert_to_tensor(parameters["unitary"])) @@ -140,10 +311,16 @@ def qir2qiskit(qir: List[Dict[str, Any]], n: int) -> Any: # Error can be presented if theta is actually complex in this procedure. exp_op = qi.Operator(unitary) index_reversed = [x for x in index[::-1]] - qiskit_circ.hamiltonian( - exp_op, time=theta, qubits=index_reversed, label=qis_name + gate = HamiltonianGate(data=exp_op, time=theta, label=qis_name) + qiskit_circ.append(gate, index_reversed) + elif gate_name == "multicontrol": + unitary = backend.numpy(backend.convert_to_tensor(parameters["unitary"])) + ctrl_str = "".join(map(str, parameters["ctrl"]))[::-1] + gate = UnitaryGate(unitary, label=qis_name).control( + len(ctrl_str), ctrl_state=ctrl_str ) - elif gate_name in ["mpo", "multicontrol"]: + qiskit_circ.append(gate, gate_info["index"]) + elif gate_name == "mpo": qop = qi.Operator( np.reshape( backend.numpy(gate_info["gatef"](**parameters).eval_matrix()), @@ -151,19 +328,25 @@ def qir2qiskit(qir: List[Dict[str, Any]], n: int) -> Any: ) ) qiskit_circ.unitary(qop, index[::-1], label=qis_name) - # TODO(@refraction-ray): support for phase gate and U, cU gate for the circuit translation + elif gate_name == "measure": + qiskit_circ.measure(index[0], measure_cbit) + measure_cbit += 1 + elif gate_name == "reset": + qiskit_circ.reset(index[0]) + elif gate_name == "barrier": + qiskit_circ.barrier(*index) else: # r cr any gate - qop = qi.Operator( - np.reshape( - backend.numpy(gate_info["gatef"](**parameters).tensor), - [2 ** len(index), 2 ** len(index)], - ) + gatem = np.reshape( + backend.numpy(gate_info["gatef"](**parameters).tensor), + [2 ** len(index), 2 ** len(index)], ) + qop = qi.Operator(gatem) try: qiskit_circ.unitary(qop, index[::-1], label=qis_name) - except ExtensionError: + except (CircuitError, ValueError) as _: logger.warning( - "omit non unitary gate in tensorcircuit when transforming to qiskit" + "omit non unitary gate in tensorcircuit when transforming to qiskit: %s" + % gate_name ) qiskit_circ.unitary( np.eye(2 ** len(index)), index[::-1], label=qis_name @@ -172,50 +355,122 @@ def qir2qiskit(qir: List[Dict[str, Any]], n: int) -> Any: return qiskit_circ +def _translate_qiskit_params( + gate_info: CircuitInstruction, binding_params: Any +) -> List[float]: + parameters = [] + for p in gate_info.operation.params: + if isinstance(p, ParameterVectorElement): + parameters.append(binding_params[p.index]) + elif isinstance(p, Parameter): + parameters.append(binding_params[p]) + elif isinstance(p, ParameterExpression): + if len(p.parameters) == 0: + parameters.append(float(p)) + continue + + # note "sym" != "sim" + expr = p.sympify().simplify() + if isinstance(expr, symengine.Expr): # qiskit uses symengine if available + expr = expr._sympy_() # sympy.Expr + + if expr.is_algebraic_expr(): # numpy ufuncs are not used + lambdify_module_name = "numpy" + else: + if backend.name == "pytorch": + lambdify_module_name = "math" + else: + lambdify_module_name = backend.name + + for free_symbol in expr.free_symbols: + # replace names: theta[0] -> theta_0 + # ParameterVector creates symbols with brackets like theta[0] + # but sympy.lambdify does not allow brackets in symbol names + free_symbol.name = free_symbol.name.replace("[", "_").replace("]", "") + + parameter_list = list(p.parameters) + sympy_symbols = [param._symbol_expr for param in parameter_list] + # replace names again: theta[0] -> theta_0 + sympy_symbols = [ + sympy.Symbol(str(symbol).replace("[", "_").replace("]", "")) + for symbol in sympy_symbols + ] + lam_f = sympy.lambdify(sympy_symbols, expr, modules=lambdify_module_name) + parameters.append( + lam_f(*[binding_params[param.index] for param in parameter_list]) + ) + else: + # numbers, arrays, etc. + parameters.append(p) + return parameters + + def ctrl_str2ctrl_state(ctrl_str: str, nctrl: int) -> List[int]: ctrl_state_bin = int(ctrl_str) return [0x1 & (ctrl_state_bin >> (i)) for i in range(nctrl)] def qiskit2tc( - qcdata: List[List[Any]], + qc: QuantumCircuit, n: int, inputs: Optional[List[float]] = None, is_dm: bool = False, + circuit_constructor: Any = None, + circuit_params: Optional[Dict[str, Any]] = None, + binding_params: Optional[Union[Sequence[float], Dict[Any, float]]] = None, ) -> Any: r""" - Generate a tensorcircuit circuit using the quantum circuit data in qiskit. + Generate a tensorcircuit circuit from the qiskit circuit. :Example: >>> qisc = QuantumCircuit(2) >>> qisc.h(0) >>> qisc.x(1) - >>> qc = tc.translation.qiskit2tc(qisc.data, 2) + >>> qc = tc.translation.qiskit2tc(qisc, 2) >>> qc.to_qir()[0]['gatef'] - h - :param qcdata: Quantum circuit data from qiskit. - :type qcdata: List[List[Any]] + :param qc: A quantum circuit in qiskit. + :type qc: QuantumCircuit :param n: # of qubits :type n: int :param inputs: Input state of the circuit. Default is None. :type inputs: Optional[List[float]] + :param circuit_constructor: ``Circuit``, ``DMCircuit`` or ``MPSCircuit`` + :type circuit_contructor: Any + :param circuit_params: kwargs given in Circuit.__init__ construction function, default to None. + :type circuit_params: Optional[Dict[str, Any]] + :param binding_params: (variational) parameters for the circuit. + Could be either a sequence or dictionary depending on the type of parameters in the Qiskit circuit. + For ``ParameterVectorElement`` use sequence. For ``Parameter`` use dictionary. + :type binding_params: Optional[Union[Sequence[float], Dict[Any, float]]] :return: A quantum circuit in tensorcircuit :rtype: Any """ - if is_dm: + if circuit_constructor is not None: + Circ = circuit_constructor + elif is_dm: Circ = DMCircuit2 else: - Circ = Circuit # type: ignore - if inputs is None: - tc_circuit: Any = Circ(n) - else: - tc_circuit = Circ(n, inputs=inputs) - for gate_info in qcdata: - idx = [qb.index for qb in gate_info[1]] + Circ = Circuit + if circuit_params is None: + circuit_params = {} + if "nqubits" not in circuit_params: + circuit_params["nqubits"] = n + if ( + len(qc.data) > 0 + and qc.data[0][0].name == "initialize" + and "inputs" not in circuit_params + ): + circuit_params["inputs"] = perm_matrix(n) @ np.array(qc.data[0][0].params) + if inputs is not None: + circuit_params["inputs"] = inputs + + tc_circuit: Any = Circ(**circuit_params) + for gate_info in qc.data: + idx = [qc.find_bit(qb).index for qb in gate_info.qubits] gate_name = gate_info[0].name - parameters = gate_info[0].params + parameters = _translate_qiskit_params(gate_info, binding_params) if gate_name in [ "h", "x", @@ -238,10 +493,21 @@ def qiskit2tc( getattr(tc_circuit, gate_name[:-1])(*idx) elif gate_name in ["cx_o0", "cy_o0", "cz_o0"]: getattr(tc_circuit, "o" + gate_name[1])(*idx) - elif gate_name in ["rx", "ry", "rz", "crx", "cry", "crz"]: + elif gate_name in ["rx", "ry", "rz", "crx", "cry", "crz", "rxx", "ryy", "rzz"]: getattr(tc_circuit, gate_name)(*idx, theta=parameters) elif gate_name in ["crx_o0", "cry_o0", "crz_o0"]: getattr(tc_circuit, "o" + gate_name[1:-3])(*idx, theta=parameters) + elif gate_name in ["r"]: + getattr(tc_circuit, "u")( + *idx, + theta=parameters[0], + phi=parameters[1] - np.pi / 2, + lbd=-parameters[1] + np.pi / 2, + ) + elif gate_name in ["u3", "u"]: + getattr(tc_circuit, "u")( + *idx, theta=parameters[0], phi=parameters[1], lbd=parameters[2] + ) elif gate_name == "hamiltonian": tc_circuit.exp(*idx, theta=parameters[-1], unitary=parameters[0]) elif gate_name == "cswap": @@ -264,7 +530,8 @@ def qiskit2tc( tc_circuit.multicontrol( *idx, ctrl=ctrl_state, unitary=gates._x_matrix, name="x" ) - elif gate_name[0] == "c": + elif gate_name[0] == "c" and gate_name[:7] != "circuit" and gate_name != "cu": + # qiskit cu bug, see https://github.com/tencent-quantum-lab/tensorcircuit/issues/199 for i in range(1, len(gate_name)): if (gate_name[-i] == "o") & (gate_name[-i - 1] == "_"): break @@ -272,7 +539,6 @@ def qiskit2tc( base_gate = gate_info[0].base_gate ctrl_state = [1] * base_gate.num_qubits idx = idx[: -base_gate.num_qubits] + idx[-base_gate.num_qubits :][::-1] - # print(idx) tc_circuit.multicontrol( *idx, ctrl=ctrl_state, unitary=base_gate.to_matrix() ) @@ -285,6 +551,33 @@ def qiskit2tc( tc_circuit.multicontrol( *idx, ctrl=ctrl_state, unitary=base_gate.to_matrix() ) + elif gate_name == "measure": + tc_circuit.measure_instruction(*idx) + # logger.warning( + # "qiskit to tc translation currently doesn't support measure instruction, just skipping" + # ) + elif gate_name == "reset": + tc_circuit.reset_instruction(*idx) + # logger.warning( + # "qiskit to tc translation currently doesn't support reset instruction, just skipping" + # ) + elif gate_name == "barrier": + tc_circuit.barrier_instruction(*idx) + elif gate_name == "initialize": + # already taken care of when initializing + continue + elif not hasattr(gate_info[0], "__array__"): + # an instruction containing a lot of gates. + # the condition is based on + # https://github.com/Qiskit/qiskit-terra/blob/2f5944d60140ceb6e30d5b891e8ffec735247ce9/qiskit/circuit/gate.py#L43 + qiskit_circuit = gate_info[0].definition + if qiskit_circuit is None: + logger.warning( + f"qiskit to tc translation doesn't support {gate_name} instruction, skipping" + ) + continue + c = Circuit.from_qiskit(qiskit_circuit, binding_params=binding_params) + tc_circuit.append(c, idx) else: # unitary gate idx_inverse = (x for x in idx[::-1]) tc_circuit.any(*idx_inverse, unitary=gate_info[0].to_matrix()) @@ -303,14 +596,21 @@ def tensor_to_json(a: Any) -> Any: a = tensor_to_numpy(a) ar = np.real(a) - ai = np.imag(a) - return [ar.tolist(), ai.tolist()] + if np.iscomplexobj(a): + ai = np.imag(a) + return [ar.tolist(), ai.tolist()] + else: + return [ar.tolist()] def json_to_tensor(a: Any) -> Any: ar = np.array(a[0]) - ai = np.array(a[1]) - return ar + 1.0j * ai + if len(a) == 1: + return ar + else: + assert len(a) == 2 + ai = np.array(a[1]) + return ar + 1.0j * ai def qir2json( @@ -400,3 +700,77 @@ def json2qir(tcqasm: List[Dict[str, Any]]) -> List[Dict[str, Any]]: } ) return qir + + +def eqasm2tc( + eqasm: str, nqubits: Optional[int] = None, headers: Tuple[int, int] = (6, 1) +) -> Circuit: + """ + Translation qexe/eqasm instruction to tensorcircuit Circuit object + + :param eqasm: _description_ + :type eqasm: str + :param nqubits: _description_, defaults to None + :type nqubits: Optional[int], optional + :param headers: lines of ignored code at the head and the tail, defaults to (6, 1) + :type headers: Tuple[int, int], optional + :return: _description_ + :rtype: Circuit + """ + eqasm_list = eqasm.split("\n") + if nqubits is None: + nqubits = len(eqasm_list[2].split(",")) + eqasm_list = eqasm_list[headers[0] : -headers[1]] + c = Circuit(nqubits) + # measure z not considered + for inst in eqasm_list: + if inst.startswith("bs"): + inst_list = inst.split(" ") + if inst_list[2].startswith("RZ"): + exponent = int(inst_list[2][3:]) + index = (int(inst_list[3][1:]),) + c.rz(*index, theta=2 * np.pi / 2**exponent) # type: ignore + elif inst_list[2] == "Z/2": + c.rz(*index, theta=-np.pi / 2) # type: ignore + elif inst_list[2] == "-Z/2": + c.rz(*index, theta=np.pi / 2) # type: ignore + # TODO(@refraction-ray): Z/2 convention to be double checked + else: + gate_name = inst_list[2].lower() + if len(inst_list) == 4: + index = (int(inst_list[3][1:]),) + elif len(inst_list) == 5: + index = (int(inst_list[3][2:-1]), int(inst_list[4][1:-1])) # type: ignore + else: + raise ValueError("Unknown format for eqasm: %s" % str(inst_list)) + getattr(c, gate_name)(*index) + + return c + + +def qiskit_from_qasm_str_ordered_measure(qasm_str: str) -> Any: + """ + qiskit ``from_qasm_str`` method cannot keep the order of measure as the qasm file, + we provide this alternative function in case the order of measure instruction matters + + :param qasm_str: open qasm str + :type qasm_str: str + :return: ``qiskit.circuit.QuantumCircuit`` + :rtype: Any + """ + measure_sequence = [] + qasm_instruction = [] + for line in qasm_str.split("\n"): + if line.startswith("measure"): + index = int(line.split(" ")[1][2:-1]) + cindex = int(line.split(" ")[3].strip(";").split("[")[1][:-1]) + # sometimes we have qasm as "measure q[3] -> meas[0];" + + measure_sequence.append((index, cindex)) + else: + qasm_instruction.append(line) + + qc = QuantumCircuit.from_qasm_str("\n".join(qasm_instruction)) + for qid, cid in measure_sequence: + qc.measure(qid, cid) + return qc diff --git a/tensorcircuit/utils.py b/tensorcircuit/utils.py index 0811cc6a..44ce9e13 100644 --- a/tensorcircuit/utils.py +++ b/tensorcircuit/utils.py @@ -4,11 +4,23 @@ from typing import Any, Callable, Union, Sequence, Tuple, Dict from functools import wraps +import os import platform import re import time +def gpu_memory_share(flag: bool = True) -> None: + # TODO(@refraction-ray): the default torch behavior should be True + # preallocate behavior for torch to be investigated + if flag is True: + os.environ["TF_FORCE_GPU_ALLOW_GROWTH"] = "true" + os.environ["XLA_PYTHON_CLIENT_PREALLOCATE"] = "false" + else: + os.environ["TF_FORCE_GPU_ALLOW_GROWTH"] = "false" + os.environ["XLA_PYTHON_CLIENT_PREALLOCATE"] = "true" + + def return_partial( f: Callable[..., Any], return_argnums: Union[int, Sequence[int]] = 0 ) -> Callable[..., Any]: @@ -136,7 +148,7 @@ def wrapper(*args: Any, **kws: Any) -> Any: if isinstance(vs, str): vs = [] for v in vs: - if kws.get(v, "qazxswedcvfr198") != "qazxswedcvfr198": + if v in kws: # in case it is None by design! kws[k] = kws[v] del kws[v] @@ -148,7 +160,7 @@ def wrapper(*args: Any, **kws: Any) -> Any: doc = doc.split("\n") # type: ignore ndoc = [] skip = False - for i, line in enumerate(doc): # type: ignore + for i, line in enumerate(doc): if not skip: ndoc.append(line) else: diff --git a/tensorcircuit/vis.py b/tensorcircuit/vis.py index ab6dd73e..19834d95 100644 --- a/tensorcircuit/vis.py +++ b/tensorcircuit/vis.py @@ -1,6 +1,7 @@ """ Visualization on circuits """ + import os import subprocess from uuid import uuid4 @@ -95,14 +96,13 @@ def qir2tex( # apply gates in qir for x in qir: - idx = x["index"] gate_length = len(idx) if x["name"].startswith("invisible"): p = max(flag[min(idx) : max(idx) + 1]) + 1 for i in range(min(idx), max(idx) + 1): tex_string_table[i] += [r"\qw "] * (p - flag[i] - 1) - tex_string_table[i] += [r"\ghost{" + x["name"][7:] + "}\qw "] + tex_string_table[i] += [r"\ghost{" + x["name"][7:] + r"}\qw "] flag[i] = p else: ctrl_number, gate_name = gate_name_trans(x["name"]) diff --git a/tests/conftest.py b/tests/conftest.py index d0c9b1d2..91185b86 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -48,6 +48,18 @@ def torchb(): pytest.skip("****** No torch backend found, skipping test suit *******") +@pytest.fixture(scope="function") +def cpb(): + try: + tc.set_backend("cupy") + yield + tc.set_backend("numpy") + except ImportError as e: + print(e) + tc.set_backend("numpy") + pytest.skip("****** No cupy backend found, skipping test suit *******") + + @pytest.fixture(scope="function") def highp(): tc.set_dtype("complex128") diff --git a/tests/test_backends.py b/tests/test_backends.py index af1666de..3c73c1ab 100644 --- a/tests/test_backends.py +++ b/tests/test_backends.py @@ -18,12 +18,6 @@ import tensorcircuit as tc dtype = np.complex64 -ii = np.eye(4, dtype=dtype) -iir = ii.reshape([2, 2, 2, 2]) -ym = np.array([[0, -1.0j], [1.0j, 0]], dtype=dtype) -zm = np.array([[1.0, 0.0], [0.0, -1.0]], dtype=dtype) -yz = np.kron(ym, zm) -yzr = yz.reshape([2, 2, 2, 2]) def universal_vmap(): @@ -50,9 +44,9 @@ def test_vmap_tf(tfb): assert r.numpy()[0, 0] == 3.0 -@pytest.mark.skip( - reason="pytorch backend to be fixed with newly added complex dtype support" -) +# @pytest.mark.skip( +# reason="pytorch backend to be fixed with newly added complex dtype support" +# ) def test_vmap_torch(torchb): r = universal_vmap() assert r.numpy()[0, 0] == 3.0 @@ -61,6 +55,7 @@ def test_vmap_torch(torchb): def test_grad_torch(torchb): a = tc.backend.ones([2], dtype="float32") + # @partial(tc.backend.jit, jit_compile=True) @tc.backend.grad def f(x): return tc.backend.sum(x) @@ -111,11 +106,6 @@ def test_backend_methods(backend): ) arr = np.random.normal(size=(6, 6)) - np.testing.assert_allclose( - tc.backend.relu(tc.array_to_tensor(arr, dtype="float32")), - np.maximum(arr, 0), - atol=1e-4, - ) np.testing.assert_allclose( tc.backend.adjoint(tc.array_to_tensor(arr + 1.0j * arr)), @@ -182,6 +172,12 @@ def test_backend_methods(backend): atol=1e-5, ) + def sum_(carry, x): + return carry + x + + r = tc.backend.scan(sum_, tc.backend.ones([10, 2]), tc.backend.zeros([2])) + np.testing.assert_allclose(r, 10 * np.ones([2]), atol=1e-5) + @pytest.mark.parametrize("backend", [lf("npb"), lf("tfb"), lf("jaxb"), lf("torchb")]) def test_backend_methods_2(backend): @@ -279,13 +275,49 @@ def test_backend_methods_2(backend): np.array([1, 3]), ) assert tc.backend.dtype(tc.backend.ones([])) == "complex64" + edges = [-1, 3.3, 9.1, 10.0] + values = tc.backend.convert_to_tensor(np.array([0.0, 4.1, 12.0], dtype=np.float32)) + r = tc.backend.numpy(tc.backend.searchsorted(edges, values)) + np.testing.assert_allclose(r, np.array([1, 2, 4])) + p = tc.backend.convert_to_tensor( + np.array( + [0.05, 0.05, 0.05, 0.05, 0.05, 0.05, 0.05, 0.05, 0.2, 0.4], dtype=np.float32 + ) + ) + r = tc.backend.probability_sample(10000, p, status=np.random.uniform(size=[10000])) + _, r = np.unique(r, return_counts=True) + np.testing.assert_allclose( + r - tc.backend.numpy(p) * 10000.0, np.zeros([10]), atol=200, rtol=1 + ) + np.testing.assert_allclose( + tc.backend.std(tc.backend.cast(tc.backend.arange(1, 4), "float32")), + 0.81649658, + atol=1e-5, + ) + arr = np.random.normal(size=(6, 6)) + np.testing.assert_allclose( + tc.backend.relu(tc.array_to_tensor(arr, dtype="float32")), + np.maximum(arr, 0), + atol=1e-4, + ) + np.testing.assert_allclose( + tc.backend.det(tc.backend.convert_to_tensor(np.eye(3) * 2)), 8, atol=1e-5 + ) + + +# @pytest.mark.parametrize("backend", [lf("npb"), lf("tfb"), lf("jaxb"), lf("torchb")]) +# def test_backend_array(backend): +# a = tc.backend.array([[0, 1], [1, 0]]) +# assert tc.interfaces.which_backend(a).name == tc.backend.name +# a = tc.backend.array([[0, 1], [1, 0]], dtype=tc.rdtypestr) +# assert tc.dtype(a) == "float32" @pytest.mark.parametrize("backend", [lf("npb"), lf("tfb"), lf("jaxb"), lf("torchb")]) def test_device_cpu_only(backend): a = tc.backend.ones([]) dev_str = tc.backend.device(a) - assert dev_str == "cpu" + assert dev_str in ["cpu", "gpu:0"] tc.backend.device_move(a, dev_str) @@ -399,12 +431,13 @@ def vqe_energy(inputs, param, n, nlayers): c.H(i) for j in range(nlayers): for i in range(n - 1): - c.any( - i, - i + 1, - unitary=tc.backend.cos(paramc[2 * j, i]) * iir - + tc.backend.sin(paramc[2 * j, i]) * 1.0j * yzr, - ) + c.ryy(i, i + 1, theta=paramc[2 * j, i]) + # c.any( + # i, + # i + 1, + # unitary=tc.backend.cos(paramc[2 * j, i]) * iir + # + tc.backend.sin(paramc[2 * j, i]) * 1.0j * yzr, + # ) for i in range(n): c.rx(i, theta=paramc[2 * j + 1, i]) e = 0.0 @@ -416,14 +449,14 @@ def vqe_energy(inputs, param, n, nlayers): return e -@pytest.mark.parametrize("backend", [lf("tfb"), lf("jaxb")]) +@pytest.mark.parametrize("backend", [lf("tfb"), lf("jaxb"), lf("torchb")]) def test_vvag(backend): n = 4 nlayers = 3 inp = tc.backend.ones([2**n]) / 2 ** (n / 2) param = tc.backend.ones([2 * nlayers, n]) - inp = tc.backend.cast(inp, "complex64") - param = tc.backend.cast(param, "complex64") + # inp = tc.backend.cast(inp, "complex64") + # param = tc.backend.cast(param, "complex64") vqe_energy_p = partial(vqe_energy, n=n, nlayers=nlayers) @@ -436,6 +469,7 @@ def test_vvag(backend): pvag = tc.backend.vvag(vqe_energy_p, argnums=(0, 1)) v1, (g10, g11) = pvag(inps, param) + print(v1.shape, g10.shape, g11.shape) np.testing.assert_allclose(v1[0], v0, atol=1e-4) np.testing.assert_allclose(g10[0], g00, atol=1e-4) np.testing.assert_allclose(g11 / batch, g01, atol=1e-4) diff --git a/tests/test_calibrating.py b/tests/test_calibrating.py index 764cbb87..4bfd9314 100644 --- a/tests/test_calibrating.py +++ b/tests/test_calibrating.py @@ -19,12 +19,10 @@ def fit_function(x_values, y_values, function, init_params): def T1_cali(t1, t2, time, method, excitedstatepopulation): - # calibrating experiments nstep = int(4 * t1 / time) pex = [] for i in range(nstep): - dmc = tc.DMCircuit(1) dmc.x(0) for _ in range(i): @@ -49,12 +47,10 @@ def T1_cali(t1, t2, time, method, excitedstatepopulation): def T2_cali(t1, t2, time, method, excitedstatepopulation): - # calibrating experiments nstep = int(4 * t2 / time) pex = [] for i in range(nstep): - dmc = tc.DMCircuit(1) dmc.h(0) for _ in range(0, i): @@ -84,7 +80,6 @@ def dep_cali(dep, nqubit): pex = [] nstep = 40 for i in range(nstep): - dmc = tc.DMCircuit(1) dmc.x(0) for _ in range(i): diff --git a/tests/test_channels.py b/tests/test_channels.py index 2bba4dca..67478473 100644 --- a/tests/test_channels.py +++ b/tests/test_channels.py @@ -3,6 +3,7 @@ import numpy as np import pytest from pytest_lazyfixture import lazy_fixture as lf +from scipy.optimize import minimize thisfile = os.path.abspath(__file__) @@ -32,7 +33,6 @@ def test_channel_identity(backend): @pytest.mark.parametrize("backend", [lf("npb"), lf("tfb"), lf("jaxb")]) def test_dep(backend): - cs = tc.channels.generaldepolarizingchannel(0.1, 1) tc.channels.kraus_identity_check(cs) @@ -42,10 +42,13 @@ def test_dep(backend): cs = tc.channels.generaldepolarizingchannel(0.02, 2) tc.channels.kraus_identity_check(cs) + cs2 = tc.channels.isotropicdepolarizingchannel(0.02 * 15, 2) + for c1, c2 in zip(cs, cs2): + np.testing.assert_allclose(c1.tensor, c2.tensor) + @pytest.mark.parametrize("backend", [lf("npb"), lf("tfb"), lf("jaxb")]) def test_rep_transformation(backend): - kraus_set = [] kraus_set.append(tc.channels.phasedampingchannel(0.2)) kraus_set.append(tc.channels.resetchannel()) @@ -106,10 +109,8 @@ def test_thermal(backend): @pytest.mark.parametrize("backend", [lf("npb"), lf("tfb"), lf("jaxb")]) def test_noisecircuit(backend): - # Monte carlo simulation def noisecircuit(X): - n = 1 c = tc.Circuit(n) c.x(0) @@ -150,3 +151,215 @@ def noisecircuitdm(): valuedm = noisec_jit() np.testing.assert_allclose(valuemc, valuedm, atol=1e-1) + + +@pytest.mark.parametrize("backend", [lf("npb"), lf("tfb"), lf("jaxb")]) +def test_readout(backend): + nqubit = 3 + c = tc.Circuit(nqubit) + c.X(0) + + value = c.sample_expectation_ps(z=[0, 1, 2]) + valueaim = -1 + np.testing.assert_allclose(value, valueaim, atol=1e-3) + + readout_error = [] + readout_error.append([0.9, 0.75]) # readout error of qubit 0 + readout_error.append([0.4, 0.7]) # readout error of qubit 1 + readout_error.append([0.7, 0.9]) # readout error of qubit 2 + + # readout_error is a list + value = c.sample_expectation_ps(z=[0, 1, 2], readout_error=readout_error) + valueaim = 0.04 + np.testing.assert_allclose(value, valueaim, atol=1e-1) + + # readout_error is a tensor + readout_error = tc.array_to_tensor(readout_error) + value = c.sample_expectation_ps(z=[0, 1, 2], readout_error=readout_error) + valueaim = 0.04 + np.testing.assert_allclose(value, valueaim, atol=1e-1) + + # test jitble + def jitest(readout_error): + nqubit = 3 + c = tc.Circuit(nqubit) + c.X(0) + return c.sample_expectation_ps(z=[0, 1, 2], readout_error=readout_error) + + calvalue = tc.backend.jit(jitest) + value = calvalue(readout_error) + valueaim = 0.04 + np.testing.assert_allclose(value, valueaim, atol=1e-1) + + # test contractor time + # start = timeit.default_timer() + # def speed(nqubit): + # c = tc.Circuit(nqubit) + # c.X(0) + # readout_error = [] + # for _ in range(nqubit): + # readout_error.append([0.9, 0.75]) # readout error of qubit 0 + # value = c.sample_expectation_ps( + # z=[0, 1, 2, 3, 4, 5, 6, 7, 8, 9], readout_error=readout_error + # ) + # return value + + # speed(10) + # stop = timeit.default_timer() + # print("Time: ", stop - start) + + +@pytest.mark.parametrize("backend", [lf("npb"), lf("tfb"), lf("jaxb")]) +def test_noisesample(backend): + readout_error = [] + readout_error.append([0.9, 0.75]) # readout error of qubit 0 + readout_error.append([0.4, 0.7]) # readout error of qubit 1 + readout_error.append([0.7, 0.9]) # readout error of qubit 2 + + c = tc.Circuit(3) + c.H(0) + c.cnot(0, 1) + print(c.sample(allow_state=True, readout_error=readout_error)) + print(c.sample(batch=8, allow_state=True, readout_error=readout_error)) + print( + c.sample( + batch=8, + allow_state=True, + readout_error=readout_error, + random_generator=tc.backend.get_random_state(42), + ) + ) + + key = tc.backend.get_random_state(42) + bs = c.sample( + batch=1000, allow_state=True, format_="count_dict_bin", random_generator=key + ) + print(bs) + bs = c.sample( + batch=1000, + allow_state=True, + readout_error=readout_error, + format_="count_dict_bin", + random_generator=key, + ) + print(bs) + + # test jitble + def jitest(readout_error): + c = tc.Circuit(3) + c.H(0) + c.cnot(0, 1) + return c.sample(batch=8, allow_state=True, format_="sample_int") + + calsample = tc.backend.jit(jitest) + sampletest = calsample(readout_error) + print(sampletest) + + +# mitigate readout error +def miti_readout_circ(nqubit): + miticirc = [] + for i in range(2**nqubit): + name = "{:0" + str(nqubit) + "b}" + lisbs = [int(x) for x in name.format(i)] + c = tc.Circuit(nqubit) + for k in range(nqubit): + if lisbs[k] == 1: + c.X(k) + miticirc.append(c) + return miticirc + + +def probability_bs(bs): + nqubit = len(list(bs.keys())[0]) + probability = [0] * 2**nqubit + shots = sum([bs[s] for s in bs]) + for s in bs: + probability[int(s, 2)] = bs[s] / shots + return probability + + +def mitigate_probability(probability_noise, calmatrix, method="inverse"): + if method == "inverse": + X = np.linalg.inv(calmatrix) + Y = probability_noise + probability_cali = X @ Y + else: # method="square" + + def fun(x): + return sum((probability_noise - calmatrix @ x) ** 2) + + x0 = np.random.rand(len(probability_noise)) + cons = {"type": "eq", "fun": lambda x: 1 - sum(x)} + bnds = tuple((0, 1) for x in x0) + res = minimize(fun, x0, method="SLSQP", constraints=cons, bounds=bnds, tol=1e-6) + probability_cali = res.x + return probability_cali + + +def mitigate_readout(nqubit, circ, readout_error): + key = tc.backend.get_random_state(42) + keys = [] + for _ in range(2**nqubit): + key, subkey = tc.backend.random_split(key) + keys.append(subkey) + + # calibration matrix + miticirc = miti_readout_circ(nqubit) + shots = 100000 + calmatrix = np.zeros((2**nqubit, 2**nqubit)) + for i in range(2**nqubit): + c = miticirc[i] + bs = c.sample( + batch=shots, + allow_state=True, + readout_error=readout_error, + format_="count_dict_bin", + random_generator=keys[i], + ) + for s in bs: + calmatrix[int(s, 2)][i] = bs[s] / shots + + key, subkey = tc.backend.random_split(key) + bs = circ.sample( + batch=shots, allow_state=True, format_="count_dict_bin", random_generator=subkey + ) + probability_perfect = probability_bs(bs) + print("probability_without_readouterror", probability_perfect) + + key, subkey = tc.backend.random_split(key) + bs = circ.sample( + batch=shots, + allow_state=True, + readout_error=readout_error, + format_="count_dict_bin", + random_generator=subkey, + ) + probability_noise = probability_bs(bs) + print("probability_with_readouterror", probability_noise) + + probability_miti = mitigate_probability( + probability_noise, calmatrix, method="inverse" + ) + print("mitigate_readouterror_method1", probability_miti) + + probability_miti = mitigate_probability( + probability_noise, calmatrix, method="square" + ) + print("mitigate_readouterror_method2", probability_miti) + + +@pytest.mark.parametrize("backend", [lf("npb"), lf("tfb"), lf("jaxb")]) +def test_readout_mitigate(backend): + nqubit = 3 + c = tc.Circuit(nqubit) + c.H(0) + c.cnot(0, 1) + c.X(2) + + readout_error = [] + readout_error.append([0.9, 0.75]) # readout error of qubit 0, p0|0=0.9, p1|1=0.75 + readout_error.append([0.4, 0.7]) # readout error of qubit 1 + readout_error.append([0.7, 0.9]) # readout error of qubit 2 + + mitigate_readout(nqubit, c, readout_error) diff --git a/tests/test_circuit.py b/tests/test_circuit.py index 67437c0e..81f20762 100644 --- a/tests/test_circuit.py +++ b/tests/test_circuit.py @@ -1,8 +1,9 @@ # pylint: disable=invalid-name -import sys import os +import sys from functools import partial + import numpy as np import opt_einsum as oem import pytest @@ -17,7 +18,8 @@ import tensorcircuit as tc -def test_wavefunction(): +@pytest.mark.parametrize("backend", [lf("npb"), lf("cpb")]) +def test_wavefunction(backend): qc = tc.Circuit(2) qc.unitary( 0, @@ -41,15 +43,17 @@ def test_wavefunction(): assert np.real(qc.wavefunction()[2]) == 2 -def test_basics(): +@pytest.mark.parametrize("backend", [lf("npb"), lf("cpb")]) +def test_basics(backend): c = tc.Circuit(2) c.x(0) - np.testing.assert_allclose(c.amplitude("10"), np.array(1.0)) + np.testing.assert_allclose(tc.backend.numpy(c.amplitude("10")), np.array(1.0)) c.CNOT(0, 1) - np.testing.assert_allclose(c.amplitude("11"), np.array(1.0)) + np.testing.assert_allclose(tc.backend.numpy(c.amplitude("11")), np.array(1.0)) -def test_measure(): +@pytest.mark.parametrize("backend", [lf("npb"), lf("cpb")]) +def test_measure(backend): c = tc.Circuit(3) c.H(0) c.h(1) @@ -232,13 +236,16 @@ def f5(key): ) -def test_expectation(): +@pytest.mark.parametrize("backend", [lf("npb"), lf("cpb")]) +def test_expectation(backend): c = tc.Circuit(2) c.H(0) - np.testing.assert_allclose(c.expectation((tc.gates.z(), [0])), 0, atol=1e-7) + np.testing.assert_allclose( + tc.backend.numpy(c.expectation((tc.gates.z(), [0]))), 0, atol=1e-7 + ) -@pytest.mark.parametrize("backend", [lf("npb"), lf("tfb"), lf("jaxb")]) +@pytest.mark.parametrize("backend", [lf("npb"), lf("tfb"), lf("jaxb"), lf("cpb")]) def test_exp1(backend): @partial(tc.backend.jit, jit_compile=True) def sf(): @@ -260,8 +267,8 @@ def s1f(): s1 = c.state() return s1 - s = sf() - s1 = s1f() + s = tc.backend.numpy(sf()) + s1 = tc.backend.numpy(s1f()) np.testing.assert_allclose(s, s1, atol=1e-4) @@ -316,7 +323,8 @@ def test_single_qubit(): np.testing.assert_allclose(w, np.array([1, 1]) / np.sqrt(2), atol=1e-4) -def test_expectation_between_two_states(): +@pytest.mark.parametrize("backend", [lf("npb"), lf("cpb")]) +def test_expectation_between_two_states(backend): zp = np.array([1.0, 0.0]) zd = np.array([0.0, 1.0]) assert tc.expectation((tc.gates.y(), [0]), ket=zp, bra=zd) == 1j @@ -330,7 +338,7 @@ def test_expectation_between_two_states(): x1z2 = [(tc.gates.x(), [0]), (tc.gates.z(), [1])] e1 = c.expectation(*x1z2) e2 = tc.expectation(*x1z2, ket=state, bra=state, normalization=True) - np.testing.assert_allclose(e2, e1) + np.testing.assert_allclose(tc.backend.numpy(e2), tc.backend.numpy(e1)) c = tc.Circuit(3) c.H(0) @@ -341,7 +349,7 @@ def test_expectation_between_two_states(): x1z2 = [(tc.gates.x(), [0]), (tc.gates.z(), [1])] e1 = c.expectation(*x1z2) / tc.backend.norm(state) ** 2 e2 = tc.expectation(*x1z2, ket=state, normalization=True) - np.testing.assert_allclose(e2, e1) + np.testing.assert_allclose(tc.backend.numpy(e2), tc.backend.numpy(e1)) c = tc.Circuit(2) c.X(1) @@ -354,12 +362,12 @@ def test_expectation_between_two_states(): s3 = c3.state() x1x2 = [(tc.gates.x(), [0]), (tc.gates.x(), [1])] e = tc.expectation(*x1x2, ket=s1, bra=s2) - np.testing.assert_allclose(e, 1.0) + np.testing.assert_allclose(tc.backend.numpy(e), 1.0) e2 = tc.expectation(*x1x2, ket=s3, bra=s2) - np.testing.assert_allclose(e2, 1.0 / np.sqrt(2)) + np.testing.assert_allclose(tc.backend.numpy(e2), 1.0 / np.sqrt(2)) -@pytest.mark.parametrize("backend", [lf("npb"), lf("tfb"), lf("jaxb")]) +@pytest.mark.parametrize("backend", [lf("npb"), lf("tfb"), lf("jaxb"), lf("cpb")]) def test_any_inputs_state(backend): c = tc.Circuit(2, inputs=tc.array_to_tensor(np.array([0.0, 0.0, 0.0, 1.0]))) c.X(0) @@ -379,10 +387,10 @@ def test_any_inputs_state(backend): ) c.X(0) z0 = c.expectation((tc.gates.z(), [0])) - np.testing.assert_allclose(z0, 0.0, rtol=1e-4, atol=1e-4) + np.testing.assert_allclose(tc.backend.numpy(z0), 0.0, rtol=1e-4, atol=1e-4) -@pytest.mark.parametrize("backend", [lf("npb"), lf("tfb")]) +@pytest.mark.parametrize("backend", [lf("npb"), lf("tfb"), lf("cpb")]) def test_postselection(backend): c = tc.Circuit(3) c.H(1) @@ -390,27 +398,45 @@ def test_postselection(backend): c.mid_measurement(1, 1) c.mid_measurement(2, 1) s = c.wavefunction() - np.testing.assert_allclose(tc.backend.real(s[3]), 0.5) + np.testing.assert_allclose(tc.backend.numpy(s[3]).real, 0.5) -def test_unitary(): +@pytest.mark.parametrize("backend", [lf("npb"), lf("cpb")]) +def test_unitary(backend): c = tc.Circuit(2, inputs=np.eye(4)) c.X(0) c.Y(1) - answer = np.kron(tc.gates.x().tensor, tc.gates.y().tensor) - np.testing.assert_allclose(c.wavefunction().reshape([4, 4]), answer, atol=1e-4) + answer = tc.backend.numpy(np.kron(tc.gates.x().tensor, tc.gates.y().tensor)) + np.testing.assert_allclose( + tc.backend.numpy(c.wavefunction().reshape([4, 4])), answer, atol=1e-4 + ) -def test_expectation_ps(): +@pytest.mark.parametrize("backend", [lf("npb"), lf("cpb")]) +def test_expectation_ps(backend): c = tc.Circuit(2) c.X(0) r = c.expectation_ps(z=[0, 1]) - np.testing.assert_allclose(r, -1, atol=1e-5) + np.testing.assert_allclose(tc.backend.numpy(r), -1, atol=1e-5) c = tc.Circuit(2) c.H(0) r = c.expectation_ps(z=[1], x=[0]) - np.testing.assert_allclose(r, 1, atol=1e-5) + np.testing.assert_allclose(tc.backend.numpy(r), 1, atol=1e-5) + r1 = c.expectation_ps(ps=[1, 3]) + np.testing.assert_allclose(tc.backend.numpy(r1), 1, atol=1e-5) + r1 = c.expectation_ps(z=[1, 2], ps=[1, 3]) + np.testing.assert_allclose(tc.backend.numpy(r1), 1, atol=1e-5) + + +def test_probability(): + for c_cls in [tc.Circuit, tc.DMCircuit]: + c = c_cls(2) + c.h(0) + c.h(1) + np.testing.assert_allclose( + c.probability(), np.array([1, 1, 1, 1]) / 4, atol=1e-5 + ) @pytest.mark.parametrize("backend", [lf("tfb"), lf("jaxb")]) @@ -785,6 +811,12 @@ def test_append_circuit(backend): c.prepend(c1) np.testing.assert_allclose(c.expectation_ps(z=[1]), -1.0) + c = tc.Circuit(2) + c1 = tc.Circuit(1) + c1.x(0) + c.append(c1, [1]) + np.testing.assert_allclose(c.state(), [0, 1, 0, 0]) + @pytest.mark.parametrize("backend", [lf("npb"), lf("tfb"), lf("jaxb")]) def test_apply_mpo_gate(backend): @@ -842,10 +874,109 @@ def test_circuit_quoperator(backend): np.testing.assert_allclose(qo.eval_matrix(), c.matrix(), atol=1e-5) +@pytest.mark.parametrize("backend", [lf("npb"), lf("tfb"), lf("jaxb")]) +def test_qir2cirq(backend): + try: + import cirq + except ImportError: + pytest.skip("cirq is not installed") + n = 6 + c = tc.Circuit(n) + for i in range(n): + c.H(i) + zz = np.array([[1, 0, 0, 0], [0, -1, 0, 0], [0, 0, -1, 0], [0, 0, 0, 1]]) + for i in range(n): + c.exp( + i, + (i + 1) % n, + theta=tc.array_to_tensor(np.random.uniform()), + unitary=tc.array_to_tensor(zz), + name="zz", + ) + c.exp1( + 1, 3, theta=tc.array_to_tensor(0.0j), unitary=tc.array_to_tensor(zz), name="zz" + ) + c.fredkin(0, 1, 2) + c.cswap(1, 2, 3) + c.ccnot(1, 2, 3) + c.cx(2, 3) + c.swap(0, 1) + c.iswap(0, 1) + c.iswap(1, 3, theta=-1.9) + c.toffoli(0, 1, 2) + c.s(1) + c.t(1) + c.sd(1) + c.td(1) + c.x(2) + c.y(2) + c.z(2) + c.wroot(3) + c.cnot(0, 1) + c.cy(0, 1) + c.cz(0, 1) + c.oy(4, 3) + c.oz(4, 3) + c.ox(4, 3) + c.oy(4, 3) + c.oz(4, 3) + c.ox(3, 4) + c.phase(2, theta=0.3) + c.cphase(1, 0, theta=-1.2) + c.rxx(0, 2, theta=0.9) + c.ryy(1, 4, theta=-2.0) + c.rzz(1, 3, theta=0.5) + c.u(2, theta=0, lbd=4.6, phi=-0.3) + c.cu(4, 1, theta=1.2) + c.rx(1, theta=tc.array_to_tensor(np.random.uniform())) + c.r(5, theta=tc.array_to_tensor(np.random.uniform())) + c.cr( + 1, + 2, + theta=tc.array_to_tensor(np.random.uniform()), + alpha=tc.array_to_tensor(np.random.uniform()), + phi=tc.array_to_tensor(np.random.uniform()), + ) + c.ry(1, theta=tc.array_to_tensor(np.random.uniform())) + c.rz(1, theta=tc.array_to_tensor(np.random.uniform())) + c.crz(2, 3, theta=tc.array_to_tensor(np.random.uniform())) + c.crx(5, 3, theta=tc.array_to_tensor(np.random.uniform())) + c.cry(1, 3, theta=tc.array_to_tensor(np.random.uniform())) + c.orx(5, 3, theta=tc.array_to_tensor(np.random.uniform())) + c.ory(5, 3, theta=tc.array_to_tensor(np.random.uniform())) + c.orz(5, 3, theta=tc.array_to_tensor(np.random.uniform())) + + c.any(1, 3, unitary=tc.array_to_tensor(np.reshape(zz, [2, 2, 2, 2]))) + + gate = tc.gates.multicontrol_gate( + tc.array_to_tensor(tc.gates._x_matrix), ctrl=[1, 0] + ) + c.mpo(0, 1, 2, mpo=gate.copy()) + c.multicontrol( + 0, + 2, + 4, + 1, + 5, + ctrl=[0, 1, 0], + unitary=tc.array_to_tensor(tc.gates._zz_matrix), + name="zz", + ) + tc_unitary = c.matrix() + tc_unitary = np.reshape(tc_unitary, [2**n, 2**n]) + + cirq = c.to_cirq() + cirq_unitary = cirq.unitary() + cirq_unitary = np.reshape(cirq_unitary, [2**n, 2**n]) + + np.testing.assert_allclose(tc_unitary, cirq_unitary, atol=1e-5) + + @pytest.mark.parametrize("backend", [lf("npb"), lf("tfb"), lf("jaxb")]) def test_qir2qiskit(backend): try: import qiskit.quantum_info as qi + from tensorcircuit.translation import perm_matrix except ImportError: pytest.skip("qiskit is not installed") @@ -873,6 +1004,7 @@ def test_qir2qiskit(backend): c.cx(2, 3) c.swap(0, 1) c.iswap(0, 1) + c.iswap(1, 3, theta=-1.9) c.toffoli(0, 1, 2) c.s(1) c.t(1) @@ -891,6 +1023,13 @@ def test_qir2qiskit(backend): c.oy(4, 3) c.oz(4, 3) c.ox(3, 4) + c.phase(2, theta=0.3) + c.cphase(1, 0, theta=-1.2) + c.rxx(0, 2, theta=0.9) + c.ryy(1, 4, theta=-2.0) + c.rzz(1, 3, theta=0.5) + c.u(2, theta=0, lbd=4.6, phi=-0.3) + c.cu(4, 1, theta=1.2) c.rx(1, theta=tc.array_to_tensor(np.random.uniform())) c.r(5, theta=tc.array_to_tensor(np.random.uniform())) c.cr( @@ -937,9 +1076,11 @@ def test_qir2qiskit(backend): def test_qiskit2tc(): try: - from qiskit import QuantumCircuit import qiskit.quantum_info as qi + from qiskit import QuantumCircuit + from qiskit.circuit.library import HamiltonianGate from qiskit.circuit.library.standard_gates import MCXGate, SwapGate + from tensorcircuit.translation import perm_matrix except ImportError: pytest.skip("qiskit is not installed") @@ -950,12 +1091,18 @@ def test_qiskit2tc(): zz = np.array([[1, 0, 0, 0], [0, -1, 0, 0], [0, 0, -1, 0], [0, 0, 0, 1]]) exp_op = qi.Operator(zz) for i in range(n): - qisc.hamiltonian(exp_op, time=np.random.uniform(), qubits=[i, (i + 1) % n]) + gate = HamiltonianGate(exp_op, time=np.random.uniform()) + qisc.append(gate, [i, (i + 1) % n]) qisc.fredkin(1, 2, 3) qisc.cswap(1, 2, 3) qisc.swap(0, 1) qisc.iswap(0, 1) qisc.toffoli(0, 1, 2) + # test Instructions + qisc2 = QuantumCircuit(1) + qisc2.h(0) + qisc.compose(qisc2, qubits=[1], inplace=True, wrap=True) + qisc.barrier(0, 1, 2) qisc.s(1) qisc.t(1) qisc.sdg(2) @@ -963,11 +1110,17 @@ def test_qiskit2tc(): qisc.x(3) qisc.y(3) qisc.z(3) + qisc.cu( + 5.868768495722669, 2.24809352294186, 3.59102783505607, 2.0223650288392, 1, 3 + ) qisc.cnot(0, 1) qisc.cy(0, 1) qisc.cz(0, 1, ctrl_state=0) qisc.cy(0, 1, ctrl_state=0) qisc.cx(0, 1, ctrl_state=0) + qisc.rxx(0.3, 1, 2) + qisc.rzz(-0.8, 2, 0) + qisc.u(0.3, 0.9, -1.2, 2) qisc.rx(np.random.uniform(), 1) qisc.ry(np.random.uniform(), 2) qisc.rz(np.random.uniform(), 3) @@ -977,6 +1130,7 @@ def test_qiskit2tc(): qisc.crz(np.random.uniform(), 2, 3, ctrl_state=0) qisc.crz(np.random.uniform(), 2, 3, ctrl_state=0) qisc.crz(np.random.uniform(), 2, 3, ctrl_state=0) + qisc.r(np.random.uniform(), np.random.uniform(), 1) qisc.unitary(exp_op, [1, 3]) mcx_g = MCXGate(3, ctrl_state="010") qisc.append(mcx_g, [0, 1, 2, 3]) @@ -991,6 +1145,109 @@ def test_qiskit2tc(): qis_unitary = np.reshape(qis_unitary, [2**n, 2**n]) p_mat = perm_matrix(n) np.testing.assert_allclose(p_mat @ tc_unitary @ p_mat, qis_unitary, atol=1e-5) + qisc_from_tc = c.to_qiskit(enable_instruction=True) + qis_unitary2 = qi.Operator(qisc_from_tc) + qis_unitary2 = np.reshape(qis_unitary2, [2**n, 2**n]) + np.testing.assert_allclose(qis_unitary2, qis_unitary, atol=1e-5) + + +@pytest.mark.parametrize("backend", [lf("tfb"), lf("jaxb"), lf("torchb")]) +def test_qiskit2tc_parameterized(backend): + try: + from qiskit.circuit import Parameter, ParameterVector, QuantumCircuit + from qiskit.circuit.library import TwoLocal + from qiskit.quantum_info import Operator + from qiskit_nature.second_q.circuit.library import UCCSD + from qiskit_nature.second_q.mappers import ParityMapper + except ImportError: + pytest.skip("qiskit or qiskit-nature is not installed") + from tensorcircuit.translation import perm_matrix + + mapper = ParityMapper() + ansatz1 = UCCSD(2, [1, 1], mapper) + ansatz2 = TwoLocal(2, rotation_blocks="ry", entanglement_blocks="cz") + ansatz3 = QuantumCircuit(1) + ansatz3_param = Parameter("θ") + ansatz3.rx(ansatz3_param, 0) + ansatz4 = QuantumCircuit(1) + ansatz4_param = ParameterVector("φ", 3) + ansatz4.rx(2.0 * ansatz4_param[0] + 5.0, 0) + ansatz4.ry(ansatz4_param[0] * ansatz4_param[1] + ansatz4_param[2], 0) + ansatz4.rz( + np.exp(np.sin(ansatz4_param[0])) + + np.abs(ansatz4_param[1]) / np.arctan(ansatz4_param[2]), + 0, + ) + ansatz_list = [ansatz1, ansatz2, ansatz3, ansatz4] + for ansatz in ansatz_list: + n = ansatz.num_qubits + if ansatz in [ansatz1, ansatz2, ansatz4]: + params = np.random.rand(ansatz.num_parameters) + else: + params = {ansatz3_param: 0.618} + qisc = ansatz.assign_parameters(params) + qiskit_unitary = Operator(qisc) + qiskit_unitary = np.reshape(qiskit_unitary, [2**n, 2**n]) + + # test jit + @tc.backend.jit + def get_unitary(params): + return tc.Circuit.from_qiskit( + ansatz, inputs=np.eye(2**n), binding_params=params + ).state() + + tc_unitary = get_unitary(params) + tc_unitary = np.reshape(tc_unitary, [2**n, 2**n]) + p_mat = tc.array_to_tensor(perm_matrix(n)) + np.testing.assert_allclose( + p_mat @ tc_unitary @ p_mat, qiskit_unitary, atol=1e-5 + ) + + # test grad + def cost_fn(params): + return tc.backend.real(tc.backend.sum(get_unitary(params))) + + if ansatz in [ansatz1, ansatz2, ansatz4]: + grad = tc.backend.grad(cost_fn)(tc.backend.convert_to_tensor(params)) + assert tc.backend.sum(tc.num_to_tensor(np.isnan(grad))) == 0 + else: + # tf only supports tf tensor as input + grad = tc.backend.grad(cost_fn)( + {ansatz3_param: tc.backend.convert_to_tensor(0.618)} + ) + assert not np.isnan(grad[ansatz3_param]) + + +@pytest.mark.parametrize("backend", [lf("npb"), lf("tfb"), lf("jaxb")]) +def test_qiskit_vs_tc_intialization(backend): + try: + import qiskit.quantum_info as qi + from qiskit import QuantumCircuit + except ImportError: + pytest.skip("qiskit is not installed") + + n = 3 + + qis_c = QuantumCircuit(n) + qis_c.h(0) + qis_c.cnot(0, 1) + qis_c.y(2) + state = qi.Statevector(qis_c) + qis_c = QuantumCircuit(n) + qis_c.initialize(state) + qis_c.cnot(1, 2) + c = tc.Circuit.from_qiskit(qis_c) + c2 = tc.Circuit(n) + c2.h(0) + c2.cnot(0, 1) + c2.y(2) + c2.cnot(1, 2) + np.testing.assert_allclose(c.state(), c2.state(), atol=1e-8) + np.testing.assert_allclose( + qi.Statevector(c.to_qiskit(enable_inputs=True)), + qi.Statevector(qis_c), + atol=1e-8, + ) @pytest.mark.parametrize("backend", [lf("npb"), lf("tfb"), lf("jaxb")]) @@ -1008,6 +1265,14 @@ def test_batch_sample(backend): batch=8, allow_state=True, random_generator=tc.backend.get_random_state(42) ) ) + print( + c.sample( + batch=8, + allow_state=True, + status=np.random.uniform(size=[8]), + format="sample_bin", + ) + ) def test_expectation_y_bug(): @@ -1059,6 +1324,25 @@ def test_circuit_inverse(backend): np.testing.assert_allclose(c.state(), inputs, atol=1e-5) +@pytest.mark.parametrize("backend", [lf("npb"), lf("tfb"), lf("jaxb")]) +def test_circuit_inverse_2(backend): + inputs = np.random.uniform(size=[8]) + inputs /= np.linalg.norm(inputs) + c = tc.Circuit(3, inputs=inputs) + c.iswap(0, 1) + c.iswap(1, 0, theta=0.6) + c.rxx(1, 2, theta=-0.2) + c.cu(0, 1, lbd=2.0, theta=-0.7) + c.r(2, alpha=0.3) + c.sd(2) + c.cx(1, 2) + c.unitary(0, unitary=tc.gates._x_matrix) + c1 = c.inverse() + c.append(c1) + print(c.draw()) + np.testing.assert_allclose(c.state(), inputs, atol=1e-5) + + @pytest.mark.parametrize("backend", [lf("tfb"), lf("jaxb")]) def test_jittable_amplitude(backend): # @tc.backend.jit @@ -1180,6 +1464,7 @@ def test_circuit_to_json(backend): def test_gate_count(): c = tc.Circuit(3) + c.x(0) c.h(0) c.rx(1, theta=-0.2) c.h(2) @@ -1187,9 +1472,184 @@ def test_gate_count(): c.toffoli(0, 2, 1) c.ccnot(1, 2, 0) c.ccx(1, 2, 0) - assert c.gate_count() == 7 - assert c.gate_count(["h"]) == 2 + assert c.gate_count() == 8 + assert c.gate_count("h") == 2 assert c.gate_count(["ccnot"]) == 3 assert c.gate_count(["rx", "multicontrol"]) == 2 + assert c.gate_count_by_condition(lambda qir: qir["index"] == (0,)) == 2 + assert c.gate_count_by_condition(lambda qir: qir["mpo"]) == 1 print(c.gate_summary()) - # {'h': 2, 'rx': 1, 'multicontrol': 1, 'toffoli': 3} + # {'x': 1, 'h': 2, 'rx': 1, 'multicontrol': 1, 'toffoli': 3} + + +def test_to_openqasm(): + c = tc.Circuit(3) + c.H(0) + c.rz(2, theta=0.2) + c.cnot(2, 1) + c.rzz(0, 1, theta=-1.0) + c.ccx(1, 2, 0) + c.u(2, theta=0.5, lbd=1.3) + c.measure_instruction(1) + print(c.to_openqasm(formatted=True)) + s = c.to_openqasm() + c1 = tc.Circuit.from_openqasm(s) + print(c1.draw()) + np.testing.assert_allclose(c.state(), c1.state()) + c.to_openqasm(filename="test.qasm") + c2 = tc.Circuit.from_openqasm_file("test.qasm") + np.testing.assert_allclose(c.state(), c2.state()) + + +def test_from_qasm_keep_measure_order(): + qasm_str = """OPENQASM 2.0; +include "qelib1.inc"; +qreg q[2]; +creg c[2]; +h q[0]; +measure q[1] -> c[1]; +measure q[0] -> c[0];""" + c = tc.Circuit.from_openqasm(qasm_str) + c.to_openqasm().split("\n")[-2][-3] == "1" + c = tc.Circuit.from_openqasm(qasm_str, keep_measure_order=True) + c.to_openqasm().split("\n")[-2][-3] == "0" + + +def test_initial_mapping(): + c = tc.Circuit(3) + c.cnot(0, 1) + c.h(1) + c.rx(1, theta=0.5) + c.cz(2, 1) + c.measure_instruction(2) + + c1 = c.initial_mapping({0: 1, 1: 2, 2: 0}) + print(c1.draw()) + + np.testing.assert_allclose( + c.expectation_ps(z=[1]), c1.expectation_ps(z=[2]), atol=1e-5 + ) + assert c1._extra_qir[0]["index"][0] == 0 + + c2 = c1.initial_mapping({1: 0, 2: 1, 0: 2}) + np.testing.assert_allclose( + c.expectation_ps(z=[1]), c2.expectation_ps(z=[1]), atol=1e-5 + ) + + c3 = c.initial_mapping({0: 2, 1: 7, 2: 0}, n=9) + np.testing.assert_allclose( + c.expectation_ps(z=[1]), c3.expectation_ps(z=[7]), atol=1e-5 + ) + print(c3.draw()) + + +def test_get_positional_logical_mapping(): + c = tc.Circuit(3) + c.cx(0, 1) + c.cz(1, 2) + c.h(1) + assert c.get_positional_logical_mapping() == {0: 0, 1: 1, 2: 2} + c = tc.Circuit(3) + c.cx(0, 1) + c.h(1) + c.measure_instruction(2) + c.measure_instruction(0) + assert c.get_positional_logical_mapping() == {0: 2, 1: 0} + + +@pytest.mark.parametrize("backend", [lf("tfb"), lf("jaxb")]) +def test_inverse_jit(backend): + K = tc.backend + + def simple_ansatz(param): + c = tc.Circuit(3) + for i in range(3): + c.cx(i, (i + 1) % 3) + c.rzz(i, (i + 1) % 3, theta=param[i]) + c1 = c.inverse() + c2 = tc.Circuit(3) + c2.x(1) + c1.append(c2) + return tc.backend.real(c1.expectation_ps(z=[1])) + + v_ansatz = K.jit(K.vvag(simple_ansatz)) + vs, gs = v_ansatz(K.ones([2, 3], dtype="float32")) + assert K.shape_tuple(gs) == (2, 3) + np.testing.assert_allclose(K.numpy(vs), -1.0 * K.ones([2]), atol=1e-5) + + +@pytest.mark.parametrize("backend", [lf("tfb"), lf("jaxb"), lf("npb")]) +def test_fancy_circuit_indexing(backend): + c = tc.Circuit(4) + c.cx([0, 1], [-1, -2]) + c.h(range(c._nqubits)) + c.rz([0], theta=0.2) + c.rx([1, 2], theta=[0.3, 0.5]) + c.rzz([2, 3], [0, 1], theta=tc.backend.ones([2])) + c.rxx([2, 0, 1], [0, 1, 2], theta=tc.backend.ones([1])) + assert c.gate_count("h") == 4 + assert c.gate_count("rzz") == 2 + assert c.gate_count("rxx") == 3 + + +@pytest.mark.parametrize("backend", [lf("tfb"), lf("jaxb"), lf("npb")]) +def test_general_kraus(backend): + c = tc.Circuit(2) + c.h([0, 1]) + p = 0.5 + status = [0.3, 0.8] + rs = [] + for i in range(2): + rs.append( + c.general_kraus( + [ + np.sqrt(p) * np.array([[1.0, 0], [0, 0]]), + np.sqrt(p) * np.array([[0, 0], [0, 1.0]]), + np.sqrt(1 - p) * np.eye(2), + ], + i, + status=status[i], + ) + ) + np.testing.assert_allclose(rs[0], 1) + np.testing.assert_allclose(rs[1], 2) + np.testing.assert_allclose(c.expectation_ps(z=[0]), -1, atol=1e-5) + np.testing.assert_allclose(c.expectation_ps(z=[1]), 0, atol=1e-5) + + +@pytest.mark.parametrize("backend", [lf("tfb"), lf("jaxb"), lf("npb")]) +def test_general_kraus_with_prob(backend): + c = tc.Circuit(2) + c.h([0, 1]) + p = 0.5 + status = [0.3, 0.8] + rs = [] + for i in range(2): + rs.append( + c.general_kraus( + [ + np.sqrt(p) * np.array([[1.0, 0], [0, 0]]), + np.sqrt(p) * np.array([[0, 0], [0, 1.0]]), + np.sqrt(1 - p) * np.eye(2), + ], + i, + status=status[i], + with_prob=True, + ) + ) + np.testing.assert_allclose(rs[0][0], 1) + np.testing.assert_allclose(rs[1][0], 2) + np.testing.assert_allclose(c.expectation_ps(z=[0]), -1, atol=1e-5) + np.testing.assert_allclose(c.expectation_ps(z=[1]), 0, atol=1e-5) + np.testing.assert_allclose(rs[0][1], [0.25, 0.25, 0.5], atol=1e-5) + np.testing.assert_allclose(rs[1][1], [0.25, 0.25, 0.5], atol=1e-5) + np.testing.assert_allclose(tc.backend.norm(c.state()), 1, atol=1e-5) + + +@pytest.mark.parametrize("backend", [lf("tfb"), lf("jaxb"), lf("npb")]) +def test_circuit_copy(backend): + c = tc.Circuit(2) + c.h(0) + c1 = c.copy() + c.rz(0, theta=0.1) + assert c1.gate_count() == 1 diff --git a/tests/test_cloud.py b/tests/test_cloud.py new file mode 100644 index 00000000..fb24d318 --- /dev/null +++ b/tests/test_cloud.py @@ -0,0 +1,219 @@ +import sys +import os +import time +import pytest +import numpy as np + +thisfile = os.path.abspath(__file__) +modulepath = os.path.dirname(os.path.dirname(thisfile)) + +sys.path.insert(0, modulepath) +import tensorcircuit as tc +from tensorcircuit.cloud import apis, wrapper +from tensorcircuit.results import counts + +if "TC_CLOUD_TEST" not in os.environ: + pytest.skip(allow_module_level=True) + # skip on CI due to no token + + +def test_get_token(): + print(apis.get_token(provider="Tencent")) + p = apis.get_provider("tencent") + print(p.get_token()) + print(p.get_device("simulator:tc").get_token()) + + +def test_list_devices(): + print(apis.list_devices()) + p = apis.get_provider() + print(p.list_devices()) + print(p.list_devices(state="on")) + + +def test_get_device(): + d1 = apis.get_device(device="tencent::hello") + assert d1.name == "hello" + assert d1.provider.name == "tencent" + d2 = apis.get_device(device="hello") + assert d2.name == "hello" + assert d2.provider.name == "tencent" + p = apis.get_provider() + d3 = p.get_device("tencent::hello") + assert d3.name == "hello" + assert d3.provider.name == "tencent" + d4 = p.get_device("hello") + assert d4.name == "hello" + assert d4.provider.name == "tencent" + + +def test_get_device_cache(): + d1 = apis.get_device("local::testing") + d2 = apis.get_device(provider="local", device="testing") + apis.set_provider("local") + d3 = apis.get_device("testing") + assert id(d1) == id(d2) + assert id(d3) == id(d1) + apis.set_provider("tencent") + d4 = apis.get_device("testing") + assert d4.provider.name == "tencent" + assert id(d4) != id(d1) + + +def test_list_properties(): + d = apis.get_device(device="simulator:aer") + print(d.list_properties()) + print(apis.list_properties(device="simulator:aer")) + with pytest.raises(ValueError): + apis.list_properties(device="hell") + + +def test_submit_task(): + c = tc.Circuit(3) + c.H(0) + c.H(1) + c.H(2) + t = apis.submit_task(device="simulator:tc", circuit=c) + r = t.details() + assert r["state"] in ["pending", "completed"] + print(t.results(blocked=True)) + assert t.get_logical_physical_mapping() == {0: 0, 1: 1, 2: 2} + + +def test_resubmit_task(): + c = tc.Circuit(3) + c.H(0) + c.H(1) + t = apis.submit_task(device="simulator:aer", circuit=c) + time.sleep(15) + t1 = apis.resubmit_task(t) + print(t.details()) + print(t1.details(wait=True)) + + +def test_get_task(): + apis.set_device("simulator:tcn1") + c = tc.Circuit(2) + c.cx(0, 1) + t = apis.submit_task(circuit=c) + t1 = apis.get_task(t.id_) + assert t1.id_ == t.id_ + t2 = apis.get_device("tencent::simulator:tcn1").get_task(t.id_) + assert t2.id_ == t.id_ + + apis.set_device() + + +def test_list_tasks(): + d = apis.get_device(device="simulator:aer") + print(d.list_tasks()) + print(apis.list_tasks(device="simulator:tc")) + + +def test_local_list_device(): + dd = apis.list_devices(provider="local") + assert dd[0].name == "testing" + + +def test_local_submit_task(): + c = tc.Circuit(2) + c.h(0) + c.cx(0, 1) + + t = apis.submit_task(device="local::testing", circuit=c, shots=2048) + r = t.results(blocked=True) + assert counts.kl_divergence({"00": 0.5, "11": 0.5}, r) < 0.1 + print(t.details()) + print(t.get_device()) + + +def test_local_list_tasks(): + print(apis.list_tasks(provider="local")) + + +def test_local_batch_submit(): + apis.set_provider("local") + c = tc.Circuit(2) + c.h(1) + c.ry(1, theta=0.8) + + ts = apis.submit_task(device="testing", circuit=[c, c]) + print(ts[0].results()) + + apis.set_device("testing") + ts = apis.submit_task(circuit=[c, c]) + print(ts[1].results()) + print(ts[1].details()) + apis.set_provider("tencent") + + +def test_batch_exp_ps(): + pss = [[1, 0], [0, 3]] + c = tc.Circuit(2) + c.h(0) + c.x(1) + np.testing.assert_allclose(wrapper.batch_expectation_ps(c, pss), [1, -1], atol=1e-5) + np.testing.assert_allclose( + wrapper.batch_expectation_ps(c, pss, ws=[1, -0.5]), 1.5, atol=1e-5 + ) + np.testing.assert_allclose( + wrapper.batch_expectation_ps(c, pss, device="simulator:tcn1"), + [1, -1], + atol=1e-1, + ) + np.testing.assert_allclose( + wrapper.batch_expectation_ps(c, pss, device="local::default", with_rem=False), + [1, -1], + atol=1e-1, + ) + + +def test_batch_submit_template(): + run = tc.cloud.wrapper.batch_submit_template( + device="simulator:tc", batch_limit=2, prior=10 + ) + cs = [] + for i in range(4): + c = tc.Circuit(4) + c.h(i) + cs.append(c) + + rs = run(cs[:3], prior=1) + assert len(rs) == 3 + rs = run(cs[:4]) + assert len(rs) == 4 + + +def test_allz_batch(): + n = 5 + + def makec(inputs, params): + c = tc.Circuit(n) + for i in range(n): + c.rx(i, theta=inputs[i]) + for i in range(n): + c.rz(i, theta=params[0, i]) + for i in range(n - 1): + c.cx(i, i + 1) + for i in range(n): + c.rx(i, theta=params[1, i]) + return c + + pss = [] + for i in range(n): + ps = [0 for _ in range(n)] + ps[i] = 3 + pss.append(ps) + + def exp_val(c, device=None): + rs = tc.cloud.wrapper.batch_expectation_ps(c, pss, device) + return tc.backend.stack(rs) + + def qmlf(inputs, params, device=None): + c = makec(inputs, params) + return exp_val(c, device) + + inputs = tc.array_to_tensor(np.array([0, 1, 0, 1, 0])) + params = np.ones([2, n]) + print(qmlf(inputs, params, device="9gmon")) + print(qmlf(inputs, params)) diff --git a/tests/test_compiler.py b/tests/test_compiler.py new file mode 100644 index 00000000..5b297289 --- /dev/null +++ b/tests/test_compiler.py @@ -0,0 +1,147 @@ +import sys +import os +import pytest +import numpy as np + +# from pytest_lazyfixture import lazy_fixture as lf + + +thisfile = os.path.abspath(__file__) +modulepath = os.path.dirname(os.path.dirname(thisfile)) + +sys.path.insert(0, modulepath) +import tensorcircuit as tc + + +def test_qsikit_compiler(): + try: + import qiskit as _ + except ImportError: + pytest.skip("qiskit is not installed") + + from tensorcircuit.compiler.qiskit_compiler import qiskit_compile + + c = tc.Circuit(2) + c.x(1) + c.cx(0, 1) + + c1, info = qiskit_compile( + c, + info=None, + output="qasm", + compiled_options={ + "basis_gates": ["cz", "rz", "h"], + "optimization_level": 3, + "coupling_map": [[0, 2], [2, 0], [1, 0], [0, 1]], + }, + ) + assert "cz" in c1 + print(info["logical_physical_mapping"]) + + c = tc.Circuit(2) + c.x(1) + c.cx(0, 1) + c.measure_instruction(1) + c1, info = qiskit_compile( + c, + info=None, + output="tc", + compiled_options={ + "basis_gates": ["cx", "rz", "h"], + "optimization_level": 3, + "coupling_map": [[0, 2], [2, 0], [1, 0], [0, 1]], + "initial_layout": [1, 2], + }, + ) + for inst in c1.to_qir(): + if inst["name"] == "h": + assert inst["index"][0] == 2 + print(c1.draw()) + assert info["logical_physical_mapping"][1] in [0, 2] + print(info) + c2, info2 = qiskit_compile( + c1, + info=info, + output="tc", + compiled_options={ + "basis_gates": ["cx", "rz", "h"], + "optimization_level": 3, + "coupling_map": [[0, 2], [2, 0], [1, 0], [0, 1]], + }, + ) + assert info2["positional_logical_mapping"] == {0: 1} + print(c2.draw()) + + +def test_composed_compiler(): + from tensorcircuit.compiler import DefaultCompiler + + c = tc.Circuit(3) + c.rx(0) + c.cx(0, 1) + c.cz(1, 0) + c.rxx(0, 2, theta=0.2) + c.measure_instruction(2) + c.measure_instruction(0) + default_compiler = DefaultCompiler() + c1, info = default_compiler(c) + print(c1.draw()) + assert c1.gate_count_by_condition(lambda qir: qir["name"] == "cnot") == 3 + assert info["positional_logical_mapping"][0] == 2 + + default_compiler = DefaultCompiler( + { + "basis_gates": ["h", "rz", "cz"], + "optimization_level": 2, + "coupling_map": [[0, 1], [1, 2]], + } + ) + + c1, info = default_compiler(c) + assert c1.gate_count_by_condition(lambda qir: qir["name"] == "cnot") == 0 + print(info) + + +def test_replace_r(): + c = tc.Circuit(3) + c.rz(0, theta=0.1) + c.cx(0, 2) + c.ry(1) + c.rxx(1, 0, theta=0.2) + c.rx(0, theta=3.9) + c.ry(1, theta=-0.2) + c.rzz(1, 0, theta=-0.3) + c.ryy(1, 0, theta=-0.6) + c.rx(2) + + print(c.draw()) + c1 = tc.compiler.simple_compiler.replace_r(c) + print(c1.draw()) + np.testing.assert_allclose(c.matrix(), c1.matrix(), atol=1e-5) + + +def test_default_compiler(): + c = tc.Circuit(3) + c.cx(0, 1) + c.rx(0, theta=1e-5) + c.x(1) + c.y(1) + c.z(1) + c.h(1) + c.cz(2, 0) + c.h(1) + c.cz(2, 0) + c.s(2) + c.sd(2) + c.s(2) + c.s(2) + c.y(2) + c.ry(2, theta=0.1) + c.t(2) + c.td(2) + c.ry(2, theta=-0.1) + c.rz(1, theta=0.3) + + c1, _ = tc.compiler.simple_compiler.simple_compile(c) + print(c1.draw()) + assert c1.gate_count() == 3 diff --git a/tests/test_dmcircuit.py b/tests/test_dmcircuit.py index a8117267..674e1f5d 100644 --- a/tests/test_dmcircuit.py +++ b/tests/test_dmcircuit.py @@ -498,6 +498,7 @@ def test_dm_sexpps(backend): ye = c.expectation_ps(x=[1], y=[0], z=[2, 3]) np.testing.assert_allclose(ye, y, atol=1e-5) y2 = c.sample_expectation_ps(x=[1], y=[0], z=[2, 3], shots=81920) + print(y, y2) assert np.abs(y2 - y) < 0.015 diff --git a/tests/test_ensemble.py b/tests/test_ensemble.py new file mode 100644 index 00000000..92869fd8 --- /dev/null +++ b/tests/test_ensemble.py @@ -0,0 +1,72 @@ +import os +import sys +import tensorflow as tf +import numpy as np +import pytest + +thisfile = os.path.abspath(__file__) +modulepath = os.path.dirname(os.path.dirname(thisfile)) + +sys.path.insert(0, modulepath) + +from tensorcircuit.applications.ai.ensemble import bagging + + +@pytest.mark.xfail( + int(tf.__version__.split(".")[1]) >= 16, reason="legacy optimizer fails tf>=2.16" +) +def test_ensemble_bagging(): + data_amount = 100 # Amount of data to be used + linear_dimension = 4 # linear demension of the data + epochs = 10 + batch_size = 32 + lr = 1e-3 + + x_train, y_train = ( + np.ones([data_amount, linear_dimension]), + np.ones([data_amount, 1]), + ) + + obj_bagging = bagging() + + def model(): + DROP = 0.1 + + activation = "selu" + inputs = tf.keras.Input(shape=(linear_dimension,), name="digits") + x0 = tf.keras.layers.Dense( + 1, + kernel_regularizer=tf.keras.regularizers.l2(9.613e-06), + activation=activation, + )(inputs) + x0 = tf.keras.layers.Dropout(DROP)(x0) + + x = tf.keras.layers.Dense( + 1, + kernel_regularizer=tf.keras.regularizers.l2(1e-07), + activation="sigmoid", + )(x0) + + model = tf.keras.Model(inputs, x) + + return model + + obj_bagging.append(model(), False) + obj_bagging.append(model(), False) + obj_bagging.append(model(), False) + obj_bagging.compile( + loss=tf.keras.losses.BinaryCrossentropy(), + optimizer=tf.keras.optimizers.legacy.Adam(lr), + metrics=[tf.keras.metrics.AUC(), "acc"], + ) + obj_bagging.train( + x=x_train, y=y_train, epochs=epochs, batch_size=batch_size, verbose=0 + ) + + v_weight = obj_bagging.predict(x_train, "weight") + v_average = obj_bagging.predict(x_train, "average") + v_most = obj_bagging.predict(x_train, "most") + validation_data = [] + validation_data.append(obj_bagging.eval([y_train, v_weight], "acc")) + validation_data.append(obj_bagging.eval([y_train, v_average], "auc")) + validation_data.append(obj_bagging.eval([y_train, v_most], "acc")) diff --git a/tests/test_fgs.py b/tests/test_fgs.py new file mode 100644 index 00000000..dd307b3c --- /dev/null +++ b/tests/test_fgs.py @@ -0,0 +1,267 @@ +import numpy as np +import pytest +from pytest_lazyfixture import lazy_fixture as lf +import tensorflow as tf + +try: + import openfermion as _ +except ModuleNotFoundError: + pytestmark = pytest.mark.skip("skip fgs test due to missing openfermion module") + +import tensorcircuit as tc + +F = tc.fgs.FGSSimulator +FT = tc.fgs.FGSTestSimulator + + +@pytest.mark.parametrize("backend", [lf("npb"), lf("tfb"), lf("jaxb")]) +def test_cmatrix(backend, highp): + import openfermion + + def asymmetric_hopping(J, gamma, i, j, L): + m0 = tc.fgs.onehot_matrix(i, j, 2 * L) - tc.fgs.onehot_matrix( + j + L, i + L, 2 * L + ) + m = (J + gamma) * m0 + (J - gamma) * tc.backend.adjoint(m0) + return m / 2 + + def asymmetric_hopping_jw(J, gamma, i, j, L): + op = (J + gamma) * openfermion.FermionOperator(f"{str(i)}^ {str(j)}") + np.conj( + J - gamma + ) * openfermion.FermionOperator(f"{str(j)}^ {str(i)}") + sop = openfermion.transforms.jordan_wigner(op) + m = openfermion.get_sparse_operator(sop, n_qubits=L).todense() + return m + + c = tc.fgs.FGSSimulator(4, [0]) + c1 = tc.fgs.FGSTestSimulator(4, [0]) + c.evol_hp(0, 1, 1.2) + c1.evol_hp(0, 1, 1.2) + c.evol_icp(2, 0.5) + c1.evol_icp(2, 0.5) + c.evol_cp(0, -0.8) + c1.evol_cp(0, -0.8) + c.evol_sp(1, 0, 0.5) + c1.evol_sp(1, 0, 0.5) + c.evol_ghamiltonian(asymmetric_hopping(1, 0.5, 0, 2, 4)) + c1.evol_ghamiltonian(np.array(asymmetric_hopping_jw(1, 0.5, 0, 2, 4))) + c.evol_sp(1, 2, 1.5) + c1.evol_sp(1, 2, 1.5) + c.evol_ihamiltonian(c.chemical_potential(1.0, 1, 4)) + c1.evol_ihamiltonian(c1.chemical_potential_jw(1.0, 1, 4)) + np.testing.assert_allclose( + c1.get_cmatrix_majorana(), c.get_cmatrix_majorana(), atol=1e-5 + ) + np.testing.assert_allclose(c1.get_cmatrix(), c.get_cmatrix(), atol=1e-5) + np.testing.assert_allclose(c1.entropy([0, 2]), c.entropy([0, 2]), atol=1e-5) + np.testing.assert_allclose( + c1.renyi_entropy(2, [1]), c.renyi_entropy(2, [1]), atol=1e-5 + ) + + +@pytest.mark.parametrize("backend", [lf("npb"), lf("tfb"), lf("jaxb")]) +def test_otoc(backend, highp): + c = tc.FGSSimulator(4, [0, 1]) + c1 = tc.fgs.FGSTestSimulator(4, [0, 1]) + h = c.hopping(0.6, 1, 2, 4) + h += c.hopping(-0.8, 3, 2, 4) + h += c.chemical_potential(0.3, 2, 4) + h1 = c1.hopping_jw(0.6, 1, 2, 4) + h1 += c1.hopping_jw(-0.8, 3, 2, 4) + h1 += c1.chemical_potential_jw(0.3, 2, 4) + c.evol_hamiltonian(h) + m = c.get_cmatrix(now_i=True, now_j=False) + m0 = c1.get_ot_cmatrix(h1) + np.testing.assert_allclose(m, m0, atol=1e-5) + m = c.get_cmatrix(now_i=False, now_j=True) + m0 = c1.get_ot_cmatrix(h1, now_i=False) + np.testing.assert_allclose(m, m0, atol=1e-5) + + +@pytest.mark.parametrize("backend", [lf("tfb"), lf("jaxb")]) +def test_fgs_ad(backend, highp): + import optax + + N = 18 + + def f(chi): + c = tc.fgs.FGSSimulator(N, [0]) + for i in range(N - 1): + c.evol_hp(i, i + 1, chi[i]) + cm = c.get_cmatrix() + return -tc.backend.real(1 - cm[N, N]) + + chi_vg = tc.backend.jit(tc.backend.value_and_grad(f)) + + if tc.backend.name == "tensorflow": + opt = tc.backend.optimizer(tf.keras.optimizers.Adam(1e-2)) + else: + opt = tc.backend.optimizer(optax.adam(1e-2)) + + param = tc.backend.ones([N - 1], dtype="float64") + for _ in range(300): + vs, gs = chi_vg(param) + param = opt.update(gs, param) + + np.testing.assert_allclose(vs, -0.9986, atol=1e-2) + + +@pytest.mark.parametrize("backend", [lf("npb"), lf("tfb"), lf("jaxb")]) +def test_hamiltonian_generation(backend): + hc = F.chemical_potential(0.8, 0, 3) + h1 = FT.get_hmatrix(hc, 3) + 0.4 * tc.backend.eye(2**3) + # constant shift + h2 = FT.chemical_potential_jw(0.8, 0, 3) + np.testing.assert_allclose(h1, h2, atol=1e-5) + + hc = F.hopping(0.8j, 0, 1, 3) + h1 = FT.get_hmatrix(hc, 3) + h2 = FT.hopping_jw(0.8j, 0, 1, 3) + np.testing.assert_allclose(h1, h2, atol=1e-5) + + hc = F.sc_pairing(0.8, 1, 0, 3) + h1 = FT.get_hmatrix(hc, 3) + h2 = FT.sc_pairing_jw(0.8, 1, 0, 3) + np.testing.assert_allclose(h1, h2, atol=1e-5) + + +@pytest.mark.parametrize("backend", [lf("npb"), lf("tfb"), lf("jaxb")]) +def test_ground_state(backend, highp): + N = 3 + hc = ( + F.hopping(1.0, 0, 1, N) + + F.hopping(1.0, 1, 2, N) + + F.chemical_potential(0.4, 0, N) + - F.chemical_potential(0.4, 1, N) + + F.sc_pairing(0.8, 0, 1, N) + ) + + c = F(N, hc=hc) + c1 = FT(N, hc=hc) + + np.testing.assert_allclose(c.get_cmatrix(), c1.get_cmatrix(), atol=1e-6) + np.testing.assert_allclose(c1.entropy([0]), c.entropy([0]), atol=1e-5) + + +@pytest.mark.parametrize("backend", [lf("npb"), lf("tfb"), lf("jaxb")]) +def test_post_select(backend, highp): + for ind in [0, 1, 2]: + for keep in [0, 1]: + c = F(3, filled=[0, 2]) + c.evol_hp(0, 1, 0.2) + c.evol_cp(0, 0.5) + c.evol_hp(1, 2, 0.3j) + c.evol_cp(1, -0.8) + c.evol_sp(0, 2, 0.3) + c.post_select(ind, keep) + c1 = FT(3, filled=[0, 2]) + c1.evol_hp(0, 1, 0.2) + c1.evol_cp(0, 0.5) + c1.evol_hp(1, 2, 0.3j) + c1.evol_cp(1, -0.8) + c1.evol_sp(0, 2, 0.3) + c1.post_select(ind, keep) + np.testing.assert_allclose(c.get_cmatrix(), c1.get_cmatrix(), atol=1e-5) + + +@pytest.mark.parametrize("backend", [lf("tfb"), lf("jaxb")]) +def test_jittable_measure(backend): + @tc.backend.jit + def get_cmatrix(status): + r = [] + c = F(3, filled=[0]) + c.evol_hp(0, 1, 0.2) + c.evol_cp(0, 0.5) + c.evol_hp(1, 2, 0.3j) + r.append(c.cond_measure(1, status[0])) + c.evol_cp(1, -0.8) + r.append(c.cond_measure(2, status[1])) + r.append(c.cond_measure(1, status[3])) + c.evol_sp(0, 2, 0.3) + c.evol_hp(2, 1, 0.2) + c.evol_hp(0, 1, 0.8) + return c.get_cmatrix(), tc.backend.stack(r) + + def get_cmatrix_baseline(status): + r = [] + c = FT(3, filled=[0]) + c.evol_hp(0, 1, 0.2) + c.evol_cp(0, 0.5) + c.evol_hp(1, 2, 0.3j) + r.append(c.cond_measure(1, status[0])) + c.evol_cp(1, -0.8) + r.append(c.cond_measure(2, status[1])) + r.append(c.cond_measure(1, status[3])) + c.evol_sp(0, 2, 0.3) + c.evol_hp(2, 1, 0.2) + c.evol_hp(0, 1, 0.8) + return c.get_cmatrix(), np.array(r) + + status = np.array([0.2, 0.83, 0.61, 0.07]) + m, his = get_cmatrix(tc.backend.convert_to_tensor(status)) + m1, his1 = get_cmatrix_baseline(status) + np.testing.assert_allclose(m, m1, atol=1e-5) + np.testing.assert_allclose(his, his1, atol=1e-5) + + +@pytest.mark.parametrize("backend", [lf("npb"), lf("tfb"), lf("jaxb")]) +def test_exp_2body(backend): + c = F(3, filled=[1]) + np.testing.assert_allclose(c.expectation_2body(4, 1), 1.0, atol=1e-5) + np.testing.assert_allclose(c.expectation_2body(5, 2), 0.0, atol=1e-5) + np.testing.assert_allclose(c.expectation_2body(1, 4), 0.0, atol=1e-5) + + +@pytest.mark.parametrize("backend", [lf("npb"), lf("tfb"), lf("jaxb")]) +def test_exp_4body(backend): + c = F(4, filled=[0, 2]) + c.evol_hp(0, 1, 0.3) + c.evol_hp(2, 3, 0.3) + c.evol_sp(0, 3, 0.5) + c.evol_sp(0, 2, 0.9) + c.evol_cp(0, -0.4) + + c1 = FT(4, filled=[0, 2]) + c1.evol_hp(0, 1, 0.3) + c1.evol_hp(2, 3, 0.3) + c1.evol_sp(0, 3, 0.5) + c1.evol_sp(0, 2, 0.9) + c1.evol_cp(0, -0.4) + + np.testing.assert_allclose( + c.expectation_4body(0, 4, 1, 5), c.expectation_4body(0, 4, 1, 5), atol=1e-5 + ) + np.testing.assert_allclose( + c.expectation_4body(0, 1, 4, 5), c.expectation_4body(0, 1, 4, 5), atol=1e-5 + ) + np.testing.assert_allclose( + c.expectation_4body(0, 4, 2, 6), c.expectation_4body(0, 4, 2, 6), atol=1e-5 + ) + np.testing.assert_allclose( + c.expectation_4body(0, 2, 3, 1), c.expectation_4body(0, 2, 3, 1), atol=1e-5 + ) + np.testing.assert_allclose( + c.expectation_4body(0, 1, 4, 6), c.expectation_4body(0, 1, 4, 6), atol=1e-5 + ) + np.testing.assert_allclose( + c.expectation_4body(1, 0, 6, 7), c.expectation_4body(1, 0, 6, 7), atol=1e-5 + ) + + +@pytest.mark.parametrize("backend", [lf("npb"), lf("tfb"), lf("jaxb")]) +def test_overlap(backend): + def compute_overlap(FGScls): + c = FGScls(3, filled=[0, 2]) + c.evol_hp(0, 1, 1.2) + c.evol_hp(1, 2, 0.3) + c.evol_cp(0, 0.5) + c.evol_sp(0, 2, 0.3) + c1 = FGScls(3, filled=[0, 2]) + c1.evol_hp(0, 1, 0.2) + c1.evol_hp(1, 2, 0.6) + c1.evol_sp(1, 0, -1.1) + return tc.backend.abs(c.overlap(c1)) + + np.testing.assert_allclose( + compute_overlap(tc.FGSSimulator), compute_overlap(tc.fgs.FGSTestSimulator), 1e-5 + ) diff --git a/tests/test_interfaces.py b/tests/test_interfaces.py index b966d189..deecc87f 100644 --- a/tests/test_interfaces.py +++ b/tests/test_interfaces.py @@ -108,6 +108,74 @@ def f3(x): np.testing.assert_allclose(pg, 2 * np.ones([2]).astype(np.complex64), atol=1e-5) +@pytest.mark.skipif(is_torch is False, reason="torch not installed") +@pytest.mark.parametrize("backend", [lf("tfb"), lf("jaxb")]) +def test_torch_interface_kws(backend): + def f(param, n): + c = tc.Circuit(n) + c = tc.templates.blocks.example_block(c, param) + loss = c.expectation( + [ + tc.gates.x(), + [ + 1, + ], + ] + ) + return tc.backend.real(loss) + + f_jit_torch = tc.interfaces.torch_interface_kws(f, jit=True, enable_dlpack=True) + + param = torch.ones([4, 4], requires_grad=True) + l = f_jit_torch(param, n=4) + l = l**2 + l.backward() + + pg = param.grad + np.testing.assert_allclose(pg.shape, [4, 4]) + np.testing.assert_allclose(pg[0, 1], -2.146e-3, atol=1e-5) + + def f2(paramzz, paramx, n, nlayer): + c = tc.Circuit(n) + for i in range(n): + c.H(i) + for j in range(nlayer): # 2 + for i in range(n - 1): + c.exp1(i, i + 1, unitary=tc.gates._zz_matrix, theta=paramzz[j, i]) + for i in range(n): + c.rx(i, theta=paramx[j, i]) + loss1 = c.expectation( + [ + tc.gates.x(), + [ + 1, + ], + ] + ) + loss2 = c.expectation( + [ + tc.gates.x(), + [ + 2, + ], + ] + ) + return tc.backend.real(loss1), tc.backend.real(loss2) + + f2_torch = tc.interfaces.torch_interface_kws(f2, jit=True, enable_dlpack=True) + + paramzz = torch.ones([2, 4], requires_grad=True) + paramx = torch.ones([2, 4], requires_grad=True) + + l1, l2 = f2_torch(paramzz, paramx, n=4, nlayer=2) + l = l1 - l2 + l.backward() + + pg = paramzz.grad + np.testing.assert_allclose(pg.shape, [2, 4]) + np.testing.assert_allclose(pg[0, 0], -0.41609, atol=1e-5) + + @pytest.mark.skipif(is_torch is False, reason="torch not installed") @pytest.mark.xfail( (int(tf.__version__.split(".")[1]) < 9) @@ -297,6 +365,7 @@ def test_dlpack_transformation(backend): target_backend=b, enable_dlpack=True, ) + ans = tc.interfaces.which_backend(ans).device_move(ans, "cpu") np.testing.assert_allclose(ans, np.ones([2])) @@ -356,6 +425,5 @@ def g(a, b, c): assert tc.interfaces.which_backend(a[0], return_backend=False) == tc.backend.name assert tc.backend.shape_tuple(a[1]) == (2, 2, 2, 2) - assert tc.interfaces.which_backend(b, return_backend=False) == tc.backend.name - assert tc.backend.shape_tuple(b) == (2, 2, 2, 2, 2, 2) + assert tc.backend.shape_tuple(b.eval()) == (2, 2, 2, 2, 2, 2) assert tc.backend.shape_tuple(c) == (2, 2, 2, 2) diff --git a/tests/test_keras.py b/tests/test_keras.py index db11b69a..3564f828 100644 --- a/tests/test_keras.py +++ b/tests/test_keras.py @@ -113,7 +113,25 @@ def test_function_io(tfb, tmp_path, highp): print(loaded(weights=tf.ones([6, 6], dtype=tf.float64), nlayers=3, n=6)) +def test_keras_hardware(tfb): + n = 2 + + def qf(inputs, param): + c = tc.Circuit(n) + c.rx(0, theta=inputs[0]) + c.rx(1, theta=inputs[1]) + c.h(1) + c.rzz(0, 1, theta=param[0]) + return tc.backend.stack([c.expectation_ps(z=[i]) for i in range(n)]) + + ql = tc.keras.HardwareLayer(qf, [1], regularizer=tf.keras.regularizers.l2(1e-3)) + print(ql(tf.ones([1, 2]))) + + def test_keras_layer_inputs_dict(tfb): + # https://github.com/tensorflow/tensorflow/issues/65306 + # keras3 for tf2.16+ fails to accept complex valued input for keras layers + # which is vital for quantum applications n = 3 p = 0.1 K = tc.backend diff --git a/tests/test_miscs.py b/tests/test_miscs.py index 9db5048d..b7a201cb 100644 --- a/tests/test_miscs.py +++ b/tests/test_miscs.py @@ -16,6 +16,7 @@ from tensorcircuit import experimental from tensorcircuit.quantum import PauliString2COO, PauliStringSum2COO from tensorcircuit.applications.vqes import construct_matrix_v2 +from tensorcircuit.applications.physics.baseline import TFIM1Denergy, Heisenberg1Denergy i, x, y, z = [t.tensor for t in tc.gates.pauli_gates] @@ -31,6 +32,10 @@ ] +def test_about(): + print(tc.about()) + + def test_ps2coo(tfb): for l, a in check_pairs: r1 = PauliString2COO(tf.constant(l, dtype=tf.int64)) @@ -181,3 +186,71 @@ def f(theta: float, beta: float) -> float: np.testing.assert_allclose(f(beta=0.2, alpha=0.1), 0.3, atol=1e-5) print(f.__doc__) assert len(f.__doc__.strip().split("\n")) == 12 + + +def test_finite_difference_tf(tfb): + def f(param1, param2): + n = 4 + c = tc.Circuit(n) + for i in range(n): + c.rx(i, theta=param1[i]) + for i in range(n - 1): + c.cx(i, i + 1) + for i in range(n - 1): + c.rzz(i, i + 1, theta=param2[i]) + r = [c.expectation_ps(z=[i]) for i in range(n)] + return tc.backend.stack(r) + + def fsum(param1, param2): + return tc.backend.mean(f(param1, param2)) + + p1 = tf.ones([4]) + p2 = tf.ones([3]) + g1, g2 = tc.backend.value_and_grad(fsum)(p1, p2) + + f1 = experimental.finite_difference_differentiator( + f, argnums=(0, 1), shifts=(np.pi / 2, 2) + ) + + def fsum1(param1, param2): + return tc.backend.mean(f1(param1, param2)) + + g3, g4 = tc.backend.value_and_grad(fsum1)(p1, p2) + + np.testing.assert_allclose(g1, g3, atol=1e-5) + np.testing.assert_allclose(g2, g4, atol=1e-5) + + +def test_evol(jaxb): + def h_square(t, b): + return (tc.backend.sign(t - 1.0) + 1) / 2 * b * tc.gates.x().tensor + + c = tc.Circuit(3) + c.x(0) + c.cx(0, 1) + c.h(2) + c = experimental.evol_local( + c, [1], h_square, 2.0, tc.backend.convert_to_tensor(0.2) + ) + c.rx(1, theta=np.pi - 0.4) + np.testing.assert_allclose(c.expectation_ps(z=[1]), 1.0, atol=1e-5) + + ixi = tc.quantum.PauliStringSum2COO([[0, 1, 0]]) + + def h_square_sparse(t, b): + return (tc.backend.sign(t - 1.0) + 1) / 2 * b * ixi + + c = tc.Circuit(3) + c.x(0) + c.cx(0, 1) + c.h(2) + c = experimental.evol_global( + c, h_square_sparse, 2.0, tc.backend.convert_to_tensor(0.2) + ) + c.rx(1, theta=np.pi - 0.4) + np.testing.assert_allclose(c.expectation_ps(z=[1]), 1.0, atol=1e-5) + + +def test_energy_baseline(): + print(TFIM1Denergy(10)) + print(Heisenberg1Denergy(10)) diff --git a/tests/test_mpscircuit.py b/tests/test_mpscircuit.py index 52d07d6c..7050e7af 100644 --- a/tests/test_mpscircuit.py +++ b/tests/test_mpscircuit.py @@ -5,7 +5,6 @@ import sys import os import numpy as np -import scipy import pytest from pytest_lazyfixture import lazy_fixture as lf @@ -18,56 +17,46 @@ import tensorcircuit as tc -N = 16 -D = 100 +N = 8 +D = 6 split = tc.cons.split_rules(max_singular_values=D) type_test_circuits = Tuple[ tc.Circuit, Tensor, tc.MPSCircuit, Tensor, tc.MPSCircuit, Tensor ] -def reproducible_unitary(n): - A = np.arange(n**2).reshape((n, n)) - A = A + np.sin(A) * 1j - A = A - A.conj().T - return scipy.linalg.expm(A).astype(tc.dtypestr) +def reproducible_unitary(n, param): + exp2n = 2**n + A = tc.backend.cast( + tc.backend.reshape(tc.backend.arange(exp2n**2), (exp2n, exp2n)), tc.dtypestr + ) + A = A + tc.backend.sin(A) * param * 1j + A = A - tc.backend.conj(tc.backend.transpose(A)) + return tc.backend.reshape(tc.backend.expm(A), (2,) * n * 2) -def get_test_circuits(full) -> type_test_circuits: - O1 = tc.gates.any(reproducible_unitary(2).reshape((2, 2))) - O2 = tc.gates.any(reproducible_unitary(4).reshape((2, 2, 2, 2))) - O3 = tc.gates.any(reproducible_unitary(8).reshape((2, 2, 2, 2, 2, 2))) +def simulate(c, check=True, params=None): + if params is None: + params = tc.backend.ones((3,), dtype=tc.dtypestr) + O1 = tc.gates.any(reproducible_unitary(1, params[0])) + O2 = tc.gates.any(reproducible_unitary(2, params[1])) + O3 = tc.gates.any(reproducible_unitary(3, params[2])) # Construct a complicated circuit by Circuit and MPSCircuit and compare - if full: - - def rangei(j, N): - return range(0, N - 1) - - else: - - def rangei(j, N): - return range(j, N - 1 - j) - - def simulate(c): - c.H(0) - # create as much correlation as possible - for j in range(N // 2): - for i in rangei(j, N): - c.apply(O2.copy(), i, i + 1) - c.apply(O1.copy(), i) - # test non-adjacent double gates - c.apply(O2.copy(), N // 2 - 1, N // 2 + 1) - c.apply(O3.copy(), int(N * 0.2), int(N * 0.4), int(N * 0.6)) - if isinstance(c, tc.MPSCircuit): - np.testing.assert_allclose(np.abs(c._mps.check_canonical()), 0, atol=1e-12) - c.apply(O2.copy(), N // 2 - 2, N // 2 + 2) - c.apply(O3.copy(), int(N * 0.4), int(N * 0.6), int(N * 0.8)) - if isinstance(c, tc.MPSCircuit): - np.testing.assert_allclose(np.abs(c._mps.check_canonical()), 0, atol=1e-12) - c.cz(2, 3) + c.H(0) + # create as much correlation as possible + for i in range(0, N - 1, 2): + c.apply(O2.copy(), i, i + 1) + c.apply(O1.copy(), i) + c.apply(O3.copy(), int(N * 0.1), int(N * 0.5), int(N * 0.9)) + c.apply(O2.copy(), 1, N - 2) + if check and isinstance(c, tc.MPSCircuit): + np.testing.assert_allclose(np.abs(c._mps.check_canonical()), 0, atol=1e-12) + c.cz(2, 3) + +def get_test_circuits() -> type_test_circuits: c = tc.Circuit(N) simulate(c) w_c = c.wavefunction() @@ -133,10 +122,13 @@ def do_test_truncation( np.abs(tc.backend.numpy(w_mps).conj().dot(tc.backend.numpy(w_c))) ** 2 ) estimated_fedility = tc.backend.numpy(mps._fidelity) - print(real_fedility) - print(estimated_fedility) - np.testing.assert_allclose(real_fedility, real_fedility_ref, atol=1e-8) - np.testing.assert_allclose(estimated_fedility, estimated_fedility_ref, atol=1e-8) + print(real_fedility, estimated_fedility) + if real_fedility_ref is not None: + np.testing.assert_allclose(real_fedility, real_fedility_ref, atol=1e-5) + if estimated_fedility_ref is not None: + np.testing.assert_allclose( + estimated_fedility, estimated_fedility_ref, atol=1e-5 + ) def do_test_amplitude(test_circuits: type_test_circuits): @@ -165,14 +157,14 @@ def do_test_expectation(test_circuits: type_test_circuits): w_mps_exact, ) = test_circuits - single_gate = (tc.gates.z(), [11]) + single_gate = (tc.gates.z(), [3]) tensor = (np.sin(np.arange(16)) + np.cos(np.arange(16)) * 1j).reshape((2, 2, 2, 2)) double_gate_nonunitary = ( tc.gates.Gate(tc.backend.convert_to_tensor(tensor)), [2, 6], ) double_gate = (tc.gates.cnot(), [2, 6]) - triple_gate = (tc.gates.toffoli(), [1, 5, 9]) + triple_gate = (tc.gates.toffoli(), [7, 1, 5]) gates = [single_gate, double_gate_nonunitary, triple_gate] exp_mps = mps_exact.expectation(*gates) @@ -181,8 +173,8 @@ def do_test_expectation(test_circuits: type_test_circuits): # ps x = [0, 2] - y = [1, 3, 5] - z = [6, 8, 10] + y = [5, 3, 1] + z = [6, 4] exp_mps = mps_exact.expectation_ps(x=x, y=y, z=z) exp_c = c.expectation_ps(x=x, y=y, z=z) np.testing.assert_allclose(exp_mps, exp_c, atol=1e-7) @@ -216,8 +208,8 @@ def do_test_fromwavefunction(external_wavefunction, relative_err_ref): s = np.linalg.svd(w_external.reshape((2 ** (N // 2), 2 ** (N // 2))))[1] theoretical_upper_limit = np.sum(s[0:D] ** 2) relative_err = np.log((1 - real_fedility) / (1 - theoretical_upper_limit)) - print(relative_err) - np.testing.assert_allclose(relative_err, 0.11, atol=1e-2) + if relative_err_ref is not None: + np.testing.assert_allclose(relative_err, relative_err_ref, atol=1e-4) def do_test_proj(test_circuits: type_test_circuits, external_wavefunction): @@ -273,22 +265,17 @@ def do_test_measure(test_circuits: type_test_circuits): mps_exact, w_mps_exact, ) = test_circuits - index = [6, 5, 2, 9] - status = tc.backend.convert_to_tensor([0.1, 0.3, 0.5, 0.7]) + index = [6, 5, 2, 1] + status = tc.backend.convert_to_tensor([0.1, 0.3, 0.7, 0.9]) result_c = c.measure(*index, with_prob=True, status=status) - result_mps = mps.measure(*index, with_prob=True, status=status) result_mps_exact = mps_exact.measure(*index, with_prob=True, status=status) - np.testing.assert_allclose(result_mps[0], result_c[0], atol=1e-8) np.testing.assert_allclose(result_mps_exact[0], result_c[0], atol=1e-8) - np.testing.assert_allclose(result_mps[1], result_c[1], atol=1e-4) np.testing.assert_allclose(result_mps_exact[1], result_c[1], atol=1e-8) def test_MPO_conversion(highp, tfb): - O3 = tc.backend.convert_to_tensor( - reproducible_unitary(8).reshape((2, 2, 2, 2, 2, 2)) - ) - I = tc.backend.convert_to_tensor(np.eye(2).astype("complex128")) + O3 = reproducible_unitary(3, 1.0) + I = tc.backend.eye(2, dtype=tc.dtypestr) gate = tc.gates.Gate(O3) MPO3, _ = tc.MPSCircuit.gate_to_MPO(gate, 2, 3, 4) @@ -308,25 +295,47 @@ def test_MPO_conversion(highp, tfb): @pytest.mark.parametrize( "backend, dtype", [(lf("tfb"), lf("highp")), (lf("jaxb"), lf("highp"))] ) -def test_circuits_1(backend, dtype): - import time - - begin = time.time() - circuits = get_test_circuits(False) - print("time", time.time() - begin) +def test_circuits(backend, dtype): + circuits = get_test_circuits() do_test_canonical(circuits) do_test_wavefunction(circuits) - do_test_truncation(circuits, 0.9987293417932497, 0.999440840610151) + do_test_truncation(circuits, 0.902663090851, 0.910305380327) do_test_amplitude(circuits) do_test_expectation(circuits) external = external_wavefunction() - do_test_fromwavefunction(external, 0.1185) + do_test_fromwavefunction(external, 0.276089) do_test_proj(circuits, external) do_test_tensor_input(circuits) do_test_measure(circuits) - print("time", time.time() - begin) -def test_circuits_2(highp): - circuits = get_test_circuits(True) - do_test_truncation(circuits, 0.9401410770899974, 0.9654331011546374) +@pytest.mark.parametrize("backend, dtype", [(lf("tfb"), lf("highp"))]) +def test_circuits_jit(backend, dtype): + def expec(params): + mps = tc.MPSCircuit(N, split=split) + simulate(mps, check=False, params=params) + x = [0, 2] + y = [5, 3, 1] + z = [6, 4] + exp = mps.expectation_ps(x=x, y=y, z=z) + return tc.backend.real(exp) + + params = tc.backend.ones((3,), dtype=tc.dtypestr) + expec_vg = tc.backend.value_and_grad(expec) + expec_vg_jit = tc.backend.jit(expec_vg) + exp = expec(params) + exp_jit, exp_grad_jit = expec_vg_jit(params) + dir = tc.backend.convert_to_tensor(np.array([1.0, 2.0, 3.0], dtype=tc.dtypestr)) + epsilon = 1e-6 + exp_p = expec(params + dir * epsilon) + exp_m = expec(params - dir * epsilon) + exp_grad_dir_numerical = (exp_p - exp_m) / (epsilon * 2) + exp_grad_dir_jit = tc.backend.real(tc.backend.sum(exp_grad_jit * dir)) + np.testing.assert_allclose( + tc.backend.numpy(exp), tc.backend.numpy(exp_jit), atol=1e-10 + ) + np.testing.assert_allclose( + tc.backend.numpy(exp_grad_dir_numerical), + tc.backend.numpy(exp_grad_dir_jit), + atol=1e-6, + ) diff --git a/tests/test_noisemodel.py b/tests/test_noisemodel.py new file mode 100644 index 00000000..75ec82d7 --- /dev/null +++ b/tests/test_noisemodel.py @@ -0,0 +1,156 @@ +import pytest +from pytest_lazyfixture import lazy_fixture as lf +import numpy as np + +import tensorcircuit as tc +from tensorcircuit.noisemodel import ( + NoiseConf, + circuit_with_noise, + sample_expectation_ps_noisfy, + expectation_noisfy, +) +from tensorcircuit.channels import composedkraus + + +@pytest.mark.parametrize("backend", [lf("tfb"), lf("jaxb")]) +def test_noisemodel(backend): + # test data structure + # noise_conf = NoiseConf() + # noise_conf.add_noise("h1", "t0") + # noise_conf.add_noise("h1", ["t1", "t2"], [[0], [1]]) + # noise_conf.add_noise("h1", ["t3"], [[0]]) + # noise_conf.add_noise("h1", "t4") + # noise_conf.add_noise("h1", ["t5"], [[3]]) + # noise_conf.add_noise("h2", ["v1", "v2"], [[0], [1]]) + # noise_conf.add_noise("h2", ["v3"], [[0]]) + # noise_conf.add_noise("h2", "v4") + + c = tc.Circuit(2) + c.cnot(0, 1) + c.rx(0, theta=0.4) + c.rx(1, theta=0.8) + c.h(0) + c.h(1) + + dmc = tc.DMCircuit(2) + dmc.cnot(0, 1) + dmc.rx(0, theta=0.4) + dmc.rx(1, theta=0.8) + dmc.h(0) + dmc.h(1) + + error1 = tc.channels.generaldepolarizingchannel(0.1, 1) + error2 = tc.channels.generaldepolarizingchannel(0.01, 2) + error3 = tc.channels.thermalrelaxationchannel(300, 400, 100, "ByChoi", 0) + + readout_error = [] + readout_error.append([0.9, 0.75]) # readout error of qubit 0 + readout_error.append([0.4, 0.7]) # readout error of qubit 1 + + noise_conf = NoiseConf() + noise_conf.add_noise("rx", error1) + noise_conf.add_noise("rx", [error3], [[0]]) + noise_conf.add_noise("h", [error3, error1], [[0], [1]]) + noise_conf.add_noise("x", [error3], [[0]]) + noise_conf.add_noise("cnot", [error2], [[0, 1]]) + noise_conf.add_noise("readout", readout_error) + + cnoise = circuit_with_noise(c, noise_conf, [0.1] * 7) + value = cnoise.expectation_ps(x=[0, 1]) + + # value = expectation_ps_noisfy(c, x=[0, 1], noise_conf=noise_conf, nmc=10000) + # np.testing.assert_allclose(value, 0.09, atol=1e-1) + + # value = expectation_ps_noisfy(dmc, x=[0, 1], noise_conf=noise_conf) + # np.testing.assert_allclose(value, 0.09, atol=1e-1) + + # with readout_error + value = sample_expectation_ps_noisfy(dmc, x=[0, 1], noise_conf=noise_conf) + np.testing.assert_allclose(value, -0.12, atol=1e-2) + + value = sample_expectation_ps_noisfy(c, x=[0, 1], noise_conf=noise_conf, nmc=100000) + np.testing.assert_allclose(value, -0.12, atol=1e-2) + + # test composed channel and general condition + newerror = composedkraus(error1, error3) + noise_conf1 = NoiseConf() + noise_conf1.add_noise("rx", [newerror, error1], [[0], [1]]) + noise_conf1.add_noise("h", [error3, error1], [[0], [1]]) + noise_conf1.add_noise("x", [error3], [[0]]) + + def condition(d): + return d["name"] == "cnot" and d["index"] == (0, 1) + + noise_conf1.add_noise_by_condition(condition, error2) + noise_conf1.add_noise("readout", readout_error) + + value = sample_expectation_ps_noisfy(dmc, x=[0, 1], noise_conf=noise_conf1) + np.testing.assert_allclose(value, -0.12, atol=1e-2) + + # test standardized gate + newerror = composedkraus(error1, error3) + noise_conf2 = NoiseConf() + noise_conf2.add_noise("Rx", [newerror, error1], [[0], [1]]) + noise_conf2.add_noise("H", [error3, error1], [[0], [1]]) + noise_conf2.add_noise("x", [error3], [[0]]) + noise_conf2.add_noise("cx", [error2], [[0, 1]]) + noise_conf2.add_noise("readout", readout_error) + + value = sample_expectation_ps_noisfy( + c, x=[0, 1], noise_conf=noise_conf2, nmc=100000 + ) + np.testing.assert_allclose(value, -0.12, atol=1e-2) + + +@pytest.mark.parametrize("backend", [lf("tfb"), lf("jaxb")]) +def test_general_noisemodel(backend): + c = tc.Circuit(2) + c.cnot(0, 1) + c.rx(0, theta=0.4) + c.rx(1, theta=0.8) + c.h(0) + c.h(1) + + dmc = tc.DMCircuit(2) + dmc.cnot(0, 1) + dmc.rx(0, theta=0.4) + dmc.rx(1, theta=0.8) + dmc.h(0) + dmc.h(1) + + error1 = tc.channels.generaldepolarizingchannel(0.1, 1) + error2 = tc.channels.generaldepolarizingchannel(0.06, 2) + error3 = tc.channels.thermalrelaxationchannel(300, 400, 100, "ByChoi", 0) + + readout_error = [] + readout_error.append([0.9, 0.75]) + readout_error.append([0.4, 0.7]) + + noise_conf = NoiseConf() + noise_conf.add_noise("rx", error1) + noise_conf.add_noise("rx", [error3], [[0]]) + noise_conf.add_noise("h", [error3, error1], [[0], [1]]) + noise_conf.add_noise("x", [error3], [[0]]) + noise_conf.add_noise("cnot", [error2], [[0, 1]]) + noise_conf.add_noise("readout", readout_error) + + nmc = 100000 + # # test sample_expectation_ps + value1 = sample_expectation_ps_noisfy(c, x=[0, 1], noise_conf=noise_conf, nmc=nmc) + value2 = c.sample_expectation_ps(x=[0, 1], noise_conf=noise_conf, nmc=nmc) + value3 = dmc.sample_expectation_ps(x=[0, 1], noise_conf=noise_conf) + np.testing.assert_allclose(value1, value2, atol=1e-2) + np.testing.assert_allclose(value3, value2, atol=1e-2) + + # test expectation + value1 = expectation_noisfy(c, (tc.gates.z(), [0]), noise_conf=noise_conf, nmc=nmc) + value2 = c.expectation((tc.gates.z(), [0]), noise_conf=noise_conf, nmc=nmc) + value3 = dmc.expectation((tc.gates.z(), [0]), noise_conf=noise_conf) + np.testing.assert_allclose(value1, value2, atol=1e-2) + np.testing.assert_allclose(value3, value2, atol=1e-2) + + # test expectation_ps + # value = expectation_ps_noisfy(c, x=[0], noise_conf=noise_conf, nmc=10000) + value1 = c.expectation_ps(x=[0], noise_conf=noise_conf, nmc=nmc) + value2 = dmc.expectation_ps(x=[0], noise_conf=noise_conf) + np.testing.assert_allclose(value1, value2, atol=1e-2) diff --git a/tests/test_qaoa.py b/tests/test_qaoa.py index fae3c69a..a1b762f9 100644 --- a/tests/test_qaoa.py +++ b/tests/test_qaoa.py @@ -7,11 +7,14 @@ sys.path.insert(0, modulepath) import numpy as np +import pytest from tensorcircuit.applications.dqas import set_op_pool from tensorcircuit.applications.graphdata import get_graph from tensorcircuit.applications.layers import Hlayer, rxlayer, zzlayer from tensorcircuit.applications.vags import evaluate_vag +from tensorcircuit.templates.ansatz import QAOA_ansatz_for_Ising +from tensorcircuit.circuit import Circuit def test_vag(tfb): @@ -25,3 +28,59 @@ def test_vag(tfb): ) print(expene, eneg, p) np.testing.assert_allclose(ene.numpy(), -7.01, rtol=1e-2) + + +cases = [ + ("X", True), + ("X", False), + ("XY", True), + ("XY", False), + ("ZZ", True), + ("ZZ", False), +] + + +@pytest.fixture +def example_inputs(): + params = [0.1, 0.2, 0.3, 0.4] + nlayers = 2 + pauli_terms = [[0, 1, 0], [1, 0, 1]] + weights = [0.5, -0.5] + return params, nlayers, pauli_terms, weights + + +@pytest.mark.parametrize("mixer, full_coupling", cases) +def test_QAOA_ansatz_for_Ising(example_inputs, full_coupling, mixer): + params, nlayers, pauli_terms, weights = example_inputs + circuit = QAOA_ansatz_for_Ising( + params, nlayers, pauli_terms, weights, full_coupling, mixer + ) + n = len(pauli_terms[0]) + assert isinstance(circuit, Circuit) + assert circuit._nqubits == n + + if mixer == "X": + assert circuit.gate_count() == n + nlayers * (len(pauli_terms) + n) + elif mixer == "XY": + if full_coupling is False: + assert circuit.gate_count() == n + nlayers * (len(pauli_terms) + 2 * n) + else: + assert circuit.gate_count() == n + nlayers * ( + len(pauli_terms) + sum(range(n + 1)) + ) + else: + if full_coupling is False: + assert circuit.gate_count() == n + nlayers * (len(pauli_terms) + n) + else: + assert circuit.gate_count() == n + nlayers * ( + len(pauli_terms) + sum(range(n + 1)) / 2 + ) + + +@pytest.mark.parametrize("mixer, full_coupling", [("AB", True), ("XY", 1), ("TC", 5)]) +def test_QAOA_ansatz_errors(example_inputs, full_coupling, mixer): + params, nlayers, pauli_terms, weights = example_inputs + with pytest.raises(ValueError): + QAOA_ansatz_for_Ising( + params, nlayers, pauli_terms, weights, full_coupling, mixer + ) diff --git a/tests/test_qem.py b/tests/test_qem.py new file mode 100644 index 00000000..6ef01cec --- /dev/null +++ b/tests/test_qem.py @@ -0,0 +1,152 @@ +from functools import partial +import pytest +from pytest_lazyfixture import lazy_fixture as lf +import numpy as np +import networkx as nx + +import tensorcircuit as tc +from tensorcircuit.noisemodel import NoiseConf +from tensorcircuit.results import qem +from tensorcircuit.results.qem import ( + zne_option, + apply_zne, + dd_option, + apply_dd, + apply_rc, +) +from tensorcircuit.results.qem import benchmark_circuits + + +@pytest.mark.parametrize("backend", [lf("tfb"), lf("jaxb")]) +def test_benchmark_circuits(backend): + # QAOA + graph = [(2, 0), (0, 3), (1, 2)] + weight = [1] * len(graph) + params = np.array([[1, 1]]) + + _ = benchmark_circuits.QAOA_circuit(graph, weight, params) + + # mirror circuit + # return circuit and ideal counts {"01000":1} + _, _ = benchmark_circuits.mirror_circuit( + depth=5, two_qubit_gate_prob=1, connectivity_graph=nx.complete_graph(3), seed=20 + ) + + # GHZ circuit + _ = benchmark_circuits.generate_ghz_circuit(10) + + # Werner-state with linear complexity + # {'1000': 0.25, '0100': 0.25, '0010': 0.25, '0001': 0.25} + _ = benchmark_circuits.generate_w_circuit(5) + + # RB cirucit + _ = benchmark_circuits.generate_rb_circuits(2, 7)[0] + + +@pytest.mark.parametrize("backend", [lf("tfb"), lf("jaxb")]) +def test_zne(backend): + c = tc.Circuit(2) + for _ in range(3): + c.rx(range(2), theta=0.4) + + error1 = tc.channels.generaldepolarizingchannel(0.01, 1) + noise_conf = NoiseConf() + noise_conf.add_noise("rx", error1) + + def execute(circuit): + value = circuit.expectation_ps(z=[0], noise_conf=noise_conf, nmc=10000) + return value + + random_state = np.random.RandomState(0) + noise_scaling_function = partial( + zne_option.scaling.fold_gates_at_random, + # fidelities = {"single": 1.0}, + random_state=random_state, + ) + factory = zne_option.inference.PolyFactory(scale_factors=[1, 3, 5], order=1) + # factory = zne_option.inference.ExpFactory(scale_factors=[1,1.5,2],asymptote=0.) + # factory = zne_option.inference.RichardsonFactory(scale_factors=[1,1.5,2]) + # factory = zne_option.inference.AdaExpFactory(steps=5, asymptote=0.) + + result = apply_zne( + circuit=c, + executor=execute, + factory=factory, + scale_noise=noise_scaling_function, + num_to_average=1, + ) + + ideal_value = c.expectation_ps(z=[0]) + mit_value = result + + np.testing.assert_allclose(ideal_value, mit_value, atol=4e-2) + + +@pytest.mark.parametrize("backend", [lf("tfb"), lf("jaxb")]) +def test_dd(backend): + c = tc.Circuit(2) + for _ in range(3): + c.rx(range(2), theta=0.4) + + def execute(circuit): + value = circuit.expectation_ps(z=[0]) + return value + + def execute2(circuit): + key = tc.backend.get_random_state(42) + count = circuit.sample( + batch=1000, allow_state=True, format_="count_dict_bin", random_generator=key + ) + return count + + _ = apply_dd( + circuit=c, + executor=execute, + rule=["X", "X"], + rule_args={"spacing": -1}, + full_output=True, + ignore_idle_qubit=True, + fulldd=False, + ) + + _ = apply_dd( + circuit=c, + executor=execute2, + rule=dd_option.rules.xyxy, + rule_args={"spacing": -1}, + full_output=True, + ignore_idle_qubit=True, + fulldd=True, + iscount=True, + ) + + # wash circuit based on use_qubits and washout iden gates + _ = qem.prune_ddcircuit(c, qlist=list(range(c.circuit_param["nqubits"]))) + + +@pytest.mark.parametrize("backend", [lf("tfb"), lf("jaxb")]) +def test_rc(backend): + c = tc.Circuit(2) + for _ in range(3): + c.rx(range(2), theta=0.4) + c.cnot(0, 1) + + def execute(circuit): + value = circuit.expectation_ps(z=[0]) + return value + + def execute2(circuit): + key = tc.backend.get_random_state(42) + count = circuit.sample( + batch=1000, allow_state=True, format_="count_dict_bin", random_generator=key + ) + return count + + _ = apply_rc(circuit=c, executor=execute, num_to_average=6, simplify=False) + + _ = apply_rc( + circuit=c, executor=execute2, num_to_average=6, simplify=True, iscount=True + ) + + # generate a circuit with rc + _ = qem.rc_circuit(c) diff --git a/tests/test_quantum.py b/tests/test_quantum.py index f2969f76..63ff755e 100644 --- a/tests/test_quantum.py +++ b/tests/test_quantum.py @@ -1,13 +1,13 @@ # pylint: disable=invalid-name -from functools import partial import os import sys +from functools import partial import numpy as np import pytest -from pytest_lazyfixture import lazy_fixture as lf import tensornetwork as tn +from pytest_lazyfixture import lazy_fixture as lf thisfile = os.path.abspath(__file__) modulepath = os.path.dirname(os.path.dirname(thisfile)) @@ -189,7 +189,6 @@ def test_mul(backend): @pytest.mark.parametrize("backend", [lf("npb"), lf("tfb"), lf("jaxb")]) def test_expectations(backend): - psi_tensor = np.random.rand(2, 2, 2) + 1.0j * np.random.rand(2, 2, 2) op_tensor = np.random.rand(2, 2) + 1.0j * np.random.rand(2, 2) @@ -313,7 +312,8 @@ def test_extract_from_measure(backend): np.testing.assert_allclose(r, 1, atol=1e-5) -def test_heisenberg_ham(tfb): +@pytest.mark.parametrize("backend", [lf("npb"), lf("tfb"), lf("jaxb")]) +def test_heisenberg_ham(backend): g = tc.templates.graphs.Line1D(6) h = tc.quantum.heisenberg_hamiltonian(g, sparse=False) e, _ = tc.backend.eigh(h) @@ -365,6 +365,38 @@ def test_expectation_quantum(backend): np.testing.assert_allclose(exp1, exp2, atol=1e-5) +@pytest.mark.parametrize("backend", [lf("npb"), lf("tfb"), lf("jaxb")]) +def test_ee(backend): + c = tc.Circuit(3) + c.h(0) + c.cx(0, 1) + c.cx(1, 2) + s = c.state() + np.testing.assert_allclose( + tc.quantum.entanglement_entropy(s, [0, 1]), np.log(2.0), atol=1e-5 + ) + + +@pytest.mark.parametrize("backend", [lf("npb"), lf("tfb"), lf("jaxb")]) +def test_negativity(backend, highp): + c = tc.DMCircuit(2) + c.h(0) + c.cnot(0, 1) + c.depolarizing(0, px=0.1, py=0.1, pz=0.1) + dm = c.state() + np.testing.assert_allclose( + tc.quantum.log_negativity(dm, [0], base="2"), 0.485427, atol=1e-5 + ) + np.testing.assert_allclose( + tc.quantum.partial_transpose(tc.quantum.partial_transpose(dm, [0]), [0]), + dm, + atol=1e-6, + ) + np.testing.assert_allclose( + tc.quantum.entanglement_negativity(dm, [1]), -0.33176, atol=1e-5 + ) + + @pytest.mark.parametrize("backend", [lf("npb"), lf("tfb"), lf("jaxb")]) def test_tn2qop(backend): nwires = 6 @@ -389,7 +421,7 @@ def test_qb2qop(backend): except ImportError: pytest.skip("quimb is not installed") nwires = 6 - qb_mpo = quimb.tensor.tensor_gen.MPO_ham_ising(nwires, 4, 2, cyclic=True) + qb_mpo = quimb.tensor.tensor_builder.MPO_ham_ising(nwires, 4, 2, cyclic=True) qu_mpo = tc.quantum.quimb2qop(qb_mpo) h1 = qu_mpo.eval_matrix() g = tc.templates.graphs.Line1D(nwires, pbc=True) @@ -399,7 +431,7 @@ def test_qb2qop(backend): np.testing.assert_allclose(h1, h2, atol=1e-5) # in out edge order test - builder = quimb.tensor.tensor_gen.SpinHam() + builder = quimb.tensor.tensor_builder.SpinHam1D() # new version quimb breaking API change: SpinHam1D -> SpinHam builder += 1, "Y" builder += 1, "X" @@ -412,6 +444,14 @@ def test_qb2qop(backend): ) np.testing.assert_allclose(m1, m2, atol=1e-5) + # test mps case + + s1 = quimb.tensor.tensor_builder.MPS_rand_state(3, 4) + s2 = tc.quantum.quimb2qop(s1) + m1 = s1.to_dense() + m2 = s2.eval_matrix() + np.testing.assert_allclose(m1, m2, atol=1e-5) + @pytest.mark.parametrize("backend", [lf("npb"), lf("tfb"), lf("jaxb")]) def test_counts_2(backend): @@ -450,3 +490,37 @@ def test_measurement_results(backend): w, counts=c, format="count_dict_int", jittable=True ) print(r) + + +def test_ps2xyz(): + xyz = {"x": [1], "z": [2]} + assert tc.quantum.xyz2ps(xyz) == [0, 1, 3] + assert tc.quantum.xyz2ps(xyz, 6) == [0, 1, 3, 0, 0, 0] + xyz.update({"y": []}) + assert tc.quantum.ps2xyz([0, 1, 3]) == xyz + assert tc.quantum.ps2xyz([0, 1, 3, 0]) == xyz + + +@pytest.mark.parametrize("backend", [lf("npb"), lf("tfb"), lf("jaxb")]) +def test_reduced_wavefunction(backend): + c = tc.Circuit(3) + c.h(0) + c.cnot(0, 1) + r = c.cond_measure(0) + s = c.state() + s1 = tc.quantum.reduced_wavefunction(s, [0, 2], [r, 0]) + if tc.backend.cast(r, tc.rdtypestr) < 0.5: + np.testing.assert_allclose(s1, np.array([1, 0]), atol=1e-5) + else: + np.testing.assert_allclose(s1, np.array([0, 1]), atol=1e-5) + + c = tc.Circuit(3) + c.h(0) + c.cnot(0, 1) + s = c.state() + s1 = tc.quantum.reduced_wavefunction(s, [2], [0]) + + c1 = tc.Circuit(2) + c1.h(0) + c1.cnot(0, 1) + np.testing.assert_allclose(s1, c1.state(), atol=1e-5) diff --git a/tests/test_results.py b/tests/test_results.py new file mode 100644 index 00000000..8c11eddd --- /dev/null +++ b/tests/test_results.py @@ -0,0 +1,347 @@ +import pytest +import numpy as np + + +import tensorcircuit as tc +from tensorcircuit.results import counts +from tensorcircuit.results.readout_mitigation import ReadoutMit + +d = {"000": 2, "101": 3, "100": 4} + + +def test_marginal_count(): + assert counts.marginal_count(d, [1, 2])["00"] == 6 + assert counts.marginal_count(d, [1])["0"] == 9 + assert counts.marginal_count(d, [2, 1, 0])["001"] == 4 + + +def test_count2vec(): + assert counts.vec2count(counts.count2vec(d, normalization=False), prune=True) == d + + +def test_kl(): + a = {"00": 512, "11": 512} + assert counts.kl_divergence(a, a) == 0 + + +def test_expectation(): + assert counts.expectation(d, [0, 1]) == -5 / 9 + assert counts.expectation(d, None, [[1, -1], [1, 0], [1, 1]]) == -5 / 9 + + +def test_plot_histogram(): + d = {"00": 10, "01": 2, "11": 8} + d1 = {"00": 11, "11": 9} + print(counts.plot_histogram([d, d1])) + + +def test_readout(): + nqubit = 4 + shots = 4096 + c = tc.Circuit(nqubit) + c.H(0) + c.cnot(0, 1) + c.x(3) + + idea_count = c.sample(batch=shots, allow_state=True, format="count_dict_bin") + raw_count = run([c], shots)[0] + + # test "inverse", "constrained_least_square", "M3" + mit = ReadoutMit(execute=run) + mit.cals_from_system([0, 1, 2, 3, 6], shots=10000, method="local") + + mit_count1 = mit.apply_correction( + raw_count, [1, 3, 2], method="inverse" + ) # direct(Max2),iterative(Max3), inverse,square + mit_count2 = mit.apply_correction( + raw_count, [1, 3, 2], method="constrained_least_square" + ) + idea_count2 = counts.marginal_count(idea_count, [1, 3, 2]) + + assert counts.kl_divergence(idea_count2, mit_count1) < 0.05 + assert counts.kl_divergence(idea_count2, mit_count2) < 0.05 + + # test "global" and "equal" + mit = ReadoutMit(execute=run) + mit.cals_from_system([0, 1, 2, 3], shots=100000, method="global") + A_global = mit.get_matrix([1, 3, 2]) + mit_countg = mit.apply_correction( + raw_count, [1, 3, 2], method="constrained_least_square" + ) + + mit = ReadoutMit(execute=run) + mit.cals_from_system([0, 1, 2, 3], shots=100000, method="local") + A_local = mit.get_matrix([1, 3, 2]) + mit_countl = mit.apply_correction( + raw_count, [1, 3, 2], method="constrained_least_square" + ) + + np.testing.assert_allclose(A_global, A_local, atol=1e-2) + assert counts.kl_divergence(mit_countg, mit_countl) < 0.05 + + +def test_readout_masks(): + mit = ReadoutMit(execute=run) + mit.cals_from_system( + [1, 2, 4], shots=8192, method="local", masks=["01010", "10101", "11111"] + ) + np.testing.assert_allclose( + mit.single_qubit_cals[1][0, 0], 0.02 * np.sin(2) + 0.978, atol=1e-2 + ) + + +def test_readout_expv(): + nqubit = 4 + c = tc.Circuit(nqubit) + c.H(0) + c.cnot(0, 1) + c.x(3) + + idea_count = c.sample(batch=100000, allow_state=True, format="count_dict_bin") + raw_count = run([c], 100000)[0] + + cal_qubits = [0, 1, 2, 3] + use_qubits = [0, 1] + + # idea_value = c.expectation_ps(z=[0,1]) + idea_count2 = counts.marginal_count(idea_count, use_qubits) + idea_value = counts.expectation(idea_count2, z=[0, 1]) + + mit = ReadoutMit(execute=run) + mit.cals_from_system(cal_qubits, shots=100000, method="local") + mit_count = mit.apply_correction(raw_count, use_qubits, method="inverse") + mit_value = counts.expectation(mit_count, z=[0, 1]) + + mit = ReadoutMit(execute=run) + mit.cals_from_system(cal_qubits, shots=100000, method="local") + mit_value1 = mit.expectation(raw_count, z=[0, 1], method="inverse") + + mit = ReadoutMit(execute=run) + mit.cals_from_system(cal_qubits, shots=100000, method="global") + mit_value2 = mit.expectation(raw_count, z=[0, 1], method="square") + + np.testing.assert_allclose(idea_value, mit_value, atol=1e-2) + np.testing.assert_allclose(idea_value, mit_value1, atol=1e-2) + np.testing.assert_allclose(idea_value, mit_value2, atol=1e-2) + + # test large size + nqubit = 20 + c = tc.Circuit(nqubit) + c.H(0) + for i in range(nqubit - 1): + c.cnot(i, i + 1) + c.rx(1, theta=0.9) + + idea_count = c.sample(batch=100000, allow_state=True, format="count_dict_bin") + raw_count = run([c], 100000)[0] + + cal_qubits = list(range(nqubit)) + use_qubits = list(range(nqubit)) + + # idea_value = c.expectation_ps(z=[0,1]) + idea_count2 = counts.marginal_count(idea_count, use_qubits) + idea_value = counts.expectation(idea_count2, z=list(range(nqubit))) + + mit = ReadoutMit(execute=run) + mit.cals_from_system(cal_qubits, shots=100000, method="local") + mit_value1 = mit.expectation(raw_count, z=list(range(nqubit)), method="inverse") + + np.testing.assert_allclose(idea_value, mit_value1, atol=1e-1) + + +def test_M3(): + try: + import mthree # pylint: disable=unused-import + except ImportError: + pytest.skip("****** No mthree, skipping test suit *******") + + nqubit = 20 + c = tc.Circuit(nqubit) + c.H(0) + for i in range(nqubit - 1): + c.cnot(i, i + 1) + c.rx(1, theta=0.9) + + idea_count = c.sample(batch=100000, allow_state=True, format="count_dict_bin") + raw_count = run([c], 100000)[0] + + cal_qubits = list(range(nqubit)) + use_qubits = list(range(nqubit)) + + idea_count2 = counts.marginal_count(idea_count, use_qubits) + idea_value = counts.expectation(idea_count2, z=list(range(nqubit))) + + mit = ReadoutMit(execute=run) + mit.cals_from_system(cal_qubits, shots=100000, method="local") + mit_count = mit.apply_correction(raw_count, use_qubits, method="M3_auto") + mit_value = counts.expectation(mit_count, z=list(range(nqubit))) + np.testing.assert_allclose(idea_value, mit_value, atol=1e-1) + + nqubit = 4 + shots = 4096 + c = tc.Circuit(nqubit) + c.H(0) + c.cnot(0, 1) + c.x(3) + + idea_count = c.sample(batch=shots, allow_state=True, format="count_dict_bin") + raw_count = run([c], shots)[0] + + mit_count3 = mit.apply_correction(raw_count, [1, 3, 2], method="M3_direct") + mit_count4 = mit.apply_correction(raw_count, [1, 3, 2], method="M3_iterative") + idea_count2 = counts.marginal_count(idea_count, [1, 3, 2]) + assert counts.kl_divergence(idea_count2, mit_count3) < 0.05 + assert counts.kl_divergence(idea_count2, mit_count4) < 0.05 + + +def partial_sample(c, batch, readout_error=None): + measure_index = [] + for inst in c._extra_qir: + if inst["name"] == "measure": + measure_index.append(inst["index"][0]) + if len(measure_index) == 0: + measure_index = list(range(c._nqubits)) + + ct = c.sample( + allow_state=True, + batch=batch, + readout_error=readout_error, + format="count_dict_bin", + ) + return tc.results.counts.marginal_count(ct, measure_index) + + +def run(cs, shots): + # customized backend for mitigation test + ts = [] + for c in cs: + count = simulator(c, shots) + ts.append(count) + return ts + + +def simulator(c, shots, logical_physical_mapping=None): + # with readout_error noise + nqubit = c._nqubits + if logical_physical_mapping is None: + logical_physical_mapping = {i: i for i in range(nqubit)} + + gg = [] + for i in range(200): + gg.append(np.sin(i) * 0.02 + 0.978) + # gg.append(0.98 - i * 0.01) + readout_error = np.reshape(gg[0 : nqubit * 2], (nqubit, 2)) + mapped_readout_error = [[1, 1]] * nqubit + for lq, phyq in logical_physical_mapping.items(): + mapped_readout_error[lq] = readout_error[phyq] + return partial_sample(c, shots, mapped_readout_error) + + +def test_mapping(): + nqubit = 15 + shots = 100000 + c = tc.Circuit(nqubit) + c.H(4) + c.cnot(4, 5) + c.cnot(5, 6) + c.cnot(6, 7) + c.rx(4, theta=0.8) + c.rx(7, theta=1.8) + c.measure_instruction(4) + c.measure_instruction(5) + c.measure_instruction(6) + c.measure_instruction(7) + + mit = ReadoutMit(execute=run) + mit.cals_from_system(list(range(15)), shots=100000, method="local") + + show_qubits = [6, 7, 5] + + idea_count = c.sample(batch=shots, allow_state=True, format="count_dict_bin") + idea_count1 = counts.marginal_count(idea_count, show_qubits) + + def miti_kl_mean(logical_physical_mapping): + ls = [] + for _ in range(10): + raw_count = simulator(c, shots, logical_physical_mapping) + mit_count1 = mit.apply_correction( + raw_count, + qubits=show_qubits, + positional_logical_mapping={1: 5, 0: 4, 2: 6, 3: 7}, + logical_physical_mapping=logical_physical_mapping, + method="square", + ) + ls.append(counts.kl_divergence(idea_count1, mit_count1)) + # print("std", np.std(listtt), np.mean(listtt)) # smaller error rate and larger shots, better mititation. + np.testing.assert_allclose(np.mean(ls), 0.01, atol=1e-2) + + logical_physical_mapping = {4: 0, 6: 2, 7: 3, 5: 1} + miti_kl_mean(logical_physical_mapping) + + logical_physical_mapping = {4: 4, 5: 5, 6: 6, 7: 7} + miti_kl_mean(logical_physical_mapping) + + logical_physical_mapping = {4: 8, 5: 9, 6: 10, 7: 11} + miti_kl_mean(logical_physical_mapping) + + +def test_readout_expv_map(): + shots = 100000 + nqubit = 7 + c = tc.Circuit(nqubit) + c.H(3) + c.cnot(3, 4) + c.cnot(4, 5) + c.rx(3, theta=0.8) + c.rx(4, theta=1.2) + c.measure_instruction(3) + c.measure_instruction(4) + c.measure_instruction(5) + + idea_count = c.sample(batch=100000, allow_state=True, format="count_dict_bin") + idea_value = counts.expectation(idea_count, z=[4, 5]) + + # logical_physical_mapping = {3: 3, 4: 4, 5: 5} + logical_physical_mapping = {3: 1, 5: 3, 4: 6} + positional_logical_mapping = {1: 4, 0: 3, 2: 5} + + raw_count = simulator(c, shots, logical_physical_mapping) + + cal_qubits = list(range(nqubit)) + + mit = ReadoutMit(execute=run) + mit.cals_from_system(cal_qubits, shots=100000, method="local") + mit_value1 = mit.expectation( + raw_count, + z=[4, 5], + positional_logical_mapping=positional_logical_mapping, + logical_physical_mapping=logical_physical_mapping, + method="inverse", + ) + + mit_count = mit.apply_correction( + raw_count, + qubits=[3, 4, 5], + positional_logical_mapping=positional_logical_mapping, + logical_physical_mapping=logical_physical_mapping, + method="inverse", + ) + mit_value = counts.expectation(mit_count, z=[1, 2]) + # print("idea", idea_value) + # print("mit", mit_value) + + mit = ReadoutMit(execute=run) + mit.cals_from_system(cal_qubits, shots=100000, method="global") + mit_value2 = mit.expectation( + raw_count, + z=[4, 5], + positional_logical_mapping=positional_logical_mapping, + logical_physical_mapping=logical_physical_mapping, + method="inverse", + ) + + # print("mit1", mit_value1) + # print("mit2", mit_value2) + np.testing.assert_allclose(idea_value, mit_value, atol=3 * 1e-2) + np.testing.assert_allclose(idea_value, mit_value1, atol=3 * 1e-2) + np.testing.assert_allclose(idea_value, mit_value2, atol=3 * 1e-2) diff --git a/tests/test_shadows.py b/tests/test_shadows.py new file mode 100644 index 00000000..9d6406b6 --- /dev/null +++ b/tests/test_shadows.py @@ -0,0 +1,160 @@ +import pytest +from pytest_lazyfixture import lazy_fixture as lf +import numpy as np +import tensorcircuit as tc +from tensorcircuit.shadows import ( + shadow_bound, + shadow_snapshots, + global_shadow_state, + entropy_shadow, + renyi_entropy_2, + expectation_ps_shadow, + global_shadow_state1, +) + + +@pytest.mark.parametrize("backend", [lf("tfb"), lf("jaxb")]) +def test_jit(backend): + nq, repeat = 8, 5 + ps = [1, 0, 0, 0, 2, 0, 0, 0] + sub = (1, 3, 6, 7) + error = 0.1 + ns, k = shadow_bound(ps, error) + ns //= repeat + + thetas = 2 * np.random.rand(2, nq) - 1 + + c = tc.Circuit(nq) + for i in range(nq): + c.H(i) + for i in range(2): + for j in range(nq): + c.cnot(j, (j + 1) % nq) + for j in range(nq): + c.rz(j, theta=thetas[i, j] * np.pi) + + psi = c.state() + pauli_strings = tc.backend.convert_to_tensor(np.random.randint(1, 4, size=(ns, nq))) + status = tc.backend.convert_to_tensor(np.random.rand(ns, repeat)) + + def classical_shadow(psi, pauli_strings, status): + lss_states = shadow_snapshots(psi, pauli_strings, status) + expc = expectation_ps_shadow(lss_states, ps=ps, k=k) + ent = entropy_shadow(lss_states, sub=sub, alpha=2) + return expc, ent + + csjit = tc.backend.jit(classical_shadow) + + exact_expc = c.expectation_ps(ps=ps) + exact_rdm = tc.quantum.reduced_density_matrix(psi, cut=[0, 2, 4, 5]) + exact_ent = tc.quantum.renyi_entropy(exact_rdm, k=2) + expc, ent = csjit(psi, pauli_strings, status) + expc = np.median(expc) + + np.testing.assert_allclose(expc, exact_expc, atol=error) + np.testing.assert_allclose(ent, exact_ent, atol=5 * error) + + +@pytest.mark.parametrize("backend", [lf("tfb"), lf("jaxb")]) +def test_state(backend): + nq, ns = 2, 10000 + + c = tc.Circuit(nq) + c.H(0) + c.cnot(0, 1) + + psi = c.state() + bell_state = psi[:, None] @ psi[None, :] + + pauli_strings = tc.backend.convert_to_tensor(np.random.randint(1, 4, size=(ns, nq))) + status = tc.backend.convert_to_tensor(np.random.rand(ns, 5)) + lss_states = shadow_snapshots(c.state(), pauli_strings, status) + sdw_state = global_shadow_state(lss_states) + sdw_state1 = global_shadow_state1(lss_states) + + np.testing.assert_allclose(sdw_state, bell_state, atol=0.1) + np.testing.assert_allclose(sdw_state1, bell_state, atol=0.1) + + +@pytest.mark.parametrize("backend", [lf("tfb"), lf("jaxb")]) +def test_ent(backend): + nq, ns, repeat = 6, 1000, 500 + + thetas = 2 * np.random.rand(2, nq) - 1 + + c = tc.Circuit(nq) + for i in range(nq): + c.H(i) + for i in range(2): + for j in range(nq): + c.cnot(j, (j + 1) % nq) + for j in range(nq): + c.rz(j, theta=thetas[i, j] * np.pi) + + sub = [1, 4] + psi = c.state() + + pauli_strings = tc.backend.convert_to_tensor(np.random.randint(1, 4, size=(ns, nq))) + status = tc.backend.convert_to_tensor(np.random.rand(ns, repeat)) + snapshots = shadow_snapshots(psi, pauli_strings, status, measurement_only=True) + + exact_rdm = tc.quantum.reduced_density_matrix( + psi, cut=[i for i in range(nq) if i not in sub] + ) + exact_ent = tc.quantum.renyi_entropy(exact_rdm, k=2) + ent = entropy_shadow(snapshots, pauli_strings, sub, alpha=2) + ent2 = renyi_entropy_2(snapshots, sub) + + np.testing.assert_allclose(ent, exact_ent, atol=0.1) + np.testing.assert_allclose(ent2, exact_ent, atol=0.1) + + +# @pytest.mark.parametrize("backend", [lf("tfb"), lf("jaxb")]) +# def test_expc(backend): +# import pennylane as qml +# +# nq, ns, repeat = 6, 2000, 1000 +# +# thetas = 2 * np.random.rand(2, nq) - 1 +# +# c = tc.Circuit(nq) +# for i in range(nq): +# c.H(i) +# for i in range(2): +# for j in range(nq): +# c.cnot(j, (j + 1) % nq) +# for j in range(nq): +# c.rz(j, theta=thetas[i, j] * np.pi) +# +# ps = [1, 0, 0, 0, 0, 3] +# sub = [1, 4] +# psi = c.state() +# +# pauli_strings = tc.backend.convert_to_tensor(np.random.randint(1, 4, size=(ns, nq))) +# status = tc.backend.convert_to_tensor(np.random.rand(ns, repeat)) +# snapshots = shadow_snapshots(psi, pauli_strings, status, measurement_only=True) +# +# exact_expc = c.expectation_ps(ps=ps) +# exact_rdm = tc.quantum.reduced_density_matrix( +# psi, cut=[i for i in range(nq) if i not in sub] +# ) +# exact_ent = tc.quantum.renyi_entropy(exact_rdm, k=2) +# print(exact_expc, exact_ent) +# +# expc = np.median(expection_ps_shadow(snapshots, pauli_strings, ps=ps, k=9)) +# ent = entropy_shadow(snapshots, pauli_strings, sub, alpha=2) +# ent2 = renyi_entropy_2(snapshots, sub) +# print(expc, ent, ent2) +# +# pl_snapshots = np.asarray(snapshots).reshape(ns * repeat, nq) +# pl_ps = np.tile(np.asarray(pauli_strings - 1)[:, None, :], (1, repeat, 1)).reshape( +# ns * repeat, nq +# ) +# shadow = qml.ClassicalShadow(pl_snapshots, pl_ps) +# H = qml.PauliX(0) @ qml.PauliZ(5) +# pl_expc = shadow.expval(H, k=9) +# pl_ent = shadow.entropy(sub, alpha=2) +# print(pl_expc, pl_ent) +# +# assert np.isclose(expc, pl_expc) +# assert np.isclose(ent, pl_ent) diff --git a/tests/test_templates.py b/tests/test_templates.py index 0174deac..79e2b176 100644 --- a/tests/test_templates.py +++ b/tests/test_templates.py @@ -1,7 +1,8 @@ # pylint: disable=invalid-name -import sys import os +import sys + import numpy as np import pytest from pytest_lazyfixture import lazy_fixture as lf @@ -25,7 +26,7 @@ def test_any_measurement(): np.testing.assert_allclose(r2, 0.0, atol=1e-5) -@pytest.mark.parametrize("backend", [lf("jaxb"), lf("tfb"), lf("jaxb")]) +@pytest.mark.parametrize("backend", [lf("jaxb"), lf("tfb")]) def test_parameterized_local_measurement(backend): c = tc.Circuit(3) c.X(0) @@ -64,6 +65,13 @@ def test_bell_block(): assert s[2] != s[3] +def test_qft_block() -> None: + c = tc.Circuit(4) + c = tc.templates.blocks.qft(c, 0, 1, 2, 3) + s = c.perfect_sampling() + assert s[1] - 0.0624999 < 10e-6 + + def test_grid_coord(): cd = tc.templates.graphs.Grid2DCoord(3, 2) assert cd.all_cols() == [(0, 3), (1, 4), (2, 5)] @@ -153,10 +161,6 @@ def test_operator_measurement(backend): ) dense = tc.array_to_tensor(np.kron(tc.gates._x_matrix, np.eye(2))) sparse = tc.quantum.PauliString2COO([1, 0]) - if tc.backend.name == "jax": - sparse = tc.backend.coo_sparse_matrix( - sparse.indices, sparse.values, sparse.shape - ) for h in [dense, sparse, mpo]: @@ -171,3 +175,34 @@ def f(theta): np.testing.assert_allclose(v, 0.84147, atol=1e-4) np.testing.assert_allclose(g, 0.54032, atol=1e-4) + + +@pytest.fixture +def symmetric_matrix(): + matrix = np.array([[-5.0, -2.0], [-2.0, 6.0]]) + nsym_matrix = np.array( + [[1.0, 2.0, 3.0], [2.0, 4.0, 5.0], [3.0, 5.0, 6.0], [8.0, 7.0, 6.0]] + ) + return matrix, nsym_matrix + + +def test_QUBO_to_Ising(symmetric_matrix): + matrix1, matrix2 = symmetric_matrix + pauli_terms, weights, offset = tc.templates.conversions.QUBO_to_Ising(matrix1) + n = matrix1.shape[0] + expected_num_terms = n + n * (n - 1) // 2 + assert len(pauli_terms) == expected_num_terms + assert len(weights) == expected_num_terms + assert isinstance(pauli_terms, list) + assert isinstance(weights, np.ndarray) + assert isinstance(offset, float) + assert pauli_terms == [ + [1, 0], + [0, 1], + [1, 1], + ] + assert all(weights == np.array([3.5, -2.0, -1.0])) + assert offset == -0.5 + + with pytest.raises(ValueError): + tc.templates.conversions.QUBO_to_Ising(matrix2) diff --git a/tests/test_torchnn.py b/tests/test_torchnn.py index 93432c3a..88549248 100644 --- a/tests/test_torchnn.py +++ b/tests/test_torchnn.py @@ -14,12 +14,11 @@ try: import torch except ImportError: - pytest.skip("torch is not installed") + pytest.skip("torch is not installed", allow_module_level=True) @pytest.mark.parametrize("backend", [lf("tfb"), lf("jaxb"), lf("torchb")]) def test_quantumnet(backend): - n = 6 nlayers = 2 @@ -56,6 +55,7 @@ def test_inputs_multiple(backend): n = 3 p = 0.1 K = tc.backend + torchb = tc.get_backend("pytorch") def f(state, noise, weights): c = tc.Circuit(n, inputs=state) @@ -66,8 +66,34 @@ def f(state, noise, weights): return K.real(c.expectation_ps(x=[0])) layer = tc.TorchLayer(f, [n], use_vmap=True, vectorized_argnums=[0, 1]) - l = layer( - tc.get_backend("pytorch").ones([2, 2**n]) / 2 ** (n / 2), - 0.2 * tc.get_backend("pytorch").ones([2, n], dtype="float32"), - ) + state = torchb.ones([2, 2**n]) / 2 ** (n / 2) + noise = 0.2 * torchb.ones([2, n], dtype="float32") + l = layer(state, noise) + lsum = torchb.sum(l) print(l) + lsum.backward() + for p in layer.parameters(): + print(p.grad) + + +@pytest.mark.parametrize("backend", [lf("tfb"), lf("jaxb"), lf("torchb")]) +def test_torchnn_hardware(backend): + n = 2 + + def qf(inputs, param): + inputs = tc.backend.convert_to_tensor(tc.get_backend("pytorch").numpy(inputs)) + param = tc.backend.convert_to_tensor(tc.get_backend("pytorch").numpy(param)) + + c = tc.Circuit(n) + c.rx(0, theta=inputs[0]) + c.rx(1, theta=inputs[1]) + c.h(1) + c.rzz(0, 1, theta=param[0]) + r = tc.backend.stack([c.expectation_ps(z=[i]) for i in range(n)]) + + r = tc.get_backend("pytorch").convert_to_tensor(tc.backend.numpy(r)) + return torch.real(r) + + ql = tc.torchnn.HardwareNet(qf, [1]) + qnet = torch.nn.Sequential(ql, torch.nn.Linear(2, 1)) + print(qnet(torch.ones([5, 2])))