예제 #1
0
 def test_cases(self) -> List[Dict]:
     data1 = load_json(
         os.path.join(os.path.dirname(__file__),
                      "data/test_instance_1.json"))
     data2 = load_json(
         os.path.join(os.path.dirname(__file__),
                      "data/test_instance_3.json"))
     return [data1, data2]
예제 #2
0
 def test_check_wrong_schema_1(self):
     schema = load_json(
         self.get_project_data_file("vrp_solution_schema.json"))
     del schema['properties']['routes']['items']['required']
     sm = SchemaManager(schema)
     val = sm.validate_schema()
     self.assertFalse(val)
예제 #3
0
class Solution(SolutionCore):
    schema = load_json(
        os.path.join(os.path.dirname(__file__), "../schemas/solution.json"))

    def to_json(self, path):
        with open(path, "w") as f:
            json.dump(self.to_dict(), f)

    def copy(self):
        return Solution(pickle.loads(pickle.dumps(self.data, -1)))

    def get_solution_dict(self):
        return self.data["flows"]

    def get_pair_of_nodes_flows(self):
        return {(v["origin"], v["destination"]): v["flow"]
                for v in self.data["flows"]}

    def get_used_nodes(self):
        return TupList([v["origin"] for v in self.data["flows"]] +
                       [v["destination"]
                        for v in self.data["flows"]]).unique()

    def get_amount_supplied(self):
        return (TupList({
            "origin": v["origin"],
            "product": v["product"],
            "flow": v["flow"]
        } for v in self.data["flows"]).to_dict(
            result_col="flow", indices=["origin",
                                        "product"]).vapply(lambda v: sum(v)))
예제 #4
0
class Solution(SolutionCore):
    schema = load_json(
        os.path.join(os.path.dirname(__file__), "../schemas/output.json")
    )

    @classmethod
    def from_dict(cls, data: dict) -> "Solution":
        data_p = dict()
        data_p["detail_cutting_patterns"] = {
            (el["id_bar"], el["id_pattern"], el["id_product"]): el
            for el in data["detail_cutting_patterns"]
        }
        data_p["number_cutting_patterns"] = {
            (el["id_bar"], el["id_pattern"]): el
            for el in data["number_cutting_patterns"]
        }
        return cls(data_p)

    def to_dict(self) -> dict:
        tables = ["detail_cutting_patterns", "number_cutting_patterns"]
        data_p = {el: self.data[el].values_tl() for el in tables}
        return pickle.loads(pickle.dumps(data_p, -1))

    def _get_property(self, key, prop) -> SuperDict:
        return self.data[key].get_property(prop)

    def get_number_bars_patterns(self) -> SuperDict:
        """
        Returns a SuperDict with the ids of the bars and the patterns as keys and the number of patterns that should be cut as values.
        For example, {('bar1, 'pa2'): 1, ('bar1', 'pa3'): 200, ...}
        """
        return self._get_property("number_cutting_patterns", "number_of_patterns")

    def get_number_products_per_pattern(self) -> SuperDict:
        """
        Returns a SuperDict with the ids of the bars, the patterns and the products as keys and the number of products per bar and pattern as values.
        For example, {('bar1', 'pa2', 'pr1'): 0, ('bar1', 'pa2', 'pr2'): 2, ('bar1', 'pa2', 'pr3'): 0, ...}
        """
        return self._get_property("detail_cutting_patterns", "number_of_products")

    def get_total_number_cut_products(self) -> SuperDict:
        """
        Returns a SuperDict the products as keys and the total number of cut products as values.
        For example, {'pr1': 600, 'pr2': 500, ...}
        """
        number_patterns = self.get_number_bars_patterns()
        number_products_per_pattern = self.get_number_products_per_pattern()
        total_number_cut_products = SuperDict()
        for (bar, pattern, product) in number_products_per_pattern.keys_tl():
            if product not in total_number_cut_products.keys():
                total_number_cut_products[product] = (
                    number_products_per_pattern[(bar, pattern, product)]
                    * number_patterns[(bar, pattern)]
                )
            else:
                total_number_cut_products[product] += (
                    number_products_per_pattern[(bar, pattern, product)]
                    * number_patterns[(bar, pattern)]
                )
        return total_number_cut_products
예제 #5
0
class Rostering(ApplicationCore):
    name = "rostering"
    description = "Rostering model"
    instance = Instance
    solution = Solution
    solvers = dict(mip=MipModel)
    schema = load_json(
        os.path.join(os.path.dirname(__file__), "./schemas/config.json"))

    @property
    def test_cases(self) -> List[Dict]:
        data1 = load_json(
            os.path.join(os.path.dirname(__file__),
                         "data/test_instance_1.json"))
        data2 = load_json(
            os.path.join(os.path.dirname(__file__),
                         "data/test_instance_3.json"))
        return [data1, data2]

    def get_solver(self, name: str = "mip"):
        if "." in name:
            solver, _ = name.split(".")
        else:
            solver = name
        return self.solvers.get(solver)
예제 #6
0
class Solution(SolutionCore):
    schema = load_json(
        os.path.join(os.path.dirname(__file__), "../schemas/output.json")
    )

    def get_assignments(self):
        return pt.SuperDict({v["node"]: v["color"] for v in self.data["assignment"]})
예제 #7
0
 def test_check_wrong_schema_2(self):
     schema = load_json(
         self.get_project_data_file("vrp_solution_schema.json"))
     schema['properties']['routes']['items']['properties']['pos'][
         'type'] = 'not_a_type'
     sm = SchemaManager(schema)
     val = sm.validate_schema()
     self.assertFalse(val)
예제 #8
0
 def test_check_wrong_schema_3(self):
     schema = load_json(
         self.get_project_data_file("vrp_solution_schema.json"))
     del schema["properties"]["routes"]["items"]["properties"]["pos"][
         "type"]
     sm = SchemaManager(schema)
     val = sm.validate_schema()
     self.assertFalse(val)
예제 #9
0
    def load_json(path):
        """
        Load a json file

        :param path: the path of the json file.json

        return the json content.
        """
        return load_json(path)
예제 #10
0
class Instance(InstanceCore):
    schema = load_json(
        os.path.join(os.path.dirname(__file__), "../schemas/input.json"))

    @classmethod
    def from_dict(cls, data) -> "Instance":
        demand = pt.SuperDict({v["n"]: v for v in data["demand"]})

        weights = (pt.TupList(data["arcs"]).vapply(
            lambda v: v.values()).vapply(lambda x: list(x)).to_dict(
                result_col=2, is_list=False))
        datap = {**data, **dict(demand=demand, arcs=weights)}
        return cls(datap)

    def to_dict(self):
        data = pickle.loads(pickle.dumps(self.data, -1))
        data["demand"] = data["demand"].values_l()
        data["arcs"] = (data["arcs"].kvapply(
            lambda k, v: dict(n1=k[0], n2=k[1], w=v)).values_l())

        return data

    def get_nodes(self):
        return [i["n"] for i in self.data["demand"].values()]

    def get_arcs(self):
        return self.data["arcs"].keys_tl()

    def get_vehicles(self):
        if self.data["parameters"]["numVehicles"] < 1:
            return list(range(self.data["parameters"]["size"]))
        return list(range(self.data["parameters"]["numVehicles"]))

    def get_weights(self):
        return self.data["arcs"]

    def _get_property(self, key, prop) -> SuperDict:
        return self.data[key].get_property(prop)

    def get_demand(self):
        return self._get_property("demand", "demand")

    def get_capacity(self):
        return {None: self.data["parameters"]["capacity"]}

    def get_depots(self):
        return [n["n"] for n in self.data["depots"]]

    def get_num_nodes(self):
        return self.data["parameters"]["size"]

    def get_num_vehicles(self):
        if self.data["parameters"]["numVehicles"] < 1:
            return self.data["parameters"]["size"]
        return self.data["parameters"]["numVehicles"]
