Example #1
0
def test_init_with_basic_solution(supply_chain_name):
    supply_chain_filename = os.path.join(dirname, supply_chain_name)
    stages = read_supply_chain_from_txt(supply_chain_filename)

    gsm = dag_gsm.GuaranteedServiceModelDAG(stages)
    sol = gsm.find_optimal_solution()
    sol_init = gsm.find_optimal_solution_with_basic_solution_init()
    np.testing.assert_approx_equal(sol.cost, sol_init.cost)
def test_check_tree_topology(supply_chain_filename, assertion_failure):
    supply_chain_filename = os.path.join(dirname, supply_chain_filename)
    stages = read_supply_chain_from_txt(supply_chain_filename)
    if assertion_failure:
        with pytest.raises(tree_gsm.IncompatibleGraphTopology):
            tree_gsm.GuaranteedServiceModelTree(stages)
    else:
        tree_gsm.GuaranteedServiceModelTree(stages)
Example #3
0
def test_dag_gsm(supply_chain_name, ref_cost, ref_stock):
    supply_chain_filename = os.path.join(dirname, supply_chain_name)
    stages = read_supply_chain_from_txt(supply_chain_filename)

    print(supply_chain_name)
    gsm = dag_gsm.GuaranteedServiceModelDAG(stages)
    solution = gsm.find_optimal_solution()

    base_stocks = tree_gsm.compute_base_stocks(solution.policy, stages)
    print(sum(i for i in base_stocks.values()), ref_stock)
    print(solution.cost, ref_cost, ref_cost / solution.cost)
    print()
Example #4
0
def test_root_invariance(supply_chain_filename, root_ids_list):
    supply_chain_filename = os.path.join(dirname, supply_chain_filename)
    stages = read_supply_chain_from_txt(supply_chain_filename)
    gsm_1 = dag_gsm.GuaranteedServiceModelDAG(stages)
    sol_1 = gsm_1.find_optimal_solution(root_stage_id=root_ids_list[0])

    for root_stage_id in root_ids_list[1:]:
        gsm_2 = dag_gsm.GuaranteedServiceModelDAG(stages)
        sol_2 = gsm_2.find_optimal_solution(root_stage_id=root_stage_id)
        np.testing.assert_approx_equal(sol_1.cost, sol_2.cost)
        assert solution_policies_equal(sol_1.policy,
                                       sol_2.policy,
                                       stages,
                                       verbose=False)
Example #5
0
def test_against_tree_GSM():
    supply_chain_filename = os.path.join(dirname, "bulldozer.txt")
    stages = read_supply_chain_from_txt(supply_chain_filename)

    gsm_1 = tree_gsm.GuaranteedServiceModelTree(stages)
    sol_1 = gsm_1.find_optimal_solution()

    gsm_2 = dag_gsm.GuaranteedServiceModelDAG(stages)
    sol_2 = gsm_2.find_optimal_solution()

    np.testing.assert_approx_equal(sol_1.cost, sol_2.cost)
    assert solution_policies_equal(sol_1.policy,
                                   sol_2.policy,
                                   stages,
                                   verbose=False)
def test_compute_basic_solution_cost(supply_chain_filename):
    supply_chain_filename = os.path.join(dirname, supply_chain_filename)
    stages = read_supply_chain_from_txt(supply_chain_filename)
    gsm = dag_gsm.GuaranteedServiceModelDAG(stages)
    sol = gsm.find_optimal_solution()

    basic_sol_cost = tree_gsm.compute_basic_solution_cost(stages)

    if sol.cost > basic_sol_cost:
        np.testing.assert_approx_equal(sol.cost, basic_sol_cost)

    additional_constraints = {}
    for stage_id in stages:
        additional_constraints[(stage_id, "max_s")] = 0

    const_sol = gsm.find_optimal_solution(constraints=additional_constraints)

    np.testing.assert_approx_equal(const_sol.cost, basic_sol_cost)
def test_additional_constraints(supply_chain_name, constraints_list):
    supply_chain_filename = os.path.join(dirname,
                                         "{}.txt".format(supply_chain_name))
    stages = read_supply_chain_from_txt(supply_chain_filename)
    gsm = tree_gsm.GuaranteedServiceModelTree(stages)

    unconstrained_solution = gsm.find_optimal_solution()

    monotone_solution = check_additional_constraints(
        gsm_obj=gsm,
        constraints_list=constraints_list,
        unconstrained_solution=unconstrained_solution,
        monotone_increase=True)
    monotone_done = False
    for const_list in permutations(constraints_list):
        if (not monotone_done) and const_list == constraints_list:
            monotone_done = True
        else:
            solution = check_additional_constraints(
                gsm_obj=gsm, constraints_list=const_list)
            assert abs(solution.cost - monotone_solution.cost
                       ) / monotone_solution.cost <= 0.001
