kostis-init/CP-Bench huggingface dataset:
Problem description:
The rectangular packing problem involves placing a set of rectangular items into a larger rectangular area such that no items overlap and all items are within the boundaries of the larger area. The objective is to minimize the total area of the larger rectangle required to pack all the items.  Given the widths and heights of the items, determine their positions within the larger rectangle while ensuring no overlap and minimizing the total area.  Print the positions of the items (pos_x, pos_y) and the dimensions of the larger rectangle (total_x, total_y).

Input data:
widths = [3, 4, 2, 1]  # Widths of the items heights = [2, 3, 1, 4]  # Heights of the items

Decision variables: total_y, pos_x, pos_y, total_x

CPMpy model:
# Data
widths = [3, 4, 2, 1]  # Widths of the items
heights = [2, 3, 1, 4]  # Heights of the items
# End of data

# Import libraries
from cpmpy import *
import json
from cpmpy.expressions.utils import all_pairs


def model_packing_rectangular(widths, heights):
    # Number of different items
    n = len(widths)

    # max dimensions of the whole area
    area_min_x, area_max_x = max(widths), sum(widths)
    area_min_y, area_max_y = max(heights), sum(heights)

    # Decision variables
    pos_x = intvar(0, area_max_x, shape=n)
    pos_y = intvar(0, area_max_y, shape=n)

    total_x = intvar(area_min_x, area_max_x)
    total_y = intvar(area_min_y, area_max_y)

    m = Model()

    ## Necessary constraints
    # Every item has to be within the overall area
    m += [pos_x + widths <= total_x,
          pos_y + heights <= total_y]

    # No-overlap: every item has to be fully above, below or next to every other item
    for i, j in all_pairs(range(n)):
        m += ((pos_x[i] + widths[i] <= pos_x[j]) |
              (pos_x[j] + widths[j] <= pos_x[i]) |
              (pos_y[i] + heights[i] <= pos_y[j]) |
              (pos_y[j] + heights[j] <= pos_y[i]))

    # Minimize wrt the overall area
    m.minimize(total_x * total_y)

    ## Optional constraints
    # The needed space needs to be wider than taller
    # m += (total_x > total_y),

    # The needed space has to have a width larger than 10
    # m += (total_x >= 10),

    # The needed space has to have a height larger than 10
    # m += (total_y >= 10)

    return m, (pos_x, pos_y, total_x, total_y)


# Example usage
model, (pos_x, pos_y, total_x, total_y) = model_packing_rectangular(widths, heights)
model.solve()

# Print
solution = {
    "pos_x": pos_x.value().tolist(),
    "pos_y": pos_y.value().tolist(),
    "total_x": total_x.value(),
    "total_y": total_y.value()
}
print(json.dumps(solution))
# End of CPMPy script
Problem description:
The resource-constrained project scheduling problem involves scheduling a set of jobs, each with a specific duration and resource requirement, such that the total project duration (makespan) is minimized. Each job may have precedence constraints, meaning certain jobs must be completed before others can start. Additionally, there are multiple types of resources, each with a limited capacity that must not be exceeded at any time.  Given the durations of the jobs, their resource requirements, the capacities of the resources, and the precedence constraints between the jobs, determine the start times of the jobs that minimize the makespan while satisfying all constraints.  Print the start times of the jobs (start_time).

Input data:
durations_data = [0, 3, 2, 5, 4, 2, 3, 4, 2, 4, 6, 0] resource_needs_data = [[0, 0], [5, 1], [0, 4], [1, 4], [1, 3], [3, 2], [3, 1], [2, 4], [4, 0], [5, 2], [2, 5], [0, 0]] resource_capacities_data = [6, 8] successors_link_data = [[0, 1], [0, 2], [0, 3], [1, 4], [1, 5], [2, 9], [2, 10], [3, 8], [4, 6], [4, 7], [5, 9], [5, 10], [6, 8], [6, 9], [7, 8], [8, 11], [9, 11], [10, 11]]

Decision variables: start_time

