Exemple #1
0
    def setUp(self) -> None:
        graph_obj = Graph.build_from_parameters(n=2, l=10, g=0.5, p=2)
        demand_obj = Demand.build_from_parameters(graph_obj=graph_obj, y=100, a=0.5, alpha=1 / 3, beta=1 / 3)
        passenger_obj = Passenger.get_default_passenger()
        [bus_obj, metro_obj] = TransportMode.get_default_modes()

        self.network_obj = TransportNetwork(graph_obj=graph_obj)

        feeder_routes_metro = self.network_obj.get_feeder_routes(mode_obj=metro_obj)
        radial_routes_bus = self.network_obj.get_radial_routes(mode_obj=bus_obj)

        for route in feeder_routes_metro:
            self.network_obj.add_route(route_obj=route)

        for route in radial_routes_bus:
            self.network_obj.add_route(route_obj=route)

        self.extended_graph_obj = ExtendedGraph(graph_obj=graph_obj, routes=self.network_obj.get_routes(),
                                                TP=passenger_obj.pt, frequency_routes=None)
        hyperpath_obj = Hyperpath(extended_graph_obj=self.extended_graph_obj, passenger_obj=passenger_obj)

        self.hyperpaths, self.labels, self.successors, self.frequency, self.Vij = hyperpath_obj.get_all_hyperpaths(
            OD_matrix=demand_obj.get_matrix())

        self.OD_assignment = Assignment.get_assignment(hyperpaths=self.hyperpaths, labels=self.labels, p=2,
                                                       vp=passenger_obj.va, spa=passenger_obj.spa,
                                                       spv=passenger_obj.spv)
    def test_resources_consumer(self):

        graph_obj = Graph.build_from_parameters(n=2, l=10, g=0.5, p=2)
        demand_obj = Demand.build_from_parameters(graph_obj=graph_obj,
                                                  y=100,
                                                  a=0.5,
                                                  alpha=1 / 3,
                                                  beta=1 / 3)
        passenger_obj = Passenger.get_default_passenger()
        [bus_obj, metro_obj] = TransportMode.get_default_modes()

        network_obj = TransportNetwork(graph_obj=graph_obj)

        feeder_routes_metro = network_obj.get_feeder_routes(mode_obj=metro_obj)
        radial_routes_bus = network_obj.get_radial_routes(mode_obj=bus_obj)

        for route in feeder_routes_metro:
            network_obj.add_route(route_obj=route)

        for route in radial_routes_bus:
            network_obj.add_route(route_obj=route)

        extended_graph_obj = ExtendedGraph(graph_obj=graph_obj,
                                           routes=network_obj.get_routes(),
                                           TP=passenger_obj.pt,
                                           frequency_routes=None)
        hyperpath_obj = Hyperpath(extended_graph_obj=extended_graph_obj,
                                  passenger_obj=passenger_obj)

        hyperpaths, labels, successors, frequency, Vij = hyperpath_obj.get_all_hyperpaths(
            OD_matrix=demand_obj.get_matrix())

        OD_assignment = Assignment.get_assignment(hyperpaths=hyperpaths,
                                                  labels=labels,
                                                  p=2,
                                                  vp=passenger_obj.va,
                                                  spa=passenger_obj.spa,
                                                  spv=passenger_obj.spv)

        f = defaultdict(float)
        z = defaultdict(lambda: defaultdict(lambda: defaultdict(float)))
        v = defaultdict(lambda: defaultdict(lambda: defaultdict(float)))

        for route in network_obj.get_routes():
            f[route.id] = 28

        CU_obj = UsersCost()
        ta, te, tv, t = CU_obj.resources_consumer(hyperpaths, Vij,
                                                  OD_assignment, successors,
                                                  extended_graph_obj,
                                                  passenger_obj.va, f, z, v)

        self.assertEqual(round(ta, 4), 3.069)
        self.assertEqual(round(te, 4), 13.9597)
        self.assertEqual(round(tv, 4), 74.685)
        self.assertEqual(round(t, 4), 41.6667)
Exemple #3
0
    def test_get_hyperpath_OD(self):
        """
        to test get_hyperpath_OD method of class Hyperpath
        :return:
        """

        graph_obj = Graph.build_from_parameters(n=2, l=10, g=0.5, p=2)

        [bus_obj, metro_obj] = TransportMode.get_default_modes()

        passenger_obj = Passenger.get_default_passenger()

        network_obj = TransportNetwork(graph_obj)
        radial = network_obj.get_radial_routes(bus_obj)
        diametral = network_obj.get_diametral_routes(bus_obj, jump=1)
        diametral_metro = network_obj.get_diametral_routes(metro_obj, jump=1)

        for route in radial:
            network_obj.add_route(route)
        for route in diametral:
            network_obj.add_route(route)
        for route in diametral_metro:
            network_obj.add_route(route)

        extended_graph_obj = ExtendedGraph(graph_obj, network_obj.get_routes(),
                                           16)

        nodes = extended_graph_obj.get_extended_graph_nodes()

        P1 = None
        P2 = None
        stop_bus = None
        stop_metro = None
        for city_node in nodes:
            if city_node.graph_node.name == str("P_1"):
                P1 = city_node
                for stop_node in nodes[city_node]:
                    if stop_node.mode.name == "bus":
                        stop_bus = stop_node
                    if stop_node.mode.name == "metro":
                        stop_metro = stop_node
            if city_node.graph_node.name == str("P_2"):
                P2 = city_node

        hyper_path_obj = Hyperpath(extended_graph_obj, passenger_obj)

        hyperpaths_od, label, successors, frequencies = hyper_path_obj.get_hyperpath_OD(
            P1, P2)

        self.assertEqual(label[P1], float("inf"))
        self.assertEqual(label[P2], 0)
        self.assertEqual(round(label[stop_bus], 5), 1.30238)
        self.assertEqual(round(label[stop_metro], 5), 0.83571 + 0.05)
        self.assertEqual(len(hyperpaths_od), 2)
        self.assertEqual(len(hyperpaths_od[stop_bus]), 2)
        self.assertEqual(len(hyperpaths_od[stop_metro]), 1)
