def csv_msob_fp_time(name: str, number_flows: int, number_servers: int,
                     arrival_enum: ArrivalEnum,
                     perform_param: PerformParameter, opt_method: OptMethod,
                     mc_dist: MonteCarloDist, comparator: callable,
                     total_iterations: int, target_util: float) -> dict:
    """Chooses parameters by Monte Carlo type random choice."""

    param_array = mc_enum_to_dist(arrival_enum=arrival_enum,
                                  mc_dist=mc_dist,
                                  number_flows=number_flows,
                                  number_servers=number_servers,
                                  total_iterations=total_iterations)

    time_array = np.empty([total_iterations, 3])
    # 3 approaches to compare

    for i in tqdm(range(total_iterations), total=total_iterations):
        if arrival_enum == ArrivalEnum.DM1:
            arr_list = [
                DM1(lamb=param_array[i, j]) for j in range(number_flows)
            ]

        elif arrival_enum == ArrivalEnum.MD1:
            arr_list = [
                MD1(lamb=param_array[i, j], mu=1.0)
                for j in range(number_flows)
            ]

        elif arrival_enum == ArrivalEnum.MMOODisc:
            arr_list = [
                MMOODisc(stay_on=param_array[i, j],
                         stay_off=param_array[i, number_flows + j],
                         peak_rate=param_array[i, 2 * number_flows + j])
                for j in range(number_flows)
            ]

        elif arrival_enum == ArrivalEnum.MMOOFluid:
            arr_list = [
                MMOOCont(mu=param_array[i, j],
                         lamb=param_array[i, number_flows + j],
                         peak_rate=param_array[i, 2 * number_flows + j])
                for j in range(number_flows)
            ]

        else:
            raise NotImplementedError(f"Arrival parameter {arrival_enum.name} "
                                      f"is infeasible")

        ser_list = [
            ConstantRateServer(
                rate=param_array[i,
                                 arrival_enum.number_parameters() *
                                 number_flows + j])
            for j in range(number_servers)
        ]

        if name == "overlapping_tandem":
            setting = OverlappingTandemPerform(arr_list=arr_list,
                                               ser_list=ser_list,
                                               perform_param=perform_param)
        elif name == "square":
            setting = SquarePerform(arr_list=arr_list,
                                    ser_list=ser_list,
                                    perform_param=perform_param)
        else:
            raise NotImplementedError("this topology is not implemented")

        computation_necessary = True

        if target_util > 0.0:
            util = setting.approximate_utilization()
            if util < target_util or util > 1:
                time_array[i, ] = np.nan
                computation_necessary = False

        if computation_necessary:
            # standard_bound, server_bound, fp_bound = compare_avoid_dep()
            time_array[i, 0], time_array[i, 1], time_array[i, 2] = comparator(
                setting=setting)

            if (perform_param.perform_metric == PerformEnum.DELAY_PROB
                    and np.nanmin(time_array[i, ]) > 1.0):
                # np.nanmin(time_array[i, ]) is the smallest value
                time_array[i, ] = np.nan

    time_dict = time_array_to_results(title=name, time_array=time_array)

    time_dict.update({
        "iterations": total_iterations,
        "T": perform_param.value,
        "optimization": opt_method.name,
        "MCDistribution": mc_dist.to_name(),
        "MCParam": mc_dist.param_to_string()
    })

    filename = name
    filename += f"_time_{perform_param.to_name()}_{arrival_enum.name}" \
        f"_MC{mc_dist.to_name()}_{opt_method.name}_util_{target_util}"

    with open(filename + ".csv", 'w') as csv_file:
        writer = csv.writer(csv_file)
        for key, value in time_dict.items():
            writer.writerow([key, value])

    return time_dict
