def test_factor():

    instrs: List[Instruction] = []
    candidate_instrs: List[Instruction] = []

    mconst = MultConst(debug=True)
    # mconst = MultConst(debug=debug)
    mconst.search_methods = (search_short_factors, )

    n = 27

    s_cost, s_instrs = search_short_factors(
        mconst,
        n,
        upper=20,
        lower=0,
        instrs=instrs,
        candidate_instrs=candidate_instrs,
    )

    check(n, s_cost, s_instrs)
    cost, instrs = mconst.try_shift_op_factor(n, 5, "add", 2, s_cost, 0, [],
                                              s_instrs)
    assert s_cost == cost, "5 is not a factor of 27, so we keep old s_cost"

    for n, factor, shift_amount in ((51, 3, 1), (85, 5, 2)):
        s_cost, s_instrs = binary_method.binary_sequence(mconst, n)
        print_instructions(s_instrs, n, s_cost)
        result = []
        cost, result = mconst.try_shift_op_factor(n, factor, "add",
                                                  shift_amount, s_cost, 0, [],
                                                  s_instrs)
        assert cost < s_cost, f"should use the fact that {factor} is a factor of {n}"
        print_instructions(result, n, cost)
Example #2
0
    def find_mult_sequence(
            self,
            n: int,
            search_methods=None) -> Tuple[float, List[Instruction]]:
        """Top-level searching routine. Computes binary method upper bound
        and then does setup to the alpha-beta search
        """

        cache_lower, limit, finished, cache_instrs = self.mult_cache[n]
        if finished:
            return limit, cache_instrs

        if n < 0 and not self.cpu_model.can_negate():
            raise RuntimeError(
                f"""CPU model "{self.cpu_model.name}" can't handle negative numbers."""
            )

        # FIXME: move elswhere, such as into search_methods.
        if search_methods is None:
            # Note timings show search_cache() *without* binary search is faster on one-shot
            # searches than search_binary_method_with_cache().
            if n > 0:
                if self.cpu_model.can_negate():
                    self.search_methods = (search_cache, search_short_factors,
                                           search_add_one, search_subtract_one)
                else:
                    self.search_methods = (
                        search_cache,
                        search_short_add_factors,
                        search_add_one,
                    )
            else:
                self.search_methods = (
                    search_cache,
                    search_short_factors,
                    search_add_or_subtract_one,
                    search_negate_subtract_one,
                )
                pass
            pass

        if limit == inf_cost:
            # The binary sequence gives a workable upper bound on the cost
            cache_lower, cache_instrs = binary_sequence(self, n)
            self.mult_cache.insert_or_update(n, 0, limit, False, cache_instrs)

        cost, instrs = self.alpha_beta_search(n, 0, limit=limit)
        self.mult_cache.update_field(n,
                                     upper=cost,
                                     finished=True,
                                     instrs=instrs)
        if instrs:
            return cost, instrs
        else:
            return limit, cache_instrs
Example #3
0
def main(to, model, showcache, debug, binary_method, fmt, compact, output, numbers):
    """Searches for short sequences of shift, add, subtract instruction to compute multiplication
    by a constant.
    """
    model = SHORT2MODEL[model]
    mult = MultConst(cpu_model=model, debug=debug)
    if to:
        for number in range(2, to + 1):
            if binary_method:
                cost, instrs = binary_sequence(mult, number)
            else:
                cost, instrs = mult.find_mult_sequence(number)
                pass
            pass
        pass
    else:
        for number in numbers:
            if binary_method:
                cost, instrs = binary_sequence(mult, number)
            else:
                cost, instrs = mult.find_mult_sequence(number)

            print_instructions(instrs, number, cost)
            pass
        pass
    if output or showcache or to:
        if output is None:
            output = sys.stdout
        if fmt == "text":
            dump(mult.mult_cache, out=output)
        elif fmt == "csv":
            dump_csv(mult.mult_cache, out=output)
        elif fmt == "yaml":
            dump_yaml(mult.mult_cache, out=output, compact=compact)
        else:
            assert fmt == "json"
            indent = None if compact else 2
            dump_json(mult.mult_cache, out=output, indent=indent)

    return