Exemple #4
0
    def test_build_hyperpath_graph(self):
        """
        to test build_hyperpath_graph method of class Hyperpath
        :return:
        """

        graph_obj = Graph.build_from_parameters(n=2, l=10, g=0.5, p=2)

        [bus_obj, _] = TransportMode.get_default_modes()

        passenger_obj = Passenger.get_default_passenger()

        network_obj = TransportNetwork(graph_obj)
        radial = network_obj.get_radial_routes(bus_obj)
        diametral = network_obj.get_diametral_routes(bus_obj, jump=1)

        for route in radial:
            network_obj.add_route(route)
        for route in diametral:
            network_obj.add_route(route)

        extended_graph_obj = ExtendedGraph(graph_obj, network_obj.get_routes(),
                                           16)

        nodes = extended_graph_obj.get_extended_graph_nodes()

        P1 = None
        P2 = None
        stop = None
        for city_node in nodes:
            if city_node.graph_node.name == str("P_1"):
                P1 = city_node
                stop = None
                for stop_node in nodes[city_node]:
                    stop = stop_node
            if city_node.graph_node.name == str("P_2"):
                P2 = city_node

        hyper_path_obj = Hyperpath(extended_graph_obj, passenger_obj)

        successors, label, frequencies = hyper_path_obj.build_hyperpath_graph(
            P1, P2)

        self.assertEqual(label[P1], float("inf"))
        self.assertEqual(label[P2], 0)
        self.assertEqual(label[stop], 1.55)
Exemple #5
0
    def __init__(self, graph_obj: Graph, demand_obj: Demand, passenger_obj: Passenger, network_obj: TransportNetwork,
                 f: defaultdict_float = None):

        # definimos ciudad
        self.graph_obj = graph_obj
        _, _, _, self.p, _, _, _, _, _ = self.graph_obj.get_parameters()
        # definimos demanda
        self.demand_obj = demand_obj
        self.total_trips = demand_obj.get_total_trips()
        # definimos pasajeros
        self.passenger_obj = passenger_obj
        self.vp = self.passenger_obj.va
        self.pa = self.passenger_obj.pa
        self.pv = self.passenger_obj.pv
        self.TP = self.passenger_obj.pt

        # definimos red de transporte
        self.network_obj = network_obj

        # definimos frecuencia
        self.f, self.f_opt, self.lines_position = self.f0(f)

        self.extended_graph_obj = ExtendedGraph(self.graph_obj, self.network_obj.get_routes(), self.TP, self.f)
        self.hyperpath_obj = Hyperpath(self.extended_graph_obj, self.passenger_obj)

        # en este punto se debería levantar exception de que la red tiene mas de dos modos defnidos
        # o que existe un par OD con viaje y sin conexion
        self.hyperpaths, self.labels, self.successors, self.frequency, self.Vij = self.hyperpath_obj.get_all_hyperpaths(
            self.demand_obj.get_matrix())

        self.assignment = Assignment.get_assignment(self.hyperpaths, self.labels, self.p, self.vp, self.pa,
                                                    self.pv)

        self.len_constrains = len(self.get_constrains(self.f_opt))
        self.len_var = len(self.f_opt)

        self.better_res = None  # (fopt, success, status, message, constr_violation, vrc)
Exemple #6
0
    def test_network_validator(self):
        """
        to test network_validator method in class Hyperpath
        :return:
        """

        graph_obj = Graph.build_from_parameters(n=2, l=10000, g=0.5, p=2000)

        demand_obj = Demand.build_from_parameters(graph_obj,
                                                  y=1000,
                                                  a=0.5,
                                                  alpha=1 / 3,
                                                  beta=1 / 3)
        OD_matrix = demand_obj.get_matrix()

        [bus_obj, _] = TransportMode.get_default_modes()

        passenger_obj = Passenger.get_default_passenger()

        network_obj = TransportNetwork(graph_obj)
        radial = network_obj.get_radial_routes(bus_obj)

        extended_graph_obj = ExtendedGraph(graph_obj, network_obj.get_routes(),
                                           16)

        hyper_path_obj = Hyperpath(extended_graph_obj, passenger_obj)

        with self.assertRaises(TransportNetworkException):
            hyper_path_obj.network_validator(OD_matrix)

        for route in radial:
            network_obj.add_route(route)

        extended_graph_obj = ExtendedGraph(graph_obj, network_obj.get_routes(),
                                           16)

        hyper_path_obj = Hyperpath(extended_graph_obj, passenger_obj)

        self.assertTrue(hyper_path_obj.network_validator(OD_matrix))
