{ "cells": [ { "cell_type": "markdown", "id": "4c39f12b", "metadata": {}, "source": [ "# Chemistry" ] }, { "cell_type": "markdown", "id": "1de33dce", "metadata": {}, "source": [ "In chemistry applications, the electronic Hamiltonian written as a linear combination of Pauli strings is the principal object.\n", "\n", "This a setting in which the `TermSum` types in Zixy are very useful.\n", "\n", "The `TermSum` is instantiable from a sparse string representation of the Pauli strings and coefficients." ] }, { "cell_type": "code", "execution_count": 1, "id": "0fc5451e", "metadata": {}, "outputs": [], "source": [ "import numpy as np\n", "from zixy.qubit.pauli import RealTermSum as RealPauliOperator\n", "\n", "H2_STO3G_HAM_JW_INPUT = \"\\\n", "(-0.05962058276034765, ), (0.1757594291831968, Z0), (0.17575942918319679, Z1), (0.17001546439603182, Z0 Z1), \\\n", "(0.044917169890753894, X0 Y1 Y2 X3), (-0.044917169890753894, X0 X1 Y2 Y3), (-0.044917169890753894, Y0 Y1 X2 X3), \\\n", "(0.044917169890753894, Y0 X1 X2 Y3), (-0.23667117678035537, Z2), (0.12222714936261826, Z0 Z2), (0.16714431925337217, Z1 Z2), \\\n", "(-0.23667117678035543, Z3), (0.16714431925337217, Z0 Z3), (0.12222714936261826, Z1 Z3), (0.1757033833190701, Z2 Z3) \\\n", "\"\n", "H2_STO3G_HF_ENERGY = -1.1175058842043306\n", "H2_STO3G_FCI_ENERGY = -1.136846575472054\n", "ham_op = RealPauliOperator.from_str(H2_STO3G_HAM_JW_INPUT)" ] }, { "cell_type": "markdown", "id": "a6c2fd64-29d8-42d7-97e1-d7b1901cce96", "metadata": {}, "source": [ "Alternatively we can use fermionic encodings to construct the qubit Pauli operators" ] }, { "cell_type": "code", "execution_count": 2, "id": "24c94dd4-b83b-4e2e-b6e0-571ac7eda1c6", "metadata": {}, "outputs": [], "source": [ "from zixy.fermion.mappings import JordanWignerMapper\n", "jw = JordanWignerMapper(4, mode_ordering=None)" ] }, { "cell_type": "code", "execution_count": 3, "id": "07250957-1eb3-43db-b212-5b399b3f6a7a", "metadata": {}, "outputs": [], "source": [ "tmp = RealPauliOperator(4)\n", "tmp += jw.encode_ccaa(2, 3, 0, 1) * 8\n", "tmp += jw.encode_caca(0, 1, 2, 3) * 1.2\n", "tmp -= jw.encode_ca(2, 1) * 2.3\n", "tmp -= jw.encode_n(2) * 2\n", "tmp += jw.encode_nn(2, 3) * 2\n", "tmp\n", "assert tmp.conserves_hamming_weight()" ] }, { "cell_type": "markdown", "id": "c8df5ed2", "metadata": {}, "source": [ "The Rust implementation constructs temporary number operators to detect symmetries via commutation:" ] }, { "cell_type": "code", "execution_count": 4, "id": "c05b126b", "metadata": {}, "outputs": [ { "data": { "text/plain": [ "(True, True)" ] }, "execution_count": 4, "metadata": {}, "output_type": "execute_result" } ], "source": [ "ham_op.conserves_hamming_weight(), ham_op.conserves_odd_bit_hamming_weight()" ] }, { "cell_type": "markdown", "id": "87476748", "metadata": {}, "source": [ "It's convenient to be able to view the terms in a pandas DataFrame" ] }, { "cell_type": "code", "execution_count": 5, "id": "84400445", "metadata": {}, "outputs": [ { "data": { "text/html": [ "
\n", "\n", "\n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "
ComponentCoefficient
0-0.05962058276034765
1Z00.1757594291831968
2Z10.17575942918319679
3Z0 Z10.17001546439603182
4X0 Y1 Y2 X30.044917169890753894
5X0 X1 Y2 Y3-0.044917169890753894
6Y0 Y1 X2 X3-0.044917169890753894
7Y0 X1 X2 Y30.044917169890753894
8Z2-0.23667117678035537
9Z0 Z20.12222714936261826
10Z1 Z20.16714431925337217
11Z3-0.23667117678035543
12Z0 Z30.16714431925337217
13Z1 Z30.12222714936261826
14Z2 Z30.1757033833190701
\n", "
" ], "text/plain": [ " Component Coefficient\n", "0 -0.05962058276034765\n", "1 Z0 0.1757594291831968\n", "2 Z1 0.17575942918319679\n", "3 Z0 Z1 0.17001546439603182\n", "4 X0 Y1 Y2 X3 0.044917169890753894\n", "5 X0 X1 Y2 Y3 -0.044917169890753894\n", "6 Y0 Y1 X2 X3 -0.044917169890753894\n", "7 Y0 X1 X2 Y3 0.044917169890753894\n", "8 Z2 -0.23667117678035537\n", "9 Z0 Z2 0.12222714936261826\n", "10 Z1 Z2 0.16714431925337217\n", "11 Z3 -0.23667117678035543\n", "12 Z0 Z3 0.16714431925337217\n", "13 Z1 Z3 0.12222714936261826\n", "14 Z2 Z3 0.1757033833190701" ] }, "execution_count": 5, "metadata": {}, "output_type": "execute_result" } ], "source": [ "ham_op.to_dataframe()" ] }, { "cell_type": "markdown", "id": "0f902cc7", "metadata": {}, "source": [ "The terms of a list can be sorted in various ways. Either by Pauli string (integer value of the X part, then that of the Z part):" ] }, { "cell_type": "code", "execution_count": 6, "id": "bee39af9", "metadata": {}, "outputs": [ { "data": { "text/html": [ "
\n", "\n", "\n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "
ComponentCoefficient
0X0 X1 Y2 Y3-0.044917169890753894
1Y0 X1 X2 Y30.044917169890753894
2X0 Y1 Y2 X30.044917169890753894
3Y0 Y1 X2 X3-0.044917169890753894
4Z2 Z30.1757033833190701
5Z1 Z30.12222714936261826
6Z0 Z30.16714431925337217
7Z3-0.23667117678035543
8Z1 Z20.16714431925337217
9Z0 Z20.12222714936261826
10Z2-0.23667117678035537
11Z0 Z10.17001546439603182
12Z10.17575942918319679
13Z00.1757594291831968
14-0.05962058276034765
\n", "
" ], "text/plain": [ " Component Coefficient\n", "0 X0 X1 Y2 Y3 -0.044917169890753894\n", "1 Y0 X1 X2 Y3 0.044917169890753894\n", "2 X0 Y1 Y2 X3 0.044917169890753894\n", "3 Y0 Y1 X2 X3 -0.044917169890753894\n", "4 Z2 Z3 0.1757033833190701\n", "5 Z1 Z3 0.12222714936261826\n", "6 Z0 Z3 0.16714431925337217\n", "7 Z3 -0.23667117678035543\n", "8 Z1 Z2 0.16714431925337217\n", "9 Z0 Z2 0.12222714936261826\n", "10 Z2 -0.23667117678035537\n", "11 Z0 Z1 0.17001546439603182\n", "12 Z1 0.17575942918319679\n", "13 Z0 0.1757594291831968\n", "14 -0.05962058276034765" ] }, "execution_count": 6, "metadata": {}, "output_type": "execute_result" } ], "source": [ "terms = ham_op.to_terms()\n", "terms.lexicographic_sort(ascending=False)\n", "terms.to_dataframe()" ] }, { "cell_type": "markdown", "id": "ca599f94", "metadata": {}, "source": [ "Or numerically:" ] }, { "cell_type": "code", "execution_count": 7, "id": "abf5a692", "metadata": {}, "outputs": [ { "data": { "text/html": [ "
\n", "\n", "\n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "
ComponentCoefficient
0Z3-0.23667117678035543
1Z2-0.23667117678035537
2Z00.1757594291831968
3Z10.17575942918319679
4Z2 Z30.1757033833190701
5Z0 Z10.17001546439603182
6Z1 Z20.16714431925337217
7Z0 Z30.16714431925337217
8Z0 Z20.12222714936261826
9Z1 Z30.12222714936261826
10-0.05962058276034765
11Y0 Y1 X2 X3-0.044917169890753894
12X0 Y1 Y2 X30.044917169890753894
13Y0 X1 X2 Y30.044917169890753894
14X0 X1 Y2 Y3-0.044917169890753894
\n", "
" ], "text/plain": [ " Component Coefficient\n", "0 Z3 -0.23667117678035543\n", "1 Z2 -0.23667117678035537\n", "2 Z0 0.1757594291831968\n", "3 Z1 0.17575942918319679\n", "4 Z2 Z3 0.1757033833190701\n", "5 Z0 Z1 0.17001546439603182\n", "6 Z1 Z2 0.16714431925337217\n", "7 Z0 Z3 0.16714431925337217\n", "8 Z0 Z2 0.12222714936261826\n", "9 Z1 Z3 0.12222714936261826\n", "10 -0.05962058276034765\n", "11 Y0 Y1 X2 X3 -0.044917169890753894\n", "12 X0 Y1 Y2 X3 0.044917169890753894\n", "13 Y0 X1 X2 Y3 0.044917169890753894\n", "14 X0 X1 Y2 Y3 -0.044917169890753894" ] }, "execution_count": 7, "metadata": {}, "output_type": "execute_result" } ], "source": [ "terms.numeric_sort(ascending=False, by_magnitude=True)\n", "terms.to_dataframe()" ] }, { "cell_type": "markdown", "id": "70bafc8f", "metadata": {}, "source": [ "Linear combinations of Pauli strings can be converted to sparse matrix representation in either little- or big-endian ordering." ] }, { "cell_type": "code", "execution_count": 8, "id": "096015c7", "metadata": {}, "outputs": [], "source": [ "ham_mat = ham_op.to_sparse_matrix(False).toarray()\n", "assert np.isclose(np.linalg.eigh(ham_mat)[0][0], H2_STO3G_FCI_ENERGY)" ] }, { "cell_type": "markdown", "id": "64a30ee0", "metadata": {}, "source": [ "Generally, the full computational basis is too large for enumeration and energy estimation, so it is useful to have access to subspace-projected matrices.\n", "\n", "In this example, only the basis vector corresponding to the Hartree-Fock state and its double excitation are non-zero in the ground state wavefunction." ] }, { "cell_type": "code", "execution_count": 9, "id": "30b99c97", "metadata": {}, "outputs": [ { "data": { "text/plain": [ "array([[-1.11750588, 0.17966868],\n", " [ 0.17966868, 0.53221654]])" ] }, "execution_count": 9, "metadata": {}, "output_type": "execute_result" } ], "source": [ "from zixy.qubit.state import Strings as StateStrings\n", "subspace = StateStrings.from_iterable(([1, 1, 0, 0], [0, 0, 1, 1]), ham_op.qubits)\n", "ham_mat = ham_op.project_into_ortho_subspace(subspace)\n", "ham_mat" ] }, { "cell_type": "markdown", "id": "6deede0a", "metadata": {}, "source": [ "As such, the `[1, 1, 0, 0], [0, 0, 1, 1]` subspace is sufficient to obtain the correct correlation energy" ] }, { "cell_type": "code", "execution_count": 10, "id": "3c14f04d", "metadata": {}, "outputs": [], "source": [ "assert np.isclose(ham_mat[0, 0], H2_STO3G_HF_ENERGY)\n", "assert np.isclose(np.linalg.eigh(ham_mat)[0][0], H2_STO3G_FCI_ENERGY)" ] }, { "cell_type": "markdown", "id": "96139300", "metadata": {}, "source": [ "The subspace is checked for orthonormality before projection of the operator into it is performed." ] }, { "cell_type": "code", "execution_count": 11, "id": "dbd046e6", "metadata": {}, "outputs": [ { "ename": "ValueError", "evalue": "The subspace is non-orthogonal (it contains repeated basis vectors)", "output_type": "error", "traceback": [ "\u001b[31m---------------------------------------------------------------------------\u001b[39m", "\u001b[31mValueError\u001b[39m Traceback (most recent call last)", "\u001b[36mCell\u001b[39m\u001b[36m \u001b[39m\u001b[32mIn[11]\u001b[39m\u001b[32m, line 2\u001b[39m\n\u001b[32m 1\u001b[39m subspace.append([\u001b[32m1\u001b[39m, \u001b[32m1\u001b[39m, \u001b[32m0\u001b[39m, \u001b[32m0\u001b[39m])\n\u001b[32m----> \u001b[39m\u001b[32m2\u001b[39m ham_mat = \u001b[43mham_op\u001b[49m\u001b[43m.\u001b[49m\u001b[43mproject_into_ortho_subspace\u001b[49m\u001b[43m(\u001b[49m\u001b[43msubspace\u001b[49m\u001b[43m)\u001b[49m\n", "\u001b[36mFile \u001b[39m\u001b[32m~/Desktop/zixy/zixy-py/docs/.venv/lib/python3.14/site-packages/zixy/qubit/pauli/_terms.py:655\u001b[39m, in \u001b[36mRealTermSum.project_into_ortho_subspace\u001b[39m\u001b[34m(self, subspace)\u001b[39m\n\u001b[32m 646\u001b[39m \u001b[38;5;250m\u001b[39m\u001b[33;03m\"\"\"Project the term into an orthogonal subspace defined by an ordered sequence of states.\u001b[39;00m\n\u001b[32m 647\u001b[39m \n\u001b[32m 648\u001b[39m \u001b[33;03mArgs:\u001b[39;00m\n\u001b[32m (...)\u001b[39m\u001b[32m 652\u001b[39m \u001b[33;03m The resulting vector of coefficients in the subspace basis.\u001b[39;00m\n\u001b[32m 653\u001b[39m \u001b[33;03m\"\"\"\u001b[39;00m\n\u001b[32m 654\u001b[39m \u001b[38;5;28;01massert\u001b[39;00m \u001b[38;5;28misinstance\u001b[39m(\u001b[38;5;28mself\u001b[39m._impl._coeffs, RealCoeffs)\n\u001b[32m--> \u001b[39m\u001b[32m655\u001b[39m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[43mQubitPauliArray\u001b[49m\u001b[43m.\u001b[49m\u001b[43mlincomb_project_into_ortho_subspace_real\u001b[49m\u001b[43m(\u001b[49m\n\u001b[32m 656\u001b[39m \u001b[43m \u001b[49m\u001b[38;5;28;43mself\u001b[39;49m\u001b[43m.\u001b[49m\u001b[43m_impl\u001b[49m\u001b[43m.\u001b[49m\u001b[43m_cmpnts\u001b[49m\u001b[43m.\u001b[49m\u001b[43m_impl\u001b[49m\u001b[43m,\u001b[49m\n\u001b[32m 657\u001b[39m \u001b[43m \u001b[49m\u001b[38;5;28;43mself\u001b[39;49m\u001b[43m.\u001b[49m\u001b[43m_impl\u001b[49m\u001b[43m.\u001b[49m\u001b[43m_coeffs\u001b[49m\u001b[43m.\u001b[49m\u001b[43m_impl\u001b[49m\u001b[43m,\u001b[49m\n\u001b[32m 658\u001b[39m \u001b[43m \u001b[49m\u001b[43msubspace\u001b[49m\u001b[43m.\u001b[49m\u001b[43m_impl\u001b[49m\u001b[43m,\u001b[49m\n\u001b[32m 659\u001b[39m \u001b[43m\u001b[49m\u001b[43m)\u001b[49m\n", "\u001b[31mValueError\u001b[39m: The subspace is non-orthogonal (it contains repeated basis vectors)" ] } ], "source": [ "subspace.append([1, 1, 0, 0])\n", "ham_mat = ham_op.project_into_ortho_subspace(subspace)" ] }, { "cell_type": "markdown", "id": "b5b6d2f9", "metadata": {}, "source": [ "Sometimes, non-orthonormality of the basis is a desirable property, and Zixy also has functionality to handle these cases." ] }, { "cell_type": "code", "execution_count": 12, "id": "48f78e69", "metadata": {}, "outputs": [], "source": [ "from zixy.qubit.state import RealTermSum as RealState\n", "subspace = [\n", " RealState.from_iterable([\n", " ([1, 1, 0, 0], 0.4), ([0, 0, 1, 1], -0.5)\n", " ], ham_op.qubits),\n", " RealState.from_iterable([\n", " ([1, 1, 0, 0], 0.4), ([0, 0, 1, 1], 0.9)\n", " ], ham_op.qubits)\n", "]\n", "ham_mat, ovlp_mat = ham_op.project_into_nonortho_subspace(subspace)" ] }, { "cell_type": "markdown", "id": "c81f390b", "metadata": {}, "source": [ "The metric of the subspace is returned along with the projected operator." ] }, { "cell_type": "code", "execution_count": 13, "id": "dbe2c5bb", "metadata": {}, "outputs": [ { "data": { "text/plain": [ "array([[ 0.41, -0.29],\n", " [-0.29, 0.97]])" ] }, "execution_count": 13, "metadata": {}, "output_type": "execute_result" } ], "source": [ "ovlp_mat" ] }, { "cell_type": "markdown", "id": "06d2352c", "metadata": {}, "source": [ "Note that the two linear combinations of basis vectors have the same span as the orthonormal subspace does, so we expect the correct correlation energy here too." ] }, { "cell_type": "code", "execution_count": 14, "id": "71dab624", "metadata": {}, "outputs": [], "source": [ "import scipy\n", "assert np.allclose(scipy.linalg.eigh(ham_mat, ovlp_mat)[0][0], H2_STO3G_FCI_ENERGY)" ] }, { "cell_type": "markdown", "id": "50b95458", "metadata": {}, "source": [ "State `TermSum` instances can be added together just as those of the Pauli operator `TermSum` type can." ] }, { "cell_type": "code", "execution_count": 15, "id": "eb38fe0c", "metadata": {}, "outputs": [], "source": [ "state_sum = sum(subspace, start = RealState(ham_op.qubits))\n", "assert state_sum == RealState.from_iterable([([1, 1, 0, 0], 0.8), ([0, 0, 1, 1], 0.4)], ham_op.qubits)" ] }, { "cell_type": "markdown", "id": "4b32293b", "metadata": {}, "source": [ "Of course, it is standard to unit-normalize the states" ] }, { "cell_type": "code", "execution_count": 16, "id": "15a01e86", "metadata": {}, "outputs": [], "source": [ "state_sum.l2_normalize()\n", "assert np.isclose(state_sum.l2_norm, 1.0)\n", "assert np.isclose(state_sum.l2_norm_square, 1.0)" ] }, { "cell_type": "markdown", "id": "492d99e7", "metadata": {}, "source": [ "Additionally, we have functionality to handle products of operators.\n", "\n", "Note that even pairs of hermitian Pauli operators (those with real coefficients) multiply to give complex results (when the two are non-commuting). Rather than going to the additional cost of checking for commutivity to deduce the return type, we leave it to the user to check and discard the imaginary part where appropriate." ] }, { "cell_type": "code", "execution_count": 17, "id": "92dc6a43", "metadata": {}, "outputs": [], "source": [ "ham2_op = ham_op * ham_op\n", "assert not ham2_op.imag_part.to_terms().coeffs.any_significant(0.0)\n", "ham2_op = ham2_op.real_part\n", "assert ham_op.commutes_with(ham2_op)\n", "ham2_mat = ham2_op.to_sparse_matrix(False)\n", "assert np.isclose(scipy.sparse.linalg.eigsh(ham2_mat)[0][0], H2_STO3G_FCI_ENERGY**2)\n", "ham2_mat = ham2_mat.toarray()\n", "ham_mat = ham_op.to_sparse_matrix(False).toarray()\n", "assert np.allclose(ham2_mat, ham_mat @ ham_mat)" ] }, { "cell_type": "markdown", "id": "d92cb0cd", "metadata": {}, "source": [ "States are able to be created from dense vectors in little- or big-endian ordering" ] }, { "cell_type": "code", "execution_count": 18, "id": "a7856862", "metadata": {}, "outputs": [ { "data": { "text/plain": [ "(-0.9942559956062316, [1, 1, 0, 0]), (0.10702810472516666, [0, 0, 1, 1])" ] }, "execution_count": 18, "metadata": {}, "output_type": "execute_result" } ], "source": [ "evals, evecs = np.linalg.eigh(ham_mat)\n", "ground_state = RealState.from_dense(ham_op.qubits, evecs[:, 0].real, False)\n", "ground_state" ] }, { "cell_type": "markdown", "id": "f1845d5f", "metadata": {}, "source": [ "There are methods to apply operators on states and take the inner product of states." ] }, { "cell_type": "code", "execution_count": 19, "id": "050eb0b1", "metadata": {}, "outputs": [], "source": [ "ham_times_ground_state = ham_op.apply(ground_state)\n", "assert not ham_times_ground_state.imag_part.to_terms().coeffs.any_significant(0.0)\n", "ham_times_ground_state = ham_times_ground_state.real_part\n", "assert np.isclose(ground_state.vdot(ham_times_ground_state), H2_STO3G_FCI_ENERGY)" ] }, { "cell_type": "markdown", "id": "df13d10e", "metadata": {}, "source": [ "Expectation values and matrix elements are also available." ] }, { "cell_type": "code", "execution_count": 21, "id": "49914c86", "metadata": {}, "outputs": [], "source": [ "assert np.isclose(ham_op.exp_val(ground_state), H2_STO3G_FCI_ENERGY)\n", "assert np.isclose(ham_op.mat_elem(ground_state, ground_state), H2_STO3G_FCI_ENERGY)" ] } ], "metadata": { "kernelspec": { "display_name": "Python 3 (ipykernel)", "language": "python", "name": "python3" }, "language_info": { "codemirror_mode": { "name": "ipython", "version": 3 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.14.0" } }, "nbformat": 4, "nbformat_minor": 5 }