Example #8
0
def run_gsm(path, network, figpath, run_gsm_optimiser, plotting=True):
    # Load data
    supply_chain_filename = os.path.join(path, network)
    stages = read_supply_chain_from_txt(supply_chain_filename)
    # Run GSM
    gsm = GuaranteedServiceModelDAG(stages)

    if not run_gsm_optimiser:
        solution = None
        base_stocks = None
    else:
        solution = gsm.find_optimal_solution()
        base_stocks = tree_gsm.compute_base_stocks(solution.policy, stages)

    if plotting:
        plot_gsm(args.network,
                 filename=os.path.join(figpath, network),
                 stages=stages,
                 base_stocks=base_stocks,
                 solution=solution.serialize(),
                 do_save=True)
    return gsm
def test_numerical_simulator():
    n = 100000  # length of demand simulation

    lam = 10
    sla = 0.95

    # below is the explicit implementation of two stage system
    # index 1 stands for "Demand" stage
    # index 2 stands for "Dist" stage

    # simulate demand history
    np.random.seed(seed=8675309)
    d = np.random.poisson(size=n, lam=lam)

    # service times
    s_1 = 0
    s_2 = 3

    # internal service times
    si_1 = s_2
    si_2 = 0

    # lead times
    l_1 = 0
    l_2 = 30

    # net replenishment times
    tau_1 = si_1 + l_1 - s_1
    tau_2 = si_2 + l_2 - s_2

    # servicing history
    s_1_q = np.zeros(n + s_1 + 1)
    s_1_q[-n:] = d
    # replenishment history
    r_1_q = np.zeros(n + si_1 + l_1 + 1)
    r_1_q[-n:] = d
    r_1_q_indep = r_1_q.copy()  # copy for the indep_simulation

    s_2_q = np.zeros(n + s_2 + 1)
    s_2_q[-n:] = d
    r_2_q = np.zeros(n + si_2 + l_2 + 1)
    r_2_q[-n:] = d

    # basestocks
    b_1 = poisson.ppf(sla, tau_1 * lam)
    b_2 = poisson.ppf(sla, tau_2 * lam)

    # inventory position histories
    i_1_q_indep = b_1 + np.cumsum(r_1_q_indep[:len(s_1_q)] - s_1_q)
    i_2_q_indep = b_2 + np.cumsum(r_2_q[:len(s_2_q)] - s_2_q)

    ref_indep_corr = np.corrcoef(i_1_q_indep[100:n], i_2_q_indep[100:n])[0, 1]

    # decoupled stockout rates
    indep_demand_stockout_rate = np.mean(i_1_q_indep < 0)
    indep_dist_stouckout_rate = np.mean(i_2_q_indep < 0)

    # compute actual replenishments from Dist to Demand
    pos = i_2_q_indep.copy()
    neg = -i_2_q_indep.copy()
    pos[pos < 0] = 0
    neg[neg < 0] = 0

    required_r = neg[:len(s_2_q) - 1] + s_2_q[1:]
    available_r = pos + r_2_q[1:len(pos) + 1]
    r_1_q_actual = np.minimum(available_r[:len(required_r)], required_r)
    r_1_q[-n:] = r_1_q_actual[-n:]

    i_1_q_casc = b_1 + np.cumsum(r_1_q[:len(s_1_q)] - s_1_q)
    casc_demand_stockout_rate = np.mean(i_1_q_casc < 0)

    ref_casc_corr = np.corrcoef(i_1_q_casc[100:n], i_2_q_indep[100:n])[0, 1]

    # now lets use the simulator to replicate the stats above
    data_path = os.path.abspath(os.path.dirname(__file__))
    path = os.path.join(
        data_path,
        "../../../src/meio/experiment/basic_serial_network_config.txt")
    stages = read_supply_chain_from_txt(path)
    policy = {"Dist": {"s": 3, "si": 0}, "Demand": {"s": 0, "si": 3}}

    base_stocks = compute_base_stocks(stages, policy, lam, sla)
    assert base_stocks["Demand"] == 39
    assert base_stocks["Dist"] == 297

    indep_inv_histories = simulate(stages,
                                   policy,
                                   base_stocks, {},
                                   d,
                                   stockout_stages=[])
    casc_inv_histories = simulate(stages,
                                  policy,
                                  base_stocks, {},
                                  d,
                                  stockout_stages=None)

    np.testing.assert_almost_equal(np.mean(indep_inv_histories["Demand"] < 0),
                                   indep_demand_stockout_rate, 5)
    np.testing.assert_almost_equal(np.mean(indep_inv_histories["Dist"] < 0),
                                   indep_dist_stouckout_rate, 5)

    indep_corr = np.corrcoef(indep_inv_histories["Demand"][100:n],
                             indep_inv_histories["Dist"][100:n])[0][1]
    np.testing.assert_almost_equal(indep_corr, ref_indep_corr, 5)

    np.testing.assert_almost_equal(np.mean(casc_inv_histories["Demand"] < 0),
                                   0.06437, 5)
    np.testing.assert_almost_equal(np.mean(casc_inv_histories["Dist"] < 0),
                                   0.05176, 5)
    casc_corr = np.corrcoef(casc_inv_histories["Demand"][100:n],
                            casc_inv_histories["Dist"][100:n])[0][1]
    np.testing.assert_almost_equal(casc_corr, ref_casc_corr, 5)
