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)
def from_json(cls, path): with open(path, 'r') as f: data_json = json.load(f) data = pt.SuperDict({v['job']: pt.SuperDict(period=v['period'], mode=v['mode']) for v in data_json} ) return cls(data)
def from_json(cls, path): with open(path, 'r') as f: data_json = json.load(f) jobs = pt.SuperDict({v['id']: v for v in data_json['jobs']}) res = pt.SuperDict({v['id']: v for v in data_json['resources']}) needs = pt.SuperDict({(v['job'], v['mode'], v['resource']): v['need'] for v in data_json['needs']}) durations = pt.SuperDict({(v['job'], v['mode']): v['duration'] for v in data_json['durations']}) data = pt.SuperDict(jobs=jobs, resources=res, needs=needs.to_dictdict(), durations=durations.to_dictdict()) return cls(data)
def from_dict(cls, data_json: _solutionHint) -> "Solution": check_solution(data_json) data = pt.SuperDict( { v["job"]: pt.SuperDict(period=v["period"], mode=v["mode"]) for v in data_json["assignment"] } ) return cls(data)
def from_mm(cls, path, content=None): if content is None: with open(path, 'r') as f: content = f.readlines() content = pt.TupList(content) index_prec = \ content. \ index('PRECEDENCE RELATIONS:\n') index_requests = \ content.index('REQUESTS/DURATIONS:\n') index_avail = \ content.\ index('RESOURCEAVAILABILITIES:\n') # precedence. precedence = content[index_prec + 2:index_requests - 1] successors = pt.SuperDict() for line in precedence: _, job, modes, num_succ, *jobs, _ = re.split('\s+', line) successors[int(job)] = pt.TupList(jobs).vapply(int) successors = successors.kvapply(lambda k, v: dict(successors=v, id=k)) # requests/ durations requests = content[index_requests + 3:index_avail - 1] resources = re.findall(r'[RN] \d', content[index_requests + 1]) needs = pt.SuperDict() durations = pt.SuperDict() last_job = '' for line in requests: if line[2] == ' ': job = last_job _, mode, duration, *consumption, _ = re.split('\s+', line) else: _, job, mode, duration, *consumption, _ = re.split('\s+', line) last_job = job key = int(job), int(mode) needs[key] = \ {v: int(consumption[k]) for k, v in enumerate(resources)} needs[key] = pt.SuperDict(needs[key]) durations[key] = int(duration) # resources / availabilities line = content[index_avail + 2] _, *avail, _ = re.split('\s+', line) availability = {k: int(avail[i]) for i, k in enumerate(resources)} availability = pt.SuperDict(availability).kvapply( lambda k, v: dict(available=v, id=k)) data = dict(resources=availability, jobs=successors, durations=durations.to_dictdict(), needs=needs.to_dictdict()) return cls(data)
def from_dict(cls, data_json): jobs = pt.SuperDict({v['id']: v for v in data_json['jobs']}) res = pt.SuperDict({v['id']: v for v in data_json['resources']}) needs = pt.SuperDict({(v['job'], v['mode'], v['resource']): v['need'] for v in data_json['needs']}) durations = pt.SuperDict({(v['job'], v['mode']): v['duration'] for v in data_json['durations']}) data = pt.SuperDict(jobs=jobs, resources=res, needs=needs.to_dictdict(), durations=durations.to_dictdict()) return cls(data)
def to_json(self, path): res = self.data['resources'].values_l() job = self.data['jobs'].values_l() duration =self.data['durations'].to_dictup().to_tuplist().vapply(lambda v: pt.SuperDict(job=v[0], mode=v[1], duration=v[2])) needs = \ self.data['needs'].to_dictup().to_tuplist().\ vapply(lambda v: pt.SuperDict(job=v[0], mode=v[1], resource=v[2], need=v[3])) data = pt.SuperDict(jobs=job, resources=res, needs=needs, durations=duration) with open(path, 'w') as f: json.dump(data, f, indent=4, sort_keys=True)
def to_dict(self) -> dict: res = self.data["resources"].values_l() job = self.data["jobs"].values_l() duration = (self.data["durations"].to_dictup().to_tuplist().vapply( lambda v: pt.SuperDict(job=v[0], mode=v[1], duration=v[2]))) needs = (self.data["needs"].to_dictup().to_tuplist().vapply( lambda v: pt.SuperDict( job=v[0], mode=v[1], resource=v[2], need=v[3]))) data = pt.SuperDict(jobs=job, resources=res, needs=needs, durations=duration) return data
def solve(self, options): # takes into account successors jobs = copy.deepcopy(self.instance.data["jobs"]) all_jobs = set(jobs.keys()) solution = pt.SuperDict() succ = jobs.get_property("successors") durations = self.instance.data["durations"] # algorithm period = 0 job = 1 succ.pop(job) mode = 1 # we always chose the first mode solution[job] = dict(period=period, mode=mode) period = period + durations[job][mode] while len(succ): reverse = succ.list_reverse() possible = all_jobs - reverse.keys() - solution.keys() for job in possible: succ.pop(job) solution[job] = dict(period=period, mode=mode) period = period + durations[job][mode] self.solution = Solution(solution) return 2
def get_info_object(tables, day_of_week='monday', min_hour='11:00:00', max_hour='12:00:00', max_dist_km_walk = 0.5, **kwargs): trips = tables['trips'] calendar = tables['calendar'] calendar_r = calendar[calendar[day_of_week] == '1'] trips = trips.merge(calendar_r, on='service_id') trip_info = trips.\ set_index('trip_id')[['route_id', 'direction_id']].\ to_dict(orient='index') trip_info = pt.SuperDict.from_dict(trip_info) stop_times = tables['stop_times'] # TODO: fix this so we do not need to chop the end of the day stop_times_info = \ stop_times\ [stop_times.arrival_time.str.slice(stop=2) < '24'].\ merge(trips, on='trip_id') stop_times_info = \ stop_times_info[ stop_times_info.arrival_time. between(min_hour, max_hour)].\ copy() stop_times_info.arrival_time = pd.to_datetime(stop_times_info.arrival_time, format='%H:%M:%S') stop_times_info.stop_sequence = stop_times_info.stop_sequence.astype(int) stop_times_info_1 = \ stop_times_info.\ set_index(['trip_id', 'stop_sequence'])\ [['arrival_time', 'stop_id']].\ to_dict(orient='index') stop_times_info_1 = pt.SuperDict.from_dict(stop_times_info_1).to_dictdict() stop_times_2_info = \ stop_times_info. \ set_index('stop_id')[['arrival_time', 'trip_id', 'route_id', 'stop_sequence']] # we want the backup in the table. This is becuase R has problems with no-unique indeces. stop_times_2_info['stop_id_backup'] = stop_times_2_info.index route_info = tables['routes'].set_index('route_id').to_dict(orient='index') route_info = pt.SuperDict.from_dict(route_info) stops_neighbors = get_positions_table(tables['stops'], stop_times_2_info) complete_graph = \ stops_neighbors.\ merge(stops_neighbors, on=['stop_lon_g', 'stop_lat_g']) stops_neighbors = get_distance_dict(complete_graph, max_dist_km_walk) stops_info = tables['stops'].set_index('stop_id').to_dict(orient='index') stops_info = pt.SuperDict.from_dict(stops_info) return pt.SuperDict(trips=trip_info, # trip: {info} stop_times=stop_times_info_1, # (route, sequence): time and stop stop_times_2=stop_times_2_info, # indexed table routes=route_info, # route: {info} stops=stops_info, # stop: {info} stops_neigh=stops_neighbors # stop: {stop: dist} )
def format_solution(self): instance = self.model_solution dict_start = dict() for j in instance.sJobs: for s in instance.sSlots: if value(instance.v01Start[j, s]) == 1: dict_start[j] = int(s) dict_mode = var_to_dict(instance.v01JobMode) set_jobs = [i for i in instance.sJobs] mode_no_denso = dict() for a, b in dict_mode.keys(): if dict_mode[a, b] == 1: mode_no_denso.update({a: b}) if len(mode_no_denso) == 0: return {} final = pt.SuperDict() for j in set_jobs: final[j] = dict() final[j].update({ "period": int(dict_start[j]), "mode": int(mode_no_denso[j]) }) return final
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 from_dict(cls, data_json: dict) -> "Instance": check_instance(data_json) jobs = pt.SuperDict({v["id"]: v for v in data_json["jobs"]}) res = pt.SuperDict({v["id"]: v for v in data_json["resources"]}) needs = pt.SuperDict({(v["job"], v["mode"], v["resource"]): round(v["need"]) for v in data_json["needs"]}) durations = pt.SuperDict({(v["job"], v["mode"]): round(v["duration"]) for v in data_json["durations"]}) data = pt.SuperDict( jobs=jobs, resources=res, needs=needs.to_dictdict(), durations=durations.to_dictdict(), ) return cls(data)
def to_dict(self): res = self.data['resources'].values_l() job = self.data['jobs'].values_l() duration =self.data['durations'].to_dictup().to_tuplist().\ vapply(lambda v: pt.SuperDict(job=v[0], mode=v[1], duration=v[2])) needs = \ self.data['needs'].to_dictup().to_tuplist().\ vapply(lambda v: pt.SuperDict(job=v[0], mode=v[1], resource=v[2], need=v[3])) data = pt.SuperDict(jobs=job, resources=res, needs=needs, durations=duration) return data
def check_solution(self, list_tests=None, **params): func_list = dict( successors=self.check_successors, resources_nr=self.check_resources_nonrenewable, resources_r=self.check_resources_renewable, all_jobs_once=self.all_jobs_once, ) if list_tests is None: list_tests = func_list.keys() result = {k: func_list[k](**params) for k in list_tests} return pt.SuperDict({k: v for k, v in result.items() if v})
def check_successors(self, **params) -> pt.SuperDict[Tuple[int, int], int]: succ = self.instance.data["jobs"].get_property("successors") durations = self.instance.data["durations"] solution = ( self.solution.data.to_dictup() .to_tuplist() .to_dict(result_col=2, indices=[1, 0], is_list=False) .to_dictdict() ) sol_start = solution.get("period", pt.SuperDict()) sol_mode = solution.get("mode", pt.SuperDict()) sol_finished = sol_start.kvapply(lambda k, v: v + durations[k][sol_mode[k]]) errors = pt.SuperDict() for job, post_jobs in succ.items(): if job not in sol_finished: continue for job2 in post_jobs: if job2 not in sol_start: continue if sol_finished[job] > sol_start[job2]: errors[job, job2] = sol_finished[job] - sol_start[job2] return errors
def check_successors(self, **params): succ = self.instance.data['jobs'].get_property('successors') durations = self.instance.data['durations'] solution = self.solution.data.to_dictup().to_tuplist().\ to_dict(result_col=2, indices=[1, 0], is_list=False).\ to_dictdict() sol_start = solution['period'] sol_mode = solution['mode'] sol_finished = sol_start.kvapply( lambda k, v: v + durations[k][sol_mode[k]]) errors = pt.SuperDict() for job, post_jobs in succ.items(): for job2 in post_jobs: if sol_finished[job] > sol_start[job2]: errors[job, job2] = sol_finished[job] - sol_start[job2] return errors
def solve(self, options: dict): model = cp_model.CpModel() input_data = pt.SuperDict.from_dict(self.instance.data) pairs = input_data["pairs"] n1s = pt.TupList(pairs).vapply(lambda v: v["n1"]) n2s = pt.TupList(pairs).vapply(lambda v: v["n2"]) nodes = (n1s + n2s).unique2() max_colors = len(nodes) - 1 # variable declaration: color = pt.SuperDict({ node: model.NewIntVar(0, max_colors, "color_{}".format(node)) for node in nodes }) # TODO: identify maximum cliques and apply constraint on the cliques instead of on pairs for pair in pairs: model.Add(color[pair["n1"]] != color[pair["n2"]]) obj_var = model.NewIntVar(0, max_colors, "total_colors") model.AddMaxEquality(obj_var, color.values()) model.Minimize(obj_var) solver = cp_model.CpSolver() solver.parameters.max_time_in_seconds = options.get("timeLimit", 10) status = solver.Solve(model) status_conv = { cp_model.OPTIMAL: STATUS_OPTIMAL, cp_model.INFEASIBLE: STATUS_INFEASIBLE, cp_model.UNKNOWN: STATUS_UNDEFINED, cp_model.MODEL_INVALID: STATUS_UNDEFINED, } if status not in [cp_model.OPTIMAL, cp_model.FEASIBLE]: return dict(status=status_conv.get(status), status_sol=SOLUTION_STATUS_INFEASIBLE) color_sol = color.vapply(solver.Value) assign_list = color_sol.items_tl().vapply( lambda v: dict(node=v[0], color=v[1])) self.solution = Solution(dict(assignment=assign_list)) return dict(status=status_conv.get(status), status_sol=SOLUTION_STATUS_FEASIBLE)
def format_solution(self): instance = self.model_solution dict_start = var_to_dict(instance.vStart) dict_mode = var_to_dict(instance.v01Mode) set_jobs = [i for i in instance.sJobs] mode_no_denso = dict() for a, b in dict_mode.keys(): if dict_mode[a, b] == 1: mode_no_denso.update({a: b}) if len(mode_no_denso) == 0: return {} # final = dict() final = pt.SuperDict() for j in set_jobs: final[j] = dict() final[j].update({'period': int(dict_start[j]), 'mode': int(mode_no_denso[j])}) return final
def check_resources_renewable(self, **params): sol_mode = self.get_modes() usage = self.instance.data['needs'] resource_usage = sol_mode.kvapply(lambda k, v: usage[k][v]) avail = self.instance.data['resources'].get_property('available') renewable_res = self.instance.get_renewable_resources() sol_start = self.get_start_times() sol_finished = self.get_finished_times() job_periods = sol_start.sapply(func=range, other=sol_finished) makespan = self.get_objective() consumption_rt = \ pt.SuperDict({(r, t): 0 for r in renewable_res for t in range(makespan+1)}) for job, periods in job_periods.items(): for period in periods: # print(period) # print(job) for resource, value in resource_usage[job].items(): if resource in renewable_res: consumption_rt[resource, period] += value errors_R = consumption_rt.kvapply( lambda k, v: avail[k[0]] - v).vfilter(lambda v: v < 0) return errors_R
def graph_from_node(current, max_hour, max_hops=1): arcs = pt.SuperDict() max_time = pd.to_datetime(max_hour, format="%H:%M:%S") # nodes that I have not yet seen neighbors remaining = [current] iter = 0 while len(remaining) > 0 and iter < 10000: iter += 1 current = remaining.pop() if current in arcs: # if I've seen this node before: # it means I already added all arcs continue print('iter={}, current={}'.format(iter, current)) neighbors = current.get_neighbors_in_trip(max_time) if current.hops < max_hops: # if we have not yet reached the max number of trips, # we can search for a change of trip neighbors += current.get_neighbors_in_stop(max_time) for node in neighbors: arcs.set_m(current, node, value=1) remaining.append(node) return arcs
def from_dict(cls, data_json): data = pt.SuperDict({ v['job']: pt.SuperDict(period=v['period'], mode=v['mode']) for v in data_json }) return cls(data)
def solve(self, options): model = cp_model.CpModel() input_data = pt.SuperDict.from_dict(self.instance.data) max_dur_job = input_data['durations'].vapply(lambda v: max(v.values())) horizon = sum(max_dur_job) + 1 jobs_data = input_data['jobs'] durations_data = pt.SuperDict.from_dict(input_data['durations']) needs_data = pt.SuperDict.from_dict(input_data['needs']) mode_dictionary_to_values = lambda v: v.to_tuplist().sorted( key=lambda x: x[0]).take(1) # variable declaration: starts = pt.SuperDict({ job: model.NewIntVar(0, horizon, 'start_{}'.format(job)) for job in jobs_data }) ends = pt.SuperDict({ job: model.NewIntVar(0, horizon, 'end_{}'.format(job)) for job in jobs_data }) job_mode = pt.SuperDict({ job: model.NewIntVar(0, len(modes) - 1, 'mode_{}'.format(job)) for job, modes in durations_data.items() }) job_duration = pt.SuperDict({ job: model.NewIntVar(min(modes.values()), max(modes.values()), 'duration_{}'.format(job)) for job, modes in durations_data.items() }) interval = pt.SuperDict({ job: model.NewIntervalVar(starts[job], job_duration[job], ends[job], 'interval_{}'.format(job)) for job in jobs_data }) mode_duration_perjob = durations_data.vapply(mode_dictionary_to_values) # definition of job duration [ model.AddElement(job_mode[job], mode_duration_perjob[job], job_duration[job]) for job in jobs_data ] # for each job and resource: # an array of consumptions (one per mode in order) needs_data_perjob = \ needs_data. \ to_dictup(). \ to_tuplist(). \ take([0, 2, 1, 3]). \ to_dict([2, 3]). \ vapply(sorted). \ vapply(pt.TupList).vapply(lambda v: v.take(1)) # for each job and resource: # a variable with the consumption job_consumption = needs_data_perjob.kvapply( lambda k, v: model.NewIntVar(min(v), max(v), 'consumption_{}_{}'. format(*k))) # definition of job consumption for (job, res), needs in needs_data_perjob.items(): model.AddElement(job_mode[job], needs, job_consumption[job, res]) # succession needs to be guaranteed for job, job_data in input_data['jobs'].items(): for successor in job_data['successors']: model.Add(starts[successor] >= ends[job]) # resource usage job_consumption_per_res = job_consumption.to_tuplist().take( [1, 0, 2]).to_dict(2, is_list=False).to_dictdict() for resource, res_data in input_data['resources'].items(): # we get jobs that consume that resource and how much jobs, consumptions = zip( *job_consumption_per_res[resource].items_tl()) relevant_intervals = [interval[j] for j in jobs] if resource in self.instance.get_renewable_resources(): # renewable resources we use intervals to check them model.AddCumulative(intervals=relevant_intervals, demands=consumptions, capacity=res_data['available']) else: # non renewable resources we sum all model.Add(sum(consumptions) <= res_data['available']) # we set the objective as the makespan obj_var = model.NewIntVar(0, horizon, 'makespan') model.AddMaxEquality(obj_var, ends.values()) model.Minimize(obj_var) solver = cp_model.CpSolver() solver.parameters.max_time_in_seconds = options.get('timeLimit', 10) status = solver.Solve(model) if status not in [cp_model.OPTIMAL, cp_model.FEASIBLE]: return status start_sol = starts.vapply(solver.Value) mode_sol = job_mode.vapply(lambda v: solver.Value(v) + 1) _func = lambda x, y: dict(period=x, mode=y) solution = start_sol.sapply(func=_func, other=mode_sol) self.solution = Solution(solution) return status
# one and only one start per task for j in tasks: model += pl.lpSum(X[j, k] for k in K_j[j]) == 1 # only one task is active at any moment: for k2 in periods: model += pl.lpSum(X[j, k] for j, k in JK_k2[k2]) == 1 # solve model: solver = pl.PULP_CBC_CMD(msg=True) # solver = pl.CPLEX_CMD(msg=True) model.solve(solver) # get tasks starts starts_out = pt.SuperDict(X).vapply(pl.value).clean().keys_l() solution = [-1 for p in periods] for j, k in starts_out: for p2 in K2_jk[j, k]: solution[p2] = j print("solution is \n{}".format(solution)) # verify that all periods are filled: # counting if there is any with a minus -1 pt.TupList(solution).vfilter(lambda v: v == -1) # verify that each task t appears exactly duration[t] times task_count = pt.SuperDict().fill_with_default(keys=duration.keys()) for period, task in enumerate(solution):
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))
def get_assignments(self): return pt.SuperDict({v["node"]: v["color"] for v in self.data["assignment"]})
def __init__(self, instance: Instance, solution: Solution): super().__init__(instance, solution) if solution is None: solution = Solution(pt.SuperDict()) self.solution = solution return
def all_jobs_once(self, **params) -> pt.SuperDict[int, int]: missing = self.instance.data["jobs"].keys() - self.solution.data.keys() return pt.SuperDict({k: 1 for k in missing})
def all_jobs_once(self, **params): missing = self.instance.data['jobs'].keys() - self.solution.data.keys() return pt.SuperDict({k: 1 for k in missing})