Ejemplo n.º 1
0
    def test_simple_reentrant_line_model_scaled_rates(self):
        """Example 4.2.3, Figure 2.9  from CTCN book."""
        alpha1 = 1
        mu1 = 2.1
        mu2 = 1.1
        mu3 = mu1
        env = envex.simple_reentrant_line_model(alpha1=alpha1, mu1=mu1, mu2=mu2, mu3=mu3)
        max_mu = mu1
        alpha1 /= (max_mu*(1 + env.job_generator.add_max_rate))
        mu1 /= (max_mu*(1 + env.job_generator.add_max_rate))
        mu2 /= (max_mu*(1 + env.job_generator.add_max_rate))
        mu3 /= (max_mu*(1 + env.job_generator.add_max_rate))

        # Compute load, workload and sensitivity vectors sorted by load
        load, workload_mat, nu = wl.compute_load_workload_matrix(env)

        # Theoretical results from computation by hand
        load_theory = alpha1 * np.array([1/mu1 + 1/mu3, 1/mu2])
        workload_theory = np.array([[1./mu1 + 1./mu3, 1./mu3, 1./mu3],
                                    [1./mu2, 1./mu2, 0.]])
        nu_theory = np.array([[1, 0],
                              [0, 1]])
        # Sort the theoretical results by load and keep only the first num_wl_vec
        sort_index = np.argsort(load_theory)[::-1]
        load_theory = load_theory[sort_index]
        workload_theory = workload_theory[sort_index, :]
        nu_theory = nu_theory[sort_index, :]
        # Compare results with theoretical results
        np.testing.assert_almost_equal(load, load_theory, decimal=6)
        np.testing.assert_almost_equal(workload_mat, workload_theory, decimal=6)
        np.testing.assert_almost_equal(nu, nu_theory, decimal=6)
def study_workload_space(env, num_wl_vec):
    """
    Compute and returns the workload tuple, including the workload matrix and the load, all the
    c_bar vectors, and the intersection of the planes defined by the c_bar vectors.

    :param env: CRW environment object.
    :param num_wl_vec: Number of workload vectors.
    :return: (workload_tuple, workload_space, c_bar_vectors_vertexes, w_int)
    """
    workload_tuple = wl.compute_load_workload_matrix(env,
                                                     num_wl_vec=num_wl_vec)
    workload_space = wl.describe_workload_space_as_halfspaces(
        workload_tuple.workload_mat)

    min_drain_time, z = compute_minimal_draining_time_cvxpy(
        env.job_generator.demand_rate, env.constituency_matrix, 0,
        env.job_generator.buffer_processing_matrix)

    c_bar_vectors = utils.get_all_effective_cost_linear_vectors(
        workload_tuple.workload_mat, env.cost_per_buffer)

    int_c_bar_workload_space = find_intersection_level_set_with_workload_space(
        workload_tuple.workload_mat, c_bar_vectors)

    try:
        int_c_bar = find_intersection_c_bar_hyperplanes(c_bar_vectors)
    except:
        int_c_bar = []

    return (workload_tuple, workload_space, min_drain_time, z, c_bar_vectors,
            int_c_bar_workload_space, int_c_bar)
Ejemplo n.º 3
0
    def test_simple_reentrant_line_model(self):
        """Example 4.2.3, Figure 2.9  from CTCN book."""
        alpha1 = 0.3
        mu1 = 0.67
        mu2 = 0.35
        mu3 = 0.67
        env = envex.simple_reentrant_line_model(alpha1=alpha1, mu1=mu1, mu2=mu2, mu3=mu3)

        # Compute load, workload and sensitivity vectors sorted by load
        load, workload_mat, nu = wl.compute_load_workload_matrix(env)

        # Theoretical results from computation by hand
        load_theory = np.array([alpha1/mu1 + alpha1/mu3,
                                alpha1/mu2])
        workload_theory = np.array([[1./mu1 + 1./mu3, 1./mu3, 1./mu3],
                                    [1./mu2, 1./mu2, 0.]])
        nu_theory = np.array([[1, 0],
                              [0, 1]])
        # Sort the theoretical results by load and keep only the first num_wl_vec
        sort_index = np.argsort(load_theory)[::-1]
        load_theory = load_theory[sort_index]
        workload_theory = workload_theory[sort_index, :]
        nu_theory = nu_theory[sort_index, :]
        # Compare results with theoretical results
        np.testing.assert_almost_equal(load, load_theory, decimal=6)
        np.testing.assert_almost_equal(workload_mat, workload_theory, decimal=6)
        np.testing.assert_almost_equal(nu, nu_theory, decimal=6)
def test_zero_workloads_input():
    cost_per_buffer = np.array([1.5, 1, 2])[:, None]
    env = examples.simple_reentrant_line_model(alpha1=0.33,
                                               mu1=0.68,
                                               mu2=0.35,
                                               mu3=0.68,
                                               cost_per_buffer=cost_per_buffer)
    num_wl_vec = 2
    load, workload_mat, _ = wl.compute_load_workload_matrix(env, num_wl_vec)
    strategic_idling_params = StrategicIdlingParams()
    horizon = 100

    si_object = StrategicIdlingCoreHorizon(workload_mat, load, cost_per_buffer,
                                           env.model_type, horizon,
                                           strategic_idling_params)

    si_gto_object = StrategicIdlingGTOHorizon(workload_mat, load,
                                              cost_per_buffer, env.model_type,
                                              horizon, strategic_idling_params)

    x_zero = np.array([[0], [0], [0]])
    si_output = si_object.get_allowed_idling_directions(x_zero)
    assert np.all(si_output.w_star >= 0)

    si_output = si_gto_object.get_allowed_idling_directions(x_zero)
    assert np.all(si_output.w_star >= 0)
