diff --git a/.github/workflows/pythonpackage.yml b/.github/workflows/pythonpackage.yml index 8d4a3b11..68fcf8c2 100644 --- a/.github/workflows/pythonpackage.yml +++ b/.github/workflows/pythonpackage.yml @@ -55,6 +55,8 @@ jobs: run: | pip install pytest # RECIRQ_IMPORT_FAILSAFE: skip tests on unsupported Cirq configurations + # EXPORT_OMP_NUM_THREADS: pyscf has poor openmp performance which slows down qcqmc tests. + export OMP_NUM_THREADS=1 RECIRQ_IMPORT_FAILSAFE=y pytest -v nbformat: diff --git a/recirq/qcqmc/afqmc_generators.py b/recirq/qcqmc/afqmc_generators.py index f47614bc..97f78b3b 100644 --- a/recirq/qcqmc/afqmc_generators.py +++ b/recirq/qcqmc/afqmc_generators.py @@ -86,7 +86,7 @@ def get_charge_charge_generator(indices: Tuple[int, int]) -> of.FermionOperator: """Returns the generator for density evolution between the indices Args: - indices: The indices to for charge-charge terms.:w + indices: The indices to for charge-charge terms. Returns: The generator for density evolution for this pair of electrons. diff --git a/recirq/qcqmc/afqmc_generators_test.py b/recirq/qcqmc/afqmc_generators_test.py new file mode 100644 index 00000000..2ed059a9 --- /dev/null +++ b/recirq/qcqmc/afqmc_generators_test.py @@ -0,0 +1,125 @@ +# Copyright 2024 Google +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +"""Tests for afqmc_generators.py.""" + +import openfermion as of + +from recirq.qcqmc import afqmc_generators, layer_spec + + +def test_get_pp_plus_gate_generators(): + n_elec = 4 + heuristic_layers = ( + layer_spec.LayerSpec(base_gate="charge_charge", layout="in_pair"), + ) + gate_generators = afqmc_generators.get_pp_plus_gate_generators( + n_elec=n_elec, heuristic_layers=heuristic_layers, do_pp=False + ) + assert len(gate_generators) == 4 + assert gate_generators[0] == of.FermionOperator("2^ 2 4^ 4", 1.0) + assert gate_generators[1] == of.FermionOperator("3^ 3 5^ 5", 1.0) + assert gate_generators[2] == of.FermionOperator("0^ 0 6^ 6", 1.0) + assert gate_generators[3] == of.FermionOperator("1^ 1 7^ 7", 1.0) + + gate_generators_w_pp = afqmc_generators.get_pp_plus_gate_generators( + n_elec=n_elec, heuristic_layers=heuristic_layers, do_pp=True + ) + assert len(gate_generators_w_pp) == 6 + assert gate_generators_w_pp[0] == of.FermionOperator( + "2 3 4^ 5^", -1.0j + ) + of.FermionOperator("5 4 3^ 2^", +1.0j) + assert gate_generators_w_pp[1] == of.FermionOperator( + "0 1 6^ 7^", -1.0j + ) + of.FermionOperator("7 6 1^ 0^", 1.0j) + assert gate_generators_w_pp[2:] == gate_generators + + +def test_get_pair_hopping_gate_generators(): + n_pairs = 2 + n_elec = 4 + gate_generators = afqmc_generators.get_pair_hopping_gate_generators( + n_pairs=n_pairs, n_elec=n_elec + ) + assert len(gate_generators) == 2 + assert gate_generators[0] == of.FermionOperator( + "2 3 4^ 5^", -1.0j + ) + of.FermionOperator("5 4 3^ 2^", 1.0j) + assert gate_generators[1] == of.FermionOperator( + "0 1 6^ 7^", -1.0j + ) + of.FermionOperator("7 6 1^ 0^", 1.0j) + + +def test_get_charge_charge_generator(): + indices = (2, 3) + gate_generator = afqmc_generators.get_charge_charge_generator(indices=indices) + assert gate_generator == of.FermionOperator("2^ 2 3^ 3", 1.0) + + +def test_get_givens_generator(): + indices = (2, 3) + gate_generator = afqmc_generators.get_givens_generator(indices=indices) + assert gate_generator == of.FermionOperator("2^ 3", 1.0j) - of.FermionOperator( + "3^ 2", 1.0j + ) + + +def test_get_layer_generators(): + n_elec = 4 + layer_spec_in_pair = layer_spec.LayerSpec( + base_gate="charge_charge", layout="in_pair" + ) + gate_generators = afqmc_generators.get_layer_generators( + layer_spec=layer_spec_in_pair, n_elec=n_elec + ) + assert len(gate_generators) == 4 + assert gate_generators[0] == of.FermionOperator("2^ 2 4^ 4", 1.0) + assert gate_generators[1] == of.FermionOperator("3^ 3 5^ 5", 1.0) + assert gate_generators[2] == of.FermionOperator("0^ 0 6^ 6", 1.0) + assert gate_generators[3] == of.FermionOperator("1^ 1 7^ 7", 1.0) + + layer_spec_cross_pair = layer_spec.LayerSpec( + base_gate="givens", layout="cross_pair" + ) + gate_generators = afqmc_generators.get_layer_generators( + layer_spec=layer_spec_cross_pair, n_elec=n_elec + ) + assert len(gate_generators) == 2 + assert gate_generators[0] == of.FermionOperator("0^ 4", -1.0j) + of.FermionOperator( + "4^ 0", 1.0j + ) + assert gate_generators[1] == of.FermionOperator("1^ 5", -1.0j) + of.FermionOperator( + "5^ 1", 1.0j + ) + + +def test_get_heuristic_gate_generators(): + n_elec = 4 + heuristic_layers = ( + layer_spec.LayerSpec(base_gate="charge_charge", layout="in_pair"), + layer_spec.LayerSpec(base_gate="givens", layout="cross_pair"), + ) + gate_generators = afqmc_generators.get_heuristic_gate_generators( + n_elec=n_elec, layer_specs=heuristic_layers + ) + assert len(gate_generators) == 6 + assert gate_generators[0] == of.FermionOperator("2^ 2 4^ 4", 1.0) + assert gate_generators[1] == of.FermionOperator("3^ 3 5^ 5", 1.0) + assert gate_generators[2] == of.FermionOperator("0^ 0 6^ 6", 1.0) + assert gate_generators[3] == of.FermionOperator("1^ 1 7^ 7", 1.0) + assert gate_generators[4] == of.FermionOperator("0^ 4", -1.0j) + of.FermionOperator( + "4^ 0", 1.0j + ) + assert gate_generators[5] == of.FermionOperator("1^ 5", -1.0j) + of.FermionOperator( + "5^ 1", 1.0j + ) diff --git a/recirq/qcqmc/converters.py b/recirq/qcqmc/converters.py index 88365d80..d1f0422d 100644 --- a/recirq/qcqmc/converters.py +++ b/recirq/qcqmc/converters.py @@ -62,7 +62,7 @@ def get_ansatz_qubit_wf( def get_two_body_params_from_qchem_amplitudes( qchem_amplitudes: np.ndarray, ) -> np.ndarray: - """Translates perfect pairing amplitudes from qchem to rotation angles. + r"""Translates perfect pairing amplitudes from qchem to rotation angles. qchem style: 1 |1100> + t_i |0011> our style: cos(\theta_i) |1100> + sin(\theta_i) |0011> diff --git a/recirq/qcqmc/converters_test.py b/recirq/qcqmc/converters_test.py new file mode 100644 index 00000000..e69de29b diff --git a/recirq/qcqmc/hamiltonian_test.py b/recirq/qcqmc/hamiltonian_test.py index 17215659..9c474282 100644 --- a/recirq/qcqmc/hamiltonian_test.py +++ b/recirq/qcqmc/hamiltonian_test.py @@ -11,20 +11,16 @@ # 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 cirq import numpy as np import pytest -from openfermion import ( - get_fermion_operator, - get_ground_state, - get_number_preserving_sparse_operator, -) +from openfermion import (get_fermion_operator, get_ground_state, + get_number_preserving_sparse_operator) -from recirq.qcqmc.hamiltonian import ( - HamiltonianFileParams, - PyscfHamiltonianParams, - build_hamiltonian_from_file, - build_hamiltonian_from_pyscf, -) +from recirq.qcqmc.hamiltonian import (HamiltonianFileParams, + PyscfHamiltonianParams, + build_hamiltonian_from_file, + build_hamiltonian_from_pyscf) def test_load_from_file_hamiltonian_runs(): @@ -38,6 +34,14 @@ def test_load_from_file_hamiltonian_runs(): assert hamiltonian_data.two_body_integrals_pqrs.shape == (2, 2, 2, 2) +def test_hamiltonian_serialize(): + params = HamiltonianFileParams( + name="test hamiltonian", integral_key="fh_sto3g", n_orb=2, n_elec=2 + ) + params2 = cirq.read_json(json_text=cirq.to_json(params)) + assert params2 == params + + @pytest.mark.parametrize( "integral_key, n_orb, n_elec, do_eri_restore", [ diff --git a/recirq/qcqmc/layer_spec_test.py b/recirq/qcqmc/layer_spec_test.py new file mode 100644 index 00000000..219720f7 --- /dev/null +++ b/recirq/qcqmc/layer_spec_test.py @@ -0,0 +1,70 @@ +# Copyright 2024 Google +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +import cirq +import pytest + +from recirq.qcqmc import layer_spec + + +@pytest.mark.parametrize("base_gate", ("charge_charge", "givens")) +@pytest.mark.parametrize("layout", ("in_pair", "cross_pair", "cross_spin")) +def test_layer_spec(base_gate, layout): + ls = layer_spec.LayerSpec(base_gate=base_gate, layout=layout) + ls2 = cirq.read_json(json_text=cirq.to_json(ls)) + assert ls2 == ls + with pytest.raises(ValueError, match=r"base_gate is set*"): + ls = layer_spec.LayerSpec(base_gate=base_gate + "y", layout=layout) + with pytest.raises(ValueError, match=r"layout is set*"): + ls = layer_spec.LayerSpec(base_gate=base_gate, layout=layout + "y") + with pytest.raises(ValueError, match=r"base_gate is set*"): + ls = layer_spec.LayerSpec(base_gate=base_gate + "x", layout=layout + "y") + + +@pytest.mark.parametrize("n_elec", range(1, 10)) +def test_get_indices_heuristic_layer_cross_pair(n_elec): + n_pairs = max(n_elec // 2 - 1, 0) + n_terms = 0 + for x in layer_spec.get_indices_heuristic_layer_cross_pair(n_elec): + assert len(x) == 2 + if n_terms % 2 == 0: + assert x[0] + x[1] == 2 * n_elec - 4 + else: + assert x[0] + x[1] == 2 * n_elec - 2 + n_terms += 1 + assert n_terms == 2 * n_pairs + + +@pytest.mark.parametrize("n_elec", range(1, 10)) +def test_get_indices_heuristic_layer_cross_spin(n_elec): + n_pairs = n_elec // 2 + n_terms = 0 + for x in layer_spec.get_indices_heuristic_layer_cross_spin(n_elec): + assert len(x) == 2 + assert x[1] - x[0] == 1 + n_terms += 1 + assert n_terms == 2 * n_pairs + + +@pytest.mark.parametrize("n_elec", range(1, 10)) +def test_get_indices_heuristic_layer_in_pair(n_elec): + n_pairs = n_elec // 2 + n_terms = 0 + for x in layer_spec.get_indices_heuristic_layer_in_pair(n_elec): + assert len(x) == 2 + if n_terms % 2 == 0: + assert x[1] + x[0] == 2 * n_elec - 2 + else: + assert x[1] + x[0] == 2 * n_elec + n_terms += 1 + assert n_terms == 2 * n_pairs diff --git a/recirq/qcqmc/optimize_wf_test.py b/recirq/qcqmc/optimize_wf_test.py index c3788ba6..15b7e2fa 100644 --- a/recirq/qcqmc/optimize_wf_test.py +++ b/recirq/qcqmc/optimize_wf_test.py @@ -113,7 +113,7 @@ def test_pp_plus_wf_energy_sloppy_1(fixture_8_qubit_ham: HamiltonianData): trial_wf = build_pp_plus_trial_wavefunction( params, dependencies={fixture_8_qubit_ham.params: fixture_8_qubit_ham}, - do_print=True, + do_print=False, ) assert trial_wf.ansatz_energy < -1.947 @@ -133,7 +133,7 @@ def test_diamond_pp_wf_energy(fixture_12_qubit_ham: HamiltonianData): trial_wf = build_pp_plus_trial_wavefunction( params, dependencies={fixture_12_qubit_ham.params: fixture_12_qubit_ham}, - do_print=True, + do_print=False, ) assert trial_wf.ansatz_energy < -10.4 diff --git a/recirq/qcqmc/qubit_maps_test.py b/recirq/qcqmc/qubit_maps_test.py new file mode 100644 index 00000000..3ea78004 --- /dev/null +++ b/recirq/qcqmc/qubit_maps_test.py @@ -0,0 +1,127 @@ +# Copyright 2024 Google +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +"""Various mappings between fermions and qubits.""" + +import cirq +import pytest + +from recirq.qcqmc import qubit_maps + + +def test_get_qubits_a_b(): + n_orb = 4 + qubits = qubit_maps.get_qubits_a_b(n_orb=n_orb) + assert len(qubits) == 8 + assert qubits == ( + cirq.GridQubit(0, 0), + cirq.GridQubit(0, 1), + cirq.GridQubit(0, 2), + cirq.GridQubit(0, 3), + cirq.GridQubit(1, 0), + cirq.GridQubit(1, 1), + cirq.GridQubit(1, 2), + cirq.GridQubit(1, 3), + ) + + +def test_get_qubits_a_b_reversed(): + n_orb = 4 + qubits = qubit_maps.get_qubits_a_b_reversed(n_orb=n_orb) + assert len(qubits) == 8 + assert qubits == ( + cirq.GridQubit(0, 0), + cirq.GridQubit(0, 1), + cirq.GridQubit(0, 2), + cirq.GridQubit(0, 3), + cirq.GridQubit(1, 3), + cirq.GridQubit(1, 2), + cirq.GridQubit(1, 1), + cirq.GridQubit(1, 0), + ) + + +def test_get_4_qubit_fermion_qubit_map(): + fermion_qubit_map = qubit_maps.get_4_qubit_fermion_qubit_map() + assert fermion_qubit_map == { + 2: cirq.GridQubit(0, 0), + 3: cirq.GridQubit(1, 0), + 0: cirq.GridQubit(0, 1), + 1: cirq.GridQubit(1, 1), + } + + +def test_get_8_qubit_fermion_qubit_map(): + fermion_qubit_map = qubit_maps.get_8_qubit_fermion_qubit_map() + assert fermion_qubit_map == { + 2: cirq.GridQubit(0, 0), + 3: cirq.GridQubit(1, 0), + 4: cirq.GridQubit(0, 1), + 5: cirq.GridQubit(1, 1), + 0: cirq.GridQubit(0, 2), + 1: cirq.GridQubit(1, 2), + 6: cirq.GridQubit(0, 3), + 7: cirq.GridQubit(1, 3), + } + + +def test_get_12_qubit_fermion_qubit_map(): + fermion_qubit_map = qubit_maps.get_12_qubit_fermion_qubit_map() + assert fermion_qubit_map == { + 4: cirq.GridQubit(0, 0), + 5: cirq.GridQubit(1, 0), + 6: cirq.GridQubit(0, 1), + 7: cirq.GridQubit(1, 1), + 2: cirq.GridQubit(0, 2), + 3: cirq.GridQubit(1, 2), + 8: cirq.GridQubit(0, 3), + 9: cirq.GridQubit(1, 3), + 0: cirq.GridQubit(0, 4), + 1: cirq.GridQubit(1, 4), + 10: cirq.GridQubit(0, 5), + 11: cirq.GridQubit(1, 5), + } + + +def test_get_16_qubit_fermion_qubit_map(): + fermion_qubit_map = qubit_maps.get_16_qubit_fermion_qubit_map() + assert fermion_qubit_map == { + 6: cirq.GridQubit(0, 0), + 7: cirq.GridQubit(1, 0), + 8: cirq.GridQubit(0, 1), + 9: cirq.GridQubit(1, 1), + 4: cirq.GridQubit(0, 2), + 5: cirq.GridQubit(1, 2), + 10: cirq.GridQubit(0, 3), + 11: cirq.GridQubit(1, 3), + 2: cirq.GridQubit(0, 4), + 3: cirq.GridQubit(1, 4), + 12: cirq.GridQubit(0, 5), + 13: cirq.GridQubit(1, 5), + 0: cirq.GridQubit(0, 6), + 1: cirq.GridQubit(1, 6), + 14: cirq.GridQubit(0, 7), + 15: cirq.GridQubit(1, 7), + } + + +@pytest.mark.parametrize("n_qubits", (4, 8, 12, 16)) +def test_get_fermion_qubit_map_pp_plus(n_qubits): + fermion_qubit_map = qubit_maps.get_fermion_qubit_map_pp_plus(n_qubits=n_qubits) + assert len(fermion_qubit_map) == n_qubits + + +@pytest.mark.parametrize("n_qubits", (4, 8, 12, 16)) +def test_get_mode_qubit_map_pp_plus(n_qubits): + mode_qubit_map = qubit_maps.get_mode_qubit_map_pp_plus(n_qubits=n_qubits) + assert len(mode_qubit_map) == n_qubits diff --git a/recirq/qcqmc/trial_wf.py b/recirq/qcqmc/trial_wf.py index 0dfb43fb..eeea6585 100644 --- a/recirq/qcqmc/trial_wf.py +++ b/recirq/qcqmc/trial_wf.py @@ -110,13 +110,18 @@ class PerfectPairingPlusTrialWavefunctionParams(TrialWavefunctionParams): n_optimization_restarts: int = 1 seed: int = 0 initial_orbital_rotation: Optional[np.ndarray] = attrs.field( - default=None, converter=lambda v: _to_numpy(v) if v is not None else None + default=None, + converter=lambda v: _to_numpy(v) if v is not None else None, + eq=attrs.cmp_using(eq=np.array_equal), ) initial_two_body_qchem_amplitudes: Optional[np.ndarray] = attrs.field( - default=None, converter=lambda v: _to_numpy(v) if v is not None else None + default=None, + converter=lambda v: _to_numpy(v) if v is not None else None, + eq=attrs.cmp_using(eq=np.array_equal), ) do_optimization: bool = True use_fast_gradients: bool = False + path_prefix: str = "" @property def n_orb(self) -> int: @@ -136,7 +141,14 @@ def n_pairs(self) -> int: @property def path_string(self) -> str: - return config.OUTDIRS.DEFAULT_TRIAL_WAVEFUNCTION_DIRECTORY + self.name + if self.path_prefix: + return ( + self.path_prefix + + config.OUTDIRS.DEFAULT_TRIAL_WAVEFUNCTION_DIRECTORY.strip(".") + + self.name + ) + else: + return config.OUTDIRS.DEFAULT_TRIAL_WAVEFUNCTION_DIRECTORY + self.name @property def bitstrings(self) -> Iterable[Tuple[bool, ...]]: @@ -144,7 +156,9 @@ def bitstrings(self) -> Iterable[Tuple[bool, ...]]: return bitstrings.get_bitstrings_a_b(n_orb=self.n_orb, n_elec=self.n_elec) def _json_dict_(self): - return attrs.asdict(self) + simple_dict = attrs.asdict(self) + simple_dict["hamiltonian_params"] = self.hamiltonian_params + return simple_dict @property def qubits_jordan_wigner_ordered(self) -> Tuple[cirq.GridQubit, ...]: @@ -184,9 +198,17 @@ class TrialWavefunctionData(data.Data): hf_energy: float ansatz_energy: float fci_energy: float - one_body_basis_change_mat: np.ndarray = attrs.field(converter=_to_numpy) - one_body_params: np.ndarray = attrs.field(converter=_to_numpy) - two_body_params: np.ndarray = attrs.field(converter=_to_numpy) + one_body_basis_change_mat: np.ndarray = attrs.field( + converter=_to_numpy, eq=attrs.cmp_using(eq=np.array_equal) + ) + one_body_params: np.ndarray = attrs.field( + converter=_to_numpy, eq=attrs.cmp_using(eq=np.array_equal) + ) + two_body_params: np.ndarray = attrs.field( + converter=_to_numpy, eq=attrs.cmp_using(eq=np.array_equal) + ) def _json_dict_(self): - return attrs.asdict(self) + simple_dict = attrs.asdict(self) + simple_dict["params"] = self.params + return simple_dict diff --git a/recirq/qcqmc/trial_wf_test.py b/recirq/qcqmc/trial_wf_test.py new file mode 100644 index 00000000..af4fb8a2 --- /dev/null +++ b/recirq/qcqmc/trial_wf_test.py @@ -0,0 +1,100 @@ +# Copyright 2024 Google +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import math + +import cirq +import numpy as np +import pytest + +from recirq.qcqmc import hamiltonian, qubit_maps, trial_wf + + +def test_trial_wavefunction_params(): + integral_key, n_orb, n_elec, do_eri_restore = "fh_sto3g", 2, 2, False + params = hamiltonian.HamiltonianFileParams( + name="test hamiltonian", + integral_key=integral_key, + n_orb=n_orb, + n_elec=n_elec, + do_eri_restore=do_eri_restore, + ) + with pytest.raises(NotImplementedError, match="should be subclassed"): + _ = trial_wf.TrialWavefunctionParams( + name="test", + hamiltonian_params=params, + ).bitstrings() + with pytest.raises(NotImplementedError, match="should be subclassed"): + _ = trial_wf.TrialWavefunctionParams( + name="test", + hamiltonian_params=params, + ).qubits_jordan_wigner_ordered() + with pytest.raises(NotImplementedError, match="should be subclassed"): + _ = trial_wf.TrialWavefunctionParams( + name="test", + hamiltonian_params=params, + ).qubits_linearly_connected() + + +def test_perfect_pairing_plus_trial_wavefunction_params(): + ham_params = hamiltonian.HamiltonianFileParams( + name="test hamiltonian 4 qubits", integral_key="fh_sto3g", n_orb=2, n_elec=2 + ) + trial_params = trial_wf.PerfectPairingPlusTrialWavefunctionParams( + name="pp_test_wf_1", + hamiltonian_params=ham_params, + heuristic_layers=tuple(), + do_pp=True, + restricted=True, + path_prefix="/tmp", + ) + assert trial_params.n_orb == ham_params.n_orb + assert trial_params.n_elec == ham_params.n_elec + assert trial_params.n_qubits == 2 * ham_params.n_orb + assert trial_params.n_pairs == ham_params.n_elec // 2 + assert trial_params.path_string == "/tmp/data/trial_wfs/pp_test_wf_1" + n_orb = trial_params.n_orb + n_el_each_spin = trial_params.n_elec // 2 + assert len(list(trial_params.bitstrings)) == math.comb(n_orb, n_el_each_spin) ** 2 + assert trial_params.qubits_jordan_wigner_ordered == qubit_maps.get_qubits_a_b( + n_orb=trial_params.n_orb + ) + assert trial_params.qubits_linearly_connected == qubit_maps.get_qubits_a_b_reversed( + n_orb=trial_params.n_orb + ) + assert trial_params.mode_qubit_map == qubit_maps.get_mode_qubit_map_pp_plus( + n_qubits=trial_params.n_qubits + ) + params2 = cirq.read_json(json_text=cirq.to_json(trial_params)) + assert params2 == trial_params + + +def test_trial_wavefunction_data( + fixture_4_qubit_ham_and_trial_wf, +): + _, trial_params = fixture_4_qubit_ham_and_trial_wf + data = trial_wf.TrialWavefunctionData( + params=trial_params, + ansatz_circuit=cirq.Circuit(), + superposition_circuit=cirq.Circuit(), + hf_energy=0.0, + ansatz_energy=0.0, + fci_energy=0.0, + one_body_basis_change_mat=np.zeros((2, 2)), + one_body_params=np.zeros((2, 2)), + two_body_params=np.zeros((2, 2, 2, 2)), + ) + data2 = cirq.read_json(json_text=cirq.to_json(data)) + + assert data2 == data