예제 #11
0
class Instance(InstanceCore):
    schema = load_json(
        os.path.join(os.path.dirname(__file__), "../schemas/input.json"))

    def __init__(self, data):
        data = SuperDict(data)
        data["arcs"] = TupList(data["arcs"])
        super().__init__(data)

    @classmethod
    def from_tsplib_file(cls, path):
        return cls.from_tsplib95(tsp.load(path))

    @classmethod
    def from_tsplib95(cls, problem):
        nodes = list(problem.get_nodes())
        edge_to_dict = lambda e: dict(
            n1=nodes[e[0]], n2=nodes[e[1]], w=problem.get_weight(*e))
        arcs = TupList(edge_to_dict(e) for e in problem.get_edges())
        return cls(dict(arcs=arcs))

    def to_tsplib95(self):
        arcs = TupList(self.data["arcs"])
        nodes = (arcs.take("n1") + arcs.take("n2")).unique()
        pos = {k: v for v, k in enumerate(nodes)}
        arc_dict = arcs.to_dict(result_col="w",
                                indices=["n1", "n2"],
                                is_list=False).to_dictdict()
        arc_weights = [[]] * len(nodes)
        for n1, n2dict in arc_dict.items():
            n1list = arc_weights[pos[n1]] = [0] * len(n2dict)
            for n2, w in n2dict.items():
                n1list[pos[n2]] = w

        if len(nodes)**2 == len(arcs):
            edge_weight_format = "FULL_MATRIX"
        elif abs(len(nodes)**2 - len(arcs) * 2) <= 2:
            edge_weight_format = "LOWER_DIAG_ROW"
        else:
            # TODO: can there another possibility?
            edge_weight_format = "LOWER_DIAG_ROW"
        dict_data = dict(
            name="TSP",
            type="TSP",
            comment="",
            dimension=len(nodes),
            edge_weight_type="EXPLICIT",
            edge_weight_format=edge_weight_format,
            edge_weights=arc_weights,
        )
        return tsp.models.StandardProblem(**dict_data)

    def get_arcs(self) -> TupList:
        return self.data["arcs"]
예제 #12
0
class Solution(SolutionCore):
    schema = load_json(
        os.path.join(os.path.dirname(__file__), "../schemas/solution.json"))

    @classmethod
    def from_dict(cls, data: dict) -> "Solution":
        data_p = {
            el: {(v["id_employee"], v["time_slot"]): v
                 for v in data[el]}
            for el in ["works"]
        }

        return cls(SuperDict(data_p))

    def to_dict(self) -> dict:
        return {
            "works":
            pickle.loads(pickle.dumps(self.data["works"].values_l(), -1))
        }

    def get_time_slots(self) -> TupList[str]:
        """
        Returns a TupList with all the time slots where someone has worked. It can contain duplicated values
        For example: ["2021-09-06T07:00", "2021-09-06T08:00", "2021-09-06T08:00", ...]
        """
        return TupList([(v["time_slot"]) for v in self.data["works"].values()])

    def get_working_hours(self) -> int:
        """
        Returns the number of total hours worked in the solution
        """
        return len(self.data["works"])

    def get_hours_worked_per_week(self) -> SuperDict:
        """
        Returns a SuperDict with the amount of time slots worked by each employee in each week.
        For example: {(0, 1): 40, ...}
        """
        return (TupList({
            "id_employee": id_employee,
            "ts": ts,
            "week": get_week_from_string(ts)
        } for (id_employee, ts) in self.data["works"]).to_dict(
            result_col="ts", indices=["week",
                                      "id_employee"]).vapply(lambda v: len(v)))

    def get_ts_employee(self) -> SuperDict:
        """
        Returns a SuperDict with the time slot as a key and a list of employees that work each time slot as the value
        For example: {"2021-09-06T07:00": [1, 2], "2021-09-06T08:00": [1, 2, 4], ...}
        """
        return TupList([v for v in self.data["works"]]).to_dict(0)
예제 #13
0
class TspApp(ApplicationCore):
    name = "tsp"
    instance = Instance
    solution = Solution
    solvers = dict(naive=TSPNaive, aco=ACOpy, cpsat=OrToolsCP)
    schema = load_json(
        os.path.join(os.path.dirname(__file__), "schemas/config.json"))

    @property
    def test_cases(self) -> List[Dict]:
        instance = Instance.from_tsplib_file(
            os.path.join(os.path.dirname(__file__), "data/gr17.tsp"))
        return [instance.to_dict()]
예제 #14
0
    def test_cases(self) -> List[Union[Dict, Tuple[Dict, Dict]]]:

        options_instance = ["data/example_instance_1.json"]

        options_solution = ["data/example_solution_1.json"]

        return [
            (
                load_json(
                    os.path.join(
                        os.path.dirname(__file__),
                        options_instance[i],
                    )
                ),
                load_json(
                    os.path.join(
                        os.path.dirname(__file__),
                        options_solution[i],
                    )
                ),
            )
            for i in range(len(options_instance))
        ]
예제 #15
0
class Roadef(ApplicationCore):
    name = "roadef"
    instance = Instance
    solution = Solution
    schema = load_json(os.path.join(os.path.dirname(__file__), "./schemas/config.json"))
    solvers = dict(Greedy=Greedy, MIPModel=MIPModel, PeriodicMIPModel=PeriodicMIP)

    @property
    def test_cases(self):
        cwd = os.path.dirname(os.path.realpath(__file__))
        _get_file = lambda name: os.path.join(cwd, "data", name)
        _get_instance = lambda fn: Instance.from_json(_get_file(fn)).to_dict()
        _get_solution = lambda fn: Solution.from_json(_get_file(fn)).to_dict()
        return TupList(
            [("example_instance_filtered.json", "example_solution_filtered.json")]
        ).vapply(lambda v: (_get_instance(v[0]), _get_solution(v[1])))
예제 #16
0
    def validate_schema(self, print_errors=False):
        """
        Validate the loaded jsonschema
        :param bool print_errors: If true, will print the errors

        :return: True if jsonschema is valid, else False
        """
        path_schema_validator = os.path.join(os.path.dirname(__file__),
                                             "../data/schema_validator.json")
        validation_schema = load_json(path_schema_validator)
        v = self.validator(validation_schema)

        if v.is_valid(self.get_jsonschema()):
            return True

        error_list = [e for e in v.iter_errors(self.get_jsonschema())]
        if print_errors:
            for e in error_list:
                print(e)
        return False