def get_environment(env_name: str,
                    agent_name: str,
                    episode_len_to_min_drain_time_ratio: float,
                    terminal_discount_factor: float = 0.7,
                    action_repetitions: int = 1,
                    parallel_environments: int = 8,
                    env_overload_params: Optional[Dict] = None,
                    agent_params: Optional[Dict] = None,
                    seed: Optional[int] = None) \
        -> Tuple[TFPyEnvironment, float, float, int, Tuple[int, ...]]:
    """
    Builds and initialises a TensorFlow environment implementation of the Single Server Queue.

    :param env_name: The name of the scenario to load. Must be in the list of implemented scenarios.
    :param agent_name: The name of the RL agent the environment is to be set up for.
    :param episode_len_to_min_drain_time_ratio: Maximum number of time steps per episode as a
        proportion of the minimal draining time.
    :param terminal_discount_factor: The discount applied to the final time step from which a
        per-step discount factor is calculated.
    :param action_repetitions: Number of time steps each selected action is repeated for.
    :param parallel_environments: Number of environments to run in parallel.
    :param env_overload_params: Dictionary of parameters to override the scenario defaults.
    :param agent_params: Optional dictionary of agent parameters the environment can be adapted for.
    :param seed: Random seed used to initialise the environment.
    :return: The environment wrapped and ready for TensorFlow Agents.
    """
    # Handle some default argument clean up.
    if env_overload_params is None:
        env_overload_params = {}

    env = scenarios.load_scenario(env_name, seed, env_overload_params).env

    if np.all(env.state_initialiser.initial_state == 0):
        env.max_episode_length = 450
    else:
        if env.state_initialiser.initial_state.ndim == 1:
            initial_state = env.state_initialiser.initial_state.reshape((-1, 1))
        else:
            initial_state = env.state_initialiser.initial_state
        minimal_draining_time = compute_minimal_draining_time_from_env_cvxpy(initial_state, env)
        env.max_episode_length = int(episode_len_to_min_drain_time_ratio * minimal_draining_time)
    discount_factor = np.exp(np.log(terminal_discount_factor) / env.max_episode_length)
    load = np.max(compute_load_workload_matrix(env).load)
    max_ep_len = env.max_episode_length

    # Allow toggling of observation normalisation in the environment.
    # The typical behaviour for PPO is that PPO normalises observations internally as necessary so
    # normalisation in the environment is not necessary.
    if agent_name == 'ppo' and agent_params.get('normalize_observations', True):
        normalise_obs_in_env = False
    else:
        normalise_obs_in_env = True

    # Wrap and parallelise environment for tf agents.
    tf_env, action_dims = rl_env_from_snc_env(env,
                                              discount_factor,
                                              action_repetitions,
                                              parallel_environments,
                                              normalise_observations=normalise_obs_in_env)
    return tf_env, discount_factor, load, max_ep_len, action_dims
Ejemplo n.º 6
0
def test_return_workload_vectors_based_on_medium_load_threshold_simple_routing_model():
    """Example 6.2.2. from CTCN online, Example 6.2.4 from printed version. Returns workload vectors
    that correspond with threshold > 0.6."""
    alpha_r = 0.18
    mu1 = 0.13
    mu2 = 0.07
    mu_r = 0.3

    env = envex.simple_routing_model(alpha_r, mu1, mu2, mu_r)

    # Compute load, workload and sensitivity vectors sorted by load
    load_threshold = 0.6
    load, workload, nu = wl.compute_load_workload_matrix(env, load_threshold=load_threshold)

    # Compare results with theoretical result from CTCN book for load >= value.
    load_theory = np.array([alpha_r / (mu1 + mu2),
                            alpha_r / mu_r])
    workload_theory = np.vstack((1 / (mu1 + mu2) * np.ones(env.num_buffers),
                                 [0, 0, 1/mu_r]))
    nu_theory = np.array([[mu1 / (mu1 + mu2), mu2 / (mu1 + mu2), 0],
                          [0, 0, 1]])

    np.testing.assert_almost_equal(load, load_theory, decimal=6)
    np.testing.assert_almost_equal(workload, workload_theory, decimal=6)
    np.testing.assert_almost_equal(nu, nu_theory, decimal=6)
Ejemplo n.º 7
0
def test_compute_network_load_and_workload_klimov_model():
    """Example 4.2.1. Klimov model form CTCN online"""
    alpha1 = .1
    alpha2 = .12
    alpha3 = .13
    alpha4 = .14
    mu1 = 0.6
    mu2 = 0.7
    mu3 = 0.8
    mu4 = 0.9

    env = envex.klimov_model(alpha1, alpha2, alpha3, alpha4, mu1, mu2, mu3, mu4)

    # Compute load, workload and sensitivity vectors sorted by load
    load, workload, nu = wl.compute_load_workload_matrix(env)
    # Compute network load and associated workload and sensitivity vectors. Useful to validate that
    # we obtain the same results with two different methods.
    network_load, xi_bottleneck, nu_bottleneck, constraints \
        = alt_methods_test.compute_network_load_and_bottleneck_workload(env)
    # Third method of computing the network load:
    network_load_bis = alt_methods_test.compute_network_load(env)

    # Compare the three methods of obtaining the network load.
    np.testing.assert_almost_equal(network_load, network_load_bis, decimal=6)
    np.testing.assert_almost_equal(network_load, load[0], decimal=6)
    np.testing.assert_almost_equal(xi_bottleneck.value, np.reshape(workload[0, :],
                                                                   (env.num_buffers, 1)), decimal=6)
    # Compare results with theoretical result from CTCN book.
    np.testing.assert_almost_equal(load, alpha1/mu1 + alpha2/mu2 + alpha3/mu3 + alpha4/mu4,
                                   decimal=6)
    np.testing.assert_almost_equal(workload, np.array([[1/mu1, 1/mu2, 1/mu3, 1/mu4]]), decimal=6)
