Пример #1
0
/**
 * Shift all dates (join dates and edge annotations) of link graphs and link
 * graph jobs by the number of days given.
 * @param interval Number of days to be added or subtracted.
 */
void LinkGraphSchedule::ShiftDates(int interval)
{
	LinkGraph *lg;
	FOR_ALL_LINK_GRAPHS(lg) lg->ShiftDates(interval);
	LinkGraphJob *lgj;
	FOR_ALL_LINK_GRAPH_JOBS(lgj) lgj->ShiftJoinDate(interval);
}
Пример #2
0
/**
 * Create the DemandCalculator and immediately do the calculation.
 * @param job Job to calculate the demands for.
 */
DemandCalculator::DemandCalculator(LinkGraphJob &job) :
	max_distance(DistanceMaxPlusManhattan(TileXY(0,0), TileXY(MapMaxX(), MapMaxY())))
{
	const LinkGraphSettings &settings = job.Settings();
	CargoID cargo = job.Cargo();

	this->accuracy = settings.accuracy;
	this->mod_dist = settings.demand_distance;
	if (this->mod_dist > 100) {
		/* Increase effect of mod_dist > 100 */
		int over100 = this->mod_dist - 100;
		this->mod_dist = 100 + over100 * over100;
	}

	switch (settings.GetDistributionType(cargo)) {
		case DT_SYMMETRIC:
			this->CalcDemand<SymmetricScaler>(job, SymmetricScaler(settings.demand_size));
			break;
		case DT_ASYMMETRIC:
			this->CalcDemand<AsymmetricScaler>(job, AsymmetricScaler());
			break;
		default:
			/* Nothing to do. */
			break;
	}
}
Пример #3
0
/**
 * Join the next finished job, if available.
 */
void LinkGraphSchedule::JoinNext()
{
	if (this->running.empty()) return;
	LinkGraphJob *next = this->running.front();
	if (!next->IsFinished()) return;
	this->running.pop_front();
	LinkGraphID id = next->LinkGraphIndex();
	delete next; // implicitly joins the thread
	if (LinkGraph::IsValidID(id)) {
		LinkGraph *lg = LinkGraph::Get(id);
		this->Unqueue(lg); // Unqueue to avoid double-queueing recycled IDs.
		this->Queue(lg);
	}
}
Пример #4
0
/**
 * Load all link graph jobs.
 */
static void Load_LGRJ()
{
	int index;
	while ((index = SlIterateArray()) != -1) {
		if (!LinkGraphJob::CanAllocateItem()) {
			/* Impossible as they have been present in previous game. */
			NOT_REACHED();
		}
		LinkGraphJob *lgj = new (index) LinkGraphJob();
		SlObject(lgj, GetLinkGraphJobDesc());
		LinkGraph &lg = const_cast<LinkGraph &>(lgj->Graph());
		SlObject(&lg, GetLinkGraphDesc());
		lg.Init(_num_nodes);
		SaveLoad_LinkGraph(lg);
	}
}
Пример #5
0
/**
 * Map the paths generated by the MCF solver into flows associated with nodes.
 * @param component the link graph component to be used.
 */
void FlowMapper::Run(LinkGraphJob &job) const
{
	for (NodeID node_id = 0; node_id < job.Size(); ++node_id) {
		Node prev_node = job[node_id];
		StationID prev = prev_node.Station();
		PathList &paths = prev_node.Paths();
		for (PathList::iterator i = paths.begin(); i != paths.end(); ++i) {
			Path *path = *i;
			uint flow = path->GetFlow();
			if (flow == 0) break;
			Node node = job[path->GetNode()];
			StationID via = node.Station();
			StationID origin = job[path->GetOrigin()].Station();
			assert(prev != via && via != origin);
			/* Mark all of the flow for local consumption at "first". */
			node.Flows().AddFlow(origin, via, flow);
			if (prev != origin) {
				/* Pass some of the flow marked for local consumption at "prev" on
				 * to this node. */
				prev_node.Flows().PassOnFlow(origin, via, flow);
			} else {
				/* Prev node is origin. Simply add flow. */
				prev_node.Flows().AddFlow(origin, via, flow);
			}
		}
	}

	for (NodeID node_id = 0; node_id < job.Size(); ++node_id) {
		/* Remove local consumption shares marked as invalid. */
		Node node = job[node_id];
		FlowStatMap &flows = node.Flows();
		flows.FinalizeLocalConsumption(node.Station());
		if (this->scale) {
			/* Scale by time the graph has been running without being compressed. */
			uint runtime = job.JoinDate() - job.Settings().recalc_time - job.LastCompression();
			for (FlowStatMap::iterator i = flows.begin(); i != flows.end(); ++i) {
				i->second.ScaleToMonthly(runtime);
			}
		}
		/* Clear paths. */
		PathList &paths = node.Paths();
		for (PathList::iterator i = paths.begin(); i != paths.end(); ++i) {
			delete *i;
		}
		paths.clear();
	}
}
Пример #6
0
/**
 * Spawn the threads for running link graph calculations.
 * Has to be done after loading as the cargo classes might have changed.
 */