Example #2
0
def csv_single_param_exp(start_time: int,
                         delay: int,
                         mc_dist: MonteCarloDist,
                         target_util: float,
                         total_iterations: int,
                         sample=False) -> dict:
    valid_iterations = total_iterations
    metric = ChangeEnum.RATIO_REF_NEW
    sample_size = 10**2

    delta = 0.05

    size_array = [total_iterations, 2]
    # [rows, columns]

    if mc_dist.mc_enum == MCEnum.UNIFORM:
        param_array = np.random.uniform(low=0,
                                        high=mc_dist.param_list[0],
                                        size=size_array)
    elif mc_dist.mc_enum == MCEnum.EXPONENTIAL:
        param_array = np.random.exponential(scale=mc_dist.param_list[0],
                                            size=size_array)
    else:
        raise NameError(
            f"Distribution parameter {mc_dist.mc_enum} is infeasible")

    res_array = np.empty([total_iterations, 2])
    res_array_sample = np.empty([total_iterations, 2])

    for i in tqdm(range(total_iterations)):
        single_setting = SingleServerMitPerform(
            arr_list=[DM1(lamb=param_array[i, 0])],
            server=ConstantRateServer(rate=param_array[i, 1]),
            perform_param=PerformParameter(
                perform_metric=PerformEnum.DELAY_PROB, value=delay))

        computation_necessary = True

        # print(res_array[i, ])
        if target_util > 0.0:
            util = single_setting.approximate_utilization()
            if util < target_util or util > 1:
                res_array[i, ] = np.nan
                computation_necessary = False

        if computation_necessary:

            theta_bounds = [(0.1, 4.0)]

            res_array[i, 0] = Optimize(setting=single_setting,
                                       number_param=1).grid_search(
                                           grid_bounds=theta_bounds,
                                           delta=delta)

            res_array[i,
                      1] = delay_prob_lower_exp_dm1_opt(t=start_time,
                                                        delay=delay,
                                                        lamb=param_array[i, 0],
                                                        rate=param_array[i, 1])

            if sample:
                res_array_sample[i, 1] = delay_prob_sample_exp_dm1_opt(
                    t=start_time,
                    delay=delay,
                    lamb=param_array[i, 0],
                    rate=param_array[i, 1],
                    sample_size=sample_size)

            if res_array[i, 0] > 1.0:
                res_array[i, ] = np.nan
                if sample:
                    res_array_sample[i, ] = np.nan

        if np.isnan(res_array[i, 0]) or np.isnan(res_array[i, 1]):
            res_array[i, ] = np.nan
            res_array_sample[i, ] = np.nan
            valid_iterations -= 1

    # print("exponential results", res_array[:, 2])

    res_dict = two_col_array_to_results(arrival_enum=ArrivalEnum.DM1,
                                        param_array=param_array,
                                        res_array=res_array,
                                        number_servers=1,
                                        valid_iterations=valid_iterations,
                                        compare_metric=metric)

    res_dict.update({
        "iterations": total_iterations,
        "delta_time": delay,
        "optimization": "grid_search",
        "metric": metric.name,
        "MCDistribution": mc_dist.to_name(),
        "MCParam": mc_dist.param_to_string()
    })

    if sample:
        res_array_sample[:, 0] = res_array[:, 0]

        res_dict_sample = two_col_array_to_results(
            arrival_enum=ArrivalEnum.DM1,
            param_array=param_array,
            res_array=res_array_sample,
            number_servers=1,
            valid_iterations=valid_iterations,
            compare_metric=metric)

        res_dict_sample.update({
            "iterations": total_iterations,
            "delta_time": delay,
            "optimization": "grid_search",
            "metric": metric.name,
            "MCDistribution": mc_dist.to_name(),
            "MCParam": mc_dist.param_to_string()
        })

    suffix = f"single_DELAY_PROB_DM1_results" \
        f"_MC{mc_dist.to_name()}_power_exp.csv"

    with open("lower_" + suffix, 'w') as csv_file:
        writer = csv.writer(csv_file)
        for key, value in res_dict.items():
            writer.writerow([key, value])
    if sample:
        with open("sample_" + suffix, 'w') as csv_file:
            writer = csv.writer(csv_file)
            for key, value in res_dict_sample.items():
                writer.writerow([key, value])

    return res_dict