Ejemplo n.º 8
0
def test_get_policy_examples_two_alternative_methods_multiple_horizons(
        ex_env, horizon):
    seed = 42
    np.random.seed(seed)

    env = eval('examples.' + ex_env)(job_gen_seed=seed)

    kappa_w = 1e2
    kappa = 1e2
    safety_stocks_vec = 2 * np.ones((env.num_resources, 1))
    policy_params = BigStepPenaltyPolicyParams('cvx.CPLEX', False, kappa_w,
                                               kappa)

    load, workload_mat, nu = workload.compute_load_workload_matrix(env)
    num_wl_vec = workload_mat.shape[0]
    k_idling_set = np.random.randint(0, workload_mat.shape[0], 1)
    draining_bottlenecks = set(np.random.randint(0, workload_mat.shape[0], 1))

    state = 20 * np.ones((env.num_buffers, 1))

    policy_cvx = BigStepPolicy(env.cost_per_buffer, env.constituency_matrix,
                               env.job_generator.demand_rate,
                               env.job_generator.buffer_processing_matrix,
                               workload_mat, nu,
                               env.list_boundary_constraint_matrices,
                               env.ind_surplus_buffers, policy_params)
    kwargs = {
        'safety_stocks_vec': safety_stocks_vec,
        'k_idling_set': k_idling_set,
        'draining_bottlenecks': draining_bottlenecks,
        'horizon': horizon
    }
    z_star_cvx, opt_val_cvx = policy_cvx.get_policy(state, **kwargs)

    allowed_activities = get_allowed_activities(policy_cvx, num_wl_vec,
                                                k_idling_set)
    z_star_scipy, opt_val_scipy = policy_utils.feedback_policy_nonidling_penalty_scipy(
        state,
        env.cost_per_buffer,
        env.constituency_matrix,
        env.job_generator.buffer_processing_matrix,
        workload_mat,
        safety_stocks_vec,
        k_idling_set,
        draining_bottlenecks,
        kappa_w,
        env.list_boundary_constraint_matrices,
        allowed_activities,
        method='revised simplex',
        demand_rate=env.job_generator.demand_rate,
        horizon=horizon)

    # SciPy has safety stock constraints, while CVX has a safety stock penalty.
    opt_val_cvx = (
        env.cost_per_buffer.T @ env.job_generator.buffer_processing_matrix
        @ z_star_cvx)[0]
    opt_val_scipy = (
        env.cost_per_buffer.T @ env.job_generator.buffer_processing_matrix
        @ z_star_scipy[:, None])[0]
    np.testing.assert_allclose(opt_val_cvx, opt_val_scipy, rtol=5e-2)
Ejemplo n.º 9
0
def test_compute_network_load_and_workload_simple_reentrant_line_model():
    """Example 4.2.3. Simple re-entrant line from CTCN online."""
    alpha1 = 0.3
    mu1 = 0.67
    mu2 = 0.35
    mu3 = 0.67

    env = envex.simple_reentrant_line_model(alpha1, mu1, mu2, mu3)

    # Compute load, workload and sensitivity vectors sorted by load
    load, workload, nu = wl.compute_load_workload_matrix(env)
    # Compute network load and associated workload and sensitivity vectors. Useful to validate that
    # we obtain the same results with two different methods.
    network_load, xi_bottleneck, nu_bottleneck, constraints \
        = alt_methods_test.compute_network_load_and_bottleneck_workload(env)
    # Third method of computing the network load:
    network_load_bis = alt_methods_test.compute_network_load(env)

    # Compare the three methods of obtaining the network load.
    np.testing.assert_almost_equal(network_load, network_load_bis, decimal=6)
    np.testing.assert_almost_equal(network_load, load[0], decimal=6)
    np.testing.assert_almost_equal(xi_bottleneck.value, np.reshape(workload[0, :],
                                                                   (env.num_buffers, 1)), decimal=6)
    # Compare results with theoretical result from CTCN book.
    load_theory = np.array([alpha1 * (1/mu1 + 1/mu3), alpha1/mu2])
    workload_theory = np.vstack(([1/mu1 + 1/mu3, 1/mu3, 1/mu3],
                                 [1/mu2, 1/mu2, 0]))
    np.testing.assert_almost_equal(load, load_theory, decimal=6)
    np.testing.assert_almost_equal(workload, workload_theory, decimal=6)
Ejemplo n.º 10
0
def test_three_station_network_model():
    """Example 5.3.2 from CTCN online (Example 5.3.5 from printed version). Figure 5.2."""
    env = envex.three_station_network_model()

    # Compute load, workload and sensitivity vectors sorted by load
    load, workload, nu = wl.compute_load_workload_matrix(env)
    # Third method of computing the network load:
    network_load_bis = alt_methods_test.compute_network_load(env)
    # Compute network load and associated workload and sensitivity vectors. Useful to validate that
    # we obtain the same results with two different methods.
    network_load, xi_bottleneck, nu_bottleneck, constraints \
        = alt_methods_test.compute_network_load_and_bottleneck_workload(env)

    workload_theory = np.array([[1, 1, 0, 1, 0, 1], [1, 0, 1, 1, 0, 1], [1, 0, 1, 0, 1, 1]])
    # Due to numerical noise, different computers can obtain the barc vectors in different order.
    # So we will compare sets instead of ndarrays.
    np.around(workload, decimals=6, out=workload)
    np.around(workload_theory, decimals=6, out=workload_theory)
    workload_set = set(map(tuple, workload))
    workload_theory_set = set(map(tuple, workload_theory))

    # Compare the three methods of obtaining the network load.
    np.testing.assert_almost_equal(network_load, network_load_bis, decimal=6)
    np.testing.assert_almost_equal(network_load, load[0], decimal=6)
    # We don't compare the two methods of obtaining the bottleneck workload for zero demand.
    # Compare workload with theoretical result from CTCN book.
    assert workload_set == workload_theory_set