Exemple #7
0
class Optimizer:
    def __init__(self, graph_obj: Graph, demand_obj: Demand, passenger_obj: Passenger, network_obj: TransportNetwork,
                 f: defaultdict_float = None):

        # definimos ciudad
        self.graph_obj = graph_obj
        _, _, _, self.p, _, _, _, _, _ = self.graph_obj.get_parameters()
        # definimos demanda
        self.demand_obj = demand_obj
        self.total_trips = demand_obj.get_total_trips()
        # definimos pasajeros
        self.passenger_obj = passenger_obj
        self.vp = self.passenger_obj.va
        self.pa = self.passenger_obj.pa
        self.pv = self.passenger_obj.pv
        self.TP = self.passenger_obj.pt

        # definimos red de transporte
        self.network_obj = network_obj

        # definimos frecuencia
        self.f, self.f_opt, self.lines_position = self.f0(f)

        self.extended_graph_obj = ExtendedGraph(self.graph_obj, self.network_obj.get_routes(), self.TP, self.f)
        self.hyperpath_obj = Hyperpath(self.extended_graph_obj, self.passenger_obj)

        # en este punto se debería levantar exception de que la red tiene mas de dos modos defnidos
        # o que existe un par OD con viaje y sin conexion
        self.hyperpaths, self.labels, self.successors, self.frequency, self.Vij = self.hyperpath_obj.get_all_hyperpaths(
            self.demand_obj.get_matrix())

        self.assignment = Assignment.get_assignment(self.hyperpaths, self.labels, self.p, self.vp, self.pa,
                                                    self.pv)

        self.len_constrains = len(self.get_constrains(self.f_opt))
        self.len_var = len(self.f_opt)

        self.better_res = None  # (fopt, success, status, message, constr_violation, vrc)

    def f0(self, f: defaultdict_float = None) -> (defaultdict_float, List[float], defaultdict_str):
        """
        to get a relation between f as a dictionary and f_opt as a list to the optimizer
        :param f: dic[route_id] = frequency [veh/hr] for all D lines
        :return: dic[route_id] = frequency [veh/hr] for all D lines, List[frequency], dic[position] = route_id
        """
        fini = defaultdict(float)
        fopt = []
        lines_position = defaultdict(None)
        n = 0
        if f is None:
            for route in self.network_obj.get_routes():
                fini[route.id] = route.mode.fini
                fopt.append(route.mode.fini)
                lines_position[n] = route.id
                n += 1
        else:
            for route in self.network_obj.get_routes():
                fini[route.id] = f[route.id]
                fopt.append(route.mode.fini)
                lines_position[n] = route.id
                n += 1

        return fini, fopt, lines_position

    def fopt_to_f(self, fopt: List[float]) -> defaultdict_float:
        """
        to get f as a dictionary
        :param fopt: List[frequency]
        :return: dic[route_id] = frequency [veh/hr] all D lines
        """
        f = defaultdict(float)
        n = 0
        for fr in fopt:
            f[self.lines_position[n]] = fr
            n += 1
        return f

    @staticmethod
    def get_k(loaded_section_route: defaultdict3_float) -> defaultdict_float:
        """
        :param loaded_section_route: dic[route_id][direction][stop: StopNode] = pax [pax/veh]
        :return: k: dic[route_id] = vehicle capacity [pax/veh]
        """
        most_loaded_section = Assignment.most_loaded_section(loaded_section_route)
        k = defaultdict(float)
        for route_id in most_loaded_section:
            k[route_id] = most_loaded_section[route_id]
        return k

    def operators_cost(self, z: defaultdict3_float, v: defaultdict3_float, f: defaultdict_float,
                       k: defaultdict_float) -> float:
        """
        to get operators cost
        :param z: boarding, dic[route_id][direction][stop: StopNode] = pax [pax/veh]
        :param v: alighting, dic[route_id][direction][stop: StopNode] = pax [pax/veh]
        :param f: dic[route_id] = frequency [veh/hr]
        :param k: dic[route_id] = frequency [pax/veh]
        :return: float, operator cost
        """
        operators_cost_obj = OperatorsCost()

        edge_distance = self.graph_obj.get_edges_distance()
        routes = self.network_obj.get_routes()
        line_travel_time = operators_cost_obj.lines_travel_time(routes, edge_distance)

        cycle_time = operators_cost_obj.get_cycle_time(z, v, routes, line_travel_time)
        cost = operators_cost_obj.get_operators_cost(routes, cycle_time, f, k)
        return cost

    def infrastructure_cost(self, f: defaultdict_float) -> float:
        """
        to get infrastructure cost
        :param f: dic[route_id] = frequency [veh/hr]
        :return: float, infrastructure cost
        """
        infrastructure_cost_obj = InfrastructureCost()
        cost = infrastructure_cost_obj.get_infrastruture_cost(self.graph_obj, self.network_obj, f)
        return cost

    def user_cost(self, hyperpaths: dic_hyperpaths, Vij: dic_Vij, assignment: dic_assigment,
                  successors: dic_successors, extended_graph: ExtendedGraph, f: defaultdict_float,
                  z: defaultdict3_float, v: defaultdict3_float) -> float:
        """
        to get users cost
        :param hyperpaths: Dic[origin: CityNode][destination: CityNode][StopNode] = List[List[ExtendedNodes]]
        :param Vij: dic[origin: CityNode][destination: CityNode] = vij
        :param assignment: dic[origin: CityNode][destination: CityNode][Stop: StopNode] = %V_OD
        :param successors: dic[origin: CityNode][destination: CityNode][ExtendedNode] = List[ExtendedEdge]
        :param extended_graph: ExtendedGraph object
        :param f: dict with frequency [veh/hr] for each route_id
        :param z: boarding, dic[route_id][direction][stop: StopNode] = pax [pax/veh]
        :param v: alighting, dic[route_id][direction][stop: StopNode] = pax [pax/veh]
        :return: float, users cost
        """
        user_cost_obj = UsersCost()
        cost = user_cost_obj.get_users_cost(hyperpaths, Vij, assignment, successors, extended_graph, f,
                                            self.passenger_obj, z, v)
        return cost

    def constrains(self, loaded_section_route: defaultdict3_float, f: defaultdict_float) -> (
            List[float], List[float]):
        """
        to get k constrains and f constrains
        :param loaded_section_route: dic[route_id][direction][stop: StopNode] = pax [pax/veh]
        :param f: dict with frequency [veh/hr] for each route_id
        :return: (k_ineq_constrains, f_ineq_constrains)
        """

        most_loaded_section = Assignment.most_loaded_section(loaded_section_route)

        constrains_obj = Constrains()

        ineq_k = constrains_obj.most_loaded_section_constrains(self.network_obj.get_routes(), most_loaded_section)
        ineq_f = constrains_obj.fmax_constrains(self.graph_obj, self.network_obj.get_routes(),
                                                self.network_obj.get_modes(), f)

        return ineq_k, ineq_f

    def VRC(self, fopt: List[float]) -> float:
        """
        to get VRC objective function to minime in optimizer
        :param fopt: variable to optimize
        :return: float, VRC value function
        """

        f = self.fopt_to_f(fopt)

        z, v, loaded_section_route = Assignment.get_alighting_and_boarding(self.Vij, self.hyperpaths, self.successors,
                                                                           self.assignment, f)
        k = self.get_k(loaded_section_route)

        CO = self.operators_cost(z, v, f, k)
        CI = self.infrastructure_cost(f)
        CU = self.user_cost(self.hyperpaths, self.Vij, self.assignment, self.successors, self.extended_graph_obj, f, z,
                            v)

        return CO + CI + CU

    def get_constrains(self, fopt: List[float]) -> List[float]:
        """
        to get all constrains as a List[float]
        :param fopt: variable to optimize
        :return: all constrains as a List[float]
        """

        f = self.fopt_to_f(fopt)

        z, v, loaded_section_route = Assignment.get_alighting_and_boarding(self.Vij, self.hyperpaths, self.successors,
                                                                           self.assignment, f)

        most_loaded_section = Assignment.most_loaded_section(loaded_section_route)
        constrain_obj = Constrains()
        ineq_k = constrain_obj.most_loaded_section_constrains(self.network_obj.get_routes(), most_loaded_section)
        ineq_f = constrain_obj.fmax_constrains(self.graph_obj, self.network_obj.get_routes(),
                                               self.network_obj.get_modes(), f)

        con = []
        for c in ineq_k:
            con.append(c)
        for c in ineq_f:
            con.append(c)

        return con

    def internal_optimization(self) -> OptimizeResult:
        """
        method to do internal optimization process, with a hyperpath setted you can get a optimization of the network
        :return:     res : OptimizeResult
        The optimization result represented as a ``OptimizeResult`` object.
        Important attributes are: ``x`` the solution array, ``success`` a
        Boolean flag indicating if the optimizer exited successfully and
        ``message`` which describes the cause of the termination. See
        `OptimizeResult` for a description of other attributes.
        """

        constr_func = lambda fopt: np.array(self.get_constrains(fopt))

        lb = [-1 * np.inf] * self.len_constrains
        ub = [0] * self.len_constrains
        nonlin_con = NonlinearConstraint(constr_func, lb=lb, ub=ub)

        lb = [0] * self.len_var
        ub = [np.inf] * self.len_var

        bounds = Bounds(lb=lb, ub=ub)
        res = minimize(self.VRC, self.f_opt, method='trust-constr', constraints=nonlin_con, tol=0.01, bounds=bounds)
        logger.info(self.string_information_internal_optimization(res))

        return res

    @staticmethod
    def string_information_internal_optimization(res: OptimizeResult) -> str:
        """
        to get a string with information about internal optimization
        :param res: OptimizeResult
        :return: str
        """

        success = res.success
        status = res.status
        message = res.message
        new_f = res.x
        constr_violation = res.constr_violation
        fun = res.fun

        line = "Internal optimization\n\tSuccess: {}\n\tStatus: {}\n\tMessage: {}\n\tnew_f: {}\n\tConstrain violation: {}\n\tVRC: {}".format(
            success, status, message, new_f, constr_violation, fun)
        return line

    @staticmethod
    def f_distance(prev_f: List[float], new_f: List[float]) -> float:
        """
        to get distance between 2 list of frequency results.
        :param prev_f: previous frequency
        :param new_f: new frequency
        :return: float, distance with normal distance
        """
        dif = 0
        for i in range(len(prev_f)):
            dif += abs(prev_f[i] - new_f[i])
        dif = dif  # ** (1 / len(prev_f))
        logger.info("f_norm_distance: {}".format(dif))
        return dif

    def external_optimization_tolerance(self, prev_f: List[float], new_f: List[float], tol: float = 0.01) -> bool:
        """
        True, if tolerance criteria is success
        :param prev_f: previous frequency
        :param new_f: new frequency
        :param tol: float, tolerance
        :return: True, if tolerance criteria is success
        """

        if tol > abs(self.f_distance(prev_f, new_f)):
            return True

        return False

    @staticmethod
    def status_optimization(better_res) -> bool:
        """
        to get a information about status of external optimization
        :param better_res:(fopt, success, status, message, constr_violation, vrc)
        :return: true if status is success, exceptions if not
        """

        if better_res is None:
            raise NoOptimalSolutionFoundException(
                "No optimal solution was found. You can try with other fini or total demand is very large for the proposed network.")

        fopt, success, status, message, constr_violation, fun = better_res

        for f in fopt:
            if f < -0.1:
                raise NegativeFrequencyException("Solution with negative frequency. You can try with other fini")

        if abs(constr_violation) > 0.1:
            raise ConstraintViolationException(
                "Maximum constraint violation at the solution {}. You can try with other fini or total demand is very large for the proposed network.".format(
                    constr_violation))

        if status == 0 or status == 3:
            raise NoOptimalSolutionFoundException(
                "No optimal solution was found. You can try with other fini or total demand is very large for the proposed network.")

        return True

    @staticmethod
    def external_optimization(graph_obj: Graph, demand_obj: Demand, passenger_obj: Passenger,
                              network_obj: TransportNetwork,
                              f: defaultdict_float = None, tolerance: float = 0.01,
                              number_of_iteration: int = None) -> Tuple:
        """
        method to do external optimization process, several iterations of internal optimization with fixed
        hyperpaths in each
        :param graph_obj: Graph object
        :param demand_obj: Demand object
        :param passenger_obj: Passenger object
        :param network_obj: TransportNetwork object
        :param f: dict with frequency [veh/hr] for each route_id, dic[route_id] = frequency
        :param tolerance: float, tolerance to external optimization
        :param number_of_iteration: int, max. number of iterations. Default value is infinity.
        it is recommended to set this value in a small number of iterations (e.x. 5) in the beginning to know if it
        converges
        :return: (fopt, success, status, message, constr_violation, vrc)
        """

        list_res = []

        opt_obj = Optimizer(graph_obj, demand_obj, passenger_obj, network_obj, f)
        # inicialización
        list_res.append((opt_obj.f_opt, "initialization", -1, "initialization", -1, -1))

        # primera iteracion
        res = opt_obj.internal_optimization()
        list_res.append((res.x, res.success, res.status, res.message, res.constr_violation, res.fun))

        pre_f, _, _, _, _, _ = list_res[0]
        new_f, _, _, _, _, _ = list_res[1]

        iteration = 1
        # mientras criterio de tolerancia externo no se cumpla o se llegue al maximo numero de iteraciones
        while not opt_obj.external_optimization_tolerance(pre_f, new_f, tolerance):
            if number_of_iteration is not None:
                if iteration > number_of_iteration:
                    break
            pre_f = new_f
            dic_new_f = opt_obj.fopt_to_f(new_f)
            opt_obj = Optimizer(graph_obj, demand_obj, passenger_obj, network_obj, dic_new_f)
            res = opt_obj.internal_optimization()
            list_res.append((res.x, res.success, res.status, res.message, res.constr_violation, res.fun))
            new_f = res.x
            iteration += 1

        better_result = opt_obj.get_better_result(list_res)

        if better_result is not None:
            fopt, success, status, message, constr_violation, fun = better_result
            fopt2 = []
            for x in fopt:
                if x < 1 / 24:
                    fopt2.append(0)
                else:
                    fopt2.append(x)
            fopt = fopt2
            better_result = fopt, success, status, message, constr_violation, fun

        if opt_obj.status_optimization(better_result):
            return better_result

    @staticmethod
    def get_better_result(list_res):
        valid_result = []
        for x, success, status, message, constr_violation, fun in list_res:
            if status not in [0, 3, -1] and abs(constr_violation) < 0.001:
                cond_f = True
                for f in x:
                    if f < 0:
                        cond_f = False
                        break
                if cond_f:
                    valid_result.append((x, success, status, message, constr_violation, fun))

        better_result = None
        max_fun = float("inf")
        for x, success, status, message, constr_violation, fun in valid_result:
            if fun < max_fun:
                max_fun = fun
                better_result = (x, success, status, message, constr_violation, fun)

        return better_result

    @staticmethod
    def network_optimization(graph_obj: Graph, demand_obj: Demand, passenger_obj: Passenger,
                             network_obj: TransportNetwork,
                             f: defaultdict_float = None, tolerance: float = 0.01,
                             max_number_of_iteration: int = None) -> Optimizer:
        """
        obtain optimal frequency for the defined network if possible or raise exceptions in case of not being able
        :param graph_obj: Graph object
        :param demand_obj: Demand object
        :param passenger_obj: Passenger object
        :param network_obj: TransportNetwork object
        :param f: dict with frequency [veh/hr] for each route_id, dic[route_id] = frequency, if f is None then fini of
        each TransportMode defined in TransportNetwork define f.
        :param tolerance: float, tolerance to external optimization
        :param max_number_of_iteration: int, max. number of iterations. Default value is infinity.
        it is recommended to set this value in a small number of iterations (e.x. 5) in the beginning to know if it
        converges
        :return: opt_obj
        """

        opt_obj = Optimizer(graph_obj, demand_obj, passenger_obj, network_obj, f)
        opt_obj.better_res = opt_obj.external_optimization(graph_obj, demand_obj, passenger_obj, network_obj, f,
                                                           tolerance, number_of_iteration=max_number_of_iteration)

        logger.info(opt_obj.string_network_optimization(opt_obj.better_res))

        return opt_obj

    def get_optimization_value(self) -> Tuple:
        """
        to get optimization value saved
        :return: (fopt, success, status, message, constr_violation, vrc)
        """
        return self.better_res

    def string_network_optimization(self, res: Tuple) -> str:
        """
        get a str summary about last external optimization in network_optimization
        :param res: Tuple, (fopt, success, status, message, constr_violation, vrc)
        :return:
        """
        fopt, success, status, message, constr_violation, vrc = res

        f = self.fopt_to_f(fopt)

        line = "\n\nOptimization Results"
        line += "\nSuccess: {}".format(success)
        line += "\nStatus: {}".format(status)
        line += "\nMessage: {}".format(message)
        line += "\nMax constrain violation: {}".format(constr_violation)
        line += "\nVRC: {}".format(vrc)
        line += "\n\nFrequency information [veh/hr]: "
        for route_id in f:
            line += "\n\t{}: {:.2f}".format(route_id, f[route_id])

        return line

    def last_iteration(self, res: Tuple):
        """
        return last network optimized with optimization result
        :param res: res: Tuple, (fopt, success, status, message, constr_violation, vrc)
        :return: Optimizer object, boarding dictionary, alighting dictionary, k dictionary, loaded_section_route
        """
        fopt, success, status, message, constr_violation, vrc = res

        f = self.fopt_to_f(fopt)
        final_optimizer = Optimizer(self.graph_obj, self.demand_obj, self.passenger_obj, self.network_obj, f)
        z, v, loaded_section_route = Assignment.get_alighting_and_boarding(final_optimizer.Vij,
                                                                           final_optimizer.hyperpaths,
                                                                           final_optimizer.successors,
                                                                           final_optimizer.assignment, f)
        k = self.get_k(loaded_section_route)
        return final_optimizer, z, v, k, loaded_section_route

    def get_network_results(self) -> List[Tuple]:
        """
        to get transport network results per line-direction
        :return: List[(route.id: str, f [veh/hr]: float, k [pax/veh]: float, B [veh]: float, cycle_time [hr]: float,
        CO [US$/hr-pax]: float, lambda_min, sub_table_i: List[(node_i, node_j, lambda)],
        sub_table_i: List[(node_i, node_j, lambda)]
        """
        if self.better_res is not None:
            res = self.better_res
        else:
            raise NoOptimalSolutionFoundException("not solution found in optimizer object")

        output = []

        fopt, success, status, message, constr_violation, vrc = res
        final_optimizer, z, v, k, loaded_section_route = self.last_iteration(res)

        f = self.fopt_to_f(fopt)

        # resultados de modos
        travel_time_line = OperatorsCost.lines_travel_time(final_optimizer.network_obj.get_routes(),
                                                           final_optimizer.graph_obj.get_edges_distance())
        cycle_time_line = OperatorsCost.get_cycle_time(z, v, final_optimizer.network_obj.get_routes(), travel_time_line)

        for route in final_optimizer.network_obj.get_routes():

            if f[route.id] > 0:

                # flota de buses
                b = cycle_time_line[route.id] * f[route.id]
                nodes_sequence_i = route.nodes_sequence_i
                nodes_sequence_r = route.nodes_sequence_r
                # carga que hay en los tramos
                total_pax = 0
                charge_i = []
                charge_r = []
                # total de subidas a la ruta
                total_b = 0

                # caso circular
                if route._type == RouteType.CIRCULAR:

                    # circular con sentido de ida
                    if len(nodes_sequence_i) > 0:
                        node_sequence = nodes_sequence_i
                        direction = "I"
                    # circular con sentido de vuelta
                    else:
                        node_sequence = nodes_sequence_r
                        direction = "R"
                    load_i = []
                    for i in node_sequence:
                        for stop_node in z[route.id][direction]:
                            if stop_node.city_node.graph_node.id == i:
                                total_b += z[route.id][direction][stop_node]
                                break
                        for stop_node in loaded_section_route[route.id][direction]:
                            if stop_node.city_node.graph_node.id == i:
                                load_i.append(loaded_section_route[route.id][direction][stop_node])
                                break

                    if total_b == 0:
                        co = 0
                    else:
                        co = (route.mode.co + route.mode.c1 * k[route.id]) * f[route.id] * cycle_time_line[route.id] / (
                                total_b * f[route.id])

                    if len(load_i) >= 1:
                        charge_min = min(load_i) / max(load_i)
                    else:
                        load_i = [1] * len(node_sequence)
                        charge_min = 0

                    sub_table = []
                    sub_table_i = []
                    sub_table_r = []

                    node_i = None
                    charge_ij = None
                    for i in range(len(node_sequence)):
                        if i == 0:
                            node_i = node_sequence[i]
                            charge_ij = load_i[i] / max(load_i)
                            continue
                        else:
                            node_j = node_sequence[i]
                            sub_table.append((node_i, node_j, abs(charge_ij)))
                            node_i = node_j
                            charge_ij = load_i[i] / max(load_i)

                    # circular con sentido de ida
                    if len(nodes_sequence_i) > 0:
                        sub_table_i = sub_table
                    # circular con sentido de vuelta
                    else:
                        sub_table_r = sub_table

                    output.append((
                        route.id, f[route.id], f[route.id] / route.mode.d, k[route.id], b,
                        cycle_time_line[route.id] * 60,
                        co, abs(charge_min), sub_table_i, sub_table_r))
                else:
                    # z and v: dic[route_id][direction][stop: StopNode] = pax [pax/veh]
                    total_pax = 0
                    for node_id in nodes_sequence_i:
                        bool_add = False
                        for stopnode in z[route.id]["I"]:
                            if stopnode.city_node.graph_node.id == node_id:
                                pax_b = z[route.id]["I"][stopnode]
                                pax_a = v[route.id]["I"][stopnode]
                                total_pax += pax_b - pax_a
                                total_b += pax_b

                                charge_i.append((node_id, total_pax / k[route.id]))
                                bool_add = True
                                break
                        if bool_add is False:
                            charge_i.append((node_id, total_pax / k[route.id]))

                    total_pax = 0
                    for node_id in nodes_sequence_r:
                        bool_add = False
                        for stopnode in z[route.id]["R"]:
                            if stopnode.city_node.graph_node.id == node_id:
                                pax_b = z[route.id]["R"][stopnode]
                                pax_a = v[route.id]["R"][stopnode]
                                total_pax += pax_b - pax_a
                                total_b += pax_b

                                charge_r.append((node_id, total_pax / k[route.id]))
                                bool_add = True
                                break
                        if bool_add is False:
                            charge_r.append((node_id, total_pax / k[route.id]))

                    co = (route.mode.co + route.mode.c1 * k[route.id]) * f[route.id] * cycle_time_line[route.id] / (
                            total_b * f[route.id])

                    charge_min = float("inf")
                    sub_table_i = []
                    node_i = None
                    charge_ij = None
                    for node_j, charge in charge_i:
                        if node_i is None:
                            node_i = node_j
                            charge_ij = charge
                            continue
                        else:
                            sub_table_i.append((node_i, node_j, abs(charge_ij)))
                            if charge_min > charge_ij:
                                charge_min = charge_ij
                            node_i = node_j
                            charge_ij = charge

                    sub_table_r = []
                    node_i = None
                    charge_ij = None
                    for node_j, charge in charge_r:
                        if node_i is None:
                            node_i = node_j
                            charge_ij = charge
                            continue
                        else:
                            sub_table_r.append((node_i, node_j, abs(charge_ij)))
                            if charge_min > charge_ij:
                                charge_min = charge_ij
                            node_i = node_j
                            charge_ij = charge

                    output.append((
                        route.id, f[route.id], f[route.id] / route.mode.d, k[route.id], b,
                        cycle_time_line[route.id] * 60,
                        co, abs(charge_min), sub_table_i, sub_table_r))
            else:
                nodes_sequence_i = route.nodes_sequence_i
                nodes_sequence_r = route.nodes_sequence_r

                sub_table_i = []
                sub_table_r = []

                node_i = None
                charge_ij = None
                for i in range(len(nodes_sequence_i)):
                    if i == 0:
                        node_i = nodes_sequence_i[i]
                        charge_ij = 0
                        continue
                    else:
                        node_j = nodes_sequence_i[i]
                        sub_table_i.append((node_i, node_j, abs(charge_ij)))
                        node_i = node_j
                        charge_ij = 0
                node_i = None
                charge_ij = None
                for i in range(len(nodes_sequence_r)):
                    if i == 0:
                        node_i = nodes_sequence_r[i]
                        charge_ij = 0
                        continue
                    else:
                        node_j = nodes_sequence_r[i]
                        sub_table_r.append((node_i, node_j, abs(charge_ij)))
                        node_i = node_j
                        charge_ij = 0

                output.append((
                    route.id, 0, 0, 0, 0, 0, 0, 0, sub_table_i, sub_table_r))

        return output

    def string_network_results(self) -> str:
        """
        to get a string with network results
        :return: str
        """

        output_network_results = self.get_network_results()
        line = "route_id;F[veh/hr];f[veh/hr-line];k[pax/veh];B[veh];tc[min];CO[US$/hr-pax];load_min;sub_table_i;sub_table_r"

        for route_id, F, f, k, b, cycle_time, co, charge_min, sub_table_i, sub_table_r in output_network_results:
            line += "\n{};{:.2f};{:.2f};{:.2f};{:.2f};{:.2f};{:.2f};{:.2f};{};{}".format(route_id, F, f, k, b,
                                                                                         cycle_time, co,
                                                                                         charge_min, sub_table_i,
                                                                                         sub_table_r)
        return line

    def write_file_network_results(self, file_path) -> None:
        """
        to write output file with result of optimization transport network
        :param file_path: file path
        :return: None
        """
        string_lines = self.string_network_results()
        file = open(file_path, 'w', encoding='utf-8')
        file.write(string_lines)
        file.close()

    def get_overall_results(self) -> defaultdict:
        """
        to get overall cost results per passenger
        :return: defaultdict:

        Key [unit]: value type

        VRC [USD$/hr-pax]: float,
        operators_cost [USD$/hr-pax]: float,
        infrastructure_cost [USD$/hr-pax]: float,
        users_cost [USD$/hr-pax]: float,
        travel_time_on_board [min/pax]: float,
        waiting time [min/pax]: float,
        access_time [min/pax]: float,
        transfers [transfer/pax]: float,
        vehicles_mode [veh/mode]: dic[TransportMode] = float [veh],
        vehicle_capacity_mode [pax/veh]: dic[TransportMode] = float [pax/veh],
        lines_mode : dic[TransportMode]=int [lines])
        """
        if self.better_res is not None:
            res = self.better_res
        else:
            raise NoOptimalSolutionFoundException("not solution found in optimizer object")

        fopt, success, status, message, constr_violation, vrc = res
        final_optimizer, z, v, k, loaded_section_route = self.last_iteration(res)

        f = self.fopt_to_f(fopt)

        # resultados de costos
        CO = self.operators_cost(z, v, f, k)
        CI = self.infrastructure_cost(f)
        CU = self.user_cost(final_optimizer.hyperpaths, final_optimizer.Vij, final_optimizer.assignment,
                            final_optimizer.successors, final_optimizer.extended_graph_obj, f, z, v)

        VRC = CO + CI + CU

        # resultados de usuarios
        ta, te, tv, t = UsersCost.resources_consumer(final_optimizer.hyperpaths, final_optimizer.Vij,
                                                     final_optimizer.assignment, final_optimizer.successors,
                                                     final_optimizer.extended_graph_obj,
                                                     final_optimizer.passenger_obj.va, f, z, v)

        # resultados de modos
        travel_time_line = OperatorsCost.lines_travel_time(final_optimizer.network_obj.get_routes(),
                                                           final_optimizer.graph_obj.get_edges_distance())
        cycle_time_line = OperatorsCost.get_cycle_time(z, v, final_optimizer.network_obj.get_routes(), travel_time_line)

        B = defaultdict(float)
        L = defaultdict(int)

        K_list = defaultdict(list)
        K = defaultdict(float)

        for route in self.network_obj.get_routes():
            if f[route.id] > 0:
                B[route.mode] += f[route.id] * cycle_time_line[route.id]
                L[route.mode] += route.mode.d
                K_list[route.mode].append(k[route.id])

        for mode in K_list:
            l = K_list[mode]
            K[mode] = sum(l) / len(l)

        output = defaultdict(None)
        output["VRC"] = VRC / self.total_trips
        output["operators_cost"] = CO / self.total_trips
        output["infrastructure_cost"] = CI / self.total_trips
        output["users_cost"] = CU / self.total_trips
        output["travel_time_on_board"] = tv / self.total_trips * 60
        output["waiting_time"] = te / self.total_trips * 60
        output["access_time"] = ta / self.total_trips * 60
        output["transfers"] = t / self.total_trips
        output["vehicles_mode"] = B
        output["vehicle_capacity_mode"] = K
        output["lines_mode"] = L
        return output

    def string_overall_results(self) -> str:
        """
        to get a string to print overall results in console

        Key [unit]: value type

        VRC [USD$/hr-pax]: float,
        operators_cost [USD$/hr-pax]: float,
        infrastructure_cost [USD$/hr-pax]: float,
        users_cost [USD$/hr-pax]: float,
        travel_time_on_board [min/pax]: float,
        waiting time [min/pax]: float,
        access_time [min/pax]: float,
        transfers [transfer/pax]: float,
        vehicles_mode [veh/mode]: dic[TransportMode] = float [veh],
        vehicle_capacity_mode [pax/veh]: dic[TransportMode] = float [pax/veh],
        lines_mode : dic[TransportMode]=int [lines])
        :return: string to print overall results in console
        """
        overall_results = self.get_overall_results()

        vrc = overall_results["VRC"]
        co = overall_results["operators_cost"]
        ci = overall_results["infrastructure_cost"]
        cu = overall_results["users_cost"]
        tv = overall_results["travel_time_on_board"]
        te = overall_results["waiting_time"]
        ta = overall_results["access_time"]
        t = overall_results["transfers"]
        b = overall_results["vehicles_mode"]
        k = overall_results["vehicle_capacity_mode"]
        l = overall_results["lines_mode"]

        line = "\n\nObjective function VRC [USD$/hr-pax]: {:.2f}".format(vrc)
        line += "\nOperators cost [USD$/hr-pax]        : {:.2f}".format(co)
        line += "\nInfrastructure cost [USD$/hr-pax]   : {:.2f}".format(ci)
        line += "\nUsers cost [USD$/hr-pax]            : {:.2f}".format(cu)

        line += "\n\nResources consumer for users"
        line += "\nTime on board vehicle [min/pax]: {:.2f}".format(tv)
        line += "\nWaiting time [min/pax]         : {:.2f}".format(te)
        line += "\nAccess time [min/pax]          : {:.2f}".format(ta)
        line += "\ntotal travel time [min/pax]    : {:.2f}".format(tv + ta + te)
        line += "\nTransfers [transfers/pax]      : {:.2f}".format(t)

        line += "\n\n"
        line += "Transport mode information: {}".format(len(b))

        for mode in b:
            line += "\n\n\tMode name: {}".format(mode.name)
            line += "\n\tB [veh]      : {:.2f}".format(b[mode])
            line += "\n\tK [pax/veh]  : {:.2f}".format(k[mode])
            line += "\n\tL [lines]    : {}".format(l[mode])

        return line