Example #3
0
def csv_single_param_exp_lower(start_time: int,
                               perform_param: PerformParameter,
                               mc_dist: MonteCarloDist,
                               sample=False) -> dict:
    total_iterations = 10**2
    valid_iterations = total_iterations
    metric = "relative"
    sample_size = 10**3

    delta = 0.05

    size_array = [total_iterations, 2]
    # [rows, columns]

    if mc_dist.mc_enum == MCEnum.UNIFORM:
        param_array = np.random.uniform(
            low=0, high=mc_dist.param_list[0], size=size_array)
    elif mc_dist.mc_enum == MCEnum.EXPONENTIAL:
        param_array = np.random.exponential(
            scale=mc_dist.param_list[0], size=size_array)
    else:
        raise NameError("Distribution parameter {0} is infeasible".format(
            mc_dist.mc_enum))

    res_array = np.empty([total_iterations, 3])
    if sample:
        res_array_sample = np.empty([total_iterations, 3])

    for i in tqdm(range(total_iterations)):
        setting = SingleServerPerform(
            arr=DM1(lamb=param_array[i, 0]),
            const_rate=ConstantRate(rate=param_array[i, 1]),
            perform_param=perform_param)

        theta_bounds = [(0.1, 4.0)]
        bound_array = theta_bounds[:]

        res_array[i, 0] = Optimize(setting=setting).grid_search(
            bound_list=bound_array, delta=delta)

        bound_array_power = theta_bounds[:]
        bound_array_power.append((0.9, 4.0))

        res_array[i, 1] = OptimizeNew(setting_new=setting).grid_search(
            bound_list=bound_array_power, delta=delta)

        if perform_param.perform_metric == PerformEnum.DELAY_PROB:
            res_array[i, 2] = delay_prob_lower_exp_dm1_opt(
                t=start_time,
                delay=perform_param.value,
                lamb=param_array[i, 0],
                rate=param_array[i, 1])

            if sample:
                res_array_sample[i, 0] = res_array[i, 0]
                res_array_sample[i, 1] = res_array[i, 1]
                res_array_sample[i, 2] = delay_prob_sample_exp_dm1_opt(
                    t=start_time,
                    delay=perform_param.value,
                    lamb=param_array[i, 0],
                    rate=param_array[i, 1],
                    sample_size=sample_size)

            if res_array[i, 0] > 1.0:
                res_array[i, ] = nan
                if sample:
                    res_array_sample[i, ] = nan

        elif perform_param.perform_metric == PerformEnum.OUTPUT:
            res_array[i, 2] = output_lower_exp_dm1_opt(
                s=start_time,
                delta_time=perform_param.value,
                lamb=param_array[i, 0],
                rate=param_array[i, 1])

        else:
            raise NameError("{0} is an infeasible performance metric".format(
                perform_param.perform_metric))

        if (res_array[i, 1] == inf or res_array[i, 2] == inf
                or res_array[i, 0] == nan or res_array[i, 1] == nan
                or res_array[i, 2] == nan):
            res_array[i, ] = nan
            res_array_sample[i, ] = nan
            valid_iterations -= 1

    # print("exponential results", res_array[:, 2])

    res_dict = three_col_array_to_results(
        arrival_enum=ArrivalEnum.DM1,
        res_array=res_array,
        valid_iterations=valid_iterations,
        metric=metric)

    res_dict.update({
        "iterations": total_iterations,
        "delta_time": perform_param.value,
        "optimization": "grid_search",
        "metric": "relative",
        "MCDistribution": mc_dist.to_name(),
        "MCParam": mc_dist.param_to_string()
    })

    res_dict_sample = three_col_array_to_results(
        arrival_enum=ArrivalEnum.DM1,
        res_array=res_array_sample,
        valid_iterations=valid_iterations,
        metric=metric)

    res_dict_sample.update({
        "iterations": total_iterations,
        "delta_time": perform_param.value,
        "optimization": "grid_search",
        "metric": "relative",
        "MCDistribution": mc_dist.to_name(),
        "MCParam": mc_dist.param_to_string()
    })

    with open(
            "lower_single_{0}_DM1_results_MC{1}_power_exp.csv".format(
                perform_param.to_name(), mc_dist.to_name()), 'w') as csv_file:
        writer = csv.writer(fileobj=csv_file)
        for key, value in res_dict.items():
            writer.writerow([key, value])
    if sample:
        with open(
                "sample_single_{0}_DM1_results_MC{1}_power_exp.csv".format(
                    perform_param.to_name(), mc_dist.to_name()),
                'w') as csv_file:
            writer = csv.writer(fileobj=csv_file)
            for key, value in res_dict_sample.items():
                writer.writerow([key, value])

    return res_dict
