def make_excitation_generator( self, indices: typing.Iterable[typing.Tuple[int, int]]) -> QubitHamiltonian: """ Notes ---------- Creates the transformed hermitian generator of UCC type unitaries: M(a^\dagger_{a_0} a_{i_0} a^\dagger{a_1}a_{i_1} ... - h.c.) where the qubit map M depends is self.transformation Parameters ---------- indices : typing.Iterable[typing.Tuple[int, int]] : List of tuples [(a_0, i_0), (a_1, i_1), ... ] - recommended format, in spin-orbital notation (alpha odd numbers, beta even numbers) can also be given as one big list: [a_0, i_0, a_1, i_1 ...] Returns ------- type 1j*Transformed qubit excitation operator, depends on self.transformation """ # check indices and convert to list of tuples if necessary if len(indices) == 0: raise TequilaException( "make_excitation_operator: no indices given") elif not isinstance(indices[0], typing.Iterable): if len(indices) % 2 != 0: raise TequilaException( "make_excitation_generator: unexpected input format of indices\n" "use list of tuples as [(a_0, i_0),(a_1, i_1) ...]\n" "or list as [a_0, i_0, a_1, i_1, ... ]\n" "you gave: {}".format(indices)) converted = [(indices[2 * i], indices[2 * i + 1]) for i in range(len(indices) // 2)] else: converted = indices # convert to openfermion input format ofi = [] dag = [] for pair in converted: assert (len(pair) == 2) ofi += [ (int(pair[0]), 1), (int(pair[1]), 0) ] # openfermion does not take other types of integers like numpy.int64 dag += [(int(pair[0]), 0), (int(pair[1]), 1)] op = openfermion.FermionOperator(tuple(ofi), 1.j) # 1j makes it hermitian op += openfermion.FermionOperator(tuple(reversed(dag)), -1.j) qop = QubitHamiltonian(qubit_hamiltonian=self.transformation(op)) # check if the operator is hermitian and cast coefficients to floats # in order to avoid trouble with the simulation backends assert qop.is_hermitian() for k, v in qop.qubit_operator.terms.items(): qop.qubit_operator.terms[k] = to_float(v) qop = qop.simplify() return qop
def test_paulistring_conversion(): X1 = QubitHamiltonian.from_string("X0", openfermion_format=True) X2 = paulis.X(0) keys = [i for i in X2.keys()] pwx = PauliString.from_openfermion(key=keys[0], coeff=X2[keys[0]]) X3 = QubitHamiltonian.from_paulistrings(pwx) assert (X1 == X2) assert (X2 == X3) H = paulis.X(0) * paulis.Y(1) * paulis.Z(2) + paulis.X(3) * paulis.Y( 4) * paulis.Z(5) PS = [] for key, value in H.items(): PS.append(PauliString.from_openfermion(key, value)) PS2 = H.paulistrings assert (PS == PS2) H = make_random_pauliword(complex=True) for i in range(5): H += make_random_pauliword(complex=True) PS = [] for key, value in H.items(): PS.append(PauliString.from_openfermion(key, value)) PS2 = H.paulistrings assert (PS == PS2)
def test_simple_arithmetic(): qubit = random.randint(0, 5) primitives = [paulis.X, paulis.Y, paulis.Z] assert (paulis.X(qubit).conjugate() == paulis.X(qubit)) assert (paulis.Y(qubit).conjugate() == -1 * paulis.Y(qubit)) assert (paulis.Z(qubit).conjugate() == paulis.Z(qubit)) assert (paulis.X(qubit).transpose() == paulis.X(qubit)) assert (paulis.Y(qubit).transpose() == -1 * paulis.Y(qubit)) assert (paulis.Z(qubit).transpose() == paulis.Z(qubit)) for P in primitives: assert (P(qubit) * P(qubit) == QubitHamiltonian(1.0)) n = random.randint(0, 10) nP = QubitHamiltonian.zero() for i in range(n): nP += P(qubit) assert (n * P(qubit) == nP) for i, Pi in enumerate(primitives): i1 = (i + 1) % 3 i2 = (i + 2) % 3 assert (Pi(qubit) * primitives[i1](qubit) == 1j * primitives[i2](qubit)) assert (primitives[i1](qubit) * Pi(qubit) == -1j * primitives[i2](qubit)) for qubit2 in random.randint(6, 10, 5): if qubit2 == qubit: continue P = primitives[random.randint(0, 2)] assert (Pi(qubit) * primitives[i1](qubit) * P(qubit2) == 1j * primitives[i2](qubit) * P(qubit2)) assert (P(qubit2) * primitives[i1](qubit) * Pi(qubit) == -1j * P(qubit2) * primitives[i2](qubit))
def pair_unitary(a, b): ''' Accepts a BinaryPauliString. Return the paired unitary 1/sqrt(2) (a + b) in qubit hamiltonian. ''' a = QubitHamiltonian.from_paulistrings(a.to_pauli_strings()) b = QubitHamiltonian.from_paulistrings(b.to_pauli_strings()) return (1 / 2) ** (1 / 2) * (a + b)
def test_trace_out_xy(theta): a = numpy.sin(theta) b = numpy.cos(theta) state = QubitWaveFunction.from_array([a,b]) H1 = QubitHamiltonian.from_string("1.0*X(0)*X(1)*X(100)") H2 = QubitHamiltonian.from_string("1.0*X(0)*X(100)") factor = a.conjugate()*b + b.conjugate()*a assert factor*H2 == H1.trace_out_qubits(qubits=[1,3,5], states=[state]*3) factor *= factor H1 = QubitHamiltonian.from_string("1.0*X(0)*X(1)*X(5)*X(100)") assert factor*H2 == H1.trace_out_qubits(qubits=[1,3,5], states=[state]*3) H1 = QubitHamiltonian.from_string("1.0*X(0)*Y(1)*X(100)") H2 = QubitHamiltonian.from_string("1.0*X(0)*X(100)") factor = -1.0j*(a.conjugate()*b - b.conjugate()*a) assert factor*H2 == H1.trace_out_qubits(qubits=[1,3,5], states=[state]*3) factor *= factor H1 = QubitHamiltonian.from_string("1.0*X(0)*Y(1)*Y(5)*X(100)") assert factor*H2 == H1.trace_out_qubits(qubits=[1,3,5], states=[state]*3) H1 = QubitHamiltonian.from_string("1.0*X(0)*X(1)*X(100)") H2 = QubitHamiltonian.from_string("1.0*X(0)*X(100)") factor = a.conjugate()*b + b.conjugate()*a assert factor*H2 == H1.trace_out_qubits(qubits=[1,3,5], states=[state]*3) factor *= -1.0j*(a.conjugate()*b - b.conjugate()*a) H1 = QubitHamiltonian.from_string("1.0*X(0)*X(1)*Y(5)*X(100)") assert factor*H2 == H1.trace_out_qubits(qubits=[1,3,5], states=[state]*3)
def test_initialization(): H = paulis.I() for i in range(10): H += paulis.pauli(qubit=numpy.random.randint(0,5,3), type=numpy.random.choice(["X", "Y", "Z"],1)) for H1 in [H, paulis.I(), paulis.Zero(), paulis.X(0), paulis.Y(1), 1.234*paulis.Z(2)]: string = str(H1) ofstring = str(H1.to_openfermion()) H2 = QubitHamiltonian.from_string(string=string) assert H1 == H2 H3 = QubitHamiltonian.from_string(string=ofstring, openfermion_format=True) assert H1 == H3
def test_special_operators(): # sigma+ sigma- as well as Q+ and Q- assert (paulis.Sp(0) * paulis.Sp(0) == QubitHamiltonian.zero()) assert (paulis.Sm(0) * paulis.Sm(0) == QubitHamiltonian.zero()) assert (paulis.Qp(0) * paulis.Qp(0) == paulis.Qp(0)) assert (paulis.Qm(0) * paulis.Qm(0) == paulis.Qm(0)) assert (paulis.Qp(0) * paulis.Qm(0) == QubitHamiltonian.zero()) assert (paulis.Qm(0) * paulis.Qp(0) == QubitHamiltonian.zero()) assert (paulis.Sp(0) * paulis.Sm(0) == paulis.Qp(0)) assert (paulis.Sm(0) * paulis.Sp(0) == paulis.Qm(0)) assert (paulis.Sp(0) + paulis.Sm(0) == paulis.X(0)) assert (paulis.Qp(0) + paulis.Qm(0) == paulis.I(0))
def pauli(qubit, type) -> QubitHamiltonian: """ Parameters ---------- qubit: int or list of ints type: str or int or list of string or int: define if X, Y or Z (0,1,2) Returns ------- QubitHamiltonian """ def assign_axis(axis): if axis in QubitHamiltonian.axis_to_string: return QubitHamiltonian.axis_to_string[axis] elif hasattr(axis, "upper"): return axis.upper() else: raise TequilaException( "unknown initialization for pauli operator: {}".format(axis)) if not isinstance(qubit, typing.Iterable): qubit = [qubit] type = [type] type = [assign_axis(x) for x in type] init_string = "".join("{}{} ".format(t, q) for t, q in zip(type, qubit)) return QubitHamiltonian.from_string(string=init_string, openfermion_format=True)
def decompose_transfer_operator( ket: BitString, bra: BitString, qubits: typing.List[int] = None) -> QubitHamiltonian: """ Notes ---------- Create the operator Note that this is operator is not necessarily hermitian So be careful when using it as a generator for gates e.g. decompose_transfer_operator(ket="01", bra="10", qubits=[2,3]) gives the operator .. math:: \\lvert 01 \\rangle \\langle 10 \\rvert_{2,3} acting on qubits 2 and 3 Parameters ---------- ket: pass an integer, string, or tequila BitString bra: pass an integer, string, or tequila BitString qubits: pass the qubits onto which the operator acts Returns ------- """ opmap = {(0, 0): Qp, (0, 1): Sp, (1, 0): Sm, (1, 1): Qm} nbits = None if qubits is not None: nbits = len(qubits) if isinstance(bra, int): bra = BitString.from_int(integer=bra, nbits=nbits) if isinstance(ket, int): ket = BitString.from_int(integer=ket, nbits=nbits) b_arr = bra.array k_arr = ket.array assert (len(b_arr) == len(k_arr)) n_qubits = len(k_arr) if qubits is None: qubits = range(n_qubits) assert (n_qubits <= len(qubits)) result = QubitHamiltonian.unit() for q, b in enumerate(b_arr): k = k_arr[q] result *= opmap[(k, b)](qubit=qubits[q]) return result
def test_conjugation(): primitives = [paulis.X, paulis.Y, paulis.Z] factors = [1, -1, 1j, -1j, 0.5 + 1j] string = QubitHamiltonian.unit() cstring = QubitHamiltonian.unit() for repeat in range(10): for q in random.randint(0, 7, 5): ri = random.randint(0, 2) P = primitives[ri] sign = 1 if ri == 1: sign = -1 factor = factors[random.randint(0, len(factors) - 1)] cfactor = factor.conjugate() string *= factor * P(qubit=q) cstring *= cfactor * sign * P(qubit=q) assert (string.conjugate() == cstring)
def test_convenience(): i = numpy.random.randint(0, 10, 1)[0] assert paulis.X(i) + paulis.I(i) == paulis.X(i) + 1.0 assert paulis.Qp(i) == 0.5 * (1.0 + paulis.Z(i)) assert paulis.Qm(i) == 0.5 * (1.0 - paulis.Z(i)) assert paulis.Sp(i) == 0.5 * (paulis.X(i) + 1.j * paulis.Y(i)) assert paulis.Sm(i) == 0.5 * (paulis.X(i) - 1.j * paulis.Y(i)) i = numpy.random.randint(0, 10, 1)[0] assert paulis.Qp(i) == (0.5 + 0.5 * paulis.Z(i)) assert paulis.Qm(i) == (0.5 - 0.5 * paulis.Z(i)) assert paulis.Sp(i) == (0.5 * paulis.X(i) + 0.5j * paulis.Y(i)) assert paulis.Sm(i) == (0.5 * paulis.X(i) - 0.5j * paulis.Y(i)) assert -1.0 * paulis.Y(i) == -paulis.Y(i) test = paulis.Z(i) test *= -1.0 assert test == -paulis.Z(i) test = paulis.Z(i) test += 1.0 assert test == paulis.Z(i) + 1.0 test = paulis.X(i) test += paulis.Y(i + 1) assert test == paulis.X(i) + paulis.Y(i + 1) test = paulis.X(i) test -= paulis.Y(i) test += 3.0 test = -test assert test == -1.0 * (paulis.X(i) - paulis.Y(i) + 3.0) test = paulis.X([0, 1, 2, 3]) assert test == QubitHamiltonian.from_string("X(0)X(1)X(2)X(3)", False) test = paulis.Y([0, 1, 2, 3]) assert test == QubitHamiltonian.from_string("Y(0)Y(1)Y(2)Y(3)", False) test = paulis.Z([0, 1, 2, 3]) assert test == QubitHamiltonian.from_string("Z(0)Z(1)Z(2)Z(3)", False)
def Zero(*args, **kwargs) -> QubitHamiltonian: """ Initialize 0 Operator Returns ------- QubitHamiltonian """ return QubitHamiltonian.zero()
def I(*args, **kwargs) -> QubitHamiltonian: """ Initialize unit Operator Returns ------- QubitHamiltonian """ return QubitHamiltonian.unit()
def test_transposition(): primitives = [paulis.X, paulis.Y, paulis.Z] factors = [1, -1, 1j, -1j, 0.5 + 1j] assert ((paulis.X(0) * paulis.X(1) * paulis.Y(2)).transpose() == -1 * paulis.X(0) * paulis.X(1) * paulis.Y(2)) assert ((paulis.X(0) * paulis.X(1) * paulis.Z(2)).transpose() == paulis.X(0) * paulis.X(1) * paulis.Z(2)) for repeat in range(10): string = QubitHamiltonian.unit() tstring = QubitHamiltonian.unit() for q in range(5): ri = random.randint(0, 2) P = primitives[ri] sign = 1 if ri == 1: sign = -1 factor = factors[random.randint(0, len(factors) - 1)] string *= factor * P(qubit=q) tstring *= factor * sign * P(qubit=q) assert (string.transpose() == tstring)
def __init__(self, paulistring: PauliString, angle: float, control: typing.List[int] = None): super().__init__(eigenvalues_magnitude=0.5, name="Exp-Pauli", target=tuple(t for t in paulistring.keys()), control=control, parameter=angle) self.paulistring = paulistring self.generator = QubitHamiltonian.from_paulistrings(paulistring) self.finalize()
def make_random_pauliword(complex=True): primitives = [paulis.X, paulis.Y, paulis.Z] result = QubitHamiltonian.unit() for q in random.choice(range(10), 5, replace=False): P = primitives[random.randint(0, 2)] real = random.uniform(0, 1) imag = 0 if complex: imag = random.uniform(0, 1) factor = real + imag * 1j result *= factor * P(q) return result
def make_hamiltonian(self, occupied_indices=None, active_indices=None) -> QubitHamiltonian: """ """ if occupied_indices is None and self.active_space is not None: occupied_indices = self.active_space.frozen_reference_orbitals if active_indices is None and self.active_space is not None: active_indices = self.active_space.active_orbitals fop = openfermion.transforms.get_fermion_operator( self.molecule.get_molecular_hamiltonian(occupied_indices, active_indices)) return QubitHamiltonian(qubit_hamiltonian=self.transformation(fop))
def KetBra(ket: QubitWaveFunction, bra: QubitWaveFunction, hermitian: bool = False, threshold: float = 1.e-6, n_qubits=None): """ Notes ---------- Initialize the general KetBra operator .. math:: H = \\lvert ket \\rangle \\langle bra \\rvert e.g. wfn1 = tq.QubitWaveFunction.from_string("1.0*|00> + 1.0*|11>").normalize() wfn2 = tq.QubitWaveFunction.from_string("1.0*|00>") operator = tq.paulis.KetBra(ket=wfn1, bra=wfn1) initializes the transfer operator from the all-zero state to a Bell state Parameters ---------- ket: QubitWaveFunction: QubitWaveFunction which defines the ket element can also be given as string or array or integer bra: QubitWaveFunction: QubitWaveFunction which defines the bra element can also be given as string or array or integer hermitian: bool: (Default False) if True the hermitian version H + H^\dagger is returned threshold: float: (Default 1.e-6) elements smaller than the threshold will be ignored n_qubits: only needed if ket and/or bra are passed down as integers Returns ------- a tequila QubitHamiltonian (not necessarily hermitian) """ H = QubitHamiltonian.zero() ket = QubitWaveFunction(state=ket, n_qubits=n_qubits) bra = QubitWaveFunction(state=bra, n_qubits=n_qubits) for k1, v1 in bra.items(): for k2, v2 in ket.items(): c = v1.conjugate() * v2 if not numpy.isclose(c, 0.0, atol=threshold): H += c * decompose_transfer_operator(bra=k1, ket=k2) if hermitian: return H.split()[0] else: return H.simplify(threshold=threshold)
def brute_force_transformation(H, old_basis, new_basis): def pair_unitary(a, b): ''' Accepts a BinaryPauliString. Return the paired unitary 1/sqrt(2) (a + b) in qubit hamiltonian. ''' a = QubitHamiltonian.from_paulistrings(a.to_pauli_strings()) b = QubitHamiltonian.from_paulistrings(b.to_pauli_strings()) return (1 / 2) ** (1 / 2) * (a + b) U = QubitHamiltonian(1) for i, i_basis in enumerate(old_basis): U *= pair_unitary(i_basis, new_basis[i]) return U * H * U
def __init__(self, name, generator: QubitHamiltonian, target: list, power, control: list = None): if generator is None: assert name is not None and name.upper() in ["X", "Y", "Z"] generator = QubitHamiltonian.from_string("{}({})".format( name.upper(), target)) if name is None: assert generator is not None name = str(generator) super().__init__(name=name, parameter=power * pi, target=target, control=control, generator=generator)
def reference_state(self, reference_orbitals: list = None, n_qubits: int = None) -> BitString: """Does a really lazy workaround ... but it works :return: Hartree-Fock Reference as binary-number Parameters ---------- reference_orbitals: list: give list of doubly occupied orbitals default is None which leads to automatic list of the first n_electron/2 orbitals Returns ------- """ if reference_orbitals is None: reference_orbitals = [i for i in range(self.n_electrons // 2)] spin_orbitals = sorted([2 * i for i in reference_orbitals] + [2 * i + 1 for i in reference_orbitals]) if n_qubits is None: n_qubits = 2 * self.n_orbitals string = "" for i in spin_orbitals: string += str(i) + "^ " fop = openfermion.FermionOperator(string, 1.0) op = QubitHamiltonian(qubit_hamiltonian=self.transformation(fop)) from tequila.wavefunction.qubit_wavefunction import QubitWaveFunction wfn = QubitWaveFunction.from_int(0, n_qubits=n_qubits) wfn = wfn.apply_qubitoperator(operator=op) assert (len(wfn.keys()) == 1) keys = [k for k in wfn.keys()] return keys[-1]
def Projector(wfn, threshold=0.0, n_qubits=None) -> QubitHamiltonian: """ Notes ---------- Initialize a projector given by .. math:: H = \\lvert \\Psi \\rangle \\langle \\Psi \\rvert Parameters ---------- wfn: QubitWaveFunction or int, or string, or array : The wavefunction onto which the projector projects Needs to be passed down as tequilas QubitWaveFunction type See the documentation on how to initialize a QubitWaveFunction from integer, string or array (can also be passed down diretly as one of those types) threshold: float: (Default value = 0.0) neglect small parts of the operator n_qubits: only needed when an integer is given as wavefunction Returns ------- """ wfn = QubitWaveFunction(state=wfn, n_qubits=n_qubits) H = QubitHamiltonian.zero() for k1, v1 in wfn.items(): for k2, v2 in wfn.items(): c = v1.conjugate() * v2 if not numpy.isclose(c, 0.0, atol=threshold): H += c * decompose_transfer_operator(bra=k1, ket=k2) assert (H.is_hermitian()) return H
def to_qubit_hamiltonian(self): qub_ham = QubitHamiltonian() for p in self.binary_terms: qub_ham += QubitHamiltonian.from_paulistrings(p.to_pauli_strings()) return qub_ham
def test_simple_trace_out(): H1 = QubitHamiltonian.from_string("1.0*Z(0)*Z(1)") H2 = QubitHamiltonian.from_string("1.0*Z(0)") assert H2 == H1.trace_out_qubits(qubits=[1], states=None) H1 = QubitHamiltonian.from_string("1.0*Z(0)*Z(1)X(100)") H2 = QubitHamiltonian.from_string("1.0*Z(1)X(100)") assert H2 == H1.trace_out_qubits(qubits=[0], states=None) H1 = QubitHamiltonian.from_string("1.0*Z(0)*Z(1)X(100)") H2 = QubitHamiltonian.from_string("-1.0*Z(0)X(100)") assert H2 == H1.trace_out_qubits(qubits=[1], states=[QubitWaveFunction.from_string("1.0*|1>")]) H1 = QubitHamiltonian.from_string("1.0*Z(0)*Z(1)X(100)") H2 = QubitHamiltonian.from_string("-1.0*Z(1)X(100)") assert H2 == H1.trace_out_qubits(qubits=[0], states=[QubitWaveFunction.from_string("1.0*|1>")]) H1 = QubitHamiltonian.from_string("1.0*X(0)*Z(1)*Z(5)*X(100)Y(50)") H2 = QubitHamiltonian.from_string("1.0*X(0)X(100)Y(50)") assert H2 == H1.trace_out_qubits(qubits=[1,5], states=[QubitWaveFunction.from_string("1.0*|1>")]*2) H1 = QubitHamiltonian.from_string("1.0*X(0)*Z(1)*X(100)Y(50)") H2 = QubitHamiltonian.from_string("-1.0*X(0)X(100)Y(50)") assert H2 == H1.trace_out_qubits(qubits=[1,5], states=[QubitWaveFunction.from_string("1.0*|1>")]*2)