Ejemplo n.º 1
0
def test_construct_operators():
    prods = [i.walked() for i in L * L]
    ans = [
        [L("u0 i0") * L("u1 i1")],
        [L("u0 i0") * L("u1 i1") * eps("-i0 -i1")],
        [L("u0 i0") * L("u1 i1") * eps("-u0 -u1")],
        [L("u0 i0") * L("u1 i1") * eps("-u0 -u1") * eps("-i0 -i1")],
    ]

    # need to define a better operator equality to test this better
    assert len([construct_operators(i) for i in prods]) == len(ans)
Ejemplo n.º 2
0
def test_extract_relabellings():
    a = [eps("-i0 -i2"), eps("-i3 -i1")]
    b = [eps("-i3 -i2"), eps("-i2 -i1")]
    # relabellings action on a
    #    0 -> 2, 2 -> 3, 3 -> 2
    #    1 -> 2, 0 -> 1
    relabellings1 = [
        (Index("-i0"), Index("-i2")),
        (Index("-i2"), Index("-i3")),
        (Index("-i3"), Index("-i2")),
    ]
    relabellings2 = [(Index("-i1"), Index("-i2")),
                     (Index("-i0"), Index("-i1"))]
    assert extract_relabellings(a, b) == [relabellings1, relabellings2]
Ejemplo n.º 3
0
def contract_su2_helper(
    left: Indexed, right: Indexed, out: Union[int, str], index_type: str
):
    """Returns epsilon structures so that `left * right * epsilon` for each
    structure transforms as `out`.

    Example:
        >>> contract_su2_helper(left=L("u0 i0"), right=L("u1 i1"), out=0, index_type="i")
        [eps(-i0, -i1)]

    """
    # create an index dict mapping index type to position in dynkin str
    index_dict = {}
    for pos, (_, label) in enumerate(Index.get_dynkin_labels()):
        index_dict[label] = pos

    # if out is a string, take to be dynkin string and extract appropriate
    # dynkin label for index type
    if isinstance(out, int):
        n_target_indices = out
    else:
        n_target_indices = int(out[index_dict[index_type]])

    n_input_indices = (
        left.dynkin_ints[index_dict[index_type]]
        + right.dynkin_ints[index_dict[index_type]]
    )

    assert n_target_indices <= n_input_indices
    assert (n_target_indices - n_input_indices) % 2 == 0

    # if target indices == input indices, no need for epsilons
    if n_target_indices == n_input_indices:
        return [1]

    # get indices of index_type from tensors
    left_indices = left.indices_by_type[Index.get_index_types()[index_type]]
    right_indices = right.indices_by_type[Index.get_index_types()[index_type]]

    # There is in general more than one way to contract the indices.
    # Construct every possible way and return a list of results.

    # get different epsilon combinations
    shortest, longest = sorted([left_indices, right_indices], key=len)
    eps_index_combos = [tuple(zip(perm, shortest)) for perm in permutations(longest)]

    results = []
    for combo in eps_index_combos:
        prod = 1
        counter = n_input_indices
        for i, j in combo:
            prod *= eps(" ".join([(-i).label, (-j).label]))
            counter -= 2
            if counter == n_target_indices:
                results.append(prod)

    return results
Ejemplo n.º 4
0
def colour_singlets(operators: List[Operator], overcomplete=False):
    """Contracts colour indices into singlets."""
    result = []
    for op in operators:
        # collect colour indices
        colour_indices = []
        for i in op.free_indices:
            if i.index_type == Index.get_index_types()["c"]:
                colour_indices.append(i)

        # separate up and down indices
        ups, downs = [], []
        for i in colour_indices:
            if i.is_up:
                ups.append(i)
            else:
                downs.append(i)

        # can only contract into singlet with deltas if equal number of raised
        # and lowered colour indices. Otherwise need to contract with epsilons ΔL
        # = 2 EFT only contains 2 and 4 quark operators up to dim 11, so no need
        # to implement contraction with epsilons yet. Make exception for simple
        # contraction with epsilon

        if len(ups) == 3 and not downs:
            result.append(op * eps(" ".join(str(-i) for i in ups)))
        elif len(downs) == 3 and not ups:
            result.append(op * eps(" ".join(str(-i) for i in downs)))
        elif len(downs) == 3 and len(ups) == 3:
            result.append(
                op
                * eps(" ".join(str(-i) for i in ups))
                * eps(" ".join(str(-i) for i in downs))
            )
        elif len(ups) != len(downs):
            raise ValueError("Cannot contract colour indices into a singlet.")

        delta_index_combos = [tuple(zip(perm, downs)) for perm in permutations(ups)]
        for combo in delta_index_combos:
            deltas = [delta(" ".join([(-j).label, (-i).label])) for i, j in combo]

            # multiply deltas into operator
            prod = op
            for d in deltas:
                prod *= d
            result.append(prod)

        if len(deltas) == 2 and overcomplete:
            a, b = deltas
            upa, downa = a.indices
            upb, downb = b.indices
            dummy = Index.fresh("c")
            up_index_str = " ".join(str(i) for i in (upa, upb, dummy))
            down_index_str = " ".join(str(i) for i in (downa, downb, -dummy))
            up_epsilon = eps(up_index_str)
            down_epsilon = eps(down_index_str)
            # append additional structure to results
            result.append(op * up_epsilon * down_epsilon)

    return result