Example #4
0
def csv_msob_fp_param(name: str,
                      number_flows: int,
                      number_servers: int,
                      arrival_enum: ArrivalEnum,
                      perform_param: PerformParameter,
                      opt_method: OptMethod,
                      mc_dist: MonteCarloDist,
                      comparator: callable,
                      compare_metric: ChangeEnum,
                      total_iterations: int,
                      target_util: float,
                      filter_standard_inf=False) -> dict:
    """Chooses parameters by Monte Carlo type random choice."""
    param_array = mc_enum_to_dist(arrival_enum=arrival_enum,
                                  mc_dist=mc_dist,
                                  number_flows=number_flows,
                                  number_servers=number_servers,
                                  total_iterations=total_iterations)

    res_array = np.empty([total_iterations, 3])
    # 3 approaches to compare

    for i in tqdm(range(total_iterations), total=total_iterations):
        if arrival_enum == ArrivalEnum.DM1:
            arr_list = [
                DM1(lamb=param_array[i, j]) for j in range(number_flows)
            ]

        elif arrival_enum == ArrivalEnum.DGamma1:
            arr_list = [
                DGamma1(alpha_shape=param_array[i, j],
                        beta_rate=param_array[i, number_flows + j])
                for j in range(number_flows)
            ]

        elif arrival_enum == ArrivalEnum.DWeibull1:
            arr_list = [
                DWeibull1(lamb=param_array[i, j]) for j in range(number_flows)
            ]

        elif arrival_enum == ArrivalEnum.MD1:
            arr_list = [
                MD1(lamb=param_array[i, j], mu=1.0)
                for j in range(number_flows)
            ]

        elif arrival_enum == ArrivalEnum.MMOODisc:
            arr_list = [
                MMOODisc(stay_on=param_array[i, j],
                         stay_off=param_array[i, number_flows + j],
                         peak_rate=param_array[i, 2 * number_flows + j])
                for j in range(number_flows)
            ]

        elif arrival_enum == ArrivalEnum.MMOOFluid:
            arr_list = [
                MMOOCont(mu=param_array[i, j],
                         lamb=param_array[i, number_flows + j],
                         peak_rate=param_array[i, 2 * number_flows + j])
                for j in range(number_flows)
            ]

        else:
            raise NotImplementedError(f"Arrival parameter {arrival_enum.name} "
                                      f"is infeasible")

        ser_list = [
            ConstantRateServer(
                rate=param_array[i,
                                 arrival_enum.number_parameters() *
                                 number_flows + j])
            for j in range(number_servers)
        ]

        if name == "overlapping_tandem":
            setting = OverlappingTandemPerform(arr_list=arr_list,
                                               ser_list=ser_list,
                                               perform_param=perform_param)

        elif name == "square":
            setting = SquarePerform(arr_list=arr_list,
                                    ser_list=ser_list,
                                    perform_param=perform_param)
        else:
            raise NotImplementedError("this topology is not implemented")

        computation_necessary = True

        if target_util > 0.0:
            util = setting.approximate_utilization()
            if util < target_util or util > 1:
                res_array[i, ] = np.nan
                computation_necessary = False

        if computation_necessary:
            # standard_bound, server_bound, fp_bound = compare_avoid_dep()
            res_array[i, 0], res_array[i, 1], res_array[i, 2] = comparator(
                setting=setting)

            if (perform_param.perform_metric == PerformEnum.DELAY_PROB
                    and np.nanmin(res_array[i, ]) > 1.0):
                # np.nanmin(res_array[i, ]) is the smallest value
                res_array[i, ] = np.nan
            elif np.nanmin(res_array[i, ]) == inf:
                res_array[i, ] = np.nan

            if filter_standard_inf and res_array[i, 0] == inf:
                res_array[i, ] = np.nan

    res_array_no_full_nan = remove_full_nan_rows(full_array=res_array)
    valid_iterations = res_array_no_full_nan.shape[0]

    if valid_iterations < total_iterations * 0.2:
        warn(f"Many nan's: {total_iterations - valid_iterations} nans "
             f"out of {total_iterations}!")

        if valid_iterations < 100:
            raise NotEnoughResults("result is useless")

    res_name = name
    res_name += f"_res_array_{perform_param.to_name()}_" \
        f"{arrival_enum.name}_validiter_{valid_iterations}"

    if filter_standard_inf:
        res_name += "_filter_standard_inf"

    np.savetxt(fname=res_name + ".csv", X=res_array_no_full_nan, delimiter=",")

    res_dict = msob_fp_array_to_results(title=name,
                                        arrival_enum=arrival_enum,
                                        perform_param=perform_param,
                                        opt_method=opt_method,
                                        mc_dist=mc_dist,
                                        param_array=param_array,
                                        res_array=res_array,
                                        number_flows=number_flows,
                                        number_servers=number_servers,
                                        compare_metric=compare_metric)

    res_dict.update({
        "iterations": total_iterations,
        "target_util": target_util,
        "T": perform_param.value,
        "optimization": opt_method.name,
        "compare_metric": compare_metric.name,
        "MCDistribution": mc_dist.to_name(),
        "MCParam": mc_dist.param_to_string()
    })

    filename = name
    filename += f"_results_{perform_param.to_name()}_{arrival_enum.name}_" \
        f"MC{mc_dist.to_name()}_{opt_method.name}_" \
                f"{compare_metric.name}_util_{target_util}"

    if filter_standard_inf:
        filename += "_filter_standard_inf"

    with open(filename + ".csv", 'w') as csv_file:
        writer = csv.writer(csv_file)
        for key, value in res_dict.items():
            writer.writerow([key, value])

    return res_dict