Ejemplo n.º 11
0
def test_compute_network_load_and_workload_dai_wang_model():
    """Example 4.6.4 and Figure 4.15 from CTCN online ed."""

    alpha1 = 0.2
    mu1 = 0.66
    mu2 = mu1
    mu3 = 0.42
    mu4 = mu3
    mu5 = mu1
    env = envex.dai_wang_model(alpha1=alpha1, mu1=mu1, mu2=mu2, mu3=mu3, mu4=mu4, mu5=mu5)
    load_theory = np.array([2 * alpha1/mu3, 3 * alpha1/mu1])
    workload_theory = np.array([[1/mu3 + 1/mu4, 1/mu3 + 1/mu4, 1/mu3 + 1/mu4, 1/mu4, 0],
                                [1/mu1 + 1/mu2 + 1/mu5, 1/mu2 + 1/mu5, 1/mu5, 1/mu5, 1/mu5]])

    # Compute load, workload and sensitivity vectors sorted by load
    load, workload, nu = wl.compute_load_workload_matrix(env)
    # Compute network load and associated workload and sensitivity vectors. Useful to validate that
    # we obtain the same results with two different methods.
    network_load, xi_bottleneck, nu_bottleneck, constraints \
        = alt_methods_test.compute_network_load_and_bottleneck_workload(env)
    # Third method of computing the network load:
    network_load_bis = alt_methods_test.compute_network_load(env)

    # Compare the three methods of obtaining the network load.
    np.testing.assert_almost_equal(network_load, network_load_bis, decimal=6)
    np.testing.assert_almost_equal(network_load, load[0], decimal=6)
    np.testing.assert_almost_equal(xi_bottleneck.value, np.reshape(workload[0, :],
                                                                   (env.num_buffers, 1)), decimal=6)
    # Compare results with theoretical result from CTCN book.
    np.testing.assert_almost_equal(load, load_theory, decimal=6)
    np.testing.assert_almost_equal(workload, workload_theory, decimal=6)
Ejemplo n.º 12
0
def test_compute_network_load_and_workload_ksrs_network_model_different_parameters():
    """Trying different parameters for Example 4.2.4. KSRS model from CTCN online."""
    alpha1 = 0.4
    alpha3 = 0.2
    mu1 = 0.7
    mu2 = 0.75
    mu3 = 0.8
    mu4 = 0.8

    env = envex.ksrs_network_model(alpha1, alpha3, mu1, mu2, mu3, mu4)

    # Compute load, workload and sensitivity vectors sorted by load
    load, workload, nu = wl.compute_load_workload_matrix(env)
    # Compute network load and associated workload and sensitivity vectors. Useful to validate that
    # we obtain the same results with two different methods.
    network_load, xi_bottleneck, nu_bottleneck, constraints \
        = alt_methods_test.compute_network_load_and_bottleneck_workload(env)
    # Third method of computing the network load:
    network_load_bis = alt_methods_test.compute_network_load(env)

    workload_theory = np.array([[1 / mu1, 0, 1 / mu4, 1 / mu4], [1 / mu2, 1 / mu2, 1 / mu3, 0]])
    # Due to numerical noise, different computers can obtain the barc vectors in different order.
    # So we will compare sets instead of ndarrays.
    np.around(workload, decimals=6, out=workload)
    np.around(workload_theory, decimals=6, out=workload_theory)
    workload_set = set(map(tuple, workload))
    workload_theory_set = set(map(tuple, workload_theory))
    assert workload_set == workload_theory_set

    # Compare the three methods of obtaining the network load.
    np.testing.assert_almost_equal(network_load, network_load_bis, decimal=6)
    np.testing.assert_almost_equal(network_load, load[0], decimal=6)
    # Compare the two methods of obtaining the bottleneck workload.
    np.testing.assert_almost_equal(xi_bottleneck.value, np.reshape(workload[0, :],
                                                                   (env.num_buffers, 1)), decimal=6)
Ejemplo n.º 13
0
def prepare_describe_workload_space_as_halfspaces_test(num_wl_vec=None):
    env = examples.simple_link_constrained_model(
        mu13=4, alpha1=4.8, mu12=2, mu25=2, mu32=3, mu34=1.7, mu35=2, mu45=1, mu5=5.2,
        cost_per_buffer=np.array([[1], [0.5], [4], [2], [4]]))
    workload_tuple = wl.compute_load_workload_matrix(env, num_wl_vec, use_cdd_for_vertices=True)
    workload_space = wl.describe_workload_space_as_halfspaces(workload_tuple.workload_mat)

    return workload_space, workload_tuple, env
