def run_gsm_with_timeout(data_set_filename: str, gsm_type: GSM, timeout: int = 15) \
        -> Optional[Dict]:
    """
    Run the specified version of gsm
    """
    execution_start_time = datetime.utcnow()
    _, gsm = create_gsm_instance(gsm_type, data_set_filename)
    signal.signal(signal.SIGALRM, handler)
    signal.alarm(timeout)
    results = None
    try:
        solution = gsm.find_optimal_solution()  # type: GSM_Solution
        signal.alarm(0)  # Cancel timeout
        execution_time = (datetime.utcnow() -
                          execution_start_time).total_seconds()

        safety_stocks = compute_expected_inventories(solution.policy,
                                                     gsm.stages)
        base_stocks = compute_base_stocks(solution.policy, gsm.stages)
        results = dict(execution_time=execution_time,
                       solution_cost=solution.cost,
                       solution=solution.serialize(),
                       safety_stocks=safety_stocks,
                       base_stocks=base_stocks)
    except GSMException:
        print("The {} version of GSM timed out for {}".format(
            gsm_type, data_set_filename))

    return results
def test_expected_inventories_and_basestocks_computations():
    stages, gsm = create_gsm_instance(GSM.Tree,
                                      os.path.join(dirname, "bulldozer.txt"))

    sol = gsm.find_optimal_solution()
    expected_inventories = tree_gsm.compute_expected_inventories(
        sol.policy, stages)
    base_stocks = tree_gsm.compute_base_stocks(sol.policy, stages)
    assert len(stages) == len(base_stocks)
    assert len(base_stocks) == len(expected_inventories)
def test_against_basic_serial_network_experiment():
    """
    Hand coded answers were taken from Graves and Schoenmeyr 2016 (Table 5, No constraint row)
    """
    stages = create_serial_stages(added_cost_prof="constant",
                                  lead_time_prof="upstream_heavy")

    gsm = GuaranteedServiceModelTree(stages)
    solution = gsm.find_optimal_solution(root_stage_id="4")
    # there is another policy with the same cost
    # it just happens that lead time and cost numbers add up this way
    assert int(solution.cost) == 368
    safety_stocks = tree_gsm.compute_expected_inventories(
        solution.policy, stages)
    assert int(safety_stocks["5"]) == 240
    assert int(safety_stocks["1"]) == 320
def test_against_gound_truth_camera(scenario, service_time_constraints,
                                    correct_cost):

    inventory_holding_rate = 0.24

    stages, gsm = create_gsm_instance(
        GSM.Tree, os.path.join(dirname, "digital_camera.txt"))

    for stage_id, max_s_time in service_time_constraints:
        gsm.stages[stage_id].max_s_time = max_s_time

    sol = gsm.find_optimal_solution()
    sol = sol.serialize()

    solution_cost = sol["cost"] * inventory_holding_rate
    assert abs(solution_cost - correct_cost) / correct_cost < 0.01

    policy = sol["policy"]
    inventories = tree_gsm.compute_expected_inventories(policy, gsm.stages)

    if scenario == 2:
        for stage_id in stages:

            if stage_id in [
                    "Camera", "Imager", "Circuit_Board", "Other_Parts_L_60",
                    "Other_Parts_M_60", "Build_Test_Pack"
            ]:

                assert policy[stage_id]["s"] == 0
                assert inventories[stage_id] > 0
            else:
                assert inventories[stage_id] == 0
                if stage_id == "Transfer":
                    assert policy[stage_id]["s"] == 2

                elif stage_id == "Ship":
                    assert policy[stage_id]["s"] == 5

    elif scenario == 4:
        for stage_id in stages:
            if stage_id in ["Build_Test_Pack", "Ship"]:
                assert inventories[stage_id] == 0
            else:
                assert inventories[stage_id] > 0
def test_against_basic_serial_network_experiments_cap_location(
        added_cost_prof, lead_time_prof, cap_loc, cap_cost, exp_safety_stocks):
    """
    Hand coded answers were taken from Graves and Schoenmeyr 2016 (Table 5)
    """
    stages = create_serial_stages(added_cost_prof=added_cost_prof,
                                  lead_time_prof=lead_time_prof)
    stages[cap_loc].cap_constraint = 45
    gsm = GuaranteedServiceModelTree(stages, propagate_bounds=True)

    solution = gsm.find_optimal_solution()
    safety_stocks = tree_gsm.compute_expected_inventories(
        solution.policy, stages)
    print(tree_gsm.compute_replenishment_times(solution.policy, stages))

    assert abs(int(solution.cost) - cap_cost) <= 1

    for stage_id in exp_safety_stocks:
        assert abs(safety_stocks[stage_id] - exp_safety_stocks[stage_id]) <= 1