void AfterLoadLinkGraphs()
{
	if (IsSavegameVersionBefore(191)) {
		LinkGraph *lg;
		FOR_ALL_LINK_GRAPHS(lg) {
			for (NodeID node_id = 0; node_id < lg->Size(); ++node_id) {
				(*lg)[node_id].UpdateLocation(Station::Get((*lg)[node_id].Station())->xy);
			}
		}

		LinkGraphJob *lgj;
		FOR_ALL_LINK_GRAPH_JOBS(lgj) {
			lg = &(const_cast<LinkGraph &>(lgj->Graph()));
			for (NodeID node_id = 0; node_id < lg->Size(); ++node_id) {
				(*lg)[node_id].UpdateLocation(Station::Get((*lg)[node_id].Station())->xy);
			}
		}
	}
Пример #7
0
	/**
	 * Constructor.
	 * @param job Link graph job to work with.
	 */
	FlowEdgeIterator(LinkGraphJob &job) : job(job)
	{
		for (NodeID i = 0; i < job.Size(); ++i) {
			StationID st = job[i].Station();
			if (st >= this->station_to_node.size()) {
				this->station_to_node.resize(st + 1);
			}
			this->station_to_node[st] = i;
		}
	}
Пример #8
0
/**
 * Start the next job in the schedule.
 */
void LinkGraphSchedule::SpawnNext()
{
	if (this->schedule.empty()) return;
	LinkGraph *next = this->schedule.front();
	LinkGraph *first = next;
	while (next->Size() < 2) {
		this->schedule.splice(this->schedule.end(), this->schedule, this->schedule.begin());
		next = this->schedule.front();
		if (next == first) return;
	}
	assert(next == LinkGraph::Get(next->index));
	this->schedule.pop_front();
	if (LinkGraphJob::CanAllocateItem()) {
		LinkGraphJob *job = new LinkGraphJob(*next);
		job->SpawnThread();
		this->running.push_back(job);
	} else {
		NOT_REACHED();
	}
}
Пример #9
0
/**
 * Run the second pass of the MCF calculation which assigns all remaining
 * demands to existing paths.
 * @param job Link graph job to calculate.
 */
MCF2ndPass::MCF2ndPass(LinkGraphJob &job) : MultiCommodityFlow(job)
{
	this->max_saturation = UINT_MAX; // disable artificial cap on saturation
	PathVector paths;
	uint size = job.Size();
	uint accuracy = job.Settings().accuracy;
	bool demand_left = true;
	while (demand_left) {
		demand_left = false;
		for (NodeID source = 0; source < size; ++source) {
			this->Dijkstra<CapacityAnnotation, FlowEdgeIterator>(source, paths);
			for (NodeID dest = 0; dest < size; ++dest) {
				Edge edge = this->job[source][dest];
				Path *path = paths[dest];
				if (edge.UnsatisfiedDemand() > 0 && path->GetFreeCapacity() > INT_MIN) {
					this->PushFlow(edge, path, accuracy, UINT_MAX);
					if (edge.UnsatisfiedDemand() > 0) demand_left = true;
				}
			}
			this->CleanupPaths(source, paths);
		}
	}
}
Пример #10
0
/**
 * Run the first pass of the MCF calculation.
 * @param job Link graph job to calculate.
 */
MCF1stPass::MCF1stPass(LinkGraphJob &job) : MultiCommodityFlow(job)
{
	PathVector paths;
	uint size = job.Size();
	uint accuracy = job.Settings().accuracy;
	bool more_loops;

	do {
		more_loops = false;
		for (NodeID source = 0; source < size; ++source) {
			/* First saturate the shortest paths. */
			this->Dijkstra<DistanceAnnotation, GraphEdgeIterator>(source, paths);

			for (NodeID dest = 0; dest < size; ++dest) {
				Edge edge = job[source][dest];
				if (edge.UnsatisfiedDemand() > 0) {
					Path *path = paths[dest];
					assert(path != NULL);
					/* Generally only allow paths that don't exceed the
					 * available capacity. But if no demand has been assigned
					 * yet, make an exception and allow any valid path *once*. */
					if (path->GetFreeCapacity() > 0 && this->PushFlow(edge, path,
							accuracy, this->max_saturation) > 0) {
						/* If a path has been found there is a chance we can
						 * find more. */
						more_loops = more_loops || (edge.UnsatisfiedDemand() > 0);
					} else if (edge.UnsatisfiedDemand() == edge.Demand() &&
							path->GetFreeCapacity() > INT_MIN) {
						this->PushFlow(edge, path, accuracy, UINT_MAX);
					}
				}
			}
			this->CleanupPaths(source, paths);
		}
	} while (more_loops || this->EliminateCycles());
}
Пример #11
0
	/**
	 * Constructor.
	 * @param job Link graph job to work with.
	 */
	FlowEdgeIterator(LinkGraphJob &job) : job(job)
	{
		for (NodeID i = 0; i < job.Size(); ++i) {
			this->station_to_node[job[i].Station()] = i;
		}
	}
Пример #12
0
void DemandCalculator::CalcDemand(LinkGraphJob &job, Tscaler scaler)
{
	NodeList supplies;
	NodeList demands;
	uint num_supplies = 0;
	uint num_demands = 0;

	for (NodeID node = 0; node < job.Size(); node++) {
		scaler.AddNode(job[node]);
		if (job[node].Supply() > 0) {
			supplies.push_back(node);
			num_supplies++;
		}
		if (job[node].Demand() > 0) {
			demands.push_back(node);
			num_demands++;
		}
	}

	if (num_supplies == 0 || num_demands == 0) return;

	/* Mean acceptance attributed to each node. If the distribution is
	 * symmetric this is relative to remote supply, otherwise it is
	 * relative to remote demand. */
	scaler.SetDemandPerNode(num_demands);
	uint chance = 0;

	while (!supplies.empty() && !demands.empty()) {
		NodeID from_id = supplies.front();
		supplies.pop_front();

		for (uint i = 0; i < num_demands; ++i) {
			assert(!demands.empty());
			NodeID to_id = demands.front();
			demands.pop_front();
			if (from_id == to_id) {
				/* Only one node with supply and demand left */
				if (demands.empty() && supplies.empty()) return;

				demands.push_back(to_id);
				continue;
			}

			int32 supply = scaler.EffectiveSupply(job[from_id], job[to_id]);
			assert(supply > 0);

			/* Scale the distance by mod_dist around max_distance */
			int32 distance = this->max_distance - (this->max_distance -
					(int32)job[from_id][to_id].Distance()) * this->mod_dist / 100;

			/* Scale the accuracy by distance around accuracy / 2 */
			int32 divisor = this->accuracy * (this->mod_dist - 50) / 100 +
					this->accuracy * distance / this->max_distance + 1;

			assert(divisor > 0);

			uint demand_forw = 0;
			if (divisor <= supply) {
				/* At first only distribute demand if
				 * effective supply / accuracy divisor >= 1
				 * Others are too small or too far away to be considered. */
				demand_forw = supply / divisor;
			} else if (++chance > this->accuracy * num_demands * num_supplies) {
				/* After some trying, if there is still supply left, distribute
				 * demand also to other nodes. */
				demand_forw = 1;
			}

			demand_forw = min(demand_forw, job[from_id].UndeliveredSupply());

			scaler.SetDemands(job, from_id, to_id, demand_forw);

			if (scaler.HasDemandLeft(job[to_id])) {
				demands.push_back(to_id);
			} else {
				num_demands--;
			}

			if (job[from_id].UndeliveredSupply() == 0) break;
		}

		if (job[from_id].UndeliveredSupply() != 0) {
			supplies.push_back(from_id);
		} else {
			num_supplies--;
		}
	}
}