Example #10
0
def print_supply_chain_log(supply_chain: int,
                           results: Optional[Dict],
                           willems_dir: str,
                           supply_chain_root: str,
                           network_description: Dict,
                           stage_configs: Dict,
                           up_stages: Dict,
                           plot: bool = True):
    if results is None:
        print('No results for supply chain {} timeout may have occured'.format(
            supply_chain))
        return
    # Stock levels from stochastic leads paper
    #   -> paper http://willems.utk.edu/papers/dl/Humair_et_al_IF_v43n5_SepOct2013.pdf
    reported_stock = pd.read_csv(
        '../../../meio/willems_dataset/data/optimal_inventory_levels.csv')
    ref_stock_row = reported_stock[(reported_stock.SupplyChain == supply_chain)
                                   & (reported_stock.LT == 'mean')]
    reported_safety_stock, reported_base_stock = np.nan, np.nan
    assert ref_stock_row.shape[0] in [
        0, 1
    ], 'cannot have more than one optimal stock level'
    if ref_stock_row.shape[0] == 1:
        reported_safety_stock = int(ref_stock_row.SafetyStock.values)
        reported_base_stock = int(ref_stock_row.TotalStock.values)

    ref_cost = get_reported_costs(willems_dir)[supply_chain]
    base_stock_levels = np.sum(
        np.fromiter(results['base_stocks'].values(), dtype=int))
    safety_stock_levels = np.sum(
        np.fromiter(results['safety_stocks'].values(), dtype=int))
    cost = results['solution']['cost']

    def relative_difference(a, b):
        return np.abs(a - b) / a

    def str_relative_difference(a, b):
        return 'relative difference {:.2f}'.format(relative_difference(a, b))

    print('*' * 30, 'Supply chain', supply_chain, '*' * 30)
    print('Total base stock', base_stock_levels, 'reference=',
          reported_base_stock,
          str_relative_difference(base_stock_levels, reported_base_stock))
    print('Total safety stock', safety_stock_levels, 'reference=',
          reported_safety_stock,
          str_relative_difference(safety_stock_levels, reported_safety_stock))
    print('cost={:.2f} reference={:.2f} ratio={:.2f}'.format(
        cost, ref_cost, ref_cost / cost))
    print('network', network_description, 'stages with base stock',
          (base_stock_levels > 0).sum())

    if plot:
        plt.ion()
        # generate temporary csv file that gets loaded back in
        # TODO: could avoid writing out intermediate CSV by using convert_to_stages
        write_to_csv(supply_chain_root + '.csv', stage_configs, up_stages)
        print(supply_chain_root + '.csv', network_description)
        stages = read_supply_chain_from_txt(supply_chain_root + '.csv')
        plot_gsm(str(supply_chain),
                 filename=supply_chain_root + '.png',
                 stages=stages,
                 base_stocks=results['base_stocks'],
                 solution=results['solution'],
                 coc_clusters={},
                 do_save=True,
                 show=True)
Example #11
0
def test_find_minimum_spanning_tree(supply_chain_filename):
    supply_chain_filename = os.path.join(dirname, supply_chain_filename)
    stages = read_supply_chain_from_txt(supply_chain_filename)

    gsm = dag_gsm.GuaranteedServiceModelDAG(stages)
    MST, _ = gsm._find_MST(stages)