Ejemplo n.º 14
0
def test_three_workload_vectors_in_complex_demand_driven_model():
    """Example 7.2.3, Figure 7.5, from CTCN."""
    params = {'d1': 19/75, 'd2': 19/75, 'mu1': 13/15, 'mu2': 26/15, 'mu3': 13/15, 'mu4': 26/15,
              'mu5': 1, 'mu6': 2, 'mu7': 1, 'mu8': 2, 'mu9': 1, 'mu10a': 1/3, 'mu10b': 1/3,
              'mu11': 1/2, 'mu12': 1/10, 'mud1': 100, 'mus1': 100, 'mud2': 100, 'mus2': 100}
    scaling_factor = 100
    params = {k: v/scaling_factor for k, v in params.items()}
    env = envex.complex_demand_driven_model(**params)

    # Compute load, workload and sensitivity vectors sorted by load
    num_wl_vec = 4
    load, workload, nu = wl.compute_load_workload_matrix(env, num_wl_vec)

    # Compare results with first two vectors given by from CTCN book.
    workload_book_t = np.array(
        [
            [0, 0, 0],
            [0, 0, 0],
            [-0.58, -0.5, -3.03],
            [-1.15, -2, 0],
            [-1.15, 0, 0],
            [-0.58, 0, 0],
            [-1.73, -0.5, -3.03],
            [-0.58, -0.26, -3.03],
            [-1.15, -1, 0],
            [-0.58, -0.5, 0],
            [-0.58, -0.5, -3.03],
            [-0.58, -0.5, 0],
            [-2.02, -1.75, -3.79],
            [-1.73, -2, 0],
            [2.02, 1.75, 3.79],  # Book says [2.01, 1.75, 3.79]
            [1.73, 2, 0]
        ]
    )
    workload_book_t *= scaling_factor

    # network_load, xi_bottleneck, nu_bottleneck, constraints \
    #     = alt_methods_test.compute_network_load_and_bottleneck_workload(env)

    # We obtain 4 bottlenecks. But the compute_vertex routing is susceptible of numerical noise.
    # Check that they all have same network load equal to the one in the book: 0.95.
    np.testing.assert_almost_equal(load, 0.95)

    # Due to numerical noise, different computers can obtain the barc vectors in different order.
    # So we will compare sets instead of ndarrays.
    np.around(workload, out=workload)

    # We don't get similar but not exactly the same workload vectors as in the book. Most of the
    # values are equal, but there are some that are slightly different. I asked Sean and he said
    # that this example was done by his PhD student several years ago and that he cannot confirm
    # whether the values are correct. So we will compare only with the first workload vector, which
    # only differs from one of the computed workload vectors in the second decimal of one of the
    # values.
    equal = False
    for w in workload:
        equal |= np.allclose(workload_book_t.T[0, :], w)
    assert equal
Ejemplo n.º 15
0
def test_physical_resources_to_workload_simple_routing_model():
    # For this model, there should be a pooled resource
    env = examples.simple_routing_model(
        alpha_r=0.2, mu1=0.13, mu2=0.07, mu_r=0.2, cost_per_buffer=np.ones((3, 1)),
        initial_state=(1, 1, 1), capacity=np.ones((3, 1)) * np.inf, job_conservation_flag=True,
        job_gen_seed=42)
    nu = wl.compute_load_workload_matrix(env=env, num_wl_vec=None, load_threshold=None,
                                         feasible_tol=1e-10).nu
    phys_resources_to_wl_index = validation_utils.workload_to_physical_resources_index(nu)
    assert np.all(np.array([[2], [0, 1], [1], [0]]) == phys_resources_to_wl_index)
Ejemplo n.º 16
0
def test_limited_workload_vectors_in_three_station_network_model():
    """Example 5.3.2 from CTCN online (Example 5.3.5 from printed version). Figure 5.2. This test
    specifies that only one workload vector should be returned."""
    env = envex.three_station_network_model()

    # Compute load, workload and sensitivity vectors sorted by load
    num_wl_vec = 1
    load, workload, nu = wl.compute_load_workload_matrix(env, num_wl_vec)

    # Compare results with first vector given by from CTCN book.
    np.testing.assert_almost_equal(workload, np.array([[1, 1, 0, 1, 0, 1]]), decimal=6)

    # Compute load, workload and sensitivity vectors sorted by load
    num_wl_vec = 2
    load, workload, nu = wl.compute_load_workload_matrix(env, num_wl_vec)

    # Compare results with first two vectors given by from CTCN book.
    np.testing.assert_almost_equal(workload, np.array([[1, 1, 0, 1, 0, 1],
                                                       [1, 0, 1, 1, 0, 1]]), decimal=6)