예제 #17
0
class Solution(SolutionCore):
    schema = load_json(
        os.path.join(os.path.dirname(__file__), "../schemas/output.json"))

    # {routes: {route1: [n1, n2, n3], route2: [n4, n5, n6]}}

    def to_dict(self):
        routes = pt.SuperDict()
        for k, v in self.data["routes"].items():
            routes[k] = pt.TupList(v).kvapply(lambda k, v: (k, v))
        routes = routes.to_tuplist().vapply(
            lambda v: dict(route=v[0], pos=v[1], node=v[2]))
        return pt.SuperDict(routes=routes)

    @classmethod
    def from_dict(cls, data):
        data_p = (data["routes"].to_dict(
            result_col=["pos", "node"],
            indices=["route"
                     ]).vapply(lambda r: r.sorted(key=lambda t: t[0]).take(1)))
        return cls(pt.SuperDict(routes=data_p))
예제 #18
0
class BarCutting(ApplicationCore):
    name = "bar_cutting"
    instance = Instance
    solution = Solution
    solvers = dict(mip=MipModel, CG=ColumnGeneration)
    schema = load_json(os.path.join(os.path.dirname(__file__), "./schemas/config.json"))
    schema["properties"]["solver"]["enum"].append("mip.cbc")
    schema["properties"]["solver"]["enum"].append("CG.cbc")

    @property
    def test_cases(self) -> List[Union[Dict, Tuple[Dict, Dict]]]:

        options_instance = ["data/example_instance_1.json"]

        options_solution = ["data/example_solution_1.json"]

        return [
            (
                load_json(
                    os.path.join(
                        os.path.dirname(__file__),
                        options_instance[i],
                    )
                ),
                load_json(
                    os.path.join(
                        os.path.dirname(__file__),
                        options_solution[i],
                    )
                ),
            )
            for i in range(len(options_instance))
        ]

    def get_solver(self, name: str = "mip") -> Union[Type[Experiment], None]:
        if "." in name:
            solver, _ = name.split(".")
        else:
            solver = name
        return self.solvers.get(solver)
예제 #19
0
class Instance(InstanceCore):
    schema = load_json(
        os.path.join(os.path.dirname(__file__), "../schemas/instance.json")
    )

    def __init__(self, data):
        super().__init__(data)
        self._id_trailers = self.data["trailers"].keys_tl()

    def copy(self):
        return Instance(copy(self.data))

    @classmethod
    def from_file(cls, path):
        with open(path, "r") as fd:
            data_file = xmltodict.parse(fd.read())

        data = data_file["IRP_Roadef_Challenge_Instance"].copy()
        data = cls.dict_to_int_or_float(data)

        matrices = get_matrices_from_file(data)
        drivers = get_drivers_from_file(data)
        trailers = get_trailers_from_file(data)
        bases = get_bases_from_file(data)
        sources = get_sources_from_file(data)
        customers = get_customers_from_file(data)

        data_out = dict(
            matrices=matrices,
            drivers=drivers,
            trailers=trailers,
            bases=bases,
            sources=sources,
            customers=customers,
            horizon=data["horizon"],
            unit=data["unit"],
        )

        return cls(data_out)

    @classmethod
    def from_dict(cls, data_dict):

        # main tables
        trailers = _index(data_dict["trailers"])
        drivers = get_drivers(data_dict)
        customers = get_customers(data_dict)
        sources = get_sources(data_dict)

        # dist and time matrices
        matrices = {(el["L1"], el["L2"]): el for el in data_dict["matrices"]}

        data_out = SuperDict(
            trailers=trailers,
            drivers=drivers,
            customers=customers,
            sources=sources,
            matrices=matrices,
            coordinates=data_dict.get("coordinates", [])
        )

        # other parameters
        for param in ["unit", "horizon"]:
            data_out[param] = data_dict["parameters"][param]

        data_out["bases"] = data_dict["bases"][0]
        return cls(data_out)

    def to_dict(self):
        data_dict = pickle.loads(pickle.dumps(self.data, -1))

        drivers_tables = drivers_to_jsonschema(data_dict["drivers"])
        customer_tables = customers_to_jsonschema(data_dict["customers"])
        sources_tables = sources_to_jsonschema(data_dict["sources"])

        result = SuperDict({key: data_dict[key].values_tl() for key in ["trailers", "matrices"]})

        result["parameters"] = dict(
            unit=data_dict["unit"], horizon=data_dict["horizon"]
        )
        result["bases"] = [data_dict["bases"]]
        result["coordinates"] = data_dict.get("coordinates", [])
        # we merge the allowedTrailers tables:
        for key in ["allowedTrailers"]:
            result[key] = sources_tables.pop(key) + customer_tables.pop(key)
        return (
            result._update(drivers_tables)
            ._update(customer_tables)
            ._update(sources_tables)
        )

    @staticmethod
    def dict_to_int_or_float(data_dict):
        """
        Tranforms a dictionary to change all strings into integer of floating numbers if the strings
            represent numbers
        For example: Transforms {a: '4', b: {c: '7', d: ['8.7', '9']}}
            into {a: 4, b: {c: 7, d: [8.7, 9]}}
        """
        for key in data_dict.keys():
            if isinstance(data_dict[key], str):
                if data_dict[key].isnumeric():
                    data_dict[key] = int(data_dict[key])
                else:
                    try:
                        fl = float(data_dict[key])
                        data_dict[key] = fl
                    except ValueError:
                        pass
            elif isinstance(data_dict[key], list):
                if isinstance(data_dict[key][0], str):
                    try:
                        data_dict[key] = list(map(int, data_dict[key]))
                    except ValueError:
                        try:
                            data_dict[key] = list(map(float, data_dict[key]))
                        except ValueError:
                            pass
                elif isinstance(data_dict[key][0], OrderedDict):
                    data_dict[key] = list(
                        map(Instance.dict_to_int_or_float, data_dict[key])
                    )
            elif isinstance(data_dict[key], OrderedDict):
                data_dict[key] = Instance.dict_to_int_or_float(data_dict[key])
        return dict(data_dict)

    @staticmethod
    def from_element_or_list_to_dict(element_or_list):
        """
        Converts a list into a dictionary indexed by the field 'index' of each element of the list.
        If the input is not a list, it is converted into a list before converting to a dictionary
        For example: [{'index': 4, 'value': 5}, {'index': 7, 'value': 8}]
            is transformed to {4: {'index': 4, 'value': 5}, 7: {'index': 7, 'value': 8}}
        """
        if not isinstance(element_or_list, list):
            element_or_list = [element_or_list]
        return {int(el["index"]): el for el in element_or_list}

    def get_number_customers(self):
        """ Returns the total number of customers """
        return len(self.data["customers"])

    def get_number_drivers(self):
        """ Returns the total number of drivers """
        return len(self.data["drivers"])

    def get_number_sources(self):
        """ Returns the total number of sources """
        return len(self.data["sources"])

    def get_number_locations(self):
        """ Returns the total number of locations """
        return len(self.data["sources"]) + len(self.data["customers"]) + 1

    def get_number_trailers(self):
        """ Returns the total number of trailers """
        return len(self.data["trailers"])

    def get_horizon(self):
        """ Returns the total number of hours in the time horizon """
        return self.data["horizon"]

    def get_unit(self):
        return self.data["unit"]

    def get_distance_between(self, point1, point2):
        """ Returns the distance between two locations """
        return self.data["matrices"][point1, point2]["dist"]

    def get_time_between(self, point1, point2):
        """ Returns the travelling time between two locations """
        return self.data["matrices"][point1, point2]["time"]

    def get_id_customers(self):
        """ Returns the indices of all the customers """
        return self.data["customers"].keys_tl()

    def get_id_sources(self):
        """ Returns the indices of all the sources """
        return self.data["sources"].keys_tl()

    def get_id_trailers(self):
        """ Returns the indices of all the trailers """
        return self._id_trailers

    def get_id_drivers(self):
        """ Returns the indices of all the drivers """
        return self.data["drivers"].keys_tl()

    def get_id_base(self):
        """ Returns the index of the base """
        return self.data["bases"]["index"]

    def get_customer(self, id_customer):
        """ Returns the customer of given index """
        return self.data["customers"][id_customer]

    def get_source(self, id_source):
        """ Returns the source of given index """
        return self.data["sources"][id_source]

    def get_driver(self, id_driver):
        """ Returns the driver of given index """
        return self.data["drivers"][id_driver]

    def get_trailer(self, id_trailer):
        """ Returns the trailer of given index """
        return self.data["trailers"][id_trailer]

    def get_property(self, key, prop):
        return self.data[key].get_property(prop)

    def get_trailer_property(self, id_trailer, prop):
        """ Returns the property prop of the trailer of given index """
        return self.data["trailers"][id_trailer][prop]

    def get_driver_property(self, id_driver, prop):
        """ Returns the property prop of the driver of given index """
        return self.data["drivers"][id_driver][prop]

    def get_customer_property(self, id_customer, prop):
        """ Returns the property prop of the customer of given index """
        return self.data["customers"][id_customer][prop]

    def get_source_property(self, id_source, prop):
        """ Returns the property prop of the source of given index """
        return self.data["sources"][id_source][prop]

    def get_location_property(self, id_location, prop):
        """
        Returns the property prop of the location of given index
        The location should be a customer of source
        """
        if self.is_source(id_location):
            return self.data["sources"][id_location][prop]
        elif self.is_customer(id_location):
            return self.data["customers"][id_location][prop]

    def is_call_in_customer(self, id_customer):
        """
        Returns True if the customer is a call-in customer
        """
        return self.get_location_property(id_customer, "callIn") == 1

    @staticmethod
    def is_base(location):
        """ Returns True if the location is a base """
        return location == 0

    def is_source(self, location):
        """ Returns True if the location is a source """
        return 0 < location < 1 + self.get_number_sources()

    def is_customer(self, location):
        """ Returns True if the location is a customer """
        return location >= 1 + self.get_number_sources()

    def is_valid_location(self, location):
        """
        Return True if the input is a valid location index
        """
        return 0 <= location < 1 + self.get_number_sources() + self.get_number_customers()

    def is_customer_or_source(self, location):
        """ Returns True if the location is customer or a source """
        return self.is_customer(location) or self.is_source(location)