Ejemplo n.º 5
0
def get_lorentz_epsilons(fields: Tuple[IndexedField]) -> Tuple[bool, List[Tensor]]:
    """Takes a list of two or three fields (possibly with derivatives) and returns
    the lorentz epsilons that contract the fields to as low a Lorentz irrep as
    possible as well as a boolean indicating whether the contraction is allowed.

    """

    deriv_structure = [f.derivs for f in fields]
    n_derivs = sum(deriv_structure)

    if n_derivs > 2:
        raise Exception(
            f"Not currently supporting {n_derivs} derivatives in an operator."
        )

    if not n_derivs and len(fields) == 4:
        return True, []

    if not n_derivs and len(fields) == 3:
        if all_scalars(fields):
            return True, []

        elif all_fermions(fields):
            return False, []

        return get_lorentz_epsilons(drop_scalar(fields))

    if n_derivs == 2 and len(fields) == 3:
        fields = sorted(fields, key=lambda f: -f.derivs)

    prod = reduce(lambda x, y: x * y, fields)
    undotted, dotted, _, _, _, = prod.indices_by_type.values()

    # Reject vector contraction
    if len(undotted) == 1 and len(dotted) == 1:
        return False, []

    epsilons = []
    for indices in [undotted, dotted]:
        # skip single indices (fermion, scalar) contraction
        if len(indices) == 1:
            continue

        # pair up all even indices; if odd, leave last index
        if len(indices) % 2 != 0:
            indices.pop(-1)

        for i, j in chunks(indices, 2):
            epsilons.append(eps(f"-{i} -{j}"))

    return True, epsilons
Ejemplo n.º 6
0
    def lower_su2(self, skip=[]):
        undotted, dotted, _, isospin, _ = self.indices_by_type.values()
        epsilons = []
        partner = self
        for idx in [*undotted, *dotted, *isospin]:
            if idx.index_type in skip:
                continue
            lower = str(idx) + "^"
            partner = partner.substituted_indices((idx, lower))
            epsilon = eps("-" + lower + " -" + str(idx))
            epsilons.append(epsilon)

        # For VectorLikeDiracFermion, keep track of (un)barred boolean
        is_unbarred = None
        if hasattr(self, "is_unbarred"):
            is_unbarred = self.is_unbarred

        return cons_completion_field(partner,
                                     is_unbarred=is_unbarred), epsilons
Ejemplo n.º 7
0
def su2_singlets_type(op: Operator, index_type: str) -> List[Operator]:
    """Contracts su2 indices into singlets by type."""
    result = []
    # collect undotted indices
    indices = []
    for i in op.free_indices:
        if i.index_type == Index.get_index_types()[index_type]:
            indices.append(i)

    # can only contract into singlet if even number of indices
    if len(indices) % 2:
        raise ValueError("Cannot contract odd number of indices into a singlet.")

    for combo in epsilon_combos(indices):
        epsilons = [eps(" ".join([(-i).label, (-j).label])) for i, j in combo]

        # multiply epsilons into operator
        prod = op
        for e in epsilons:
            prod *= e
        result.append(prod)

    return result
