soco 1.0.1

Algorithms for Smoothed Online Convex Optimization
Documentation
#[cfg(test)]
mod make_pow_of_2 {
    use crate::factories::constant;
    use crate::init;
    use soco::algorithms::offline::uni_dimensional::optimal_graph_search::make_pow_of_2;
    use soco::config::Config;
    use soco::problem::{Problem, SimplifiedSmoothedConvexOptimization};
    use soco::verifiers::VerifiableProblem;

    #[test]
    fn _1() {
        init();

        let p = SimplifiedSmoothedConvexOptimization {
            d: 1,
            t_end: 1_000,
            bounds: vec![103],
            switching_cost: vec![1.],
            hitting_cost: constant(),
        };
        p.verify().unwrap();
        let transformed_p = make_pow_of_2(p.clone()).unwrap();
        transformed_p.verify().unwrap();

        assert_eq!(transformed_p.t_end, p.t_end);
        assert_eq!(transformed_p.bounds[0], 128);
        assert_abs_diff_eq!(
            transformed_p.switching_cost[0],
            p.switching_cost[0]
        );

        for t in 1..=transformed_p.t_end {
            for j in 0..=transformed_p.bounds[0] {
                assert_abs_diff_eq!(
                    transformed_p.hit_cost(t, Config::single(j)).cost.raw(),
                    if j <= p.bounds[0] {
                        1.
                    } else {
                        j as f64 * (1. + std::f64::EPSILON)
                    }
                );
            }
        }
    }
}

#[cfg(test)]
mod optimal_graph_search {
    use std::sync::Arc;

    use crate::{
        factories::{penalize_zero, random},
        init,
        utils::hash_map,
    };
    use soco::schedule::Schedule;
    use soco::verifiers::VerifiableProblem;
    use soco::{algorithms::offline::CachedPath, config::Config};
    use soco::{
        algorithms::offline::{
            multi_dimensional::optimal_graph_search::optimal_graph_search as md_optimal_graph_search,
            uni_dimensional::optimal_graph_search::{
                make_pow_of_2, optimal_graph_search, Options,
            },
            OfflineAlgorithm, OfflineOptions,
        },
        problem::Problem,
    };
    use soco::{
        model::{
            data_center::{
                loads::LoadProfile,
                model::{
                    DataCenterModel, DataCenterOfflineInput, JobType, Location,
                    ServerType, Source, DEFAULT_KEY,
                },
                models::{
                    energy_consumption::{
                        EnergyConsumptionModel,
                        SimplifiedLinearEnergyConsumptionModel,
                    },
                    energy_cost::{EnergyCostModel, LinearEnergyCostModel},
                    revenue_loss::{
                        MinimalDetectableDelayRevenueLossModel,
                        RevenueLossModel,
                    },
                    switching_cost::{SwitchingCost, SwitchingCostModel},
                },
                DataCenterModelOutputFailure, DataCenterModelOutputSuccess,
            },
            Model,
        },
        problem::{
            IntegralSimplifiedSmoothedConvexOptimization,
            SimplifiedSmoothedConvexOptimization,
        },
    };

    #[test]
    fn _1() {
        init();

        let p = SimplifiedSmoothedConvexOptimization {
            d: 1,
            t_end: 2,
            bounds: vec![2],
            switching_cost: vec![1.],
            hitting_cost: penalize_zero(),
        };
        p.verify().unwrap();

        let path = optimal_graph_search
            .solve_with_default_options(p.clone(), OfflineOptions::default())
            .unwrap();
        path.xs.verify(p.t_end, &p.bounds).unwrap();
        let inv_path = optimal_graph_search
            .solve_with_default_options(p.clone(), OfflineOptions::inverted())
            .unwrap();
        inv_path.xs.verify(p.t_end, &p.bounds).unwrap();

        assert_eq!(path.xs, inv_path.xs);
        assert_abs_diff_eq!(path.cost, inv_path.cost);
        assert_eq!(
            path.xs,
            Schedule::new(vec![Config::single(1), Config::single(1)])
        );
        assert_abs_diff_eq!(path.cost, 1.);
        assert_relative_eq!(
            path.cost,
            p.objective_function(&path.xs).unwrap().cost.raw(),
            max_relative = 1e-4
        );
    }

    #[test]
    fn _2() {
        init();

        let p = SimplifiedSmoothedConvexOptimization {
            d: 1,
            t_end: 100,
            bounds: vec![8],
            switching_cost: vec![1.],
            hitting_cost: random(),
        };
        p.verify().unwrap();

        let path = optimal_graph_search
            .solve_with_default_options(p.clone(), OfflineOptions::default())
            .unwrap();
        path.xs.verify(p.t_end, &p.bounds).unwrap();
        let inv_path = optimal_graph_search
            .solve_with_default_options(p.clone(), OfflineOptions::inverted())
            .unwrap();
        inv_path.xs.verify(p.t_end, &p.bounds).unwrap();

        assert_eq!(path.xs, inv_path.xs);
        assert_abs_diff_eq!(path.cost, inv_path.cost);
        assert_relative_eq!(
            path.cost,
            p.objective_function(&path.xs).unwrap().cost.raw(),
            max_relative = 1e-4
        );
    }

