def build_hamiltonian_operators(self, qubit_mapping, two_qubit_reduction, epsilon, num_particles): self.M_h_elements = [] self.Q_h_elements = [] self.times_MQ_h = [] for (I, J) in self.elements: t0 = time.time() EI, EJ = self.E_mu[I], self.E_mu[J] MIJ_oper, MIJ_idx = triple_commutator_adj_twobody_nor( self.n, EI, EJ, self.h) QIJ_oper, QIJ_idx = triple_commutator_adj_twobody_adj( self.n, EI, EJ, self.h) t1 = time.time() self.logfile.write( "M,Q elements (H) %d %d --- time for FermionicOperator %f\n" % (I, J, t1 - t0)) MIJ_oper = MIJ_oper.mapping(map_type=qubit_mapping.value, threshold=epsilon, idx=MIJ_idx) QIJ_oper = QIJ_oper.mapping(map_type=qubit_mapping.value, threshold=epsilon, idx=QIJ_idx) if qubit_mapping == 'parity' and two_qubit_reduction: MIJ_oper = Z2Symmetries.two_qubit_reduction( MIJ_oper, num_particles) QIJ_oper = Z2Symmetries.two_qubit_reduction( QIJ_oper, num_particles) self.M_h_elements.append(MIJ_oper) self.Q_h_elements.append(QIJ_oper) t2 = time.time() self.times_MQ_h.append((I, J, t1 - t0, t2 - t1)) self.logfile.write( "M,Q elements (H) %d %d --- time for Mapping %f\n" % (I, J, t2 - t1))
def _build_commutator_routine(params: List, operator: WeightedPauliOperator, z2_symmetries: Z2Symmetries, sign: int ) -> Tuple[int, int, WeightedPauliOperator, WeightedPauliOperator, WeightedPauliOperator, WeightedPauliOperator]: """Numerically computes the commutator / double commutator between operators. Args: params: list containing the indices of matrix element and the corresponding excitation operators operator: the hamiltonian z2_symmetries: z2_symmetries in case of tapering sign: commute or anticommute Returns: The indices of the matrix element and the corresponding qubit operator for each of the EOM matrices """ m_u, n_u, left_op, right_op_1, right_op_2 = params if left_op is None: q_mat_op = None w_mat_op = None m_mat_op = None v_mat_op = None else: if right_op_1 is None and right_op_2 is None: q_mat_op = None w_mat_op = None m_mat_op = None v_mat_op = None else: if right_op_1 is not None: q_mat_op = commutator(left_op, operator, right_op_1, sign=sign) w_mat_op = commutator(left_op, right_op_1, sign=sign) q_mat_op = None if q_mat_op.is_empty() else q_mat_op w_mat_op = None if w_mat_op.is_empty() else w_mat_op else: q_mat_op = None w_mat_op = None if right_op_2 is not None: m_mat_op = commutator(left_op, operator, right_op_2, sign=sign) v_mat_op = commutator(left_op, right_op_2, sign=sign) m_mat_op = None if m_mat_op.is_empty() else m_mat_op v_mat_op = None if v_mat_op.is_empty() else v_mat_op else: m_mat_op = None v_mat_op = None if not z2_symmetries.is_empty(): if q_mat_op is not None and not q_mat_op.is_empty(): q_mat_op = z2_symmetries.taper(q_mat_op) if w_mat_op is not None and not w_mat_op.is_empty(): w_mat_op = z2_symmetries.taper(w_mat_op) if m_mat_op is not None and not m_mat_op.is_empty(): m_mat_op = z2_symmetries.taper(m_mat_op) if v_mat_op is not None and not v_mat_op.is_empty(): v_mat_op = z2_symmetries.taper(v_mat_op) return m_u, n_u, q_mat_op, w_mat_op, m_mat_op, v_mat_op
def build_operators(self, task, qubit_mapping, two_qubit_reduction, num_particles, epsilon): self.matrix_elements_adj_nor[task] = [] self.matrix_elements_adj_adj[task] = [] self.time_statistics[task] = [] # ----- for (I, J) in self.elements: # ----- computation t0 = time.time() EI, EJ = self.E_mu[I], self.E_mu[J] if (task == 'overlap'): adj_nor_oper, adj_nor_idx = commutator_adj_nor(self.n, EI, EJ) adj_adj_oper, adj_adj_idx = commutator_adj_adj(self.n, EI, EJ) if (task == 'hamiltonian'): adj_nor_oper, adj_nor_idx = triple_commutator_adj_twobody_nor( self.n, EI, EJ, self.h) adj_adj_oper, adj_adj_idx = triple_commutator_adj_twobody_adj( self.n, EI, EJ, self.h) if (task == 'diagnosis_number'): adj_nor_oper, adj_nor_idx = triple_commutator_adj_onebody_nor( self.n, EI, EJ, self.ne) adj_adj_oper, adj_adj_idx = triple_commutator_adj_onebody_adj( self.n, EI, EJ, self.ne) if (task == 'diagnosis_spin-z'): adj_nor_oper, adj_nor_idx = triple_commutator_adj_onebody_nor( self.n, EI, EJ, self.sz) adj_adj_oper, adj_adj_idx = triple_commutator_adj_onebody_adj( self.n, EI, EJ, self.sz) if (task == 'diagnosis_spin-squared'): adj_nor_oper, adj_nor_idx = triple_commutator_adj_twobody_nor( self.n, EI, EJ, self.ss) adj_adj_oper, adj_adj_idx = triple_commutator_adj_twobody_adj( self.n, EI, EJ, self.ss) t1 = time.time() self.logfile.write( "task: " + task + " elements %d %d --- time for FermionicOperator %f\n" % (I, J, t1 - t0)) # ----- mapping adj_nor_oper = adj_nor_oper.mapping(map_type=qubit_mapping.value, threshold=epsilon, idx=adj_nor_idx) adj_adj_oper = adj_adj_oper.mapping(map_type=qubit_mapping.value, threshold=epsilon, idx=adj_adj_idx) if qubit_mapping.value == 'parity' and two_qubit_reduction: adj_nor_oper = Z2Symmetries.two_qubit_reduction( adj_nor_oper, num_particles) adj_adj_oper = Z2Symmetries.two_qubit_reduction( adj_adj_oper, num_particles) self.matrix_elements_adj_nor[task].append(adj_nor_oper) self.matrix_elements_adj_adj[task].append(adj_adj_oper) t2 = time.time() # ----- timings self.logfile.write("task: " + task + " elements %d %d --- time for Mapping %f\n" % (I, J, t2 - t1)) self.time_statistics[task].append((I, J, t1 - t0, t2 - t1))
def _process_z2symmetry_reduction(self, qubit_op, aux_ops): z2_symmetries = Z2Symmetries.find_Z2_symmetries(qubit_op) if z2_symmetries.is_empty(): logger.debug('No Z2 symmetries found') z2_qubit_op = qubit_op z2_aux_ops = aux_ops z2_symmetries = Z2Symmetries([], [], [], None) else: logger.debug('%s Z2 symmetries found: %s', len(z2_symmetries.symmetries), ','.join([symm.to_label() for symm in z2_symmetries.symmetries])) # Check auxiliary operators commute with main operator's symmetry logger.debug('Checking operators commute with symmetry:') symmetry_ops = [] for symmetry in z2_symmetries.symmetries: symmetry_ops.append(WeightedPauliOperator(paulis=[[1.0, symmetry]])) commutes = Hamiltonian._check_commutes(symmetry_ops, qubit_op) if not commutes: raise QiskitChemistryError('Z2 symmetry failure main operator must commute ' 'with symmetries found from it') for i, aux_op in enumerate(aux_ops): commutes = Hamiltonian._check_commutes(symmetry_ops, aux_op) if not commutes: aux_ops[i] = None # Discard since no meaningful measurement can be done if self._z2symmetry_reduction == 'auto': hf_state = HartreeFock(num_orbitals=self._molecule_info[self.INFO_NUM_ORBITALS], qubit_mapping=self._qubit_mapping, two_qubit_reduction=self._two_qubit_reduction, num_particles=self._molecule_info[self.INFO_NUM_PARTICLES]) z2_symmetries = Hamiltonian._pick_sector(z2_symmetries, hf_state.bitstr) else: if len(self._z2symmetry_reduction) != len(z2_symmetries.symmetries): raise QiskitChemistryError('z2symmetry_reduction tapering values list has ' 'invalid length {} should be {}'. format(len(self._z2symmetry_reduction), len(z2_symmetries.symmetries))) valid = np.all(np.isin(self._z2symmetry_reduction, [-1, 1])) if not valid: raise QiskitChemistryError('z2symmetry_reduction tapering values list must ' 'contain -1\'s and/or 1\'s only was {}'. format(self._z2symmetry_reduction,)) z2_symmetries.tapering_values = self._z2symmetry_reduction logger.debug('Apply symmetry with tapering values %s', z2_symmetries.tapering_values) chop_to = 0.00000001 # Use same threshold as qubit mapping to chop tapered operator z2_qubit_op = z2_symmetries.taper(qubit_op).chop(chop_to) z2_aux_ops = [] for aux_op in aux_ops: z2_aux_ops.append(z2_symmetries.taper(aux_op).chop(chop_to) if aux_op is not None else None) return z2_qubit_op, z2_aux_ops, z2_symmetries
def build_overlap_operators(self, qubit_mapping, two_qubit_reduction, epsilon, num_particles): self.V_elements = [] self.W_elements = [] self.times_VW = [] for (I, J) in self.elements: t0 = time.time() EI, EJ = self.E_mu[I], self.E_mu[J] VIJ_oper, VIJ_idx = commutator_adj_nor(self.n, EI, EJ) WIJ_oper, WIJ_idx = [None] * 4, [None] * 4 t1 = time.time() self.logfile.write( "V,W elements %d %d --- time for FermionicOperator %f\n" % (I, J, t1 - t0)) VIJ_oper = VIJ_oper.mapping(map_type=qubit_mapping.value, threshold=epsilon, idx=VIJ_idx) if qubit_mapping.value == 'parity' and two_qubit_reduction: VIJ_oper = Z2Symmetries.two_qubit_reduction( VIJ_oper, num_particles) self.V_elements.append(VIJ_oper) t2 = time.time() self.times_VW.append((I, J, t1 - t0, t2 - t1)) self.logfile.write("V,W elements %d %d --- time for Mapping %f\n" % (I, J, t2 - t1))
def get_qubit_op(target_molecule): geometry, multiplicity, charge = generate_molecule_dict() driver = PySCFDriver(atom=geometry_convert(target_molecule), unit=UnitsType.ANGSTROM, charge=charge[target_molecule], spin=0, basis='sto3g') molecule = driver.run() repulsion_energy = molecule.nuclear_repulsion_energy num_particles = molecule.num_alpha + molecule.num_beta num_spin_orbitals = molecule.num_orbitals * 2 one_RDM = make_one_rdm(target_molecule) w = calculate_noons(one_RDM) freeze_list, remove_list = generate_freeze_remove_list(w) remove_list = [x % molecule.num_orbitals for x in remove_list] freeze_list = [x % molecule.num_orbitals for x in freeze_list] remove_list = [x - len(freeze_list) for x in remove_list] remove_list += [ x + molecule.num_orbitals - len(freeze_list) for x in remove_list ] freeze_list += [x + molecule.num_orbitals for x in freeze_list] ferOp = FermionicOperator(h1=molecule.one_body_integrals, h2=molecule.two_body_integrals) ferOp, energy_shift = ferOp.fermion_mode_freezing(freeze_list) num_spin_orbitals -= len(freeze_list) num_particles -= len(freeze_list) ferOp = ferOp.fermion_mode_elimination(remove_list) num_spin_orbitals -= len(remove_list) qubitOp = ferOp.mapping(map_type='bravyi_kitaev', threshold=0.00000001) qubitOp = Z2Symmetries.two_qubit_reduction(qubitOp, num_particles) shift = energy_shift + repulsion_energy return qubitOp, num_particles, num_spin_orbitals, shift
def test_h2_one_qubit(self): """Test H2 with tapering.""" two_qubit_reduction = False qubit_mapping = 'jordan_wigner' core = Hamiltonian(transformation=TransformationType.FULL, qubit_mapping=QubitMappingType.JORDAN_WIGNER, two_qubit_reduction=two_qubit_reduction, freeze_core=False, orbital_reduction=[]) qubit_op, _ = core.run(self.molecule) num_orbitals = core.molecule_info['num_orbitals'] num_particles = core.molecule_info['num_particles'] z2_symmetries = Z2Symmetries.find_Z2_symmetries(qubit_op) tapered_op = z2_symmetries.taper(qubit_op)[5] eom_ee = QEomEE(tapered_op, num_orbitals=num_orbitals, num_particles=num_particles, qubit_mapping=qubit_mapping, two_qubit_reduction=two_qubit_reduction, z2_symmetries=tapered_op.z2_symmetries, untapered_op=qubit_op) result = eom_ee.run() np.testing.assert_array_almost_equal(self.reference, result['energies'])
def lih(dist=1.5): mol = PySCFDriver(atom= 'H 0.0 0.0 0.0;'\ 'Li 0.0 0.0 {}'.format(dist), unit=UnitsType.ANGSTROM, charge=0, spin=0, basis='sto-3g') mol = mol.run() freeze_list = [0] remove_list = [-3, -2] repulsion_energy = mol.nuclear_repulsion_energy num_particles = mol.num_alpha + mol.num_beta num_spin_orbitals = mol.num_orbitals * 2 remove_list = [x % mol.num_orbitals for x in remove_list] freeze_list = [x % mol.num_orbitals for x in freeze_list] remove_list = [x - len(freeze_list) for x in remove_list] remove_list += [ x + mol.num_orbitals - len(freeze_list) for x in remove_list ] freeze_list += [x + mol.num_orbitals for x in freeze_list] ferOp = FermionicOperator(h1=mol.one_body_integrals, h2=mol.two_body_integrals) ferOp, energy_shift = ferOp.fermion_mode_freezing(freeze_list) num_spin_orbitals -= len(freeze_list) num_particles -= len(freeze_list) ferOp = ferOp.fermion_mode_elimination(remove_list) num_spin_orbitals -= len(remove_list) qubitOp = ferOp.mapping(map_type='parity', threshold=0.00000001) qubitOp = Z2Symmetries.two_qubit_reduction(qubitOp, num_particles) shift = energy_shift + repulsion_energy cHam = op_converter.to_matrix_operator(qubitOp) cHam = cHam.dense_matrix + shift * numpy.identity(16) return cHam
def test_h2_one_qubit_qasm(self): """Test H2 with tapering and qasm backend""" two_qubit_reduction = True qubit_mapping = 'parity' core = Hamiltonian(transformation=TransformationType.FULL, qubit_mapping=QubitMappingType.PARITY, two_qubit_reduction=two_qubit_reduction, freeze_core=False, orbital_reduction=[]) qubit_op, _ = core.run(self.molecule) num_orbitals = core.molecule_info['num_orbitals'] num_particles = core.molecule_info['num_particles'] # tapering z2_symmetries = Z2Symmetries.find_Z2_symmetries(qubit_op) # know the sector tapered_op = z2_symmetries.taper(qubit_op)[1] var_form = RY(tapered_op.num_qubits, depth=1) optimizer = SPSA(max_trials=50) eom_vqe = QEomVQE(tapered_op, var_form, optimizer, num_orbitals=num_orbitals, num_particles=num_particles, qubit_mapping=qubit_mapping, two_qubit_reduction=two_qubit_reduction, z2_symmetries=tapered_op.z2_symmetries, untapered_op=qubit_op) backend = BasicAer.get_backend('qasm_simulator') quantum_instance = QuantumInstance(backend, shots=65536) result = eom_vqe.run(quantum_instance) np.testing.assert_array_almost_equal(self.reference, result['energies'], decimal=2)
def _map_fermionic_operator_to_qubit(fer_op, qubit_mapping, num_particles, two_qubit_reduction): qubit_op = fer_op.mapping(map_type=qubit_mapping, threshold=0.00000001) if qubit_mapping == 'parity' and two_qubit_reduction: qubit_op = Z2Symmetries.two_qubit_reduction( qubit_op, num_particles) return qubit_op
def _build_hopping_operator(index, num_orbitals, num_particles, qubit_mapping, two_qubit_reduction, z2_symmetries): h_1 = np.zeros((num_orbitals, num_orbitals)) h_2 = np.zeros((num_orbitals, num_orbitals, num_orbitals, num_orbitals)) if len(index) == 2: i, j = index h_1[i, j] = 1.0 h_1[j, i] = -1.0 elif len(index) == 4: i, j, k, m = index h_2[i, j, k, m] = 1.0 h_2[m, k, j, i] = -1.0 dummpy_fer_op = FermionicOperator(h1=h_1, h2=h_2) qubit_op = dummpy_fer_op.mapping(qubit_mapping) if two_qubit_reduction: qubit_op = Z2Symmetries.two_qubit_reduction(qubit_op, num_particles) if not z2_symmetries.is_empty(): symm_commuting = True for symmetry in z2_symmetries.symmetries: symmetry_op = WeightedPauliOperator(paulis=[[1.0, symmetry]]) symm_commuting = qubit_op.commute_with(symmetry_op) if not symm_commuting: break qubit_op = z2_symmetries.taper(qubit_op) if symm_commuting else None if qubit_op is None: logger.debug('Excitation (%s) is skipped since it is not commuted ' 'with symmetries', ','.join([str(x) for x in index])) return qubit_op, index
def _build_single_hopping_operator(index, num_particles, num_orbitals, qubit_mapping, two_qubit_reduction, z2_symmetries): h_1 = np.zeros((num_orbitals, num_orbitals), dtype=complex) h_2 = np.zeros((num_orbitals, num_orbitals, num_orbitals, num_orbitals), dtype=complex) if len(index) == 2: i, j = index h_1[i, j] = 4.0 elif len(index) == 4: i, j, k, m = index h_2[i, j, k, m] = 16.0 fer_op = FermionicOperator(h_1, h_2) qubit_op = fer_op.mapping(qubit_mapping) if qubit_mapping == 'parity' and two_qubit_reduction: qubit_op = Z2Symmetries.two_qubit_reduction(qubit_op, num_particles) commutativities = [] if not z2_symmetries.is_empty(): for symmetry in z2_symmetries.symmetries: symmetry_op = WeightedPauliOperator(paulis=[[1.0, symmetry]]) commuting = qubit_op.commute_with(symmetry_op) anticommuting = qubit_op.anticommute_with(symmetry_op) if commuting != anticommuting: # only one of them is True if commuting: commutativities.append(True) elif anticommuting: commutativities.append(False) else: raise AquaError("Symmetry {} is nor commute neither anti-commute " "to exciting operator.".format(symmetry.to_label())) return qubit_op, commutativities
def get_H2_data(dist): """ Use the qiskit chemistry package to get the qubit Hamiltonian for LiH Parameters ---------- dist : float The nuclear separations Returns ------- qubitOp : qiskit.aqua.operators.WeightedPauliOperator Qiskit representation of the qubit Hamiltonian shift : float The ground state of the qubit Hamiltonian needs to be corrected by this amount of energy to give the real physical energy. This includes the replusive energy between the nuclei and the energy shift of the frozen orbitals. """ driver = PySCFDriver(atom="H .0 .0 .0; H .0 .0 " + str(dist), unit=UnitsType.ANGSTROM, charge=0, spin=0, basis='sto3g', ) molecule = driver.run() repulsion_energy = molecule.nuclear_repulsion_energy num_particles = molecule.num_alpha + molecule.num_beta num_spin_orbitals = molecule.num_orbitals * 2 ferOp = FermionicOperator(h1=molecule.one_body_integrals, h2=molecule.two_body_integrals) qubitOp = ferOp.mapping(map_type='parity', threshold=1E-8) qubitOp = Z2Symmetries.two_qubit_reduction(qubitOp,num_particles) shift = repulsion_energy return qubitOp, shift
def get_qubit_op(dist): #atom="Li .0 .0 .0; H .0 .0 " + str(dist) #atom="Be .0 .0 .0; H .0 .0 -" + str(dist) + "; H .0 .0 " + str(dist) driver = PySCFDriver("Li .0 .0 .0; H .0 .0 " + str(dist), unit=UnitsType.ANGSTROM, charge=0, spin=0, basis='sto3g') molecule = driver.run() freeze_list = [0] remove_list = [-3, -2] repulsion_energy = molecule.nuclear_repulsion_energy num_particles = molecule.num_alpha + molecule.num_beta num_spin_orbitals = molecule.num_orbitals * 2 remove_list = [x % molecule.num_orbitals for x in remove_list] freeze_list = [x % molecule.num_orbitals for x in freeze_list] remove_list = [x - len(freeze_list) for x in remove_list] remove_list += [ x + molecule.num_orbitals - len(freeze_list) for x in remove_list ] freeze_list += [x + molecule.num_orbitals for x in freeze_list] ferOp = FermionicOperator(h1=molecule.one_body_integrals, h2=molecule.two_body_integrals) ferOp, energy_shift = ferOp.fermion_mode_freezing(freeze_list) num_spin_orbitals -= len(freeze_list) num_particles -= len(freeze_list) ferOp = ferOp.fermion_mode_elimination(remove_list) num_spin_orbitals -= len(remove_list) qubitOp = ferOp.mapping(map_type='parity', threshold=0.00000001) qubitOp = Z2Symmetries.two_qubit_reduction(qubitOp, num_particles) shift = energy_shift + repulsion_energy return qubitOp, num_particles, num_spin_orbitals, shift
def test_qpe(self, distance): """ qpe test """ self.log.debug('Testing End-to-End with QPE on ' 'H2 with inter-atomic distance %s.', distance) try: driver = PySCFDriver(atom='H .0 .0 .0; H .0 .0 {}'.format(distance), unit=UnitsType.ANGSTROM, charge=0, spin=0, basis='sto3g') except QiskitChemistryError: self.skipTest('PYSCF driver does not appear to be installed') molecule = driver.run() qubit_mapping = 'parity' fer_op = FermionicOperator( h1=molecule.one_body_integrals, h2=molecule.two_body_integrals) qubit_op = fer_op.mapping(map_type=qubit_mapping, threshold=1e-10) qubit_op = Z2Symmetries.two_qubit_reduction(qubit_op, 2) exact_eigensolver = ExactEigensolver(qubit_op, k=1) results = exact_eigensolver.run() reference_energy = results['energy'] self.log.debug('The exact ground state energy is: %s', results['energy']) num_particles = molecule.num_alpha + molecule.num_beta two_qubit_reduction = True num_orbitals = qubit_op.num_qubits + \ (2 if two_qubit_reduction else 0) num_time_slices = 1 n_ancillae = 6 state_in = HartreeFock(qubit_op.num_qubits, num_orbitals, num_particles, qubit_mapping, two_qubit_reduction) iqft = Standard(n_ancillae) qpe = QPE(qubit_op, state_in, iqft, num_time_slices, n_ancillae, expansion_mode='suzuki', expansion_order=2, shallow_circuit_concat=True) backend = qiskit.BasicAer.get_backend('qasm_simulator') quantum_instance = QuantumInstance(backend, shots=100) result = qpe.run(quantum_instance) self.log.debug('eigvals: %s', result['eigvals']) self.log.debug('top result str label: %s', result['top_measurement_label']) self.log.debug('top result in decimal: %s', result['top_measurement_decimal']) self.log.debug('stretch: %s', result['stretch']) self.log.debug('translation: %s', result['translation']) self.log.debug('final energy from QPE: %s', result['energy']) self.log.debug('reference energy: %s', reference_energy) self.log.debug('ref energy (transformed): %s', (reference_energy + result['translation']) * result['stretch']) self.log.debug('ref binary str label: %s', decimal_to_binary( (reference_energy + result['translation']) * result['stretch'], max_num_digits=n_ancillae + 3, fractional_part_only=True)) np.testing.assert_approx_equal(result['energy'], reference_energy, significant=2)
def __init__(self, operator, num_orbitals, num_particles, qubit_mapping=None, two_qubit_reduction=False, active_occupied=None, active_unoccupied=None, is_eom_matrix_symmetric=True, se_list=None, de_list=None, z2_symmetries=None, untapered_op=None): """Constructor. Args: operator (WeightedPauliOperator): qubit operator num_orbitals (int): total number of spin orbitals num_particles (Union(list, int)): number of particles, if it is a list, the first number is alpha and the second number if beta. qubit_mapping (str): qubit mapping type two_qubit_reduction (bool): two qubit reduction is applied or not active_occupied (list): list of occupied orbitals to include, indices are 0 to n where n is num particles // 2 active_unoccupied (list): list of unoccupied orbitals to include, indices are 0 to m where m is (num_orbitals - num particles) // 2 is_eom_matrix_symmetric (bool): is EoM matrix symmetric se_list (list[list]): single excitation list, overwrite the setting in active space de_list (list[list]): double excitation list, overwrite the setting in active space z2_symmetries (Z2Symmetries): represent the Z2 symmetries untapered_op (WeightedPauliOperator): if the operator is tapered, we need untapered operator to build element of EoM matrix """ self._operator = operator self._num_orbitals = num_orbitals self._num_particles = num_particles self._qubit_mapping = qubit_mapping self._two_qubit_reduction = two_qubit_reduction self._active_occupied = active_occupied self._active_unoccupied = active_unoccupied se_list_default, de_list_default = UCCSD.compute_excitation_lists( self._num_particles, self._num_orbitals, self._active_occupied, self._active_unoccupied) if se_list is None: self._se_list = se_list_default else: self._se_list = se_list logger.info("Use user-specified single excitation list: %s", self._se_list) if de_list is None: self._de_list = de_list_default else: self._de_list = de_list logger.info("Use user-specified double excitation list: %s", self._de_list) self._z2_symmetries = z2_symmetries if z2_symmetries is not None \ else Z2Symmetries([], [], []) self._untapered_op = untapered_op if untapered_op is not None else operator self._is_eom_matrix_symmetric = is_eom_matrix_symmetric
def test_readme_sample(self): """ readme sample test """ # pylint: disable=import-outside-toplevel # --- Exact copy of sample code ---------------------------------------- from qiskit.chemistry import FermionicOperator from qiskit.chemistry.drivers import PySCFDriver, UnitsType from qiskit.aqua.operators import Z2Symmetries # Use PySCF, a classical computational chemistry software # package, to compute the one-body and two-body integrals in # molecular-orbital basis, necessary to form the Fermionic operator driver = PySCFDriver(atom='H .0 .0 .0; H .0 .0 0.735', unit=UnitsType.ANGSTROM, basis='sto3g') molecule = driver.run() num_particles = molecule.num_alpha + molecule.num_beta num_spin_orbitals = molecule.num_orbitals * 2 # Build the qubit operator, which is the input to the VQE algorithm in Aqua ferm_op = FermionicOperator(h1=molecule.one_body_integrals, h2=molecule.two_body_integrals) map_type = 'PARITY' qubit_op = ferm_op.mapping(map_type) qubit_op = Z2Symmetries.two_qubit_reduction(qubit_op, num_particles) num_qubits = qubit_op.num_qubits # setup a classical optimizer for VQE from qiskit.aqua.components.optimizers import L_BFGS_B optimizer = L_BFGS_B() # setup the initial state for the variational form from qiskit.chemistry.components.initial_states import HartreeFock init_state = HartreeFock(num_qubits, num_spin_orbitals, num_particles) # setup the variational form for VQE from qiskit.aqua.components.variational_forms import RYRZ var_form = RYRZ(num_qubits, initial_state=init_state) # setup and run VQE from qiskit.aqua.algorithms import VQE algorithm = VQE(qubit_op, var_form, optimizer) # set the backend for the quantum computation from qiskit import Aer backend = Aer.get_backend('statevector_simulator') result = algorithm.run(backend) print(result['energy']) # ---------------------------------------------------------------------- self.assertAlmostEqual(result['energy'], -1.8572750301938803, places=6)
def setUp(self): super().setUp() try: self.molecule = "H 0.000000 0.000000 0.735000;H 0.000000 0.000000 0.000000" self.driver = PySCFDriver(atom=self.molecule, unit=UnitsType.ANGSTROM, charge=0, spin=0, basis='631g') self.qmolecule = self.driver.run() self.core = Hamiltonian(transformation=TransformationType.FULL, qubit_mapping=QubitMappingType.PARITY, two_qubit_reduction=True, freeze_core=True, orbital_reduction=[]) self.qubit_op, _ = self.core.run(self.qmolecule) z2_symmetries = Z2Symmetries.find_Z2_symmetries(self.qubit_op) tapered_ops = z2_symmetries.taper(self.qubit_op) smallest_eig_value = 99999999999999 smallest_idx = -1 for idx, _ in enumerate(tapered_ops): ee = ExactEigensolver(tapered_ops[idx], k=1) curr_value = ee.run()['energy'] if curr_value < smallest_eig_value: smallest_eig_value = curr_value smallest_idx = idx self.the_tapered_op = tapered_ops[smallest_idx] self.reference_energy_pUCCD = -1.1434447924298028 self.reference_energy_UCCD0 = -1.1476045878481704 self.reference_energy_UCCD0full = -1.1515491334334347 # reference energy of UCCSD/VQE with tapering everywhere self.reference_energy_UCCSD = -1.1516142309717594 # reference energy of UCCSD/VQE when no tapering on excitations is used self.reference_energy_UCCSD_no_tap_exc = -1.1516142309717594 # excitations for succ self.reference_singlet_double_excitations = [[0, 1, 4, 5], [0, 1, 4, 6], [0, 1, 4, 7], [0, 2, 4, 6], [0, 2, 4, 7], [0, 3, 4, 7]] # groups for succ_full self.reference_singlet_groups = [[[0, 1, 4, 5]], [[0, 1, 4, 6], [0, 2, 4, 5]], [[0, 1, 4, 7], [0, 3, 4, 5]], [[0, 2, 4, 6]], [[0, 2, 4, 7], [0, 3, 4, 6]], [[0, 3, 4, 7]]] except QiskitChemistryError: self.skipTest('PYSCF driver does not appear to be installed')
def get_LiH_qubit_op(dist): """ Use the qiskit chemistry package to get the qubit Hamiltonian for LiH Parameters ---------- dist : float The nuclear separations Returns ------- qubitOp : qiskit.aqua.operators.WeightedPauliOperator Qiskit representation of the qubit Hamiltonian shift : float The ground state of the qubit Hamiltonian needs to be corrected by this amount of energy to give the real physical energy. This includes the replusive energy between the nuclei and the energy shift of the frozen orbitals. """ driver = PySCFDriver( atom="Li .0 .0 .0; H .0 .0 " + str(dist), unit=UnitsType.ANGSTROM, charge=0, spin=0, basis='sto3g', ) molecule = driver.run() freeze_list = [0] remove_list = [-3, -2] repulsion_energy = molecule.nuclear_repulsion_energy num_particles = molecule.num_alpha + molecule.num_beta num_spin_orbitals = molecule.num_orbitals * 2 remove_list = [x % molecule.num_orbitals for x in remove_list] freeze_list = [x % molecule.num_orbitals for x in freeze_list] remove_list = [x - len(freeze_list) for x in remove_list] remove_list += [ x + molecule.num_orbitals - len(freeze_list) for x in remove_list ] freeze_list += [x + molecule.num_orbitals for x in freeze_list] ferOp = FermionicOperator(h1=molecule.one_body_integrals, h2=molecule.two_body_integrals) ferOp, energy_shift = ferOp.fermion_mode_freezing(freeze_list) num_spin_orbitals -= len(freeze_list) num_particles -= len(freeze_list) ferOp = ferOp.fermion_mode_elimination(remove_list) num_spin_orbitals -= len(remove_list) qubitOp = ferOp.mapping(map_type='parity', threshold=1E-8) #qubitOp = qubitOp.two_qubit_reduced_operator(num_particles) qubitOp = Z2Symmetries.two_qubit_reduction(qubitOp, num_particles) shift = repulsion_energy + energy_shift return qubitOp, shift
def test_h2_one_qubit_statevector(self): """Test H2 with tapering and statevector backend.""" two_qubit_reduction = True qubit_mapping = 'parity' warnings.filterwarnings('ignore', category=DeprecationWarning) core = Hamiltonian(transformation=TransformationType.FULL, qubit_mapping=QubitMappingType.PARITY, two_qubit_reduction=two_qubit_reduction, freeze_core=False, orbital_reduction=[]) warnings.filterwarnings('always', category=DeprecationWarning) qubit_op, _ = core.run(self.molecule) num_orbitals = core.molecule_info['num_orbitals'] num_particles = core.molecule_info['num_particles'] # tapering z2_symmetries = Z2Symmetries.find_Z2_symmetries(qubit_op) # know the sector tapered_op = z2_symmetries.taper(qubit_op)[1] initial_state = HartreeFock(num_orbitals=num_orbitals, num_particles=num_particles, qubit_mapping=qubit_mapping, two_qubit_reduction=two_qubit_reduction, sq_list=tapered_op.z2_symmetries.sq_list) var_form = UCCSD(num_orbitals=num_orbitals, num_particles=num_particles, initial_state=initial_state, qubit_mapping=qubit_mapping, two_qubit_reduction=two_qubit_reduction, z2_symmetries=tapered_op.z2_symmetries) optimizer = SPSA(maxiter=50) eom_vqe = QEomVQE(tapered_op, var_form, optimizer, num_orbitals=num_orbitals, num_particles=num_particles, qubit_mapping=qubit_mapping, two_qubit_reduction=two_qubit_reduction, z2_symmetries=tapered_op.z2_symmetries, untapered_op=qubit_op) backend = BasicAer.get_backend('statevector_simulator') quantum_instance = QuantumInstance(backend) result = eom_vqe.run(quantum_instance) np.testing.assert_array_almost_equal(self.reference, result['energies'], decimal=5)
def get_qubit_op(dist): driver = PySCFDriver(atom="H .0 .0 .0; H .0 .0 " + str(dist), unit=UnitsType.ANGSTROM, charge=0, spin=0, basis='sto3g') molecule = driver.run() nuc_energy = molecule.nuclear_repulsion_energy num_particles = molecule.num_alpha + molecule.num_beta num_spin_orbitals = molecule.num_orbitals * 2 ferOp = FermionicOperator(h1=molecule.one_body_integrals, h2=molecule.two_body_integrals) qubitOp = ferOp.mapping(map_type='parity', threshold=0.00000001) qubitOp = Z2Symmetries.two_qubit_reduction(qubitOp, num_particles) return qubitOp, num_particles, num_spin_orbitals, nuc_energy
def load_qubitop_for_molecule(molecule_data): atom_list = [a[0] + ' ' + " ".join([str(elem) for elem in a[1]]) for a in molecule_data['geometry']] atom = "; ".join(atom_list) #atom = 'Li .0 .0 .0; H .0 .0 3.9' basis = molecule_data['basis'] transform = molecule_data['transform'] electrons = molecule_data['electrons'] active = molecule_data['active_orbitals'] driver = PySCFDriver(atom=atom, unit=UnitsType.ANGSTROM, basis=basis, charge=0, spin=0) molecule = driver.run() num_particles = molecule.num_alpha + molecule.num_beta num_spin_orbitals = molecule.num_orbitals * 2 #print("# of electrons: {}".format(num_particles)) #print("# of spin orbitals: {}".format(num_spin_orbitals)) freeze_list = [x for x in range(int(active/2), int(num_particles/2))] remove_list = [-x for x in range(active,molecule.num_orbitals-int(num_particles/2)+int(active/2))] #print(freeze_list) #print(remove_list) if transform == 'BK': map_type = 'bravyi_kitaev' elif transform == 'JW': map_type = 'jordan_wigner' else: map_type = 'parity' remove_list = [x % molecule.num_orbitals for x in remove_list] freeze_list = [x % molecule.num_orbitals for x in freeze_list] remove_list = [x - len(freeze_list) for x in remove_list] remove_list += [x + molecule.num_orbitals - len(freeze_list) for x in remove_list] freeze_list += [x + molecule.num_orbitals for x in freeze_list] fermiOp = FermionicOperator(h1=molecule.one_body_integrals, h2=molecule.two_body_integrals) energy_shift = 0 if len(freeze_list) > 0: fermiOp, energy_shift = fermiOp.fermion_mode_freezing(freeze_list) num_spin_orbitals -= len(freeze_list) num_particles -= len(freeze_list) if len(remove_list) > 0: fermiOp = fermiOp.fermion_mode_elimination(remove_list) num_spin_orbitals -= len(remove_list) qubitOp = fermiOp.mapping(map_type=map_type, threshold=0.00000001) if len(freeze_list) > 0 or len(remove_list) >0: qubitOp = Z2Symmetries.two_qubit_reduction(qubitOp, num_particles) #print(qubitOp.print_operators()) num_spin_orbitals= qubitOp.num_qubits return molecule, qubitOp, map_type, num_particles, num_spin_orbitals
def h2(dist=0.75): mol = PySCFDriver(atom= 'H 0.0 0.0 0.0;'\ 'H 0.0 0.0 {}'.format(dist), unit=UnitsType.ANGSTROM, charge=0, spin=0, basis='sto-3g') mol = mol.run() h1 = mol.one_body_integrals h2 = mol.two_body_integrals nuclear_repulsion_energy = mol.nuclear_repulsion_energy num_particles = mol.num_alpha + mol.num_beta + 0 ferOp = FermionicOperator(h1=h1, h2=h2) qubitOp = ferOp.mapping(map_type='parity', threshold=0.00000001) qubitOp = Z2Symmetries.two_qubit_reduction(qubitOp, num_particles) cHam = op_converter.to_matrix_operator(qubitOp) cHam = cHam.dense_matrix + nuclear_repulsion_energy * numpy.identity(4) return cHam
def _pick_sector(z2_symmetries: Z2Symmetries, hf_str: np.ndarray) -> Z2Symmetries: """ Based on Hartree-Fock bit string and found symmetries to determine the sector. The input z2 symmetries will be mutated with the determined tapering values. Args: z2_symmetries: the z2 symmetries object. hf_str: Hartree-Fock bit string (the last index is for qubit 0). Returns: the original z2 symmetries filled with the correct tapering values. """ # Finding all the symmetries using the find_Z2_symmetries: taper_coef = [] for sym in z2_symmetries.symmetries: # pylint: disable=no-member coef = -1 if np.logical_xor.reduce(np.logical_and(sym.z[::-1], hf_str)) else 1 taper_coef.append(coef) z2_symmetries.tapering_values = taper_coef return z2_symmetries
def _map_fermionic_operator_to_qubit(fer_op: FermionicOperator, qubit_mapping: str, num_particles: List[int], two_qubit_reduction: bool) -> WeightedPauliOperator: """ Args: fer_op: Fermionic Operator qubit_mapping: fermionic to qubit mapping num_particles: number of particles two_qubit_reduction: two qubit reduction Returns: qubit operator """ qubit_op = fer_op.mapping(map_type=qubit_mapping, threshold=0.00000001) if qubit_mapping == 'parity' and two_qubit_reduction: qubit_op = Z2Symmetries.two_qubit_reduction(qubit_op, num_particles) return qubit_op
def setUp(self): super().setUp() try: driver = PySCFDriver(atom='Li .0 .0 .0; H .0 .0 1.6', unit=UnitsType.ANGSTROM, charge=0, spin=0, basis='sto3g') except QiskitChemistryError: self.skipTest('PYSCF driver does not appear to be installed') self.qmolecule = driver.run() self.core = Hamiltonian(transformation=TransformationType.FULL, qubit_mapping=QubitMappingType.PARITY, two_qubit_reduction=True, freeze_core=True, orbital_reduction=[]) self.qubit_op, _ = self.core.run(self.qmolecule) self.z2_symmetries = Z2Symmetries.find_Z2_symmetries(self.qubit_op) self.reference_energy = -7.882096489442
def run(self, qmolecule): logger.debug('Processing started...') # Save these values for later combination with the quantum computation result self._hf_energy = qmolecule.hf_energy self._nuclear_repulsion_energy = qmolecule.nuclear_repulsion_energy self._nuclear_dipole_moment = qmolecule.nuclear_dipole_moment self._reverse_dipole_sign = qmolecule.reverse_dipole_sign core_list = qmolecule.core_orbitals if self._freeze_core else [] reduce_list = self._orbital_reduction if self._freeze_core: logger.info("Freeze_core specified. Core orbitals to be frozen: %s", core_list) if reduce_list: logger.info("Configured orbital reduction list: %s", reduce_list) reduce_list = [x + qmolecule.num_orbitals if x < 0 else x for x in reduce_list] freeze_list = [] remove_list = [] # Orbitals are specified by their index from 0 to n-1, where n is the number of orbitals the # molecule has. The combined list of the core orbitals, when freeze_core is true, with any # user supplied orbitals is what will be used. Negative numbers may be used to indicate the # upper virtual orbitals, so -1 is the highest, then -2 etc. and these will # be converted to the # positive 0-based index for computation. # In the combined list any orbitals that are occupied are added to a freeze list and an # energy is stored from these orbitals to be added later. # Unoccupied orbitals are just discarded. # Because freeze and eliminate is done in separate steps, # with freeze first, we have to re-base # the indexes for elimination according to how many orbitals were removed when freezing. # orbitals_list = list(set(core_list + reduce_list)) num_alpha = qmolecule.num_alpha num_beta = qmolecule.num_beta new_num_alpha = num_alpha new_num_beta = num_beta if orbitals_list: orbitals_list = np.array(orbitals_list) orbitals_list = \ orbitals_list[(orbitals_list >= 0) & (orbitals_list < qmolecule.num_orbitals)] freeze_list_alpha = [i for i in orbitals_list if i < num_alpha] freeze_list_beta = [i for i in orbitals_list if i < num_beta] freeze_list = np.append(freeze_list_alpha, [i + qmolecule.num_orbitals for i in freeze_list_beta]) remove_list_alpha = [i for i in orbitals_list if i >= num_alpha] remove_list_beta = [i for i in orbitals_list if i >= num_beta] rla_adjust = -len(freeze_list_alpha) rlb_adjust = -len(freeze_list_alpha) - len(freeze_list_beta) + qmolecule.num_orbitals remove_list = np.append([i + rla_adjust for i in remove_list_alpha], [i + rlb_adjust for i in remove_list_beta]) logger.info("Combined orbital reduction list: %s", orbitals_list) logger.info(" converting to spin orbital reduction list: %s", np.append(np.array(orbitals_list), np.array(orbitals_list) + qmolecule.num_orbitals)) logger.info(" => freezing spin orbitals: %s", freeze_list) logger.info(" => removing spin orbitals: %s (indexes accounting for freeze %s)", np.append(remove_list_alpha, np.array(remove_list_beta) + qmolecule.num_orbitals), remove_list) new_num_alpha -= len(freeze_list_alpha) new_num_beta -= len(freeze_list_beta) new_nel = [new_num_alpha, new_num_beta] fer_op = FermionicOperator(h1=qmolecule.one_body_integrals, h2=qmolecule.two_body_integrals) fer_op, self._energy_shift, did_shift = \ Hamiltonian._try_reduce_fermionic_operator(fer_op, freeze_list, remove_list) if did_shift: logger.info("Frozen orbital energy shift: %s", self._energy_shift) if self._transformation == TransformationType.PARTICLE_HOLE.value: fer_op, ph_shift = fer_op.particle_hole_transformation(new_nel) self._ph_energy_shift = -ph_shift logger.info("Particle hole energy shift: %s", self._ph_energy_shift) logger.debug('Converting to qubit using %s mapping', self._qubit_mapping) qubit_op = Hamiltonian._map_fermionic_operator_to_qubit(fer_op, self._qubit_mapping, new_nel, self._two_qubit_reduction) qubit_op.name = 'Electronic Hamiltonian' logger.debug(' num paulis: %s, num qubits: %s', len(qubit_op.paulis), qubit_op.num_qubits) aux_ops = [] def _add_aux_op(aux_op, name): aux_qop = Hamiltonian._map_fermionic_operator_to_qubit(aux_op, self._qubit_mapping, new_nel, self._two_qubit_reduction) aux_qop.name = name aux_ops.append(aux_qop) logger.debug(' num paulis: %s', aux_qop.paulis) logger.debug('Creating aux op for Number of Particles') _add_aux_op(fer_op.total_particle_number(), 'Number of Particles') logger.debug('Creating aux op for S^2') _add_aux_op(fer_op.total_angular_momentum(), 'S^2') logger.debug('Creating aux op for Magnetization') _add_aux_op(fer_op.total_magnetization(), 'Magnetization') if qmolecule.has_dipole_integrals(): def _dipole_op(dipole_integrals, axis): logger.debug('Creating aux op for dipole %s', axis) fer_op_ = FermionicOperator(h1=dipole_integrals) fer_op_, shift, did_shift_ = self._try_reduce_fermionic_operator(fer_op_, freeze_list, remove_list) if did_shift_: logger.info("Frozen orbital %s dipole shift: %s", axis, shift) ph_shift_ = 0.0 if self._transformation == TransformationType.PARTICLE_HOLE.value: fer_op_, ph_shift_ = fer_op_.particle_hole_transformation(new_nel) ph_shift_ = -ph_shift_ logger.info("Particle hole %s dipole shift: %s", axis, ph_shift_) qubit_op_ = self._map_fermionic_operator_to_qubit(fer_op_, self._qubit_mapping, new_nel, self._two_qubit_reduction) qubit_op_.name = 'Dipole ' + axis logger.debug(' num paulis: %s', len(qubit_op_.paulis)) return qubit_op_, shift, ph_shift_ op_dipole_x, self._x_dipole_shift, self._ph_x_dipole_shift = \ _dipole_op(qmolecule.x_dipole_integrals, 'x') op_dipole_y, self._y_dipole_shift, self._ph_y_dipole_shift = \ _dipole_op(qmolecule.y_dipole_integrals, 'y') op_dipole_z, self._z_dipole_shift, self._ph_z_dipole_shift = \ _dipole_op(qmolecule.z_dipole_integrals, 'z') aux_ops.append(op_dipole_x) aux_ops.append(op_dipole_y) aux_ops.append(op_dipole_z) logger.info('Molecule num electrons: %s, remaining for processing: %s', [num_alpha, num_beta], new_nel) nspinorbs = qmolecule.num_orbitals * 2 new_nspinorbs = nspinorbs - len(freeze_list) - len(remove_list) logger.info('Molecule num spin orbitals: %s, remaining for processing: %s', nspinorbs, new_nspinorbs) self._add_molecule_info(self.INFO_NUM_PARTICLES, [new_num_alpha, new_num_beta]) self._add_molecule_info(self.INFO_NUM_ORBITALS, new_nspinorbs) self._add_molecule_info(self.INFO_TWO_QUBIT_REDUCTION, self._two_qubit_reduction if self._qubit_mapping == 'parity' else False) z2symmetries = Z2Symmetries([], [], [], None) if self._z2symmetry_reduction is not None: logger.debug('Processing z2 symmetries') qubit_op, aux_ops, z2symmetries = self._process_z2symmetry_reduction(qubit_op, aux_ops) self._add_molecule_info(self.INFO_Z2SYMMETRIES, z2symmetries) logger.debug('Processing complete ready to run algorithm') return qubit_op, aux_ops
def __init__(self, num_qubits: int, depth: int, num_orbitals: int, num_particles: Union[List[int], int], active_occupied: Optional[List[int]] = None, active_unoccupied: Optional[List[int]] = None, initial_state: Optional[InitialState] = None, qubit_mapping: str = 'parity', two_qubit_reduction: bool = True, num_time_slices: int = 1, shallow_circuit_concat: bool = True, z2_symmetries: Optional[Z2Symmetries] = None, method_singles: str = 'both', method_doubles: str = 'ucc', excitation_type: str = 'sd', same_spin_doubles: bool = True, skip_commute_test: bool = False) -> None: """Constructor. Args: num_qubits: number of qubits, has a min. value of 1. depth: number of replica of basic module, has a min. value of 1. num_orbitals: number of spin orbitals, has a min. value of 1. num_particles: number of particles, if it is a list, the first number is alpha and the second number if beta. active_occupied: list of occupied orbitals to consider as active space. active_unoccupied: list of unoccupied orbitals to consider as active space. initial_state: An initial state object. qubit_mapping: qubit mapping type. two_qubit_reduction: two qubit reduction is applied or not. num_time_slices: parameters for dynamics, has a min. value of 1. shallow_circuit_concat: indicate whether to use shallow (cheap) mode for circuit concatenation. z2_symmetries: represent the Z2 symmetries, including symmetries, sq_paulis, sq_list, tapering_values, and cliffords. method_singles: specify the single excitation considered. 'alpha', 'beta', 'both' only alpha or beta spin-orbital single excitations or both (all of them). method_doubles: specify the single excitation considered. 'ucc' (conventional ucc), succ (singlet ucc), succ_full (singlet ucc full), pucc (pair ucc). excitation_type: specify the excitation type 'sd', 's', 'd' respectively for single and double, only single, only double excitations. same_spin_doubles: enable double excitations of the same spin. skip_commute_test: when tapering excitation operators we test and exclude any that do not commute with symmetries. This test can be skipped to include all tapered excitation operators whether they commute or not. Raises: ValueError: Computed qubits do not match actual value """ validate_min('num_qubits', num_qubits, 1) validate_min('depth', depth, 1) validate_min('num_orbitals', num_orbitals, 1) if isinstance(num_particles, list) and len(num_particles) != 2: raise ValueError( 'Num particles value {}. Number of values allowed is 2'.format( num_particles)) validate_in_set('qubit_mapping', qubit_mapping, {'jordan_wigner', 'parity', 'bravyi_kitaev'}) validate_min('num_time_slices', num_time_slices, 1) validate_in_set('method_singles', method_singles, {'both', 'alpha', 'beta'}) validate_in_set('method_doubles', method_doubles, {'ucc', 'pucc', 'succ', 'succ_full'}) validate_in_set('excitation_type', excitation_type, {'sd', 's', 'd'}) super().__init__() self._z2_symmetries = Z2Symmetries([], [], [], []) \ if z2_symmetries is None else z2_symmetries self._num_qubits = num_orbitals if not two_qubit_reduction else num_orbitals - 2 self._num_qubits = self._num_qubits if self._z2_symmetries.is_empty() \ else self._num_qubits - len(self._z2_symmetries.sq_list) if self._num_qubits != num_qubits: raise ValueError( 'Computed num qubits {} does not match actual {}'.format( self._num_qubits, num_qubits)) self._depth = depth self._num_orbitals = num_orbitals if isinstance(num_particles, list): self._num_alpha = num_particles[0] self._num_beta = num_particles[1] else: logger.info( "We assume that the number of alphas and betas are the same.") self._num_alpha = num_particles // 2 self._num_beta = num_particles // 2 self._num_particles = [self._num_alpha, self._num_beta] if sum(self._num_particles) > self._num_orbitals: raise ValueError( '# of particles must be less than or equal to # of orbitals.') self._initial_state = initial_state self._qubit_mapping = qubit_mapping self._two_qubit_reduction = two_qubit_reduction self._num_time_slices = num_time_slices self._shallow_circuit_concat = shallow_circuit_concat # advanced parameters self._method_singles = method_singles self._method_doubles = method_doubles self._excitation_type = excitation_type self.same_spin_doubles = same_spin_doubles self._skip_commute_test = skip_commute_test self._single_excitations, self._double_excitations = \ UCCSD.compute_excitation_lists([self._num_alpha, self._num_beta], self._num_orbitals, active_occupied, active_unoccupied, same_spin_doubles=self.same_spin_doubles, method_singles=self._method_singles, method_doubles=self._method_doubles, excitation_type=self._excitation_type,) self._hopping_ops, self._num_parameters = self._build_hopping_operators( ) self._excitation_pool = None self._bounds = [(-np.pi, np.pi) for _ in range(self._num_parameters)] self._logging_construct_circuit = True self._support_parameterized_circuit = True self.uccd_singlet = False if self._method_doubles == 'succ_full': self.uccd_singlet = True self._single_excitations, self._double_excitations = \ UCCSD.compute_excitation_lists([self._num_alpha, self._num_beta], self._num_orbitals, active_occupied, active_unoccupied, same_spin_doubles=self.same_spin_doubles, method_singles=self._method_singles, method_doubles=self._method_doubles, excitation_type=self._excitation_type, ) if self.uccd_singlet: self._hopping_ops, _ = self._build_hopping_operators() else: self._hopping_ops, self._num_parameters = self._build_hopping_operators( ) self._bounds = [(-np.pi, np.pi) for _ in range(self._num_parameters)] if self.uccd_singlet: self._double_excitations_grouped = \ UCCSD.compute_excitation_lists_singlet(self._double_excitations, num_orbitals) self.num_groups = len(self._double_excitations_grouped) logging.debug('Grouped double excitations for singlet ucc') logging.debug(self._double_excitations_grouped) self._num_parameters = self.num_groups self._bounds = [(-np.pi, np.pi) for _ in range(self.num_groups)] # this will order the hopping operators self.labeled_double_excitations = [] for i in range(len(self._double_excitations)): self.labeled_double_excitations.append( (self._double_excitations[i], i)) order_hopping_op = UCCSD.order_labels_for_hopping_ops( self._double_excitations, self._double_excitations_grouped) logging.debug('New order for hopping ops') logging.debug(order_hopping_op) self._hopping_ops_doubles_temp = [] self._hopping_ops_doubles = self._hopping_ops[ len(self._single_excitations):] for i in order_hopping_op: self._hopping_ops_doubles_temp.append( self._hopping_ops_doubles[i]) self._hopping_ops[len(self._single_excitations ):] = self._hopping_ops_doubles_temp self._logging_construct_circuit = True
def _build_hopping_operator(index, num_orbitals, num_particles, qubit_mapping, two_qubit_reduction, z2_symmetries, skip_commute_test=False): """ Builds a hopping operator given the list of indices (index) that is a single or a double excitation. Args: index (list): a single or double excitation (e.g. double excitation [0,1,2,3] for a 4 spin-orbital system) num_orbitals (int): number of spin-orbitals num_particles (int): number of electrons qubit_mapping (str): qubit mapping type two_qubit_reduction (bool): reduce the number of qubits by 2 if parity qubit mapping is used z2_symmetries (Z2Symmetries): class that contains the symmetries of hamiltonian for tapering skip_commute_test (bool): when tapering excitation operators we test and exclude any that do not commute with symmetries. This test can be skipped to include all tapered excitation operators whether they commute or not. Returns: WeightedPauliOperator: qubit_op list: index """ h_1 = np.zeros((num_orbitals, num_orbitals)) h_2 = np.zeros( (num_orbitals, num_orbitals, num_orbitals, num_orbitals)) if len(index) == 2: i, j = index h_1[i, j] = 1.0 h_1[j, i] = -1.0 elif len(index) == 4: i, j, k, m = index h_2[i, j, k, m] = 1.0 h_2[m, k, j, i] = -1.0 dummpy_fer_op = FermionicOperator(h1=h_1, h2=h_2) qubit_op = dummpy_fer_op.mapping(qubit_mapping) if two_qubit_reduction: qubit_op = Z2Symmetries.two_qubit_reduction( qubit_op, num_particles) if not z2_symmetries.is_empty(): symm_commuting = True for symmetry in z2_symmetries.symmetries: symmetry_op = WeightedPauliOperator(paulis=[[1.0, symmetry]]) symm_commuting = qubit_op.commute_with(symmetry_op) if not symm_commuting: break if not skip_commute_test: qubit_op = z2_symmetries.taper( qubit_op) if symm_commuting else None else: qubit_op = z2_symmetries.taper(qubit_op) if qubit_op is None: logger.debug( 'Excitation (%s) is skipped since it is not commuted ' 'with symmetries', ','.join([str(x) for x in index])) return qubit_op, index
def _process_z2symmetry_reduction( self, qubit_op: WeightedPauliOperator, aux_ops: List[WeightedPauliOperator]) -> Tuple: """ Implement z2 symmetries in the qubit operator Args: qubit_op : qubit operator aux_ops: auxiliary operators Returns: (z2_qubit_op, z2_aux_ops, z2_symmetries) Raises: QiskitChemistryError: Invalid input """ z2_symmetries = Z2Symmetries.find_Z2_symmetries(qubit_op) if z2_symmetries.is_empty(): logger.debug('No Z2 symmetries found') z2_qubit_op = qubit_op z2_aux_ops = aux_ops z2_symmetries = Z2Symmetries([], [], [], None) else: logger.debug( '%s Z2 symmetries found: %s', len(z2_symmetries.symmetries), ','.join( [symm.to_label() for symm in z2_symmetries.symmetries])) # Check auxiliary operators commute with main operator's symmetry logger.debug('Checking operators commute with symmetry:') symmetry_ops = [] for symmetry in z2_symmetries.symmetries: symmetry_ops.append( WeightedPauliOperator(paulis=[[1.0, symmetry]])) commutes = FermionicTransformation._check_commutes( symmetry_ops, qubit_op) if not commutes: raise QiskitChemistryError( 'Z2 symmetry failure main operator must commute ' 'with symmetries found from it') for i, aux_op in enumerate(aux_ops): commutes = FermionicTransformation._check_commutes( symmetry_ops, aux_op) if not commutes: aux_ops[ i] = None # Discard since no meaningful measurement can be done if self._z2symmetry_reduction == 'auto': from ..circuit.library.initial_states.hartree_fock import hartree_fock_bitstring hf_bitstr = hartree_fock_bitstring( num_orbitals=self._molecule_info['num_orbitals'], qubit_mapping=self._qubit_mapping, two_qubit_reduction=self._two_qubit_reduction, num_particles=self._molecule_info['num_particles']) z2_symmetries = FermionicTransformation._pick_sector( z2_symmetries, hf_bitstr) else: if len(self._z2symmetry_reduction) != len( z2_symmetries.symmetries): raise QiskitChemistryError( 'z2symmetry_reduction tapering values list has ' 'invalid length {} should be {}'.format( len(self._z2symmetry_reduction), len(z2_symmetries.symmetries))) valid = np.all(np.isin(self._z2symmetry_reduction, [-1, 1])) if not valid: raise QiskitChemistryError( 'z2symmetry_reduction tapering values list must ' 'contain -1\'s and/or 1\'s only was {}'.format( self._z2symmetry_reduction, )) z2_symmetries.tapering_values = self._z2symmetry_reduction logger.debug('Apply symmetry with tapering values %s', z2_symmetries.tapering_values) chop_to = 0.00000001 # Use same threshold as qubit mapping to chop tapered operator z2_qubit_op = z2_symmetries.taper(qubit_op).chop(chop_to) z2_aux_ops = [] for aux_op in aux_ops: if aux_op is None: z2_aux_ops += [None] else: z2_aux_ops += [z2_symmetries.taper(aux_op).chop(chop_to)] return z2_qubit_op, z2_aux_ops, z2_symmetries