def test_negate():
    debug = "DEBUG" in os.environ

    instrs: List[Instruction] = []
    candidate_instrs: List[Instruction] = []

    mconst = MultConst(debug=debug)
    mconst.search_methods = search_negate

    n = 10
    bin_cost, bin_instrs = binary_method.binary_sequence(mconst, n)
    negate_cost = mconst.op_costs["negate"]

    # upper is too low to allow us to get a solution
    scost, s_instrs = search_negate(
        mconst,
        -n,
        upper=bin_cost + negate_cost,
        lower=bin_cost,
        instrs=bin_instrs,
        candidate_instrs=candidate_instrs,
    )

    assert s_instrs == candidate_instrs

    # upper is okay (and exact) to allow us to get a solution
    s_cost, s_instrs = search_negate(
        mconst,
        -n,
        upper=bin_cost + negate_cost + 1,
        lower=bin_cost,
        instrs=bin_instrs,
        candidate_instrs=candidate_instrs,
    )
    assert s_cost == bin_cost + negate_cost
    check(-n, s_cost, s_instrs, debug)

    # Try something not in the cache.
    # Negate will use the binary method
    n = -23
    s_cost, s_instrs = search_negate(
        mconst,
        n,
        upper=inf_cost,
        lower=0,
        instrs=instrs,
        candidate_instrs=candidate_instrs,
    )

    check(n, s_cost, instrs=s_instrs, debug=debug)
Example #5
0
def test_binary_method():
    debug = "DEBUG" in os.environ
    mconst = MultConstClass(debug=debug)
    for (n, expect_cost) in (
        (0, 1),
        (1, 0),
        (-1, 1),
        (2, 1),
        (3, 2),
        (4, 1),
        (5, 2),
        (6, 3),
        (7, 2),
        (-7, 2),
        (-3, 2),
        (8, 1),
        (53, 6),
        (340, 7),
    ):
        cost, result = binary_method.binary_sequence(mconst, n)
        if debug:
            print_instructions(result, n, cost)
        assert expect_cost == cost, f"cost({n}) = {cost}; expected it to be {expect_cost}."
