# Source description: https://www.csplib.org/Problems/prob001/

A number of cars are to be produced; they are not identical, because different options are available as variants on the basic model. The assembly line has different stations which install the various options (air-conditioning, sunroof, etc.). These stations have been designed to handle at most a certain percentage of  the cars passing along the assembly line. Furthermore, the cars requiring a certain option must not be bunched together, otherwise the station will not be able to cope. Consequently, the cars must be arranged in a sequence so that the capacity of each station is never exceeded. For instance, if a particular station can only cope with at most half of the cars passing along the line, the sequence must be built so that at most 1 car in any 2 requires that option.  Print the sequence of car types in the assembly line (sequence); each car type is represented by a number starting from 0.

# Data
at_most = [1, 2, 2, 2, 1]  # The amount of times a property can be present
# in a group of consecutive timeslots (see next variable)
per_slots = [2, 3, 3, 5, 5]  # The amount of consecutive timeslots
demand = [1, 1, 2, 2, 2, 2]  # The demand per type of car
requires = [[1, 0, 1, 1, 0],
            [0, 0, 0, 1, 0],
            [0, 1, 0, 0, 1],
            [0, 1, 0, 1, 0],
            [1, 0, 1, 0, 0],
            [1, 1, 0, 0, 0]]  # The properties per type of car
# End of data

# Import libraries
from cpmpy import *
import json

# Parameters
n_cars = sum(demand)  # The amount of cars to sequence
n_options = len(at_most)  # The amount of different options
n_types = len(demand)  # The amount of different car types
requires = cpm_array(requires)  # For element constraint

# Decision Variables
sequence = intvar(0, n_types - 1, shape=n_cars, name="sequence")  # The sequence of car types
setup = boolvar(shape=(n_cars, n_options), name="setup")  # Sequence of different options based on the car type

# Model
model = Model()

# The amount of each type of car in the sequence has to be equal to the demand for that type
model += [sum(sequence == t) == demand[t] for t in range(n_types)]

# Make sure that the options in the setup table correspond to those of the car type
for s in range(n_cars):
    model += [setup[s, o] == requires[sequence[s], o] for o in range(n_options)]

# Check that no more than "at most" car options are used per "per_slots" slots
for o in range(n_options):
    for s in range(n_cars - per_slots[o]):
        slot_range = range(s, s + per_slots[o])
        model += (sum(setup[slot_range, o]) <= at_most[o])

# Solve
model.solve()

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


# Source description: https://www.csplib.org/Problems/prob002/

This problem arises from a colour printing firm which produces a variety of products from thin board, including cartons for human and animal food and magazine inserts. Food products, for example, are often marketed as a basic brand with several variations (typically flavours). Packaging for such variations usually has the same overall design, in particular the same size and shape, but differs in a small proportion of the text displayed and/or in colour. For instance, two variations of a cat food carton may differ only in that on one is printed ‘Chicken Flavour’ on a blue background whereas the other has ‘Rabbit Flavour’ printed on a green background. A typical order is for a variety of quantities of several design variations. Because each variation is identical in dimension, we know in advance exactly how many items can be printed on each mother sheet of board, whose dimensions are largely determined by the dimensions of the printing machinery. Each mother sheet is printed from a template, consisting of a thin aluminium sheet on which the design for several of the variations is etched. The problem is to decide, firstly, how many distinct templates to produce, and secondly, which variations, and how many copies of each, to include on each template. The given example is based on data from an order for cartons for different varieties of dry cat-food.  Print the number of printed sheets (production) and the layout of the templates (layout).

# Data
n_slots = 9  # The amount of slots on a template
n_templates = 2  # The amount of templates
n_var = 7  # The amount of different variations
demand = [250, 255, 260, 500, 500, 800, 1100]  # The demand per variation
# End of data

# Import libraries
from cpmpy import *
import json

# Parameters
ub = max(demand)  # The upper bound for the production

# create model
model = Model()

# decision variables
production = intvar(1, ub, shape=n_templates, name="production")
layout = intvar(0, n_var, shape=(n_templates, n_var), name="layout")

# all slots are populated in a template
model += all(sum(layout[i]) == n_slots for i in range(n_templates))

# meet demand
for var in range(n_var):
    model += sum(production * layout[:, var]) >= demand[var]

# break symmetry
# equal demand
for i in range(n_var - 1):
    if demand[i] == demand[i + 1]:
        model += layout[0, i] <= layout[0, i + 1]
        for j in range(n_templates - 1):
            model += (layout[j, i] == layout[j, i + 1]).implies(layout[j + 1, i] <= layout[j + 1, i + 1])

# distinguish templates
for i in range(n_templates - 1):
    model += production[i] <= production[i + 1]

# static symmetry
for i in range(n_var - 1):
    if demand[i] < demand[i + 1]:
        model += sum(production * layout[:, i]) <= sum(production * layout[:, i + 1])

# minimize number of printed sheets
model.minimize(sum(production))

# Solve
model.solve()

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


# Source description: https://www.csplib.org/Problems/prob005/

These problems have many practical applications in communications and electrical engineering. The objective is to construct a binary sequence of length n that minimizes the autocorrelations between bits. Each bit in the sequence takes the value +1 or -1. With non-periodic (or open) boundary conditions, the k-th autocorrelation, Ck is defined to be \[ C_k = \sum_{i=0}^{n-k-1} S_i \cdot S_{i+k} \]. With periodic (or cyclic) boundary conditions, the k-th autocorrelation, Ck is defined to be \[ C_k = \sum_{i=0}^{n-1} S_i \cdot S_{(i+k) \mod n} \]. The aim is to minimize the sum of the squares of these autocorrelations. That is, to minimize \[ E = \sum_{k=1}^{n-1} C_k^2 \].  Print the binary sequence (sequence) and the energy value (E).

# Data
n = 10  # Length of the binary sequence
# End of data

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

# periodic auto correlation
def PAF(arr, s):
    # roll the array 's' indices
    return sum(arr * np.roll(arr, -s))

# Decision Variables
sequence = intvar(-1, 1, shape=n, name="sequence")  # binary sequence
E = sum([PAF(sequence, s) ** 2 for s in range(1, n)])  # energy value

model = Model()

# exclude 0
model += sequence != 0

# minimize sum of squares
model.minimize(E)

# Solve
model.solve()

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


# Source description: https://www.csplib.org/Problems/prob006/

These problems are said to have many practical applications including sensor placements for x-ray crystallography and radio astronomy. A Golomb ruler may be defined as a set of \( m \) integers \( 0 = a_1 < a_2 < \cdots < a_m \) such that the \( \frac{m(m-1)}{2} \) differences \( a_j - a_i, \, 1 \leq i < j \leq m \) are distinct. Such a ruler is said to contain \( m \) marks and is of length \( a_m \). The objective is to find optimal (minimum length) or near-optimal rulers. Note that a symmetry can be removed by adding the constraint that \( a_2 - a_1 < a_m - a_{m-1} \), the first difference is less than the last.  There is no requirement that a Golomb ruler measures all distances up to its length - the only requirement is that each distance is only measured in one way. However, if a ruler does measure all distances, it is classified as a perfect Golomb ruler.  There exist several interesting generalizations of the problem which have received attention like modular Golomb rulers (differences are all distinct mod a given base), disjoint Golomb rulers, Golomb rectangles (the 2-dimensional generalization of Golomb rulers), and difference triangle sets (sets of rulers with no common difference).  Print the positions of the marks (marks) and the total length of the Golomb ruler (length).

# Data
size = 10  # Number of marks on the Golomb ruler
# End of data

# Import libraries
from cpmpy import *
import json

# Decision variables
marks = intvar(0, size * size, shape=size, name="marks")
length = marks[-1]

# Model
model = Model()

# first mark is 0
model += (marks[0] == 0)

# marks must be increasing
model += marks[:-1] < marks[1:]

# golomb constraint
diffs = [marks[j] - marks[i] for i in range(0, size - 1) for j in range(i + 1, size)]
model += AllDifferent(diffs)

# Symmetry breaking
model += (marks[size - 1] - marks[size - 2] > marks[1] - marks[0])
model += (diffs[0] < diffs[size - 1])

# find optimal ruler
model.minimize(length)

# Solve
model.solve()

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


# Source description: https://www.csplib.org/Problems/prob007/