Exemple #8
0
    def test_get_cycle_time(self):
        """
        test get_cycle_time method of class operators_cost
        :return:
        """
        graph_obj = Graph.build_from_parameters(n=2, l=10, g=0.5, p=2)
        demand_obj = Demand.build_from_parameters(graph_obj=graph_obj,
                                                  y=100,
                                                  a=0.5,
                                                  alpha=1 / 3,
                                                  beta=1 / 3)
        passenger_obj = Passenger.get_default_passenger()
        [bus_obj, metro_obj] = TransportMode.get_default_modes()

        network_obj = TransportNetwork(graph_obj=graph_obj)

        feeder_routes_metro = network_obj.get_feeder_routes(mode_obj=metro_obj)
        radial_routes_bus = network_obj.get_radial_routes(mode_obj=bus_obj)

        for route in feeder_routes_metro:
            network_obj.add_route(route_obj=route)

        for route in radial_routes_bus:
            network_obj.add_route(route_obj=route)

        extended_graph_obj = ExtendedGraph(graph_obj=graph_obj,
                                           routes=network_obj.get_routes(),
                                           TP=passenger_obj.pt,
                                           frequency_routes=None)
        hyperpath_obj = Hyperpath(extended_graph_obj=extended_graph_obj,
                                  passenger_obj=passenger_obj)

        hyperpaths, labels, successors, frequency, Vij = hyperpath_obj.get_all_hyperpaths(
            OD_matrix=demand_obj.get_matrix())

        OD_assignment = Assignment.get_assignment(hyperpaths=hyperpaths,
                                                  labels=labels,
                                                  p=2,
                                                  vp=passenger_obj.va,
                                                  spa=passenger_obj.spa,
                                                  spv=passenger_obj.spv)

        f = defaultdict(float)
        for route in network_obj.get_routes():
            f[route.id] = 28

        z, v, loaded_section_route = Assignment.get_alighting_and_boarding(
            Vij=Vij,
            hyperpaths=hyperpaths,
            successors=successors,
            assignment=OD_assignment,
            f=f)

        lines_travel_time = OperatorsCost.lines_travel_time(
            routes=network_obj.get_routes(),
            edge_distance=graph_obj.get_edges_distance())

        line_cycle_time = OperatorsCost.get_cycle_time(
            z, v, network_obj.get_routes(), lines_travel_time)

        self.assertEqual(round(line_cycle_time["F_metro_1"], 7), 0.2500082)
        self.assertEqual(round(line_cycle_time["R_bus_1"], 7), 1.5032756)
        self.assertEqual(line_cycle_time["l1"], 0)