Ejemplo n.º 17
0
def test_physical_resources_to_workload_index_simple_reentrant_line():
    # For this model and these parameters, the second resource should have higher load and so should
    # correspond to the first workload
    env = examples.simple_reentrant_line_model(
        alpha1=9, mu1=22, mu2=10, mu3=22, cost_per_buffer=np.ones((3, 1)), initial_state=(0, 0, 0),
        capacity=np.ones((3, 1)) * np.inf, job_conservation_flag=True, job_gen_seed=42)
    nu = wl.compute_load_workload_matrix(env=env, num_wl_vec=None, load_threshold=None,
                                         feasible_tol=1e-10).nu
    phys_resources_to_wl_index = validation_utils.workload_to_physical_resources_index(nu)
    assert np.all(np.array([[1], [0]]) == phys_resources_to_wl_index)
    def perform_test(alpha1, mu1, mu2, mu3, cost_per_buffer, num_wl_vec, w):
        """ Test for the simple re-entrant line (see example 4.2.3, Figure 2.9  from CTCN book)
        given parameters of the environment, the relaxation and a specific point w. """

        assert num_wl_vec == 2  # The test is only implemented without relaxation

        env = examples.simple_reentrant_line_model(
            alpha1=alpha1,
            mu1=mu1,
            mu2=mu2,
            mu3=mu3,
            cost_per_buffer=cost_per_buffer)

        # We define the theoretical workload matrix and compare it to the one we compute in order to
        # find which relaxation we are doing
        # Theoretical workload matrix and load
        workload_mat_theory = np.array(
            [[1. / mu1 + 1. / mu3, 1. / mu3, 1. / mu3],
             [1. / mu2, 1. / mu2, 0.]])
        load_theory = np.array([alpha1 / mu1 + alpha1 / mu3, alpha1 / mu2])
        # Computed workload matrix (sorted by load)
        _, workload_mat, _ = wl.compute_load_workload_matrix(env, num_wl_vec)

        # Theoretical vertexes of the \bar{c} feasible region based on the dim of the relaxation
        vertexes_2d = np.array(
            [[
                mu3 * cost_per_buffer[2], mu2 * cost_per_buffer[0] -
                cost_per_buffer[2] * mu2 * (mu3 / mu1 + 1.)
            ],
             [
                 mu1 * (cost_per_buffer[0] - cost_per_buffer[1]),
                 mu2 * cost_per_buffer[1] + (mu1 * mu2 / mu3) *
                 (cost_per_buffer[1] - cost_per_buffer[0])
             ],
             [
                 mu3 * cost_per_buffer[2],
                 mu2 * (cost_per_buffer[1] - cost_per_buffer[2])
             ]])
        # We select which vertexes are feasible based on the env parameters
        if mu1 * (cost_per_buffer[0] -
                  cost_per_buffer[1]) <= mu3 * cost_per_buffer[2]:
            feasible_vertexes = vertexes_2d[[0, 1], :, :]
        else:
            feasible_vertexes = vertexes_2d[[2], :, :]
        # The theoretical \bar{c} vectors were computed for a specific order of the workload
        # vectors. So we compute sort_by_load_index to be able to reorder the theoretical
        # \bar{c} components based on the sort made by load
        sort_by_load_index = np.argsort(load_theory)[::-1]
        feasible_vertexes = feasible_vertexes[:, sort_by_load_index, :]
        # Compute the index of the theoretical vertex which satisfy the max
        max_vertex_index = np.argmax(np.dot(w.T, feasible_vertexes).flatten())
        barc_theory = feasible_vertexes[max_vertex_index]
        barc, _, _ = alt_methods_test.compute_dual_effective_cost_cvxpy(
            w, workload_mat, cost_per_buffer, method='cvx.ECOS')
        np.testing.assert_almost_equal(barc, barc_theory, decimal=4)
Ejemplo n.º 19
0
def get_layered_policy_object_for_simple_link_constrained_model(env):
    _, workload_mat, nu = workload.compute_load_workload_matrix(env, 6)
    policy_params = BigStepLayeredPolicyParams('cvx.CPLEX')
    policy_obj = BigStepLayeredPolicy(
        env.cost_per_buffer, env.constituency_matrix,
        env.job_generator.demand_rate,
        env.job_generator.buffer_processing_matrix, env.workload_mat, env.nu,
        env.list_boundary_constraint_matrices, env.ind_surplus_buffers,
        policy_params)

    return policy_obj
Ejemplo n.º 20
0
def test_compute_network_load_and_workload_simple_routing_model():
    """Example 6.2.2. from CTCN online, Example 6.2.4 from printed version."""
    alpha_r = 0.18
    mu1 = 0.13
    mu2 = 0.07
    mu_r = 0.3

    env = envex.simple_routing_model(alpha_r, mu1, mu2, mu_r)

    # Compute load, workload and sensitivity vectors sorted by load
    load, workload, nu = wl.compute_load_workload_matrix(env)
    # Compute network load and associated workload and sensitivity vectors. Useful to validate that
    # we obtain the same results with two different methods.
    network_load, xi_bottleneck, nu_bottleneck, constraints \
        = alt_methods_test.compute_network_load_and_bottleneck_workload(env)
    # Third method of computing the network load:
    network_load_bis = alt_methods_test.compute_network_load(env)

    # Compare the two methods of obtaining the network load, directly and as the highest load.
    np.testing.assert_almost_equal(network_load, network_load_bis, decimal=6)
    np.testing.assert_almost_equal(network_load, load[0], decimal=6)
    np.testing.assert_almost_equal(xi_bottleneck.value, np.reshape(workload[0, :],
                                                                   (env.num_buffers, 1)), decimal=6)
    # Compare results with theoretical result from CTCN book, for the 4 resources.
    load_theory = np.array([alpha_r / (mu1 + mu2),
                            alpha_r / mu_r,
                            0,
                            0])
    workload_theory = np.vstack((1 / (mu1 + mu2) * np.ones(env.num_buffers),
                                 [0, 0, 1/mu_r],
                                 [0, 1 / mu2, 0],
                                 [1/mu1, 0, 0]))
    nu_theory = np.array([[mu1 / (mu1 + mu2), mu2 / (mu1 + mu2), 0],
                          [0, 0, 1],
                          [0, 1, 0],
                          [1, 0, 0]])

    # Due to numerical noise, different computers can obtain the barc vectors in different order.
    # So we will compare sets instead of ndarrays.
    np.around(workload, decimals=6, out=workload)
    np.around(workload_theory, decimals=6, out=workload_theory)
    np.around(nu, decimals=6, out=nu)
    np.around(nu_theory, decimals=6, out=nu_theory)
    workload_set = set(map(tuple, workload))
    workload_theory_set = set(map(tuple, workload_theory))
    nu_set = set(map(tuple, nu))
    nu_theory_set = set(map(tuple, nu_theory))

    np.testing.assert_almost_equal(load, load_theory, decimal=6)
    assert workload_set == workload_theory_set
    assert nu_set == nu_theory_set
Ejemplo n.º 21
0
def test_get_forbidden_actions_per_bottleneck_loop_2_queues():
    env = examples.loop_2_queues(mu1=1,
                                 mu12=0.5,
                                 mu2=0.8,
                                 mu21=1,
                                 cost_per_buffer=np.array([[1], [0.1]]),
                                 demand_rate=np.array([[0.1], [0.95]]))
    load, workload_mat, nu = workload.compute_load_workload_matrix(env)
    xi_s = workload_mat[0, :]  # Pooled resource.
    nu_s = nu[0, :]  # Pooled resource.
    fa_s = BigStepPolicy.get_forbidden_activities_per_bottleneck(
        xi_s, nu_s, env.job_generator.buffer_processing_matrix,
        env.constituency_matrix)
    assert fa_s == [1]  # We cannot send stuff back through mu12.