Given the twelve standard pitch-classes (c, c#, d, …), represented by numbers \(0, 1, \ldots, 11\), find a series in which each pitch-class occurs exactly once and in which the musical intervals between neighbouring notes cover the full set of intervals from the minor second (1 semitone) to the major seventh (11 semitones). That is, for each of the intervals, there is a pair of neighbouring pitch-classes in the series, between which this interval appears.  The problem of finding such a series can be easily formulated as an instance of a more general arithmetic problem on \(\mathbb{Z}_n\), the set of integer residues modulo \(n\). Given \(n \in \mathbb{N}\), find a vector \(s = (s_1, \ldots, s_n)\), such that  - \(s\) is a permutation of \(\mathbb{Z}_n = \{0, 1, \ldots, n-1\}\); and - the interval vector \(v = (|s_2 - s_1|, |s_3 - s_2|, \ldots, |s_n - s_{n-1}|)\) is a permutation of   \(\mathbb{Z}_n \setminus \{0\} = \{1, 2, \ldots, n-1\}\).  A vector \(v\) satisfying these conditions is called an all-interval series of size \(n\); the problem of finding such a series is the all-interval series problem of size \(n\). We may also be interested in finding all possible series of a given size.  The All-Interval Series is a special case of the Graceful Graphs in which the graph is a line.  Print the sequence of pitch-classes (x) and the corresponding intervals (diffs).

# Data
n = 12  # Number of pitch-classes
# End of data

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

# Create the solver
model = Model()

# Declare variables
x = intvar(0, n - 1, shape=n, name="x")  # Pitch-classes
diffs = intvar(1, n - 1, shape=n - 1, name="diffs")  # Intervals

# Constraints
model += [AllDifferent(x),
          AllDifferent(diffs)]

# Differences between successive values
model += diffs == np.abs(x[1:] - x[:-1])

# Symmetry breaking
model += [x[0] < x[-1]]  # Mirroring array is equivalent solution
model += [diffs[0] < diffs[1]]  # Further symmetry breaking

# Solve
model.solve()

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


# Source description: https://www.csplib.org/Problems/prob008/

Supply vessels transport containers from site to site. The deck area is rectangular. Containers are cuboid, and are laid out in a single layer. All containers are positioned parallel to the sides of the deck. The contents of the containers determine their class. Certain classes of containers are constrained to be separated by minimum distances either along the deck or across the deck.  The vessel loading decision problem is to determine whether a given set of containers can be positioned on a given deck, without overlapping, and without violating any of the separation constraints. The problem can be modelled as packing of a set of rectangles into a larger rectangle, subject to constraints.  In practice, the layout may be further constrained by the physical loading sequence. Containers are manoeuvred into position from the southeast corner. Each successive container in the loading sequence must be positioned so that it touches part of another container or a deck wall both to the north and to the west.  Print the positions of the containers (left, right, top, bottom).

# Data
deck_width = 5  # Width of the deck
deck_length = 5  # Length of the deck
n_containers = 3  # Number of containers
width = [5, 2, 3]  # Widths of containers
length = [1, 4, 4]  # Lengths of containers
classes = [1, 1, 1]  # Classes of containers
separation = [  # Separation constraints between classes
    [0, 0],
    [0, 0]
]
# End of data

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

# Create the model
model = Model()

# Declare variables
left = intvar(0, deck_width, shape=n_containers, name="left")
right = intvar(0, deck_width, shape=n_containers, name="right")
top = intvar(0, deck_length, shape=n_containers, name="top")
bottom = intvar(0, deck_length, shape=n_containers, name="bottom")

# Set shape of containers
for i in range(n_containers):
    model += ((right[i] - left[i] == width[i]) & (top[i] - bottom[i] == length[i])) | \
             ((right[i] - left[i] == length[i]) & (top[i] - bottom[i] == width[i]))

# No overlap between containers
for x, y in all_pairs(range(n_containers)):
    c1, c2 = classes[x], classes[y]
    sep = separation[c1 - 1][c2 - 1]
    model += (
            (right[x] + sep <= left[y]) |  # x at least sep left of y or
            (left[x] >= right[y] + sep) |  # x at least sep right of y or
            (top[x] + sep <= bottom[y]) |  # x at least sep under y or
            (bottom[x] >= top[y] + sep)  # x at least sep above y
    )

# Solve
model.solve()

# Print
solution = {
    "left": left.value().tolist(),
    "right": right.value().tolist(),
    "top": top.value().tolist(),
    "bottom": bottom.value().tolist()
}
print(json.dumps(solution))
# End of CPMPy script


# Source description: https://www.csplib.org/Problems/prob009/

The perfect square placement problem (also called the squared square problem) is to pack a set of squares with given integer sizes into a bigger square in such a way that no squares overlap each other and all square borders are parallel to the border of the big square. For a perfect placement problem, all squares have different sizes. The sum of the square surfaces is equal to the surface of the packing square, so that there is no spare capacity. A simple perfect square placement problem is a perfect square placement problem in which no subset of the squares (greater than one) are placed in a rectangle.  Print the coordinates of the squares (x_coords, y_coords).

# Data
base = 6  # Side length of the large square
sides = [3, 3, 3, 2, 1, 1, 1, 1, 1]  # Side lengths of the smaller squares
# End of data

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

def perfect_squares(base, sides):
    model = Model()
    sides = np.array(sides)

    squares = range(len(sides))

    # Ensure that the squares cover the base exactly
    assert np.square(sides).sum() == base ** 2, "Squares do not cover the base exactly!"

    # Variables
    x_coords = intvar(0, base, shape=len(squares), name="x_coords")
    y_coords = intvar(0, base, shape=len(squares), name="y_coords")

    # Squares must be in bounds of big square
    model += x_coords + sides <= base
    model += y_coords + sides <= base

    # No overlap between squares
    for a, b in all_pairs(squares):
        model += (
            (x_coords[a] + sides[a] <= x_coords[b]) |
            (x_coords[b] + sides[b] <= x_coords[a]) |
            (y_coords[a] + sides[a] <= y_coords[b]) |
            (y_coords[b] + sides[b] <= y_coords[a])
        )

    return model, (x_coords, y_coords)

# Example usage
model, (x_coords, y_coords) = perfect_squares(base, sides)

# Solve
model.solve()

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


# Source description: https://www.csplib.org/Problems/prob010/

The coordinator of a local golf club has come to you with the following problem. In their club, there are 32 social golfers, each of whom play golf once a week, and always in groups of 4. They would like you to come up with a schedule of play for these golfers, to last as many weeks as possible, such that no golfer plays in the same group as any other golfer on more than one occasion. Possible variants of the above problem include: finding a 10-week schedule with "maximum socialisation"; that is, as few repeated pairs as possible (this has the same solutions as the original problem if it is possible to have no repeated pairs), and finding a schedule of minimum length such that each golfer plays with every other golfer at least once ("full socialisation").  The problem can easily be generalized to that of scheduling \( m \) groups of \( n \) golfers over \( p \) weeks, such that no golfer plays in the same group as any other golfer twice (i.e. maximum socialisation is achieved).  This problem is derived from a question posted to sci.op-research by bigwind777@aol.com (Bigwind777) in May 1998. It is a generalisation of the problem of constructing a round-robin tournament schedule, where the number of players in a "game" is more than two. The optimal solution for 32 golfers is not yet known.  Print the assignments of golfers to groups for each week (assign).

# Data
n_weeks = 4  # Number of weeks
n_groups = 3  # Number of groups
group_size = 3  # Size of each group
# End of data

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

def social_golfers(n_weeks, n_groups, group_size):

    n_golfers = n_groups * group_size
    golfers = np.arange(n_golfers)
    weeks = np.arange(n_weeks)
    groups = np.arange(n_groups)

    # Possible configurations
    assign = intvar(0, n_groups - 1, shape=(n_golfers, n_weeks), name="assign")

    model = Model()

    # C1: Each group has exactly group_size players
    for gr in groups:
        for w in weeks:
            model += sum(assign[:, w] == gr) == group_size

    # C2: Each pair of players only meets at most once
    for g1, g2 in all_pairs(golfers):
        model += sum(assign[g1] == assign[g2]) <= 1

    # SBSA: Symmetry-breaking by selective assignment
    # On the first week, the first group_size golfers play in group 1, the
    # second group_size golfers play in group 2, etc. On the second week,
    # golfer 1 plays in group 1, golfer 2 plays in group 2, etc.
    # model += [assign[:, 0] == (golfers // group_size)]

    for g in golfers:
        if g < group_size:
            model += [assign[g, 1] == g]

    # First golfer always in group 0
    model += [assign[0, :] == 0]

    return model, assign

# Example usage
model, assign = social_golfers(n_weeks, n_groups, group_size)

# Solve
model.solve()

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


# Source description: https://www.csplib.org/Problems/prob011/

The problem is finding a timetable for the 1997/98 Atlantic Coast Conference (ACC) in basketball. It was first tackled by Nemhauser and Trick.  The 9 basketball teams in the tournament are Clemson (Clem), Duke (Duke), Florida State (FSU), Georgia Tech (GT), Maryland (UMD), North Carolina (UNC), North Carolina State (NCSt), Virginia (UVA), and Wake Forest (Wake). The problem is to determine a double round robin schedule for these 9 teams subject to some additional constraints. In a double round robin, each team plays each other, once at home, once away. The schedule is to be played over 18 dates. The first and all subsequent odd dates are weekday fixtures. The second and all subsequent even dates are weekend fixtures. There are nine other sets of constraints.  1. Mirroring. The dates are grouped into pairs (r1, r2), such that each team will get to play against the same team in dates r1 and r2. Such a grouping is called a mirroring scheme. Nemhauser and Trick use the mirroring scheme {(1, 8), (2, 9), (3, 12), (4, 13), (5, 14), (6, 15), (7, 16), (10, 17), (11, 18)}  2. No Two Final Aways. No team can play away on both last dates.  3. Home/Away/Bye Pattern Constraints. No team may have more than two away matches in a row. No team may have more than two home matches in a row. No team may have more than three away matches or byes in a row. No team may have more than four home matches or byes in a row.  4. Weekend Pattern. Of the weekends, each team plays four at home, four away, and one bye.  5. First Weekends. Each team must have home matches or byes at least on two of the first five weekends.  6. Rival Matches. Every team except FSU has a traditional rival. The rival pairs are Duke-UNC, Clem-GT, NCSt-Wake, and UMD-UVA. In the last date, every team except FSU plays against its rival, unless it plays against FSU or has a bye.  7. Constrained Matches. The following pairings must occur at least once in dates 11 to 18: Wake-UNC, Wake-Duke, GT-UNC, and GT-Duke.  8. Opponent Sequence Constraints. No team plays in two consecutive dates away against UNC and Duke. No team plays in three consecutive dates against UNC, Duke and Wake (independent of home/away).  9. Other Constraints. UNC plays its rival Duke in the last date and in date 11. UNC plays Clem in the second date. Duke has a bye in date 16. Wake does not play home in date 17. Wake has a bye in the first date. Clem, Duke, UMD and Wake do not play away in the last date. Clem, FSU, GT and Wake do not play away in the first date. Neither FSU nor NCSt have a bye in the last date. UNC does not have a bye in the first date.  Print the match configuration (config) and whether the team is playing home, away, or has a bye (where).

# Data
n_teams = 9
n_days = 18
# End of data

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

def basketball_schedule():
    n_teams = 9
    n_days = 18

    # Teams
    teams = np.arange(n_teams)
    CLEM, DUKE, FSU, GT, UMD, UNC, NCSt, UVA, WAKE = teams
    rivals = [GT, UNC, FSU, CLEM, UVA, DUKE, WAKE, UMD, NCSt]

    # Days
    days = np.arange(n_days)
    weekdays = np.where(days % 2 == 0)[0]
    weekends = np.where(days % 2 == 1)[0]

    # matrix indicating which teams play against each other at what date
    # config[d,i] == j iff team i plays team j on day d of the tournament
    # config[d,i] == i iff team i plays bye on day d of the tournament
    config = intvar(0, n_teams - 1, shape=(n_days, n_teams), name="config")
    # home[d,i] == True iff team i plays home on day d of the tournament
    where = intvar(0, 2, shape=(n_days, n_teams), name="where")
    HOME, BYE, AWAY = 0, 1, 2

    model = Model()

    # a team cannot have different opponents on the same day
    for day_conf in config:
        model += AllDifferent(day_conf)

    # team plays itself when playing BYE
    for day in range(n_days):
        model += (config[day] == teams) == (where[day] == BYE)

    # symmetry
    for day in range(n_days):
        for t in range(n_teams):
            model += config[day, config[day, t]] == t

    # 1. mirroring constraint
    scheme = np.array([7, 8, 11, 12, 13, 14, 15, 0, 1, 16, 17, 2, 3, 4, 5, 6, 9, 10])
    model += config == config[scheme]
    model += where == (2 - where[scheme])

    # 2. no two final days away
    for t in range(n_teams):
        model += sum(where[-2:, t] == AWAY) <= 1

    # 3. home/away/bye pattern constraint
    for t in teams:
        for d in days[:-3]:
            # No team may have more than two home matches in a row
            model += sum(where[d:d + 3, t] == HOME) <= 2
            # No team may have more than two away matches in a row
            model += sum(where[d:d + 3, t] == AWAY) <= 2

        for d in days[:-4]:
            # No team may have more than three away matches or byes in a row
            model += sum((where[d:d + 4, t] == AWAY) |
                         (where[d:d + 4, t] == BYE)) <= 3

        for d in days[:-5]:
            # No team may have more than four home matches or byes in a row.
            model += sum((where[d:d + 5, t] == HOME) |
                         (where[d:d + 5, t] == BYE)) <= 4

    # 4. weekend pattern constraint
    # Of the weekends, each team plays four at home, four away, and one bye.
    for t in range(n_teams):
        model += sum(where[weekends, t] == HOME) == 4
        model += sum(where[weekends, t] == AWAY) == 4
        model += sum(where[weekends, t] == BYE) == 1

    # 5. first weekends constraint
    # Each team must have home matches or byes at least on two of the first five weekends.
    for t in range(n_teams):
        model += (sum(where[weekends[:5], t] == HOME) +
                  sum(where[weekends[:5], t] == BYE)) >= 2

    # 6. rival matches constraint
    # In the last date, every team except FSU plays against its rival, unless it plays against FSU or has a bye.
    for t in teams:
        if t != FSU:
            model += (config[-1, t] == rivals[t]) | \
                     (config[-1, t] == FSU) | \
                     (where[-1, t] == BYE)

    # 7. Constrained matches
    # The following pairings must occur at least once in dates 11 to 18:
    # Wake-UNC, Wake-Duke, GT-UNC, and GT-Duke.
    model += sum(config[10:, WAKE] == UNC) >= 1
    model += sum(config[10:, WAKE] == DUKE) >= 1
    model += sum(config[10:, GT] == UNC) >= 1
    model += sum(config[10:, GT] == DUKE) >= 1

    # 8. Opponent Sequence constraints
    for t in teams:
        for d in days[:-2]:
            if t != DUKE and t != UNC:
                # No team plays in two consecutive dates away against UNC and Duke
                model += ((config[d, t] == UNC) & (where[d, t] == AWAY) &
                          (config[d + 1, t] == DUKE) & (where[d + 1, t] == AWAY)).implies(False)
                model += ((config[d, t] == DUKE) & (where[d, t] == AWAY) &
                          (config[d + 1, t] == UNC) & (where[d + 1, t] == AWAY)).implies(False)
        for d in days[:-3]:
            if t not in [UNC, DUKE, WAKE]:
                # No team plays in three consecutive dates against UNC, Duke and Wake (independent of home/away).
                model += ((config[d, t] == UNC) & (config[d + 1, t] == DUKE) & (config[d + 2, t] == WAKE)).implies(
                    False)
                model += ((config[d, t] == UNC) & (config[d + 1, t] == WAKE) & (config[d + 2, t] == DUKE)).implies(
                    False)
                model += ((config[d, t] == DUKE) & (config[d + 1, t] == UNC) & (config[d + 2, t] == WAKE)).implies(
                    False)
                model += ((config[d, t] == DUKE) & (config[d + 1, t] == WAKE) & (config[d + 2, t] == UNC)).implies(
                    False)
                model += ((config[d, t] == WAKE) & (config[d + 1, t] == UNC) & (config[d + 2, t] == DUKE)).implies(
                    False)
                model += ((config[d, t] == WAKE) & (config[d + 1, t] == DUKE) & (config[d + 2, t] == UNC)).implies(
                    False)

    # 9. Other constraints
    # UNC plays its rival Duke in the last date and in date 11
    model += config[10, UNC] == DUKE
    model += config[-1, UNC] == DUKE
    # UNC plays Clem in the second date
    model += config[1, UNC] == CLEM
    # Duke has a bye in date 16
    model += where[15, DUKE] == BYE
    # Wake does not play home in date 17
    model += where[16, WAKE] != HOME
    # Wake has a bye in the first date
    model += where[0, WAKE] == BYE
    # Clem, Duke, UMD and Wake do not play away in the last date
    model += where[-1, [CLEM, DUKE, UMD, WAKE]] != AWAY
    # Clem, FSU, GT and Wake do not play away in the first date
    model += where[0, [CLEM, FSU, GT, WAKE]] != AWAY
    # Neither FSU nor NCSt have a bye in last date
    model += where[-1, [FSU, NCSt]] != BYE
    # UNC does not have a bye in the first date.
    model += where[0, UNC] != BYE

    return model, (config, where)

# Example usage
model, (config, where) = basketball_schedule()

# Solve
model.solve()

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


# Source description: https://www.csplib.org/Problems/prob012/

Nonograms are a popular puzzle, which goes by different names in different countries. Solvers have to shade in squares in a grid so that blocks of consecutive shaded squares satisfy constraints given for each row and column. Constraints typically indicate the sequence of shaded blocks (e.g. 3,1,2 means that there is a block of 3, then a gap of unspecified size, a block of length 1, another gap, and then a block of length 2).  Print the solution board (board).

# Data
rows = 8  # Number of rows
row_rule_len = 2  # Maximum length of row rules
row_rules = [
    [0, 1],
    [0, 2],
    [4, 4],
    [0, 12],
    [0, 8],
    [0, 9],
    [3, 4],
    [2, 2]
]  # Rules for rows
cols = 13  # Number of columns
col_rule_len = 2  # Maximum length of column rules
col_rules = [
    [0, 2],
    [2, 1],
    [3, 2],
    [0, 6],
    [1, 4],
    [0, 3],
    [0, 4],
    [0, 4],
    [0, 4],
    [0, 5],
    [0, 4],
    [1, 3],
    [0, 2]
]  # Rules for columns
# End of data

# Import libraries
import json
from cpmpy import *

def nonogram(row_rules, col_rules, **kwargs):
    solver = SolverLookup.get("ortools")
    n_rows, n_cols = len(row_rules), len(col_rules)
    board = intvar(0, 1, shape=(n_rows, n_cols), name="board")
    solver.user_vars.update(set(board.flatten()))

    # Patterns of each row must be correct
    for r, pattern in enumerate(row_rules):
        automaton_func, final_states = transition_function(pattern)
        solver.ort_model.AddAutomaton(
            solver.solver_vars(board[r]),
            starting_state=0, final_states=final_states,
            transition_triples=automaton_func
        )

    # Patterns of each column must be correct
    for c, pattern in enumerate(col_rules):
        automaton_func, final_states = transition_function(pattern)
        solver.ort_model.AddAutomaton(
            solver.solver_vars(board[:, c]),
            starting_state=0, final_states=final_states,
            transition_triples=automaton_func
        )

    return solver, (board,)

def transition_function(pattern):
    """
    Pattern is a vector containing the lengths of blocks with value 1
    """
    func = []
    n_states = 0
    for block_length in pattern:
        if block_length == 0:
            continue
        func += [(n_states, 0, n_states)]
        for _ in range(block_length):
            func += [(n_states, 1, n_states + 1)]
            n_states += 1

        func += [(n_states, 0, n_states + 1)]
        n_states += 1

    func += [(n_states, 0, n_states)]
    # Line can end with 0 or 1
    return func, [n_states - 1, n_states]

# Example usage
model, (board,) = nonogram(row_rules, col_rules)

# Solve
model.solve()

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


# Source description: https://www.csplib.org/Problems/prob013/

The problem is to timetable a party at a yacht club. Certain boats are to be designated hosts, and the crews of the remaining boats in turn visit the host boats for several successive half-hour periods. The crew of a host boat remains on board to act as hosts while the crew of a guest boat together visits several hosts. Every boat can only hold a limited number of people at a time (its capacity) and crew sizes are different. The total number of people aboard a boat, including the host crew and guest crews, must not exceed the capacity. A guest boat cannot not revisit a host and guest crews cannot meet more than once. The problem facing the rally organizer is that of minimizing the number of host boats.  Print the visit schedule (visits) and the host designations (is_host).

# Data
n_boats = 5  # Number of boats
n_periods = 4  # Number of periods
capacity = [6, 8, 12, 12, 12]  # Capacities of the boats
crew_size = [2, 2, 2, 2, 4]  # Crew sizes of the boats
# End of data

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

def progressive_party(n_boats, n_periods, capacity, crew_size, **kwargs):
    is_host = boolvar(shape=n_boats, name="is_host")
    visits = intvar(0, n_boats - 1, shape=(n_periods, n_boats), name="visits")

    model = Model()

    # Crews of host boats stay on boat
    for boat in range(n_boats):
        model += (is_host[boat]).implies((visits[:, boat] == boat).all())

    # Number of visitors can never exceed capacity of boat
    for slot in range(n_periods):
        for boat in range(n_boats):
            model += sum((visits[slot] == boat) * crew_size) + crew_size[boat] * is_host[boat] <= capacity[boat]

    # Guests cannot visit a boat twice
    for boat in range(n_boats):
        model += (~is_host[boat]).implies(AllDifferent(visits[:, boat]))

    # Non-host boats cannot be visited
    for boat in range(n_boats):
        model += (~is_host[boat]).implies((visits != boat).all())

    # Crews cannot meet more than once
    for c1, c2 in all_pairs(range(n_boats)):
        model += sum(visits[:, c1] == visits[:, c2]) <= 1

    # Minimize number of hosts needed
    model.minimize(sum(is_host))

    return model, (visits, is_host)

# Example usage
model, (visits, is_host) = progressive_party(n_boats, n_periods, capacity, crew_size)
model.solve()

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


# Source description: https://www.csplib.org/Problems/prob015/

The problem is to put \( n \) balls labelled \( 1, \ldots, n \) into 3 boxes so that for any triple of balls \( (x, y, z) \) with \( x + y = z \), not all are in the same box. This has a solution iff \( n < 14 \). The problem can be formulated as a 0-1 problem using the variables \( M_{ij} \) for \( i \in \{1, \ldots, n\}, j \in \{1, 2, 3\} \) with \( M_{ij} \) true if ball \( i \) is in box \( j \). The constraints are that a ball must be in exactly one box, \( M_{i1} + M_{i2} + M_{i3} = 1 \) for all \( i \in \{1, \ldots, n\} \). And for each \( x + y = z \) and \( j \in \{1, 2, 3\} \), not \( (M_{xj} \land M_{yj} \land M_{zj}) \). This converts to, \( (1 - M_{xj}) + (1 - M_{yj}) + (1 - M_{zj}) \geq 1 \) or, \( M_{xj} + M_{yj} + M_{zj} \leq 2 \).  Print the assignment of balls to boxes (balls) as a list of n integers from 1 to 3.

# Data
n = 13  # Number of balls
c = 3  # Number of boxes
# End of data

# Import libraries
import json
from cpmpy import *

def shur_lemma(n, c):
    # balls[i] = j iff ball i is in box j
    balls = intvar(1, c, shape=n, name="balls")

    model = Model()

    # Ensure each triple (x, y, z) with x + y = z are not in the same box
    for x in range(1, n):
        for y in range(1, n - x + 1):
            z = x + y
            if z <= n:
                model += (balls[x - 1] != balls[y - 1]) | \
                         (balls[x - 1] != balls[z - 1]) | \
                         (balls[y - 1] != balls[z - 1])

    return model, (balls,)

# Example usage
model, (balls,) = shur_lemma(n, c)
model.solve()

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


# Source description: https://www.csplib.org/Problems/prob019/

An order \( n \) magic square is a \( n \) by \( n \) matrix containing the numbers 1 to \( n^2 \), with each row, column, and main diagonal equal to the same sum. As well as finding magic squares, we are interested in the number of a given size that exist. There are several interesting variations. For example, we may insist on certain values in certain squares (like in quasigroup completion) and ask if the magic square can be completed. In a heterosquare, each row, column, and diagonal sums to a different value. In an anti-magic square, the row, column, and diagonal sums form a sequence of consecutive integers.  A magic sequence of length \( n \) is a sequence of integers \( x_0, \ldots, x_{n-1} \) between 0 and \( n-1 \), such that for all \( i \) in 0 to \( n-1 \), the number \( i \) occurs exactly \( x_i \) times in the sequence. For instance, 6, 2, 1, 0, 0, 0, 1, 0, 0, 0 is a magic sequence since 0 occurs 6 times in it, 1 occurs twice, etc.  Print the magic sequence (x).

# Data
n = 12  # Length of the magic sequence
# End of data

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

def magic_sequence(n):
    model = Model()

    x = intvar(0, n - 1, shape=n, name="x")

    # Constraints
    for i in range(n):
        model += x[i] == sum(x == i)

    # Speedup search
    model += sum(x) == n
    model += sum(x * np.arange(n)) == n

    return model, (x,)

# Example usage
model, (x,) = magic_sequence(n)
model.solve()

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


# Source description: https://www.csplib.org/Problems/prob021/

Crossfigures are the numerical equivalent of crosswords. You have a grid and some clues with numerical answers to place on this grid. Clues come in several different forms (for example: Across 1. 25 across times two, 2. five dozen, 5. a square number, 10. prime, 14. 29 across times 21 down ...).  Here is the specific problem:    1  2  3  4  5  6  7  8  9   ---------------------------   1  2  _  3  X  4  _  5  6    1   7  _  X  8  _  _  X  9  _    2   _  X  10 _  X  11 12 X  _    3   13 14 _  _  X  15 _  16 _    4   X  _  X  X  X  X  X  _  X    5   17 _  18 19 X  20 21 _ 22    6   _  X  23 _  X  24 _  X  _    7   25 26 X  27 _  _  X  28 _    8   29 _  _  _  X  30 _  _  _    9  Here are the clues:      #  Across     #  1 27 across times two     #  4 4 down plus seventy-one     #  7 18 down plus four     #  8 6 down divided by sixteen     #  9 2 down minus eighteen     # 10 Dozen in six gross     # 11 5 down minus seventy     # 13 26 down times 23 across     # 15 6 down minus 350     # 17 25 across times 23 across     # 20 A square number     # 23 A prime number     # 24 A square number     # 25 20 across divided by seventeen     # 27 6 down divided by four     # 28 Four dozen     # 29 Seven gross     # 30 22 down plus 450      # Down     #     #  1 1 across plus twenty-seven     #  2 Five dozen     #  3 30 across plus 888     #  4 Two times 17 across     #  5 29 across divided by twelve     #  6 28 across times 23 across     # 10 10 across plus four     # 12 Three times 24 across     # 14 13 across divided by sixteen     # 16 28 down times fifteen     # 17 13 across minus 399     # 18 29 across divided by eighteen     # 19 22 down minus ninety-four     # 20 20 across minus nine     # 21 25 across minus fifty-two     # 22 20 down times six     # 26 Five times 24 across     # 28 21 down plus twenty-seven   Print the solved crossfigure as a list of lists of integers (M).

# Import libraries
import math
from cpmpy import *
import json

def member_of(x, val):
    """
    member_of(x, val)

    Ensures that the value `val` is in the array `x`.
    """
    n = len(x)
    # cc = intvar(0,n)
    # constraints = [count(x, val, cc), cc >  0]
    constraints = [sum([x[i] == val for i in range(n)]) > 0]
    return constraints

def is_prime(n):
    """
    is_prime(n)

    Returns True if the number n is a prime number, otherwise return False.
    """
    if n < 2: return False
    if n == 2: return True
    if not n & 1:
        return False
    for i in range(3, 1 + int(math.sqrt(n)), 2):
        if n % i == 0:
            return False
    return True

def to_num(a, n, base):
    """
    to_num(a, n, base)

    Ensure that the digits in array `a` corresponds to the number `n` in base `base`.
    """
    tlen = len(a)
    return n == sum([(base ** (tlen - i - 1)) * a[i] for i in range(tlen)])

#
# across(Matrix, Across, Len, Row, Col)
#        Constrains 'Across' to be equal to the number represented by the
#        'Len' digits starting at position (Row, Col) of the array 'Matrix'
#        and proceeding across.
#
def across(Matrix, Across, Len, Row, Col):
    Row -= 1
    Col -= 1
    tmp = intvar(0, 9999, shape=Len)
    constraints = []
    constraints += [to_num(tmp, Across, 10)]
    for i in range(Len):
        constraints += [Matrix[Row, Col + i] == tmp[i]]
    return constraints

#
# down(Matrix, Down, Len, Row, Col):
#	Constrains 'Down' to be equal to the number represented by the
#	'Len' digits starting at position (Row, Col) of the array 'Matrix'
#	and proceeding down.
#
def down(Matrix, Down, Len, Row, Col):
    Row -= 1
    Col -= 1
    tmp = intvar(0, 9999, shape=Len)
    constraints = []
    constraints += [to_num(tmp, Down, 10)]
    for i in range(Len):
        constraints += [Matrix[Row + i, Col] == tmp[i]]
    return constraints

model = Model()
n = 9

D = 9999  # the max length of the numbers in this problem is 4
primes = [i for i in range(2, D + 1) if is_prime(i)]
squares = [i ** 2 for i in range(1, 1 + math.ceil(math.sqrt(D)))]

Z = -1
B = -2  # Black box
# The valid squares (or rather the invalid are marked as B)
Valid = [[Z, Z, Z, Z, B, Z, Z, Z, Z],
         [Z, Z, B, Z, Z, Z, B, Z, Z],
         [Z, B, Z, Z, B, Z, Z, B, Z],
         [Z, Z, Z, Z, B, Z, Z, Z, Z],
         [B, Z, B, B, B, B, B, Z, B],
         [Z, Z, Z, Z, B, Z, Z, Z, Z],
         [Z, B, Z, Z, B, Z, Z, B, Z],
         [Z, Z, B, Z, Z, Z, B, Z, Z],
         [Z, Z, Z, Z, B, Z, Z, Z, Z]]

M = intvar(0, 9, shape=(n, n), name="M")  # The matrix

for i in range(n):
    for j in range(n):
        if Valid[i][j] == B:
            model += (M[i, j] == 0)

A1 = intvar(0, D, name="A1")
A4 = intvar(0, D, name="A4")
A7 = intvar(0, D, name="A7")
A8 = intvar(0, D, name="A8")
A9 = intvar(0, D, name="A9")
A10 = intvar(0, D, name="A10")
A11 = intvar(0, D, name="A11")
A13 = intvar(0, D, name="A13")
A15 = intvar(0, D, name="A15")
A17 = intvar(0, D, name="A17")
A20 = intvar(0, D, name="A20")
A23 = intvar(0, D, name="A23")
A24 = intvar(0, D, name="A24")
A25 = intvar(0, D, name="A25")
A27 = intvar(0, D, name="A27")
A28 = intvar(0, D, name="A28")
A29 = intvar(0, D, name="A29")
A30 = intvar(0, D, name="A30")

AList = [A1, A4, A7, A8, A9, A10, A11, A13, A15, A17, A20, A23, A24, A25, A27, A28, A29, A30]

D1 = intvar(0, D, name="D1")
D2 = intvar(0, D, name="D2")
D3 = intvar(0, D, name="D3")
D4 = intvar(0, D, name="D4")
D5 = intvar(0, D, name="D5")
D6 = intvar(0, D, name="D6")
D10 = intvar(0, D, name="D10")
D12 = intvar(0, D, name="D12")
D14 = intvar(0, D, name="D14")
D16 = intvar(0, D, name="D16")
D17 = intvar(0, D, name="D17")
D18 = intvar(0, D, name="D18")
D19 = intvar(0, D, name="D19")
D20 = intvar(0, D, name="D20")
D21 = intvar(0, D, name="D21")
D22 = intvar(0, D, name="D22")
D26 = intvar(0, D, name="D26")
D28 = intvar(0, D, name="D28")
DList = [D1, D2, D3, D4, D5, D6, D10, D12, D14, D17, D18, D19, D20, D21, D22, D26, D28]

# Set up the constraints between the matrix elements and the
# clue numbers.
#
# Note: Row/Col are adjusted to base-0 in the
#       across and down methods.
#
model += (across(M, A1, 4, 1, 1))
model += (across(M, A4, 4, 1, 6))
model += (across(M, A7, 2, 2, 1))
model += (across(M, A8, 3, 2, 4))
model += (across(M, A9, 2, 2, 8))
model += (across(M, A10, 2, 3, 3))
model += (across(M, A11, 2, 3, 6))
model += (across(M, A13, 4, 4, 1))
model += (across(M, A15, 4, 4, 6))
model += (across(M, A17, 4, 6, 1))
model += (across(M, A20, 4, 6, 6))
model += (across(M, A23, 2, 7, 3))
model += (across(M, A24, 2, 7, 6))
model += (across(M, A25, 2, 8, 1))
model += (across(M, A27, 3, 8, 4))
model += (across(M, A28, 2, 8, 8))
model += (across(M, A29, 4, 9, 1))
model += (across(M, A30, 4, 9, 6))

model += (down(M, D1, 4, 1, 1))
model += (down(M, D2, 2, 1, 2))
model += (down(M, D3, 4, 1, 4))
model += (down(M, D4, 4, 1, 6))
model += (down(M, D5, 2, 1, 8))
model += (down(M, D6, 4, 1, 9))
model += (down(M, D10, 2, 3, 3))
model += (down(M, D12, 2, 3, 7))
model += (down(M, D14, 3, 4, 2))
model += (down(M, D16, 3, 4, 8))
model += (down(M, D17, 4, 6, 1))
model += (down(M, D18, 2, 6, 3))
model += (down(M, D19, 4, 6, 4))
model += (down(M, D20, 4, 6, 6))
model += (down(M, D21, 2, 6, 7))
model += (down(M, D22, 4, 6, 9))
model += (down(M, D26, 2, 8, 2))
model += (down(M, D28, 2, 8, 8))

# Set up the clue constraints.
#  Across
#  1 27 across times two
#  4 4 down plus seventy-one
#  7 18 down plus four
#  8 6 down divided by sixteen
#  9 2 down minus eighteen
# 10 Dozen in six gross
# 11 5 down minus seventy
# 13 26 down times 23 across
# 15 6 down minus 350
# 17 25 across times 23 across
# 20 A square number
# 23 A prime number
# 24 A square number
# 25 20 across divided by seventeen
# 27 6 down divided by four
# 28 Four dozen
# 29 Seven gross
# 30 22 down plus 450

model += (A1 == 2 * A27)
model += (A4 == D4 + 71)
model += (A7 == D18 + 4)
# model += (A8 == D6 / 16)
model += (16 * A8 == D6)
model += (A9 == D2 - 18)
# model += (A10 == 6 * 144 / 12)
model += (12 * A10 == 6 * 144)
model += (A11 == D5 - 70)
model += (A13 == D26 * A23)
model += (A15 == D6 - 350)
model += (A17 == A25 * A23)
# model += (square(A20))
model += (member_of(squares, A20))
# model += (is_prime(A23))
model += (member_of(primes, A23))
# model += (square(A24))
model += (member_of(squares, A24))
# model += (A25 == A20 / 17)
model += (17 * A25 == A20)
# model += (A27 == D6 / 4)
model += (4 * A27 == D6)
model += (A28 == 4 * 12)
model += (A29 == 7 * 144)
model += (A30 == D22 + 450)

# Down
#
#  1 1 across plus twenty-seven
#  2 Five dozen
#  3 30 across plus 888
#  4 Two times 17 across
#  5 29 across divided by twelve
#  6 28 across times 23 across
# 10 10 across plus four
# 12 Three times 24 across
# 14 13 across divided by sixteen
# 16 28 down times fifteen
# 17 13 across minus 399
# 18 29 across divided by eighteen
# 19 22 down minus ninety-four
# 20 20 across minus nine
# 21 25 across minus fifty-two
# 22 20 down times six
# 26 Five times 24 across
# 28 21 down plus twenty-seven

model += (D1 == A1 + 27)
model += (D2 == 5 * 12)
model += (D3 == A30 + 888)
model += (D4 == 2 * A17)
# model += (D5 == A29 / 12)
model += (12 * D5 == A29)
model += (D6 == A28 * A23)
model += (D10 == A10 + 4)
model += (D12 == A24 * 3)
# model += (D14 == A13 / 16)
model += (16 * D14 == A13)
model += (D16 == 15 * D28)
model += (D17 == A13 - 399)
# model += (D18 == A29 / 18)
model += (18 * D18 == A29)
model += (D19 == D22 - 94)
model += (D20 == A20 - 9)
model += (D21 == A25 - 52)
model += (D22 == 6 * D20)
model += (D26 == 5 * A24)
model += (D28 == D21 + 27)

model.solve()

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


# Source description: https://www.csplib.org/Problems/prob024/

Consider two sets of the numbers from 1 to 4. The problem is to arrange the eight numbers in the two sets into a single sequence in which the two 1’s appear one number apart, the two 2’s appear two numbers apart, the two 3’s appear three numbers apart, and the two 4’s appear four numbers apart.  The problem generalizes to the L(k, n) problem, which is to arrange k sets of numbers 1 to n, so that each appearance of the number m is m numbers on from the last. For example, the L(3, 9) problem is to arrange 3 sets of the numbers 1 to 9 so that the first two 1’s and the second two 1’s appear one number apart, the first two 2’s and the second two 2’s appear two numbers apart, etc.  Print the positions (position) and the solution sequence (solution).

# Data
k = 4  # Number of sets
# End of data

# Import libraries
import json
from cpmpy import *

def langford(k):
    model = Model()

    if not (k % 4 == 0 or k % 4 == 3):
        print("There is no solution for K unless K mod 4 == 0 or K mod 4 == 3")
        return None, None

    # Variables
    position = intvar(0, 2 * k - 1, shape=2 * k, name="position")
    solution = intvar(1, k, shape=2 * k, name="solution")

    # Constraints
    model += [AllDifferent(position)]

    for i in range(1, k + 1):
        model += [position[i + k - 1] == position[i - 1] + i + 1]
        model += [i == solution[position[i - 1]]]
        model += [i == solution[position[k + i - 1]]]

    # Symmetry breaking
    model += [solution[0] < solution[2 * k - 1]]

    return model, (position, solution)

# Example usage
model, (position, solution) = langford(k)
if model:
    model.solve()

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


# Source description: https://www.csplib.org/Problems/prob026/

The problem is to schedule a tournament of \( n \) teams over \( n-1 \) weeks, with each week divided into \( n/2 \) periods, and each period divided into two slots. The first team in each slot plays at home, whilst the second plays the first team away. A tournament must satisfy the following three constraints: every team plays once a week; every team plays at most twice in the same period over the tournament; every team plays every other team.  An example schedule for 8 teams is:  | Week 1  | Week 2  | Week 3  | Week 4  | Week 5  | Week 6  | Week 7  | |---------|---------|---------|---------|---------|---------|---------| | 0 v 1   | 0 v 2   | 4 v 7   | 3 v 6   | 3 v 7   | 1 v 5   | 2 v 4   | | 2 v 3   | 1 v 7   | 0 v 3   | 5 v 7   | 1 v 4   | 0 v 6   | 5 v 6   | | 4 v 5   | 3 v 5   | 1 v 6   | 0 v 4   | 2 v 6   | 2 v 7   | 0 v 7   | | 6 v 7   | 4 v 6   | 2 v 5   | 1 v 2   | 0 v 5   | 3 v 4   | 1 v 3   |  One extension of the problem is to double round robin tournaments in which each team plays every other team (as before) but now both at home and away. This is often solved by repeating the round robin pattern, but swapping home games for away games in the repeat.  Print the home and away schedules (home, away).

# Data
n_teams = 8  # Number of teams
# End of data

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

def sport_scheduling(n_teams):
    n_weeks, n_periods, n_matches = n_teams - 1, n_teams // 2, (n_teams - 1) * n_teams // 2

    home = intvar(1, n_teams, shape=(n_weeks, n_periods), name="home")
    away = intvar(1, n_teams, shape=(n_weeks, n_periods), name="away")

    model = Model()

    # teams cannot play each other
    model += home != away

    # every teams plays once a week
    # can be written cleaner, see issue #117
    # model += AllDifferent(np.append(home, away, axis=1), axis=0)
    for w in range(n_weeks):
        model += AllDifferent(np.append(home[w], away[w]))

    # every team plays each other
    for t1, t2 in all_pairs(range(1, n_teams + 1)):
        model += (sum((home == t1) & (away == t2)) + sum((home == t2) & (away == t1))) >= 1

    # every team plays at most twice in the same period
    for t in range(1, n_teams + 1):
        # can be written cleaner, see issue #117
        # sum((home == t) | (away == t), axis=1) <= 2
        for p in range(n_periods):
            model += sum((home[p] == t) | (away[p] == t)) <= 2

    return model, (home, away)

# Example usage
model, (home, away) = sport_scheduling(n_teams)
model.solve()

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


# Source description: https://www.csplib.org/Problems/prob028/

Balanced Incomplete Block Design (BIBD) generation is a standard combinatorial problem from design theory, originally used in the design of statistical experiments but since finding other applications such as cryptography. It is a special case of Block Design, which also includes Latin Square problems.  BIBD generation is described in most standard textbooks on combinatorics. A BIBD is defined as an arrangement of \( v \) distinct objects into \( b \) blocks such that each block contains exactly \( k \) distinct objects, each object occurs in exactly \( r \) different blocks, and every two distinct objects occur together in exactly \( \lambda \) blocks. Another way of defining a BIBD is in terms of its incidence matrix, which is a \( v \) by \( b \) binary matrix with exactly \( r \) ones per row, \( k \) ones per column, and with a scalar product of \( \lambda \) between any pair of distinct rows. A BIBD is therefore specified by its parameters \( (v, b, r, k, \lambda) \). An example of a solution for \( (7, 7, 3, 3, 1) \) is:  0 1 1 0 0 1 0 1 0 1 0 1 0 0 0 0 1 1 0 0 1 1 1 0 0 0 0 1 0 0 0 0 1 1 1 1 0 0 1 0 1 0 0 1 0 1 1 0 0  Lam’s problem is that of finding a BIBD with parameters \( (111, 111, 11, 11, 1) \).  Print the incidence matrix of the BIBD (matrix).

# Data
v = 9  # Number of distinct objects
b = 12  # Number of blocks
r = 4  # Number of blocks each object occurs in
k = 3  # Number of objects each block contains
l = 1  # Number of blocks in which each pair of distinct objects occurs together
# End of data

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

def bibd(v, b, r, k, l):
    matrix = boolvar(shape=(v, b), name="matrix")

    model = Model()

    # Every row adds up to r
    model += [sum(row) == r for row in matrix]
    # Every column adds up to k
    model += [sum(col) == k for col in matrix.T]

    # The scalar product of every pair of columns adds up to l
    model += [np.dot(row_i, row_j) == l for row_i, row_j in all_pairs(matrix)]

    # Break symmetry
    # Lexicographic ordering of rows
    for r in range(v - 1):
        bvar = boolvar(shape=(b + 1))
        model += bvar[0] == 1
        model += bvar == ((matrix[r] <= matrix[r + 1]) &
                          ((matrix[r] < matrix[r + 1]) | (bvar[1:] == 1)))
        model += bvar[-1] == 0
    # Lexicographic ordering of columns
    for c in range(b - 1):
        bvar = boolvar(shape=(v + 1))
        model += bvar[0] == 1
        model += bvar == ((matrix.T[c] <= matrix.T[c + 1]) &
                          ((matrix.T[c] < matrix.T[c + 1]) | (bvar[1:] == 1)))
        model += bvar[-1] == 0

    return model, (matrix,)

# Example usage
model, (matrix,) = bibd(v, b, r, k, l)
model.solve()

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


# Source description: https://www.csplib.org/Problems/prob033/

This problem has its roots in Bioinformatics and Coding Theory.  Problem: find as large a set \( S \) of strings (words) of length 8 over the alphabet \( W = \{ A,C,G,T \} \) with the following properties:  - Each word in \( S \) has 4 symbols from \{ C,G \}; - Each pair of distinct words in \( S \) differ in at least 4 positions; and - Each pair of words \( x \) and \( y \) in \( S \) (where \( x \) and \( y \) may be identical) are such that \( x^R \) and \( y^C \) differ in at least 4 positions. Here, \( ( x_1,\ldots,x_8 )^R = ( x_8,\ldots,x_1 ) \) is the reverse of \( ( x_1,\ldots,x_8 ) \) and \( ( y_1,\ldots,y_8 )^C \) is the Watson-Crick complement of \( ( y_1,\ldots,y_8 ) \), i.e. the word where each \( A \) is replaced by a \( T \) and vice versa and each \( C \) is replaced by a \( G \) and vice versa.  Print the set of words (words).

# Data
n = 8  # Number of words to find
# End of data

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

def word_design(n=2):
    A, C, G, T = 1, 2, 3, 4

    # words[i,j] is the j'th letter of the i'th word
    words = intvar(A, T, shape=(n, 8), name="words")

    model = Model()

    # 4 symbols from {C,G}
    for w in words:
        model += sum((w == C) | (w == G)) >= 4

    # Each pair of distinct words differ in at least 4 positions
    for x, y in all_pairs(words):
        model += (sum(x != y) >= 4)

    # Each pair of words x and y (where x and y may be identical)
    # are such that x^R and y^C differ in at least 4 positions
    for y in words:
        y_c = 5 - y  # Watson-Crick complement
        for x in words:
            x_r = x[::-1]  # reversed x
            model += sum(x_r != y_c) >= 4

    # Break symmetry
    for r in range(n - 1):
        b = boolvar(shape=(9,))
        model += b[0] == 1
        model += b == ((words[r] <= words[r + 1]) &
                       ((words[r] < words[r + 1]) | (b[1:] == 1)))
        model += b[-1] == 0

    return model, (words,)

# Example usage
model, (words,) = word_design(n)
model.solve()

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


# Source description: https://www.csplib.org/Problems/prob044/

The ternary Steiner problem of order \( n \) consists of finding a set of \( n \cdot (n-1)/6 \) triples of distinct integer elements in \(\{1, \ldots, n\}\) such that any two triples have at most one common element. It is a hypergraph problem coming from combinatorial mathematics [luneburg1989tools] where \( n \) modulo 6 has to be equal to 1 or 3 [lindner2011topics]. One possible solution for \( n = 7 \) is \(\{\{1, 2, 3\}, \{1, 4, 5\}, \{1, 6, 7\}, \{2, 4, 6\}, \{2, 5, 7\}, \{3, 4, 7\}, \{3, 5, 6\}\}\). The solution contains \( 7 \cdot (7-1)/6 = 7 \) triples.  This is a particular case of the more general Steiner system.  More generally still, you may refer to Balanced Incomplete Block Designs. In fact, a Steiner Triple System with \( n \) elements is a BIBD(\( n, n \cdot (n-1)/6, (n-1)/2, 3, 1 \)).  Print the set of triples (sets).

# Data
n = 9  # Order of the Steiner Triple System
# End of data

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

def steiner(n=15):
    assert n % 6 == 1 or n % 6 == 3, "N must be (1|3) modulo 6"

    n_sets = int(n * (n - 1) // 6)

    model = Model()

    # boolean representation of sets
    # sets[i,j] = true iff item j is part of set i
    sets = boolvar(shape=(n_sets, n), name="sets")

    # cardinality of set if 3
    # can be written cleaner, see issue #117
    # model += sum(sets, axis=0) == 3
    model += [sum(s) == 3 for s in sets]

    # cardinality of intersection <= 1
    for s1, s2 in all_pairs(sets):
        model += sum(s1 & s2) <= 1

    # symmetry breaking
    model += (sets[(0, 0)] == 1)

    return model, (sets,)

# Example usage
model, (sets,) = steiner(n)
model.solve()

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


# Source description: https://www.csplib.org/Problems/prob049/

This problem consists in finding a partition of numbers 1..N into two sets A and B such that:  - A and B have the same cardinality - Sum of numbers in A = sum of numbers in B - Sum of squares of numbers in A = sum of squares of numbers in B  There is no solution for \( N < 8 \).  Here is an example for \( N = 8 \): A = (1, 4, 6, 7) and B = (2, 3, 5, 8)  From \( N \geq 8 \), there is no solution if \( N \) is not a multiple of 4.  Print the sets A and B (A, B).

# Data
n = 12  # The number N
# End of data

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

def number_partitioning(n=8):
    assert n % 2 == 0, "The value of n must be even"

    # x[i] is the ith value of the first set
    x = intvar(1, n, shape=n // 2)

    # y[i] is the ith value of the second set
    y = intvar(1, n, shape=n // 2)

    model = Model()

    model += AllDifferent(np.append(x, y))

    # sum of numbers is equal in both sets
    model += sum(x) == sum(y)

    # sum of squares is equal in both sets
    model += sum(x ** 2) == sum(y ** 2)

    # break symmetry
    model += x[:-1] <= x[1:]
    model += y[:-1] <= x[1:]

    return model, (x,y)

# Example usage
model, (x, y) = number_partitioning(n)
model.solve()

A = x
B = y

# Print
solution = {"A": x.value().tolist(), "B": y.value().tolist()}
print(json.dumps(solution))
# End of CPMPy script


# Source description: https://www.csplib.org/Problems/prob050/

Given a simple undirected graph \( G = (V, E) \), where \( V \) is the set of vertices and \( E \) the set of undirected edges, the edge \(\{u, v\}\) is in \( E \) if and only if vertex \( u \) is adjacent to vertex \( v \in G \). The graph is simple in that there are no loop edges, i.e., we have no edges of the form \(\{v, v\}\). Each vertex \( v \in V \) has a degree \( d_v \) i.e., the number of edges incident on that vertex. Consequently, a graph has a degree sequence \( d_1, \ldots, d_n \), where \( d_i \geq d_{i+1} \). A diamond is a set of four vertices in \( V \) such that there are at least five edges between those vertices. Conversely, a graph is diamond-free if it has no diamond as an induced subgraph, i.e., for every set of four vertices the number of edges between those vertices is at most four.  In our problem, we have additional properties required of the degree sequences of the graphs, in particular, that the degree of each vertex is greater than zero (i.e., isolated vertices are disallowed), the degree of each vertex is modulo 3, and the sum of the degrees is modulo 12 (i.e., \(|E|\) is modulo 6).  The problem is then for a given value of \( n \), produce all unique degree sequences \( d_1, \ldots, d_n \) such that  - \( d_i \geq d_{i+1} \) - Each degree \( d_i > 0 \) and \( d_i \) is modulo 3 - The sum of the degrees is modulo 12 - There exists a simple diamond-free graph with that degree sequence  Print the adjacency matrix of the graph (matrix).

# Data
N = 10  # Number of vertices in the graph
# End of data

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

def diamond_free(N=10):
    # By definition a and b will have the same cardinality:
    matrix = boolvar(shape=(N, N), name="matrix")

    model = Model()

    # No rows contain just zeroes.
    model += [sum(row) > 0 for row in matrix] # can be written cleaner, see issue #117
    # Every row has a sum modulo 3.
    model += [sum(row) % 3 == 0 for row in matrix]
    # The sum of the matrix is modulo 12.
    model += sum(matrix) % 12 == 0
    # No row R contains a 1 in its Rth column.
    model += [matrix[np.diag_indices(N)] == 0]

    # Every grouping of 4 rows can have at most a sum of 4 between them.
    for a, b, c, d in combinations(range(N), 4):
        model += sum([matrix[a][b], matrix[a][c], matrix[a][d],
                      matrix[b][c], matrix[b][d], matrix[c][d]]) <= 4

    # Undirected graph
    model += matrix == matrix.T

    # Symmetry breaking
    # lexicographic ordering of rows
    for r in range(N - 1):
        b = boolvar(N + 1)
        model += b[0] == 1
        model += b == ((matrix[r] <= matrix[r + 1]) &
                       ((matrix[r] < matrix[r + 1]) | b[1:] == 1))
        model += b[-1] == 0
    # lexicographic ordering of cols
    for c in range(N - 1):
        b = boolvar(N + 1)
        model += b[0] == 1
        model += b == ((matrix.T[c] <= matrix.T[c + 1]) &
                       ((matrix.T[c] < matrix.T[c + 1]) | b[1:] == 1))
        model += b[-1] == 0

    return model, matrix

# Example usage
model, matrix = diamond_free(N)
model.solve()

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


# Source description: https://www.csplib.org/Problems/prob053/

A labelling \( f \) of the nodes of a graph with \( q \) edges is graceful if \( f \) assigns each node a unique label from \( \{0, 1, \ldots, q\} \) and when each edge \( xy \) is labelled with \( |f(x) - f(y)| \), the edge labels are all different. Gallian surveys graceful graphs, i.e., graphs with a graceful labelling, and lists the graphs whose status is known.  All-Interval Series is a special case of a graceful graph where the graph is a line.  Print the node labels and edge labels (nodes, edges).

# Data
m = 16  # Number of edges in the graph
n = 8  # Number of nodes in the graph
graph = [[0, 1], [0, 2], [0, 3], [1, 2], [1, 3], [2, 3],
         [4, 5], [4, 6], [4, 7], [5, 6], [5, 7], [6, 7],
         [0, 4], [1, 5], [2, 6], [3, 7]]  # Edges of the graph
# End of data

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

def graceful_graphs(m, n, graph):
    graph = np.array(graph)

    model = Model()

    # variables
    nodes = intvar(0, m, shape=n, name="nodes")
    edges = intvar(1, m, shape=m, name="edges")

    # constraints
    model += np.abs(nodes[graph[:, 0]] - nodes[graph[:, 1]]) == edges

    model += (AllDifferent(edges))
    model += (AllDifferent(nodes))

    return model, (nodes, edges)

# Example usage
model, (nodes, edges) = graceful_graphs(m, n, graph)
model.solve()

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


# Source description: https://www.csplib.org/Problems/prob054/

Can \( n \) queens (of the same color) be placed on a \( n \times n \) chessboard so that none of the queens can attack each other? In chess, a queen attacks other squares on the same row, column, or either diagonal as itself. So the \( n \)-queens problem is to find a set of \( n \) locations on a chessboard, no two of which are on the same row, column or diagonal.  A simple arithmetical observation may be helpful in understanding models. Suppose a queen is represented by an ordered pair \((\alpha, \beta)\), the value \(\alpha\) represents the queen’s column, and \(\beta\) its row on the chessboard. Then two queens do not attack each other iff they have different values of all of \(\alpha\), \(\beta\), \(\alpha - \beta\), and \(\alpha + \beta\). It may not be intuitively obvious that chessboard diagonals correspond to sums and differences, but consider moving one square along the two orthogonal diagonals: in one direction the sum of the coordinates does not change, while in the other direction the difference does not change. (We do not suggest that pairs \((\alpha, \beta)\) is a good representation for solving.)  The problem has inherent symmetry. That is, for any solution we obtain another solution by any of the 8 symmetries of the chessboard (including the identity) obtained by combinations of rotations by 90 degrees and reflections.  Print the positions of the queens on the chessboard (queens).

# Data
n = 10  # Size of the chessboard and number of queens
# End of data

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

def n_queens(n=8):

    queens = intvar(1, n, shape=n, name="queens")

    # Constraints on columns and left/right diagonal
    model = Model([
        AllDifferent(queens),
        AllDifferent(queens - np.arange(n)),
        AllDifferent(queens + np.arange(n)),
    ])

    return model, (queens,)

# Example usage
model, (queens,) = n_queens(n)
model.solve()

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


# Source description: https://www.csplib.org/Problems/prob076/

A Costas array is a pattern of \( n \) marks on an \( n \times n \) grid, one mark per row and one per column, in which the \( n \cdot (n-1)/2 \) vectors between the marks are all different.  Such patterns are important as they provide a template for generating radar and sonar signals with ideal ambiguity functions.  A model for Costas Array Problem (CAP) is to define an array of variables \( X_1, \ldots, X_n \) which form a permutation. For each length \( l \in \{1, \ldots, n-1\} \), we add \( n-l \) more variables \( X_{l1}, \ldots, X_{ln-1} \), whereby each of these variables is assigned the difference of \( X_i - X_{i+l} \) for \( i \in \{1, \ldots, n-l\} \). These additional variables form a difference triangle.  Each line of this difference triangle must not contain any value twice. That is, the CAP is simply a collection of all-different constraints on \( X_1, \ldots, X_n \) and \( X_{l1}, \ldots, X_{ln-l} \) for all \( l \in \{1, \ldots, n-1\} \).  Print the Costas array (costas).

# Data
n = 8  # Size of the Costas array
# End of data

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

def costas_array(n=6):
    model = Model()

    # Declare variables
    costas = intvar(1, n, shape=n, name="costas")
    differences = intvar(-n + 1, n - 1, shape=(n, n), name="differences")

    tril_idx = np.tril_indices(n, -1)
    triu_idx = np.triu_indices(n, 1)

    # Constraints
    # Fix the values in the lower triangle in the difference matrix to -n+1.
    model += differences[tril_idx] == -n + 1

    model += [AllDifferent(costas)]

    # Define the differences
    for i, j in zip(*triu_idx):
        model += [differences[i, j] == costas[j] - costas[j - i - 1]]

    # All entries in a particular row of the difference triangle must be distinct
    for i in range(n - 2):
        model += [AllDifferent([differences[i, j] for j in range(n) if j > i])]

    # Additional constraints to speed up the search
    model += differences[triu_idx] != 0
    for k, l in zip(*triu_idx):
        if k < 2 or l < 2:
            continue
        model += [differences[k - 2, l - 1] + differences[k, l] ==
                  differences[k - 1, l - 1] + differences[k - 1, l]]

    return model, (costas, differences)

# Example usage
model, (costas, differences) = costas_array(n)
model.solve()

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


# Source description: https://www.csplib.org/Problems/prob084/

For every odd positive integer \( \ell \) (and \( m = \frac{\ell - 1}{2} \)) we define the 2cc Hadamard matrix Legendre pairs CSP using the \{V, D, C\} format (Variables, Domains, Constraints) as follows:  - \( V = \{a_1, \ldots, a_\ell, b_1, \ldots, b_\ell\} \), a set of \( 2 \cdot \ell \) variables - \( D = \{D_{a_1}, \ldots, D_{a_\ell}, D_{b_1}, \ldots, D_{b_\ell}\} \), a set of \( 2 \cdot \ell \) domains, all of them equal to \{-1, +1\} - \( C = \{c_1, \ldots, c_m, c_{m+1}, c_{m+2}\} \), a set of \( m+2 \) constraints (\( m \) quadratic constraints and 2 linear constraints)  The \( m \) quadratic constraints are given by: \[ c_s := \text{PAF}(A, s) + \text{PAF}(B, s) = -2, \forall s = 1, \ldots, m \] where PAF denotes the periodic autocorrelation function: (\(i + s\) is taken mod \( \ell \) when it exceeds \( \ell \)) \[ A = [a_1, \ldots, a_\ell], \, \text{PAF}(A, s) = \sum_{i=1}^\ell a_i a_{i+s} \] \[ B = [b_1, \ldots, b_\ell], \, \text{PAF}(B, s) = \sum_{i=1}^\ell b_i b_{i+s} \]  The 2 linear constraints are given by: \[ c_{m+1} := a_1 + \ldots + a_\ell = 1 \] \[ c_{m+2} := b_1 + \ldots + b_\ell = 1 \]  The 2cc Hadamard matrix Legendre pairs CSP for all odd \( \ell = 3, \ldots, 99 \) are given in http://www.cargo.wlu.ca/CSP_2cc_Hadamard/ (and in the data section). There are 49 CSPs. All of them are known to have solutions.  It is conjectured that the 2cc Hadamard matrix Legendre pairs CSP has solutions, for every odd \( \ell \), and this is linked to the famous Hadamard conjecture [Kotsireas].  Print the variables a and b (a, b).

# Data
l = 9  # Value of l (must be an odd positive integer)
# End of data

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

def PAF(arr, s):
    return sum(arr * np.roll(arr,-s))

def hadamard_matrix(l=5):

    m = int((l - 1) / 2)

    a = intvar(-1,1, shape=l, name="a")
    b = intvar(-1,1, shape=l, name="b")

    model = Model()

    model += a != 0 # exclude 0 from dom
    model += b != 0 # exclude 0 from dom

    model += sum(a) == 1
    model += sum(b) == 1

    for s in range(1,m+1):
        model += (PAF(a,s) + PAF(b,s)) == -2

    return model, (a,b)

# Example usage
model, (a, b) = hadamard_matrix(l)
model.solve()

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


If 100 bushels of corn were distributed among 100 people such that each man received three bushels, each woman two, and each child half a bushel, and there are five times as many women as men, find the number of men, women, and children.  Print the number of men, women, and children (men, women, children).

# Import libraries
from cpmpy import *
import json

# Decision variables
men = intvar(0, 100, name="men")
women = intvar(0, 100, name="women")
children = intvar(0, 100, name="children")

# Model
model = Model([
    men + women + children == 100,  # Total number of people
    men * 6 + women * 4 + children == 200,  # Total bushels of corn
    men * 5 == women  # Five times as many women as men
])

# Solve the model
model.solve()

# Print the solution
solution = {
    "men": men.value(),
    "women": women.value(),
    "children": children.value()
}
print(json.dumps(solution))
# End of CPMPy script


Enter the digits 1 through 8 in the circles and squares such that the number in each square is equal to the sum of the numbers in the adjoining circles. ...   C F C   F   F   C F C '''  Print the values for each position (positions).

# Import libraries
from cpmpy import *
import json

# Parameters
n = 8  # Number of digits

# Decision variables
positions = intvar(1, n, shape=n, name="positions")
a, b, c, d, e, f, g, h = positions

# Model
model = Model([
    AllDifferent(positions),
    b == a + c,
    d == a + f,
    e == c + h,
    g == f + h
])

# Solve the model
model.solve()

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


# Source description: http://blog.athico.com/2007/08/drools-puzzle-round-1-ages-of-sons.html

An old man asked a mathematician to guess the ages of his three sons.  Old man said: "The product of their ages is 36." Mathematician said: "I need more information."  Old man said:"Over there you can see a building. The sum of their ages equals the number of the windows in that building." After a short while the mathematician said: "I need more information."  Old man said: "The oldest son has blue eyes." Mathematician said: "I got it."  What are the ages of the three sons of the old man?  Print the ages of the sons (A1, A2, A3) starting from the oldest.

# Import libraries
from cpmpy import *
import json

A1 = intvar(0, 36, name="A1")  # oldest son
A2 = intvar(0, 36, name="A2")
A3 = intvar(0, 36, name="A3")

B1 = intvar(0, 36, name="B1")
B2 = intvar(0, 36, name="B2")
B3 = intvar(0, 36, name="B3")
AS = intvar(0, 1000, name="AS")
BS = intvar(0, 1000, name="BS")

model = Model([A1 > A2,
               A2 >= A3,
               36 == A1 * A2 * A3,

               B1 >= B2,
               B2 >= B3,
               A1 != B1,

               36 == B1 * B2 * B3,
               AS == A1 + A2 + A3,
               BS == B1 + B2 + B3,
               AS == BS
               ])

# Solve
model.solve()

# Print the solution
solution = {"A1": A1.value(), "A2": A2.value(), "A3": A3.value()}
print(json.dumps(solution))
# End of CPMPy script


# Source description: https://enigmaticcode.wordpress.com/2015/06/20/enigma-1224-age-changing/

If you start with my age, in years, and apply the four operations:  [    +2  /8     -3  *7  ]  in some order, then the final answer you get is my husband's age.  Funnily enough, if you start with his age and apply the same four operations in a different order, then you get my age.  What are our two ages?  Print my age and my husband's age (m, h).

# Import libraries
from cpmpy import *
import json

def check(perm, old, new):
    return [
        (perm == 0).implies(new == old + 2),
        # (perm == 1).implies(new == old / 8), # This give a lot of bad solutions
        (perm == 1).implies(8*new == old),     # This works
        (perm == 2).implies(new == old - 3),
        (perm == 3).implies(new == old * 7)
        ]

n = 4

perms = ["+2", "/8", "-3", "*7"]

# ages 16..120
age_low = 16
age_high = 120

# variables
m = intvar(age_low, age_high, name="m")  # my age
h = intvar(age_low, age_high, name="h")  # my age

perm1 = intvar(0, n - 1, shape=n, name="perm1")
perm2 = intvar(0, n - 1, shape=n, name="perm2")

# for calculating my age and husband's age
mlist = intvar(1, 1000, shape=n + 1, name="mlist")
hlist = intvar(1, 1000, shape=n + 1, name="hlist")

# constraints
model = Model([AllDifferent(perm1),
               AllDifferent(perm2),

               # same operations in different order
               sum([perm1[i] != perm2[i] for i in range(n)]) > 0,

               # find husbands age, start with my age
               hlist[0] == m,

               # husband's age is last in hlist
               h == hlist[n],

               # checking my age, start with husband's age
               mlist[0] == h,

               # my age is last in mlist
               m == mlist[n],

               # check the operations
               [check(perm1[i], hlist[i], hlist[i + 1]) for i in range(n)],
               [check(perm2[i], mlist[i], mlist[i + 1]) for i in range(n)],

               # Symmetry breaking: I'm younger than husband
               # m < h
               ])

# Solve
model.solve()

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


Four friends (two women named Debra and Janet, and two men named Hugh and Rick) found that each of them is allergic to something different: eggs, mold, nuts and ragweed. We would like to match each one's surname (Baxter, Lemon, Malone and Fleet) with his or her allergy. We know that:  - Rick is not allergic to mold  - Baxter is allergic to eggs  - Hugh is neither surnamed Lemon nor Fleet  - Debra is allergic to ragweed  - Janet (who isn't Lemon) is neither allergic to eggs nor to mold  Print the values of each food (eggs, mold, nuts, ragweed) and surname (baxter, lemon, malone, fleet) for each friend, where Debra = 0, Janet = 1, Hugh = 2, Rick = 3.

# Import libraries
from cpmpy import *
import json

n = 4
friends = Debra, Janet, Hugh, Rick = list(range(n))
friends_s = ["Debra", "Janet", "Hugh", "Rick"]

# foods[i] is the friend allergic to the ith food
eggs, mold, nuts, ragweed = foods = intvar(0, n - 1, shape=n, name="foods")
foods_s = ["eggs", "mold", "nuts", "ragweed"]

# surnames[i] is the friend with the ith surname
baxter, lemon, malone, fleet = surnames = intvar(0, n - 1, shape=n, name="surnames")
surnames_s = ["baxter", "lemon", "malone", "fleet"]

model = Model([AllDifferent(foods),
               AllDifferent(surnames),

               mold != Rick,
               eggs == baxter,
               lemon != Hugh,
               fleet != Hugh,
               ragweed == Debra,
               lemon != Janet,
               eggs != Janet,
               mold != Janet])

# Solve
model.solve()

# Print the solution
solution = {"eggs": eggs.value(), "mold": mold.value(), "nuts": nuts.value(), "ragweed": ragweed.value(),
            "baxter": baxter.value(), "lemon": lemon.value(), "malone": malone.value(), "fleet": fleet.value()}
print(json.dumps(solution))
# End of CPMPy script

malone baxter nuts ragweed mold fleet lemon eggs


Requires exactly m variables in x to take one of the values in v.  Print the array x (x).

# Data
n = 5  # Length of x
m = 3  # Number of values
v = [1, 5, 8]  # Values to be among in x
# End of data

# Import libraries
from cpmpy import *
import json

def among(m,x,v):
  """
  among(m,x,v)

  Requires exactly m variables in x to take one of the values in v.
  """
  return [m == sum([x[i] == j for i in range(len(x)) for j in v])]

# Decision variables
x = intvar(1, 8, shape=n, name="x")

# Model
model = Model([
    among(m, x, v)
])

# Solve the model
model.solve()

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


Schedule 4 people into 4 interview slots based on their free-busy schedules.  Print the assignment of people to slots (x) where 1 means the person is assigned to the slot and 0 means the person is not assigned to the slot.

# Data
m = [
    [1, 1, 1, 1],
    [0, 1, 1, 0],
    [1, 0, 0, 1],
    [1, 0, 0, 1]
]  # Matrix representing the free-busy schedules
# End of data

# Import libraries
from cpmpy import *
import json
import random

model = Model()

n = len(m)

# decision variables

# the assignment of persons to a slot (appointment number 0..n)
x = boolvar(shape=(n, n), name="x")

# constraints

for i in range(n):
    # ensure a free slot
    model += (sum([m[i][j] * x[(i, j)] for j in range(n)]) == 1)

    # ensure one assignment per slot
    model += (sum([x[(i, j)] for j in range(n)]) == 1)
    model += (sum([x[(j, i)] for j in range(n)]) == 1)
# Solve the model
model.solve()

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


How close can the young archer come to scoring a total of 100 using as many arrows as she pleases? The targets are: 16, 17, 23, 24, 39, 40.  Print the number of hits on each target (hits).

# Import libraries
from cpmpy import *
import json

# Parameters
targets = [16, 17, 23, 24, 39, 40]
n = len(targets)
target_score = 100

# Decision variables
hits = intvar(0, 100, shape=n, name="hits")
score = intvar(0, 100, name="score")
deviation = intvar(0, 100, name="deviation")

# Model
model = Model([
    score == sum(hits * targets),
    deviation == abs(target_score - score)
])

# Objective
model.minimize(deviation)

# Solve the model
model.solve()

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


Harriet, upon returning from the mall, is happily describing her four shoe purchases to her friend Aurora. Aurora just loves the four different kinds of shoes that Harriet bought   (ecru espadrilles, fuchsia flats, purple pumps, and suede sandals), but Harriet can't recall at which different store   (Foot Farm, Heels in a Handcart, The Shoe Palace, or Tootsies) she got each pair. Can you help these two figure out the order in which Harriet bought each pair of shoes, and where she bought each?  1. Harriet bought fuchsia flats at Heels in a Handcart. 2. The store she visited just after buying her purple pumps was not   Tootsies. 3. The Foot Farm was Harriet's second stop. 4. Two stops after leaving The Shoe Place, Harriet bought her suede   sandals.  Print the values of each shoe (ecruespadrilles, fuchsiaflats, purplepumps, suedesandals) and store (footfarm, heelsinahandcart, theshoepalace, tootsies), such that the same value denotes the same shoe or store.

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

n = 4
model = Model()

shoes = intvar(1, n, shape=n, name="shoes")
ecruespadrilles, fuchsiaflats, purplepumps, suedesandals = shoes

store = intvar(1, n, shape=n, name="store")
footfarm, heelsinahandcart, theshoepalace, tootsies = store

model += [AllDifferent(shoes),
          AllDifferent(store),

          # 1. Harriet bought fuchsia flats at Heels in a Handcart.
          fuchsiaflats == heelsinahandcart,

          # 2. The store she visited just after buying her purple pumps was not
          #    Tootsies.
          purplepumps + 1 != tootsies,

          # 3. The Foot Farm was Harriet's second stop.
          footfarm == 2,

          # 4. Two stops after leaving The Shoe Place, Harriet bought her suede
          # sandals.
          theshoepalace + 2 == suedesandals
          ]

model.solve()

# Print
solution = {"ecruespadrilles": ecruespadrilles.value(),
            "fuchsiaflats": fuchsiaflats.value(),
            "purplepumps": purplepumps.value(),
            "suedesandals": suedesandals.value(),
            "footfarm": footfarm.value(),
            "heelsinahandcart": heelsinahandcart.value(),
            "theshoepalace": theshoepalace.value(),
            "tootsies": tootsies.value()
            }
print(json.dumps(solution))
# End of CPMPy script

ecruespadrilles purplepumps theshoepalace suedesandals footfarm fuchsiaflats tootsies heelsinahandcart


# Source description: Winston 'Operations Research', Assignment Problems, page 393f

Assign people to 4 tasks with different costs, so that the total cost is minimized. Each task must be assigned to exactly one person, but it is not necessary to assign all people to a task.  Print whether each task is assigned to a person (x), where 1 means the task is assigned to the person and 0 means the task is not assigned to the person.

# Data
cost = [  # Cost matrix, rows are tasks, columns are people
    [14, 5, 8, 7, 15],
    [2, 12, 6, 5, 3],
    [7, 8, 3, 9, 7],
    [2, 4, 6, 10, 1]
]  # Cost matrix
# End of data

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

# Parameters
rows = len(cost)
cols = len(cost[0])

# Decision variables
max_cost = np.sum(np.array(cost))

total_cost = intvar(0, max_cost, name='cost')
x = boolvar(shape=(rows, cols), name="x")

model = Model(
    total_cost >= 0,
    total_cost == np.sum([x_row * cost_row for (x_row, cost_row) in zip(x, cost)]),

    # exacly one assignment per row, all rows (tasks) must be assigned.
    [sum(row) == 1 for row in x],

    # zero or one assignments per column (people)
    [sum(col) <= 1 for col in x.transpose()],
)

# Objective
model.minimize(total_cost)

# Solve the model
model.solve()

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


Given an integer n > 0 and an integer m >= 0, find a non-empty finite series S=(s0, s1, ..., sn, sn+1) such that ( 1) there are si occurrences of i in S for each integer i ranging from 0 to n, and (2) sn+1=m.  Print the series S (s).

# Data
n = 27
m = 5
# End of data

# Import libraries
from cpmpy import *
import json

def count(a, val, c):
    """
    count(a,val,c)

    c is the number of occurrences of val in array a.
    """
    return [c == sum([a[i] == val for i in range(len(a))])]

def global_cardinality_count(a, gcc):
    """
    global_cardinality_count(a, gcc)

    Global cardinality count: Collect the number of occurrences of each value 0..a.ub
    in gcc. The array gcc must be of length 0..ub.
    """
    n = len(a)
    ub = max([a[i].ub for i in range(n)])
    constraints = []
    for i in range(ub + 1):
        constraints += [count(a, i, gcc[i])]

    return constraints

def autoref(s):
    """
    autoref(s)
    Ensure that the number of occurrences of i in s is s[i].
    s should be an array of 0..n+1
    """
    return [global_cardinality_count(s, s)]

# Decision Variables
s = intvar(0, n, shape=n + 2, name="s")

# Model
model = Model(s[n + 1] == m, autoref(s))

# Solve the model
model.solve()

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


You have five bales of hay.  For some reason, instead of being weighed individually, they were weighed in all possible combinations of two. The weights of each of these combinations were written down and arranged in numerical order, without keeping track of which weight matched which pair of bales. The weights, in kilograms, were 80, 82, 83, 84, 85, 86, 87, 88, 90, and 91.  Print the weights of each bale (bales).

# Import libraries
from cpmpy import *
import json

# Parameters
n = 5
weights = [80, 82, 83, 84, 85, 86, 87, 88, 90, 91]

# variables
bales = intvar(0, 50, shape=n, name="bales")

model = Model()

def increasing(args):
    """
    Ensure that the values in args are increasing.
    """
    return [args[i - 1] <= args[i] for i in range(1, len(args))]

# constraints
model += [increasing(bales)]

for w in weights:
    i = intvar(0, n - 1)
    j = intvar(0, n - 1)
    model += [i < j]
    model += [w == bales[i] + bales[j]]

# Solve the model
model.solve()

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


In three dollars, you get 5 bananas, in five dollars, 7 oranges, in seven dollars, 9 mangoes and in nine dollars, three apples, I need to purchase 100 fruits in 100 dollars. Please keep in mind that all type of fruits need to be purchased but I do not like banana and apple, so these should be of minimum quantity.  Print the quantities of each fruit (bananas, oranges, mangoes, apples).

# Import libraries
from cpmpy import *
import json

x = intvar(1, 100, shape=4, name="x")
bananas, oranges, mangoes, apples = x

the_sum = intvar(1, 2000, name="the_sum")

model = Model([the_sum == bananas + apples,
               # This don't work since "/" does integer division
               # 3*bananas/5 + 5*oranges/7 + 7*mangoes/9 + 9*apples/3 == 100,
               # we multiply with 3*5*7*9=945 on both sides to weed out the divisions
               3 * bananas * 189 + 5 * oranges * 135 + 7 * mangoes * 105 + 9 * apples * 315 == 100 * 945,
               sum(x) == 100
               ])

model.minimize(the_sum)

# Solve the model
model.solve()

# Print the solution
solution = {
    "bananas": bananas.value(),
    "oranges": oranges.value(),
    "mangoes": mangoes.value(),
    "apples": apples.value()
}
print(json.dumps(solution))
# End of CPMPy script


# Source description: http://www.informs.org/ORMS-Today/Public-Articles/February-Volume-38-Number-1/THE-PUZZLOR

Hosting a dinner party requires several skills to pull off a successful evening. One of your duties, aside from preparing dinner and selecting the drinks, is to make sure your guests enjoy themselves.  Figure 1 shows a dinner table with six seats for your guests. Some guests, however, do not get along with each other. If two guests who do not get along are seated next to each other, it will create conflict at dinner. As host, you must arrange the guests in a seating order that minimizes conflict.  Andrew will only sit next to Dave and Frank; Betty will only sit next to Cara and Erica; Cara will only sit next to Betty and Frank; Dave will only sit next to Andrew and Erica; Erica will only sit next to Betty and Dave; Frank will only sit next to Andrew and Cara.  [   Figure 1 shows the following arrangement:                Andrew        Frank         Betty        Erica         Cara               Dave  ]  In the example seating arrangement above, there are three conflicts (Andrew and Betty, Cara and Dave, Erica and Frank).  Question:  What seating arrangement will minimize the conflict at dinner?  Print the list of guests (x) in the seating order, where 0 is Andrew, and the rest are in alphabetical order.

# Import libraries
from cpmpy import *
import json

def member_of(x, val):
    """
    member_of(x, val)

    Ensures that the value `val` is in the array `x`.
    """
    n = len(x)
    # cc = intvar(0,n)
    # constraints = [count(x, val, cc), cc >  0]
    constraints = [sum([x[i] == val for i in range(n)]) > 0]
    return constraints

model = Model()

n = 6

Andrew = 0
Betty = 1
Cara = 2
Dave = 3
Erica = 4
Frank = 5

prefs = cpm_array([
    [Dave, Frank],  # Andrew
    [Cara, Erica],  # Betty
    [Betty, Frank],  # Cara
    [Andrew, Erica],  # Dave
    [Betty, Dave],  # Erica
    [Andrew, Cara]  # Frank
])

# declare variables
x = intvar(0, n - 1, shape=n, name="x")

#
# constraints
#
model += (AllDifferent(x))

# symmetry breaking
model += (x[0] == Andrew)

for i in range(n):
    # This don't work:
    # TypeError: object of type 'Element' has no len()
    # model += [member_of(prefs[x[i]], x[(i-1) % n])]
    # model += [member_of(prefs[x[i]], x[(i+1) % n])]

    # It works if we expand the array:
    model += [member_of([prefs[x[i], j] for j in range(2)], x[(i - 1) % n])]
    model += [member_of([prefs[x[i], j] for j in range(2)], x[(i + 1) % n])]

    # Another approach that works
    # model += [sum([prefs[x[i],j] == x[(i-1) % n] for j in range(2)])>0]
    # model += [sum([prefs[x[i],j] == x[(i+1) % n] for j in range(2)])>0]

# Solve the model
model.solve()

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


Nontransitive dice a la The Big Bang Theory in Comet Thore Graepel (thoregraepel@googlemail.com)  The idea is to create a set of five dice such that the dominance relationships between the dice is isomorphic to the corresponding relationships in the game Rock-Paper-Scissors extended by the choices Lizard and Spock. (originally from http://www.samkass.com/theories/RPSSL.html).  Each choice beats two other choices and is beaten by two other choices. In particular, we have the following ten 'beats' relationships  1:  Rock(1) crushes Scissors(3) 2:  Rock(1) crushes Lizard(4) 3:  Paper(2) covers Rock(1) 4:  Paper(2) disproves Spock(5) 5:  Scissors(3) cuts Paper(2) 6:  Scissors(3) decapitate Lizard(4) 7:  Lizard(4) eats Paper(2) 8:  Lizard(4) poisons Spock(5) 9:  Spock(5) vaporizes Rock(1) 10: Spock(5) smashes Scissors(3)  Print the dice (dice) as a list of lists where each sublist is a die.

# Import libraries
from cpmpy import *
import json

def increasing(args):
    """
    Ensure that the values in args are increasing.
    """
    return [args[i - 1] <= args[i] for i in range(1, len(args))]

rock = 0
paper = 1
scissors = 2
lizard = 3
spock = 4

m = 5  # number of dice
n = 8  # number of faces of each die
f = 2 * n  # max face value of dice
k = 10  # number of competitions
edge = [
    [rock, scissors],
    [rock, lizard],
    [paper, rock],
    [paper, spock],
    [scissors, paper],
    [scissors, lizard],
    [lizard, paper],
    [lizard, spock],
    [spock, rock],
    [spock, scissors]
]

#
# declare variables
#

dice = intvar(1, f, shape=(m, n), name="dice")
comp = intvar(0, n * n, shape=(k, 2), name="comp")
max_val = intvar(0, f, name="max_val")

model = Model()
#
# constraints
#
model += (max_val == max(dice))

# Symmetry breaking
# order of the number of each die, lowest first
model += [increasing(dice[i]) for i in range(m)]
# This is faster but yield max_val = 11 instead of the correct 9!
# model += [lex_less(dice[i],dice[i+1]) for i in range(m-1)]
model += [dice[0, 0] == 1]
model += [AllDifferent([dice[i, 0] for i in range(m)])]

# and now we roll...
for c in range(k):
    model += (comp[c, 0] == sum([dice[edge[c][0], r1] > dice[edge[c][1], r2]
                                 for r1 in range(n) for r2 in range(n)]))

    model += (comp[c, 1] == sum([dice[edge[c][1], r1] > dice[edge[c][0], r2]
                                 for r1 in range(n) for r2 in range(n)]))

    model += (comp[c, 0] > comp[c, 1])

    # added later by Thore:
    model += (comp[c, 0] + comp[c, 1] == n * n)  # make sure there are no ties

# Solve the model
model.solve()

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


Given several items of specific weights and a number of bins of a fixed capacity, assign each item to a bin so that the total weight of the items in each bin does not exceed the capacity.  Print the bin each item is assigned to (bins) as a list of numbers.

# Data
weights = [4, 3, 1, 3, 2, 5, 2]
capacity = 5
num_bins = 5
# End of data

# Import libraries
from cpmpy import *
import json

# Parameters
n = len(weights)

# Decision variables
bins = intvar(0, num_bins - 1, shape=n, name="bins")  # Which bin each item is assigned to

# Model
model = Model([
    [sum(weights[j] * (bins[j] == i) for j in range(n)) <= capacity for i in range(n)]
])

# Solve the model
model.solve()

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


# Source description: https://matmod.ch/lpl/PDF//math10.pdf

Tommy was given 15 coins for his birthday (half-crowns, shillings and sixpence). When he added it up, he found that he had £1. 5s. 6d (one pound 5 shillings and 6 pences, see below). How many half-crowns was he given?  (This puzzle involve coins from the old British currency. A pound is 20 shillings and a shilling is 12 pence (hence, a pound is 20 ⋅ 12 = 240 pence). A half-sovereign is 10 shillings, a crown is 5 shillings, a double-florin is four shillings, a half-crown is 2 shillings and sixpence (hence, a half-crown is 2 ⋅ 12 + 6 = 30 pence), a florin is 2 shillings. £3. 2s. 6d. means three pounds, two shillings and six pence.).  Print the number of half-crowns he was given (half_crowns).

# Import libraries
from cpmpy import *
import json

# Parameters
coin_types = 3
values = [30, 12, 6]  # Values in pence for half-crowns, shillings, and sixpences
total_value = 240 + 5 * 12 + 6  # Total value in pence
total_coins = 15  # Total number of coins

# Decision variables
coins = intvar(1, 15, shape=coin_types, name="coins")
half_crowns = coins[0]

# Model
model = Model([
    sum(values[i] * coins[i] for i in range(coin_types)) == total_value,
    sum(coins) == total_coins
])

# Solve the model
model.solve()

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


# Source description: http://surana.wordpress.com/2011/06/01/constraint-programming-example/

You have 40 bowls, all placed in a line at exact intervals of 1 meter. You also have 9 oranges. You wish to place all the oranges in the bowls, no more than one orange in each bowl, so that there are no three oranges A, B, and C such that the distance between A and B is equal to the distance between B and C.  Print a solution to the problem as a list of 9 numbers between 1 and 40 representing the bowls in which the oranges are placed (x).

# Import libraries
from cpmpy import *
import json

# Parameters
n = 40  # Number of bowls
m = 9  # Number of oranges

# Decision variables
x = intvar(1, n, shape=m, name="x")

def increasing(args):
    """
    Ensure that the values in args are increasing.
    """
    return [args[i - 1] <= args[i] for i in range(1, len(args))]

# Model
model = Model([
    AllDifferent(x),
    increasing(x)
])

# Constraint to ensure no three oranges A, B, and C have equal distances
for i in range(m):
    for j in range(m):
        for k in range(m):
            if i < j < k:
                model += ((x[j] - x[i]) != (x[k] - x[j]))

# Solve the model
model.solve()

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