예제 #20
0
class Instance(InstanceCore):
    schema = load_json(
        os.path.join(os.path.dirname(__file__), "../schemas/input.json"))

    def get_pairs(self):
        return pt.TupList((el["n1"], el["n2"]) for el in self.data["pairs"])
예제 #21
0
 def test_cases(self) -> List[Dict]:
     data = load_json(
         os.path.join(os.path.dirname(__file__), "data/input_test_1_small.json")
     )
     return [data]
예제 #22
0
 def test_cases(self):
     data = load_json(
         os.path.join(os.path.dirname(__file__), "data/input_data_test1.json")
     )
     return [data]
예제 #23
0
class Instance(InstanceCore):
    schema = load_json(
        os.path.join(os.path.dirname(__file__), "../schemas/input.json"))

    @classmethod
    def from_dict(cls, data: dict) -> "Instance":
        tables = [
            "bars",
            "products",
            "demand",
        ]
        data_p = {el: {v["id"]: v for v in data[el]} for el in tables}
        data_p["cutting_patterns"] = {(el["id_bar"], el["id_pattern"],
                                       el["id_product"]): el
                                      for el in data["cutting_patterns"]}

        return cls(data_p)

    def to_dict(self) -> dict:
        tables = [
            "bars",
            "products",
            "demand",
            "cutting_patterns",
        ]
        data_p = {el: self.data[el].values_tl() for el in tables}

        return pickle.loads(pickle.dumps(data_p, -1))

    def get_bars(self) -> TupList:
        """
        Returns a TupList with the ids of the bars.
        For example, ['bar1', 'bar2', ...]
        """
        return self.data["bars"].keys_tl()

    def get_bar1_id(self) -> str:
        """
        Returns a string with the id of the bar1.
        For example, 'bar1'.
        """
        return self.get_bars()[0]

    def get_bar2_id(self) -> str:
        """
        Returns a string with the id of the bar1.
        For example, 'bar2'.
        """
        return self.get_bars()[1]

    def get_products(self) -> TupList:
        """
        Returns a TupList with the ids of the products.
        For example, ['pr1', 'pr2', ...]
        """
        return self.data["products"].keys_tl()

    def get_patterns(self) -> TupList:
        """
        Returns a TupList with the ids of the patterns.
        For example, ['pa1', 'pa2', ...]
        """
        return self.data["cutting_patterns"].keys_tl().take(1).unique()

    def get_bars_patterns(self) -> TupList:
        """
        Returns a TupList with combinations of bars and patterns.
        For example, [('bar1', 'pa1'), ('bar1', 'pa2'), ...]
        Here we use unique2 instead of unique to get a list of tuples instead of a list of lists.
        """
        return self.data["cutting_patterns"].keys_tl().take([0, 1]).unique2()

    def get_bars_patterns_products(self) -> TupList:
        """
        Returns a TupList with combinations of bars, patterns and products.
        For example, [('bar1', 'pa1', 'pr1'), ('bar1', 'pa1', 'pr2'), ...]
        """
        return self.data["cutting_patterns"].keys_tl()

    def _get_property(self, key, prop) -> SuperDict:
        return self.data[key].get_property(prop)

    def get_bars_length(self) -> SuperDict:
        """
        Returns a SuperDict with the bars as keys and the length of the bars as values.
        For example, {'bar1': 150, 'bar2': 200, ...}
        """
        return self._get_property("bars", "length")

    def get_bar1_length(self) -> int:
        """
        Returns an integer the length of the bar1.
        For example, 150.
        """
        id_bar1 = self.get_bar1_id()
        return self._get_property("bars", "length")[id_bar1]

    def get_bar2_length(self) -> int:
        """
        Returns an integer the length of the bar2.
        For example, 200.
        """
        id_bar2 = self.get_bar2_id()
        return self._get_property("bars", "length")[id_bar2]

    def get_product_length(self) -> SuperDict:
        """
        Returns a SuperDict with the products as keys and the length of the products as values.
        For example, {'pr1': 40, 'pr2': 60, ...}
        """
        return self._get_property("products", "length")

    def get_demand(self) -> SuperDict:
        """
        Returns a SuperDict with the products as keys and the demand per product as values.
        For example, {'pr1': 600, 'pr2': 500, ...}
        """
        return self._get_property("demand", "demand")

    def get_number_products_per_bar_pattern(self) -> SuperDict:
        """
        Returns a SuperDict with the bars, patterns and products as keys and the number of products per bar and pattern as values.
        For example, {('bar1', 'pa1', 'pr1'): 3, ('bar1', 'pa1', 'pr2'): 0, ...}
        """
        return self._get_property("cutting_patterns", "number_of_products")