CPMpy model:
# Data
durations_data = [0, 3, 2, 5, 4, 2, 3, 4, 2, 4, 6, 0]
resource_needs_data = [[0, 0], [5, 1], [0, 4], [1, 4], [1, 3], [3, 2], [3, 1], [2, 4], [4, 0], [5, 2], [2, 5], [0, 0]]
resource_capacities_data = [6, 8]
successors_link_data = [[0, 1], [0, 2], [0, 3], [1, 4], [1, 5], [2, 9], [2, 10], [3, 8], [4, 6], [4, 7], [5, 9], [5, 10], [6, 8], [6, 9], [7, 8], [8, 11], [9, 11], [10, 11]]
# End of data

# Import libraries
from cpmpy import *
import numpy as np
import json

# Parameters
durations = np.array(durations_data)
resource_needs = np.array(resource_needs_data)
resource_capacities = np.array(resource_capacities_data)
successors_link = np.array(successors_link_data)

nb_resource = len(resource_capacities)
nb_jobs = len(durations)
max_duration = sum(durations)  # dummy upper bound, can be improved of course

# Decision Variables
start_time = intvar(0, max_duration, shape=nb_jobs)  # start time of each job

model = Model()

# Precedence constraints
for j in range(successors_link.shape[0]):
    model += start_time[successors_link[j, 1]] >= start_time[successors_link[j, 0]]+durations[successors_link[j, 0]]

# Cumulative resource constraint
for r in range(nb_resource):
    model += Cumulative(start=start_time, duration=durations, end=start_time+durations,
                        demand=resource_needs[:, r], capacity=resource_capacities[r])

makespan = max(start_time)
model.minimize(makespan)

# Solve
model.solve()

# Print
solution = {"start_time": start_time.value().tolist()}
print(json.dumps(solution))
# End of CPMPy script
Problem description:
The room assignment problem involves assigning a set of requests to a limited number of rooms such that each request is assigned to one room for its entire duration. Some requests may already have a room pre-assigned. The main constraint is that a room can only serve one request at a time, meaning no overlapping requests can be assigned to the same room.  Print the room assignments for the requests (room_assignments).

Input data:
max_rooms = 5  # Maximum number of rooms available start_data = ["2024-05-01", "2024-05-02", "2024-05-03", "2024-05-04"]  # Start date of the requests end_data = ["2024-05-05", "2024-05-06", "2024-05-07", "2024-05-08"]  # End date of the requests preassigned_room_data = [3, -1, -1, -1]  # Room 3 pre-assigned for the first request, -1 for no pre-assignment. So, the second, third, and fourth requests have no pre-assigned rooms.

Decision variables: room_assignments

CPMpy model:
# Data
max_rooms = 5  # Maximum number of rooms available
start_data = ["2024-05-01", "2024-05-02", "2024-05-03", "2024-05-04"]  # Start date of the requests
end_data = ["2024-05-05", "2024-05-06", "2024-05-07", "2024-05-08"]  # End date of the requests
preassigned_room_data = [3, -1, -1, -1]  # Room 3 pre-assigned for the first request, -1 for no pre-assignment. So, the second, third, and fourth requests have no pre-assigned rooms.
# End of data

# Import libraries
import pandas as pd
from cpmpy import *
import numpy as np
import json

# Parameters
data = {
    "start": pd.to_datetime(start_data),
    "end": pd.to_datetime(end_data),
    # convert 0 to NaN
    "room": [r if r != -1 else np.nan for r in preassigned_room_data]
}
df = pd.DataFrame(data)


def model_rooms(df, max_rooms):
    n_requests = len(df)

    # All requests must be assigned to one out of the rooms (same room during entire period).
    requestvars = intvar(0, max_rooms - 1, shape=(n_requests,))

    m = Model()

    # Some requests already have a room pre-assigned
    for idx, row in df.iterrows():
        if not pd.isna(row['room']):
            m += (requestvars[idx] == int(row['room']))

    # A room can only serve one request at a time.
    # <=> requests on the same day must be in different rooms
    for day in pd.date_range(min(df['start']), max(df['end'])):
        overlapping = df[(df['start'] <= day) & (day < df['end'])]
        if len(overlapping) > 1:
            m += AllDifferent(requestvars[overlapping.index])

    return m, requestvars


# Example usage
model, room_assignments = model_rooms(df, max_rooms)
model.solve()