    #[test]
    fn _3() {
        init();

        let p = SimplifiedSmoothedConvexOptimization {
            d: 1,
            t_end: 1_000,
            bounds: vec![9],
            switching_cost: vec![1.],
            hitting_cost: random(),
        };
        p.verify().unwrap();

        let CachedPath { path: md_path, .. } = md_optimal_graph_search
            .solve_with_default_options(p.clone(), OfflineOptions::default())
            .unwrap();
        md_path.xs.verify(p.t_end, &p.bounds).unwrap();

        let transformed_p = make_pow_of_2(p).unwrap();
        let path = optimal_graph_search
            .solve_with_default_options(
                transformed_p.clone(),
                OfflineOptions::default(),
            )
            .unwrap();
        path.xs
            .verify(transformed_p.t_end, &transformed_p.bounds)
            .unwrap();
        let inv_path = optimal_graph_search
            .solve_with_default_options(
                transformed_p.clone(),
                OfflineOptions::inverted(),
            )
            .unwrap();
        inv_path
            .xs
            .verify(transformed_p.t_end, &transformed_p.bounds)
            .unwrap();

        assert!(path.cost.is_finite());
        assert_abs_diff_eq!(path.cost, md_path.cost);
        assert_eq!(path.xs, inv_path.xs);
        assert_abs_diff_eq!(path.cost, inv_path.cost);
        assert_relative_eq!(
            path.cost,
            transformed_p
                .objective_function(&path.xs)
                .unwrap()
                .cost
                .raw(),
            max_relative = 1e-4
        );
    }

    #[test]
    fn _4() {
        init();

        let p = SimplifiedSmoothedConvexOptimization {
            d: 1,
            t_end: 2,
            bounds: vec![2],
            switching_cost: vec![1.],
            hitting_cost: penalize_zero(),
        };
        p.verify().unwrap();

        let path = optimal_graph_search
            .solve(p.clone(), Options::new(2), OfflineOptions::default())
            .unwrap();
        path.xs.verify(p.t_end, &p.bounds).unwrap();

        assert_eq!(
            path.xs,
            Schedule::new(vec![Config::single(1), Config::single(1)])
        );
        assert_abs_diff_eq!(path.cost, 0.);
        assert_relative_eq!(
            path.cost,
            p.objective_function_with_default(&path.xs, &Config::single(2))
                .unwrap()
                .cost
                .raw(),
            max_relative = 1e-4
        );
    }

    #[test]
    fn _5() {
        init();

        let loads = vec![LoadProfile::raw(vec![1.])];
        let delta = 10. * 60.;
        let m = 32;
        let model = DataCenterModel::new(
            delta,
            vec![Location {
                key: DEFAULT_KEY.to_string(),
                m: hash_map(&[(DEFAULT_KEY.to_string(), m)]),
            }],
            vec![ServerType::default()],
            vec![Source::default()],
            vec![JobType::default()],
            EnergyConsumptionModel::SimplifiedLinear(hash_map(&[(
                DEFAULT_KEY.to_string(),
                SimplifiedLinearEnergyConsumptionModel { phi_max: 1. },
            )])),
            EnergyCostModel::Linear(hash_map(&[(
                DEFAULT_KEY.to_string(),
                LinearEnergyCostModel {
                    cost: Arc::new(|_| 1.),
                },
            )])),
            RevenueLossModel::MinimalDetectableDelay(hash_map(&[(
                DEFAULT_KEY.to_string(),
                MinimalDetectableDelayRevenueLossModel::default(),
            )])),
            SwitchingCostModel::new(hash_map(&[(
                DEFAULT_KEY.to_string(),
                SwitchingCost {
                    energy_cost: 1.,
                    phi_min: 0.5,
                    phi_max: 1.,
                    epsilon: 1.,
                    delta: 1.,
                    tau: 5.,
                    rho: 5.,
                },
            )])),
        );
        let input = DataCenterOfflineInput { loads };

        let p: IntegralSimplifiedSmoothedConvexOptimization<
            DataCenterModelOutputSuccess,
            DataCenterModelOutputFailure,
        > = model.to(input);
        p.verify().unwrap();

        let CachedPath { path: md_path, .. } = md_optimal_graph_search
            .solve_with_default_options(p.clone(), OfflineOptions::default())
            .unwrap();
        md_path.xs.verify(p.t_end, &p.bounds).unwrap();

        let path = optimal_graph_search
            .solve(p.clone(), Options::default(), OfflineOptions::default())
            .unwrap();
        path.xs.verify(p.t_end, &p.bounds).unwrap();

        assert!(path.cost.is_finite());
        assert_abs_diff_eq!(path.cost, md_path.cost);
        assert_relative_eq!(
            path.cost,
            p.objective_function(&path.xs).unwrap().cost.raw(),
            max_relative = 1e-4
        );
    }
}