Ejemplo n.º 22
0
def test_return_no_workload_vectors_due_on_too_high_load_threshold_simple_routing_model():
    """Example 6.2.2. from CTCN online, Example 6.2.4 from printed version. Returns workload vectors
    that correspond with threshold > 0.99, but there are none."""
    alpha_r = 0.18
    mu1 = 0.13
    mu2 = 0.07
    mu_r = 0.3

    env = envex.simple_routing_model(alpha_r, mu1, mu2, mu_r)

    # Compute load, workload and sensitivity vectors sorted by load
    load_threshold = 0.99
    load, workload, nu = wl.compute_load_workload_matrix(env, load_threshold=load_threshold)

    assert load.size == workload.size == nu.size == 0
Ejemplo n.º 23
0
def test_compute_network_load_and_workload_simple_reentrant_line_model_only_one_vector_required():
    """Example 4.2.3. Simple re-entrant line from CTCN online."""
    alpha1 = 0.3
    mu1 = 0.67
    mu2 = 0.35
    mu3 = 0.67

    env = envex.simple_reentrant_line_model(alpha1, mu1, mu2, mu3)

    # Compute load, workload and sensitivity vectors sorted by load
    num_wl_vec = 1
    load, workload, nu = wl.compute_load_workload_matrix(env, num_wl_vec=num_wl_vec)
    # Compare results with theoretical result from CTCN book.
    load_theory = alpha1 * (1/mu1 + 1/mu3)
    workload_theory = np.array([[1/mu1 + 1/mu3, 1/mu3, 1/mu3]])
    np.testing.assert_almost_equal(load, load_theory, decimal=6)
    np.testing.assert_almost_equal(workload, workload_theory, decimal=6)
Ejemplo n.º 24
0
    def generate_workload_tuple(env: crw.ControlledRandomWalk,
                                num_workload_vectors: int,
                                load_threshold: float,
                                solver: str) -> WorkloadTuple:
        """
        Generate the workload tuple from the first relaxation.

        :param env: Environment (defining the topology and constraints of the network).
        :param num_workload_vectors: Number of leading workload vectors to choose.
        :param load_threshold: Lower bound on load to choose workload vectors.
        :param solver: Convex optimisation solver to be called when computing a feasible point
            inside the intersection of halfspaces.
        """
        return compute_load_workload_matrix(env,
                                            num_workload_vectors,
                                            load_threshold,
                                            solver=solver)
def compute_minimal_draining_time_computing_workload_from_env(
        initial_state: types.StateSpace, env: ControlledRandomWalk,
        demand_rate: types.StateSpace) -> float:
    """
    Computes the minimal draining time for an initial state.
    Perform Eq. (6.8) from CTCN book (online version). This equation consider only the xi_s which
    are workload vectors (o_s=1) and is not the general Eq. (7.4). Thus, it can only be used for
    push model.
    This method is not used by the algorithm, but just by the tests for checking the results of
    comparing the same magnitudes computed via different methods.
    """
    workload_mat = compute_load_workload_matrix(
        env=env, num_wl_vec=None, load_threshold=None,
        feasible_tol=1e-10).workload_mat
    minimal_draining_time = compute_minimal_draining_time_computing_workload(
        initial_state, workload_mat, demand_rate)
    return minimal_draining_time
Ejemplo n.º 26
0
def test_cum_cost_computation_switching_curve_regime_homogenous_cost():
    neg_log_discount_factor = - np.log(0.99999)
    env = examples.simple_reentrant_line_model(alpha1=0.33, mu1=0.7, mu2=0.34, mu3=0.7,
                                               cost_per_buffer=np.array([1, 1.001, 1])[:, None])
    num_wl_vec = 2
    load, workload_mat, nu = wl.compute_load_workload_matrix(env, num_wl_vec)
    strategic_idling_params = StrategicIdlingParams()
    workload_cov = np.array([[2, 0.5], [0.5, 3]])

    kappa_w = np.sum(env.cost_per_buffer) * 10
    kappa = 1e4
    policy_params = BigStepPenaltyPolicyParams('cvx.CPLEX', False, kappa_w, kappa)

    policy_object = BigStepPolicy(
        env.cost_per_buffer, env.constituency_matrix, env.job_generator.demand_rate,
        env.job_generator.buffer_processing_matrix, workload_mat, nu,
        env.list_boundary_constraint_matrices, env.ind_surplus_buffers, policy_params)

    si_object = StrategicIdlingForesight(workload_mat=workload_mat,
                                         neg_log_discount_factor=neg_log_discount_factor,
                                         load=load,
                                         cost_per_buffer=env.cost_per_buffer,
                                         model_type=env.model_type,
                                         policy_object=policy_object,
                                         strategic_idling_params=strategic_idling_params,
                                         workload_cov=workload_cov)

    init_state = np.array([0, 0, 10000])[:, np.newaxis]
    init_w = workload_mat @ init_state

    si_object._current_state = init_state

    cw_vars = si_object._non_negative_workloads(init_w)
    cw_vars = super(StrategicIdlingForesight, si_object)._handle_switching_curve_regime(init_w,
                                                                                        cw_vars)

    gto_cost = si_object._roll_out_gto_fluid_policy(cw_vars)
    std_hedging_cost = si_object._roll_out_hedgehog_fluid_policy(cw_vars)

    np.testing.assert_allclose(std_hedging_cost/1e6,1214,rtol=0.03)
    np.testing.assert_allclose(gto_cost/1e6,950,rtol=0.03)

    si_object.get_allowed_idling_directions(init_state)
    assert si_object._current_regime == "gto"
    assert si_object._original_target_dyn_bot_set == set([0,1])