# Print
solution = {"room_assignments": room_assignments.value().tolist()}
print(json.dumps(solution))
# End of CPMPy script
Problem description:
Send More Money Puzzle  The "Send More Money" puzzle is a classic cryptarithmetic problem where each letter represents a unique digit. The goal is to assign digits to the letters such that the following equation holds true:     SEND  + MORE  ------   MONEY  Each letter must be assigned a unique digit from 0 to 9, and the first letter of each word cannot be zero.  Print the values assigned to each letter (s, e, n, d, m, o, r, y).

Input data:

Decision variables: y, e, s, r, o, d, m, n

CPMpy model:
# Import libraries
from cpmpy import *
import numpy as np
import json

# Decision variables
s, e, n, d, m, o, r, y = intvar(0, 9, shape=8)

model = Model(
    AllDifferent([s, e, n, d, m, o, r, y]),
    (sum([s, e, n, d] * np.array([1000, 100, 10, 1])) \
     + sum([m, o, r, e] * np.array([1000, 100, 10, 1])) \
     == sum([m, o, n, e, y] * np.array([10000, 1000, 100, 10, 1]))),
    s > 0,
    m > 0,
)

# Solve
model.solve()

# Print
solution = {
    "s": s.value(),
    "e": e.value(),
    "n": n.value(),
    "d": d.value(),
    "m": m.value(),
    "o": o.value(),
    "r": r.value(),
    "y": y.value()
}
print(json.dumps(solution))
# End of CPMPy script
Problem description:
The goal of Sudoku is to fill a 9x9 grid with numbers so that each row, column and 3x3 section contain all the digits between 1 and 9.  Input is a grid with some cells filled with numbers and some empty.  Print the solved grid (grid).