Example #5
0
def csv_fat_cross_param_power(name: str, arrival_enum: ArrivalEnum,
                              number_flows: int, number_servers: int,
                              perform_param: PerformParameter,
                              opt_method: OptMethod, mc_dist: MonteCarloDist,
                              compare_metric: ChangeEnum,
                              total_iterations: int,
                              target_util: float) -> dict:
    """Chooses parameters by Monte Carlo type random choice."""
    param_array = mc_enum_to_dist(arrival_enum=arrival_enum,
                                  mc_dist=mc_dist,
                                  number_flows=number_flows,
                                  number_servers=number_servers,
                                  total_iterations=total_iterations)

    res_array = np.empty([total_iterations, 2])

    # print(res_array)

    for i in tqdm(range(total_iterations), total=total_iterations):
        if arrival_enum == ArrivalEnum.DM1:
            arr_list = [
                DM1(lamb=param_array[i, j]) for j in range(number_flows)
            ]

        elif arrival_enum == ArrivalEnum.DGamma1:
            arr_list = [
                DGamma1(alpha_shape=param_array[i, j],
                        beta_rate=param_array[i, number_flows + j])
                for j in range(number_flows)
            ]

        elif arrival_enum == ArrivalEnum.DWeibull1:
            arr_list = [
                DWeibull1(lamb=param_array[i, j]) for j in range(number_flows)
            ]

        elif arrival_enum == ArrivalEnum.MD1:
            arr_list = [
                MD1(lamb=param_array[i, j], mu=1.0)
                for j in range(number_flows)
            ]

        elif arrival_enum == ArrivalEnum.MMOODisc:
            arr_list = [
                MMOODisc(stay_on=param_array[i, j],
                         stay_off=param_array[i, number_flows + j],
                         peak_rate=param_array[i, 2 * number_flows + j])
                for j in range(number_flows)
            ]

        elif arrival_enum == ArrivalEnum.MMOOFluid:
            arr_list = [
                MMOOCont(mu=param_array[i, j],
                         lamb=param_array[i, number_flows + j],
                         peak_rate=param_array[i, 2 * number_flows + j])
                for j in range(number_flows)
            ]

        elif arrival_enum == ArrivalEnum.Massoulie:
            arr_list = [
                LeakyBucketMassoulie(sigma_single=param_array[i, j],
                                     rho_single=param_array[i,
                                                            number_flows + j],
                                     m=20) for j in range(number_flows)
            ]
            # NOTE: n is fixed

        elif arrival_enum == ArrivalEnum.TBConst:
            arr_list = [
                DetermTokenBucket(sigma_single=param_array[i, j],
                                  rho_single=param_array[i, number_flows + j],
                                  m=1) for j in range(number_flows)
            ]

        else:
            raise NotImplementedError(f"Arrival parameter {arrival_enum.name} "
                                      f"is infeasible")

        ser_list = [
            ConstantRateServer(
                rate=param_array[i,
                                 arrival_enum.number_parameters() *
                                 number_flows + j])
            for j in range(number_servers)
        ]

        fat_cross_setting = FatCrossPerform(arr_list=arr_list,
                                            ser_list=ser_list,
                                            perform_param=perform_param)

        computation_necessary = True

        if target_util > 0.0:
            util = fat_cross_setting.approximate_utilization()
            if util < target_util or util > 1:
                res_array[i, ] = np.nan
                computation_necessary = False

        if computation_necessary:
            # standard_bound, h_mit_bound = compare_mitigator()
            res_array[i, 0], res_array[i, 1] = compare_mitigator(
                setting=fat_cross_setting,
                opt_method=opt_method,
                number_l=number_servers - 1)

            if (perform_param.perform_metric == PerformEnum.DELAY_PROB
                    and res_array[i, 1] > 1.0):
                # write as nan if second (in particular both) value(s) are > 1.0
                res_array[i, ] = np.nan

        if np.isnan(res_array[i, 0]) or np.isnan(res_array[i, 1]):
            res_array[i, ] = np.nan

    res_array_no_full_nan = remove_full_nan_rows(full_array=res_array)
    valid_iterations = res_array_no_full_nan.shape[0]

    if valid_iterations < total_iterations * 0.2:
        warn(f"Many nan's: {total_iterations - valid_iterations} nans "
             f"out of {total_iterations}!")

        if valid_iterations < 100:
            raise NotEnoughResults("result is useless")

    res_dict = two_col_array_to_results(arrival_enum=arrival_enum,
                                        param_array=param_array,
                                        res_array=res_array,
                                        number_servers=number_servers,
                                        compare_metric=compare_metric)

    res_dict.update({
        "iterations": total_iterations,
        "PerformParamValue": perform_param.value,
        "optimization": opt_method.name,
        "compare_metric": compare_metric.name,
        "MCDistribution": mc_dist.to_name(),
        "MCParam": mc_dist.param_to_string(),
        "number_servers": number_servers
    })

    filename = name
    filename += f"_results_{perform_param.to_name()}_{arrival_enum.name}_" \
                f"MC{mc_dist.to_name()}_{opt_method.name}_" \
                f"{compare_metric.name}_util_{target_util}"

    with open(filename + ".csv", 'w') as csv_file:
        writer = csv.writer(csv_file)
        for key, value in res_dict.items():
            writer.writerow([key, value])

    return res_dict
