Ejemplo n.º 1
0
    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
Ejemplo n.º 2
0
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)
Ejemplo n.º 3
0
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))
Ejemplo n.º 4
0
 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)
Ejemplo n.º 5
0
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)
Ejemplo n.º 6
0
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
Ejemplo n.º 7
0
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))
Ejemplo n.º 8
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)
Ejemplo n.º 9
0
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
Ejemplo n.º 10
0
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)
Ejemplo n.º 11
0
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)
Ejemplo n.º 12
0
def Zero(*args, **kwargs) -> QubitHamiltonian:
    """
    Initialize 0 Operator

    Returns
    -------
    QubitHamiltonian

    """
    return QubitHamiltonian.zero()
Ejemplo n.º 13
0
def I(*args, **kwargs) -> QubitHamiltonian:
    """
    Initialize unit Operator

    Returns
    -------
    QubitHamiltonian

    """
    return QubitHamiltonian.unit()
Ejemplo n.º 14
0
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)
Ejemplo n.º 15
0
 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()
Ejemplo n.º 16
0
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
Ejemplo n.º 17
0
    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))
Ejemplo n.º 18
0
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)
Ejemplo n.º 19
0
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
Ejemplo n.º 20
0
 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)
Ejemplo n.º 21
0
    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]
Ejemplo n.º 22
0
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
Ejemplo n.º 23
0
 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
Ejemplo n.º 24
0
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)