예제 #24
0
class Instance(InstanceCore):
    schema = load_json(
        os.path.join(os.path.dirname(__file__), "../schemas/instance.json")
    )

    def __init__(self, data: dict):
        super().__init__(data)

        # Stores a list of the starting date of each week ordered
        self.weeks = TupList()

        # First object stores a list of the dates ordered,
        # the second the properties for each date.
        self.dates = TupList()
        self.dates_properties = SuperDict()

        # First object stores a list of the time slots ordered,
        # the second the properties for each one.
        self.time_slots = TupList()
        self.time_slots_properties = SuperDict()

        self.cache_properties()

    @classmethod
    def from_dict(cls, data: dict) -> "Instance":
        tables = ["employees", "shifts", "contracts"]

        data_p = {el: {v["id"]: v for v in data[el]} for el in tables}

        data_p["demand"] = {(el["day"], el["hour"]): el for el in data["demand"]}

        data_p["parameters"] = pickle.loads(pickle.dumps(data["parameters"], -1))

        if data.get("skill_demand"):
            data_p["skill_demand"] = {
                (el["day"], el["hour"], el["id_skill"]): el for el in data["skill_demand"]
            }
        else:
            data_p["skill_demand"] = {}

        if data.get("skills"):
            data_p["skills"] = {
                el["id"]: el for el in data["skills"]
            }
        else:
            data_p["skills"] = {}

        if data.get("skills_employees"):
            data_p["skills_employees"] = TupList(data["skills_employees"]).to_dict(
                result_col=["id_employee"], is_list=True, indices=["id_skill"]
            )
        else:
            data_p["skills_employees"] = TupList()

        return cls(data_p)

    def to_dict(self) -> Dict:
        tables = ["employees", "shifts", "contracts", "demand", "skill_demand", "skills"]

        data_p = {el: self.data[el].values_l() for el in tables}

        data_p["parameters"] = self.data["parameters"]
        data_p["skills_employees"] = [
            dict(id_employee=id_employee, id_skill=id_skill)
            for id_skill in self.data["skills_employees"]
            for id_employee in self.data["skills_employees"][id_skill]
        ]
        return pickle.loads(pickle.dumps(data_p, -1))

    def cache_properties(self):
        """Caches the list of weeks, dates and time slots and its associated properties"""
        self.weeks = self._get_weeks()
        self.dates = self._get_dates()
        self.dates_properties = self._get_dates_properties()
        self.time_slots = self._get_time_slots()
        self.time_slots_properties = self._get_time_slots_properties()

    def _get_weeks(self) -> TupList:
        """
        Returns a TupList with the starting date of each week in date time format
        For example: [datetime(2021, 9, 6, 0, 0, 0), datetime(2021, 9, 13, 0, 0, 0), ...]
        """
        return TupList(
            [
                get_one_date(self._get_start_date(), weeks=i)
                for i in range(0, self._get_horizon())
            ]
        ).sorted()

    def _get_dates(self) -> TupList:
        """
        Returns a TupList with the dates of the whole horizon in datetime format
        For example: [datetime(2021, 9, 6, 0, 0, 0), datetime(2021, 9, 7, 0, 0, 0), ...]
        """
        return TupList(
            [
                get_one_date(self._get_start_date(), pos, d)
                for d in range(0, self._get_opening_days())
                for pos, value in enumerate(self.weeks)
            ]
        ).sorted()

    def _get_dates_properties(self) -> SuperDict:
        """
        Returns a SuperDict with dates as key and its dict properties as a value
        For example: {datetime(2021, 9, 6, 0, 0, 0): {"string": "2021-09-06", "week": 36}, ...}
        """
        return SuperDict(
            {
                date: {
                    "string": get_date_string_from_ts(date),
                    "week": get_week_from_ts(date),
                }
                for date in self.dates
            }
        )

    def _get_date_string_from_date(self, date):
        """Returns the date string of a given date"""
        return self.dates_properties[date]["string"]

    def _get_week_from_date(self, date):
        """Returns the week number of a given date"""
        return self.dates_properties[date]["week"]

    def _get_time_slots(self) -> TupList:
        """
        Returns a TupList with the time slots of the whole horizon in datetime format
        For example: [datetime(2021, 9, 6, 7, 0, 0), datetime(2021, 9, 6, 8, 0, 0), ...]
        """
        nb_hours = self._get_ending_hour() - self._get_starting_hour()
        nb_slots = int(self._hour_to_slot(nb_hours))

        def date_hour_ts(d, s):
            return get_one_date_time(d, self._get_minutes(s))

        return TupList(
            [date_hour_ts(date, s) for date in self.dates for s in range(nb_slots)]
        ).sorted()

    def _get_time_slots_properties(self) -> SuperDict:
        """
        Returns a SuperDict with the time slots as key and their properties dict as a value
        For example: {datetime(2021, 9, 6, 7, 0, 0): {"string": "2021-09-06T07:00",
            "date": "2021-09-06", "week": 36, "hour": 7.0}, ...}
        """
        return SuperDict(
            {
                ts: {
                    "string": get_time_slot_string(ts),
                    "date": get_date_string_from_ts(ts),
                    "week": get_week_from_ts(ts),
                    "hour": get_hour_from_date_time(ts),
                }
                for ts in self.time_slots
            }
        )

    def _get_time_slot_string(self, ts):
        """Returns the time slot string of a given time slot"""
        return self.time_slots_properties[ts]["string"]

    def _get_date_string_from_ts(self, ts):
        """Returns the date string of a given time slot"""
        return self.time_slots_properties[ts]["date"]

    def _get_week_from_ts(self, ts):
        """Returns the week number of a given time slot"""
        return self.time_slots_properties[ts]["week"]

    def _get_hour_from_ts(self, ts):
        """Returns the hour of a given time slot"""
        return self.time_slots_properties[ts]["hour"]

    def _get_property(self, key, prop) -> SuperDict:
        """Returns a SuperDict with the key of a given 'table' and the prop as value"""
        return self.data[key].get_property(prop)

    def _get_employees(self, prop) -> SuperDict:
        """Returns a SuperDict with the employee id as key and the prop as value"""
        return self._get_property("employees", prop)

    def _get_contracts(self, prop) -> SuperDict:
        """Returns a SuperDict with the contract id as key and the prop as value"""
        return self._get_property("contracts", prop)

    def _get_shifts(self, prop) -> SuperDict:
        """Returns a SuperDict with the shift id as key and the prop as value"""
        return self._get_property("shifts", prop)

    def _get_horizon(self) -> int:
        """Returns the value of the horizon parameter"""
        return self.data["parameters"]["horizon"]

    def _get_start_date(self) -> datetime:
        """Returns the datetime object of the starting date"""
        return get_date_from_string(self.data["parameters"]["starting_date"])

    def _get_end_date(self) -> datetime:
        """
        Returns the last working day based on the starting day, the horizon in weeks
        and the number of days worked
        It assumes that the working days start at the end of the week.
        For example: 5 opening days, 2 week horizon and starting date of 2021-09-06
        would result in 2021-09-17 being the end date.
        """
        days = -(7 - self._get_opening_days()) - 1
        return get_one_date(
            self._get_start_date(), weeks=self._get_horizon(), days=days
        )

    def _get_skills(self) -> TupList:
        """Returns a TupList containing the id of the skills"""
        return self.data["skills"].keys_tl()

    def get_employees_by_skill(self, id_skill) -> TupList:
        """Returns a TupList with the employees that have the given skill"""
        return self.data["skills_employees"][id_skill]

    def _get_opening_days(self) -> int:
        """Returns the number of days that have to be worked each week"""
        return self.data["parameters"]["opening_days"]

    def _get_starting_hour(self) -> float:
        """Returns the first hour of the day that has to be worked."""
        return self.data["parameters"]["starting_hour"]

    def _get_ending_hour(self) -> float:
        """Returns the last hour of the day that has to be worked."""
        return self.data["parameters"]["ending_hour"]

    def _get_min_resting_hours(self) -> int:
        """Returns the number of resting hours that the employees have to have between working days"""
        return self.data["parameters"]["min_resting_hours"]

    def _get_slot_length(self) -> float:
        """Returns the length of a time slot in minutes"""
        return self.data["parameters"]["slot_length"]

    def slot_to_hour(self, slots) -> int:
        """Converts from slots to hours"""
        return int(slots * self._get_slot_length() / 60)

    def _hour_to_slot(self, hours) -> int:
        """Converts from a hours to slots"""
        return int(hours * 60 / self._get_slot_length())

    def _get_minutes(self, s) -> float:
        """Method to get the number of minutes from the start of the day given a slot"""
        return self._get_starting_hour() * 60 + s * self._get_slot_length()

    def _get_employees_contracts(self) -> SuperDict[Tuple[int, int], int]:
        """
        Returns a SuperDict with the week and employee tuple as key
        and the contract id as value
        Contracts are supposed to start on Monday and end on Sunday
        For example: {(36, 1): 10, ...}
        """
        default_date = get_date_string_from_ts(self._get_end_date())

        contract_start = self._get_contracts("start_contract")
        contract_end = self._get_contracts("end_contract").vapply(
            lambda v: v or default_date
        )

        return SuperDict(
            {
                (self._get_week_from_date(week), e): c
                for week in self.weeks
                for c, e in self._get_contracts("id_employee").items()
                if contract_start[c]
                <= self._get_date_string_from_date(week)
                <= contract_end[c]
            }
        )

    def _get_employees_contract_hours(self) -> SuperDict[Tuple[int, int], int]:
        """
        Returns a SuperDict with the week and employee tuple as key
        and the weekly hours of the contract as the value
        Contracts are supposed to start on Monday and end on Sunday
        For example: {(36, 1): 40, ...}
        """
        contract_hours = self._get_contracts("weekly_hours")
        return self._get_employees_contracts().vapply(
            lambda c: self._hour_to_slot(contract_hours[c])
        )

    def _get_employees_contract_days(self) -> SuperDict[Tuple[int, int], int]:
        """
        Returns a SuperDict with the week and employee tuple as key and
        the max days worked weekly of the contract as the value
        Contracts are supposed to start on Monday and end on Sunday
        For example: {(36, 1): 5, ...}
        """
        contract_days = self._get_contracts("days_worked")
        return self._get_employees_contracts().vapply(lambda c: contract_days[c])

    def _get_employees_contract_shift(self) -> SuperDict[Tuple[int, int], int]:
        """
        Returns a SuperDict with the week and employee tuple as key
        and the shift id as value
        Contracts are supposed to start on Monday and end on Sunday
        For example: {(36, 1): 1, ...}
        """
        contract_shift = self._get_contracts("id_shift")
        return self._get_employees_contracts().vapply(lambda c: contract_shift[c])

    def _get_employees_contract_starting_hour(self) -> SuperDict:
        """
        Returns a SuperDict with the week and employee tuple as key
        and the shift starting hour
        Contracts are supposed to start on Monday and end on Sunday
        For example: {(36, 1): 7, ...}
        """
        start = self._get_shifts("start")
        return self._get_employees_contract_shift().vapply(lambda s: start[s])

    def _get_employees_contract_ending_hour(self) -> SuperDict[Tuple[int, int], float]:
        """
        Returns a SuperDict with the week and employee tuple as key
        and the shift ending hour
        Contracts are supposed to start on Monday and end on Sunday
        For example: {(36, 1): 21, ...}
        """
        end = self._get_shifts("end")
        return self._get_employees_contract_shift().vapply(lambda s: end[s])

    def _get_employee_time_slots_week(self) -> TupList:
        """
        Returns a TupList with the combinations of employees, weeks, dates and time slots
        in which the employees can work.
        For example: [("2021-09-06T07:00", 36, "2021-09-06", 1),
            ("2021-09-06T08:00", 36, "2021-09-06", 1), ...]
        """
        start = self._get_employees_contract_starting_hour()
        end = self._get_employees_contract_ending_hour()

        return TupList(
            (self._get_time_slot_string(ts), w, self._get_date_string_from_ts(ts), e)
            for ts in self.time_slots
            for (w, e) in start
            if self._get_week_from_ts(ts) == w
            and start[w, e] <= self._get_hour_from_ts(ts) < end[w, e]
        )

    def get_employees_time_slots_week(self) -> SuperDict:
        """
        Returns a SuperDict with the week and employee tuple as key
        and a list of time slots as value
        For example: {(36, 1): ["2021-09-06T07:00", "2021-09-06T08:00", ...], ...}
        """
        return self._get_employee_time_slots_week().take([0, 1, 3]).to_dict(0)

    def get_employees_time_slots_day(self) -> SuperDict:
        """
        Returns a SuperDict with the date and employee tuple as key
        and a list of time slots as value
        For example: {("2021-09-06", 1): ["2021-09-06T07:00", "2021-09-06T08:00", ...], ...}
        """
        return self._get_employee_time_slots_week().take([0, 2, 3]).to_dict(0)

    def get_consecutive_time_slots_employee(self) -> TupList:
        """
        Returns a TupList with a time slot, the nex time slot in the same day
        and an employee according to the employee availability
        For example: [("2021-09-06T07:00", "2021-09-06T08:00", 1), ...]
        """
        return TupList(
            [
                (ts, ts2, e)
                for (d, e), _time_slots in self.get_employees_time_slots_day().items()
                for ts, ts2 in zip(_time_slots, _time_slots[1:])
            ]
        )

    def get_opening_time_slots_set(self) -> set:
        """
        Returns a TupList with the time slots strings
        For example: ["2021-09-06T07:00", "2021-09-06T08:00", ...]
        """
        return self.time_slots.vapply(lambda v: self._get_time_slot_string(v)).to_set()

    def get_employees_ts_availability(self) -> TupList[Tuple[str, int]]:
        """
        Returns a TupList with the combinations of employees and time slots
        in which the employees can work.
        For example: [("2021-09-06T07:00", 1), ("2021-09-06T07:00", 2), ...]
        """
        return self._get_employee_time_slots_week().take([0, 3])

    def get_max_working_slots_week(self) -> SuperDict[Tuple[int, int], int]:
        """
        Returns a SuperDict with the week and employee tuple as key
        and the weekly hours of the contract as the value
        Contracts are supposed to start on Monday and end on Sunday
        For example: {(36, 1): 40, ...}
        """
        return self._get_employees_contract_hours()

    def get_max_working_slots_day(self) -> SuperDict[Tuple[str, int], float]:
        """
        Returns a SuperDict with the dates and employees tuple as a key
        and the maximum slots that can be worked on a day as value
        For example: {("2021-09-06", 1): 9, ("2021-09-06", 2): 9, ("2021-09-06", 3): 8, ...}
        """
        # TODO: set up a hard limit from the parameters of the instance, set up as optional,
        #  if there is value it should be that one, if not the current calculation

        return SuperDict(
            {
                (self._get_date_string_from_date(d), e): hours
                / self._get_employees_contract_days()[w, e]
                + 1
                for d in self.dates
                for (w, e), hours in self._get_employees_contract_hours().items()
                if self._get_week_from_date(d) == w
            }
        )

    def get_min_working_slots_day(self) -> SuperDict[Tuple[str, int], int]:
        """
        Returns a SuperDict with the dates and employees tuple as a key and
        the minimum amount of slots that need to be worked each day
        For example: {("2021-09-06", 1): 4, ("2021-09-06", 2): 4, ("2021-09-06", 3): 4, ...}
        """
        return SuperDict(
            {
                (self._get_date_string_from_date(d), e): self._hour_to_slot(
                    self.data["parameters"]["min_working_hours"]
                )
                for d in self.dates
                for e in self._get_employees("id")
            }
        )

    def get_first_time_slot_day_employee(self) -> SuperDict[Tuple[str, int], str]:
        """
        Returns a SuperDict with the date and employee tuple as a key
        and the first time slot that the employee can work in the day as the value
        For example: {("2021-09-06", 1): "2021-09-06T07:00", ("2021-09-06", 2): "2021-09-06T13:00", ...}
        """
        return (
            self._get_employee_time_slots_week()
            .take([0, 2, 3])
            .to_dict(0)
            .vapply(lambda v: min(v))
        )

    def get_max_working_days(self) -> SuperDict[Tuple[int, int], int]:
        """
        Returns a SuperDict with the week and employee tuple as key and
        the max days worked weekly of the contract as the value
        Contracts are supposed to start on Monday and end on Sunday
        For example: {(36, 1): 5, ...}
        """
        return self._get_employees_contract_days()

    def _get_incompatible_slots(self) -> TupList:
        """
        Returns a TupList with tuples that have time slots in consecutive days where
        if the employee works in one time slot it can not work
        in the other based on the minimum resting time
        For example: [("2021-09-06T20:00", "2021-09-07T07:00"),
            ("2021-09-06T21:00", "2021-09-07T07:00"), ...]
        """
        if (
            24 - (self._get_ending_hour() - self._get_starting_hour())
            >= self._get_min_resting_hours()
        ):
            return TupList()

        nb_incompatible = self._hour_to_slot(
            int(
                self._get_min_resting_hours()
                - (24 - (self._get_ending_hour() - self._get_starting_hour()))
            )
        )

        time_slots_wo_last_day = self.time_slots.vfilter(
            lambda v: self._get_date_string_from_ts(v)
            != get_date_string_from_ts(self._get_end_date())
        )

        def check_same_day(ts, ts2):
            return ts.date() == ts2.date()

        def check_one_day_apart(ts, ts2):
            return (ts2 - ts).days <= 1

        return (
            TupList(
                [
                    (val, self.time_slots[pos + i])
                    for pos, val in enumerate(time_slots_wo_last_day)
                    for i in range(1, nb_incompatible + 1)
                ]
            )
            .vfilter(lambda v: not check_same_day(v[0], v[1]))
            .vfilter(lambda v: check_one_day_apart(v[0], v[1]))
            .vapply(
                lambda v: (
                    self._get_time_slot_string(v[0]),
                    self._get_time_slot_string(v[1]),
                )
            )
        )

    def get_incompatible_slots_employee(self) -> TupList:
        """
        Returns a TupList with the incompatible time slots for each employee
        taking into account if the employee can work in both of them
        For example: [("2021-09-06T20:00", "2021-09-07T07:00", 1),
            ("2021-09-06T21:00", "2021-09-07T07:00", 1), ...]
        """
        availability = self.get_employees_ts_availability()
        return TupList(
            [
                (ts, ts2, e)
                for (ts, ts2) in self._get_incompatible_slots()
                for e in self._get_employees("name")
                if (ts, e) in availability and (ts2, e) in availability
            ]
        )

    def get_employees_managers(self) -> TupList[int]:
        """Returns the list of employees ids that are managers"""
        return self._get_employees("manager").vfilter(lambda v: v).keys_tl()

    def _filter_demand(self, ts) -> float:
        """
        Given a time slot (date time) it returns the demand, if exists, zero otherwise
        """
        return self._get_property("demand", "demand").get(
            (self._get_date_string_from_ts(ts), self._get_hour_from_ts(ts)), 0
        )

    def get_demand(self) -> SuperDict:
        """
        Returns a SuperDict indexed by the time slot (string) and the demand as value
        For example: {"2021-09-06T07:00": 10, "2021-09-06T08:00": 15, ...}
        """
        return SuperDict(
            {
                self._get_time_slot_string(ts): self._filter_demand(ts)
                for ts in self.time_slots
            }
        )

    def _filter_skills_demand(self, ts, id_skill) -> int:
        """
        Given a time slot (date time) and the id of a skill, returns the skill demand if it exists, zero otherwise
        """
        return self._get_property("skill_demand", "demand").get(
            (self._get_date_string_from_ts(ts), self._get_hour_from_ts(ts), id_skill), 0
        )

    def _employee_available(self, ts, id_employee) -> bool:
        """
        Returns a boolean indicating if the employee is available on the time slot or not
        """
        return (self._get_time_slot_string(ts), id_employee) in self.get_employees_ts_availability()

    def get_ts_demand_employees_skill(self) -> TupList:
        """
        Returns a TupList with the combinations of:
         - Time slots
         - Skill
         - Demand for the given skill on this time slot
         - Employees that master the skill and are available on the timeslot
        For example: [("2021-09-06T07:00", 1, 1, [2, 3]), ("2021-09-06T08:00", 2, 1, [1, 2]), ...]
        """
        return TupList([
            (
                self._get_time_slot_string(ts),
                id_skill,
                self._filter_skills_demand(ts, id_skill),
                self.get_employees_by_skill(id_skill).vfilter(lambda e: self._employee_available(ts, e))
            )
            for ts in self.time_slots
            for id_skill in self._get_skills()
        ])