Example #6
0
def msob_fp_array_to_results(title: str, arrival_enum: ArrivalEnum,
                             perform_param: PerformParameter,
                             opt_method: OptMethod, mc_dist: MonteCarloDist,
                             param_array: np.array, res_array: np.array,
                             number_flows: int, number_servers: int,
                             compare_metric: ChangeEnum) -> dict:
    """Writes the array values into a dictionary"""
    if res_array.shape[1] != 3:
        raise IllegalArgumentError(f"Array must have 3 columns,"
                                   f"not {res_array.shape[1]}")

    np.seterr(all='warn')

    res_array_no_full_nan = remove_full_nan_rows(full_array=res_array)
    valid_iterations = res_array_no_full_nan.shape[0]

    if compare_metric == ChangeEnum.RATIO_REF_NEW:
        change_vec_server_bound = np.divide(res_array[:, 0], res_array[:, 1])
        change_vec_pmoo_fp = np.divide(res_array[:, 0], res_array[:, 2])

    elif compare_metric == ChangeEnum.RATIO_NEW_REF:
        change_vec_server_bound = np.divide(res_array[:, 1], res_array[:, 0])
        change_vec_pmoo_fp = np.divide(res_array[:, 2], res_array[:, 0])

    elif compare_metric == ChangeEnum.RELATIVE_CHANGE:
        abs_vec_server_bound = np.subtract(res_array[:, 0], res_array[:, 1])
        change_vec_server_bound = np.divide(abs_vec_server_bound, res_array[:,
                                                                            0])

        abs_vec_pmoo_fp = np.subtract(res_array[:, 0], res_array[:, 2])
        change_vec_pmoo_fp = np.divide(abs_vec_pmoo_fp, res_array[:, 0])

    else:
        raise NotImplementedError(
            f"Metric={compare_metric.name} is not implemented")

    only_improved_server_bound = change_vec_server_bound[
        res_array[:, 0] > res_array[:, 1]]
    only_improved_pmoo_fp = change_vec_pmoo_fp[res_array[:, 0] > res_array[:,
                                                                           2]]

    row_max_msob = np.nanargmax(change_vec_server_bound)
    opt_msob = change_vec_server_bound[row_max_msob]
    mean_msob = np.nanmean(change_vec_server_bound)
    median_improved_server_bound = np.nanmedian(only_improved_server_bound)

    row_max_pmoo_fp = np.nanargmax(change_vec_pmoo_fp)
    opt_pmoo_fp = change_vec_pmoo_fp[row_max_pmoo_fp]
    mean_pmoo_fp = np.nanmean(change_vec_pmoo_fp)
    median_improved_pmoo_fp = np.nanmedian(only_improved_pmoo_fp)

    if (perform_param.perform_metric == PerformEnum.DELAY_PROB
            or perform_param.perform_metric == PerformEnum.BACKLOG_PROB):
        number_standard_bound_valid = np.nansum(
            res_array_no_full_nan[:, 0] < 1)
        number_server_bound_valid = np.nansum(res_array_no_full_nan[:, 1] < 1)
        number_pmoo_fp_valid = np.nansum(res_array_no_full_nan[:, 2] < 1)
    else:
        number_standard_bound_valid = np.nansum(
            res_array_no_full_nan[:, 0] < inf)
        number_server_bound_valid = np.nansum(
            res_array_no_full_nan[:, 1] < inf)
        number_pmoo_fp_valid = np.nansum(res_array_no_full_nan[:, 2] < inf)

    number_improved_server_bound = np.sum(
        res_array_no_full_nan[:, 0] > res_array_no_full_nan[:, 1])
    number_improved_pmoo_fp = np.sum(
        res_array_no_full_nan[:, 0] > res_array_no_full_nan[:, 2])

    best_approach = np.nanargmin(res_array_no_full_nan, axis=1)
    standard_best = np.count_nonzero(best_approach == 0)
    msob_best = np.count_nonzero(best_approach == 1)
    fp_best = np.count_nonzero(best_approach == 2)

    res_dict = {
        "Name": "Value",
        "topology": title,
        "arrival_distribution": arrival_enum.name
    }

    opt_dict = {
        "Name": "Value",
        "topology": title,
        "arrival_distribution": arrival_enum.name
    }

    for j in range(number_flows):
        if arrival_enum == ArrivalEnum.DM1:
            opt_dict[f"pmoo_fp_lamb{j + 1}"] = format(
                param_array[row_max_pmoo_fp, j], '.3f')
            opt_dict[f"server_bound_lamb{j + 1}"] = format(
                param_array[row_max_msob, j], '.3f')

        elif arrival_enum == ArrivalEnum.MD1:
            opt_dict[f"pmoo_fp_lamb{j + 1}"] = format(
                param_array[row_max_pmoo_fp, j], '.3f')
            opt_dict[f"ser_bound_lamb{j + 1}"] = format(
                param_array[row_max_msob, j], '.3f')

        elif arrival_enum == ArrivalEnum.MMOODisc:
            opt_dict[f"pmoo_fp_stay_on{j + 1}"] = format(
                param_array[row_max_pmoo_fp, j], '.3f')
            opt_dict[f"pmoo_fp_stay_off{j + 1}"] = format(
                param_array[row_max_pmoo_fp, number_flows + j], '.3f')
            opt_dict[f"pmoo_fp_burst{j + 1}"] = format(
                param_array[row_max_pmoo_fp, 2 * number_flows + j], '.3f')

            opt_dict[f"ser_bound_stay_on{j + 1}"] = format(
                param_array[row_max_msob, j], '.3f')
            opt_dict[f"ser_bound_stay_off{j + 1}"] = format(
                param_array[row_max_msob, number_flows + j], '.3f')
            opt_dict[f"ser_bound_burst{j + 1}"] = format(
                param_array[row_max_msob, 2 * number_flows + j], '.3f')

        elif arrival_enum == ArrivalEnum.MMOOFluid:
            opt_dict[f"pmoo_fp_mu{j + 1}"] = format(
                param_array[row_max_pmoo_fp, j], '.3f')
            opt_dict[f"pmoo_fp_lamb{j + 1}"] = format(
                param_array[row_max_pmoo_fp, number_flows + j], '.3f')
            opt_dict[f"pmoo_fp_burst{j + 1}"] = format(
                param_array[row_max_pmoo_fp, 2 * number_flows + j], '.3f')

            opt_dict[f"ser_bound_mu{j + 1}"] = format(
                param_array[row_max_msob, j], '.3f')
            opt_dict[f"ser_bound_lamb{j + 1}"] = format(
                param_array[row_max_msob, number_flows + j], '.3f')
            opt_dict[f"ser_bound_burst{j + 1}"] = format(
                param_array[row_max_msob, 2 * number_flows + j], '.3f')

        else:
            raise NotImplementedError(
                f"Arrival parameter={arrival_enum.name} is not implemented")

    for j in range(number_servers):
        opt_dict[f"pmoo_fp_rate{j + 1}"] = format(
            param_array[row_max_pmoo_fp,
                        arrival_enum.number_parameters() * number_flows + j],
            '.3f')
        opt_dict[f"server_bound_rate{j + 1}"] = format(
            param_array[row_max_msob,
                        arrival_enum.number_parameters() * number_flows + j],
            '.3f')

    opt_dict.update({
        "opt_pmoo_fp": format(opt_pmoo_fp, '.3f'),
        "opt_msob": format(opt_msob, '.3f'),
        "valid iterations": res_array.shape[0],
        "PerformParamValue": perform_param.value,
        "optimization": opt_method.name,
        "compare_metric": compare_metric.name,
        "MCDistribution": mc_dist.to_name(),
        "MCParam": mc_dist.param_to_string()
    })

    res_dict.update({
        "mean_pmoo_fp": mean_pmoo_fp,
        "mean_msob": mean_msob,
        "median_improved_pmoo_fp": median_improved_pmoo_fp,
        "median_improved_server_bound": median_improved_server_bound,
        "number standard bound is valid": number_standard_bound_valid,
        "number server bound is valid": number_server_bound_valid,
        "number PMOO_FP bound is valid": number_pmoo_fp_valid,
        "number server bound is improvement": number_improved_server_bound,
        "number PMOO_FP is improvement": number_improved_pmoo_fp,
        "valid iterations": valid_iterations,
        "number standard bound is best": standard_best,
        "number server bound is best": msob_best,
        "number PMOO_FP bound is best": fp_best,
    })

    filename = title
    filename += f"_optimal_{perform_param.to_name()}_{arrival_enum.name}_" \
                f"MC{mc_dist.to_name()}_{opt_method.name}_" \
                f"{compare_metric.name}"

    with open(filename + ".csv", 'w') as csv_file:
        writer = csv.writer(csv_file)
        for key, value in opt_dict.items():
            writer.writerow([key, value])

    return res_dict
