From b5f092c8a3847fadaaefc01af5fb2c0d631690ba Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Thu, 29 Jun 2023 16:01:20 -0700 Subject: [PATCH] New release v0.4.0.RC into main (#326) * Update develop to catch up with main version 0.3.3 (#249) * Fix link of fig in qchem & excited states notebook (#250) * allow single flip index dis for qcc (#247) * allow single flip index dis for jkmn * Richardson extrapolation: bug fix + error estimation (#252) * Bugfix: DMET with QCC (#253) * iQCC using only Clifford circuits notebook (#254) * pUCCD ansatz (#251) * UHF reference (#240) * uhf implementation with VQESolver functionality * support for all types of orbital freezing * add active_spin and uhf attributes to SecondQuantizedDMETFragment * add spin to adapt_ansatz arguments * added multi-product, grid_circuits and discrete_clock (#257) * added multi-product, grid_circuits and discrete_clock * translation to pennylane (#260) * tangelo to pennylane format translation Co-authored-by: Valentin Senicourt <41597680+ValentinS4t1qbit@users.noreply.github.com> * bump testing version to 3.8 (#262) Updating python version to 3.8 in automated tests, as 3.7 is no longer maintained by the Python dev team * Auto threshold cutoff for small coefficients in LCU (#261) * check for small value lcu * changed to keep same vector length but apply no operations * Openshell DMET (#208) * Open-shell DMET. * Fix for get_rdm CCSD. * Added NAO localization. * Added LiO2 spin=1 DMET test. * Added UHF MF for DMET. New get_rdm for VQESolver. * Save mid-circuit measurement (#256) * A flag now allows users to save mid-circuit measurements for each shot run. Co-authored-by: Valentin Senicourt * Fix for IBMQConnection (#264) * Updated copyright year to 2023 (#267) * added draw method to circuit (#266) * added draw method to circuit (warning about font comes up in linux env) * Cirq, qulacs, pennylane and projectq operator translation functions (#268) * Support to bidirectional translation of operators for cirq, qulacs, PennyLane, projectq. * Fixed a bug in QulacsSimulator.expectation_value_from_prepared_state. * Important bug in qiskit -> tangelo op translation fixed * Fixed identity operator evaluation and extended the accepted input types in compute_rdms (#269) * Compute RDMs function can now hangle both term formats (string and openfermion tuple). * Unhashable list for of -> conversion to tuples. * Handling all the inputs in compute_rdms. * Temporarily disabling OS-DMET (#271) * Temporarily disabling for code verification purpose, as the implementation seems to return very odd and incorrect results for simple systems. * Link to new example repo * Update README.rst * Revert changes that broke tests (#275) * reverted changes that broke tests * DMET + frozen orbitals for each fragment (#276) * DMET + frozen orbitals, added DMET/frozen tests. * Example folder relocated to new repo Tangelo-Examples (#274) * removed examples folder, removed notebooks from automated tests, added link higher in README.rst * Docs update (#272) * Updated conf.py, tested building docs and fixed comments/docs. * Removed tutorials.rst with the notebooks. * Desired measurement result (#263) * New feature: return frequencies matching desired mid-circuit measurement values. --------- Co-authored-by: James Brown * fix error for imaginary qubit_op with desired meas result (#278) * Integrate bug fix into main before release (#278) (#279) (#280) Co-authored-by: James Brown * fix error for imaginary qubit_op with desired meas result (#278) * Depth function performance increase (#285) * Depth function takes now less than half a second to compute it from scratch for ~120K depth circuit. * Fix for (inverse of S and T gate) #287. (#288) * Translation perf tests (operator, circuit) (#289) * Performance test for translation layer of linq, for most formats --------- Co-authored-by: Valentin Senicourt Co-authored-by: ValentinS4t1qbit * deterministic desired_meas_result (#290) * deterministic desired_meas_result * Mifnohelper print: remove problem handle (#293) * Removed problem handle from print. It now only focuses on chemistry information Co-authored-by: Valentin Senicourt Co-authored-by: ValentinS4t1qbit * Combinatorial mapping of fermionic Hamiltonians (#286) * Initial implementation. Known performance scaling issues, may attempt to introduce a different QubitOperator in the future. * Re-enabling open-shell DMET (#291) * Re-enabling OS-DMET. * Support for symbolic simulator (simpy) (#292) Support for symbolic backend based on sympy, to provide algebraic expressions for states / circuits with less than 10 qubits. Notebook / examples to follow later on. * Arg simulate_options and projective_circuit to VQESolver (#298) * It is now possible to request desired measurements and other options for the "simulate" method called by the backend * Projective circuits can be used with VQE algorithm(s). Either passed by the user explicitly or integrated through the ansatz. --------- Co-authored-by: Valentin Senicourt <41597680+ValentinS4t1qbit@users.noreply.github.com> * Supporting symbols as parameters and add nsimplify to results (#300) * Symbolic expression simplified. * Simplification of exp. value and fixed a bug in translate op. * Add ability to get inverse with Symbol parameters. * Switch nsimplify to evalf. * Trim trivial qubits from circuit and Hamiltonian (#302) Remove qubits that are in a deterministic state in quantum circuits, and simplify qubit Hamiltonian accordingly, in order to reduce resource requirements while computing expectation values. --------- Co-authored-by: Valentin Senicourt <41597680+ValentinS4t1qbit@users.noreply.github.com> * Push contributors towards develop and not to main for PRs (#303) * Update to CONTRIB (main -> develop) and added workflow to help avoid PR to main that are not from develop * Set the DMET virtual orbital truncation threshold at the user level (#304) * Add option to turn off virt. orb. truncation. * Changed the threshold instead of a flag. Occ. set to their abs values. * Trim bug (#305) * minor bug fixes + test * Added typing for option dictionaries in quantum agorithms (#307) * added typing for option dictionaries, which improves user experience (autocomplete, auto-loading of docstrings for different GUIs) and handle options better overall * IntegralSolver class: base + support for Psi4 (#297) * Tests for pyscf, test for psi4, tests for custom IntegralSolver * temporary: install NO_PYSCF available to users * new: MP2Solver This PR will be followed by 1-2 other PRs to decouple pyscf from almost all algorithms and streamline the use of classical chem backends (support for all classical solvers, turning pyscf as an optional dependency for Tangelo, fixing docs and README etc as a result) * Change multi-controlled CNOT to multi-controlled CX (#308) * change multi-controlled CNOT to multi-controlled CX --------- Co-authored-by: Valentin Senicourt <41597680+ValentinS4t1qbit@users.noreply.github.com> * Braket connection (#312) Support for Braket connection, including batch submission of jobs. Tests, docs, refresh of env_var.sh A PR for the QPU notebook will follow --------- Co-authored-by: Valentin Senicourt Co-authored-by: ValentinS4t1qbit * Support for UMP2 initial parameters (#310) * Support for UMP2 initial parameters. * Checkfile for IntegralSolverPySCF (#311) * chkfile for IntegralSolverPySCF. * QubitOperator import from Tangelo, for better encapsulation (#299) * Use Tangelo's `QubitOperator` instead of OpenFermion when possible. * Constructor method `from_openfermion` and export method `to_openfermion` available in `QubitOperator` class. Remaining usage of Openfermion is tied to qubit mappings mostly, and code relying on other Openfermion features. --------- Co-authored-by: Valentin Senicourt Co-authored-by: ValentinS4t1qbit * FCISolverPsi4 (#309) * moved chem tests to molecular_computation tests * Changed FCISolver to class with solver attribute --------- Co-authored-by: Valentin Senicourt <41597680+ValentinS4t1qbit@users.noreply.github.com> * Removed deprecated Linq functions (#316) * Removed deprecated versions of trasnlation functions and Simulator. --------- Co-authored-by: Valentin Senicourt Co-authored-by: ValentinS4t1qbit * CCSD solver psi4 (#313) * add ccsd_solver test to psi4 testing * get_rdm currently not supported, suggestions added in comments * ADAPT: add spin to available options (#317) * add spin to available options and docstring * MP2 psi4 (#315) * Support for MP2 with psi4 backend. * get_rdm and get_mp2_parameters currently unsupported, suggestions left in docstrings. * skip performance tests (Linq) in pytest (#319) Perrformance tests take time and should not be run systematically * Typos in docs (#321) * Added spaces in messages. * Typos and code format. * Add period. * DMET Effective Core Potential fix(related to #306) (#318) * potential fix for #306, test added * Change installation (#320) * remove no_pyscf, attempt windows test * updated psi4 test --------- Co-authored-by: Valentin Senicourt <41597680+ValentinS4t1qbit@users.noreply.github.com> * DMET-ecp test fix: change optimizer to minimize square (#323) * improved Error message for MP2 initialization in UCCSD (pySCf currently required) (#322) * improved Error message for MP2 initialization if pyscf is not found * Qiskit-related: deprecation update to SparsePauliOp and bug workaround (#325) * Stim clifford simulator integration (#314) * direct tableau * noise and circuit sampler * decomposition into clifford gates * clifford decomp tests, and SDAG in cirq * is_clifford in gate class * expand clifford decomp to integer values * small comments, add pip install stim to workflow Co-authored-by: Valentin Senicourt <41597680+ValentinS4t1qbit@users.noreply.github.com> * Bumping Tangelo version number in _version.py * Update CHANGELOG.md --------- Co-authored-by: Valentin Senicourt <41597680+ValentinS4t1qbit@users.noreply.github.com> Co-authored-by: AlexandreF-1qbit <76115575+AlexandreF-1qbit@users.noreply.github.com> Co-authored-by: James Brown <84878946+JamesB-1qbit@users.noreply.github.com> Co-authored-by: KrzysztofB-1qbit <86750444+KrzysztofB-1qbit@users.noreply.github.com> Co-authored-by: Valentin Senicourt Co-authored-by: James Brown Co-authored-by: ValentinS4t1qbit Co-authored-by: elloyd-1qbit <58313607+elloyd-1qbit@users.noreply.github.com> Co-authored-by: GitHub Actions --- .github/workflows/continuous_integration.yml | 23 +- .github/workflows/protect_main.yaml | 14 + .github/workflows/run_psi4_test.yml | 76 ++++ CHANGELOG.md | 23 ++ CONTRIBUTIONS.rst | 13 +- README.rst | 50 +-- env_var.sh | 24 +- setup.py | 8 +- tangelo/_version.py | 2 +- tangelo/algorithms/__init__.py | 2 +- tangelo/algorithms/classical/__init__.py | 1 + tangelo/algorithms/classical/ccsd_solver.py | 207 ++++++++++- tangelo/algorithms/classical/fci_solver.py | 196 +++++++++- tangelo/algorithms/classical/mp2_solver.py | 336 ++++++++++++++++++ .../classical/semi_empirical_solver.py | 3 +- .../classical/tests/test_ccsd_solver.py | 26 +- .../classical/tests/test_fci_solver.py | 20 +- .../classical/tests/test_mp2_solver.py | 117 ++++++ .../tests/test_semi_empirical_solver.py | 3 +- .../projective/quantum_imaginary_time.py | 43 ++- .../variational/adapt_vqe_solver.py | 75 ++-- .../algorithms/variational/iqcc_ilc_solver.py | 45 ++- tangelo/algorithms/variational/iqcc_solver.py | 46 +-- .../variational/sa_oo_vqe_solver.py | 10 +- .../algorithms/variational/sa_vqe_solver.py | 16 +- .../tests/test_adapt_vqe_solver.py | 48 ++- .../variational/tests/test_sa_vqe_solver.py | 26 ++ .../variational/tests/test_vqe_solver.py | 37 +- tangelo/algorithms/variational/vqe_solver.py | 64 ++-- tangelo/helpers/math.py | 26 ++ tangelo/helpers/utils.py | 7 +- tangelo/linq/__init__.py | 3 +- tangelo/linq/circuit.py | 81 ++++- tangelo/linq/gate.py | 34 +- .../helpers/circuits/clifford_circuits.py | 89 +++++ .../circuits/tests/test_clifford_circuits.py | 43 +++ tangelo/linq/qpu_connection/__init__.py | 1 + .../linq/qpu_connection/braket_connection.py | 185 ++++++++++ tangelo/linq/simulator.py | 6 - tangelo/linq/target/__init__.py | 6 +- tangelo/linq/target/backend.py | 107 ++++-- tangelo/linq/target/target_cirq.py | 48 ++- tangelo/linq/target/target_qiskit.py | 94 +++-- tangelo/linq/target/target_qulacs.py | 98 +++-- tangelo/linq/target/target_stim.py | 147 ++++++++ tangelo/linq/target/target_sympy.py | 115 ++++++ tangelo/linq/tests/test_braket_connection.py | 86 +++++ tangelo/linq/tests/test_circuits.py | 23 +- tangelo/linq/tests/test_gates.py | 33 ++ tangelo/linq/tests/test_ibm_connection.py | 2 +- tangelo/linq/tests/test_ionq_connection.py | 2 +- tangelo/linq/tests/test_simulator.py | 78 ++-- tangelo/linq/tests/test_simulator_noisy.py | 44 ++- tangelo/linq/tests/test_symbolic_simulator.py | 82 +++++ tangelo/linq/tests/test_translator_circuit.py | 125 +++++-- tangelo/linq/tests/test_translator_perf.py | 104 ++++++ tangelo/linq/tests/test_translator_qubitop.py | 41 +-- tangelo/linq/translator/__init__.py | 20 +- tangelo/linq/translator/translate_braket.py | 15 - tangelo/linq/translator/translate_circuit.py | 7 +- tangelo/linq/translator/translate_cirq.py | 24 +- .../linq/translator/translate_json_ionq.py | 17 - tangelo/linq/translator/translate_openqasm.py | 39 +- tangelo/linq/translator/translate_projectq.py | 34 -- tangelo/linq/translator/translate_qdk.py | 22 +- tangelo/linq/translator/translate_qiskit.py | 34 +- tangelo/linq/translator/translate_qubitop.py | 8 +- tangelo/linq/translator/translate_qulacs.py | 21 +- tangelo/linq/translator/translate_stim.py | 129 +++++++ tangelo/linq/translator/translate_sympy.py | 234 ++++++++++++ tangelo/molecule_library.py | 8 +- .../dmet/_helpers/dmet_bath.py | 22 +- .../dmet/_helpers/dmet_orbitals.py | 19 +- .../dmet/_helpers/dmet_scf.py | 4 +- .../dmet/dmet_problem_decomposition.py | 120 ++++--- .../problem_decomposition/dmet/fragment.py | 2 +- .../electron_localization/iao_localization.py | 9 +- .../meta_lowdin_localization.py | 3 +- .../electron_localization/nao_localization.py | 3 +- .../incremental/mifno_helper.py | 3 +- .../oniom/_helpers/helper_classes.py | 138 +++---- .../oniom/oniom_problem_decomposition.py | 21 +- .../tests/dmet/test_dmet.py | 21 ++ .../tests/dmet/test_osdmet.py | 2 - .../ansatz_generator/ansatz_utils.py | 3 +- .../tests/test_ansatz_util.py | 16 +- .../ansatz_generator/tests/test_vsqs.py | 16 +- tangelo/toolboxes/ansatz_generator/uccgd.py | 11 +- tangelo/toolboxes/ansatz_generator/uccsd.py | 64 ++-- tangelo/toolboxes/ansatz_generator/vsqs.py | 7 +- .../circuits/tests/test_diagonal_coulomb.py | 4 +- .../circuits/tests/test_discrete_clock.py | 21 +- tangelo/toolboxes/circuits/tests/test_grid.py | 3 - tangelo/toolboxes/circuits/tests/test_lcu.py | 22 +- tangelo/toolboxes/circuits/tests/test_mp.py | 13 +- tangelo/toolboxes/circuits/tests/test_qsp.py | 14 +- .../measurements/tests/test_measurements.py | 4 +- .../tests/test_qubit_terms_grouping.py | 8 +- .../molecular_computation/__init__.py | 4 + .../molecular_computation/frozen_orbitals.py | 6 +- .../molecular_computation/integral_solver.py | 110 ++++++ .../integral_solver_psi4.py | 218 ++++++++++++ .../integral_solver_pyscf.py | 282 +++++++++++++++ .../molecular_computation/molecule.py | 290 ++++----------- .../toolboxes/molecular_computation/rdms.py | 155 +++++++- ...H2O_UHF_sto3g_onerdm_frozen0345_alpha.data | 3 + .../H2O_UHF_sto3g_onerdm_frozen0345_beta.data | 3 + ..._sto3g_padded_onerdm_frozen0345_alpha.data | 7 + ...F_sto3g_padded_onerdm_frozen0345_beta.data | 7 + ...g_padded_twordm_frozen0345_alphaalpha.data | 49 +++ ...3g_padded_twordm_frozen0345_alphabeta.data | 49 +++ ...o3g_padded_twordm_frozen0345_betabeta.data | 49 +++ ...HF_sto3g_twordm_frozen0345_alphaalpha.data | 9 + ...UHF_sto3g_twordm_frozen0345_alphabeta.data | 9 + ..._UHF_sto3g_twordm_frozen0345_betabeta.data | 9 + .../tests/data/h2_631g.npz | Bin 0 -> 4912 bytes .../tests/test_molecule.py | 58 ++- .../tests/test_nopyscf.py | 99 ++++++ .../molecular_computation/tests/test_psi4.py | 99 ++++++ .../molecular_computation/tests/test_rdms.py | 38 +- tangelo/toolboxes/operators/operators.py | 29 +- .../operators/tests/test_operators.py | 5 +- .../tests/test_trim_trivial_qubits.py | 101 ++++++ .../operators/trim_trivial_qubits.py | 177 +++++++++ .../post_processing/post_selection.py | 13 +- tangelo/toolboxes/qubit_mappings/__init__.py | 1 + .../toolboxes/qubit_mappings/combinatorial.py | 233 ++++++++++++ .../qubit_mappings/statevector_mapping.py | 2 +- .../tests/test_combinatorial.py | 120 +++++++ .../tests/test_statevector_mapping.py | 2 +- 130 files changed, 5532 insertions(+), 1153 deletions(-) create mode 100644 .github/workflows/protect_main.yaml create mode 100755 .github/workflows/run_psi4_test.yml create mode 100644 tangelo/algorithms/classical/mp2_solver.py create mode 100644 tangelo/algorithms/classical/tests/test_mp2_solver.py create mode 100644 tangelo/linq/helpers/circuits/clifford_circuits.py create mode 100644 tangelo/linq/helpers/circuits/tests/test_clifford_circuits.py create mode 100644 tangelo/linq/qpu_connection/braket_connection.py create mode 100644 tangelo/linq/target/target_stim.py create mode 100644 tangelo/linq/target/target_sympy.py create mode 100644 tangelo/linq/tests/test_braket_connection.py create mode 100644 tangelo/linq/tests/test_symbolic_simulator.py create mode 100644 tangelo/linq/tests/test_translator_perf.py create mode 100644 tangelo/linq/translator/translate_stim.py create mode 100644 tangelo/linq/translator/translate_sympy.py create mode 100644 tangelo/toolboxes/molecular_computation/integral_solver.py create mode 100644 tangelo/toolboxes/molecular_computation/integral_solver_psi4.py create mode 100644 tangelo/toolboxes/molecular_computation/integral_solver_pyscf.py create mode 100644 tangelo/toolboxes/molecular_computation/tests/data/H2O_UHF_sto3g_onerdm_frozen0345_alpha.data create mode 100644 tangelo/toolboxes/molecular_computation/tests/data/H2O_UHF_sto3g_onerdm_frozen0345_beta.data create mode 100644 tangelo/toolboxes/molecular_computation/tests/data/H2O_UHF_sto3g_padded_onerdm_frozen0345_alpha.data create mode 100644 tangelo/toolboxes/molecular_computation/tests/data/H2O_UHF_sto3g_padded_onerdm_frozen0345_beta.data create mode 100644 tangelo/toolboxes/molecular_computation/tests/data/H2O_UHF_sto3g_padded_twordm_frozen0345_alphaalpha.data create mode 100644 tangelo/toolboxes/molecular_computation/tests/data/H2O_UHF_sto3g_padded_twordm_frozen0345_alphabeta.data create mode 100644 tangelo/toolboxes/molecular_computation/tests/data/H2O_UHF_sto3g_padded_twordm_frozen0345_betabeta.data create mode 100644 tangelo/toolboxes/molecular_computation/tests/data/H2O_UHF_sto3g_twordm_frozen0345_alphaalpha.data create mode 100644 tangelo/toolboxes/molecular_computation/tests/data/H2O_UHF_sto3g_twordm_frozen0345_alphabeta.data create mode 100644 tangelo/toolboxes/molecular_computation/tests/data/H2O_UHF_sto3g_twordm_frozen0345_betabeta.data create mode 100644 tangelo/toolboxes/molecular_computation/tests/data/h2_631g.npz create mode 100644 tangelo/toolboxes/molecular_computation/tests/test_nopyscf.py create mode 100644 tangelo/toolboxes/molecular_computation/tests/test_psi4.py create mode 100644 tangelo/toolboxes/operators/tests/test_trim_trivial_qubits.py create mode 100644 tangelo/toolboxes/operators/trim_trivial_qubits.py create mode 100644 tangelo/toolboxes/qubit_mappings/combinatorial.py create mode 100644 tangelo/toolboxes/qubit_mappings/tests/test_combinatorial.py diff --git a/.github/workflows/continuous_integration.yml b/.github/workflows/continuous_integration.yml index d4f77c193..1f0438003 100755 --- a/.github/workflows/continuous_integration.yml +++ b/.github/workflows/continuous_integration.yml @@ -11,9 +11,9 @@ jobs: python-version: [3.8] steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 - name: Set up Python - uses: actions/setup-python@v2 + uses: actions/setup-python@v3 with: python-version: ${{ matrix.python-version }} @@ -44,6 +44,7 @@ jobs: pip install cirq pip install projectq pip install pennylane + pip install stim if: always() - name: Install Microsoft qsharp/qdk @@ -63,12 +64,30 @@ jobs: python -m pip install . if: always() + - name: tangelo no_pyscf tests + run: | + cd tangelo/toolboxes/molecular_computation/tests + pytest --doctest-modules --junitxml=junit/nopyscf-test-results.xml test_nopyscf.py + if: always() + + - name: Install pyscf + run: | + python -m pip install pyscf + python -m pip install git+https://github.com/pyscf/semiempirical + if: always() + - name: tangelo tests run: | cd tangelo pytest --doctest-modules --junitxml=junit/tangelo-test-results.xml --cov=. --cov-report=xml --cov-report=html if: always() + - name: Upload nopyscf test results + uses: actions/upload-artifact@v3 + with: + name: tangelo-no-pyscf-test-results + path: tangelo/toolboxes/molecular_computation/tests/junit/nopyscf-test-results.xml + - name: Upload pytest test results uses: actions/upload-artifact@v3 with: diff --git a/.github/workflows/protect_main.yaml b/.github/workflows/protect_main.yaml new file mode 100644 index 000000000..4e095b09f --- /dev/null +++ b/.github/workflows/protect_main.yaml @@ -0,0 +1,14 @@ +name: 'Protect Main Branch' + +on: + pull_request: + +jobs: + check_branch: + runs-on: ubuntu-latest + steps: + - name: Check branch + if: github.base_ref == 'main' && github.head_ref != 'develop' + run: | + echo "ERROR: You can only merge to main from develop. Make sure your PR merges into the right branch." + exit 1 diff --git a/.github/workflows/run_psi4_test.yml b/.github/workflows/run_psi4_test.yml new file mode 100755 index 000000000..4b63bb788 --- /dev/null +++ b/.github/workflows/run_psi4_test.yml @@ -0,0 +1,76 @@ +name: psi4 test + +on: [pull_request] + +jobs: + build: + + runs-on: ubuntu-latest + strategy: + matrix: + python-version: [3.8] + + defaults: + run: + shell: bash -el {0} + + steps: + - uses: actions/checkout@v3 + - uses: conda-incubator/setup-miniconda@v2 + with: + activate-environment: anaconda-client-env + - run: conda info + - run: conda list + - run: conda config --show + + - name: Install psi4 + shell: bash -el {0} + run: | + conda install psi4 python=3.8 -c psi4 + conda init + if: always() + + - name: Install pip, wheel, pytest, jupyter + run: | + python -m pip install --upgrade pip + pip install wheel + pip install pytest + pip install pytest-cov + + - name: Install qulacs + run: | + pip install qulacs + if: always() + + - name: tangelo install + run: | + python -m pip install . + if: always() + + - name: tangelo psi4 integral tests + run: | + cd tangelo/toolboxes/molecular_computation/tests + pytest --doctest-modules --junitxml=junit/psi4-test-results.xml test_psi4.py + if: always() + + - name: tangelo psi4 classical tests + run: | + cd tangelo/algorithms/classical/tests + pytest --doctest-modules --junitxml=junit/psi4-classical-test-results.xml + if: always() + + - name: Upload psi4 test results + uses: actions/upload-artifact@v3 + with: + name: tangelo-psi4-test-results + path: tangelo/toolboxes/molecular_computation/tests/junit/psi4-test-results.xml + + - name: Upload classical psi4 test results + uses: actions/upload-artifact@v3 + with: + name: tangelo-classical-psi4-test-results + path: tangelo/algorithms/classical/tests/junit/psi4-classical-test-results.xml + + - name: Download all workflow run artifacts + uses: actions/download-artifact@v3 + if: always() diff --git a/CHANGELOG.md b/CHANGELOG.md index 000fd50e3..86dd4f521 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,29 @@ This file documents the main changes between versions of the code. +## [0.4.0] - 2023-06-29 + +### Added + +- Psi4 and pyscf optional dependencies, can be used as chemistry backends for classical calculations +- symbolic simulator +- stim clifford simulator +- Support for UHF reference mean-field in DMET +- trimming trivial qubits from Hamiltonians and circuits +- BraketConnection class +- combinatorial qubit mapping +- MP2Solver + +### Changed + +- Bugfix: DMET with virtual space truncation threshold, as well as ecp +- ADAPT now supports spin as parameter + +### Deprecatedv / Removed + +- in linq: Old translation functions, and Simulator class (use get_backend or translate_circuit instead) + + ## [0.3.4] - 2023-02-15 ### Added diff --git a/CONTRIBUTIONS.rst b/CONTRIBUTIONS.rst index 0133184b1..5155d731f 100644 --- a/CONTRIBUTIONS.rst +++ b/CONTRIBUTIONS.rst @@ -43,24 +43,25 @@ In your terminal, clone the repo on your local machine, and move into the newly cd Tangelo From the perspective of your local clone, your fork is called the ``origin`` remote. -Let's synchronize your fork with the main Tangelo repo by adding the latter as the upstream remote, and then update your local ``main`` branch: +Let's synchronize your fork with the main Tangelo repo by adding the latter as the upstream remote, and then update your local ``develop`` branch: .. code-block:: shell git remote add upstream https://github.com/goodchemistryco/Tangelo.git git fetch upstream - git checkout main - git merge upstream/main + git checkout develop + git merge upstream/develop +Note: we here suggest the ``develop`` branch, as this is where contributions will be merged. No one should be merging directly to ``main``, unless it is to sync it with ``develop`` once in a while, and just before a new version release. **2. Work on your own developments** -Create your development branch, based on the ``main`` branch (or the current development branch listed on the `DevBranch badge <./README.rst>`_) +Create your development branch, based on the ``develop`` branch (or the current development branch listed on the `DevBranch badge <./README.rst>`_, if different) .. code-block:: shell - git checkout main -b your_branch_name + git checkout develop -b your_branch_name where ``your_branch_name`` is the name of your own development branch, preferably related to what you will be working on. Let's assume you've made some changes and committed them with ``git commit``, and that you'd like to push them to your fork (which is referred to as "origin"): @@ -72,7 +73,7 @@ Let's assume you've made some changes and committed them with ``git commit``, an **3. The Pull Request (PR)** -Now when you go to https://github.com/goodchemistryco/Tangelo, you should be able to create a pull request from the branch on your fork to a branch on the main Tangelo repo. Give your pull request a name and briefly describe what the purpose is and include a reference to the associated issue if there's one. +Now when you go to https://github.com/goodchemistryco/Tangelo, you should be able to create a pull request from the branch on your fork to a branch on the main Tangelo repo. Give your pull request a name, verify that the destination branch is ``develop`` (not ``main``), and briefly describe what the purpose is / include a reference to the associated issue if there's one. Several Tangelo users will receive a notification, and will review your code and leave comments in the PR. You can reply to these comments, or simply apply the recommended changes locally, and then commit and push them like above: it automatically updates your PR. If there are conflicts, you can solve them locally and push, or directly through Github. diff --git a/README.rst b/README.rst index 6f1f85a7a..cc86f0f4c 100644 --- a/README.rst +++ b/README.rst @@ -96,43 +96,31 @@ separately with ``pip``\ , before trying again. With Docker ^^^^^^^^^^^ -Use our Docker file to deploy Tangelo in a Linux environment, either retrieved from pip or mounted locally. -Comment / uncomment the relevant sections of the Dockerfile to control installation and dependencies. +Use our Docker file to deploy Tangelo in a Linux environment, either retrieved from pip or mounted locally. Comment / uncomment the relevant sections of the Dockerfile to control installation and dependencies. "No install" notebook method ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -A good alternative for users that simply want to quickly get a working environment ready, especially for quick tests, demos, tutorials. -Check out the tutorial section below to see how services such as Google Colab may help you circumvent local installation challenges or go beyond the limitations of your personal computer if you feel short of compute power or memory. +Check out the tutorial section below to see how services such as Google Colab, Binder or JupyterLab may help you circumvent local installation challenges or go beyond the compute capabilities of your laptop. +Optional dependencies: Quantum Simulators +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -Optional dependencies -^^^^^^^^^^^^^^^^^^^^^ +Tangelo enables users to target various backends. In particular, it integrates quantum circuit simulators such as ``qulacs``\ , ``qiskit``\ , ``cirq`` or ``qdk``. We leave it to you to install the packages of your choice, and refer to their own documentation. Most packages can be installed through pip or conda in a straightforward way. -Tangelo enables users to target various backends. In particular, it integrates quantum circuit simulators such as -``qulacs``\ , ``qiskit``\ , ``cirq`` or ``qdk``. We leave it to you to install the packages of your choice, and refer to their own documentation. -Most packages can be installed through pip in a straightforward way: -.. code-block:: +Optional dependencies: Classical Quantum Chemistry Packages +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - pip install qulacs - pip install qiskit - pip install cirq - ... +Tangelo can be used without having a classical quantum chemistry package installed but many algorithms, by default, depend on one being installed. The two quantum chemistry packages that are natively supported are `PySCF `_ and `Psi4 `_. -Depending on your OS and environment, some of these packages may be more challenging to install. For installing Microsoft's QDK -or any issue regarding the above packages, please check their respective documentation. +You are also welcome to provide your own interface to a quantum chemistry package of your choice by defining a subclass of `IntegralSolver `_. An example of this can be found in `this test `_. Quick note for Windows users ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -Our installation instructions will work on Linux and MacOS systems. If you are using Windows, we recommend -you install the `Windows Linux Subsystem `_, which allows you -to run Ubuntu as an application. Once it has been installed, you can type ``explorer.exe`` in your Ubuntu terminal to -drag and drop files between your Windows and Linux environment. - -Here are a few essentials to install inside a brand new Ubuntu environment, before trying to install Tangelo: +Depending on your OS and environment, some of the optional packages may be more challenging to install. If you are using Windows, we recommend you install the `Windows Linux Subsystem `_, which allows you to run Ubuntu as an application. Once it has been installed, you can type ``explorer.exe`` in your Ubuntu terminal to drag and drop files between your Windows and Linux environment. Here are a few essentials to install inside a brand new Ubuntu environment, before trying to install Tangelo: .. code-block:: @@ -145,21 +133,17 @@ Here are a few essentials to install inside a brand new Ubuntu environment, befo Optional: environment variables ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -Some environment variables can impact performance (ex: using GPU for quantum circuit simulation, or changing -the number of CPU threads used) or are used to connect to web services providing access to some compute backends. +Some environment variables can impact performance (ex: using GPU for quantum circuit simulation, or changing the number of CPU threads used) or are used to connect to web services providing access to some compute backends. -See the list of relevant environment variables and their use in ``env_var.sh``. In order for these variables to be set to -the desired values in your environment, you can run this shell script in Linux with the following command line: -``source env_var.sh`` (you may need to set execution permissions with ``chmod +x set_env_var.sh`` first), or you can set -them in whatever way your OS supports it, or even inside your python script using the ``os`` package. +See the list of relevant environment variables and their use in ``env_var.sh``. In order for these variables to be set to the desired values in your environment, you can run this shell script in Linux with the following command line: +``source env_var.sh`` (you may need to set execution permissions with ``chmod +x set_env_var.sh`` first), or you can set them in whatever way your OS supports it, or even inside your python script using the ``os`` package. Tutorials and examples ---------------------- We have a `dedicated repository `_ for examples and tutorials ! -We wrote a number of them, and tried to provide material that doesn't just explain how to use the software, but provides insights into the complex topics of chemistry, quantum computing, and digs into the challenges we encountered in our previous hardware experiments. -Nothing prevents users from contributing and showcasing what they have been doing with Tangelo. +We wrote a number of them, and tried to provide material that doesn't just explain how to use the software, but provides insights into the complex topics of chemistry, quantum computing, and digs into the challenges we encountered in our previous hardware experiments. Nothing prevents users from contributing and showcasing what they have been doing with Tangelo. You can visualize notebooks directly on Github, most of them have been pre-run. If you'd like to be able to run them locally, we suggest you use `Jupyter notebooks inside a virtual environment `_. @@ -181,8 +165,7 @@ Check out our `tutorials <./TUTORIALS.rst>`_ file for more details. Tests ----- -Unit tests can be found in the ``tests`` folders, located in the various toolboxes they are related to. To automatically -find and run all tests (assuming you are in the ``tangelo`` subfolder that contains the code of the package): +Unit tests can be found in the ``tests`` folders, located in the various toolboxes they are related to. To automatically find and run all tests (assuming you are in the ``tangelo`` subfolder that contains the code of the package): .. code-block:: @@ -198,8 +181,7 @@ You do not need to be a seasoned software developer or expert in your field to m However we need some guidelines and processes to ensure that we build something of quality for the community. We describe them in the `contributions <./CONTRIBUTIONS.rst>`_ file. There are many ways you can contribute, but in case you're considering contributing to the codebase: don't be scared of the infamous pull request process ! It can feel intimidating, but we've had a few researchers or high-schoolers go through their first one and... they came back for more ! Mostly. -You can use the `Issue tab `_ to open a bug report or feature request. -If you're not sure, starting a discussion in the `Discussion tab `_ is a good start: we'll figure it out from there. +You can use the `Issue tab `_ to open a bug report or feature request. If you're not sure, starting a discussion in the `Discussion tab `_ is a good start: we'll figure it out from there. By joining the Tangelo community and sharing your ideas and developments, you are creating an opportunity for us to learn and grow together, and take ideas to the finish line and beyond. diff --git a/env_var.sh b/env_var.sh index 31205af84..2bbc73239 100755 --- a/env_var.sh +++ b/env_var.sh @@ -1,14 +1,34 @@ #!/bin/bash -# To set the number of OpenMP threads used by the package (may have a strong impact on performance) + +# 1. Performance +# --------------------------------------------------------------- + +# Multithreading: OpenMP threads used in libraries such as quantum simulation backends export OMP_NUM_THREADS= -# To use GPUs, if qulacs-gpu has been installed (values: 0 or 1) +# TODO: replace with a flag that just says USE_GPU. Everything GPU-enabled and supported will be accelerated. +# Quantum simulator: use GPUs, if qulacs-gpu has been installed (values: 0 or 1) export QULACS_USE_GPU= + +# 2. QPU Connections +# --------------------------------------------------------------- + +# IBM Q services: Qiskit & Qiskit runtime need to be installed, you also +# need a IBM Q account set up. +export IBM_TOKEN= + # IONQ QPU services: your personal APIKEY to use IonQ REST services export IONQ_APIKEY= # Honeywell QPU services: your personal login info to use Honeywell REST services export HONEYWELL_EMAIL= export HONEYWELL_PASSWORD= + +# Amazon Braket services (set variables here or use ~/.aws/credentials) +# You need to have an account and the Amazon CLI installed. Check out their documentation. +export AWS_REGION= +export AWS_ACCESS_KEY_ID= +export AWS_SECRET_ACCESS_KEY= +export AWS_SESSION_TOKEN= diff --git a/setup.py b/setup.py index 34bd34cb7..ba50071c4 100755 --- a/setup.py +++ b/setup.py @@ -1,11 +1,13 @@ import setuptools import sys import subprocess +import os def install(package): subprocess.check_call([sys.executable, "-m", "pip", "install", package]) + with open("tangelo/_version.py") as f: version = f.readlines()[-1].split()[-1].strip("\"'") @@ -13,8 +15,6 @@ def install(package): long_description = f.read() install('wheel') -install('pyscf') -install('git+https://github.com/pyscf/semiempirical') description = "Maintained by Good Chemistry Company, focusing on the development of end-to-end materials simulation workflows on quantum computers." @@ -24,10 +24,10 @@ def install(package): version=version, description=description, long_description=description, - #long_description_content_type=description, + # long_description_content_type=description, url="https://github.com/goodchemistryco/Tangelo", packages=setuptools.find_packages(), test_suite="tangelo", setup_requires=['h5py'], - install_requires=['h5py', 'bitarray', 'openfermion', 'openfermionpyscf'] + install_requires=['h5py', 'bitarray', 'openfermion'] ) diff --git a/tangelo/_version.py b/tangelo/_version.py index 78dcec058..5c4abc295 100644 --- a/tangelo/_version.py +++ b/tangelo/_version.py @@ -14,4 +14,4 @@ """ Define version number here. It is read in setup.py, and bumped automatically when using the new release Github action. """ -__version__ = "0.3.4" +__version__ = "0.4.0.RC" diff --git a/tangelo/algorithms/__init__.py b/tangelo/algorithms/__init__.py index 251edf532..1ec9364e9 100644 --- a/tangelo/algorithms/__init__.py +++ b/tangelo/algorithms/__init__.py @@ -13,5 +13,5 @@ # limitations under the License. from .variational import BuiltInAnsatze, VQESolver, ADAPTSolver, SA_VQESolver, SA_OO_Solver -from .classical import FCISolver, CCSDSolver, MINDO3Solver +from .classical import FCISolver, CCSDSolver, MINDO3Solver, MP2Solver from .projective import QITESolver diff --git a/tangelo/algorithms/classical/__init__.py b/tangelo/algorithms/classical/__init__.py index bd16f8a1e..4e9fc9d42 100644 --- a/tangelo/algorithms/classical/__init__.py +++ b/tangelo/algorithms/classical/__init__.py @@ -15,3 +15,4 @@ from .fci_solver import FCISolver from .ccsd_solver import CCSDSolver from .semi_empirical_solver import MINDO3Solver +from .mp2_solver import MP2Solver diff --git a/tangelo/algorithms/classical/ccsd_solver.py b/tangelo/algorithms/classical/ccsd_solver.py index 980ccbc61..7dea84699 100644 --- a/tangelo/algorithms/classical/ccsd_solver.py +++ b/tangelo/algorithms/classical/ccsd_solver.py @@ -15,16 +15,25 @@ """Class performing electronic structure calculation employing the CCSD method. """ +from typing import Union, Type + import numpy as np -from pyscf import cc, lib -from pyscf.cc.ccsd_rdm import _make_rdm1, _make_rdm2, _gamma1_intermediates, _gamma2_outcore -from pyscf.cc.uccsd_rdm import (_make_rdm1 as _umake_rdm1, _make_rdm2 as _umake_rdm2, - _gamma1_intermediates as _ugamma1_intermediates, _gamma2_outcore as _ugamma2_outcore) +from sympy.combinatorics.permutations import Permutation +from tangelo.toolboxes.molecular_computation.molecule import SecondQuantizedMolecule +from tangelo.toolboxes.molecular_computation import IntegralSolverPsi4, IntegralSolverPySCF from tangelo.algorithms.electronic_structure_solver import ElectronicStructureSolver +from tangelo.helpers.utils import installed_chem_backends, is_package_installed +if 'pyscf' in installed_chem_backends: + default_ccsd_solver = 'pyscf' +elif 'psi4' in installed_chem_backends: + default_ccsd_solver = 'psi4' +else: + default_ccsd_solver = None -class CCSDSolver(ElectronicStructureSolver): + +class CCSDSolverPySCF(ElectronicStructureSolver): """Uses the CCSD method to solve the electronic structure problem, through pyscf. @@ -38,6 +47,11 @@ class CCSDSolver(ElectronicStructureSolver): """ def __init__(self, molecule): + if not is_package_installed("pyscf"): + raise ModuleNotFoundError(f"Using {self.__class__.__name__} requires the installation of the pyscf package") + from pyscf import cc + + self.cc = cc self.cc_fragment = None self.spin = molecule.spin @@ -53,7 +67,7 @@ def simulate(self): float: CCSD energy. """ # Execute CCSD calculation - self.cc_fragment = cc.CCSD(self.mean_field, frozen=self.frozen) + self.cc_fragment = self.cc.CCSD(self.mean_field, frozen=self.frozen) self.cc_fragment.verbose = 0 self.cc_fragment.conv_tol = 1e-9 self.cc_fragment.conv_tol_normt = 1e-7 @@ -74,6 +88,10 @@ def get_rdm(self): Raises: RuntimeError: If no simulation has been run. """ + from pyscf import lib + from pyscf.cc.ccsd_rdm import _make_rdm1, _make_rdm2, _gamma1_intermediates, _gamma2_outcore + from pyscf.cc.uccsd_rdm import (_make_rdm1 as _umake_rdm1, _make_rdm2 as _umake_rdm2, + _gamma1_intermediates as _ugamma1_intermediates, _gamma2_outcore as _ugamma2_outcore) # Check if CCSD calculation is performed if self.cc_fragment is None: @@ -104,3 +122,180 @@ def get_rdm(self): two_rdm = np.sum((two_rdm[0], 2*two_rdm[1], two_rdm[2]), axis=0) return one_rdm, two_rdm + + +class CCSDSolverPsi4(ElectronicStructureSolver): + """ Uses the CCSD method to solve the electronic structure problem, + through Psi4. + + Args: + molecule (SecondQuantizedMolecule): The molecule to simulate. + + Attributes: + ccwfn (psi4.core.CCWavefunction): The CCSD wavefunction (float64). + backend (psi4): The psi4 module + molecule (SecondQuantizedMolecule): The molecule with symmetry=False + """ + + def __init__(self, molecule: SecondQuantizedMolecule): + if not is_package_installed("psi4"): + raise ModuleNotFoundError(f"Using {self.__class__.__name__} requires the installation of the Psi4 package") + + import psi4 + self.backend = psi4 + self.backend.core.clean_options() + self.backend.core.clean() + self.backend.core.clean_variables() + self.ccwfn = None + + self.n_frozen_vir = len(molecule.frozen_virtual) if not molecule.uhf else len(molecule.frozen_virtual[0]) + self.n_frozen_occ = len(molecule.frozen_occupied) if not molecule.uhf else len(molecule.frozen_occupied[0]) + if not molecule.uhf: + self.ref = 'rhf' if molecule.spin == 0 else 'rohf' + else: + self.ref = 'uhf' + self.n_frozen_vir_b = len(molecule.frozen_virtual[1]) + self.n_frozen_occ_b = len(molecule.frozen_occupied[1]) + if (self.n_frozen_vir, self.n_frozen_occ) != (self.n_frozen_vir_b, self.n_frozen_occ_b): + raise ValueError(f"Tangelo does not support unequal number of alpha v. beta frozen or virtual orbitals" + f"with a UHF reference in {self.__class__.__name__}") + + # Frozen orbitals must be declared before calling compute_mean_field to be saved in ref_wfn for Psi4 ccsd. + intsolve = IntegralSolverPsi4() + self.backend.set_options({'basis': molecule.basis, 'frozen_docc': [self.n_frozen_occ], 'frozen_uocc': [self.n_frozen_vir], + 'reference': self.ref}) + self.molecule = SecondQuantizedMolecule(xyz=molecule.xyz, q=molecule.q, spin=molecule.spin, + solver=intsolve, + basis=molecule.basis, + ecp=molecule.ecp, + symmetry=False, + uhf=molecule.uhf, + frozen_orbitals=molecule.frozen_orbitals) + self.basis = molecule.basis + + def simulate(self): + """Perform the simulation (energy calculation) for the molecule. + + Returns: + float: Total CCSD energy. + """ + # Copy reference wavefunction and swap orbitals to obtain correct active space if necessary + wfn = self.backend.core.Wavefunction(self.molecule.solver.mol_nosym, self.molecule.solver.wfn.basisset()) + wfn.deep_copy(self.molecule.solver.wfn) + if self.n_frozen_occ or self.n_frozen_vir: + if not self.molecule.uhf: + mo_order = self.molecule.frozen_occupied + self.molecule.active_occupied + self.molecule.active_virtual + self.molecule.frozen_virtual + # Obtain swap operations that will take the unordered list back to ordered with the correct active space in the middle. + swap_ops = Permutation(mo_order).transpositions() + for swap_op in swap_ops: + wfn.Ca().rotate_columns(0, swap_op[0], swap_op[1], np.deg2rad(90)) + + else: + + # Obtain swap operations that will take the unordered list back to ordered with the correct active space in the middle. + mo_order = (self.molecule.frozen_occupied[0] + self.molecule.active_occupied[0] + + self.molecule.active_virtual[0] + self.molecule.frozen_virtual[0]) + swap_ops = Permutation(mo_order).transpositions() + for swap_op in swap_ops: + wfn.Ca().rotate_columns(0, swap_op[0], swap_op[1], np.deg2rad(90)) + + # Repeat for Beta orbitals + mo_order_b = (self.molecule.frozen_occupied[1] + self.molecule.active_occupied[1] + + self.molecule.active_virtual[1] + self.molecule.frozen_virtual[1]) + swap_ops = Permutation(mo_order_b).transpositions() + for swap_op in swap_ops: + wfn.Cb().rotate_columns(0, swap_op[0], swap_op[1], np.deg2rad(90)) + + self.backend.set_options({'basis': self.basis, 'frozen_docc': [self.n_frozen_occ], 'frozen_uocc': [self.n_frozen_vir], + 'qc_module': 'ccenergy', 'reference': self.ref}) + energy, self.ccwfn = self.backend.energy('ccsd', molecule=self.molecule.solver.mol, + basis=self.basis, return_wfn=True, ref_wfn=wfn) + return energy + + def get_rdm(self): + """Compute the Full CI 1- and 2-particle reduced density matrices. + + Returning RDMS from a CCSD calculation in Psi4 is not implemented at this time. + + It may be possible to obtain the one-rdm by running a psi4 CCSD gradient calculation + (https://forum.psicode.org/t/saving-ccsd-density-for-read-in/2416/2) + Another option to obtain the one-rdm is to use pycc (https://github.com/CrawfordGroup/pycc) + + Raises: + NotImplementedError: Returning RDMs from Psi4 in Tangelo is not supported at this time. + """ + raise NotImplementedError(f"{self.__class__.__name__} does not currently support returning RDMs") + + +ccsd_solver_dict = {'pyscf': CCSDSolverPySCF, 'psi4': CCSDSolverPsi4} + + +def get_ccsd_solver(molecule: SecondQuantizedMolecule, solver: Union[None, str, Type[ElectronicStructureSolver]] = default_ccsd_solver, **solver_kwargs): + """Return requested target CCSDSolverName object. + + Args: + molecule (SecondQuantizedMolecule) : Molecule + solver (string or Type[ElectronicStructureSolver] or None): Supported string identifiers can be found in + ccsd_solver_dict (from tangelo.algorithms.classical.ccsd_solver). Can also provide a user-defined backend (child to ElectronicStructureSolver class) + solver_kwargs: Other arguments that could be passed to a target. Examples are solver type (e.g. lambdacc, fnocc), Convergence options etc. + """ + + if solver is None: + if isinstance(molecule.solver, IntegralSolverPySCF): + solver = CCSDSolverPySCF + elif isinstance(molecule.solver, IntegralSolverPsi4): + solver = CCSDSolverPsi4 + elif default_ccsd_solver is not None: + solver = default_ccsd_solver + else: + raise ModuleNotFoundError(f"One of the backends for {list(ccsd_solver_dict.keys())} needs to be installed to use a CCSDSolver" + "without providing a user-defined implementation.") + + # If target is a string use target_dict to return built-in backend + elif isinstance(solver, str): + try: + solver = ccsd_solver_dict[solver.lower()] + except KeyError: + raise ValueError(f"Error: backend {solver} not supported. Available built-in options: {list(ccsd_solver_dict.keys())}") + elif not issubclass(solver, ElectronicStructureSolver): + raise TypeError(f"Target must be a str or a subclass of ElectronicStructureSolver but received class {type(solver).__name__}") + + return solver(molecule, **solver_kwargs) + + +class CCSDSolver(ElectronicStructureSolver): + """Uses the Full CI method to solve the electronic structure problem. + + Args: + molecule (SecondQuantizedMolecule) : Molecule + solver (string or Type[ElectronicStructureSolver] or None): Supported string identifiers can be found in + available_ccsd_solvers (from tangelo.algorithms.classical.ccsd_solver). Can also provide a user-defined CCSD implementation + (child to ElectronicStructureSolver class) + solver_kwargs: Other arguments that could be passed to a target. Examples are solver type (e.g. lambdacc, fnocc), Convergence options etc. + + Attributes: + solver (Type[ElectronicStructureSolver]): The backend specific CCSD solver + """ + + def __init__(self, molecule: SecondQuantizedMolecule, solver: Union[None, str, Type[ElectronicStructureSolver]] = default_ccsd_solver, **solver_kwargs): + self.solver = get_ccsd_solver(molecule, solver, **solver_kwargs) + + def simulate(self): + """Perform the simulation (energy calculation) for the molecule. + + Returns: + float: Total CCSD energy. + """ + return self.solver.simulate() + + def get_rdm(self): + """Compute the Full CI 1- and 2-particle reduced density matrices. + + Returns: + numpy.array: One-particle RDM. + numpy.array: Two-particle RDM. + + Raises: + RuntimeError: If method "simulate" hasn't been run. + """ + return self.solver.get_rdm() diff --git a/tangelo/algorithms/classical/fci_solver.py b/tangelo/algorithms/classical/fci_solver.py index df10a8d7d..34ce88f34 100644 --- a/tangelo/algorithms/classical/fci_solver.py +++ b/tangelo/algorithms/classical/fci_solver.py @@ -15,13 +15,26 @@ """Define electronic structure solver employing the full configuration interaction (CI) method. """ +from typing import Union, Type +import warnings -from pyscf import ao2mo, fci, mcscf +import numpy as np +from sympy.combinatorics.permutations import Permutation +from tangelo.toolboxes.molecular_computation.molecule import SecondQuantizedMolecule +from tangelo.toolboxes.molecular_computation import IntegralSolverPsi4, IntegralSolverPySCF from tangelo.algorithms.electronic_structure_solver import ElectronicStructureSolver +from tangelo.helpers.utils import installed_chem_backends, is_package_installed +if 'pyscf' in installed_chem_backends: + default_fci_solver = 'pyscf' +elif 'psi4' in installed_chem_backends: + default_fci_solver = 'psi4' +else: + default_fci_solver = None -class FCISolver(ElectronicStructureSolver): + +class FCISolverPySCF(ElectronicStructureSolver): """ Uses the Full CI method to solve the electronic structure problem, through pyscf. @@ -37,10 +50,15 @@ class FCISolver(ElectronicStructureSolver): """ def __init__(self, molecule): + if not is_package_installed("pyscf"): + raise ModuleNotFoundError(f"Using {self.__class__.__name__} requires the installation of the pyscf package") if molecule.uhf: raise NotImplementedError(f"SecondQuantizedMolecule that use UHF are not currently supported in {self.__class__.__name__}. Use CCSDSolver") + from pyscf import ao2mo, fci, mcscf + self.ao2mo = ao2mo + self.ci = None self.norb = molecule.n_active_mos self.nelec = molecule.n_active_electrons @@ -64,7 +82,7 @@ def __init__(self, molecule): else: self.cas = False if self.spin == 0: - self.cisolver = fci.direct_spin0.FCI(molecule.to_pyscf(molecule.basis)) + self.cisolver = fci.direct_spin0.FCI(molecule.mean_field.mol) else: self.cisolver = fci.direct_spin1.FCI() @@ -89,9 +107,9 @@ def simulate(self): twoint = self.mean_field._eri - eri = ao2mo.restore(8, twoint, self.norb) - eri = ao2mo.incore.full(eri, self.mean_field.mo_coeff) - eri = ao2mo.restore(1, eri, self.norb) + eri = self.ao2mo.restore(8, twoint, self.norb) + eri = self.ao2mo.incore.full(eri, self.mean_field.mo_coeff) + eri = self.ao2mo.restore(1, eri, self.norb) ecore = self.mean_field.energy_nuc() @@ -127,3 +145,169 @@ def get_rdm(self): one_rdm, two_rdm = self.cisolver.make_rdm12(self.ci, self.norb, (self.n_alpha, self.n_beta)) return one_rdm, two_rdm + + +class FCISolverPsi4(ElectronicStructureSolver): + """ Uses the Full CI method to solve the electronic structure problem, + through Psi4. + + Args: + molecule (SecondQuantizedMolecule): The molecule to simulate. + + Attributes: + ciwfn (psi4.core.CIWavefunction): The CI wavefunction (float64). + backend (psi4): The psi4 module + molecule (SecondQuantizedMolecule): The molecule with symmetry=False + """ + + def __init__(self, molecule: SecondQuantizedMolecule): + if not is_package_installed("psi4"): + raise ModuleNotFoundError(f"Using {self.__class__.__name__} requires the installation of the Psi4 package") + + if molecule.uhf: + raise NotImplementedError(f"SecondQuantizedMolecule that use UHF are not currently supported in {self.__class__.__name__}. Use CCSDSolver") + + import psi4 + self.backend = psi4 + self.backend.core.clean_options() + self.backend.core.clean() + self.backend.core.clean_variables() + self.ciwfn = None + self.degenerate_mo_energies = False + if isinstance(molecule.solver, IntegralSolverPsi4) and not molecule.symmetry: + self.molecule = molecule + else: + self.degenerate_mo_energies = np.any(np.isclose(molecule.mo_energies[1:], molecule.mo_energies[:-1])) + self.molecule = SecondQuantizedMolecule(xyz=molecule.xyz, q=molecule.q, spin=molecule.spin, + solver=IntegralSolverPsi4(), + basis=molecule.basis, + ecp=molecule.ecp, + symmetry=False, + uhf=molecule.uhf, + frozen_orbitals=molecule.frozen_orbitals) + self.basis = molecule.basis + + def simulate(self): + """Perform the simulation (energy calculation) for the molecule. + + Returns: + float: Total FCI energy. + """ + n_frozen_vir = len(self.molecule.frozen_virtual) + n_frozen_occ = len(self.molecule.frozen_occupied) + ref = 'rhf' if self.molecule.spin == 0 else 'rohf' + self.backend.set_options({'basis': self.basis, 'mcscf_maxiter': 300, 'mcscf_diis_start': 20, + 'opdm': True, 'tpdm': True, 'frozen_docc': [n_frozen_occ], 'frozen_uocc': [n_frozen_vir], + 'qc_module': 'detci', 'fci': True, 'reference': ref}) + + # Copy reference wavefunction and swap orbitals to obtain correct active space if necessary + wfn = self.backend.core.Wavefunction(self.molecule.solver.mol_nosym, self.molecule.solver.wfn.basisset()) + wfn.deep_copy(self.molecule.solver.wfn) + if n_frozen_occ or n_frozen_vir: + mo_order = self.molecule.frozen_occupied + self.molecule.active_occupied + self.molecule.active_virtual + self.molecule.frozen_virtual + # Obtain swap operations that will take the unordered list back to ordered with the correct active space in the middle. + swap_ops = Permutation(mo_order).transpositions() + for swap_op in swap_ops: + wfn.Ca().rotate_columns(0, swap_op[0], swap_op[1], np.deg2rad(90)) + + energy, self.ciwfn = self.backend.energy('fci', molecule=self.molecule.solver.mol, + basis=self.basis, return_wfn=True, + ref_wfn=wfn) + return energy + + def get_rdm(self): + """Compute the Full CI 1- and 2-particle reduced density matrices. + + Returns: + numpy.array: One-particle RDM. + numpy.array: Two-particle RDM. + + Raises: + RuntimeError: If method "simulate" hasn't been run. + """ + + # Check if Full CI has been performed + if self.ciwfn is None: + raise RuntimeError("FCISolver: Cannot retrieve RDM. Please run the 'simulate' method first") + if self.degenerate_mo_energies: + warnings.warn("Degenerate orbitals are present in the molecule. The fci calculation is performed " + "without symmetry so the rdms may not correspond to the integrals in molecule. A c1 " + "version of the molecule with the correct integrals is present as FCISolverPsi4.molecule.") + one_rdm = np.asarray(self.ciwfn.get_opdm(0, 0, "SUM", False)) + two_rdm = np.asarray(self.ciwfn.get_tpdm("SUM", True)) + + return one_rdm, two_rdm + + +available_fci_solvers = {'pyscf': FCISolverPySCF, 'psi4': FCISolverPsi4} + + +def get_fci_solver(molecule: SecondQuantizedMolecule, solver: Union[None, str, Type[ElectronicStructureSolver]] = default_fci_solver, **solver_kwargs): + """Return requested target FCISolverName object. + + Args: + molecule (SecondQuantizedMolecule) : Molecule + solver (string or Type[ElectronicStructureSolver] or None): Supported string identifiers can be found in + available_fci_solvers (from tangelo.algorithms.classical.fci_solver). Can also provide a user-defined FCI implementation + (child to ElectronicStructureSolver class) + solver_kwargs: Other arguments that could be passed to a target. Examples are solver type (e.g. mcscf, fci), Convergence options etc. + """ + + if solver is None: + if isinstance(molecule.solver, IntegralSolverPySCF): + solver = FCISolverPySCF + elif isinstance(molecule.solver, IntegralSolverPsi4): + solver = FCISolverPsi4 + elif default_fci_solver is not None: + solver = default_fci_solver + else: + raise ModuleNotFoundError(f"One of the backends for {list(available_fci_solvers.keys())} needs to be installed to use FCISolver" + "without providing a user-defined implementation.") + + # If target is a string use target_dict to return built-in backend + elif isinstance(solver, str): + try: + solver = available_fci_solvers[solver.lower()] + except KeyError: + raise ValueError(f"Error: backend {solver} not supported. Available built-in options: {list(available_fci_solvers.keys())}") + elif not issubclass(solver, ElectronicStructureSolver): + raise TypeError(f"Target must be a str or a subclass of ElectronicStructureSolver but received class {type(solver).__name__}") + + return solver(molecule, **solver_kwargs) + + +class FCISolver(ElectronicStructureSolver): + """Uses the Full CI method to solve the electronic structure problem. + + Args: + molecule (SecondQuantizedMolecule) : Molecule + solver (string or Type[ElectronicStructureSolver] or None): Supported string identifiers can be found in + available_fci_solvers (from tangelo.algorithms.classical.fci_solver). Can also provide a user-defined FCI implementation + (child to ElectronicStructureSolver class) + solver_kwargs: Other arguments that could be passed to a target. Examples are solver type (e.g. mcscf, fci), Convergence options etc. + + Attributes: + solver (Type[ElectronicStructureSolver]): The solver that is used for obtaining the FCI solution. + """ + def __init__(self, molecule: SecondQuantizedMolecule, solver: Union[None, str, Type[ElectronicStructureSolver]] = default_fci_solver, **solver_kwargs): + self.solver = get_fci_solver(molecule, solver, **solver_kwargs) + + def simulate(self): + """Perform the simulation (energy calculation) for the molecule. + + Returns: + float: Total FCI energy. + """ + return self.solver.simulate() + + def get_rdm(self): + """Compute the Full CI 1- and 2-particle reduced density matrices. + + Returns: + numpy.array: One-particle RDM. + numpy.array: Two-particle RDM. + + Raises: + RuntimeError: If method "simulate" hasn't been run. + """ + return self.solver.get_rdm() diff --git a/tangelo/algorithms/classical/mp2_solver.py b/tangelo/algorithms/classical/mp2_solver.py new file mode 100644 index 000000000..e4f1c02d2 --- /dev/null +++ b/tangelo/algorithms/classical/mp2_solver.py @@ -0,0 +1,336 @@ +# Copyright 2023 Good Chemistry Company. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""Define electronic structure solver employing the Moller-Plesset perturbation theory +to second order (MP2) method. +""" + +from typing import Union, Type +from itertools import combinations, product +from math import ceil + +import numpy as np + +from tangelo.algorithms.electronic_structure_solver import ElectronicStructureSolver +from tangelo.helpers.utils import installed_chem_backends, is_package_installed +from tangelo.toolboxes.molecular_computation.molecule import SecondQuantizedMolecule +from tangelo.toolboxes.molecular_computation import IntegralSolverPsi4, IntegralSolverPySCF +from tangelo.toolboxes.ansatz_generator._unitary_cc_openshell import uccsd_openshell_get_packed_amplitudes + +if 'pyscf' in installed_chem_backends: + default_mp2_solver = 'pyscf' +elif 'psi4' in installed_chem_backends: + default_mp2_solver = 'psi4' +else: + default_mp2_solver = None + + +class MP2SolverPySCF(ElectronicStructureSolver): + """Uses the Second-order Moller-Plesset perturbation theory (MP2) method to solve the electronic structure problem, + through pyscf. + + Args: + molecule (SecondQuantizedMolecule): The molecule to simulate. + + Attributes: + mp2_fragment (pyscf.mp.MP2): The coupled-cluster object. + mean_field (pyscf.scf.RHF): The mean field of the molecule. + frozen (list or int): Frozen molecular orbitals. + """ + + def __init__(self, molecule): + if not is_package_installed("pyscf"): + raise ModuleNotFoundError(f"Using {self.__class__.__name__} requires the installation of the pyscf package") + from pyscf import mp + + self.mp = mp + self.mp2_fragment = None + + self.spin = molecule.spin + + self.mean_field = molecule.mean_field + self.frozen = molecule.frozen_mos + self.uhf = molecule.uhf + + # Define variables used to transform the MP2 parameters into an ordered + # list of parameters with single and double excitations. + if self.spin != 0 or self.uhf: + self.n_alpha, self.n_beta = molecule.n_active_ab_electrons + self.n_active_moa, self.n_active_mob = molecule.n_active_mos if self.uhf else (molecule.n_active_mos,)*2 + else: + self.n_occupied = ceil(molecule.n_active_electrons / 2) + self.n_virtual = molecule.n_active_mos - self.n_occupied + + def simulate(self): + """Perform the simulation (energy calculation) for the molecule. + + Returns: + float: MP2 energy. + """ + + # Execute MP2 calculation + if self.uhf: + self.mp2_fragment = self.mp.UMP2(self.mean_field, frozen=self.frozen) + else: + self.mp2_fragment = self.mp.RMP2(self.mean_field, frozen=self.frozen) + + self.mp2_fragment.verbose = 0 + _, self.mp2_t2 = self.mp2_fragment.kernel() + + total_energy = self.mp2_fragment.e_tot + + return total_energy + + def get_rdm(self): + """Calculate the 1- and 2-particle reduced density matrices. + + Returns: + numpy.array: One-particle RDM. + numpy.array: Two-particle RDM. + + Raises: + RuntimeError: If no simulation has been run. + """ + + # Check if MP2 has been performed + if self.mp2_fragment is None: + raise RuntimeError(f"{self.__class__.__name__}: Cannot retrieve RDM. Please run the 'simulate' method first") + if self.frozen is not None: + raise RuntimeError(f"{self.__class__.__name__}: RDM calculation is not implemented with frozen orbitals.") + + one_rdm = self.mp2_fragment.make_rdm1() + two_rdm = self.mp2_fragment.make_rdm2() + + return one_rdm, two_rdm + + def get_mp2_amplitudes(self): + """Compute the double amplitudes from the MP2 perturbative method, and + then reorder the elements into a dense list. The single (T1) amplitudes + are set to a small non-zero value. The ordering is single, double + (diagonal), double (non-diagonal). + + Returns: + list of float: The electronic excitation amplitudes. + """ + + # Check if MP2 has been performed. + if self.mp2_fragment is None: + raise RuntimeError(f"{self.__class__.__name__}: Cannot retrieve MP2 parameters. Please run the 'simulate' method first") + + if self.spin != 0 or self.uhf: + # Reorder the T2 amplitudes in a dense list. + mp2_params = uccsd_openshell_get_packed_amplitudes( + self.mp2_t2[0], # aa + self.mp2_t2[2], # bb + self.mp2_t2[1], # ab + self.n_alpha, + self.n_beta, + self.n_active_moa, + self.n_active_mob + ) + else: + # Get singles amplitude. Just get "up" amplitude, since "down" should be the same + singles = [2.e-5] * (self.n_virtual * self.n_occupied) + + # Get singles and doubles amplitudes associated with one spatial occupied-virtual pair + doubles_1 = [-self.mp2_t2[q, q, p, p]/2. if (abs(-self.mp2_t2[q, q, p, p]/2.) > 1e-15) else 0. + for p, q in product(range(self.n_virtual), range(self.n_occupied))] + + # Get doubles amplitudes associated with two spatial occupied-virtual pairs + doubles_2 = [-self.mp2_t2[q, s, p, r] for (p, q), (r, s) + in combinations(product(range(self.n_virtual), range(self.n_occupied)), 2)] + + mp2_params = singles + doubles_1 + doubles_2 + + return mp2_params + + +class MP2SolverPsi4(ElectronicStructureSolver): + """ Uses the MP2 method to solve the electronic structure problem, through Psi4. + + Only supports frozen core (active) orbitals sequentially from bottom (top) of energy ordering. + + Args: + molecule (SecondQuantizedMolecule): The molecule to simulate. + + Attributes: + mp2wfn (psi4.core.Wavefunction): The Psi4 Wavefunction returned from an mp2 calculation. + backend (psi4): The psi4 module + molecule (SecondQuantizedMolecule): The molecule with symmetry=False + """ + + def __init__(self, molecule: SecondQuantizedMolecule): + if not is_package_installed("psi4"): + raise ModuleNotFoundError(f"Using {self.__class__.__name__} requires the installation of the Psi4 package") + + import psi4 + self.backend = psi4 + self.backend.core.clean_options() + self.backend.core.clean() + self.backend.core.clean_variables() + self.mp2wfn = None + + self.molecule = SecondQuantizedMolecule(xyz=molecule.xyz, q=molecule.q, spin=molecule.spin, + solver=IntegralSolverPsi4(), + basis=molecule.basis, + ecp=molecule.ecp, + symmetry=False, + uhf=molecule.uhf, + frozen_orbitals=molecule.frozen_orbitals) + self.basis = molecule.basis + + def simulate(self): + """Perform the simulation (energy calculation) for the molecule. + + Returns: + float: Total MP2 energy. + """ + n_frozen_vir = len(self.molecule.frozen_virtual) + n_frozen_occ = len(self.molecule.frozen_occupied) + + if n_frozen_occ or n_frozen_vir: + if self.molecule.uhf: + if (set(self.molecule.frozen_occupied[0]) != set(self.molecule.frozen_occupied[1]) or + set(self.molecule.frozen_virtual[0]) != set(self.molecule.frozen_virtual)): + raise ValueError(f"Only identical frozen orbitals for alpha and beta are supported in {self.__class__.__name__}") + focc = np.array(self.molecule.frozen_occupied) + fvir = np.array(self.molecule.frozen_virtual) + if np.any(focc > n_frozen_occ-1) or np.any(fvir < self.molecule.n_mos-n_frozen_vir): + raise ValueError(f"{self.__class__.__name__} does not support freezing interior orbitals") + + if not self.molecule.uhf: + ref = 'rhf' if self.molecule.spin == 0 else 'rohf' + else: + ref = 'uhf' + self.backend.set_options({'basis': self.basis, 'mcscf_maxiter': 300, 'mcscf_diis_start': 20, + 'opdm': True, 'tpdm': True, 'frozen_docc': [n_frozen_occ], 'frozen_uocc': [n_frozen_vir], + 'reference': ref}) + + energy, self.mp2wfn = self.backend.energy('mp2', molecule=self.molecule.solver.mol, + basis=self.basis, return_wfn=True) + return energy + + def get_rdm(self): + """Calculate the 1- and 2-particle reduced density matrices. + + Obtaining MP2 rdms from Psi4 is not currently supported in Tangelo. + + Using https://github.com/psi4/psi4numpy/blob/master/Tutorials/10_Orbital_Optimized_Methods/10a_orbital-optimized-mp2.ipynb + should return appropriate RDMs for a closed shell RHF reference. + + Raises: + NotImplementedError: Not implemented at this time""" + raise NotImplementedError("Returning MP2 rdms from Psi4 is not currently supported in Tangelo") + + def get_mp2_amplitudes(self): + """Compute the double amplitudes from the MP2 perturbative method, and + then reorder the elements into a dense list. The single (T1) amplitudes + are set to a small non-zero value. The ordering is single, double + (diagonal), double (non-diagonal). + + Returning MP2 amplitudes from Psi4 is not currently supported in Tangelo + + Using https://github.com/psi4/psi4numpy/blob/master/Tutorials/10_Orbital_Optimized_Methods/10a_orbital-optimized-mp2.ipynb + should return appropriate amplitudes for a closed shell RHF reference. + + Raises: + NotImplementedError: Not implemented at this time""" + raise NotImplementedError("Returning MP2 amplitudes from Psi4 is not currently supported in Tangelo") + + +available_mp2_solvers = {'pyscf': MP2SolverPySCF, 'psi4': MP2SolverPsi4} + + +def get_mp2_solver(molecule: SecondQuantizedMolecule, solver: Union[None, str, Type[ElectronicStructureSolver]] = default_mp2_solver, **solver_kwargs): + """Return requested target MP2SolverName object. + + Args: + molecule (SecondQuantizedMolecule) : Molecule + solver (string or Type[ElectronicStructureSolver] or None): Supported string identifiers can be found in + available_mp2_solvers (see mp2_solver.py). Can also provide a user-defined MP2 implementation + (child to ElectronicStructureSolver class) + solver_kwargs: Other arguments that could be passed to a target. Examples are solver type (e.g. mcscf, mp2), Convergence options etc. + + Raises: + ModuleNoyFoundError: No solver is specified and a user defined IntegralSolver was used in molecule. + ValueError: The specified solver str is not one of the available_mp2_solvers (see mp2_solver.py) + TypeError: The specified solver was not a string or sub class of ElectronicStructureSolver. + """ + + if solver is None: + if isinstance(molecule.solver, IntegralSolverPySCF): + solver = MP2SolverPySCF + elif isinstance(molecule.solver, IntegralSolverPsi4): + solver = MP2SolverPsi4 + elif default_mp2_solver is not None: + solver = default_mp2_solver + else: + raise ModuleNotFoundError(f"One of the backends for {list(available_mp2_solvers.keys())} needs to be installed to use MP2Solver" + "without providing a user-defined implementation.") + + # If target is a string use target_dict to return built-in backend + elif isinstance(solver, str): + try: + solver = available_mp2_solvers[solver.lower()] + except KeyError: + raise ValueError(f"Error: backend {solver} not supported. Available built-in options: {list(available_mp2_solvers.keys())}") + elif not issubclass(solver, ElectronicStructureSolver): + raise TypeError(f"Target must be a str or a subclass of ElectronicStructureSolver but received class {type(solver).__name__}") + + return solver(molecule, **solver_kwargs) + + +class MP2Solver(ElectronicStructureSolver): + """Uses the MP2 method to solve the electronic structure problem. + + Args: + molecule (SecondQuantizedMolecule) : Molecule + solver (string or Type[ElectronicStructureSolver] or None): Supported string identifiers can be found in + available_mp2_solvers (see mp2_solver.py). Can also provide a user-defined MP2 implementation + (child to ElectronicStructureSolver class) + solver_kwargs: Other arguments that could be passed to a target. Examples are solver type (e.g. dfmp2, mp2), Convergence options etc. + + Attributes: + solver (Type[ElectronicStructureSolver]): The solver that is used for obtaining the MP2 solution. + """ + def __init__(self, molecule: SecondQuantizedMolecule, solver: Union[None, str, Type[ElectronicStructureSolver]] = default_mp2_solver, **solver_kwargs): + self.solver = get_mp2_solver(molecule, solver, **solver_kwargs) + + def simulate(self): + """Perform the simulation (energy calculation) for the molecule. + + Returns: + float: Total MP2 energy. + """ + return self.solver.simulate() + + def get_rdm(self): + """Compute the Full CI 1- and 2-particle reduced density matrices. + + Returns: + numpy.array: One-particle RDM. + numpy.array: Two-particle RDM. + """ + return self.solver.get_rdm() + + def get_mp2_amplitudes(self): + """Compute the double amplitudes from the MP2 perturbative method, and + then reorder the elements into a dense list. The single (T1) amplitudes + are set to a small non-zero value. The ordering is single, double + (diagonal), double (non-diagonal). + + Returns: + list of float: The electronic excitation amplitudes. + """ + return self.solver.get_mp2_amplitudes() diff --git a/tangelo/algorithms/classical/semi_empirical_solver.py b/tangelo/algorithms/classical/semi_empirical_solver.py index 5004a59a9..8f41cf530 100644 --- a/tangelo/algorithms/classical/semi_empirical_solver.py +++ b/tangelo/algorithms/classical/semi_empirical_solver.py @@ -32,6 +32,7 @@ from tangelo.helpers.utils import is_package_installed from tangelo.algorithms.electronic_structure_solver import ElectronicStructureSolver +from tangelo.toolboxes.molecular_computation.integral_solver_pyscf import mol_to_pyscf class MINDO3Solver(ElectronicStructureSolver): @@ -62,7 +63,7 @@ def simulate(self): """ from pyscf.semiempirical import mindo3 - solver = mindo3.RMINDO3(self.molecule.to_pyscf()).run(verbose=0) + solver = mindo3.RMINDO3(mol_to_pyscf(self.molecule)).run(verbose=0) total_energy = solver.e_tot return total_energy diff --git a/tangelo/algorithms/classical/tests/test_ccsd_solver.py b/tangelo/algorithms/classical/tests/test_ccsd_solver.py index 61b8b76a8..5af14d222 100644 --- a/tangelo/algorithms/classical/tests/test_ccsd_solver.py +++ b/tangelo/algorithms/classical/tests/test_ccsd_solver.py @@ -14,11 +14,11 @@ import unittest -from tangelo.algorithms.classical.ccsd_solver import CCSDSolver -from tangelo.molecule_library import mol_H2_321g, mol_Be_321g, mol_H4_sto3g_uhf_a1_frozen +from tangelo import SecondQuantizedMolecule +from tangelo.algorithms.classical.ccsd_solver import CCSDSolver, default_ccsd_solver +from tangelo.molecule_library import mol_H2_321g, mol_Be_321g, mol_H4_sto3g_uhf_a1_frozen, xyz_H4 -# TODO: Can we test the get_rdm method on H2 ? How do we get our reference? Whole matrix or its properties? class CCSDSolverTest(unittest.TestCase): def test_ccsd_h2(self): @@ -27,10 +27,11 @@ def test_ccsd_h2(self): solver = CCSDSolver(mol_H2_321g) energy = solver.simulate() - self.assertAlmostEqual(energy, -1.1478300596229851, places=6) + self.assertAlmostEqual(energy, -1.1478300, places=5) + @unittest.skipIf("pyscf" != default_ccsd_solver, "Test Skipped: Only functions for pyscf \n") def test_ccsd_h4_uhf_a1_frozen(self): - """Test CCSDSolver against result from reference implementation.""" + """Test CCSDSolver against result from reference implementation for single alpha frozen orbital and rdms returned.""" solver = CCSDSolver(mol_H4_sto3g_uhf_a1_frozen) energy = solver.simulate() @@ -41,13 +42,22 @@ def test_ccsd_h4_uhf_a1_frozen(self): self.assertAlmostEqual(mol_H4_sto3g_uhf_a1_frozen.energy_from_rdms(one_rdms, two_rdms), -1.95831052, places=6) + def test_ccsd_h4_uhf_different_alpha_beta_frozen(self): + """Test energy for case when different but equal number of alpha/beta orbitals are frozen.""" + + mol = SecondQuantizedMolecule(xyz_H4, q=0, spin=0, basis="3-21g", frozen_orbitals=[[2, 3, 4, 5], [2, 3, 6, 5]], symmetry=False, uhf=True) + solver = CCSDSolver(mol) + energy = solver.simulate() + + self.assertAlmostEqual(energy, -2.0409800, places=5) + def test_ccsd_be(self): """Test CCSDSolver against result from reference implementation.""" solver = CCSDSolver(mol_Be_321g) energy = solver.simulate() - self.assertAlmostEqual(energy, -14.531416589890926, places=6) + self.assertAlmostEqual(energy, -14.531416, places=5) def test_ccsd_be_frozen_core(self): """ Test CCSDSolver against result from reference implementation, with @@ -59,7 +69,7 @@ def test_ccsd_be_frozen_core(self): solver = CCSDSolver(mol_Be_321g_freeze1) energy = solver.simulate() - self.assertAlmostEqual(energy, -14.530687987160581, places=6) + self.assertAlmostEqual(energy, -14.5306879, places=5) def test_ccsd_be_as_two_levels(self): """ Test CCSDSolver against result from reference implementation, with @@ -72,7 +82,7 @@ def test_ccsd_be_as_two_levels(self): solver = CCSDSolver(mol_Be_321g_freeze_list) energy = solver.simulate() - self.assertAlmostEqual(energy, -14.498104489160106, places=6) + self.assertAlmostEqual(energy, -14.498104, places=5) def test_ccsd_get_rdm_without_simulate(self): """Test that the runtime error is raised when user calls get RDM without diff --git a/tangelo/algorithms/classical/tests/test_fci_solver.py b/tangelo/algorithms/classical/tests/test_fci_solver.py index 5af0306d6..56b9618b8 100644 --- a/tangelo/algorithms/classical/tests/test_fci_solver.py +++ b/tangelo/algorithms/classical/tests/test_fci_solver.py @@ -15,10 +15,9 @@ import unittest from tangelo.algorithms import FCISolver -from tangelo.molecule_library import mol_H2_321g, mol_Be_321g, mol_H4_cation_sto3g +from tangelo.molecule_library import mol_H2_321g, mol_Be_321g, mol_H4_cation_sto3g, mol_H4_sto3g -# TODO: Can we test the get_rdm method on H2 ? How do we get our reference? Whole matrix or its properties? class FCISolverTest(unittest.TestCase): def test_fci_h2(self): @@ -29,6 +28,9 @@ def test_fci_h2(self): self.assertAlmostEqual(energy, -1.1478300596229851, places=6) + one_rdm, two_rdm = solver.get_rdm() + self.assertAlmostEqual(energy, mol_H2_321g.energy_from_rdms(one_rdm, two_rdm), places=6) + def test_fci_be(self): """Test FCISolver against result from reference implementation (Be).""" @@ -65,6 +67,20 @@ def test_fci_be_frozen_core(self): self.assertAlmostEqual(energy, -14.530687987160581, places=6) + def test_fci_H4_interior_frozen_orbitals(self): + """ Test FCISolver against result from reference implementation with interior frozen orbitals + """ + + mol_H4_sto3g_freeze3 = mol_H4_sto3g.freeze_mos([1, 3, 4], inplace=False) + + solver = FCISolver(mol_H4_sto3g_freeze3) + energy = solver.simulate() + + self.assertAlmostEqual(energy, -1.803792808, places=5) + + one_rdm, two_rdm = solver.get_rdm() + self.assertAlmostEqual(energy, mol_H4_sto3g_freeze3.energy_from_rdms(one_rdm, two_rdm), places=5) + if __name__ == "__main__": unittest.main() diff --git a/tangelo/algorithms/classical/tests/test_mp2_solver.py b/tangelo/algorithms/classical/tests/test_mp2_solver.py new file mode 100644 index 000000000..53c7c4e0c --- /dev/null +++ b/tangelo/algorithms/classical/tests/test_mp2_solver.py @@ -0,0 +1,117 @@ +# Copyright 2023 Good Chemistry Company. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import unittest + +import numpy as np + +from tangelo.algorithms.classical.mp2_solver import MP2Solver, default_mp2_solver +from tangelo.molecule_library import mol_H2_321g, mol_Be_321g, mol_H2_sto3g, mol_H2_sto3g_uhf + + +class MP2SolverTest(unittest.TestCase): + + def test_h2(self): + """Test MP2Solver against result from reference implementation (H2).""" + + solver = MP2Solver(mol_H2_321g) + energy = solver.simulate() + + self.assertAlmostEqual(energy, -1.14025452, places=3) + + @unittest.skipIf("pyscf" != default_mp2_solver, "Test Skipped: Only functions for pyscf \n") + def test_be(self): + """Test MP2Solver against result from reference implementation (Be).""" + + solver = MP2Solver(mol_Be_321g) + energy = solver.simulate() + self.assertAlmostEqual(energy, -14.51026131, places=3) + + # Assert energy calculated from RDMs and MP2 calculation are the same. + one_rdm, two_rdm = solver.get_rdm() + self.assertAlmostEqual(mol_Be_321g.energy_from_rdms(one_rdm, two_rdm), energy) + + def test_get_rdm_without_simulate(self): + """Test that the runtime error is raised when user calls get RDM without + first running a simulation. + """ + + solver = MP2Solver(mol_H2_321g) + self.assertRaises(RuntimeError, solver.get_rdm) + + def test_be_frozen_core(self): + """ Test MP2Solver against result from reference implementation, with no mean-field provided as input. + Frozen core is considered. + """ + + mol_Be_321g_freeze1 = mol_Be_321g.freeze_mos(1, inplace=False) + + solver = MP2Solver(mol_Be_321g_freeze1) + energy = solver.simulate() + + self.assertAlmostEqual(energy, -14.5092873, places=3) + + @unittest.skipIf("pyscf" != default_mp2_solver, "Test Skipped: Only functions for pyscf \n") + def test_get_mp2_params_restricted(self): + """Test the packing of RMP2 amplitudes as initial parameters for coupled + cluster based methods. + """ + + solver = MP2Solver(mol_H2_sto3g) + solver.simulate() + + ref_params = [2.e-05, 3.632537e-02] + + np.testing.assert_array_almost_equal(ref_params, solver.get_mp2_amplitudes()) + + @unittest.skipIf("pyscf" != default_mp2_solver, "Test Skipped: Only functions for pyscf \n") + def test_get_mp2_params_unrestricted(self): + """Test the packing of UMP2 amplitudes as initial parameters for coupled + cluster based methods. + """ + + solver = MP2Solver(mol_H2_sto3g_uhf) + solver.simulate() + ref_params = [0., 0., 0.030736] + + np.testing.assert_array_almost_equal(ref_params, solver.get_mp2_amplitudes()) + + @unittest.skipIf("pyscf" != default_mp2_solver, "Test Skipped: Only functions for pyscf \n") + def test_get_mp2_params_restricted(self): + """Test the packing of RMP2 amplitudes as initial parameters for coupled + cluster based methods. + """ + + solver = MP2Solver(mol_H2_sto3g) + solver.simulate() + + ref_params = [2.e-05, 3.632537e-02] + + np.testing.assert_array_almost_equal(ref_params, solver.get_mp2_amplitudes()) + + @unittest.skipIf("pyscf" != default_mp2_solver, "Test Skipped: Only functions for pyscf \n") + def test_get_mp2_params_unrestricted(self): + """Test the packing of UMP2 amplitudes as initial parameters for coupled + cluster based methods. + """ + + solver = MP2Solver(mol_H2_sto3g_uhf) + solver.simulate() + ref_params = [0., 0., 0.030736] + + np.testing.assert_array_almost_equal(ref_params, solver.get_mp2_amplitudes()) + + +if __name__ == "__main__": + unittest.main() diff --git a/tangelo/algorithms/classical/tests/test_semi_empirical_solver.py b/tangelo/algorithms/classical/tests/test_semi_empirical_solver.py index ab49a80b7..3a13b8bbe 100644 --- a/tangelo/algorithms/classical/tests/test_semi_empirical_solver.py +++ b/tangelo/algorithms/classical/tests/test_semi_empirical_solver.py @@ -20,7 +20,8 @@ class SemiEmpiricalSolverTest(unittest.TestCase): - @unittest.skipIf(not is_package_installed("pyscf.semiempirical"), "Test Skipped: pyscf.semiempirical module not available \n") + @unittest.skipIf(not is_package_installed("pyscf") or not is_package_installed("pyscf.semiempirical"), + "Test Skipped: pyscf.semiempirical module not available \n") def test_mindo3_energy(self): """Test MINDO3Solver with pyridine. Validated with: - MINDO/3-derived geometries and energies of alkylpyridines and the diff --git a/tangelo/algorithms/projective/quantum_imaginary_time.py b/tangelo/algorithms/projective/quantum_imaginary_time.py index 83cbfec12..482e6a7d4 100644 --- a/tangelo/algorithms/projective/quantum_imaginary_time.py +++ b/tangelo/algorithms/projective/quantum_imaginary_time.py @@ -14,12 +14,14 @@ """Module that defines the Quantum Imaginary Time Algorithm (QITE) """ +from typing import Union, Callable, List from copy import copy import math from openfermion import FermionOperator as ofFermionOperator import numpy as np +from tangelo import SecondQuantizedMolecule from tangelo.toolboxes.ansatz_generator.ansatz_utils import trotterize from tangelo.toolboxes.operators.operators import FermionOperator, QubitOperator from tangelo.toolboxes.qubit_mappings.mapping_transform import fermion_to_qubit_mapping @@ -55,28 +57,25 @@ class QITESolver: def __init__(self, opt_dict): default_backend_options = {"target": None, "n_shots": None, "noise_model": None} - default_options = {"molecule": None, - "dt": 0.5, "max_cycles": 100, - "min_de": 1.e-7, - "pool": uccgsd_pool, - "pool_args": None, - "frozen_orbitals": "frozen_core", - "qubit_mapping": "jw", - "qubit_hamiltonian": None, - "up_then_down": False, - "n_spinorbitals": None, - "n_electrons": None, - "backend_options": default_backend_options, - "verbose": True} - - # Initialize with default values - self.__dict__ = default_options - # Overwrite default values with user-provided ones, if they correspond to a valid keyword - for k, v in opt_dict.items(): - if k in default_options: - setattr(self, k, v) - else: - raise KeyError(f"Keyword :: {k}, not available in {self.__class__.__name__}") + + copt_dict = opt_dict.copy() + self.molecule: SecondQuantizedMolecule = copt_dict.pop("molecule", None) + self.dt: float = copt_dict.pop("dt", 0.5) + self.max_cycles: int = copt_dict.pop("max_cycles", 100) + self.min_de: float = copt_dict.pop("min_de", 1.e-7) + self.pool: Callable[..., Union[List[QubitOperator], List[FermionOperator]]] = copt_dict.pop("pool", uccgsd_pool) + self.pool_args: dict = copt_dict.pop("pool_args", None) + self.frozen_orbitals: Union[str, List] = copt_dict.pop("frozen_orbitals", "frozen_core") + self.qubit_mapping: str = copt_dict.pop("qubit_mapping", "jw") + self.qubit_hamiltonian: QubitOperator = copt_dict.pop("qubit_hamiltonian", None) + self.up_then_down: bool = copt_dict.pop("up_then_down", False) + self.n_spinorbitals: int = copt_dict.pop("n_spinorbitals", None) + self.n_electrons: int = copt_dict.pop("n_electrons", None) + self.backend_options: dict = copt_dict.pop("backend_options", default_backend_options) + self.verbose: bool = copt_dict.pop("verbose", True) + + if len(copt_dict) > 0: + raise KeyError(f"The following keywords are not supported in {self.__class__.__name__}: \n {copt_dict.keys()}") # Raise error/warnings if input is not as expected. Only a single input # must be provided to avoid conflicts. diff --git a/tangelo/algorithms/variational/adapt_vqe_solver.py b/tangelo/algorithms/variational/adapt_vqe_solver.py index 7c304d2e2..82f95c667 100644 --- a/tangelo/algorithms/variational/adapt_vqe_solver.py +++ b/tangelo/algorithms/variational/adapt_vqe_solver.py @@ -28,11 +28,13 @@ import math import warnings +from typing import Optional, Union, List, Callable from scipy.optimize import minimize from openfermion import commutator from openfermion import FermionOperator as ofFermionOperator +from tangelo import SecondQuantizedMolecule from tangelo.toolboxes.operators.operators import FermionOperator, QubitOperator from tangelo.toolboxes.ansatz_generator.adapt_ansatz import ADAPTAnsatz from tangelo.toolboxes.qubit_mappings.mapping_transform import fermion_to_qubit_mapping @@ -51,7 +53,7 @@ class ADAPTSolver: tol (float): Maximum gradient allowed for a particular operator before convergence. max_cycles (int): Maximum number of iterations for ADAPT. - pool (func): Function that returns a list of FermionOperator. Each + pool (func): Function that returns a list of FermionOperator or QubitOperator. Each element represents excitation/operator that has an effect of the total energy. pool_args (dict) : The arguments for the pool function. Will be unpacked in @@ -61,44 +63,47 @@ class ADAPTSolver: up_then_down (bool): Spin orbitals ordering. n_spinorbitals (int): Self-explanatory. n_electrons (int): Self-explanatory. + spin (int): The spin of the system (# alpha - # beta electrons) optimizer (func): Optimization function for VQE minimization. backend_options (dict): Backend options for the underlying VQE object. + simulate_options (dict): Options for fine-control of the simulator backend, including desired measurement results, etc. verbose (bool): Flag for verbosity of VQE. deflation_circuits (list[Circuit]): Deflation circuits to add an orthogonalization penalty with. deflation_coeff (float): The coefficient of the deflation. + projective_circuit (Circuit): A terminal circuit that projects into the correct space, always added to + the end of the ansatz circuit. ref_state (array or Circuit): The reference configuration to use. Replaces HF state """ def __init__(self, opt_dict): default_backend_options = {"target": None, "n_shots": None, "noise_model": None} - default_options = {"molecule": None, - "tol": 1e-3, "max_cycles": 15, - "pool": uccgsd_pool, - "pool_args": None, - "qubit_mapping": "jw", - "qubit_hamiltonian": None, - "up_then_down": False, - "n_spinorbitals": None, - "n_electrons": None, - "spin": None, - "optimizer": self.LBFGSB_optimizer, - "backend_options": default_backend_options, - "verbose": False, - "ref_state": None, - "deflation_circuits": list(), - "deflation_coeff": 1} - - # Initialize with default values - self.__dict__ = default_options - # Overwrite default values with user-provided ones, if they correspond to a valid keyword - for k, v in opt_dict.items(): - if k in default_options: - setattr(self, k, v) - else: - # TODO Raise a warning instead, that variable will not be used unless user made mods to code - raise KeyError(f"Keyword :: {k}, not available in {self.__class__.__name__}") + + copt_dict = opt_dict.copy() + + self.molecule: Optional[SecondQuantizedMolecule] = copt_dict.pop("molecule", None) + self.tol: float = copt_dict.pop("tol", 1e-3) + self.max_cycles: int = copt_dict.pop("max_cycles", 15) + self.pool: Callable[..., Union[List[QubitOperator], List[FermionOperator]]] = copt_dict.pop("pool", uccgsd_pool) + self.pool_args: dict = copt_dict.pop("pool_args", None) + self.qubit_mapping: str = copt_dict.pop("qubit_mapping", "jw") + self.optimizer = copt_dict.pop("optimizer", self.LBFGSB_optimizer) + self.backend_options: dict = copt_dict.pop("backend_options", default_backend_options) + self.simulate_options: dict = copt_dict.pop("simulate_options", dict()) + self.deflation_circuits: Optional[List[Circuit]] = copt_dict.pop("deflation_circuits", list()) + self.deflation_coeff: float = copt_dict.pop("deflation_coeff", 1.) + self.up_then_down: bool = copt_dict.pop("up_then_down", False) + self.spin: int = copt_dict.pop("spin", 0) + self.qubit_hamiltonian: QubitOperator = copt_dict.pop("qubit_hamiltonian", None) + self.verbose: bool = copt_dict.pop("verbose", False) + self.projective_circuit: Circuit = copt_dict.pop("projective_circuit", None) + self.ref_state: Optional[Union[list, Circuit]] = copt_dict.pop("ref_state", None) + self.n_spinorbitals: Union[None, int] = copt_dict.pop("n_spinorbitals", None) + self.n_electrons: Union[None, int] = copt_dict.pop("n_electrons", None) + + if len(copt_dict) > 0: + raise KeyError(f"The following keywords are not supported in {self.__class__.__name__}: \n {copt_dict.keys()}") # Raise error/warnings if input is not as expected. Only a single input # must be provided to avoid conflicts. @@ -160,8 +165,10 @@ def build(self): "ansatz": self.ansatz, "optimizer": self.optimizer, "backend_options": self.backend_options, + "simulate_options": self.simulate_options, "deflation_circuits": self.deflation_circuits, "deflation_coeff": self.deflation_coeff, + "projective_circuit": self.projective_circuit, "ref_state": self.ref_state } @@ -187,10 +194,10 @@ def build(self): pool_list = self.pool(**self.pool_args) # Only a qubit operator is provided with a FermionOperator pool. - if not (self.n_spinorbitals and self.n_electrons and self.spin is not None): + if not (self.n_spinorbitals and self.n_electrons and isinstance(self.spin, int)): raise ValueError("Expecting the number of spin-orbitals (n_spinorbitals), " - "the number of electrons (n_electrons) and the spin (spin) with " - "a qubit_hamiltonian when working with a pool of fermion operators.") + "the number of electrons (n_electrons) and the spin (spin) with " + "a qubit_hamiltonian when working with a pool of fermion operators.") if isinstance(pool_list[0], QubitOperator): self.pool_type = 'qubit' @@ -232,6 +239,8 @@ def simulate(self): full_circuit = (self.vqe_solver.ansatz.circuit if self.ref_state is None else self.vqe_solver.reference_circuit + self.vqe_solver.ansatz.circuit) + if self.projective_circuit: + full_circuit += self.projective_circuit gradients = self.compute_gradients(full_circuit, backend=self.vqe_solver.backend) pool_select = self.choose_operator(gradients, tolerance=self.tol) @@ -280,15 +289,15 @@ def compute_gradients(self, circuit, backend): list of float: Operator gradients. """ - gradient = [abs(backend.get_expectation_value(element, circuit)) for element in self.pool_commutators] + gradient = [abs(backend.get_expectation_value(element, circuit, **self.simulate_options)) for element in self.pool_commutators] for deflate_circuit in self.deflation_circuits: for i, pool_op in enumerate(self.pool_operators): op_circuit = Circuit([Gate(op[1], op[0]) for tuple in pool_op.terms for op in tuple]) pool_over = deflate_circuit.inverse() + op_circuit + circuit - f_dict, _ = backend.simulate(pool_over) + f_dict, _ = backend.simulate(pool_over, **self.simulate_options) grad = f_dict.get("0"*self.vqe_solver.ansatz.circuit.width, 0) pool_over = deflate_circuit.inverse() + circuit - f_dict, _ = backend.simulate(pool_over) + f_dict, _ = backend.simulate(pool_over, **self.simulate_options) gradient[i] += self.deflation_coeff * grad * f_dict.get("0"*self.vqe_solver.ansatz.circuit.width, 0) return gradient diff --git a/tangelo/algorithms/variational/iqcc_ilc_solver.py b/tangelo/algorithms/variational/iqcc_ilc_solver.py index 47d38f696..87c5090f8 100644 --- a/tangelo/algorithms/variational/iqcc_ilc_solver.py +++ b/tangelo/algorithms/variational/iqcc_ilc_solver.py @@ -29,11 +29,14 @@ 2. R. A. Lang, I. G. Ryabinkin, and A. F. Izmaylov. J. Chem. Theory Comput. 2021, 17, 1, 66–78. """ +from typing import List, Union, Optional +from tangelo import SecondQuantizedMolecule +from tangelo.algorithms.variational.vqe_solver import VQESolver from tangelo.linq import get_backend +from tangelo.toolboxes.operators import QubitOperator from tangelo.toolboxes.ansatz_generator.ilc import ILC from tangelo.toolboxes.ansatz_generator.qcc import QCC -from tangelo.algorithms.variational.vqe_solver import VQESolver from tangelo.toolboxes.ansatz_generator._qubit_ilc import ilc_op_dress @@ -83,28 +86,24 @@ class iQCC_ILC_solver: def __init__(self, opt_dict): default_backend_options = {"target": None, "n_shots": None, "noise_model": None} - default_options = {"molecule": None, - "qubit_mapping": "jw", - "up_then_down": False, - "initial_var_params": None, - "backend_options": default_backend_options, - "penalty_terms": None, - "ilc_ansatz_options": dict(), - "qcc_ansatz_options": dict(), - "qubit_hamiltonian": None, - "max_ilc_iter": 3, - "compress_qubit_ham": False, - "compress_eps": 1.59e-3, - "verbose": False} - - # Initialize with default values - self.__dict__ = default_options - # Overwrite default values with user-provided ones, if they correspond to a valid keyword - for param, val in opt_dict.items(): - if param in default_options: - setattr(self, param, val) - else: - raise KeyError(f"The keyword {param} is not available in self.__class__.__name__.") + + copt_dict = opt_dict.copy() + self.molecule: SecondQuantizedMolecule = copt_dict.pop("molecule", None) + self.qubit_mapping: str = copt_dict.pop("qubit_mapping", "jw") + self.up_then_down: str = copt_dict.pop("up_then_down", False) + self.initial_var_params: Union[List, str] = copt_dict.pop("initial_var_params", None) + self.backend_options: dict = copt_dict.pop("backend_options", default_backend_options) + self.penalty_terms: Optional[dict] = copt_dict.pop("penalty_terms", None) + self.ilc_ansatz_options: dict = copt_dict.pop("ilc_ansatz_options", dict()) + self.qcc_ansatz_options: dict = copt_dict.pop("qcc_ansatz_options", dict()) + self.qubit_hamiltonian: QubitOperator = copt_dict.pop("qubit_hamiltonian", None) + self.max_ilc_iter: int = copt_dict.pop("max_ilc_iter", 3) + self.compress_qubit_ham: bool = copt_dict.pop("compress_qubit_ham", False) + self.compress_eps: float = copt_dict.pop("compress_eps", 1.59e-3) + self.verbose: bool = copt_dict.pop("verbose", False) + + if len(copt_dict) > 0: + raise KeyError(f"The following keywords are not supported in {self.__class__.__name__}: \n {copt_dict.keys()}") if not self.molecule and not self.qubit_hamiltonian: raise ValueError("An instance of SecondQuantizedMolecule or QubitOperator " diff --git a/tangelo/algorithms/variational/iqcc_solver.py b/tangelo/algorithms/variational/iqcc_solver.py index b086077ff..93b7926e4 100644 --- a/tangelo/algorithms/variational/iqcc_solver.py +++ b/tangelo/algorithms/variational/iqcc_solver.py @@ -28,7 +28,11 @@ J. Chem. Theory Comput. 2020, 16, 2, 1055–1063. """ +from typing import Union + +from tangelo import SecondQuantizedMolecule from tangelo.linq import get_backend +from tangelo.toolboxes.operators import QubitOperator from tangelo.toolboxes.ansatz_generator.qcc import QCC from tangelo.algorithms.variational.vqe_solver import VQESolver, BuiltInAnsatze from tangelo.toolboxes.ansatz_generator._qubit_cc import qcc_op_dress @@ -91,29 +95,25 @@ class iQCC_solver: def __init__(self, opt_dict): default_backend_options = {"target": None, "n_shots": None, "noise_model": None} - default_options = {"molecule": None, - "qubit_mapping": "jw", - "up_then_down": False, - "initial_var_params": None, - "backend_options": default_backend_options, - "penalty_terms": None, - "ansatz_options": dict(), - "qubit_hamiltonian": None, - "deqcc_thresh": 1e-5, - "max_iqcc_iter": 100, - "max_iqcc_retries": 10, - "compress_qubit_ham": False, - "compress_eps": 1.59e-3, - "verbose": False} - - # Initialize with default values - self.__dict__ = default_options - # Overwrite default values with user-provided ones, if they correspond to a valid keyword - for param, val in opt_dict.items(): - if param in default_options: - setattr(self, param, val) - else: - raise KeyError(f"Keyword {param} not available in self.__class__.__name__.") + + copt_dict = opt_dict.copy() + self.molecule: SecondQuantizedMolecule = copt_dict.pop("molecule", None) + self.qubit_mapping: str = copt_dict.pop("qubit_mapping", "jw") + self.up_then_down: bool = copt_dict.pop("up_then_down", False) + self.initial_var_params: Union[str, list] = copt_dict.pop("initial_var_params", None) + self.backend_options: dict = copt_dict.pop("backend_options", default_backend_options) + self.penalty_terms: dict = copt_dict.pop("penalty_terms", None) + self.ansatz_options: dict = copt_dict.pop("ansatz_options", dict()) + self.qubit_hamiltonian: QubitOperator = copt_dict.pop("qubit_hamiltonian", None) + self.deqcc_thresh: float = copt_dict.pop("deqcc_thresh", 1e-5) + self.max_iqcc_iter: int = copt_dict.pop("max_iqcc_iter", 100) + self.max_iqcc_retries: int = copt_dict.pop("max_iqcc_retries", 10) + self.compress_qubit_ham: bool = copt_dict.pop("compress_qubit_ham", False) + self.compress_eps: float = copt_dict.pop("compress_eps", 1.59e-3) + self.verbose: bool = copt_dict.pop("verbose", False) + + if len(copt_dict) > 0: + raise KeyError(f"The following keywords are not supported in {self.__class__.__name__}: \n {copt_dict.keys()}") if not self.molecule: raise ValueError(f"An instance of SecondQuantizedMolecule is required for initializing {self.__class__.__name__}.") diff --git a/tangelo/algorithms/variational/sa_oo_vqe_solver.py b/tangelo/algorithms/variational/sa_oo_vqe_solver.py index 4523134e8..30a45d904 100644 --- a/tangelo/algorithms/variational/sa_oo_vqe_solver.py +++ b/tangelo/algorithms/variational/sa_oo_vqe_solver.py @@ -49,6 +49,7 @@ class SA_OO_Solver(SA_VQESolver): optimizer (function handle): a function defining the classical optimizer and its behavior. initial_var_params (str or array-like) : initial value for the classical optimizer. backend_options (dict): parameters to build the underlying compute backend (simulator, etc). + simulate_options (dict): Options for fine-control of the simulator backend, including desired measurement results, etc. penalty_terms (dict): parameters for penalty terms to append to target qubit Hamiltonian (see penalty_terms for more details). ansatz_options (dict): parameters for the given ansatz (see given ansatz file for details). @@ -56,6 +57,8 @@ class SA_OO_Solver(SA_VQESolver): Default, False has alternating spin up/down ordering. qubit_hamiltonian (QubitOperator-like): Self-explanatory. verbose (bool): Flag for VQE verbosity. + projective_circuit (Circuit): A terminal circuit that projects into the correct space, always added to + the end of the ansatz circuit. ref_states (list): The vector occupations of the reference configurations weights (array): The weights of the occupations """ @@ -79,8 +82,9 @@ def __init__(self, opt_dict: dict): super().__init__(opt_dict_sa_vqe) # Add oo_options to attributes - for k, v in oo_options.items(): - setattr(self, k, v) + self.tol: float = oo_options["tol"] + self.max_cycles: int = oo_options["max_cycles"] + self.n_oo_per_iter: int = oo_options["n_oo_per_iter"] self.n_ref_states = len(self.ref_states) @@ -110,7 +114,7 @@ def iterate(self): break for _ in range(self.n_oo_per_iter): u_mat = self.generate_oo_unitary() - self.molecule.mean_field.mo_coeff = self.molecule.mean_field.mo_coeff @ u_mat + self.molecule.mo_coeff = self.molecule.mo_coeff @ u_mat self.energies.append(self.energy_from_rdms()) if self.verbose: print(f"The State-Averaged Orbital Optimized energy for iteration {iter} is: {self.energies[-1]}") diff --git a/tangelo/algorithms/variational/sa_vqe_solver.py b/tangelo/algorithms/variational/sa_vqe_solver.py index c4a93e6ec..4667aa1de 100644 --- a/tangelo/algorithms/variational/sa_vqe_solver.py +++ b/tangelo/algorithms/variational/sa_vqe_solver.py @@ -23,6 +23,8 @@ Phys. Rev. Research 1, 033062 (2019) """ +from typing import List, Union, Type + import numpy as np from tangelo.linq import get_backend, Circuit @@ -54,6 +56,7 @@ class SA_VQESolver(VQESolver): optimizer (function handle): a function defining the classical optimizer and its behavior. initial_var_params (str or array-like) : initial value for the classical optimizer. backend_options (dict): parameters to build the underlying compute backend (simulator, etc). + simulate_options (dict): Options for fine-control of the simulator backend, including desired measurement results, etc. penalty_terms (dict): parameters for penalty terms to append to target qubit Hamiltonian (see penalty_terms for more details). deflation_circuits (list[Circuit]): Deflation circuits to add an orthogonalization penalty with. @@ -63,6 +66,8 @@ class SA_VQESolver(VQESolver): Default, False has alternating spin up/down ordering. qubit_hamiltonian (QubitOperator-like): Self-explanatory. verbose (bool): Flag for VQE verbosity. + projective_circuit (Circuit): A terminal circuit that projects into the correct space, always added to + the end of the ansatz circuit. ref_states (list): The vector occupations of the reference configurations or the reference circuits. weights (array): The weights of the occupations """ @@ -83,8 +88,9 @@ def __init__(self, opt_dict): self.builtin_ansatze = set([BuiltInAnsatze.UpCCGSD, BuiltInAnsatze.UCCGD, BuiltInAnsatze.HEA, BuiltInAnsatze.UCCSD]) # Add sa_vqe_options to attributes - for k, v in sa_vqe_options.items(): - setattr(self, k, v) + self.ref_states: Union[List[int], np.ndarray] = sa_vqe_options["ref_states"] + self.weights: Union[List[float], np.ndarray] = sa_vqe_options["weights"] + self.ansatz: Type[agen.Ansatz] = sa_vqe_options["ansatz"] if self.ref_states is None: raise ValueError(f"ref_states must be provided when instantiating {self.__class__.__name__}") @@ -195,9 +201,11 @@ def energy_estimation(self, var_params): energy = 0 self.state_energies = list() for i, reference_circuit in enumerate(self.reference_circuits): - state_energy = self.backend.get_expectation_value(self.qubit_hamiltonian, reference_circuit + self.ansatz.circuit) + full_circ = (reference_circuit + self.ansatz.circuit + self.projective_circuit if self.projective_circuit + else reference_circuit + self.ansatz.circuit) + state_energy = self.backend.get_expectation_value(self.qubit_hamiltonian, full_circ, **self.simulate_options) for circ in self.deflation_circuits: - f_dict, _ = self.backend.simulate(circ + self.ansatz.circuit.inverse() + reference_circuit.inverse()) + f_dict, _ = self.backend.simulate(circ + full_circ.inverse(), **self.simulate_options) state_energy += self.deflation_coeff * f_dict.get("0"*self.ansatz.circuit.width, 0) energy += state_energy*self.weights[i] self.state_energies.append(state_energy) diff --git a/tangelo/algorithms/variational/tests/test_adapt_vqe_solver.py b/tangelo/algorithms/variational/tests/test_adapt_vqe_solver.py index 2820792df..b6a7ffe94 100644 --- a/tangelo/algorithms/variational/tests/test_adapt_vqe_solver.py +++ b/tangelo/algorithms/variational/tests/test_adapt_vqe_solver.py @@ -14,8 +14,14 @@ import unittest -from tangelo.algorithms.variational import ADAPTSolver +import numpy as np + from tangelo.molecule_library import mol_H2_sto3g, xyz_H4 +from tangelo.linq import Circuit, Gate +from tangelo.algorithms.variational import ADAPTSolver +from tangelo.toolboxes.ansatz_generator.fermionic_operators import spinz_operator +from tangelo.toolboxes.ansatz_generator.ansatz_utils import trotterize, get_qft_circuit +from tangelo.toolboxes.qubit_mappings.mapping_transform import fermion_to_qubit_mapping from tangelo.toolboxes.ansatz_generator._unitary_majorana_cc import get_majorana_uccgsd_pool, get_majorana_uccsd_pool from tangelo.toolboxes.molecular_computation.molecule import SecondQuantizedMolecule @@ -25,7 +31,7 @@ class ADAPTSolverTest(unittest.TestCase): def test_build_adapt(self): """Try instantiating ADAPTSolver with basic input.""" - opt_dict = {"molecule": mol_H2_sto3g, "max_cycles": 15} + opt_dict = {"molecule": mol_H2_sto3g, "max_cycles": 15, "spin": 0} adapt_solver = ADAPTSolver(opt_dict) adapt_solver.build() @@ -61,7 +67,7 @@ def test_multiple_cycle_adapt_majorana_pool(self): from a Majorana UCCGSD pool and a Majorana UCCSD pool """ - mol = SecondQuantizedMolecule(xyz_H4, 0, 0, "sto-3g", frozen_orbitals=[0]) + mol = SecondQuantizedMolecule(xyz_H4, 0, 0, basis="sto-3g", frozen_orbitals=[0]) opt_dict = {"molecule": mol, "max_cycles": 4, "verbose": False, "pool": get_majorana_uccgsd_pool, "pool_args": {"n_sos": mol.n_active_sos}} adapt_solver = ADAPTSolver(opt_dict) @@ -70,7 +76,7 @@ def test_multiple_cycle_adapt_majorana_pool(self): self.assertAlmostEqual(adapt_solver.optimal_energy, -1.8945, places=3) - mol = SecondQuantizedMolecule(xyz_H4, 0, 0, "sto-3g", frozen_orbitals=[0]) + mol = SecondQuantizedMolecule(xyz_H4, 0, 0, basis="sto-3g", frozen_orbitals=[0]) opt_dict = {"molecule": mol, "max_cycles": 4, "verbose": False, "pool": get_majorana_uccsd_pool, "pool_args": {"n_electrons": mol.n_active_electrons, "n_sos": mol.n_active_sos}} adapt_solver = ADAPTSolver(opt_dict) @@ -95,7 +101,7 @@ def test_multiple_cycle_adapt_majorana_pool_with_deflation(self): from a Majorana UCCGSD pool followed by deflation for an orthogonal state triplet state. """ - mol = SecondQuantizedMolecule(xyz_H4, 0, 0, "sto-3g", frozen_orbitals=[0]) + mol = SecondQuantizedMolecule(xyz_H4, 0, 0, basis="sto-3g", frozen_orbitals=[0]) opt_dict = {"molecule": mol, "max_cycles": 4, "verbose": False, "pool": get_majorana_uccgsd_pool, "pool_args": {"n_sos": mol.n_active_sos}} adapt_solver = ADAPTSolver(opt_dict) @@ -115,6 +121,38 @@ def test_multiple_cycle_adapt_majorana_pool_with_deflation(self): self.assertAlmostEqual(optimal_energy, -1.91062, places=3) + def test_sym_projected(self): + """Solve Linear H3 with one frozen orbtial with ADAPTSolver using 4 cycles and operators chosen + from a Majorana UCCGSD pool, with a sz symmetry projection. + """ + + xyz_H3 = [("H", [0., 0., 0.]), ("H", [1., 0., 0.]), ("H", [2., 0., 0.])] + mol = SecondQuantizedMolecule(xyz_H3, 0, 1, basis="sto-3g") + + # QPE based Sz projection. + def sz_check(n_state: int, molecule: SecondQuantizedMolecule, mapping: str, up_then_down): + n_qft = 3 + spin_fe_op = spinz_operator(molecule.n_active_mos) + q_spin = fermion_to_qubit_mapping(spin_fe_op, mapping, molecule.n_active_sos, molecule.n_active_electrons, up_then_down, molecule.spin) + + sym_var_circuit = Circuit([Gate("H", n_state + q) for q in range(n_qft)]) + for j, i in enumerate(range(n_state, n_state+n_qft)): + sym_var_circuit += trotterize(2*q_spin+3, -2*np.pi/2**(j+1), control=i) + sym_var_circuit += get_qft_circuit(list(range(n_state+n_qft-1, n_state-1, -1)), inverse=True) + sym_var_circuit += Circuit([Gate("MEASURE", i) for i in range(n_state, n_state+n_qft)]) + return sym_var_circuit + + proj_circuit = sz_check(6, mol, "JW", False) + + opt_dict = {"molecule": mol, "max_cycles": 4, "verbose": False, "pool": get_majorana_uccgsd_pool, + "pool_args": {"n_sos": mol.n_active_sos}, "simulate_options": {"desired_meas_result": "100"}, + "projective_circuit": proj_circuit} + adapt_solver = ADAPTSolver(opt_dict) + adapt_solver.build() + adapt_solver.simulate() + + self.assertAlmostEqual(adapt_solver.optimal_energy, -1.56835, places=5) + if __name__ == "__main__": unittest.main() diff --git a/tangelo/algorithms/variational/tests/test_sa_vqe_solver.py b/tangelo/algorithms/variational/tests/test_sa_vqe_solver.py index 1f1cfb0ed..6e90081ac 100644 --- a/tangelo/algorithms/variational/tests/test_sa_vqe_solver.py +++ b/tangelo/algorithms/variational/tests/test_sa_vqe_solver.py @@ -16,6 +16,7 @@ import numpy as np +from tangelo.linq import Circuit, Gate from tangelo.algorithms import BuiltInAnsatze, SA_VQESolver from tangelo.molecule_library import mol_H2_sto3g from tangelo.toolboxes.ansatz_generator import UCCSD @@ -137,6 +138,31 @@ def test_simulate_h2(self): energy = vqe_solver_2.simulate() self.assertAlmostEqual(exact_energies[2], energy, delta=1.e-3) + def test_projected_simulate_h2(self): + """Run SA-VQE on H2 molecule with a parity check projection, with UpCCGSD ansatz, JW qubit mapping, + ref_states and exact simulator. + """ + + sym_check = Circuit([Gate("CNOT", target=q, control=4) for q in {0, 2}] + [Gate("CNOT", target=q, control=5) for q in [1, 3]]) + sym_check += Circuit([Gate("MEASURE", 4), Gate("MEASURE", 5)]) + + vqe_options = {"molecule": mol_H2_sto3g, "ansatz": BuiltInAnsatze.UpCCGSD, "qubit_mapping": "jw", + "verbose": True, "ref_states": [[1, 1, 0, 0], [1, 0, 0, 1]], "weights": [1.2, 0.9], + "projective_circuit": sym_check, "simulate_options": {"desired_meas_result": "00"}} + sa_vqe_solver = SA_VQESolver(vqe_options) + sa_vqe_solver.build() + + # code to generate exact results. + # from pyscf import mcscf + # mc = mcscf.CASCI(mol_H2_sto3g.mean_field, 2, 2) + # mc.fcisolver.nroots = 3 + # exact_energies = mc.casci()[0] + exact_energies = [-1.1372702, -0.5324790, -0.1699013] + + # Use state averaging to get ground and first excited state. + _ = sa_vqe_solver.simulate() + np.testing.assert_array_almost_equal(exact_energies[:2], sa_vqe_solver.state_energies, decimal=3) + def test_get_rdm_h2(self): """Compute RDMs with UCCSD ansatz, JW qubit mapping, optimized parameters, exact simulator (H2). diff --git a/tangelo/algorithms/variational/tests/test_vqe_solver.py b/tangelo/algorithms/variational/tests/test_vqe_solver.py index c2b69edad..7cee69fca 100644 --- a/tangelo/algorithms/variational/tests/test_vqe_solver.py +++ b/tangelo/algorithms/variational/tests/test_vqe_solver.py @@ -15,7 +15,7 @@ import unittest import numpy as np -from tangelo.linq import get_backend +from tangelo.linq import get_backend, Circuit, Gate from tangelo.helpers.utils import installed_backends from tangelo.linq.target import QiskitSimulator from tangelo.algorithms import BuiltInAnsatze, VQESolver @@ -24,6 +24,9 @@ from tangelo.toolboxes.qubit_mappings.mapping_transform import fermion_to_qubit_mapping from tangelo.toolboxes.molecular_computation.rdms import matricize_2rdm from tangelo.toolboxes.optimizers.rotosolve import rotosolve +from tangelo.toolboxes.molecular_computation.molecule import SecondQuantizedMolecule +from tangelo.toolboxes.ansatz_generator.fermionic_operators import spinz_operator +from tangelo.toolboxes.ansatz_generator.ansatz_utils import trotterize, get_qft_circuit class VQESolverTest(unittest.TestCase): @@ -265,7 +268,7 @@ def test_simulate_qmf_h4(self): def test_simulate_qcc_h4(self): """Run VQE on H4 molecule, with QCC ansatz, JW qubit mapping, initial - parameters, exact simulator. + parameters, exact simulator. Followed by calculation with Sz symmetry projection """ vqe_options = {"molecule": mol_H4_sto3g, "ansatz": BuiltInAnsatze.QCC, "qubit_mapping": "jw", @@ -276,6 +279,36 @@ def test_simulate_qcc_h4(self): energy = vqe_solver.simulate() self.assertAlmostEqual(energy, -1.963270, delta=1e-4) + # QPE based Sz projection. + def sz_check(n_state: int, molecule: SecondQuantizedMolecule, mapping: str, up_then_down): + n_qft = 3 + spin_fe_op = spinz_operator(molecule.n_active_mos) + q_spin = fermion_to_qubit_mapping(spin_fe_op, mapping, molecule.n_active_sos, molecule.n_active_electrons, up_then_down, molecule.spin) + + sym_var_circuit = Circuit([Gate("H", q) for q in range(n_state, n_state+n_qft)]) + for j, i in enumerate(range(n_state, n_state+n_qft)): + sym_var_circuit += trotterize(2*q_spin+3, -2*np.pi/2**(j+1), control=i) + sym_var_circuit += get_qft_circuit(list(range(n_state+n_qft-1, n_state-1, -1)), inverse=True) + sym_var_circuit += Circuit([Gate("MEASURE", i) for i in range(n_state, n_state+n_qft)]) + return sym_var_circuit + + # Use a circuit with variational gates and mid-circuit measurements as the ansatz. + proj_circuit = sz_check(8, mol_H4_sto3g, "JW", vqe_solver.up_then_down) + var_circuit = vqe_solver.optimal_circuit + proj_circuit + + vqe_solver_p = VQESolver({"ansatz": var_circuit, "qubit_hamiltonian": vqe_solver.qubit_hamiltonian, "simulate_options": {"desired_meas_result": "011"}}) + vqe_solver_p.build() + energyp = vqe_solver_p.simulate() + self.assertAlmostEqual(energyp, -1.97622, delta=1e-4) + + # Use a circuit with variational gates as the ansatz, add a projective circuit separately. + var_circuit = vqe_solver.optimal_circuit + vqe_solver_p = VQESolver({"ansatz": var_circuit, "qubit_hamiltonian": vqe_solver.qubit_hamiltonian, + "simulate_options": {"desired_meas_result": "011"}, "projective_circuit": proj_circuit}) + vqe_solver_p.build() + energyp = vqe_solver_p.simulate() + self.assertAlmostEqual(energyp, -1.97622, delta=1e-4) + def test_simulate_ilc_h4(self): """Run VQE on H4 molecule, with ILC ansatz, JW qubit mapping, initial parameters, exact simulator. diff --git a/tangelo/algorithms/variational/vqe_solver.py b/tangelo/algorithms/variational/vqe_solver.py index 9d703b3e3..7df4ab747 100644 --- a/tangelo/algorithms/variational/vqe_solver.py +++ b/tangelo/algorithms/variational/vqe_solver.py @@ -18,15 +18,16 @@ import warnings import itertools +from typing import Optional, Union, List from enum import Enum import numpy as np -from openfermion.ops.operators.qubit_operator import QubitOperator from tangelo.helpers.utils import HiddenPrints +from tangelo import SecondQuantizedMolecule from tangelo.linq import get_backend, Circuit from tangelo.linq.helpers.circuits.measurement_basis import measurement_basis_gates -from tangelo.toolboxes.operators import count_qubits, FermionOperator +from tangelo.toolboxes.operators import count_qubits, FermionOperator, QubitOperator from tangelo.toolboxes.qubit_mappings.mapping_transform import fermion_to_qubit_mapping from tangelo.toolboxes.qubit_mappings.statevector_mapping import get_mapped_vector, vector_to_circuit from tangelo.toolboxes.post_processing.bootstrapping import get_resampled_frequencies @@ -72,6 +73,7 @@ class VQESolver: initial_var_params (str or array-like) : initial value for the classical optimizer. backend_options (dict): parameters to build the underlying compute backend (simulator, etc). + simulate_options (dict): Options for fine-control of the simulator backend, including desired measurement results, etc. penalty_terms (dict): parameters for penalty terms to append to target qubit Hamiltonian (see penalty_terms for more details). deflation_circuits (list[Circuit]): Deflation circuits to add an @@ -84,6 +86,8 @@ class VQESolver: spin up/down ordering. qubit_hamiltonian (QubitOperator-like): Self-explanatory. verbose (bool): Flag for VQE verbosity. + projective_circuit (Circuit): A terminal circuit that projects into the correct space, always added to + the end of the ansatz circuit. ref_state (array or Circuit): The reference configuration to use. Replaces HF state QMF, QCC, ILC require ref_state to be an array. UCC1, UCC3, VSQS can not use a different ref_state than HF by construction. @@ -92,28 +96,27 @@ class VQESolver: def __init__(self, opt_dict): default_backend_options = {"target": None, "n_shots": None, "noise_model": None} - default_options = {"molecule": None, - "qubit_mapping": "jw", "ansatz": BuiltInAnsatze.UCCSD, - "optimizer": self._default_optimizer, - "initial_var_params": None, - "backend_options": default_backend_options, - "penalty_terms": None, - "deflation_circuits": list(), - "deflation_coeff": 1, - "ansatz_options": dict(), - "up_then_down": False, - "qubit_hamiltonian": None, - "verbose": False, - "ref_state": None} - - # Initialize with default values - self.__dict__ = default_options - # Overwrite default values with user-provided ones, if they correspond to a valid keyword - for k, v in opt_dict.items(): - if k in default_options: - setattr(self, k, v) - else: - raise KeyError(f"Keyword :: {k}, not available in VQESolver") + copt_dict = opt_dict.copy() + + self.molecule: Optional[SecondQuantizedMolecule] = copt_dict.pop("molecule", None) + self.qubit_mapping: str = copt_dict.pop("qubit_mapping", "jw") + self.ansatz: agen.Ansatz = copt_dict.pop("ansatz", BuiltInAnsatze.UCCSD) + self.optimizer = copt_dict.pop("optimizer", self._default_optimizer) + self.initial_var_params: Optional[Union[str, list]] = copt_dict.pop("initial_var_params", None) + self.backend_options: dict = copt_dict.pop("backend_options", default_backend_options) + self.penalty_terms: Optional[dict] = copt_dict.pop("penalty_terms", None) + self.simulate_options: dict = copt_dict.pop("simulate_options", dict()) + self.deflation_circuits: Optional[List[Circuit]] = copt_dict.pop("deflation_circuits", list()) + self.deflation_coeff: float = copt_dict.pop("deflation_coeff", 1) + self.ansatz_options: dict = copt_dict.pop("ansatz_options", dict()) + self.up_then_down: bool = copt_dict.pop("up_then_down", False) + self.qubit_hamiltonian: QubitOperator = copt_dict.pop("qubit_hamiltonian", None) + self.verbose: bool = copt_dict.pop("verbose", False) + self.projective_circuit: Circuit = copt_dict.pop("projective_circuit", None) + self.ref_state: Optional[Union[list, Circuit]] = copt_dict.pop("ref_state", None) + + if len(copt_dict) > 0: + raise KeyError(f"The following keywords are not supported in {self.__class__.__name__}: \n {copt_dict.keys()}") # Raise error/warnings if input is not as expected. Only a single input # must be provided to avoid conflicts. @@ -128,7 +131,7 @@ def __init__(self, opt_dict): self.up_then_down = True if self.ansatz == BuiltInAnsatze.pUCCD and self.qubit_mapping.lower() != "hcb": warnings.warn("Forcing the hard-core boson mapping for the pUCCD ansatz.", RuntimeWarning) - self.mapping = "HCB" + self.qubit_mapping = "HCB" # QCC and QMF and ILC require a reference state that can be represented by a single layer of RZ-RX gates on each qubit. # This decomposition can not be determined from a general Circuit reference state. if isinstance(self.ref_state, Circuit): @@ -255,6 +258,8 @@ def simulate(self): self.optimal_energy = optimal_energy self.ansatz.build_circuit(self.optimal_var_params) self.optimal_circuit = self.reference_circuit+self.ansatz.circuit if self.ref_state is not None else self.ansatz.circuit + if self.projective_circuit: + self.optimal_circuit += self.projective_circuit return self.optimal_energy def get_resources(self): @@ -294,7 +299,9 @@ def energy_estimation(self, var_params): # Update variational parameters, compute energy using the hardware backend self.ansatz.update_var_params(var_params) circuit = self.ansatz.circuit if self.ref_state is None else self.reference_circuit + self.ansatz.circuit - energy = self.backend.get_expectation_value(self.qubit_hamiltonian, circuit) + if self.projective_circuit: + circuit += self.projective_circuit + energy = self.backend.get_expectation_value(self.qubit_hamiltonian, circuit, **self.simulate_options) # Additional computation for deflation (optional) for circ in self.deflation_circuits: @@ -380,7 +387,10 @@ def operator_expectation(self, operator, var_params=None, n_active_mos=None, n_a spin=spin) self.ansatz.update_var_params(var_params) - expectation = self.backend.get_expectation_value(self.qubit_hamiltonian, ref_state+self.ansatz.circuit) + circuit = ref_state + self.ansatz.circuit + if self.projective_circuit: + circuit += self.projective_circuit + expectation = self.backend.get_expectation_value(self.qubit_hamiltonian, circuit, **self.simulate_options) # Restore the current target hamiltonian self.qubit_hamiltonian = tmp_hamiltonian diff --git a/tangelo/helpers/math.py b/tangelo/helpers/math.py index 48c7e9d39..cf24c6cfc 100644 --- a/tangelo/helpers/math.py +++ b/tangelo/helpers/math.py @@ -53,3 +53,29 @@ def bool_col_echelon(bool_array): pivot -= 1 return bool_array + + +def arrays_almost_equal_up_to_global_phase(array1, array2, atol=1e-6): + """ + Checks if two arrays are almost equal up to a global phase. + + Args: + array1 (array): Self-explanatory. + array2 (array): Self-explanatory. + atol (float) : Optional, absolute tolerance + + Returns: + bool : True if arrays are almost equal up to a global phase, False otherwise. + """ + if len(array1) != len(array2): + return False + + array1 = np.asarray(array1) + array2 = np.asarray(array2) + + if np.allclose(array1, array2, atol=atol): + return True + + # Check for global phase difference + phase_diff = np.angle(array1[0] / array2[0]) + return np.allclose(array1, array2 * np.exp(1j * phase_diff), atol=atol) diff --git a/tangelo/helpers/utils.py b/tangelo/helpers/utils.py index 6ebc20324..a77f01e31 100644 --- a/tangelo/helpers/utils.py +++ b/tangelo/helpers/utils.py @@ -73,9 +73,12 @@ def new_func(*args, **kwargs): # List all built-in backends supported -all_backends = {"qulacs", "qiskit", "cirq", "braket", "projectq", "qdk", "pennylane"} +all_backends = {"qulacs", "qiskit", "cirq", "braket", "projectq", "qdk", "pennylane", "sympy", "stim"} all_backends_simulator = {"qulacs", "qiskit", "cirq", "qdk"} sv_backends_simulator = {"qulacs", "qiskit", "cirq"} +symbolic_backends = {"sympy"} +chem_backends = {"pyscf", "psi4"} +clifford_backends_simulator = {"stim"} # Dictionary mapping package names to their identifier in this module packages = {p: p for p in all_backends} @@ -85,6 +88,8 @@ def new_func(*args, **kwargs): installed_backends = {p_id for p_id, p_name in packages.items() if is_package_installed(p_name)} installed_simulator = installed_backends & all_backends_simulator installed_sv_simulator = installed_backends & sv_backends_simulator +installed_chem_backends = {p_id for p_id in chem_backends if is_package_installed(p_id)} +installed_clifford_simulators = {p_id for p_id in clifford_backends_simulator if is_package_installed(p_id)} # Check if qulacs installed (better performance for tests). If not, defaults to cirq (always installed with openfermion) default_simulator = "qulacs" if "qulacs" in installed_sv_simulator else "cirq" diff --git a/tangelo/linq/__init__.py b/tangelo/linq/__init__.py index 436d97f7d..c826cad75 100644 --- a/tangelo/linq/__init__.py +++ b/tangelo/linq/__init__.py @@ -13,8 +13,9 @@ # limitations under the License. from .gate import * -from .circuit import Circuit, stack, remove_small_rotations, remove_redundant_gates +from .circuit import Circuit, stack, remove_small_rotations, remove_redundant_gates, get_unitary_circuit_pieces from .translator import * from .simulator import get_backend from .target.backend import get_expectation_value_from_frequencies_oneterm from .target import backend_info, Backend +from .noisy_simulation import NoiseModel diff --git a/tangelo/linq/circuit.py b/tangelo/linq/circuit.py index e0368960c..ef2e7704b 100644 --- a/tangelo/linq/circuit.py +++ b/tangelo/linq/circuit.py @@ -19,7 +19,7 @@ """ import copy -from typing import List +from typing import List, Tuple, Iterator import numpy as np from cirq.contrib.svg import SVGCircuit @@ -55,6 +55,7 @@ def __init__(self, gates: List[Gate] = None, n_qubits=None, name="no_name"): self._gate_counts = dict() self._n_qubit_gate_counts = dict() self._variational_gates = [] + self._probabilities = dict() if gates: _ = [self.add_gate(g) for g in gates] @@ -145,6 +146,21 @@ def is_mixed_state(self): """ return "MEASURE" in self.counts + @property + def success_probabilities(self): + """Returns the dictionary of probabilities populated by simulating with different desired_meas_result. + + The keys of the dictionary are bit strings, corresponding to the desired outcomes in the order + the measurement gates arise in the circuit. + + Each bit string must be simulated using a backend with n_shots=None and desired_meas_result=bitstring + in order to populate the corresponding probability. + """ + if not self.is_mixed_state: + return {"": 1} + else: + return self._probabilities + def draw(self): """Method to output a prettier version of the circuit for use in jupyter notebooks that uses cirq SVGCircuit""" # circular import @@ -195,36 +211,41 @@ def check_index_valid(index): def depth(self): """ Return the depth of the quantum circuit, by computing the number of moments. Does not count - qubit initialization as a moment (unlike Cirq, for example). + qubit initialization as a moment (unlike Cirq, for example). Compute from scratch. """ + # List of qubit indices involved in each moment. Look up dict for latest moment for each index. moments = list() + latest_moment = dict() - for g in self._gates: + # Traverse gates and compute moments + for g in self: qubits = set(g.target) if g.control is None else set(g.target + g.control) if not moments: moments.append(qubits) + for i in qubits: + latest_moment[i] = 0 else: - # Find latest moment involving at least one of the qubits targeted by the current gate - # The current gate is part of the moment following that one - for i, m in reversed(list(enumerate(moments))): - if m & qubits: - if (i+1) < len(moments): - moments[i+1] = moments[i+1] | qubits - else: - moments.append(qubits) - break - # Case where none of the target qubits have been used before - elif i == 0: - moments[0] = moments[0] | qubits - + # Find latest moment involving one of the qubits targeted by the gate + # -1 means the qubit index was encountered for the very first time + b = max([latest_moment.get(i, -1) for i in qubits]) + for i in qubits: + latest_moment[i] = b + 1 + + # Case 1: Gate can be included in a previous moment + # Includes b = -1 case where all qubits are encountered for the 1st time + if (b + 1) < len(moments): + moments[b + 1] = moments[b + 1] | qubits + # Case 2: Gate is part of a new moment + else: + moments.append(qubits) return len(moments) def trim_qubits(self): """Trim unnecessary qubits and update indices with the lowest values possible. """ qubits_in_use = set().union(*self.get_entangled_indices()) - mapping = {ind: i for i, ind in enumerate(qubits_in_use)} + mapping = {ind: i for i, ind in enumerate(sorted(list(qubits_in_use)))} for g in self._gates: g.target = [mapping[ind] for ind in g.target] if g.control: @@ -437,3 +458,29 @@ def remove_redundant_gates(circuit): gates = [gate for gate_i, gate in enumerate(circuit._gates) if gate_i not in indices_to_remove] return Circuit(gates) + + +def get_unitary_circuit_pieces(circuit: Circuit) -> Tuple[List[Circuit], List[int]]: + """Split circuit into the unitary circuits between mid-circuit non-unitary MEASURE gates. + + Args: + circuit (Circuit): the circuit to split + + Returns: + List[Circuit]: The list of unitary circuits with a terminal non-unitary operation. + List[int]: The qubits that the "MEASURE" operation is applied to. + """ + + n_qubits = circuit.width + circuits, gates, measure_qubits = list(), list(), list() + + for g in circuit: + if g.name != "MEASURE": + gates += [Gate(g.name, g.target, g.control, g.parameter, g.is_variational)] + else: + circuits += [Circuit(copy.deepcopy(gates), n_qubits=n_qubits)] + measure_qubits += [g.target[0]] + gates = list() + circuits += [Circuit(copy.deepcopy(gates), n_qubits=n_qubits)] + + return circuits, measure_qubits diff --git a/tangelo/linq/gate.py b/tangelo/linq/gate.py index ff52ebf00..c6842c20a 100644 --- a/tangelo/linq/gate.py +++ b/tangelo/linq/gate.py @@ -16,10 +16,12 @@ gate operation, without tying it to a particular backend or an underlying mathematical operation. """ -from math import pi +from math import pi, isclose from typing import Union from numpy import integer, ndarray, floating +from sympy import Symbol + ONE_QUBIT_GATES = {"H", "X", "Y", "Z", "S", "T", "RX", "RY", "RZ", "PHASE"} TWO_QUBIT_GATES = {"CNOT", "CX", "CY", "CZ", "CRX", "CRY", "CRZ", "CPHASE", "XX", "SWAP"} @@ -34,6 +36,8 @@ "CNOT", "CX", "CY", "CZ", "CRX", "CRY", "CRZ", "CPHASE", "XX", "SWAP", "CSWAP"} +CLIFFORD_GATES = {"H", "S", "X", "Z", "Y", "SDAG", "CNOT", "CX", "CY", "CZ", "SWAP", "CXSWAP"} + class Gate(dict): """An abstract gate class that exposes all the gate information such as gate @@ -165,17 +169,37 @@ def inverse(self): """ if self.name not in INVERTIBLE_GATES: raise AttributeError(f"{self.name} is not an invertible gate") + + if self.name in {"T", "S"}: + new_parameter = -pi / 2 if self.name == "S" else -pi / 4 + return Gate("PHASE", self.target, self.control, new_parameter, self.is_variational) + if self.parameter == "": new_parameter = "" - elif isinstance(self.parameter, (float, floating, int, integer)): + elif isinstance(self.parameter, (float, floating, int, integer, Symbol)): new_parameter = -self.parameter - elif self.name in {"T", "S"}: - new_parameter = -pi / 2 if self.name == "T" else -pi / 4 - return Gate("PHASE", self.target, self.control, new_parameter, self.is_variational) else: raise AttributeError(f"{self.name} is not an invertible gate when parameter is {self.parameter}") return Gate(self.name, self.target, self.control, new_parameter, self.is_variational) + def is_clifford(self, abs_tol=1e-4): + """ + Check if a quantum gate is a Clifford gate. + + Args: + abs_tol (float) : Optional, Absolute tolerance for the difference between gate parameter clifford parameter values + + Returns: + bool: True if the gate is in Clifford gate list or has a parameter corresponding to Clifford points, + False otherwise. + """ + if self.name in CLIFFORD_GATES: + return True + elif self.name in {"RX", "RY", "RZ", "PHASE"}: + return isclose(self.parameter % (pi / 2), 0, abs_tol=abs_tol) + else: + return False + def serialize(self): return {"type": "Gate", "params": {"name": self.name, "target": self.target, diff --git a/tangelo/linq/helpers/circuits/clifford_circuits.py b/tangelo/linq/helpers/circuits/clifford_circuits.py new file mode 100644 index 000000000..f262eba01 --- /dev/null +++ b/tangelo/linq/helpers/circuits/clifford_circuits.py @@ -0,0 +1,89 @@ +# Copyright 2023 Good Chemistry Company. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from math import pi, isclose + +from tangelo.linq import Gate + + +def decompose_gate_to_cliffords(gate, abs_tol=1e-4): + """ + Decomposes a single qubit parameterized gate into Clifford gates. + + Args: + gate (Gate): The gate to be decomposed. + abs_tol (float): Optional, absolute tolerance for value comparison (default: 1e-4). + + Returns: + list: A list of Clifford gates representing the decomposition. + + Raises: + ValueError: If parameterized gate cannot be decomposed into Clifford gates. + + """ + # Return an error if the gate is not in the Clifford gate set + if not gate.is_clifford(abs_tol): + raise ValueError(f"Error. The following gate cannot be decomposed into Clifford gates:\n {gate}") + # If gate is not in the parameterized gate set, it decomposes to itself + elif gate.name not in {"RX", "RY", "RZ", "PHASE"}: + return gate + # If gate parameter is close to 0, the gate operation is the identity + elif isclose(gate.parameter, 0, abs_tol=abs_tol): + return [] + + # Find which Clifford parameter gate parameter corresponds to. + clifford_values = [0, pi, pi / 2, -pi / 2] + clifford_parameter = next((value for value in clifford_values if + isclose(gate.parameter % (2 * pi), value % (2 * pi), abs_tol=abs_tol)), None) + + if clifford_parameter is None: + raise ValueError(f"Error: Parameterized gate {gate} cannot be decomposed into Clifford gates") + + gate_list = [] + if gate.name == "RY": + if clifford_parameter == -pi / 2: + gate_list = [Gate("H", gate.target), Gate("Z", gate.target)] + elif clifford_parameter == pi / 2: + gate_list = [Gate("Z", gate.target), Gate("H", gate.target)] + + elif clifford_parameter == pi: + gate_list = [Gate("SDAG", gate.target), Gate("Y", gate.target), Gate("SDAG", gate.target)] + + elif gate.name == "RX": + if clifford_parameter == -pi / 2: + gate_list = [Gate("S", gate.target), Gate("H", gate.target), Gate("S", gate.target)] + elif clifford_parameter == pi / 2: + gate_list = [Gate("SDAG", gate.target), Gate("H", gate.target), Gate("SDAG", gate.target)] + elif clifford_parameter == pi: + gate_list = [Gate("SDAG", gate.target), Gate("X", gate.target), Gate("SDAG", gate.target)] + + elif gate.name == "RZ": + if clifford_parameter == -pi / 2: + gate_list = [Gate("H", gate.target), Gate("S", gate.target), Gate("H", gate.target), Gate("S", gate.target), + Gate("H", gate.target)] + elif clifford_parameter == pi / 2: + gate_list = [Gate("H", gate.target), Gate("SDAG", gate.target), Gate("H", gate.target), + Gate("SDAG", gate.target), Gate("H", gate.target)] + elif clifford_parameter == pi: + gate_list = [Gate("H", gate.target), Gate("SDAG", gate.target), Gate("X", gate.target), Gate("SDAG", gate.target), Gate("H", gate.target)] + + elif gate.name == "PHASE": + if clifford_parameter == -pi / 2: + gate_list = [Gate("SDAG", gate.target)] + elif clifford_parameter == pi / 2: + gate_list = [Gate("S", gate.target)] + elif clifford_parameter == pi: + gate_list = [Gate("Z", gate.target)] + + return gate_list diff --git a/tangelo/linq/helpers/circuits/tests/test_clifford_circuits.py b/tangelo/linq/helpers/circuits/tests/test_clifford_circuits.py new file mode 100644 index 000000000..8fefec560 --- /dev/null +++ b/tangelo/linq/helpers/circuits/tests/test_clifford_circuits.py @@ -0,0 +1,43 @@ +# Copyright 2023 Good Chemistry Company. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import unittest + +import numpy as np + +from tangelo.linq import Gate, Circuit +from tangelo.linq.helpers.circuits.clifford_circuits import decompose_gate_to_cliffords +from tangelo.linq.translator.translate_cirq import translate_c_to_cirq +from tangelo.helpers.math import arrays_almost_equal_up_to_global_phase + +clifford_angles = [0, np.pi/2, np.pi, -np.pi/2, 3*np.pi/2, 7*np.pi, -5*np.pi] +non_clifford_gate = Gate("RY", 0, parameter=np.pi/3) + + +class CliffordCircuitTest(unittest.TestCase): + + def test_decompose_gate_to_cliffords(self): + """Test if gate decomposition returns correct sequence. Uses Cirq to validate that the unitaries returned + are equal up to a global phase""" + for gate in ["RY", "RX", "RZ", "PHASE"]: + for angle in clifford_angles: + ref_gate = Gate(gate, 0, parameter=angle) + u_ref = translate_c_to_cirq(Circuit([ref_gate])) + u_decompose = translate_c_to_cirq(Circuit(decompose_gate_to_cliffords(ref_gate))) + arrays_almost_equal_up_to_global_phase(u_ref.unitary(), u_decompose.unitary()) + + def test_non_clifford_gates(self): + """Test if non-clifford gate raises value error""" + + self.assertRaises(ValueError, decompose_gate_to_cliffords, non_clifford_gate) diff --git a/tangelo/linq/qpu_connection/__init__.py b/tangelo/linq/qpu_connection/__init__.py index e71e98fa1..9875e2c49 100644 --- a/tangelo/linq/qpu_connection/__init__.py +++ b/tangelo/linq/qpu_connection/__init__.py @@ -15,3 +15,4 @@ from .qemist_cloud_connection import QEMISTCloudConnection from .ionq_connection import IonQConnection from .ibm_connection import IBMConnection +from .braket_connection import BraketConnection diff --git a/tangelo/linq/qpu_connection/braket_connection.py b/tangelo/linq/qpu_connection/braket_connection.py new file mode 100644 index 000000000..e9cbafd4c --- /dev/null +++ b/tangelo/linq/qpu_connection/braket_connection.py @@ -0,0 +1,185 @@ +""" + Wrappers around Braket python's API, to manage quantum experiments run with Braket from Tangelo. + Braket is available at https://github.com/aws/amazon-braket-sdk-python +""" + +from enum import Enum + +from tangelo.linq.translator import translate_circuit +from tangelo.linq.qpu_connection.qpu_connection import QpuConnection + + +class SupportedBraketProviders(str, Enum): + """ List of the providers currently supported. Needs to be occasionally + updated in the future as new providers arise. """ + + # SV1, TN1 and DM1 devices (simulators) + AMAZON = "Amazon Braket" + + # Hardware providers + IONQ = "IonQ" + RIGETTI = "Rigetti" + OXFORD = "Oxford" + + +def refresh_available_braket_devices(): + """Function to get the available gate-based devices on Braket. + Note: OFFLINE and RETIRED devices are filtered out. + Returns: + list of braket.aws.AwsDevice: Available gate-based Braket devices. + """ + from braket.aws import AwsDevice + + return AwsDevice.get_devices( + provider_names=[provider.value for provider in SupportedBraketProviders], + statuses=["ONLINE"]) + + +class BraketConnection(QpuConnection): + """ Wrapper around Amazon Braket API to facilitate quantum job management from Tangelo """ + + def __init__(self, s3_bucket=None, folder=None): + """ + Initialize connection object. Requires Braket to be installed (see above). + The destination s3_bucket and folder can be specified later, before submitting jobs. + + Args: + s3_bucket (str): Optional, name of target s3_bucket for saving results + folder (str): Optional, name of target folder in bucket for results + + Returns: + str | List[str]: Job id(s) + """ + + try: + import braket + self.braket = braket + except ModuleNotFoundError: + raise ModuleNotFoundError("Braket needs to be installed.") + + self.jobs = dict() + self.jobs_results = dict() + self.s3_bucket = s3_bucket + self.folder = folder + + def job_submit(self, backend_arn, n_shots, circuits): + """ Submit job as batch, return job id(s). + + Args: + backend_arn (str): arn for braket backend + n_shots (int): Number of shots to use on the target backend + circuits (Circuit | List[Circuit]): Tangelo circuit(s) + + Returns: + str | List[str]: Job id(s) + """ + + # Set up device object + from braket.aws import AwsDevice + device = AwsDevice(backend_arn) + + # Ensure s3 location for results is set to a valid value + if not (self.s3_bucket and self.folder): + raise ValueError(f"{self.__class__.__name__} :: Please set the following attributes: s3_bucket, folder") + s3_location = (self.s3_bucket, self.folder) + + # Ensure input is a list of circuits + if not isinstance(circuits, list): + circuits = [circuits] + + # Translate circuits in braket format, submit as batch + braket_circuits = [translate_circuit(c, "braket") for c in circuits] + my_task = device.run_batch(braket_circuits, s3_location, shots=n_shots, + poll_timeout_seconds=300, poll_interval_seconds=60) + + # Store job object(s) + for t in my_task.tasks: + self.jobs[t.id] = t + + # If batch has more than one circuit, return a list of ids + if len(my_task.tasks) > 1: + return [t.id for t in my_task.tasks] + else: + return my_task.tasks[0].id + + def job_status(self, job_id): + """ Return job information corresponding to the input job id + + Args: + job_id (str): string representing the job id + + Returns: + enum | str : status response from the native API + """ + return self.jobs[job_id].state() + + def job_results(self, job_id): + """ Blocking call requesting job results. + + Args: + job_id (str): string representing the job id + + Returns: + dict: histogram of measurements + """ + + from braket.aws import AwsQuantumTask + + # Retrieve job object + if job_id in self.jobs: + job = self.jobs[job_id] + else: + job = AwsQuantumTask(arn=job_id) + + # Check status of job, raise error if results cannot be retrieved + if self.job_status(job_id) in {'CANCELLED', 'FAILED'}: + print(f"Job {job_id} was cancelled or failed, and no results can be retrieved.") + return None + + # Retrieve results, update job dictionary. + result = job.result() + self.jobs_results[job_id] = result + return result.measurement_probabilities if result else None + + def job_cancel(self, job_id): + """ Attempt to cancel an existing job. May fail depending on job status (e.g too late) + + Args: + job_id (str): string representing the job id + + Returns: + bool: whether the job was successfully cancelled. + """ + job = self.jobs[job_id] + is_cancelled = True + + try: + job.cancel() + except Exception as err: + is_cancelled = False + + message = "successful" if is_cancelled else "failed" + print(f"Job {job_id} :: cancellation {message}.") + + return is_cancelled + + @staticmethod + def get_backend_info(): + """ Wrapper method to cut down the information returned by AWS SDK and provide a consistent interface for our code. + + Returns: + Dictionary containing device information in the format: + { + arn: { + provider: , + price: , + unit: , + } + } + """ + + return {device.arn: { + "provider_name": device.provider_name, + "price": device.properties.service.deviceCost.price, + "unit": device.properties.service.deviceCost.unit} + for device in refresh_available_braket_devices()} diff --git a/tangelo/linq/simulator.py b/tangelo/linq/simulator.py index 330ea3e32..624a3f65d 100644 --- a/tangelo/linq/simulator.py +++ b/tangelo/linq/simulator.py @@ -51,9 +51,3 @@ def get_backend(target: Union[None, str, Type[Backend]] = default_simulator, n_s raise TypeError(f"Target must be a str or a subclass of Backend but received class {type(target).__name__}") return target(n_shots=n_shots, noise_model=noise_model, **kwargs) - - -@deprecated("Please use get_backend.") -def Simulator(target: Union[None, str, Type[Backend]] = default_simulator, n_shots: Union[None, int] = None, - noise_model=None, **kwargs): - return get_backend(target, n_shots, noise_model, **kwargs) diff --git a/tangelo/linq/target/__init__.py b/tangelo/linq/target/__init__.py index 91fc9c6be..24f95055f 100644 --- a/tangelo/linq/target/__init__.py +++ b/tangelo/linq/target/__init__.py @@ -17,10 +17,12 @@ from .target_qiskit import QiskitSimulator from .target_qulacs import QulacsSimulator from .target_qdk import QDKSimulator -from tangelo.helpers.utils import all_backends_simulator +from .target_sympy import SympySimulator +from .target_stim import StimSimulator +from tangelo.helpers.utils import all_backends_simulator, clifford_backends_simulator -target_dict = {"qiskit": QiskitSimulator, "cirq": CirqSimulator, "qdk": QDKSimulator, "qulacs": QulacsSimulator} +target_dict = {"qiskit": QiskitSimulator, "cirq": CirqSimulator, "qdk": QDKSimulator, "qulacs": QulacsSimulator, "sympy": SympySimulator, "stim": StimSimulator} # Generate backend info dictionary backend_info = {sim_id: target_dict[sim_id].backend_info() for sim_id in all_backends_simulator} diff --git a/tangelo/linq/target/backend.py b/tangelo/linq/target/backend.py index fc446bee1..6082ea81d 100644 --- a/tangelo/linq/target/backend.py +++ b/tangelo/linq/target/backend.py @@ -37,10 +37,10 @@ import numpy as np from scipy import stats from bitarray import bitarray -from openfermion.ops import QubitOperator from tangelo.linq import Gate, Circuit from tangelo.linq.helpers.circuits.measurement_basis import measurement_basis_gates +from tangelo.toolboxes.operators import QubitOperator def get_expectation_value_from_frequencies_oneterm(term, frequencies): @@ -48,8 +48,7 @@ def get_expectation_value_from_frequencies_oneterm(term, frequencies): the result of a state-preparation. Args: - term (openfermion-style QubitOperator object): a qubit operator, with - only a single term. + term (QubitOperator): a single-term qubit operator. frequencies (dict): histogram of frequencies of measurements (assumed to be in lsq-first format). @@ -82,8 +81,7 @@ def get_variance_from_frequencies_oneterm(term, frequencies): """Return the variance of the expectation value of a single-term qubit-operator, given the result of a state-preparation. Args: - term (openfermion-style QubitOperator object): a qubit operator, with - only a single term. + term (QubitOperator): a single-term qubit operator. frequencies (dict): histogram of frequencies of measurements (assumed to be in lsq-first format). Returns: @@ -112,6 +110,50 @@ def get_variance_from_frequencies_oneterm(term, frequencies): return variance_term +def collapse_statevector_to_desired_measurement(statevector, qubit, result, order="lsq_first"): + """Take 0 or 1 part of a statevector for a given qubit and return a normalized statevector and probability. + + Args: + statevector (array): The statevector for which the collapse to the desired qubit value is performed. + qubit (int): Index of target qubit to collapse in the desired classical state. + result (int): 0 or 1. + order (string): The qubit ordering of the statevector, lsq_first or msq_first. + + Returns: + array: The collapsed and renormalized statevector. + float: The probability for the desired measurement to occur. + """ + + n_qubits = round(math.log2(len(statevector))) + + if 2**n_qubits != len(statevector): + raise ValueError(f"Statevector length of {len(statevector)} is not a power of 2.") + + if qubit > n_qubits-1: + raise ValueError("qubit index to measure is larger than number of qubits in statevector") + + if result not in {0, 1}: + raise ValueError(f"Result is not valid, must be an integer of 0 or 1 but received {result}") + + if order.lower() not in {"lsq_first", "msq_first"}: + raise ValueError("Order must be lsq_first or msq_first") + + before_index_length = 2**qubit if order == "lsq_first" else 2**(n_qubits-1-qubit) + after_index_length = 2**(n_qubits-1-qubit) if order == "lsq_first" else 2**qubit + + sv_selected = np.reshape(statevector, (before_index_length, 2, after_index_length)) + sv_selected[:, (result + 1) % 2, :] = 0 + sv_selected = sv_selected.flatten() + + sqrt_probability = np.linalg.norm(sv_selected) + if sqrt_probability < 1.e-14: + raise ValueError(f"Probability of desired measurement={0} for qubit={qubit} is zero.") + + sv_selected = sv_selected/sqrt_probability # casting issue if inplace for probability 1 + + return sv_selected, sqrt_probability**2 + + class Backend(abc.ABC): def __init__(self, n_shots=None, noise_model=None): @@ -213,16 +255,16 @@ def simulate(self, source_circuit, return_statevector=False, initial_statevector if desired_meas_result is not None: if not isinstance(desired_meas_result, str) or len(desired_meas_result) != n_meas: - raise ValueError("desired_meas result is not a string with the same length as the number of measurements" + raise ValueError("desired_meas result is not a string with the same length as the number of measurements " "in the circuit.") save_mid_circuit_meas = True elif save_mid_circuit_meas and return_statevector: if self.n_shots != 1: - raise ValueError("The combination of save_mid_circuit_meas and return_statevector without specifying desired_meas_result" - "is only valid for self.n_shots=1 as the result is a mixed state otherwise, " + raise ValueError("The combination of save_mid_circuit_meas and return_statevector without specifying desired_meas_result " + "is only valid for self.n_shots=1. The result is a mixed state otherwise, " f"but you requested n_shots={self.n_shots}.") elif source_circuit.is_mixed_state and not self.n_shots: - raise ValueError("Circuit contains MEASURE instruction, and is assumed to prepare a mixed state." + raise ValueError("Circuit contains MEASURE instruction, and is assumed to prepare a mixed state. " "Please set the n_shots attribute to an appropriate value.") if source_circuit.width == 0: @@ -271,8 +313,7 @@ def get_expectation_value(self, qubit_operator, state_prep_circuit, initial_stat actual QPU. Args: - qubit_operator (openfermion-style QubitOperator class): a qubit - operator. + qubit_operator (QubitOperator): the qubit operator. state_prep_circuit (Circuit): an abstract circuit used for state preparation. initial_statevector (array): The initial statevector for the simulation desired_meas_result (str): The mid-circuit measurement results to select for. @@ -297,7 +338,7 @@ def get_expectation_value(self, qubit_operator, state_prep_circuit, initial_stat # If the underlying operator is hermitian, expectation value is real and can be computed right away if are_coefficients_real: if self._noise_model or not self.statevector_available \ - or state_prep_circuit.is_mixed_state or state_prep_circuit.size == 0: + or (state_prep_circuit.is_mixed_state and self.n_shots is not None) or state_prep_circuit.size == 0: return self._get_expectation_value_from_frequencies(qubit_operator, state_prep_circuit, initial_statevector=initial_statevector, @@ -305,7 +346,8 @@ def get_expectation_value(self, qubit_operator, state_prep_circuit, initial_stat elif self.statevector_available: return self._get_expectation_value_from_statevector(qubit_operator, state_prep_circuit, - initial_statevector=initial_statevector) + initial_statevector=initial_statevector, + desired_meas_result=desired_meas_result) # Else, separate the operator into 2 hermitian operators, use linearity and call this function twice else: @@ -332,8 +374,7 @@ def get_variance(self, qubit_operator, state_prep_circuit, initial_statevector=N actual QPU. Args: - qubit_operator (openfermion-style QubitOperator class): a qubit - operator. + qubit_operator (QubitOperator): the qubit operator. state_prep_circuit (Circuit): an abstract circuit used for state preparation. initial_statevector (list/array) : A valid statevector in the format supported by the target backend. @@ -387,8 +428,7 @@ def get_standard_error(self, qubit_operator, state_prep_circuit, initial_stateve actual QPU. Args: - qubit_operator (openfermion-style QubitOperator class): a qubit - operator. + qubit_operator (QubitOperator): the qubit operator. state_prep_circuit (Circuit): an abstract circuit used for state preparation. initial_statevector (list/array): A valid statevector in the format supported by the target backend. @@ -401,14 +441,14 @@ def get_standard_error(self, qubit_operator, state_prep_circuit, initial_stateve variance = self.get_variance(qubit_operator, state_prep_circuit, initial_statevector, desired_meas_result=desired_meas_result) return np.sqrt(variance/self.n_shots) if self.n_shots else 0. - def _get_expectation_value_from_statevector(self, qubit_operator, state_prep_circuit, initial_statevector=None): + def _get_expectation_value_from_statevector(self, qubit_operator, state_prep_circuit, initial_statevector=None, desired_meas_result=None): r"""Take as input a qubit operator H and a state preparation returning a ket |\psi>. Return the expectation value <\psi | H | \psi>, computed without drawing samples (statevector only). Users should not be calling this function directly, please call "get_expectation_value" instead. Args: - qubit_operator (openfermion-style QubitOperator class): a qubit operator. + qubit_operator (QubitOperator): the qubit operator. state_prep_circuit (Circuit): an abstract circuit used for state preparation (only pure states). initial_statevector (array): The initial state of the system @@ -416,10 +456,12 @@ def _get_expectation_value_from_statevector(self, qubit_operator, state_prep_cir complex: The expectation value of this operator with regards to the state preparation. """ + n_qubits = state_prep_circuit.width expectation_value = 0. - prepared_frequencies, prepared_state = self.simulate(state_prep_circuit, return_statevector=True, initial_statevector=initial_statevector) + prepared_frequencies, prepared_state = self.simulate(state_prep_circuit, return_statevector=True, + initial_statevector=initial_statevector, desired_meas_result=desired_meas_result) if hasattr(self, "expectation_value_from_prepared_state"): return self.expectation_value_from_prepared_state(qubit_operator, n_qubits, prepared_state) @@ -459,7 +501,7 @@ def _get_expectation_value_from_frequencies(self, qubit_operator, state_prep_cir using the frequencies of observable states. Args: - qubit_operator (openfermion-style QubitOperator class): a qubitoperator. + qubit_operator (QubitOperator): the qubit operator. state_prep_circuit (Circuit): an abstract circuit used for state preparation. initial_statevector (array): The initial state of the system desired_meas_result (str): The mid-circuit measurement results to select for. @@ -507,7 +549,7 @@ def _get_variance_from_frequencies(self, qubit_operator, state_prep_circuit, ini using the frequencies of observable states. Args: - qubit_operator (openfermion-style QubitOperator class): a qubit operator. + qubit_operator (QubitOperator): the qubit operator. state_prep_circuit (Circuit): an abstract circuit used for state preparation. initial_statevector (list/array) : A valid statevector in the format supported by the target backend. @@ -555,8 +597,7 @@ def get_expectation_value_from_frequencies_oneterm(term, frequencies): the result of a state-preparation. Args: - term (openfermion-style QubitOperator object): a qubit operator, with - only a single term. + term (QubitOperator): a single-term qubit operator frequencies (dict): histogram of frequencies of measurements (assumed to be in lsq-first format). @@ -573,8 +614,7 @@ def get_variance_from_frequencies_oneterm(term, frequencies): the result of a state-preparation. Args: - term (openfermion-style QubitOperator object): a qubit operator, with - only a single term. + term (QubitOperator): a single-term qubit operator. frequencies (dict): histogram of frequencies of measurements (assumed to be in lsq-first format). @@ -651,6 +691,21 @@ def _int_to_binstr(self, i, n_qubits, use_ordering=True): return state_binstr if use_ordering and (self.statevector_order == "lsq_first") else state_binstr[::-1] + def collapse_statevector_to_desired_measurement(self, statevector, qubit, result): + """Take 0 or 1 part of a statevector for a given qubit and return a normalized statevector and probability. + + Args: + statevector (array): The statevector for which the collapse to the desired qubit value is performed. + qubit (int): The index of the qubit to collapse to the classical result. + result (string): "0" or "1". + + Returns: + array: the collapsed and renormalized statevector + float: the probability this occured + """ + + return collapse_statevector_to_desired_measurement(statevector, qubit, result, self.backend_info()['statevector_order']) + @staticmethod @abc.abstractmethod def backend_info() -> dict: diff --git a/tangelo/linq/target/target_cirq.py b/tangelo/linq/target/target_cirq.py index d17dda2de..04813324c 100644 --- a/tangelo/linq/target/target_cirq.py +++ b/tangelo/linq/target/target_cirq.py @@ -16,7 +16,7 @@ import numpy as np -from tangelo.linq import Circuit +from tangelo.linq import Circuit, get_unitary_circuit_pieces from tangelo.linq.target.backend import Backend from tangelo.linq.translator import translate_circuit as translate_c from tangelo.linq.translator import translate_operator @@ -77,7 +77,7 @@ def simulate_circuit(self, source_circuit: Circuit, return_statevector=False, in cirq_simulator = self.cirq.Simulator(dtype=np.complex128) # If requested, set initial state - cirq_initial_statevector = initial_statevector if initial_statevector is not None else 0 + cirq_initial_statevector = np.asarray(initial_statevector, dtype=complex) if initial_statevector is not None else 0 # Calculate final density matrix and sample from that for noisy simulation or simulating mixed states if (self._noise_model or source_circuit.is_mixed_state) and not save_mid_circuit_meas: @@ -108,17 +108,49 @@ def simulate_circuit(self, source_circuit: Circuit, return_statevector=False, in self.all_frequencies = {k: v / self.n_shots for k, v in samples.items()} frequencies = self.all_frequencies - # Run shot by shot and keep track of desired_meas_result only (generally slower) + # Run shot by shot and keep track of desired_meas_result only if n_shots is set + # Otherwised, Split circuit into chunks between mid-circuit measurements. Simulate a chunk, collapse the statevector according + # to the desired measurement and simulate the next chunk using this new statevector as input elif desired_meas_result or (save_mid_circuit_meas and return_statevector): - translated_circuit = translate_c(source_circuit, "cirq", output_options={"noise_model": self._noise_model, - "save_measurements": True}) - self._current_state = None - indices = list(range(source_circuit.width)) - n_attempts = 0 + + # desired_meas_result without a noise model + if self.n_shots is None: + success_probability = 1 + if initial_statevector is not None: + sv = cirq_initial_statevector + else: + sv = np.zeros(2**source_circuit.width) + sv[0] = 1 + + unitary_circuits, qubits = get_unitary_circuit_pieces(source_circuit) + + for i, circ in enumerate(unitary_circuits[:-1]): + if circ.size > 0: + translated_circuit = translate_c(circ, "cirq", output_options={"save_measurements": True}) + job_sim = cirq_simulator.simulate(translated_circuit, initial_state=sv) + sv, cprob = self.collapse_statevector_to_desired_measurement(job_sim.final_state_vector, qubits[i], int(desired_meas_result[i])) + else: + sv, cprob = self.collapse_statevector_to_desired_measurement(sv, qubits[i], int(desired_meas_result[i])) + success_probability *= cprob + source_circuit._probabilities[desired_meas_result] = success_probability + + translated_circuit = translate_c(unitary_circuits[-1], "cirq", output_options={"save_measurements": True}) + job_sim = cirq_simulator.simulate(translated_circuit, initial_state=sv) + self._current_state = job_sim.final_state_vector + # Either desired_meas_result with noise_model. Or 1 shot save_mid_circuit_meas + else: + translated_circuit = translate_c(source_circuit, "cirq", output_options={"noise_model": self._noise_model, + "save_measurements": True}) + self._current_state = None + indices = list(range(source_circuit.width)) # Permit 0.1% probability events + n_attempts = 0 max_attempts = 1000 if self.n_shots is None else 1000*self.n_shots + # Use density matrix simulator until success if noise_model. + # TODO: implement collapse operations for density matrix simulation. + # Loop also used for 1 shot if no desired_meas_result and save_mid_circuit_meas. while self._current_state is None and n_attempts < max_attempts: job_sim = cirq_simulator.simulate(translated_circuit, initial_state=cirq_initial_statevector) measure = "".join([str(job_sim.measurements[str(i)][0]) for i in range(n_meas)]) diff --git a/tangelo/linq/target/target_qiskit.py b/tangelo/linq/target/target_qiskit.py index 67af58fa1..c90dea50c 100644 --- a/tangelo/linq/target/target_qiskit.py +++ b/tangelo/linq/target/target_qiskit.py @@ -17,7 +17,7 @@ import numpy as np -from tangelo.linq import Circuit +from tangelo.linq import Circuit, get_unitary_circuit_pieces from tangelo.linq.target.backend import Backend from tangelo.linq.translator import translate_circuit as translate_c from tangelo.linq.noisy_simulation.noise_models import get_qiskit_noise_model @@ -76,26 +76,36 @@ def aer_backend_with_statevector(translated_circuit): n_meas = source_circuit.counts.get("MEASURE", 0) qiskit_noise_model = get_qiskit_noise_model(self._noise_model) if self._noise_model else None + def load_statevector(translated_circuit, initial_statevector): + "Load statevector into translated_circuit" + n_qubits = int(math.log2(len(initial_statevector))) + n_meas = source_circuit.counts.get("MEASURE", 0) + n_registers = n_meas + source_circuit.width if save_mid_circuit_meas else source_circuit.width + initial_state_circuit = self.qiskit.QuantumCircuit(n_qubits, n_registers) + initial_state_circuit.initialize(initial_statevector, list(range(n_qubits))) + translated_circuit = initial_state_circuit.compose(translated_circuit) + return translated_circuit + def run_and_measure_one_shot(backend, translated_circuit): "Return statevector and mid-circuit measurement for one shot" sim_results = backend.run(translated_circuit, noise_model=qiskit_noise_model, shots=1).result() current_state = sim_results.get_statevector(translated_circuit) - measure = next(iter(self.qiskit.result.marginal_counts(sim_results, indices=list(range(n_meas))).get_counts()))[::-1] + measure = next(iter(self.qiskit.result.marginal_counts(sim_results, indices=list(range(n_meas)), inplace=True).get_counts()))[::-1] return current_state, measure - translated_circuit = translate_c(source_circuit, "qiskit", output_options={"save_measurements": save_mid_circuit_meas}) + if desired_meas_result is not None and not self._noise_model: + # Split circuit into chunks between mid-circuit measurements. Simulate a chunk, collapse the statevector according + # to the desired measurement and simulate the next chunk using this new statevector as input + unitary_circuits, qubits = get_unitary_circuit_pieces(source_circuit) + else: + translated_circuit = translate_c(source_circuit, "qiskit", output_options={"save_measurements": save_mid_circuit_meas}) - # If requested, set initial state - if initial_statevector is not None: - if self._noise_model: - raise ValueError("Cannot load an initial state if using a noise model, with Qiskit") - else: - n_qubits = int(math.log2(len(initial_statevector))) - n_meas = source_circuit.counts.get("MEASURE", 0) - n_registers = n_meas + source_circuit.width if save_mid_circuit_meas else source_circuit.width - initial_state_circuit = self.qiskit.QuantumCircuit(n_qubits, n_registers) - initial_state_circuit.initialize(initial_statevector, list(range(n_qubits))) - translated_circuit = initial_state_circuit.compose(translated_circuit) + # If requested, set initial state + if initial_statevector is not None: + if self._noise_model: + raise ValueError("Cannot load an initial state if using a noise model, with Qiskit") + else: + translated_circuit = load_statevector(translated_circuit, initial_statevector) # Noiseless simulation using the statevector simulator if not self._noise_model and not source_circuit.is_mixed_state: @@ -139,31 +149,47 @@ def run_and_measure_one_shot(backend, translated_circuit): frequencies = self.all_frequencies # desired_meas_result without a noise model + # Split circuit into chunks between mid-circuit measurements. Simulate a chunk, collapse the statevector according + # to the desired measurement and simulate the next chunk using this new statevector as input elif desired_meas_result is not None: - backend, translated_circuit = aer_backend_with_statevector(translated_circuit) - - samples = dict() - self._current_state = None - n_attempts = 0 - # Permit 0.1% probability events - max_attempts = 1000 - - while self._current_state is None and n_attempts < max_attempts: - current_state, measure = run_and_measure_one_shot(backend, translated_circuit) + success_probability = 1 - if measure == desired_meas_result: - self._current_state = current_state - if self.n_shots is not None: - self.all_frequencies = {measure + state[::-1]: count for state, count in current_state.sample_counts(self.n_shots).items()} - else: - freqs = self._statevector_to_frequencies(np.array(current_state)) - self.all_frequencies = {measure + meas: val for meas, val in freqs.items()} + if initial_statevector is not None: + sv = np.asarray(initial_statevector, dtype=complex) + else: + sv = np.zeros(2**source_circuit.width) + sv[0] = 1. + + for i, circ in enumerate(unitary_circuits[:-1]): + if circ.size != 0: + translated_circuit = translate_c(circ, "qiskit", output_options={"save_measurements": False}) + translated_circuit = load_statevector(translated_circuit, sv) + backend, translated_circuit = aer_backend_with_statevector(translated_circuit) + sim_results = backend.run(translated_circuit).result() + current_state = sim_results.get_statevector(translated_circuit) + sv, cprob = self.collapse_statevector_to_desired_measurement(np.asarray(current_state), qubits[i], + int(desired_meas_result[i])) + else: + sv, cprob = self.collapse_statevector_to_desired_measurement(sv, qubits[i], + int(desired_meas_result[i])) + + success_probability *= cprob + + translated_circuit = translate_c(unitary_circuits[-1], "qiskit", output_options={"save_measurements": False}) + translated_circuit = load_statevector(translated_circuit, sv) + backend, translated_circuit = aer_backend_with_statevector(translated_circuit) + sim_results = backend.run(translated_circuit).result() + current_state = sim_results.get_statevector(translated_circuit) + self._current_state = np.asarray(current_state) - n_attempts += 1 + source_circuit._probabilities[desired_meas_result] = success_probability - if n_attempts == max_attempts: - raise ValueError(f"desired_meas_result was not measured after {n_attempts} attempts") + if self.n_shots is not None: + self.all_frequencies = {desired_meas_result + state[::-1]: count for state, count in current_state.sample_counts(self.n_shots).items()} + else: + freqs = self._statevector_to_frequencies(np.array(current_state)) + self.all_frequencies = {desired_meas_result + meas: val for meas, val in freqs.items()} frequencies = self.all_frequencies.copy() diff --git a/tangelo/linq/target/target_qulacs.py b/tangelo/linq/target/target_qulacs.py index 1e983cab7..7d5d6c1c7 100644 --- a/tangelo/linq/target/target_qulacs.py +++ b/tangelo/linq/target/target_qulacs.py @@ -18,7 +18,7 @@ import numpy as np -from tangelo.linq import Circuit +from tangelo.linq import Circuit, get_unitary_circuit_pieces from tangelo.linq.target.backend import Backend from tangelo.linq.translator import translate_circuit as translate_c from tangelo.linq.translator import translate_operator @@ -64,8 +64,9 @@ def simulate_circuit(self, source_circuit: Circuit, return_statevector=False, in numpy.array: The statevector, if available for the target backend and requested by the user (if not, set to None). """ - translated_circuit = translate_c(source_circuit, "qulacs", output_options={"noise_model": self._noise_model, - "save_measurements": save_mid_circuit_meas}) + if desired_meas_result is None: + translated_circuit = translate_c(source_circuit, "qulacs", output_options={"noise_model": self._noise_model, + "save_measurements": save_mid_circuit_meas}) n_meas = source_circuit.counts.get("MEASURE", 0) @@ -92,47 +93,70 @@ def simulate_circuit(self, source_circuit: Circuit, return_statevector=False, in frequencies = self._statevector_to_frequencies(python_statevector) return (frequencies, python_statevector) if return_statevector else (frequencies, None) - # If a desired_meas_result, repeat until success if no noise model or repeat until n_shots desired_meas_results. + # If a desired_meas_result, + # If no noise model, Split circuit into chunks between mid-circuit measurements. Simulate a chunk, collapse the statevector according + # to the desired measurement and simulate the next chunk using this new statevector as input + # If noise_model, repeat until n_shots desired_meas_results. elif desired_meas_result is not None: - self._current_state = None - n_attempts = 0 - n_success = 0 - n_shots = self.n_shots if self.n_shots is not None else -1 - - # Permit 0.1% probability events - max_attempts = 1000*abs(n_shots) - samples = dict() - while self._current_state is None and n_attempts < max_attempts and n_success != n_shots: + if not self._noise_model: + success_probability = 1 + unitary_circuits, qubits = get_unitary_circuit_pieces(source_circuit) + + for i, circ in enumerate(unitary_circuits[:-1]): + if circ.size > 0: + translated_circuit = translate_c(circ, "qulacs", output_options={"save_measurements": True}) + translated_circuit.update_quantum_state(state) + sv, cprob = self.collapse_statevector_to_desired_measurement(state.get_vector(), qubits[i], int(desired_meas_result[i])) + success_probability *= cprob + state.load(sv) + + translated_circuit = translate_c(unitary_circuits[-1], "qulacs", output_options={"save_measurements": True}) translated_circuit.update_quantum_state(state) - measure = "".join([str(state.get_classical_value(i)) for i in range(n_meas)]) - - if measure == desired_meas_result: - python_statevector = state.get_vector() - if self._noise_model: - n_success += 1 - sample = state.sampling(1)[0] - samples[sample] = samples.get(sample, 0) + 1 - else: - self._current_state = python_statevector - if self.n_shots is not None: - samples = Counter(state.sampling(self.n_shots)) - else: - frequencies = self._statevector_to_frequencies(python_statevector) - self.all_frequencies = dict() - for meas, val in frequencies.items(): - self.all_frequencies[measure + meas] = val + python_statevector = state.get_vector() + self._current_state = python_statevector + source_circuit._probabilities[desired_meas_result] = success_probability - if initial_statevector is not None: - state.load(initial_statevector) + if self.n_shots is not None: + samples = Counter(state.sampling(self.n_shots)) else: - state.set_zero_state() - n_attempts += 1 + frequencies = self._statevector_to_frequencies(python_statevector) + self.all_frequencies = dict() + for meas, val in frequencies.items(): + self.all_frequencies[desired_meas_result + meas] = val + else: + translated_circuit = translate_c(source_circuit, "qulacs", output_options={"noise_model": self._noise_model, + "save_measurements": save_mid_circuit_meas}) + self._current_state = None + + n_success = 0 + + # Permit 0.1% probability events + n_attempts = 0 + max_attempts = 1000*self.n_shots + samples = dict() + + while self._current_state is None and n_attempts < max_attempts and n_success != self.n_shots: + translated_circuit.update_quantum_state(state) + measure = "".join([str(state.get_classical_value(i)) for i in range(n_meas)]) + + if measure == desired_meas_result: + python_statevector = state.get_vector() + if self._noise_model: + n_success += 1 + sample = state.sampling(1)[0] + samples[sample] = samples.get(sample, 0) + 1 + + if initial_statevector is not None: + state.load(initial_statevector) + else: + state.set_zero_state() + n_attempts += 1 - if n_attempts == max_attempts: - raise ValueError(f"desired_meas_result was not measured after {n_attempts} attempts") + if n_attempts == max_attempts: + raise ValueError(f"desired_meas_result was not measured after {n_attempts} attempts") if self.n_shots is not None: - self.all_frequencies = {measure + self._int_to_binstr(k, source_circuit.width): v / self.n_shots + self.all_frequencies = {desired_meas_result + self._int_to_binstr(k, source_circuit.width): v / self.n_shots for k, v in samples.items()} return (self.all_frequencies, python_statevector) if return_statevector else (self.all_frequencies, None) diff --git a/tangelo/linq/target/target_stim.py b/tangelo/linq/target/target_stim.py new file mode 100644 index 000000000..6ca79c7ad --- /dev/null +++ b/tangelo/linq/target/target_stim.py @@ -0,0 +1,147 @@ +# Copyright 2023 Good Chemistry Company. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""The stim simulation package is made for fast simulation of Clifford circuits. +See https://arxiv.org/abs/2103.02202v3 for more details. +""" + +from collections import Counter + +import numpy as np + +from tangelo.linq import Circuit +from tangelo.linq.target.backend import Backend +from tangelo.linq.translator import translate_circuit as translate_c +from tangelo.linq.translator.translate_stim import translate_tableau +from tangelo.linq.helpers.circuits.measurement_basis import pauli_of_to_string, measurement_basis_gates + + +class StimSimulator(Backend): + """Interface to the stim simulator""" + def __init__(self, n_shots=None, noise_model=None): + import stim + super().__init__(n_shots=n_shots, noise_model=noise_model) + self.stim = stim + + def simulate_circuit(self, source_circuit: Circuit, return_statevector=False, initial_statevector=None, desired_meas_result=None): + """Perform state preparation corresponding to the input circuit on the + target backend, return the frequencies of the different observables, and + either the statevector or None depending on if return_statevector is set to True. + The initial_statevector, and desired_meas_result features are currently not implemented + and will return and error if not None. + + Args: + source_circuit (Circuit): a circuit in the abstract format to be translated + for the target backend. + return_statevector (bool): option to return the statevector + initial_statevector (list/array) : Not currently implemented, will raise an error + desired_meas_result (str) : Not currently implemented, will raise an error + + Returns: + dict: A dictionary mapping multi-qubit states to their corresponding + frequency. + numpy.array: The statevector, if available for the target backend + and requested by the user (if not, set to None). + """ + if initial_statevector is not None: + raise NotImplementedError("initial_statevector not yet implemented with stim ") + if desired_meas_result is not None: + raise NotImplementedError("desired_meas_result not yet implemented with stim ") + + if self.n_shots or self._noise_model: + translated_circuit = translate_c(source_circuit, "stim", + output_options={"noise_model": self._noise_model}) + for qubit in range(source_circuit.width): + translated_circuit.append("M", [qubit]) + isamples = translated_circuit.compile_sampler().sample(self.n_shots) + samples = [''.join([str(int(q))for q in isamples[i]]) for i in range(self.n_shots)] + frequencies = {k: v / self.n_shots for k, v in Counter(samples).items()} + else: + self._current_state = translate_tableau(source_circuit).state_vector() + frequencies = self._statevector_to_frequencies(self._current_state) + + return (frequencies, np.array(self._current_state)) if return_statevector else (frequencies, None) + + def _get_expectation_value_from_statevector(self, qubit_operator, state_prep_circuit, initial_statevector=None, desired_meas_result=None): + """ + Calculates the expectation value of a qubit operator using a TableauSimulator. + + Args: + qubit_operator (QubitOperator): The qubit operator for which the expectation value is calculated. + state_prep_circuit (Circuit): The stabilizer circuit used to prepare the quantum state. + initial_statevector (list/array) : Not currently implemented, will raise an error + desired_meas_result (str) : Not currently implemented, will raise an error + + Returns: + float: The real-valued expectation value of the qubit operator. + """ + n_qubits = state_prep_circuit.width + + if initial_statevector is not None: + raise NotImplementedError("initial_statevector not yet implemented with stim ") + if desired_meas_result is not None: + raise NotImplementedError("desired_meas_result not yet implemented with stim ") + + if self.n_shots: + return self._get_expectation_value_from_frequencies(qubit_operator, state_prep_circuit) + + else: + s = translate_tableau(state_prep_circuit) + paulisum = 0 + for term, coef in qubit_operator.terms.items(): + if len(term) > n_qubits: # Cannot have a qubit index beyond circuit size + raise ValueError(f"Size of term in qubit operator beyond number of qubits in circuit ({n_qubits}).\n Term = {term}") + elif not term: # Empty term: no simulation needed + paulisum += coef + continue + paulisum += coef * s.peek_observable_expectation(self.stim.PauliString(pauli_of_to_string(term, n_qubits))) + return np.real(paulisum) + + def _get_expectation_value_from_frequencies(self, qubit_operator, state_prep_circuit, initial_statevector=None, desired_meas_result=None): + """Take as input a qubit operator H and a state preparation returning a ket |\psi>. + Return the expectation value <\psi | H | \psi> computed using the frequencies of observable states. + + Args: + qubit_operator (QubitOperator): the qubit operator. + state_prep_circuit (Circuit): an abstract circuit used for state preparation. + initial_statevector (list/array) : Not currently implemented, will raise an error + desired_meas_result (str) : Not currently implemented, will raise an error + + Returns: + complex: The expectation value of this operator with regard to the + state preparation. + """ + if initial_statevector is not None: + raise NotImplementedError("initial_statevector not yet implemented with stim ") + if desired_meas_result is not None: + raise NotImplementedError("desired_meas_result not yet implemented with stim ") + + n_qubits = state_prep_circuit.width + expectation_value = 0. + for term, coef in qubit_operator.terms.items(): + if len(term) > n_qubits: + raise ValueError(f"Size of term in qubit operator beyond number of qubits in circuit ({n_qubits}).\n Term = {term}") + elif not term: # Empty term: no simulation needed + expectation_value += coef + continue + basis_circuit = Circuit(measurement_basis_gates(term)) + full_circuit = state_prep_circuit + basis_circuit if (basis_circuit.size > 0) else state_prep_circuit + frequencies, _ = self.simulate(full_circuit) + expectation_term = self.get_expectation_value_from_frequencies_oneterm(term, frequencies) + expectation_value += coef * expectation_term + return expectation_value + + @staticmethod + def backend_info(): + return {"statevector_available": True, "statevector_order": "msq_first", "noisy_simulation": True} diff --git a/tangelo/linq/target/target_sympy.py b/tangelo/linq/target/target_sympy.py new file mode 100644 index 000000000..96587645f --- /dev/null +++ b/tangelo/linq/target/target_sympy.py @@ -0,0 +1,115 @@ +# Copyright 2023 Good Chemistry Company. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import numpy as np + +from tangelo.linq import Circuit +from tangelo.linq.target.backend import Backend +from tangelo.linq.translator import translate_circuit as translate_c +from tangelo.linq.translator import translate_operator + + +class SympySimulator(Backend): + + def __init__(self, n_shots=None, noise_model=None): + super().__init__(n_shots, noise_model) + + def simulate_circuit(self, source_circuit: Circuit, return_statevector=False, initial_statevector=None): + """This simulator manipulates symbolic expressions, i.e. gates can have + unspecified parameters (strings interpreted as variables). As with the + other simulators, it performs state preparation corresponding to the + input circuit, returns the frequencies of the different observables, and + either the statevector or None depending on if return_statevector is set + to True. + + Args: + source_circuit (Circuit): A circuit in the abstract format to be + translated for the target backend. + return_statevector (bool): Option to return the statevector as well, + if available. + initial_statevector (array/matrix or sympy.physics.quantum.Qubit): A + valid statevector in the format supported by the target backend. + + Returns: + dict: A dictionary mapping multi-qubit states to their corresponding + frequency. + sympy.Matrix: The symbolic statevector, if requested + by the user (if not, set to None). + """ + + from sympy import simplify + from sympy.physics.quantum import qapply + from sympy.physics.quantum.qubit import Qubit, matrix_to_qubit, \ + qubit_to_matrix, measure_all + + translated_circuit = translate_c(source_circuit, "sympy") + + # Transform the initial_statevector if it is provided. + if initial_statevector is None: + python_statevector = Qubit("0"*(source_circuit.width)) + elif isinstance(initial_statevector, Qubit): + python_statevector = initial_statevector + elif isinstance(initial_statevector, (np.ndarray, np.matrix)): + python_statevector = matrix_to_qubit(initial_statevector) + else: + raise ValueError(f"The {type(initial_statevector)} type for initial_statevector is not supported.") + + # Deterministic circuit, run once. + state = qapply(translated_circuit * python_statevector) + self._current_state = state + python_statevector = qubit_to_matrix(state) + + measurements = measure_all(state) + + frequencies = dict() + for vec, prob in measurements: + prob = simplify(prob, tolerance=1e-4).evalf() + if not prob.is_zero: + bistring = "".join(str(bit) for bit in reversed(vec.qubit_values)) + frequencies[bistring] = prob + + return (frequencies, python_statevector) if return_statevector else (frequencies, None) + + def expectation_value_from_prepared_state(self, qubit_operator, n_qubits, prepared_state=None): + """Compute an expectation value using a representation of the state + using sympy functionalities. + + Args: + qubit_operator (QubitOperator): a qubit operator in tangelo format + n_qubits (int): Number of qubits. + prepared_state (array/matrix or sympy.physics.quantum.Qubit): A + numpy or a sympy object representing the state. Internally, a + numpy object is transformed into the sympy representation. + Default is None, in this case it is set to the current state in + the simulator object. + + Returns: + sympy.core.add.Add: Eigenvalue represented as a symbolic sum. + """ + + from sympy import simplify, cos + from sympy.physics.quantum import Dagger + + prepared_state = self._current_state if prepared_state is None else prepared_state + operator = translate_operator(qubit_operator, source="tangelo", target="sympy", n_qubits=n_qubits) + + eigenvalue = Dagger(prepared_state) * operator * prepared_state + eigenvalue = eigenvalue[0, 0].rewrite(cos) + eigenvalue = simplify(eigenvalue).evalf() + + return eigenvalue + + @staticmethod + def backend_info(): + return {"statevector_available": True, "statevector_order": "lsq_first", "noisy_simulation": False} diff --git a/tangelo/linq/tests/test_braket_connection.py b/tangelo/linq/tests/test_braket_connection.py new file mode 100644 index 000000000..fa256317e --- /dev/null +++ b/tangelo/linq/tests/test_braket_connection.py @@ -0,0 +1,86 @@ +""" + A test class to check that features related to Braket SDK for quantum experiments are behaving as expected. + Tests requiring actual interactions with the services are not run automatically. +""" + +import os +import time +import unittest + +from tangelo.linq import Gate, Circuit +from tangelo.linq.qpu_connection.braket_connection import BraketConnection +from tangelo.helpers.utils import assert_freq_dict_almost_equal + +# Circuit and qubit operator for test, with reference values +circ = Circuit([Gate("H", 0), Gate("X", 1)]) +ref = {'01': 0.5, '11': 0.5} + +# Set sv1 device arn for tests +sv1_arn = "arn:aws:braket:::device/quantum-simulator/amazon/sv1" + +# Set up S3 bucket for tests +s3_bucket = "amazon-braket-gc-quantum-dev" # bucket name +folder = "tangelo_test" # destination folder + +# Set up credentials [clear before pushing to public repo] +os.environ["AWS_REGION"] = "" +os.environ["AWS_ACCESS_KEY_ID"] = "" +os.environ["AWS_SECRET_ACCESS_KEY"] = "" +os.environ["AWS_SESSION_TOKEN"] = "" + + +@unittest.skip("Manual testing only to avoid uploading login info.") +class TestBraketConnection(unittest.TestCase): + + def test_init(self): + """ Attempt to instantiate connection """ + BraketConnection() + + def test_submit_job(self): + """ Submit a simple job to a simulator backend, query status and retrieve results """ + + conn = BraketConnection(s3_bucket=s3_bucket, folder=folder) + + job_id = conn.job_submit(sv1_arn, 100000, circ) + print(conn.job_status(job_id)) + + freqs = conn.job_results(job_id) + print(conn.job_status(job_id)) + + assert_freq_dict_almost_equal(freqs, ref, 1e-2) + + def test_submit_batch_job(self): + """ Submit a batch job (several circuits) to a simulator backend. """ + + conn = BraketConnection(s3_bucket=s3_bucket, folder=folder) + + # Retrieve list of job_ids, check they are logged in "jobs" attribute + job_ids = conn.job_submit(sv1_arn, 100, [circ]*2) + print(conn.jobs) + + # Ensure individual jobs can be accessed as usual + for job_id in job_ids: + conn.job_results(job_id) + conn.job_status(job_id) + + def test_cancel_job(self): + """ Submit a job to a valid backend, attempt to cancel """ + + conn = BraketConnection(s3_bucket=s3_bucket, folder=folder) + + for sleep_time in [0., 20.]: + job_id = conn.job_submit(sv1_arn, 100000, circ) + time.sleep(sleep_time) + conn.job_cancel(job_id) + print(conn.job_status(job_id)) + print(conn.job_results(job_id)) + + def test_get_backend_info(self): + """ Return backend info of supported providers """ + + conn = BraketConnection() + print(conn.get_backend_info()) + + +if __name__ == "__main__": + unittest.main() diff --git a/tangelo/linq/tests/test_circuits.py b/tangelo/linq/tests/test_circuits.py index 233d72a95..dd2e7b4b7 100644 --- a/tangelo/linq/tests/test_circuits.py +++ b/tangelo/linq/tests/test_circuits.py @@ -22,7 +22,7 @@ from math import pi from collections import Counter -from tangelo.linq import Gate, Circuit, stack +from tangelo.linq import Gate, Circuit, stack, get_unitary_circuit_pieces # Create several abstract circuits with different features mygates = [Gate("H", 2), Gate("CNOT", 1, control=0), Gate("CNOT", 2, control=1), @@ -301,6 +301,27 @@ def test_iterate(self): self.assertEqual([g for g in circuit3], circuit3._gates) self.assertEqual([g for g in circuit4], circuit4._gates) + def test_unitary_pieces(self): + """ Test the splitting of circuit into unitary pieces between measurements.""" + test_circuit = Circuit([Gate("H", 0), Gate("H", 1), Gate("MEASURE", 0), + Gate("X", 1), Gate("MEASURE", 1), + Gate("H", 0), Gate("H", 1), Gate("CNOT", control=0, target=1), Gate("MEASURE", 0), + Gate("MEASURE", 1), + Gate("H", 0), Gate("MEASURE", 1)]) + circuits = [Circuit([Gate("H", 0), Gate("H", 1)], n_qubits=2), + Circuit([Gate("X", 1)], n_qubits=2), + Circuit([Gate("H", 0), Gate("H", 1), Gate("CNOT", control=0, target=1)], n_qubits=2), + Circuit(n_qubits=2), + Circuit([Gate("H", 0)], n_qubits=2), + Circuit(n_qubits=2)] + measure_qs = [0, 1, 0, 1, 1] + split_circuits, qubits_to_measure = get_unitary_circuit_pieces(test_circuit) + + for i, circ in enumerate(circuits[:-1]): + self.assertEqual(circ, split_circuits[i]) + self.assertEqual(measure_qs[i], qubits_to_measure[i]) + self.assertEqual(circuits[-1], split_circuits[-1]) + if __name__ == "__main__": unittest.main() diff --git a/tangelo/linq/tests/test_gates.py b/tangelo/linq/tests/test_gates.py index 41c10c6cc..d1ed44013 100644 --- a/tangelo/linq/tests/test_gates.py +++ b/tangelo/linq/tests/test_gates.py @@ -85,6 +85,28 @@ def test_some_gates_inverse(self): with self.assertRaises(AttributeError): RZ_gate.inverse() + def test_is_clifford(self): + """ Test that some basic gates are correctly identified as Clifford or non Clifford""" + + # test single qubit Clifford gates + for name in {"H", "S", "X", "Z", "Y", "SDAG"}: + self.assertEqual(True, Gate(name, 0).is_clifford()) + + # test two qubit Clifford gates + for name in {"CNOT", "CX", "CY", "CZ"}: + self.assertEqual(True, Gate(name, target=0, control=1).is_clifford()) + + # test parameterized single qubit gates at Clifford and non Clifford parameters + for name in {"RX", "RY", "RZ", "PHASE"}: + for clifford_point in [0, np.pi/2, np.pi, -np.pi/2, 4*np.pi]: + self.assertEqual(True, Gate(name, 0, parameter=clifford_point).is_clifford()) + for non_clifford_point in [0.1, -np.pi/3, 5*np.pi/4, 1.5]: + self.assertEqual(False, Gate(name, 0, parameter=non_clifford_point).is_clifford()) + + # test two qubit non Clifford gates + for name in {"CRX", "CRY", "CRZ", "CPHASE"}: + self.assertEqual(False, Gate(name, target=0, control=1).is_clifford()) + def test_incorrect_gate(self): """ Test to catch a gate with inputs that do not make sense """ @@ -126,6 +148,17 @@ def test_too_many_qubits_on_gates(self): # Try to create a XX gate acting on qubits 0, 1 and 2. self.assertRaises(ValueError, Gate, "XX", target=[0, 1, 2]) + def test_non_hermitian_gates_inverse(self): + """ Test that non-hermitian gates (S, T) can be inversed.""" + + S_gate = Gate("S", 0) + S_gate_inverse = Gate("PHASE", 0, parameter=-np.pi/2) + self.assertEqual(S_gate.inverse(), S_gate_inverse) + + T_gate = Gate("T", 0) + T_gate_inverse = Gate("PHASE", 0, parameter=-np.pi/4) + self.assertEqual(T_gate.inverse(), T_gate_inverse) + if __name__ == "__main__": unittest.main() diff --git a/tangelo/linq/tests/test_ibm_connection.py b/tangelo/linq/tests/test_ibm_connection.py index fdbadf07f..bb678500b 100644 --- a/tangelo/linq/tests/test_ibm_connection.py +++ b/tangelo/linq/tests/test_ibm_connection.py @@ -11,7 +11,7 @@ from tangelo.helpers.utils import assert_freq_dict_almost_equal from tangelo.linq import Gate, Circuit, get_backend -from tangelo.linq.qpu_connection import IBMConnection +from tangelo.linq.qpu_connection.ibm_connection import IBMConnection from tangelo.toolboxes.operators import QubitOperator # Circuit and qubit operator for test diff --git a/tangelo/linq/tests/test_ionq_connection.py b/tangelo/linq/tests/test_ionq_connection.py index dd0784420..b7f512388 100644 --- a/tangelo/linq/tests/test_ionq_connection.py +++ b/tangelo/linq/tests/test_ionq_connection.py @@ -8,7 +8,7 @@ import pprint from tangelo.linq import Gate, Circuit -from tangelo.linq.qpu_connection import IonQConnection +from tangelo.linq.qpu_connection.ionq_connection import IonQConnection from tangelo.helpers.utils import assert_freq_dict_almost_equal circ1 = Circuit([Gate("H", 0), Gate("X", 1)]) diff --git a/tangelo/linq/tests/test_simulator.py b/tangelo/linq/tests/test_simulator.py index 8aaacaab5..1de64abcf 100644 --- a/tangelo/linq/tests/test_simulator.py +++ b/tangelo/linq/tests/test_simulator.py @@ -22,15 +22,15 @@ import time import numpy as np -from openfermion.ops import QubitOperator from openfermion import load_operator, get_sparse_operator +from tangelo.toolboxes.operators import QubitOperator from tangelo.linq import Gate, Circuit, get_backend from tangelo.linq.translator import translate_circuit as translate_c from tangelo.linq.gate import PARAMETERIZED_GATES -from tangelo.helpers.utils import installed_simulator, installed_sv_simulator, installed_backends from tangelo.linq.target.backend import Backend, get_expectation_value_from_frequencies_oneterm -from tangelo.helpers.utils import assert_freq_dict_almost_equal +from tangelo.helpers.utils import installed_simulator, installed_sv_simulator, installed_backends, installed_clifford_simulators, assert_freq_dict_almost_equal + path_data = os.path.dirname(os.path.abspath(__file__)) + '/data' @@ -49,7 +49,7 @@ # Circuit for the parametrized rotation gate Rz. Some convention about the sign of theta or a phase may appear circuit4 = Circuit([Gate("RZ", 0, parameter=2.)], n_qubits=2) -# Circuit that tests all gates that are supported on all simulators +# Circuit that tests all gates that are supported on all general simulators init_gates = [Gate('H', 0), Gate('X', 1), Gate('H', 2)] one_qubit_gate_names = ["H", "X", "Y", "Z", "S", "T", "RX", "RY", "RZ", "PHASE"] one_qubit_gates = [Gate(name, target=0) if name not in PARAMETERIZED_GATES else Gate(name, target=0, parameter=0.5) @@ -62,6 +62,16 @@ swap_gates = [Gate('SWAP', target=[1, 0]), Gate('CSWAP', target=[1, 2], control=0)] circuit5 = Circuit(init_gates + one_qubit_gates + two_qubit_gates + swap_gates) +# Circuit that tests all gates are supported on clifford simulator +one_qubit_gate_names = ["H", "X", "Y", "Z", "S", "RX", "RY", "RZ"] +one_qubit_gates = [Gate(name, target=0) if name not in PARAMETERIZED_GATES else Gate(name, target=0, parameter=-np.pi/2) + for name in one_qubit_gate_names] +one_qubit_gates += [Gate(name, target=1) if name not in PARAMETERIZED_GATES else Gate(name, target=1, parameter=np.pi) + for name in one_qubit_gate_names] +clifford_two_qubit_gate_names = ["CNOT", "CX", "CY", "CZ"] +clifford_two_qubit_gates = [Gate(name, target=1, control=0) for name in clifford_two_qubit_gate_names] +circuit_clifford = Circuit(init_gates + one_qubit_gates + clifford_two_qubit_gates + [Gate('SWAP', target=[1, 0])]) + # Circuit preparing a mixed-state (i.e. containing a MEASURE instruction in the middle of the circuit) circuit_mixed = Circuit([Gate("RX", 0, parameter=2.), Gate("RY", 1, parameter=-1.), Gate("MEASURE", 0), Gate("X", 0)]) circuit_mixed_1 = Circuit([Gate("RX", 0, parameter=2.), Gate("RY", 0, parameter=-1.), Gate("MEASURE", 0), Gate("X", 0)]) @@ -84,8 +94,10 @@ ref_freqs.append({'000': 0.15972060437359714, '100': 0.2828171838599203, '010': 0.03984122195648572, '110': 0.28281718385992016, '001': 0.15972060437359714, '101': 0.017620989809996816, '011': 0.039841221956485706, '111': 0.01762098980999681}) +ref_freqs_clifford = {bs: 0.125 for bs in ['000', '100', '010', '110', '001', '101', '011', '111']} reference_exp_values = np.array([[0., 0., 0.], [0., -1., 0.], [-0.41614684, 0.7651474, -1.6096484], [1., 0., 0.], [-0.20175269, -0.0600213, 1.2972912]]) +clifford_reference_exp_values = np.array([0, 0, 2]) reference_mixed = {'01': 0.163, '11': 0.066, '10': 0.225, '00': 0.545} # With Qiskit noiseless, 1M shots reference_all = {'101': 0.163, '011': 0.066, '010': 0.225, '100': 0.545} reference_mid = {'1': 0.7, '0': 0.3} @@ -95,20 +107,20 @@ class TestSimulateAllBackends(unittest.TestCase): def test_get_exp_value_operator_too_long(self): """ Ensure an error is returned if the qubit operator acts on more qubits than are present in the circuit """ - for b in installed_simulator: + for b in (installed_simulator | installed_clifford_simulators): simulator = get_backend(target=b, n_shots=1) self.assertRaises(ValueError, simulator.get_expectation_value, op4, circuit1) def test_get_exp_value_empty_operator(self): """ If qubit operator is empty, the expectation value is 0 and no computation occurs """ - for b in installed_simulator: + for b in (installed_simulator | installed_clifford_simulators): simulator = get_backend(target=b, n_shots=1) exp_value = simulator.get_expectation_value(QubitOperator(), circuit1) self.assertTrue(exp_value == 0.) def test_get_exp_value_constant_operator(self): """ The expectation of the identity term must be 1. """ - for b in installed_simulator: + for b in (installed_simulator | installed_clifford_simulators): simulator = get_backend(target=b, n_shots=1) const_op = QubitOperator() const_op.terms = {(): 777.} @@ -120,7 +132,7 @@ def test_simulate_mixed_state(self): results = dict() for b in installed_simulator: - sim = get_backend(target=b, n_shots=10 ** 5) + sim = get_backend(target=b, n_shots=10**5) results[b], _ = sim.simulate(circuit_mixed) assert_freq_dict_almost_equal(results[b], reference_mixed, 1e-2) @@ -128,7 +140,7 @@ def test_simulate_mixed_state_save_measures(self): """ Test mid-circuit measurement (mixed-state simulation) for all installed backends.""" results = dict() for b in installed_simulator: - sim = get_backend(target=b, n_shots=10 ** 3) + sim = get_backend(target=b, n_shots=10**3) results[b], _ = sim.simulate(circuit_mixed, save_mid_circuit_meas=True) assert_freq_dict_almost_equal(results[b], reference_mixed, 8e-2) assert_freq_dict_almost_equal(sim.all_frequencies, reference_all, 8e-2) @@ -140,13 +152,13 @@ def test_simulate_mixed_state_desired_state(self): results = dict() exact = {'11': 0.23046888414227926, '10': 0.7695311158577207} for b in installed_simulator: - sim = get_backend(target=b, n_shots=10 ** 3) + sim = get_backend(target=b, n_shots=10**3) results[b], _ = sim.simulate(circuit_mixed, desired_meas_result="0") assert_freq_dict_almost_equal(results[b], exact, 8.e-2) def test_desired_meas_len(self): """ Test if the desired_meas_result parameter is a string and of the right length.""" - sim = get_backend(target="cirq", n_shots=10 ** 3) + sim = get_backend(target="cirq", n_shots=10**3) self.assertRaises(ValueError, sim.simulate, circuit_mixed, desired_meas_result=0) self.assertRaises(ValueError, sim.simulate, circuit_mixed, desired_meas_result="01") @@ -156,7 +168,7 @@ def test_get_exp_value_mixed_state(self): reference = 0.41614683 # Exact value results = dict() for b in installed_simulator: - sim = get_backend(target=b, n_shots=10 ** 5) + sim = get_backend(target=b, n_shots=10**5) results[b] = sim.get_expectation_value(op1, circuit_mixed) np.testing.assert_almost_equal(results[b], reference, decimal=2) @@ -215,6 +227,11 @@ def test_simulate_statevector(self): frequencies, _ = simulator.simulate(circuit) assert_freq_dict_almost_equal(ref_freqs[i], frequencies, atol=1e-5) + for b in installed_clifford_simulators: + simulator = get_backend(target=b) + frequencies, _ = simulator.simulate(circuit_clifford) + assert_freq_dict_almost_equal(ref_freqs_clifford, frequencies, atol=1e-5) + def test_simulate_mixed_state_desired_statevector(self): """ Test mid-circuit measurement (mixed-state simulation) for compatible/testable formats and backends when returning a statevector.""" @@ -231,12 +248,15 @@ def test_simulate_mixed_state_desired_statevector(self): f, sv = sim.simulate(circuit_mixed, desired_meas_result="0", return_statevector=True) np.testing.assert_array_almost_equal(sv, results[b]) assert_freq_dict_almost_equal(f, freqs_exact, 1.e-7) + self.assertAlmostEqual(0.2919265817264289, circuit_mixed.success_probabilities["0"], places=7) # Test that initial_statevector is respected - f, sv = sim.simulate(Circuit([Gate("MEASURE", 0), Gate("MEASURE", 1)]), desired_meas_result="11", + meas_2_circuit = Circuit([Gate("MEASURE", 0), Gate("MEASURE", 1)]) + f, sv = sim.simulate(meas_2_circuit, desired_meas_result="11", return_statevector=True, initial_statevector=initial_state) np.testing.assert_array_almost_equal(sv, initial_state) assert_freq_dict_almost_equal(f, {"11": 1}, 1.e-7) + self.assertAlmostEqual(1., meas_2_circuit.success_probabilities["11"], places=7) # Test that ValueError is raised for desired_meas_result="0" with probability 0. i.e. loop exits successfully self.assertRaises(ValueError, sim.simulate, Circuit([Gate("X", 0), Gate("MEASURE", 0)]), True, None, "0") @@ -268,11 +288,16 @@ def test_simulate_nshots_from_statevector(self): the exact one. """ for b in installed_sv_simulator: - simulator = get_backend(target=b, n_shots=10 ** 6) + simulator = get_backend(target=b, n_shots=10**6) for i, circuit in enumerate(circuits): frequencies, _ = simulator.simulate(circuit) assert_freq_dict_almost_equal(ref_freqs[i], frequencies, atol=1e-2) + for b in installed_clifford_simulators: + simulator = get_backend(target=b, n_shots=10**6) + frequencies, _ = simulator.simulate(circuit_clifford) + assert_freq_dict_almost_equal(ref_freqs_clifford, frequencies, atol=1e-2) + def test_simulate_empty_circuit_from_statevector(self): """ Test the generation of frequencies using an initial_statevector and an empty_circuit """ for b in installed_sv_simulator: @@ -292,6 +317,13 @@ def test_get_exp_value_from_statevector(self): exp_values[i][j] = simulator._get_expectation_value_from_statevector(op, circuit) np.testing.assert_almost_equal(exp_values, reference_exp_values, decimal=5) + for b in installed_clifford_simulators: + simulator = get_backend(target=b) + clifford_exp_values = np.zeros(len(ops)) + for j, op in enumerate(ops): + clifford_exp_values[j] = simulator._get_expectation_value_from_statevector(op, circuit_clifford) + np.testing.assert_almost_equal(clifford_exp_values, clifford_reference_exp_values, decimal=5) + def test_get_exp_value_from_frequencies_using_initial_statevector(self): """ Test the method computing the expectation value from frequencies, with a given simulator by generating the statevector first and sampling using an empty state_prep_circuit @@ -412,7 +444,7 @@ def test_get_exp_value_from_statevector_with_shots_h2(self): openqasm_circ = circ_handle.read() abs_circ = translate_c(openqasm_circ, "tangelo", source="openqasm") - simulator = get_backend(target="qulacs", n_shots=10 ** 6) + simulator = get_backend(target="qulacs", n_shots=10**6) expected = -1.1372704 energy = simulator.get_expectation_value(qubit_operator, abs_circ) @@ -422,7 +454,7 @@ def test_get_exp_value_mixed_state_desired_measurement_with_shots(self): """ Get expectation value of mixed state by post-selecting on desired measurement.""" qubit_operator = QubitOperator("X0 X1") + QubitOperator("Y0 Y1") + QubitOperator("Z0 Z1") + QubitOperator("X0 Y1", 1j) - ham = get_sparse_operator(qubit_operator).toarray() + ham = get_sparse_operator(qubit_operator.to_openfermion()).toarray() exact_sv = np.array([0.+0.j, 0.+0.j, 0.87758256+0.j, -0.47942554+0.j]) exact_exp = np.vdot(exact_sv, ham @ exact_sv) @@ -437,7 +469,7 @@ def test_get_exp_value_empty_circuit(self): empty_circuit = Circuit([], n_qubits=2) identity_circuit = Circuit([Gate('X', 0), Gate('X', 1)] * 2) - for b in installed_sv_simulator: + for b in (installed_sv_simulator | installed_clifford_simulators): simulator = get_backend(target=b) for op in [op1, op2]: exp_value_empty = simulator.get_expectation_value(op, empty_circuit) @@ -474,9 +506,15 @@ def test_get_exp_value_from_frequencies(self): exp_values[i][j] = simulator._get_expectation_value_from_frequencies(op, circuit) np.testing.assert_almost_equal(exp_values, reference_exp_values, decimal=5) + for b in installed_clifford_simulators: + simulator = get_backend(target=b) + clifford_exp_values = np.zeros(len(ops)) + for j, op in enumerate(ops): + clifford_exp_values[j] = simulator._get_expectation_value_from_frequencies(op, circuit_clifford) + np.testing.assert_almost_equal(clifford_exp_values, clifford_reference_exp_values, decimal=5) + class TestSimulateMisc(unittest.TestCase): - @unittest.skipIf("qdk" not in installed_backends, "Test Skipped: Backend not available \n") def test_n_shots_needed(self): """ @@ -492,7 +530,7 @@ def test_simulate_qdk(self): The accuracy is correlated to the number of shots taken in the simulation. Backend: qdk. """ - simulator = get_backend(target="qdk", n_shots=10 ** 4) + simulator = get_backend(target="qdk", n_shots=10**4) for i, circuit in enumerate(circuits): frequencies, _ = simulator.simulate(circuit) assert_freq_dict_almost_equal(ref_freqs[i], frequencies, atol=1e-1) @@ -502,7 +540,7 @@ def test_get_exp_value_from_frequencies_qdk(self): """ Test specific to QDK to ensure results are not impacted by code specific to frequency computation as well as the recompilation of the Q# file used in successive simulations """ - simulator = get_backend(target="qdk", n_shots=10 ** 4) + simulator = get_backend(target="qdk", n_shots=10**4) exp_values = np.zeros((len(ops)), dtype=float) for j, op in enumerate(ops): exp_values[j] = simulator.get_expectation_value(op, circuit3) diff --git a/tangelo/linq/tests/test_simulator_noisy.py b/tangelo/linq/tests/test_simulator_noisy.py index 2d79408cc..89a71bad5 100644 --- a/tangelo/linq/tests/test_simulator_noisy.py +++ b/tangelo/linq/tests/test_simulator_noisy.py @@ -19,11 +19,11 @@ import unittest import numpy as np -from openfermion.ops import QubitOperator from tangelo.linq import Gate, Circuit, get_backend, backend_info from tangelo.linq.noisy_simulation import NoiseModel, get_qiskit_noise_dict from tangelo.helpers.utils import default_simulator, installed_backends, assert_freq_dict_almost_equal +from tangelo.toolboxes.operators import QubitOperator # Noisy simulation: circuits, noise models, references cn1 = Circuit([Gate('X', target=0)]) @@ -111,25 +111,25 @@ def test_noisy_simulation_qulacs(self): """ # Pauli noise for one- and two-qubit gates. Circuits are only a X gate, or just a CNOT gate. - s_nmp = get_backend(target='qulacs', n_shots=10 ** 6, noise_model=nmp) + s_nmp = get_backend(target='qulacs', n_shots=10**6, noise_model=nmp) res_pauli1, _ = s_nmp.simulate(cn1) assert_freq_dict_almost_equal(res_pauli1, ref_pauli1, 1e-2) res_pauli2, _ = s_nmp.simulate(cn2) assert_freq_dict_almost_equal(res_pauli2, ref_pauli2, 1e-2) # Depol noise for one- and two-qubit gates. Circuits are only a X gate or just a CNOT gate. - s_nmd = get_backend(target='qulacs', n_shots=10 ** 6, noise_model=nmd) + s_nmd = get_backend(target='qulacs', n_shots=10**6, noise_model=nmd) res_depol1, _ = s_nmd.simulate(cn1) assert_freq_dict_almost_equal(res_depol1, ref_depol1, 1e-2) res_depol2, _ = s_nmd.simulate(cn2) assert_freq_dict_almost_equal(res_depol2, ref_depol2, 1e-2) # Cumulate several noises on a given gate (here noise simplifies to identity) - s_nmc = get_backend(target='qulacs', n_shots=10 ** 6, noise_model=nmc) + s_nmc = get_backend(target='qulacs', n_shots=10**6, noise_model=nmc) res_cumul, _ = s_nmc.simulate(cn1) assert_freq_dict_almost_equal(res_cumul, ref_cumul, 1e-2) - s_nmm = get_backend(target="qulacs", n_shots=10 ** 4, noise_model=nmm) + s_nmm = get_backend(target="qulacs", n_shots=10**4, noise_model=nmm) res_mixed, _ = s_nmm.simulate(circuit_mixed) assert_freq_dict_almost_equal(res_mixed, ref_mixed, 7.e-2) @@ -145,25 +145,25 @@ def test_noisy_simulation_qiskit(self): """ # Pauli noise for one- and two-qubit gates. Circuits are only a X gate, or just a CNOT gate. - s_nmp = get_backend(target='qiskit', n_shots=10 ** 6, noise_model=nmp) + s_nmp = get_backend(target='qiskit', n_shots=10**6, noise_model=nmp) res_pauli1, _ = s_nmp.simulate(cn1) assert_freq_dict_almost_equal(res_pauli1, ref_pauli1, 1e-2) res_pauli2, _ = s_nmp.simulate(cn2) assert_freq_dict_almost_equal(res_pauli2, ref_pauli2, 1e-2) # Depol noise for one- and two-qubit gates. Circuits are only a X gate or just a CNOT gate. - s_nmd = get_backend(target='qiskit', n_shots=10 ** 6, noise_model=nmd) + s_nmd = get_backend(target='qiskit', n_shots=10**6, noise_model=nmd) res_depol1, _ = s_nmd.simulate(cn1) assert_freq_dict_almost_equal(res_depol1, ref_depol1, 1e-2) res_depol2, _ = s_nmd.simulate(cn2) assert_freq_dict_almost_equal(res_depol2, ref_depol2, 1e-2) # Cumulate several noises on a given gate (here noise simplifies to identity) - s_nmp = get_backend(target='qiskit', n_shots=10 ** 6, noise_model=nmc) + s_nmp = get_backend(target='qiskit', n_shots=10**6, noise_model=nmc) res_cumul, _ = s_nmp.simulate(cn1) assert_freq_dict_almost_equal(res_cumul, ref_cumul, 1e-2) - s_nmm = get_backend(target="qiskit", n_shots=10 ** 4, noise_model=nmm) + s_nmm = get_backend(target="qiskit", n_shots=10**4, noise_model=nmm) res_mixed, _ = s_nmm.simulate(circuit_mixed) assert_freq_dict_almost_equal(ref_mixed, res_mixed, 7.e-2) @@ -180,26 +180,26 @@ def test_noisy_simulation_cirq(self): """ # Pauli noise for one- and two-qubit gates. Circuits are only a X gate, or just a CNOT gate. - s_nmp = get_backend(target='cirq', n_shots=10 ** 6, noise_model=nmp) + s_nmp = get_backend(target='cirq', n_shots=10**6, noise_model=nmp) res_pauli1, _ = s_nmp.simulate(cn1) assert_freq_dict_almost_equal(res_pauli1, ref_pauli1, 1e-2) res_pauli2, _ = s_nmp.simulate(cn2) assert_freq_dict_almost_equal(res_pauli2, ref_pauli2, 1e-2) # Depol noise for one- and two-qubit gates. Circuits are only a X gate or just a CNOT gate. - s_nmd = get_backend(target='cirq', n_shots=10 ** 6, noise_model=nmd) + s_nmd = get_backend(target='cirq', n_shots=10**6, noise_model=nmd) res_depol1, _ = s_nmd.simulate(cn1) assert_freq_dict_almost_equal(res_depol1, ref_depol1, 1e-2) res_depol2, _ = s_nmd.simulate(cn2) assert_freq_dict_almost_equal(res_depol2, ref_depol2, 1e-2) # Cumulate several noises on a given gate (here noise simplifies to identity) - s_nmc = get_backend(target='cirq', n_shots=10 ** 6, noise_model=nmc) + s_nmc = get_backend(target='cirq', n_shots=10**6, noise_model=nmc) res_cumul, _ = s_nmc.simulate(cn1) assert_freq_dict_almost_equal(res_cumul, ref_cumul, 1e-2) # Noisy mixed state without returning mid-circuit measurements - s_nmm = get_backend(target="cirq", n_shots=10 ** 4, noise_model=nmm) + s_nmm = get_backend(target="cirq", n_shots=10**4, noise_model=nmm) res_mixed, _ = s_nmm.simulate(circuit_mixed) assert_freq_dict_almost_equal(ref_mixed, res_mixed, 7.e-2) @@ -217,6 +217,20 @@ def test_noisy_simulation_cirq(self): [ 0.00000000 - 0.j, 0.00000000 + 0.j, -0.33658839 - 0.j, 0.18387908 + 0.j]]) np.testing.assert_array_almost_equal(sv, exact_sv) + @unittest.skipIf("stim" not in installed_backends, "Test Skipped: Backend not available \n") + def test_noisy_simulation_stim(self): + """ + Test noisy simulation through stim. + Currently tested: pauli noise, depolarization noise (1 and 2 qubit gates) + """ + + # Pauli noise for one- and two-qubit gates. Circuits are only a X gate, or just a CNOT gate. + s_nmp = get_backend(target='stim', n_shots=10**6, noise_model=nmp) + res_pauli1, _ = s_nmp.simulate(cn1) + assert_freq_dict_almost_equal(res_pauli1, ref_pauli1, 1e-2) + res_pauli2, _ = s_nmp.simulate(cn2) + assert_freq_dict_almost_equal(res_pauli2, ref_pauli2, 1e-2) + def test_get_expectation_value_noisy(self): """Test of the get_expectation_value function with a noisy simulator""" # Test Hamiltonian. @@ -241,13 +255,13 @@ def test_get_expectation_value_noisy(self): nmp_no_noise = NoiseModel() noise = 0.00 nmp_no_noise.add_quantum_error("CNOT", "pauli", [noise, noise, noise]) - sim_no_noise = get_backend(target=default_simulator, n_shots=10 ** 6, noise_model=nmp_no_noise) + sim_no_noise = get_backend(target=default_simulator, n_shots=10**6, noise_model=nmp_no_noise) # Small Noise model nmp_small_noise = NoiseModel() noise = 0.01 nmp_small_noise.add_quantum_error("CNOT", "pauli", [noise, noise, noise]) - sim_small_noise = get_backend(target=default_simulator, n_shots=10 ** 6, noise_model=nmp_small_noise) + sim_small_noise = get_backend(target=default_simulator, n_shots=10**6, noise_model=nmp_small_noise) energy_no_noise = sim_no_noise.get_expectation_value(H, circuit) energy_small_noise = sim_small_noise.get_expectation_value(H, circuit) diff --git a/tangelo/linq/tests/test_symbolic_simulator.py b/tangelo/linq/tests/test_symbolic_simulator.py new file mode 100644 index 000000000..79bb6d2e7 --- /dev/null +++ b/tangelo/linq/tests/test_symbolic_simulator.py @@ -0,0 +1,82 @@ +# Copyright 2023 Good Chemistry Company. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""A test class to check that the simulator class functionalities are behaving +as expected for the symbolic backend. +""" + +import unittest +from math import pi + +from tangelo.helpers.utils import assert_freq_dict_almost_equal +from tangelo.linq import Gate, Circuit +from tangelo.helpers.utils import installed_backends +from tangelo.linq.target.target_sympy import SympySimulator + + +class TestSymbolicSimulate(unittest.TestCase): + + @unittest.skipIf("sympy" not in installed_backends, "Test Skipped: Sympy backend not available \n") + def test_simple_simulate(self): + """Test simulate of a simple rotation gate with a symbolic parameter.""" + + from sympy import symbols, cos, sin + + simple_circuit = Circuit([Gate("RY", 0, parameter="alpha")]) + backend = SympySimulator() + probs, _ = backend.simulate(simple_circuit, return_statevector=False) + + alpha = symbols("alpha", real=True) + + self.assertDictEqual(probs, {"0": (cos(alpha/2))**2, "1": (sin(alpha/2))**2}) + + @unittest.skipIf("sympy" not in installed_backends, "Test Skipped: Sympy backend not available \n") + def test_simulate_with_control(self): + """Test simulate of a control rotation gate with a symbolic parameter.""" + + from sympy import symbols, cos, sin + + backend = SympySimulator() + + no_action_circuit = Circuit([Gate("CRY", 1, 0, parameter="alpha")]) + no_action_probs, _ = backend.simulate(no_action_circuit, return_statevector=False) + + self.assertDictEqual(no_action_probs, {"00": 1.}) + + action_circuit = Circuit([Gate("X", 0), Gate("CRY", 1, 0, parameter="alpha")]) + action_probs, _ = backend.simulate(action_circuit, return_statevector=False) + alpha = symbols("alpha", real=True) + + self.assertDictEqual(action_probs, {"10": (cos(alpha/2))**2, "11": (sin(alpha/2))**2}) + + @unittest.skipIf("sympy" not in installed_backends, "Test Skipped: Sympy backend not available \n") + def test_evaluate_bell_state(self): + """Test the numerical evaluation to a known state (Bell state).""" + + backend = SympySimulator() + + variable_bell_circuit = Circuit([Gate("RY", 0, parameter="alpha"), Gate("CNOT", 1, 0)]) + variable_bell_probs, _ = backend.simulate(variable_bell_circuit, return_statevector=False) + + # Replace alpha by pi/2. + numerical_bell_probs = { + bitstring: prob.subs(list(prob.free_symbols)[0], pi/2) for + bitstring, prob in variable_bell_probs.items() + } + + assert_freq_dict_almost_equal(numerical_bell_probs, {"00": 0.5, "11": 0.5}, atol=1e-3) + + +if __name__ == "__main__": + unittest.main() diff --git a/tangelo/linq/tests/test_translator_circuit.py b/tangelo/linq/tests/test_translator_circuit.py index 84acda14c..3cdf09f5b 100644 --- a/tangelo/linq/tests/test_translator_circuit.py +++ b/tangelo/linq/tests/test_translator_circuit.py @@ -19,8 +19,8 @@ import unittest import os + import numpy as np -import time from tangelo.linq import Gate, Circuit from tangelo.linq.gate import PARAMETERIZED_GATES @@ -31,19 +31,29 @@ gates = [Gate("H", 2), Gate("CNOT", 1, control=0), Gate("CNOT", 2, control=1), Gate("Y", 0), Gate("S", 0)] abs_circ = Circuit(gates) + Circuit([Gate("RX", 1, parameter=2.)]) +clifford_abs_circ = Circuit(gates) + Circuit([Gate("RX", 1, parameter=np.pi/2)]) multi_controlled_gates = [Gate("X", 0), Gate("X", 1), Gate("CX", target=2, control=[0, 1])] +multi_controlled_gates2 = [Gate("X", 0), Gate("X", 1), Gate("CNOT", target=2, control=[0, 1])] abs_multi_circ = Circuit(multi_controlled_gates) +abs_multi_circ2 = Circuit(multi_controlled_gates2) init_gates = [Gate('H', 0), Gate('X', 1), Gate('H', 2)] one_qubit_gate_names = ["H", "X", "Y", "Z", "S", "T", "RX", "RY", "RZ", "PHASE"] one_qubit_gates = [Gate(name, target=0) if name not in PARAMETERIZED_GATES else Gate(name, target=0, parameter=0.5) for name in one_qubit_gate_names] one_qubit_gates += [Gate(name, target=1) if name not in PARAMETERIZED_GATES else Gate(name, target=1, parameter=0.2) for name in one_qubit_gate_names] +clifford_one_qubit_gates = [Gate(name, target=0) if name not in PARAMETERIZED_GATES else Gate(name, target=0, parameter=np.pi) + for name in one_qubit_gate_names.pop(5)[:-1]] +clifford_one_qubit_gates += [Gate(name, target=1) if name not in PARAMETERIZED_GATES else Gate(name, target=1, parameter=-np.pi/2) + for name in one_qubit_gate_names.pop(5)[:-1]] two_qubit_gate_names = ["CNOT", "CX", "CY", "CZ", "CPHASE", "CRZ"] +clifford_two_qubit_gate_names = ["CNOT", "CX", "CY", "CZ"] two_qubit_gates = [Gate(name, target=1, control=0) if name not in PARAMETERIZED_GATES else Gate(name, target=1, control=0, parameter=0.5) for name in two_qubit_gate_names] +clifford_two_qubit_gates = [Gate(name, target=1, control=0) for name in clifford_two_qubit_gate_names] swap_gates = [Gate('SWAP', target=[1, 0]), Gate('CSWAP', target=[1, 2], control=0)] big_circuit = Circuit(init_gates + one_qubit_gates + two_qubit_gates + swap_gates + [Gate('XX', [0, 1], parameter=0.5)]) +clifford_big_circuit = Circuit(init_gates + clifford_one_qubit_gates + clifford_two_qubit_gates + [Gate('SWAP', target=[1, 0])]) references = [0., 0.38205142 ** 2, 0., 0.59500984 ** 2, 0., 0.38205142 ** 2, 0., 0.59500984 ** 2] references_multi = [0., 0., 0., 0., 0., 0., 0., 1.] @@ -54,6 +64,8 @@ -0.37427729 + 0.41885117j, -0.34400321 + 0.12534970j, -0.04807414 + 0.07971841j, 0.15939615 + 0.15293440j, -0.05511766 + 0.20825737j] +clifford_ref = [0. + 0.j, 0.5+0.j, 0. - 0.5j, 0. + 0.j, 0. + 0.j, 0.5 + 0.j, 0. - 0.5j, 0. + 0.j] + abs_circ_mixed = Circuit(gates) + Circuit([Gate("RX", 1, parameter=1.5), Gate("MEASURE", 0)]) @@ -67,10 +79,9 @@ def test_qulacs(self): """ import qulacs - # Generates the qulacs circuit by translating from the abstract one + # Test 1: Simulation of trasnlated circuit VS native circuit + # Generates the qulacs circuit by translating from the abstract one, run the simulation afterwards translated_circuit = translate_c(abs_circ, "qulacs") - - # Run the simulation state1 = qulacs.QuantumState(abs_circ.width) translated_circuit.update_quantum_state(state1) @@ -84,22 +95,21 @@ def test_qulacs(self): qulacs_circuit.add_S_gate(0) qulacs_circuit.add_RX_gate(1, -2.) # Convention: sign of theta - # Run the simulation + # Run the simulation of the native qulacs circuit, assert state vectors are identical state2 = qulacs.QuantumState(abs_circ.width) qulacs_circuit.update_quantum_state(state2) - # Assert that both simulations returned the same state vector np.testing.assert_array_equal(state1.get_vector(), state2.get_vector()) - # Generates the qulacs circuit by translating from the abstract one + # Test 2: Multi-controlled CNOT and multi-controlled X test translated_circuit = translate_c(abs_multi_circ, "qulacs") - - # Run the simulation state1 = qulacs.QuantumState(abs_multi_circ.width) translated_circuit.update_quantum_state(state1) + translated_circuit = translate_c(abs_multi_circ2, "qulacs") + state1b = qulacs.QuantumState(abs_multi_circ2.width) + translated_circuit.update_quantum_state(state1b) - # Directly define the same circuit through qulacs - # NB: this includes convention fixes for some parametrized rotation gates (-theta instead of theta) + # Test against native circuit qulacs_circuit = qulacs.QuantumCircuit(3) qulacs_circuit.add_X_gate(0) qulacs_circuit.add_X_gate(1) @@ -108,17 +118,14 @@ def test_qulacs(self): mat_gate.add_control_qubit(1, 1) qulacs_circuit.add_gate(mat_gate) - # Run the simulation + # Run, assert state vectors are equal. state2 = qulacs.QuantumState(abs_multi_circ.width) qulacs_circuit.update_quantum_state(state2) - - # Assert that both simulations returned the same state vector np.testing.assert_array_equal(state1.get_vector(), state2.get_vector()) + np.testing.assert_array_equal(state1b.get_vector(), state2.get_vector()) - # Test that the translated circuit reports the same result for all cross-supported gates + # Test 3: translated circuit reports the same result for all cross-supported gates translated_circuit = translate_c(big_circuit, "qulacs") - - # Run the simulation state1 = qulacs.QuantumState(big_circuit.width) translated_circuit.update_quantum_state(state1) np.testing.assert_array_almost_equal(state1.get_vector(), reference_big_msq, decimal=6) @@ -131,7 +138,7 @@ def test_qiskit(self): """ import qiskit - from qiskit.providers.aer import AerSimulator + from qiskit_aer import AerSimulator # Generate the qiskit circuit by translating from the abstract one and print it translated_circuit = translate_c(abs_circ, "qiskit") @@ -166,20 +173,6 @@ def test_qiskit(self): # Generate the qiskit circuit by translating from the abstract one and print it translated_circuit = translate_c(big_circuit, "qiskit") - # Big translate/translate back test (32000 gates) - very_big_circuit = big_circuit*10**3 - tstart1 = time.time() - qc_very_big_circuit = translate_c(very_big_circuit, "qiskit") - tstop1 = time.time() - print(f"Circuit -> QuantumCircuit took {tstop1-tstart1:.2f} s") - - tstart2 = time.time() - c_very_big_circuit = translate_c(qc_very_big_circuit, "tangelo", source="qiskit") - tstop2 = time.time() - print(f"QuantumCircuit -> Circuit took {tstop2-tstart2:.2f} s") - - self.assertEqual(c_very_big_circuit, very_big_circuit) - # Simulate both circuits, assert state vectors are equal qiskit_simulator = AerSimulator(method="statevector") translated_circuit = qiskit.transpile(translated_circuit, qiskit_simulator) @@ -196,10 +189,10 @@ def test_cirq(self): import cirq - # Generate the qiskit circuit by translating from the abstract one and print it + # Translated circuit translated_circuit = translate_c(abs_circ, "cirq") - # Generate the cirq circuit directly and print it + # Native circuit qubit_labels = cirq.LineQubit.range(3) circ = cirq.Circuit() circ.append(cirq.H(qubit_labels[2])) @@ -212,15 +205,13 @@ def test_cirq(self): # Simulate both circuits, assert state vectors are equal cirq_simulator = cirq.Simulator() - job_sim = cirq_simulator.simulate(circ) v1 = job_sim.final_state_vector - job_sim = cirq_simulator.simulate(translated_circuit) v2 = job_sim.final_state_vector - np.testing.assert_array_equal(v1, v2) + # Test 2: multi-controlled gates translated_circuit = translate_c(abs_multi_circ, "cirq") circ = cirq.Circuit() circ.append(cirq.X(qubit_labels[0])) @@ -230,13 +221,18 @@ def test_cirq(self): job_sim = cirq_simulator.simulate(circ) v1 = job_sim.final_state_vector - job_sim = cirq_simulator.simulate(translated_circuit) v2 = job_sim.final_state_vector + np.testing.assert_array_equal(v1, v2) + # Test 3: multi-controlled CNOT turn into multi-controlled X + # The same statevector is expected. + translated_circuit2 = translate_c(abs_multi_circ2, "cirq") + job_sim = cirq_simulator.simulate(translated_circuit2) + v2 = job_sim.final_state_vector np.testing.assert_array_equal(v1, v2) - # Test that translated circuit is correct for all cross-supported gates + # Test 4: translated circuit must be correct for all cross-supported gates translated_circuit = translate_c(big_circuit, "cirq") job_sim = cirq_simulator.simulate(translated_circuit) np.testing.assert_array_almost_equal(job_sim.final_state_vector, reference_big_lsq, decimal=6) @@ -270,6 +266,10 @@ def test_qdk(self): translated_circuit = translate_c(abs_multi_circ, "qdk") print(translated_circuit) + # Check that multi-controlled CNOT is changed correctly to multi-controlled "CX" + translated_circuit2 = translate_c(abs_multi_circ2, "qdk") + self.assertEqual(translated_circuit, translated_circuit2) + # Write to file with open('tmp_circuit.qs', 'w+') as f_out: f_out.write(translated_circuit) @@ -501,6 +501,53 @@ def circuit(ops): # Compare statevectors np.testing.assert_array_almost_equal(v1, reference_big_lsq, decimal=6) + @unittest.skipIf("sympy" not in installed_backends, "Test Skipped: Sympy backend not available \n") + def test_to_sympy(self): + """Translate abtract format to sympy format.""" + + from sympy.physics.quantum.gate import HadamardGate, XGate, YGate, ZGate, CNotGate + + # Equivalent native sympy circuit. + ref_circ = ZGate(3) * YGate(1) * XGate(0) * CNotGate(0, 1) * HadamardGate(2) + + gates = [Gate("H", 2), Gate("CNOT", 1, control=0), Gate("X", 0), Gate("Y", 1), Gate("Z", 3)] + abs_circ = Circuit(gates) + + # Generate the sympy circuit by translating from the abstract one. + translated_circuit = translate_c(abs_circ, "sympy") + + self.assertEqual(translated_circuit, ref_circ) + + @unittest.skipIf("stim" not in installed_backends, "Test Skipped: Backend not available \n") + def test_stim_translate(self): + """ Compares stim translated circuit with one made directly with stim""" + + import stim + # Create stim circuit from Tangelo circuit + translated_circuit = translate_c(clifford_abs_circ, "stim") + # Assert against native equivalent stim circuit + circ = stim.Circuit() + for qubit in range(clifford_abs_circ.width): + circ.append("I", [qubit]) + circ.append("H", [2]) + circ.append("CNOT", [0, 1]) + circ.append("CNOT", [1, 2]) + circ.append("Y", [0]) + circ.append("S", [0]) + circ.append("S_DAG", [1]) + circ.append("H", [1]) + circ.append("S_DAG", [1]) + assert(circ == translated_circuit) + + @unittest.skipIf("stim" not in installed_backends, "Test Skipped: Backend not available \n") + def test_stim_tableau(self): + """ Compares stim statevector produced by tableau with reference state""" + + from tangelo.linq.translator.translate_stim import translate_tableau + # Get statevector from tableau + v1 = translate_tableau(clifford_big_circuit).state_vector() + np.testing.assert_array_almost_equal(v1, clifford_ref, decimal=6) + if __name__ == "__main__": unittest.main() diff --git a/tangelo/linq/tests/test_translator_perf.py b/tangelo/linq/tests/test_translator_perf.py new file mode 100644 index 000000000..c3f682521 --- /dev/null +++ b/tangelo/linq/tests/test_translator_perf.py @@ -0,0 +1,104 @@ +# Copyright 2023 Good Chemistry Company. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +""" Test class to keep track of performance of translator module, for format convertion """ + +import unittest +import os +import time + +from itertools import product +import pytest + +from tangelo.linq import Gate, Circuit +from tangelo.toolboxes.operators import QubitOperator +from tangelo.helpers.utils import symbolic_backends +from tangelo.linq import translate_operator, translate_circuit +from tangelo.linq.translator.translate_qubitop import FROM_TANGELO as FROM_TANGELO_OP +from tangelo.linq.translator.translate_qubitop import TO_TANGELO as TO_TANGELO_OP +from tangelo.linq.translator.translate_circuit import FROM_TANGELO as FROM_TANGELO_C +from tangelo.linq.translator.translate_circuit import TO_TANGELO as TO_TANGELO_C + + +# Build artificially large operator made of all possible "full" Pauli words (no 'I') of length n_qubits +n_qubits_op = 10 +n_terms = 3 ** n_qubits_op +terms = {tuple(zip(range(n_qubits_op), pw)): 1.0 for pw in product(['X', 'Y', 'Z'], repeat=n_qubits_op)} +tangelo_op = QubitOperator() +tangelo_op.terms = terms + +# Build artificially large quantum circuit +n_qubits = 20 +n_repeat = 4000 +gates = [Gate('X', i) for i in range(n_qubits)] + [Gate("CNOT", (i+1) % n_qubits, control=i) for i in range(n_qubits)] +tangelo_c = Circuit(gates * n_repeat) + + +class PerfTranslatorTest(unittest.TestCase): + + @pytest.mark.skip(reason="Takes a long time and doesn't print the desired information.") + def test_perf_operator(self): + """ Performance test with a reasonable large input for operator. + Symbolic backends are not included in this test. + """ + + print(f'\n[Performance Test :: linq operator format conversion]') + print(f'\tInput size: n_qubits={n_qubits_op}, n_terms={n_terms}\n') + + perf_backends = FROM_TANGELO_OP.keys() - symbolic_backends + for f in perf_backends: + try: + tstart = time.time() + target_op = translate_operator(tangelo_op, source="tangelo", target=f) + print(f"\tFormat conversion from {'tangelo':12} to {f:12} :: {time.time()-tstart} s") + except Exception: + continue + + if f in TO_TANGELO_OP: + try: + tstart = time.time() + translate_operator(target_op, source=f, target="tangelo") + print(f"\tFormat conversion from {f:12} to {'tangelo':12} :: {time.time()-tstart} s") + except Exception: + continue + + @pytest.mark.skip(reason="Takes a long time and doesn't print the desired information.") + def test_perf_circuit(self): + """ Performance test with a reasonable large input for quantum circuit. + Symbolic backends are not included in this test. + """ + + print(f'\n[Performance Test :: linq circuit format conversion]') + print(f'\tInput size: n_qubits={tangelo_c.width}, n_gates={tangelo_c.size}\n') + + perf_backends = FROM_TANGELO_C.keys() - symbolic_backends + for f in perf_backends: + try: + tstart = time.time() + target_c = translate_circuit(tangelo_c, source="tangelo", target=f) + print(f"\tFormat conversion from {'tangelo':12} to {f:12} :: {time.time()-tstart} s") + except Exception: + continue + + if f in TO_TANGELO_C: + try: + tstart = time.time() + translate_circuit(target_c, source=f, target="tangelo") + print(f"\tFormat conversion from {f:12} to {'tangelo':12} :: {time.time()-tstart} s") + except Exception: + continue + + +if __name__ == "__main__": + unittest.main() diff --git a/tangelo/linq/tests/test_translator_qubitop.py b/tangelo/linq/tests/test_translator_qubitop.py index c4a751b57..d8175fa3a 100644 --- a/tangelo/linq/tests/test_translator_qubitop.py +++ b/tangelo/linq/tests/test_translator_qubitop.py @@ -49,8 +49,8 @@ def test_unsupported_target(self): def test_qiskit_to_tangelo(self): """Test translation from a qiskit to a tangelo operator.""" - from qiskit.opflow.primitive_ops import PauliSumOp - qiskit_op = PauliSumOp.from_list([("ZYX", 2.), ("III", 3.)]) + from qiskit.quantum_info.operators import SparsePauliOp + qiskit_op = SparsePauliOp.from_list([("ZYX", 2.), ("III", 3.)]) test_op = translate_operator(qiskit_op, source="qiskit", target="tangelo") self.assertEqual(test_op, tangelo_op) @@ -59,8 +59,8 @@ def test_qiskit_to_tangelo(self): def test_tangelo_to_qiskit(self): """Test translation from a tangelo to a qiskit operator.""" - from qiskit.opflow.primitive_ops import PauliSumOp - qiskit_op = PauliSumOp.from_list([("ZYX", 2.), ("III", 3.)]) + from qiskit.quantum_info.operators import SparsePauliOp + qiskit_op = SparsePauliOp.from_list([("ZYX", 2.), ("III", 3.)]) test_op = translate_operator(tangelo_op, source="tangelo", target="qiskit") self.assertEqual(qiskit_op, test_op) @@ -185,43 +185,14 @@ def test_tangelo_to_projectq(self): def test_tangelo_to_qiskit_H2_eigenvalues(self): """Test eigenvalues resulting from a tangelo to qiskit translation.""" - from qiskit.algorithms import NumPyEigensolver - qu_op = load_operator("H2_JW_occfirst.data", data_directory=pwd_this_test+"/data", plain_text=True) test_op = translate_operator(qu_op, source="tangelo", target="qiskit") eigenvalues_tangelo = eigenspectrum(qu_op) - qiskit_solver = NumPyEigensolver(2**4) - eigenvalues_qiskit = qiskit_solver.compute_eigenvalues(test_op) - - np.testing.assert_array_almost_equal(eigenvalues_tangelo, eigenvalues_qiskit.eigenvalues) + eigenvalues_qiskit = np.linalg.eigh(test_op.to_matrix(sparse=False))[0] - @unittest.skipIf("qiskit" not in installed_backends, "Test Skipped: Qiskit not available \n") - def test_tangelo_to_qiskit_big(self): - """Test translation from a tangelo to a qiskit operator, for a large input""" - - n_qubits = 10 - n_terms = 3**n_qubits - - # Build large operator made of all possible "full" Pauli words (no 'I') of length n_qubits - terms = {tuple(zip(range(n_qubits), pw)): 1.0 for pw in product(['X', 'Y', 'Z'], repeat=n_qubits)} - q_op = QubitOperator() - q_op.terms = terms - - s, t = "tangelo", "qiskit" - tstart1 = time.time() - tmp_op = translate_operator(q_op, source=s, target=t) - tstop1 = time.time() - print(f"Qubit operator conversion {s} to {t}: {tstop1 - tstart1:.1f} (terms = {n_terms})") - - t, s = s, t - tstart2 = time.time() - q_op2 = translate_operator(tmp_op, source=s, target=t) - tstop2 = time.time() - print(f"Qubit operator conversion {s} to {t}: {tstop2 - tstart2:.1f} (terms = {n_terms})") - - assert(q_op == q_op2) + np.testing.assert_array_almost_equal(eigenvalues_tangelo, eigenvalues_qiskit) if __name__ == "__main__": diff --git a/tangelo/linq/translator/__init__.py b/tangelo/linq/translator/__init__.py index a0fe09682..99649c1a2 100644 --- a/tangelo/linq/translator/__init__.py +++ b/tangelo/linq/translator/__init__.py @@ -14,17 +14,18 @@ from tangelo.helpers.utils import installed_backends -from .translate_braket import translate_braket, get_braket_gates -from .translate_qiskit import translate_qiskit, get_qiskit_gates -from .translate_qulacs import translate_qulacs, get_qulacs_gates -from .translate_cirq import translate_cirq, get_cirq_gates -from .translate_json_ionq import translate_json_ionq, get_ionq_gates -from .translate_qdk import translate_qsharp, get_qdk_gates -from .translate_projectq import translate_projectq, _translate_projectq2abs, get_projectq_gates -from .translate_openqasm import translate_openqasm, _translate_openqasm2abs, get_openqasm_gates +from .translate_braket import get_braket_gates +from .translate_qiskit import get_qiskit_gates +from .translate_qulacs import get_qulacs_gates +from .translate_cirq import get_cirq_gates +from .translate_json_ionq import get_ionq_gates +from .translate_qdk import get_qdk_gates +from .translate_projectq import get_projectq_gates +from .translate_openqasm import get_openqasm_gates +from .translate_pennylane import get_pennylane_gates +from .translate_sympy import get_sympy_gates from .translate_qubitop import translate_operator from .translate_circuit import translate_circuit -from .translate_pennylane import get_pennylane_gates def get_supported_gates(): @@ -40,5 +41,6 @@ def get_supported_gates(): supported_gates["cirq"] = sorted(get_cirq_gates().keys()) supported_gates["braket"] = sorted(get_braket_gates().keys()) supported_gates["pennylane"] = sorted(get_pennylane_gates().keys()) + supported_gates["sympy"] = sorted(get_sympy_gates().keys()) return supported_gates diff --git a/tangelo/linq/translator/translate_braket.py b/tangelo/linq/translator/translate_braket.py index fe24f9944..745dc1041 100644 --- a/tangelo/linq/translator/translate_braket.py +++ b/tangelo/linq/translator/translate_braket.py @@ -23,7 +23,6 @@ """ from tangelo.linq import Circuit, Gate -from tangelo.helpers import deprecated def get_braket_gates(): @@ -59,20 +58,6 @@ def get_braket_gates(): return GATE_BRAKET -@deprecated("Please use the translate_circuit function.") -def translate_braket(source_circuit): - """Take in an abstract circuit, return a quantum circuit object as defined - in the Python Braket SDK. - - Args: - source_circuit: quantum circuit in the abstract format. - - Returns: - braket.circuits.Circuit: quantum circuit in Python Braket SDK format. - """ - return translate_c_to_braket(source_circuit) - - def translate_c_to_braket(source_circuit): """Take in an abstract circuit, return a quantum circuit object as defined in the Python Braket SDK. diff --git a/tangelo/linq/translator/translate_circuit.py b/tangelo/linq/translator/translate_circuit.py index 65b9f4a54..ee06ec737 100644 --- a/tangelo/linq/translator/translate_circuit.py +++ b/tangelo/linq/translator/translate_circuit.py @@ -23,7 +23,8 @@ from tangelo.linq.translator.translate_qiskit import translate_c_to_qiskit, translate_c_from_qiskit from tangelo.linq.translator.translate_qulacs import translate_c_to_qulacs from tangelo.linq.translator.translate_pennylane import translate_c_to_pennylane - +from tangelo.linq.translator.translate_sympy import translate_c_to_sympy +from tangelo.linq.translator.translate_stim import translate_c_to_stim FROM_TANGELO = { "braket": translate_c_to_braket, @@ -34,7 +35,9 @@ "qdk": translate_c_to_qsharp, "qiskit": translate_c_to_qiskit, "qulacs": translate_c_to_qulacs, - "pennylane": translate_c_to_pennylane + "pennylane": translate_c_to_pennylane, + "stim": translate_c_to_stim, + "sympy": translate_c_to_sympy } TO_TANGELO = { diff --git a/tangelo/linq/translator/translate_cirq.py b/tangelo/linq/translator/translate_cirq.py index 008c4c0de..774b72b94 100644 --- a/tangelo/linq/translator/translate_cirq.py +++ b/tangelo/linq/translator/translate_cirq.py @@ -25,7 +25,6 @@ from math import pi from tangelo.toolboxes.operators import QubitOperator -from tangelo.helpers import deprecated def get_cirq_gates(): @@ -43,6 +42,7 @@ def get_cirq_gates(): GATE_CIRQ["CY"] = cirq.Y GATE_CIRQ["CZ"] = cirq.Z GATE_CIRQ["S"] = cirq.S + GATE_CIRQ["SDAG"] = cirq.ZPowGate(exponent=-0.5) GATE_CIRQ["T"] = cirq.T GATE_CIRQ["CH"] = cirq.H GATE_CIRQ["RX"] = cirq.rx @@ -61,22 +61,6 @@ def get_cirq_gates(): return GATE_CIRQ -@deprecated("Please use the translate_circuit function.") -def translate_cirq(source_circuit): - """Take in an abstract circuit, return an equivalent cirq QuantumCircuit - instance. - - Args: - source_circuit: quantum circuit in the abstract format. - - Returns: - cirq.Circuit: a corresponding cirq Circuit. Right now, the structure is - of LineQubit. It is possible in the future that we may support - NamedQubit or GridQubit. - """ - return translate_c_to_cirq(source_circuit) - - def translate_c_to_cirq(source_circuit, noise_model=None, save_measurements=False): """Take in an abstract circuit, return an equivalent cirq QuantumCircuit object. @@ -108,10 +92,12 @@ def translate_c_to_cirq(source_circuit, noise_model=None, save_measurements=Fals # Maps the gate information properly. Different for each backend (order, values) for gate in source_circuit._gates: - if (gate.control is not None) and gate.name != 'CNOT': + if gate.control is not None: num_controls = len(gate.control) control_list = [qubit_list[c] for c in gate.control] - if gate.name in {"H", "X", "Y", "Z", "S", "T"}: + if gate.name == 'CNOT' and num_controls > 1: + gate.name = 'CX' + if gate.name in {"H", "X", "Y", "Z", "S", "SDAG", "T"}: target_circuit.append(GATE_CIRQ[gate.name](qubit_list[gate.target[0]])) elif gate.name in {"CH", "CX", "CY", "CZ"}: next_gate = GATE_CIRQ[gate.name].controlled(num_controls) diff --git a/tangelo/linq/translator/translate_json_ionq.py b/tangelo/linq/translator/translate_json_ionq.py index 56223658b..458a52f9f 100644 --- a/tangelo/linq/translator/translate_json_ionq.py +++ b/tangelo/linq/translator/translate_json_ionq.py @@ -23,7 +23,6 @@ """ from tangelo.linq import Circuit, Gate -from tangelo.helpers import deprecated def get_ionq_gates(): @@ -46,22 +45,6 @@ def get_ionq_gates(): return GATE_JSON_IONQ -@deprecated("Please use the translate_circuit function.") -def translate_json_ionq(source_circuit): - """Take in an abstract circuit, return a dictionary following the IonQ JSON - format as described below. - https://dewdrop.ionq.co/#json-specification - - Args: - source_circuit: quantum circuit in the abstract format. - - Returns: - dict: representation of the quantum circuit following the IonQ JSON - format. - """ - return translate_c_to_json_ionq(source_circuit) - - def translate_c_to_json_ionq(source_circuit): """Take in an abstract circuit, return a dictionary following the IonQ JSON format as described below. diff --git a/tangelo/linq/translator/translate_openqasm.py b/tangelo/linq/translator/translate_openqasm.py index f131c1141..ee4f13149 100644 --- a/tangelo/linq/translator/translate_openqasm.py +++ b/tangelo/linq/translator/translate_openqasm.py @@ -27,12 +27,11 @@ from math import pi from tangelo.linq import Gate, Circuit -from tangelo.helpers import deprecated def get_openqasm_gates(): """Map gate name of the abstract format to the equivalent gate name used in - openqasm OpenQASM is a general format that allows users to express a quantum + OpenQASM. OpenQASM is a general format that allows users to express a quantum program, define conditional operations manipulating quantum and qubit registers, as well as defining new quantum unitaries. We however make the choice here to support well-known gate operations. @@ -49,42 +48,6 @@ def get_openqasm_gates(): return GATE_OPENQASM -@deprecated("Please use the translate_circuit function.") -def translate_openqasm(source_circuit): - """Take in an abstract circuit, return a OpenQASM 2.0 string using IBM - Qiskit (they are the reference for OpenQASM). - - Args: - source_circuit: quantum circuit in the abstract format. - - Returns: - str: the corresponding OpenQASM program, as per IBM Qiskit. - """ - return translate_c_to_openqasm(source_circuit) - - -@deprecated("Please use the translate_circuit function.") -def _translate_openqasm2abs(source_circuit): - """Take an OpenQASM 2.0 string as input (as defined by IBM Qiskit), return - the equivalent abstract circuit. Only a subset of OpenQASM supported, mostly - to be able to go back and forth QASM and abstract representations to - leverage tools and innovation implemented to work in the QASM format. Not - designed to support elaborate QASM programs defining their own operations. - Compatible with qiskit.QuantumCircuit.from_qasm method. - - Assumes single-qubit measurement instructions only. Final qubit register - measurement is implicit. - - Args: - openqasm_string(str): an OpenQASM program, as a string, as defined by - IBM Qiskit. - - Returns: - Circuit: corresponding quantum circuit in the abstract format. - """ - return translate_c_from_openqasm(source_circuit) - - def translate_c_to_openqasm(source_circuit): """Take in an abstract circuit, return a OpenQASM 2.0 string using IBM Qiskit (they are the reference for OpenQASM). diff --git a/tangelo/linq/translator/translate_projectq.py b/tangelo/linq/translator/translate_projectq.py index 84c383b93..7a6dbc5d2 100644 --- a/tangelo/linq/translator/translate_projectq.py +++ b/tangelo/linq/translator/translate_projectq.py @@ -26,7 +26,6 @@ from tangelo.linq import Gate, Circuit from tangelo.toolboxes.operators import QubitOperator -from tangelo.helpers import deprecated def get_projectq_gates(): @@ -46,39 +45,6 @@ def get_projectq_gates(): return GATE_PROJECTQ -@deprecated("Please use the translate_circuit function.") -def translate_projectq(source_circuit): - """Take in an abstract circuit, return a string containing equivalent - projectq instructions. - - Args: - source_circuit: quantum circuit in the abstract format. - - Returns: - str: the corresponding projectq instructions (allocation , gates, - deallocation). - """ - return translate_c_to_projectq(source_circuit) - - -@deprecated("Please use the translate_circuit function.") -def _translate_projectq2abs(projectq_str): - """Convenience function to help people move away from their ProjectQ - workflow. Take ProjectQ instructions, expressed as a string, similar to one - from the ProjectQ `CommandPrinter` engine. Will bundle all qubit allocation - (resp. deallocation) at the beginning (resp. end) of the circuit. Example - available in the `examples` folder. - - Args: - projectq_str(str): ProjectQ program, as a string (Allocate, Deallocate, - gate operations...). - - Returns: - Circuit: corresponding quantum circuit in the abstract format. - """ - return translate_c_from_projectq(projectq_str) - - def translate_c_to_projectq(source_circuit): """Take in an abstract circuit, return a string containing equivalent projectq instructions. diff --git a/tangelo/linq/translator/translate_qdk.py b/tangelo/linq/translator/translate_qdk.py index b11b76660..57daf0cc7 100644 --- a/tangelo/linq/translator/translate_qdk.py +++ b/tangelo/linq/translator/translate_qdk.py @@ -22,8 +22,6 @@ may also differ. """ -from tangelo.helpers import deprecated - def get_qdk_gates(): """Map gate name of the abstract format to the equivalent gate name used in @@ -48,22 +46,6 @@ def get_qdk_gates(): return GATE_QDK -@deprecated("Please use the translate_circuit function.") -def translate_qsharp(source_circuit): - """Take in an abstract circuit, generate the corresponding Q# operation - (state prep + measurement) string, in the appropriate Q# template. The Q# - output can be written to file and will be compiled at runtime. - - Args: - source_circuit: quantum circuit in the abstract format. - - Returns: - str: The Q# code (operation + template). This needs to be written into a - .qs file, and compiled at runtime. - """ - return translate_c_to_qsharp(source_circuit) - - def translate_c_to_qsharp(source_circuit, operation="MyQsharpOperation", save_measurements=False): """Take in an abstract circuit, generate the corresponding Q# operation (state prep + measurement) string, in the appropriate Q# template. The Q# @@ -97,11 +79,13 @@ def translate_c_to_qsharp(source_circuit, operation="MyQsharpOperation", save_me # Generate Q# strings with the right syntax, order and values for the gate inputs body_str = "" for gate in source_circuit._gates: - if gate.control is not None and gate.name != "CNOT": + if gate.control is not None: control_string = '[' num_controls = len(gate.control) for i, c in enumerate(gate.control): control_string += f'qreg[{c}]]' if i == num_controls - 1 else f'qreg[{c}], ' + if num_controls > 1 and gate.name == 'CNOT': + gate.name = 'CX' if gate.name in {"H", "X", "Y", "Z", "S", "T"}: body_str += f"\t\t{GATE_QDK[gate.name]}(qreg[{gate.target[0]}]);\n" diff --git a/tangelo/linq/translator/translate_qiskit.py b/tangelo/linq/translator/translate_qiskit.py index 77d024fca..b2da2fa31 100644 --- a/tangelo/linq/translator/translate_qiskit.py +++ b/tangelo/linq/translator/translate_qiskit.py @@ -28,7 +28,6 @@ from tangelo.linq import Circuit, Gate from tangelo.toolboxes.operators import QubitOperator from tangelo.linq.helpers import pauli_of_to_string, pauli_string_to_of -from tangelo.helpers import deprecated def get_qiskit_gates(): @@ -66,19 +65,6 @@ def get_qiskit_gates(): return GATE_QISKIT -@deprecated("Please use the translate_circuit function.") -def translate_qiskit(source_circuit): - """Take in a Circuit, return an equivalent qiskit.QuantumCircuit - - Args: - source_circuit (Circuit): quantum circuit in the Tangelo format. - - Returns: - qiskit.QuantumCircuit: the corresponding quantum circuit in Qiskit format. - """ - return translate_c_to_qiskit(source_circuit) - - def translate_c_to_qiskit(source_circuit: Circuit, save_measurements=False): """Take in a Circuit, return an equivalent qiskit.QuantumCircuit @@ -172,18 +158,18 @@ def translate_c_from_qiskit(source_circuit): def translate_op_to_qiskit(qubit_operator, n_qubits): """Helper function to translate a Tangelo QubitOperator to a qiskit - PauliSumOp. Qiskit must be installed for the function to work. + SparsePauliOp. Qiskit must be installed for the function to work. Args: qubit_operator (tangelo.toolboxes.operators.QubitOperator): Self-explanatory. n_qubits (int): Number of qubits relevant to the operator. Returns: - (qiskit.opflow.primitive_ops.PauliSumOp): Qiskit qubit operator. + (qiskit.quantum_info.operators.SparsePauliOp): Qiskit qubit operator. """ # Import qiskit qubit operator. - from qiskit.opflow.primitive_ops import PauliSumOp + from qiskit.quantum_info.operators import SparsePauliOp # Convert each term sequencially. term_list = list() @@ -193,15 +179,15 @@ def translate_op_to_qiskit(qubit_operator, n_qubits): # Reverse the string because of qiskit convention. term_list += [(term_string[::-1], coeff)] - return PauliSumOp.from_list(term_list) + return SparsePauliOp.from_list(term_list) def translate_op_from_qiskit(qubit_operator): - """Helper function to translate a qiskit PauliSumOp to a Tangelo + """Helper function to translate a qiskit SparsePauliOp to a Tangelo QubitOperator. Args: - qubit_operator (qiskit.opflow.primitive_ops.PauliSumOp): Self-explanatory. + qubit_operator (qiskit.quantum_info.operators.SparsePauliOp): Self-explanatory. Returns: (tangelo.toolboxes.operators.QubitOperator): Tangelo qubit operator. @@ -209,11 +195,11 @@ def translate_op_from_qiskit(qubit_operator): # Create a dictionary to append all terms at once. terms_dict = dict() - for pauli_word in qubit_operator: + terms = qubit_operator.to_list() + for term_string, coeff in terms: # Inversion of the string because of qiskit ordering. - term_string = pauli_word.to_pauli_op().primitive.to_label()[::-1] - term_tuple = pauli_string_to_of(term_string) - terms_dict[tuple(term_tuple)] = complex(pauli_word.coeffs) + term_tuple = pauli_string_to_of(term_string[::-1]) + terms_dict[tuple(term_tuple)] = coeff # Create and copy the information into a new QubitOperator. tangelo_op = QubitOperator() diff --git a/tangelo/linq/translator/translate_qubitop.py b/tangelo/linq/translator/translate_qubitop.py index abe54d651..d38626b7c 100644 --- a/tangelo/linq/translator/translate_qubitop.py +++ b/tangelo/linq/translator/translate_qubitop.py @@ -20,6 +20,7 @@ from tangelo.linq.translator.translate_qulacs import translate_op_from_qulacs, translate_op_to_qulacs from tangelo.linq.translator.translate_pennylane import translate_op_from_pennylane, translate_op_to_pennylane from tangelo.linq.translator.translate_projectq import translate_op_from_projectq, translate_op_to_projectq +from tangelo.linq.translator.translate_sympy import translate_op_to_sympy FROM_TANGELO = { @@ -27,7 +28,8 @@ "cirq": translate_op_to_cirq, "qulacs": translate_op_to_qulacs, "pennylane": translate_op_to_pennylane, - "projectq": translate_op_to_projectq + "projectq": translate_op_to_projectq, + "sympy": translate_op_to_sympy } TO_TANGELO = { @@ -71,10 +73,10 @@ def translate_operator(qubit_operator, source, target, n_qubits=None): raise NotImplementedError(f"Qubit operator conversion from {source} to {target} is not supported.") # For translation functions that need an explicit number of qubits. - if target in {"qiskit"}: + if target in {"qiskit", "sympy"}: # The count_qubits function has no way to detect the number of # qubits when an operator is only a tensor product of I. - if qubit_operator == QubitOperator((), qubit_operator.constant): + if qubit_operator == QubitOperator((), qubit_operator.constant) and n_qubits is None: raise ValueError("The number of qubits (n_qubits) must be provided.") n_qubits = count_qubits(qubit_operator) if n_qubits is None else n_qubits qubit_operator = FROM_TANGELO[target](qubit_operator, n_qubits) diff --git a/tangelo/linq/translator/translate_qulacs.py b/tangelo/linq/translator/translate_qulacs.py index 4e899802d..c8b7a6d6c 100644 --- a/tangelo/linq/translator/translate_qulacs.py +++ b/tangelo/linq/translator/translate_qulacs.py @@ -25,7 +25,6 @@ from numpy import exp, cos, sin from tangelo.toolboxes.operators import QubitOperator -from tangelo.helpers import deprecated def get_qulacs_gates(): @@ -62,24 +61,6 @@ def get_qulacs_gates(): return GATE_QULACS -@deprecated("Please use the translate_circuit function.") -def translate_qulacs(source_circuit, noise_model=None): - """Take in an abstract circuit, return an equivalent qulacs QuantumCircuit - instance. If provided with a noise model, will add noisy gates at - translation. Not very useful to look at, as qulacs does not provide much - information about the noisy gates added when printing the "noisy circuit". - - Args: - source_circuit: quantum circuit in the abstract format. - noise_model: A NoiseModel object from this package, located in the - noisy_simulation subpackage. - - Returns: - qulacs.QuantumCircuit: the corresponding qulacs quantum circuit. - """ - return translate_c_to_qulacs(source_circuit, noise_model) - - def translate_c_to_qulacs(source_circuit, noise_model=None, save_measurements=False): """Take in an abstract circuit, return an equivalent qulacs QuantumCircuit instance. If provided with a noise model, will add noisy gates at @@ -108,6 +89,8 @@ def translate_c_to_qulacs(source_circuit, noise_model=None, save_measurements=Fa # Maps the gate information properly. Different for each backend (order, values) for gate in source_circuit._gates: + if gate.name == 'CNOT' and len(gate.control) > 1: + gate.name = 'CX' if gate.name in {"H", "X", "Y", "Z", "S", "T"}: (GATE_QULACS[gate.name])(target_circuit, gate.target[0]) elif gate.name in {"CH", "CX", "CY", "CZ"}: diff --git a/tangelo/linq/translator/translate_stim.py b/tangelo/linq/translator/translate_stim.py new file mode 100644 index 000000000..16a18c02e --- /dev/null +++ b/tangelo/linq/translator/translate_stim.py @@ -0,0 +1,129 @@ +# Copyright 2023 Good Chemistry Company. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""Functions helping with quantum circuit and operator format conversion between +Tangelo format and stim format. + +In order to produce an equivalent circuit for the target backend, it is +necessary to account for: +- how the gate names differ between the source backend to the target backend. +- how the order and conventions for some of the inputs to the gate operations + may also differ. +""" + +from tangelo.linq import Circuit +from tangelo.linq.helpers.circuits.clifford_circuits import decompose_gate_to_cliffords + + +def get_stim_gates(): + """Map gate name of the Tangelo format to the equivalent add_gate method of + Stim's CircuitInstruction class API and supported gates: + https://github.com/quantumlib/Stim/blob/main/doc/gates.md + """ + GATE_STIM = dict() + for g in {'I', 'H', 'X', 'Y', 'Z', 'S', 'CX', 'CY', 'CZ', 'SWAP'}: + GATE_STIM[g] = g.lower() + GATE_STIM["CNOT"] = "cx" + GATE_STIM["SDAG"] = "s_dag" + GATE_STIM["MEASURE"] = "m" + return GATE_STIM + + +def translate_tableau(source_circuit): + """Take in an abstract circuit and build it directly into a stim TableauSimulator + instance. For noiseless expectations values, this method is faster than translating + into a stim circuit object first and then building it into the stim TableauSimulator. + + Args: + source_circuit (Circuit): quantum circuit in the abstract format. + + Returns: + stim.TableauSimulator: the corresponding Tableau Simulator + """ + + import stim + + GATE_STIM = get_stim_gates() + target_circuit = stim.TableauSimulator() + + # Maps the gate information properly. + for gate in source_circuit._gates: + if gate.name in {"H", "X", "Y", "Z", "S", "SDAG"}: + bar = getattr(target_circuit, GATE_STIM[gate.name]) + bar(gate.target[0]) + elif gate.name in {"RY", "RX", "RZ", "PHASE"}: + clifford_decomposition = decompose_gate_to_cliffords(gate) + for cliff_gate in clifford_decomposition: + bar = getattr(target_circuit, GATE_STIM[cliff_gate.name]) + bar(gate.target[0]) + elif gate.name in {"CX", "CY", "CZ", "CNOT"}: + bar = getattr(target_circuit, GATE_STIM[gate.name]) + bar(gate.control[0], gate.target[0]) + elif gate.name in {"SWAP"}: + bar = getattr(target_circuit, GATE_STIM[gate.name]) + bar(gate.target[0], gate.target[1]) + return target_circuit + + +def translate_c_to_stim(source_circuit, noise_model=None): + """Take in an abstract circuit, return an equivalent stim QuantumCircuit + instance. If provided with a noise model, will add noisy gates at + translation. + + Args: + source_circuit (Circuit): quantum circuit in the abstract format. + noise_model (NoiseModel): The noise model to use. + + Returns: + stim.Circuit: the corresponding stim quantum circuit. + """ + + import stim + GATE_STIM = get_stim_gates() + target_circuit = stim.Circuit() + for qubit in range(source_circuit.width): + target_circuit.append("I", [qubit]) + + # Maps the gate information properly. Different for each backend (order, values) + for gate in source_circuit._gates: + if gate.name in {"H", "X", "Y", "Z", "S", "SDAG"}: + target_circuit.append(GATE_STIM[gate.name], [gate.target[0]]) + elif gate.name in {"RY", "RX", "RZ", "PHASE"}: + clifford_decomposition = decompose_gate_to_cliffords(gate) + for cliff_gate in clifford_decomposition: + target_circuit.append(GATE_STIM[cliff_gate.name], [cliff_gate.target[0]]) + elif gate.name in {"CX", "CY", "CZ", "CNOT"}: + target_circuit.append(GATE_STIM[gate.name], [gate.control[0], gate.target[0]]) + elif gate.name in {"SWAP"}: + target_circuit.append(GATE_STIM[gate.name], [gate.target[0], gate.target[1]]) + + if noise_model and (gate.name in noise_model.noisy_gates): + for nt, np in noise_model._quantum_errors[gate.name]: + if nt == 'pauli': + target_circuit.append(stim.CircuitInstruction('PAULI_CHANNEL_1', [gate.target[0]], [np[0], np[1], np[2]])) + if gate.control is not None: + target_circuit.append(stim.CircuitInstruction('PAULI_CHANNEL_1', [gate.control[0]], [np[0], np[1], np[2]])) + elif nt == 'depol': + depol_list = [t for t in gate.target] + if gate.control is not None: + depol_list += [c for c in gate.control] + n_depol = len(depol_list) + if n_depol == 1: + target_circuit.append(stim.CircuitInstruction('DEPOLARIZE1', [gate.target[0]], [np])) + elif n_depol == 2: + target_circuit.append(stim.CircuitInstruction('DEPOLARIZE2', [gate.target[0], gate.control[0]], [np])) + else: + raise ValueError(f'{gate.name} has more than 2 qubits, stim DepolarizingNoise only supports 1- and 2-qubits') + + return target_circuit diff --git a/tangelo/linq/translator/translate_sympy.py b/tangelo/linq/translator/translate_sympy.py new file mode 100644 index 000000000..a5d2a368f --- /dev/null +++ b/tangelo/linq/translator/translate_sympy.py @@ -0,0 +1,234 @@ +# Copyright 2023 Good Chemistry Company. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""Functions helping with quantum circuit format conversion between abstract +format and SYMPY format + +In order to produce an equivalent circuit for the target backend, it is necessary +to account for: +- how the gate names differ between the source backend to the target backend. +- how the order and conventions for some of the inputs to the gate operations + may also differ. +""" + +from tangelo.linq.helpers import pauli_of_to_string + + +def rx_gate(target, theta): + """Implementation of the RX gate with a unitary matrix. + + Args: + target (int): Target qubit. + theta (float): Rotation angle. + + Returns: + sympy.physics.quantum.gate.UGate: Self-explanatory. + """ + + from sympy import ImmutableMatrix, sin, cos, I + from sympy.physics.quantum.gate import UGate + + cos_term = cos(theta / 2) + sin_term = -I*sin(theta / 2) + rx_matrix = ImmutableMatrix([[cos_term, sin_term], [sin_term, cos_term]]) + + return UGate(target, rx_matrix) + + +def ry_gate(target, theta): + """Implementation of the RY gate with a unitary matrix. + + Args: + target (int): Target qubit. + theta (float): Rotation angle. + + Returns: + sympy.physics.quantum.gate.UGate: Self-explanatory. + """ + + from sympy import ImmutableMatrix, sin, cos + from sympy.physics.quantum.gate import UGate + + cos_term = cos(theta / 2) + sin_term = sin(theta / 2) + ry_matrix = ImmutableMatrix([[cos_term, -sin_term], [sin_term, cos_term]]) + + return UGate(target, ry_matrix) + + +def rz_gate(target, theta): + """Implementation of the RZ gate with a unitary matrix. + + Args: + target (int): Target qubit. + theta (float): Rotation angle. + + Returns: + sympy.physics.quantum.gate.UGate: Self-explanatory. + """ + + from sympy import ImmutableMatrix, I, exp + from sympy.physics.quantum.gate import UGate + + rz_matrix = ImmutableMatrix([[exp(- I * theta / 2), 0], [0, exp(I * theta / 2)]]) + + return UGate(target, rz_matrix) + + +def p_gate(target, theta): + """Implementation of the PHASE gate with a unitary matrix. + + Args: + target (int): Target qubit. + theta (float): Phase parameter. + + Returns: + sympy.physics.quantum.gate.UGate: Self-explanatory. + """ + + from sympy import ImmutableMatrix, I, exp + from sympy.physics.quantum.gate import UGate + + p_matrix = ImmutableMatrix([[1, 0], [0, exp(I * theta)]]) + + return UGate(target, p_matrix) + + +def controlled_gate(gate_function): + """Change a one-qubit gate to a controlled gate. + + Args: + gate_function (sympy.physics.quantum.gate): Class for a specific gate. + + Returns: + function: Function wrapping a gate with CGate. + """ + + from sympy.physics.quantum.gate import CGate + + def cgate(control, target, *args, **kwargs): + return CGate(control, gate_function(target, *args, **kwargs)) + + return cgate + + +def get_sympy_gates(): + """Map gate name of the abstract format to the equivalent methods of the + sympy backend (symbolic simulation). + """ + + import sympy.physics.quantum.gate as SYMPYGate + + GATE_SYMPY = dict() + GATE_SYMPY["H"] = SYMPYGate.HadamardGate + GATE_SYMPY["X"] = SYMPYGate.XGate + GATE_SYMPY["Y"] = SYMPYGate.YGate + GATE_SYMPY["Z"] = SYMPYGate.ZGate + GATE_SYMPY["S"] = SYMPYGate.PhaseGate + GATE_SYMPY["T"] = SYMPYGate.TGate + GATE_SYMPY["PHASE"] = p_gate + GATE_SYMPY["SWAP"] = SYMPYGate.SwapGate + GATE_SYMPY["RX"] = rx_gate + GATE_SYMPY["RY"] = ry_gate + GATE_SYMPY["RZ"] = rz_gate + GATE_SYMPY["RX"] = rx_gate + GATE_SYMPY["CH"] = controlled_gate(SYMPYGate.HadamardGate) + GATE_SYMPY["CNOT"] = SYMPYGate.CNotGate + GATE_SYMPY["CX"] = SYMPYGate.CNotGate + GATE_SYMPY["CY"] = controlled_gate(SYMPYGate.YGate) + GATE_SYMPY["CZ"] = controlled_gate(SYMPYGate.ZGate) + GATE_SYMPY["CRX"] = controlled_gate(rx_gate) + GATE_SYMPY["CRY"] = controlled_gate(ry_gate) + GATE_SYMPY["CRZ"] = controlled_gate(rz_gate) + GATE_SYMPY["CS"] = controlled_gate(SYMPYGate.PhaseGate) + GATE_SYMPY["CT"] = controlled_gate(SYMPYGate.TGate) + GATE_SYMPY["CPHASE"] = controlled_gate(p_gate) + + return GATE_SYMPY + + +def translate_c_to_sympy(source_circuit): + """Take in an abstract circuit, return a quantum circuit object as defined + in the Python SYMPY SDK. + + Args: + source_circuit: quantum circuit in the abstract format. + + Returns: + SYMPY.circuits.Circuit: quantum circuit in Python SYMPY SDK format. + """ + + from sympy import symbols + + GATE_SYMPY = get_sympy_gates() + + # Identity as an empty circuit. + target_circuit = 1 + + # Map the gate information properly. + for gate in reversed(source_circuit._gates): + # If the parameter is a string, we use it as a variable. + if gate.parameter and isinstance(gate.parameter, str): + gate.parameter = symbols(gate.parameter, real=True) + + if gate.name in {"H", "X", "Y", "Z"}: + target_circuit *= GATE_SYMPY[gate.name](gate.target[0]) + elif gate.name in {"T", "S"} and gate.parameter == "": + target_circuit *= GATE_SYMPY[gate.name](gate.target[0]) + elif gate.name in {"PHASE", "RX", "RY", "RZ"}: + target_circuit *= GATE_SYMPY[gate.name](gate.target[0], gate.parameter) + elif gate.name in {"CNOT", "CH", "CX", "CY", "CZ", "CS", "CT"}: + target_circuit *= GATE_SYMPY[gate.name](gate.control[0], gate.target[0]) + elif gate.name in {"SWAP"}: + target_circuit *= GATE_SYMPY[gate.name](gate.target[0], gate.target[1]) + elif gate.name in {"CRX", "CRY", "CRZ", "CPHASE"}: + target_circuit *= GATE_SYMPY[gate.name](gate.control[0], gate.target[0], gate.parameter) + else: + raise ValueError(f"Gate '{gate.name}' not supported on backend SYMPY") + + return target_circuit + + +def translate_op_to_sympy(qubit_operator, n_qubits): + """Helper function to translate a Tangelo QubitOperator to a sympy linear + combination of tensor products. + + Args: + qubit_operator (tangelo.toolboxes.operators.QubitOperator): Self-explanatory. + n_qubits (int): The number of qubit the operator acts on. + + Returns: + sympy.core.add.Add: Summation of sympy.physics.quantum.TensorProduct + objects. + """ + from sympy import Matrix, I, zeros + from sympy.physics.quantum import TensorProduct + + # Pauli string to sympy Pauli algebra objects. + map_to_paulis = { + "I": Matrix([[1, 0], [0, 1]]), + "X": Matrix([[0, 1], [1, 0]]), + "Y": Matrix([[0, -I], [I, 0]]), + "Z": Matrix([[1, 0], [0, -1]]) + } + + # Contruct the TensorProduct objects. + sum_tensor_paulis = zeros(2**n_qubits, 2**n_qubits) + for term_tuple, coeff in qubit_operator.terms.items(): + term_string = pauli_of_to_string(term_tuple, n_qubits) + paulis = [map_to_paulis[p] for p in term_string[::-1]] + tensor_paulis = TensorProduct(*paulis) + sum_tensor_paulis += coeff * tensor_paulis + + return sum_tensor_paulis diff --git a/tangelo/molecule_library.py b/tangelo/molecule_library.py index f6367caf4..2ca1e6991 100644 --- a/tangelo/molecule_library.py +++ b/tangelo/molecule_library.py @@ -16,7 +16,9 @@ from tangelo import Molecule, SecondQuantizedMolecule +from tangelo.helpers.utils import is_package_installed +has_pyscf = is_package_installed("pyscf") # Dihydrogen. xyz_H2 = [ @@ -42,9 +44,9 @@ ] mol_H4_sto3g = SecondQuantizedMolecule(xyz_H4, q=0, spin=0, basis="sto-3g") mol_H4_sto3g_symm = SecondQuantizedMolecule(xyz_H4, q=0, spin=0, basis="sto-3g", symmetry=True) -mol_H4_minao = SecondQuantizedMolecule(xyz_H4, q=0, spin=0, basis="minao") +mol_H4_minao = SecondQuantizedMolecule(xyz_H4, q=0, spin=0, basis="minao") if has_pyscf else None mol_H4_cation_sto3g = SecondQuantizedMolecule(xyz_H4, q=1, spin=1, basis="sto-3g") -mol_H4_doublecation_minao = SecondQuantizedMolecule(xyz_H4, q=2, spin=0, basis="minao") +mol_H4_doublecation_minao = SecondQuantizedMolecule(xyz_H4, q=2, spin=0, basis="minao") if has_pyscf else None mol_H4_doublecation_321g = SecondQuantizedMolecule(xyz_H4, q=2, spin=0, basis="3-21g") mol_H4_sto3g_uhf_a1_frozen = SecondQuantizedMolecule(xyz_H4, q=0, spin=0, basis="sto-3g", uhf=True, frozen_orbitals=[[1], []]) @@ -61,7 +63,7 @@ ("H", ( 0.300000000000, -0.923305061153, 0.)), ("H", ( 0.785410196625, -0.570633909777, 0.)) ] -mol_H10_minao = SecondQuantizedMolecule(xyz_H10, q=0, spin=0, basis="minao") +mol_H10_minao = SecondQuantizedMolecule(xyz_H10, q=0, spin=0, basis="minao") if has_pyscf else None mol_H10_321g = SecondQuantizedMolecule(xyz_H10, q=0, spin=0, basis="3-21g") diff --git a/tangelo/problem_decomposition/dmet/_helpers/dmet_bath.py b/tangelo/problem_decomposition/dmet/_helpers/dmet_bath.py index 991d230e5..4a667bb94 100644 --- a/tangelo/problem_decomposition/dmet/_helpers/dmet_bath.py +++ b/tangelo/problem_decomposition/dmet/_helpers/dmet_bath.py @@ -21,7 +21,7 @@ import numpy as np -def dmet_fragment_bath(mol, t_list, temp_list, onerdm_low): +def dmet_fragment_bath(mol, t_list, temp_list, onerdm_low, virtual_orbital_threshold=1e-13, verbose=False): """ Construct the bath orbitals for DMET fragment calculation. Args: @@ -32,6 +32,10 @@ def dmet_fragment_bath(mol, t_list, temp_list, onerdm_low): orbitals (int). onerdm_low (numpy.array): One-particle RDM from the low-level calculation (float64). + virtual_orbital_threshold (float): Occupation threshold for the density + matrix, used to discard virtual orbitals. + verbose (bool): Print the orbital occupancy eigenvalues for prototyping + purposes (setting virtual_orbital_threshold). Returns: numpy.array: The bath orbitals (float64). @@ -44,8 +48,13 @@ def dmet_fragment_bath(mol, t_list, temp_list, onerdm_low): # Diagonalize it e, c = np.linalg.eigh(onerdm_embedded) - # Sort the eigenvectors with the eigenvalues - e_sorted, c_sorted = dmet_bath_orb_sort(t_list, e, c) + # Sort the eigenvectors with the eigenvalues (should be positive unless + # there is numerical noise, therefore we take the absolute values). + e = np.abs(e) + if verbose: + print(f"\t{e}\n") + + e_sorted, c_sorted = dmet_bath_orb_sort(t_list, e, c, virtual_orbital_threshold) # Add the core contribution bath_orb, e_core = dmet_add_to_bath_orb(mol, t_list, temp_list, e_sorted, c_sorted) @@ -90,7 +99,7 @@ def dmet_onerdm_embed(mol, temp_list, onerdm_before): return onerdm_temp3 -def dmet_bath_orb_sort(t_list, e_before, c_before): +def dmet_bath_orb_sort(t_list, e_before, c_before, virtual_orbital_threshold): """ Sort the bath orbitals with the eigenvalues (orbital energies). Args: @@ -98,6 +107,8 @@ def dmet_bath_orb_sort(t_list, e_before, c_before): e_before (numpy.array): Orbitals energies before sorting (float64). c_before (numpy.array): Coefficients of the orbitals before sorting (float64). + virtual_orbital_threshold (float): Occupation threshold for the density + matrix, used to discard virtual orbitals. Returns: numpy.array: Sorted orbital energies (float64). @@ -108,10 +119,11 @@ def dmet_bath_orb_sort(t_list, e_before, c_before): new_index = np.maximum(-e_before, e_before - 2.0).argsort() # Throw away some orbitals above threshold - thresh_orb = np.sum(-np.maximum(-e_before, e_before - 2.0)[new_index] > 1e-13) + thresh_orb = np.sum(-np.maximum(-e_before, e_before - 2.0)[new_index] > virtual_orbital_threshold) # Determine the number of bath orbitals norb = min(np.sum(thresh_orb), t_list[0]) + t_list.append(norb) # Sort the bath orbitals with its energies diff --git a/tangelo/problem_decomposition/dmet/_helpers/dmet_orbitals.py b/tangelo/problem_decomposition/dmet/_helpers/dmet_orbitals.py index f4e11f0e7..a290d43cd 100644 --- a/tangelo/problem_decomposition/dmet/_helpers/dmet_orbitals.py +++ b/tangelo/problem_decomposition/dmet/_helpers/dmet_orbitals.py @@ -22,7 +22,6 @@ PySCF program. However, using Boys localization is not recommended. """ -from pyscf import scf, ao2mo import numpy as np @@ -68,6 +67,9 @@ def __init__(self, mol, mf, active_space, localization_function, uhf): localization_function (string): Localization scheme. uhf (bool): Flag for an unrestricted mean-field. """ + from pyscf import scf, ao2mo + self.pyscfscf = scf + self.pyscfao2mo = ao2mo # General quantities. self.mol_full = mol @@ -96,14 +98,17 @@ def _restricted_init(self): if self.mol_full.spin == 0: # Obtain the elements from the low-level SCF calculations. low_scf_dm = self.mf_full.mo_coeff @ np.diag(self.mf_full.mo_occ) @ self.mf_full.mo_coeff.T - low_scf_twoint = scf.hf.get_veff(self.mf_full.mol, low_scf_dm, 0, 0, 1) + low_scf_twoint = self.pyscfscf.hf.get_veff(self.mf_full.mol, low_scf_dm, 0, 0, 1) self.low_scf_fock = self.mf_full.mol.intor("cint1e_kin_sph") + self.mf_full.mol.intor("cint1e_nuc_sph") + low_scf_twoint + # Add effective core potential to Fock matrix if applicable. + if len(self.mol_full._ecpbas) > 0: + self.low_scf_fock += self.mf_full.mol.intor_symmetric('ECPscalar') # Define the core space if possible (Initial calculations treat the entire molecule ...). core_mo_dm = np.array(self.mf_full.mo_occ, copy=True) core_mo_dm[self.dmet_active_orbitals == 1] = 0 core_ao_dm = self.mf_full.mo_coeff @ np.diag(core_mo_dm) @ self.mf_full.mo_coeff.T - core_twoint = scf.hf.get_veff(self.mf_full.mol, core_ao_dm, 0, 0, 1) + core_twoint = self.pyscfscf.hf.get_veff(self.mf_full.mol, core_ao_dm, 0, 0, 1) core_oneint = self.low_scf_fock - low_scf_twoint + core_twoint # Define the energies and matrix elements based on the localized orbitals. @@ -137,11 +142,11 @@ def _restricted_init(self): rdm_total = np.array((rdm_a, rdm_b)) overlap = np.eye(self.number_active_orbitals) - two_int = scf.hf.get_veff(self.mol_full, rdm_total, 0, 0, 1) + two_int = self.pyscfscf.hf.get_veff(self.mol_full, rdm_total, 0, 0, 1) new_fock_alpha = self.active_oneint + (self.localized_mo.T @ two_int[0] @ self.localized_mo) new_fock_beta = self.active_oneint + (self.localized_mo.T @ two_int[1] @ self.localized_mo) fock_total = np.array((new_fock_alpha, new_fock_beta)) - self.active_fock = scf.rohf.get_roothaan_fock(fock_total, rdm_total, overlap) + self.active_fock = self.pyscfscf.rohf.get_roothaan_fock(fock_total, rdm_total, overlap) def _unrestricted_init(self): """Initialize the attributes for an unrestricted mean-field.""" @@ -185,13 +190,13 @@ def dmet_fragment_hamiltonian(self, bath_orb, norb_high, onerdm_core): # Calculate the fock matrix. density_matrix = self.localized_mo @ onerdm_core @ self.localized_mo.T - two_int = scf.hf.get_veff(self.mol_full, density_matrix, 0, 0, 1) + two_int = self.pyscfscf.hf.get_veff(self.mol_full, density_matrix, 0, 0, 1) new_fock = self.active_oneint + (self.localized_mo.T @ two_int @ self.localized_mo) frag_fock = bath_orb[:, : norb_high].T @ new_fock @ bath_orb[:, : norb_high] # Calculate the two-electron integrals. coefficients = np.dot(self.localized_mo, bath_orb[:, : norb_high]) - frag_twoint = ao2mo.outcore.full_iofree(self.mol_full, coefficients, compact=False).reshape( + frag_twoint = self.pyscfao2mo.outcore.full_iofree(self.mol_full, coefficients, compact=False).reshape( norb_high, norb_high, norb_high, norb_high) return frag_oneint, frag_fock, frag_twoint diff --git a/tangelo/problem_decomposition/dmet/_helpers/dmet_scf.py b/tangelo/problem_decomposition/dmet/_helpers/dmet_scf.py index 9849e7049..6c8d6247c 100644 --- a/tangelo/problem_decomposition/dmet/_helpers/dmet_scf.py +++ b/tangelo/problem_decomposition/dmet/_helpers/dmet_scf.py @@ -17,7 +17,6 @@ The fragment SCF calculation for DMET calculation is done here. """ -from pyscf import gto, scf, ao2mo import numpy as np @@ -41,7 +40,7 @@ def dmet_fragment_scf_rhf(t_list, two_ele, fock, n_electrons, n_orbitals, guess_ (float64). pyscf.gto.Mole: The molecule to simulate (Fragment calculation). """ - + from pyscf import gto, scf, ao2mo # Deep copy the fock matrix fock_frag_copy = fock.copy() @@ -93,6 +92,7 @@ def dmet_fragment_scf_rohf_uhf(nele_ab, two_ele, fock, n_electrons, n_orbitals, (float64). pyscf.gto.Mole: The molecule to simulate (Fragment calculation). """ + from pyscf import gto, scf, ao2mo # Deep copy the fock matrix fock_frag_copy = fock.copy() diff --git a/tangelo/problem_decomposition/dmet/dmet_problem_decomposition.py b/tangelo/problem_decomposition/dmet/dmet_problem_decomposition.py index c17f17326..c9b541a73 100644 --- a/tangelo/problem_decomposition/dmet/dmet_problem_decomposition.py +++ b/tangelo/problem_decomposition/dmet/dmet_problem_decomposition.py @@ -15,18 +15,22 @@ """Employ DMET as a problem decomposition technique.""" from enum import Enum +from typing import Union, List, Callable, Dict + import numpy as np -from pyscf import gto, scf import scipy import warnings +from tangelo.helpers.utils import is_package_installed +from tangelo import SecondQuantizedMolecule from tangelo.problem_decomposition.dmet import _helpers as helpers from tangelo.problem_decomposition.problem_decomposition import ProblemDecomposition from tangelo.problem_decomposition.electron_localization import iao_localization, meta_lowdin_localization, nao_localization -from tangelo.problem_decomposition.dmet.fragment import SecondQuantizedDMETFragment from tangelo.algorithms import FCISolver, CCSDSolver, VQESolver from tangelo.toolboxes.post_processing.mc_weeny_rdm_purification import mcweeny_purify_2rdm -from tangelo.toolboxes.molecular_computation.rdms import pad_rdms_with_frozen_orbitals +from tangelo.toolboxes.molecular_computation.rdms import pad_rdms_with_frozen_orbitals_restricted, \ + pad_rdms_with_frozen_orbitals_unrestricted +from tangelo.toolboxes.molecular_computation.integral_solver_pyscf import mol_to_pyscf class Localization(Enum): @@ -68,49 +72,53 @@ class DMETProblemDecomposition(ProblemDecomposition): options. If only a single dictionary is passed, the same options are applied for every solver. This will raise an error if different solvers are parsed. + virtual_orbital_threshold (float): Occupation threshold for the virtual + orbital space. Setting it to 0. is the equivalent of turning off + the virtual orbital space truncation. + Default=1e-13. verbose (bool) : Flag for DMET verbosity. """ def __init__(self, opt_dict): - + if not is_package_installed("pyscf"): + raise ModuleNotFoundError(f"Using {self.__class__.__name__} requires the installation of the pyscf package.") + from pyscf import gto, scf + from tangelo.problem_decomposition.dmet.fragment import SecondQuantizedDMETFragment default_ccsd_options = dict() default_fci_options = dict() default_vqe_options = {"qubit_mapping": "jw", "initial_var_params": "ones", "verbose": False} - default_options = {"molecule": None, - "electron_localization": Localization.meta_lowdin, - "fragment_atoms": list(), - "fragment_solvers": "ccsd", - "fragment_frozen_orbitals": list(), - "optimizer": self._default_optimizer, - "initial_chemical_potential": 0.0, - "solvers_options": list(), - "verbose": False} + copt_dict = opt_dict.copy() + self.molecule: SecondQuantizedMolecule = copt_dict.pop("molecule", None) + self.electron_localization: Localization = copt_dict.pop("electron_localization", Localization.meta_lowdin) + self.fragment_atoms: List[int] = copt_dict.pop("fragment_atoms", list()) + self.fragment_solvers: Union[str, List[str]] = copt_dict.pop("fragment_solvers", "ccsd") + self.fragment_frozen_orbitals: List[List[Union[int, str]]] = copt_dict.pop("fragment_frozen_orbitals", list()) + self.optimizer: Callable[..., float] = copt_dict.pop("optimizer", self._default_optimizer) + self.initial_chemical_potential: float = copt_dict.pop("initial_chemical_potential", 0.0) + self.solvers_options: List[dict] = copt_dict.pop("solvers_options", list()) + self.virtual_orbital_threshold: float = copt_dict.pop("virtual_orbital_threshold", 1e-13) + self.verbose: bool = copt_dict.pop("verbose", False) self.builtin_localization = set(Localization) - # Initialize with default values - self.__dict__ = default_options - # Overwrite default values with user-provided ones, if they correspond to a valid keyword - for k, v in opt_dict.items(): - if k in default_options: - setattr(self, k, v) - else: - raise KeyError(f"Keyword :: {k}, not available in DMETProblemDecomposition.") + self.fragment_builder = SecondQuantizedDMETFragment + + if len(copt_dict) > 0: + raise KeyError(f"The following keywords are not supported in {self.__class__.__name__}: \n {copt_dict.keys()}") # Raise error/warnings if input is not as expected if not self.molecule: raise ValueError(f"A SecondQuantizedMolecule object must be provided when instantiating DMETProblemDecomposition.") - if self.molecule.uhf or self.molecule.spin != 0: - raise NotImplementedError("ROHF-DMET and UHF-DMET have been disabled temporarily for code validation purposes.") + self.uhf = self.molecule.uhf # Converting our interface to pyscf.mol.gto and pyscf.scf (used by this # code). self.mean_field = self.molecule.mean_field - self.molecule = self.molecule.to_pyscf(self.molecule.basis) + self.molecule = mol_to_pyscf(self.molecule, self.molecule.basis, ecp=self.molecule.ecp) # If fragment_atoms is detected as a nested list of int, atoms are reordered to be # consistent with a list of numbers representing the number of atoms in each fragment. @@ -143,7 +151,7 @@ def __init__(self, opt_dict): # Force recomputing the mean field if the atom ordering has been changed. warnings.warn("The mean field will be recomputed even if one has been provided by the user.", RuntimeWarning) - self.mean_field = scf.RHF(self.molecule) + self.mean_field = scf.UHF(self.molecule) if self.uhf else scf.RHF(self.molecule) self.mean_field.verbose = 0 self.mean_field.scf() @@ -194,7 +202,7 @@ def __init__(self, opt_dict): self.onerdm_low = None # If save_results in _oneshot_loop is True, the dict is populated. - self.solver_fragment_dict = dict() + self.solver_fragment_dict: Dict[int, VQESolver] = dict() # To keep track the number of iteration (was done with an energy list # before). @@ -238,13 +246,19 @@ def build(self): raise TypeError(f"Invalid electron localization function. Expecting a function.") # Construct orbital object. - self.orbitals = helpers._orbitals(self.molecule, self.mean_field, range(self.molecule.nao_nr()), self.electron_localization, False) + self.orbitals = helpers._orbitals(self.molecule, self.mean_field, range(self.molecule.nao_nr()), self.electron_localization, self.uhf) # TODO: remove last argument, combining fragments not supported. self.orb_list, self.orb_list2, _ = helpers._fragment_constructor(self.molecule, self.fragment_atoms, 0) # Calculate the 1-RDM for the entire molecule. - self.onerdm_low = helpers._low_rdm_rhf(self.orbitals.active_fock, self.orbitals.number_active_electrons) + if self.molecule.spin == 0 and not self.uhf: + self.onerdm_low = helpers._low_rdm_rhf(self.orbitals.active_fock, self.orbitals.number_active_electrons) + else: + self.onerdm_low = helpers._low_rdm_rohf_uhf(self.orbitals.active_fock_alpha, + self.orbitals.active_fock_beta, + self.orbitals.number_active_electrons_alpha, + self.orbitals.number_active_electrons_beta) def simulate(self): """Perform DMET loop to optimize the chemical potential. It converges @@ -337,8 +351,12 @@ def _build_scf_fragments(self, chemical_potential): t_list.append(norb) temp_list = self.orb_list2[i] + if self.verbose: + print(f"\tSCF Occupancy Eigenvalues for Fragment Number : # {i}") + # Construct bath orbitals. - bath_orb, e_occupied = helpers._fragment_bath(self.orbitals.mol_full, t_list, temp_list, self.onerdm_low) + bath_orb, e_occupied = helpers._fragment_bath(self.orbitals.mol_full, t_list, temp_list, + self.onerdm_low, self.virtual_orbital_threshold, self.verbose) # Obtain one particle rdm for a fragment. norb_high, nelec_high, onerdm_high = helpers._fragment_rdm(t_list, bath_orb, e_occupied, @@ -349,11 +367,22 @@ def _build_scf_fragments(self, chemical_potential): # Construct guess orbitals for fragment SCF calculations. # Carry out SCF calculation for a fragment. - guess_orbitals = helpers._fragment_guess_rhf(t_list, bath_orb, chemical_potential, norb_high, nelec_high, - self.orbitals.active_fock) - mf_fragment, fock_frag_copy, mol_frag = helpers._fragment_scf_rhf( - t_list, two_ele, fock, nelec_high, norb_high, guess_orbitals, - chemical_potential) + if self.uhf or self.molecule.spin != 0: + guess_orbitals, nelec_high_ab = helpers._fragment_guess_rohf_uhf( + t_list, bath_orb, chemical_potential, norb_high, nelec_high, + self.orbitals.active_fock_alpha, self.orbitals.active_fock_beta, + self.orbitals.number_active_electrons_alpha, + self.orbitals.number_active_electrons_beta) + + mf_fragment, fock_frag_copy, mol_frag = helpers._fragment_scf_rohf_uhf( + nelec_high_ab, two_ele, fock, nelec_high, norb_high, + guess_orbitals, chemical_potential, self.uhf) + else: + guess_orbitals = helpers._fragment_guess_rhf(t_list, bath_orb, chemical_potential, norb_high, nelec_high, + self.orbitals.active_fock) + mf_fragment, fock_frag_copy, mol_frag = helpers._fragment_scf_rhf( + t_list, two_ele, fock, nelec_high, norb_high, guess_orbitals, + chemical_potential) scf_fragments.append([mf_fragment, fock_frag_copy, mol_frag, t_list, one_ele, two_ele, fock]) @@ -429,8 +458,8 @@ def _oneshot_loop(self, chemical_potential, save_results=False, resample=False, # We create a dummy SecondQuantizedMolecule with a DMETFragment class. # It has the same important attributes and methods to be used with # functions of this package. - dummy_mol = SecondQuantizedDMETFragment(mol_frag, mf_fragment, fock, - fock_frag_copy, t_list, one_ele, two_ele, False, + dummy_mol = self.fragment_builder(mol_frag, mf_fragment, fock, + fock_frag_copy, t_list, one_ele, two_ele, self.uhf, self.fragment_frozen_orbitals[i]) if self.verbose: @@ -469,9 +498,11 @@ def _oneshot_loop(self, chemical_potential, save_results=False, resample=False, solver_fragment.build() solver_fragment.simulate() - if purify and solver_fragment.molecule.n_active_electrons == 2: + if purify and solver_fragment.molecule.n_active_electrons == 2 and not self.uhf: onerdm, twordm = solver_fragment.get_rdm(solver_fragment.optimal_var_params, resample=resample, sum_spin=False) onerdm, twordm = mcweeny_purify_2rdm(twordm) + elif self.uhf: + onerdm, twordm = solver_fragment.get_rdm_uhf(solver_fragment.optimal_var_params, resample=resample) else: onerdm, twordm = solver_fragment.get_rdm(solver_fragment.optimal_var_params, resample=resample) if save_results: @@ -479,9 +510,14 @@ def _oneshot_loop(self, chemical_potential, save_results=False, resample=False, self.rdm_measurements[i] = self.solver_fragment_dict[i].rdm_freq_dict # Compute the fragment energy and sum up the number of electrons - onerdm_padded, twordm_padded = pad_rdms_with_frozen_orbitals(dummy_mol, onerdm, twordm) - fragment_energy, onerdm = self._compute_energy_restricted(dummy_mol, onerdm_padded, twordm_padded) - n_electron_frag = np.trace(onerdm[: t_list[0], : t_list[0]]) + if self.uhf: + onerdm_padded, twordm_padded = pad_rdms_with_frozen_orbitals_unrestricted(dummy_mol, onerdm, twordm) + fragment_energy, onerdm_a, onerdm_b = self._compute_energy_unrestricted(dummy_mol, onerdm_padded, twordm_padded) + n_electron_frag = np.trace(onerdm_a[ : t_list[0], : t_list[0]]) + np.trace(onerdm_b[ : t_list[0], : t_list[0]]) + else: + onerdm_padded, twordm_padded = pad_rdms_with_frozen_orbitals_restricted(dummy_mol, onerdm, twordm) + fragment_energy, onerdm = self._compute_energy_restricted(dummy_mol, onerdm_padded, twordm_padded) + n_electron_frag = np.trace(onerdm[: t_list[0], : t_list[0]]) number_of_electron += n_electron_frag @@ -518,8 +554,8 @@ def get_resources(self): # Unpacking the information for the selected fragment. mf_fragment, fock_frag_copy, mol_frag, t_list, one_ele, two_ele, fock = info_fragment - dummy_mol = SecondQuantizedDMETFragment(mol_frag, mf_fragment, fock, - fock_frag_copy, t_list, one_ele, two_ele, False, + dummy_mol = self.fragment_builder(mol_frag, mf_fragment, fock, + fock_frag_copy, t_list, one_ele, two_ele, self.uhf, self.fragment_frozen_orbitals[i]) # Buiding SCF fragments and quantum circuit. Resources are then diff --git a/tangelo/problem_decomposition/dmet/fragment.py b/tangelo/problem_decomposition/dmet/fragment.py index 8f3744620..960fec548 100644 --- a/tangelo/problem_decomposition/dmet/fragment.py +++ b/tangelo/problem_decomposition/dmet/fragment.py @@ -67,7 +67,7 @@ def __post_init__(self): self.basis = self.molecule.basis - self.n_mos = len(self.mean_field.mo_energy) + self.n_mos = len(self.mean_field.mo_energy[0]) if self.uhf else len(self.mean_field.mo_energy) self.mo_occ = self.mean_field.mo_occ list_of_active_frozen = convert_frozen_orbitals(self, self.frozen_orbitals) diff --git a/tangelo/problem_decomposition/electron_localization/iao_localization.py b/tangelo/problem_decomposition/electron_localization/iao_localization.py index d37329c07..a3956366d 100644 --- a/tangelo/problem_decomposition/electron_localization/iao_localization.py +++ b/tangelo/problem_decomposition/electron_localization/iao_localization.py @@ -24,10 +24,7 @@ - G. Knizia, JCTC 9, 4834-4843 (2013). """ -from pyscf import gto -from pyscf.lo import iao from functools import reduce -from pyscf.lo import orth import numpy as np import scipy @@ -68,6 +65,9 @@ def _iao_occupied_orbitals(mol, mf): Returns: numpy.array: The localized orbitals for the occupied space (float64). """ + from pyscf import gto + from pyscf.lo import iao + from pyscf.lo import orth # Get MO coefficient of occupied MOs occupied_orbitals = mf.mo_coeff[:, mf.mo_occ > 0.5] @@ -118,6 +118,8 @@ def _iao_complementary_orbitals(mol, iao_ref): Returns: numpy.array: IAO in complementary space (float64). """ + from pyscf.lo import iao + from pyscf.lo import orth # Get the total number of AOs norbital_total = mol.nao_nr() @@ -224,6 +226,7 @@ def _iao_atoms(mol, iao1, iao2): Returns: numpy.array: The rearranged IAO (float64). """ + from pyscf.lo import orth # Calclate the integrals for assignment number_orbitals = mol.nao_nr() diff --git a/tangelo/problem_decomposition/electron_localization/meta_lowdin_localization.py b/tangelo/problem_decomposition/electron_localization/meta_lowdin_localization.py index 87d94b343..b15de72ea 100644 --- a/tangelo/problem_decomposition/electron_localization/meta_lowdin_localization.py +++ b/tangelo/problem_decomposition/electron_localization/meta_lowdin_localization.py @@ -21,8 +21,6 @@ - Q. Sun et al., JCTC 10, 3784-3790 (2014). """ -from pyscf.lo import orth - def meta_lowdin_localization(mol, mf): """Localize the orbitals using Meta-Löwdin localization. @@ -34,4 +32,5 @@ def meta_lowdin_localization(mol, mf): Returns: numpy.array: The localized orbitals (float64). """ + from pyscf.lo import orth return orth.orth_ao(mol, "meta_lowdin") diff --git a/tangelo/problem_decomposition/electron_localization/nao_localization.py b/tangelo/problem_decomposition/electron_localization/nao_localization.py index 40fe561db..094df9548 100644 --- a/tangelo/problem_decomposition/electron_localization/nao_localization.py +++ b/tangelo/problem_decomposition/electron_localization/nao_localization.py @@ -22,8 +22,6 @@ Natural population analysis. J. Chem. Phys., 83(2):735-746, 1985. """ -from pyscf.lo import orth - def nao_localization(mol, mf): """Localize the orbitals using NAO localization. @@ -35,4 +33,5 @@ def nao_localization(mol, mf): Returns: numpy.array: The localized orbitals (float64). """ + from pyscf.lo import orth return orth.orth_ao(mf, "NAO") diff --git a/tangelo/problem_decomposition/incremental/mifno_helper.py b/tangelo/problem_decomposition/incremental/mifno_helper.py index 14bb2a9d3..5c245d635 100644 --- a/tangelo/problem_decomposition/incremental/mifno_helper.py +++ b/tangelo/problem_decomposition/incremental/mifno_helper.py @@ -171,6 +171,7 @@ def __repr__(self): """ data_fragments = self.to_dataframe + data_fragments.drop(["problem_handle"], axis=1, inplace=True) data_fragments.drop(["frozen_orbitals_truncated"], axis=1, inplace=True) data_fragments.drop(["complete_orbital_space"], axis=1, inplace=True) str_rep = f"(All the energy values are in hartree)\n" \ @@ -189,7 +190,7 @@ def __getitem__(self, frag_id): @property def to_dataframe(self): - """Outputs the fragment informations as a pandas.DataFrame.""" + """Outputs fragment information as a pandas.DataFrame.""" df = pd.DataFrame.from_dict(self.frag_info_flattened, orient="index") # Replace frozen_orbitals_truncated=None with an empty list. diff --git a/tangelo/problem_decomposition/oniom/_helpers/helper_classes.py b/tangelo/problem_decomposition/oniom/_helpers/helper_classes.py index 087318ad6..4a532f3b2 100644 --- a/tangelo/problem_decomposition/oniom/_helpers/helper_classes.py +++ b/tangelo/problem_decomposition/oniom/_helpers/helper_classes.py @@ -18,6 +18,7 @@ """ import warnings +from typing import Union, List import numpy as np from scipy.spatial.transform import Rotation as R @@ -27,9 +28,76 @@ from tangelo.problem_decomposition.oniom._helpers.capping_groups import elements, chemical_groups +class Link: + + def __init__(self, staying, leaving, factor=1.0, species="H"): + """Bonds broken during the layer-construction process in ONIOM must be + mended. This class represents a broken-bond link, and has associated + methods to generate a new bond, appending the intended species. + + Args: + staying (int): Atom id retained. + leaving (int): Atom id lost. + factor (float) optional: Rescale length of bond, from that in the + original molecule. + species (str) optional: Atomic species or a chemical group + identifier for new link. Can be a list (first element = "X" to + detect the orientation) for a custom chemical group. + """ + + self.staying = staying + self.leaving = leaving + self.factor = factor + + if isinstance(species, str) and species in elements: + self.species = [(species, (0., 0., 0.))] + elif isinstance(species, str) and species in chemical_groups: + self.species = chemical_groups[species] + elif isinstance(species, (list, tuple)) and species[0][0].upper() == "X": + self.species = species + else: + raise ValueError(f"{species} is not supported. It must be a string identifier or a list of atoms (with a ghost atom ('X') as the first element).") + + def relink(self, geometry): + """Create atom at location of mended-bond link. + + Args: + geometry (list of positions): Atomic positions in format + [[str,tuple(float,float,float)],...]. + + Returns: + list: List of atomic species and position (x, y, z) of replacement + atom / chemical group. + """ + + elements = [a[0] for a in self.species if a[0].upper() != "X"] + chem_group_xyz = np.array([[a[1][0], a[1][1], a[1][2]] for a in self.species if a[0].upper() != "X"]) + + staying = np.array(geometry[self.staying][1]) + leaving = np.array(geometry[self.leaving][1]) + + # Rotation (if not a single atom). + if len(elements) > 1: + axis_old = leaving - staying + axis_new = chem_group_xyz[0] - np.array(self.species[0][1]) + + with warnings.catch_warnings(): + warnings.simplefilter("ignore", UserWarning) + rot, _ = R.align_vectors([axis_old], [axis_new]) + chem_group_xyz = rot.apply(chem_group_xyz) + + # Move the atom / group to the right position in space. + replacement = self.factor*(leaving-staying) + staying + translation = replacement - chem_group_xyz[0] + chem_group_xyz += translation + + return [(element, (xyz[0], xyz[1], xyz[2])) for element, xyz in zip(elements, chem_group_xyz)] + + class Fragment: - def __init__(self, solver_low, options_low=None, solver_high=None, options_high=None, selected_atoms=None, charge=0, spin=0, broken_links=None): + def __init__(self, solver_low: str, options_low: dict = None, solver_high: str = None, options_high: dict = None, + selected_atoms: Union[int, List[int]] = None, charge: int = 0, spin: int = 0, broken_links: List[Link] = None): """Fragment class for the ONIOM solver. Each fragment can have broken links. In this case, they are capped with a chosen atom. Each fragment can also have up to two solvers (low and high accuracy). @@ -146,7 +214,7 @@ def get_mol(self, basis, frozen=None): SecondQuantizedMolecule: Molecule object. """ - return SecondQuantizedMolecule(self.geometry, self.charge, self.spin, basis, frozen_orbitals=frozen) + return SecondQuantizedMolecule(self.geometry, self.charge, self.spin, basis=basis, frozen_orbitals=frozen) @staticmethod def get_energy(molecule, solver): @@ -212,69 +280,3 @@ def get_resources(self): resources = self.solver_high.get_resources() return resources - - -class Link: - - def __init__(self, staying, leaving, factor=1.0, species="H"): - """Bonds broken during the layer-construction process in ONIOM must be - mended. This class represents a broken-bond link, and has associated - methods to generate a new bond, appending the intended species. - - Args: - staying (int): Atom id retained. - leaving (int): Atom id lost. - factor (float) optional: Rescale length of bond, from that in the - original molecule. - species (str) optional: Atomic species or a chemical group - identifier for new link. Can be a list (first element = "X" to - detect the orientation) for a custom chemical group. - """ - - self.staying = staying - self.leaving = leaving - self.factor = factor - - if isinstance(species, str) and species in elements: - self.species = [(species, (0., 0., 0.))] - elif isinstance(species, str) and species in chemical_groups: - self.species = chemical_groups[species] - elif isinstance(species, (list, tuple)) and species[0][0].upper() == "X": - self.species = species - else: - raise ValueError(f"{species} is not supported. It must be a string identifier or a list of atoms (with a ghost atom ('X') as the first element).") - - def relink(self, geometry): - """Create atom at location of mended-bond link. - - Args: - geometry (list of positions): Atomic positions in format - [[str,tuple(float,float,float)],...]. - - Returns: - list: List of atomic species and position (x, y, z) of replacement - atom / chemical group. - """ - - elements = [a[0] for a in self.species if a[0].upper() != "X"] - chem_group_xyz = np.array([[a[1][0], a[1][1], a[1][2]] for a in self.species if a[0].upper() != "X"]) - - staying = np.array(geometry[self.staying][1]) - leaving = np.array(geometry[self.leaving][1]) - - # Rotation (if not a single atom). - if len(elements) > 1: - axis_old = leaving - staying - axis_new = chem_group_xyz[0] - np.array(self.species[0][1]) - - with warnings.catch_warnings(): - warnings.simplefilter("ignore", UserWarning) - rot, _ = R.align_vectors([axis_old], [axis_new]) - chem_group_xyz = rot.apply(chem_group_xyz) - - # Move the atom / group to the right position in space. - replacement = self.factor*(leaving-staying) + staying - translation = replacement - chem_group_xyz[0] - chem_group_xyz += translation - - return [(element, (xyz[0], xyz[1], xyz[2])) for element, xyz in zip(elements, chem_group_xyz)] diff --git a/tangelo/problem_decomposition/oniom/oniom_problem_decomposition.py b/tangelo/problem_decomposition/oniom/oniom_problem_decomposition.py index d978bcf19..ee34d4eba 100644 --- a/tangelo/problem_decomposition/oniom/oniom_problem_decomposition.py +++ b/tangelo/problem_decomposition/oniom/oniom_problem_decomposition.py @@ -29,8 +29,10 @@ and Keiji Morokuma Chemical Reviews 2015 115 (12), 5678-5796. DOI: 10.1021/cr5004419. """ +from typing import List, Union, Tuple from tangelo.problem_decomposition.problem_decomposition import ProblemDecomposition +from tangelo.problem_decomposition.oniom._helpers.helper_classes import Fragment from tangelo.toolboxes.molecular_computation.molecule import atom_string_to_list @@ -41,25 +43,20 @@ def __init__(self, opt_dict): different electronic solvers. Attributes: - geometry (strin or list): XYZ atomic coords (in "str float float..." + geometry (string or list): XYZ atomic coords (in "str float float..." or [[str, (float, float, float)], ...] format). fragments (list of Fragment): Specification of different system-subgroups and their solvers. verbose (bolean): Verbose flag. """ - default_options = {"geometry": None, - "fragments": list(), - "verbose": False} + copt_dict = opt_dict.copy() + self.geometry: Union[str, List[Tuple[str, Tuple[float]]]] = copt_dict.pop("geometry", None) + self.fragments: List[Fragment] = copt_dict.pop("fragments", list()) + self.verbose: bool = copt_dict.pop("verbose", False) - # Initialize with default values - self.__dict__ = default_options - # Overwrite default values with user-provided ones, if they correspond to a valid keyword - for k, v in opt_dict.items(): - if k in default_options: - setattr(self, k, v) - else: - raise KeyError(f"Keyword :: {k}, not available in {self.__class__.__name__}.") + if len(copt_dict.keys()) > 0: + raise KeyError(f"Keywords :: {copt_dict.keys()}, not available in {self.__class__.__name__}.") # Raise error/warnings if input is not as expected if not self.geometry or not self.fragments: diff --git a/tangelo/problem_decomposition/tests/dmet/test_dmet.py b/tangelo/problem_decomposition/tests/dmet/test_dmet.py index 0e85a6cae..bcb2fc372 100644 --- a/tangelo/problem_decomposition/tests/dmet/test_dmet.py +++ b/tangelo/problem_decomposition/tests/dmet/test_dmet.py @@ -14,7 +14,9 @@ import unittest import numpy as np +import scipy +from tangelo import SecondQuantizedMolecule from tangelo.molecule_library import mol_H4_doublecation_minao, mol_H4_doublecation_321g, mol_H10_321g, mol_H10_minao from tangelo.problem_decomposition import DMETProblemDecomposition from tangelo.problem_decomposition.dmet import Localization @@ -261,6 +263,25 @@ def test_dmet_frozen_orbitals(self): energy = solver.simulate() self.assertAlmostEqual(energy, -4.41503, places=4) + def test_dmet_ecp(self): + """Tests the DMET energy for Zn with ECP with custom optimizer.""" + def optimizer(func, var_params): + """Custom optimizer used as convergence sometimes fails with default.""" + def func2(params): + val = func(params) + return val.real*val.real + result = scipy.optimize.minimize(func2, var_params, tol=1.e-1) + return result.x[0] + + mol_zn = SecondQuantizedMolecule("Zn", q=2, spin=0, basis="lanl2dz", ecp="lanl2dz") + + options_zn_dmet = {"molecule": mol_zn, "fragment_atoms": [1], "fragment_solvers": "ccsd", "optimizer": optimizer} + + solver = DMETProblemDecomposition(options_zn_dmet) + solver.build() + energy = solver.simulate() + self.assertAlmostEqual(energy, -62.77176, places=4) + def test_dmet_wrong_number_frozen_orbitals(self): """Tests if the program raises the error when the number of frozen orbital elements is not equal to the number of fragment. diff --git a/tangelo/problem_decomposition/tests/dmet/test_osdmet.py b/tangelo/problem_decomposition/tests/dmet/test_osdmet.py index ab7718b4d..554477b26 100644 --- a/tangelo/problem_decomposition/tests/dmet/test_osdmet.py +++ b/tangelo/problem_decomposition/tests/dmet/test_osdmet.py @@ -27,7 +27,6 @@ class OSDMETProblemDecompositionTest(unittest.TestCase): - @unittest.skip("Open-shell DMET has been disabled.") def test_lio2_sto6g_rohf(self): """Tests the result from OS-DMET (ROHF) against a value from a reference implementation with nao localization and CCSD solution to fragments. @@ -48,7 +47,6 @@ def test_lio2_sto6g_rohf(self): self.assertAlmostEqual(energy, -156.6317605935, places=4) - @unittest.skip("Open-shell DMET has been disabled.") def test_lio2_sto6g_uhf(self): """Tests the result from OS-DMET (UHF) against a value from a reference implementation with nao localization and CCSD solution to fragments. diff --git a/tangelo/toolboxes/ansatz_generator/ansatz_utils.py b/tangelo/toolboxes/ansatz_generator/ansatz_utils.py index 0d1631fad..2f5e4dd8d 100644 --- a/tangelo/toolboxes/ansatz_generator/ansatz_utils.py +++ b/tangelo/toolboxes/ansatz_generator/ansatz_utils.py @@ -22,12 +22,11 @@ import numpy as np from openfermion.ops import FermionOperator as ofFermionOperator -from openfermion.ops import InteractionOperator as ofInteractionOperator from openfermion.ops import QubitOperator as ofQubitOperator from tangelo.linq import Circuit, Gate from tangelo.toolboxes.operators import FermionOperator, QubitOperator -from tangelo.toolboxes.qubit_mappings.mapping_transform import fermion_to_qubit_mapping, get_fermion_operator +from tangelo.toolboxes.qubit_mappings.mapping_transform import fermion_to_qubit_mapping def pauli_op_to_gate(index, op, inverse=False): diff --git a/tangelo/toolboxes/ansatz_generator/tests/test_ansatz_util.py b/tangelo/toolboxes/ansatz_generator/tests/test_ansatz_util.py index 03818f694..9e4080566 100644 --- a/tangelo/toolboxes/ansatz_generator/tests/test_ansatz_util.py +++ b/tangelo/toolboxes/ansatz_generator/tests/test_ansatz_util.py @@ -44,7 +44,7 @@ def test_trotterize_fermion_input(self): n_electrons=mol_H4_sto3g.n_active_electrons, up_then_down=True) - ham_mat = get_sparse_operator(qubit_hamiltonian).toarray() + ham_mat = get_sparse_operator(qubit_hamiltonian.to_openfermion()).toarray() evolve_exact = expm(-1j * time * ham_mat) @ refwave options = {"up_then_down": True, @@ -75,7 +75,7 @@ def test_trotterize_qubit_input(self): n_electrons=mol_H4_sto3g.n_active_electrons, up_then_down=True) - ham_mat = get_sparse_operator(qubit_hamiltonian).toarray() + ham_mat = get_sparse_operator(qubit_hamiltonian.to_openfermion()).toarray() evolve_exact = expm(-1j * time * ham_mat) @ refwave tcircuit, phase = trotterize(qubit_hamiltonian, trotter_order=1, n_trotter_steps=1, time=time, @@ -103,7 +103,7 @@ def test_trotterize_different_order_and_steps(self): n_electrons=mol_H4_sto3g.n_active_electrons, up_then_down=True) - ham_mat = get_sparse_operator(qubit_hamiltonian).toarray() + ham_mat = get_sparse_operator(qubit_hamiltonian.to_openfermion()).toarray() evolve_exact = expm(-1j * time * ham_mat) @ refwave for trotter_order, n_trotter_steps in [(1, 1), (2, 1), (1, 2), (4, 1), (6, 1)]: @@ -144,7 +144,7 @@ def test_trotterize_fermionic_input_different_times(self): total_fermion_operator += fermion_operators[i] qubit_hamiltonian = fermion_to_qubit_mapping(fermion_operator=fermion_operators[i], mapping=mapping) - ham_mat = get_sparse_operator(qubit_hamiltonian, n_qubits=4).toarray() + ham_mat = get_sparse_operator(qubit_hamiltonian.to_openfermion(), n_qubits=4).toarray() evolve_exact = expm(-1j * time[next(iter(fermion_operators[i].terms))] * ham_mat) @ evolve_exact # Apply trotter-suzuki steps using different times for each term @@ -171,7 +171,7 @@ def test_trotterize_qubit_input_different_times(self): # Exactly evolve for each time step evolve_exact = refwave for i in range(3): - ham_mat = get_sparse_operator(qubit_operator_list[i], n_qubits=4).toarray() + ham_mat = get_sparse_operator(qubit_operator_list[i].to_openfermion(), n_qubits=4).toarray() evolve_exact = expm(-1j * time[next(iter(qubit_operator_list[i].terms))] * ham_mat) @ evolve_exact # Apply trotter-suzuki with different times for each qubit operator term @@ -219,7 +219,7 @@ def test_controlled_time_evolution_by_phase_estimation(self): qu_op = (QubitOperator("X0 X1", 0.125) + QubitOperator("Y1 Y2", 0.125) + QubitOperator("Z2 Z3", 0.125) + QubitOperator("", 0.125)) - ham_mat = get_sparse_operator(qu_op).toarray() + ham_mat = get_sparse_operator(qu_op.to_openfermion()).toarray() _, wavefunction = eigh(ham_mat) # Append four qubits in the zero state to eigenvector 9 @@ -275,9 +275,9 @@ def test_derangement_circuit_by_estimating_pauli_string(self): qu_op = QubitOperator("X0 Y1", 0.125) + QubitOperator("Y1 Y2", 0.125) + QubitOperator("Z2 Z3", 0.125) pa = QubitOperator("X0 X1 X2 X3", 1) - ham_mat = get_sparse_operator(qu_op).toarray() + ham_mat = get_sparse_operator(qu_op.to_openfermion()).toarray() _, wavefunction = eigh(ham_mat) - pamat = get_sparse_operator(pa).toarray() + pamat = get_sparse_operator(pa.to_openfermion()).toarray() mixed_wave = np.sqrt(3)/2*wavefunction[:, -1] + 1/2*wavefunction[:, 0] mixed_wave_3 = np.kron(np.kron(mixed_wave, mixed_wave), mixed_wave) diff --git a/tangelo/toolboxes/ansatz_generator/tests/test_vsqs.py b/tangelo/toolboxes/ansatz_generator/tests/test_vsqs.py index a54703d54..eff018a61 100644 --- a/tangelo/toolboxes/ansatz_generator/tests/test_vsqs.py +++ b/tangelo/toolboxes/ansatz_generator/tests/test_vsqs.py @@ -18,11 +18,11 @@ import numpy as np from openfermion import load_operator +from tangelo.linq import get_backend from tangelo.molecule_library import mol_H2_sto3g from tangelo.toolboxes.operators import QubitOperator from tangelo.toolboxes.qubit_mappings import jordan_wigner, symmetry_conserving_bravyi_kitaev from tangelo.toolboxes.ansatz_generator import VSQS -from tangelo.linq import get_backend from tangelo.toolboxes.qubit_mappings.statevector_mapping import get_reference_circuit # For openfermion.load_operator function. @@ -71,17 +71,21 @@ def test_vsqs_H2(self): self.assertAlmostEqual(energy, -1.1372701255155757, delta=1e-6) def test_vsqs_H4_doublecation(self): - """Verify closed-shell VSQS functionalities for H4 2+ by using saved qubit hamiltonian and initial hamiltonian""" + """ Verify closed-shell VSQS functionalities for H4 2+ by using saved qubit and initial hamiltonians """ var_params = [-2.53957674, 0.72683888, 1.08799500, 0.49836183, -0.23020698, 0.93278630, 0.50591026, 0.50486903] - # Build qubit hamiltonian for energy evaluation - qubit_hamiltonian = load_operator("mol_H4_doublecation_minao_qubitham_jw_b.data", data_directory=pwd_this_test+"/data", plain_text=True) - initial_hamiltonian = load_operator("mol_H4_doublecation_minao_init_qubitham_jw_b.data", data_directory=pwd_this_test+"/data", plain_text=True) - reference_state = get_reference_circuit(8, 2, "jw", up_then_down=True, spin=0) + # Build qubit hamiltonian for energy evaluation, loading them with Openfermion. + qubit_ofhamiltonian = load_operator("mol_H4_doublecation_minao_qubitham_jw_b.data", data_directory=pwd_this_test+"/data", plain_text=True) + initial_ofhamiltonian = load_operator("mol_H4_doublecation_minao_init_qubitham_jw_b.data", data_directory=pwd_this_test+"/data", plain_text=True) + + # Load into Tangelo operators + qubit_hamiltonian = QubitOperator.from_openfermion(qubit_ofhamiltonian) + initial_hamiltonian = QubitOperator.from_openfermion(initial_ofhamiltonian) # Build circuit + reference_state = get_reference_circuit(8, 2, "jw", up_then_down=True, spin=0) vsqs_ansatz = VSQS(qubit_hamiltonian=qubit_hamiltonian, h_init=initial_hamiltonian, reference_state=reference_state, intervals=5, time=5, trotter_order=2) vsqs_ansatz.build_circuit() diff --git a/tangelo/toolboxes/ansatz_generator/uccgd.py b/tangelo/toolboxes/ansatz_generator/uccgd.py index c3ef43a05..4996ea35f 100644 --- a/tangelo/toolboxes/ansatz_generator/uccgd.py +++ b/tangelo/toolboxes/ansatz_generator/uccgd.py @@ -24,10 +24,10 @@ import numpy as np from openfermion import hermitian_conjugated -from openfermion import FermionOperator as ofFermionOperator from itertools import combinations_with_replacement, product from tangelo.linq import Circuit +from tangelo.toolboxes.operators import FermionOperator from tangelo.toolboxes.ansatz_generator.ansatz import Ansatz from tangelo.toolboxes.ansatz_generator.ansatz_utils import get_exponentiated_qubit_operator_circuit from tangelo.toolboxes.qubit_mappings.mapping_transform import fermion_to_qubit_mapping @@ -180,7 +180,7 @@ def _get_qubit_operator(self): Returns: QubitOperator: qubit-encoded elements of the UCCGD """ - fermion_op = ofFermionOperator() + fermion_op = FermionOperator() n_mos = self.n_spinorbitals // 2 p = -1 for indices in combinations_with_replacement(range(n_mos), 4): @@ -188,8 +188,8 @@ def _get_qubit_operator(self): u, w, v, t = indices p = p + 1 for sig, tau in product(range(2), repeat=2): - c_op = ofFermionOperator(((2*t+sig, 1), (2*v+tau, 1), (2*w+tau, 0), (2*u+sig, 0)), self.var_params[p]) - c_op += ofFermionOperator(((2*v+sig, 1), (2*t+tau, 1), (2*u+tau, 0), (2*w+sig, 0)), self.var_params[p]) + c_op = FermionOperator(((2*t+sig, 1), (2*v+tau, 1), (2*w+tau, 0), (2*u+sig, 0)), self.var_params[p]) + c_op += FermionOperator(((2*v+sig, 1), (2*t+tau, 1), (2*u+tau, 0), (2*w+sig, 0)), self.var_params[p]) fermion_op += c_op - hermitian_conjugated(c_op) qubit_op = fermion_to_qubit_mapping(fermion_operator=fermion_op, @@ -198,6 +198,9 @@ def _get_qubit_operator(self): n_electrons=self.n_electrons, up_then_down=self.up_then_down) + print(type(fermion_op)) + print(type(qubit_op)) + # Cast all coefs to floats (rotations angles are real) for key in qubit_op.terms: qubit_op.terms[key] = float(qubit_op.terms[key].imag) diff --git a/tangelo/toolboxes/ansatz_generator/uccsd.py b/tangelo/toolboxes/ansatz_generator/uccsd.py index 8e75a3b19..68c17ea32 100644 --- a/tangelo/toolboxes/ansatz_generator/uccsd.py +++ b/tangelo/toolboxes/ansatz_generator/uccsd.py @@ -28,18 +28,18 @@ Physical Review A 95, 020501 (2017). """ -import itertools import numpy as np -from pyscf import mp from openfermion.circuits import uccsd_singlet_generator +from tangelo import SecondQuantizedMolecule from tangelo.linq import Circuit - -from .ansatz import Ansatz -from .ansatz_utils import exp_pauliword_to_gates -from ._unitary_cc_openshell import uccsd_openshell_paramsize, uccsd_openshell_generator, uccsd_openshell_get_packed_amplitudes +from tangelo.helpers.utils import is_package_installed from tangelo.toolboxes.qubit_mappings.mapping_transform import fermion_to_qubit_mapping from tangelo.toolboxes.qubit_mappings.statevector_mapping import get_reference_circuit +from tangelo.toolboxes.molecular_computation import IntegralSolverPySCF +from .ansatz import Ansatz +from .ansatz_utils import exp_pauliword_to_gates +from ._unitary_cc_openshell import uccsd_openshell_paramsize, uccsd_openshell_generator class UCCSD(Ansatz): @@ -96,11 +96,10 @@ def __init__(self, molecule, mapping="JW", up_then_down=False, spin=None, refere # TODO: support for others self.supported_reference_state = {"HF", "zero"} # Supported var param initialization - self.supported_initial_var_params = {"ones", "random", "mp2"} if (self.spin == 0 and not self.molecule.uhf) else {"ones", "random"} + self.supported_initial_var_params = {"ones", "random", "mp2"} # Default initial parameters for initialization - # TODO: support for openshell MP2 initialization - self.var_params_default = "mp2" if (self.spin == 0 and not self.molecule.uhf) else "ones" + self.var_params_default = "mp2" self.reference_state = reference_state self.var_params = None @@ -252,32 +251,35 @@ def _get_openshell_qubit_operator(self): return qubit_op def _compute_mp2_params(self): - """Computes the MP2 initial variational parameters. Compute the initial - variational parameters with PySCF MP2 calculation, and then reorders the - elements into the appropriate convention. MP2 only has doubles (T2) - amplitudes, thus the single (T1) amplitudes are set to a small non-zero - value and added. The ordering is single, double (diagonal), double - (non-diagonal). + """Compute the MP2 initial variational parameters. Only double + excitation amplitudes are retrieved from an MP2 calculation (singles + set to a small number close to 0). Returns: list of float: The initial variational parameters. """ - if self.molecule.uhf: - raise NotImplementedError(f"MP2 initialization is not currently implemented for UHF reference in {self.__class__}") - - mp2_fragment = mp.MP2(self.molecule.mean_field, frozen=self.molecule.frozen_mos) - mp2_fragment.verbose = 0 - _, mp2_t2 = mp2_fragment.kernel() - # Get singles amplitude. Just get "up" amplitude, since "down" should be the same - singles = [2.e-5] * (self.n_virtual * self.n_occupied) - - # Get singles and doubles amplitudes associated with one spatial occupied-virtual pair - doubles_1 = [-mp2_t2[q, q, p, p]/2. if (abs(-mp2_t2[q, q, p, p]/2.) > 1e-15) else 0. - for p, q in itertools.product(range(self.n_virtual), range(self.n_occupied))] + if not is_package_installed("pyscf"): + supported_initial_var_params = self.supported_initial_var_params.copy() + supported_initial_var_params.remove("mp2") + raise ValueError(f"PySCF is required for MP2 initial parameters in {self.__class__.__name__}.\n" + f"Other supported keywords for initializing variational parameters are {supported_initial_var_params}. " + f"An array of floats of length {self.n_var_params} can also be provided.\n" + f"The above keywords or array can also be provided to variational algorithms through the 'initial_var_params' keyword.") + + # Import here to solve an AttributeError: partially initialized module + # tangelo.toolboxes.ansatz_generator' has no attribute 'UCCSD' + # (most likely due to a circular import). + from tangelo.algorithms.classical.mp2_solver import MP2Solver + + if not isinstance(self.molecule.solver, IntegralSolverPySCF): + pymol = SecondQuantizedMolecule(self.molecule.xyz, self.molecule.q, self.molecule.spin, basis=self.molecule.basis, + ecp=self.molecule.ecp, symmetry=self.molecule.symmetry, uhf=self.molecule.uhf, + frozen_orbitals=self.molecule.frozen_orbitals) + else: + pymol = self.molecule - # Get doubles amplitudes associated with two spatial occupied-virtual pairs - doubles_2 = [-mp2_t2[q, s, p, r] for (p, q), (r, s) - in itertools.combinations(itertools.product(range(self.n_virtual), range(self.n_occupied)), 2)] + mp2 = MP2Solver(pymol) + mp2.simulate() - return singles + doubles_1 + doubles_2 + return mp2.get_mp2_amplitudes() diff --git a/tangelo/toolboxes/ansatz_generator/vsqs.py b/tangelo/toolboxes/ansatz_generator/vsqs.py index 80c1f8597..d1bf6259c 100644 --- a/tangelo/toolboxes/ansatz_generator/vsqs.py +++ b/tangelo/toolboxes/ansatz_generator/vsqs.py @@ -16,12 +16,11 @@ Adiabatic State Preparation (ASP) inspired ansatz as described in https://arxiv.org/abs/2003.09913.""" import numpy as np -from openfermion import QubitOperator as ofQubitOperator from .ansatz import Ansatz from .ansatz_utils import get_exponentiated_qubit_operator_circuit -from tangelo.toolboxes.operators import FermionOperator from tangelo.linq import Circuit +from tangelo.toolboxes.operators import FermionOperator, QubitOperator from tangelo.toolboxes.qubit_mappings.mapping_transform import get_qubit_number, fermion_to_qubit_mapping from tangelo.toolboxes.qubit_mappings.statevector_mapping import get_reference_circuit @@ -68,7 +67,7 @@ def __init__(self, molecule=None, mapping="jw", up_then_down=False, intervals=2, if molecule is None: self.qubit_hamiltonian = qubit_hamiltonian - if not isinstance(h_init, ofQubitOperator): + if not isinstance(h_init, QubitOperator): raise ValueError("When providing a qubit hamiltonian, an initial qubit Hamiltonian must also be provided") self.h_init = h_init if not isinstance(reference_state, Circuit): @@ -102,7 +101,7 @@ def qu_op_to_list(qu_op): self.stride = 2 self.n_h_nav = 0 else: - if isinstance(self.h_nav, ofQubitOperator): + if isinstance(self.h_nav, QubitOperator): self.stride = 3 self.h_nav_list = qu_op_to_list(self.h_nav) self.n_h_nav = len(self.h_nav_list) diff --git a/tangelo/toolboxes/circuits/tests/test_diagonal_coulomb.py b/tangelo/toolboxes/circuits/tests/test_diagonal_coulomb.py index 6e37a89c8..bcf29ec78 100644 --- a/tangelo/toolboxes/circuits/tests/test_diagonal_coulomb.py +++ b/tangelo/toolboxes/circuits/tests/test_diagonal_coulomb.py @@ -14,7 +14,7 @@ import unittest -from openfermion import get_sparse_operator, linalg +from openfermion import linalg, get_sparse_operator from tangelo.linq import get_backend, Circuit from tangelo.molecule_library import mol_H4_sto3g @@ -24,7 +24,7 @@ sim = get_backend(target="cirq") -class diagonal_coulomb_Test(unittest.TestCase): +class DiagonalCoulombTest(unittest.TestCase): def test_orbital_rotations(self): """Test calculating energy expectation value of H4 hamiltonian by decomposing into diagional terms""" diff --git a/tangelo/toolboxes/circuits/tests/test_discrete_clock.py b/tangelo/toolboxes/circuits/tests/test_discrete_clock.py index df8059058..5ae0ef431 100644 --- a/tangelo/toolboxes/circuits/tests/test_discrete_clock.py +++ b/tangelo/toolboxes/circuits/tests/test_discrete_clock.py @@ -15,17 +15,16 @@ import unittest import math -from openfermion import get_sparse_operator import numpy as np from scipy.linalg import expm +from openfermion import get_sparse_operator -from tangelo.linq import get_backend, Circuit +from tangelo.molecule_library import mol_H2_sto3g from tangelo.helpers.utils import installed_backends +from tangelo.linq import get_backend, Circuit from tangelo.linq.helpers.circuits.statevector import StateVector -from tangelo.toolboxes.operators.operators import QubitOperator from tangelo.toolboxes.qubit_mappings.mapping_transform import fermion_to_qubit_mapping -from tangelo.toolboxes.ansatz_generator.ansatz_utils import get_qft_circuit, trotterize -from tangelo.molecule_library import mol_H2_sto3g +from tangelo.toolboxes.ansatz_generator.ansatz_utils import trotterize from tangelo.toolboxes.circuits.discrete_clock import get_discrete_clock_circuit from tangelo.toolboxes.circuits.grid_circuits import get_psquared_circuit, get_xsquared_circuit @@ -45,7 +44,7 @@ def test_time_independant_hamiltonian(self): qu_op = fermion_to_qubit_mapping(mol_H2_sto3g.fermionic_hamiltonian, "scbk", mol_H2_sto3g.n_active_sos, mol_H2_sto3g.n_active_electrons, True, 0) - ham = get_sparse_operator(qu_op).toarray() + ham = get_sparse_operator(qu_op.to_openfermion()).toarray() _, vecs = np.linalg.eigh(ham) vec = (vecs[:, 0] + vecs[:, 1])/np.sqrt(2) @@ -62,8 +61,8 @@ def trotter_func(t0, time, n_trotter_steps, control): sv_circuit = sv.initializing_circuit() for k in [2, 3]: - taylor_circuit = get_discrete_clock_circuit(trotter_func=trotter_func, trotter_kwargs={}, time=time, mp_order=k, n_state_qus=2, - n_time_steps=4) + taylor_circuit = get_discrete_clock_circuit(trotter_func=trotter_func, trotter_kwargs={}, + time=time, mp_order=k, n_state_qus=2, n_time_steps=4) _, v = sim.simulate(sv_circuit + taylor_circuit, return_statevector=True) n_ancilla = 2 + math.ceil(np.log2(k+2)) len_ancilla = 2**n_ancilla @@ -108,9 +107,9 @@ def trotter_func(t0, time, n_trotter_steps, control, dx, qubit_list): for k in [2, 3]: qubit_list = list(reversed(range(n_qubits))) if statevector_order == "lsq_first" else list((range(n_qubits))) - taylor_circuit = get_discrete_clock_circuit(trotter_func=trotter_func, trotter_kwargs={"dx": dx, "qubit_list": qubit_list}, time=time, - mp_order=k, n_state_qus=6, - n_time_steps=2) + taylor_circuit = get_discrete_clock_circuit(trotter_func=trotter_func, + trotter_kwargs={"dx": dx, "qubit_list": qubit_list}, time=time, + mp_order=k, n_state_qus=6, n_time_steps=2) _, v = sim.simulate(sv_circuit + taylor_circuit, return_statevector=True) n_ancilla = 1 + math.ceil(np.log2(k+2)) len_ancilla = 2**n_ancilla diff --git a/tangelo/toolboxes/circuits/tests/test_grid.py b/tangelo/toolboxes/circuits/tests/test_grid.py index ae2df84df..4f96297e0 100644 --- a/tangelo/toolboxes/circuits/tests/test_grid.py +++ b/tangelo/toolboxes/circuits/tests/test_grid.py @@ -13,11 +13,8 @@ # limitations under the License. import unittest -import math -from openfermion import get_sparse_operator import numpy as np -from scipy.linalg import expm from tangelo.linq import get_backend from tangelo.linq.helpers.circuits.statevector import StateVector diff --git a/tangelo/toolboxes/circuits/tests/test_lcu.py b/tangelo/toolboxes/circuits/tests/test_lcu.py index 0af757d74..692251134 100644 --- a/tangelo/toolboxes/circuits/tests/test_lcu.py +++ b/tangelo/toolboxes/circuits/tests/test_lcu.py @@ -15,9 +15,9 @@ import unittest import math -from openfermion import get_sparse_operator import numpy as np from scipy.linalg import expm +from openfermion import get_sparse_operator from tangelo.linq import get_backend from tangelo.helpers.utils import installed_backends @@ -36,7 +36,7 @@ sim_cirq = get_backend("cirq") -class lcu_Test(unittest.TestCase): +class LCUTest(unittest.TestCase): def test_get_truncated_taylor_series(self): """Test time-evolution of truncated Taylor series for different orders and times""" @@ -44,7 +44,7 @@ def test_get_truncated_taylor_series(self): qu_op = fermion_to_qubit_mapping(mol_H2_sto3g.fermionic_hamiltonian, "scbk", mol_H2_sto3g.n_active_sos, mol_H2_sto3g.n_active_electrons, True, 0) n_qubits_qu_op = math.ceil(math.log2(len(qu_op.terms))) - ham = get_sparse_operator(qu_op).toarray() + ham = get_sparse_operator(qu_op.to_openfermion()).toarray() _, vecs = np.linalg.eigh(ham) vec = (vecs[:, 0] + vecs[:, 1])/np.sqrt(2) @@ -65,21 +65,23 @@ def test_get_truncated_taylor_series(self): v = v.reshape([4, len_ancilla])[:, 0] if statevector_order == "lsq_first" else v.reshape([len_ancilla, 4])[0, :] self.assertAlmostEqual(1, np.abs(v.conj().dot(exact)), delta=3.e-1**k) - # Raise ValueError if Taylor series order is less than 1 or greater than 4 or imaginary coefficients in qubit operator + # Raise ValueError if Taylor series order is less than 1 or greater than 4 + # or imaginary coefficients in qubit operator self.assertRaises(ValueError, get_truncated_taylor_series, qu_op, 0, time) self.assertRaises(ValueError, get_truncated_taylor_series, qu_op * 1j, 2, time) def test_get_oaa_lcu_circuit(self): - """Test time-evolution of truncated Taylor series for order k = 3 passing explicitly calculated qubit operator exponential""" + """Test time-evolution of truncated Taylor series for order k = 3 passing explicitly calculated + qubit operator exponential""" - qu_op = fermion_to_qubit_mapping(mol_H2_sto3g.fermionic_hamiltonian, "scbk", mol_H2_sto3g.n_active_sos, mol_H2_sto3g.n_active_electrons, - True, 0) + qu_op = fermion_to_qubit_mapping(mol_H2_sto3g.fermionic_hamiltonian, "scbk", + mol_H2_sto3g.n_active_sos, mol_H2_sto3g.n_active_electrons, True, 0) time = 0.5 # Generate explicit qubit operator exponential exp_qu_op = 1 + -1j*qu_op*time + (-1j*qu_op*time)**2/2 + (-1j*qu_op*time)**3/6 exp_qu_op.compress() n_qubits_qu_op = math.ceil(math.log2(len(exp_qu_op.terms))) - ham = get_sparse_operator(qu_op).toarray() + ham = get_sparse_operator(qu_op.to_openfermion()).toarray() _, vecs = np.linalg.eigh(ham) vec = (vecs[:, 0] + vecs[:, 1])/np.sqrt(2) @@ -109,7 +111,7 @@ def test_controlled_time_evolution_by_phase_estimation_for_get_truncated_taylor_ qu_op = (QubitOperator("X0 X1", 0.125) + QubitOperator("Y1 Y2", 0.125) + QubitOperator("Z2 Z3", 0.125) + QubitOperator("", 0.125)) - ham_mat = get_sparse_operator(qu_op).toarray() + ham_mat = get_sparse_operator(qu_op.to_openfermion()).toarray() _, wavefunction = np.linalg.eigh(ham_mat) # Kronecker product 13 qubits in the zero state to eigenvector 9 to account for ancilla qubits @@ -143,7 +145,7 @@ def test_controlled_time_evolution_by_phase_estimation_for_get_oaa_lcu_circuit(s qu_op = (QubitOperator("X0 X1", 0.125) + QubitOperator("Y1 Y2", 0.125) + QubitOperator("Z2 Z3", 0.125) + QubitOperator("", 0.125)) - ham_mat = get_sparse_operator(qu_op).toarray() + ham_mat = get_sparse_operator(qu_op.to_openfermion()).toarray() _, wavefunction = np.linalg.eigh(ham_mat) # break time into 6 parts so 1-norm is less than 2. i.e. can use Oblivious Amplitude Amplification diff --git a/tangelo/toolboxes/circuits/tests/test_mp.py b/tangelo/toolboxes/circuits/tests/test_mp.py index 7d8c43ffd..ec2c09739 100644 --- a/tangelo/toolboxes/circuits/tests/test_mp.py +++ b/tangelo/toolboxes/circuits/tests/test_mp.py @@ -13,11 +13,10 @@ # limitations under the License. import unittest -import math -from openfermion import get_sparse_operator import numpy as np from scipy.linalg import expm +from openfermion import get_sparse_operator from tangelo.linq import get_backend from tangelo.helpers.utils import installed_backends @@ -41,10 +40,11 @@ class MultiProductTest(unittest.TestCase): def test_time_evolution(self): """Test time-evolution of multi-product circuit for different orders""" - qu_op = fermion_to_qubit_mapping(mol_H2_sto3g.fermionic_hamiltonian, "scbk", mol_H2_sto3g.n_active_sos, mol_H2_sto3g.n_active_electrons, + qu_op = fermion_to_qubit_mapping(mol_H2_sto3g.fermionic_hamiltonian, "scbk", + mol_H2_sto3g.n_active_sos, mol_H2_sto3g.n_active_electrons, True, 0) - ham = get_sparse_operator(qu_op).toarray() + ham = get_sparse_operator(qu_op.to_openfermion()).toarray() _, vecs = np.linalg.eigh(ham) vec = (vecs[:, 0] + vecs[:, 1])/np.sqrt(2) @@ -81,7 +81,7 @@ def test_controlled_time_evolution_by_phase_estimation(self): qu_op = (QubitOperator("X0 X1", 0.125) + QubitOperator("Y1 Y2", 0.125) + QubitOperator("Z2 Z3", 0.125) + QubitOperator("", 0.125)) - ham_mat = get_sparse_operator(qu_op).toarray() + ham_mat = get_sparse_operator(qu_op.to_openfermion()).toarray() _, wavefunction = np.linalg.eigh(ham_mat) # Kronecker product 13 qubits in the zero state to eigenvector 9 to account for ancilla qubits @@ -93,7 +93,8 @@ def test_controlled_time_evolution_by_phase_estimation(self): pe_circuit = get_qft_circuit(qubit_list) for i, qubit in enumerate(qubit_list): - pe_circuit += get_multi_product_circuit(operator=qu_op, n_state_qus=4, order=5, time=-(2*np.pi)*2**i, control=qubit) + pe_circuit += get_multi_product_circuit(operator=qu_op, n_state_qus=4, order=5, + time=-(2*np.pi)*2**i, control=qubit) pe_circuit += get_qft_circuit(qubit_list, inverse=True) freqs, _ = sim_cirq.simulate(pe_circuit, initial_statevector=wave_9) diff --git a/tangelo/toolboxes/circuits/tests/test_qsp.py b/tangelo/toolboxes/circuits/tests/test_qsp.py index 73c76b650..bb69c9b06 100644 --- a/tangelo/toolboxes/circuits/tests/test_qsp.py +++ b/tangelo/toolboxes/circuits/tests/test_qsp.py @@ -14,14 +14,14 @@ import unittest -from openfermion import get_sparse_operator import numpy as np from scipy.linalg import expm +from openfermion import get_sparse_operator from tangelo.linq import get_backend, backend_info from tangelo.helpers.utils import installed_backends from tangelo.linq.helpers.circuits.statevector import StateVector -from tangelo.toolboxes.operators.operators import QubitOperator +from tangelo.toolboxes.operators import QubitOperator from tangelo.toolboxes.qubit_mappings.mapping_transform import fermion_to_qubit_mapping from tangelo.toolboxes.ansatz_generator.ansatz_utils import get_qft_circuit from tangelo.molecule_library import mol_H2_sto3g @@ -36,16 +36,16 @@ sim_cirq = get_backend("cirq") -class lcu_Test(unittest.TestCase): +class QSPTest(unittest.TestCase): def test_get_qsp_circuit(self): """Test QSP time-evolution""" - qu_op = fermion_to_qubit_mapping(mol_H2_sto3g.fermionic_hamiltonian, "scbk", mol_H2_sto3g.n_active_sos, mol_H2_sto3g.n_active_electrons, - True, 0) + qu_op = fermion_to_qubit_mapping(mol_H2_sto3g.fermionic_hamiltonian, "scbk", + mol_H2_sto3g.n_active_sos, mol_H2_sto3g.n_active_electrons, True, 0) # need to ensure eigenvalues are between -1 and 1 qu_op /= 1.2 - ham = get_sparse_operator(qu_op).toarray() + ham = get_sparse_operator(qu_op.to_openfermion()).toarray() _, vecs = np.linalg.eigh(ham) vec = (vecs[:, 0] + vecs[:, 2])/np.sqrt(2) @@ -76,7 +76,7 @@ def test_controlled_time_evolution_by_phase_estimation_for_get_qsp_circuit(self) qu_op = (QubitOperator("X0 X1", 0.125) + QubitOperator("Y1 Y2", 0.125) + QubitOperator("Z2 Z3", 0.125) + QubitOperator("", 0.125)) - ham_mat = get_sparse_operator(qu_op).toarray() + ham_mat = get_sparse_operator(qu_op.to_openfermion()).toarray() _, wavefunction = np.linalg.eigh(ham_mat) # Kronecker product 13 qubits in the zero state to eigenvector 9 to account for ancilla qubits diff --git a/tangelo/toolboxes/measurements/tests/test_measurements.py b/tangelo/toolboxes/measurements/tests/test_measurements.py index 280b2ff40..e9c830a44 100644 --- a/tangelo/toolboxes/measurements/tests/test_measurements.py +++ b/tangelo/toolboxes/measurements/tests/test_measurements.py @@ -17,7 +17,7 @@ from openfermion import load_operator from tangelo.helpers.utils import default_simulator -from tangelo.linq import translator, get_backend, Circuit +from tangelo.linq import translate_circuit, get_backend, Circuit from tangelo.linq.helpers import measurement_basis_gates from tangelo.toolboxes.operators import QubitOperator from tangelo.toolboxes.measurements import get_measurement_estimate @@ -77,7 +77,7 @@ def test_measurement_uniform_H2(self): # Load state preparation circuit with open(f"{path_data}/H2_UCCSD.qasm", "r") as f: openqasm_circ = f.read() - abs_circ = translator._translate_openqasm2abs(openqasm_circ) + abs_circ = translate_circuit(openqasm_circ, source="openqasm", target="tangelo") # Load qubit Hamiltonian qb_ham = load_operator("mol_H2_qubitham.data", data_directory=path_data, plain_text=True) diff --git a/tangelo/toolboxes/measurements/tests/test_qubit_terms_grouping.py b/tangelo/toolboxes/measurements/tests/test_qubit_terms_grouping.py index cabe4f1e2..45f5a6083 100644 --- a/tangelo/toolboxes/measurements/tests/test_qubit_terms_grouping.py +++ b/tangelo/toolboxes/measurements/tests/test_qubit_terms_grouping.py @@ -16,10 +16,10 @@ import os from openfermion import load_operator -from openfermion.ops import QubitOperator -from tangelo.linq import translator, get_backend, Circuit +from tangelo.linq import translate_circuit, get_backend, Circuit from tangelo.helpers import measurement_basis_gates +from tangelo.toolboxes.operators import QubitOperator from tangelo.toolboxes.measurements import group_qwc, exp_value_from_measurement_bases, \ check_bases_commute_qwc, map_measurements_qwc @@ -97,7 +97,7 @@ def test_qubitwise_commutativity_of_H2(self): # Load an optimized quantum circuit (UCCSD) to compute something meaningful in this test with open(f"{path_data}/H2_UCCSD.qasm", "r") as f: openqasm_circ = f.read() - abs_circ = translator._translate_openqasm2abs(openqasm_circ) + abs_circ = translate_circuit(openqasm_circ, source="openqasm", target="tangelo") # Only simulate and measure the wavefunction in the required bases (simulator or QPU), store in dict. histograms = dict() @@ -125,7 +125,7 @@ def test_qubitwise_commutativity_of_H4(self): # Load an optimized quantum circuit (UCCSD) to compute something meaningful in this test with open(f"{path_data}/H4_UCCSD.qasm", "r") as f: openqasm_circ = f.read() - abs_circ = translator._translate_openqasm2abs(openqasm_circ) + abs_circ = translate_circuit(openqasm_circ, source="openqasm", target="tangelo") # Only simulate and measure the wavefunction in the required bases (simulator or QPU), store in dict. histograms = dict() diff --git a/tangelo/toolboxes/molecular_computation/__init__.py b/tangelo/toolboxes/molecular_computation/__init__.py index 8c1a20422..3af26873d 100644 --- a/tangelo/toolboxes/molecular_computation/__init__.py +++ b/tangelo/toolboxes/molecular_computation/__init__.py @@ -11,3 +11,7 @@ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. + +from tangelo.toolboxes.molecular_computation.integral_solver import IntegralSolver, IntegralSolverEmpty +from tangelo.toolboxes.molecular_computation.integral_solver_psi4 import IntegralSolverPsi4 +from tangelo.toolboxes.molecular_computation.integral_solver_pyscf import IntegralSolverPySCF diff --git a/tangelo/toolboxes/molecular_computation/frozen_orbitals.py b/tangelo/toolboxes/molecular_computation/frozen_orbitals.py index f2a4fbacc..fbeee571f 100644 --- a/tangelo/toolboxes/molecular_computation/frozen_orbitals.py +++ b/tangelo/toolboxes/molecular_computation/frozen_orbitals.py @@ -18,6 +18,8 @@ SecondQuantizedMolecule object can be used. """ +from collections import Counter + import numpy as np @@ -43,7 +45,7 @@ def get_frozen_core(molecule): } # Counting how many of each element is in the molecule. - elements = {i: molecule.elements.count(i) for i in molecule.elements} + elements = Counter([e[0] for e in molecule.xyz]) frozen_core = sum([v * core_orbitals.get(k, 0) for k, v in elements.items()]) return frozen_core @@ -97,7 +99,7 @@ def convert_frozen_orbitals(sec_mol, frozen_orbitals): """ if frozen_orbitals == "frozen_core": - frozen_orbitals = get_frozen_core(sec_mol.to_pyscf(sec_mol.basis)) if not sec_mol.ecp else 0 + frozen_orbitals = get_frozen_core(sec_mol) if not sec_mol.ecp else 0 elif frozen_orbitals is None: frozen_orbitals = 0 diff --git a/tangelo/toolboxes/molecular_computation/integral_solver.py b/tangelo/toolboxes/molecular_computation/integral_solver.py new file mode 100644 index 000000000..7c4088489 --- /dev/null +++ b/tangelo/toolboxes/molecular_computation/integral_solver.py @@ -0,0 +1,110 @@ +# Copyright 2023 Good Chemistry Company. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import abc + + +class IntegralSolver(abc.ABC): + """Instantiate electronic integral solver""" + def __init__(self): + pass + + @abc.abstractmethod + def set_physical_data(self, mol): + """Set molecular data that is independant of basis set in mol + + Modify mol variable: + mol.xyz to (list): Nested array-like structure with elements and coordinates + (ex:[ ["H", (0., 0., 0.)], ...]) in angstrom + Add to mol: + mol.n_electrons (int): Self-explanatory. + mol.n_atoms (int): Self-explanatory. + + Args: + mol (Molecule or SecondQuantizedMolecule): Class to add the other variables given populated. + mol.xyz (in appropriate format for solver): Definition of molecular geometry. + mol.q (float): Total charge. + mol.spin (int): Absolute difference between alpha and beta electron number. + """ + pass + + @abc.abstractmethod + def compute_mean_field(self, sqmol): + """Run a unrestricted/restricted (openshell-)Hartree-Fock calculation and modify/add the following + variables to sqmol + + Modify sqmol variables. + sqmol.mf_energy (float): Mean-field energy (RHF or ROHF energy depending on the spin). + sqmol.mo_energies (list of float): Molecular orbital energies. + sqmol.mo_occ (list of float): Molecular orbital occupancies (between 0. and 2.). + sqmol.n_mos (int): Number of molecular orbitals with a given basis set. + sqmol.n_sos (int): Number of spin-orbitals with a given basis set. + + Add to sqmol: + self.mo_coeff (ndarray or List[ndarray]): array of molecular orbital coefficients (MO coeffs) if RHF ROHF + list of arrays [alpha MO coeffs, beta MO coeffs] if UHF + + Args: + sqmol (SecondQuantizedMolecule): Populated variables of Molecule plus + sqmol.basis (string): Basis set. + sqmol.ecp (dict): The effective core potential (ecp) for any atoms in the molecule. + e.g. {"C": "crenbl"} use CRENBL ecp for Carbon atoms. + sqmol.symmetry (bool or str): Whether to use symmetry in RHF or ROHF calculation. + Can also specify point group using string. e.g. "Dooh", "D2h", "C2v", ... + sqmol.uhf (bool): If True, Use UHF instead of RHF or ROHF reference. Default False + + + """ + pass + + @abc.abstractmethod + def get_integrals(self, sqmol, mo_coeff=None): + r"""Computes core constant, one_body, and two-body integrals for all orbitals + + one-body integrals should be in the form + h[p,q]= \int \phi_p(x)* (T + V_{ext}) \phi_q(x) dx + + two-body integrals should be in the form + h[p,q,r,s] = \int \phi_p(x) * \phi_q(y) * V_{elec-elec} \phi_r(y) \phi_s(x) dxdy + + Using molecular orbitals \phi_j(x) = \sum_{ij} A_i(x) mo_coeff_{i,j} where A_i(x) are the atomic orbitals. + + For UHF (if sqmol.uhf is True) + one_body coefficients are [alpha one_body, beta one_body] + two_body coefficients are [alpha-alpha two_body, alpha-beta two_body, beta-beta two_body] + + where one_body and two_body are appropriately sized arrays for each spin sector. + + Args: + sqmol (SecondQuantizedMolecule) : SecondQuantizedMolecule populated with all variables defined above + mo_coeff : Molecular orbital coefficients to use for calculating the integrals, instead of self.mo_coeff + + Returns: + (float, array or List[array], array or List[array]): (core_constant, one_body coefficients, two_body coefficients) + """ + pass + + +class IntegralSolverEmpty(IntegralSolver): + def __init__(self): + pass + + def set_physical_data(self, mol): + pass + + def compute_mean_field(self, sqmol): + pass + + def get_integrals(self, sqmol, mo_coeff=None): + pass diff --git a/tangelo/toolboxes/molecular_computation/integral_solver_psi4.py b/tangelo/toolboxes/molecular_computation/integral_solver_psi4.py new file mode 100644 index 000000000..c5b4f9284 --- /dev/null +++ b/tangelo/toolboxes/molecular_computation/integral_solver_psi4.py @@ -0,0 +1,218 @@ +# Copyright 2023 Good Chemistry Company. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from copy import copy + +import numpy as np + +from tangelo.toolboxes.molecular_computation.integral_solver import IntegralSolver + + +class IntegralSolverPsi4(IntegralSolver): + """psi4 IntegrationSolver class""" + def __init__(self): + import psi4 + self.backend = psi4 + self.backend.core.clean() + self.backend.core.clean_options() + self.backend.core.clean_variables() + + def set_physical_data(self, mol): + """Set molecular data that is independant of basis set in mol + + Modify mol variable: + mol.xyz to (list): Nested array-like structure with elements and coordinates + (ex:[ ["H", (0., 0., 0.)], ...]) in angstrom + Add to mol: + mol.n_electrons (int): Self-explanatory. + mol.n_atoms (int): Self-explanatory. + + Args: + mol (Molecule or SecondQuantizedMolecule): Class to add the other variables given populated. + mol.xyz (in appropriate format for solver): Definition of molecular geometry. + mol.q (float): Total charge. + mol.spin (int): Absolute difference between alpha and beta electron number. + """ + self.backend.core.set_output_file('output.dat', False) + if isinstance(mol.xyz, list): + input_string = f"{mol.q} {mol.spin + 1} \n" + for line in mol.xyz: + input_string += f"{line[0]} {line[1][0]} {line[1][1]} {line[1][2]} \n" + input_string += "symmetry c1" + self.mol = self.backend.geometry(input_string) + self.mol_nosym = self.backend.geometry(input_string) + else: + self.mol = self.backend.geometry(mol.xyz) + mol.n_atoms = self.mol.natom() + mol.xyz = list() + for i in range(mol.n_atoms): + mol.xyz += [(self.mol.symbol(i), tuple(self.mol.xyz(i)[p]*0.52917721067 for p in range(3)))] + + self.backend.set_options({'basis': "def2-msvp"}) + wfn = self.backend.core.Wavefunction.build(self.mol, self.backend.core.get_global_option('basis')) + + mol.n_electrons = wfn.nalpha() + wfn.nbeta() + mol.n_atoms = self.mol.natom() + + def compute_mean_field(self, sqmol): + """Run a unrestricted/restricted (openshell-)Hartree-Fock calculation and modify/add the following + variables to sqmol + + Modify sqmol variables. + sqmol.mf_energy (float): Mean-field energy (RHF or ROHF energy depending on the spin). + sqmol.mo_energies (list of float): Molecular orbital energies. + sqmol.mo_occ (list of float): Molecular orbital occupancies (between 0. and 2.). + sqmol.n_mos (int): Number of molecular orbitals with a given basis set. + sqmol.n_sos (int): Number of spin-orbitals with a given basis set. + + Add to sqmol: + self.mo_coeff (ndarray or List[ndarray]): array of molecular orbital coefficients (MO coeffs) if RHF ROHF + list of arrays [alpha MO coeffs, beta MO coeffs] if UHF + + Args: + sqmol (SecondQuantizedMolecule): Populated variables of Molecule plus + sqmol.basis (string): Basis set. + sqmol.ecp (dict): The effective core potential (ecp) for any atoms in the molecule. + e.g. {"C": "crenbl"} use CRENBL ecp for Carbon atoms. + sqmol.symmetry (bool or str): Whether to use symmetry in RHF or ROHF calculation. + Can also specify point group using string. e.g. "Dooh", "D2h", "C2v", ... + sqmol.uhf (bool): If True, Use UHF instead of RHF or ROHF reference. Default False + + + """ + if sqmol.symmetry: + input_string = f"{sqmol.q} {sqmol.spin + 1} \n" + for line in sqmol.xyz: + input_string += f"{line[0]} {line[1][0]} {line[1][1]} {line[1][2]} \n" + if isinstance(sqmol.symmetry, str): + input_string += "symmetry" + sqmol.symmetry + self.mol = self.backend.geometry(input_string) + + self.backend.set_options({'basis': sqmol.basis}) + if sqmol.uhf: + self.backend.set_options({'reference': 'uhf', 'guess': 'gwh', 'guess_mix': True}) + elif sqmol.spin != 0: + self.backend.set_options({'reference': 'rohf', 'guess': 'core'}) + else: + self.backend.set_options({'reference': 'rhf'}) + + sqmol.mf_energy, self.sym_wfn = self.backend.energy('scf', molecule=self.mol, basis=self.backend.core.get_global_option('basis'), return_wfn=True) + self.wfn = self.sym_wfn.c1_deep_copy(self.sym_wfn.basisset()) + self.backend.core.clean_options() + + sqmol.mo_energies = np.asarray(self.wfn.epsilon_a()) + if sqmol.symmetry: + self.irreps = [self.mol.point_group().char_table().gamma(i).symbol().upper() for i in range(self.sym_wfn.nirrep())] + sym_mo_energies = [] + tmp = self.backend.driver.p4util.numpy_helper._to_array(self.sym_wfn.epsilon_a(), dense=False) + for i in self.irreps: + sym_mo_energies += [(i, j, x) for j, x in enumerate(tmp[self.irreps.index(i)])] + ordered_energies = sorted(sym_mo_energies, key=lambda x: x[1]) + sqmol.mo_symm_labels = [o[0] for o in ordered_energies] + sqmol.mo_symm_ids = [o[1] for o in ordered_energies] + else: + sqmol.mo_symm_labels = None + sqmol.mo_symm_ids = None + + self.mints = self.backend.core.MintsHelper(self.wfn.basisset()) + nbf = np.asarray(self.mints.ao_overlap()).shape[0] + + if sqmol.uhf: + na = self.wfn.nalpha() + nb = self.wfn.nbeta() + sqmol.mo_occ = [[1]*na + (nbf-na)*[0]]+[[1]*nb + (nbf-nb)*[0]] + else: + docc = self.wfn.doccpi()[0] + socc = self.wfn.soccpi()[0] + sqmol.mo_occ = [2]*docc + [1]*socc + (nbf - docc - socc)*[0] + sqmol.n_mos = nbf + sqmol.n_sos = nbf*2 + self.mo_coeff = np.asarray(self.wfn.Ca()) if not sqmol.uhf else [np.asarray(self.wfn.Ca()), np.asarray(self.wfn.Cb())] + self.ob = np.asarray(self.mints.ao_potential()) + np.asarray(self.mints.ao_kinetic()) + self.tb = np.asarray(self.mints.ao_eri()) + self.core_constant = self.mol.nuclear_repulsion_energy() + + def get_integrals(self, sqmol, mo_coeff=None): + r"""Computes core constant, one_body, and two-body integrals for all orbitals + + one-body integrals should be in the form + h[p,q]= \int \phi_p(x)* (T + V_{ext}) \phi_q(x) dx + + two-body integrals should be in the form + h[p,q,r,s] = \int \phi_p(x) * \phi_q(y) * V_{elec-elec} \phi_r(y) \phi_s(x) dxdy + + Using molecular orbitals \phi_j(x) = \sum_{ij} A_i(x) mo_coeff_{i,j} where A_i(x) are the atomic orbitals. + + For UHF (if sqmol.uhf is True) + one_body coefficients are [alpha one_body, beta one_body] + two_body coefficients are [alpha-alpha two_body, alpha-beta two_body, beta-beta two_body] + + where one_body and two_body are appropriately sized arrays for each spin sector. + + Args: + sqmol (SecondQuantizedMolecule) : SecondQuantizedMolecule populated with all variables defined above + mo_coeff : Molecular orbital coefficients to use for calculating the integrals, instead of self.mo_coeff + + Returns: + (float, array or List[array], array or List[array]): (core_constant, one_body coefficients, two_body coefficients) + """ + if mo_coeff is None: + mo_coeff = self.mo_coeff + + if sqmol.uhf: + return self.compute_uhf_integrals(mo_coeff) + + ob = mo_coeff.T@self.ob@mo_coeff + eed = self.tb.copy() + eed = np.einsum("ij,jlmn -> ilmn", mo_coeff.T, eed) + eed = np.einsum("kl,jlmn -> jkmn", mo_coeff.T, eed) + eed = np.einsum("jlmn, mk -> jlkn", eed, mo_coeff) + eed = np.einsum("jlmn, nk -> jlmk", eed, mo_coeff) + tb = eed.transpose(0, 2, 3, 1) + + return self.core_constant, ob, tb + + def compute_uhf_integrals(self, mo_coeff): + """Compute 1-electron and 2-electron integrals + The return is formatted as + [numpy.ndarray]*2 numpy array h_{pq} for alpha and beta blocks + [numpy.ndarray]*3 numpy array storing h_{pqrs} for alpha-alpha, alpha-beta, beta-beta blocks + + Args: + mo_coeff (List[array]): The molecular orbital coefficients for both spins [alpha, beta] + + Returns: + List[array], List[array]: One and two body integrals + """ + + mo_a = self.backend.core.Matrix.from_array(mo_coeff[0]) + mo_b = self.backend.core.Matrix.from_array(mo_coeff[1]) + + # calculate alpha and beta one-body integrals + hpq = [mo_coeff[0].T.dot(self.ob).dot(mo_coeff[0]), mo_coeff[1].T.dot(self.ob).dot(mo_coeff[1])] + + # mo transform the two-electron integrals + eri_a = np.asarray(self.mints.mo_eri(mo_a, mo_a, mo_a, mo_a)) + eri_b = np.asarray(self.mints.mo_eri(mo_b, mo_b, mo_b, mo_b)) + eri_ba = np.asarray(self.mints.mo_eri(mo_a, mo_a, mo_b, mo_b)) + + # # convert this into physicist ordering for OpenFermion + two_body_integrals_a = np.asarray(eri_a.transpose(0, 2, 3, 1), order='C') + two_body_integrals_b = np.asarray(eri_b.transpose(0, 2, 3, 1), order='C') + two_body_integrals_ab = np.asarray(eri_ba.transpose(0, 2, 3, 1), order='C') + + # Gpqrs has alpha, alphaBeta, Beta blocks + Gpqrs = (two_body_integrals_a, two_body_integrals_ab, two_body_integrals_b) + + return self.core_constant, hpq, Gpqrs diff --git a/tangelo/toolboxes/molecular_computation/integral_solver_pyscf.py b/tangelo/toolboxes/molecular_computation/integral_solver_pyscf.py new file mode 100644 index 000000000..56cbbf136 --- /dev/null +++ b/tangelo/toolboxes/molecular_computation/integral_solver_pyscf.py @@ -0,0 +1,282 @@ +# Copyright 2023 Good Chemistry Company. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import os + +import numpy as np + +from tangelo.toolboxes.molecular_computation.integral_solver import IntegralSolver + + +def mol_to_pyscf(mol, basis="CRENBL", symmetry=False, ecp=None): + """Method to return a pyscf.gto.Mole object. + + Args: + sqmol (SecondQuantizedMolecule or Molecule): The molecule to export to a pyscf molecule. + basis (string): Basis set. + symmetry (bool): Flag to turn symmetry on + ecp (dict): Dictionary with ecp definition for each atom e.g. {"Cu": "crenbl"} + + Returns: + pyscf.gto.Mole: PySCF compatible object. + """ + from pyscf import gto + + pymol = gto.Mole(atom=mol.xyz) + pymol.basis = basis + pymol.charge = mol.q + pymol.spin = mol.spin + pymol.symmetry = symmetry + pymol.ecp = ecp if ecp else dict() + pymol.build() + + return pymol + + +class IntegralSolverPySCF(IntegralSolver): + """Electronic Structure integration for pyscf""" + + def __init__(self, chkfile=None): + """Initialize the integral solver class for pyscf. A chkfile path can be + provided. + + Regarding the chkfile, three scenarios are possible: + - A chkfile path is provided, but the file doesn't exist: it creates + a chkfile at the end of the SCF calculation. + - A chkfile path is provided and a file already exists: the initial + guess is taken from the chkfile and this file is updated at the end + of the calculation. + - No chkfile path is provided: The SCF initial guess stays the default + one (minao). No chkfile is created. + + Args: + chkfile (string): Path of the chkfile. + """ + + from pyscf import gto, lib, scf, symm, ao2mo + self.gto = gto + self.lib = lib + self.scf = scf + self.symm = symm + self.ao2mo = ao2mo + self.chkfile = chkfile + + def set_physical_data(self, mol): + """Set molecular data that is independant of basis set in mol + + Modify mol variable: + mol.xyz to (list): Nested array-like structure with elements and coordinates + (ex:[ ["H", (0., 0., 0.)], ...]) in angstrom + Add to mol: + mol.n_electrons (int): Self-explanatory. + mol.n_atoms (int): Self-explanatory. + + Args: + mol (Molecule or SecondQuantizedMolecule): Class to add the other variables given populated. + mol.xyz (in appropriate format for solver): Definition of molecular geometry. + mol.q (float): Total charge. + mol.spin (int): Absolute difference between alpha and beta electron number. + """ + pymol = mol_to_pyscf(mol) + mol.xyz = list() + for sym, xyz in pymol._atom: + mol.xyz += [tuple([sym, tuple([x*self.lib.parameters.BOHR for x in xyz])])] + + mol.n_atoms = pymol.natm + mol.n_electrons = pymol.nelectron + + def compute_mean_field(self, sqmol): + """Run a unrestricted/restricted (openshell-)Hartree-Fock calculation and modify/add the following + variables to sqmol + + Modify sqmol variables. + sqmol.mf_energy (float): Mean-field energy (RHF or ROHF energy depending on the spin). + sqmol.mo_energies (list of float): Molecular orbital energies. + sqmol.mo_occ (list of float): Molecular orbital occupancies (between 0. and 2.). + sqmol.n_mos (int): Number of molecular orbitals with a given basis set. + sqmol.n_sos (int): Number of spin-orbitals with a given basis set. + + Add to sqmol: + self.mo_coeff (ndarray or List[ndarray]): array of molecular orbital coefficients (MO coeffs) if RHF ROHF + list of arrays [alpha MO coeffs, beta MO coeffs] if UHF + + Args: + sqmol (SecondQuantizedMolecule): Populated variables of Molecule plus + sqmol.basis (string): Basis set. + sqmol.ecp (dict): The effective core potential (ecp) for any atoms in the molecule. + e.g. {"C": "crenbl"} use CRENBL ecp for Carbon atoms. + sqmol.symmetry (bool or str): Whether to use symmetry in RHF or ROHF calculation. + Can also specify point group using string. e.g. "Dooh", "D2h", "C2v", ... + sqmol.uhf (bool): If True, Use UHF instead of RHF or ROHF reference. Default False + + + """ + + molecule = mol_to_pyscf(sqmol, sqmol.basis, sqmol.symmetry, sqmol.ecp) + + sqmol.mean_field = self.scf.RHF(molecule) if not sqmol.uhf else self.scf.UHF(molecule) + sqmol.mean_field.verbose = 0 + + chkfile_found = False + if self.chkfile: + chkfile_found = os.path.exists(self.chkfile) + sqmol.mean_field.chkfile = self.chkfile + + # Force broken symmetry for uhf calculation when spin is 0 as shown in + # https://github.com/sunqm/pyscf/blob/master/examples/scf/32-break_spin_symm.py + if sqmol.uhf and sqmol.spin == 0 and not chkfile_found: + dm_alpha, dm_beta = sqmol.mean_field.get_init_guess() + dm_beta[:1, :] = 0 + dm = (dm_alpha, dm_beta) + sqmol.mean_field.kernel(dm) + else: + sqmol.mean_field.init_guess = "chkfile" if chkfile_found else "minao" + sqmol.mean_field.kernel() + + sqmol.mean_field.analyze() + if not sqmol.mean_field.converged: + raise ValueError("Hartree-Fock calculation did not converge") + + if sqmol.symmetry: + sqmol.mo_symm_ids = list(self.symm.label_orb_symm(sqmol.mean_field.mol, sqmol.mean_field.mol.irrep_id, + sqmol.mean_field.mol.symm_orb, sqmol.mean_field.mo_coeff)) + irrep_map = {i: s for s, i in zip(molecule.irrep_name, molecule.irrep_id)} + sqmol.mo_symm_labels = [irrep_map[i] for i in sqmol.mo_symm_ids] + else: + sqmol.mo_symm_ids = None + sqmol.mo_symm_labels = None + + sqmol.mf_energy = sqmol.mean_field.e_tot + sqmol.mo_energies = sqmol.mean_field.mo_energy + sqmol.mo_occ = sqmol.mean_field.mo_occ + + sqmol.n_mos = molecule.nao_nr() + sqmol.n_sos = 2*sqmol.n_mos + + self.mo_coeff = sqmol.mean_field.mo_coeff + + def get_integrals(self, sqmol, mo_coeff=None): + r"""Computes core constant, one_body, and two-body integrals for all orbitals + + one-body integrals should be in the form + h[p,q]= \int \phi_p(x)* (T + V_{ext}) \phi_q(x) dx + + two-body integrals should be in the form + h[p,q,r,s] = \int \phi_p(x) * \phi_q(y) * V_{elec-elec} \phi_r(y) \phi_s(x) dxdy + + Using molecular orbitals \phi_j(x) = \sum_{ij} A_i(x) mo_coeff_{i,j} where A_i(x) are the atomic orbitals. + + For UHF (if sqmol.uhf is True) + one_body coefficients are [alpha one_body, beta one_body] + two_body coefficients are [alpha-alpha two_body, alpha-beta two_body, beta-beta two_body] + + where one_body and two_body are appropriately sized arrays for each spin sector. + + Args: + sqmol (SecondQuantizedMolecule) : SecondQuantizedMolecule populated with all variables defined above + mo_coeff : Molecular orbital coefficients to use for calculating the integrals, instead of self.mo_coeff + + Returns: + (float, array or List[array], array or List[array]): (core_constant, one_body coefficients, two_body coefficients) + """ + + # Pyscf molecule to get integrals. + pyscf_mol = mol_to_pyscf(sqmol, sqmol.basis, sqmol.symmetry, sqmol.ecp) + if mo_coeff is None: + mo_coeff = self.mo_coeff + + if sqmol.uhf: + one_body, two_body = self.compute_uhf_integrals(sqmol, mo_coeff) + return float(pyscf_mol.energy_nuc()), one_body, two_body + + # Corresponding to nuclear repulsion energy and static coulomb energy. + core_constant = float(pyscf_mol.energy_nuc()) + + # get_hcore is equivalent to int1e_kin + int1e_nuc. + one_electron_integrals = mo_coeff.T @ sqmol.mean_field.get_hcore() @ mo_coeff + + # Getting 2-body integrals in atomic and converting to molecular basis. + two_electron_integrals = self.ao2mo.kernel(pyscf_mol.intor("int2e"), mo_coeff) + two_electron_integrals = self.ao2mo.restore(1, two_electron_integrals, len(mo_coeff)) + + # PQRS convention in openfermion: + # h[p,q]=\int \phi_p(x)* (T + V_{ext}) \phi_q(x) dx + # h[p,q,r,s]=\int \phi_p(x)* \phi_q(y)* V_{elec-elec} \phi_r(y) \phi_s(x) dxdy + # The convention is not the same with PySCF integrals. So, a change is + # made before performing the truncation for frozen orbitals. + two_electron_integrals = two_electron_integrals.transpose(0, 2, 3, 1) + + return core_constant, one_electron_integrals, two_electron_integrals + + def compute_uhf_integrals(self, sqmol, mo_coeff): + """Compute 1-electron and 2-electron integrals + The return is formatted as + [numpy.ndarray]*2 numpy array h_{pq} for alpha and beta blocks + [numpy.ndarray]*3 numpy array storing h_{pqrs} for alpha-alpha, alpha-beta, beta-beta blocks + + Args: + sqmol (SecondQuantizedMolecule): The SecondQuantizedMolecule object to calculated UHF integrals for. + mo_coeff (List[array]): The molecular orbital coefficients for both spins [alpha, beta] + + Returns: + List[array], List[array]: One and two body integrals + """ + # step 1 : find nao, nmo (atomic orbitals & molecular orbitals) + + # molecular orbitals (alpha and beta will be the same) + # Lets take alpha blocks to find the shape and things + + # molecular orbitals + nmo = mo_coeff[0].shape[1] + # atomic orbitals + nao = mo_coeff[0].shape[0] + + # step 2 : obtain Hcore Hamiltonian in atomic orbitals basis + hcore = sqmol.mean_field.get_hcore() + + # step 3 : obatin two-electron integral in atomic basis + eri = self.ao2mo.restore(8, sqmol.mean_field._eri, nao) + + # step 4 : create the placeholder for the matrices + # one-electron matrix (alpha, beta) + hpq = [] + + # step 5 : do the mo transformation + # step the mo coeff alpha and beta + mo_a = mo_coeff[0] + mo_b = mo_coeff[1] + + # mo transform the hcore + hpq.append(mo_a.T.dot(hcore).dot(mo_a)) + hpq.append(mo_b.T.dot(hcore).dot(mo_b)) + + # mo transform the two-electron integrals + eri_a = self.ao2mo.incore.full(eri, mo_a) + eri_b = self.ao2mo.incore.full(eri, mo_b) + eri_ba = self.ao2mo.incore.general(eri, (mo_a, mo_a, mo_b, mo_b), compact=False) + + # Change the format of integrals (full) + eri_a = self.ao2mo.restore(1, eri_a, nmo) + eri_b = self.ao2mo.restore(1, eri_b, nmo) + eri_ba = eri_ba.reshape(nmo, nmo, nmo, nmo) + + # convert this into the physicist ordering for OpenFermion + two_body_integrals_a = np.asarray(eri_a.transpose(0, 2, 3, 1), order='C') + two_body_integrals_b = np.asarray(eri_b.transpose(0, 2, 3, 1), order='C') + two_body_integrals_ab = np.asarray(eri_ba.transpose(0, 2, 3, 1), order='C') + + # Gpqrs has alpha, alphaBeta, Beta blocks + Gpqrs = (two_body_integrals_a, two_body_integrals_ab, two_body_integrals_b) + + return hpq, Gpqrs diff --git a/tangelo/toolboxes/molecular_computation/molecule.py b/tangelo/toolboxes/molecular_computation/molecule.py index 1141580e6..b9a508c31 100644 --- a/tangelo/toolboxes/molecular_computation/molecule.py +++ b/tangelo/toolboxes/molecular_computation/molecule.py @@ -21,13 +21,15 @@ from itertools import product import numpy as np -from pyscf import gto, scf, ao2mo, symm, lib import openfermion import openfermion.ops.representations as reps from openfermion.utils import down_index, up_index from openfermion.chem.molecular_data import spinorb_from_spatial from openfermion.ops.representations.interaction_operator import get_active_space_integrals as of_get_active_space_integrals +from tangelo.helpers.utils import is_package_installed +from tangelo.toolboxes.molecular_computation import IntegralSolver, IntegralSolverPsi4, IntegralSolverEmpty +from tangelo.toolboxes.molecular_computation.integral_solver_pyscf import mol_to_pyscf, IntegralSolverPySCF from tangelo.toolboxes.molecular_computation.frozen_orbitals import convert_frozen_orbitals from tangelo.toolboxes.qubit_mappings.mapping_transform import get_fermion_operator @@ -78,6 +80,7 @@ class Molecule: multi-line string. q (float): Total charge. spin (int): Absolute difference between alpha and beta electron number. + solver (IntegralSolver): The class that performs the mean field and integral computation. n_atom (int): Self-explanatory. n_electrons (int): Self-explanatory. n_min_orbitals (int): Number of orbitals with a minimal basis set. @@ -89,59 +92,54 @@ class Molecule: xyz: list or str q: int = 0 spin: int = 0 + if is_package_installed("pyscf"): + default_solver = IntegralSolverPySCF + elif is_package_installed("psi4"): + default_solver = IntegralSolverPsi4 + else: + default_solver = IntegralSolverEmpty + + solver: IntegralSolver = field(default_factory=default_solver) # Defined in __post_init__. n_atoms: int = field(init=False) n_electrons: int = field(init=False) def __post_init__(self): - mol = self.to_pyscf() - self.n_atoms = mol.natm - self.n_electrons = mol.nelectron + if isinstance(self.solver, IntegralSolverEmpty): + raise ValueError("PySCF or Psi4 must be installed or a custom solver (IntegralSolver) instance must be provided.") + self.solver.set_physical_data(self) @property def elements(self): + """(list): List of all elements in the molecule.""" return [self.xyz[i][0] for i in range(self.n_atoms)] @property def coords(self): + """(array of float): N x 3 coordinates matrix.""" return np.array([self.xyz[i][1] for i in range(self.n_atoms)]) - def to_pyscf(self, basis="CRENBL", symmetry=False, ecp=None): - """Method to return a pyscf.gto.Mole object. - - Args: - basis (string): Basis set. - symmetry (bool): Flag to turn symmetry on - ecp (dict): Dictionary with ecp definition for each atom e.g. {"Cu": "crenbl"} - - Returns: - pyscf.gto.Mole: PySCF compatible object. - """ - - mol = gto.Mole(atom=self.xyz) - mol.basis = basis - mol.charge = self.q - mol.spin = self.spin - mol.symmetry = symmetry - mol.ecp = ecp if ecp else dict() - mol.build() - - self.xyz = list() - for sym, xyz in mol._atom: - self.xyz += [tuple([sym, tuple([x*lib.parameters.BOHR for x in xyz])])] - - return mol - def to_file(self, filename, format=None): """Write molecule geometry to filename in specified format Args: filename (str): The name of the file to output the geometry. format (str): The output type of "raw", "xyz", or "zmat". If None, will be inferred by the filename + Unless using IntegralSolverPySCF, only "xyz" is supported. """ - mol = self.to_pyscf() - mol.tofile(filename, format) + if isinstance(self.solver, IntegralSolverPySCF): + mol = mol_to_pyscf(self) + mol.tofile(filename, format) + elif filename[-3:] == 'xyz' or format == 'xyz': + f = open(filename, "w") if format is None else open(filename+".xyz", "w") + f.write(f"{self.n_atoms}\n") + f.write("XYZ from Tangelo\n") + for name, positions in self.xyz: + f.write(f"{name} {positions[0]} {positions[1]} {positions[2]}\n") + f.close + else: + raise ValueError("Tangelo only supports xyz format unless using IntegralSolverPySCF") def to_openfermion(self, basis="sto-3g"): """Method to return a openfermion.MolecularData object. @@ -211,8 +209,6 @@ class SecondQuantizedMolecule(Molecule): mo_energies: list = field(init=False) mo_occ: list = field(init=False) - mean_field: scf = field(init=False) - n_mos: int = field(init=False) n_sos: int = field(init=False) @@ -223,7 +219,7 @@ class SecondQuantizedMolecule(Molecule): def __post_init__(self): super().__post_init__() - self._compute_mean_field() + self.solver.compute_mean_field(self) self.freeze_mos(self.frozen_orbitals) @property @@ -299,65 +295,15 @@ def mo_coeff(self): Returns: np.array: MO coefficient as a numpy array. """ - return self.mean_field.mo_coeff + return self.solver.mo_coeff @mo_coeff.setter def mo_coeff(self, new_mo_coeff): # Asserting the new molecular coefficient matrix have the same dimensions. - if self.uhf: - assert len(new_mo_coeff) == 2, "Must provide [alpha mo_coeff, beta_mo_coeff]" - assert ((self.mean_field.mo_coeff[0].shape == new_mo_coeff[0].shape) and - (self.mean_field.mo_coeff[1].shape == new_mo_coeff[1].shape)), \ - f"The new molecular coefficients has shape {[new_mo_coeff[0].shape, new_mo_coeff[1].shape]}"\ - f" shape: expected shape is {[self.mean_field.mo_coeff[0].shape, self.mean_field.mo_coeff[1].shape]}." - else: - assert self.mean_field.mo_coeff.shape == new_mo_coeff.shape, \ - f"The new molecular coefficients matrix has a {new_mo_coeff.shape}"\ - f" shape: expected shape is {self.mean_field.mo_coeff.shape}." - self.mean_field.mo_coeff = np.array(new_mo_coeff) - - def _compute_mean_field(self): - """Computes the mean-field for the molecule. Depending on the molecule - spin, it does a restricted or a restriction open-shell Hartree-Fock - calculation. - - It is also used for defining attributes related to the mean-field - (mf_energy, mo_energies, mo_occ, n_mos and n_sos). - """ - - molecule = self.to_pyscf(self.basis, self.symmetry, self.ecp) - - self.mean_field = scf.RHF(molecule) if not self.uhf else scf.UHF(molecule) - self.mean_field.verbose = 0 - # Force broken symmetry for uhf calculation when spin is 0 as shown in - # https://github.com/sunqm/pyscf/blob/master/examples/scf/32-break_spin_symm.py - if self.uhf and self.spin == 0: - dm_alpha, dm_beta = self.mean_field.get_init_guess() - dm_beta[:1, :] = 0 - dm = (dm_alpha, dm_beta) - self.mean_field.kernel(dm) - else: - self.mean_field.kernel() - - self.mean_field.analyze() - if not self.mean_field.converged: - raise ValueError("Hartree-Fock calculation did not converge") - - if self.symmetry: - self.mo_symm_ids = list(symm.label_orb_symm(self.mean_field.mol, self.mean_field.mol.irrep_id, - self.mean_field.mol.symm_orb, self.mean_field.mo_coeff)) - irrep_map = {i: s for s, i in zip(molecule.irrep_name, molecule.irrep_id)} - self.mo_symm_labels = [irrep_map[i] for i in self.mo_symm_ids] - else: - self.mo_symm_ids = None - self.mo_symm_labels = None - - self.mf_energy = self.mean_field.e_tot - self.mo_energies = self.mean_field.mo_energy - self.mo_occ = self.mean_field.mo_occ - - self.n_mos = molecule.nao_nr() - self.n_sos = 2*self.n_mos + assert self.solver.mo_coeff.shape == (new_mo_coeff := np.array(new_mo_coeff)).shape, \ + f"The new molecular coefficients matrix has a {new_mo_coeff.shape}"\ + f" shape: expected shape is {self.solver.mo_coeff.shape}." + self.solver.mo_coeff = new_mo_coeff def _get_fermionic_hamiltonian(self, mo_coeff=None): """This method returns the fermionic hamiltonian. It written to take @@ -450,152 +396,73 @@ def energy_from_rdms(self, one_rdm, two_rdm): return e.real - def get_active_space_integrals(self, mo_coeff=None): + def get_integrals(self, mo_coeff=None, fold_frozen=True): """Computes core constant, one_body, and two-body coefficients with frozen orbitals folded into one-body coefficients - and core constant + and core constant for mo_coeff if fold_frozen is True + For UHF one_body coefficients are [alpha one_body, beta one_body] two_body coefficients are [alpha-alpha two_body, alpha-beta two_body, beta-beta two_body] Args: mo_coeff (array): The molecular orbital coefficients to use to generate the integrals + fold_frozen (bool): If False, the full integral matrices and core constant are returned. + If True, the core constant, one_body, and two-body coefficients are calculated with frozen orbitals + folded into one-body coefficients and core constant. Default True Returns: (float, array or List[array], array or List[array]): (core_constant, one_body coefficients, two_body coefficients) """ + if not self.uhf: + core_constant, one_body_integrals, two_body_integrals = self.solver.get_integrals(self, mo_coeff) + if fold_frozen: + core_offset, one_body_integrals, two_body_integrals = of_get_active_space_integrals(one_body_integrals, + two_body_integrals, + self.frozen_occupied, + self.active_mos) + + # Adding frozen electron contribution to core constant. + core_constant += core_offset + else: + core_constant, one_body_integrals, two_body_integrals = self.solver.get_integrals(self, mo_coeff) + if fold_frozen: + core_constant, one_body_integrals, two_body_integrals = self._get_active_space_integrals_uhf(core_constant, + one_body_integrals, + two_body_integrals) + return core_constant, one_body_integrals, two_body_integrals - return self.get_integrals(mo_coeff, True) - - def get_full_space_integrals(self, mo_coeff=None): - """Computes core constant, one_body, and two-body integrals for all orbitals + def get_active_space_integrals(self, mo_coeff=None): + """Computes core constant, one_body, and two-body coefficients with frozen orbitals folded into one-body coefficients + and core constant For UHF one_body coefficients are [alpha one_body, beta one_body] two_body coefficients are [alpha-alpha two_body, alpha-beta two_body, beta-beta two_body] Args: - mo_coeff (array): The molecular orbital coefficients to use to generate the integrals. + mo_coeff (array): The molecular orbital coefficients to use to generate the integrals Returns: (float, array or List[array], array or List[array]): (core_constant, one_body coefficients, two_body coefficients) """ - return self.get_integrals(mo_coeff, False) + return self.get_integrals(mo_coeff, True) - def get_integrals(self, mo_coeff=None, consider_frozen=True): - """Computes core constant, one_body, and two-body coefficients for a given active space and mo_coeff + def get_full_space_integrals(self, mo_coeff=None): + """Computes core constant, one_body, and two-body integrals for all orbitals For UHF one_body coefficients are [alpha one_body, beta one_body] two_body coefficients are [alpha-alpha two_body, alpha-beta two_body, beta-beta two_body] Args: mo_coeff (array): The molecular orbital coefficients to use to generate the integrals. - consider_frozen (bool): If True, the frozen orbitals are folded into the one_body and core constant terms. Returns: (float, array or List[array], array or List[array]): (core_constant, one_body coefficients, two_body coefficients) """ - # Pyscf molecule to get integrals. - pyscf_mol = self.to_pyscf(self.basis, self.symmetry, self.ecp) - if mo_coeff is None: - mo_coeff = self.mean_field.mo_coeff - - if self.uhf: - if consider_frozen: - return self._get_active_space_integrals_uhf(mo_coeff=mo_coeff) - else: - one_body, two_body = self._compute_uhf_integrals(mo_coeff) - return float(pyscf_mol.energy_nuc()), one_body, two_body - - # Corresponding to nuclear repulsion energy and static coulomb energy. - core_constant = float(pyscf_mol.energy_nuc()) - - # get_hcore is equivalent to int1e_kin + int1e_nuc. - one_electron_integrals = mo_coeff.T @ self.mean_field.get_hcore() @ mo_coeff - - # Getting 2-body integrals in atomic and converting to molecular basis. - two_electron_integrals = ao2mo.kernel(pyscf_mol.intor("int2e"), mo_coeff) - two_electron_integrals = ao2mo.restore(1, two_electron_integrals, len(mo_coeff)) - - # PQRS convention in openfermion: - # h[p,q]=\int \phi_p(x)* (T + V_{ext}) \phi_q(x) dx - # h[p,q,r,s]=\int \phi_p(x)* \phi_q(y)* V_{elec-elec} \phi_r(y) \phi_s(x) dxdy - # The convention is not the same with PySCF integrals. So, a change is - # made before performing the truncation for frozen orbitals. - two_electron_integrals = two_electron_integrals.transpose(0, 2, 3, 1) - if consider_frozen: - core_offset, one_electron_integrals, two_electron_integrals = of_get_active_space_integrals(one_electron_integrals, - two_electron_integrals, - self.frozen_occupied, - self.active_mos) - - # Adding frozen electron contribution to core constant. - core_constant += core_offset - - return core_constant, one_electron_integrals, two_electron_integrals - - def _compute_uhf_integrals(self, mo_coeff): - """Compute 1-electron and 2-electron integrals - The return is formatted as - [numpy.ndarray]*2 numpy array h_{pq} for alpha and beta blocks - [numpy.ndarray]*3 numpy array storing h_{pqrs} for alpha-alpha, alpha-beta, beta-beta blocks - - Args: - List[array]: The molecular orbital coefficients for both spins [alpha, beta] - - Returns: - List[array], List[array]: One and two body integrals - """ - # step 1 : find nao, nmo (atomic orbitals & molecular orbitals) - - # molecular orbitals (alpha and beta will be the same) - # Lets take alpha blocks to find the shape and things - - # molecular orbitals - nmo = self.nmo = mo_coeff[0].shape[1] - # atomic orbitals - nao = self.nao = mo_coeff[0].shape[0] - - # step 2 : obtain Hcore Hamiltonian in atomic orbitals basis - hcore = self.mean_field.get_hcore() - - # step 3 : obatin two-electron integral in atomic basis - eri = ao2mo.restore(8, self.mean_field._eri, nao) - - # step 4 : create the placeholder for the matrices - # one-electron matrix (alpha, beta) - hpq = [] - - # step 5 : do the mo transformation - # step the mo coeff alpha and beta - mo_a = mo_coeff[0] - mo_b = mo_coeff[1] - - # mo transform the hcore - hpq.append(mo_a.T.dot(hcore).dot(mo_a)) - hpq.append(mo_b.T.dot(hcore).dot(mo_b)) - - # mo transform the two-electron integrals - eri_a = ao2mo.incore.full(eri, mo_a) - eri_b = ao2mo.incore.full(eri, mo_b) - eri_ba = ao2mo.incore.general(eri, (mo_a, mo_a, mo_b, mo_b), compact=False) - - # Change the format of integrals (full) - eri_a = ao2mo.restore(1, eri_a, nmo) - eri_b = ao2mo.restore(1, eri_b, nmo) - eri_ba = eri_ba.reshape(nmo, nmo, nmo, nmo) - - # # convert this into the order OpenFemion like to receive - two_body_integrals_a = np.asarray(eri_a.transpose(0, 2, 3, 1), order='C') - two_body_integrals_b = np.asarray(eri_b.transpose(0, 2, 3, 1), order='C') - two_body_integrals_ab = np.asarray(eri_ba.transpose(0, 2, 3, 1), order='C') - - # Gpqrs has alpha, alphaBeta, Beta blocks - Gpqrs = (two_body_integrals_a, two_body_integrals_ab, two_body_integrals_b) - - return hpq, Gpqrs + return self.get_integrals(mo_coeff, False) - def _get_active_space_integrals_uhf(self, occupied_indices=None, active_indices=None, mo_coeff=None): + def _get_active_space_integrals_uhf(self, core_constant, one_body_integrals, two_body_integrals, occupied_indices=None, active_indices=None): """Get active space integrals with uhf reference The return is (core_constant, @@ -611,19 +478,11 @@ def _get_active_space_integrals_uhf(self, occupied_indices=None, active_indices= (float, List[array], List[array]): Core constant, one body integrals, two body integrals """ - if mo_coeff is None: - mo_coeff = self.mean_field.mo_coeff - - # Get integrals. - one_body_integrals, two_body_integrals = self._compute_uhf_integrals(mo_coeff) - occupied_indices = self.frozen_occupied if occupied_indices is None else occupied_indices active_indices = self.active_mos if active_indices is None else active_indices if (len(active_indices) < 1): raise ValueError('Some active indices required for reduction.') - # Determine core constant - core_constant = self.mean_field.mol.energy_nuc() # alpha part for i in occupied_indices[0]: core_constant += one_body_integrals[0][i, i] @@ -644,7 +503,7 @@ def _get_active_space_integrals_uhf(self, occupied_indices=None, active_indices= for j in occupied_indices[1]: core_constant += 0.5*(two_body_integrals[2][i, j, j, i]-two_body_integrals[2][i, j, i, j]) - # Modified one electon integrals + # Modified one electron integrals one_body_integrals_new_aa = np.copy(one_body_integrals[0]) one_body_integrals_new_bb = np.copy(one_body_integrals[1]) @@ -680,25 +539,18 @@ def _get_active_space_integrals_uhf(self, occupied_indices=None, active_indices= return core_constant, one_body_integrals_new, two_body_integrals_new - def _get_molecular_hamiltonian_uhf(self, occupied_indices=None, - active_indices=None): + def _get_molecular_hamiltonian_uhf(self): """Output arrays of the second quantized Hamiltonian coefficients. Note: The indexing convention used is that even indices correspond to spin-up (alpha) modes and odd indices correspond to spin-down (beta) modes. - Args: - occupied_indices(list): A list of spatial orbital indices - indicating which orbitals should be considered doubly occupied. - active_indices(list): A list of spatial orbital indices indicating - which orbitals should be considered active. - Returns: InteractionOperator: The molecular hamiltonian """ - constant, one_body_integrals, two_body_integrals = self._get_active_space_integrals_uhf(occupied_indices, active_indices) + constant, one_body_integrals, two_body_integrals = self.get_active_space_integrals() # Lets find the dimensions n_orb_a = one_body_integrals[0].shape[0] diff --git a/tangelo/toolboxes/molecular_computation/rdms.py b/tangelo/toolboxes/molecular_computation/rdms.py index f7fa64c0c..a826a52cc 100644 --- a/tangelo/toolboxes/molecular_computation/rdms.py +++ b/tangelo/toolboxes/molecular_computation/rdms.py @@ -17,7 +17,6 @@ import itertools as it import numpy as np -from pyscf.lib import takebak_2d from tangelo.toolboxes.molecular_computation.coefficients import spatial_from_spinorb from tangelo.linq.helpers import pauli_string_to_of, pauli_of_to_string, get_compatible_bases @@ -200,7 +199,7 @@ def energy_from_rdms(ferm_op, one_rdm, two_rdm): return e.real -def pad_rdms_with_frozen_orbitals(sec_mol, onerdm, twordm): +def pad_rdms_with_frozen_orbitals_restricted(sec_mol, onerdm, twordm): """Function to pad the RDMs with the frozen orbitals data. It is based on the pyscf.cccsd_rdm code, where we can set with_frozen=True. @@ -220,6 +219,7 @@ def pad_rdms_with_frozen_orbitals(sec_mol, onerdm, twordm): numpy.array: Two-particle reduced density matrix (shape of (N_total_mos,)*4). """ + from pyscf.lib import takebak_2d if sec_mol.uhf: raise NotImplementedError("The RDMs padding with an UHF mean-field is not implemented.") @@ -292,3 +292,154 @@ def pad_rdms_with_frozen_orbitals(sec_mol, onerdm, twordm): twordm_padded = twordm_padded.transpose(1, 0, 3, 2) return onerdm_padded, twordm_padded + + +def pad_rdms_with_frozen_orbitals_unrestricted(sec_mol, onerdm, twordm): + """Function to pad the RDMs with the frozen orbitals data. It is based on + the pyscf.ucccsd_rdm code, where we can set with_frozen=True. + + Source: + https://github.com/pyscf/pyscf/blob/master/pyscf/cc/uccsd_rdm.py + + Args: + sec_mol (SecondQuantizedMolecule): Self-explanatory. + onerdm (tuple of arrays): Tuple of alpha and beta one-particle + reduced density matrix (shape of (N_active_mos,)*2). + twordm (tuple of arrays): Tuple of alpha-alpha, alpha-beta and beta-beta + two-particle reduced density matrix (shape of (N_active_mos,)*4). + + Returns: + tuple of arrays: Tuple of alpha and beta one-particle reduced density + matrix (shape of (N_total_mos,)*2). + tuple of arrays: Tuple of alpha-alpha, alpha-beta and beta-beta + two-particle reduced density matrix (shape of (N_total_mos,)*4). + """ + from pyscf.lib import takebak_2d + + # Unpacking the tuples. + onerdm_a, onerdm_b = onerdm + twordm_aa, twordm_ab, twordm_bb = twordm + + # Defining the number of MOs and occupation numbers with and without the + # frozen orbitals. + n_mos_a, n_mos_b = sec_mol.mo_occ[0].size, sec_mol.mo_occ[1].size + n_mos0_a, n_mos0_b = sec_mol.n_active_mos + n_occ_a = np.count_nonzero(sec_mol.mo_occ[0] > 0) + n_occ_b = np.count_nonzero(sec_mol.mo_occ[1] > 0) + n_occ0_a = n_occ_a - len(sec_mol.frozen_occupied[0]) + n_occ0_b = n_occ_b - len(sec_mol.frozen_occupied[1]) + + moidx_a = np.array(sec_mol.active_mos[0]) + moidx_b = np.array(sec_mol.active_mos[1]) + + # Creating a dummy one rdm with all diagonal elements set to 1. After that, + # the true one rdm is embedded in this bigger matrix. + onerdm_a_padded = np.zeros((n_mos_a,)*2, dtype=onerdm_a.dtype) + onerdm_b_padded = np.zeros((n_mos_b,)*2, dtype=onerdm_b.dtype) + + onerdm_a_padded[np.diag_indices(n_occ_a)] = 1. + onerdm_b_padded[np.diag_indices(n_occ_b)] = 1. + + onerdm_a_padded[moidx_a[:, None], moidx_a] = onerdm_a + onerdm_b_padded[moidx_b[:, None], moidx_b] = onerdm_b + + # Deleting the one rdm contribution in the two rdm. This must be done to + # redo the operation later with the one rdm with the frozen orbital. + twordm_aa = twordm_aa.transpose(1, 0, 3, 2) + twordm_bb = twordm_bb.transpose(1, 0, 3, 2) + twordm_ab = twordm_ab.transpose(1, 0, 3, 2) + + onerdm_without_diag_a = np.copy(onerdm_a) + onerdm_without_diag_a[np.diag_indices(n_occ0_a)] -= 1 + onerdm_without_diag_b = np.copy(onerdm_b) + onerdm_without_diag_b[np.diag_indices(n_occ0_b)] -= 1 + + onerdm_without_diag_a_T = onerdm_without_diag_a.T + onerdm_without_diag_b_T = onerdm_without_diag_b.T + + for i in range(n_occ0_a): + twordm_aa[i, i, :, :] -= onerdm_without_diag_a + twordm_aa[:, :, i, i] -= onerdm_without_diag_a + twordm_aa[:, i, i, :] += onerdm_without_diag_a + twordm_aa[i, :, :, i] += onerdm_without_diag_a_T + twordm_ab[i, i, :, :] -= onerdm_without_diag_b + + for i in range(n_occ0_b): + twordm_bb[i, i, :, :] -= onerdm_without_diag_b + twordm_bb[:, :, i, i] -= onerdm_without_diag_b + twordm_bb[:, i, i, :] += onerdm_without_diag_b + twordm_bb[i, :, :, i] += onerdm_without_diag_b_T + twordm_ab[:, :, i, i] -= onerdm_without_diag_a + + for i, j in it.product(range(n_occ0_a), repeat=2): + twordm_aa[i, i, j, j] -= 1 + twordm_aa[i, j, j, i] += 1 + + for i, j in it.product(range(n_occ0_b), repeat=2): + twordm_bb[i, i, j, j] -= 1 + twordm_bb[i, j, j, i] += 1 + + for i, j in it.product(range(n_occ0_a), range(n_occ0_b), repeat=1): + twordm_ab[i, i, j, j] -= 1 + + # Creating a dummy two rdm. + dm2_aa = np.zeros((n_mos_a,)*4, dtype=twordm_aa.dtype) + dm2_bb = np.zeros((n_mos_b,)*4, dtype=twordm_bb.dtype) + dm2_ab = np.zeros((n_mos_a, n_mos_a, n_mos_b, n_mos_b), dtype=twordm_ab.dtype) + + idx_a = (moidx_a.reshape(-1, 1) * n_mos_a + moidx_a).ravel() + idx_b = (moidx_b.reshape(-1, 1) * n_mos_b + moidx_b).ravel() + + # This part is meant to replicate + # https://github.com/pyscf/pyscf/blob/8b3fef8cf18f10d430261d4a8bea21fadf19bb1f/pyscf/cc/uccsd_rdm.py#L561-L583. + # The output is not catched, maybe for memory efficiency purposes (elements + # of dm2 changed inplace?). + takebak_2d(dm2_aa.reshape(n_mos_a**2, n_mos_a**2), + twordm_aa.reshape(n_mos0_a**2, n_mos0_a**2), idx_a, idx_a) + takebak_2d(dm2_bb.reshape(n_mos_b**2, n_mos_b**2), + twordm_bb.reshape(n_mos0_b**2, n_mos0_b**2), idx_b, idx_b) + takebak_2d(dm2_ab.reshape(n_mos_a**2, n_mos_b**2), + twordm_ab.reshape(n_mos0_a**2, n_mos0_b**2), idx_a, idx_b) + twordm_aa_padded, twordm_ab_padded, twordm_bb_padded = dm2_aa, dm2_ab, dm2_bb + + # The next few lines will reembed the on rdm, but with the frozen orbital + # elements of the one rdm matrix. + onerdm_a_padded_without_diag = np.copy(onerdm_a_padded) + onerdm_a_padded_without_diag[np.diag_indices(n_occ_a)] -= 1 + + onerdm_b_padded_without_diag = np.copy(onerdm_b_padded) + onerdm_b_padded_without_diag[np.diag_indices(n_occ_b)] -= 1 + + onerdm_a_padded_without_diag_T = onerdm_a_padded_without_diag.T + onerdm_b_padded_without_diag_T = onerdm_b_padded_without_diag.T + + for i in range(n_occ_a): + twordm_aa_padded[i, i, :, :] += onerdm_a_padded_without_diag + twordm_aa_padded[:, :, i, i] += onerdm_a_padded_without_diag + twordm_aa_padded[:, i, i, :] -= onerdm_a_padded_without_diag + twordm_aa_padded[i, :, :, i] -= onerdm_a_padded_without_diag_T + twordm_ab_padded[i, i, :, :] += onerdm_b_padded_without_diag + + for i in range(n_occ_b): + twordm_bb_padded[i, i, :, :] += onerdm_b_padded_without_diag + twordm_bb_padded[:, :, i, i] += onerdm_b_padded_without_diag + twordm_bb_padded[:, i, i, :] -= onerdm_b_padded_without_diag + twordm_bb_padded[i, :, :, i] -= onerdm_b_padded_without_diag_T + twordm_ab_padded[:, :, i, i] += onerdm_a_padded_without_diag + + for i, j in it.product(range(n_occ_a), repeat=2): + twordm_aa_padded[i, i, j, j] += 1 + twordm_aa_padded[i, j, j, i] -= 1 + + for i, j in it.product(range(n_occ_b), repeat=2): + twordm_bb_padded[i, i, j, j] += 1 + twordm_bb_padded[i, j, j, i] -= 1 + + for i, j in it.product(range(n_occ_a), range(n_occ_b), repeat=1): + twordm_ab_padded[i, i, j, j] += 1 + + twordm_aa_padded = twordm_aa_padded.transpose(1, 0, 3, 2) + twordm_bb_padded = twordm_bb_padded.transpose(1, 0, 3, 2) + twordm_ab_padded = twordm_ab_padded.transpose(1, 0, 3, 2) + + return (onerdm_a_padded, onerdm_b_padded), (twordm_aa_padded, twordm_ab_padded, twordm_bb_padded) diff --git a/tangelo/toolboxes/molecular_computation/tests/data/H2O_UHF_sto3g_onerdm_frozen0345_alpha.data b/tangelo/toolboxes/molecular_computation/tests/data/H2O_UHF_sto3g_onerdm_frozen0345_alpha.data new file mode 100644 index 000000000..5ae6777e2 --- /dev/null +++ b/tangelo/toolboxes/molecular_computation/tests/data/H2O_UHF_sto3g_onerdm_frozen0345_alpha.data @@ -0,0 +1,3 @@ +9.997235091583214217e-01 1.136297046749499573e-17 -2.351834167930586350e-16 +1.136297046749499573e-17 9.934025027388243556e-01 -4.013864984228520316e-04 +-2.351834167930586350e-16 -4.013864984228520316e-04 6.873988102854210538e-03 diff --git a/tangelo/toolboxes/molecular_computation/tests/data/H2O_UHF_sto3g_onerdm_frozen0345_beta.data b/tangelo/toolboxes/molecular_computation/tests/data/H2O_UHF_sto3g_onerdm_frozen0345_beta.data new file mode 100644 index 000000000..77520bcfd --- /dev/null +++ b/tangelo/toolboxes/molecular_computation/tests/data/H2O_UHF_sto3g_onerdm_frozen0345_beta.data @@ -0,0 +1,3 @@ +9.997235091583214217e-01 1.189531294503062213e-17 -4.377458585087046756e-16 +1.189531294503062213e-17 9.934025028260732304e-01 -4.012792638230027148e-04 +-4.377458585087046756e-16 -4.012792638230027148e-04 6.873988015605295848e-03 diff --git a/tangelo/toolboxes/molecular_computation/tests/data/H2O_UHF_sto3g_padded_onerdm_frozen0345_alpha.data b/tangelo/toolboxes/molecular_computation/tests/data/H2O_UHF_sto3g_padded_onerdm_frozen0345_alpha.data new file mode 100644 index 000000000..f4db3f2ff --- /dev/null +++ b/tangelo/toolboxes/molecular_computation/tests/data/H2O_UHF_sto3g_padded_onerdm_frozen0345_alpha.data @@ -0,0 +1,7 @@ +1.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 +0.000000000000000000e+00 9.997235091583203115e-01 1.675023533827496566e-17 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 -8.360651736217055188e-18 +0.000000000000000000e+00 1.675023533827496566e-17 9.934025027388223572e-01 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 -4.013865074226849201e-04 +0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 +0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 +0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 +0.000000000000000000e+00 -8.360651736217055188e-18 -4.013865074226849201e-04 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 6.873988102857366868e-03 diff --git a/tangelo/toolboxes/molecular_computation/tests/data/H2O_UHF_sto3g_padded_onerdm_frozen0345_beta.data b/tangelo/toolboxes/molecular_computation/tests/data/H2O_UHF_sto3g_padded_onerdm_frozen0345_beta.data new file mode 100644 index 000000000..5bd7a3f2d --- /dev/null +++ b/tangelo/toolboxes/molecular_computation/tests/data/H2O_UHF_sto3g_padded_onerdm_frozen0345_beta.data @@ -0,0 +1,7 @@ +1.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 +0.000000000000000000e+00 9.997235091583203115e-01 1.597629077790424253e-17 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 2.926292297131071890e-16 +0.000000000000000000e+00 1.597629077790424253e-17 9.934025028260864421e-01 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 -4.012792541545758484e-04 +0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 +0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 +0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 +0.000000000000000000e+00 2.926292297131071890e-16 -4.012792541545758484e-04 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 6.873988015593263806e-03 diff --git a/tangelo/toolboxes/molecular_computation/tests/data/H2O_UHF_sto3g_padded_twordm_frozen0345_alphaalpha.data b/tangelo/toolboxes/molecular_computation/tests/data/H2O_UHF_sto3g_padded_twordm_frozen0345_alphaalpha.data new file mode 100644 index 000000000..8bec82011 --- /dev/null +++ b/tangelo/toolboxes/molecular_computation/tests/data/H2O_UHF_sto3g_padded_twordm_frozen0345_alphaalpha.data @@ -0,0 +1,49 @@ +0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 9.997235091583203115e-01 1.675023533827496566e-17 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 -8.360651736217055188e-18 0.000000000000000000e+00 1.675023533827496566e-17 9.934025027388223572e-01 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 -4.013865074226849201e-04 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 -8.360651736217055188e-18 -4.013865074226849201e-04 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 6.873988102857366868e-03 +0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 -9.997235091583203115e-01 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 -1.675023533827496566e-17 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 8.360651736217055188e-18 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 +0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 -1.675023533827496566e-17 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 -9.934025027388223572e-01 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 4.013865074226849201e-04 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 +0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 -1.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 +0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 -1.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 +0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 +0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 8.360651736217055188e-18 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 4.013865074226849201e-04 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 -6.873988102857366868e-03 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 +0.000000000000000000e+00 -9.997235091583203115e-01 -1.675023533827496566e-17 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 8.360651736217055188e-18 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 +9.997235091583203115e-01 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 9.931260118971426687e-01 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 -4.013865074226849201e-04 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 9.997235091583203115e-01 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 9.997235091583203115e-01 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 -4.013865074226849201e-04 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 6.597497261177630648e-03 +1.675023533827496566e-17 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 -9.931260118971426687e-01 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 8.360651736217055188e-18 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 1.675023533827496566e-17 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 1.675023533827496566e-17 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 4.013865074226849201e-04 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 1.675023533827496566e-17 +0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 -9.997235091583203115e-01 -1.675023533827496566e-17 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 8.360651736217055188e-18 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 +0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 -9.997235091583203115e-01 -1.675023533827496566e-17 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 8.360651736217055188e-18 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 +0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 +-8.360651736217055188e-18 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 4.013865074226849201e-04 -8.360651736217055188e-18 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 -8.360651736217055188e-18 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 -8.360651736217055188e-18 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 -6.597497261177630648e-03 -1.675023533827496566e-17 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 +0.000000000000000000e+00 -1.675023533827496566e-17 -9.934025027388223572e-01 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 4.013865074226849201e-04 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 +1.675023533827496566e-17 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 -9.931260118971426687e-01 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 4.013865074226849201e-04 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 1.675023533827496566e-17 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 1.675023533827496566e-17 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 8.360651736217055188e-18 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 1.675023533827496566e-17 +9.934025027388223572e-01 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 9.931260118971426687e-01 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 -8.360651736217055188e-18 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 9.934025027388223572e-01 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 9.934025027388223572e-01 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 -8.360651736217055188e-18 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 2.764908416797362198e-04 +0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 -1.675023533827496566e-17 -9.934025027388223572e-01 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 4.013865074226849201e-04 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 +0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 -1.675023533827496566e-17 -9.934025027388223572e-01 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 4.013865074226849201e-04 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 +0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 +-4.013865074226849201e-04 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 -4.013865074226849201e-04 8.360651736217055188e-18 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 -4.013865074226849201e-04 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 -4.013865074226849201e-04 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 -1.675023533827496566e-17 -2.764908416797362198e-04 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 +0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 -1.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 +0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 -9.997235091583203115e-01 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 -1.675023533827496566e-17 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 8.360651736217055188e-18 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 +0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 -1.675023533827496566e-17 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 -9.934025027388223572e-01 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 4.013865074226849201e-04 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 +1.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 9.997235091583203115e-01 1.675023533827496566e-17 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 -8.360651736217055188e-18 0.000000000000000000e+00 1.675023533827496566e-17 9.934025027388223572e-01 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 -4.013865074226849201e-04 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 -8.360651736217055188e-18 -4.013865074226849201e-04 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 6.873988102857366868e-03 +0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 -1.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 +0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 +0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 8.360651736217055188e-18 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 4.013865074226849201e-04 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 -6.873988102857366868e-03 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 +0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 -1.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 +0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 -9.997235091583203115e-01 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 -1.675023533827496566e-17 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 8.360651736217055188e-18 0.000000000000000000e+00 0.000000000000000000e+00 +0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 -1.675023533827496566e-17 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 -9.934025027388223572e-01 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 4.013865074226849201e-04 0.000000000000000000e+00 0.000000000000000000e+00 +0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 -1.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 +1.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 9.997235091583203115e-01 1.675023533827496566e-17 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 -8.360651736217055188e-18 0.000000000000000000e+00 1.675023533827496566e-17 9.934025027388223572e-01 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 -4.013865074226849201e-04 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 -8.360651736217055188e-18 -4.013865074226849201e-04 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 6.873988102857366868e-03 +0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 +0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 8.360651736217055188e-18 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 4.013865074226849201e-04 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 -6.873988102857366868e-03 0.000000000000000000e+00 0.000000000000000000e+00 +0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 +0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 +0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 +0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 +0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 +0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 +0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 +0.000000000000000000e+00 8.360651736217055188e-18 4.013865074226849201e-04 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 -6.873988102857366868e-03 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 +-8.360651736217055188e-18 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 4.013865074226849201e-04 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 -6.597497261177630648e-03 0.000000000000000000e+00 0.000000000000000000e+00 -8.360651736217055188e-18 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 -1.675023533827496566e-17 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 -8.360651736217055188e-18 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 -8.360651736217055188e-18 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 +-4.013865074226849201e-04 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 -4.013865074226849201e-04 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 -1.675023533827496566e-17 0.000000000000000000e+00 8.360651736217055188e-18 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 -2.764908416797362198e-04 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 -4.013865074226849201e-04 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 -4.013865074226849201e-04 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 +0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 8.360651736217055188e-18 4.013865074226849201e-04 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 -6.873988102857366868e-03 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 +0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 8.360651736217055188e-18 4.013865074226849201e-04 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 -6.873988102857366868e-03 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 +0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 +6.873988102857366868e-03 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 6.597497261177630648e-03 1.675023533827496566e-17 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 1.675023533827496566e-17 2.764908416797362198e-04 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 6.873988102857366868e-03 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 6.873988102857366868e-03 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 diff --git a/tangelo/toolboxes/molecular_computation/tests/data/H2O_UHF_sto3g_padded_twordm_frozen0345_alphabeta.data b/tangelo/toolboxes/molecular_computation/tests/data/H2O_UHF_sto3g_padded_twordm_frozen0345_alphabeta.data new file mode 100644 index 000000000..5f4c08c8d --- /dev/null +++ b/tangelo/toolboxes/molecular_computation/tests/data/H2O_UHF_sto3g_padded_twordm_frozen0345_alphabeta.data @@ -0,0 +1,49 @@ +1.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 9.997235091583203115e-01 1.597629077790424253e-17 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 2.926292297131071890e-16 0.000000000000000000e+00 1.597629077790424253e-17 9.934025028260864421e-01 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 -4.012792541545758484e-04 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 2.926292297131071890e-16 -4.012792541545758484e-04 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 6.873988015593263806e-03 +0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 +0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 +0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 +0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 +0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 +0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 +0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 +9.997235091583203115e-01 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 9.997235091583203115e-01 1.309010787755848313e-17 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 2.925724710819990422e-16 0.000000000000000000e+00 1.309010787755848313e-17 9.931260119844067535e-01 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 -4.012792541545758484e-04 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 9.997235091583203115e-01 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 9.997235091583203115e-01 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 2.925724710819990422e-16 -4.012792541545758484e-04 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 6.597497173913527586e-03 +1.675023533827496566e-17 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 1.409680339374736422e-17 1.350590391334188182e-03 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 -7.291471632931312719e-06 0.000000000000000000e+00 2.769710061454873114e-32 2.651935135858987479e-18 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 7.611290305903643434e-20 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 1.675023533827496566e-17 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 1.675023533827496566e-17 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 5.441488191956524424e-34 -2.772520762642460089e-19 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 1.675173214694357963e-17 +0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 +0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 +0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 +-8.360651736217055188e-18 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 -3.477777295535606800e-18 -7.289816928325241658e-06 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 -1.657076322267779858e-02 0.000000000000000000e+00 -5.097059832456533627e-32 -8.284556106000154759e-18 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 1.729759045500409628e-16 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 -8.360651736217055188e-18 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 -8.360651736217055188e-18 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 -1.002432346909492522e-33 1.496468982588449390e-21 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 -4.958970070898349587e-18 +0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 +1.675023533827496566e-17 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 1.409680339374736422e-17 2.769710061454873114e-32 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 5.441488191956524424e-34 0.000000000000000000e+00 1.350590391334188182e-03 2.651935135858987479e-18 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 -2.772520762642460089e-19 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 1.675023533827496566e-17 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 1.675023533827496566e-17 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 -7.291471632931312719e-06 7.611290305903643434e-20 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 1.675173214694357963e-17 +9.934025027388223572e-01 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 9.931260118971426687e-01 3.014922284830256187e-18 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 2.926992046298030571e-16 0.000000000000000000e+00 3.014922284830256187e-18 9.934023105392162378e-01 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 -4.368963164279367358e-04 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 9.934025027388223572e-01 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 9.934025027388223572e-01 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 2.926992046298030571e-16 -4.368963164279367358e-04 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 2.766830412857905486e-04 +0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 +0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 +0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 +-4.013865074226849201e-04 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 -4.013865074226849201e-04 6.995903678796741079e-20 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 1.590265782022741690e-16 0.000000000000000000e+00 2.385165187435370738e-17 -4.369954868672334047e-04 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 -8.094414067987755479e-02 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 -4.013865074226849201e-04 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 -4.013865074226849201e-04 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 -1.287686068428934079e-19 1.922432331543861884e-07 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 3.560897944454848463e-05 +0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 +0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 +0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 +1.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 9.997235091583203115e-01 1.597629077790424253e-17 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 2.926292297131071890e-16 0.000000000000000000e+00 1.597629077790424253e-17 9.934025028260864421e-01 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 -4.012792541545758484e-04 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 2.926292297131071890e-16 -4.012792541545758484e-04 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 6.873988015593263806e-03 +0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 +0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 +0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 +0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 +0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 +0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 +0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 +1.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 9.997235091583203115e-01 1.597629077790424253e-17 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 2.926292297131071890e-16 0.000000000000000000e+00 1.597629077790424253e-17 9.934025028260864421e-01 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 -4.012792541545758484e-04 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 2.926292297131071890e-16 -4.012792541545758484e-04 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 6.873988015593263806e-03 +0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 +0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 +0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 +0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 +0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 +0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 +0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 +0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 +0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 +0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 +-8.360651736217055188e-18 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 -3.477777295535606800e-18 -5.097059832456533627e-32 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 -1.002432346909492522e-33 0.000000000000000000e+00 -7.289816928325241658e-06 -8.284556106000154759e-18 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 1.496468982588449390e-21 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 -8.360651736217055188e-18 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 -8.360651736217055188e-18 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 -1.657076322267779858e-02 1.729759045500409628e-16 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 -4.958970070898349587e-18 +-4.013865074226849201e-04 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 -4.013865074226849201e-04 2.385165187435370738e-17 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 -1.287686068428934079e-19 0.000000000000000000e+00 6.995903678796741079e-20 -4.369954868672334047e-04 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 1.922432331543861884e-07 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 -4.013865074226849201e-04 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 -4.013865074226849201e-04 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 1.590265782022741690e-16 -8.094414067987755479e-02 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 3.560897944454848463e-05 +0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 +0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 +0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 +6.873988102857366868e-03 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 6.597497261177630648e-03 1.584755139341974266e-17 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 -1.321628558774195819e-20 0.000000000000000000e+00 1.584755139341974266e-17 2.766831285498936102e-04 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 3.561706227336088743e-05 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 6.873988102857366868e-03 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 6.873988102857366868e-03 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 -1.321628558774195819e-20 3.561706227336088743e-05 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 6.873795815987210345e-03 diff --git a/tangelo/toolboxes/molecular_computation/tests/data/H2O_UHF_sto3g_padded_twordm_frozen0345_betabeta.data b/tangelo/toolboxes/molecular_computation/tests/data/H2O_UHF_sto3g_padded_twordm_frozen0345_betabeta.data new file mode 100644 index 000000000..d163a940b --- /dev/null +++ b/tangelo/toolboxes/molecular_computation/tests/data/H2O_UHF_sto3g_padded_twordm_frozen0345_betabeta.data @@ -0,0 +1,49 @@ +0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 9.997235091583203115e-01 1.597629077790424253e-17 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 2.926292297131071890e-16 0.000000000000000000e+00 1.597629077790424253e-17 9.934025028260864421e-01 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 -4.012792541545758484e-04 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 2.926292297131071890e-16 -4.012792541545758484e-04 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 6.873988015593263806e-03 +0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 -9.997235091583203115e-01 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 -1.597629077790424253e-17 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 -2.926292297131071890e-16 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 +0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 -1.597629077790424253e-17 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 -9.934025028260864421e-01 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 4.012792541545758484e-04 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 +0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 -1.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 +0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 -1.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 +0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 +0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 -2.926292297131071890e-16 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 4.012792541545758484e-04 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 -6.873988015593263806e-03 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 +0.000000000000000000e+00 -9.997235091583203115e-01 -1.597629077790424253e-17 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 -2.926292297131071890e-16 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 +9.997235091583203115e-01 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 9.931260119844067535e-01 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 -4.012792541545758484e-04 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 9.997235091583203115e-01 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 9.997235091583203115e-01 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 -4.012792541545758484e-04 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 6.597497173913527586e-03 +1.597629077790424253e-17 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 -9.931260119844067535e-01 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 -2.926292297131071890e-16 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 1.597629077790424253e-17 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 1.597629077790424253e-17 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 4.012792541545758484e-04 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 1.597629077790424253e-17 +0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 -9.997235091583203115e-01 -1.597629077790424253e-17 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 -2.926292297131071890e-16 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 +0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 -9.997235091583203115e-01 -1.597629077790424253e-17 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 -2.926292297131071890e-16 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 +0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 +2.926292297131071890e-16 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 4.012792541545758484e-04 2.926292297131071890e-16 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 2.926292297131071890e-16 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 2.926292297131071890e-16 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 -6.597497173913527586e-03 -1.597629077790424253e-17 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 +0.000000000000000000e+00 -1.597629077790424253e-17 -9.934025028260864421e-01 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 4.012792541545758484e-04 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 +1.597629077790424253e-17 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 -9.931260119844067535e-01 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 4.012792541545758484e-04 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 1.597629077790424253e-17 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 1.597629077790424253e-17 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 -2.926292297131071890e-16 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 1.597629077790424253e-17 +9.934025028260864421e-01 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 9.931260119844067535e-01 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 2.926292297131071890e-16 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 9.934025028260864421e-01 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 9.934025028260864421e-01 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 2.926292297131071890e-16 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 2.764908416797362198e-04 +0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 -1.597629077790424253e-17 -9.934025028260864421e-01 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 4.012792541545758484e-04 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 +0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 -1.597629077790424253e-17 -9.934025028260864421e-01 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 4.012792541545758484e-04 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 +0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 +-4.012792541545758484e-04 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 -4.012792541545758484e-04 -2.926292297131071890e-16 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 -4.012792541545758484e-04 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 -4.012792541545758484e-04 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 -1.597629077790424253e-17 -2.764908416797362198e-04 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 +0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 -1.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 +0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 -9.997235091583203115e-01 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 -1.597629077790424253e-17 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 -2.926292297131071890e-16 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 +0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 -1.597629077790424253e-17 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 -9.934025028260864421e-01 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 4.012792541545758484e-04 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 +1.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 9.997235091583203115e-01 1.597629077790424253e-17 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 2.926292297131071890e-16 0.000000000000000000e+00 1.597629077790424253e-17 9.934025028260864421e-01 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 -4.012792541545758484e-04 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 2.926292297131071890e-16 -4.012792541545758484e-04 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 6.873988015593263806e-03 +0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 -1.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 +0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 +0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 -2.926292297131071890e-16 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 4.012792541545758484e-04 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 -6.873988015593263806e-03 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 +0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 -1.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 +0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 -9.997235091583203115e-01 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 -1.597629077790424253e-17 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 -2.926292297131071890e-16 0.000000000000000000e+00 0.000000000000000000e+00 +0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 -1.597629077790424253e-17 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 -9.934025028260864421e-01 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 4.012792541545758484e-04 0.000000000000000000e+00 0.000000000000000000e+00 +0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 -1.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 +1.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 9.997235091583203115e-01 1.597629077790424253e-17 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 2.926292297131071890e-16 0.000000000000000000e+00 1.597629077790424253e-17 9.934025028260864421e-01 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 -4.012792541545758484e-04 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 1.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 2.926292297131071890e-16 -4.012792541545758484e-04 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 6.873988015593263806e-03 +0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 +0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 -2.926292297131071890e-16 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 4.012792541545758484e-04 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 -6.873988015593263806e-03 0.000000000000000000e+00 0.000000000000000000e+00 +0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 +0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 +0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 +0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 +0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 +0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 +0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 +0.000000000000000000e+00 -2.926292297131071890e-16 4.012792541545758484e-04 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 -6.873988015593263806e-03 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 +2.926292297131071890e-16 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 4.012792541545758484e-04 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 -6.597497173913527586e-03 0.000000000000000000e+00 0.000000000000000000e+00 2.926292297131071890e-16 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 -1.597629077790424253e-17 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 2.926292297131071890e-16 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 2.926292297131071890e-16 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 +-4.012792541545758484e-04 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 -4.012792541545758484e-04 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 -1.597629077790424253e-17 0.000000000000000000e+00 -2.926292297131071890e-16 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 -2.764908416797362198e-04 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 -4.012792541545758484e-04 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 -4.012792541545758484e-04 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 +0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 -2.926292297131071890e-16 4.012792541545758484e-04 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 -6.873988015593263806e-03 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 +0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 -2.926292297131071890e-16 4.012792541545758484e-04 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 -6.873988015593263806e-03 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 +0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 +6.873988015593263806e-03 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 6.597497173913527586e-03 1.597629077790424253e-17 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 1.597629077790424253e-17 2.764908416797362198e-04 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 6.873988015593263806e-03 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 6.873988015593263806e-03 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 diff --git a/tangelo/toolboxes/molecular_computation/tests/data/H2O_UHF_sto3g_twordm_frozen0345_alphaalpha.data b/tangelo/toolboxes/molecular_computation/tests/data/H2O_UHF_sto3g_twordm_frozen0345_alphaalpha.data new file mode 100644 index 000000000..eb0a7553a --- /dev/null +++ b/tangelo/toolboxes/molecular_computation/tests/data/H2O_UHF_sto3g_twordm_frozen0345_alphaalpha.data @@ -0,0 +1,9 @@ +0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 9.931260118971457773e-01 -4.013864984228520316e-04 0.000000000000000000e+00 -4.013864984228520316e-04 6.597497261175658267e-03 +0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 -9.931260118971457773e-01 0.000000000000000000e+00 2.351834167930586350e-16 4.013864984228520316e-04 0.000000000000000000e+00 1.136297046749499573e-17 +0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 4.013864984228520316e-04 -2.351834167930586350e-16 0.000000000000000000e+00 -6.597497261175658267e-03 -1.136297046749499573e-17 0.000000000000000000e+00 +0.000000000000000000e+00 -9.931260118971457773e-01 4.013864984228520316e-04 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 2.351834167930586350e-16 1.136297046749499573e-17 +9.931260118971457773e-01 0.000000000000000000e+00 -2.351834167930586350e-16 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 -2.351834167930586350e-16 0.000000000000000000e+00 2.764908416785522710e-04 +-4.013864984228520316e-04 2.351834167930586350e-16 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 -1.136297046749499573e-17 -2.764908416785522710e-04 0.000000000000000000e+00 +0.000000000000000000e+00 4.013864984228520316e-04 -6.597497261175658267e-03 0.000000000000000000e+00 -2.351834167930586350e-16 -1.136297046749499573e-17 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 +-4.013864984228520316e-04 0.000000000000000000e+00 -1.136297046749499573e-17 2.351834167930586350e-16 0.000000000000000000e+00 -2.764908416785522710e-04 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 +6.597497261175658267e-03 1.136297046749499573e-17 0.000000000000000000e+00 1.136297046749499573e-17 2.764908416785522710e-04 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 diff --git a/tangelo/toolboxes/molecular_computation/tests/data/H2O_UHF_sto3g_twordm_frozen0345_alphabeta.data b/tangelo/toolboxes/molecular_computation/tests/data/H2O_UHF_sto3g_twordm_frozen0345_alphabeta.data new file mode 100644 index 000000000..d1c7f0e4c --- /dev/null +++ b/tangelo/toolboxes/molecular_computation/tests/data/H2O_UHF_sto3g_twordm_frozen0345_alphabeta.data @@ -0,0 +1,9 @@ +9.997235091583214217e-01 9.973383310184106399e-18 -4.417921387011619524e-16 9.973383310184106399e-18 9.931260119843946521e-01 -4.012792638230027148e-04 -4.417921387011619524e-16 -4.012792638230027148e-04 6.597497173926743577e-03 +9.281451584949350224e-18 1.350590391331839583e-03 -7.291471494981788818e-06 1.446890036322141365e-32 1.974812503159007884e-18 5.068412050885988548e-20 3.046158862642963939e-32 -1.976509281211910135e-17 1.146967684688163335e-17 +-2.425540068672408976e-16 -7.289817078389838840e-06 -1.657076322264228879e-02 5.123412101452217247e-32 -2.351327441726502611e-16 1.151858799908780626e-16 1.078643762464151021e-31 1.066821679740458137e-19 7.319917453773913230e-18 +9.281451584949350224e-18 1.446890036322141365e-32 3.046158862642963939e-32 1.350590391331839583e-03 1.974812503159007884e-18 -1.976509281211910135e-17 -7.291471494981788818e-06 5.068412050885988548e-20 1.146967684688163335e-17 +9.931260118971457773e-01 1.727600400873241361e-18 -4.376909657845383837e-16 1.727600400873241361e-18 9.934023105392103536e-01 -4.368963254226043225e-04 -4.376909657845383837e-16 -4.368963254226043225e-04 2.766830412925204083e-04 +-4.013864984228520316e-04 5.488026913757420046e-20 1.247504485906795796e-16 -3.600353658784097084e-17 -4.369954786005200275e-04 -8.094414067990986228e-02 1.943733367908825985e-19 1.922432334755271631e-07 3.560898017766802302e-05 +-2.425540068672408976e-16 5.123412101452217247e-32 1.078643762464151021e-31 -7.289817078389838840e-06 -2.351327441726502611e-16 1.066821679740458137e-19 -1.657076322264228879e-02 1.151858799908780626e-16 7.319917453773913230e-18 +-4.013864984228520316e-04 -3.600353658784097084e-17 1.943733367908825985e-19 5.488026913757420046e-20 -4.369954786005200275e-04 1.922432334755271631e-07 1.247504485906795796e-16 -8.094414067990986228e-02 3.560898017766802302e-05 +6.597497261175658267e-03 1.208964217900389650e-17 3.991387468290988016e-18 1.208964217900389650e-17 2.766831285414350985e-04 3.561706159960163483e-05 3.991387468290988016e-18 3.561706159960163483e-05 6.873795815991327711e-03 diff --git a/tangelo/toolboxes/molecular_computation/tests/data/H2O_UHF_sto3g_twordm_frozen0345_betabeta.data b/tangelo/toolboxes/molecular_computation/tests/data/H2O_UHF_sto3g_twordm_frozen0345_betabeta.data new file mode 100644 index 000000000..4f32a4a29 --- /dev/null +++ b/tangelo/toolboxes/molecular_computation/tests/data/H2O_UHF_sto3g_twordm_frozen0345_betabeta.data @@ -0,0 +1,9 @@ +0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 9.931260119843946521e-01 -4.012792638230027148e-04 0.000000000000000000e+00 -4.012792638230027148e-04 6.597497173926743577e-03 +0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 -9.931260119843946521e-01 0.000000000000000000e+00 4.377458585087046756e-16 4.012792638230027148e-04 0.000000000000000000e+00 1.189531294503062213e-17 +0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 4.012792638230027148e-04 -4.377458585087046756e-16 0.000000000000000000e+00 -6.597497173926743577e-03 -1.189531294503062213e-17 0.000000000000000000e+00 +0.000000000000000000e+00 -9.931260119843946521e-01 4.012792638230027148e-04 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 4.377458585087046756e-16 1.189531294503062213e-17 +9.931260119843946521e-01 0.000000000000000000e+00 -4.377458585087046756e-16 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 -4.377458585087046756e-16 0.000000000000000000e+00 2.764908416785522710e-04 +-4.012792638230027148e-04 4.377458585087046756e-16 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 -1.189531294503062213e-17 -2.764908416785522710e-04 0.000000000000000000e+00 +0.000000000000000000e+00 4.012792638230027148e-04 -6.597497173926743577e-03 0.000000000000000000e+00 -4.377458585087046756e-16 -1.189531294503062213e-17 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 +-4.012792638230027148e-04 0.000000000000000000e+00 -1.189531294503062213e-17 4.377458585087046756e-16 0.000000000000000000e+00 -2.764908416785522710e-04 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 +6.597497173926743577e-03 1.189531294503062213e-17 0.000000000000000000e+00 1.189531294503062213e-17 2.764908416785522710e-04 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 0.000000000000000000e+00 diff --git a/tangelo/toolboxes/molecular_computation/tests/data/h2_631g.npz b/tangelo/toolboxes/molecular_computation/tests/data/h2_631g.npz new file mode 100644 index 0000000000000000000000000000000000000000..2de38a994b0bd7dcd3fa45081d9847c6b29d922a GIT binary patch literal 4912 zcmdT|dsI}%86TF#2UU4!9@GRksg=}lumoDcj2%pZ7>Ph zQ7Q2aVj2+_(8dQrfkhx4ytoQJ!XaW!(8e@TimpN93uBSU^t{{7EoS^0N) zgaPcrGv>8S?!`P8I0Ee3dQc`Yf; z0Q0AQ5vp6E^Jw^LI&!xMwqGI%3KUD}0UBI6SkdvS?d7OotEkbKGPWn`Ka?8(pN!S5ck2LyV+q3%8KZ^Wn01ok%I8>S=o8QVd*{K}F~LB9;I{pfi3+--+ReFp1e z*;Zly4--CPEW23P?+RG|Qj7fyBE?lRHl~@z#Ygzn?c75Q&sY#@Jcy5sjDmHFXkl07 zU&8D1rK4+;5Q?e9@7+$deLlaCv=!N-o_xhlPKN{l>300>$yi(4(JIn$BNFC~0x!DL z1b$@-|0B-+8li+unET7^KW}{+*4-=Yb=nIgrt53QNItIl*@q;*;t+X}UIw`= zu209c28Na;))1$?YCL-Gu3Par5(zFX0eY;0pSJ8fj9uR)_~HIvICl^55BUFh&=STZ zN?m>n`#u4G1U_no{YPvh`t+|l$<3f|z`qt66fIpXIC+AMt(XmRmGoZt7q`YoIBLiDD zy>^ytw!-_szaF<9ceGT6*ArFT=@LgwYhIX<;1w~z(>{^s&b(hQ_~~r@Jwm#j)U~eg zKX%zN#wHH<&+T8=*${ekpRm7h+A7G8Dm-*h5BR-@&Md6{F#F~6(2^K#7dnfLii3o?QKLxN{R&s%_xQdj>L^=`j<3;aGrdH&B@ zyOFW|8$`b%(Y=gq8t|Xn-~NsX>P8aw`TOJA@(hHcszhFCI)+0&?!}yEj@MQ1l*gUd z1?T6vz;U3rk#c$O*`d%!TBxi3g#Ub9?mw5Ge&k^U`|fw0QgXF#RQu*Tl1g7v=}Rho zNu{r1Z{qm+7%OC*eEGuO57;xOq;T%!r}D7&|Mz&`?Afkar}K5W5#zf5;HEqkoXgyD z&$m^Fd5QcecfPrPytRzyJLB4c!xBzvoDfmwUWDEwO06_a*&T^H1BexeWwcE zr`n(2$OUlnHCzU0)b|4v=N|b7dGNbOJ9kf`2l30j7W2$c2j7yEacY#ZFLD?|0ftGx z#4IAjUFY{7hw%FU>;S5DGK!QCHtaoBql932a3Ku&Q3&o|0sZ|lxk&V+i_QJfQ^Kr38__;rS7hpGXf11 uzFJLLUhZmfezKaY*wkvuly+D1g&pz_5 ilmn", isq.T, eed1) + eed1 = np.einsum("kl,jlmn -> jkmn", isq.T, eed1) + eed1 = np.einsum("jlmn, mk -> jlkn", eed1, isq) + eed = np.einsum("jlmn, nk -> jlmk", eed1, isq) + tb = eed.transpose(0, 2, 3, 1) + + return self.core_constant, ob, tb + + +class TestNopyscf(unittest.TestCase): + @unittest.skipIf(len(installed_chem_backends) > 0, "Test Skipped: A chem backend is available \n") + def test_no_solver(self): + "Test that a ValueError is raised when SecondQuantizedMolecule is called without PySCF, Psi4 or custom solver" + with self.assertRaises(ValueError): + SecondQuantizedMolecule(h2, 0, 0) + + def test_sa_oo_vqe(self): + "Test that sa_oo_vqe works properly when using a user defined IntegralSolver that only reads in integrals" + molecule_dummy = SecondQuantizedMolecule(h2, 0, 0, IntegralSolverDummy(), basis="6-31g", frozen_orbitals=[3]) + sa_oo_vqe = SA_OO_Solver({"molecule": molecule_dummy, "ref_states": [[1, 1, 0, 0, 0, 0]], + "tol": 1.e-5, "ansatz": BuiltInAnsatze.UCCSD, "n_oo_per_iter": 25, + "initial_var_params": [1.e-5]*5}) + sa_oo_vqe.build() + sa_oo_vqe.iterate() + + self.assertAlmostEqual(sa_oo_vqe.state_energies[0], -1.15137, places=4) + + def test_adapt_vqe_solver(self): + "Test that ADAPTVQE works with a user defined IntegralSolver." + molecule_dummy = SecondQuantizedMolecule(h2, 0, 0, IntegralSolverDummy(), basis="6-31g", frozen_orbitals=[]) + + adapt_vqe = ADAPTSolver({"molecule": molecule_dummy}) + adapt_vqe.build() + energy = adapt_vqe.simulate() + self.assertAlmostEqual(energy, -1.15168, places=4) + + +if __name__ == "__main__": + unittest.main() diff --git a/tangelo/toolboxes/molecular_computation/tests/test_psi4.py b/tangelo/toolboxes/molecular_computation/tests/test_psi4.py new file mode 100644 index 000000000..e6fecf810 --- /dev/null +++ b/tangelo/toolboxes/molecular_computation/tests/test_psi4.py @@ -0,0 +1,99 @@ +# Copyright 2023 Good Chemistry Company. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import unittest + +import numpy as np + +from tangelo import SecondQuantizedMolecule +from tangelo.helpers.utils import installed_chem_backends +from tangelo.problem_decomposition.oniom.oniom_problem_decomposition import ONIOMProblemDecomposition +from tangelo.problem_decomposition.oniom._helpers.helper_classes import Fragment +from tangelo.toolboxes.molecular_computation.integral_solver_psi4 import IntegralSolverPsi4 +from tangelo.algorithms.variational import SA_OO_Solver, BuiltInAnsatze, ADAPTSolver, iQCC_solver +from tangelo.molecule_library import xyz_H4, mol_H4_minao, xyz_H2, mol_H4_sto3g_uhf_a1_frozen + + +@unittest.skipIf("psi4" not in installed_chem_backends, "Test Skipped: Backend not available \n") +class Testpsi4(unittest.TestCase): + + def test_sa_oo_vqe(self): + "Test that sa_oo_vqe works properly when using a IntegralSolverPsi4" + molecule_dummy = SecondQuantizedMolecule(xyz_H2, 0, 0, IntegralSolverPsi4(), basis="6-31g", frozen_orbitals=[3]) + sa_oo_vqe = SA_OO_Solver({"molecule": molecule_dummy, "ref_states": [[1, 1, 0, 0, 0, 0]], + "tol": 1.e-5, "ansatz": BuiltInAnsatze.UCCSD, "n_oo_per_iter": 25, + "initial_var_params": [1.e-5]*5}) + sa_oo_vqe.build() + sa_oo_vqe.iterate() + + self.assertAlmostEqual(sa_oo_vqe.state_energies[0], -1.15137, places=4) + + def test_adapt_vqe_solver(self): + "Test that ADAPT-VQE works with IntegralSolverPsi4." + molecule_dummy = SecondQuantizedMolecule(xyz_H2, 0, 0, IntegralSolverPsi4(), basis="6-31g", frozen_orbitals=[]) + + adapt_vqe = ADAPTSolver({"molecule": molecule_dummy}) + adapt_vqe.build() + energy = adapt_vqe.simulate() + self.assertAlmostEqual(energy, -1.15168, places=4) + + def test_energy_hf_vqe_uccsd_h4(self): + """Test psi4 with HF and VQE (with UCCSD) in ONIOM.""" + + options_hf = {"basis": "sto-3g"} + options_vqe = {"basis": "sto-3g", "ansatz": BuiltInAnsatze.UCCSD, "initial_var_params": "ones"} + + # With this line, the interaction between H2-H2 is computed with a low + # accuracy method. + system = Fragment(solver_low="HF", options_low=options_hf) + # VQE-UCCSD fragments. + model_vqe_1 = Fragment(solver_low="HF", + options_low=options_hf, + solver_high="VQE", + options_high=options_vqe, + selected_atoms=[0, 1]) + model_vqe_2 = Fragment(solver_low="HF", + options_low=options_hf, + solver_high="VQE", + options_high=options_vqe, + selected_atoms=[2, 3]) + oniom_model_vqe = ONIOMProblemDecomposition({"geometry": xyz_H4, "fragments": [system, model_vqe_1, model_vqe_2]}) + + e_oniom_vqe = oniom_model_vqe.simulate() + + # ONIOM + CCSD is tested in test_oniom.ONIOMTest.test_energy_hf_ccsd_h4. + self.assertAlmostEqual(-1.901623, e_oniom_vqe, places=5) + self.assertEqual(mol_H4_minao, None) + + def test_iqcc_h4_uhf(self): + """Test the energy after 3 iterations for H4 uhf with 1 alpha orbital frozen and generators limited to 8""" + + ansatz_options = {"max_qcc_gens": 8} + + iqcc_options = {"molecule": mol_H4_sto3g_uhf_a1_frozen, + "qubit_mapping": "scbk", + "up_then_down": True, + "ansatz_options": ansatz_options, + "deqcc_thresh": 1e-5, + "max_iqcc_iter": 3} + + iqcc = iQCC_solver(iqcc_options) + iqcc.build() + iqcc_energy = iqcc.simulate() + + self.assertAlmostEqual(iqcc_energy, -1.95831, places=3) + + +if __name__ == "__main__": + unittest.main() diff --git a/tangelo/toolboxes/molecular_computation/tests/test_rdms.py b/tangelo/toolboxes/molecular_computation/tests/test_rdms.py index 6f4b0905b..46a77acb1 100644 --- a/tangelo/toolboxes/molecular_computation/tests/test_rdms.py +++ b/tangelo/toolboxes/molecular_computation/tests/test_rdms.py @@ -20,10 +20,12 @@ from numpy.testing import assert_allclose from openfermion.utils import load_operator +from tangelo import SecondQuantizedMolecule from tangelo.molecule_library import mol_H2O_sto3g from tangelo.toolboxes.measurements import RandomizedClassicalShadow from tangelo.toolboxes.operators import FermionOperator -from tangelo.toolboxes.molecular_computation.rdms import energy_from_rdms, compute_rdms, pad_rdms_with_frozen_orbitals +from tangelo.toolboxes.molecular_computation.rdms import energy_from_rdms, compute_rdms, \ + pad_rdms_with_frozen_orbitals_restricted, pad_rdms_with_frozen_orbitals_unrestricted from tangelo.linq.helpers import pauli_string_to_of, pauli_of_to_string from tangelo.toolboxes.post_processing import Histogram, aggregate_histograms @@ -131,15 +133,15 @@ def test_compute_rdms_from_classical_shadow(self): assert_allclose(rdm1ssr, rdm1ss, atol=0.05) assert_allclose(rdm2ssr, rdm2ss, atol=0.05) - def test_pad_rdms_with_frozen_orbitals(self): - """Test padding of RDMs with frozen orbitals indices.""" + def test_pad_restricted_rdms_with_frozen_orbitals(self): + """Test padding of RDMs with frozen orbitals indices (restricted).""" mol = mol_H2O_sto3g.freeze_mos([0, 3, 4, 5], inplace=False) onerdm_to_pad = np.loadtxt(pwd_this_test + "/data/H2O_sto3g_onerdm_frozen0345.data") twordm_to_pad = np.loadtxt(pwd_this_test + "/data/H2O_sto3g_twordm_frozen0345.data") - test_onerdm, test_twordm = pad_rdms_with_frozen_orbitals(mol, onerdm_to_pad, twordm_to_pad.reshape((3,)*4)) + test_onerdm, test_twordm = pad_rdms_with_frozen_orbitals_restricted(mol, onerdm_to_pad, twordm_to_pad.reshape((3,)*4)) padded_onerdm = np.loadtxt(pwd_this_test + "/data/H2O_sto3g_padded_onerdm_frozen0345.data") padded_twordm = np.loadtxt(pwd_this_test + "/data/H2O_sto3g_padded_twordm_frozen0345.data") @@ -147,6 +149,34 @@ def test_pad_rdms_with_frozen_orbitals(self): np.testing.assert_array_almost_equal(padded_onerdm, test_onerdm, decimal=3) np.testing.assert_array_almost_equal(padded_twordm.reshape((7,)*4), test_twordm, decimal=3) + def test_pad_unrestricted_rdms_with_frozen_orbitals(self): + """Test padding of RDMs with frozen orbitals indices (unrestricted).""" + + mol = SecondQuantizedMolecule(mol_H2O_sto3g.xyz, uhf=True, frozen_orbitals=[(0, 3, 4, 5), (0, 3, 4, 5)]) + + onerdm_a_to_pad = np.loadtxt(pwd_this_test + "/data/H2O_UHF_sto3g_onerdm_frozen0345_alpha.data") + onerdm_b_to_pad = np.loadtxt(pwd_this_test + "/data/H2O_UHF_sto3g_onerdm_frozen0345_beta.data") + onerdm_to_pad = (onerdm_a_to_pad, onerdm_b_to_pad) + + twordm_aa_to_pad = np.loadtxt(pwd_this_test + "/data/H2O_UHF_sto3g_twordm_frozen0345_alphaalpha.data") + twordm_ab_to_pad = np.loadtxt(pwd_this_test + "/data/H2O_UHF_sto3g_twordm_frozen0345_alphabeta.data") + twordm_bb_to_pad = np.loadtxt(pwd_this_test + "/data/H2O_UHF_sto3g_twordm_frozen0345_betabeta.data") + twordm_to_pad = (twordm_aa_to_pad.reshape((3,)*4), twordm_ab_to_pad.reshape((3,)*4), twordm_bb_to_pad.reshape((3,)*4)) + + test_onerdm, test_twordm = pad_rdms_with_frozen_orbitals_unrestricted(mol, onerdm_to_pad, twordm_to_pad) + + padded_onerdm_a = np.loadtxt(pwd_this_test + "/data/H2O_UHF_sto3g_padded_onerdm_frozen0345_alpha.data") + padded_onerdm_b = np.loadtxt(pwd_this_test + "/data/H2O_UHF_sto3g_padded_onerdm_frozen0345_beta.data") + padded_onerdm = (padded_onerdm_a, padded_onerdm_b) + + padded_twordm_aa = np.loadtxt(pwd_this_test + "/data/H2O_UHF_sto3g_padded_twordm_frozen0345_alphaalpha.data") + padded_twordm_ab = np.loadtxt(pwd_this_test + "/data/H2O_UHF_sto3g_padded_twordm_frozen0345_alphabeta.data") + padded_twordm_bb = np.loadtxt(pwd_this_test + "/data/H2O_UHF_sto3g_padded_twordm_frozen0345_betabeta.data") + padded_twordm = (padded_twordm_aa.reshape((7,)*4), padded_twordm_ab.reshape((7,)*4), padded_twordm_bb.reshape((7,)*4)) + + np.testing.assert_array_almost_equal(padded_onerdm, test_onerdm, decimal=3) + np.testing.assert_array_almost_equal(padded_twordm, test_twordm, decimal=3) + if __name__ == "__main__": unittest.main() diff --git a/tangelo/toolboxes/operators/operators.py b/tangelo/toolboxes/operators/operators.py index 58059af1a..7a592b144 100644 --- a/tangelo/toolboxes/operators/operators.py +++ b/tangelo/toolboxes/operators/operators.py @@ -25,6 +25,8 @@ from tangelo.toolboxes.molecular_computation.coefficients import spatial_from_spinorb +COEFFICIENT_TYPES = (int, float, complex, np.integer, np.floating) + class FermionOperator(of.FermionOperator): """Custom FermionOperator class. Based on openfermion's, with additional functionalities. @@ -75,6 +77,10 @@ def __iadd__(self, other): f_op.terms = other.terms.copy() return super(FermionOperator, self).__iadd__(f_op) + elif isinstance(other, COEFFICIENT_TYPES): + self.constant += other + return self + else: raise RuntimeError(f"You cannot add FermionOperator and {other.__class__}.") @@ -168,6 +174,20 @@ class QubitOperator(of.QubitOperator): replaced by our own implementation. """ + @classmethod + def from_openfermion(cls, of_qop): + """ Enable instantiation of a QubitOperator from an openfermion QubitOperator object. + + Args: + of_qop (openfermion QubitOperator): an existing qubit operator defined with Openfermion + + Returns: + corresponding QubitOperator object. + """ + qop = cls() + qop.terms = of_qop.terms.copy() + return qop + def frobenius_norm_compression(self, epsilon, n_qubits): """Reduces the number of operator terms based on its Frobenius norm and a user-defined threshold, epsilon. The eigenspectrum of the @@ -230,6 +250,12 @@ def qubit_indices(self): return qubit_indices + def to_openfermion(self): + """Converts Tangelo QubitOperator to openfermion""" + qu_op = of.QubitOperator() + qu_op.terms = self.terms.copy() + return qu_op + class QubitHamiltonian(QubitOperator): """QubitHamiltonian objects are essentially openfermion.QubitOperator @@ -243,8 +269,7 @@ class QubitHamiltonian(QubitOperator): coefficient (complex): Coefficient for this term. mapping (string): Mapping procedure for fermionic to qubit encoding (ex: "JW", "BK", etc.). - up_then_down (bool): Whether or not spin ordering is all up then - all down. + up_then_down (bool): Whether spin ordering is all up then all down. Properties: n_terms (int): Number of terms in this qubit Hamiltonian. diff --git a/tangelo/toolboxes/operators/tests/test_operators.py b/tangelo/toolboxes/operators/tests/test_operators.py index 1f65530b5..98bccd38a 100644 --- a/tangelo/toolboxes/operators/tests/test_operators.py +++ b/tangelo/toolboxes/operators/tests/test_operators.py @@ -107,9 +107,12 @@ def test_add(self): fop += FermionOperator("0^ 1", 3.) self.assertEqual(fop, fop_1) + # Test addition with coefficient + self.assertEqual(2. + fop_1, fop_2 + 2.) + # Test addition with non-compatible type with self.assertRaises(RuntimeError): - fop + 1 + fop + "a" def test_mul(self): # Test in-place multiplication diff --git a/tangelo/toolboxes/operators/tests/test_trim_trivial_qubits.py b/tangelo/toolboxes/operators/tests/test_trim_trivial_qubits.py new file mode 100644 index 000000000..6a5e4a6fc --- /dev/null +++ b/tangelo/toolboxes/operators/tests/test_trim_trivial_qubits.py @@ -0,0 +1,101 @@ +# Copyright 2023 Good Chemistry Company. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import os +import unittest + +import numpy as np +from openfermion.linalg import qubit_operator_sparse +from openfermion.utils import load_operator + +from tangelo.linq import Gate, Circuit, get_backend +from tangelo.toolboxes.operators import QubitOperator +from tangelo.toolboxes.ansatz_generator.ansatz_utils import exp_pauliword_to_gates +from tangelo.toolboxes.operators.trim_trivial_qubits import trim_trivial_qubits, trim_trivial_operator, trim_trivial_circuit, is_bitflip_gate + + +pwd_this_test = os.path.dirname(os.path.abspath(__file__)) + +qb_ham = load_operator("H4_JW_spinupfirst.data", data_directory=pwd_this_test+"/data", plain_text=True) + +# Generate reference and test circuits using single qcc generator +ref_qcc_op = 0.2299941483397896 * 0.5 * QubitOperator("Y0 X1 X2 X3") +qcc_op = 0.2299941483397896 * 0.5 * QubitOperator("Y1 X3 X5 X7") + +ref_mf_gates = [Gate("RX", 0, parameter=np.pi), Gate("X", 2)] + +mf_gates = [ + Gate("RZ", 0, parameter=np.pi/2), Gate("RX", 0, parameter=3.14159), + Gate("RX", 1, parameter=np.pi), Gate("RZ", 2, parameter=np.pi), + Gate("X", 4), Gate("X", 5), Gate("Z", 6), + Gate("RZ", 6, parameter=np.pi), Gate("RX", 8, parameter=-3*np.pi), + Gate("X", 8), Gate("RZ", 9, parameter=np.pi), Gate("Z", 9) + ] + +ref_pauli_words_gates = sum((exp_pauliword_to_gates(pword, coef) for pword, coef in ref_qcc_op.terms.items()), start=[]) +pauli_words_gates = sum((exp_pauliword_to_gates(pword, coef) for pword, coef in qcc_op.terms.items()), start=[]) + +ref_circ = Circuit(ref_mf_gates + ref_pauli_words_gates) +circ = Circuit(mf_gates + pauli_words_gates) + +# Reference energy for H4 molecule with single QCC generator +ref_value = -1.8039875664891176 + +# Reference indices and states to be removed from system +ref_trim_states = {0: 1, 2: 0, 4: 1, 6: 0, 8: 0, 9: 0} + +# Reference for which mf_gates are bitflip gates +ref_bool = [False, True, True, False, True, True, False, False, True, True, False, False] + +sim = get_backend() + + +class TrimTrivialQubits(unittest.TestCase): + def test_trim_trivial_operator(self): + """ Test if trimming operator returns the correct eigenvalue """ + + trimmed_operator = trim_trivial_operator(qb_ham, trim_states={key: ref_trim_states[key] for key in [0, 2, 4, 6]}, reindex=False) + self.assertAlmostEqual(np.min(np.linalg.eigvalsh(qubit_operator_sparse(trimmed_operator).todense())), ref_value, places=5) + + def test_is_bitflip_gate(self): + """ Test if bitflip gate function correctly identifies bitflip gates """ + self.assertEqual(ref_bool, [is_bitflip_gate(g) for g in mf_gates]) + + def test_trim_trivial_circuit(self): + """ Test if circuit trimming returns the correct circuit, states, and indices """ + + trimmed_circuit, trim_states = trim_trivial_circuit(circ) + self.assertEqual(ref_circ._gates, trimmed_circuit._gates) + self.assertEqual(ref_trim_states, trim_states) + + def test_trim_trivial_qubits(self): + """ Test if trim trivial qubit function produces correct and compatible circuits and operators """ + + trimmed_operator, trimmed_circuit = trim_trivial_qubits(qb_ham, circ) + self.assertAlmostEqual(np.min(np.linalg.eigvalsh(qubit_operator_sparse(trimmed_operator).todense())), ref_value, places=5) + self.assertEqual(ref_circ._gates, trimmed_circuit._gates) + self.assertAlmostEqual(sim.get_expectation_value(trimmed_operator, trimmed_circuit), ref_value, places=5) + + def test_trim_trivial_qubits(self): + """ Test if trim trivial qubit function produces correct and compatible circuits and operators """ + + circ = Circuit(mf_gates + pauli_words_gates, n_qubits=12) + trimmed_operator, trimmed_circuit = trim_trivial_qubits(qb_ham+QubitOperator("Z10"), circ) + self.assertAlmostEqual(np.min(np.linalg.eigvalsh(qubit_operator_sparse(trimmed_operator).todense())), ref_value+1, places=5) + self.assertEqual(ref_circ._gates, trimmed_circuit._gates) + self.assertAlmostEqual(sim.get_expectation_value(trimmed_operator, trimmed_circuit), ref_value+1, places=5) + + +if __name__ == "__main__": + unittest.main() diff --git a/tangelo/toolboxes/operators/trim_trivial_qubits.py b/tangelo/toolboxes/operators/trim_trivial_qubits.py new file mode 100644 index 000000000..9d572237d --- /dev/null +++ b/tangelo/toolboxes/operators/trim_trivial_qubits.py @@ -0,0 +1,177 @@ +# Copyright 2023 Good Chemistry Company. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import numpy as np + +from tangelo.toolboxes.operators import QubitOperator, count_qubits +from tangelo.linq import Circuit +from tangelo.linq.helpers.circuits import pauli_string_to_of, pauli_of_to_string + + +def trim_trivial_operator(qu_op, trim_states, n_qubits=None, reindex=True): + """ + Calculate expectation values of a QubitOperator acting on qubits in a + trivial |0> or |1> state. Return a trimmed QubitOperator with updated coefficients + + Args: + qu_op (QubitOperator): Operator to trim + trim_states (dict): Dictionary mapping qubit indices to states to trim, e.g. {1: 0, 3: 1} + n_qubits (int): Optional, number of qubits in full system + reindex (bool): Optional, if True, remaining qubits will be reindexed + + Returns: + QubitOperator : trimmed QubitOperator with updated coefficients + """ + + qu_op_trim = QubitOperator() + n_qubits = count_qubits(qu_op) if n_qubits is None else n_qubits + + # Calculate expectation values of trivial qubits, update coefficients + for op, coeff in qu_op.terms.items(): + term = pauli_of_to_string(op, n_qubits) + c = np.ones(len(trim_states)) + new_term = term + for i, qubit in enumerate(trim_states.keys()): + if term[qubit] in {'X', 'Y'}: + c[i] = 0 + break + elif (term[qubit], trim_states[qubit]) == ('Z', 1): + c[i] = -1 + + new_term = new_term[:qubit - i] + new_term[qubit - i + 1:] if reindex else new_term[:qubit] + 'I' + new_term[qubit + 1:] + + if 0 in c: + continue + + qu_op_trim += np.prod(c) * coeff * QubitOperator(pauli_string_to_of(new_term)) + return qu_op_trim + + +def is_bitflip_gate(gate, atol=1e-5): + """ + Check if a gate is a bitflip gate. + + A gate is a bitflip gate if it satisfies one of the following conditions: + 1. The gate name is either X, Y. + 2. The gate name is RX or RY, and has a parameter that is an odd multiple of pi. + + Args: + gate (Gate): The gate to check. + atol (float): Optional, the absolute tolerance for gate parameter + + Returns: + bool: True if the gate is a single qubit bitflip gate, False otherwise. + """ + if gate is None: + return False + + if gate.name in {"X", "Y"}: + return True + elif gate.name in {"RX", "RY"}: + try: + parameter_float = float(gate.parameter) + except (TypeError, ValueError): + return False + + # Check if parameter is close to an odd multiple of pi + return abs(parameter_float % (np.pi * 2) - np.pi) <= atol + else: + return False + + +def trim_trivial_circuit(circuit): + """ + Split Circuit into entangled and unentangled components. + Returns entangled Circuit, and the indices and states of unentangled qubits + + Args: + circuit (Circuit): circuit to be trimmed + + Returns: + Circuit : Trimmed, entangled circuit + dict : dictionary mapping trimmed qubit indices to their states (0 or 1) + """ + # Split circuit and get relevant indices + circs = circuit.split() + e_indices = circuit.get_entangled_indices() + used_qubits = set() + for eq in e_indices: + used_qubits.update(eq) + + # Find qubits with no gates applied to them, store qubit index and state |0> + trim_states = {} + for qubit_idx in set(range(circuit.width)) - used_qubits: + trim_states[qubit_idx] = 0 + + circuit_new = Circuit() + # Go through circuit components, trim if trivial, otherwise append to new circuit + for i, circ in enumerate(circs): + if circ.width != 1 or circ.size not in (1, 2): + circuit_new += circ + continue + + # Calculate state of single qubit clifford circuits, ideally this would be done with a clifford simulator + # for now only look at first two gate combinations typical of the QMF state in QCC methods + gate0, gate1 = circ._gates[:2] + [None] * (2 - circ.size) + gate_0_is_bitflip = is_bitflip_gate(gate0) + gate_1_is_bitflip = is_bitflip_gate(gate1) + + if circ.size == 1: + if gate0.name in {"RZ", "Z"}: + qubit_idx = e_indices[i].pop() + trim_states[qubit_idx] = 0 + elif gate0.name in {"X", "RX"} and gate_0_is_bitflip: + qubit_idx = e_indices[i].pop() + trim_states[qubit_idx] = 1 + else: + circuit_new += circ + elif circ.size == 2: + if gate1.name in {"Z", "RZ"}: + if gate0.name in {"RZ", "Z"}: + qubit_idx = e_indices[i].pop() + trim_states[qubit_idx] = 0 + else: + circuit_new += circ + elif gate1.name in {"X", "RX"} and gate_1_is_bitflip: + if gate0.name in {"RX", "X"} and gate_0_is_bitflip: + qubit_idx = e_indices[i].pop() + trim_states[qubit_idx] = 0 + elif gate0.name in {"Z", "RZ"}: + qubit_idx = e_indices[i].pop() + trim_states[qubit_idx] = 1 + else: + circuit_new += circ + else: + circuit_new += circ + + return circuit_new, dict(sorted(trim_states.items())) + + +def trim_trivial_qubits(operator, circuit): + """ + Trim circuit and operator based on expectation values calculated from + trivial components of the circuit. + + Args: + operator (QubitOperator): Operator to trim + circuit (Circuit): circuit to be trimmed + + Returns: + QubitOperator : Trimmed qubit operator + Circuit : Trimmed circuit + """ + trimmed_circuit, trim_states = trim_trivial_circuit(circuit) + trimmed_operator = trim_trivial_operator(operator, trim_states, circuit.width, reindex=True) + + return trimmed_operator, trimmed_circuit diff --git a/tangelo/toolboxes/post_processing/post_selection.py b/tangelo/toolboxes/post_processing/post_selection.py index 4aabf7cc7..614f11d14 100644 --- a/tangelo/toolboxes/post_processing/post_selection.py +++ b/tangelo/toolboxes/post_processing/post_selection.py @@ -35,7 +35,9 @@ def ancilla_symmetry_circuit(circuit, sym_op): Returns: Circuit: The input circuit appended with the proper basis rotation and entanglement with an ancilla qubit, which is added as the last qubit. - This appends an additional qubit to the circuit.""" + This appends an additional qubit to the circuit. + """ + if isinstance(sym_op, QubitOperator): op_len = count_qubits(sym_op) else: @@ -81,6 +83,7 @@ def post_select(freqs, expected_outcomes): dict: A dictionary of post-selected, renormalized frequencies and bitstrings with removed ancilla qubits """ + hist = Histogram(freqs, n_shots=0) hist.post_select(expected_outcomes) return hist.frequencies @@ -95,7 +98,9 @@ def strip_post_selection(freqs, *qubits): qubits (variable number of int): The ancilla qubit indices to be removed from the bitstrings Returns: - dict: A frequency dictionary with the qubits stripped and data aggregated""" + dict: A frequency dictionary with the qubits stripped and data aggregated + """ + hist = Histogram(freqs, n_shots=0) hist.remove_qubit_indices(*qubits) return hist.frequencies @@ -115,7 +120,9 @@ def split_frequency_dict(frequencies, indices, desired_measurement=None): Returns: dict: The marginal frequencies for provided indices - dict: The marginal frequencies for remaining indices""" + dict: The marginal frequencies for remaining indices + """ + key_length = len(next(iter(frequencies))) other_indices = [i for i in range(key_length) if i not in indices] diff --git a/tangelo/toolboxes/qubit_mappings/__init__.py b/tangelo/toolboxes/qubit_mappings/__init__.py index 72f4c493b..f589caed6 100644 --- a/tangelo/toolboxes/qubit_mappings/__init__.py +++ b/tangelo/toolboxes/qubit_mappings/__init__.py @@ -16,3 +16,4 @@ from .bravyi_kitaev import bravyi_kitaev from .symmetry_conserving_bravyi_kitaev import symmetry_conserving_bravyi_kitaev from .jkmn import jkmn +from .combinatorial import combinatorial diff --git a/tangelo/toolboxes/qubit_mappings/combinatorial.py b/tangelo/toolboxes/qubit_mappings/combinatorial.py new file mode 100644 index 000000000..78c35cabf --- /dev/null +++ b/tangelo/toolboxes/qubit_mappings/combinatorial.py @@ -0,0 +1,233 @@ +# Copyright 2023 Good Chemistry Company. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""Combinatorial mapping as described in references (1) and (2). In contrast to +qubit mappings such as JW, BK that use occupation/parity, the mappings in this +file use the Fock configurations as the elements of the basis set. Thus, the +number of required qubits scales with the number of electronic configuration +instead of the number of spinorbitals. + +References: + 1. Streltsov, A. I., Alon, O. E. & Cederbaum, L. S. General mapping for + bosonic and fermionic operators in Fock space. Phys. Rev. A 81, 022124 + (2010). + 2. Chamaki, D., Metcalf, M. & de Jong, W. A. Compact Molecular Simulation on + Quantum Computers via Combinatorial Mapping and Variational State + Preparation. Preprint at https://doi.org/10.48550/arXiv.2205.11742 + (2022). +""" + +import itertools +from collections import OrderedDict +from math import ceil +from copy import deepcopy + +import numpy as np +from scipy.special import comb +from openfermion.transforms import chemist_ordered + +from tangelo.toolboxes.operators import QubitOperator + + +def combinatorial(ferm_op, n_modes, n_electrons): + """Function to transform the fermionic Hamiltonian into a basis constructed + in the Fock space. + + Args: + ferm_op (FermionOperator). Fermionic operator, with alternate ordering + as followed in the openfermion package + n_modes (int): Number of relevant molecular orbitals, i.e. active molecular + orbitals. + n_electrons (int): Number of active electrons. + + Returns: + QubitOperator: Self-explanatory. + """ + + # The chemist ordering splits some 1-body and 2-body terms. + ferm_op_chemist = chemist_ordered(ferm_op) + + # Specify the number of alpha and beta electrons. + if isinstance(n_electrons, tuple) and len(n_electrons) == 2: + n_alpha, n_beta = n_electrons + elif isinstance(n_electrons, int) and n_electrons % 2 == 0: + n_alpha = n_beta = n_electrons // 2 + else: + raise ValueError(f"{n_electrons} is not a valid entry for n_electrons, must be a tuple or an int.") + + # Get the number of qubits n. + n_choose_alpha = comb(n_modes, n_alpha, exact=True) + n = ceil(np.log2(n_choose_alpha * comb(n_modes, n_beta, exact=True))) + + # Construct the basis set where each configuration is mapped to a unique integer. + basis_set_alpha = basis(n_modes, n_alpha) + basis_set_beta = basis(n_modes, n_beta) + basis_set = OrderedDict() + for sigma_alpha, int_alpha in basis_set_alpha.items(): + for sigma_beta, int_beta in basis_set_beta.items(): + # Alternate ordering (like FermionOperator in openfermion). + sigma = tuple(sorted([2*sa for sa in sigma_alpha] + [2*sb+1 for sb in sigma_beta])) + unique_int = (int_alpha * n_choose_alpha) + int_beta + basis_set[sigma] = unique_int + + qu_op = QubitOperator() + # Check what is the effect of every term. + for term, coeff in ferm_op_chemist.terms.items(): + # Core term. + if not term: + qu_op += coeff + continue + + # Get the effect of each operator to the basis set items. + for b, unique_int in basis_set.items(): + + new_state, phase = one_body_op_on_state(term[-2:], b) + + if len(term) == 4 and new_state: + new_state, phase_two = one_body_op_on_state(term[:2], new_state) + phase *= phase_two + + if not new_state: + continue + + new_unique_int = basis_set[new_state] + qu_op += element_to_qubitop(n, unique_int, new_unique_int, phase*coeff) + + return qu_op + + +def element_to_qubitop(n_qubits, i, j, coeff=1.): + """Map a matrix element to a qubit operator. + + Args: + n_qubits (int): The number of qubits that the whole matrix represents. + i (int): i row of the matrix element. + j (int): j column of the matrix element. + coeff (complex): Value at position i,j in the matrix. + + Returns: + QubitOperator: Self-explanatory. + """ + + # Must add 2 to the padding because of the "0b" prefix. + bin_i = format(i, f"#0{n_qubits+2}b") + bin_j = format(j, f"#0{n_qubits+2}b") + + qu_op = QubitOperator("", coeff) + for qubit, (bi, bj) in enumerate(zip(bin_i[2:][::-1], bin_j[2:][::-1])): + if (bi, bj) == ("0", "0"): + qu_op *= 0.5 + QubitOperator(f"Z{qubit}", 0.5) + elif (bi, bj) == ("0", "1"): + qu_op *= QubitOperator(f"X{qubit}", 0.5) + QubitOperator(f"Y{qubit}", 0.5j) + elif (bi, bj) == ("1", "0"): + qu_op *= QubitOperator(f"X{qubit}", 0.5) + QubitOperator(f"Y{qubit}", -0.5j) + # The remaining case is 11. + else: + qu_op *= 0.5 + QubitOperator(f"Z{qubit}", -0.5) + + return qu_op + + +def basis(M, N): + """Function to construct the combinatorial basis set, i.e. a basis set + respecting the number of electrons and the total spin. + + Args: + M (int): Number of spatial orbitals. + N (int): Number of alpha or beta electrons. + + Returns: + OrderedDict: Lexicographically sorted basis set, mapping electronic + configuration to unique integers. + """ + + mapping = [(sigma, conf_to_integer(sigma, M)) for sigma in itertools.combinations(range(M), N)] + return OrderedDict(mapping) + + +def conf_to_integer(sigma, M): + """Function to map an electronic configuration to a unique integer, as done + in arXiv.2205.11742 eq. (14). + + Args: + sigma (tuple of int): Orbital indices where the electron are in the + electronic configuration. + M (int): Number of modes, i.e. number of spatial orbitals. + + Returns: + int: Unique integer for the input electronic state. + """ + + # Equivalent to the number of electrons. + N = len(sigma) + + # Eq. (14) in the reference. + terms_k = [comb(M - sigma[N - 1 - k] - 1, k + 1) for k in range(N)] + unique_int = comb(M, N) - 1 - np.sum(terms_k) + + return int(unique_int) + + +def one_body_op_on_state(op, state_in): + """Function to apply a a^{\dagger}_i a_j operator as described in Phys. Rev. + A 81, 022124 (2010) eq. (8). + + Args: + op (tuple): Operator, written as ((qubit_i, 1), (qubit_j, 0)), where 0/1 + means annihilation/creation on the specified qubit. + state_in (tuple): Electronic configuration described as tuple of + spinorbital indices where there is an electron. + + Returns: + tuple: Resulting state with the same form as in the input state. + Can be 0. + int: Phase shift. Can be -1 or 1. + """ + + assert len(op) == 2, f"Operator {op} has length {len(op)}, but a length of 2 is expected." + + # Copy the state, then transform it into a list (it will be mutated). + state = deepcopy(state_in) + state = list(state) + + # Unpack the creation and annihilation operators. + creation_op, annihilation_op = op + creation_qubit, creation_dagger = creation_op + annihilation_qubit, annihilation_dagger = annihilation_op + + # Confirm dagger operator to the left. + assert creation_dagger == 1, f"The left operator in {op} is not a creation operator." + assert annihilation_dagger == 0, f"The right operator in {op} is not an annihilation operator." + + # annihilation logics on the state. + if annihilation_qubit in state: + state.remove(annihilation_qubit) + else: + return (), 0 + + # Creation logics on the state. + if creation_qubit not in state: + state = [*state, creation_qubit] + else: + return (), 0 + + # Compute the phase shift. + if annihilation_qubit > creation_qubit: + d = sum(creation_qubit < i < annihilation_qubit for i in state) + elif annihilation_qubit < creation_qubit: + d = sum(annihilation_qubit < i < creation_qubit for i in state) + else: + d = 0 + + return tuple(sorted(state)), (-1)**d diff --git a/tangelo/toolboxes/qubit_mappings/statevector_mapping.py b/tangelo/toolboxes/qubit_mappings/statevector_mapping.py index f8f6adea1..2f74d5025 100644 --- a/tangelo/toolboxes/qubit_mappings/statevector_mapping.py +++ b/tangelo/toolboxes/qubit_mappings/statevector_mapping.py @@ -20,7 +20,7 @@ import warnings import numpy as np -from openfermion import QubitOperator +from openfermion.ops.operators import QubitOperator from openfermion.transforms import bravyi_kitaev_code from openfermion.transforms.opconversions.bravyi_kitaev_tree import _transform_ladder_operator, FenwickTree diff --git a/tangelo/toolboxes/qubit_mappings/tests/test_combinatorial.py b/tangelo/toolboxes/qubit_mappings/tests/test_combinatorial.py new file mode 100644 index 000000000..2356db85e --- /dev/null +++ b/tangelo/toolboxes/qubit_mappings/tests/test_combinatorial.py @@ -0,0 +1,120 @@ +# Copyright 2023 Good Chemistry Company. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import unittest + +import numpy as np + +from tangelo.molecule_library import mol_H2_sto3g +from tangelo.toolboxes.operators import QubitOperator +from tangelo.toolboxes.qubit_mappings import combinatorial +from tangelo.toolboxes.qubit_mappings.combinatorial import element_to_qubitop, basis, \ + conf_to_integer, one_body_op_on_state + + +class FunctionsCombinatorialTest(unittest.TestCase): + + def test_element_to_qubitop_2by2(self): + """Test the mapping of a 2x2 matrix to qubit operators.""" + + array = np.array([ + [1, 2.22], + [3j, 4+1j] + ]) + + quop_00 = element_to_qubitop(1, 0, 0, array[0][0]) + quop_01 = element_to_qubitop(1, 0, 1, array[0][1]) + quop_10 = element_to_qubitop(1, 1, 0, array[1][0]) + quop_11 = element_to_qubitop(1, 1, 1, array[1][1]) + + self.assertEqual(quop_00, QubitOperator("", 0.5) + QubitOperator("Z0", 0.5)) + self.assertEqual(quop_01, QubitOperator("X0", 0.5*2.22) + QubitOperator("Y0", 0.5j*2.22)) + self.assertEqual(quop_10, QubitOperator("X0", 0.5*3j) + QubitOperator("Y0", -0.5j*3j)) + self.assertEqual(quop_11, QubitOperator("", 0.5*(4+1j)) + QubitOperator("Z0", -0.5*(4+1j))) + + def test_element_to_qubitop_4by4(self): + """Test the mapping of a 4x4 matrix element to a qubit operator.""" + + quop_01_00 = element_to_qubitop(2, 1, 0, 5.) + + ref_op = QubitOperator("X0", 0.25) + QubitOperator("Y0", -0.25j) + \ + QubitOperator("X0 Z1", 0.25) + QubitOperator("Y0 Z1", -0.25j) + ref_op *= 5. + + self.assertEqual(quop_01_00, ref_op) + + def test_basis(self): + """Test the basis function to construct a combinatorial set.""" + + bs = basis(4, 1) + + self.assertEqual(bs.keys(), {(0,), (1,), (2,), (3,)}) + self.assertEqual(list(bs.values()), [0, 1, 2, 3]) + + def test_conf_to_integer(self): + """Test the mapping of an electronic configuration to a unique int.""" + + self.assertEqual(conf_to_integer((0, 1), 4), 0) + self.assertEqual(conf_to_integer((0, 2), 4), 1) + self.assertEqual(conf_to_integer((0, 3), 4), 2) + self.assertEqual(conf_to_integer((1, 2), 4), 3) + self.assertEqual(conf_to_integer((1, 3), 4), 4) + self.assertEqual(conf_to_integer((2, 3), 4), 5) + + def test_one_body_op_on_state(self): + """Test the function that applies an operator a^{\dagger}_j a_i.""" + + conf_in = (0, 1, 2) + + # Test case where i is in conf_in and not j (phase unchanged). + conf_out_a, phase_a = one_body_op_on_state(((3, 1), (0, 0)), conf_in) + self.assertEqual(conf_out_a, (1, 2, 3)) + self.assertEqual(phase_a, 1) + + # Test case where i is in conf_in and not j (phase changed). + conf_out_b, phase_b = one_body_op_on_state(((3, 1), (1, 0)), conf_in) + self.assertEqual(conf_out_b, (0, 2, 3)) + self.assertEqual(phase_b, -1) + + # Test case where i is not in conf_in. + conf_out_c, phase_c = one_body_op_on_state(((4, 1), (3, 0)), conf_in) + self.assertEqual(conf_out_c, ()) + self.assertEqual(phase_c, 0) + + # Test case where j is in conf_in. + conf_out_d, phase_d = one_body_op_on_state(((2, 1), (1, 0)), conf_in) + self.assertEqual(conf_out_d, ()) + self.assertEqual(phase_d, 0) + + +class CombinatorialTest(unittest.TestCase): + + def test_combinatorial_h2_sto3g(self): + """Test the mapping of H2 STO-3G to a combinatorial (qubit) Hamiltonian.""" + + H_ferm = mol_H2_sto3g.fermionic_hamiltonian + qubit_op = combinatorial(H_ferm, mol_H2_sto3g.n_active_mos, + mol_H2_sto3g.n_active_electrons) + + ref_qubit_op = QubitOperator("", -0.3399536) + ref_qubit_op += QubitOperator("Y0 Y1", -0.181288) + ref_qubit_op += QubitOperator("Z0", -0.3939836) + ref_qubit_op += QubitOperator("Z0 Z1", 0.0112365) + ref_qubit_op += QubitOperator("Z1", -0.3939836) + + self.assertTrue(qubit_op.isclose(ref_qubit_op, tol=1e-4)) + + +if __name__ == "__main__": + unittest.main() diff --git a/tangelo/toolboxes/qubit_mappings/tests/test_statevector_mapping.py b/tangelo/toolboxes/qubit_mappings/tests/test_statevector_mapping.py index 2318fbdb1..aba15bf23 100644 --- a/tangelo/toolboxes/qubit_mappings/tests/test_statevector_mapping.py +++ b/tangelo/toolboxes/qubit_mappings/tests/test_statevector_mapping.py @@ -81,7 +81,7 @@ def test_all_same_energy_mol_H4_sto3g_and_mol_H2O_sto3g(self): """Check that all mappings return statevectors that have the same energy expectation for an even number of electrons and various spins. Molecules tested are H4, and H2O with frozen_orbitals=[0, 7] which failed previously for scbk""" - mols = [mol_H4_sto3g, SecondQuantizedMolecule(xyz_H2O, 0, 0, "sto-3g", frozen_orbitals=[0, 7])] + mols = [mol_H4_sto3g, SecondQuantizedMolecule(xyz_H2O, 0, 0, basis="sto-3g", frozen_orbitals=[0, 7])] circuits = dict() qu_ops = dict() for mol in mols: