def _reduce_degree(bqm, poly, vartype, scale):
    """helper function for make_quadratic"""

    if all(len(term) <= 2 for term in poly):
        # termination criteria, we are already quadratic
        bqm.add_interactions_from(poly)
        return bqm

    # determine which pair of variables appear most often
    paircounter = Counter()
    for term in poly:
        if len(term) > 2:
            for u, v in itertools.combinations(term, 2):
                pair = frozenset((u, v))
                paircounter[pair] += 1

    pair, __ = paircounter.most_common(1)[0]
    u, v = pair

    # make a new product variable and aux variable and add constraint that u*v == p
    p = '{}*{}'.format(u, v)

    while p in bqm.linear:
        p = '_' + p

    if vartype is Vartype.BINARY:
        constraint = _binary_product([u, v, p])

        bqm.info['reduction'][(u, v)] = {'product': p}
    else:
        aux = 'aux{},{}'.format(u, v)
        while aux in bqm.linear:
            aux = '_' + aux
        constraint = _spin_product([u, v, p, aux])

        bqm.info['reduction'][(u, v)] = {'product': p, 'auxiliary': aux}

    constraint.scale(scale)
    bqm.update(constraint)

    new_poly = {}
    for interaction, bias in poly.items():
        if u in interaction and v in interaction:

            if len(interaction) == 2:
                # in this case we are reducing a quadratic bias, so it becomes linear and can
                # be removed
                assert len(interaction) >= 2
                bqm.add_variable(p, bias)
                continue

            interaction = tuple(s for s in interaction if s not in pair)
            interaction += (p,)

        if interaction in new_poly:
            new_poly[interaction] += bias
        else:
            new_poly[interaction] = bias

    return _reduce_degree(bqm, new_poly, vartype, scale)
helper function for make_quadratic