Skip to content

Commit 85687c6

Browse files
committed
add a bonus for adjecency of driver to spotter roles
1 parent ec8e00e commit 85687c6

1 file changed

Lines changed: 83 additions & 0 deletions

File tree

src/jres_standard_solver.cpp

Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -226,6 +226,51 @@ jres::internal::SolverOutput JresStandardSolver::solve()
226226
}
227227
}
228228
}
229+
230+
// --- Soft Constraint: Adjacency of spotter/driver duties ---
231+
for (const auto& p : m_input.teamMembers) {
232+
if (p.isDriver && p.isSpotter) {
233+
for (size_t s = 0; s < m_input.stints.size(); ++s) {
234+
if (m_spotterWorkVars.count({p.name, s}) == 0) continue;
235+
236+
auto spotterVar = m_spotterWorkVars.at({p.name, s});
237+
238+
// Adjacency with drive stint BEFORE
239+
if (s > 0 && m_driverWorkVars.count({p.name, s - 1})) {
240+
auto driverBeforeVar = m_driverWorkVars.at({p.name, s - 1});
241+
242+
int adjBeforeVar = m_highs->getNumCol();
243+
m_highs->addVar(0.0, 1.0);
244+
m_highs->changeColIntegrality(adjBeforeVar, HighsVarType::kInteger);
245+
m_highs->changeColCost(adjBeforeVar, -0.5); // Reward for adjacency
246+
247+
// z <= x (spotter)
248+
m_highs->addRow(-kHighsInf, 0.0, 2, std::vector<int>{adjBeforeVar, spotterVar}.data(), std::vector<double>{1.0, -1.0}.data());
249+
// z <= y (driver before)
250+
m_highs->addRow(-kHighsInf, 0.0, 2, std::vector<int>{adjBeforeVar, driverBeforeVar}.data(), std::vector<double>{1.0, -1.0}.data());
251+
// z >= x + y - 1 => z - x - y >= -1
252+
m_highs->addRow(-1.0, kHighsInf, 3, std::vector<int>{adjBeforeVar, spotterVar, driverBeforeVar}.data(), std::vector<double>{1.0, -1.0, -1.0}.data());
253+
}
254+
255+
// Adjacency with drive stint AFTER
256+
if (s < m_input.stints.size() - 1 && m_driverWorkVars.count({p.name, s + 1})) {
257+
auto driverAfterVar = m_driverWorkVars.at({p.name, s + 1});
258+
259+
int adjAfterVar = m_highs->getNumCol();
260+
m_highs->addVar(0.0, 1.0);
261+
m_highs->changeColIntegrality(adjAfterVar, HighsVarType::kInteger);
262+
m_highs->changeColCost(adjAfterVar, -0.5); // Reward for adjacency
263+
264+
// z <= x (spotter)
265+
m_highs->addRow(-kHighsInf, 0.0, 2, std::vector<int>{adjAfterVar, spotterVar}.data(), std::vector<double>{1.0, -1.0}.data());
266+
// z <= y (driver after)
267+
m_highs->addRow(-kHighsInf, 0.0, 2, std::vector<int>{adjAfterVar, driverAfterVar}.data(), std::vector<double>{1.0, -1.0}.data());
268+
// z >= x + y - 1 => z - x - y >= -1
269+
m_highs->addRow(-1.0, kHighsInf, 3, std::vector<int>{adjAfterVar, spotterVar, driverAfterVar}.data(), std::vector<double>{1.0, -1.0, -1.0}.data());
270+
}
271+
}
272+
}
273+
}
229274
}
230275

231276
auto endSetup = high_resolution_clock::now();
@@ -275,6 +320,44 @@ jres::internal::SolverOutput JresStandardSolver::solve()
275320
}
276321
spotterSolver.setOptionValue("mip_rel_gap", m_options.optimalityGap);
277322
add_participant_model(spotterSolver, m_spotterPool, m_spotterWorkVars);
323+
324+
// --- Soft Constraint: Adjacency of spotter/driver duties ---
325+
for (const auto& p : m_spotterPool) {
326+
const auto member_it = std::find_if(m_input.teamMembers.begin(), m_input.teamMembers.end(),
327+
[&](const jres::internal::TeamMember& tm){ return tm.name == p.name; });
328+
if (member_it == m_input.teamMembers.end() || !member_it->isDriver) continue;
329+
330+
for (size_t s = 0; s < m_input.stints.size(); ++s) {
331+
if (m_spotterWorkVars.count({p.name, s})) {
332+
double cost = 0.0;
333+
334+
auto stintStartTime = jres::internal::TimeHelpers::stringToTimePoint(m_input.stints[s].startTime);
335+
std::string availabilityKey = jres::internal::TimeHelpers::timePointToKey(stintStartTime);
336+
auto member_availability_it = m_input.availability.find(p.name);
337+
if (member_availability_it != m_input.availability.end()) {
338+
auto time_availability_it = member_availability_it->second.find(availabilityKey);
339+
if (time_availability_it != member_availability_it->second.end()) {
340+
if (time_availability_it->second == jres::internal::Availability::Preferred) {
341+
cost = -1.0;
342+
}
343+
}
344+
}
345+
346+
// Adjacency reward
347+
if (s > 0 && output.schedule[s - 1].driver == p.name) {
348+
cost -= 0.5;
349+
}
350+
if (s < m_input.stints.size() - 1 && output.schedule[s + 1].driver == p.name) {
351+
cost -= 0.5;
352+
}
353+
354+
if (cost != 0.0) {
355+
spotterSolver.changeColCost(m_spotterWorkVars.at({p.name, s}), cost);
356+
}
357+
}
358+
}
359+
}
360+
278361
for (size_t s = 0; s < m_input.stints.size(); ++s) {
279362
std::vector<int> indices;
280363
std::vector<double> values;

0 commit comments

Comments
 (0)