From 4c548a1197e05fce856601258ef6fb818e944dec Mon Sep 17 00:00:00 2001 From: Marc Coons Date: Wed, 16 Feb 2022 00:18:44 -0500 Subject: [PATCH 01/28] Add ILC ansatz class --- .../toolboxes/ansatz_generator/_qubit_ilc.py | 192 +++++++++++++ tangelo/toolboxes/ansatz_generator/ilc.py | 270 ++++++++++++++++++ tangelo/toolboxes/ansatz_generator/qmf.py | 2 +- 3 files changed, 463 insertions(+), 1 deletion(-) create mode 100644 tangelo/toolboxes/ansatz_generator/_qubit_ilc.py create mode 100755 tangelo/toolboxes/ansatz_generator/ilc.py diff --git a/tangelo/toolboxes/ansatz_generator/_qubit_ilc.py b/tangelo/toolboxes/ansatz_generator/_qubit_ilc.py new file mode 100644 index 000000000..be9d1ccd6 --- /dev/null +++ b/tangelo/toolboxes/ansatz_generator/_qubit_ilc.py @@ -0,0 +1,192 @@ +# Copyright 2021 Good Chemistry Company. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""This module implements a collection of functions related to the QMF +ansatz. + +Refs: + 1. R. A. Lang, I. G. Ryabinkin, and A. F. Izmaylov. + J. Chem. Theory Comput. 2021, 17, 1, 66–78. +""" + +import numpy as np +import scipy +from random import choice + +from tangelo.toolboxes.operators.operators import QubitOperator + +def construct_acs(dis_gens, max_ilc_gens, n_qubits): + idxs, ac_idxs, not_ac_idxs = [idx for idx in range(len(dis_gens))], [], [] + while max_ilc_gens > len(ac_idxs) and len(ac_idxs) + len(not_ac_idxs) < len(dis_gens): + idxs, ilc_gens = [idx for idx in range(len(dis_gen)) if idx not in not_ac_idxs], [] + n_idxs = len(idxs) + ng2, ngnq = n_idxs * (n_idxs + 1) // 2, n_qubits * n_idxs + A, Zflip, Zidx = np.zeros((ng2, ngnq+1)), [0.]*ngnq, 0 + + # Form the z vector of flip indices (Ref. 1 Appendix A) + for idx in idxs: + T_i = choice(dis_gens[idx][0]) if isinstance(dis_gens[idx], list) else dis_gens[idx] + for term, coeff in T_i.terms.items(): + for j in range(n_qubits): + if ((j, 'X') in term): + Zflip[Z_idx*n_qubits+j] = 1. + elif ((j, 'Y') in term): + Zflip[Z_idx*n_qubits+j] = 1. + Z_idx += 1 + Zflip = np.array(Zflip) + + # Form the matrix "A" in Ref. 1 Appendix B + ridx = 0 + for i in range(Z_idx): + A[ridx, i*n_qubits:(i+1)*n_qubits] = Zflip[i*n_qubits:(i+1)*n_qubits] + A[ridx, ngnq] = 1 + ridx += 1 + for j in range(i+1, Z_idx): + A[ridx, i*n_qubits:(i+1)*n_qubits] = Zflip[j*n_qubits:(j+1)*n_qubits] + A[ridx, j*n_qubits:(j+1)*n_qubits] = Zflip[i*n_qubits:(i+1)*n_qubits] + A[ridx, ngnq] = 1 + ridx += 1 + + # Solve Az = 1 + Zsln = GF2_GaussElim_Solve(A, ngnq) + + # Check for a bad solution + candidate_gens = [] + bad_sln = False + for i in range(Z_idx): + idx = '' + Nflip, Ny = 0, 0 + for j in range(n_qubits): + zdx = i*n_qubits + j + if (Zflip[zdx] == 1. and Zsln[zdx] == 0.): + idx += 'X' + str(j) + ' ' + Nflip += 1 + elif (Zflip[zdx] == 1. and Zsln[zdx] == 1.): + idx += 'Y' + str(j) + ' ' + Nflip += 1 + Ny += 1 + elif (Zflip[zdx] == 0. and Zsln[zdx] == 1.): + idx += 'Z' + str(j) + ' ' + if Nflip < 2 or Ny % 2 != 1: + bad_sln = True + bad_idx = idxs[i] + del idxs[i] + if bad_idx not in not_ac_idxs: + not_ac_idxs.append(bad_idx) + break + else: + Ti = QubitOperator(idx) + candidate_gens.append(Ti) + + # For good solutions update the list of anti-commuting generators + if not bad_sln: + for i, Ti in enumerate(candidate_gens): + anticommutes = True + Ti_idx = idxs[i] + if (ilc_gens == []): + ilc_gens.append(Ti) + if Ti_idx not in ac_idxs: + ac_idxs.append(Ti_idx) + if Ti_idx in not_ac_idxs: + not_ac_idxs.remove(Ti_idx) + else: + for Tj in ilc_gens: + TiTj_AC = Ti * Tj + Tj * Ti + if (TiTj_AC != QubitOperator.zero()): + if Ti_idx not in not_ac_idxs: + not_ac_idxs.append(Ti_idx) + anticommutes = False + if Ti_idx in idxs: + idxs.remove(Ti_idx) + if Ti_idx in ac_idxs: + ac_idxs.remove(Ti_idx) + if not anticommutes: + break + if anticommutes: + ilc_gens.append(Ti) + if Ti_idx not in ac_idxs: + ac_idxs.append(Ti_idx) + if Ti_idx in not_ac_idxs: + not_ac_idxs.remove(Ti_idx) + if not anticommutes: + break + return ilc_gens + +def GF2_GaussElim_Solve(A, Zdim): + # Implements a Gauss-Jordan solver over the binary field + NR, NC = np.shape(A)[0], np.shape(A)[1] + A, Zs, Zsln, piv_idx = np.array(A), [], [-1]*Zdim, 0 + for i in range(NC): + Aij_max = 0. + max_idx = piv_idx + for j in range(piv_idx, NR): + if (A[j, i] > Aij_max): + max_idx = j + Aij_max = A[j, i] + elif (j == NR-1 and Aij_max == 0.): + piv_idx = max_idx + Aij_max = -1. + if (Aij_max > 0.): + if (max_idx > piv_idx): + A[[piv_idx, max_idx]] = A[[max_idx, piv_idx]] + for j in range(piv_idx+1, NR): + if (A[j, i] == 1.): + A[j, i:NC] = np.fmod(A[j, i:NC] + A[piv_idx, i:NC], 2) + piv_idx += 1 + b = A[0:NR, NC-1].tolist() + for i in range(NR-1, -1, -1): + col_idx, Zf = -1., [] + for j in range(NC-1): + if (A[i, j] == 1.): + if (col_idx == -1): + col_idx = j + else: + Zf.append(j) + if (col_idx >= 0.): + Zs.append([col_idx, Zf, b[i]]) + for Z in (Zs): + b = Z[2] + for Zf in (Z[1]): + if (Zsln[Zf] == -1): + Zsln[Zf] = choice([0., 1.]) + b = (b + Zsln[Zf]) % 2 + Zsln[Z[0]] = b + return Zsln + +def init_ilc_by_diag(qubit_ham, ilc_gens, qmf_var_params): + ilc_gens.ins(0, QubitOperator.identity()) + n_var_params = len(ilc_gens) + H = np.zeros((n_var_params, n_var_params), dtype=complex) + S = np.ones((n_var_params, n_var_params), dtype=complex) + for i in range(n_var_params): + H_i = qubit_ham * ilc_gens[i] + H[i, i] = get_op_expval(ilc_gens[i] * H_i, qmf_var_params) + for j in range(i+1, n_var_params): + H[j, i] = get_op_expval(ilc_gens[j] * H_i, qmf_var_params) + S[j, i] = get_op_expval(ilc_gens[j] * ilc_gens[i], qmf_var_params) + if (i == 0): + H[j, i] *= 1.j + S[j, i] *= 1.j + E, c = scipy.linalg.eigh(a=np.matrix(H), b=np.matrix(S), lower=True, driver="gvd") + c0 = c[:, 0].real + denom_sum, ilc_var_params = 0., [] + for i in range(n_var_params): + c_i = c0[i] + denom_sum += pow(c_i.real, 2.) + pow(c_i.imag, 2.) + if i > 0: + beta = np.arcsin(c_i / np.sqrt(denom_sum)) + if i == 1 and c0[0].real < 0.: + beta = np.pi - beta + ilc_var_params.append(beta.real) + return ilc_var_params diff --git a/tangelo/toolboxes/ansatz_generator/ilc.py b/tangelo/toolboxes/ansatz_generator/ilc.py new file mode 100755 index 000000000..847ca4f6f --- /dev/null +++ b/tangelo/toolboxes/ansatz_generator/ilc.py @@ -0,0 +1,270 @@ +# Copyright 2021 Good Chemistry Company. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""This module defines the qubit coupled cluster ansatz class for involutory +linear combinations (ILC) of anti-commuting Pauli products. + +Refs: + 1. R. A. Lang, I. G. Ryabinkin, and A. F. Izmaylov. + J. Chem. Theory Comput. 2021, 17, 1, 66–78. +""" + +import warnings +import numpy as np + +from tangelo.toolboxes.operators.operators import QubitOperator +from tangelo.toolboxes.qubit_mappings.mapping_transform import get_qubit_number,\ + fermion_to_qubit_mapping +from tangelo.linq import Circuit +from .ansatz import Ansatz +from .ansatz_utils import exp_pauliword_to_gates +from ._qubit_mf import init_qmf_from_hf, get_qmf_circuit, purify_qmf_state +from ._qubit_ilc import construct_ACS, init_ilc_diag +from ._qubit_cc import construct_dis + + +class ILC(Ansatz): + """This class implements the ILC ansatz. Closed-shell and restricted open-shell ILC are + supported. While the form of the ILC ansatz is the same for either variation, the underlying + fermionic mean-field state is treated differently depending on the spin. Closed-shell + or restricted open-shell ILC implies that spin = 0 or spin != 0 and the fermionic mean-field + state is obtained using a RHF or ROHF Hamiltonian, respectively. + + Args: + molecule (SecondQuantizedMolecule): The molecular system. + mapping (str): One of the supported qubit mapping identifiers. Default, "JW". + up_then_down (bool): Change basis ordering putting all spin up orbitals first, + followed by all spin down. Default, False. + qubit_op_list (list of QubitOperator): A list of ILC generators to use for the ansatz. + Default, None. + qmf_circuit (Circuit): An instance of tangelo.linq Circuit class implementing a QMF state + preparation circuit. A variational circuit can be passed from the QMF ansatz class. + Otherwise a non-variational circuit is created by default. Default, None. + qmf_var_params (list or numpy array of float): The QMF variational parameter set. + If None, the values are determined using a Hartree-Fock reference state. Default, None. + qubit_mf_ham (QubitOperator): Allows a qubit Hamiltonian to be passed to the ILC ansatz + class. If not None, then the fermionic Hamiltonian is ignored. Default, None. + ilc_guess (float): Sets the initial guess for all amplitudes in the ILC variational + parameter set. Default, 1.e-1 a.u. + ilc_deriv_thresh (float): Threshold value of |dEILC/dtau| so that if |dEILC/dtau| >= + ilc_deriv_thresh for a generator, add its candidate group to the DIS. + max_ilc_gens (int or None): Maximum number of generators allowed for the ansatz. + If None, one generator from each DIS group is used. If set to an int, then + min(size(DIS), max_ilc_gens) generators are used for the ansatz. Default, None. + verbose (bool): Flag for ILC verbosity. Default, False. + """ + + def __init__(self, molecule, mapping="JW", up_then_down=False, qubit_op_list=None, + qmf_circuit=None, qmf_var_params=None, qubit_mf_ham=None, ilc_guess=1.e-1, + ilc_deriv_thresh=1.e-3, max_ilc_gens=None, n_trot=1, verbose=False): + + self.molecule = molecule + self.n_spinorbitals = self.molecule.n_active_sos + if self.n_spinorbitals % 2 != 0: + raise ValueError("The total number of spin-orbitals should be even.") + + self.n_electrons = self.molecule.n_active_electrons + self.spin = molecule.spin + self.mapping = mapping + self.n_qubits = get_qubit_number(self.mapping, self.n_spinorbitals) + self.up_then_down = up_then_down + if self.mapping.upper() == "JW" and not self.up_then_down: + warnings.warn("The ILC ansatz requires spin-orbital ordering to be all spin-up " + "first followed by all spin-down for the JW mapping.", RuntimeWarning) + self.up_then_down = True + + self.ilc_guess = ilc_guess + self.ilc_deriv_thresh = ilc_deriv_thresh + self.max_ilc_gens = max_ilc_gens + self.n_trot = n_trot + self.qubit_op_list = qubit_op_list + self.qmf_var_params = qmf_var_params + self.qmf_circuit = qmf_circuit + self.verbose = verbose + + if qubit_mf_ham is None: + self.fermi_ham = self.molecule.fermionic_hamiltonian + self.qubit_ham = fermion_to_qubit_mapping(self.fermi_ham, self.mapping, + self.n_spinorbitals, self.n_electrons, + self.up_then_down, self.spin) + else: + self.qubit_ham = qubit_mf_ham + + if self.qmf_var_params is None: + self.qmf_var_params = init_qmf_from_hf(self.n_spinorbitals, self.n_electrons, + self.mapping, self.up_then_down, self.spin) + elif isinstance(self.qmf_var_params, list): + self.qmf_var_params = np.array(self.qmf_var_params) + if self.qmf_var_params.size != 2 * self.n_qubits: + raise ValueError("The number of QMF variational parameters must be 2 * n_qubits.") + + # Get purified QMF parameters and use them to build the DIS or use a list of generators. + if self.qubit_op_list is None: + pure_var_params = purify_qmf_state(self.qmf_var_params, self.n_spinorbitals, + self.n_electrons, self.mapping, self.up_then_down, + self.spin, self.verbose) + self.dis = construct_dis(pure_var_params, self.qubit_ham, self.ilc_deriv_thresh, + self.verbose) + self.acs = construct_acs(self.dis, self.max_ilc_gens, self.n_qubits) + self.n_var_params = len(self.acs) if self.max_ilc_gens is None\ + else min(len(self.acs), self.max_ilc_gens) + else: + self.dis = None + self.n_var_params = len(self.qubit_op_list) + + # Supported reference state initialization + self.supported_reference_state = {"HF"} + # Supported var param initialization + self.supported_initial_var_params = {"zeros", "diag", "ilc_guess"} + + # Default starting parameters for initialization + self.pauli_to_angles_mapping = {} + self.default_reference_state = "HF" + self.var_params_default = "diag" + self.var_params = None + self.rebuild_dis = False + self.ilc_circuit = None + self.circuit = None + + def set_var_params(self, var_params=None): + """Set values for variational parameters, such as zeros or floats, + providing some keywords for users, and also supporting direct user input + (list or numpy array). Return the parameters so that workflows such as VQE can + retrieve these values. """ + + if var_params is None: + var_params = self.var_params_default + + if isinstance(var_params, str): + var_params = var_params.lower() + if var_params not in self.supported_initial_var_params: + raise ValueError(f"Supported keywords for initializing variational parameters: " + f"{self.supported_initial_var_params}") + # Initialize the ILC wave function as |ILC> = |QMF> + if var_params == "zeros": + initial_var_params = np.zeros((self.n_var_params,), dtype=float) + # Initialize all tau parameters to the same value specified by self.ilc_guess + elif var_params == "diag": + initial_var_params = init_ilc_diag(self.qubit_ham, self.acs, self.qmf_var_params) + elif var_params == "ilc_guess": + initial_var_params = self.ilc_guess * np.ones((self.n_var_params,)) + else: + initial_var_params = np.array(var_params) + if initial_var_params.size != self.n_var_params: + raise ValueError(f"Expected {self.n_var_params} variational parameters but " + f"received {initial_var_params.size}.") + self.var_params = initial_var_params + return initial_var_params + + def prepare_reference_state(self): + """Returns circuit preparing the reference state of the ansatz (e.g prepare reference + wavefunction with HF, multi-reference state, etc). These preparations must be consistent + with the transform used to obtain the qubit operator. """ + + if self.default_reference_state not in self.supported_reference_state: + raise ValueError(f"Only supported reference state methods are: " + f"{self.supported_reference_state}.") + if self.default_reference_state == "HF": + reference_state_circuit = get_qmf_circuit(self.qmf_var_params, False) + return reference_state_circuit + + def build_circuit(self, var_params=None): + """Build and return the quantum circuit implementing the state preparation ansatz + (with currently specified initial_state and var_params). """ + + if var_params is not None: + self.set_var_params(var_params) + elif self.var_params is None: + self.set_var_params() + + # Build a QMF state preparation circuit + if self.qmf_circuit is None: + self.qmf_circuit = self.prepare_reference_state() + + # Build a qubit operator required for ILC + self._get_ilc_qubit_op() + + # Obtain quantum circuit through trivial trotterization of the qubit operator + pauli_word_gates = [] + for i in range(self.n_trot): + for j, ilc_op in enumerate(self.qubit_op_list): + pauli_word, coef = list(ilc_op.terms.items())[0] + pauli_word_gates += pauliword_to_circuit(pauli_word, float(coef/self.n_trot), variational=True) + self.ilc_circuit = Circuit(pauli_word_gates) + self.circuit = self.qmf_circuit + self.ilc_circuit if self.qmf_circuit.size != 0\ + else self.ilc_circuit + + def update_var_params(self, var_params): + """Shortcut: set value of variational parameters in the existing ansatz circuit member. + Preferable to rebuilding your circuit from scratch, which can be an involved process. + """ + + self.set_var_params(var_params) + + # Build the qubit operator required for ILC + self._get_ilc_qubit_op() + + for pauli_word, coef in qubit_op.terms.items(): + gate_index = self.pauli_to_angles_mapping[pauli_word] + gate_param = 2. * coef if coef >= 0. else 4 * np.pi + 2 * coef + self.ilc_circuit._variational_gates[gate_index].parameter = gate_param + self.circuit = self.qmf_circuit + self.ilc_circuit if self.qmf_circuit.size != 0\ + else self.ilc_circuit + + def _get_ilc_qubit_op(self): + """Returns the ILC operator by selecting one generator from n_var_params DIS groups. + The ILC qubit operator is constructed as a linear combination of generators using the + parameter set {tau} as coefficients: ILC operator = -0.5 * SUM_k P_k * tau_k. + The exponentiated terms of the ILC operator, U = PROD_k exp(-0.5j * tau_k * P_k), + are used to build a ILC circuit. + + Args: + var_params (numpy array of float): The ILC variational parameter set. + n_var_params (int): Size of the ILC variational parameter set. + qmf_var_params (numpy array of float): The QMF variational parameter set. + qubit_ham (QubitOperator): A qubit Hamiltonian. + ilc_deriv_thresh (float): Threshold value of |dEILC/dtau| so that if |dEILC/dtau| >= + ilc_deriv_thresh for a generator, add its candidate group to the DIS. + dis (list of list): The DIS of ILC generators. + acs (list of list): The ACS of DIS ILC operators. + qubit_op_list (list of QubitOperator): A list of generators to use when building the ILC + operator instead of selecting from DIS groups. + rebuild_dis (bool): Rebuild the DIS. This is useful if qubit_ham of qmf_var_params have + changed (e.g. in iterative methods like iILC or QCC-ILC). If True, qubit_op_list is + reset to None. + verbose (bool): Flag for ILC verbosity. Default, False. + + Returns: + None + """ + + # Rebuild DIS & ACS in case qubit_ham changed or they and qubit_op_list don't exist + if self.rebuild_dis or (self.dis is None and self.qubit_op_list is None): + pure_var_params = purify_qmf_state(self.qmf_var_params, self.n_spinorbitals, + self.n_electrons, self.mapping, self.up_then_down, + self.spin, self.verbose) + self.dis = construct_dis(pure_var_params, self.qubit_ham, self.ilc_deriv_thresh, + self.verbose) + self.acs = construct_acs(self.dis, self.max_ilc_gens, self.n_qubits) + self.n_var_params = len(self.acs) if self.max_ilc_gens is None\ + else min(len(self.acs), self.max_ilc_gens) + self.qubit_op_list = None + + # Build the ILC qubit operator list + self.qubit_op_list = [] + for i in range(self.n_var_params - 1, 1, -1): + self.qubit_op_list.append(-0.5 * self.var_params[i - 1] * self.acs[i]) + self.qubit_op_list.append(-1. * self.var_params[0] * self.acs[1]) + for i in range(2, self.n_var_params): + self.qubit_op_list.append(-0.5 * self.var_params[i - 1] * self.acs[i]) diff --git a/tangelo/toolboxes/ansatz_generator/qmf.py b/tangelo/toolboxes/ansatz_generator/qmf.py index fc1d2583c..19f7d75fd 100755 --- a/tangelo/toolboxes/ansatz_generator/qmf.py +++ b/tangelo/toolboxes/ansatz_generator/qmf.py @@ -149,7 +149,7 @@ def set_var_params(self, var_params=None): # Initialize |QMF> as |00...0> if var_params == "zeros": initial_var_params = np.zeros((self.n_var_params,), dtype=float) - # Initialize |QMF> as (i/sqrt(2))^n_qubits * tensor_prod(|0> + |1>) + # Initialize |QMF> as (1/sqrt(2))^n_qubits * tensor_prod(|0> + 1j|1>) elif var_params == "half_pi": initial_var_params = 0.5 * np.pi * np.ones((self.n_var_params,)) # Initialize |QMF> as (-1)^n_qubits |11...1> state From a2e122e2e4cb7989f5d8d922aaa098b006ef3916 Mon Sep 17 00:00:00 2001 From: Marc Coons Date: Wed, 16 Feb 2022 14:28:03 -0500 Subject: [PATCH 02/28] updates to qmf, qcc, and ilc ansatz classes --- .../toolboxes/ansatz_generator/_qubit_cc.py | 22 +-- .../toolboxes/ansatz_generator/_qubit_ilc.py | 159 ++++++++-------- .../toolboxes/ansatz_generator/_qubit_mf.py | 10 +- tangelo/toolboxes/ansatz_generator/ilc.py | 173 ++++++++++-------- tangelo/toolboxes/ansatz_generator/qcc.py | 124 +++++++------ tangelo/toolboxes/ansatz_generator/qmf.py | 33 ++-- 6 files changed, 282 insertions(+), 239 deletions(-) diff --git a/tangelo/toolboxes/ansatz_generator/_qubit_cc.py b/tangelo/toolboxes/ansatz_generator/_qubit_cc.py index e270cee36..9b031de99 100644 --- a/tangelo/toolboxes/ansatz_generator/_qubit_cc.py +++ b/tangelo/toolboxes/ansatz_generator/_qubit_cc.py @@ -36,7 +36,7 @@ from ._qubit_mf import get_op_expval -def construct_dis(pure_var_params, qubit_ham, qcc_deriv_thresh, verbose=False): +def construct_dis(qubit_ham, pure_var_params, deqcc_dtau_thresh, verbose=False): """Construct the DIS of QCC generators, which proceeds as follows: 1. Identify the flip indices of all Hamiltonian terms and group terms by flip indices. 2. Construct a representative generator using flip indices from each candidate DIS group @@ -47,10 +47,10 @@ def construct_dis(pure_var_params, qubit_ham, qcc_deriv_thresh, verbose=False): odd number of Y operators. Args: - pure_var_params (numpy array of float): A purified QMF variational parameter set. qubit_ham (QubitOperator): A qubit Hamiltonian. - qcc_deriv_thresh (float): Threshold value of |dEQCC/dtau| so that if |dEQCC/dtau| >= - qcc_deriv_thresh for a generator, add its candidate group to the DIS. + pure_var_params (numpy array of float): A purified QMF variational parameter set. + deqcc_dtau_thresh (float): Threshold for |dEQCC/dtau| so that a candidate group is added + to the DIS if |dEQCC/dtau| >= deqcc_dtau_thresh for a generator. verbose (bool): Flag for QCC verbosity. Returns: @@ -58,7 +58,7 @@ def construct_dis(pure_var_params, qubit_ham, qcc_deriv_thresh, verbose=False): """ # Use a qubit Hamiltonian and purified QMF parameter set to construct the DIS - dis, dis_groups = [], get_dis_groups(pure_var_params, qubit_ham, qcc_deriv_thresh) + dis, dis_groups = [], get_dis_groups(qubit_ham, pure_var_params, deqcc_dtau_thresh) if dis_groups: if verbose: print(f"The DIS contains {len(dis_groups)} unique generator group(s).\n") @@ -72,18 +72,18 @@ def construct_dis(pure_var_params, qubit_ham, qcc_deriv_thresh, verbose=False): f"{abs(dis_group[1])} a.u.\n") else: raise ValueError(f"The DIS is empty: there are no candidate DIS groups where " - f"|dEQCC/dtau| >= {qcc_deriv_thresh} a.u. Terminate the QCC simulation.\n") + f"|dEQCC/dtau| >= {deqcc_dtau_thresh} a.u. Terminate simulation.\n") return dis -def get_dis_groups(pure_var_params, qubit_ham, qcc_deriv_thresh): +def get_dis_groups(qubit_ham, pure_var_params, deqcc_dtau_thresh): """Construct unique DIS groups characterized by the flip indices and |dEQCC/dtau|. Args: - pure_var_params (numpy array of float): A purified QMF variational parameter set. qubit_ham (QubitOperator): A qubit Hamiltonian. - qcc_deriv_thresh (float): Threshold value of |dEQCC/dtau| so that if |dEQCC/dtau| >= - qcc_deriv_thresh for a generator, add its candidate group to the DIS. + pure_var_params (numpy array of float): A purified QMF variational parameter set. + deqcc_dtau_thresh (float): Threshold for |dEQCC/dtau| so that a candidate group is added + to the DIS if |dEQCC/dtau| >= deqcc_dtau_thresh for a generator. Returns: list of tuple: the DIS group flip indices (str) and signed value of dEQCC/dtau (float). @@ -102,7 +102,7 @@ def get_dis_groups(pure_var_params, qubit_ham, qcc_deriv_thresh): # Return a sorted list of flip indices and signed dEQCC/dtau values for each DIS group dis_groups = [idxs_deriv for idxs_deriv in candidates.items() - if abs(idxs_deriv[1]) >= qcc_deriv_thresh] + if abs(idxs_deriv[1]) >= deqcc_dtau_thresh] return sorted(dis_groups, key=lambda deriv: abs(deriv[1]), reverse=True) diff --git a/tangelo/toolboxes/ansatz_generator/_qubit_ilc.py b/tangelo/toolboxes/ansatz_generator/_qubit_ilc.py index be9d1ccd6..8fcaf36f3 100644 --- a/tangelo/toolboxes/ansatz_generator/_qubit_ilc.py +++ b/tangelo/toolboxes/ansatz_generator/_qubit_ilc.py @@ -6,95 +6,102 @@ # # http://www.apache.org/licenses/LICENSE-2.0 # -# Unless required by applicable law or agreed to in writing, software +# Unless required by applicable law or agreed to in writ_ing, 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. +# limitat_ions under the License. -"""This module implements a collection of functions related to the QMF -ansatz. +"""This module implements a collect_ion of funct_ions related to the ILC +ansatz: + 1. Funct_ion to create the ant_i-commut_ing set (ACS) of generators from + the QCC DIS; + 2. An efficient solver that performs Gaussian eliminat_ion over GF(2); + 3. Funct_ion that init_ializes the ILC parameters via matrix diagonalizat_ion. Refs: 1. R. A. Lang, I. G. Ryabinkin, and A. F. Izmaylov. + arXiv:2002.05701v1, 2020, 1–10. + 2. R. A. Lang, I. G. Ryabinkin, and A. F. Izmaylov. J. Chem. Theory Comput. 2021, 17, 1, 66–78. + 3. Ç. K. Koç and S. N. Arachchige. + J. Parallel Distrib. Comput., 1991, 13, 118–122. """ -import numpy as np -import scipy from random import choice +import scipy +import numpy as np from tangelo.toolboxes.operators.operators import QubitOperator +from ._qubit_mf import get_op_expval def construct_acs(dis_gens, max_ilc_gens, n_qubits): - idxs, ac_idxs, not_ac_idxs = [idx for idx in range(len(dis_gens))], [], [] - while max_ilc_gens > len(ac_idxs) and len(ac_idxs) + len(not_ac_idxs) < len(dis_gens): - idxs, ilc_gens = [idx for idx in range(len(dis_gen)) if idx not in not_ac_idxs], [] + ac_idxs, not_ac_idxs = [], [] + while max_ilc_gens > len(ac_idxs) and len(ac_idxs) + len(not_ac_idxs) < len(dis_gens): + idxs, ilc_gens = [idx for idx in range(len(dis_gens)) if idx not in not_ac_idxs], [] n_idxs = len(idxs) ng2, ngnq = n_idxs * (n_idxs + 1) // 2, n_qubits * n_idxs - A, Zflip, Zidx = np.zeros((ng2, ngnq+1)), [0.]*ngnq, 0 + A, z_flip, z_idx = np.zeros((ng2, ngnq+1)), [0.]*ngnq, 0 - # Form the z vector of flip indices (Ref. 1 Appendix A) - for idx in idxs: - T_i = choice(dis_gens[idx][0]) if isinstance(dis_gens[idx], list) else dis_gens[idx] - for term, coeff in T_i.terms.items(): + # Form the z vector of flip indices (Appendix A, Refs. 1 & 2). + for idx in idxs: + t_i = choice(dis_gens[idx][0]) if isinstance(dis_gens[idx], list) else dis_gens[idx] + for term, coef in t_i.terms.items(): for j in range(n_qubits): - if ((j, 'X') in term): - Zflip[Z_idx*n_qubits+j] = 1. - elif ((j, 'Y') in term): - Zflip[Z_idx*n_qubits+j] = 1. - Z_idx += 1 - Zflip = np.array(Zflip) + if (j, 'X') in term or (j, 'Y') in term: + z_flip[z_idx*n_qubits+j] = 1. + z_idx += 1 + z_flip = np.array(z_flip) - # Form the matrix "A" in Ref. 1 Appendix B - ridx = 0 - for i in range(Z_idx): - A[ridx, i*n_qubits:(i+1)*n_qubits] = Zflip[i*n_qubits:(i+1)*n_qubits] - A[ridx, ngnq] = 1 - ridx += 1 - for j in range(i+1, Z_idx): - A[ridx, i*n_qubits:(i+1)*n_qubits] = Zflip[j*n_qubits:(j+1)*n_qubits] - A[ridx, j*n_qubits:(j+1)*n_qubits] = Zflip[i*n_qubits:(i+1)*n_qubits] - A[ridx, ngnq] = 1 - ridx += 1 + # Form the triangular matrix A (Appendix A, Refs. 1 & 2). + r_idx = 0 + for i in range(z_idx): + A[r_idx, i*n_qubits:(i+1)*n_qubits] = z_flip[i*n_qubits:(i+1)*n_qubits] + A[r_idx, ngnq] = 1 + r_idx += 1 + for j in range(i+1, z_idx): + A[r_idx, i*n_qubits:(i+1)*n_qubits] = z_flip[j*n_qubits:(j+1)*n_qubits] + A[r_idx, j*n_qubits:(j+1)*n_qubits] = z_flip[i*n_qubits:(i+1)*n_qubits] + A[r_idx, ngnq] = 1 + r_idx += 1 # Solve Az = 1 - Zsln = GF2_GaussElim_Solve(A, ngnq) + z_sln = GF2_GaussElim_Solve(A, ngnq) - # Check for a bad solution + # Check for a bad solut_ion candidate_gens = [] bad_sln = False - for i in range(Z_idx): + for i in range(z_idx): idx = '' Nflip, Ny = 0, 0 for j in range(n_qubits): zdx = i*n_qubits + j - if (Zflip[zdx] == 1. and Zsln[zdx] == 0.): + if z_flip[zdx] == 1. and z_sln[zdx] == 0.: idx += 'X' + str(j) + ' ' Nflip += 1 - elif (Zflip[zdx] == 1. and Zsln[zdx] == 1.): + elif z_flip[zdx] == 1. and z_sln[zdx] == 1.: idx += 'Y' + str(j) + ' ' Nflip += 1 Ny += 1 - elif (Zflip[zdx] == 0. and Zsln[zdx] == 1.): - idx += 'Z' + str(j) + ' ' + elif z_flip[zdx] == 0. and z_sln[zdx] == 1.: + idx += 'z' + str(j) + ' ' if Nflip < 2 or Ny % 2 != 1: bad_sln = True bad_idx = idxs[i] del idxs[i] if bad_idx not in not_ac_idxs: not_ac_idxs.append(bad_idx) - break + #break else: Ti = QubitOperator(idx) candidate_gens.append(Ti) - # For good solutions update the list of anti-commuting generators + # For good solut_ions update the list of ant_i-commut_ing generators if not bad_sln: for i, Ti in enumerate(candidate_gens): - anticommutes = True + ant_icommutes = True Ti_idx = idxs[i] - if (ilc_gens == []): + if not ilc_gens: ilc_gens.append(Ti) if Ti_idx not in ac_idxs: ac_idxs.append(Ti_idx) @@ -106,27 +113,27 @@ def construct_acs(dis_gens, max_ilc_gens, n_qubits): if (TiTj_AC != QubitOperator.zero()): if Ti_idx not in not_ac_idxs: not_ac_idxs.append(Ti_idx) - anticommutes = False + ant_icommutes = False if Ti_idx in idxs: idxs.remove(Ti_idx) if Ti_idx in ac_idxs: ac_idxs.remove(Ti_idx) - if not anticommutes: + if not ant_icommutes: break - if anticommutes: + if ant_icommutes: ilc_gens.append(Ti) if Ti_idx not in ac_idxs: ac_idxs.append(Ti_idx) if Ti_idx in not_ac_idxs: not_ac_idxs.remove(Ti_idx) - if not anticommutes: + if not ant_icommutes: break return ilc_gens -def GF2_GaussElim_Solve(A, Zdim): - # Implements a Gauss-Jordan solver over the binary field +def GF2_GaussElim_Solve(A, zdim): + # Gaussian eliminat_ion over GF(2) -- based on Ref. 3. NR, NC = np.shape(A)[0], np.shape(A)[1] - A, Zs, Zsln, piv_idx = np.array(A), [], [-1]*Zdim, 0 + A, zs, z_sln, piv_idx = np.array(A), [], [-1]*zdim, 0 for i in range(NC): Aij_max = 0. max_idx = piv_idx @@ -146,47 +153,55 @@ def GF2_GaussElim_Solve(A, Zdim): piv_idx += 1 b = A[0:NR, NC-1].tolist() for i in range(NR-1, -1, -1): - col_idx, Zf = -1., [] + col_idx, zf = -1., [] for j in range(NC-1): if (A[i, j] == 1.): if (col_idx == -1): col_idx = j else: - Zf.append(j) + zf.append(j) if (col_idx >= 0.): - Zs.append([col_idx, Zf, b[i]]) - for Z in (Zs): - b = Z[2] - for Zf in (Z[1]): - if (Zsln[Zf] == -1): - Zsln[Zf] = choice([0., 1.]) - b = (b + Zsln[Zf]) % 2 - Zsln[Z[0]] = b - return Zsln + zs.append([col_idx, zf, b[i]]) + for z in (zs): + b = z[2] + for zf in (z[1]): + if (z_sln[zf] == -1): + z_sln[zf] = choice([0., 1.]) + b = (b + z_sln[zf]) % 2 + z_sln[z[0]] = b + return z_sln def init_ilc_by_diag(qubit_ham, ilc_gens, qmf_var_params): ilc_gens.ins(0, QubitOperator.identity()) n_var_params = len(ilc_gens) + + # Form the Hamiltonian and overlap matrices (see Appendix B, Refs. 1 & 2). H = np.zeros((n_var_params, n_var_params), dtype=complex) S = np.ones((n_var_params, n_var_params), dtype=complex) for i in range(n_var_params): H_i = qubit_ham * ilc_gens[i] H[i, i] = get_op_expval(ilc_gens[i] * H_i, qmf_var_params) - for j in range(i+1, n_var_params): + for j in range(i + 1, n_var_params): H[j, i] = get_op_expval(ilc_gens[j] * H_i, qmf_var_params) S[j, i] = get_op_expval(ilc_gens[j] * ilc_gens[i], qmf_var_params) if (i == 0): - H[j, i] *= 1.j - S[j, i] *= 1.j + H[j, i] *= 1j + S[j, i] *= 1j + + # Solve the generalized eigenvalue problem E, c = scipy.linalg.eigh(a=np.matrix(H), b=np.matrix(S), lower=True, driver="gvd") - c0 = c[:, 0].real + + # Compute the ILC parameters according to Appendix C, Ref. 1). + c0 = c[:, 0]#.real denom_sum, ilc_var_params = 0., [] - for i in range(n_var_params): - c_i = c0[i] - denom_sum += pow(c_i.real, 2.) + pow(c_i.imag, 2.) - if i > 0: - beta = np.arcsin(c_i / np.sqrt(denom_sum)) - if i == 1 and c0[0].real < 0.: - beta = np.pi - beta - ilc_var_params.append(beta.real) + for i in range(2): + denom_sum += pow(c0[i].real, 2.) + pow(c0[i].imag, 2.) + beta_1 = np.arcsin(c0[1]) / np.sqrt(denom_sum) + if c0[0].real < 0.: + beta_1 = np.pi - beta_1 + for i in range(2, n_var_params): + denom_sum += pow(c0[i].real, 2.) + pow(c0[i].imag, 2.) + beta = np.arcsin(c0[i] / np.sqrt(denom_sum)) + ilc_var_params.append(beta.real) + del ilc_gens[0] return ilc_var_params diff --git a/tangelo/toolboxes/ansatz_generator/_qubit_mf.py b/tangelo/toolboxes/ansatz_generator/_qubit_mf.py index c9065df67..db7c5ab74 100644 --- a/tangelo/toolboxes/ansatz_generator/_qubit_mf.py +++ b/tangelo/toolboxes/ansatz_generator/_qubit_mf.py @@ -44,7 +44,7 @@ def get_op_expval(qubit_op, qmf_var_params): Args: qubit_op (QubitOperator): A qubit operator to compute the expectation value of. - qmf_var_params (numpy array of float): The QMF variational parameter set. + qmf_var_params (numpy array of float): QMF variational parameter set. Returns: complex: expectation value of all qubit operator terms. @@ -138,7 +138,7 @@ def purify_qmf_state(qmf_var_params, n_spinorbitals, n_electrons, mapping, up_th vector = get_vector(n_spinorbitals, n_electrons, mapping, up_then_down, spin) pure_var_params[i] = np.pi * vector[i] if verbose: - print(f"Purified QMF_{i} Bloch angles: (theta, phi) = ({pure_var_params[i]}, {pure_var_params[i + n_qubits]})\n") + print(f"Purified |QMF_{i}> Bloch angles: (theta, phi) = ({pure_var_params[i]}, {pure_var_params[i + n_qubits]})\n") return pure_var_params @@ -148,11 +148,11 @@ def get_qmf_circuit(qmf_var_params, variational=True): and the second n_qubit elements in {Omega} are parameters for RZ gates. Args: - qmf_var_params (numpy array of float): The QMF variational parameter set. - variational (bool): Flag to treat {Omega} variationally or not. + qmf_var_params (numpy array of float): QMF variational parameter set. + variational (bool): Flag to treat {Omega} variationally or keep them fixed. Returns: - Circuit: instance of tangelo.linq Circuit class. + Circuit: instance of tangelo.linq Circuit class for a QMF state preparation circuit. """ n_qubits, gates = qmf_var_params.size // 2, [] diff --git a/tangelo/toolboxes/ansatz_generator/ilc.py b/tangelo/toolboxes/ansatz_generator/ilc.py index 847ca4f6f..50574515d 100755 --- a/tangelo/toolboxes/ansatz_generator/ilc.py +++ b/tangelo/toolboxes/ansatz_generator/ilc.py @@ -12,25 +12,26 @@ # See the License for the specific language governing permissions and # limitations under the License. -"""This module defines the qubit coupled cluster ansatz class for involutory -linear combinations (ILC) of anti-commuting Pauli products. +"""This module defines the coupled cluster ansatz class for involutory +linear combinations (ILC) of anti-commuting sets (ACS) of Pauli words. Refs: 1. R. A. Lang, I. G. Ryabinkin, and A. F. Izmaylov. + arXiv:2002.05701v1, 2020, 1–10. + 2. R. A. Lang, I. G. Ryabinkin, and A. F. Izmaylov. J. Chem. Theory Comput. 2021, 17, 1, 66–78. """ import warnings import numpy as np -from tangelo.toolboxes.operators.operators import QubitOperator from tangelo.toolboxes.qubit_mappings.mapping_transform import get_qubit_number,\ fermion_to_qubit_mapping from tangelo.linq import Circuit from .ansatz import Ansatz from .ansatz_utils import exp_pauliword_to_gates from ._qubit_mf import init_qmf_from_hf, get_qmf_circuit, purify_qmf_state -from ._qubit_ilc import construct_ACS, init_ilc_diag +from ._qubit_ilc import construct_acs, init_ilc_by_diag from ._qubit_cc import construct_dis @@ -43,31 +44,30 @@ class ILC(Ansatz): Args: molecule (SecondQuantizedMolecule): The molecular system. - mapping (str): One of the supported qubit mapping identifiers. Default, "JW". - up_then_down (bool): Change basis ordering putting all spin up orbitals first, - followed by all spin down. Default, False. - qubit_op_list (list of QubitOperator): A list of ILC generators to use for the ansatz. - Default, None. + mapping (str): One of the supported mapping identifiers. Default, "JW". + up_then_down (bool): Change basis ordering putting all spin-up orbitals first, + followed by all spin-down. Default, False. + ilc_op_list (list of QubitOperator): Generator list for the ILC ansatz. Default, None. qmf_circuit (Circuit): An instance of tangelo.linq Circuit class implementing a QMF state - preparation circuit. A variational circuit can be passed from the QMF ansatz class. - Otherwise a non-variational circuit is created by default. Default, None. - qmf_var_params (list or numpy array of float): The QMF variational parameter set. + preparation circuit. If passed from the QMF ansatz class, parameters are variational. + If None, one is created with QMF parameters that are not variational. Default, None. + qmf_var_params (list or numpy array of float): QMF variational parameter set. If None, the values are determined using a Hartree-Fock reference state. Default, None. - qubit_mf_ham (QubitOperator): Allows a qubit Hamiltonian to be passed to the ILC ansatz - class. If not None, then the fermionic Hamiltonian is ignored. Default, None. - ilc_guess (float): Sets the initial guess for all amplitudes in the ILC variational - parameter set. Default, 1.e-1 a.u. - ilc_deriv_thresh (float): Threshold value of |dEILC/dtau| so that if |dEILC/dtau| >= - ilc_deriv_thresh for a generator, add its candidate group to the DIS. - max_ilc_gens (int or None): Maximum number of generators allowed for the ansatz. - If None, one generator from each DIS group is used. If set to an int, then - min(size(DIS), max_ilc_gens) generators are used for the ansatz. Default, None. - verbose (bool): Flag for ILC verbosity. Default, False. + qubit_ham (QubitOperator): Pass a qubit Hamiltonian to the ansatz class and ignore + the fermionic Hamiltonian in molecule. Default, None. + deilc_dtau_thresh (float): Threshold for |dEILC/dtau| so that a candidate group is added + to the DIS if |dEILC/dtau| >= deilc_dtau_thresh for a generator. Default, 1.e-3 a.u. + ilc_tau_guess (float): The initial guess for all ILC variational parameters. + Default, 1.e-2 a.u. + max_ilc_gens (int or None): Maximum number of generators allowed in the ansatz. If None, + one generator from each DIS group is selected. If int, then min(|DIS|, max_ilc_gens) + generators are selected in order of decreasing |dEILC/dtau|. Default, None. + verbose (bool): Flag for QCC verbosity. Default, False. """ - def __init__(self, molecule, mapping="JW", up_then_down=False, qubit_op_list=None, - qmf_circuit=None, qmf_var_params=None, qubit_mf_ham=None, ilc_guess=1.e-1, - ilc_deriv_thresh=1.e-3, max_ilc_gens=None, n_trot=1, verbose=False): + def __init__(self, molecule, mapping="JW", up_then_down=False, ilc_op_list=None, + qmf_circuit=None, qmf_var_params=None, qubit_ham=None, ilc_tau_guess=1.e-2, + deilc_dtau_thresh=1.e-3, max_ilc_gens=None, n_trotter=1, verbose=False): self.molecule = molecule self.n_spinorbitals = self.molecule.n_active_sos @@ -80,26 +80,26 @@ def __init__(self, molecule, mapping="JW", up_then_down=False, qubit_op_list=Non self.n_qubits = get_qubit_number(self.mapping, self.n_spinorbitals) self.up_then_down = up_then_down if self.mapping.upper() == "JW" and not self.up_then_down: - warnings.warn("The ILC ansatz requires spin-orbital ordering to be all spin-up " + warnings.warn("The QCC ansatz requires spin-orbital ordering to be all spin-up " "first followed by all spin-down for the JW mapping.", RuntimeWarning) self.up_then_down = True - self.ilc_guess = ilc_guess - self.ilc_deriv_thresh = ilc_deriv_thresh + self.ilc_tau_guess = ilc_tau_guess + self.deilc_dtau_thresh = deilc_dtau_thresh self.max_ilc_gens = max_ilc_gens - self.n_trot = n_trot - self.qubit_op_list = qubit_op_list + self.ilc_op_list = ilc_op_list self.qmf_var_params = qmf_var_params self.qmf_circuit = qmf_circuit + self.n_trotter = n_trotter self.verbose = verbose - if qubit_mf_ham is None: + if qubit_ham is None: self.fermi_ham = self.molecule.fermionic_hamiltonian self.qubit_ham = fermion_to_qubit_mapping(self.fermi_ham, self.mapping, self.n_spinorbitals, self.n_electrons, self.up_then_down, self.spin) else: - self.qubit_ham = qubit_mf_ham + self.qubit_ham = qubit_ham if self.qmf_var_params is None: self.qmf_var_params = init_qmf_from_hf(self.n_spinorbitals, self.n_electrons, @@ -109,24 +109,29 @@ def __init__(self, molecule, mapping="JW", up_then_down=False, qubit_op_list=Non if self.qmf_var_params.size != 2 * self.n_qubits: raise ValueError("The number of QMF variational parameters must be 2 * n_qubits.") - # Get purified QMF parameters and use them to build the DIS or use a list of generators. - if self.qubit_op_list is None: + # Get purified QMF parameters and build the DIS & ACS or use a list of generators. + if self.ilc_op_list is None: pure_var_params = purify_qmf_state(self.qmf_var_params, self.n_spinorbitals, self.n_electrons, self.mapping, self.up_then_down, self.spin, self.verbose) - self.dis = construct_dis(pure_var_params, self.qubit_ham, self.ilc_deriv_thresh, + print(pure_var_params) + self.dis = construct_dis(pure_var_params, self.qubit_ham, self.deilc_dtau_thresh, self.verbose) + print(self.dis) self.acs = construct_acs(self.dis, self.max_ilc_gens, self.n_qubits) + print(self.acs) self.n_var_params = len(self.acs) if self.max_ilc_gens is None\ else min(len(self.acs), self.max_ilc_gens) + print(self.n_var_params) else: self.dis = None - self.n_var_params = len(self.qubit_op_list) + self.acs = None + self.n_var_params = len(self.ilc_op_list) # Supported reference state initialization self.supported_reference_state = {"HF"} # Supported var param initialization - self.supported_initial_var_params = {"zeros", "diag", "ilc_guess"} + self.supported_initial_var_params = {"zeros", "diag", "ilc_tau_guess"} # Default starting parameters for initialization self.pauli_to_angles_mapping = {} @@ -134,6 +139,7 @@ def __init__(self, molecule, mapping="JW", up_then_down=False, qubit_op_list=Non self.var_params_default = "diag" self.var_params = None self.rebuild_dis = False + self.rebuild_acs = False self.ilc_circuit = None self.circuit = None @@ -154,11 +160,12 @@ def set_var_params(self, var_params=None): # Initialize the ILC wave function as |ILC> = |QMF> if var_params == "zeros": initial_var_params = np.zeros((self.n_var_params,), dtype=float) - # Initialize all tau parameters to the same value specified by self.ilc_guess + # Initialize ILC parameters by matrix diagonalization (see Appendix B, Refs. 1 & 2). elif var_params == "diag": - initial_var_params = init_ilc_diag(self.qubit_ham, self.acs, self.qmf_var_params) - elif var_params == "ilc_guess": - initial_var_params = self.ilc_guess * np.ones((self.n_var_params,)) + initial_var_params = init_ilc_by_diag(self.qubit_ham, self.acs, self.qmf_var_params) + # Initialize all ILC parameters to the same value specified by self.ilc_tau_guess + elif var_params == "ilc_tau_guess": + initial_var_params = self.ilc_tau_guess * np.ones((self.n_var_params,)) else: initial_var_params = np.array(var_params) if initial_var_params.size != self.n_var_params: @@ -170,7 +177,7 @@ def set_var_params(self, var_params=None): def prepare_reference_state(self): """Returns circuit preparing the reference state of the ansatz (e.g prepare reference wavefunction with HF, multi-reference state, etc). These preparations must be consistent - with the transform used to obtain the qubit operator. """ + with the transform used to obtain the operator. """ if self.default_reference_state not in self.supported_reference_state: raise ValueError(f"Only supported reference state methods are: " @@ -192,15 +199,15 @@ def build_circuit(self, var_params=None): if self.qmf_circuit is None: self.qmf_circuit = self.prepare_reference_state() - # Build a qubit operator required for ILC - self._get_ilc_qubit_op() + # Build create the list of ILC qubit operators + self.ilc_op_list = self._get_ilc_op() - # Obtain quantum circuit through trivial trotterization of the qubit operator + # Obtain quantum circuit through trotterization of the list of ILC operators pauli_word_gates = [] - for i in range(self.n_trot): - for j, ilc_op in enumerate(self.qubit_op_list): + for i in range(self.n_trotter): + for ilc_op in self.ilc_op_list: pauli_word, coef = list(ilc_op.terms.items())[0] - pauli_word_gates += pauliword_to_circuit(pauli_word, float(coef/self.n_trot), variational=True) + pauli_word_gates += exp_pauliword_to_gates(pauli_word, float(coef/self.n_trotter), variational=True) self.ilc_circuit = Circuit(pauli_word_gates) self.circuit = self.qmf_circuit + self.ilc_circuit if self.qmf_circuit.size != 0\ else self.ilc_circuit @@ -210,19 +217,22 @@ def update_var_params(self, var_params): Preferable to rebuilding your circuit from scratch, which can be an involved process. """ + # Update the ILC variational parameters self.set_var_params(var_params) - # Build the qubit operator required for ILC - self._get_ilc_qubit_op() + # Build the ILC ansatz operator + self.ilc_op_list = self._get_ilc_op() - for pauli_word, coef in qubit_op.terms.items(): - gate_index = self.pauli_to_angles_mapping[pauli_word] - gate_param = 2. * coef if coef >= 0. else 4 * np.pi + 2 * coef - self.ilc_circuit._variational_gates[gate_index].parameter = gate_param + pauli_word_gates = [] + for i in range(self.n_trotter): + for ilc_op in self.ilc_op_list: + pauli_word, coef = list(ilc_op.terms.items())[0] + pauli_word_gates += exp_pauliword_to_gates(pauli_word, float(coef/self.n_trotter), variational=True) + self.ilc_circuit = Circuit(pauli_word_gates) self.circuit = self.qmf_circuit + self.ilc_circuit if self.qmf_circuit.size != 0\ else self.ilc_circuit - def _get_ilc_qubit_op(self): + def _get_ilc_op(self): """Returns the ILC operator by selecting one generator from n_var_params DIS groups. The ILC qubit operator is constructed as a linear combination of generators using the parameter set {tau} as coefficients: ILC operator = -0.5 * SUM_k P_k * tau_k. @@ -230,41 +240,50 @@ def _get_ilc_qubit_op(self): are used to build a ILC circuit. Args: - var_params (numpy array of float): The ILC variational parameter set. - n_var_params (int): Size of the ILC variational parameter set. - qmf_var_params (numpy array of float): The QMF variational parameter set. + rebuild_dis (bool): Rebuilds DIS and sets ilc_op_list to None. + rebuild_acs (bool): Rebuilds DIS & ACS and sets ilc_op_list to None. + dis (list of list): DIS of QCC generators. + acs (list of list): ACS of selected QCC generators from the DIS. + ilc_op_list (list of QubitOperator): ACS generator list for the ILC ansatz. + var_params (numpy array of float): ILC variational parameter set. + n_var_params (int): Number of ILC variational parameters. + qmf_var_params (numpy array of float): QMF variational parameter set. + n_spinorbitals (int): Number of spin-orbitals in the molecular system. + n_electrons (int): Number of electrons in the molecular system. + mapping (str) : One of the supported mapping identifiers. + up_then_down (bool): Change basis ordering putting all spin-up orbitals first, + followed by all spin-down. + spin (int): 2*S = n_alpha - n_beta. qubit_ham (QubitOperator): A qubit Hamiltonian. - ilc_deriv_thresh (float): Threshold value of |dEILC/dtau| so that if |dEILC/dtau| >= - ilc_deriv_thresh for a generator, add its candidate group to the DIS. - dis (list of list): The DIS of ILC generators. - acs (list of list): The ACS of DIS ILC operators. - qubit_op_list (list of QubitOperator): A list of generators to use when building the ILC - operator instead of selecting from DIS groups. - rebuild_dis (bool): Rebuild the DIS. This is useful if qubit_ham of qmf_var_params have - changed (e.g. in iterative methods like iILC or QCC-ILC). If True, qubit_op_list is - reset to None. - verbose (bool): Flag for ILC verbosity. Default, False. + deilc_dtau_thresh (float): Threshold for |dEILC/dtau| so that a candidate group is added + to the DIS if |dEILC/dtau| >= deilc_dtau_thresh for a generator. + max_ilc_gens (int or None): Maximum number of generators allowed in the ansatz. If None, + one generator from each DIS group is selected. If int, min(|DIS|, max_ilc_gens) + generators are selected in order of decreasing |dEILC/dtau| values. + verbose (bool): Flag for QCC verbosity. Returns: - None + list of QubitOperator: the list of ILC qubit operators ordered according to the + argument of Eq. C1, Appendix C, Ref. 1. """ # Rebuild DIS & ACS in case qubit_ham changed or they and qubit_op_list don't exist - if self.rebuild_dis or (self.dis is None and self.qubit_op_list is None): + if self.rebuild_dis or self.rebuild_acs or ((self.dis is None or self.acs is None) and self.ilc_op_list is None): pure_var_params = purify_qmf_state(self.qmf_var_params, self.n_spinorbitals, self.n_electrons, self.mapping, self.up_then_down, self.spin, self.verbose) - self.dis = construct_dis(pure_var_params, self.qubit_ham, self.ilc_deriv_thresh, + self.dis = construct_dis(pure_var_params, self.qubit_ham, self.deilc_dtau_thresh, self.verbose) self.acs = construct_acs(self.dis, self.max_ilc_gens, self.n_qubits) self.n_var_params = len(self.acs) if self.max_ilc_gens is None\ else min(len(self.acs), self.max_ilc_gens) - self.qubit_op_list = None + self.ilc_op_list = None - # Build the ILC qubit operator list - self.qubit_op_list = [] + # Build the ILC qubit operator list + ilc_op_list = [] for i in range(self.n_var_params - 1, 1, -1): - self.qubit_op_list.append(-0.5 * self.var_params[i - 1] * self.acs[i]) - self.qubit_op_list.append(-1. * self.var_params[0] * self.acs[1]) + ilc_op_list.append(-0.5 * self.var_params[i - 1] * self.acs[i]) + ilc_op_list.append(-1. * self.var_params[0] * self.acs[1]) for i in range(2, self.n_var_params): - self.qubit_op_list.append(-0.5 * self.var_params[i - 1] * self.acs[i]) + ilc_op_list.append(-0.5 * self.var_params[i - 1] * self.acs[i]) + return ilc_op_list diff --git a/tangelo/toolboxes/ansatz_generator/qcc.py b/tangelo/toolboxes/ansatz_generator/qcc.py index 737da5975..7cabfbfbf 100755 --- a/tangelo/toolboxes/ansatz_generator/qcc.py +++ b/tangelo/toolboxes/ansatz_generator/qcc.py @@ -56,30 +56,29 @@ class QCC(Ansatz): Args: molecule (SecondQuantizedMolecule): The molecular system. mapping (str): One of the supported qubit mapping identifiers. Default, "JW". - up_then_down (bool): Change basis ordering putting all spin up orbitals first, - followed by all spin down. Default, False. - qubit_op_list (list of QubitOperator): A list of QCC generators to use for the ansatz. - Default, None. + up_then_down (bool): Change basis ordering putting all spin-up orbitals first, + followed by all spin-down. Default, False. + qcc_op_list (list of QubitOperator): Generator list for the QCC ansatz. Default, None. qmf_circuit (Circuit): An instance of tangelo.linq Circuit class implementing a QMF state - preparation circuit. A variational circuit can be passed from the QMF ansatz class. - Otherwise a non-variational circuit is created by default. Default, None. - qmf_var_params (list or numpy array of float): The QMF variational parameter set. + preparation circuit. If passed from the QMF ansatz class, parameters are variational. + If None, one is created with QMF parameters that are not variational. Default, None. + qmf_var_params (list or numpy array of float): QMF variational parameter set. If None, the values are determined using a Hartree-Fock reference state. Default, None. - qubit_mf_ham (QubitOperator): Allows a qubit Hamiltonian to be passed to the QCC ansatz - class. If not None, then the fermionic Hamiltonian is ignored. Default, None. - qcc_guess (float): Sets the initial guess for all amplitudes in the QCC variational - parameter set. Default, 1.e-1 a.u. - qcc_deriv_thresh (float): Threshold value of |dEQCC/dtau| so that if |dEQCC/dtau| >= - qcc_deriv_thresh for a generator, add its candidate group to the DIS. - max_qcc_gens (int or None): Maximum number of generators allowed for the ansatz. - If None, one generator from each DIS group is used. If set to an int, then - min(size(DIS), max_qcc_gens) generators are used for the ansatz. Default, None. + qubit_ham (QubitOperator): Pass a qubit Hamiltonian to the QCC ansatz class and ignore + the fermionic Hamiltonian in molecule. Default, None. + deqcc_dtau_thresh (float): Threshold for |dEQCC/dtau| so that a candidate group is added + to the DIS if |dEQCC/dtau| >= deqcc_dtau_thresh for a generator. Default, 1.e-3 a.u. + qcc_tau_guess (float): The initial guess for all QCC variational parameters. + Default, 1.e-2 a.u. + max_qcc_gens (int or None): Maximum number of generators allowed in the ansatz. If None, + one generator from each DIS group is selected. If int, then min(|DIS|, max_qcc_gens) + generators are selected in order of decreasing |dEQCC/dtau|. Default, None. verbose (bool): Flag for QCC verbosity. Default, False. """ - def __init__(self, molecule, mapping="JW", up_then_down=False, qubit_op_list=None, - qmf_circuit=None, qmf_var_params=None, qubit_mf_ham=None, qcc_guess=1.e-1, - qcc_deriv_thresh=1.e-3, max_qcc_gens=None, verbose=False): + def __init__(self, molecule, mapping="JW", up_then_down=False, qcc_op_list=None, + qmf_circuit=None, qmf_var_params=None, qubit_ham=None, qcc_tau_guess=1.e-2, + deqcc_dtau_thresh=1.e-3, max_qcc_gens=None, verbose=False): self.molecule = molecule self.n_spinorbitals = self.molecule.n_active_sos @@ -96,21 +95,21 @@ def __init__(self, molecule, mapping="JW", up_then_down=False, qubit_op_list=Non "first followed by all spin-down for the JW mapping.", RuntimeWarning) self.up_then_down = True - self.qcc_guess = qcc_guess - self.qcc_deriv_thresh = qcc_deriv_thresh + self.qcc_tau_guess = qcc_tau_guess + self.deqcc_dtau_thresh = deqcc_dtau_thresh self.max_qcc_gens = max_qcc_gens - self.qubit_op_list = qubit_op_list + self.qcc_op_list = qcc_op_list self.qmf_var_params = qmf_var_params self.qmf_circuit = qmf_circuit self.verbose = verbose - if qubit_mf_ham is None: + if qubit_ham is None: self.fermi_ham = self.molecule.fermionic_hamiltonian self.qubit_ham = fermion_to_qubit_mapping(self.fermi_ham, self.mapping, self.n_spinorbitals, self.n_electrons, self.up_then_down, self.spin) else: - self.qubit_ham = qubit_mf_ham + self.qubit_ham = qubit_ham if self.qmf_var_params is None: self.qmf_var_params = init_qmf_from_hf(self.n_spinorbitals, self.n_electrons, @@ -121,27 +120,27 @@ def __init__(self, molecule, mapping="JW", up_then_down=False, qubit_op_list=Non raise ValueError("The number of QMF variational parameters must be 2 * n_qubits.") # Get purified QMF parameters and use them to build the DIS or use a list of generators. - if self.qubit_op_list is None: + if self.qcc_op_list is None: pure_var_params = purify_qmf_state(self.qmf_var_params, self.n_spinorbitals, self.n_electrons, self.mapping, self.up_then_down, self.spin, self.verbose) - self.dis = construct_dis(pure_var_params, self.qubit_ham, self.qcc_deriv_thresh, + self.dis = construct_dis(self.qubit_ham, pure_var_params, self.deqcc_dtau_thresh, self.verbose) self.n_var_params = len(self.dis) if self.max_qcc_gens is None\ else min(len(self.dis), self.max_qcc_gens) else: self.dis = None - self.n_var_params = len(self.qubit_op_list) + self.n_var_params = len(self.qcc_op_list) # Supported reference state initialization self.supported_reference_state = {"HF"} # Supported var param initialization - self.supported_initial_var_params = {"zeros", "qcc_guess"} + self.supported_initial_var_params = {"zeros", "qcc_tau_guess"} # Default starting parameters for initialization self.pauli_to_angles_mapping = {} self.default_reference_state = "HF" - self.var_params_default = "qcc_guess" + self.var_params_default = "qcc_tau_guess" self.var_params = None self.rebuild_dis = False self.qcc_circuit = None @@ -164,9 +163,9 @@ def set_var_params(self, var_params=None): # Initialize the QCC wave function as |QCC> = |QMF> if var_params == "zeros": initial_var_params = np.zeros((self.n_var_params,), dtype=float) - # Initialize all tau parameters to the same value specified by self.qcc_guess - elif var_params == "qcc_guess": - initial_var_params = self.qcc_guess * np.ones((self.n_var_params,)) + # Initialize all tau parameters to the same value specified by self.qcc_tau_guess + elif var_params == "qcc_tau_guess": + initial_var_params = self.qcc_tau_guess * np.ones((self.n_var_params,)) else: initial_var_params = np.array(var_params) if initial_var_params.size != self.n_var_params: @@ -204,9 +203,9 @@ def build_circuit(self, var_params=None): self.qmf_circuit = self.prepare_reference_state() # Obtain quantum circuit through trivial trotterization of the qubit operator - # Keep track of the order in which pauli words have been visited for fast parameter updates - pauli_words = sorted(qubit_op.terms.items(), key=lambda x: len(x[0])) + # Track the order in which pauli words have been visited for fast parameter updates pauli_words_gates = [] + pauli_words = sorted(qubit_op.terms.items(), key=lambda x: len(x[0])) for i, (pauli_word, coef) in enumerate(pauli_words): pauli_words_gates += exp_pauliword_to_gates(pauli_word, coef) self.pauli_to_angles_mapping[pauli_word] = i @@ -219,14 +218,16 @@ def update_var_params(self, var_params): Preferable to rebuilding your circuit from scratch, which can be an involved process. """ + # Update the QCC variational parameters self.set_var_params(var_params) - # Build the qubit operator required for QCC + # Build the QCC ansatz qubit operator qubit_op = self._get_qcc_qubit_op() - # If qubit_op terms have changed, rebuild circuit. else update variational gates directly + # If qubit_op terms have changed, rebuild circuit if set(self.pauli_to_angles_mapping.keys()) != set(qubit_op.terms.keys()): self.build_circuit(var_params) + # Otherwise update variational gates directly else: for pauli_word, coef in qubit_op.terms.items(): gate_index = self.pauli_to_angles_mapping[pauli_word] @@ -243,49 +244,56 @@ def _get_qcc_qubit_op(self): are used to build a QCC circuit. Args: - var_params (numpy array of float): The QCC variational parameter set. - n_var_params (int): Size of the QCC variational parameter set. - qmf_var_params (numpy array of float): The QMF variational parameter set. + rebuild_dis (bool): Rebuilds dis and sets qcc_op_list to None. This is critical + to do when qubit_ham or qmf_var_params have changed. + dis (list of list): DIS of QCC generators. + qcc_op_list (list of QubitOperator): Generator list for the QCC ansatz. + var_params (numpy array of float): QCC variational parameter set. + n_var_params (int): Number of QCC variational parameters. + qmf_var_params (numpy array of float): QMF variational parameter set. + n_spinorbitals (int): Number of spin-orbitals in the molecular system. + n_electrons (int): Number of electrons in the molecular system. + mapping (str) : One of the supported qubit mapping identifiers. + up_then_down (bool): Change basis ordering putting all spin-up orbitals first, + followed by all spin-down. + spin (int): 2*S = n_alpha - n_beta. qubit_ham (QubitOperator): A qubit Hamiltonian. - qcc_deriv_thresh (float): Threshold value of |dEQCC/dtau| so that if |dEQCC/dtau| >= - qcc_deriv_thresh for a generator, add its candidate group to the DIS. - dis (list of list): The DIS of QCC generators. - qubit_op_list (list of QubitOperator): A list of generators to use when building the QCC - operator instead of selecting from DIS groups. - rebuild_dis (bool): Rebuild the DIS. This is useful if qubit_ham of qmf_var_params have - changed (e.g. in iterative methods like iQCC or QCC-ILC). If True, qubit_op_list is - reset to None. - verbose (bool): Flag for QCC verbosity. Default, False. + deqcc_dtau_thresh (float): Threshold for |dEQCC/dtau| so that a candidate group is added + to the DIS if |dEQCC/dtau| >= deqcc_dtau_thresh for a generator. + max_qcc_gens (int or None): Maximum number of generators allowed in the ansatz. If None, + one generator from each DIS group is selected. If int, min(|DIS|, max_qcc_gens) + generators are selected in order of decreasing |dEQCC/dtau| values. + verbose (bool): Flag for QCC verbosity. Returns: QubitOperator: QCC ansatz qubit operator. """ - # Rebuild the DIS in case qubit_ham changed or both the DIS and qubit_op_list don't exist - if self.rebuild_dis or (self.dis is None and self.qubit_op_list is None): + # Rebuild DIS if qubit_ham or qmf_var_params changed or if DIS and qcc_op_list are None. + if self.rebuild_dis or (self.dis is None and self.qcc_op_list is None): pure_var_params = purify_qmf_state(self.qmf_var_params, self.n_spinorbitals, self.n_electrons, self.mapping, self.up_then_down, self.spin, self.verbose) - self.dis = construct_dis(pure_var_params, self.qubit_ham, self.qcc_deriv_thresh, + self.dis = construct_dis(self.qubit_ham, pure_var_params, self.deqcc_dtau_thresh, self.verbose) self.n_var_params = len(self.dis) if self.max_qcc_gens is None\ else min(len(self.dis), self.max_qcc_gens) - self.qubit_op_list = None + self.qcc_op_list = None # Build the QCC operator using the DIS or a list of generators qcc_qubit_op = QubitOperator.zero() - if self.qubit_op_list is None: - self.qubit_op_list = [] + if self.qcc_op_list is None: + self.qcc_op_list = [] for i in range(self.n_var_params): # Instead of randomly choosing a generator, get the last one. qcc_gen = self.dis[i][-1] qcc_qubit_op -= 0.5 * self.var_params[i] * qcc_gen - self.qubit_op_list.append(qcc_gen) + self.qcc_op_list.append(qcc_gen) else: - if len(self.qubit_op_list) == self.n_var_params: - for i, qcc_gen in enumerate(self.qubit_op_list): + if len(self.qcc_op_list) == self.n_var_params: + for i, qcc_gen in enumerate(self.qcc_op_list): qcc_qubit_op -= 0.5 * self.var_params[i] * qcc_gen else: raise ValueError(f"Expected {self.n_var_params} generators in " - f"{self.qubit_op_list} but received {len(self.qubit_op_list)}.\n") + f"{self.qcc_op_list} but received {len(self.qcc_op_list)}.\n") return qcc_qubit_op diff --git a/tangelo/toolboxes/ansatz_generator/qmf.py b/tangelo/toolboxes/ansatz_generator/qmf.py index 19f7d75fd..27fbc1a43 100755 --- a/tangelo/toolboxes/ansatz_generator/qmf.py +++ b/tangelo/toolboxes/ansatz_generator/qmf.py @@ -49,7 +49,7 @@ class QMF(Ansatz): especially when a random initial guess is used. It is recommended that penalty terms are added to the mean-field Hamiltonian to enforce appropriate electron number and spin angular momentum symmetries on the QMF wave function during optimization (see Ref. 4). If using - penalty terms is to be avoided, an inital guess based on a Hartree-Fock reference state will + penalty terms is to be avoided, an initial guess based on a Hartree-Fock reference state will likely converge quickly to the desired state, but this is not guaranteed. Args: @@ -57,15 +57,15 @@ class QMF(Ansatz): mapping (str): One of the supported qubit mapping identifiers. Default, "JW". up_then_down (bool): Change basis ordering putting all spin up orbitals first, followed by all spin down. Default, False. - init_qmf (dict): Parameters for initializing the QMF variational parameter set and - mean-field Hamiltonian penalization. The keys are "init_params", "N", "S^2", or "Sz" - (str). The value of "init_params" must be in self.supported_initial_var_params (str). - The value of "N", "S^2", or "Sz" is (tuple or None). If a tuple, its elements are - the penalty term coefficient, mu (float), and target value of a penalty operator - (int). Example - "key": (mu, target). If "N", "S^2", or "Sz" is None, a penalty term - is added with default mu and target values: mu = 1.5 and target is derived - from molecule as = n_electrons, = spin_z * (spin_z + 1), and = spin_z, - where spin_z = spin // 2. Key, value pairs are case sensitive and mu > 0. + init_qmf (dict): Controls for QMF variational parameter initialization and mean-field + Hamiltonian penalization. Supported keys are "init_params", "N", "S^2", or "Sz" (str). + Values of "init_params" must be in self.supported_initial_var_params (str). Values of + "N", "S^2", or "Sz" are (tuple or None). If tuple, the elements are a penalty + term coefficient, mu (float), and a target value of the penalty operator (int). + Example - "key": (mu, target). If "N", "S^2", or "Sz" is None, a penalty term is added + with default mu and target values: mu = 1.5 and target is derived from molecule as + = n_electrons, = spin_z * (spin_z + 1), and = spin_z, where + spin_z = spin // 2. Key, value pairs are case sensitive and mu > 0. Default, {"init_params": "hf_state"}. """ @@ -114,7 +114,7 @@ def __init__(self, molecule, mapping="JW", up_then_down=False, init_qmf=None): self.fermi_ham += penalize_mf_ham(self.init_qmf, self.n_orbitals) else: if self.var_params_default != "hf_state": - warnings.warn("It is recommended that the QMF parameters are intialized " + warnings.warn("It is recommended that the QMF parameters are initialized " "using a Hartree-Fock reference state if penalty terms are " "not added to the mean-field Hamiltonian.", RuntimeWarning) else: @@ -123,6 +123,7 @@ def __init__(self, molecule, mapping="JW", up_then_down=False, init_qmf=None): else: raise TypeError(f"{self.init_qmf} must be dictionary type.") + # Get the qubit Hamiltonian self.qubit_ham = fermion_to_qubit_mapping(self.fermi_ham, self.mapping, self.n_spinorbitals, self.n_electrons, self.up_then_down, self.spin) @@ -146,21 +147,21 @@ def set_var_params(self, var_params=None): if var_params not in self.supported_initial_var_params: raise ValueError(f"Supported keywords for initializing variational parameters: " f"{self.supported_initial_var_params}") - # Initialize |QMF> as |00...0> if var_params == "zeros": + # Initialize |QMF> as |00...0> initial_var_params = np.zeros((self.n_var_params,), dtype=float) - # Initialize |QMF> as (1/sqrt(2))^n_qubits * tensor_prod(|0> + 1j|1>) elif var_params == "half_pi": + # Initialize |QMF> as (1/sqrt(2))^n_qubits * tensor_prod(|0> + 1j|1>) initial_var_params = 0.5 * np.pi * np.ones((self.n_var_params,)) - # Initialize |QMF> as (-1)^n_qubits |11...1> state elif var_params == "pis": + # Initialize |QMF> as (-1)^n_qubits |11...1> initial_var_params = np.pi * np.ones((self.n_var_params,)) - # Initialize theta and phi angles randomly over [0, pi] and [0, 2*pi], respectively elif var_params == "random": + # Random initialization of thetas over [0, pi] and phis over [0, 2 * pi] initial_thetas = np.pi * np.random.random((self.n_qubits,)) initial_phis = 2. * np.pi * np.random.random((self.n_qubits,)) initial_var_params = np.concatenate((initial_thetas, initial_phis)) - # Initialize theta angles so that |QMF> = |HF> state and set all phi angles to 0. + # Initialize thetas as 0 or pi such that |QMF> = |HF> and set all phis to 0 elif var_params == "hf_state": initial_var_params = init_qmf_from_hf(self.n_spinorbitals, self.n_electrons, self.mapping, self.up_then_down, self.spin) From 808e0f91c010a5786fac447f506651acc3dfe6fe Mon Sep 17 00:00:00 2001 From: Marc Coons Date: Thu, 17 Feb 2022 11:45:34 -0500 Subject: [PATCH 03/28] enable QMF and ILC classes to read-in and process data from OpenFermion *.hdf5 files and other small fixes. --- .../toolboxes/ansatz_generator/__init__.py | 1 + .../toolboxes/ansatz_generator/_qubit_ilc.py | 216 +++++++++--------- tangelo/toolboxes/ansatz_generator/ilc.py | 42 ++-- tangelo/toolboxes/ansatz_generator/qmf.py | 33 ++- .../ansatz_generator/tests/test_qcc.py | 2 +- 5 files changed, 153 insertions(+), 141 deletions(-) diff --git a/tangelo/toolboxes/ansatz_generator/__init__.py b/tangelo/toolboxes/ansatz_generator/__init__.py index 1940ada44..0ea91b6a7 100644 --- a/tangelo/toolboxes/ansatz_generator/__init__.py +++ b/tangelo/toolboxes/ansatz_generator/__init__.py @@ -13,6 +13,7 @@ # limitations under the License. from .vsqs import VSQS +from .ilc import ILC from .qcc import QCC from .qmf import QMF from .uccsd import UCCSD diff --git a/tangelo/toolboxes/ansatz_generator/_qubit_ilc.py b/tangelo/toolboxes/ansatz_generator/_qubit_ilc.py index 8fcaf36f3..48f28bb18 100644 --- a/tangelo/toolboxes/ansatz_generator/_qubit_ilc.py +++ b/tangelo/toolboxes/ansatz_generator/_qubit_ilc.py @@ -6,18 +6,18 @@ # # http://www.apache.org/licenses/LICENSE-2.0 # -# Unless required by applicable law or agreed to in writ_ing, software +# 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 -# limitat_ions under the License. +# limitations under the License. -"""This module implements a collect_ion of funct_ions related to the ILC +"""This module implements a collection of functions related to the ILC ansatz: - 1. Funct_ion to create the ant_i-commut_ing set (ACS) of generators from + 1. Function to create the anti-commuting set (ACS) of generators from the QCC DIS; - 2. An efficient solver that performs Gaussian eliminat_ion over GF(2); - 3. Funct_ion that init_ializes the ILC parameters via matrix diagonalizat_ion. + 2. An efficient solver that performs Gaussian elimination over GF(2); + 3. Function that initializes the ILC parameters via matrix diagonalization. Refs: 1. R. A. Lang, I. G. Ryabinkin, and A. F. Izmaylov. @@ -35,146 +35,138 @@ from tangelo.toolboxes.operators.operators import QubitOperator from ._qubit_mf import get_op_expval + def construct_acs(dis_gens, max_ilc_gens, n_qubits): ac_idxs, not_ac_idxs = [], [] while max_ilc_gens > len(ac_idxs) and len(ac_idxs) + len(not_ac_idxs) < len(dis_gens): - idxs, ilc_gens = [idx for idx in range(len(dis_gens)) if idx not in not_ac_idxs], [] - n_idxs = len(idxs) - ng2, ngnq = n_idxs * (n_idxs + 1) // 2, n_qubits * n_idxs - A, z_flip, z_idx = np.zeros((ng2, ngnq+1)), [0.]*ngnq, 0 - - # Form the z vector of flip indices (Appendix A, Refs. 1 & 2). - for idx in idxs: - t_i = choice(dis_gens[idx][0]) if isinstance(dis_gens[idx], list) else dis_gens[idx] - for term, coef in t_i.terms.items(): - for j in range(n_qubits): - if (j, 'X') in term or (j, 'Y') in term: - z_flip[z_idx*n_qubits+j] = 1. - z_idx += 1 - z_flip = np.array(z_flip) + gen_idxs, ilc_gens = [idx for idx in range(len(dis_gens)) if idx not in not_ac_idxs], [] + n_gens = len(gen_idxs) + ng2, ngnq = n_gens * (n_gens + 1) // 2, n_gens * n_qubits + # cons_mat --> A and z_vec --> z in Appendix A, Refs. 1 & 2. + cons_matrix, z_vec = np.zeros((ng2, ngnq + 1)), np.zeros(ngnq) + for idx, gen_idx in enumerate(gen_idxs): + gen = dis_gens[gen_idx][-1] + for term, _ in gen.terms.items(): + for paulis in term: + p_idx, pauli = paulis + if 'X' in pauli or 'Y' in pauli: + z_vec[idx * n_qubits + p_idx] = 1. # Form the triangular matrix A (Appendix A, Refs. 1 & 2). r_idx = 0 - for i in range(z_idx): - A[r_idx, i*n_qubits:(i+1)*n_qubits] = z_flip[i*n_qubits:(i+1)*n_qubits] - A[r_idx, ngnq] = 1 + for i in range(n_gens): + cons_matrix[r_idx, i*n_qubits:(i+1)*n_qubits] = z_vec[i*n_qubits:(i+1)*n_qubits] + cons_matrix[r_idx, ngnq] = 1 r_idx += 1 - for j in range(i+1, z_idx): - A[r_idx, i*n_qubits:(i+1)*n_qubits] = z_flip[j*n_qubits:(j+1)*n_qubits] - A[r_idx, j*n_qubits:(j+1)*n_qubits] = z_flip[i*n_qubits:(i+1)*n_qubits] - A[r_idx, ngnq] = 1 + for j in range(i+1, n_gens): + cons_matrix[r_idx, i*n_qubits:(i+1)*n_qubits] = z_vec[j*n_qubits:(j+1)*n_qubits] + cons_matrix[r_idx, j*n_qubits:(j+1)*n_qubits] = z_vec[i*n_qubits:(i+1)*n_qubits] + cons_matrix[r_idx, ngnq] = 1 r_idx += 1 - # Solve Az = 1 - z_sln = GF2_GaussElim_Solve(A, ngnq) + # Solve Az = 1 + z_sln = gauss_elim_over_gf2(cons_matrix, ngnq) - # Check for a bad solut_ion - candidate_gens = [] - bad_sln = False - for i in range(z_idx): - idx = '' - Nflip, Ny = 0, 0 + # Check for a bad solutions + candidate_gens, good_sln = [], True + for i in range(n_gens): + n_flip, n_y, gen_list = 0, 0, [] for j in range(n_qubits): - zdx = i*n_qubits + j - if z_flip[zdx] == 1. and z_sln[zdx] == 0.: - idx += 'X' + str(j) + ' ' - Nflip += 1 - elif z_flip[zdx] == 1. and z_sln[zdx] == 1.: - idx += 'Y' + str(j) + ' ' - Nflip += 1 - Ny += 1 - elif z_flip[zdx] == 0. and z_sln[zdx] == 1.: - idx += 'z' + str(j) + ' ' - if Nflip < 2 or Ny % 2 != 1: - bad_sln = True - bad_idx = idxs[i] - del idxs[i] - if bad_idx not in not_ac_idxs: - not_ac_idxs.append(bad_idx) - #break - else: - Ti = QubitOperator(idx) - candidate_gens.append(Ti) - - # For good solut_ions update the list of ant_i-commut_ing generators - if not bad_sln: - for i, Ti in enumerate(candidate_gens): - ant_icommutes = True - Ti_idx = idxs[i] - if not ilc_gens: - ilc_gens.append(Ti) - if Ti_idx not in ac_idxs: - ac_idxs.append(Ti_idx) - if Ti_idx in not_ac_idxs: - not_ac_idxs.remove(Ti_idx) + gen = None + idx = i * n_qubits + j + if z_vec[idx] == 1.: + n_flip += 1 + if z_sln[idx] == 0.: + gen = (j, 'X') + else: + gen = (j, 'Y') + n_y += 1 else: - for Tj in ilc_gens: - TiTj_AC = Ti * Tj + Tj * Ti - if (TiTj_AC != QubitOperator.zero()): - if Ti_idx not in not_ac_idxs: - not_ac_idxs.append(Ti_idx) - ant_icommutes = False - if Ti_idx in idxs: - idxs.remove(Ti_idx) - if Ti_idx in ac_idxs: - ac_idxs.remove(Ti_idx) - if not ant_icommutes: - break - if ant_icommutes: - ilc_gens.append(Ti) - if Ti_idx not in ac_idxs: - ac_idxs.append(Ti_idx) - if Ti_idx in not_ac_idxs: - not_ac_idxs.remove(Ti_idx) - if not ant_icommutes: - break + if z_sln[idx] == 1.: + gen = (j, 'Z') + if gen: + gen_list.append(gen) + if n_flip < 2 or n_y % 2 == 0: + good_sln = False + gen_idx = gen_idxs.pop(i) + if gen_idx not in not_ac_idxs: + not_ac_idxs.append(gen_idx) + if gen_idx in gen_idxs: + gen_idxs.remove(gen_idx) + if gen_idx in ac_idxs: + ac_idxs.remove(gen_idx) + else: + candidate_gens.append(QubitOperator(tuple(gen_list), 1.)) + + # For good solutions check that they anti-commute and update ilc_gens + if good_sln: + for i, gen_i in enumerate(candidate_gens): + anticommutes = True + gen_idx = gen_idxs[i] + for gen_j in ilc_gens: + anti_com = gen_i * gen_j + gen_j * gen_i + if anti_com != QubitOperator.zero(): + anticommutes = False + if gen_idx not in not_ac_idxs: + not_ac_idxs.append(gen_idx) + if gen_idx in gen_idxs: + gen_idxs.remove(gen_idx) + if gen_idx in ac_idxs: + ac_idxs.remove(gen_idx) + if anticommutes: + ilc_gens.append(gen_i) + if gen_idx not in ac_idxs: + ac_idxs.append(gen_idx) + if gen_idx in not_ac_idxs: + not_ac_idxs.remove(gen_idx) return ilc_gens - -def GF2_GaussElim_Solve(A, zdim): - # Gaussian eliminat_ion over GF(2) -- based on Ref. 3. - NR, NC = np.shape(A)[0], np.shape(A)[1] + + +def gauss_elim_over_gf2(A, zdim): + # Gaussian elimination over GF(2) -- based on Ref. 3. + n_rows, n_cols = np.shape(A)[0], np.shape(A)[1] A, zs, z_sln, piv_idx = np.array(A), [], [-1]*zdim, 0 - for i in range(NC): + for i in range(n_cols): Aij_max = 0. max_idx = piv_idx - for j in range(piv_idx, NR): - if (A[j, i] > Aij_max): + for j in range(piv_idx, n_rows): + if A[j, i] > Aij_max: max_idx = j Aij_max = A[j, i] - elif (j == NR-1 and Aij_max == 0.): + elif j == n_rows-1 and Aij_max == 0.: piv_idx = max_idx Aij_max = -1. - if (Aij_max > 0.): - if (max_idx > piv_idx): + if Aij_max > 0.: + if max_idx > piv_idx: A[[piv_idx, max_idx]] = A[[max_idx, piv_idx]] - for j in range(piv_idx+1, NR): - if (A[j, i] == 1.): - A[j, i:NC] = np.fmod(A[j, i:NC] + A[piv_idx, i:NC], 2) + for j in range(piv_idx+1, n_rows): + if A[j, i] == 1.: + A[j, i:n_cols] = np.fmod(A[j, i:n_cols] + A[piv_idx, i:n_cols], 2) piv_idx += 1 - b = A[0:NR, NC-1].tolist() - for i in range(NR-1, -1, -1): + b = A[0:n_rows, n_cols-1].tolist() + for i in range(n_rows-1, -1, -1): col_idx, zf = -1., [] - for j in range(NC-1): - if (A[i, j] == 1.): - if (col_idx == -1): + for j in range(n_cols-1): + if A[i, j] == 1.: + if col_idx == -1: col_idx = j else: zf.append(j) - if (col_idx >= 0.): + if col_idx >= 0.: zs.append([col_idx, zf, b[i]]) for z in (zs): b = z[2] for zf in (z[1]): - if (z_sln[zf] == -1): + if z_sln[zf] == -1: z_sln[zf] = choice([0., 1.]) b = (b + z_sln[zf]) % 2 z_sln[z[0]] = b return z_sln + def init_ilc_by_diag(qubit_ham, ilc_gens, qmf_var_params): - ilc_gens.ins(0, QubitOperator.identity()) + ilc_gens.insert(0, QubitOperator.identity()) n_var_params = len(ilc_gens) - # Form the Hamiltonian and overlap matrices (see Appendix B, Refs. 1 & 2). H = np.zeros((n_var_params, n_var_params), dtype=complex) S = np.ones((n_var_params, n_var_params), dtype=complex) @@ -184,7 +176,7 @@ def init_ilc_by_diag(qubit_ham, ilc_gens, qmf_var_params): for j in range(i + 1, n_var_params): H[j, i] = get_op_expval(ilc_gens[j] * H_i, qmf_var_params) S[j, i] = get_op_expval(ilc_gens[j] * ilc_gens[i], qmf_var_params) - if (i == 0): + if i == 0: H[j, i] *= 1j S[j, i] *= 1j @@ -200,8 +192,8 @@ def init_ilc_by_diag(qubit_ham, ilc_gens, qmf_var_params): if c0[0].real < 0.: beta_1 = np.pi - beta_1 for i in range(2, n_var_params): - denom_sum += pow(c0[i].real, 2.) + pow(c0[i].imag, 2.) + denom_sum += pow(c0[i].real, 2.) + pow(c0[i].imag, 2.) beta = np.arcsin(c0[i] / np.sqrt(denom_sum)) ilc_var_params.append(beta.real) del ilc_gens[0] - return ilc_var_params + return ilc_var_params diff --git a/tangelo/toolboxes/ansatz_generator/ilc.py b/tangelo/toolboxes/ansatz_generator/ilc.py index 50574515d..6657a9cf2 100755 --- a/tangelo/toolboxes/ansatz_generator/ilc.py +++ b/tangelo/toolboxes/ansatz_generator/ilc.py @@ -62,25 +62,35 @@ class ILC(Ansatz): max_ilc_gens (int or None): Maximum number of generators allowed in the ansatz. If None, one generator from each DIS group is selected. If int, then min(|DIS|, max_ilc_gens) generators are selected in order of decreasing |dEILC/dtau|. Default, None. + n_trotter (int): Number of Trotterization steps for the ILC ansatz. Default, 1. + scfdata (tuple): tuple containing an instance of OpenFermion MolecularData and a + FermionOperator corresponding to the fermionic Hamiltonian. verbose (bool): Flag for QCC verbosity. Default, False. """ def __init__(self, molecule, mapping="JW", up_then_down=False, ilc_op_list=None, qmf_circuit=None, qmf_var_params=None, qubit_ham=None, ilc_tau_guess=1.e-2, - deilc_dtau_thresh=1.e-3, max_ilc_gens=None, n_trotter=1, verbose=False): + deilc_dtau_thresh=1.e-3, max_ilc_gens=None, n_trotter=1, scfdata=None, verbose=False): - self.molecule = molecule - self.n_spinorbitals = self.molecule.n_active_sos - if self.n_spinorbitals % 2 != 0: - raise ValueError("The total number of spin-orbitals should be even.") + if molecule: + self.molecule = molecule + self.n_spinorbitals = self.molecule.n_active_sos + if self.n_spinorbitals % 2 != 0: + raise ValueError("The total number of spin-orbitals should be even.") - self.n_electrons = self.molecule.n_active_electrons - self.spin = molecule.spin + self.spin = molecule.spin + self.fermi_ham = self.molecule.fermionic_hamiltonian + elif scfdata: + self.molecule = scfdata[0] + self.n_spinorbitals = 2 * self.molecule.n_orbitals + self.spin = self.molecule.multiplicity - 1 + self.fermi_ham = scfdata[1] + self.n_electrons = self.molecule.n_electrons self.mapping = mapping self.n_qubits = get_qubit_number(self.mapping, self.n_spinorbitals) self.up_then_down = up_then_down if self.mapping.upper() == "JW" and not self.up_then_down: - warnings.warn("The QCC ansatz requires spin-orbital ordering to be all spin-up " + warnings.warn("The ILC ansatz requires spin-orbital ordering to be all spin-up " "first followed by all spin-down for the JW mapping.", RuntimeWarning) self.up_then_down = True @@ -94,7 +104,6 @@ def __init__(self, molecule, mapping="JW", up_then_down=False, ilc_op_list=None, self.verbose = verbose if qubit_ham is None: - self.fermi_ham = self.molecule.fermionic_hamiltonian self.qubit_ham = fermion_to_qubit_mapping(self.fermi_ham, self.mapping, self.n_spinorbitals, self.n_electrons, self.up_then_down, self.spin) @@ -115,13 +124,14 @@ def __init__(self, molecule, mapping="JW", up_then_down=False, ilc_op_list=None, self.n_electrons, self.mapping, self.up_then_down, self.spin, self.verbose) print(pure_var_params) - self.dis = construct_dis(pure_var_params, self.qubit_ham, self.deilc_dtau_thresh, + self.dis = construct_dis(self.qubit_ham, pure_var_params, self.deilc_dtau_thresh, self.verbose) print(self.dis) + self.max_ilc_gens = len(self.dis) if self.max_ilc_gens is None\ + else min(len(self.dis), self.max_ilc_gens) self.acs = construct_acs(self.dis, self.max_ilc_gens, self.n_qubits) print(self.acs) - self.n_var_params = len(self.acs) if self.max_ilc_gens is None\ - else min(len(self.acs), self.max_ilc_gens) + self.n_var_params = len(self.acs) print(self.n_var_params) else: self.dis = None @@ -204,7 +214,7 @@ def build_circuit(self, var_params=None): # Obtain quantum circuit through trotterization of the list of ILC operators pauli_word_gates = [] - for i in range(self.n_trotter): + for _ in range(self.n_trotter): for ilc_op in self.ilc_op_list: pauli_word, coef = list(ilc_op.terms.items())[0] pauli_word_gates += exp_pauliword_to_gates(pauli_word, float(coef/self.n_trotter), variational=True) @@ -224,7 +234,7 @@ def update_var_params(self, var_params): self.ilc_op_list = self._get_ilc_op() pauli_word_gates = [] - for i in range(self.n_trotter): + for _ in range(self.n_trotter): for ilc_op in self.ilc_op_list: pauli_word, coef = list(ilc_op.terms.items())[0] pauli_word_gates += exp_pauliword_to_gates(pauli_word, float(coef/self.n_trotter), variational=True) @@ -268,11 +278,11 @@ def _get_ilc_op(self): """ # Rebuild DIS & ACS in case qubit_ham changed or they and qubit_op_list don't exist - if self.rebuild_dis or self.rebuild_acs or ((self.dis is None or self.acs is None) and self.ilc_op_list is None): + if self.rebuild_dis or self.rebuild_acs or ((not self.dis or not self.acs) and not self.ilc_op_list): pure_var_params = purify_qmf_state(self.qmf_var_params, self.n_spinorbitals, self.n_electrons, self.mapping, self.up_then_down, self.spin, self.verbose) - self.dis = construct_dis(pure_var_params, self.qubit_ham, self.deilc_dtau_thresh, + self.dis = construct_dis(self.qubit_ham, pure_var_params, self.deilc_dtau_thresh, self.verbose) self.acs = construct_acs(self.dis, self.max_ilc_gens, self.n_qubits) self.n_var_params = len(self.acs) if self.max_ilc_gens is None\ diff --git a/tangelo/toolboxes/ansatz_generator/qmf.py b/tangelo/toolboxes/ansatz_generator/qmf.py index 27fbc1a43..3d60fd2b9 100755 --- a/tangelo/toolboxes/ansatz_generator/qmf.py +++ b/tangelo/toolboxes/ansatz_generator/qmf.py @@ -67,18 +67,28 @@ class QMF(Ansatz): = n_electrons, = spin_z * (spin_z + 1), and = spin_z, where spin_z = spin // 2. Key, value pairs are case sensitive and mu > 0. Default, {"init_params": "hf_state"}. + scfdata (tuple): tuple containing an instance of OpenFermion MolecularData and a + FermionOperator corresponding to the fermionic Hamiltonian. """ - def __init__(self, molecule, mapping="JW", up_then_down=False, init_qmf=None): - - self.molecule = molecule - self.n_spinorbitals = self.molecule.n_active_sos - if self.n_spinorbitals % 2 != 0: - raise ValueError("The total number of spin-orbitals should be even.") - - self.n_orbitals = self.n_spinorbitals // 2 - self.n_electrons = self.molecule.n_active_electrons - self.spin = molecule.spin + def __init__(self, molecule=None, mapping="JW", up_then_down=False, init_qmf=None, scfdata=None): + + if molecule: + self.molecule = molecule + self.n_spinorbitals = self.molecule.n_active_sos + if self.n_spinorbitals % 2 != 0: + raise ValueError("The total number of spin-orbitals should be even.") + + self.n_orbitals = self.n_spinorbitals // 2 + self.spin = molecule.spin + self.fermi_ham = self.molecule.fermionic_hamiltonian + elif scfdata: + self.molecule = scfdata[0] + self.n_orbitals = self.molecule.n_orbitals + self.n_spinorbitals = 2 * self.n_orbitals + self.spin = self.molecule.multiplicity - 1 + self.fermi_ham = scfdata[1] + self.n_electrons = self.molecule.n_electrons self.mapping = mapping self.n_qubits = get_qubit_number(self.mapping, self.n_spinorbitals) @@ -91,8 +101,7 @@ def __init__(self, molecule, mapping="JW", up_then_down=False, init_qmf=None): # Supported reference state initialization self.supported_reference_state = {"HF"} - # Get the mean-field fermionic Hamiltonian and check for penalty terms - self.fermi_ham = self.molecule.fermionic_hamiltonian + # Add any penalty terms to the fermionic Hamiltonian if isinstance(self.init_qmf, dict): if "init_params" not in self.init_qmf.keys(): raise KeyError(f"Missing key 'init_params' in {self.init_qmf}. " diff --git a/tangelo/toolboxes/ansatz_generator/tests/test_qcc.py b/tangelo/toolboxes/ansatz_generator/tests/test_qcc.py index 8d392aea2..12e3cdd5d 100644 --- a/tangelo/toolboxes/ansatz_generator/tests/test_qcc.py +++ b/tangelo/toolboxes/ansatz_generator/tests/test_qcc.py @@ -72,7 +72,7 @@ def test_qcc_h2(self): # Build the QCC ansatz, which sets the QMF parameters automatically if none are passed qcc_var_params = [0.22613627] qcc_op_list = [QubitOperator("X0 Y1 Y2 Y3")] - qcc_ansatz = QCC(mol_H2_sto3g, up_then_down=True, qubit_op_list=qcc_op_list) + qcc_ansatz = QCC(mol_H2_sto3g, up_then_down=True, qcc_op_list=qcc_op_list) # Build a QMF + QCC circuit qcc_ansatz.build_circuit() From 344bbb4bb8c12dc6403dfe8a0bed5d6a7820d84f Mon Sep 17 00:00:00 2001 From: Marc Coons Date: Thu, 17 Feb 2022 17:10:45 -0500 Subject: [PATCH 04/28] fixes to ILC ansatz; tests for selected FNO and MI-FNO Hamiltonians for Be --- .../toolboxes/ansatz_generator/_qubit_ilc.py | 17 ++-- tangelo/toolboxes/ansatz_generator/ilc.py | 15 ++-- .../FNO/VS_1/Be1_cc-pvdz_singlet.hdf5 | Bin 0 -> 27016 bytes .../Be_iso_tests/FNO/VS_1/test_ilc_be_iso.py | 58 +++++++++++++ .../FNO/VS_2/Be1_cc-pvdz_singlet.hdf5 | Bin 0 -> 27016 bytes .../Be_iso_tests/FNO/VS_2/test_ilc_be_iso.py | 71 ++++++++++++++++ .../MI-FNO/VS_1/Be1_cc-pvdz_singlet_:1.hdf5 | Bin 0 -> 27016 bytes .../MI-FNO/VS_1/test_ilc_be_iso.py | 55 ++++++++++++ .../MI-FNO/VS_2/Be1_cc-pvdz_singlet_:1.hdf5 | Bin 0 -> 27016 bytes .../MI-FNO/VS_2/test_ilc_be_iso.py | 65 ++++++++++++++ .../MI-FNO/VS_3/Be1_cc-pvdz_singlet_:1.hdf5 | Bin 0 -> 27016 bytes .../MI-FNO/VS_3/test_ilc_be_iso.py | 70 +++++++++++++++ .../MI-FNO/VS_4/Be1_cc-pvdz_singlet_:1.hdf5 | Bin 0 -> 29064 bytes .../MI-FNO/VS_4/test_ilc_be_iso.py | 70 +++++++++++++++ .../MI-FNO/VS_5/Be1_cc-pvdz_singlet_:1.hdf5 | Bin 0 -> 30482 bytes .../MI-FNO/VS_5/test_ilc_be_iso.py | 80 ++++++++++++++++++ 16 files changed, 485 insertions(+), 16 deletions(-) create mode 100644 tangelo/toolboxes/ansatz_generator/tests/Be_iso_tests/FNO/VS_1/Be1_cc-pvdz_singlet.hdf5 create mode 100644 tangelo/toolboxes/ansatz_generator/tests/Be_iso_tests/FNO/VS_1/test_ilc_be_iso.py create mode 100644 tangelo/toolboxes/ansatz_generator/tests/Be_iso_tests/FNO/VS_2/Be1_cc-pvdz_singlet.hdf5 create mode 100644 tangelo/toolboxes/ansatz_generator/tests/Be_iso_tests/FNO/VS_2/test_ilc_be_iso.py create mode 100644 tangelo/toolboxes/ansatz_generator/tests/Be_iso_tests/MI-FNO/VS_1/Be1_cc-pvdz_singlet_:1.hdf5 create mode 100644 tangelo/toolboxes/ansatz_generator/tests/Be_iso_tests/MI-FNO/VS_1/test_ilc_be_iso.py create mode 100644 tangelo/toolboxes/ansatz_generator/tests/Be_iso_tests/MI-FNO/VS_2/Be1_cc-pvdz_singlet_:1.hdf5 create mode 100644 tangelo/toolboxes/ansatz_generator/tests/Be_iso_tests/MI-FNO/VS_2/test_ilc_be_iso.py create mode 100644 tangelo/toolboxes/ansatz_generator/tests/Be_iso_tests/MI-FNO/VS_3/Be1_cc-pvdz_singlet_:1.hdf5 create mode 100644 tangelo/toolboxes/ansatz_generator/tests/Be_iso_tests/MI-FNO/VS_3/test_ilc_be_iso.py create mode 100644 tangelo/toolboxes/ansatz_generator/tests/Be_iso_tests/MI-FNO/VS_4/Be1_cc-pvdz_singlet_:1.hdf5 create mode 100644 tangelo/toolboxes/ansatz_generator/tests/Be_iso_tests/MI-FNO/VS_4/test_ilc_be_iso.py create mode 100644 tangelo/toolboxes/ansatz_generator/tests/Be_iso_tests/MI-FNO/VS_5/Be1_cc-pvdz_singlet_:1.hdf5 create mode 100644 tangelo/toolboxes/ansatz_generator/tests/Be_iso_tests/MI-FNO/VS_5/test_ilc_be_iso.py diff --git a/tangelo/toolboxes/ansatz_generator/_qubit_ilc.py b/tangelo/toolboxes/ansatz_generator/_qubit_ilc.py index 48f28bb18..9233744e6 100644 --- a/tangelo/toolboxes/ansatz_generator/_qubit_ilc.py +++ b/tangelo/toolboxes/ansatz_generator/_qubit_ilc.py @@ -38,8 +38,8 @@ def construct_acs(dis_gens, max_ilc_gens, n_qubits): ac_idxs, not_ac_idxs = [], [] - while max_ilc_gens > len(ac_idxs) and len(ac_idxs) + len(not_ac_idxs) < len(dis_gens): - gen_idxs, ilc_gens = [idx for idx in range(len(dis_gens)) if idx not in not_ac_idxs], [] + while max_ilc_gens > len(ac_idxs) and len(ac_idxs) + len(not_ac_idxs) < max_ilc_gens: + gen_idxs, ilc_gens = [idx for idx in range(max_ilc_gens) if idx not in not_ac_idxs], [] n_gens = len(gen_idxs) ng2, ngnq = n_gens * (n_gens + 1) // 2, n_gens * n_qubits # cons_mat --> A and z_vec --> z in Appendix A, Refs. 1 & 2. @@ -169,10 +169,11 @@ def init_ilc_by_diag(qubit_ham, ilc_gens, qmf_var_params): n_var_params = len(ilc_gens) # Form the Hamiltonian and overlap matrices (see Appendix B, Refs. 1 & 2). H = np.zeros((n_var_params, n_var_params), dtype=complex) - S = np.ones((n_var_params, n_var_params), dtype=complex) + S = np.zeros((n_var_params, n_var_params), dtype=complex) for i in range(n_var_params): H_i = qubit_ham * ilc_gens[i] H[i, i] = get_op_expval(ilc_gens[i] * H_i, qmf_var_params) + S[i, i] = 1. + 0j for j in range(i + 1, n_var_params): H[j, i] = get_op_expval(ilc_gens[j] * H_i, qmf_var_params) S[j, i] = get_op_expval(ilc_gens[j] * ilc_gens[i], qmf_var_params) @@ -182,18 +183,22 @@ def init_ilc_by_diag(qubit_ham, ilc_gens, qmf_var_params): # Solve the generalized eigenvalue problem E, c = scipy.linalg.eigh(a=np.matrix(H), b=np.matrix(S), lower=True, driver="gvd") + print(" MCSCF eigenvalues from matrix diagonalization = ", E) # Compute the ILC parameters according to Appendix C, Ref. 1). - c0 = c[:, 0]#.real + c0 = c[:, 0] + print(" Ground state eigenvector = ", c0) denom_sum, ilc_var_params = 0., [] for i in range(2): denom_sum += pow(c0[i].real, 2.) + pow(c0[i].imag, 2.) - beta_1 = np.arcsin(c0[1]) / np.sqrt(denom_sum) - if c0[0].real < 0.: + beta_1 = np.arcsin(c0[1] / np.sqrt(denom_sum)) + if c0[0].real > 0.: beta_1 = np.pi - beta_1 + ilc_var_params.append(beta_1.real) for i in range(2, n_var_params): denom_sum += pow(c0[i].real, 2.) + pow(c0[i].imag, 2.) beta = np.arcsin(c0[i] / np.sqrt(denom_sum)) ilc_var_params.append(beta.real) del ilc_gens[0] + print(" ILC var params (beta's in Appendix C, ref. 1) = ", ilc_var_params) return ilc_var_params diff --git a/tangelo/toolboxes/ansatz_generator/ilc.py b/tangelo/toolboxes/ansatz_generator/ilc.py index 6657a9cf2..52b58bfc4 100755 --- a/tangelo/toolboxes/ansatz_generator/ilc.py +++ b/tangelo/toolboxes/ansatz_generator/ilc.py @@ -123,16 +123,12 @@ def __init__(self, molecule, mapping="JW", up_then_down=False, ilc_op_list=None, pure_var_params = purify_qmf_state(self.qmf_var_params, self.n_spinorbitals, self.n_electrons, self.mapping, self.up_then_down, self.spin, self.verbose) - print(pure_var_params) self.dis = construct_dis(self.qubit_ham, pure_var_params, self.deilc_dtau_thresh, self.verbose) - print(self.dis) self.max_ilc_gens = len(self.dis) if self.max_ilc_gens is None\ else min(len(self.dis), self.max_ilc_gens) self.acs = construct_acs(self.dis, self.max_ilc_gens, self.n_qubits) - print(self.acs) self.n_var_params = len(self.acs) - print(self.n_var_params) else: self.dis = None self.acs = None @@ -144,7 +140,6 @@ def __init__(self, molecule, mapping="JW", up_then_down=False, ilc_op_list=None, self.supported_initial_var_params = {"zeros", "diag", "ilc_tau_guess"} # Default starting parameters for initialization - self.pauli_to_angles_mapping = {} self.default_reference_state = "HF" self.var_params_default = "diag" self.var_params = None @@ -291,9 +286,9 @@ def _get_ilc_op(self): # Build the ILC qubit operator list ilc_op_list = [] - for i in range(self.n_var_params - 1, 1, -1): - ilc_op_list.append(-0.5 * self.var_params[i - 1] * self.acs[i]) - ilc_op_list.append(-1. * self.var_params[0] * self.acs[1]) - for i in range(2, self.n_var_params): - ilc_op_list.append(-0.5 * self.var_params[i - 1] * self.acs[i]) + for i in range(self.n_var_params - 1, 0, -1): + ilc_op_list.append(-0.5 * self.var_params[i] * self.acs[i]) + ilc_op_list.append(-1. * self.var_params[0] * self.acs[0]) + for i in range(1, self.n_var_params): + ilc_op_list.append(-0.5 * self.var_params[i] * self.acs[i]) return ilc_op_list diff --git a/tangelo/toolboxes/ansatz_generator/tests/Be_iso_tests/FNO/VS_1/Be1_cc-pvdz_singlet.hdf5 b/tangelo/toolboxes/ansatz_generator/tests/Be_iso_tests/FNO/VS_1/Be1_cc-pvdz_singlet.hdf5 new file mode 100644 index 0000000000000000000000000000000000000000..451b68bf946088e9b4bdacc5209f3595a4e23302 GIT binary patch literal 27016 zcmeI43se(V8h~$jtXS#;m7;Zvv>uh+wTQOIViEC$(sf%|(aQQDhGZZqd6|TV55%s= znpW3$D~kANeQm4g5wy@AA?lt(VLjq5dTJHLts>P`q`F2B3p+FSzr-YD0<{KW?{Jv= z`2V^0pKty<^Uux9d>B1(Qn%+`d=7Doj}P)fJ^3tIHN#?5;2KgU@_CREL286_p9ju} z5ch{rU#Rzk_HFg1Pm78|=r@S8Pg2+nUGc`bP*NeonJ_4u`s2(YDDRWtj2z0*FXK!W zl*i7*nIK{~)YGJM5{Ql(7sFX*0>27|blPb)pD$L8RXu z{e-va3*xoCg!N2BJcwjkPtSniafz+VN$oz+t4km?t)~T`7tORTC$*uFIn;;LJXtDY zuT6Z{(7y<-Xd)4=fa;dxjZL^EY zGa3&huimzJ-I7vWc=L#zUrfGh&fXJIdoR20o2HWfQFG5Nof2MMw&6grGUjOIKkgOY zUoO9LIw1Dik-hmv=gX0(Tg;Z@KhBDo=b!x7h_Av*21{-%Iu>AFsjt?K-Wr@=k?>Ld zD$~~QbCjp58^V{&F@@G#JeM3^X}A=6YSh9@XX5M5jeP!o+KJUm4u;(MZdpjvjMQSE z|1Q7t^_29&kV$h_UeGTHk1hLWiC$S?=>B$r`GVhsjDi!pR9|k~Vm_M}_}0qq$)Rt) zl;8Z`x!fVy=A4FF-Kv*Md!eEJsWZ~!8}AM9x_oi!`q|PU$F5&%y4v6TyWf_MN-Q5c z5arbjJ9{~=NIz{cfWb{XPgwIbN3+uxV8O z>Z)~#!$Ly;eQP;-criSt$vdFjbhjTmzIs;O^YVhq>$iKE-t(Kf=B<*7;@aUu3;yO? z6qz0`O^UdDK67G|>GYAz`mepGUZ|<;W0Z~9rYiI=+ca18?rV`XHA|RF)$7+akF4F= z{o|R-?#_%@SYXa9UGo#luOFyn4`ZR${O3={Y*<$2tr-3}&uF|0y3Ft}OvKi$VG7G&R^zj@7+h*Oa}14eJy)gwMHt+b-KA@yKm zVB90qBfbC4%((QEl>uGP_3(|%%2<4+?)dJ&7b3Z%vf{>y!t#DM1Ls`7I<3db0a=;v zZf7oD`OZ)gh=%kTbIscQX^9e`1So+HCV=Bg4~S9_3hp9o5V`MJ>L;xCYm?f?mBBm$ z5e^d{izhJ|5-*YH$djE<;p%#_;UbP0$MTAfxtqn4*GNA8y&-%Fpd@r`Hb+A{CNO_4hu z??&JP6Y1a{_Wo_`RE}fsAI^BR_xkuz8~tTzr{F^7chljtogOc2mJ#%f?2Mw&k_CEM z^Mtez`T_LhNy2(Q;AZJ^Cwd?bf&dqqN4CGW90I+yuL!r-R;j&TVK*TCw?EEa-$pdE%W}LKI1(4^7o~FL6I%@rLX&<*PCHnJH9S`-bOCQwhnEV=;8CQ7fOfT z`UVPjORNtbAgmV+;xjJ@>yfBDEJ#?dvrCnnGy;^u+y zHOmK08uzEzD7J3;v>9B&3mlKT-M+jIY>o2>*~f##!-AXLuCG~ zJM12rZ?i3Xvh!%%rcTdup12!d6vSuXXJaPPy&y9`SbIKA}n2V z-FL`+#RMmgKL(CAQM2Vl?<)uynoPPy&cO8h=h70SR2XS<$u--fnA9_(( zF9_`WVY0AZM@j8}_r!O~s!n6z zzP73sGfGBk(2*~%s>K>zg2EtC!iQMZ;`u3T8A=GLQ>02pq7&;FZHiJ)dO=8@C}z}* zE{PZ_m8dmpg;b)nX-|{N=#&zzSfMsBNxGJ95cG;0Q7|q2Y1E83K_gpWyMZB1(`vm+ zJHpBksX{M%yn-)tjp}474iI1F8iR78RPnev;>wSy^X13XkyL8aK3B%y`V>o4+Lry` z^<VXN{~i82Kck@H0sf3!Q#%5~#;k6Vmq z*YVg(d9cHv<$Y4oKFY(2$KTz82l>807@XjYal+@eAQ129Evy#<;v-&^ UJlMfuxxOEuJgj)+x&@E_108qIg#Z8m literal 0 HcmV?d00001 diff --git a/tangelo/toolboxes/ansatz_generator/tests/Be_iso_tests/FNO/VS_1/test_ilc_be_iso.py b/tangelo/toolboxes/ansatz_generator/tests/Be_iso_tests/FNO/VS_1/test_ilc_be_iso.py new file mode 100644 index 000000000..4bca754ee --- /dev/null +++ b/tangelo/toolboxes/ansatz_generator/tests/Be_iso_tests/FNO/VS_1/test_ilc_be_iso.py @@ -0,0 +1,58 @@ +# Copyright 2021 Good Chemistry Company. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# FNO, VS = 1, SCBK +# +# REFERENCE ENERGIES: +# EQMF = -14.57233763 Eh +# EILC = -14.58926922 Eh + +import numpy as np + +from openfermion.chem import MolecularData +from openfermion.transforms import get_fermion_operator +from tangelo.linq import Simulator +from tangelo.toolboxes.ansatz_generator.qmf import QMF +from tangelo.toolboxes.ansatz_generator.ilc import ILC + +sim = Simulator() + +file_name = "./Be1_cc-pvdz_singlet.hdf5" + +# Prepare classical data from SCF calculation +molecule = MolecularData(filename=file_name) +fermi_ham = get_fermion_operator(molecule.get_molecular_hamiltonian()) +scfdata = (molecule, fermi_ham) + +# Instantiate QMF ansatz -- note that SCBK mapping doesn't work for VS = 1 +qmf = QMF(molecule=None, mapping="SCBK", up_then_down=True, init_qmf=None, scfdata=scfdata) +qmf.set_var_params([np.pi, 0., np.pi, 0., 0., 0., 0., 0.]) +qmf.build_circuit() +print(qmf.var_params) +print(qmf.circuit) + +energy = sim.get_expectation_value(qmf.qubit_ham, qmf.circuit) +print(" EQMF = ", energy) +print(" EQMF (ref.) = -14.57233763") + +ilc = ILC(molecule=None, mapping="SCBK", up_then_down=True, qmf_circuit=qmf.circuit, qmf_var_params=qmf.var_params, + qubit_ham=qmf.qubit_ham, max_ilc_gens=None, n_trotter=1, scfdata=scfdata) +ilc.build_circuit() +#print(ilc.qmf_var_params) +#print(ilc.var_params) +#print(ilc.qmf_circuit) +#print(ilc.circuit) +energy = sim.get_expectation_value(ilc.qubit_ham, ilc.circuit) +print(" EILC = ", energy) +print(" EILC (ref.) = -14.58926922") diff --git a/tangelo/toolboxes/ansatz_generator/tests/Be_iso_tests/FNO/VS_2/Be1_cc-pvdz_singlet.hdf5 b/tangelo/toolboxes/ansatz_generator/tests/Be_iso_tests/FNO/VS_2/Be1_cc-pvdz_singlet.hdf5 new file mode 100644 index 0000000000000000000000000000000000000000..5d3a1ec6308676c6030b58c89180517850b9a3ae GIT binary patch literal 27016 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 literal 0 HcmV?d00001 diff --git a/tangelo/toolboxes/ansatz_generator/tests/Be_iso_tests/MI-FNO/VS_1/test_ilc_be_iso.py b/tangelo/toolboxes/ansatz_generator/tests/Be_iso_tests/MI-FNO/VS_1/test_ilc_be_iso.py new file mode 100644 index 000000000..27b8ee471 --- /dev/null +++ b/tangelo/toolboxes/ansatz_generator/tests/Be_iso_tests/MI-FNO/VS_1/test_ilc_be_iso.py @@ -0,0 +1,55 @@ +# Copyright 2021 Good Chemistry Company. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# MI-FNO ID # 1, VS = 1, SCBK +# +# REFERENCE ENERGIES: +# EQMF = -14.57233763 Eh +# EILC = -14.58922316 Eh + +from openfermion.chem import MolecularData +from openfermion.transforms import get_fermion_operator +from tangelo.linq import Simulator +from tangelo.toolboxes.ansatz_generator.qmf import QMF +from tangelo.toolboxes.ansatz_generator.ilc import ILC + +sim = Simulator() + +file_name = "./Be1_cc-pvdz_singlet_:1.hdf5" + +# Prepare classical data from SCF calculation +molecule = MolecularData(filename=file_name) +fermi_ham = get_fermion_operator(molecule.get_molecular_hamiltonian()) +scfdata = (molecule, fermi_ham) + +# Instantiate QMF ansatz -- note that SCBK mapping works for VS = 1 +qmf = QMF(molecule=None, mapping="SCBK", up_then_down=True, init_qmf=None, scfdata=scfdata) +qmf.build_circuit() +#print(qmf.var_params) +#print(qmf.circuit) + +energy = sim.get_expectation_value(qmf.qubit_ham, qmf.circuit) +print(" EQMF = ", energy) +print(" EQMF (ref.) = -14.57233763") + +ilc = ILC(molecule=None, mapping="SCBK", up_then_down=True, qmf_circuit=qmf.circuit, qmf_var_params=qmf.var_params, + qubit_ham=qmf.qubit_ham, max_ilc_gens=None, n_trotter=1, scfdata=scfdata) +ilc.build_circuit() +#print(ilc.qmf_var_params) +#print(ilc.var_params) +#print(ilc.qmf_circuit) +#print(ilc.circuit) +energy = sim.get_expectation_value(ilc.qubit_ham, ilc.circuit) +print(" EILC = ", energy) +print(" EILC (ref.) = -14.58922316") diff --git a/tangelo/toolboxes/ansatz_generator/tests/Be_iso_tests/MI-FNO/VS_2/Be1_cc-pvdz_singlet_:1.hdf5 b/tangelo/toolboxes/ansatz_generator/tests/Be_iso_tests/MI-FNO/VS_2/Be1_cc-pvdz_singlet_:1.hdf5 new file mode 100644 index 0000000000000000000000000000000000000000..2efe3cf6a14dd727f3e335069e9a8f57417bce75 GIT binary patch literal 27016 zcmeI44NwzT9>8A+D(F>3YE`U2Wwdk@1+|FM6m_&>tA#@ysaFXhEF{;E4M~7dh&bm5 zbnFS$6R}og<_4ustK8uL#Tv2o2CLlJp4!nvr4=ij1^nRns6n~ieeZ!u$Ob9~V&5|C z`}qI&y|=&pzuo_P*?k{`E`8&?}o}Z*IN^@Y#9K<0WKeh=rHiZFc|a48L==DgwmChVWb$PizF~2L+Q|$ zU?dNv7p;R4Z`^Q{PlBOIAavQ{aJFTY$gX@*ShW7>w%3~dh6-W5GGv#~eg#(THwbkr z6g=HVlhTJA0c^jKsAb+L%tH2q8!XY%2^uD$qBU}@oK~`NcEh(Re7}Raw)d;w4qGY! zBLJ?qU=sf^6OlM)9KT*A7Ei;G-otNE!z1H*m*d66t0NrF6_HgUWad#~Xno;Sh^i;V{^Q+WF?MqHPP+jp#aHEGuNTA=?+gDqBKf3et z5z+3nQdzC$T>BUF?3T*<|5Q|sYCALj(&^({R$Y~T+*W>`{>sOAETx53$Rnert)Y$g z4v%qH1l~(m>R<9G-&Oy$aQKgDhnfmwHhix)7{AIFjGR+Hyi7E{s5s503-ukg*@3oG*-#p42Kj!Gwj7iEC?^SIFR%U0ea{5aMy^&sf z>GTL=?U~(W`7_UyeIeYPkrJ{regCRco_Vi6b z`t#Q+lCOAXi$;eVYW;j3hFsa#baaK%FGAE%UhlOgD)sEyJHaW=&90ly)|_1WbMK3o|+QTc6!E)0}Zpq2Q`SP&)+r~|f?5yl`f3DSQ=9iLNOkA#E(AwUQW3<2m@Iw3Cw0N)Y( z<>GPACO3Y)-jmzx426U1RG!l!%9^1MO%$?9?p> zteS!E-&>xs{taj7`ZDjt-`_v%V}H*YuxmRI1zFmMEgt=SKJMtFy=)qY(|O`D@cgb0 zIY_!F{vxxGUKTT>$hYMoy}W2XEdUyjo-CeU&jmeJbTS)yNbHRqTvR+T|GlLc>E%r2 zFR!msOS^(*fa~wSpPk!BJNm8oS^0zqV~joX{s*6N9uv87shf95_qcT4C@`-J^=s>X z*O5MQ$?9v@wu>IT4?6=Ls%@etf3>*vzFz!#p-B9#sT*o>5cbN0ye&iym0|ln{A=y1 z)FJK4HV6%H37KGupipx%Y$ z=A6vLSdD~)03kpK5CVh%AwUQa0uD-`uYNT43(Z42_|cI8kS$v^`#>w9Ll!?Qk_! zFy}=4Xj48UMFnnAYx6zO!JY57 zHLmF40I$4dx{rK*{rr5fsHbYS`av02EU{tuI&`(MnmH#juE1nSNC*%Dga9Ex2oM5< zz_UTX(vJ@1PE6noU+ggV2m7CXT7I@S*G{3+unyQ_?j0DmdHCOozYWJt+`+xZAcsMR(zRO|bPyU_L4Tt#7 z$$mWQ9D)b7%Xo@TptR~#oQ{!b%klt!wStJtMf zxkHqa1a9R@r5LWX+P&VYn%1(btt!Qof|6?0c;!{4m{!NgwGstdh*c?0Ok&b_SyrG- zk}4>PTCAp2NeT_F1prx`m{L;ec-&B_L`f^JUW?DDE zF8!D~kV?(UXVY-0Pq8FH)qNhEUM!syqo7Rk<5_A9C5DtViTNUqT>@A99+ip@>{XsD zQ6wQm_vtNJy$szi){dn9WJeY3K{XMCmp71c?(clm~@cIHl=mwV+$$xM2 zM&bf@e!Xxc&f)*L1jvw{%M||dhPu>#_nrSblwE})yUKfq{|ZJTy#gP8JsHxIZQ$1n oLgLEt{CY!OYCpTW?GSclKOR359!%%xdA>Ii9wt1R9D>LH0Yr1rDgXcg literal 0 HcmV?d00001 diff --git a/tangelo/toolboxes/ansatz_generator/tests/Be_iso_tests/MI-FNO/VS_2/test_ilc_be_iso.py b/tangelo/toolboxes/ansatz_generator/tests/Be_iso_tests/MI-FNO/VS_2/test_ilc_be_iso.py new file mode 100644 index 000000000..66fb92ad9 --- /dev/null +++ b/tangelo/toolboxes/ansatz_generator/tests/Be_iso_tests/MI-FNO/VS_2/test_ilc_be_iso.py @@ -0,0 +1,65 @@ +# Copyright 2021 Good Chemistry Company. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# MI-FNO ID # 1, VS = 2, SCBK +# +# REFERENCE ENERGIES: +# EQMF = -14.57233763 Eh +# EILC = -14.60296795 Eh + +import numpy as np + +from openfermion.chem import MolecularData +from openfermion.transforms import get_fermion_operator +from tangelo.linq import Simulator +from tangelo.toolboxes.ansatz_generator.qmf import QMF +from tangelo.toolboxes.ansatz_generator.ilc import ILC + +sim = Simulator() + +file_name = "./Be1_cc-pvdz_singlet_:1.hdf5" + +# Prepare classical data from SCF calculation +molecule = MolecularData(filename=file_name) +fermi_ham = get_fermion_operator(molecule.get_molecular_hamiltonian()) +scfdata = (molecule, fermi_ham) + +# Instantiate QMF ansatz -- note that SCBK mapping doesn't work for VS = 2 in Tangelo +# The short term fix is to manually pass the correct QMF var params +qmf = QMF(molecule=None, mapping="SCBK", up_then_down=True, init_qmf=None, scfdata=scfdata) +qmf.set_var_params([np.pi, np.pi, np.pi, np.pi, 0., 0., 0., 0.]) +qmf.build_circuit() +#print(qmf.var_params) +#print(qmf.circuit) + +energy = sim.get_expectation_value(qmf.qubit_ham, qmf.circuit) +print(" EQMF = ", energy) +print(" EQMF (ref.) = -14.57233763") + +# n_trotter = 1 is sufficient to achieve satisfactory agreement with the reference value +ilc = ILC(molecule=None, mapping="SCBK", up_then_down=True, qmf_circuit=qmf.circuit, qmf_var_params=qmf.var_params, + qubit_ham=qmf.qubit_ham, max_ilc_gens=None, n_trotter=1, scfdata=scfdata) +ilc.build_circuit() +#print(ilc.qmf_var_params) +#print(ilc.var_params) +#print(ilc.qmf_circuit) +#print(ilc.circuit) +energy = sim.get_expectation_value(ilc.qubit_ham, ilc.circuit) +print(" EILC (n_trot = 1) = ", energy) +ilc.n_trotter = 2 +ilc.build_circuit() +# n_trotter = 2 is slightly better but not worth doubling the ilc circuit +energy = sim.get_expectation_value(ilc.qubit_ham, ilc.circuit) +print(" EILC (n_trot = 2) = ", energy) +print(" EILC (ref.) = -14.60296795") diff --git a/tangelo/toolboxes/ansatz_generator/tests/Be_iso_tests/MI-FNO/VS_3/Be1_cc-pvdz_singlet_:1.hdf5 b/tangelo/toolboxes/ansatz_generator/tests/Be_iso_tests/MI-FNO/VS_3/Be1_cc-pvdz_singlet_:1.hdf5 new file mode 100644 index 0000000000000000000000000000000000000000..b40f38a8f6845be6f3c746acef290467550cc0d0 GIT binary patch literal 27016 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 literal 0 HcmV?d00001 diff --git a/tangelo/toolboxes/ansatz_generator/tests/Be_iso_tests/MI-FNO/VS_4/test_ilc_be_iso.py b/tangelo/toolboxes/ansatz_generator/tests/Be_iso_tests/MI-FNO/VS_4/test_ilc_be_iso.py new file mode 100644 index 000000000..f225fe58d --- /dev/null +++ b/tangelo/toolboxes/ansatz_generator/tests/Be_iso_tests/MI-FNO/VS_4/test_ilc_be_iso.py @@ -0,0 +1,70 @@ +# Copyright 2021 Good Chemistry Company. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# MI-FNO ID # 1, VS = 4, SCBK +# +# REFERENCE ENERGIES: +# EQMF = -14.57233763 Eh +# EILC = -14.61629810 Eh + +import numpy as np + +from openfermion.chem import MolecularData +from openfermion.transforms import get_fermion_operator +from tangelo.linq import Simulator +from tangelo.toolboxes.ansatz_generator.qmf import QMF +from tangelo.toolboxes.ansatz_generator.ilc import ILC + +sim = Simulator() + +file_name = "./Be1_cc-pvdz_singlet_:1.hdf5" + +# Prepare classical data from SCF calculation +molecule = MolecularData(filename=file_name) +fermi_ham = get_fermion_operator(molecule.get_molecular_hamiltonian()) +scfdata = (molecule, fermi_ham) + +# Instantiate QMF ansatz -- note that SCBK mapping doesn't work for VS = 4 in Tangelo +# The short term fix is to manually pass the correct QMF var params +qmf = QMF(molecule=None, mapping="SCBK", up_then_down=True, init_qmf=None, scfdata=scfdata) +qmf.set_var_params([np.pi, np.pi, np.pi, 0., np.pi, np.pi, np.pi, 0., 0., 0., 0., 0., 0., 0., 0., 0.]) +qmf.build_circuit() +#print(qmf.var_params) +#print(qmf.circuit) + +energy = sim.get_expectation_value(qmf.qubit_ham, qmf.circuit) +print(" EQMF = ", energy) +print(" EQMF (ref.) = -14.57233763") + + +ilc = ILC(molecule=None, mapping="SCBK", up_then_down=True, qmf_circuit=qmf.circuit, qmf_var_params=qmf.var_params, + qubit_ham=qmf.qubit_ham, max_ilc_gens=None, n_trotter=1, scfdata=scfdata) +ilc.build_circuit() +#print(ilc.qmf_var_params) +#print(ilc.var_params) +#print(ilc.qmf_circuit) +#print(ilc.circuit) +energy = sim.get_expectation_value(ilc.qubit_ham, ilc.circuit) +print(" EILC (n_trot = 1) = ", energy) + +ilc.n_trotter = 2 +ilc.build_circuit() +energy = sim.get_expectation_value(ilc.qubit_ham, ilc.circuit) +print(" EILC (n_trot = 2) = ", energy) + +ilc.n_trotter = 3 +ilc.build_circuit() +energy = sim.get_expectation_value(ilc.qubit_ham, ilc.circuit) +print(" EILC (n_trot = 3) = ", energy) +print(" EILC (ref.) = -14.61629810") diff --git a/tangelo/toolboxes/ansatz_generator/tests/Be_iso_tests/MI-FNO/VS_5/Be1_cc-pvdz_singlet_:1.hdf5 b/tangelo/toolboxes/ansatz_generator/tests/Be_iso_tests/MI-FNO/VS_5/Be1_cc-pvdz_singlet_:1.hdf5 new file mode 100644 index 0000000000000000000000000000000000000000..81577268a6f4d8f97dbd36ee5dd1ad38a2b33744 GIT binary patch literal 30482 zcmeHQ2V4`&)(=e(uA-t?kYX=XMX5q8pm;&xf{0QQA%-F)KY{)s3G=+WdGihssj8#Dm)lB~ZpU)5hRz^s7a-y|)y+Fum`ivFGl z1mpoicR+tAa_d+kOpt_x83Ji&X^1c-#)$`Inxf2i+ZSyo1t_zh?u*0DSglUts{Up8$L{7EAL90pyhROY?m%m4RqZI1WDm1&C0jp{c$x+!88rfWmhiP7kmG zlRE)(^6?uG&faEQnI*4M1i!MgwfW#*^?G2f zxd<|2MqVkwFtyxRPrGFdmpVspekU;T1&eW}K^yYbL^i_3&Cu1iBkf+!6h1$AJM_FR za%aj;r;@}*k-9E_5tqD?SI#B-SUjGCHH{VCa7|2|vAqNp??T`7niWlC(@ROaKFimd z?q8uIUFV47N`KQU!jj-J;5(L6Eh_1PL7h1grU`XlF=!d)-?i6hem0-6+HHZK7}~UR6m7}Ck zM=CE@P29c*5^ejVu=jS6j~7la>nawdooPQ`ZtL_ADH1rNdIzQB?D)10TRVH#Wsh#q zv)ZB8m>Ctr@!mN_$>8QoQUZg?p~J!ku%78TLoC}&`e5Kd?3k7C%i&;}iDZLzLsn>Q zla!N4vLiiLH0F6GLTs*Gdk9sLi97my zH^v>xNwb(S@n~jqI$+T&n|)eDMKik-wGqMC)_%&}Uss$HSALTaD`1-L4 zPQ+KmQ}R0d`NpyyK}V;{HSeU@})No9r`mL#fH5Mm5HF4$M7n zu2Ig^FNwHm^`JFUg!de$c}L!OG2XHYAFq~!GPd`ZMAAL%Q{X5S9lUImw$UmT`M-l(1CQ-grD0&X2!VbTQN%hny@{2*!TEO4&1)2<>Gqi zqdP$9U4n00)ANxk;%!pVAx=W`h5b78Ue4hmPLniId8Ky{ozqhtYj22I6%D6v3=)6r z)8?lqC?HhR?*^L2NGjtQHlhnhH-n0NG=?v)Rq{Pu`ldiL$2ulvFb`}8gjawt3tP9Z zhx-lKzgpq`_BZdrwh;Mu=^-tX!{yFyrNROG5AD7nf5f1-8y}OtN6%<|X)lUF*(n;roq@_V?B$A-xbUI4ais{W^P{XIukPvu&}3vq z=nY1N?_$GQn!I*wEeFHV+hg*PHCijZ*Q$wd*jF~8axq5fpkC<}4DNZ%%44gK)xOZ0 z*3cWCdy5Z&4&{WJ2&BJrou}uV+XFq!6wIq)D2y7r9gC8zpKF1K@F$~P!afH z2$20sTEI&If%Y;kT{UIhb7k$)eE)0I#eU^p5(G44H}Nn2BpDfsuLLYVL3D)u6k^^Q zg1boe7>|+|zwU2Jk=Z`&$dKz@>~DU<5hoY>;pfG8kj4Gn@6UcUQRTkgPc@*Q3l(#5 zKZAdGAAV$o-^Sygj=vUw3lIRpF2?2dkL<<|tLw+(ap}JtkI(i(TvdJDUW@@8C~GhB zkS*4W0*x#96y^5qfx@%DZFzs$bLJ9OW(@3geN-}+8_cmJgn zXR&^K3y*(#ef0TGeYx^W;6mBowFMj`TH~d~atHX(aG6;7{w)LoA>}^MGA{-49LrPF&o-*R+2MA9)_82iXFxZQ@$GS&H;~ zxtHdX1ISei-OxpaC|jYX?@%0 zo18^#nkWWIj~9>2Q8L&6^Gtk>g(wf@`725o4)||ht3DsFE(-lioW3Aa*r*{D0V)Di z1gHp55uhSKMSzOHGDYBf{b-6`_+|JL{OI!s$k-B_CjS3N{Ah!3j4y;`A;XtAQT^zJ z_)t?+1gHp55uhSKMSzL`6@lfBz~cS>h0ixo_Ms`^cl*tYes@#?c$z{BAX~hz{GZ+L z|JJ&Kg2UIs@5bj}-=9yBA|pr?f%yL)Sy%Kg7{$d2+!yPLFY%(TD;8r$&7mSdMSzL` z6#*&&R0OC9EN283`_Xcw?$FSaAAI@JAN;53f1jVtLz0t@mSK`CG0Dd!ibwhXioZ=> zH<1ee&FO2;xp=Pn8|Edl+|Ms0ehW9MpS=(-YKn>g6#*&&R0OC9P!XUaKt+Ix02Ki$ z0?P#f^6$O~zp4R#KN5aqX~~BH@@>hb`5FMS+``*)iwa!?a#FS|A}z?^0p#mGwIKVK zN!UUHHSDx}3^%gFz4-zU8*Ogd*!RTFGfSpM)4;DL>CxjqJY`t+%d1{k2i<*LLDnz! zo~Xh@9li+TwQ$|4^9Sn$^M@voNfW+PMuYQ16Lzi0Fk})^yWZ}y^`*$U*=)uA)}dN# z!q}THBV3;MMHh%%Iz7dlT^DP|*If8CRwnYS#E7oy@VzHyH;~AoyZ2^ApPcm%5w%Y9 zg1q<*I=%&8^eQY=y6s4Q_c7#$*HZ7c*$Jvn?zM-EL<~7#IB&r%rnOIu=v|OKE5zcl zFBxYCYz)j3myOMIv!<+eC6T}m44m1o#Bsim+#;P^;JSuzcmZ=u6vL5!Z zrBdCwQYZ0-)abLrxgn~Qx_0R-7_f-j0W>C{o>Nf4FV|{4R5w@j zU|*?51U3`Gmo`>coXHF6m%*2!Jk+l?s9$^I))U~=5a%m(q35YgtkOWIv06REaZbo< zw4`pJ=6>zIUIDE1Z1cXveRp=`O?B>`*Ozs27-o}>(Dy!a!mcRPYZV;BI;>T3S1H87 zdVf^@yre$1BD7|ZN5nkgROZJg{jJwq8uw|n$V*l6@%npU)ep70S%ztPr|~9bcaJ1- zzJ##f7t-B*rX0^^?~syX85Z|Y+eCuTM;9UFI6LL=YIdsmLIi3+_Q zC98SGM*Z@D<9Nv@ue71qx)_PHx*aF{4bMxIYlqsR3!PfieWyn@J&opIyg$mkWBX)) zfA33fUOu}kshxLQs#zu{8LPKQKFhQ?InkjhbUeR$o9uz6a}#xIG8|7E8K;JN8HurC zB+-}mLHV`N52iMF*M|-Y?`}4ql9LDPrH!IaYI@$X0b`o;TU!DS*Eqc#sPskOD>#!T z&}gEyj;%O;oV_tf>R!3YVasT-v#Z`?x=|giL!6_zS=`{7?mOvcYeS+ z8XyB=F~75)#^#E5*W7G~Y@KaS%N2Md&-_TfXf(!I)-*wBv=*mr@7%2Hq%rX3w$LDK ztC?fL!14UzzSzO54EebN+a70homZFMW&h?Gc9Q<|`ed0I^(QEXG2SghdON_;H=-nZ z@b$Y`HEYkMX4kTsY%N|l6&m4jhtm|%KC;cqzvv^(e+(ZQbmj?iZ_^xAl<&jZZzBbOFy+|33;Mk}SIMSBx-BrQ=M=RYAO0S0$eDXWu)- z>V&^O-0E(Ycx$>oH@S4cqZjpZ=0pqlQT|L%cU9Pmz?;G^9@%zw@S65Z)gpO6tg=e+ z$Cm0Am!B`b)_HcKEZHD8SW}-*BbayvyN0S) z?Fo~_L)b%_o$cMWA)_|_=6VWvdEv|_N)xxsHv7Ds&+=)#cN8uB2pMJpwLQ`s#-Abh z)?3xcQ!RWjw0oZO;e>t7*xTlU&@p#Oqx%7W?jfo^?Ut3v2$=xTpN+Kojvd z2ai}>J-`uq7>D#PxKC5xTE^IXrs=am_58A zQyQXq&QFoSLt3pG6SbaTFaOxN!(a`M0GnP!D_2}l8Dmz18iLtMuL%n4E2Td_JY$*U zHL^y{;n{ER@Ea2Kt7kn+ys|gMIcRATlv+w^)U-4(&*(yF_xqq#IqQN8a_85$A=RCG zA`B}YzMA<{tabhZwrc`)%v149{E-%G`qN&A6LQtw((Yt)=sQt^gF9Y)TM=b$EgXpK zYwHL!nD?wpH*l8ie9>%g{$#ZF!@KdCyf@J<=VH^({fSM8bM6S`%hoi&>P1!P?+!dA z(YnR_`U!-NscTrdM6QD{SjhXrI+qYR<$90ih966U+3*(6lLRbwaWf`ut{R)xaFLww z8Lld<)q|i;h0~gcqg^5=P?25U50JiNY3HE9DbRJ~5oXDA%T_xvej&$Er)r2-rSVKU z^Ue2_mCfkA7@f;KdXadL&x8i&!`SSLo1sGTQpR(9C!EU@OZJu`*hM#mit%PioGveQ zHG-Xk;#hEPh4;KoxbERlMH$^%HD%dF69Z{4X4F$qvV{3e@FP3+98*PVJ=mu_)PU{P zk$~P-&T)(PqSF=Ac^UFgRa`)&>YMNyW&>X758#vW>9OK|D#IWA%zGOvwe}du!Ok5I z7lL@;k`1K1h!)(<|2naws_^j#`M$_znxUG~U6H@bXVJ6E8oD9(Ngey}v7c{s*cy8l zSRfP|F>T!riRxlfQRZ(H)WGXL8!1I(m9iu3NtS?t$??Dl3Fq@J8cPHkN|J zYK2PGU3q0~SA0ab=P`%T5XNcVeVWJfBi^VcqpGD{Fy>nJ`=Isyf+^k~2^KoMG9pVT zE1Vmcc7pMW<*913!0C3{@LGqLZ9_D_#|*By8#D^T5dP-y>ed(oFYk5U>4TK{l zpnDLGIl0PDTx9!5YFfPKdTVjB#u?=6_ZclgvJ=;)cFJcB=+%|~-u4^8B$I-ar$T#1 znRn6p_Oz1~8D&2h(qd&R7Jvt^n{;c3y z!r+y@6TU(Kk)zB7#}l@}`GBS9BunqbsWgOi2HjNZgAv^V29jFvUhTc>hbqZuB#&2U z>WTIy#*hTok&XS}_8r6-#J&P!7tiCsITmSOBn`@Ep4>M*yK{=8YJ}2;%Qo)ksE_%i zY0Q~LdK*E*>zJRSeOI*Ea^j?hE5ZrJdgc^HqgR3L0>}8;UIvob{8uKCn?_{%>DuFo zLxg5Si|mlzr$O>>HqP*q$Gh4xY>o+1N9O71EHvu6GvlR)3nK z=hz*O6OEy7cVChw9OAdS+o;b5#`V&P70*r7#HgYyVtE5{=e8ze^xt5 zC8Uz44>gGApG%YL|0tbwP-{wi1x}N!)|pk1Rc-q&w$ZiyhU6A=_H|U~ADGYPrY9&| zqo3(>E)Fyu7zCCLgDLJPpy2Yb2a?it9r|0#D*ROmvxvEHgWO30I!f2jWo<8Pm>VVv zNt!%!8&%TEHN!e7*WOu}aj-+M0S4m+K^bP(TYWe-e$~MhyqaWyh?98o^}=Rz1EPqe z8~e9~(af%&;hH3BulcYKFrGk}!puTM^QqzAIsB1v7^0SU@e{W>&^XN-H3cfgTiY z3b)Xw2ny3NMVK1Gbd0~K4`B_rFxD}H8k$eV4%J0mviq`t zr48b5^G(bSE)WD}h`jXI3{sj{sl_GIyV*!-Vr4*D4`%pRb`UZB7j{zmFYF)~?2Gz| zY4S%GpgJaIpZY=KyM(aPHHI(9*C(PzK#719%!=@C65_{5phj>zO2NOSTk9BG0T5B< zi(ra^{37Acv*!=X!k&K~9#|?K1RMDDW*?d^Djo~)=w23hP~P+=22614^p>93cmQ%Z z+tPf>0QvUP53Yd>06r$prR)3EsXuSt=~-5KB?su$cKxMC@I1g5zGG=V1AuSz^3r@_ q0J(6((tN)<_2=nT@3PRVpNB^u6%T@Oe7?T-Q}I}U$H20{ Date: Tue, 22 Feb 2022 14:12:36 -0500 Subject: [PATCH 05/28] cleaning up ILC ansatz files; updated tests for Be system: tests for all FNO and MI-FNO Hamiltonians --- .../toolboxes/ansatz_generator/_qubit_ilc.py | 84 +++++++----------- tangelo/toolboxes/ansatz_generator/ilc.py | 8 +- .../Be_iso_tests/FNO/VS_2/test_ilc_be_iso.py | 4 +- .../FNO/VS_3/Be1_cc-pvdz_singlet.hdf5 | Bin 0 -> 29064 bytes .../Be_iso_tests/FNO/VS_3/test_ilc_be_iso.py | 71 +++++++++++++++ .../FNO/VS_4/Be1_cc-pvdz_singlet.hdf5 | Bin 0 -> 30621 bytes .../Be_iso_tests/FNO/VS_4/test_ilc_be_iso.py | 71 +++++++++++++++ .../FNO/VS_5/Be1_cc-pvdz_singlet.hdf5 | Bin 0 -> 35101 bytes .../Be_iso_tests/FNO/VS_5/test_ilc_be_iso.py | 71 +++++++++++++++ .../MI-FNO/VS_4/test_ilc_be_iso.py | 4 +- .../MI-FNO/VS_5/test_ilc_be_iso.py | 4 +- 11 files changed, 253 insertions(+), 64 deletions(-) create mode 100644 tangelo/toolboxes/ansatz_generator/tests/Be_iso_tests/FNO/VS_3/Be1_cc-pvdz_singlet.hdf5 create mode 100644 tangelo/toolboxes/ansatz_generator/tests/Be_iso_tests/FNO/VS_3/test_ilc_be_iso.py create mode 100644 tangelo/toolboxes/ansatz_generator/tests/Be_iso_tests/FNO/VS_4/Be1_cc-pvdz_singlet.hdf5 create mode 100644 tangelo/toolboxes/ansatz_generator/tests/Be_iso_tests/FNO/VS_4/test_ilc_be_iso.py create mode 100644 tangelo/toolboxes/ansatz_generator/tests/Be_iso_tests/FNO/VS_5/Be1_cc-pvdz_singlet.hdf5 create mode 100644 tangelo/toolboxes/ansatz_generator/tests/Be_iso_tests/FNO/VS_5/test_ilc_be_iso.py diff --git a/tangelo/toolboxes/ansatz_generator/_qubit_ilc.py b/tangelo/toolboxes/ansatz_generator/_qubit_ilc.py index 9233744e6..c9a6cf9c9 100644 --- a/tangelo/toolboxes/ansatz_generator/_qubit_ilc.py +++ b/tangelo/toolboxes/ansatz_generator/_qubit_ilc.py @@ -37,9 +37,11 @@ def construct_acs(dis_gens, max_ilc_gens, n_qubits): - ac_idxs, not_ac_idxs = [], [] - while max_ilc_gens > len(ac_idxs) and len(ac_idxs) + len(not_ac_idxs) < max_ilc_gens: - gen_idxs, ilc_gens = [idx for idx in range(max_ilc_gens) if idx not in not_ac_idxs], [] + """ DOCSTRING + """ + bad_sln_idxs, good_sln = [], False + while not good_sln: + gen_idxs, ilc_gens = [idx for idx in range(max_ilc_gens) if idx not in bad_sln_idxs], [] n_gens = len(gen_idxs) ng2, ngnq = n_gens * (n_gens + 1) // 2, n_gens * n_qubits # cons_mat --> A and z_vec --> z in Appendix A, Refs. 1 & 2. @@ -51,29 +53,25 @@ def construct_acs(dis_gens, max_ilc_gens, n_qubits): p_idx, pauli = paulis if 'X' in pauli or 'Y' in pauli: z_vec[idx * n_qubits + p_idx] = 1. - - # Form the triangular matrix A (Appendix A, Refs. 1 & 2). + # Form the triangular matrix-vector product A * z; last column is the soln vec (Appendix A, Refs. 1 & 2). r_idx = 0 for i in range(n_gens): - cons_matrix[r_idx, i*n_qubits:(i+1)*n_qubits] = z_vec[i*n_qubits:(i+1)*n_qubits] + cons_matrix[r_idx, i * n_qubits:(i+1) * n_qubits] = z_vec[i * n_qubits:(i+1) * n_qubits] cons_matrix[r_idx, ngnq] = 1 r_idx += 1 for j in range(i+1, n_gens): - cons_matrix[r_idx, i*n_qubits:(i+1)*n_qubits] = z_vec[j*n_qubits:(j+1)*n_qubits] - cons_matrix[r_idx, j*n_qubits:(j+1)*n_qubits] = z_vec[i*n_qubits:(i+1)*n_qubits] + cons_matrix[r_idx, i * n_qubits:(i+1) * n_qubits] = z_vec[j * n_qubits:(j+1) * n_qubits] + cons_matrix[r_idx, j * n_qubits:(j+1) * n_qubits] = z_vec[i * n_qubits:(i+1) * n_qubits] cons_matrix[r_idx, ngnq] = 1 r_idx += 1 - - # Solve Az = 1 + # Solve A * z = 1 z_sln = gauss_elim_over_gf2(cons_matrix, ngnq) - - # Check for a bad solutions - candidate_gens, good_sln = [], True + # Check solution: odd # of Y ops, at least two flip indices, and mutually anti-commutes + good_sln = True for i in range(n_gens): - n_flip, n_y, gen_list = 0, 0, [] + n_flip, n_y, gen_idx, gen_list = 0, 0, gen_idxs[i], [] for j in range(n_qubits): - gen = None - idx = i * n_qubits + j + gen, idx = None, i * n_qubits + j if z_vec[idx] == 1.: n_flip += 1 if z_sln[idx] == 0.: @@ -87,42 +85,24 @@ def construct_acs(dis_gens, max_ilc_gens, n_qubits): if gen: gen_list.append(gen) if n_flip < 2 or n_y % 2 == 0: - good_sln = False - gen_idx = gen_idxs.pop(i) - if gen_idx not in not_ac_idxs: - not_ac_idxs.append(gen_idx) - if gen_idx in gen_idxs: - gen_idxs.remove(gen_idx) - if gen_idx in ac_idxs: - ac_idxs.remove(gen_idx) - else: - candidate_gens.append(QubitOperator(tuple(gen_list), 1.)) - - # For good solutions check that they anti-commute and update ilc_gens - if good_sln: - for i, gen_i in enumerate(candidate_gens): - anticommutes = True - gen_idx = gen_idxs[i] + if good_sln and gen_idx not in bad_sln_idxs: + bad_sln_idxs.append(gen_idx) + good_sln = False + elif good_sln: + gen_i = QubitOperator(tuple(gen_list), 1.) for gen_j in ilc_gens: - anti_com = gen_i * gen_j + gen_j * gen_i - if anti_com != QubitOperator.zero(): - anticommutes = False - if gen_idx not in not_ac_idxs: - not_ac_idxs.append(gen_idx) - if gen_idx in gen_idxs: - gen_idxs.remove(gen_idx) - if gen_idx in ac_idxs: - ac_idxs.remove(gen_idx) - if anticommutes: + if gen_i * gen_j != -1. * gen_j * gen_i: + if good_sln and gen_idx not in bad_sln_idxs: + bad_sln_idxs.append(gen_idx) + good_sln = False + if good_sln: ilc_gens.append(gen_i) - if gen_idx not in ac_idxs: - ac_idxs.append(gen_idx) - if gen_idx in not_ac_idxs: - not_ac_idxs.remove(gen_idx) return ilc_gens def gauss_elim_over_gf2(A, zdim): + """ DOCSTRING + """ # Gaussian elimination over GF(2) -- based on Ref. 3. n_rows, n_cols = np.shape(A)[0], np.shape(A)[1] A, zs, z_sln, piv_idx = np.array(A), [], [-1]*zdim, 0 @@ -165,6 +145,8 @@ def gauss_elim_over_gf2(A, zdim): def init_ilc_by_diag(qubit_ham, ilc_gens, qmf_var_params): + """ DOCSTRING + """ ilc_gens.insert(0, QubitOperator.identity()) n_var_params = len(ilc_gens) # Form the Hamiltonian and overlap matrices (see Appendix B, Refs. 1 & 2). @@ -180,20 +162,18 @@ def init_ilc_by_diag(qubit_ham, ilc_gens, qmf_var_params): if i == 0: H[j, i] *= 1j S[j, i] *= 1j - # Solve the generalized eigenvalue problem E, c = scipy.linalg.eigh(a=np.matrix(H), b=np.matrix(S), lower=True, driver="gvd") - print(" MCSCF eigenvalues from matrix diagonalization = ", E) - + print(" Eigenvalues from matrix diagonalization = ", E) # Compute the ILC parameters according to Appendix C, Ref. 1). c0 = c[:, 0] - print(" Ground state eigenvector = ", c0) + if c0[0].real > 0.: + c0 *= -1. + print(" Ground state eigenvector c0 = ", c0) denom_sum, ilc_var_params = 0., [] for i in range(2): denom_sum += pow(c0[i].real, 2.) + pow(c0[i].imag, 2.) beta_1 = np.arcsin(c0[1] / np.sqrt(denom_sum)) - if c0[0].real > 0.: - beta_1 = np.pi - beta_1 ilc_var_params.append(beta_1.real) for i in range(2, n_var_params): denom_sum += pow(c0[i].real, 2.) + pow(c0[i].imag, 2.) diff --git a/tangelo/toolboxes/ansatz_generator/ilc.py b/tangelo/toolboxes/ansatz_generator/ilc.py index 52b58bfc4..5f7ba4a06 100755 --- a/tangelo/toolboxes/ansatz_generator/ilc.py +++ b/tangelo/toolboxes/ansatz_generator/ilc.py @@ -239,10 +239,6 @@ def update_var_params(self, var_params): def _get_ilc_op(self): """Returns the ILC operator by selecting one generator from n_var_params DIS groups. - The ILC qubit operator is constructed as a linear combination of generators using the - parameter set {tau} as coefficients: ILC operator = -0.5 * SUM_k P_k * tau_k. - The exponentiated terms of the ILC operator, U = PROD_k exp(-0.5j * tau_k * P_k), - are used to build a ILC circuit. Args: rebuild_dis (bool): Rebuilds DIS and sets ilc_op_list to None. @@ -279,9 +275,9 @@ def _get_ilc_op(self): self.spin, self.verbose) self.dis = construct_dis(self.qubit_ham, pure_var_params, self.deilc_dtau_thresh, self.verbose) + self.max_ilc_gens = len(self.dis) if self.max_ilc_gens is None\ + else min(len(self.dis), self.max_ilc_gens) self.acs = construct_acs(self.dis, self.max_ilc_gens, self.n_qubits) - self.n_var_params = len(self.acs) if self.max_ilc_gens is None\ - else min(len(self.acs), self.max_ilc_gens) self.ilc_op_list = None # Build the ILC qubit operator list diff --git a/tangelo/toolboxes/ansatz_generator/tests/Be_iso_tests/FNO/VS_2/test_ilc_be_iso.py b/tangelo/toolboxes/ansatz_generator/tests/Be_iso_tests/FNO/VS_2/test_ilc_be_iso.py index 32259e82a..72edaf1f7 100644 --- a/tangelo/toolboxes/ansatz_generator/tests/Be_iso_tests/FNO/VS_2/test_ilc_be_iso.py +++ b/tangelo/toolboxes/ansatz_generator/tests/Be_iso_tests/FNO/VS_2/test_ilc_be_iso.py @@ -16,7 +16,7 @@ # # REFERENCE ENERGIES: # EQMF = -14.57233763 Eh -# EILC = -14.60306939 Eh +# EILC = -14.60306743 Eh import numpy as np @@ -67,5 +67,5 @@ # n_trotter = 3 is slightly better but not worth doubling the ilc circuit energy = sim.get_expectation_value(ilc.qubit_ham, ilc.circuit) print(" EILC (n_trot = 3) = ", energy) -print(" EILC (ref.) = -14.60306939") +print(" EILC (ref.) = -14.60306743") diff --git a/tangelo/toolboxes/ansatz_generator/tests/Be_iso_tests/FNO/VS_3/Be1_cc-pvdz_singlet.hdf5 b/tangelo/toolboxes/ansatz_generator/tests/Be_iso_tests/FNO/VS_3/Be1_cc-pvdz_singlet.hdf5 new file mode 100644 index 0000000000000000000000000000000000000000..004fc3f863198576624725718e6562d8fab763d5 GIT binary patch literal 29064 zcmeI43p`Y58^DibQz}W46v|fGHkZwcH0eSqmA-CDX&PgUDP~-Ai>d9hQmJ%7jM0{| zyF`c*L$}XTLKjIgTjb72xy%?d-<&z`QOKxozg6}-$Ip2$&vV|(|9#$bp7YK*=a9MS z(orKOj)34zRu&oxjS)xDoe*pRE=UfVM&;Wxfcd zuakhe*`hUY%d&u7X@M}t_U~=LnGsXp!35KJo&{Jo9FO8@Ezp!T~7 zv`hnp0l!2vN<-~?3-qyxl^14`Yi-!xml0S>Sb37+(oMBw9 z@3PCQgn7fGpOUXXb1Gz2o#D8#1v!*3i<|!I+BU6xxIFfH@%3eIYTKUiV`m#Y=Y|Zo z!3C$X^&4E6?ETAHcf8bLhu|Y@lN=vLAM@GBFi@O4_nXEBTA8L*q+=&(U*hZOk@;~t zrqZ4QJ~gw3SRd3G{-fYPg1@no(A57N=|!V*>&P($P_&9c8nfEm--DL9_hDEkk8-TR z>1l0gTA|-AleDz`KYO>;&WkKINnBw}t9D_t>V5MrQ=fWt+`aBRvrxgTt}-q=Lk(%AtyN zIcJ=32GzB@CXGq(?%=+lUWw;DZJ)TY@yzR-$7Fiz)3p5|b+(VM#^6Fy=R~LPo}azc zIa}FRlU#_SoOqRzlap%@vT5ZFk5Y#2edDH+o-Y&6@AZw~aJ
)t(BPCVGIlR7=z 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 lQr|j*K{9Z5RX-@~YT$S@U_3+<`O`B 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 literal 0 HcmV?d00001 diff --git a/tangelo/toolboxes/ansatz_generator/tests/Be_iso_tests/FNO/VS_5/test_ilc_be_iso.py b/tangelo/toolboxes/ansatz_generator/tests/Be_iso_tests/FNO/VS_5/test_ilc_be_iso.py new file mode 100644 index 000000000..06ddcd00b --- /dev/null +++ b/tangelo/toolboxes/ansatz_generator/tests/Be_iso_tests/FNO/VS_5/test_ilc_be_iso.py @@ -0,0 +1,71 @@ +# Copyright 2021 Good Chemistry Company. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# FNO, VS = 5, SCBK +# +# REFERENCE ENERGIES: +# EQMF = -14.57233763 Eh +# EILC = -14.61597638 Eh + +import numpy as np + +from openfermion.chem import MolecularData +from openfermion.transforms import get_fermion_operator +from tangelo.linq import Simulator +from tangelo.toolboxes.ansatz_generator.qmf import QMF +from tangelo.toolboxes.ansatz_generator.ilc import ILC + +sim = Simulator() + +file_name = "./Be1_cc-pvdz_singlet.hdf5" + +# Prepare classical data from SCF calculation +molecule = MolecularData(filename=file_name) +fermi_ham = get_fermion_operator(molecule.get_molecular_hamiltonian()) +scfdata = (molecule, fermi_ham) + +# Instantiate QMF ansatz -- note that SCBK mapping doesn't work for VS = 5 +qmf = QMF(molecule=None, mapping="SCBK", up_then_down=True, init_qmf=None, scfdata=scfdata) +qmf.set_var_params([np.pi, 0., 0., 0., 0., 0., np.pi, 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.]) +qmf.build_circuit() +print(qmf.var_params) +print(qmf.circuit) + +energy = sim.get_expectation_value(qmf.qubit_ham, qmf.circuit) +print(" EQMF = ", energy) +print(" EQMF (ref.) = -14.57233763") + +ilc = ILC(molecule=None, mapping="SCBK", up_then_down=True, qmf_circuit=qmf.circuit, qmf_var_params=qmf.var_params, + qubit_ham=qmf.qubit_ham, max_ilc_gens=None, n_trotter=1, scfdata=scfdata) +ilc.build_circuit() +#print(ilc.qmf_var_params) +#print(ilc.var_params) +#print(ilc.qmf_circuit) +#print(ilc.circuit) +energy = sim.get_expectation_value(ilc.qubit_ham, ilc.circuit) +print(" EILC (n_trot = 1) = ", energy) + +ilc.n_trotter = 2 +ilc.build_circuit() +# n_trotter = 2 is slightly better but not worth doubling the ilc circuit +energy = sim.get_expectation_value(ilc.qubit_ham, ilc.circuit) +print(" EILC (n_trot = 2) = ", energy) + +ilc.n_trotter = 3 +ilc.build_circuit() +# n_trotter = 3 is slightly better but not worth doubling the ilc circuit +energy = sim.get_expectation_value(ilc.qubit_ham, ilc.circuit) +print(" EILC (n_trot = 3) = ", energy) +print(" EILC (ref.) = -14.61597638") + diff --git a/tangelo/toolboxes/ansatz_generator/tests/Be_iso_tests/MI-FNO/VS_4/test_ilc_be_iso.py b/tangelo/toolboxes/ansatz_generator/tests/Be_iso_tests/MI-FNO/VS_4/test_ilc_be_iso.py index f225fe58d..be09a91cb 100644 --- a/tangelo/toolboxes/ansatz_generator/tests/Be_iso_tests/MI-FNO/VS_4/test_ilc_be_iso.py +++ b/tangelo/toolboxes/ansatz_generator/tests/Be_iso_tests/MI-FNO/VS_4/test_ilc_be_iso.py @@ -16,7 +16,7 @@ # # REFERENCE ENERGIES: # EQMF = -14.57233763 Eh -# EILC = -14.61629810 Eh +# EILC = -14.61580907 Eh import numpy as np @@ -67,4 +67,4 @@ ilc.build_circuit() energy = sim.get_expectation_value(ilc.qubit_ham, ilc.circuit) print(" EILC (n_trot = 3) = ", energy) -print(" EILC (ref.) = -14.61629810") +print(" EILC (ref.) = -14.61580907") diff --git a/tangelo/toolboxes/ansatz_generator/tests/Be_iso_tests/MI-FNO/VS_5/test_ilc_be_iso.py b/tangelo/toolboxes/ansatz_generator/tests/Be_iso_tests/MI-FNO/VS_5/test_ilc_be_iso.py index a30a7aed2..70dcad9be 100644 --- a/tangelo/toolboxes/ansatz_generator/tests/Be_iso_tests/MI-FNO/VS_5/test_ilc_be_iso.py +++ b/tangelo/toolboxes/ansatz_generator/tests/Be_iso_tests/MI-FNO/VS_5/test_ilc_be_iso.py @@ -16,7 +16,7 @@ # # REFERENCE ENERGIES: # EQMF = -14.57233763 Eh -# EILC = -14.61639218 Eh +# EILC = -14.61590226 Eh import numpy as np @@ -77,4 +77,4 @@ ilc.build_circuit() energy = sim.get_expectation_value(ilc.qubit_ham, ilc.circuit) print(" EILC (n_trot = 5) = ", energy) -print(" EILC (ref.) = -14.61639218") +print(" EILC (ref.) = -14.61590226") From 896fc307003224baa56b86d9cae15de6fb640f93 Mon Sep 17 00:00:00 2001 From: Marc Coons Date: Thu, 24 Feb 2022 13:15:42 -0500 Subject: [PATCH 06/28] small changes to ilc.py --- tangelo/toolboxes/ansatz_generator/ilc.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tangelo/toolboxes/ansatz_generator/ilc.py b/tangelo/toolboxes/ansatz_generator/ilc.py index 5f7ba4a06..ff2ab29f4 100755 --- a/tangelo/toolboxes/ansatz_generator/ilc.py +++ b/tangelo/toolboxes/ansatz_generator/ilc.py @@ -284,7 +284,7 @@ def _get_ilc_op(self): ilc_op_list = [] for i in range(self.n_var_params - 1, 0, -1): ilc_op_list.append(-0.5 * self.var_params[i] * self.acs[i]) - ilc_op_list.append(-1. * self.var_params[0] * self.acs[0]) + ilc_op_list.append(-self.var_params[0] * self.acs[0]) for i in range(1, self.n_var_params): ilc_op_list.append(-0.5 * self.var_params[i] * self.acs[i]) return ilc_op_list From 0c147a9421b7047d66ffe56a1a11205f37d01818 Mon Sep 17 00:00:00 2001 From: Marc Coons Date: Wed, 2 Mar 2022 15:05:24 -0500 Subject: [PATCH 07/28] improvements to qmf, qcc, ilc, and tests --- tangelo/toolboxes/ansatz_generator/ilc.py | 13 +-- tangelo/toolboxes/ansatz_generator/qcc.py | 7 +- tangelo/toolboxes/ansatz_generator/qmf.py | 21 +++-- .../FNO/VS_1/Be1_cc-pvdz_singlet.hdf5 | Bin 27016 -> 0 bytes .../Be_iso_tests/FNO/VS_1/test_ilc_be_iso.py | 58 ------------- .../FNO/VS_2/Be1_cc-pvdz_singlet.hdf5 | Bin 27016 -> 0 bytes .../Be_iso_tests/FNO/VS_2/test_ilc_be_iso.py | 71 ---------------- .../FNO/VS_3/Be1_cc-pvdz_singlet.hdf5 | Bin 29064 -> 0 bytes .../Be_iso_tests/FNO/VS_3/test_ilc_be_iso.py | 71 ---------------- .../FNO/VS_4/Be1_cc-pvdz_singlet.hdf5 | Bin 30621 -> 0 bytes .../Be_iso_tests/FNO/VS_4/test_ilc_be_iso.py | 71 ---------------- .../FNO/VS_5/Be1_cc-pvdz_singlet.hdf5 | Bin 35101 -> 0 bytes .../Be_iso_tests/FNO/VS_5/test_ilc_be_iso.py | 71 ---------------- .../MI-FNO/VS_1/Be1_cc-pvdz_singlet_:1.hdf5 | Bin 27016 -> 0 bytes .../MI-FNO/VS_1/test_ilc_be_iso.py | 55 ------------ .../MI-FNO/VS_2/Be1_cc-pvdz_singlet_:1.hdf5 | Bin 27016 -> 0 bytes .../MI-FNO/VS_2/test_ilc_be_iso.py | 65 -------------- .../MI-FNO/VS_3/Be1_cc-pvdz_singlet_:1.hdf5 | Bin 27016 -> 0 bytes .../MI-FNO/VS_3/test_ilc_be_iso.py | 70 --------------- .../MI-FNO/VS_4/Be1_cc-pvdz_singlet_:1.hdf5 | Bin 29064 -> 0 bytes .../MI-FNO/VS_4/test_ilc_be_iso.py | 70 --------------- .../MI-FNO/VS_5/Be1_cc-pvdz_singlet_:1.hdf5 | Bin 30482 -> 0 bytes .../MI-FNO/VS_5/test_ilc_be_iso.py | 80 ------------------ .../ansatz_generator/tests/test_qcc.py | 2 +- .../ansatz_generator/tests/test_qmf.py | 4 +- 25 files changed, 28 insertions(+), 701 deletions(-) delete mode 100644 tangelo/toolboxes/ansatz_generator/tests/Be_iso_tests/FNO/VS_1/Be1_cc-pvdz_singlet.hdf5 delete mode 100644 tangelo/toolboxes/ansatz_generator/tests/Be_iso_tests/FNO/VS_1/test_ilc_be_iso.py delete mode 100644 tangelo/toolboxes/ansatz_generator/tests/Be_iso_tests/FNO/VS_2/Be1_cc-pvdz_singlet.hdf5 delete mode 100644 tangelo/toolboxes/ansatz_generator/tests/Be_iso_tests/FNO/VS_2/test_ilc_be_iso.py delete mode 100644 tangelo/toolboxes/ansatz_generator/tests/Be_iso_tests/FNO/VS_3/Be1_cc-pvdz_singlet.hdf5 delete mode 100644 tangelo/toolboxes/ansatz_generator/tests/Be_iso_tests/FNO/VS_3/test_ilc_be_iso.py delete mode 100644 tangelo/toolboxes/ansatz_generator/tests/Be_iso_tests/FNO/VS_4/Be1_cc-pvdz_singlet.hdf5 delete mode 100644 tangelo/toolboxes/ansatz_generator/tests/Be_iso_tests/FNO/VS_4/test_ilc_be_iso.py delete mode 100644 tangelo/toolboxes/ansatz_generator/tests/Be_iso_tests/FNO/VS_5/Be1_cc-pvdz_singlet.hdf5 delete mode 100644 tangelo/toolboxes/ansatz_generator/tests/Be_iso_tests/FNO/VS_5/test_ilc_be_iso.py delete mode 100644 tangelo/toolboxes/ansatz_generator/tests/Be_iso_tests/MI-FNO/VS_1/Be1_cc-pvdz_singlet_:1.hdf5 delete mode 100644 tangelo/toolboxes/ansatz_generator/tests/Be_iso_tests/MI-FNO/VS_1/test_ilc_be_iso.py delete mode 100644 tangelo/toolboxes/ansatz_generator/tests/Be_iso_tests/MI-FNO/VS_2/Be1_cc-pvdz_singlet_:1.hdf5 delete mode 100644 tangelo/toolboxes/ansatz_generator/tests/Be_iso_tests/MI-FNO/VS_2/test_ilc_be_iso.py delete mode 100644 tangelo/toolboxes/ansatz_generator/tests/Be_iso_tests/MI-FNO/VS_3/Be1_cc-pvdz_singlet_:1.hdf5 delete mode 100644 tangelo/toolboxes/ansatz_generator/tests/Be_iso_tests/MI-FNO/VS_3/test_ilc_be_iso.py delete mode 100644 tangelo/toolboxes/ansatz_generator/tests/Be_iso_tests/MI-FNO/VS_4/Be1_cc-pvdz_singlet_:1.hdf5 delete mode 100644 tangelo/toolboxes/ansatz_generator/tests/Be_iso_tests/MI-FNO/VS_4/test_ilc_be_iso.py delete mode 100644 tangelo/toolboxes/ansatz_generator/tests/Be_iso_tests/MI-FNO/VS_5/Be1_cc-pvdz_singlet_:1.hdf5 delete mode 100644 tangelo/toolboxes/ansatz_generator/tests/Be_iso_tests/MI-FNO/VS_5/test_ilc_be_iso.py diff --git a/tangelo/toolboxes/ansatz_generator/ilc.py b/tangelo/toolboxes/ansatz_generator/ilc.py index ff2ab29f4..99abbd55a 100755 --- a/tangelo/toolboxes/ansatz_generator/ilc.py +++ b/tangelo/toolboxes/ansatz_generator/ilc.py @@ -137,7 +137,7 @@ def __init__(self, molecule, mapping="JW", up_then_down=False, ilc_op_list=None, # Supported reference state initialization self.supported_reference_state = {"HF"} # Supported var param initialization - self.supported_initial_var_params = {"zeros", "diag", "ilc_tau_guess"} + self.supported_initial_var_params = {"qmf_state", "ilc_tau_guess", "random", "diag"} # Default starting parameters for initialization self.default_reference_state = "HF" @@ -163,14 +163,17 @@ def set_var_params(self, var_params=None): raise ValueError(f"Supported keywords for initializing variational parameters: " f"{self.supported_initial_var_params}") # Initialize the ILC wave function as |ILC> = |QMF> - if var_params == "zeros": + if var_params == "qmf_state": initial_var_params = np.zeros((self.n_var_params,), dtype=float) - # Initialize ILC parameters by matrix diagonalization (see Appendix B, Refs. 1 & 2). - elif var_params == "diag": - initial_var_params = init_ilc_by_diag(self.qubit_ham, self.acs, self.qmf_var_params) # Initialize all ILC parameters to the same value specified by self.ilc_tau_guess elif var_params == "ilc_tau_guess": initial_var_params = self.ilc_tau_guess * np.ones((self.n_var_params,)) + # Initialize tau parameters randomly over the domain [-ilc_tau_guess, ilc_tau_guess] + elif var_params == "random": + initial_var_params = 2. * self.ilc_tau_guess * np.random.random((self.n_var_params,)) - self.ilc_tau_guess + # Initialize ILC parameters by matrix diagonalization (see Appendix B, Refs. 1 & 2). + elif var_params == "diag": + initial_var_params = init_ilc_by_diag(self.qubit_ham, self.acs, self.qmf_var_params) else: initial_var_params = np.array(var_params) if initial_var_params.size != self.n_var_params: diff --git a/tangelo/toolboxes/ansatz_generator/qcc.py b/tangelo/toolboxes/ansatz_generator/qcc.py index 7cabfbfbf..6df9879ee 100755 --- a/tangelo/toolboxes/ansatz_generator/qcc.py +++ b/tangelo/toolboxes/ansatz_generator/qcc.py @@ -135,7 +135,7 @@ def __init__(self, molecule, mapping="JW", up_then_down=False, qcc_op_list=None, # Supported reference state initialization self.supported_reference_state = {"HF"} # Supported var param initialization - self.supported_initial_var_params = {"zeros", "qcc_tau_guess"} + self.supported_initial_var_params = {"qmf_state", "qcc_tau_guess", "random"} # Default starting parameters for initialization self.pauli_to_angles_mapping = {} @@ -161,11 +161,14 @@ def set_var_params(self, var_params=None): raise ValueError(f"Supported keywords for initializing variational parameters: " f"{self.supported_initial_var_params}") # Initialize the QCC wave function as |QCC> = |QMF> - if var_params == "zeros": + if var_params == "qmf_state": initial_var_params = np.zeros((self.n_var_params,), dtype=float) # Initialize all tau parameters to the same value specified by self.qcc_tau_guess elif var_params == "qcc_tau_guess": initial_var_params = self.qcc_tau_guess * np.ones((self.n_var_params,)) + # Initialize tau parameters randomly over the domain [-qcc_tau_guess, qcc_tau_guess] + elif var_params == "random": + initial_var_params = 2. * self.qcc_tau_guess * np.random.random((self.n_var_params,)) - self.qcc_tau_guess else: initial_var_params = np.array(var_params) if initial_var_params.size != self.n_var_params: diff --git a/tangelo/toolboxes/ansatz_generator/qmf.py b/tangelo/toolboxes/ansatz_generator/qmf.py index 3d60fd2b9..63ef15f8f 100755 --- a/tangelo/toolboxes/ansatz_generator/qmf.py +++ b/tangelo/toolboxes/ansatz_generator/qmf.py @@ -96,7 +96,7 @@ def __init__(self, molecule=None, mapping="JW", up_then_down=False, init_qmf=Non self.init_qmf = {"init_params": "hf_state"} if init_qmf is None else init_qmf # Supported var param initialization - self.supported_initial_var_params = {"zeros", "half_pi", "pis", "random", "hf_state"} + self.supported_initial_var_params = {"vacuum", "full_Q", "full_Qdag", "full_occ", "random", "hf_state"} # Supported reference state initialization self.supported_reference_state = {"HF"} @@ -156,21 +156,24 @@ def set_var_params(self, var_params=None): if var_params not in self.supported_initial_var_params: raise ValueError(f"Supported keywords for initializing variational parameters: " f"{self.supported_initial_var_params}") - if var_params == "zeros": - # Initialize |QMF> as |00...0> + # Initialize |QMF> as |00...0> + if var_params == "vacuum": initial_var_params = np.zeros((self.n_var_params,), dtype=float) - elif var_params == "half_pi": - # Initialize |QMF> as (1/sqrt(2))^n_qubits * tensor_prod(|0> + 1j|1>) + # Initialize |QMF> as (1/sqrt(2))^n_qubits * tensor_prod(|0> + 1j|1>) + elif var_params == "full_Q": initial_var_params = 0.5 * np.pi * np.ones((self.n_var_params,)) - elif var_params == "pis": - # Initialize |QMF> as (-1)^n_qubits |11...1> + # Initialize |QMF> as (1/sqrt(2))^n_qubits * tensor_prod(|0> - 1j|1>) + elif var_params == "full_Qdag": + initial_var_params = 1.5 * np.pi * np.ones((self.n_var_params,)) + # Initialize |QMF> as (-1)^n_qubits |11...1> + elif var_params == "full_occ": initial_var_params = np.pi * np.ones((self.n_var_params,)) + # Random initialization of thetas over [0, pi] and phis over [0, 2 * pi] elif var_params == "random": - # Random initialization of thetas over [0, pi] and phis over [0, 2 * pi] initial_thetas = np.pi * np.random.random((self.n_qubits,)) initial_phis = 2. * np.pi * np.random.random((self.n_qubits,)) initial_var_params = np.concatenate((initial_thetas, initial_phis)) - # Initialize thetas as 0 or pi such that |QMF> = |HF> and set all phis to 0 + # Initialize thetas as 0 or pi such that |QMF> = |HF> and set all phis to 0 elif var_params == "hf_state": initial_var_params = init_qmf_from_hf(self.n_spinorbitals, self.n_electrons, self.mapping, self.up_then_down, self.spin) diff --git a/tangelo/toolboxes/ansatz_generator/tests/Be_iso_tests/FNO/VS_1/Be1_cc-pvdz_singlet.hdf5 b/tangelo/toolboxes/ansatz_generator/tests/Be_iso_tests/FNO/VS_1/Be1_cc-pvdz_singlet.hdf5 deleted file mode 100644 index 451b68bf946088e9b4bdacc5209f3595a4e23302..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 27016 zcmeI43se(V8h~$jtXS#;m7;Zvv>uh+wTQOIViEC$(sf%|(aQQDhGZZqd6|TV55%s= znpW3$D~kANeQm4g5wy@AA?lt(VLjq5dTJHLts>P`q`F2B3p+FSzr-YD0<{KW?{Jv= z`2V^0pKty<^Uux9d>B1(Qn%+`d=7Doj}P)fJ^3tIHN#?5;2KgU@_CREL286_p9ju} z5ch{rU#Rzk_HFg1Pm78|=r@S8Pg2+nUGc`bP*NeonJ_4u`s2(YDDRWtj2z0*FXK!W zl*i7*nIK{~)YGJM5{Ql(7sFX*0>27|blPb)pD$L8RXu z{e-va3*xoCg!N2BJcwjkPtSniafz+VN$oz+t4km?t)~T`7tORTC$*uFIn;;LJXtDY zuT6Z{(7y<-Xd)4=fa;dxjZL^EY zGa3&huimzJ-I7vWc=L#zUrfGh&fXJIdoR20o2HWfQFG5Nof2MMw&6grGUjOIKkgOY zUoO9LIw1Dik-hmv=gX0(Tg;Z@KhBDo=b!x7h_Av*21{-%Iu>AFsjt?K-Wr@=k?>Ld zD$~~QbCjp58^V{&F@@G#JeM3^X}A=6YSh9@XX5M5jeP!o+KJUm4u;(MZdpjvjMQSE z|1Q7t^_29&kV$h_UeGTHk1hLWiC$S?=>B$r`GVhsjDi!pR9|k~Vm_M}_}0qq$)Rt) zl;8Z`x!fVy=A4FF-Kv*Md!eEJsWZ~!8}AM9x_oi!`q|PU$F5&%y4v6TyWf_MN-Q5c z5arbjJ9{~=NIz{cfWb{XPgwIbN3+uxV8O z>Z)~#!$Ly;eQP;-criSt$vdFjbhjTmzIs;O^YVhq>$iKE-t(Kf=B<*7;@aUu3;yO? z6qz0`O^UdDK67G|>GYAz`mepGUZ|<;W0Z~9rYiI=+ca18?rV`XHA|RF)$7+akF4F= z{o|R-?#_%@SYXa9UGo#luOFyn4`ZR${O3={Y*<$2tr-3}&uF|0y3Ft}OvKi$VG7G&R^zj@7+h*Oa}14eJy)gwMHt+b-KA@yKm zVB90qBfbC4%((QEl>uGP_3(|%%2<4+?)dJ&7b3Z%vf{>y!t#DM1Ls`7I<3db0a=;v zZf7oD`OZ)gh=%kTbIscQX^9e`1So+HCV=Bg4~S9_3hp9o5V`MJ>L;xCYm?f?mBBm$ z5e^d{izhJ|5-*YH$djE<;p%#_;UbP0$MTAfxtqn4*GNA8y&-%Fpd@r`Hb+A{CNO_4hu z??&JP6Y1a{_Wo_`RE}fsAI^BR_xkuz8~tTzr{F^7chljtogOc2mJ#%f?2Mw&k_CEM z^Mtez`T_LhNy2(Q;AZJ^Cwd?bf&dqqN4CGW90I+yuL!r-R;j&TVK*TCw?EEa-$pdE%W}LKI1(4^7o~FL6I%@rLX&<*PCHnJH9S`-bOCQwhnEV=;8CQ7fOfT z`UVPjORNtbAgmV+;xjJ@>yfBDEJ#?dvrCnnGy;^u+y zHOmK08uzEzD7J3;v>9B&3mlKT-M+jIY>o2>*~f##!-AXLuCG~ zJM12rZ?i3Xvh!%%rcTdup12!d6vSuXXJaPPy&y9`SbIKA}n2V z-FL`+#RMmgKL(CAQM2Vl?<)uynoPPy&cO8h=h70SR2XS<$u--fnA9_(( zF9_`WVY0AZM@j8}_r!O~s!n6z zzP73sGfGBk(2*~%s>K>zg2EtC!iQMZ;`u3T8A=GLQ>02pq7&;FZHiJ)dO=8@C}z}* zE{PZ_m8dmpg;b)nX-|{N=#&zzSfMsBNxGJ95cG;0Q7|q2Y1E83K_gpWyMZB1(`vm+ zJHpBksX{M%yn-)tjp}474iI1F8iR78RPnev;>wSy^X13XkyL8aK3B%y`V>o4+Lry` z^<VXN{~i82Kck@H0sf3!Q#%5~#;k6Vmq z*YVg(d9cHv<$Y4oKFY(2$KTz82l>807@XjYal+@eAQ129Evy#<;v-&^ UJlMfuxxOEuJgj)+x&@E_108qIg#Z8m diff --git a/tangelo/toolboxes/ansatz_generator/tests/Be_iso_tests/FNO/VS_1/test_ilc_be_iso.py b/tangelo/toolboxes/ansatz_generator/tests/Be_iso_tests/FNO/VS_1/test_ilc_be_iso.py deleted file mode 100644 index 4bca754ee..000000000 --- a/tangelo/toolboxes/ansatz_generator/tests/Be_iso_tests/FNO/VS_1/test_ilc_be_iso.py +++ /dev/null @@ -1,58 +0,0 @@ -# Copyright 2021 Good Chemistry Company. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -# FNO, VS = 1, SCBK -# -# REFERENCE ENERGIES: -# EQMF = -14.57233763 Eh -# EILC = -14.58926922 Eh - -import numpy as np - -from openfermion.chem import MolecularData -from openfermion.transforms import get_fermion_operator -from tangelo.linq import Simulator -from tangelo.toolboxes.ansatz_generator.qmf import QMF -from tangelo.toolboxes.ansatz_generator.ilc import ILC - -sim = Simulator() - -file_name = "./Be1_cc-pvdz_singlet.hdf5" - -# Prepare classical data from SCF calculation -molecule = MolecularData(filename=file_name) -fermi_ham = get_fermion_operator(molecule.get_molecular_hamiltonian()) -scfdata = (molecule, fermi_ham) - -# Instantiate QMF ansatz -- note that SCBK mapping doesn't work for VS = 1 -qmf = QMF(molecule=None, mapping="SCBK", up_then_down=True, init_qmf=None, scfdata=scfdata) -qmf.set_var_params([np.pi, 0., np.pi, 0., 0., 0., 0., 0.]) -qmf.build_circuit() -print(qmf.var_params) -print(qmf.circuit) - -energy = sim.get_expectation_value(qmf.qubit_ham, qmf.circuit) -print(" EQMF = ", energy) -print(" EQMF (ref.) = -14.57233763") - -ilc = ILC(molecule=None, mapping="SCBK", up_then_down=True, qmf_circuit=qmf.circuit, qmf_var_params=qmf.var_params, - qubit_ham=qmf.qubit_ham, max_ilc_gens=None, n_trotter=1, scfdata=scfdata) -ilc.build_circuit() -#print(ilc.qmf_var_params) -#print(ilc.var_params) -#print(ilc.qmf_circuit) -#print(ilc.circuit) -energy = sim.get_expectation_value(ilc.qubit_ham, ilc.circuit) -print(" EILC = ", energy) -print(" EILC (ref.) = -14.58926922") diff --git a/tangelo/toolboxes/ansatz_generator/tests/Be_iso_tests/FNO/VS_2/Be1_cc-pvdz_singlet.hdf5 b/tangelo/toolboxes/ansatz_generator/tests/Be_iso_tests/FNO/VS_2/Be1_cc-pvdz_singlet.hdf5 deleted file mode 100644 index 5d3a1ec6308676c6030b58c89180517850b9a3ae..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 27016 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
)t(BPCVGIlR7=z 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 lQr|j*K{9Z5RX-@~YT$S@U_3+<`O`B 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 diff --git a/tangelo/toolboxes/ansatz_generator/tests/Be_iso_tests/FNO/VS_5/test_ilc_be_iso.py b/tangelo/toolboxes/ansatz_generator/tests/Be_iso_tests/FNO/VS_5/test_ilc_be_iso.py deleted file mode 100644 index 06ddcd00b..000000000 --- a/tangelo/toolboxes/ansatz_generator/tests/Be_iso_tests/FNO/VS_5/test_ilc_be_iso.py +++ /dev/null @@ -1,71 +0,0 @@ -# Copyright 2021 Good Chemistry Company. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -# FNO, VS = 5, SCBK -# -# REFERENCE ENERGIES: -# EQMF = -14.57233763 Eh -# EILC = -14.61597638 Eh - -import numpy as np - -from openfermion.chem import MolecularData -from openfermion.transforms import get_fermion_operator -from tangelo.linq import Simulator -from tangelo.toolboxes.ansatz_generator.qmf import QMF -from tangelo.toolboxes.ansatz_generator.ilc import ILC - -sim = Simulator() - -file_name = "./Be1_cc-pvdz_singlet.hdf5" - -# Prepare classical data from SCF calculation -molecule = MolecularData(filename=file_name) -fermi_ham = get_fermion_operator(molecule.get_molecular_hamiltonian()) -scfdata = (molecule, fermi_ham) - -# Instantiate QMF ansatz -- note that SCBK mapping doesn't work for VS = 5 -qmf = QMF(molecule=None, mapping="SCBK", up_then_down=True, init_qmf=None, scfdata=scfdata) -qmf.set_var_params([np.pi, 0., 0., 0., 0., 0., np.pi, 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.]) -qmf.build_circuit() -print(qmf.var_params) -print(qmf.circuit) - -energy = sim.get_expectation_value(qmf.qubit_ham, qmf.circuit) -print(" EQMF = ", energy) -print(" EQMF (ref.) = -14.57233763") - -ilc = ILC(molecule=None, mapping="SCBK", up_then_down=True, qmf_circuit=qmf.circuit, qmf_var_params=qmf.var_params, - qubit_ham=qmf.qubit_ham, max_ilc_gens=None, n_trotter=1, scfdata=scfdata) -ilc.build_circuit() -#print(ilc.qmf_var_params) -#print(ilc.var_params) -#print(ilc.qmf_circuit) -#print(ilc.circuit) -energy = sim.get_expectation_value(ilc.qubit_ham, ilc.circuit) -print(" EILC (n_trot = 1) = ", energy) - -ilc.n_trotter = 2 -ilc.build_circuit() -# n_trotter = 2 is slightly better but not worth doubling the ilc circuit -energy = sim.get_expectation_value(ilc.qubit_ham, ilc.circuit) -print(" EILC (n_trot = 2) = ", energy) - -ilc.n_trotter = 3 -ilc.build_circuit() -# n_trotter = 3 is slightly better but not worth doubling the ilc circuit -energy = sim.get_expectation_value(ilc.qubit_ham, ilc.circuit) -print(" EILC (n_trot = 3) = ", energy) -print(" EILC (ref.) = -14.61597638") - diff --git a/tangelo/toolboxes/ansatz_generator/tests/Be_iso_tests/MI-FNO/VS_1/Be1_cc-pvdz_singlet_:1.hdf5 b/tangelo/toolboxes/ansatz_generator/tests/Be_iso_tests/MI-FNO/VS_1/Be1_cc-pvdz_singlet_:1.hdf5 deleted file mode 100644 index f996b9b4d2bb1fd58b7c0124b50a247921f73cdc..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 27016 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 diff --git a/tangelo/toolboxes/ansatz_generator/tests/Be_iso_tests/MI-FNO/VS_1/test_ilc_be_iso.py b/tangelo/toolboxes/ansatz_generator/tests/Be_iso_tests/MI-FNO/VS_1/test_ilc_be_iso.py deleted file mode 100644 index 27b8ee471..000000000 --- a/tangelo/toolboxes/ansatz_generator/tests/Be_iso_tests/MI-FNO/VS_1/test_ilc_be_iso.py +++ /dev/null @@ -1,55 +0,0 @@ -# Copyright 2021 Good Chemistry Company. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -# MI-FNO ID # 1, VS = 1, SCBK -# -# REFERENCE ENERGIES: -# EQMF = -14.57233763 Eh -# EILC = -14.58922316 Eh - -from openfermion.chem import MolecularData -from openfermion.transforms import get_fermion_operator -from tangelo.linq import Simulator -from tangelo.toolboxes.ansatz_generator.qmf import QMF -from tangelo.toolboxes.ansatz_generator.ilc import ILC - -sim = Simulator() - -file_name = "./Be1_cc-pvdz_singlet_:1.hdf5" - -# Prepare classical data from SCF calculation -molecule = MolecularData(filename=file_name) -fermi_ham = get_fermion_operator(molecule.get_molecular_hamiltonian()) -scfdata = (molecule, fermi_ham) - -# Instantiate QMF ansatz -- note that SCBK mapping works for VS = 1 -qmf = QMF(molecule=None, mapping="SCBK", up_then_down=True, init_qmf=None, scfdata=scfdata) -qmf.build_circuit() -#print(qmf.var_params) -#print(qmf.circuit) - -energy = sim.get_expectation_value(qmf.qubit_ham, qmf.circuit) -print(" EQMF = ", energy) -print(" EQMF (ref.) = -14.57233763") - -ilc = ILC(molecule=None, mapping="SCBK", up_then_down=True, qmf_circuit=qmf.circuit, qmf_var_params=qmf.var_params, - qubit_ham=qmf.qubit_ham, max_ilc_gens=None, n_trotter=1, scfdata=scfdata) -ilc.build_circuit() -#print(ilc.qmf_var_params) -#print(ilc.var_params) -#print(ilc.qmf_circuit) -#print(ilc.circuit) -energy = sim.get_expectation_value(ilc.qubit_ham, ilc.circuit) -print(" EILC = ", energy) -print(" EILC (ref.) = -14.58922316") diff --git a/tangelo/toolboxes/ansatz_generator/tests/Be_iso_tests/MI-FNO/VS_2/Be1_cc-pvdz_singlet_:1.hdf5 b/tangelo/toolboxes/ansatz_generator/tests/Be_iso_tests/MI-FNO/VS_2/Be1_cc-pvdz_singlet_:1.hdf5 deleted file mode 100644 index 2efe3cf6a14dd727f3e335069e9a8f57417bce75..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 27016 zcmeI44NwzT9>8A+D(F>3YE`U2Wwdk@1+|FM6m_&>tA#@ysaFXhEF{;E4M~7dh&bm5 zbnFS$6R}og<_4ustK8uL#Tv2o2CLlJp4!nvr4=ij1^nRns6n~ieeZ!u$Ob9~V&5|C z`}qI&y|=&pzuo_P*?k{`E`8&?}o}Z*IN^@Y#9K<0WKeh=rHiZFc|a48L==DgwmChVWb$PizF~2L+Q|$ zU?dNv7p;R4Z`^Q{PlBOIAavQ{aJFTY$gX@*ShW7>w%3~dh6-W5GGv#~eg#(THwbkr z6g=HVlhTJA0c^jKsAb+L%tH2q8!XY%2^uD$qBU}@oK~`NcEh(Re7}Raw)d;w4qGY! zBLJ?qU=sf^6OlM)9KT*A7Ei;G-otNE!z1H*m*d66t0NrF6_HgUWad#~Xno;Sh^i;V{^Q+WF?MqHPP+jp#aHEGuNTA=?+gDqBKf3et z5z+3nQdzC$T>BUF?3T*<|5Q|sYCALj(&^({R$Y~T+*W>`{>sOAETx53$Rnert)Y$g z4v%qH1l~(m>R<9G-&Oy$aQKgDhnfmwHhix)7{AIFjGR+Hyi7E{s5s503-ukg*@3oG*-#p42Kj!Gwj7iEC?^SIFR%U0ea{5aMy^&sf z>GTL=?U~(W`7_UyeIeYPkrJ{regCRco_Vi6b z`t#Q+lCOAXi$;eVYW;j3hFsa#baaK%FGAE%UhlOgD)sEyJHaW=&90ly)|_1WbMK3o|+QTc6!E)0}Zpq2Q`SP&)+r~|f?5yl`f3DSQ=9iLNOkA#E(AwUQW3<2m@Iw3Cw0N)Y( z<>GPACO3Y)-jmzx426U1RG!l!%9^1MO%$?9?p> zteS!E-&>xs{taj7`ZDjt-`_v%V}H*YuxmRI1zFmMEgt=SKJMtFy=)qY(|O`D@cgb0 zIY_!F{vxxGUKTT>$hYMoy}W2XEdUyjo-CeU&jmeJbTS)yNbHRqTvR+T|GlLc>E%r2 zFR!msOS^(*fa~wSpPk!BJNm8oS^0zqV~joX{s*6N9uv87shf95_qcT4C@`-J^=s>X z*O5MQ$?9v@wu>IT4?6=Ls%@etf3>*vzFz!#p-B9#sT*o>5cbN0ye&iym0|ln{A=y1 z)FJK4HV6%H37KGupipx%Y$ z=A6vLSdD~)03kpK5CVh%AwUQa0uD-`uYNT43(Z42_|cI8kS$v^`#>w9Ll!?Qk_! zFy}=4Xj48UMFnnAYx6zO!JY57 zHLmF40I$4dx{rK*{rr5fsHbYS`av02EU{tuI&`(MnmH#juE1nSNC*%Dga9Ex2oM5< zz_UTX(vJ@1PE6noU+ggV2m7CXT7I@S*G{3+unyQ_?j0DmdHCOozYWJt+`+xZAcsMR(zRO|bPyU_L4Tt#7 z$$mWQ9D)b7%Xo@TptR~#oQ{!b%klt!wStJtMf zxkHqa1a9R@r5LWX+P&VYn%1(btt!Qof|6?0c;!{4m{!NgwGstdh*c?0Ok&b_SyrG- zk}4>PTCAp2NeT_F1prx`m{L;ec-&B_L`f^JUW?DDE zF8!D~kV?(UXVY-0Pq8FH)qNhEUM!syqo7Rk<5_A9C5DtViTNUqT>@A99+ip@>{XsD zQ6wQm_vtNJy$szi){dn9WJeY3K{XMCmp71c?(clm~@cIHl=mwV+$$xM2 zM&bf@e!Xxc&f)*L1jvw{%M||dhPu>#_nrSblwE})yUKfq{|ZJTy#gP8JsHxIZQ$1n oLgLEt{CY!OYCpTW?GSclKOR359!%%xdA>Ii9wt1R9D>LH0Yr1rDgXcg diff --git a/tangelo/toolboxes/ansatz_generator/tests/Be_iso_tests/MI-FNO/VS_2/test_ilc_be_iso.py b/tangelo/toolboxes/ansatz_generator/tests/Be_iso_tests/MI-FNO/VS_2/test_ilc_be_iso.py deleted file mode 100644 index 66fb92ad9..000000000 --- a/tangelo/toolboxes/ansatz_generator/tests/Be_iso_tests/MI-FNO/VS_2/test_ilc_be_iso.py +++ /dev/null @@ -1,65 +0,0 @@ -# Copyright 2021 Good Chemistry Company. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -# MI-FNO ID # 1, VS = 2, SCBK -# -# REFERENCE ENERGIES: -# EQMF = -14.57233763 Eh -# EILC = -14.60296795 Eh - -import numpy as np - -from openfermion.chem import MolecularData -from openfermion.transforms import get_fermion_operator -from tangelo.linq import Simulator -from tangelo.toolboxes.ansatz_generator.qmf import QMF -from tangelo.toolboxes.ansatz_generator.ilc import ILC - -sim = Simulator() - -file_name = "./Be1_cc-pvdz_singlet_:1.hdf5" - -# Prepare classical data from SCF calculation -molecule = MolecularData(filename=file_name) -fermi_ham = get_fermion_operator(molecule.get_molecular_hamiltonian()) -scfdata = (molecule, fermi_ham) - -# Instantiate QMF ansatz -- note that SCBK mapping doesn't work for VS = 2 in Tangelo -# The short term fix is to manually pass the correct QMF var params -qmf = QMF(molecule=None, mapping="SCBK", up_then_down=True, init_qmf=None, scfdata=scfdata) -qmf.set_var_params([np.pi, np.pi, np.pi, np.pi, 0., 0., 0., 0.]) -qmf.build_circuit() -#print(qmf.var_params) -#print(qmf.circuit) - -energy = sim.get_expectation_value(qmf.qubit_ham, qmf.circuit) -print(" EQMF = ", energy) -print(" EQMF (ref.) = -14.57233763") - -# n_trotter = 1 is sufficient to achieve satisfactory agreement with the reference value -ilc = ILC(molecule=None, mapping="SCBK", up_then_down=True, qmf_circuit=qmf.circuit, qmf_var_params=qmf.var_params, - qubit_ham=qmf.qubit_ham, max_ilc_gens=None, n_trotter=1, scfdata=scfdata) -ilc.build_circuit() -#print(ilc.qmf_var_params) -#print(ilc.var_params) -#print(ilc.qmf_circuit) -#print(ilc.circuit) -energy = sim.get_expectation_value(ilc.qubit_ham, ilc.circuit) -print(" EILC (n_trot = 1) = ", energy) -ilc.n_trotter = 2 -ilc.build_circuit() -# n_trotter = 2 is slightly better but not worth doubling the ilc circuit -energy = sim.get_expectation_value(ilc.qubit_ham, ilc.circuit) -print(" EILC (n_trot = 2) = ", energy) -print(" EILC (ref.) = -14.60296795") diff --git a/tangelo/toolboxes/ansatz_generator/tests/Be_iso_tests/MI-FNO/VS_3/Be1_cc-pvdz_singlet_:1.hdf5 b/tangelo/toolboxes/ansatz_generator/tests/Be_iso_tests/MI-FNO/VS_3/Be1_cc-pvdz_singlet_:1.hdf5 deleted file mode 100644 index b40f38a8f6845be6f3c746acef290467550cc0d0..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 27016 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 diff --git a/tangelo/toolboxes/ansatz_generator/tests/Be_iso_tests/MI-FNO/VS_4/test_ilc_be_iso.py b/tangelo/toolboxes/ansatz_generator/tests/Be_iso_tests/MI-FNO/VS_4/test_ilc_be_iso.py deleted file mode 100644 index be09a91cb..000000000 --- a/tangelo/toolboxes/ansatz_generator/tests/Be_iso_tests/MI-FNO/VS_4/test_ilc_be_iso.py +++ /dev/null @@ -1,70 +0,0 @@ -# Copyright 2021 Good Chemistry Company. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -# MI-FNO ID # 1, VS = 4, SCBK -# -# REFERENCE ENERGIES: -# EQMF = -14.57233763 Eh -# EILC = -14.61580907 Eh - -import numpy as np - -from openfermion.chem import MolecularData -from openfermion.transforms import get_fermion_operator -from tangelo.linq import Simulator -from tangelo.toolboxes.ansatz_generator.qmf import QMF -from tangelo.toolboxes.ansatz_generator.ilc import ILC - -sim = Simulator() - -file_name = "./Be1_cc-pvdz_singlet_:1.hdf5" - -# Prepare classical data from SCF calculation -molecule = MolecularData(filename=file_name) -fermi_ham = get_fermion_operator(molecule.get_molecular_hamiltonian()) -scfdata = (molecule, fermi_ham) - -# Instantiate QMF ansatz -- note that SCBK mapping doesn't work for VS = 4 in Tangelo -# The short term fix is to manually pass the correct QMF var params -qmf = QMF(molecule=None, mapping="SCBK", up_then_down=True, init_qmf=None, scfdata=scfdata) -qmf.set_var_params([np.pi, np.pi, np.pi, 0., np.pi, np.pi, np.pi, 0., 0., 0., 0., 0., 0., 0., 0., 0.]) -qmf.build_circuit() -#print(qmf.var_params) -#print(qmf.circuit) - -energy = sim.get_expectation_value(qmf.qubit_ham, qmf.circuit) -print(" EQMF = ", energy) -print(" EQMF (ref.) = -14.57233763") - - -ilc = ILC(molecule=None, mapping="SCBK", up_then_down=True, qmf_circuit=qmf.circuit, qmf_var_params=qmf.var_params, - qubit_ham=qmf.qubit_ham, max_ilc_gens=None, n_trotter=1, scfdata=scfdata) -ilc.build_circuit() -#print(ilc.qmf_var_params) -#print(ilc.var_params) -#print(ilc.qmf_circuit) -#print(ilc.circuit) -energy = sim.get_expectation_value(ilc.qubit_ham, ilc.circuit) -print(" EILC (n_trot = 1) = ", energy) - -ilc.n_trotter = 2 -ilc.build_circuit() -energy = sim.get_expectation_value(ilc.qubit_ham, ilc.circuit) -print(" EILC (n_trot = 2) = ", energy) - -ilc.n_trotter = 3 -ilc.build_circuit() -energy = sim.get_expectation_value(ilc.qubit_ham, ilc.circuit) -print(" EILC (n_trot = 3) = ", energy) -print(" EILC (ref.) = -14.61580907") diff --git a/tangelo/toolboxes/ansatz_generator/tests/Be_iso_tests/MI-FNO/VS_5/Be1_cc-pvdz_singlet_:1.hdf5 b/tangelo/toolboxes/ansatz_generator/tests/Be_iso_tests/MI-FNO/VS_5/Be1_cc-pvdz_singlet_:1.hdf5 deleted file mode 100644 index 81577268a6f4d8f97dbd36ee5dd1ad38a2b33744..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 30482 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 Date: Mon, 7 Mar 2022 11:52:53 -0500 Subject: [PATCH 08/28] improvements to qmf, qcc, and ilc ansatze --- .../toolboxes/ansatz_generator/_qubit_cc.py | 9 +--- .../toolboxes/ansatz_generator/_qubit_ilc.py | 4 +- .../toolboxes/ansatz_generator/_qubit_mf.py | 25 +++++----- tangelo/toolboxes/ansatz_generator/ilc.py | 35 +++++++------ tangelo/toolboxes/ansatz_generator/qcc.py | 50 +++++++++++-------- 5 files changed, 62 insertions(+), 61 deletions(-) diff --git a/tangelo/toolboxes/ansatz_generator/_qubit_cc.py b/tangelo/toolboxes/ansatz_generator/_qubit_cc.py index 9b031de99..80792e42b 100644 --- a/tangelo/toolboxes/ansatz_generator/_qubit_cc.py +++ b/tangelo/toolboxes/ansatz_generator/_qubit_cc.py @@ -36,7 +36,7 @@ from ._qubit_mf import get_op_expval -def construct_dis(qubit_ham, pure_var_params, deqcc_dtau_thresh, verbose=False): +def construct_dis(qubit_ham, pure_var_params, deqcc_dtau_thresh): """Construct the DIS of QCC generators, which proceeds as follows: 1. Identify the flip indices of all Hamiltonian terms and group terms by flip indices. 2. Construct a representative generator using flip indices from each candidate DIS group @@ -51,7 +51,6 @@ def construct_dis(qubit_ham, pure_var_params, deqcc_dtau_thresh, verbose=False): pure_var_params (numpy array of float): A purified QMF variational parameter set. deqcc_dtau_thresh (float): Threshold for |dEQCC/dtau| so that a candidate group is added to the DIS if |dEQCC/dtau| >= deqcc_dtau_thresh for a generator. - verbose (bool): Flag for QCC verbosity. Returns: list of list: the DIS of QCC generators. @@ -60,16 +59,10 @@ def construct_dis(qubit_ham, pure_var_params, deqcc_dtau_thresh, verbose=False): # Use a qubit Hamiltonian and purified QMF parameter set to construct the DIS dis, dis_groups = [], get_dis_groups(qubit_ham, pure_var_params, deqcc_dtau_thresh) if dis_groups: - if verbose: - print(f"The DIS contains {len(dis_groups)} unique generator group(s).\n") for i, dis_group in enumerate(dis_groups): dis_group_idxs = [int(idxs) for idxs in dis_group[0].split(" ")] dis_group_gens = get_gens_from_idxs(dis_group_idxs) dis.append(dis_group_gens) - if verbose: - print(f"DIS group {i} | group size = {len(dis_group_gens)} | " - f"flip indices = {dis_group_idxs} | |dEQCC/dtau| = " - f"{abs(dis_group[1])} a.u.\n") else: raise ValueError(f"The DIS is empty: there are no candidate DIS groups where " f"|dEQCC/dtau| >= {deqcc_dtau_thresh} a.u. Terminate simulation.\n") diff --git a/tangelo/toolboxes/ansatz_generator/_qubit_ilc.py b/tangelo/toolboxes/ansatz_generator/_qubit_ilc.py index c9a6cf9c9..c124fc588 100644 --- a/tangelo/toolboxes/ansatz_generator/_qubit_ilc.py +++ b/tangelo/toolboxes/ansatz_generator/_qubit_ilc.py @@ -28,7 +28,7 @@ J. Parallel Distrib. Comput., 1991, 13, 118–122. """ -from random import choice +#from random import choice import scipy import numpy as np @@ -138,7 +138,7 @@ def gauss_elim_over_gf2(A, zdim): b = z[2] for zf in (z[1]): if z_sln[zf] == -1: - z_sln[zf] = choice([0., 1.]) + z_sln[zf] = 1. #choice([0., 1.]) b = (b + z_sln[zf]) % 2 z_sln[z[0]] = b return z_sln diff --git a/tangelo/toolboxes/ansatz_generator/_qubit_mf.py b/tangelo/toolboxes/ansatz_generator/_qubit_mf.py index db7c5ab74..0eddb4000 100644 --- a/tangelo/toolboxes/ansatz_generator/_qubit_mf.py +++ b/tangelo/toolboxes/ansatz_generator/_qubit_mf.py @@ -13,12 +13,17 @@ # limitations under the License. """This module implements a collection of functions related to the QMF -ansatz: (1) analytically evaluate an expectation value of a QubitOperator -using a QMF wave function; (2) initialize the QMF variational parameter set -{Omega} from a Hartree-Fock reference state; (3) purify {Omega} when building -and screening the DIS of QCC generators; (4) build a QMF state preparation -circuit using {Omega}; (5) create penalty terms for N, S^2, and Sz to penalize -a mean-field Hamiltonian. For more information, see references below. +ansatz: + 1. Analytical evaluation of an expectation value of a QubitOperator + using a QMF wave function; + 2. Initialization of the QMF variational parameter set {Omega} from a + Hartree-Fock reference state; + 3. Purification {Omega} when building and screening the DIS of QCC generators; + 4. Construction of a QMF state circuit using {Omega}; + 5. Addition of terms for N, S^2, and Sz that penalize a mean-field Hamiltonian + in order to obtain solutions corresponding to specific electron number + and spin symmetries. +For more information, see references below. Refs: 1. I. G. Ryabinkin and S. N. Genin. @@ -105,8 +110,7 @@ def init_qmf_from_hf(n_spinorbitals, n_electrons, mapping, up_then_down=False, s return np.concatenate((np.pi * thetas, np.zeros((len(thetas),), dtype=float))) -def purify_qmf_state(qmf_var_params, n_spinorbitals, n_electrons, mapping, up_then_down=False, - spin=None, verbose=False): +def purify_qmf_state(qmf_var_params, n_spinorbitals, n_electrons, mapping, up_then_down=False, spin=None): """The efficient construction and screening of the DIS requires a z-collinear QMF state. If the QMF state specified by qmf_var_params is not z-collinear, this function adjusts the parameters to the nearest z-collinear computational basis state. @@ -119,7 +123,6 @@ def purify_qmf_state(qmf_var_params, n_spinorbitals, n_electrons, mapping, up_th up_then_down (bool): Change basis ordering putting all spin-up orbitals first, followed by all spin-down. spin (int): 2*S = n_alpha - n_beta. - verbose (bool): Flag for QMF verbosity. Returns: numpy array of float: purified QMF parameter set that corresponds to the @@ -137,8 +140,6 @@ def purify_qmf_state(qmf_var_params, n_spinorbitals, n_electrons, mapping, up_th else: vector = get_vector(n_spinorbitals, n_electrons, mapping, up_then_down, spin) pure_var_params[i] = np.pi * vector[i] - if verbose: - print(f"Purified |QMF_{i}> Bloch angles: (theta, phi) = ({pure_var_params[i]}, {pure_var_params[i + n_qubits]})\n") return pure_var_params @@ -152,7 +153,7 @@ def get_qmf_circuit(qmf_var_params, variational=True): variational (bool): Flag to treat {Omega} variationally or keep them fixed. Returns: - Circuit: instance of tangelo.linq Circuit class for a QMF state preparation circuit. + Circuit: instance of tangelo.linq Circuit class. """ n_qubits, gates = qmf_var_params.size // 2, [] diff --git a/tangelo/toolboxes/ansatz_generator/ilc.py b/tangelo/toolboxes/ansatz_generator/ilc.py index 99abbd55a..58fddc2d6 100755 --- a/tangelo/toolboxes/ansatz_generator/ilc.py +++ b/tangelo/toolboxes/ansatz_generator/ilc.py @@ -12,8 +12,13 @@ # See the License for the specific language governing permissions and # limitations under the License. -"""This module defines the coupled cluster ansatz class for involutory -linear combinations (ILC) of anti-commuting sets (ACS) of Pauli words. +"""This module defines the qubit coupled cluster ansatz class for involutory +linear combinations (ILC) of anti-commuting sets (ACS) of Pauli words +(generators). Relative to the direct interation set (DIS) of QCC generators, +which incur an exponential growth of Hamiltonian terms upon dressing, the ACS +of ILC generators enables Hamiltonian dressing such that the number of terms +grows quadratically and exact quadratic truncation of the Baker-Campbell-Hausdorff +expansion. For more information about this ansatz, see references below. Refs: 1. R. A. Lang, I. G. Ryabinkin, and A. F. Izmaylov. @@ -31,8 +36,8 @@ from .ansatz import Ansatz from .ansatz_utils import exp_pauliword_to_gates from ._qubit_mf import init_qmf_from_hf, get_qmf_circuit, purify_qmf_state -from ._qubit_ilc import construct_acs, init_ilc_by_diag from ._qubit_cc import construct_dis +from ._qubit_ilc import construct_acs, init_ilc_by_diag class ILC(Ansatz): @@ -49,7 +54,7 @@ class ILC(Ansatz): followed by all spin-down. Default, False. ilc_op_list (list of QubitOperator): Generator list for the ILC ansatz. Default, None. qmf_circuit (Circuit): An instance of tangelo.linq Circuit class implementing a QMF state - preparation circuit. If passed from the QMF ansatz class, parameters are variational. + circuit. If passed from the QMF ansatz class, parameters are variational. If None, one is created with QMF parameters that are not variational. Default, None. qmf_var_params (list or numpy array of float): QMF variational parameter set. If None, the values are determined using a Hartree-Fock reference state. Default, None. @@ -65,12 +70,11 @@ class ILC(Ansatz): n_trotter (int): Number of Trotterization steps for the ILC ansatz. Default, 1. scfdata (tuple): tuple containing an instance of OpenFermion MolecularData and a FermionOperator corresponding to the fermionic Hamiltonian. - verbose (bool): Flag for QCC verbosity. Default, False. """ def __init__(self, molecule, mapping="JW", up_then_down=False, ilc_op_list=None, qmf_circuit=None, qmf_var_params=None, qubit_ham=None, ilc_tau_guess=1.e-2, - deilc_dtau_thresh=1.e-3, max_ilc_gens=None, n_trotter=1, scfdata=None, verbose=False): + deilc_dtau_thresh=1.e-3, max_ilc_gens=None, n_trotter=1, scfdata=None): if molecule: self.molecule = molecule @@ -90,8 +94,9 @@ def __init__(self, molecule, mapping="JW", up_then_down=False, ilc_op_list=None, self.n_qubits = get_qubit_number(self.mapping, self.n_spinorbitals) self.up_then_down = up_then_down if self.mapping.upper() == "JW" and not self.up_then_down: - warnings.warn("The ILC ansatz requires spin-orbital ordering to be all spin-up " - "first followed by all spin-down for the JW mapping.", RuntimeWarning) + warnings.warn("Efficient generator screening for the ILC ansatz requires spin-orbital " + "ordering to be all spin-up first followed by all spin-down for the JW " + "mapping.", RuntimeWarning) self.up_then_down = True self.ilc_tau_guess = ilc_tau_guess @@ -101,7 +106,6 @@ def __init__(self, molecule, mapping="JW", up_then_down=False, ilc_op_list=None, self.qmf_var_params = qmf_var_params self.qmf_circuit = qmf_circuit self.n_trotter = n_trotter - self.verbose = verbose if qubit_ham is None: self.qubit_ham = fermion_to_qubit_mapping(self.fermi_ham, self.mapping, @@ -121,10 +125,8 @@ def __init__(self, molecule, mapping="JW", up_then_down=False, ilc_op_list=None, # Get purified QMF parameters and build the DIS & ACS or use a list of generators. if self.ilc_op_list is None: pure_var_params = purify_qmf_state(self.qmf_var_params, self.n_spinorbitals, - self.n_electrons, self.mapping, self.up_then_down, - self.spin, self.verbose) - self.dis = construct_dis(self.qubit_ham, pure_var_params, self.deilc_dtau_thresh, - self.verbose) + self.n_electrons, self.mapping, self.up_then_down, self.spin) + self.dis = construct_dis(self.qubit_ham, pure_var_params, self.deilc_dtau_thresh) self.max_ilc_gens = len(self.dis) if self.max_ilc_gens is None\ else min(len(self.dis), self.max_ilc_gens) self.acs = construct_acs(self.dis, self.max_ilc_gens, self.n_qubits) @@ -264,7 +266,6 @@ def _get_ilc_op(self): max_ilc_gens (int or None): Maximum number of generators allowed in the ansatz. If None, one generator from each DIS group is selected. If int, min(|DIS|, max_ilc_gens) generators are selected in order of decreasing |dEILC/dtau| values. - verbose (bool): Flag for QCC verbosity. Returns: list of QubitOperator: the list of ILC qubit operators ordered according to the @@ -274,10 +275,8 @@ def _get_ilc_op(self): # Rebuild DIS & ACS in case qubit_ham changed or they and qubit_op_list don't exist if self.rebuild_dis or self.rebuild_acs or ((not self.dis or not self.acs) and not self.ilc_op_list): pure_var_params = purify_qmf_state(self.qmf_var_params, self.n_spinorbitals, - self.n_electrons, self.mapping, self.up_then_down, - self.spin, self.verbose) - self.dis = construct_dis(self.qubit_ham, pure_var_params, self.deilc_dtau_thresh, - self.verbose) + self.n_electrons, self.mapping, self.up_then_down, self.spin) + self.dis = construct_dis(self.qubit_ham, pure_var_params, self.deilc_dtau_thresh) self.max_ilc_gens = len(self.dis) if self.max_ilc_gens is None\ else min(len(self.dis), self.max_ilc_gens) self.acs = construct_acs(self.dis, self.max_ilc_gens, self.n_qubits) diff --git a/tangelo/toolboxes/ansatz_generator/qcc.py b/tangelo/toolboxes/ansatz_generator/qcc.py index 6df9879ee..dc3870ef9 100755 --- a/tangelo/toolboxes/ansatz_generator/qcc.py +++ b/tangelo/toolboxes/ansatz_generator/qcc.py @@ -60,7 +60,7 @@ class QCC(Ansatz): followed by all spin-down. Default, False. qcc_op_list (list of QubitOperator): Generator list for the QCC ansatz. Default, None. qmf_circuit (Circuit): An instance of tangelo.linq Circuit class implementing a QMF state - preparation circuit. If passed from the QMF ansatz class, parameters are variational. + circuit. If passed from the QMF ansatz class, parameters are variational. If None, one is created with QMF parameters that are not variational. Default, None. qmf_var_params (list or numpy array of float): QMF variational parameter set. If None, the values are determined using a Hartree-Fock reference state. Default, None. @@ -73,35 +73,48 @@ class QCC(Ansatz): max_qcc_gens (int or None): Maximum number of generators allowed in the ansatz. If None, one generator from each DIS group is selected. If int, then min(|DIS|, max_qcc_gens) generators are selected in order of decreasing |dEQCC/dtau|. Default, None. - verbose (bool): Flag for QCC verbosity. Default, False. + scfdata (tuple): tuple containing an instance of OpenFermion MolecularData and a + FermionOperator corresponding to the fermionic Hamiltonian. """ def __init__(self, molecule, mapping="JW", up_then_down=False, qcc_op_list=None, qmf_circuit=None, qmf_var_params=None, qubit_ham=None, qcc_tau_guess=1.e-2, - deqcc_dtau_thresh=1.e-3, max_qcc_gens=None, verbose=False): + deqcc_dtau_thresh=1.e-3, max_qcc_gens=None, scfdata=None): - self.molecule = molecule - self.n_spinorbitals = self.molecule.n_active_sos - if self.n_spinorbitals % 2 != 0: - raise ValueError("The total number of spin-orbitals should be even.") + if molecule: + self.molecule = molecule + self.n_spinorbitals = self.molecule.n_active_sos + if self.n_spinorbitals % 2 != 0: + raise ValueError("The total number of spin-orbitals should be even.") - self.n_electrons = self.molecule.n_active_electrons - self.spin = molecule.spin + self.spin = molecule.spin + self.fermi_ham = self.molecule.fermionic_hamiltonian + elif scfdata: + self.molecule = scfdata[0] + self.n_spinorbitals = 2 * self.molecule.n_orbitals + self.spin = self.molecule.multiplicity - 1 + self.fermi_ham = scfdata[1] + self.n_electrons = self.molecule.n_electrons self.mapping = mapping self.n_qubits = get_qubit_number(self.mapping, self.n_spinorbitals) self.up_then_down = up_then_down if self.mapping.upper() == "JW" and not self.up_then_down: - warnings.warn("The QCC ansatz requires spin-orbital ordering to be all spin-up " - "first followed by all spin-down for the JW mapping.", RuntimeWarning) + warnings.warn("Efficient generator screening for the QCC ansatz requires spin-orbital " + "ordering to be all spin-up first followed by all spin-down for the JW " + "mapping.", RuntimeWarning) self.up_then_down = True + self.molecule = molecule + self.n_spinorbitals = self.molecule.n_active_sos + if self.n_spinorbitals % 2 != 0: + raise ValueError("The total number of spin-orbitals should be even.") + self.qcc_tau_guess = qcc_tau_guess self.deqcc_dtau_thresh = deqcc_dtau_thresh self.max_qcc_gens = max_qcc_gens self.qcc_op_list = qcc_op_list self.qmf_var_params = qmf_var_params self.qmf_circuit = qmf_circuit - self.verbose = verbose if qubit_ham is None: self.fermi_ham = self.molecule.fermionic_hamiltonian @@ -122,10 +135,8 @@ def __init__(self, molecule, mapping="JW", up_then_down=False, qcc_op_list=None, # Get purified QMF parameters and use them to build the DIS or use a list of generators. if self.qcc_op_list is None: pure_var_params = purify_qmf_state(self.qmf_var_params, self.n_spinorbitals, - self.n_electrons, self.mapping, self.up_then_down, - self.spin, self.verbose) - self.dis = construct_dis(self.qubit_ham, pure_var_params, self.deqcc_dtau_thresh, - self.verbose) + self.n_electrons, self.mapping, self.up_then_down, self.spin) + self.dis = construct_dis(self.qubit_ham, pure_var_params, self.deqcc_dtau_thresh) self.n_var_params = len(self.dis) if self.max_qcc_gens is None\ else min(len(self.dis), self.max_qcc_gens) else: @@ -266,7 +277,6 @@ def _get_qcc_qubit_op(self): max_qcc_gens (int or None): Maximum number of generators allowed in the ansatz. If None, one generator from each DIS group is selected. If int, min(|DIS|, max_qcc_gens) generators are selected in order of decreasing |dEQCC/dtau| values. - verbose (bool): Flag for QCC verbosity. Returns: QubitOperator: QCC ansatz qubit operator. @@ -275,10 +285,8 @@ def _get_qcc_qubit_op(self): # Rebuild DIS if qubit_ham or qmf_var_params changed or if DIS and qcc_op_list are None. if self.rebuild_dis or (self.dis is None and self.qcc_op_list is None): pure_var_params = purify_qmf_state(self.qmf_var_params, self.n_spinorbitals, - self.n_electrons, self.mapping, self.up_then_down, - self.spin, self.verbose) - self.dis = construct_dis(self.qubit_ham, pure_var_params, self.deqcc_dtau_thresh, - self.verbose) + self.n_electrons, self.mapping, self.up_then_down, self.spin) + self.dis = construct_dis(self.qubit_ham, pure_var_params, self.deqcc_dtau_thresh) self.n_var_params = len(self.dis) if self.max_qcc_gens is None\ else min(len(self.dis), self.max_qcc_gens) self.qcc_op_list = None From 5e23f3c87dd99becd481d54e5db5be691d63f9b1 Mon Sep 17 00:00:00 2001 From: Marc Coons Date: Mon, 7 Mar 2022 12:37:37 -0500 Subject: [PATCH 09/28] improvements to qmf, qcc, and ilc ansatze --- tangelo/toolboxes/ansatz_generator/_qubit_cc.py | 2 +- tangelo/toolboxes/ansatz_generator/_qubit_ilc.py | 3 +-- tangelo/toolboxes/ansatz_generator/_qubit_mf.py | 4 ++-- 3 files changed, 4 insertions(+), 5 deletions(-) diff --git a/tangelo/toolboxes/ansatz_generator/_qubit_cc.py b/tangelo/toolboxes/ansatz_generator/_qubit_cc.py index 80792e42b..eda4b751d 100644 --- a/tangelo/toolboxes/ansatz_generator/_qubit_cc.py +++ b/tangelo/toolboxes/ansatz_generator/_qubit_cc.py @@ -59,7 +59,7 @@ def construct_dis(qubit_ham, pure_var_params, deqcc_dtau_thresh): # Use a qubit Hamiltonian and purified QMF parameter set to construct the DIS dis, dis_groups = [], get_dis_groups(qubit_ham, pure_var_params, deqcc_dtau_thresh) if dis_groups: - for i, dis_group in enumerate(dis_groups): + for dis_group in dis_groups: dis_group_idxs = [int(idxs) for idxs in dis_group[0].split(" ")] dis_group_gens = get_gens_from_idxs(dis_group_idxs) dis.append(dis_group_gens) diff --git a/tangelo/toolboxes/ansatz_generator/_qubit_ilc.py b/tangelo/toolboxes/ansatz_generator/_qubit_ilc.py index c124fc588..a97e1bc99 100644 --- a/tangelo/toolboxes/ansatz_generator/_qubit_ilc.py +++ b/tangelo/toolboxes/ansatz_generator/_qubit_ilc.py @@ -28,7 +28,6 @@ J. Parallel Distrib. Comput., 1991, 13, 118–122. """ -#from random import choice import scipy import numpy as np @@ -138,7 +137,7 @@ def gauss_elim_over_gf2(A, zdim): b = z[2] for zf in (z[1]): if z_sln[zf] == -1: - z_sln[zf] = 1. #choice([0., 1.]) + z_sln[zf] = 1. b = (b + z_sln[zf]) % 2 z_sln[z[0]] = b return z_sln diff --git a/tangelo/toolboxes/ansatz_generator/_qubit_mf.py b/tangelo/toolboxes/ansatz_generator/_qubit_mf.py index 0eddb4000..691d640d9 100644 --- a/tangelo/toolboxes/ansatz_generator/_qubit_mf.py +++ b/tangelo/toolboxes/ansatz_generator/_qubit_mf.py @@ -17,7 +17,7 @@ 1. Analytical evaluation of an expectation value of a QubitOperator using a QMF wave function; 2. Initialization of the QMF variational parameter set {Omega} from a - Hartree-Fock reference state; + Hartree-Fock reference state; 3. Purification {Omega} when building and screening the DIS of QCC generators; 4. Construction of a QMF state circuit using {Omega}; 5. Addition of terms for N, S^2, and Sz that penalize a mean-field Hamiltonian @@ -153,7 +153,7 @@ def get_qmf_circuit(qmf_var_params, variational=True): variational (bool): Flag to treat {Omega} variationally or keep them fixed. Returns: - Circuit: instance of tangelo.linq Circuit class. + Circuit: instance of tangelo.linq Circuit class. """ n_qubits, gates = qmf_var_params.size // 2, [] From dbb14529a09f1d33321e6130323e2bf9c537eb0c Mon Sep 17 00:00:00 2001 From: Marc Coons Date: Wed, 9 Mar 2022 12:43:13 -0500 Subject: [PATCH 10/28] added ILC ansatz to vqesolver and test_vqe_solver --- .../variational/tests/test_vqe_solver.py | 38 ++++++ tangelo/algorithms/variational/vqe_solver.py | 13 +- .../toolboxes/ansatz_generator/_qubit_ilc.py | 125 ++++++++++-------- 3 files changed, 113 insertions(+), 63 deletions(-) diff --git a/tangelo/algorithms/variational/tests/test_vqe_solver.py b/tangelo/algorithms/variational/tests/test_vqe_solver.py index eefabdc4f..fac5497dd 100644 --- a/tangelo/algorithms/variational/tests/test_vqe_solver.py +++ b/tangelo/algorithms/variational/tests/test_vqe_solver.py @@ -21,6 +21,7 @@ from tangelo.toolboxes.ansatz_generator.uccsd import UCCSD from tangelo.toolboxes.ansatz_generator.qmf import QMF from tangelo.toolboxes.ansatz_generator.qcc import QCC +from tangelo.toolboxes.ansatz_generator.ilc import ILC from tangelo.toolboxes.qubit_mappings.mapping_transform import fermion_to_qubit_mapping from tangelo.toolboxes.molecular_computation.rdms import matricize_2rdm @@ -137,6 +138,18 @@ def test_simulate_qcc_h2(self): energy = vqe_solver.simulate() self.assertAlmostEqual(energy, -1.137270, delta=1e-4) + def test_simulate_ilc_h2(self): + """Run VQE on H2 molecule, with ILC ansatz, JW qubit mapping, initial + parameters, exact simulator. + """ + vqe_options = {"molecule": mol_H2_sto3g, "ansatz": BuiltInAnsatze.ILC, "qubit_mapping": "jw", + "verbose": True} + vqe_solver = VQESolver(vqe_options) + vqe_solver.build() + + energy = vqe_solver.simulate() + self.assertAlmostEqual(energy, -1.137270, delta=1e-4) + def test_simulate_vsqs_h2(self): """Run VQE on H2 molecule, with vsqs ansatz, JW qubit mapping, exact simulator for both molecule input and qubit_hamiltonian/hini/reference_state input @@ -214,6 +227,18 @@ def test_simulate_qcc_h4(self): energy = vqe_solver.simulate() self.assertAlmostEqual(energy, -1.963270, delta=1e-4) + def test_simulate_ilc_h4(self): + """Run VQE on H4 molecule, with ILC ansatz, JW qubit mapping, initial + parameters, exact simulator. + """ + vqe_options = {"molecule": mol_H4_sto3g, "ansatz": BuiltInAnsatze.ILC, "qubit_mapping": "jw", + "verbose": True} + vqe_solver = VQESolver(vqe_options) + vqe_solver.build() + + energy = vqe_solver.simulate() + self.assertAlmostEqual(energy, -1.960877, delta=1e-4) + def test_simulate_h4_open(self): """Run VQE on H4 molecule, with UCCSD ansatz, JW qubit mapping, initial parameters, exact simulator """ vqe_options = {"molecule": mol_H4_cation_sto3g, "ansatz": BuiltInAnsatze.UCCSD, "qubit_mapping": "jw", @@ -250,6 +275,19 @@ def test_simulate_qcc_h4_open(self): energy = vqe_solver.simulate() self.assertAlmostEqual(energy, -1.638020, delta=1e-4) + def test_simulate_ilc_h4_open(self): + """Run VQE on H4 + molecule, with ILC ansatz, JW qubit mapping, initial + parameters, exact simulator. + """ + + vqe_options = {"molecule": mol_H4_cation_sto3g, "ansatz": BuiltInAnsatze.ILC, "qubit_mapping": "jw", + "verbose": True} + vqe_solver = VQESolver(vqe_options) + vqe_solver.build() + + energy = vqe_solver.simulate() + self.assertAlmostEqual(energy, -1.638020, delta=1e-4) + def test_optimal_circuit_h4(self): """Run VQE on H4 molecule, save optimal circuit. Verify it yields optimal energy. diff --git a/tangelo/algorithms/variational/vqe_solver.py b/tangelo/algorithms/variational/vqe_solver.py index 6f8180696..28d2df9a0 100644 --- a/tangelo/algorithms/variational/vqe_solver.py +++ b/tangelo/algorithms/variational/vqe_solver.py @@ -29,7 +29,7 @@ from tangelo.toolboxes.operators import count_qubits, FermionOperator, qubitop_to_qubitham from tangelo.toolboxes.qubit_mappings.mapping_transform import fermion_to_qubit_mapping from tangelo.toolboxes.ansatz_generator.ansatz import Ansatz -from tangelo.toolboxes.ansatz_generator import UCCSD, RUCC, HEA, UpCCGSD, QMF, QCC, VSQS, VariationalCircuitAnsatz +from tangelo.toolboxes.ansatz_generator import UCCSD, RUCC, HEA, UpCCGSD, QMF, QCC, VSQS, VariationalCircuitAnsatz, ILC from tangelo.toolboxes.ansatz_generator.penalty_terms import combined_penalty from tangelo.toolboxes.post_processing.bootstrapping import get_resampled_frequencies from tangelo.toolboxes.ansatz_generator.fermionic_operators import number_operator, spinz_operator, spin2_operator @@ -45,6 +45,7 @@ class BuiltInAnsatze(Enum): QMF = 5 QCC = 6 VSQS = 7 + ILC = 8 class VQESolver: @@ -110,11 +111,11 @@ def __init__(self, opt_dict): if not (bool(self.molecule) ^ bool(self.qubit_hamiltonian)): raise ValueError(f"A molecule OR qubit Hamiltonian object must be provided when instantiating {self.__class__.__name__}.") - # The QCC ansatz requires up_then_down=True when mapping="jw" + # The QCC & ILC ansatze require up_then_down=True when mapping="jw" if isinstance(self.ansatz, BuiltInAnsatze): - if self.ansatz == BuiltInAnsatze.QCC and self.qubit_mapping.lower() == "jw" and not self.up_then_down: - warnings.warn("The QCC ansatz requires spin-orbital ordering to be all spin-up " - "first followed by all spin-down for the JW mapping.", RuntimeWarning) + if self.ansatz in (BuiltInAnsatze.QCC, BuiltInAnsatze.ILC) and self.qubit_mapping.lower() == "jw" and not self.up_then_down: + warnings.warn("Efficient generator screening for QCC-based ansatze require spin-orbital ordering to be " + "all spin-up first followed by all spin-down for the JW mapping.", RuntimeWarning) self.up_then_down = True self.optimal_energy = None @@ -183,6 +184,8 @@ def build(self): self.ansatz = QCC(self.molecule, self.qubit_mapping, self.up_then_down, **self.ansatz_options) elif self.ansatz == BuiltInAnsatze.VSQS: self.ansatz = VSQS(self.molecule, self.qubit_mapping, self.up_then_down, **self.ansatz_options) + elif self.ansatz == BuiltInAnsatze.ILC: + self.ansatz = ILC(self.molecule, self.qubit_mapping, self.up_then_down, **self.ansatz_options) else: raise ValueError(f"Unsupported ansatz. Built-in ansatze:\n\t{self.builtin_ansatze}") elif not isinstance(self.ansatz, Ansatz): diff --git a/tangelo/toolboxes/ansatz_generator/_qubit_ilc.py b/tangelo/toolboxes/ansatz_generator/_qubit_ilc.py index a97e1bc99..43f3bca34 100644 --- a/tangelo/toolboxes/ansatz_generator/_qubit_ilc.py +++ b/tangelo/toolboxes/ansatz_generator/_qubit_ilc.py @@ -38,13 +38,14 @@ def construct_acs(dis_gens, max_ilc_gens, n_qubits): """ DOCSTRING """ + bad_sln_idxs, good_sln = [], False while not good_sln: gen_idxs, ilc_gens = [idx for idx in range(max_ilc_gens) if idx not in bad_sln_idxs], [] n_gens = len(gen_idxs) ng2, ngnq = n_gens * (n_gens + 1) // 2, n_gens * n_qubits # cons_mat --> A and z_vec --> z in Appendix A, Refs. 1 & 2. - cons_matrix, z_vec = np.zeros((ng2, ngnq + 1)), np.zeros(ngnq) + cons_mat, z_vec = np.zeros((ng2, ngnq + 1)), np.zeros(ngnq) for idx, gen_idx in enumerate(gen_idxs): gen = dis_gens[gen_idx][-1] for term, _ in gen.terms.items(): @@ -55,16 +56,16 @@ def construct_acs(dis_gens, max_ilc_gens, n_qubits): # Form the triangular matrix-vector product A * z; last column is the soln vec (Appendix A, Refs. 1 & 2). r_idx = 0 for i in range(n_gens): - cons_matrix[r_idx, i * n_qubits:(i+1) * n_qubits] = z_vec[i * n_qubits:(i+1) * n_qubits] - cons_matrix[r_idx, ngnq] = 1 + cons_mat[r_idx, i * n_qubits:(i+1) * n_qubits] = z_vec[i * n_qubits:(i+1) * n_qubits] + cons_mat[r_idx, ngnq] = 1 r_idx += 1 for j in range(i+1, n_gens): - cons_matrix[r_idx, i * n_qubits:(i+1) * n_qubits] = z_vec[j * n_qubits:(j+1) * n_qubits] - cons_matrix[r_idx, j * n_qubits:(j+1) * n_qubits] = z_vec[i * n_qubits:(i+1) * n_qubits] - cons_matrix[r_idx, ngnq] = 1 + cons_mat[r_idx, i * n_qubits:(i+1) * n_qubits] = z_vec[j * n_qubits:(j+1) * n_qubits] + cons_mat[r_idx, j * n_qubits:(j+1) * n_qubits] = z_vec[i * n_qubits:(i+1) * n_qubits] + cons_mat[r_idx, ngnq] = 1 r_idx += 1 # Solve A * z = 1 - z_sln = gauss_elim_over_gf2(cons_matrix, ngnq) + z_sln = gauss_elim_over_gf2(cons_mat, ngnq) # Check solution: odd # of Y ops, at least two flip indices, and mutually anti-commutes good_sln = True for i in range(n_gens): @@ -99,85 +100,93 @@ def construct_acs(dis_gens, max_ilc_gens, n_qubits): return ilc_gens -def gauss_elim_over_gf2(A, zdim): - """ DOCSTRING +def gauss_elim_over_gf2(cons_mat, z_dim): + """ For more details see Ref. 3. """ - # Gaussian elimination over GF(2) -- based on Ref. 3. - n_rows, n_cols = np.shape(A)[0], np.shape(A)[1] - A, zs, z_sln, piv_idx = np.array(A), [], [-1]*zdim, 0 + + # Gaussian elimination over GF(2) + n_rows, n_cols = np.shape(cons_mat) + cons_mat, z_vals, z_sln, piv_idx = np.array(cons_mat), [], [-1] * z_dim, 0 for i in range(n_cols): - Aij_max = 0. + cons_mat_max = 0. max_idx = piv_idx for j in range(piv_idx, n_rows): - if A[j, i] > Aij_max: + if cons_mat[j, i] > cons_mat_max: max_idx = j - Aij_max = A[j, i] - elif j == n_rows-1 and Aij_max == 0.: + cons_mat_max = cons_mat[j, i] + elif j == n_rows-1 and cons_mat_max == 0.: piv_idx = max_idx - Aij_max = -1. - if Aij_max > 0.: + cons_mat_max = -1. + if cons_mat_max > 0.: if max_idx > piv_idx: - A[[piv_idx, max_idx]] = A[[max_idx, piv_idx]] - for j in range(piv_idx+1, n_rows): - if A[j, i] == 1.: - A[j, i:n_cols] = np.fmod(A[j, i:n_cols] + A[piv_idx, i:n_cols], 2) + cons_mat[[piv_idx, max_idx]] = cons_mat[[max_idx, piv_idx]] + for j in range(piv_idx + 1, n_rows): + if cons_mat[j, i] == 1.: + cons_mat[j, i:n_cols] = np.fmod(cons_mat[j, i : n_cols] + cons_mat[piv_idx, i : n_cols], 2) piv_idx += 1 - b = A[0:n_rows, n_cols-1].tolist() - for i in range(n_rows-1, -1, -1): - col_idx, zf = -1., [] + b_vec = cons_mat[0 : n_rows, n_cols-1].tolist() + for i in range(n_rows - 1, -1, -1): + col_idx, z_free = -1., [] for j in range(n_cols-1): - if A[i, j] == 1.: + if cons_mat[i, j] == 1.: if col_idx == -1: col_idx = j else: - zf.append(j) + z_free.append(j) if col_idx >= 0.: - zs.append([col_idx, zf, b[i]]) - for z in (zs): - b = z[2] - for zf in (z[1]): - if z_sln[zf] == -1: - z_sln[zf] = 1. - b = (b + z_sln[zf]) % 2 - z_sln[z[0]] = b + z_vals.append([col_idx, z_free, b_vec[i]]) + for z_val in (z_vals): + b_val = z_val[2] + for z_free in (z_val[1]): + if z_sln[z_free] == -1: + z_sln[z_free] = 1. + b_val = (b_val + z_sln[z_free]) % 2 + z_sln[z_val[0]] = b_val return z_sln def init_ilc_by_diag(qubit_ham, ilc_gens, qmf_var_params): - """ DOCSTRING + """ For more information see Appendix B, Refs. 1 & 2 and Appendix C, Ref. 1. """ + + # Temporarily add the identity operator to the ACS ilc_gens.insert(0, QubitOperator.identity()) n_var_params = len(ilc_gens) - # Form the Hamiltonian and overlap matrices (see Appendix B, Refs. 1 & 2). - H = np.zeros((n_var_params, n_var_params), dtype=complex) - S = np.zeros((n_var_params, n_var_params), dtype=complex) + qubit_ham_mat = np.zeros((n_var_params, n_var_params), dtype=complex) + qubit_overlap_mat = np.zeros((n_var_params, n_var_params), dtype=complex) + + # Construct the lower triangular matrices for the qubit Hamiltonian and overlap integrals for i in range(n_var_params): - H_i = qubit_ham * ilc_gens[i] - H[i, i] = get_op_expval(ilc_gens[i] * H_i, qmf_var_params) - S[i, i] = 1. + 0j + # H T_i|QMF> = H |psi_i> + h_psi_i = qubit_ham * ilc_gens[i] + # = = H_ii + qubit_ham_mat[i, i] = get_op_expval(ilc_gens[i] * h_psi_i, qmf_var_params) + # = = 1 + qubit_overlap_mat[i, i] = 1. + 0j for j in range(i + 1, n_var_params): - H[j, i] = get_op_expval(ilc_gens[j] * H_i, qmf_var_params) - S[j, i] = get_op_expval(ilc_gens[j] * ilc_gens[i], qmf_var_params) + # = = H_ji + qubit_ham_mat[j, i] = get_op_expval(ilc_gens[j] * h_psi_i, qmf_var_params) + # = --> exactly zero only for pure QMF states + qubit_overlap_mat[j, i] = get_op_expval(ilc_gens[j] * ilc_gens[i], qmf_var_params) if i == 0: - H[j, i] *= 1j - S[j, i] *= 1j + qubit_ham_mat[j, i] *= 1j + qubit_overlap_mat[j, i] *= 1j + # Solve the generalized eigenvalue problem - E, c = scipy.linalg.eigh(a=np.matrix(H), b=np.matrix(S), lower=True, driver="gvd") - print(" Eigenvalues from matrix diagonalization = ", E) - # Compute the ILC parameters according to Appendix C, Ref. 1). - c0 = c[:, 0] - if c0[0].real > 0.: - c0 *= -1. - print(" Ground state eigenvector c0 = ", c0) + _, subspace_coefs = scipy.linalg.eigh(a=qubit_ham_mat, b=qubit_overlap_mat, lower=True, driver="gvd") + + # Compute the ILC parameters using the ground state coefficients + gs_coefs = subspace_coefs[:, 0] + if gs_coefs[0].real > 0.: + gs_coefs *= -1. denom_sum, ilc_var_params = 0., [] for i in range(2): - denom_sum += pow(c0[i].real, 2.) + pow(c0[i].imag, 2.) - beta_1 = np.arcsin(c0[1] / np.sqrt(denom_sum)) + denom_sum += pow(gs_coefs[i].real, 2.) + pow(gs_coefs[i].imag, 2.) + beta_1 = np.arcsin(gs_coefs[1] / np.sqrt(denom_sum)) ilc_var_params.append(beta_1.real) for i in range(2, n_var_params): - denom_sum += pow(c0[i].real, 2.) + pow(c0[i].imag, 2.) - beta = np.arcsin(c0[i] / np.sqrt(denom_sum)) + denom_sum += pow(gs_coefs[i].real, 2.) + pow(gs_coefs[i].imag, 2.) + beta = np.arcsin(gs_coefs[i] / np.sqrt(denom_sum)) ilc_var_params.append(beta.real) del ilc_gens[0] - print(" ILC var params (beta's in Appendix C, ref. 1) = ", ilc_var_params) return ilc_var_params From 9820d4bdbe9b2281f524999a3c31e0ff6ab37f14 Mon Sep 17 00:00:00 2001 From: Marc Coons Date: Wed, 9 Mar 2022 12:50:03 -0500 Subject: [PATCH 11/28] added ILC ansatz to vqesolver and test_vqe_solver --- tangelo/toolboxes/ansatz_generator/_qubit_ilc.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tangelo/toolboxes/ansatz_generator/_qubit_ilc.py b/tangelo/toolboxes/ansatz_generator/_qubit_ilc.py index 43f3bca34..a2103196c 100644 --- a/tangelo/toolboxes/ansatz_generator/_qubit_ilc.py +++ b/tangelo/toolboxes/ansatz_generator/_qubit_ilc.py @@ -122,9 +122,9 @@ def gauss_elim_over_gf2(cons_mat, z_dim): cons_mat[[piv_idx, max_idx]] = cons_mat[[max_idx, piv_idx]] for j in range(piv_idx + 1, n_rows): if cons_mat[j, i] == 1.: - cons_mat[j, i:n_cols] = np.fmod(cons_mat[j, i : n_cols] + cons_mat[piv_idx, i : n_cols], 2) + cons_mat[j, i:n_cols] = np.fmod(cons_mat[j, i:n_cols] + cons_mat[piv_idx, i:n_cols], 2) piv_idx += 1 - b_vec = cons_mat[0 : n_rows, n_cols-1].tolist() + b_vec = cons_mat[0:n_rows, n_cols-1].tolist() for i in range(n_rows - 1, -1, -1): col_idx, z_free = -1., [] for j in range(n_cols-1): From 50c66c02fe0b1fedbce8cdd45006f448114241b6 Mon Sep 17 00:00:00 2001 From: Marc Coons Date: Wed, 9 Mar 2022 20:39:13 -0500 Subject: [PATCH 12/28] last set of revisions to prepare the ilc_ansatz branch for PR --- .../variational/tests/test_vqe_solver.py | 3 - .../toolboxes/ansatz_generator/_qubit_ilc.py | 53 ++++++-- tangelo/toolboxes/ansatz_generator/ilc.py | 14 +- tangelo/toolboxes/ansatz_generator/qcc.py | 8 +- tangelo/toolboxes/ansatz_generator/qmf.py | 13 +- .../ansatz_generator/tests/test_ilc.py | 127 ++++++++++++++++++ .../ansatz_generator/tests/test_qcc.py | 3 +- .../ansatz_generator/tests/test_qmf.py | 2 +- 8 files changed, 188 insertions(+), 35 deletions(-) create mode 100644 tangelo/toolboxes/ansatz_generator/tests/test_ilc.py diff --git a/tangelo/algorithms/variational/tests/test_vqe_solver.py b/tangelo/algorithms/variational/tests/test_vqe_solver.py index fac5497dd..601b6a0f9 100644 --- a/tangelo/algorithms/variational/tests/test_vqe_solver.py +++ b/tangelo/algorithms/variational/tests/test_vqe_solver.py @@ -19,9 +19,6 @@ from tangelo.algorithms import BuiltInAnsatze, VQESolver from tangelo.molecule_library import mol_H2_sto3g, mol_H4_sto3g, mol_H4_cation_sto3g, mol_NaH_sto3g from tangelo.toolboxes.ansatz_generator.uccsd import UCCSD -from tangelo.toolboxes.ansatz_generator.qmf import QMF -from tangelo.toolboxes.ansatz_generator.qcc import QCC -from tangelo.toolboxes.ansatz_generator.ilc import ILC from tangelo.toolboxes.qubit_mappings.mapping_transform import fermion_to_qubit_mapping from tangelo.toolboxes.molecular_computation.rdms import matricize_2rdm diff --git a/tangelo/toolboxes/ansatz_generator/_qubit_ilc.py b/tangelo/toolboxes/ansatz_generator/_qubit_ilc.py index a2103196c..b18d1c321 100644 --- a/tangelo/toolboxes/ansatz_generator/_qubit_ilc.py +++ b/tangelo/toolboxes/ansatz_generator/_qubit_ilc.py @@ -12,12 +12,10 @@ # See the License for the specific language governing permissions and # limitations under the License. -"""This module implements a collection of functions related to the ILC -ansatz: - 1. Function to create the anti-commuting set (ACS) of generators from - the QCC DIS; - 2. An efficient solver that performs Gaussian elimination over GF(2); - 3. Function that initializes the ILC parameters via matrix diagonalization. +"""This module implements a collection of functions related to the ILC ansatz: +1. Function to create the anti-commuting set (ACS) of generators from the QCC DIS; +2. An efficient solver that performs Gaussian elimination over GF(2); +3. Function that computes the ILC parameters via matrix diagonalization. Refs: 1. R. A. Lang, I. G. Ryabinkin, and A. F. Izmaylov. @@ -35,8 +33,17 @@ from ._qubit_mf import get_op_expval -def construct_acs(dis_gens, max_ilc_gens, n_qubits): - """ DOCSTRING +def construct_acs(dis, max_ilc_gens, n_qubits): + """Driver function for constructing the anti-commuting set of generators from + the direct interaction set (DIS) of QCC generators. + + Args: + dis (list of list): DIS of QCC generators. + max_ilc_gens (int): Maximum number of generators allowed in the ansatz. + n_qubits (int): number of qubits + + Returns: + list of QubitOperator: the anti-commuting set (ACS) of ILC generators """ bad_sln_idxs, good_sln = [], False @@ -47,13 +54,13 @@ def construct_acs(dis_gens, max_ilc_gens, n_qubits): # cons_mat --> A and z_vec --> z in Appendix A, Refs. 1 & 2. cons_mat, z_vec = np.zeros((ng2, ngnq + 1)), np.zeros(ngnq) for idx, gen_idx in enumerate(gen_idxs): - gen = dis_gens[gen_idx][-1] + gen = dis[gen_idx][-1] for term, _ in gen.terms.items(): for paulis in term: p_idx, pauli = paulis if 'X' in pauli or 'Y' in pauli: z_vec[idx * n_qubits + p_idx] = 1. - # Form the triangular matrix-vector product A * z; last column is the soln vec (Appendix A, Refs. 1 & 2). + # Form the rectangular matrix-vector product A * z; last column is the soln vec (Appendix A, Refs. 1 & 2). r_idx = 0 for i in range(n_gens): cons_mat[r_idx, i * n_qubits:(i+1) * n_qubits] = z_vec[i * n_qubits:(i+1) * n_qubits] @@ -101,7 +108,18 @@ def construct_acs(dis_gens, max_ilc_gens, n_qubits): def gauss_elim_over_gf2(cons_mat, z_dim): - """ For more details see Ref. 3. + """Driver function that performs Gaussian elimination to solve A * z = b + over the binary field where b is a vector of ones. This routine was adapted + based on Ref. 3. + + Args: + cons_mat (numpy array of int): the rectangular constraint matrix A (see Refs. 1 & 2). + The last column initially holds the vector b. + z_dim (int): the dimensionality of the constraint matrix A: number of ILC generators + times the number of qubits. + + Returns: + list of float: the solution vector """ # Gaussian elimination over GF(2) @@ -145,8 +163,17 @@ def gauss_elim_over_gf2(cons_mat, z_dim): return z_sln -def init_ilc_by_diag(qubit_ham, ilc_gens, qmf_var_params): - """ For more information see Appendix B, Refs. 1 & 2 and Appendix C, Ref. 1. +def get_ilc_params_by_diag(qubit_ham, ilc_gens, qmf_var_params): + """Driver function that solves the generalized eigenvalue problem Hc = ESc required + to obtain the ground state coefficients (ILC parameters). These are subsequently recast + according to Appendix C of Ref. 1 in a form that is suitable for constructing ILC circuits. + + Args: + qubit_ham (QubitOperator): the qubit Hamiltonian of the system. + ilc_gens (list of QubitOperator): the anti-commuting set of ILC Pauli words. + + Returns: + list of float: the ILC parameters corresponding to the ACS of ILC generators """ # Temporarily add the identity operator to the ACS diff --git a/tangelo/toolboxes/ansatz_generator/ilc.py b/tangelo/toolboxes/ansatz_generator/ilc.py index 58fddc2d6..5d29ae47d 100755 --- a/tangelo/toolboxes/ansatz_generator/ilc.py +++ b/tangelo/toolboxes/ansatz_generator/ilc.py @@ -12,7 +12,7 @@ # See the License for the specific language governing permissions and # limitations under the License. -"""This module defines the qubit coupled cluster ansatz class for involutory +"""This module defines the qubit coupled cluster ansatz class with involutory linear combinations (ILC) of anti-commuting sets (ACS) of Pauli words (generators). Relative to the direct interation set (DIS) of QCC generators, which incur an exponential growth of Hamiltonian terms upon dressing, the ACS @@ -37,7 +37,7 @@ from .ansatz_utils import exp_pauliword_to_gates from ._qubit_mf import init_qmf_from_hf, get_qmf_circuit, purify_qmf_state from ._qubit_cc import construct_dis -from ._qubit_ilc import construct_acs, init_ilc_by_diag +from ._qubit_ilc import construct_acs, get_ilc_params_by_diag class ILC(Ansatz): @@ -99,10 +99,10 @@ def __init__(self, molecule, mapping="JW", up_then_down=False, ilc_op_list=None, "mapping.", RuntimeWarning) self.up_then_down = True + self.ilc_op_list = ilc_op_list self.ilc_tau_guess = ilc_tau_guess self.deilc_dtau_thresh = deilc_dtau_thresh self.max_ilc_gens = max_ilc_gens - self.ilc_op_list = ilc_op_list self.qmf_var_params = qmf_var_params self.qmf_circuit = qmf_circuit self.n_trotter = n_trotter @@ -133,7 +133,7 @@ def __init__(self, molecule, mapping="JW", up_then_down=False, ilc_op_list=None, self.n_var_params = len(self.acs) else: self.dis = None - self.acs = None + self.acs = self.ilc_op_list self.n_var_params = len(self.ilc_op_list) # Supported reference state initialization @@ -143,7 +143,7 @@ def __init__(self, molecule, mapping="JW", up_then_down=False, ilc_op_list=None, # Default starting parameters for initialization self.default_reference_state = "HF" - self.var_params_default = "diag" + self.var_params_default = "ilc_tau_guess" self.var_params = None self.rebuild_dis = False self.rebuild_acs = False @@ -175,7 +175,7 @@ def set_var_params(self, var_params=None): initial_var_params = 2. * self.ilc_tau_guess * np.random.random((self.n_var_params,)) - self.ilc_tau_guess # Initialize ILC parameters by matrix diagonalization (see Appendix B, Refs. 1 & 2). elif var_params == "diag": - initial_var_params = init_ilc_by_diag(self.qubit_ham, self.acs, self.qmf_var_params) + initial_var_params = get_ilc_params_by_diag(self.qubit_ham, self.acs, self.qmf_var_params) else: initial_var_params = np.array(var_params) if initial_var_params.size != self.n_var_params: @@ -273,7 +273,7 @@ def _get_ilc_op(self): """ # Rebuild DIS & ACS in case qubit_ham changed or they and qubit_op_list don't exist - if self.rebuild_dis or self.rebuild_acs or ((not self.dis or not self.acs) and not self.ilc_op_list): + if self.rebuild_dis or self.rebuild_acs or not self.acs: pure_var_params = purify_qmf_state(self.qmf_var_params, self.n_spinorbitals, self.n_electrons, self.mapping, self.up_then_down, self.spin) self.dis = construct_dis(self.qubit_ham, pure_var_params, self.deilc_dtau_thresh) diff --git a/tangelo/toolboxes/ansatz_generator/qcc.py b/tangelo/toolboxes/ansatz_generator/qcc.py index dc3870ef9..31c78253c 100755 --- a/tangelo/toolboxes/ansatz_generator/qcc.py +++ b/tangelo/toolboxes/ansatz_generator/qcc.py @@ -134,8 +134,8 @@ def __init__(self, molecule, mapping="JW", up_then_down=False, qcc_op_list=None, # Get purified QMF parameters and use them to build the DIS or use a list of generators. if self.qcc_op_list is None: - pure_var_params = purify_qmf_state(self.qmf_var_params, self.n_spinorbitals, - self.n_electrons, self.mapping, self.up_then_down, self.spin) + pure_var_params = purify_qmf_state(self.qmf_var_params, self.n_spinorbitals, self.n_electrons, + self.mapping, self.up_then_down, self.spin) self.dis = construct_dis(self.qubit_ham, pure_var_params, self.deqcc_dtau_thresh) self.n_var_params = len(self.dis) if self.max_qcc_gens is None\ else min(len(self.dis), self.max_qcc_gens) @@ -284,8 +284,8 @@ def _get_qcc_qubit_op(self): # Rebuild DIS if qubit_ham or qmf_var_params changed or if DIS and qcc_op_list are None. if self.rebuild_dis or (self.dis is None and self.qcc_op_list is None): - pure_var_params = purify_qmf_state(self.qmf_var_params, self.n_spinorbitals, - self.n_electrons, self.mapping, self.up_then_down, self.spin) + pure_var_params = purify_qmf_state(self.qmf_var_params, self.n_spinorbitals, self.n_electrons, + self.mapping, self.up_then_down, self.spin) self.dis = construct_dis(self.qubit_ham, pure_var_params, self.deqcc_dtau_thresh) self.n_var_params = len(self.dis) if self.max_qcc_gens is None\ else min(len(self.dis), self.max_qcc_gens) diff --git a/tangelo/toolboxes/ansatz_generator/qmf.py b/tangelo/toolboxes/ansatz_generator/qmf.py index 63ef15f8f..efaf467c7 100755 --- a/tangelo/toolboxes/ansatz_generator/qmf.py +++ b/tangelo/toolboxes/ansatz_generator/qmf.py @@ -96,7 +96,8 @@ def __init__(self, molecule=None, mapping="JW", up_then_down=False, init_qmf=Non self.init_qmf = {"init_params": "hf_state"} if init_qmf is None else init_qmf # Supported var param initialization - self.supported_initial_var_params = {"vacuum", "full_Q", "full_Qdag", "full_occ", "random", "hf_state"} + self.supported_initial_var_params = {"vacuum", "half_pi", "minus_half_pi", "full_pi", + "random", "hf_state"} # Supported reference state initialization self.supported_reference_state = {"HF"} @@ -160,13 +161,13 @@ def set_var_params(self, var_params=None): if var_params == "vacuum": initial_var_params = np.zeros((self.n_var_params,), dtype=float) # Initialize |QMF> as (1/sqrt(2))^n_qubits * tensor_prod(|0> + 1j|1>) - elif var_params == "full_Q": + elif var_params == "half_pi": initial_var_params = 0.5 * np.pi * np.ones((self.n_var_params,)) # Initialize |QMF> as (1/sqrt(2))^n_qubits * tensor_prod(|0> - 1j|1>) - elif var_params == "full_Qdag": - initial_var_params = 1.5 * np.pi * np.ones((self.n_var_params,)) - # Initialize |QMF> as (-1)^n_qubits |11...1> - elif var_params == "full_occ": + elif var_params == "minus_half_pi": + initial_var_params = -0.5 * np.pi * np.ones((self.n_var_params,)) + # Initialize |QMF> as (i)^n_qubits * |11...1> + elif var_params == "full_pi": initial_var_params = np.pi * np.ones((self.n_var_params,)) # Random initialization of thetas over [0, pi] and phis over [0, 2 * pi] elif var_params == "random": diff --git a/tangelo/toolboxes/ansatz_generator/tests/test_ilc.py b/tangelo/toolboxes/ansatz_generator/tests/test_ilc.py new file mode 100644 index 000000000..2fb1d11fd --- /dev/null +++ b/tangelo/toolboxes/ansatz_generator/tests/test_ilc.py @@ -0,0 +1,127 @@ +# Copyright 2021 Good Chemistry Company. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""Unit tests for closed-shell and restricted open-shell qubit coupled cluster +with involutory linear combinations (ILC) of anti-commuting sets (ACS) of Pauli words.""" + +import unittest +import numpy as np + +from tangelo.linq import Simulator +from tangelo.toolboxes.ansatz_generator.ilc import ILC +from tangelo.toolboxes.operators.operators import QubitOperator +from tangelo.molecule_library import mol_H2_sto3g, mol_H4_sto3g, mol_H4_cation_sto3g + +sim = Simulator() + + +class ILCTest(unittest.TestCase): + """Unit tests for various functionalities of the ILC ansatz class. Examples for both closed- + and restricted open-shell ILC are provided using H2, H4, and H4 +. + """ + + @staticmethod + def test_ilc_set_var_params(): + """ Verify behavior of set_var_params for different inputs (keyword, list, numpy array). """ + + ilc_ansatz = ILC(mol_H2_sto3g, up_then_down=True) + + one_zero = np.zeros((1,), dtype=float) + + ilc_ansatz.set_var_params("qmf_state") + np.testing.assert_array_almost_equal(ilc_ansatz.var_params, one_zero, decimal=6) + + ilc_ansatz.set_var_params([0.]) + np.testing.assert_array_almost_equal(ilc_ansatz.var_params, one_zero, decimal=6) + + one_tenth = 0.1 * np.ones((1,)) + + ilc_ansatz.set_var_params([0.1]) + np.testing.assert_array_almost_equal(ilc_ansatz.var_params, one_tenth, decimal=6) + + ilc_ansatz.set_var_params(np.array([0.1])) + np.testing.assert_array_almost_equal(ilc_ansatz.var_params, one_tenth, decimal=6) + + def test_ilc_incorrect_number_var_params(self): + """ Return an error if user provide incorrect number of variational parameters """ + + ilc_ansatz = ILC(mol_H2_sto3g, up_then_down=True) + + self.assertRaises(ValueError, ilc_ansatz.set_var_params, np.array([1.] * 2)) + + def test_ilc_h2(self): + """ Verify closed-shell functionality when using the ILC class separately for H2 """ + + # Build the ILC ansatz, which sets the QMF parameters automatically if none are passed + ilc_var_params = [0.11360304] + ilc_op_list = [QubitOperator("X0 Y1 Y2 Y3")] + ilc_ansatz = ILC(mol_H2_sto3g, up_then_down=True, ilc_op_list=ilc_op_list) + + # Build a QMF + ILC circuit + ilc_ansatz.build_circuit() + + # Get qubit hamiltonian for energy evaluation + qubit_hamiltonian = ilc_ansatz.qubit_ham + + # Assert energy returned is as expected for given parameters + ilc_ansatz.update_var_params(ilc_var_params) + energy = sim.get_expectation_value(qubit_hamiltonian, ilc_ansatz.circuit) + self.assertAlmostEqual(energy, -1.1372697, delta=1e-6) + + def test_ilc_h4(self): + """ Verify restricted open-shell functionality when using the ILC class for H4 """ + + # Build the ILC ansatz, which sets the QMF parameters automatically if none are passed + ilc_op_list = [QubitOperator("Z0 X1 Z3 Y4 Z5"), QubitOperator("X0 X1 Y2 X3 Y4 Y5"), QubitOperator("Z0 X1 Y2 Y4 Y5"), + QubitOperator("Y0 X1 Y2 Y4"), QubitOperator("Y1 X3 Y4 Y5"), QubitOperator("Y0 Y1 X3 Y4 Z5"), + QubitOperator("X0 Y1 X2 Z3 Y4 Y5"), QubitOperator("X0 Y1 Z2 Z3 Y4 Y5"), QubitOperator("Z0 X1 Y2 Y3 Y4 Z5")] + ilc_var_params = [ 0.01902128, -0.01425816, 0.68617146, 0.18603782, -0.20042697, 0.07894877, + -0.09087040, 0.06681858, -0.07056268] + ilc_ansatz = ILC(mol_H4_sto3g, "SCBK", False, ilc_op_list) + + # Build a QMF + ILC circuit + ilc_ansatz.build_circuit() + + # Get qubit hamiltonian for energy evaluation + qubit_hamiltonian = ilc_ansatz.qubit_ham + + # Assert energy returned is as expected for given parameters + ilc_ansatz.update_var_params(ilc_var_params) + energy = sim.get_expectation_value(qubit_hamiltonian, ilc_ansatz.circuit) + self.assertAlmostEqual(energy, -1.9608801, delta=1e-6) + + def test_ilc_h4_cation(self): + """ Verify restricted open-shell functionality when using the ILC class for H4 + """ + + # Build the ILC ansatz, which sets the QMF parameters automatically if none are passed + ilc_op_list = [QubitOperator("Y0 Z2 X4 Z6"), QubitOperator("Y1 Y2 Z4 X5 Y6"), QubitOperator("X0 Z2 Z4 Y6"), + QubitOperator("X1 Y2 X4 Z6"), QubitOperator("Y1 Y2 X4 Y5 Z6"), QubitOperator("Y1 Y2 Z4 Z5 Y6"), + QubitOperator("Y0 Z1 Z2 Y5 Y6"), QubitOperator("Y0 Z1 Z2 Y4 Y5 Z6")] + ilc_var_params = [ 0.14017492, -0.10792805, -0.05835484, 0.12468933, 0.07173118, 0.04683807, 0.02852163, -0.03133538] + ilc_ansatz = ILC(mol_H4_cation_sto3g, "BK", False, ilc_op_list) + + # Build a QMF + ILC circuit + ilc_ansatz.build_circuit() + + # Get qubit hamiltonian for energy evaluation + qubit_hamiltonian = ilc_ansatz.qubit_ham + + # Assert energy returned is as expected for given parameters + ilc_ansatz.update_var_params(ilc_var_params) + energy = sim.get_expectation_value(qubit_hamiltonian, ilc_ansatz.circuit) + self.assertAlmostEqual(energy, -1.6379638, delta=1e-6) + + +if __name__ == "__main__": + unittest.main() diff --git a/tangelo/toolboxes/ansatz_generator/tests/test_qcc.py b/tangelo/toolboxes/ansatz_generator/tests/test_qcc.py index bf6b04eb4..0bee22ec4 100644 --- a/tangelo/toolboxes/ansatz_generator/tests/test_qcc.py +++ b/tangelo/toolboxes/ansatz_generator/tests/test_qcc.py @@ -14,9 +14,10 @@ """Unit tests for closed-shell and restricted open-shell qubit coupled cluster (QCC) ansatze. """ +import os import unittest import numpy as np -import os + from openfermion import load_operator from tangelo.linq import Simulator diff --git a/tangelo/toolboxes/ansatz_generator/tests/test_qmf.py b/tangelo/toolboxes/ansatz_generator/tests/test_qmf.py index 11d9a0778..3da5229a2 100644 --- a/tangelo/toolboxes/ansatz_generator/tests/test_qmf.py +++ b/tangelo/toolboxes/ansatz_generator/tests/test_qmf.py @@ -45,7 +45,7 @@ def test_qmf_set_var_params(): eight_pis = np.pi * np.ones((8,)) - qmf_ansatz.set_var_params("full_occ") + qmf_ansatz.set_var_params("full_pi") np.testing.assert_array_almost_equal(qmf_ansatz.var_params, eight_pis, decimal=6) qmf_ansatz.set_var_params(np.array([np.pi] * 8)) From 88f33c38b836d230cfcdf063f20ce3822f8f92e6 Mon Sep 17 00:00:00 2001 From: Marc Coons Date: Wed, 9 Mar 2022 21:12:26 -0500 Subject: [PATCH 13/28] last set of revisions to prepare the ilc_ansatz branch for PR --- .../ansatz_generator/tests/test_ilc.py | 24 +------------------ 1 file changed, 1 insertion(+), 23 deletions(-) diff --git a/tangelo/toolboxes/ansatz_generator/tests/test_ilc.py b/tangelo/toolboxes/ansatz_generator/tests/test_ilc.py index 2fb1d11fd..15fa68103 100644 --- a/tangelo/toolboxes/ansatz_generator/tests/test_ilc.py +++ b/tangelo/toolboxes/ansatz_generator/tests/test_ilc.py @@ -21,7 +21,7 @@ from tangelo.linq import Simulator from tangelo.toolboxes.ansatz_generator.ilc import ILC from tangelo.toolboxes.operators.operators import QubitOperator -from tangelo.molecule_library import mol_H2_sto3g, mol_H4_sto3g, mol_H4_cation_sto3g +from tangelo.molecule_library import mol_H2_sto3g, mol_H4_cation_sto3g sim = Simulator() @@ -79,28 +79,6 @@ def test_ilc_h2(self): energy = sim.get_expectation_value(qubit_hamiltonian, ilc_ansatz.circuit) self.assertAlmostEqual(energy, -1.1372697, delta=1e-6) - def test_ilc_h4(self): - """ Verify restricted open-shell functionality when using the ILC class for H4 """ - - # Build the ILC ansatz, which sets the QMF parameters automatically if none are passed - ilc_op_list = [QubitOperator("Z0 X1 Z3 Y4 Z5"), QubitOperator("X0 X1 Y2 X3 Y4 Y5"), QubitOperator("Z0 X1 Y2 Y4 Y5"), - QubitOperator("Y0 X1 Y2 Y4"), QubitOperator("Y1 X3 Y4 Y5"), QubitOperator("Y0 Y1 X3 Y4 Z5"), - QubitOperator("X0 Y1 X2 Z3 Y4 Y5"), QubitOperator("X0 Y1 Z2 Z3 Y4 Y5"), QubitOperator("Z0 X1 Y2 Y3 Y4 Z5")] - ilc_var_params = [ 0.01902128, -0.01425816, 0.68617146, 0.18603782, -0.20042697, 0.07894877, - -0.09087040, 0.06681858, -0.07056268] - ilc_ansatz = ILC(mol_H4_sto3g, "SCBK", False, ilc_op_list) - - # Build a QMF + ILC circuit - ilc_ansatz.build_circuit() - - # Get qubit hamiltonian for energy evaluation - qubit_hamiltonian = ilc_ansatz.qubit_ham - - # Assert energy returned is as expected for given parameters - ilc_ansatz.update_var_params(ilc_var_params) - energy = sim.get_expectation_value(qubit_hamiltonian, ilc_ansatz.circuit) - self.assertAlmostEqual(energy, -1.9608801, delta=1e-6) - def test_ilc_h4_cation(self): """ Verify restricted open-shell functionality when using the ILC class for H4 + """ From 0676b93c5d6bf330895a03b13bad3e3375b4f726 Mon Sep 17 00:00:00 2001 From: Marc Coons Date: Tue, 22 Mar 2022 16:55:11 -0400 Subject: [PATCH 14/28] first round of revisions of the PR --- .../variational/tests/test_vqe_solver.py | 27 +++---- .../toolboxes/ansatz_generator/_qubit_ilc.py | 80 +++++++++++-------- tangelo/toolboxes/ansatz_generator/ilc.py | 28 +------ tangelo/toolboxes/ansatz_generator/qcc.py | 21 ----- 4 files changed, 57 insertions(+), 99 deletions(-) diff --git a/tangelo/algorithms/variational/tests/test_vqe_solver.py b/tangelo/algorithms/variational/tests/test_vqe_solver.py index 601b6a0f9..70fe2fd7d 100644 --- a/tangelo/algorithms/variational/tests/test_vqe_solver.py +++ b/tangelo/algorithms/variational/tests/test_vqe_solver.py @@ -115,8 +115,7 @@ def test_simulate_qmf_h2(self): parameters, exact simulator. """ - vqe_options = {"molecule": mol_H2_sto3g, "ansatz": BuiltInAnsatze.QMF, "qubit_mapping": "jw", - "verbose": True} + vqe_options = {"molecule": mol_H2_sto3g, "ansatz": BuiltInAnsatze.QMF, "qubit_mapping": "jw"} vqe_solver = VQESolver(vqe_options) vqe_solver.build() @@ -127,8 +126,7 @@ def test_simulate_qcc_h2(self): """Run VQE on H2 molecule, with QCC ansatz, JW qubit mapping, initial parameters, exact simulator. """ - vqe_options = {"molecule": mol_H2_sto3g, "ansatz": BuiltInAnsatze.QCC, "qubit_mapping": "jw", - "verbose": True} + vqe_options = {"molecule": mol_H2_sto3g, "ansatz": BuiltInAnsatze.QCC, "qubit_mapping": "jw"} vqe_solver = VQESolver(vqe_options) vqe_solver.build() @@ -139,8 +137,7 @@ def test_simulate_ilc_h2(self): """Run VQE on H2 molecule, with ILC ansatz, JW qubit mapping, initial parameters, exact simulator. """ - vqe_options = {"molecule": mol_H2_sto3g, "ansatz": BuiltInAnsatze.ILC, "qubit_mapping": "jw", - "verbose": True} + vqe_options = {"molecule": mol_H2_sto3g, "ansatz": BuiltInAnsatze.ILC, "qubit_mapping": "jw"} vqe_solver = VQESolver(vqe_options) vqe_solver.build() @@ -204,8 +201,7 @@ def test_simulate_qmf_h4(self): parameters, exact simulator. """ - vqe_options = {"molecule": mol_H4_sto3g, "ansatz": BuiltInAnsatze.QMF, "qubit_mapping": "jw", - "verbose": True} + vqe_options = {"molecule": mol_H4_sto3g, "ansatz": BuiltInAnsatze.QMF, "qubit_mapping": "jw"} vqe_solver = VQESolver(vqe_options) vqe_solver.build() @@ -216,8 +212,7 @@ def test_simulate_qcc_h4(self): """Run VQE on H4 molecule, with QCC ansatz, JW qubit mapping, initial parameters, exact simulator. """ - vqe_options = {"molecule": mol_H4_sto3g, "ansatz": BuiltInAnsatze.QCC, "qubit_mapping": "jw", - "verbose": True} + vqe_options = {"molecule": mol_H4_sto3g, "ansatz": BuiltInAnsatze.QCC, "qubit_mapping": "jw"} vqe_solver = VQESolver(vqe_options) vqe_solver.build() @@ -228,8 +223,7 @@ def test_simulate_ilc_h4(self): """Run VQE on H4 molecule, with ILC ansatz, JW qubit mapping, initial parameters, exact simulator. """ - vqe_options = {"molecule": mol_H4_sto3g, "ansatz": BuiltInAnsatze.ILC, "qubit_mapping": "jw", - "verbose": True} + vqe_options = {"molecule": mol_H4_sto3g, "ansatz": BuiltInAnsatze.ILC, "qubit_mapping": "jw"} vqe_solver = VQESolver(vqe_options) vqe_solver.build() @@ -251,8 +245,7 @@ def test_simulate_qmf_h4_open(self): parameters, exact simulator. """ - vqe_options = {"molecule": mol_H4_cation_sto3g, "ansatz": BuiltInAnsatze.QMF, "qubit_mapping": "jw", - "verbose": True} + vqe_options = {"molecule": mol_H4_cation_sto3g, "ansatz": BuiltInAnsatze.QMF, "qubit_mapping": "jw"} vqe_solver = VQESolver(vqe_options) vqe_solver.build() @@ -264,8 +257,7 @@ def test_simulate_qcc_h4_open(self): parameters, exact simulator. """ - vqe_options = {"molecule": mol_H4_cation_sto3g, "ansatz": BuiltInAnsatze.QCC, "qubit_mapping": "jw", - "verbose": True} + vqe_options = {"molecule": mol_H4_cation_sto3g, "ansatz": BuiltInAnsatze.QCC, "qubit_mapping": "jw"} vqe_solver = VQESolver(vqe_options) vqe_solver.build() @@ -277,8 +269,7 @@ def test_simulate_ilc_h4_open(self): parameters, exact simulator. """ - vqe_options = {"molecule": mol_H4_cation_sto3g, "ansatz": BuiltInAnsatze.ILC, "qubit_mapping": "jw", - "verbose": True} + vqe_options = {"molecule": mol_H4_cation_sto3g, "ansatz": BuiltInAnsatze.ILC, "qubit_mapping": "jw"} vqe_solver = VQESolver(vqe_options) vqe_solver.build() diff --git a/tangelo/toolboxes/ansatz_generator/_qubit_ilc.py b/tangelo/toolboxes/ansatz_generator/_qubit_ilc.py index b18d1c321..df45e2552 100644 --- a/tangelo/toolboxes/ansatz_generator/_qubit_ilc.py +++ b/tangelo/toolboxes/ansatz_generator/_qubit_ilc.py @@ -51,32 +51,34 @@ def construct_acs(dis, max_ilc_gens, n_qubits): gen_idxs, ilc_gens = [idx for idx in range(max_ilc_gens) if idx not in bad_sln_idxs], [] n_gens = len(gen_idxs) ng2, ngnq = n_gens * (n_gens + 1) // 2, n_gens * n_qubits - # cons_mat --> A and z_vec --> z in Appendix A, Refs. 1 & 2. - cons_mat, z_vec = np.zeros((ng2, ngnq + 1)), np.zeros(ngnq) + + # a_mat --> A and z_vec --> z in Appendix A, Refs. 1 & 2. + a_mat, z_vec, one_vec = np.zeros((ng2, ngnq)), np.zeros(ngnq), np.ones((ng2, 1)) for idx, gen_idx in enumerate(gen_idxs): - gen = dis[gen_idx][-1] - for term, _ in gen.terms.items(): + gen = dis[gen_idx][0] + for term in gen.terms: for paulis in term: p_idx, pauli = paulis if 'X' in pauli or 'Y' in pauli: z_vec[idx * n_qubits + p_idx] = 1. - # Form the rectangular matrix-vector product A * z; last column is the soln vec (Appendix A, Refs. 1 & 2). + + # Form the rectangular matrix-vector product A * z (Appendix A, Refs. 1 & 2). r_idx = 0 for i in range(n_gens): - cons_mat[r_idx, i * n_qubits:(i+1) * n_qubits] = z_vec[i * n_qubits:(i+1) * n_qubits] - cons_mat[r_idx, ngnq] = 1 + a_mat[r_idx, i * n_qubits:(i+1) * n_qubits] = z_vec[i * n_qubits:(i+1) * n_qubits] r_idx += 1 for j in range(i+1, n_gens): - cons_mat[r_idx, i * n_qubits:(i+1) * n_qubits] = z_vec[j * n_qubits:(j+1) * n_qubits] - cons_mat[r_idx, j * n_qubits:(j+1) * n_qubits] = z_vec[i * n_qubits:(i+1) * n_qubits] - cons_mat[r_idx, ngnq] = 1 + a_mat[r_idx, i * n_qubits:(i+1) * n_qubits] = z_vec[j * n_qubits:(j+1) * n_qubits] + a_mat[r_idx, j * n_qubits:(j+1) * n_qubits] = z_vec[i * n_qubits:(i+1) * n_qubits] r_idx += 1 + # Solve A * z = 1 - z_sln = gauss_elim_over_gf2(cons_mat, ngnq) + z_sln = gauss_elim_over_gf2(a_mat, b_vec=one_vec) + # Check solution: odd # of Y ops, at least two flip indices, and mutually anti-commutes good_sln = True for i in range(n_gens): - n_flip, n_y, gen_idx, gen_list = 0, 0, gen_idxs[i], [] + n_flip, n_y, gen_idx, gen_tup = 0, 0, gen_idxs[i], tuple() for j in range(n_qubits): gen, idx = None, i * n_qubits + j if z_vec[idx] == 1.: @@ -90,13 +92,13 @@ def construct_acs(dis, max_ilc_gens, n_qubits): if z_sln[idx] == 1.: gen = (j, 'Z') if gen: - gen_list.append(gen) + gen_tup += (gen, ) if n_flip < 2 or n_y % 2 == 0: if good_sln and gen_idx not in bad_sln_idxs: bad_sln_idxs.append(gen_idx) good_sln = False elif good_sln: - gen_i = QubitOperator(tuple(gen_list), 1.) + gen_i = QubitOperator(gen_tup, 1.) for gen_j in ilc_gens: if gen_i * gen_j != -1. * gen_j * gen_i: if good_sln and gen_idx not in bad_sln_idxs: @@ -107,46 +109,52 @@ def construct_acs(dis, max_ilc_gens, n_qubits): return ilc_gens -def gauss_elim_over_gf2(cons_mat, z_dim): +def gauss_elim_over_gf2(a_mat, b_vec=None): """Driver function that performs Gaussian elimination to solve A * z = b over the binary field where b is a vector of ones. This routine was adapted - based on Ref. 3. + based on Ref. 3. All elements of a_mat and b_vec are assumed to be either + the integer 0 or 1. Args: - cons_mat (numpy array of int): the rectangular constraint matrix A (see Refs. 1 & 2). - The last column initially holds the vector b. - z_dim (int): the dimensionality of the constraint matrix A: number of ILC generators - times the number of qubits. + a_mat (numpy array of int): rectangular matrix of dimension n x m that + holds the action of A * z, where z is a column vector of dimension m x 1. + No default. + b_vec (numpy array of int): column vector of dimension n x 1 holding the + initial solution of A * z. Default, np.zeros(n). Returns: list of float: the solution vector """ # Gaussian elimination over GF(2) - n_rows, n_cols = np.shape(cons_mat) - cons_mat, z_vals, z_sln, piv_idx = np.array(cons_mat), [], [-1] * z_dim, 0 + n_rows, n_cols = np.shape(a_mat) + if not b_vec: + b_vec = np.zeros((n_rows, 1)) + a_mat = np.append(a_mat, b_vec, 1) + n_cols += 1 + z_vals, z_sln, piv_idx = [], [-1] * n_cols, 0 for i in range(n_cols): - cons_mat_max = 0. + a_mat_max = 0. max_idx = piv_idx for j in range(piv_idx, n_rows): - if cons_mat[j, i] > cons_mat_max: + if a_mat[j, i] > a_mat_max: max_idx = j - cons_mat_max = cons_mat[j, i] - elif j == n_rows-1 and cons_mat_max == 0.: + a_mat_max = a_mat[j, i] + elif j == n_rows-1 and a_mat_max == 0.: piv_idx = max_idx - cons_mat_max = -1. - if cons_mat_max > 0.: + a_mat_max = -1. + if a_mat_max > 0.: if max_idx > piv_idx: - cons_mat[[piv_idx, max_idx]] = cons_mat[[max_idx, piv_idx]] + a_mat[[piv_idx, max_idx]] = a_mat[[max_idx, piv_idx]] for j in range(piv_idx + 1, n_rows): - if cons_mat[j, i] == 1.: - cons_mat[j, i:n_cols] = np.fmod(cons_mat[j, i:n_cols] + cons_mat[piv_idx, i:n_cols], 2) + if a_mat[j, i] == 1.: + a_mat[j, i:n_cols] = np.fmod(a_mat[j, i:n_cols] + a_mat[piv_idx, i:n_cols], 2) piv_idx += 1 - b_vec = cons_mat[0:n_rows, n_cols-1].tolist() + b_vec = a_mat[0:n_rows, n_cols-1].tolist() for i in range(n_rows - 1, -1, -1): col_idx, z_free = -1., [] for j in range(n_cols-1): - if cons_mat[i, j] == 1.: + if a_mat[i, j] == 1.: if col_idx == -1: col_idx = j else: @@ -155,10 +163,12 @@ def gauss_elim_over_gf2(cons_mat, z_dim): z_vals.append([col_idx, z_free, b_vec[i]]) for z_val in (z_vals): b_val = z_val[2] + # Here we have to make a choice for the free solns as either 0 or 1; + # 0 leads to an I op, while 1 leads to a Z op -- seems 0 is perhaps the better choice. for z_free in (z_val[1]): if z_sln[z_free] == -1: - z_sln[z_free] = 1. - b_val = (b_val + z_sln[z_free]) % 2 + z_sln[z_free] = 0. + b_val = np.fmod(b_val + z_sln[z_free], 2) z_sln[z_val[0]] = b_val return z_sln diff --git a/tangelo/toolboxes/ansatz_generator/ilc.py b/tangelo/toolboxes/ansatz_generator/ilc.py index 5d29ae47d..0a0e1bffc 100755 --- a/tangelo/toolboxes/ansatz_generator/ilc.py +++ b/tangelo/toolboxes/ansatz_generator/ilc.py @@ -243,33 +243,11 @@ def update_var_params(self, var_params): else self.ilc_circuit def _get_ilc_op(self): - """Returns the ILC operator by selecting one generator from n_var_params DIS groups. - - Args: - rebuild_dis (bool): Rebuilds DIS and sets ilc_op_list to None. - rebuild_acs (bool): Rebuilds DIS & ACS and sets ilc_op_list to None. - dis (list of list): DIS of QCC generators. - acs (list of list): ACS of selected QCC generators from the DIS. - ilc_op_list (list of QubitOperator): ACS generator list for the ILC ansatz. - var_params (numpy array of float): ILC variational parameter set. - n_var_params (int): Number of ILC variational parameters. - qmf_var_params (numpy array of float): QMF variational parameter set. - n_spinorbitals (int): Number of spin-orbitals in the molecular system. - n_electrons (int): Number of electrons in the molecular system. - mapping (str) : One of the supported mapping identifiers. - up_then_down (bool): Change basis ordering putting all spin-up orbitals first, - followed by all spin-down. - spin (int): 2*S = n_alpha - n_beta. - qubit_ham (QubitOperator): A qubit Hamiltonian. - deilc_dtau_thresh (float): Threshold for |dEILC/dtau| so that a candidate group is added - to the DIS if |dEILC/dtau| >= deilc_dtau_thresh for a generator. - max_ilc_gens (int or None): Maximum number of generators allowed in the ansatz. If None, - one generator from each DIS group is selected. If int, min(|DIS|, max_ilc_gens) - generators are selected in order of decreasing |dEILC/dtau| values. + """Returns the ILC operators ordered according to the argument of + Eq. C1, Appendix C, Ref. 1. Returns: - list of QubitOperator: the list of ILC qubit operators ordered according to the - argument of Eq. C1, Appendix C, Ref. 1. + list of QubitOperator: the list of ILC qubit operators """ # Rebuild DIS & ACS in case qubit_ham changed or they and qubit_op_list don't exist diff --git a/tangelo/toolboxes/ansatz_generator/qcc.py b/tangelo/toolboxes/ansatz_generator/qcc.py index 31c78253c..3b7e65174 100755 --- a/tangelo/toolboxes/ansatz_generator/qcc.py +++ b/tangelo/toolboxes/ansatz_generator/qcc.py @@ -257,27 +257,6 @@ def _get_qcc_qubit_op(self): The exponentiated terms of the QCC operator, U = PROD_k exp(-0.5j * tau_k * P_k), are used to build a QCC circuit. - Args: - rebuild_dis (bool): Rebuilds dis and sets qcc_op_list to None. This is critical - to do when qubit_ham or qmf_var_params have changed. - dis (list of list): DIS of QCC generators. - qcc_op_list (list of QubitOperator): Generator list for the QCC ansatz. - var_params (numpy array of float): QCC variational parameter set. - n_var_params (int): Number of QCC variational parameters. - qmf_var_params (numpy array of float): QMF variational parameter set. - n_spinorbitals (int): Number of spin-orbitals in the molecular system. - n_electrons (int): Number of electrons in the molecular system. - mapping (str) : One of the supported qubit mapping identifiers. - up_then_down (bool): Change basis ordering putting all spin-up orbitals first, - followed by all spin-down. - spin (int): 2*S = n_alpha - n_beta. - qubit_ham (QubitOperator): A qubit Hamiltonian. - deqcc_dtau_thresh (float): Threshold for |dEQCC/dtau| so that a candidate group is added - to the DIS if |dEQCC/dtau| >= deqcc_dtau_thresh for a generator. - max_qcc_gens (int or None): Maximum number of generators allowed in the ansatz. If None, - one generator from each DIS group is selected. If int, min(|DIS|, max_qcc_gens) - generators are selected in order of decreasing |dEQCC/dtau| values. - Returns: QubitOperator: QCC ansatz qubit operator. """ From 18d7a978f7c2318751dd39e79a9dc587f9aa88c1 Mon Sep 17 00:00:00 2001 From: Marc Coons Date: Tue, 22 Mar 2022 17:14:20 -0400 Subject: [PATCH 15/28] fix to gauss_elim_over_gf2 --- tangelo/toolboxes/ansatz_generator/_qubit_ilc.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tangelo/toolboxes/ansatz_generator/_qubit_ilc.py b/tangelo/toolboxes/ansatz_generator/_qubit_ilc.py index df45e2552..b96af96e8 100644 --- a/tangelo/toolboxes/ansatz_generator/_qubit_ilc.py +++ b/tangelo/toolboxes/ansatz_generator/_qubit_ilc.py @@ -128,9 +128,9 @@ def gauss_elim_over_gf2(a_mat, b_vec=None): # Gaussian elimination over GF(2) n_rows, n_cols = np.shape(a_mat) - if not b_vec: + if not isinstance(b_vec, np.ndarray): b_vec = np.zeros((n_rows, 1)) - a_mat = np.append(a_mat, b_vec, 1) + a_mat = np.append(a_mat, b_vec, axis=1) n_cols += 1 z_vals, z_sln, piv_idx = [], [-1] * n_cols, 0 for i in range(n_cols): From 81d10a249c30ab054e1523c5231ab0afb3eb8f5c Mon Sep 17 00:00:00 2001 From: Marc Coons Date: Tue, 22 Mar 2022 17:44:57 -0400 Subject: [PATCH 16/28] second batch of PR revisions --- tangelo/algorithms/variational/vqe_solver.py | 2 +- tangelo/toolboxes/ansatz_generator/ilc.py | 26 ++++++------------ tangelo/toolboxes/ansatz_generator/qcc.py | 22 +++++---------- tangelo/toolboxes/ansatz_generator/qmf.py | 29 +++++++------------- 4 files changed, 27 insertions(+), 52 deletions(-) diff --git a/tangelo/algorithms/variational/vqe_solver.py b/tangelo/algorithms/variational/vqe_solver.py index 28d2df9a0..57d4c1d22 100644 --- a/tangelo/algorithms/variational/vqe_solver.py +++ b/tangelo/algorithms/variational/vqe_solver.py @@ -114,7 +114,7 @@ def __init__(self, opt_dict): # The QCC & ILC ansatze require up_then_down=True when mapping="jw" if isinstance(self.ansatz, BuiltInAnsatze): if self.ansatz in (BuiltInAnsatze.QCC, BuiltInAnsatze.ILC) and self.qubit_mapping.lower() == "jw" and not self.up_then_down: - warnings.warn("Efficient generator screening for QCC-based ansatze require spin-orbital ordering to be " + warnings.warn("Efficient generator screening for QCC-based ansatze requires spin-orbital ordering to be " "all spin-up first followed by all spin-down for the JW mapping.", RuntimeWarning) self.up_then_down = True diff --git a/tangelo/toolboxes/ansatz_generator/ilc.py b/tangelo/toolboxes/ansatz_generator/ilc.py index 0a0e1bffc..ebf58956e 100755 --- a/tangelo/toolboxes/ansatz_generator/ilc.py +++ b/tangelo/toolboxes/ansatz_generator/ilc.py @@ -68,27 +68,19 @@ class ILC(Ansatz): one generator from each DIS group is selected. If int, then min(|DIS|, max_ilc_gens) generators are selected in order of decreasing |dEILC/dtau|. Default, None. n_trotter (int): Number of Trotterization steps for the ILC ansatz. Default, 1. - scfdata (tuple): tuple containing an instance of OpenFermion MolecularData and a - FermionOperator corresponding to the fermionic Hamiltonian. """ def __init__(self, molecule, mapping="JW", up_then_down=False, ilc_op_list=None, qmf_circuit=None, qmf_var_params=None, qubit_ham=None, ilc_tau_guess=1.e-2, - deilc_dtau_thresh=1.e-3, max_ilc_gens=None, n_trotter=1, scfdata=None): - - if molecule: - self.molecule = molecule - self.n_spinorbitals = self.molecule.n_active_sos - if self.n_spinorbitals % 2 != 0: - raise ValueError("The total number of spin-orbitals should be even.") - - self.spin = molecule.spin - self.fermi_ham = self.molecule.fermionic_hamiltonian - elif scfdata: - self.molecule = scfdata[0] - self.n_spinorbitals = 2 * self.molecule.n_orbitals - self.spin = self.molecule.multiplicity - 1 - self.fermi_ham = scfdata[1] + deilc_dtau_thresh=1.e-3, max_ilc_gens=None, n_trotter=1): + + self.molecule = molecule + self.n_spinorbitals = self.molecule.n_active_sos + if self.n_spinorbitals % 2 != 0: + raise ValueError("The total number of spin-orbitals should be even.") + + self.spin = molecule.spin + self.fermi_ham = self.molecule.fermionic_hamiltonian self.n_electrons = self.molecule.n_electrons self.mapping = mapping self.n_qubits = get_qubit_number(self.mapping, self.n_spinorbitals) diff --git a/tangelo/toolboxes/ansatz_generator/qcc.py b/tangelo/toolboxes/ansatz_generator/qcc.py index 3b7e65174..4437135a3 100755 --- a/tangelo/toolboxes/ansatz_generator/qcc.py +++ b/tangelo/toolboxes/ansatz_generator/qcc.py @@ -73,27 +73,19 @@ class QCC(Ansatz): max_qcc_gens (int or None): Maximum number of generators allowed in the ansatz. If None, one generator from each DIS group is selected. If int, then min(|DIS|, max_qcc_gens) generators are selected in order of decreasing |dEQCC/dtau|. Default, None. - scfdata (tuple): tuple containing an instance of OpenFermion MolecularData and a - FermionOperator corresponding to the fermionic Hamiltonian. """ def __init__(self, molecule, mapping="JW", up_then_down=False, qcc_op_list=None, qmf_circuit=None, qmf_var_params=None, qubit_ham=None, qcc_tau_guess=1.e-2, - deqcc_dtau_thresh=1.e-3, max_qcc_gens=None, scfdata=None): + deqcc_dtau_thresh=1.e-3, max_qcc_gens=None): - if molecule: - self.molecule = molecule - self.n_spinorbitals = self.molecule.n_active_sos - if self.n_spinorbitals % 2 != 0: - raise ValueError("The total number of spin-orbitals should be even.") + self.molecule = molecule + self.n_spinorbitals = self.molecule.n_active_sos + if self.n_spinorbitals % 2 != 0: + raise ValueError("The total number of spin-orbitals should be even.") - self.spin = molecule.spin - self.fermi_ham = self.molecule.fermionic_hamiltonian - elif scfdata: - self.molecule = scfdata[0] - self.n_spinorbitals = 2 * self.molecule.n_orbitals - self.spin = self.molecule.multiplicity - 1 - self.fermi_ham = scfdata[1] + self.spin = molecule.spin + self.fermi_ham = self.molecule.fermionic_hamiltonian self.n_electrons = self.molecule.n_electrons self.mapping = mapping self.n_qubits = get_qubit_number(self.mapping, self.n_spinorbitals) diff --git a/tangelo/toolboxes/ansatz_generator/qmf.py b/tangelo/toolboxes/ansatz_generator/qmf.py index efaf467c7..7accc3e5e 100755 --- a/tangelo/toolboxes/ansatz_generator/qmf.py +++ b/tangelo/toolboxes/ansatz_generator/qmf.py @@ -67,27 +67,18 @@ class QMF(Ansatz): = n_electrons, = spin_z * (spin_z + 1), and = spin_z, where spin_z = spin // 2. Key, value pairs are case sensitive and mu > 0. Default, {"init_params": "hf_state"}. - scfdata (tuple): tuple containing an instance of OpenFermion MolecularData and a - FermionOperator corresponding to the fermionic Hamiltonian. """ - def __init__(self, molecule=None, mapping="JW", up_then_down=False, init_qmf=None, scfdata=None): - - if molecule: - self.molecule = molecule - self.n_spinorbitals = self.molecule.n_active_sos - if self.n_spinorbitals % 2 != 0: - raise ValueError("The total number of spin-orbitals should be even.") - - self.n_orbitals = self.n_spinorbitals // 2 - self.spin = molecule.spin - self.fermi_ham = self.molecule.fermionic_hamiltonian - elif scfdata: - self.molecule = scfdata[0] - self.n_orbitals = self.molecule.n_orbitals - self.n_spinorbitals = 2 * self.n_orbitals - self.spin = self.molecule.multiplicity - 1 - self.fermi_ham = scfdata[1] + def __init__(self, molecule=None, mapping="JW", up_then_down=False, init_qmf=None): + + self.molecule = molecule + self.n_spinorbitals = self.molecule.n_active_sos + if self.n_spinorbitals % 2 != 0: + raise ValueError("The total number of spin-orbitals should be even.") + + self.n_orbitals = self.n_spinorbitals // 2 + self.spin = molecule.spin + self.fermi_ham = self.molecule.fermionic_hamiltonian self.n_electrons = self.molecule.n_electrons self.mapping = mapping From f1f4e7e02e74f9eb7782e3ef8e9623e6ba02dbd6 Mon Sep 17 00:00:00 2001 From: Marc Coons Date: Wed, 23 Mar 2022 11:36:19 -0400 Subject: [PATCH 17/28] improvements to qcc and ilc; added a test for gaussian elim over GF(2) --- .../toolboxes/ansatz_generator/_qubit_cc.py | 8 +-- .../toolboxes/ansatz_generator/_qubit_ilc.py | 64 +++++++++---------- .../ansatz_generator/tests/test_ilc.py | 19 ++++++ 3 files changed, 55 insertions(+), 36 deletions(-) diff --git a/tangelo/toolboxes/ansatz_generator/_qubit_cc.py b/tangelo/toolboxes/ansatz_generator/_qubit_cc.py index eda4b751d..60782b3f4 100644 --- a/tangelo/toolboxes/ansatz_generator/_qubit_cc.py +++ b/tangelo/toolboxes/ansatz_generator/_qubit_cc.py @@ -117,18 +117,18 @@ def get_idxs_deriv(qham_term, *qham_qmf_data): """ coef, pure_params = qham_qmf_data - idxs, gen_list, idxs_deriv = "", [], None + idxs, gen_tup, idxs_deriv = "", tuple(), None for pauli_factor in qham_term: # The indices of X and Y operators are flip indices idx, pauli_op = pauli_factor if "X" in pauli_op or "Y" in pauli_op: gen = (idx, "Y") if idxs == "" else (idx, "X") idxs = idxs + f" {idx}" if idxs != "" else f"{idx}" - gen_list.append(gen) + gen_tup += (gen, ) # Generators must have at least two flip indices - if len(gen_list) > 1: + if len(gen_tup) > 1: qham_gen_comm = QubitOperator(qham_term, -1j * coef) - qham_gen_comm *= QubitOperator(tuple(gen_list), 1.) + qham_gen_comm *= QubitOperator(gen_tup, 1.) deriv = get_op_expval(qham_gen_comm, pure_params).real idxs_deriv = (idxs, deriv) return idxs_deriv diff --git a/tangelo/toolboxes/ansatz_generator/_qubit_ilc.py b/tangelo/toolboxes/ansatz_generator/_qubit_ilc.py index b96af96e8..586de9bd3 100644 --- a/tangelo/toolboxes/ansatz_generator/_qubit_ilc.py +++ b/tangelo/toolboxes/ansatz_generator/_qubit_ilc.py @@ -39,7 +39,7 @@ def construct_acs(dis, max_ilc_gens, n_qubits): Args: dis (list of list): DIS of QCC generators. - max_ilc_gens (int): Maximum number of generators allowed in the ansatz. + max_ilc_gens (int): maximum number of ILC generators allowed in the ansatz. n_qubits (int): number of qubits Returns: @@ -63,16 +63,16 @@ def construct_acs(dis, max_ilc_gens, n_qubits): z_vec[idx * n_qubits + p_idx] = 1. # Form the rectangular matrix-vector product A * z (Appendix A, Refs. 1 & 2). - r_idx = 0 + rowdx = 0 for i in range(n_gens): - a_mat[r_idx, i * n_qubits:(i+1) * n_qubits] = z_vec[i * n_qubits:(i+1) * n_qubits] - r_idx += 1 - for j in range(i+1, n_gens): - a_mat[r_idx, i * n_qubits:(i+1) * n_qubits] = z_vec[j * n_qubits:(j+1) * n_qubits] - a_mat[r_idx, j * n_qubits:(j+1) * n_qubits] = z_vec[i * n_qubits:(i+1) * n_qubits] - r_idx += 1 - - # Solve A * z = 1 + a_mat[rowdx, i * n_qubits:(i+1) * n_qubits] = z_vec[i * n_qubits:(i+1) * n_qubits] + rowdx += 1 + for j in range(i + 1, n_gens): + a_mat[rowdx, i * n_qubits:(i+1) * n_qubits] = z_vec[j * n_qubits:(j+1) * n_qubits] + a_mat[rowdx, j * n_qubits:(j+1) * n_qubits] = z_vec[i * n_qubits:(i+1) * n_qubits] + rowdx += 1 + + # Solve A * z = b --> here b = 1 z_sln = gauss_elim_over_gf2(a_mat, b_vec=one_vec) # Check solution: odd # of Y ops, at least two flip indices, and mutually anti-commutes @@ -97,45 +97,42 @@ def construct_acs(dis, max_ilc_gens, n_qubits): if good_sln and gen_idx not in bad_sln_idxs: bad_sln_idxs.append(gen_idx) good_sln = False - elif good_sln: - gen_i = QubitOperator(gen_tup, 1.) - for gen_j in ilc_gens: - if gen_i * gen_j != -1. * gen_j * gen_i: - if good_sln and gen_idx not in bad_sln_idxs: - bad_sln_idxs.append(gen_idx) - good_sln = False - if good_sln: - ilc_gens.append(gen_i) + gen_i = QubitOperator(gen_tup, 1.) + for gen_j in ilc_gens: + if gen_i * gen_j != -1. * gen_j * gen_i: + if good_sln and gen_idx not in bad_sln_idxs: + bad_sln_idxs.append(gen_idx) + good_sln = False + if good_sln: + ilc_gens.append(gen_i) return ilc_gens def gauss_elim_over_gf2(a_mat, b_vec=None): """Driver function that performs Gaussian elimination to solve A * z = b over the binary field where b is a vector of ones. This routine was adapted - based on Ref. 3. All elements of a_mat and b_vec are assumed to be either - the integer 0 or 1. + based on Ref. 3. All elements of a_mat and b_vec are assumed to be the + integers 0 or 1. Args: a_mat (numpy array of int): rectangular matrix of dimension n x m that holds the action of A * z, where z is a column vector of dimension m x 1. No default. b_vec (numpy array of int): column vector of dimension n x 1 holding the - initial solution of A * z. Default, np.zeros(n). + initial solution of A * z. Default, np.zeros((n, 1)). Returns: - list of float: the solution vector + numpy array of float: the solution vector of dimension (n, ) """ - # Gaussian elimination over GF(2) n_rows, n_cols = np.shape(a_mat) if not isinstance(b_vec, np.ndarray): b_vec = np.zeros((n_rows, 1)) a_mat = np.append(a_mat, b_vec, axis=1) - n_cols += 1 z_vals, z_sln, piv_idx = [], [-1] * n_cols, 0 + n_cols += 1 for i in range(n_cols): - a_mat_max = 0. - max_idx = piv_idx + a_mat_max, max_idx = 0., piv_idx for j in range(piv_idx, n_rows): if a_mat[j, i] > a_mat_max: max_idx = j @@ -163,14 +160,14 @@ def gauss_elim_over_gf2(a_mat, b_vec=None): z_vals.append([col_idx, z_free, b_vec[i]]) for z_val in (z_vals): b_val = z_val[2] - # Here we have to make a choice for the free solns as either 0 or 1; - # 0 leads to an I op, while 1 leads to a Z op -- seems 0 is perhaps the better choice. + # Make a choice for the free solns as either 0 or 1; + # 0 leads to an I op, while 1 leads to a Z op -- 0 seems to be the slightly better choice. for z_free in (z_val[1]): if z_sln[z_free] == -1: z_sln[z_free] = 0. b_val = np.fmod(b_val + z_sln[z_free], 2) z_sln[z_val[0]] = b_val - return z_sln + return np.array(z_sln) def get_ilc_params_by_diag(qubit_ham, ilc_gens, qmf_var_params): @@ -186,7 +183,7 @@ def get_ilc_params_by_diag(qubit_ham, ilc_gens, qmf_var_params): list of float: the ILC parameters corresponding to the ACS of ILC generators """ - # Temporarily add the identity operator to the ACS + # Add the identity operator to the local copy of the ACS ilc_gens.insert(0, QubitOperator.identity()) n_var_params = len(ilc_gens) qubit_ham_mat = np.zeros((n_var_params, n_var_params), dtype=complex) @@ -196,13 +193,17 @@ def get_ilc_params_by_diag(qubit_ham, ilc_gens, qmf_var_params): for i in range(n_var_params): # H T_i|QMF> = H |psi_i> h_psi_i = qubit_ham * ilc_gens[i] + # = = H_ii qubit_ham_mat[i, i] = get_op_expval(ilc_gens[i] * h_psi_i, qmf_var_params) + # = = 1 qubit_overlap_mat[i, i] = 1. + 0j + for j in range(i + 1, n_var_params): # = = H_ji qubit_ham_mat[j, i] = get_op_expval(ilc_gens[j] * h_psi_i, qmf_var_params) + # = --> exactly zero only for pure QMF states qubit_overlap_mat[j, i] = get_op_expval(ilc_gens[j] * ilc_gens[i], qmf_var_params) if i == 0: @@ -225,5 +226,4 @@ def get_ilc_params_by_diag(qubit_ham, ilc_gens, qmf_var_params): denom_sum += pow(gs_coefs[i].real, 2.) + pow(gs_coefs[i].imag, 2.) beta = np.arcsin(gs_coefs[i] / np.sqrt(denom_sum)) ilc_var_params.append(beta.real) - del ilc_gens[0] return ilc_var_params diff --git a/tangelo/toolboxes/ansatz_generator/tests/test_ilc.py b/tangelo/toolboxes/ansatz_generator/tests/test_ilc.py index 15fa68103..c8dd08867 100644 --- a/tangelo/toolboxes/ansatz_generator/tests/test_ilc.py +++ b/tangelo/toolboxes/ansatz_generator/tests/test_ilc.py @@ -20,6 +20,7 @@ from tangelo.linq import Simulator from tangelo.toolboxes.ansatz_generator.ilc import ILC +from tangelo.toolboxes.ansatz_generator._qubit_ilc import gauss_elim_over_gf2 from tangelo.toolboxes.operators.operators import QubitOperator from tangelo.molecule_library import mol_H2_sto3g, mol_H4_cation_sto3g @@ -60,6 +61,24 @@ def test_ilc_incorrect_number_var_params(self): self.assertRaises(ValueError, ilc_ansatz.set_var_params, np.array([1.] * 2)) + @staticmethod + def test_gauss_elim_over_gf2(): + """ Verify behavior of the Gaussian elimination over the binary field function. """ + + # a_matrix stores the action of A * z over GF(2); dimension is n x m + a_matrix = np.array([[1, 0, 1, 1], [1, 1, 0, 1], [1, 1, 1, 0], [0, 0, 1, 0]]) + + # b_vec stores the solution vector for the equation A * z = b_vec; dimension is n x 1 + b_vec = np.array([1, 0, 1, 0]).reshape((4, 1)) + + # z_ref stores the serves as the reference for the output of gauss_elim_over_gf2 + z_ref = np.array([0, 1, 0, 1]) + + # solve A * z = b and compare to reference solution + z_sln = gauss_elim_over_gf2(a_matrix, b_vec) + + np.testing.assert_array_almost_equal(z_sln, z_ref, decimal=6) + def test_ilc_h2(self): """ Verify closed-shell functionality when using the ILC class separately for H2 """ From df8eee58e148536627a7fa77444bf1c1634571e8 Mon Sep 17 00:00:00 2001 From: Marc Coons Date: Wed, 23 Mar 2022 13:17:24 -0400 Subject: [PATCH 18/28] minor change to a file to prompt check --- tangelo/toolboxes/ansatz_generator/ilc.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tangelo/toolboxes/ansatz_generator/ilc.py b/tangelo/toolboxes/ansatz_generator/ilc.py index ebf58956e..6aa4ef72f 100755 --- a/tangelo/toolboxes/ansatz_generator/ilc.py +++ b/tangelo/toolboxes/ansatz_generator/ilc.py @@ -67,7 +67,7 @@ class ILC(Ansatz): max_ilc_gens (int or None): Maximum number of generators allowed in the ansatz. If None, one generator from each DIS group is selected. If int, then min(|DIS|, max_ilc_gens) generators are selected in order of decreasing |dEILC/dtau|. Default, None. - n_trotter (int): Number of Trotterization steps for the ILC ansatz. Default, 1. + n_trotter (int): Number of Trotterization steps for the ILC ansatz circuit. Default, 1. """ def __init__(self, molecule, mapping="JW", up_then_down=False, ilc_op_list=None, From 52807404d64963f24fe2948d7604904692279662 Mon Sep 17 00:00:00 2001 From: Marc Coons Date: Wed, 23 Mar 2022 13:22:02 -0400 Subject: [PATCH 19/28] minor change to a file to prompt check --- tangelo/toolboxes/ansatz_generator/ilc.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tangelo/toolboxes/ansatz_generator/ilc.py b/tangelo/toolboxes/ansatz_generator/ilc.py index 6aa4ef72f..a5ad5d972 100755 --- a/tangelo/toolboxes/ansatz_generator/ilc.py +++ b/tangelo/toolboxes/ansatz_generator/ilc.py @@ -67,7 +67,8 @@ class ILC(Ansatz): max_ilc_gens (int or None): Maximum number of generators allowed in the ansatz. If None, one generator from each DIS group is selected. If int, then min(|DIS|, max_ilc_gens) generators are selected in order of decreasing |dEILC/dtau|. Default, None. - n_trotter (int): Number of Trotterization steps for the ILC ansatz circuit. Default, 1. + n_trotter (int): Number of Trotterization steps used to create the ILC ansatz circuit. + Default, 1. """ def __init__(self, molecule, mapping="JW", up_then_down=False, ilc_op_list=None, From 25d83ab523046f6c1754a9e56749434f8f5871fd Mon Sep 17 00:00:00 2001 From: Marc Coons Date: Tue, 19 Apr 2022 11:24:47 -0400 Subject: [PATCH 20/28] added comments to Gaussian elimination function; added a warning when a solution is not found; added additional tests to test_ilc.py for rectangular matrices and a case where there is linear dependence present in the rectangular matrix. --- .../toolboxes/ansatz_generator/_qubit_ilc.py | 26 ++++++++++--- .../ansatz_generator/tests/test_ilc.py | 38 ++++++++++++++++++- 2 files changed, 57 insertions(+), 7 deletions(-) diff --git a/tangelo/toolboxes/ansatz_generator/_qubit_ilc.py b/tangelo/toolboxes/ansatz_generator/_qubit_ilc.py index 586de9bd3..428e2a9db 100644 --- a/tangelo/toolboxes/ansatz_generator/_qubit_ilc.py +++ b/tangelo/toolboxes/ansatz_generator/_qubit_ilc.py @@ -26,6 +26,7 @@ J. Parallel Distrib. Comput., 1991, 13, 118–122. """ +import warnings import scipy import numpy as np @@ -110,9 +111,9 @@ def construct_acs(dis, max_ilc_gens, n_qubits): def gauss_elim_over_gf2(a_mat, b_vec=None): """Driver function that performs Gaussian elimination to solve A * z = b - over the binary field where b is a vector of ones. This routine was adapted - based on Ref. 3. All elements of a_mat and b_vec are assumed to be the - integers 0 or 1. + over the binary field where b is the known solution vector. This routine + was adapted based on Ref. 3. All elements of a_mat and b_vec are assumed + to be the integers 0 or 1. Args: a_mat (numpy array of int): rectangular matrix of dimension n x m that @@ -122,24 +123,32 @@ def gauss_elim_over_gf2(a_mat, b_vec=None): initial solution of A * z. Default, np.zeros((n, 1)). Returns: - numpy array of float: the solution vector of dimension (n, ) + numpy array of float: solution for the z vector of dimension (n, ) """ n_rows, n_cols = np.shape(a_mat) + z_vals, z_sln, piv_idx = [], [-1] * n_cols, 0 + + # check that b_vec was properly supplied; ortherwise initialize as a vector of zeros if not isinstance(b_vec, np.ndarray): b_vec = np.zeros((n_rows, 1)) a_mat = np.append(a_mat, b_vec, axis=1) + z_vals, z_sln, piv_idx = [], [-1] * n_cols, 0 n_cols += 1 for i in range(n_cols): a_mat_max, max_idx = 0., piv_idx + # locate the pivot index by searching each row for a non-zero value. for j in range(piv_idx, n_rows): + # if a pivot index is found, set the value to the col index for the row in which it was found if a_mat[j, i] > a_mat_max: max_idx = j a_mat_max = a_mat[j, i] + # if a pivot index is not found in a given row, reset a_mat_max to -1 and move to the next row elif j == n_rows-1 and a_mat_max == 0.: piv_idx = max_idx a_mat_max = -1. + # update the matrix by flipping the row and columns to achieve row echelon form if a_mat_max > 0.: if max_idx > piv_idx: a_mat[[piv_idx, max_idx]] = a_mat[[max_idx, piv_idx]] @@ -147,6 +156,7 @@ def gauss_elim_over_gf2(a_mat, b_vec=None): if a_mat[j, i] == 1.: a_mat[j, i:n_cols] = np.fmod(a_mat[j, i:n_cols] + a_mat[piv_idx, i:n_cols], 2) piv_idx += 1 + # extract the solution from the bottom to the top since it is now in row echelon form b_vec = a_mat[0:n_rows, n_cols-1].tolist() for i in range(n_rows - 1, -1, -1): col_idx, z_free = -1., [] @@ -158,15 +168,19 @@ def gauss_elim_over_gf2(a_mat, b_vec=None): z_free.append(j) if col_idx >= 0.: z_vals.append([col_idx, z_free, b_vec[i]]) + # check for free solutions -- select 0 for the free solution + # for the ILC generator screening procedure, 0 leads to an I op and 1 leads to a Z Pauli op for z_val in (z_vals): b_val = z_val[2] - # Make a choice for the free solns as either 0 or 1; - # 0 leads to an I op, while 1 leads to a Z op -- 0 seems to be the slightly better choice. for z_free in (z_val[1]): if z_sln[z_free] == -1: z_sln[z_free] = 0. b_val = np.fmod(b_val + z_sln[z_free], 2) z_sln[z_val[0]] = b_val + # check that z_sln does not have any -1 values left -- if so, a solution was not found. + for z_val in z_sln: + if z_val == -1: + warnings.warn("Gaussian elimination over GF(2) failed to find a solution.", RuntimeWarning) return np.array(z_sln) diff --git a/tangelo/toolboxes/ansatz_generator/tests/test_ilc.py b/tangelo/toolboxes/ansatz_generator/tests/test_ilc.py index c8dd08867..fa0388df3 100644 --- a/tangelo/toolboxes/ansatz_generator/tests/test_ilc.py +++ b/tangelo/toolboxes/ansatz_generator/tests/test_ilc.py @@ -62,7 +62,7 @@ def test_ilc_incorrect_number_var_params(self): self.assertRaises(ValueError, ilc_ansatz.set_var_params, np.array([1.] * 2)) @staticmethod - def test_gauss_elim_over_gf2(): + def test_gauss_elim_over_gf2_sqrmat(): """ Verify behavior of the Gaussian elimination over the binary field function. """ # a_matrix stores the action of A * z over GF(2); dimension is n x m @@ -79,6 +79,42 @@ def test_gauss_elim_over_gf2(): np.testing.assert_array_almost_equal(z_sln, z_ref, decimal=6) + @staticmethod + def test_gauss_elim_over_gf2_rectmat(): + """ Verify behavior of the Gaussian elimination over the binary field function. """ + + # a_matrix stores the action of A * z over GF(2); dimension is n x m + a_matrix = np.array([[0, 0, 1, 0, 1], [1, 1, 0, 0, 0], [0, 0, 0, 1, 1]]) + + # b_vec stores the solution vector for the equation A * z = b_vec; dimension is n x 1 + b_vec = np.array([1, 1, 0]).reshape((3, 1)) + + # z_ref stores the serves as the reference for the output of gauss_elim_over_gf2 + z_ref = np.array([1, 0, 1, 0, 0]) + + # solve A * z = b and compare to reference solution + z_sln = gauss_elim_over_gf2(a_matrix, b_vec) + + np.testing.assert_array_almost_equal(z_sln, z_ref, decimal=6) + + @staticmethod + def test_gauss_elim_over_gf2_lindep(): + """ Verify behavior of the Gaussian elimination over the binary field function. """ + + # a_matrix stores the action of A * z over GF(2); dimension is n x m + a_matrix = np.array([[0, 0, 1, 0, 1], [0, 0, 1, 0, 1], [0, 0, 0, 1, 1]]) + + # b_vec stores the solution vector for the equation A * z = b_vec; dimension is n x 1 + b_vec = np.array([1, 0, 1]).reshape((3, 1)) + + # z_ref stores the serves as the reference for the output of gauss_elim_over_gf2 + z_ref = np.array([-1, -1, 1, 1, 0]) + + # solve A * z = b and compare to reference solution + z_sln = gauss_elim_over_gf2(a_matrix, b_vec) + + np.testing.assert_array_almost_equal(z_sln, z_ref, decimal=6) + def test_ilc_h2(self): """ Verify closed-shell functionality when using the ILC class separately for H2 """ From 7110b94fd06339d049e434b7bdb93298bbdb1518 Mon Sep 17 00:00:00 2001 From: Marc Coons Date: Tue, 19 Apr 2022 15:23:43 -0400 Subject: [PATCH 21/28] fixed indentation error; added initial_var_params for qmf, qcc, and ilc methods --- .../variational/tests/test_vqe_solver.py | 24 ++++++++++--------- 1 file changed, 13 insertions(+), 11 deletions(-) diff --git a/tangelo/algorithms/variational/tests/test_vqe_solver.py b/tangelo/algorithms/variational/tests/test_vqe_solver.py index cf24e906b..ff04951c2 100644 --- a/tangelo/algorithms/variational/tests/test_vqe_solver.py +++ b/tangelo/algorithms/variational/tests/test_vqe_solver.py @@ -18,7 +18,6 @@ from tangelo.linq import Simulator from tangelo.algorithms import BuiltInAnsatze, VQESolver from tangelo.molecule_library import mol_H2_sto3g, mol_H4_sto3g, mol_H4_cation_sto3g, mol_NaH_sto3g, mol_H4_sto3g_symm -from tangelo.toolboxes.ansatz_generator.uccsd import UCCSD from tangelo.toolboxes.qubit_mappings.mapping_transform import fermion_to_qubit_mapping from tangelo.toolboxes.molecular_computation.rdms import matricize_2rdm from tangelo.toolboxes.optimizers.rotosolve import rotosolve @@ -117,7 +116,7 @@ def test_simulate_qmf_h2(self): """ vqe_options = {"molecule": mol_H2_sto3g, "ansatz": BuiltInAnsatze.QMF, "qubit_mapping": "jw", - "verbose": True} + "initial_var_params": "hf_state", "verbose": False} vqe_solver = VQESolver(vqe_options) vqe_solver.build() @@ -129,7 +128,7 @@ def test_simulate_qcc_h2(self): parameters, exact simulator. """ vqe_options = {"molecule": mol_H2_sto3g, "ansatz": BuiltInAnsatze.QCC, "qubit_mapping": "jw", - "verbose": True} + "initial_var_params": "random", "verbose": False} vqe_solver = VQESolver(vqe_options) vqe_solver.build() @@ -140,8 +139,9 @@ def test_simulate_ilc_h2(self): """Run VQE on H2 molecule, with ILC ansatz, JW qubit mapping, initial parameters, exact simulator. """ + vqe_options = {"molecule": mol_H2_sto3g, "ansatz": BuiltInAnsatze.ILC, "qubit_mapping": "jw", - "verbose": True} + "initial_var_params": "diag", "verbose": False} vqe_solver = VQESolver(vqe_options) vqe_solver.build() @@ -219,7 +219,7 @@ def test_simulate_qmf_h4(self): """ vqe_options = {"molecule": mol_H4_sto3g, "ansatz": BuiltInAnsatze.QMF, "qubit_mapping": "jw", - "verbose": True} + "initial_var_params": "hf_state", "verbose": False} vqe_solver = VQESolver(vqe_options) vqe_solver.build() @@ -230,8 +230,9 @@ def test_simulate_qcc_h4(self): """Run VQE on H4 molecule, with QCC ansatz, JW qubit mapping, initial parameters, exact simulator. """ + vqe_options = {"molecule": mol_H4_sto3g, "ansatz": BuiltInAnsatze.QCC, "qubit_mapping": "jw", - "verbose": True} + "initial_var_params": "random", "verbose": False} vqe_solver = VQESolver(vqe_options) vqe_solver.build() @@ -242,8 +243,9 @@ def test_simulate_ilc_h4(self): """Run VQE on H4 molecule, with ILC ansatz, JW qubit mapping, initial parameters, exact simulator. """ + vqe_options = {"molecule": mol_H4_sto3g, "ansatz": BuiltInAnsatze.ILC, "qubit_mapping": "jw", - "verbose": True} + "initial_var_params": "diag", "verbose": False} vqe_solver = VQESolver(vqe_options) vqe_solver.build() @@ -266,7 +268,7 @@ def test_simulate_qmf_h4_open(self): """ vqe_options = {"molecule": mol_H4_cation_sto3g, "ansatz": BuiltInAnsatze.QMF, "qubit_mapping": "jw", - "verbose": True} + "initial_var_params": "hf_state", "verbose": False} vqe_solver = VQESolver(vqe_options) vqe_solver.build() @@ -278,8 +280,8 @@ def test_simulate_qcc_h4_open(self): parameters, exact simulator. """ - vqe_options = {"molecule": mol_H4_cation_sto3g, "ansatz": BuiltInAnsatze.QCC, "qubit_mapping": "jw", - "verbose": True} + vqe_options = {"molecule": mol_H4_cation_sto3g, "ansatz": BuiltInAnsatze.QCC, "qubit_mapping": "jw", + "initial_var_params": "random", "verbose": False} vqe_solver = VQESolver(vqe_options) vqe_solver.build() @@ -292,7 +294,7 @@ def test_simulate_ilc_h4_open(self): """ vqe_options = {"molecule": mol_H4_cation_sto3g, "ansatz": BuiltInAnsatze.ILC, "qubit_mapping": "jw", - "verbose": True} + "initial_var_params": "diag", "verbose": False} vqe_solver = VQESolver(vqe_options) vqe_solver.build() From 592e06e1b23a727cefb8bb589264ba0ce51c5b88 Mon Sep 17 00:00:00 2001 From: Marc Coons Date: Tue, 19 Apr 2022 15:49:43 -0400 Subject: [PATCH 22/28] addressed errors with test_vqe_solver --- .../variational/tests/test_vqe_solver.py | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/tangelo/algorithms/variational/tests/test_vqe_solver.py b/tangelo/algorithms/variational/tests/test_vqe_solver.py index ff04951c2..ec43b0bb6 100644 --- a/tangelo/algorithms/variational/tests/test_vqe_solver.py +++ b/tangelo/algorithms/variational/tests/test_vqe_solver.py @@ -18,6 +18,7 @@ from tangelo.linq import Simulator from tangelo.algorithms import BuiltInAnsatze, VQESolver from tangelo.molecule_library import mol_H2_sto3g, mol_H4_sto3g, mol_H4_cation_sto3g, mol_NaH_sto3g, mol_H4_sto3g_symm +from tangelo.toolboxes.ansatz_generator.uccsd import UCCSD from tangelo.toolboxes.qubit_mappings.mapping_transform import fermion_to_qubit_mapping from tangelo.toolboxes.molecular_computation.rdms import matricize_2rdm from tangelo.toolboxes.optimizers.rotosolve import rotosolve @@ -116,7 +117,7 @@ def test_simulate_qmf_h2(self): """ vqe_options = {"molecule": mol_H2_sto3g, "ansatz": BuiltInAnsatze.QMF, "qubit_mapping": "jw", - "initial_var_params": "hf_state", "verbose": False} + "verbose": False} vqe_solver = VQESolver(vqe_options) vqe_solver.build() @@ -128,7 +129,7 @@ def test_simulate_qcc_h2(self): parameters, exact simulator. """ vqe_options = {"molecule": mol_H2_sto3g, "ansatz": BuiltInAnsatze.QCC, "qubit_mapping": "jw", - "initial_var_params": "random", "verbose": False} + "verbose": False} vqe_solver = VQESolver(vqe_options) vqe_solver.build() @@ -141,7 +142,7 @@ def test_simulate_ilc_h2(self): """ vqe_options = {"molecule": mol_H2_sto3g, "ansatz": BuiltInAnsatze.ILC, "qubit_mapping": "jw", - "initial_var_params": "diag", "verbose": False} + "verbose": False} vqe_solver = VQESolver(vqe_options) vqe_solver.build() @@ -219,7 +220,7 @@ def test_simulate_qmf_h4(self): """ vqe_options = {"molecule": mol_H4_sto3g, "ansatz": BuiltInAnsatze.QMF, "qubit_mapping": "jw", - "initial_var_params": "hf_state", "verbose": False} + "verbose": False} vqe_solver = VQESolver(vqe_options) vqe_solver.build() @@ -232,7 +233,7 @@ def test_simulate_qcc_h4(self): """ vqe_options = {"molecule": mol_H4_sto3g, "ansatz": BuiltInAnsatze.QCC, "qubit_mapping": "jw", - "initial_var_params": "random", "verbose": False} + "verbose": False} vqe_solver = VQESolver(vqe_options) vqe_solver.build() @@ -245,7 +246,7 @@ def test_simulate_ilc_h4(self): """ vqe_options = {"molecule": mol_H4_sto3g, "ansatz": BuiltInAnsatze.ILC, "qubit_mapping": "jw", - "initial_var_params": "diag", "verbose": False} + "verbose": False} vqe_solver = VQESolver(vqe_options) vqe_solver.build() @@ -268,7 +269,7 @@ def test_simulate_qmf_h4_open(self): """ vqe_options = {"molecule": mol_H4_cation_sto3g, "ansatz": BuiltInAnsatze.QMF, "qubit_mapping": "jw", - "initial_var_params": "hf_state", "verbose": False} + "verbose": False} vqe_solver = VQESolver(vqe_options) vqe_solver.build() @@ -281,7 +282,7 @@ def test_simulate_qcc_h4_open(self): """ vqe_options = {"molecule": mol_H4_cation_sto3g, "ansatz": BuiltInAnsatze.QCC, "qubit_mapping": "jw", - "initial_var_params": "random", "verbose": False} + "verbose": False} vqe_solver = VQESolver(vqe_options) vqe_solver.build() @@ -294,7 +295,7 @@ def test_simulate_ilc_h4_open(self): """ vqe_options = {"molecule": mol_H4_cation_sto3g, "ansatz": BuiltInAnsatze.ILC, "qubit_mapping": "jw", - "initial_var_params": "diag", "verbose": False} + "verbose": False} vqe_solver = VQESolver(vqe_options) vqe_solver.build() From 851b676ca36b1bee445c7143ed188d27c40199e2 Mon Sep 17 00:00:00 2001 From: Marc Coons Date: Fri, 22 Apr 2022 16:10:50 -0400 Subject: [PATCH 23/28] cleaned up construct_acs function and tests related to Gaussian elimination --- .../toolboxes/ansatz_generator/_qubit_ilc.py | 27 +++++++++++-------- .../ansatz_generator/tests/test_ilc.py | 6 ++--- 2 files changed, 19 insertions(+), 14 deletions(-) diff --git a/tangelo/toolboxes/ansatz_generator/_qubit_ilc.py b/tangelo/toolboxes/ansatz_generator/_qubit_ilc.py index 428e2a9db..aea68282d 100644 --- a/tangelo/toolboxes/ansatz_generator/_qubit_ilc.py +++ b/tangelo/toolboxes/ansatz_generator/_qubit_ilc.py @@ -74,10 +74,9 @@ def construct_acs(dis, max_ilc_gens, n_qubits): rowdx += 1 # Solve A * z = b --> here b = 1 - z_sln = gauss_elim_over_gf2(a_mat, b_vec=one_vec) + z_sln, _ = gauss_elim_over_gf2(a_mat, b_vec=one_vec) # Check solution: odd # of Y ops, at least two flip indices, and mutually anti-commutes - good_sln = True for i in range(n_gens): n_flip, n_y, gen_idx, gen_tup = 0, 0, gen_idxs[i], tuple() for j in range(n_qubits): @@ -94,16 +93,20 @@ def construct_acs(dis, max_ilc_gens, n_qubits): gen = (j, 'Z') if gen: gen_tup += (gen, ) - if n_flip < 2 or n_y % 2 == 0: - if good_sln and gen_idx not in bad_sln_idxs: + # check number of flip indices and number of Y Pauli ops + if n_flip > 1 and n_y % 2 == 1: + gen_i = QubitOperator(gen_tup, 1.) + good_sln = True + # check mutual anti-commutativity of each new ILC generator with all the rest + for gen_j in ilc_gens: + if gen_i * gen_j != -1. * gen_j * gen_i: + if gen_idx not in bad_sln_idxs: + bad_sln_idxs.append(gen_idx) + good_sln = False + else: + if gen_idx not in bad_sln_idxs: bad_sln_idxs.append(gen_idx) good_sln = False - gen_i = QubitOperator(gen_tup, 1.) - for gen_j in ilc_gens: - if gen_i * gen_j != -1. * gen_j * gen_i: - if good_sln and gen_idx not in bad_sln_idxs: - bad_sln_idxs.append(gen_idx) - good_sln = False if good_sln: ilc_gens.append(gen_i) return ilc_gens @@ -178,10 +181,12 @@ def gauss_elim_over_gf2(a_mat, b_vec=None): b_val = np.fmod(b_val + z_sln[z_free], 2) z_sln[z_val[0]] = b_val # check that z_sln does not have any -1 values left -- if so, a solution was not found. + sln_found = True for z_val in z_sln: if z_val == -1: warnings.warn("Gaussian elimination over GF(2) failed to find a solution.", RuntimeWarning) - return np.array(z_sln) + sln_found = False + return np.array(z_sln), sln_found def get_ilc_params_by_diag(qubit_ham, ilc_gens, qmf_var_params): diff --git a/tangelo/toolboxes/ansatz_generator/tests/test_ilc.py b/tangelo/toolboxes/ansatz_generator/tests/test_ilc.py index fa0388df3..3e480f894 100644 --- a/tangelo/toolboxes/ansatz_generator/tests/test_ilc.py +++ b/tangelo/toolboxes/ansatz_generator/tests/test_ilc.py @@ -75,7 +75,7 @@ def test_gauss_elim_over_gf2_sqrmat(): z_ref = np.array([0, 1, 0, 1]) # solve A * z = b and compare to reference solution - z_sln = gauss_elim_over_gf2(a_matrix, b_vec) + z_sln, _ = gauss_elim_over_gf2(a_matrix, b_vec) np.testing.assert_array_almost_equal(z_sln, z_ref, decimal=6) @@ -93,7 +93,7 @@ def test_gauss_elim_over_gf2_rectmat(): z_ref = np.array([1, 0, 1, 0, 0]) # solve A * z = b and compare to reference solution - z_sln = gauss_elim_over_gf2(a_matrix, b_vec) + z_sln, _ = gauss_elim_over_gf2(a_matrix, b_vec) np.testing.assert_array_almost_equal(z_sln, z_ref, decimal=6) @@ -111,7 +111,7 @@ def test_gauss_elim_over_gf2_lindep(): z_ref = np.array([-1, -1, 1, 1, 0]) # solve A * z = b and compare to reference solution - z_sln = gauss_elim_over_gf2(a_matrix, b_vec) + z_sln, _ = gauss_elim_over_gf2(a_matrix, b_vec) np.testing.assert_array_almost_equal(z_sln, z_ref, decimal=6) From 89f08ca2721cb176bf4b2a476baae9e30eb25c3d Mon Sep 17 00:00:00 2001 From: Marc Coons Date: Tue, 3 May 2022 18:18:38 -0400 Subject: [PATCH 24/28] updating ilc_ansatz branch with main --- .../toolboxes/ansatz_generator/_qubit_cc.py | 2 +- .../toolboxes/ansatz_generator/_qubit_ilc.py | 74 ++++++++++++++++--- tangelo/toolboxes/ansatz_generator/ilc.py | 6 +- tangelo/toolboxes/ansatz_generator/qcc.py | 8 +- tangelo/toolboxes/ansatz_generator/qmf.py | 6 +- .../ansatz_generator/tests/test_ilc.py | 6 +- 6 files changed, 77 insertions(+), 25 deletions(-) diff --git a/tangelo/toolboxes/ansatz_generator/_qubit_cc.py b/tangelo/toolboxes/ansatz_generator/_qubit_cc.py index 60782b3f4..973aafe70 100644 --- a/tangelo/toolboxes/ansatz_generator/_qubit_cc.py +++ b/tangelo/toolboxes/ansatz_generator/_qubit_cc.py @@ -87,7 +87,7 @@ def get_dis_groups(qubit_ham, pure_var_params, deqcc_dtau_thresh): for qham_items in qubit_ham.terms.items()) flip_idxs = list(filter(None, (get_idxs_deriv(q_gen[0], *q_gen[1]) for q_gen in qham_gen))) - # Group Hamiltonian terms with the same flip indices and sum signed dEQCC/tau values + # Group Hamiltonian terms with the same flip indices and sum of the signed dEQCC/tau values candidates = dict() for idxs in flip_idxs: deriv_old = candidates.get(idxs[0], 0.) diff --git a/tangelo/toolboxes/ansatz_generator/_qubit_ilc.py b/tangelo/toolboxes/ansatz_generator/_qubit_ilc.py index aea68282d..e0fb1cf01 100644 --- a/tangelo/toolboxes/ansatz_generator/_qubit_ilc.py +++ b/tangelo/toolboxes/ansatz_generator/_qubit_ilc.py @@ -74,7 +74,7 @@ def construct_acs(dis, max_ilc_gens, n_qubits): rowdx += 1 # Solve A * z = b --> here b = 1 - z_sln, _ = gauss_elim_over_gf2(a_mat, b_vec=one_vec) + z_sln = gauss_elim_over_gf2(a_mat, b_vec=one_vec) # Check solution: odd # of Y ops, at least two flip indices, and mutually anti-commutes for i in range(n_gens): @@ -97,7 +97,7 @@ def construct_acs(dis, max_ilc_gens, n_qubits): if n_flip > 1 and n_y % 2 == 1: gen_i = QubitOperator(gen_tup, 1.) good_sln = True - # check mutual anti-commutativity of each new ILC generator with all the rest + # check mutual anti-commutativity of each new ILC generator with all previous ones for gen_j in ilc_gens: if gen_i * gen_j != -1. * gen_j * gen_i: if gen_idx not in bad_sln_idxs: @@ -131,14 +131,10 @@ def gauss_elim_over_gf2(a_mat, b_vec=None): n_rows, n_cols = np.shape(a_mat) z_vals, z_sln, piv_idx = [], [-1] * n_cols, 0 - # check that b_vec was properly supplied; ortherwise initialize as a vector of zeros if not isinstance(b_vec, np.ndarray): b_vec = np.zeros((n_rows, 1)) a_mat = np.append(a_mat, b_vec, axis=1) - - z_vals, z_sln, piv_idx = [], [-1] * n_cols, 0 - n_cols += 1 for i in range(n_cols): a_mat_max, max_idx = 0., piv_idx # locate the pivot index by searching each row for a non-zero value. @@ -160,7 +156,6 @@ def gauss_elim_over_gf2(a_mat, b_vec=None): a_mat[j, i:n_cols] = np.fmod(a_mat[j, i:n_cols] + a_mat[piv_idx, i:n_cols], 2) piv_idx += 1 # extract the solution from the bottom to the top since it is now in row echelon form - b_vec = a_mat[0:n_rows, n_cols-1].tolist() for i in range(n_rows - 1, -1, -1): col_idx, z_free = -1., [] for j in range(n_cols-1): @@ -170,7 +165,7 @@ def gauss_elim_over_gf2(a_mat, b_vec=None): else: z_free.append(j) if col_idx >= 0.: - z_vals.append([col_idx, z_free, b_vec[i]]) + z_vals.append([col_idx, z_free, a_mat[i][n_cols-1]]) # check for free solutions -- select 0 for the free solution # for the ILC generator screening procedure, 0 leads to an I op and 1 leads to a Z Pauli op for z_val in (z_vals): @@ -181,13 +176,70 @@ def gauss_elim_over_gf2(a_mat, b_vec=None): b_val = np.fmod(b_val + z_sln[z_free], 2) z_sln[z_val[0]] = b_val # check that z_sln does not have any -1 values left -- if so, a solution was not found. - sln_found = True for z_val in z_sln: if z_val == -1: warnings.warn("Gaussian elimination over GF(2) failed to find a solution.", RuntimeWarning) - sln_found = False - return np.array(z_sln), sln_found + return np.array(z_sln) + n_rows, n_cols = np.shape(a_mat) + z_vals, z_sln, piv_idx = [], [-1] * n_cols, 0 + # check that b_vec was properly supplied; ortherwise initialize as a vector of zeros + if not isinstance(b_vec, np.ndarray): + b_vec = np.zeros((n_rows, 1)) + a_mat = np.append(a_mat, b_vec, axis=1) + n_cols += 1 + print("start a_mat = ", a_mat) + for i in range(n_cols): + a_mat_max, max_idx = 0., piv_idx + # locate the pivot index by searching each row for a non-zero value. + for j in range(piv_idx, n_rows): + # if a pivot index is found, set the value to the col index for the row in which it was found + if a_mat[j, i] > a_mat_max: + max_idx = j + a_mat_max = a_mat[j, i] + # if a pivot index is not found in a given row, reset a_mat_max to -1 and move to the next row + elif j == n_rows-1 and a_mat_max == 0.: + piv_idx = max_idx + a_mat_max = -1. + # update the matrix by flipping the row and columns to achieve row echelon form + if a_mat_max > 0.: + if max_idx > piv_idx: + a_mat[[piv_idx, max_idx]] = a_mat[[max_idx, piv_idx]] + for j in range(piv_idx + 1, n_rows): + if a_mat[j, i] == 1.: + a_mat[j, i:n_cols] = np.fmod(a_mat[j, i:n_cols] + a_mat[piv_idx, i:n_cols], 2) + piv_idx += 1 + # extract the solution from the bottom to the top since it is now in row echelon form + print("end a_mat = ", a_mat) + z_free, z_not_free = list(range(n_cols-1)), [] + for i in range(n_rows):#-1, -1, -1): +# col_idx, z_free = -1., list(range(n_cols-1)) + col_idx = -1 + for j in range(n_cols-1): + if a_mat[i, j] == 1.: + if col_idx == -1: + col_idx = j + z_not_free.append(z_free.pop(j)) + elif col_idx > -1 and j in z_not_free: + z_free.pop(j) + if col_idx >= 0.: + print("col_idx = ", col_idx, z_free) + z_vals.append([col_idx, z_free, a_mat[i][n_cols-1]]) + # check for free solutions -- select 0 for the free solution + # for the ILC generator screening procedure, 0 leads to an I op and 1 leads to a Z Pauli op + print("z_not_free = ", z_not_free) + for z_val in (z_vals): + b_val = z_val[2] + for z_free in (z_val[1]): + if z_sln[z_free] == -1: + z_sln[z_free] = 1. + b_val = np.fmod(b_val + z_sln[z_free], 2) + z_sln[z_val[0]] = b_val + # check that z_sln does not have any -1 values left -- if so, a solution was not found. + for z_val in z_sln: + if z_val == -1: + warnings.warn("Gaussian elimination over GF(2) failed to find a solution.", RuntimeWarning) + return np.array(z_sln) def get_ilc_params_by_diag(qubit_ham, ilc_gens, qmf_var_params): """Driver function that solves the generalized eigenvalue problem Hc = ESc required diff --git a/tangelo/toolboxes/ansatz_generator/ilc.py b/tangelo/toolboxes/ansatz_generator/ilc.py index a5ad5d972..7a75a062e 100755 --- a/tangelo/toolboxes/ansatz_generator/ilc.py +++ b/tangelo/toolboxes/ansatz_generator/ilc.py @@ -49,7 +49,7 @@ class ILC(Ansatz): Args: molecule (SecondQuantizedMolecule): The molecular system. - mapping (str): One of the supported mapping identifiers. Default, "JW". + mapping (str): One of the supported mapping identifiers. Default, "jw". up_then_down (bool): Change basis ordering putting all spin-up orbitals first, followed by all spin-down. Default, False. ilc_op_list (list of QubitOperator): Generator list for the ILC ansatz. Default, None. @@ -71,7 +71,7 @@ class ILC(Ansatz): Default, 1. """ - def __init__(self, molecule, mapping="JW", up_then_down=False, ilc_op_list=None, + def __init__(self, molecule, mapping="jw", up_then_down=False, ilc_op_list=None, qmf_circuit=None, qmf_var_params=None, qubit_ham=None, ilc_tau_guess=1.e-2, deilc_dtau_thresh=1.e-3, max_ilc_gens=None, n_trotter=1): @@ -86,7 +86,7 @@ def __init__(self, molecule, mapping="JW", up_then_down=False, ilc_op_list=None, self.mapping = mapping self.n_qubits = get_qubit_number(self.mapping, self.n_spinorbitals) self.up_then_down = up_then_down - if self.mapping.upper() == "JW" and not self.up_then_down: + if self.mapping.lower() == "jw" and not self.up_then_down: warnings.warn("Efficient generator screening for the ILC ansatz requires spin-orbital " "ordering to be all spin-up first followed by all spin-down for the JW " "mapping.", RuntimeWarning) diff --git a/tangelo/toolboxes/ansatz_generator/qcc.py b/tangelo/toolboxes/ansatz_generator/qcc.py index 4437135a3..c2e0ab7fe 100755 --- a/tangelo/toolboxes/ansatz_generator/qcc.py +++ b/tangelo/toolboxes/ansatz_generator/qcc.py @@ -55,7 +55,7 @@ class QCC(Ansatz): Args: molecule (SecondQuantizedMolecule): The molecular system. - mapping (str): One of the supported qubit mapping identifiers. Default, "JW". + mapping (str): One of the supported qubit mapping identifiers. Default, "jw". up_then_down (bool): Change basis ordering putting all spin-up orbitals first, followed by all spin-down. Default, False. qcc_op_list (list of QubitOperator): Generator list for the QCC ansatz. Default, None. @@ -75,7 +75,7 @@ class QCC(Ansatz): generators are selected in order of decreasing |dEQCC/dtau|. Default, None. """ - def __init__(self, molecule, mapping="JW", up_then_down=False, qcc_op_list=None, + def __init__(self, molecule, mapping="jw", up_then_down=False, qcc_op_list=None, qmf_circuit=None, qmf_var_params=None, qubit_ham=None, qcc_tau_guess=1.e-2, deqcc_dtau_thresh=1.e-3, max_qcc_gens=None): @@ -90,7 +90,7 @@ def __init__(self, molecule, mapping="JW", up_then_down=False, qcc_op_list=None, self.mapping = mapping self.n_qubits = get_qubit_number(self.mapping, self.n_spinorbitals) self.up_then_down = up_then_down - if self.mapping.upper() == "JW" and not self.up_then_down: + if self.mapping.lower() == "jw" and not self.up_then_down: warnings.warn("Efficient generator screening for the QCC ansatz requires spin-orbital " "ordering to be all spin-up first followed by all spin-down for the JW " "mapping.", RuntimeWarning) @@ -268,7 +268,7 @@ def _get_qcc_qubit_op(self): self.qcc_op_list = [] for i in range(self.n_var_params): # Instead of randomly choosing a generator, get the last one. - qcc_gen = self.dis[i][-1] + qcc_gen = self.dis[i][0] qcc_qubit_op -= 0.5 * self.var_params[i] * qcc_gen self.qcc_op_list.append(qcc_gen) else: diff --git a/tangelo/toolboxes/ansatz_generator/qmf.py b/tangelo/toolboxes/ansatz_generator/qmf.py index 7accc3e5e..30a8b2812 100755 --- a/tangelo/toolboxes/ansatz_generator/qmf.py +++ b/tangelo/toolboxes/ansatz_generator/qmf.py @@ -54,7 +54,7 @@ class QMF(Ansatz): Args: molecule (SecondQuantizedMolecule): The molecular system. - mapping (str): One of the supported qubit mapping identifiers. Default, "JW". + mapping (str): One of the supported qubit mapping identifiers. Default, "jw". up_then_down (bool): Change basis ordering putting all spin up orbitals first, followed by all spin down. Default, False. init_qmf (dict): Controls for QMF variational parameter initialization and mean-field @@ -69,7 +69,7 @@ class QMF(Ansatz): Default, {"init_params": "hf_state"}. """ - def __init__(self, molecule=None, mapping="JW", up_then_down=False, init_qmf=None): + def __init__(self, molecule=None, mapping="jw", up_then_down=False, init_qmf=None): self.molecule = molecule self.n_spinorbitals = self.molecule.n_active_sos @@ -79,7 +79,7 @@ def __init__(self, molecule=None, mapping="JW", up_then_down=False, init_qmf=Non self.n_orbitals = self.n_spinorbitals // 2 self.spin = molecule.spin self.fermi_ham = self.molecule.fermionic_hamiltonian - self.n_electrons = self.molecule.n_electrons + self.n_electrons = self.molecule.n_active_electrons self.mapping = mapping self.n_qubits = get_qubit_number(self.mapping, self.n_spinorbitals) diff --git a/tangelo/toolboxes/ansatz_generator/tests/test_ilc.py b/tangelo/toolboxes/ansatz_generator/tests/test_ilc.py index 3e480f894..fa0388df3 100644 --- a/tangelo/toolboxes/ansatz_generator/tests/test_ilc.py +++ b/tangelo/toolboxes/ansatz_generator/tests/test_ilc.py @@ -75,7 +75,7 @@ def test_gauss_elim_over_gf2_sqrmat(): z_ref = np.array([0, 1, 0, 1]) # solve A * z = b and compare to reference solution - z_sln, _ = gauss_elim_over_gf2(a_matrix, b_vec) + z_sln = gauss_elim_over_gf2(a_matrix, b_vec) np.testing.assert_array_almost_equal(z_sln, z_ref, decimal=6) @@ -93,7 +93,7 @@ def test_gauss_elim_over_gf2_rectmat(): z_ref = np.array([1, 0, 1, 0, 0]) # solve A * z = b and compare to reference solution - z_sln, _ = gauss_elim_over_gf2(a_matrix, b_vec) + z_sln = gauss_elim_over_gf2(a_matrix, b_vec) np.testing.assert_array_almost_equal(z_sln, z_ref, decimal=6) @@ -111,7 +111,7 @@ def test_gauss_elim_over_gf2_lindep(): z_ref = np.array([-1, -1, 1, 1, 0]) # solve A * z = b and compare to reference solution - z_sln, _ = gauss_elim_over_gf2(a_matrix, b_vec) + z_sln = gauss_elim_over_gf2(a_matrix, b_vec) np.testing.assert_array_almost_equal(z_sln, z_ref, decimal=6) From fab6729f5ce75a9d4e02fa65811466c47a6c0408 Mon Sep 17 00:00:00 2001 From: Marc Coons Date: Thu, 5 May 2022 12:04:28 -0400 Subject: [PATCH 25/28] last round of revisions to ilc_ansatz branch before merge --- .../toolboxes/ansatz_generator/_qubit_cc.py | 2 +- .../toolboxes/ansatz_generator/_qubit_ilc.py | 69 ++----------------- .../toolboxes/ansatz_generator/_qubit_mf.py | 4 +- tangelo/toolboxes/ansatz_generator/ilc.py | 11 +-- tangelo/toolboxes/ansatz_generator/qcc.py | 11 +-- tangelo/toolboxes/ansatz_generator/qmf.py | 6 +- .../ansatz_generator/tests/test_ilc.py | 5 +- .../ansatz_generator/tests/test_qcc.py | 12 ++-- .../ansatz_generator/tests/test_qmf.py | 1 + 9 files changed, 35 insertions(+), 86 deletions(-) diff --git a/tangelo/toolboxes/ansatz_generator/_qubit_cc.py b/tangelo/toolboxes/ansatz_generator/_qubit_cc.py index 973aafe70..d7ecb26cc 100644 --- a/tangelo/toolboxes/ansatz_generator/_qubit_cc.py +++ b/tangelo/toolboxes/ansatz_generator/_qubit_cc.py @@ -33,7 +33,7 @@ from itertools import combinations from tangelo.toolboxes.operators.operators import QubitOperator -from ._qubit_mf import get_op_expval +from tangelo.toolboxes.ansatz_generator._qubit_mf import get_op_expval def construct_dis(qubit_ham, pure_var_params, deqcc_dtau_thresh): diff --git a/tangelo/toolboxes/ansatz_generator/_qubit_ilc.py b/tangelo/toolboxes/ansatz_generator/_qubit_ilc.py index e0fb1cf01..3a2cc617d 100644 --- a/tangelo/toolboxes/ansatz_generator/_qubit_ilc.py +++ b/tangelo/toolboxes/ansatz_generator/_qubit_ilc.py @@ -27,11 +27,12 @@ """ import warnings + import scipy import numpy as np from tangelo.toolboxes.operators.operators import QubitOperator -from ._qubit_mf import get_op_expval +from tangelo.toolboxes.ansatz_generator._qubit_mf import get_op_expval def construct_acs(dis, max_ilc_gens, n_qubits): @@ -97,7 +98,7 @@ def construct_acs(dis, max_ilc_gens, n_qubits): if n_flip > 1 and n_y % 2 == 1: gen_i = QubitOperator(gen_tup, 1.) good_sln = True - # check mutual anti-commutativity of each new ILC generator with all previous ones + # check mutual anti-commutativity of each new ILC generator with all the rest for gen_j in ilc_gens: if gen_i * gen_j != -1. * gen_j * gen_i: if gen_idx not in bad_sln_idxs: @@ -135,6 +136,7 @@ def gauss_elim_over_gf2(a_mat, b_vec=None): if not isinstance(b_vec, np.ndarray): b_vec = np.zeros((n_rows, 1)) a_mat = np.append(a_mat, b_vec, axis=1) + n_cols += 1 for i in range(n_cols): a_mat_max, max_idx = 0., piv_idx # locate the pivot index by searching each row for a non-zero value. @@ -156,6 +158,7 @@ def gauss_elim_over_gf2(a_mat, b_vec=None): a_mat[j, i:n_cols] = np.fmod(a_mat[j, i:n_cols] + a_mat[piv_idx, i:n_cols], 2) piv_idx += 1 # extract the solution from the bottom to the top since it is now in row echelon form + b_vec = a_mat[0:n_rows, n_cols-1].tolist() for i in range(n_rows - 1, -1, -1): col_idx, z_free = -1., [] for j in range(n_cols-1): @@ -165,7 +168,7 @@ def gauss_elim_over_gf2(a_mat, b_vec=None): else: z_free.append(j) if col_idx >= 0.: - z_vals.append([col_idx, z_free, a_mat[i][n_cols-1]]) + z_vals.append([col_idx, z_free, b_vec[i]]) # check for free solutions -- select 0 for the free solution # for the ILC generator screening procedure, 0 leads to an I op and 1 leads to a Z Pauli op for z_val in (z_vals): @@ -181,66 +184,6 @@ def gauss_elim_over_gf2(a_mat, b_vec=None): warnings.warn("Gaussian elimination over GF(2) failed to find a solution.", RuntimeWarning) return np.array(z_sln) - n_rows, n_cols = np.shape(a_mat) - z_vals, z_sln, piv_idx = [], [-1] * n_cols, 0 - # check that b_vec was properly supplied; ortherwise initialize as a vector of zeros - if not isinstance(b_vec, np.ndarray): - b_vec = np.zeros((n_rows, 1)) - a_mat = np.append(a_mat, b_vec, axis=1) - n_cols += 1 - print("start a_mat = ", a_mat) - for i in range(n_cols): - a_mat_max, max_idx = 0., piv_idx - # locate the pivot index by searching each row for a non-zero value. - for j in range(piv_idx, n_rows): - # if a pivot index is found, set the value to the col index for the row in which it was found - if a_mat[j, i] > a_mat_max: - max_idx = j - a_mat_max = a_mat[j, i] - # if a pivot index is not found in a given row, reset a_mat_max to -1 and move to the next row - elif j == n_rows-1 and a_mat_max == 0.: - piv_idx = max_idx - a_mat_max = -1. - # update the matrix by flipping the row and columns to achieve row echelon form - if a_mat_max > 0.: - if max_idx > piv_idx: - a_mat[[piv_idx, max_idx]] = a_mat[[max_idx, piv_idx]] - for j in range(piv_idx + 1, n_rows): - if a_mat[j, i] == 1.: - a_mat[j, i:n_cols] = np.fmod(a_mat[j, i:n_cols] + a_mat[piv_idx, i:n_cols], 2) - piv_idx += 1 - # extract the solution from the bottom to the top since it is now in row echelon form - print("end a_mat = ", a_mat) - z_free, z_not_free = list(range(n_cols-1)), [] - for i in range(n_rows):#-1, -1, -1): -# col_idx, z_free = -1., list(range(n_cols-1)) - col_idx = -1 - for j in range(n_cols-1): - if a_mat[i, j] == 1.: - if col_idx == -1: - col_idx = j - z_not_free.append(z_free.pop(j)) - elif col_idx > -1 and j in z_not_free: - z_free.pop(j) - if col_idx >= 0.: - print("col_idx = ", col_idx, z_free) - z_vals.append([col_idx, z_free, a_mat[i][n_cols-1]]) - # check for free solutions -- select 0 for the free solution - # for the ILC generator screening procedure, 0 leads to an I op and 1 leads to a Z Pauli op - print("z_not_free = ", z_not_free) - for z_val in (z_vals): - b_val = z_val[2] - for z_free in (z_val[1]): - if z_sln[z_free] == -1: - z_sln[z_free] = 1. - b_val = np.fmod(b_val + z_sln[z_free], 2) - z_sln[z_val[0]] = b_val - # check that z_sln does not have any -1 values left -- if so, a solution was not found. - for z_val in z_sln: - if z_val == -1: - warnings.warn("Gaussian elimination over GF(2) failed to find a solution.", RuntimeWarning) - return np.array(z_sln) - def get_ilc_params_by_diag(qubit_ham, ilc_gens, qmf_var_params): """Driver function that solves the generalized eigenvalue problem Hc = ESc required to obtain the ground state coefficients (ILC parameters). These are subsequently recast diff --git a/tangelo/toolboxes/ansatz_generator/_qubit_mf.py b/tangelo/toolboxes/ansatz_generator/_qubit_mf.py index 691d640d9..9219f3005 100644 --- a/tangelo/toolboxes/ansatz_generator/_qubit_mf.py +++ b/tangelo/toolboxes/ansatz_generator/_qubit_mf.py @@ -39,8 +39,8 @@ from tangelo.linq import Circuit, Gate from tangelo.toolboxes.operators.operators import FermionOperator from tangelo.toolboxes.qubit_mappings.statevector_mapping import get_vector -from .penalty_terms import combined_penalty, number_operator_penalty, spin2_operator_penalty,\ - spin_operator_penalty +from tangelo.toolboxes.ansatz_generator.penalty_terms import combined_penalty, number_operator_penalty,\ + spin2_operator_penalty, spin_operator_penalty def get_op_expval(qubit_op, qmf_var_params): diff --git a/tangelo/toolboxes/ansatz_generator/ilc.py b/tangelo/toolboxes/ansatz_generator/ilc.py index 7a75a062e..117e2b742 100755 --- a/tangelo/toolboxes/ansatz_generator/ilc.py +++ b/tangelo/toolboxes/ansatz_generator/ilc.py @@ -28,16 +28,17 @@ """ import warnings + import numpy as np from tangelo.toolboxes.qubit_mappings.mapping_transform import get_qubit_number,\ fermion_to_qubit_mapping from tangelo.linq import Circuit -from .ansatz import Ansatz -from .ansatz_utils import exp_pauliword_to_gates -from ._qubit_mf import init_qmf_from_hf, get_qmf_circuit, purify_qmf_state -from ._qubit_cc import construct_dis -from ._qubit_ilc import construct_acs, get_ilc_params_by_diag +from tangelo.toolboxes.ansatz_generator.ansatz import Ansatz +from tangelo.toolboxes.ansatz_generator.ansatz_utils import exp_pauliword_to_gates +from tangelo.toolboxes.ansatz_generator._qubit_mf import init_qmf_from_hf, get_qmf_circuit, purify_qmf_state +from tangelo.toolboxes.ansatz_generator._qubit_cc import construct_dis +from tangelo.toolboxes.ansatz_generator._qubit_ilc import construct_acs, get_ilc_params_by_diag class ILC(Ansatz): diff --git a/tangelo/toolboxes/ansatz_generator/qcc.py b/tangelo/toolboxes/ansatz_generator/qcc.py index c2e0ab7fe..12c15fc72 100755 --- a/tangelo/toolboxes/ansatz_generator/qcc.py +++ b/tangelo/toolboxes/ansatz_generator/qcc.py @@ -34,16 +34,17 @@ """ import warnings + import numpy as np from tangelo.toolboxes.operators.operators import QubitOperator from tangelo.toolboxes.qubit_mappings.mapping_transform import get_qubit_number,\ fermion_to_qubit_mapping from tangelo.linq import Circuit -from .ansatz import Ansatz -from .ansatz_utils import exp_pauliword_to_gates -from ._qubit_mf import init_qmf_from_hf, get_qmf_circuit, purify_qmf_state -from ._qubit_cc import construct_dis +from tangelo.toolboxes.ansatz_generator.ansatz import Ansatz +from tangelo.toolboxes.ansatz_generator.ansatz_utils import exp_pauliword_to_gates +from tangelo.toolboxes.ansatz_generator._qubit_mf import init_qmf_from_hf, get_qmf_circuit, purify_qmf_state +from tangelo.toolboxes.ansatz_generator._qubit_cc import construct_dis class QCC(Ansatz): @@ -267,7 +268,7 @@ def _get_qcc_qubit_op(self): if self.qcc_op_list is None: self.qcc_op_list = [] for i in range(self.n_var_params): - # Instead of randomly choosing a generator, get the last one. + # Instead of randomly choosing a generator, grab the first one. qcc_gen = self.dis[i][0] qcc_qubit_op -= 0.5 * self.var_params[i] * qcc_gen self.qcc_op_list.append(qcc_gen) diff --git a/tangelo/toolboxes/ansatz_generator/qmf.py b/tangelo/toolboxes/ansatz_generator/qmf.py index 30a8b2812..3729434eb 100755 --- a/tangelo/toolboxes/ansatz_generator/qmf.py +++ b/tangelo/toolboxes/ansatz_generator/qmf.py @@ -30,12 +30,14 @@ """ import warnings + import numpy as np from tangelo.toolboxes.qubit_mappings.mapping_transform import get_qubit_number,\ fermion_to_qubit_mapping -from .ansatz import Ansatz -from ._qubit_mf import get_qmf_circuit, init_qmf_from_hf, penalize_mf_ham +from tangelo.toolboxes.ansatz_generator.ansatz import Ansatz +from tangelo.toolboxes.ansatz_generator._qubit_mf import get_qmf_circuit, init_qmf_from_hf,\ + penalize_mf_ham class QMF(Ansatz): diff --git a/tangelo/toolboxes/ansatz_generator/tests/test_ilc.py b/tangelo/toolboxes/ansatz_generator/tests/test_ilc.py index fa0388df3..619ed973a 100644 --- a/tangelo/toolboxes/ansatz_generator/tests/test_ilc.py +++ b/tangelo/toolboxes/ansatz_generator/tests/test_ilc.py @@ -16,6 +16,7 @@ with involutory linear combinations (ILC) of anti-commuting sets (ACS) of Pauli words.""" import unittest + import numpy as np from tangelo.linq import Simulator @@ -29,7 +30,7 @@ class ILCTest(unittest.TestCase): """Unit tests for various functionalities of the ILC ansatz class. Examples for both closed- - and restricted open-shell ILC are provided using H2, H4, and H4 +. + and restricted open-shell ILC are provided using H2, H4, and H4+. """ @staticmethod @@ -135,7 +136,7 @@ def test_ilc_h2(self): self.assertAlmostEqual(energy, -1.1372697, delta=1e-6) def test_ilc_h4_cation(self): - """ Verify restricted open-shell functionality when using the ILC class for H4 + """ + """ Verify restricted open-shell functionality when using the ILC class for H4+ """ # Build the ILC ansatz, which sets the QMF parameters automatically if none are passed ilc_op_list = [QubitOperator("Y0 Z2 X4 Z6"), QubitOperator("Y1 Y2 Z4 X5 Y6"), QubitOperator("X0 Z2 Z4 Y6"), diff --git a/tangelo/toolboxes/ansatz_generator/tests/test_qcc.py b/tangelo/toolboxes/ansatz_generator/tests/test_qcc.py index 0bee22ec4..602c8f8eb 100644 --- a/tangelo/toolboxes/ansatz_generator/tests/test_qcc.py +++ b/tangelo/toolboxes/ansatz_generator/tests/test_qcc.py @@ -16,8 +16,8 @@ import os import unittest -import numpy as np +import numpy as np from openfermion import load_operator from tangelo.linq import Simulator @@ -34,7 +34,7 @@ class QCCTest(unittest.TestCase): """Unit tests for various functionalities of the QCC ansatz class. Examples for both closed- - and restricted open-shell QCC are provided using H2, H4 +, and H4 2+ as well as for using the + and restricted open-shell QCC are provided using H2, H4+, and H4+2 as well as for using the QMF and QCC classes together. """ @@ -112,7 +112,7 @@ def test_qmf_qcc_h2(self): self.assertAlmostEqual(energy, -1.137270174660901, delta=1e-6) def test_qcc_h4_cation(self): - """ Verify restricted open-shell functionality when using the QCC class for H4 + """ + """ Verify restricted open-shell functionality when using the QCC class for H4+ """ # Build the QCC ansatz, which sets the QMF parameters automatically if none are passed qcc_op_list = [QubitOperator("X0 Y1 Y2 X3 X4 Y5"), QubitOperator("Y1 X3 X4 X5"), @@ -136,7 +136,7 @@ def test_qcc_h4_cation(self): self.assertAlmostEqual(energy, -1.6380901, delta=1e-6) def test_qmf_qcc_h4_cation(self): - """ Verify restricted open-shell functionality when using QMF + QCC ansatze for H4 + """ + """ Verify restricted open-shell functionality when using QMF + QCC ansatze for H4+ """ # Build the QMF ansatz with optimized parameters qmf_var_params = [3.14159302e+00, 6.20193478e-07, 1.51226426e-06, 3.14159350e+00, @@ -167,7 +167,7 @@ def test_qmf_qcc_h4_cation(self): self.assertAlmostEqual(energy, -1.6382913, delta=1e-6) def test_qcc_h4_double_cation(self): - """ Verify restricted open-shell functionality when using the QCC class for H4 2+ """ + """ Verify restricted open-shell functionality when using the QCC class for H4+2 """ # Build the QCC ansatz, which sets the QMF parameters automatically if none are passed qcc_op_list = [QubitOperator("X0 Y2"), QubitOperator("Y0 X4"), QubitOperator("X0 Y6"), @@ -187,7 +187,7 @@ def test_qcc_h4_double_cation(self): self.assertAlmostEqual(energy, -0.8547019, delta=1e-6) def test_qmf_qcc_h4_double_cation(self): - """ Verify restricted open-shell functionality when using QMF + QCC ansatze for H4 2+ """ + """ Verify restricted open-shell functionality when using QMF + QCC ansatze for H4+2 """ # Build the QMF ansatz with optimized parameters qmf_var_params = [3.14159247e+00, 3.14158884e+00, 1.37660700e-06, 3.14159264e+00, diff --git a/tangelo/toolboxes/ansatz_generator/tests/test_qmf.py b/tangelo/toolboxes/ansatz_generator/tests/test_qmf.py index 3da5229a2..4d9c08726 100644 --- a/tangelo/toolboxes/ansatz_generator/tests/test_qmf.py +++ b/tangelo/toolboxes/ansatz_generator/tests/test_qmf.py @@ -15,6 +15,7 @@ """Unit tests for closed-shell and restricted open-shell qubit mean field (QMF) ansatz. """ import unittest + import numpy as np from tangelo.linq import Simulator From a7a4e9b3976ba866aea601f0ce351130297f3625 Mon Sep 17 00:00:00 2001 From: Marc Coons Date: Thu, 5 May 2022 12:13:28 -0400 Subject: [PATCH 26/28] last round of revisions to ilc_ansatz branch before merge --- tangelo/toolboxes/ansatz_generator/_qubit_ilc.py | 1 + 1 file changed, 1 insertion(+) diff --git a/tangelo/toolboxes/ansatz_generator/_qubit_ilc.py b/tangelo/toolboxes/ansatz_generator/_qubit_ilc.py index 3a2cc617d..d9543f561 100644 --- a/tangelo/toolboxes/ansatz_generator/_qubit_ilc.py +++ b/tangelo/toolboxes/ansatz_generator/_qubit_ilc.py @@ -184,6 +184,7 @@ def gauss_elim_over_gf2(a_mat, b_vec=None): warnings.warn("Gaussian elimination over GF(2) failed to find a solution.", RuntimeWarning) return np.array(z_sln) + def get_ilc_params_by_diag(qubit_ham, ilc_gens, qmf_var_params): """Driver function that solves the generalized eigenvalue problem Hc = ESc required to obtain the ground state coefficients (ILC parameters). These are subsequently recast From cc004ffdd9dc39bf0e6691b6581166b1a291606f Mon Sep 17 00:00:00 2001 From: ValentinS4t1qbit <41597680+ValentinS4t1qbit@users.noreply.github.com> Date: Tue, 10 May 2022 09:57:09 -0700 Subject: [PATCH 27/28] Update vqe_solver.py --- tangelo/algorithms/variational/vqe_solver.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tangelo/algorithms/variational/vqe_solver.py b/tangelo/algorithms/variational/vqe_solver.py index b7d6c3b5c..33a80174a 100644 --- a/tangelo/algorithms/variational/vqe_solver.py +++ b/tangelo/algorithms/variational/vqe_solver.py @@ -29,8 +29,8 @@ from tangelo.toolboxes.operators import count_qubits, FermionOperator, qubitop_to_qubitham from tangelo.toolboxes.qubit_mappings.mapping_transform import fermion_to_qubit_mapping from tangelo.toolboxes.ansatz_generator.ansatz import Ansatz -from tangelo.toolboxes.ansatz_generator import UCCSD, RUCC, HEA, UpCCGSD, QMF, QCC, VSQS, UCCGD,\ - VariationalCircuitAnsatz, ILC +from tangelo.toolboxes.ansatz_generator import UCCSD, RUCC, HEA, UpCCGSD, QMF, QCC, VSQS, UCCGD, ILC, + VariationalCircuitAnsatz from tangelo.toolboxes.ansatz_generator.penalty_terms import combined_penalty from tangelo.toolboxes.post_processing.bootstrapping import get_resampled_frequencies from tangelo.toolboxes.ansatz_generator.fermionic_operators import number_operator, spinz_operator, spin2_operator From 6dfd71915869f23feb0671c2f01b93ad0993d839 Mon Sep 17 00:00:00 2001 From: ValentinS4t1qbit <41597680+ValentinS4t1qbit@users.noreply.github.com> Date: Tue, 10 May 2022 09:59:29 -0700 Subject: [PATCH 28/28] Update vqe_solver.py --- tangelo/algorithms/variational/vqe_solver.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tangelo/algorithms/variational/vqe_solver.py b/tangelo/algorithms/variational/vqe_solver.py index 33a80174a..7c3519c93 100644 --- a/tangelo/algorithms/variational/vqe_solver.py +++ b/tangelo/algorithms/variational/vqe_solver.py @@ -29,7 +29,7 @@ from tangelo.toolboxes.operators import count_qubits, FermionOperator, qubitop_to_qubitham from tangelo.toolboxes.qubit_mappings.mapping_transform import fermion_to_qubit_mapping from tangelo.toolboxes.ansatz_generator.ansatz import Ansatz -from tangelo.toolboxes.ansatz_generator import UCCSD, RUCC, HEA, UpCCGSD, QMF, QCC, VSQS, UCCGD, ILC, +from tangelo.toolboxes.ansatz_generator import UCCSD, RUCC, HEA, UpCCGSD, QMF, QCC, VSQS, UCCGD, ILC,\ VariationalCircuitAnsatz from tangelo.toolboxes.ansatz_generator.penalty_terms import combined_penalty from tangelo.toolboxes.post_processing.bootstrapping import get_resampled_frequencies