def csv_fat_cross_param_power(arrival_enum: ArrivalEnum, number_servers: int,
                              perform_param: PerformParameter,
                              opt_method: OptMethod,
                              mc_dist: MonteCarloDist) -> dict:
    """Chooses parameters by Monte Carlo type random choice."""
    total_iterations = 10**4
    valid_iterations = total_iterations
    metric = "relative"

    size_array = [
        total_iterations,
        (arrival_enum.number_parameters() + 1) * number_servers
        # const_rate has 1 parameter
    ]
    # [rows, columns]

    param_array = mc_enum_to_dist(mc_dist=mc_dist, size=size_array)

    res_array = np.empty([total_iterations, 2])

    # print(res_array)

    for i in tqdm(range(total_iterations), total=total_iterations):
        if arrival_enum == ArrivalEnum.DM1:
            arrive_list = [
                DM1(lamb=param_array[i, j]) for j in range(number_servers)
            ]

        elif arrival_enum == ArrivalEnum.MD1:
            arrive_list = [
                MD1(lamb=param_array[i, j],
                    mu=1 / (param_array[i,
                                        arrival_enum.number_parameters() *
                                        number_servers + j]))
                for j in range(number_servers)
            ]

        elif arrival_enum == ArrivalEnum.MMOO:
            arrive_list = [
                MMOOFluid(mu=param_array[i, j],
                          lamb=param_array[i, number_servers + j],
                          burst=param_array[i, 2 * number_servers + j])
                for j in range(number_servers)
            ]

        elif arrival_enum == ArrivalEnum.EBB:
            arrive_list = [
                EBB(factor_m=param_array[i, j],
                    decay=param_array[i, number_servers + j],
                    rho_single=param_array[i, 2 * number_servers + j])
                for j in range(number_servers)
            ]

        elif arrival_enum == ArrivalEnum.MassOne:
            arrive_list = [
                LeakyBucketMassOne(sigma_single=param_array[i, j],
                                   rho_single=param_array[i,
                                                          number_servers + j],
                                   n=20) for j in range(number_servers)
            ]
            # TODO: note that n is fixed

        elif arrival_enum == ArrivalEnum.TBConst:
            arrive_list = [
                TokenBucketConstant(sigma_single=param_array[i, j],
                                    rho_single=param_array[i,
                                                           number_servers + j],
                                    n=1) for j in range(number_servers)
            ]

        else:
            raise NameError("Arrival parameter {0} is infeasible".format(
                arrival_enum.name))

        if arrival_enum == ArrivalEnum.MD1 or arrival_enum == ArrivalEnum.MM1:
            service_list = [
                ConstantRate(rate=1.0) for j in range(number_servers)
            ]
        else:
            service_list = [
                ConstantRate(
                    rate=param_array[i,
                                     arrival_enum.number_parameters() *
                                     number_servers + j])
                for j in range(number_servers)
            ]

        setting = FatCrossPerform(arr_list=arrive_list,
                                  ser_list=service_list,
                                  perform_param=perform_param)

        # print(res_array[i, ])

        # standard_bound, new_bound = compute_improvement()
        res_array[i, 0], res_array[i, 1] = compute_improvement(
            setting=setting,
            opt_method=opt_method,
            number_l=number_servers - 1)

        if perform_param.perform_metric == PerformEnum.DELAY_PROB:
            if res_array[i, 1] > 1.0:
                res_array[i, ] = nan

        if res_array[i, 0] == nan or res_array[i, 1] == nan:
            res_array[i, ] = nan
            valid_iterations -= 1

    res_dict = two_col_array_to_results(arrival_enum=arrival_enum,
                                        param_array=param_array,
                                        res_array=res_array,
                                        number_servers=number_servers,
                                        valid_iterations=valid_iterations,
                                        metric=metric)

    res_dict.update({
        "iterations": total_iterations,
        "T": perform_param.value,
        "optimization": opt_method.name,
        "metric": metric,
        "MCDistribution": mc_dist.to_name(),
        "MCParam": mc_dist.param_to_string(),
        "number_servers": number_servers
    })

    with open(
            "sim_{0}_{1}_results_MC{2}_{3}_{4}.csv".format(
                perform_param.to_name(), arrival_enum.name, mc_dist.to_name(),
                opt_method.name, metric), 'w') as csv_file:
        writer = csv.writer(csv_file)
        for key, value in res_dict.items():
            writer.writerow([key, value])

    return res_dict