From df0739b9ffb26a21c0ee7c868812e4d328c139e5 Mon Sep 17 00:00:00 2001 From: Max Rossmannek Date: Mon, 22 Nov 2021 15:04:30 +0100 Subject: [PATCH 1/3] Fix #434 (#435) The manual orbital selection of the `ActiveSpaceTransformer` was broken when specifying the number of active electrons as a tuple instead of an integer. This commit fixes that. --- .../electronic/active_space_transformer.py | 7 +- ...let-manual-selection-d022bdbfaccc7e65.yaml | 5 ++ .../test_active_space_transformer.py | 82 +++++++++++++++++++ 3 files changed, 93 insertions(+), 1 deletion(-) create mode 100644 releasenotes/notes/fix-active-space-transformer-non-singlet-manual-selection-d022bdbfaccc7e65.yaml diff --git a/qiskit_nature/transformers/second_quantization/electronic/active_space_transformer.py b/qiskit_nature/transformers/second_quantization/electronic/active_space_transformer.py index e5fb12972a..00ad6b1e44 100644 --- a/qiskit_nature/transformers/second_quantization/electronic/active_space_transformer.py +++ b/qiskit_nature/transformers/second_quantization/electronic/active_space_transformer.py @@ -335,7 +335,12 @@ def _validate_num_orbitals(self, nelec_inactive: int, particle_number: ParticleN ) if max(self._active_orbitals) >= particle_number._num_spin_orbitals // 2: raise QiskitNatureError("More orbitals requested than available.") - if sum(self._mo_occ_total[self._active_orbitals]) != self._num_electrons: + expected_num_electrons = ( + self._num_electrons + if isinstance(self._num_electrons, int) + else sum(self._num_electrons) + ) + if sum(self._mo_occ_total[self._active_orbitals]) != expected_num_electrons: raise QiskitNatureError( "The number of electrons in the selected active orbitals " "does not match the specified number of active electrons." diff --git a/releasenotes/notes/fix-active-space-transformer-non-singlet-manual-selection-d022bdbfaccc7e65.yaml b/releasenotes/notes/fix-active-space-transformer-non-singlet-manual-selection-d022bdbfaccc7e65.yaml new file mode 100644 index 0000000000..a85621fdfd --- /dev/null +++ b/releasenotes/notes/fix-active-space-transformer-non-singlet-manual-selection-d022bdbfaccc7e65.yaml @@ -0,0 +1,5 @@ +--- +fixes: + - | + Fixed the manual active orbital selection when specifying the active number + of electrons as a tuple rather than an integer. diff --git a/test/transformers/second_quantization/electronic/test_active_space_transformer.py b/test/transformers/second_quantization/electronic/test_active_space_transformer.py index 117a2c2ce6..218f8a7e5d 100644 --- a/test/transformers/second_quantization/electronic/test_active_space_transformer.py +++ b/test/transformers/second_quantization/electronic/test_active_space_transformer.py @@ -320,6 +320,88 @@ def test_active_space_for_q_molecule_v2(self): self.assertDriverResult(driver_result_reduced, driver_result) + def test_tuple_num_electrons_with_manual_orbitals(self): + """Regression test against https://github.com/Qiskit/qiskit-nature/issues/434.""" + driver = HDF5Driver( + hdf5_input=self.get_resource_path( + "H2_631g.hdf5", "transformers/second_quantization/electronic" + ) + ) + driver_result = driver.run() + + trafo = ActiveSpaceTransformer( + num_electrons=(1, 1), + num_molecular_orbitals=2, + active_orbitals=[0, 1], + ) + driver_result_reduced = trafo.transform(driver_result) + + expected = ElectronicStructureDriverResult() + expected.add_property( + ElectronicEnergy( + [ + OneBodyElectronicIntegrals( + ElectronicBasis.MO, + (np.asarray([[-1.24943841, 0.0], [0.0, -0.547816138]]), None), + ), + TwoBodyElectronicIntegrals( + ElectronicBasis.MO, + ( + np.asarray( + [ + [ + [[0.652098466, 0.0], [0.0, 0.433536565]], + [[0.0, 0.0794483182], [0.0794483182, 0.0]], + ], + [ + [[0.0, 0.0794483182], [0.0794483182, 0.0]], + [[0.433536565, 0.0], [0.0, 0.385524695]], + ], + ] + ), + None, + None, + None, + ), + ), + ], + energy_shift={"ActiveSpaceTransformer": 0.0}, + ) + ) + expected.add_property( + ElectronicDipoleMoment( + [ + DipoleMoment( + "x", + [OneBodyElectronicIntegrals(ElectronicBasis.MO, (np.zeros((2, 2)), None))], + shift={"ActiveSpaceTransformer": 0.0}, + ), + DipoleMoment( + "y", + [OneBodyElectronicIntegrals(ElectronicBasis.MO, (np.zeros((2, 2)), None))], + shift={"ActiveSpaceTransformer": 0.0}, + ), + DipoleMoment( + "z", + [ + OneBodyElectronicIntegrals( + ElectronicBasis.MO, + ( + np.asarray( + [[0.69447435, -1.01418298], [-1.01418298, 0.69447435]] + ), + None, + ), + ) + ], + shift={"ActiveSpaceTransformer": 0.0}, + ), + ] + ) + ) + + self.assertDriverResult(driver_result_reduced, expected) + if __name__ == "__main__": unittest.main() From 6361a09d89e32480bb32f8922f998eb09dba597c Mon Sep 17 00:00:00 2001 From: Steve Wood <40241007+woodsp-ibm@users.noreply.github.com> Date: Mon, 22 Nov 2021 10:05:31 -0500 Subject: [PATCH 2/3] Fix EOM with 2Q reduction and tapering (#432) * Fix EOM with 2Q reduction and tapering * Fix sector picker * Reno, plus added BK mapper tests for completeness * Spelling fixes in reno * Update qiskit_nature/problems/second_quantization/electronic/builders/hopping_ops_builder.py Co-authored-by: Max Rossmannek Co-authored-by: Max Rossmannek Co-authored-by: Max Rossmannek --- .../algorithms/excited_states_solvers/qeom.py | 5 +- .../builders/hopping_ops_builder.py | 10 ++-- .../electronic_structure_problem.py | 2 +- .../notes/qeom_fix-4341f74549f22891.yaml | 10 ++++ .../test_excited_states_solvers.py | 53 +++++++++++++++++-- 5 files changed, 70 insertions(+), 10 deletions(-) create mode 100644 releasenotes/notes/qeom_fix-4341f74549f22891.yaml diff --git a/qiskit_nature/algorithms/excited_states_solvers/qeom.py b/qiskit_nature/algorithms/excited_states_solvers/qeom.py index d0987c189a..4fd65e28bf 100644 --- a/qiskit_nature/algorithms/excited_states_solvers/qeom.py +++ b/qiskit_nature/algorithms/excited_states_solvers/qeom.py @@ -105,7 +105,10 @@ def solve( groundstate_result = self._gsc.solve(problem) # 2. Prepare the excitation operators - self._untapered_qubit_op_main = self._gsc._qubit_converter.map(problem.second_q_ops()[0]) + self._untapered_qubit_op_main = self._gsc.qubit_converter.convert_only( + problem.second_q_ops()[0], problem.num_particles + ) + matrix_operators_dict, size = self._prepare_matrix_operators(problem) # 3. Evaluate eom operators diff --git a/qiskit_nature/problems/second_quantization/electronic/builders/hopping_ops_builder.py b/qiskit_nature/problems/second_quantization/electronic/builders/hopping_ops_builder.py index aed0fc2c3e..d6bc992b46 100644 --- a/qiskit_nature/problems/second_quantization/electronic/builders/hopping_ops_builder.py +++ b/qiskit_nature/problems/second_quantization/electronic/builders/hopping_ops_builder.py @@ -114,16 +114,18 @@ def _build_single_hopping_operator( label[unocc] = "-" fer_op = FermionicOp(("".join(label), 4.0 ** len(excitation[0])), display_format="sparse") - qubit_op: PauliSumOp = qubit_converter.convert_match(fer_op) + qubit_op: PauliSumOp = qubit_converter.convert_only(fer_op, qubit_converter.num_particles) z2_symmetries = qubit_converter.z2symmetries commutativities = [] if not z2_symmetries.is_empty(): for symmetry in z2_symmetries.symmetries: symmetry_op = PauliSumOp.from_list([(symmetry.to_label(), 1.0)]) - commuting = qubit_op.primitive.table.commutes_with_all(symmetry_op.primitive.table) - anticommuting = qubit_op.primitive.table.anticommutes_with_all( - symmetry_op.primitive.table + paulis = qubit_op.primitive.paulis + len_paulis = len(paulis) + commuting = len(paulis.commutes_with_all(symmetry_op.primitive.paulis)) == len_paulis + anticommuting = ( + len(paulis.anticommutes_with_all(symmetry_op.primitive.paulis)) == len_paulis ) if commuting != anticommuting: # only one of them is True diff --git a/qiskit_nature/problems/second_quantization/electronic/electronic_structure_problem.py b/qiskit_nature/problems/second_quantization/electronic/electronic_structure_problem.py index b111a81d69..6f1e900963 100644 --- a/qiskit_nature/problems/second_quantization/electronic/electronic_structure_problem.py +++ b/qiskit_nature/problems/second_quantization/electronic/electronic_structure_problem.py @@ -236,7 +236,7 @@ def _pick_sector(z2_symmetries: Z2Symmetries, hf_str: List[bool]) -> List[int]: # Finding all the symmetries using the find_Z2_symmetries: taper_coeff: List[int] = [] for sym in z2_symmetries.symmetries: - coeff = -1 if np.logical_xor.reduce(np.logical_and(sym.z[::-1], hf_str)) else 1 + coeff = -1 if np.logical_xor.reduce(np.logical_and(sym.z, hf_str)) else 1 taper_coeff.append(coeff) return taper_coeff diff --git a/releasenotes/notes/qeom_fix-4341f74549f22891.yaml b/releasenotes/notes/qeom_fix-4341f74549f22891.yaml new file mode 100644 index 0000000000..43f5e92175 --- /dev/null +++ b/releasenotes/notes/qeom_fix-4341f74549f22891.yaml @@ -0,0 +1,10 @@ +--- +fixes: + - | + Fixes QEOM such that when using parity mapping with two_qubit_reduction, + or Z2 symmetries with any mapping, that the excited states are computed + as expected. + + Fix the electronic structure problem sector locator such that the 'auto' + Z2 symmetry conversion, of the qubit converter, results in the ground state + for such problems and not some other value due to incorrect sector selection. diff --git a/test/algorithms/excited_state_solvers/test_excited_states_solvers.py b/test/algorithms/excited_state_solvers/test_excited_states_solvers.py index f1eac8f7ed..5cfeacd1d6 100644 --- a/test/algorithms/excited_state_solvers/test_excited_states_solvers.py +++ b/test/algorithms/excited_state_solvers/test_excited_states_solvers.py @@ -21,7 +21,11 @@ from qiskit_nature.drivers import UnitsType from qiskit_nature.drivers.second_quantization import PySCFDriver -from qiskit_nature.mappers.second_quantization import JordanWignerMapper +from qiskit_nature.mappers.second_quantization import ( + BravyiKitaevMapper, + JordanWignerMapper, + ParityMapper, +) from qiskit_nature.converters.second_quantization import QubitConverter from qiskit_nature.problems.second_quantization import ElectronicStructureProblem from qiskit_nature.algorithms import ( @@ -75,10 +79,51 @@ def test_numpy_mes(self): for idx, energy in enumerate(self.reference_energies): self.assertAlmostEqual(results.computed_energies[idx], energy, places=4) - def test_vqe_mes(self): - """Test VQEUCCSDFactory with QEOM""" + def test_vqe_mes_jw(self): + """Test VQEUCCSDFactory with QEOM + Jordan Wigner mapping""" + converter = QubitConverter(JordanWignerMapper()) + self._solve_with_vqe_mes(converter) + + def test_vqe_mes_jw_auto(self): + """Test VQEUCCSDFactory with QEOM + Jordan Wigner mapping + auto symmetry""" + converter = QubitConverter(JordanWignerMapper(), z2symmetry_reduction="auto") + self._solve_with_vqe_mes(converter) + + def test_vqe_mes_parity(self): + """Test VQEUCCSDFactory with QEOM + Parity mapping""" + converter = QubitConverter(ParityMapper()) + self._solve_with_vqe_mes(converter) + + def test_vqe_mes_parity_2q(self): + """Test VQEUCCSDFactory with QEOM + Parity mapping + reduction""" + converter = QubitConverter(ParityMapper(), two_qubit_reduction=True) + self._solve_with_vqe_mes(converter) + + def test_vqe_mes_parity_auto(self): + """Test VQEUCCSDFactory with QEOM + Parity mapping + auto symmetry""" + converter = QubitConverter(ParityMapper(), z2symmetry_reduction="auto") + self._solve_with_vqe_mes(converter) + + def test_vqe_mes_parity_2q_auto(self): + """Test VQEUCCSDFactory with QEOM + Parity mapping + reduction + auto symmetry""" + converter = QubitConverter( + ParityMapper(), two_qubit_reduction=True, z2symmetry_reduction="auto" + ) + self._solve_with_vqe_mes(converter) + + def test_vqe_mes_bk(self): + """Test VQEUCCSDFactory with QEOM + Bravyi-Kitaev mapping""" + converter = QubitConverter(BravyiKitaevMapper()) + self._solve_with_vqe_mes(converter) + + def test_vqe_mes_bk_auto(self): + """Test VQEUCCSDFactory with QEOM + Bravyi-Kitaev mapping + auto symmetry""" + converter = QubitConverter(BravyiKitaevMapper(), z2symmetry_reduction="auto") + self._solve_with_vqe_mes(converter) + + def _solve_with_vqe_mes(self, converter: QubitConverter): solver = VQEUCCFactory(self.quantum_instance) - gsc = GroundStateEigensolver(self.qubit_converter, solver) + gsc = GroundStateEigensolver(converter, solver) esc = QEOM(gsc, "sd") results = esc.solve(self.electronic_structure_problem) From e13fd463f80b318a0f5394645d671aea90086940 Mon Sep 17 00:00:00 2001 From: Manoel Marques Date: Tue, 23 Nov 2021 12:52:25 -0500 Subject: [PATCH 3/3] Update psi4 to stable version (#438) --- .github/actions/install-psi4/action.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/actions/install-psi4/action.yml b/.github/actions/install-psi4/action.yml index 095d05c013..9b1815e2c1 100644 --- a/.github/actions/install-psi4/action.yml +++ b/.github/actions/install-psi4/action.yml @@ -26,10 +26,10 @@ runs: source "$CONDA/etc/profile.d/conda.sh" conda activate psi4env if [ "${{ inputs.os }}" == "windows-latest" ]; then - conda install -y psi4=1.4rc4.dev7 python=${{ inputs.python-version }} -c psi4/label/dev -c conda-forge + conda install -y psi4 python=${{ inputs.python-version }} -c conda-forge -c psi4/label/dev psi4 --test else - conda install -y psi4=1.4rc3 python=${{ inputs.python-version }} -c psi4/label/dev + conda install -y psi4 python=${{ inputs.python-version }} -c psi4 pip install -U numpy fi shell: bash \ No newline at end of file