def test_search_subtract_one():
    debug = "DEBUG" in os.environ

    instrs: List[Instruction] = []
    candidate_instrs: List[Instruction] = []

    mconst = MultConst(debug=True)
    # mconst = MultConst(debug=debug)
    mconst.search_methods = (search_subtract_one, )

    n = 12
    bin_cost, bin_instrs = binary_method.binary_sequence(mconst, n)
    subtract_cost = mconst.op_costs["subtract"]

    # upper is too low to allow us to get a solution
    s_cost, s_instrs = search_subtract_one(
        mconst,
        n,
        upper=bin_cost + subtract_cost,
        lower=bin_cost,
        instrs=bin_instrs,
        candidate_instrs=candidate_instrs,
    )

    assert s_instrs == candidate_instrs
    # No check since we did a cutoff.

    # We should start out with n+1, instead when
    # we subtract one, so we should get a cutoff here
    # quickly too
    s_cost, s_instrs = search_subtract_one(
        mconst,
        n,
        upper=bin_cost + subtract_cost + 1,
        lower=bin_cost,
        instrs=bin_instrs,
        candidate_instrs=candidate_instrs,
    )
    assert s_instrs == candidate_instrs
    # No check since we did a cutoff.

    # upper is okay to allow us to get a solution using
    # cached entry 3 added via binary_method.
    instrs = []
    s_cost, s_instrs = search_subtract_one(
        mconst,
        n - 1,
        upper=20,
        lower=0,
        instrs=instrs,
        candidate_instrs=candidate_instrs,
    )
    check(n - 1, s_cost, s_instrs, debug)
    from mult_by_const.io import dump

    dump(mconst.mult_cache)

    # Try something that makes subtract count up for a bit.
    instrs = []
    candidate_instrs = []
    n = 10
    s_cost, s_instrs = search_subtract_one(
        mconst,
        n,
        upper=inf_cost,
        lower=0,
        instrs=instrs,
        candidate_instrs=candidate_instrs,
    )

    check(n, s_cost, instrs=s_instrs, debug=debug)
    dump(mconst.mult_cache)

    # Try something that makes subtract count up for a bit and fail.
    mconst.mult_cache.clear()
    instrs = []
    candidate_instrs = []
    n = 20
    s_cost, s_instrs = search_subtract_one(
        mconst,
        n,
        upper=6,
        lower=0,
        instrs=instrs,
        candidate_instrs=candidate_instrs,
    )

    dump(mconst.mult_cache)
    assert not s_instrs, f"should not find a value for {n} after clearing cache"
    assert (mconst.mult_cache[n + 1][0] >
            0), f"{n} failed but lower should have been updated"

    # Negative numbers!
    instrs = []
    candidate_instrs = []
    n = -3
    s_cost, s_instrs = search_subtract_one(
        mconst,
        n,
        upper=8,
        lower=0,
        instrs=instrs,
        candidate_instrs=candidate_instrs,
    )

    check(n, s_cost, instrs=s_instrs, debug=debug)
    from mult_by_const.io import dump
    dump(mconst.mult_cache)
Example #7
0
def test_search_add_one():

    instrs: List[Instruction] = []
    candidate_instrs: List[Instruction] = []

    mconst = MultConst(debug=True)
    # mconst = MultConst(debug=debug)
    mconst.search_methods = (search_add_one, )

    n = 12
    bin_cost, bin_instrs = binary_method.binary_sequence(mconst, n)
    add_cost = mconst.op_costs["add"]

    # upper is too low to allow us to get a solution
    s_cost, s_instrs = search_add_one(
        mconst,
        n,
        upper=bin_cost + add_cost,
        lower=bin_cost,
        instrs=bin_instrs,
        candidate_instrs=candidate_instrs,
    )

    assert s_instrs == candidate_instrs
    # No check since we did a cutoff.

    # We should start out with n+1, instead when
    # we subtract one, so we should get a cutoff here
    # quickly too
    s_cost, s_instrs = search_add_one(
        mconst,
        n,
        upper=bin_cost + add_cost + 1,
        lower=bin_cost,
        instrs=bin_instrs,
        candidate_instrs=candidate_instrs,
    )
    assert s_instrs == candidate_instrs
    # No check since we did a cutoff.

    # upper is okay to allow us to get a solution using
    # cached entry 3 added via binary_method.
    instrs = []
    s_cost, s_instrs = search_add_one(
        mconst,
        n + 1,
        upper=20,
        lower=0,
        instrs=instrs,
        candidate_instrs=candidate_instrs,
    )
    check(n + 1, s_cost, s_instrs)
    from mult_by_const.io import dump

    dump(mconst.mult_cache)

    # Try something that makes add count down for a bit.
    instrs = []
    candidate_instrs = []
    n = 11
    s_cost, s_instrs = search_add_one(
        mconst,
        n,
        upper=inf_cost,
        lower=0,
        instrs=instrs,
        candidate_instrs=candidate_instrs,
    )

    check(n, s_cost, instrs=s_instrs)
    from mult_by_const.io import dump
    dump(mconst.mult_cache)

    # Try something that makes add count down for a bit and fail.
    mconst.mult_cache.clear()
    instrs = []
    candidate_instrs = []
    n = 20
    s_cost, s_instrs = search_add_one(
        mconst,
        n,
        upper=6,
        lower=0,
        instrs=instrs,
        candidate_instrs=candidate_instrs,
    )