예제 #25
0
class Instance(InstanceCore):
    schema = load_json(
        os.path.join(os.path.dirname(__file__), "../schemas/instance.json"))

    @classmethod
    def from_file(cls, path):
        data = SuperDict(suppliers=dict(sheet_name="suppliers_L1",
                                        index_col=[0, 1, 2]),
                         products=dict(sheet_name="products",
                                       index_col=[0, 1, 2]),
                         clients=dict(sheet_name="clients", index_col=[0, 1]),
                         warehouses=dict(sheet_name="warehouses",
                                         index_col=[0, 1, 2, 3]),
                         distances=dict(sheet_name="distances",
                                        index_col=[0, 1, 2]),
                         restricted_flows=dict(sheet_name="not_allowed_flows",
                                               index_col=[0, 1]))

        def read_table(**kwargs):
            return pd.read_excel(filename=path, header=0,
                                 **kwargs).index.values.tolist()

        return cls(data.vapply(lambda v: read_table(**v)))

    @classmethod
    def from_dict(cls, data: dict) -> "Instance":
        tables = ["suppliers", "clients", "warehouses", "products"]
        data_p = {el: {v["id"]: v for v in data[el]} for el in tables}
        data_p["distances"] = {(el["origin"], el["destination"]): el
                               for el in data["distances"]}
        data_p["restricted_flows"] = {(el["origin"], el["destination"]): el
                                      for el in data["restricted_flows"]}
        data_p["parameters"] = pickle.loads(
            pickle.dumps(data["parameters"], -1))
        return cls(data_p)

    def to_dict(self) -> dict:
        tables = [
            "suppliers",
            "clients",
            "warehouses",
            "products",
            "distances",
            "restricted_flows",
        ]
        data_p = {el: self.data[el].values_tl() for el in tables}
        data_p["parameters"] = self.data["parameters"]
        return pickle.loads(pickle.dumps(data_p, -1))

    def get_suppliers(self):
        return self.data["suppliers"].keys_tl()

    def get_clients(self):
        return self.data["clients"].keys_tl()

    def get_products(self):
        return self.data["products"].keys_tl()

    def get_warehouses(self):
        return self.data["warehouses"].keys_tl()

    def get_all_locations(self):
        return (self.get_suppliers() + self.get_clients() +
                self.get_warehouses())

    def get_restricted_flows(self):
        return self.data["restricted_flows"].keys_tl()

    def _get_property(self, key, prop) -> SuperDict:
        return self.data[key].get_property(prop)

    def get_unit_cost(self):
        return self._get_property("products", "unit_cost")

    def get_nb_doses(self):
        return self._get_property("products", "nb_doses")

    def get_demand(self):
        return self._get_property("clients", "demand")

    def get_capacity(self):
        return self._get_property("warehouses", "capacity")

    def get_fixed_cost(self):
        return self._get_property("warehouses", "fixed_cost")

    def get_variable_cost(self):
        return self._get_property("warehouses", "variable_cost")

    def get_distances(self):
        return self._get_property("distances", "distance")

    def get_availability(self):
        return {(k, v["id_product"]): v["availability"]
                for k, v in self.data["suppliers"].items()}

    def get_unit_flow_cost(self):
        return self._get_property("distances", "distance").vapply(
            lambda v: v * self.data["parameters"]["cost_per_km_per_dose"])

    def is_lower_level(self, iW1, iW2):
        levels = self._get_property("warehouses", "level")
        return levels[iW1] < levels[iW2]

    def is_higher_level(self, iW1, iW2):
        levels = self._get_property("warehouses", "level")
        return levels[iW1] > levels[iW2]