Exemple #9
0
    def test_get_all_hyperpaths(self):
        """
        to test get_all_hyperpaths method of class Hyperpath
        :return:
        """

        graph_obj = Graph.build_from_parameters(n=2, l=10, g=0.5, p=2)

        demand_obj = Demand.build_from_parameters(graph_obj,
                                                  y=1000,
                                                  a=0.5,
                                                  alpha=1 / 3,
                                                  beta=1 / 3)
        OD_matrix = demand_obj.get_matrix()

        [bus_obj, metro_obj] = TransportMode.get_default_modes()

        passenger_obj = Passenger.get_default_passenger()

        network_obj = TransportNetwork(graph_obj)
        radial = network_obj.get_radial_routes(bus_obj)
        diametral = network_obj.get_diametral_routes(bus_obj, jump=1)
        diametral_metro = network_obj.get_diametral_routes(metro_obj, jump=1)

        for route in radial:
            network_obj.add_route(route)
        for route in diametral:
            network_obj.add_route(route)
        for route in diametral_metro:
            network_obj.add_route(route)

        extended_graph_obj = ExtendedGraph(graph_obj, network_obj.get_routes(),
                                           16)

        nodes = extended_graph_obj.get_extended_graph_nodes()

        P1 = None
        SC2 = None
        stop_bus = None
        stop_metro = None
        for city_node in nodes:
            if city_node.graph_node.name == str("P_1"):
                P1 = city_node
                for stop in nodes[city_node]:
                    if stop.mode.name == "bus":
                        stop_bus = stop
                    if stop.mode.name == "metro":
                        stop_metro = stop
            if city_node.graph_node.name == str("SC_2"):
                SC2 = city_node

        hyper_path_obj = Hyperpath(extended_graph_obj, passenger_obj)

        hyperpaths, labels, successors, frequencies, vij = hyper_path_obj.get_all_hyperpaths(
            OD_matrix)

        self.assertEqual(round(labels[P1][SC2][stop_bus], 5), 1.17738)
        self.assertEqual(round(labels[P1][SC2][stop_metro], 5), 0.71071 + 0.05)

        self.assertEqual(len(hyperpaths), 4)
        self.assertEqual(len(hyperpaths[P1]), 3)
        self.assertEqual(len(hyperpaths[SC2]), 2)