Reindeer Pairing

https://img.shields.io/badge/AIMMS_4.89-ZIP:_Reindeer_Pairing-blue https://img.shields.io/badge/AIMMS_4.89-Github:_Reindeer_Pairing-blue https://img.shields.io/badge/AIMMS_Community-Forum-yellow ../../_images/project3.gif

Story

This AIMMS project is an illustration of the stable marriage problem (STEM). Every Christmas Santa is as always busy with preparations before his longer-than-7.5-million-kilometer trip around the world. One of the many things he has to worry about is how to pair up his reindeer in front of the sleigh. We all know that Rudolf goes right in front of everyone else because of his shiny nose, but what about his other eight four-legged friends? The traditional Christmas carols tell us that the reindeer are typically arranged in four pairs, front to back, as follows:

  1. Dasher, Dancer

  2. Prancer, Vixen

  3. Comet, Cupid

  4. Donner, Blitzen

Therefore, we are going to assume that this is an arrangement that works pretty well (after all it’s been working since 1823). However Santa kept thinking: “Are there other good ways to pair up my reindeers?”. Santa was kind enough to provide the following lists of pairing preferences for each of his reindeer. The names in each list are sorted in decreasing order of pairing preference. The lefties appear in bold, while the righties appear in italic.

  1. Dasher: Dancer, Cupid, Vixen, Blitzen

  2. Prancer: Vixen, Blitzen, Dancer, Cupid

  3. Comet: Cupid, Dancer, Blitzen, Vixen

  4. Donner: Blitzen, Vixen, Dancer, Cupid

  5. Dancer: Prancer, Comet, Dasher, Donner

  6. Vixen: Dasher, Donner, Prancer, Comet

  7. Cupid: Prancer, Dasher, Comet, Donner

  8. Blitzen: Comet, Prancer, Donner, Dasher

What Santa would like to know is whether or not there are other good pairings in addition to the traditional one. If so, he can add some variety to his line-up and the reindeer won’t get so bored by galloping side-by-side with the same companion every year.

Note

The problem formulation is originally stated elegantly at Tallys Yunes’ blog. And more information about the reindeers personalities here!

Mathematical Model

This AIMMS project illustrates the use of a Contraint Programing (CP) model.

Reindeer Pairing Problem

Sets and indices:

\(R\), \(r \in R\)

Rightys Reindeers

\(L\), \(l \in L\)

Leftys Reindeers

\(D\), \(d \in D\)

Reindeers

Parameters:

\(Pl_{l,r} \in \mathbb{I}\)

Leftys preferences

\(Pr_{r, l} \in \mathbb{I}\)

Rightys preferences

Variables:

\(rP_{l} \in R\)

Right partner

\(lP_{r} \in L\)

Left partner

Contraints:

  1. Match Each Uniquely:

1cp::Channel(
2   mapBinding        :  i_left,
3   map               :  ev_rightPartner(i_left),
4   inverseMapBinding :  i_right,
5   inverseMap        :  ev_leftPartner(i_right))
  1. Left Stable (i_left,i_right):

1if p_def_preferenceRankLefty( i_left, i_right ) < p_def_preferenceRankLefty( i_left, ev_rightPartner( i_left ) ) then
2   p_def_preferenceRankRighty( i_right, ev_leftPartner( i_right ) ) < p_def_preferenceRankRighty( i_right, i_left )
3endif;
  1. Right Stable (i_left,i_right):

1if p_def_preferenceRankRighty( i_right, i_left ) < p_def_preferenceRankRighty( i_right, ev_leftPartner( i_right ) ) then
2   p_def_preferenceRankLefty( i_left, ev_rightPartner( i_left ) ) < p_def_preferenceRankLefty( i_left, i_right )
3endif;

Remarks:

  • i_left as l;

  • i_right as r;

  • p_def_preferenceRankRighty as Pr;

  • p_def_preferenceRankLefty as Pl;

  • ev_rightPartner as rP;

  • ev_leftPartner as lP;

Language

DirectSQL

This example illustrates how to use DirectSQL to export data. Read more about how to generage a DirectSQL procedure. Access this feature per “All Solutions” table.

../../_images/directSQL.png

Multiple Solutions

To ensure the solver will return multiple solutions, the option solution_storage_limit was set to 1000.

 1option 'cpoptimizer 22.1'.'solution_storage_limit' := 1000 ;
 2solve mp_stableReindeerPairings where solution_limit := 1000, time_limit := 10 ;
 3
 4! Visit each solution in the solution repository of that generated mathematical program
 5! and store these solutions in element parameters.
 6! These element parameters can then be displayed in the GUI.
 7ep_loc_generatedModel := 'mp_stableReindeerPairings';
 8s_solutionSet := gmp::Solution::GetSolutionsSet(ep_loc_generatedModel);
 9
10for (i_sols) do
11   GMP::Solution::SendToModel(ep_loc_generatedModel, i_sols);
12   ep_variousLeftPartners(i_sols,i_right)  := ev_leftPartner(i_right);
13   ep_variousRightPartners(i_sols,i_left) := ev_rightPartner(i_left);
14endfor;

WebUI Features

The following WebUI features are used:

Page Layout

Even though Page Layout can be a little more restrictive, it is possible to create complex structures such as:

../../_images/compiled_layout.png

To develop this layout, first was done a draft plan, translated to this image:

../../_images/areas.png

Then when coding the layout, it was easier to define its structure by code,

1"gridTemplateColumns": "2fr 1fr 1fr 1fr 1fr 1fr",
2"gridTemplateRows": "5fr 2fr 2.2fr 2fr 2fr 2.2fr",
3"gridTemplateAreas": "\"area-l area-a area-a area-a area-a area-a\" \"area-y area-y area-y area-y area-y area-y\" \"area-b area-c area-e area-g area-i area-z\" \"area-b area-c area-e area-g area-i area-k\" \"area-b area-d area-f area-h area-j area-k\" \"area-b area-d area-f area-h area-j area-x\""

Resulting to our beautiful Reindeer Pairing page!

../../_images/end_page.png

UI Styling

Below there are the css files you will find with comments on what they change.

 1:root {
 2   --primaryLight: #FDFCEF;
 3   --primary: #C7EDE6;
 4   --primaryDark: #1DC1A3;
 5   --primaryDarker: #127260;
 6   --secondary: #EE3E54;
 7   --secondary2: #e9818d;
 8
 9
10   --bg_app-logo: 15px 50% / 50px 50px no-repeat url(/app-resources/resources/images/santa_logo.png);
11   --spacing_app-logo_width: 65px;
12
13   --color_border-divider_themed: var(--primaryDark);
14   --color_text_edit-select-link: var(--primaryDarker);
15   --color_text_edit-select-link_hover: var(--primary);
16   --color_bg_edit-select-link_inverted: var(--secondary);
17
18   --color_bg_button_primary: var(--primaryDark);
19   --color_text_button_primary: white;
20   --border_button_primary: 1px solid var(--primaryDark);
21
22   --color_bg_button_primary_hover: var(--primary);
23   --color_text_button_primary_hover: var(--primaryDarker);
24   --border_button_primary_hover: 1px solid var(--primaryDarker);
25
26   --color_text_button_secondary: var(--secondary);
27   --border_button_secondary: 1px solid var(--secondary);
28   --color_text_button_secondary_hover: var(--primaryDarker);
29   --border_button_secondary_hover: 1px solid var(--primaryDarker);
30
31   --color_bg_widget-header: var(--primaryDarker);
32   --border_widget-header: 3px solid var(--primaryDark);
33}
1.tag-table.focused .cell.focus-cell {
2   box-shadow: inset 0 0 0 2px var(--primaryDark);
3}
 1/*Add logo on the background*/
 2.scroll-wrapper--pagev2 .page-container {
 3   content: " ";
 4   background: url(img/RightBackground.png) rgb(249, 249, 249) no-repeat left/contain;
 5}
 6
 7/*Changing tittle to be uppercase*/
 8.title-addon {
 9   text-transform: uppercase;
10   text-shadow: 2px 2px 0px var(--primaryDark);
11   color: whitesmoke;
12}
13
14/*Changing button font*/
15.ui-widget, .ui-widget button, .ui-widget input, .ui-widget select, .ui-widget textarea {
16   font-family: var(--font_headers),Montserrat,Roboto,Arial,Helvetica,sans-serif;
17}
 1/*Link color*/
 2.ql-snow a {
 3   color: var(--primaryDarker);
 4}
 5
 6/*Change checkbox color*/
 7input.boolean-cell-editor-contents {
 8   accent-color: var(--primaryDarker);
 9}
10
11.aimms-widget .ui-button {
12   text-transform: uppercase;
13}
14
15/*Changing tittle to be uppercase*/
16.title-addon,
17.ui-dialog .ui-dialog-title,
18.page-container__dialog-header .title{
19   text-transform: uppercase;
20   text-shadow: 1px 1px 0px var(--primaryDarker);
21   color: whitesmoke;
22}
23
24.tag-table .grid-viewport .cell:not(.flag-readOnly), html:not(.using-touch) .tag-table .grid-viewport .cell:not(.flag-readOnly) {
25   color: var(--primaryDarker);
26}
27
28.widget-menu-container .widget-menu-items-wrapper .widget-menu-item .title {
29   color: var(--primaryDarker);
30}
 1/*Change color of the active step*/
 2.workflow-panel .step-item.current,
 3.workflow-panel.collapse .step-item.current {
 4   box-shadow: inset 0.3125rem 0 0 var(--primaryDark);
 5}
 6
 7/*Change color of the titles*/
 8.workflow-panel .step-item.active.complete .title,
 9.workflow-panel .step-item.active.incomplete .title {
10   color: var(--primaryDarker);
11}
12
13/*Change color of the icons*/
14.workflow-panel .step-item.active.complete .icon,
15.workflow-panel .step-item.active.incomplete .icon {
16   color: var(--primaryDarker);
17   border: 1px solid var(--primaryDarker);
18}
1.widgetdiv .awf-dock-button .chrome-button.active, .widgetdiv .awf-dock-button .chrome-button.open {
2   background-color: var(--primaryDark);
3   color: #fff;
4}
5
6.widget-menu-container .widget-menu-items-wrapper .widget-menu-item:hover {
7   background: var(--primaryDark);
8}

Minimal Requirements

AIMMS Community license is sufficient for working with this example.