예제 #26
0
class Solution(SolutionCore):
    schema = load_json(
        os.path.join(os.path.dirname(__file__), "../schemas/solution.json")
    )

    def __init__(self, data):
        super(Solution, self).__init__(dict(shifts=data))

    def to_dict(self):
        info_shifts = [
            dict(
                id_shift=id_shift,
                driver=shift["driver"],
                trailer=shift["trailer"],
                departure_time=shift["departure_time"],
                initial_quantity=shift["initial_quantity"],
                nb_steps=len(shift["route"]),
            )
            for id_shift, shift in self.data["shifts"].items()
        ]

        details_shifts = [
            dict(
                id_shift=id_shift,
                day=shift["departure_time"] // (60 * 24),
                position=i,
                location=step["location"],
                quantity=step["quantity"],
                arrival=step["arrival"],
                departure=step["departure"],
                layover_before=step["layover_before"],
                driving_time_before_layover=step["driving_time_before_layover"],
                cumulated_driving_time=step["cumulated_driving_time"],
            )
            for id_shift, shift in self.data["shifts"].items()
            for i, step in enumerate(shift["route"])
        ]

        return dict(info_shifts=info_shifts, details_shifts=details_shifts)

    @classmethod
    def from_dict(cls, data_dict):

        rows = SuperDict(
            {
                (el["id_shift"], el["position"]): SuperDict(el)
                for el in data_dict["details_shifts"]
            }
        )
        details = rows.to_dictdict().vapply(
            lambda v: v.values_tl().sorted(key=lambda x: x["position"])
        )

        data = {
            shift["id_shift"]: SuperDict(shift) for shift in data_dict["info_shifts"]
        }
        for shift in data:
            data[shift]["route"] = details[shift]

        return cls(data)

    def copy(self) -> "Solution":
        return Solution(copy(self.data["shifts"]))

    def get_shifts_dict(self) -> SuperDict:
        return self.data["shifts"]

    def get_all_shifts(self) -> TupList:
        """ Returns a list with all the shifts """
        return self.data["shifts"].values_tl()

    def get_id_and_shifts(self) -> TupList:
        """ Returns a tuplist whose items are pairs composed by the id of the shift and the shift itself """
        return self.data["shifts"].items_tl()

    def get_shift_property(self, id_shift, prop):
        """ Returns property prop of the shift of given index """
        return self.data["shifts"][id_shift][prop]

    def nb_shifts(self):
        """ Returns the total number of shifts in the solution"""
        return len(self.data["shifts"])
예제 #27
0
class Solution(SolutionCore):
    schema = load_json(
        os.path.join(os.path.dirname(__file__), "../schemas/output.json"))