Ejemplo n.º 27
0
def perform_test(env, num_batch, num_data, class_kind, job_gen_seed):
    """
    This test is designed to be imported by compute asymptotic covariance classes.
    """
    workload_tuple = workload.compute_load_workload_matrix(env)
    workload_cov_computer = class_kind(env.job_generator,
                                       env.constituency_matrix,
                                       workload_tuple.workload_mat)
    workload_cov_estimator = EstimateAsymptoticWorkloadCovBatchMeans(env)

    workload_cov_com = workload_cov_computer.compute_asymptotic_workload_cov()
    agent = SteadyStatePolicyAgent(env,
                                   agent_seed=job_gen_seed + 200,
                                   mpc_seed=job_gen_seed + 300)
    workload_cov_est = workload_cov_estimator.estimate_asymptotic_workload_cov(
        env.job_generator.buffer_processing_matrix, workload_tuple, num_batch,
        num_data, agent)
    assert np.linalg.norm(workload_cov_est - workload_cov_com) / np.linalg.norm(workload_cov_com) \
           <= 0.2
Ejemplo n.º 28
0
def test_big_step_policy_safety_stock_active():
    """
    Resource is below safety stock threshold. Nonidling penalty is zero. Minimum cost is achieved by
    draining buffer 3. However, resource 1 will drain buffer 1, so buffer 2 reaches its safety stock
    level.
    """
    state = np.array([0, 0, 100])[:, None]
    alpha1 = 8
    mu1 = 20
    mu2 = 10
    mu3 = 20
    safety_stocks_vec = 8 * np.ones((2, 1))
    horizon = 22
    cost_per_buffer = np.array([[1.5], [1], [2]])
    z_star_true = np.array([0.4, 0, 0.6])[:, None]

    env = examples.simple_reentrant_line_model(alpha1, mu1, mu2, mu3,
                                               cost_per_buffer)
    load, workload_mat, nu = workload.compute_load_workload_matrix(env)

    kappa_w = 0
    kappa = 1e2
    k_idling_set = np.array([])
    draining_bottlenecks = {}
    boolean_action_flag = False
    policy_params = BigStepPenaltyPolicyParams('cvx.CPLEX',
                                               boolean_action_flag, kappa_w,
                                               kappa)

    policy_cvx = BigStepPolicy(env.cost_per_buffer, env.constituency_matrix,
                               env.job_generator.demand_rate,
                               env.job_generator.buffer_processing_matrix,
                               workload_mat, nu,
                               env.list_boundary_constraint_matrices,
                               env.ind_surplus_buffers, policy_params)
    kwargs = {
        'safety_stocks_vec': safety_stocks_vec,
        'k_idling_set': k_idling_set,
        'draining_bottlenecks': draining_bottlenecks,
        'horizon': horizon
    }
    z_star, _ = policy_cvx.get_policy(state, **kwargs)
    np.testing.assert_allclose(z_star, z_star_true, rtol=1e-4)
Ejemplo n.º 29
0
def get_simple_link_constrained_model():
    cost_per_buffer = np.array([3, 1, 3, 1.5, 3]).reshape(-1, 1)
    param_overrides = dict(alpha1=4.8,
                           mu12=2.,
                           mu13=4.,
                           mu25=2.,
                           mu32=4.5,
                           mu34=1.8,
                           mu35=2.,
                           mu45=1.,
                           mu5=7.,
                           cost_per_buffer=cost_per_buffer)
    _, env = scenarios.load_scenario('simple_link_constrained_model', 0,
                                     param_overrides)

    _, workload_mat, nu = workload.compute_load_workload_matrix(env, 6)
    env.workload_mat = workload_mat
    env.nu = nu

    return env
Ejemplo n.º 30
0
def compute_martingale(
        env: crw.ControlledRandomWalk,
        data_actions: List[snc_types.ActionSpace],
        data_states: List[snc_types.StateSpace]) -> snc_types.Matrix:
    """ Compute the difference between the workload at time t+1 and the workload at time t minus
    the drift and plus the idling process (w(t+1) - (w(t) - delta + I(t))). The result should be a
    martingale with zero mean.

    :param env: the environment to stepped through.
    :param data_actions: List of actions.
    :param data_states: List of states.
    """
    # TODO: create a unit test for this function with a specific environment where we are sure that
    #  the martingale has zero mean.
    workload_tuple = workload.compute_load_workload_matrix(env=env,
                                                           num_wl_vec=None,
                                                           load_threshold=None,
                                                           feasible_tol=1e-10)
    workload_mat = workload_tuple.workload_mat
    nu = workload_tuple.nu

    w_process = workload_mat @ np.array(data_states).T

    sort_index = np.squeeze(
        validation_utils.workload_to_physical_resources_index(nu=nu))
    constituency_matrix = env.constituency_matrix[sort_index, :]
    actions = np.array(data_actions).T

    idling = np.ones(
        (constituency_matrix.shape[0],
         actions.shape[1] - 1)) - constituency_matrix @ actions[:, :-1]
    rho = workload_mat @ env.job_generator.demand_rate
    delta_mat = np.tile(1. - rho, (1, w_process.shape[1] - 1))

    w_tplusone = w_process[:, 1:]
    w_t = w_process[:, :-1]

    martingale = w_tplusone - w_t + delta_mat - idling
    martingale_mean = np.mean(martingale, axis=1)
    print("Martingale mean: ", martingale_mean)
    return martingale