Ejemplo n.º 8
0
def process_derivative_term(op: Operator) -> Union[Operator, str]:
    """Process term containing derivatives, return corresponding term that would
    appear in the Lagrangian.

    """
    deriv_structure = [f.derivs for f in op.fields]
    n_derivs = sum(deriv_structure)

    if n_derivs == 0:
        return op

    # Remove derivatives and lorentz epsilons and call contract_su2 on Lorentz
    # structure.
    #
    # There are a number of cases here
    # 1. (DH)(DH)SS
    # 2. (DH)(DH)S
    # 3. (DH)ψF -> in all but this case, affected fields are clear
    # 4. S(Dψ)F
    # 5. (Dψ)(Dψ)S
    # 6. S(Dψ)ψ
    scalars, fermions, exotic_fermions, epsilons = [], [], [], []
    for t in op.tensors:
        if isinstance(t, IndexedField) and t.derivs > 0 and t.is_boson:
            no_deriv_field = t.strip_derivs_with_indices()
            scalars.append(no_deriv_field)
        if isinstance(t, IndexedField) and t.derivs > 0 and t.is_fermion:
            no_deriv_field = t.strip_derivs_with_indices()
            fermions.append(no_deriv_field)
        elif isinstance(t, VectorLikeDiracFermion):
            fixed_field = t.dirac_partner().conj
            exotic_fermions.append(fixed_field)
        elif isinstance(t, MajoranaFermion):
            fixed_field = t.conj
            exotic_fermions.append(fixed_field)
        elif isinstance(t, IndexedField) and t.is_fermion:
            fermions.append(t)
        elif isinstance(t, IndexedField) and t.is_scalar:
            scalars.append(t)
        elif not isinstance(t, IndexedField):
            # is epsilon, keep gauge ones, not lorentz
            if t.indices[0].index_type in ("Undotted", "Dotted"):
                continue
            else:
                epsilons.append(t)

    # cases 1 and 2
    if len(scalars) > 2:
        term = reduce(lambda x, y: x * y, scalars + epsilons)
        if term.safe_simplify() == 0:
            return "Vanishing structure"
        return term
    # case 6
    if len(fermions) == 2 and n_derivs == 1:
        return "Not allowed contraction"
    # case 5
    if len(fermions) == 2 and n_derivs == 2:
        left, right = fermions
    # cases 3 and 4
    if len(exotic_fermions) == 1:
        assert len(fermions) == 1
        left, right = exotic_fermions[0], fermions[0]
    if len(exotic_fermions) == 2:
        left, right = exotic_fermions

    # include scalars and epsilons in su2 contraction
    right = reduce(lambda x, y: x * y, scalars + epsilons, right)
    lu, ld, _, _, _ = left.indices_by_type.values()
    ru, rd, _, _, _ = right.indices_by_type.values()

    # if the indices are equal after taking the conj, then there will be an
    # error. In this case, you can just lower one of them
    if lu == ru and ld == rd:
        partner, fix_su2_epsilons = left.lower_su2(skip=["Isospin"])
        assert len(fix_su2_epsilons) == 1
        return right * partner * fix_su2_epsilons[0]
    if lu:
        if not (len(lu) == 1 and len(ru) == 1):
            return "Not allowed contraction"
        index_str = " ".join(str(-i) for i in lu + ru)
    else:
        if not (len(ld) == 1 and len(rd) == 1):
            return "Not allowed contraction"
        index_str = " ".join(str(-i) for i in ld + rd)

    return right * left * eps(index_str)
Ejemplo n.º 9
0
    eb.conj("d1"),
    ub.conj("d2 c0"),
    ub.conj("d3 c1"),
    db("u0 -c2"),
    db("u1 -c3"),
]
o82 = [
    L("u0 i0"),
    L.conj("d0 i1"),
    eb.conj("d1"),
    eb.conj("d2"),
    ub.conj("d3 c0"),
    db("u1 -c1"),
    H("i2"),
    H("i3"),
    eps("-i0 -i2"),
    eps("-i1 -i3"),
]
oyec = [L.conj("d3 i6"), eb.conj("d4"), H("i7"), eps("-i6 -i7")]
oydc = [Q.conj("d3 -c0 i6"), db.conj("d4 c1"), H("i7"), eps("-i6 -i7")]
prime = [H("i4"), H.conj("i5"), eps("-i4 -i5")]
prime_prime = [
    H("i4"),
    H.conj("i5"),
    H("i6"),
    H.conj("i7"),
    eps("-i6 -i7"),
    eps("-i4 -i5"),
]
prime_prime_prime = [
    H("i4"),