Input data:
input_grid = [  # 0 represents empty cells     [0, 0, 0,  2, 0, 5,  0, 0, 0],     [0, 9, 0,  0, 0, 0,  7, 3, 0],     [0, 0, 2,  0, 0, 9,  0, 6, 0],      [2, 0, 0,  0, 0, 0,  4, 0, 9],     [0, 0, 0,  0, 7, 0,  0, 0, 0],     [6, 0, 9,  0, 0, 0,  0, 0, 1],      [0, 8, 0,  4, 0, 0,  1, 0, 0],     [0, 6, 3,  0, 0, 0,  0, 8, 0],     [0, 0, 0,  6, 0, 8,  0, 0, 0]]

Decision variables: grid

CPMpy model:
# Data
input_grid = [  # 0 represents empty cells
    [0, 0, 0,  2, 0, 5,  0, 0, 0],
    [0, 9, 0,  0, 0, 0,  7, 3, 0],
    [0, 0, 2,  0, 0, 9,  0, 6, 0],

    [2, 0, 0,  0, 0, 0,  4, 0, 9],
    [0, 0, 0,  0, 7, 0,  0, 0, 0],
    [6, 0, 9,  0, 0, 0,  0, 0, 1],

    [0, 8, 0,  4, 0, 0,  1, 0, 0],
    [0, 6, 3,  0, 0, 0,  0, 8, 0],
    [0, 0, 0,  6, 0, 8,  0, 0, 0]]
# End of data

# Import libraries
from cpmpy import *
import json
import numpy as np

# Parameters
e = 0  # value for empty cells
given = np.array(input_grid)  # numpy array for easy indexing

# Decision Variables
grid = intvar(1, 9, shape=given.shape, name="grid")

# Model
model = Model(
    # Constraints on given cells (non-empty)
    grid[given != e] == given[given != e],  # numpy's indexing, vectorized equality
    # Constraints on rows and columns
    [AllDifferent(row) for row in grid],
    [AllDifferent(col) for col in grid.T],  # numpy's transpose
)

# Constraints on blocks
for i in range(0, 9, 3):
    for j in range(0, 9, 3):
        model += AllDifferent(grid[i:i+3, j:j+3])  # python's indexing

# Solve
model.solve()

# Print
solution = {"grid": grid.value().tolist()}
print(json.dumps(solution))
# End of CPMPy script
Problem description:
The travelling salesman problem, also known as the travelling salesperson problem (TSP), asks the following question: "Given a list of cities and the distances between each pair of cities (or the locations of the cities), what is the shortest possible route that visits each city exactly once and returns to the origin city?  Print the optimal travel distance (travel_distance).

Input data:
locations = [     (288, 149), (288, 129), (270, 133), (256, 141), (256, 163), (246, 157),     (236, 169), (228, 169), (228, 148), (220, 164), (212, 172), (204, 159) ]

Decision variables: travel_distance

CPMpy model:
# Data
locations = [
    (288, 149), (288, 129), (270, 133), (256, 141), (256, 163), (246, 157),
    (236, 169), (228, 169), (228, 148), (220, 164), (212, 172), (204, 159)
]
# End of data

# Import libraries
import math
import numpy as np
from cpmpy import *
import json


# Parameters
def compute_euclidean_distance_matrix(locations):
    """Computes distances between all points (from ortools docs)."""
    n_city = len(locations)
    distances = np.zeros((n_city, n_city))
    for from_counter, from_node in enumerate(locations):
        for to_counter, to_node in enumerate(locations):
            if from_counter != to_counter:
                distances[from_counter][to_counter] = (int(
                    math.hypot((from_node[0] - to_node[0]),
                               (from_node[1] - to_node[1]))))
    return distances.astype(int)


distance_matrix = compute_euclidean_distance_matrix(locations)
n_city = len(locations)

# we use the successor variable formulation and circuit global constraint here
# alternative is to model like in vrp.py

# x[i]=j means that j is visited immediately after i
x = intvar(0, n_city - 1, shape=n_city)

# The 'circuit' global constraint ensures that the successor variables from a circuit
model = Model(Circuit(x))

# the objective is to minimize the travelled distance
distance_matrix = cpm_array(distance_matrix)  # for indexing with variable
travel_distance = sum(distance_matrix[i, x[i]] for i in range(n_city))
model.minimize(travel_distance)

# Solve
model.solve()

# Print
solution = {"travel_distance": int(travel_distance.value())}
print(json.dumps(solution))
Problem description:
Someone in Dreadsbury Mansion killed Aunt Agatha. Agatha, the butler, and Charles live in Dreadsbury Mansion, and are the only ones to live there. A killer always hates, and is no richer than his victim. Charles hates noone that Agatha hates. Agatha hates everybody except the butler. The butler hates everyone not richer than Aunt Agatha. The butler hates everyone whom Agatha hates. Noone hates everyone. Who killed Agatha?  Print the index of the killer (killer).

Input data:
names = ["Agatha herself", "the butler", "Charles"]

Decision variables: killer

CPMpy model:
# Data
names = ["Agatha herself", "the butler", "Charles"]
# End of data

# Import libraries
from cpmpy import *
import json

# Agatha, the butler, and Charles live in Dreadsbury Mansion, and
# are the only ones to live there.
n = 3
(agatha, butler, charles) = range(n)  # enum constants

# Who killed agatha?
victim = agatha
killer = intvar(0, n - 1, name="killer")  # 0=Agatha, 1=butler, 2=Charles

hates = boolvar(shape=(n, n), name="hates")
richer = boolvar(shape=(n, n), name="richer")

model = Model(
    # A killer always hates, and is no richer than, his victim.
    # note; 'killer' is a variable, so must write ==1/==0 explicitly
    hates[killer, victim] == 1,
    richer[killer, victim] == 0,

    # implied richness: no one richer than himself, and anti-reflexive
    [~richer[i, i] for i in range(n)],
    [(richer[i, j]) == (~richer[j, i]) for i in range(n) for j in range(i + 1, n)],

    # Charles hates noone that Agatha hates.
    [(hates[agatha, i]).implies(~hates[charles, i]) for i in range(n)],

    # Agatha hates everybody except the butler.
    hates[agatha, (agatha, charles, butler)] == [1, 1, 0],

    # The butler hates everyone not richer than Aunt Agatha.
    [(~richer[i, agatha]).implies(hates[butler, i]) for i in range(n)],

    # The butler hates everyone whom Agatha hates.
    [(hates[agatha, i]).implies(hates[butler, i]) for i in range(n)],

    # Noone hates everyone.
    [sum(hates[i, :]) <= 2 for i in range(n)],
)

# Solve
model.solve()

# Print
solution = {"killer": killer.value()}
print(json.dumps(solution))
# End of CPMPy script
Problem description:
The wolf, goat and cabbage problem is a river crossing puzzle. It dates back to at least the 9th century, and has entered the folklore of several cultures.  The story: A farmer with a wolf, a goat, and a cabbage must cross a river by boat. The boat can carry only the farmer and a single item. If left unattended together, the wolf would eat the goat, or the goat would eat the cabbage. How can they cross the river without anything being eaten?  Print whether the wolf, goat, cabbage, and boat are on the destination shore (1) or the starting shore (0) at each stage (wolf_pos, goat_pos, cabbage_pos, boat_pos).

Input data:
stage = 8  # Number of stages

Decision variables: boat_pos, 0, cabbage_pos, goat_pos, wolf_pos, 1

CPMpy model:
# Data
stage = 8  # Number of stages
# End of data

# Import libraries
from cpmpy import *
import json

wolf_pos = boolvar(stage)
cabbage_pos = boolvar(stage)
goat_pos = boolvar(stage)
boat_pos = boolvar(stage)

model = Model(
    # Initial situation
    (boat_pos[0] == 0),
    (wolf_pos[0] == 0),
    (goat_pos[0] == 0),
    (cabbage_pos[0] == 0),

    # Boat keeps moving between shores
    [boat_pos[i] != boat_pos[i - 1] for i in range(1, stage)],

    # Final situation
    (boat_pos[-1] == 1),
    (wolf_pos[-1] == 1),
    (goat_pos[-1] == 1),
    (cabbage_pos[-1] == 1),

    # # Wolf and goat cannot be left alone
    [(goat_pos[i] != wolf_pos[i]) | (boat_pos[i] == wolf_pos[i]) for i in range(stage)],

    # # Goat and cabbage cannot be left alone
    [(goat_pos[i] != cabbage_pos[i]) | (boat_pos[i] == goat_pos[i]) for i in range(stage)],

    # # Only one animal/cabbage can move per turn
    [abs(wolf_pos[i] - wolf_pos[i + 1]) + abs(goat_pos[i] - goat_pos[i + 1]) + abs(
        cabbage_pos[i] - cabbage_pos[i + 1]) <= 1 for i in range(stage - 1)],
)

# Solve
model.solve()

# Print
solution = {"wolf_pos": wolf_pos.value().tolist(), "goat_pos": goat_pos.value().tolist(),
            "cabbage_pos": cabbage_pos.value().tolist(), "boat_pos": boat_pos.value().tolist()}
print(json.dumps(solution))
# End of CPMPy script
Problem description:
The Zebra puzzle (sometimes referred to as Einstein's puzzle) is defined as follows. There are five houses in a row, numbered from left to right. Each of the five houses is painted a different color, and has one inhabitant. The inhabitants are all of different nationalities, own different pets, drink different beverages and have different jobs. We know that:  - colors are yellow, green, red, white, and blue  - nations of inhabitants are italy, spain, japan, england, and norway  - pets are cat, zebra, bear, snails, and horse  - drinks are milk, water, tea, coffee, and juice  - jobs are painter, sculptor, diplomat, pianist, and doctor  - the painter owns the horse  - the diplomat drinks coffee  - the one who drinks milk lives in the white house  - the Spaniard is a painter  - the Englishman lives in the red house  - the snails are owned by the sculptor  - the green house is on the left of the red one  - the Norwegian lives on the right of the blue house  - the doctor drinks milk  - the diplomat is Japanese  - the Norwegian owns the zebra  - the green house is next to the white one  - the horse is owned by the neighbor of the diplomat  - the Italian either lives in the red, white or green house  Print the numbers representing the house of each inhabitant (colors, nations, jobs, pets, drinks). The same number represents the same house.

Input data:

Decision variables: colors, jobs, nations, pets, drinks

CPMpy model:
# Import libraries
from cpmpy import *
import json

n_houses = 5

# colors[i] is the house of the ith color
yellow, green, red, white, blue = colors = intvar(0,n_houses-1, shape=n_houses)

# nations[i] is the house of the inhabitant with the ith nationality
italy, spain, japan, england, norway = nations = intvar(0,n_houses-1, shape=n_houses)

# jobs[i] is the house of the inhabitant with the ith job
painter, sculptor, diplomat, pianist, doctor = jobs = intvar(0,n_houses-1, shape=n_houses)

# pets[i] is the house of the inhabitant with the ith pet
cat, zebra, bear, snails, horse = pets = intvar(0,n_houses-1, shape=n_houses)

# drinks[i] is the house of the inhabitant with the ith preferred drink
milk, water, tea, coffee, juice = drinks = intvar(0,n_houses-1, shape=n_houses)

model = Model(
    AllDifferent(colors),
    AllDifferent(nations),
    AllDifferent(jobs),
    AllDifferent(pets),
    AllDifferent(drinks),

    painter == horse,
    diplomat == coffee,
    white == milk,
    spain == painter,
    england == red,
    snails == sculptor,
    green + 1 == red,
    blue + 1 == norway,
    doctor == milk,
    japan == diplomat,
    norway == zebra,
    abs(green - white) == 1,
    #horse in {diplomat - 1, diplomat + 1},
    (horse == diplomat-1)|(horse == diplomat+1),
    #italy in {red, white, green}
    (italy == red)|(italy == white)|(italy == green),
)
model.solve()

# Print
solution = {
    "colors": colors.value().tolist(),
    "nations": nations.value().tolist(),
    "jobs": jobs.value().tolist(),
    "pets": pets.value().tolist(),
    "drinks": drinks.value().tolist(),
}
print(json.dumps(solution))
# End of CPMPy script
Problem description:
My bank card has a 4 digit pin, abcd. I use the following facts to help me remember it:  - no two digits are the same - the 2-digit number cd is 3 times the 2-digit number ab - the 2-digit number da is 2 times the 2-digit number bc  Print the PIN (a, b, c, d).

Input data:

Decision variables: b, d, c, a

CPMpy model:
# Import libraries
from cpmpy import *
import json

# Decision Variables
a, b, c, d = intvar(1, 9, shape=4)  # a, b, c, d are the four digits of the PIN

# Constraints
model = Model()

model += AllDifferent([a, b, c, d])  # no two digits are the same
model += 10 * c + d == 3 * (10 * a + b)  # cd is 3 times ab
model += 10 * d + a == 2 * (10 * b + c)  # da is 2 times bc

# Solve
model.solve()

# Print the solution
solution = {"a": a.value(), "b": b.value(), "c": c.value(), "d": d.value()}
print(json.dumps(solution))
# End of CPMPy script
Problem description:
We want to climb a stair of n steps with [m1, m2] steps at a time. For example a stair of 4 steps with m1 = 1, and m2 = 2 can be climbed with a sequence of four one-step moves or with two two-steps moves. Find a way to climb a stair of 20 steps with m1 = 3 and m2 = 5, i.e. you can take only 3 or 4 or 5 steps at a time.  Print the number of steps (steps) taken at each move as a list of 20 integers, where each integer is between 3 and 5, or 0 if no steps are taken at that move, i.e. after we reach the top of the stair with the last move.

Input data:

Decision variables: steps

CPMpy model:
# Import libraries
from cpmpy import *
import json
import numpy as np

# Parameters
n = 20  # total number of steps in the stair
m1, m2 = 3, 5  # number of steps that can be taken at a time

# Decision variables
# In the worst case, we take all steps one at a time, so we have 'n' decision variables
steps = intvar(0, m2, shape=n)  # steps taken at each move

# Model setup
model = Model()

# Constraint: the sum of steps should equal the total number of stairs
model += sum(steps) == n

# Constraint: the number of steps taken at each move should be between m1 and m2 or 0
model += [(steps[i] >= m1) | (steps[i] == 0) for i in range(n)]
model += [steps[i] <= m2 for i in range(n)]

# Trailing zeros: If a step is 0, then all the following steps should be 0
for i in range(1, n):
    model += (steps[i - 1] == 0).implies(all(steps[j] == 0 for j in range(i, n)))

# Solve
model.solve()

# Print the solution
solution = {"steps": steps.value().tolist()}
print(json.dumps(solution))
# End of CPMPy script