# 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/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/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/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/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/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/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/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


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


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


# 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


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://yurichev.com/blog/cabling_Z3/

Take a rack cabinet, like this one:   [ an image ]  Let's say, there are 8 1U devices, maybe servers, routers and whatnot, named as A, B, C, D, E, F, G, H. Devices must be connected by cables: probably twisted pair or whatever network engineers using today. Some devices must be connected by several cables (2 cables, 3 or 4):  A <--- 1 cable  ---> H A <--- 2 cables ---> E B <--- 4 cables ---> F C <--- 1 cable  ---> G C <--- 1 cable  ---> D C <--- 1 cable  ---> E D <--- 3 cables ---> H G <--- 1 cable  ---> H  The problem: how we can place these 8 devices in such an order, so that sum of all cable lengths would be as short as possible?  Print the optimal sum of all cable lengths (final_sum).

# Import libraries
from cpmpy import *
import json

# Parameters
n = 8

model = Model()

# A <--- 1 cable  ---> H
# A <--- 2 cables ---> E
# B <--- 4 cables ---> F
# C <--- 1 cable  ---> G
# C <--- 1 cable  ---> D
# C <--- 1 cable  ---> E
# D <--- 3 cables ---> H
# G <--- 1 cable  ---> H
A, B, C, D, E, F, G, H = list(range(n))

cable_struct = [[A, H, 1],
                [A, E, 2],
                [B, F, 4],
                [C, G, 1],
                [C, D, 1],
                [C, E, 1],
                [D, H, 3],
                [G, H, 1]]

x = intvar(0, n - 1, shape=n, name="x")  # position of the device in the rack
t = intvar(1, n * n, shape=len(cable_struct), name="t")  # cable lengths
final_sum = intvar(0, n * n, name="final_sum")  # sum of all cable lengths

# all "devices" has distinct positions in rack:
model += [AllDifferent(x),
          final_sum == sum(t)]

for i in range(len(cable_struct)):
    a, b, num = cable_struct[i]
    model += [t[i] == abs(x[a] - x[b]) * num]

model.minimize(final_sum)

# Solve
model.solve()

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


Alice is a kindergarden teacher. She wants to give some candies to the children in her class.  All the children sit in a line and each  of them  has a rating score according to his or her usual performance. Alice wants to give at least 1 candy for each child.Children get jealous of their immediate neighbors, so if two children sit next to each other then the one with the higher rating must get more candies. Alice wants to save money, so she wants to minimize the total number of candies.  Print the optimal number of candies needed in total (z).

# Data
ratings = [2, 3, 4, 4, 4, 2, 1, 3, 4]  # Ratings of the children
# End of data

# Import libraries
from cpmpy import *
import json

# Parameters
n = len(ratings)

# variables
x = intvar(1, n, shape=n, name="x")  # number of candies for each child
z = intvar(1, n * n, name="z")  # total number of candies

# constraints
model = Model([z == sum(x),
               z >= n])

for i in range(1, n):
    if ratings[i - 1] > ratings[i]:
        model += (x[i - 1] > x[i])
    elif ratings[i - 1] < ratings[i]:
        model += (x[i - 1] < x[i])

model.minimize(z)
# Solve the model
model.solve()

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


# Source description: Winston 'Operations Research', page 478: Capital budgeting

Stockco is considering four investments. Investment 1 will yield a net present value (NPV) of $16,000; investment 2, an NPV of $22,000; investment 3, an NPV of $12,000; and investment 4, an NPV of $8,000. Each investment requires a certain cash outflow at the present time: investment 1, $5,000; investment 2, $7,000; investment 3, $4,000; and investment 4, $3,000. Currently, $14,000 is available for investment. How to maximize the NPV obtained from investments 1–4?  Print whether each investment is chosen (x) as a list of 0/1 values.

# Import libraries
from cpmpy import *
import json

# Parameters
budget = 14
npv = [16, 22, 12, 8]
cash_flow = [5, 7, 4, 3]
n = 4

# variables
x = boolvar(shape=n, name="x")  # x[i] = 1 if investments i
z = intvar(0, 1000, name="z")  # total NPV

# constraints
model = Model([
    # the sum of all choosen investments must be less than the budget
    sum(x * cash_flow) <= budget,
    z == sum(x * npv)
])

# Objective: maximize the NPV
model.maximize(z)

# Solve the model
model.solve()

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


# Source description: Applications of Optimization with XPress-MP.pdf page 11. The problem is presented on page 7.

A small joinery makes two different sizes of boxwood chess sets. The small set requires 3 hours of machining on a lathe, and the large set requires 2 hours. There are four lathes with skilled operators who each work a 40 hour week, so we have 160 lathe-hours per week. The small chess set requires 1 kg of boxwood, and the large set requires 3 kg. Unfortunately, boxwood is scarce and only 200 kg per week can be obtained. When sold, each of the large chess sets yields a profit of $20, and one of the small chess set has a profit of $5. The problem is to decide how many sets of each kind should be made each week so as to maximize profit.  Print the number of small sets and large sets, and the maximum profit (small_set, large_set, max_profit).

# Import libraries
from cpmpy import *
import json

# Decision variables
small_set = intvar(0, 100, name="small_set")
large_set = intvar(0, 100, name="large_set")
max_profit = intvar(0, 10000, name="max_profit")

# Model
model = Model([
    small_set + 3 * large_set <= 200,  # Boxwood constraint
    3 * small_set + 2 * large_set <= 160,  # Lathe-hours constraint
    max_profit == 5 * small_set + 20 * large_set  # Profit calculation
])

# Objective: maximize the profit
model.maximize(max_profit)

# Solve the model
model.solve()

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


# Source description: From the Oz examples http://www.comp.nus.edu.sg/~henz/projects/puzzles/arith/circlingsquares.html from 'Amusements in Mathematics, Dudeney', number 43.

Circling the squares:  The puzzle is to place a different number in each of the ten squares so that the sum of the squares of any two adjacent numbers shall be equal to the sum of the squares of the two numbers diametrically opposite to them. The four numbers placed, as examples, must stand as they are. The square of 16 is 256, and the square of 2 is 4. Add these together, and the result is 260. Also—the square of 14 is 196, and the square of 8 is 64. These together also make 260. Now, in precisely the same way, B and C should be equal to G and H (the sum will not necessarily be 260), A and K to F and E, H and I to C and D, and so on, with any two adjoining squares in the circle.  All you have to do is to fill in the remaining six numbers. Fractions are not allowed, and I shall show that no number need contain more than two figures.  Print the 10 numbers in the circle (A, B, C, D, E, F, G, H, I, K) when A=16, B=2, F=8, G=14.

# Import libraries
from cpmpy import *
import json

def s(x1, x2, y1, y2):
    return x1 * x1 + x2 * x2 == y1 * y1 + y2 * y2

n = 10

# variables
x = intvar(1, 99, shape=n, name="x")
A, B, C, D, E, F, G, H, I, K = x

# constraints
model = Model([AllDifferent(x),
               A == 16,
               B == 2,
               F == 8,
               G == 14,

               s(A, B, F, G),
               s(B, C, G, H),
               s(C, D, H, I),
               s(D, E, I, K),
               s(E, F, K, A),
               ])
# End of CPMPy script

# Solve
model.solve()

# Print the solution
solution = {"A": A.value(), "B": B.value(), "C": C.value(), "D": D.value(), "E": E.value(),
            "F": F.value(), "G": G.value(), "H": H.value(), "I": I.value(), "K": K.value()}
print(json.dumps(solution))
# End of CPMPy script


# Source description: From 'Constraint Logic Programming using ECLiPSe' pages 99f and 234 ff. The solution in ECLiPSe is at page 236.

Find the minimum number of coins that allows one to pay exactly any amount smaller than one Euro using the denominations 1, 2, 5, 10, 20, 50 cents.  Print the number of each type of coin used (x) as a list.

# Import libraries
from cpmpy import *
import json

# Parameters
denominations = [1, 2, 5, 10, 20, 50]  # Euro cent denominations
n = len(denominations)

# declare variables
x = intvar(0, 99, shape=n, name="x")  # The number of each type of coin used
num_coins = intvar(0, 99, name="num_coins")  # The total number of coins used

model = Model(minimize=num_coins)

# constraints

# number of used coins, to be minimized
model += [num_coins == sum(x)]

# Check that all changes from 1 to 99 can be made.
for j in range(1, 100):
    tmp = intvar(0, 99, shape=n)
    model += [sum(tmp * denominations) == j]
    model += [tmp[i] <= x[i] for i in range(n)]

# Solve the model
model.solve()

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


# Source description: Tony Hurlimann: "A coin puzzle - SVOR-contest 2007" http://www.svor.ch/competitions/competition2007/AsroContestSolution.pdf

In a quadratic grid (or a larger chessboard) with 31x31 cells, one should  place coins in such a way that the following conditions are fulfilled:    1. In each row exactly 14 coins must be placed.    2. In each column exactly 14 coins must be placed.    3. The sum of the quadratic horizontal distance from the main       diagonal of all cells containing a coin must be as       small as possible.    4. In each cell at most one coin can be placed.  The description says to place 14x31 = 434 coins on the chessboard each row containing 14 coins and each column also containing 14 coins.  Print whether a coin is placed in each cell (x) as a list of 0/1 lists, and the sum of the quadratic horizontal distance from the main diagonal (z) as a number.

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

# Parameters
n = 31
c = 14

# variables
x = intvar(0, 1, shape=(n, n), name="x")  # The coins on the grid (1 if a coin is placed, 0 otherwise)
z = intvar(0, 1000000, name="z")  # The sum of the quadratic horizontal distance from the main diagonal

model = Model([
    # every row adds up to c
    [sum(row) == c for row in x],

    # every col adds up to c
    [sum(col) == c for col in x.transpose()],

    # quadratic horizonal distance
    z == sum([x[i, j] * abs(i - j) * abs(i - j) for i in range(0, n) for j in range(0, n)]),

], minimize=z
)

# Solve the model
model.solve()

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


# Source description: http://www.comp.nus.edu.sg/~henz/projects/puzzles/arith/index.html Contracting Costs from 'Mathematical Puzzles of Sam Loyd, Volume 2', number 20.

A contractor planning the construction of a house found that he would have to pay:    * $ 1,100 to the paper hanger and the painter,   * $ 1,700 to the painter and plumber,   * $ 1,100 to the plumber and electrician,   * $ 3,300 to the electrician and carpenter,   * $ 5,300 to the carpenter and mason,   * $ 3,200 to the mason and painter.  What does each man charge for his services?  Print the cost of each person's service (paper_hanger, painter, plumber, electrician, carpenter, mason) in dollars.

# Import libraries
from cpmpy import *
import json

n = 6
x = intvar(1, 5300, shape=n, name="x")
paper_hanger, painter, plumber, electrician, carpenter, mason = x

costs = [[paper_hanger, painter, 1100],
         [painter, plumber, 1700],
         [plumber, electrician, 1100],
         [electrician, carpenter, 3300],
         [carpenter, mason, 5300],
         [mason, painter, 3200],
         ]

# model = Model([1100 == paper_hanger + painter,
#                1700 == painter + plumber,
#                1100 == plumber + electrician,
#                3300 == electrician + carpenter,
#                5300 == carpenter + mason,
#                3200 == mason + painter,
#                ])

# Alternative encoding:
model = Model([sum(costs[i][:2]) == costs[i][2] for i in range(n)])

# Solve
model.solve()

# Print the solution
solution = {"paper_hanger": paper_hanger.value(), "painter": painter.value(), "plumber": plumber.value(),
            "electrician": electrician.value(), "carpenter": carpenter.value(), "mason": mason.value()}
print(json.dumps(solution))
# End of CPMPy script

painter plumber electrician carpenter mason paper_hanger


# Source description: This example is from the OPL example covering.mod

Select a set of workers to perform all the tasks, while minimizing the cost. Each worker can perform certain tasks and has a hiring cost. Ensure that all tasks are performed.  Print the total cost (total_cost) and whether each worker is selected (workers) as a list of 0/1 values.

# Data
nb_workers = 32  # Number of workers
num_tasks = 15  # Number of tasks
Qualified = [  # Which worker is qualified for each task (1-based indexing)
    [1, 9, 19, 22, 25, 28, 31],
    [2, 12, 15, 19, 21, 23, 27, 29, 30, 31, 32],
    [3, 10, 19, 24, 26, 30, 32], [4, 21, 25, 28, 32],
    [5, 11, 16, 22, 23, 27, 31], [6, 20, 24, 26, 30, 32],
    [7, 12, 17, 25, 30, 31], [8, 17, 20, 22, 23],
    [9, 13, 14, 26, 29, 30, 31], [10, 21, 25, 31, 32],
    [14, 15, 18, 23, 24, 27, 30, 32], [18, 19, 22, 24, 26, 29, 31],
    [11, 20, 25, 28, 30, 32], [16, 19, 23, 31],
    [9, 18, 26, 28, 31, 32]]
Cost = [  # Cost of hiring each worker
    1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4, 5, 5,
    5, 6, 6, 6, 7, 8, 9
]
# End of data

# Import libraries
from cpmpy import *
import json

# Parameters
Workers = list(range(nb_workers))
Tasks = list(range(num_tasks))

#
# variables
#
workers = boolvar(shape=nb_workers, name="workers")  # 1 if the worker is hired, 0 otherwise
total_cost = intvar(0, nb_workers * sum(Cost), name="total_cost")  # Total cost of hiring the workers

model = Model(minimize=total_cost)

#
# constraints
#
model += [total_cost == sum(workers * Cost)]

for j in Tasks:
    # Sum the cost for hiring the qualified workers
    # (also, make 0-base)
    model += [sum([workers[c - 1] for c in Qualified[j]]) >= 1]

# Solve the model
model.solve()

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


# Source description: http://www.cis.temple.edu/~ingargio/cis587/readings/constraints.html

We are to complete the puzzle       1   2   3   4   5    +---+---+---+---+---+       Given the list of words:  1 | 1 |   | 2 |   | 3 |             AFT     LASER    +---+---+---+---+---+             ALE     LEE  2 | # | # |   | # |   |             EEL     LINE    +---+---+---+---+---+             HEEL    SAILS  3 | # | 4 |   | 5 |   |             HIKE    SHEET    +---+---+---+---+---+             HOSES   STEER  4 | 6 | # | 7 |   |   |             KEEL    TIE    +---+---+---+---+---+             KNOT  5 | 8 |   |   |   |   |    +---+---+---+---+---+  6 |   | # | # |   | # |       The numbers 1,2,3,4,5,6,7,8 in the crossword    +---+---+---+---+---+       puzzle correspond to the words                                that will start at those locations.   Print the selected words (E) as a list of 8 integers starting from 0. Assume that the words are first sorted starting from the longest word and then alphabetically. So, word 0 corresponds to HOSES, ..., word 14 corresponds to TIE.

# Import libraries
from cpmpy import *
import json

a = 1;
b = 2;
c = 3;
d = 4;
e = 5;
f = 6;
g = 7;
h = 8;
i = 9;
j = 10;
k = 11;
l = 12;
m = 13;
n = 14;
o = 15;
p = 16;
q = 17;
r = 18;
s = 19;
t = 20;
u = 21;
v = 22;
w = 23;
x = 24;
y = 25;
z = 26;

AA = [
    [h, o, s, e, s],  # HOSES
    [l, a, s, e, r],  # LASER
    [s, a, i, l, s],  # SAILS
    [s, h, e, e, t],  # SHEET
    [s, t, e, e, r],  # STEER
    [h, e, e, l, 0],  # HEEL
    [h, i, k, e, 0],  # HIKE
    [k, e, e, l, 0],  # KEEL
    [k, n, o, t, 0],  # KNOT
    [l, i, n, e, 0],  # LINE
    [a, f, t, 0, 0],  # AFT
    [a, l, e, 0, 0],  # ALE
    [e, e, l, 0, 0],  # EEL
    [l, e, e, 0, 0],  # LEE
    [t, i, e, 0, 0]  # TIE
]
word_len = 5
num_words = len(AA)

num_overlapping = 12
overlapping = [
    [0, 2, 1, 0],  # s
    [0, 4, 2, 0],  # s

    [3, 1, 1, 2],  # i
    [3, 2, 4, 0],  # k
    [3, 3, 2, 2],  # e

    [6, 0, 1, 3],  # l
    [6, 1, 4, 1],  # e
    [6, 2, 2, 3],  # e

    [7, 0, 5, 1],  # l
    [7, 2, 1, 4],  # s
    [7, 3, 4, 2],  # e
    [7, 4, 2, 4]  # r
]

A = intvar(0, 26, shape=(num_words, word_len), name="A")

n = 8
E = intvar(0, num_words, shape=n, name="E")  # The selected words for each slot

model = Model()

for I in range(num_words):
    for J in range(word_len):
        model += [A[I, J] == AA[I][J]]

model += [AllDifferent(E)]

for I in range(num_overlapping):
    model += [A[E[overlapping[I][0]], overlapping[I][1]] == A[E[overlapping[I][2]], overlapping[I][3]]]

model.solve()

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


# Source description: Name: crypta.pl, Title: crypt-arithmetic, Original Source: P. Van Hentenryck's book, Adapted by: Daniel Diaz - INRIA France, Date: September 1992

Cryptarithmetic puzzle, solve the operation:    B A I J J A J I I A H F C F E B B J E A    + D H F G A B C D I D B I F F A G F E J E    -----------------------------------------    = G J E G A C D D H F A F J B F I H E E F      where all letters are distinct digits.  Print the number of each letter (A, B, C, D, E, F, G, H, I, J).


Generate a random 18-hole golf course where each hole has a length of 3, 4, or 5, and the total length of the course is 72.  Print the lengths of the holes (holes).

# Import libraries
from cpmpy import *
import json

# Parameters
num_holes = 18  # Number of holes
total_length = 72  # Total length of the course
hole_lengths = [3, 4, 5]  # Possible lengths for each hole

# Decision variables
holes = intvar(3, 5, shape=num_holes, name="holes")  # Lengths of the holes

# Model
model = Model([
    sum(holes) == total_length
])

# Solve the model
model.solve()

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


A company is considering opening warehouses in four cities to meet regional demands at minimal costs. The potential cities for these warehouses are New York, Los Angeles, Chicago, and Atlanta. The company needs to decide which  warehouses to open based on several constraints: 1. If the New York warehouse is opened, then the Los Angeles  warehouse must also be opened. 2. No more than three warehouses can be operational in any given week. 3. Either the  Atlanta or the Los Angeles warehouse must be opened. The objective is to determine the optimal set of warehouses to open and the distribution of shipments to various regions to minimize total costs.  Print the total cost (total_cost), whether the warehouses are open (open_warehouse), and the shipping details for each region per warehouse (ships).

# Data
warehouse_s = ["New York", "Los Angeles", "Chicago", "Atlanta"]
fixed_costs = [400, 500, 300, 150]  # Weekly fixed costs per warehouse
max_shipping = 100  # Max units per week per warehouse
demands = [80, 70, 40]  # Weekly demands for regions 1 to 3
shipping_costs = [
    [20, 40, 50],  # New York to regions 1, 2, 3
    [48, 15, 26],  # Los Angeles to regions 1, 2, 3
    [26, 35, 18],  # Chicago to regions 1, 2, 3
    [24, 50, 35]  # Atlanta to regions 1, 2, 3
]
# End of data

# Import libraries
from cpmpy import *
import json

# Parameters
num_companies = len(warehouse_s)
num_regions = len(demands)
new_york, los_angeles, chicago, atlanta = range(num_companies)
costs = cpm_array(shipping_costs)

# Decision Variables
open_warehouse = boolvar(shape=num_companies, name="open_warehouse")  # 1 if the warehouse is open, 0 otherwise
ships = intvar(0, max_shipping, shape=(num_companies, num_regions), name="ships")  # what to ship to each region
total_cost = intvar(0, 10000, name="total_cost")  # total cost of shipping

# Model
model = Model()

# each warehouse can only send max 100 units per week
for i in range(num_companies):
    model += [sum([open_warehouse[i] * ships[i, j] for j in range(num_regions)]) <= max_shipping]

# the demands of the regions
for j in range(num_regions):
    model += [sum([open_warehouse[i] * ships[i, j] for i in range(num_companies)]) >= demands[j]]

# connect open with ships
for i in range(num_companies):
    model += [open_warehouse[i].implies(sum([ships[i, j] for j in range(num_regions)]) > 0)]

# total cost
model += [total_cost == sum([open_warehouse[i] *
                             (fixed_costs[i] + sum([ships[i, j] * costs[i, j] for j in range(num_regions)]))
                             for i in range(num_companies)])]

# 1. If the New York warehouse is opened, then the Los Angeles warehouse must be opened.
model += [open_warehouse[new_york].implies(open_warehouse[los_angeles])]

# 2. At most three warehouses can be opened.
model += [sum(open_warehouse) <= 3]

# 3. Either the Atlanta or the Los Angeles warehouse must be opened.
model += [open_warehouse[atlanta] | open_warehouse[los_angeles]]

# Objective: Minimize total cost
model.minimize(total_cost)

# Solve
model.solve()

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


# Source description: http://www.chlond.demon.co.uk/puzzles/puzzles1.html, puzzle nr. 5.

A side show at Coney Island is described as follows: "There were ten little dummies which you were to knock over with baseballs. The man said: 'Take as many throws as you like at a cent apiece and stand as close as you please. Add up the numbers on all the men that you knock down and when the sum amounts to exactly fifty, neither more nor less you get a genuine 25 cent Maggie Cline cigar with a gold band around it.'" The numbers on the ten dummies were 15, 9, 30, 21, 19, 3, 12, 6, 25, 27.  Print the dummies that are knocked over (dummies).

# Import libraries
from cpmpy import *
import json

# Parameters
target_sum = 50  # Target sum to achieve
values = [15, 9, 30, 21, 19, 3, 12, 6, 25, 27]  # Numbers on the dummies
n = len(values)

# Decision variables
dummies = boolvar(shape=n, name="dummies")  # Boolean array to indicate which dummies are knocked over

# Model
model = Model([
    sum([values[i] * dummies[i] for i in range(n)]) == target_sum
])

# Solve the model
model.solve()

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


Given a collection of integers, find three elements that sum to zero.  Print whether each element is selected (indices).

# Data
nums = [-1, 6, 8, 9, 10, -100, 78, 0, 1]  # Collection of integers
# End of data

# Import libraries
from cpmpy import *
import json

# Parameters
n = len(nums)
m = 3  # The number of elements that should sum to 0

# Decision variables
indices = boolvar(shape=n, name="indices")  # Boolean array to indicate which elements are selected

# Model
model = Model([
    sum([nums[i] * indices[i] for i in range(n)]) == 0,
    sum(indices) == m
])

# Solve the model
model.solve()

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


Given a target number of beers, find the closest combination of 7-packs and 13-packs that meets or exceeds the target.  Print the counts of 7-packs and 13-packs used (counts).

# Data
target = 20  # Target number of beers
# End of data

# Import libraries
from cpmpy import *
import json
import builtins

# Parameters
n = 2  # Number of pack sizes
packs = [7, 13]  # Pack sizes
max_val = target * 2  # Arbitrary max limit of pack counts

# Decision variables
counts = intvar(0, max_val, shape=n, name="counts")  # Count of each pack size used
total = intvar(0, max_val * n, name="total")  # Total number of beers

# Model
model = Model([
    total == (counts * packs).sum(),
    total >= target,
])

# Objective
model.minimize(total)

# Solve the model
model.solve()

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


# Source description: Problem from Taha "Introduction to Operations Research", page 58.

Progress City is considering a mass-transit bus system to reduce in-city driving. The goal is to determine the minimum number of buses required to meet the transportation needs throughout the day. The demand for buses is approximated as constant over successive 4-hour intervals. Each bus can operate only 8 successive hours a day.  We need to ensure that the number of buses available in each 4-hour interval meets the demand for that interval. Also, due to overlapping shifts, the schedule must account for buses transitioning between intervals.  Print the number of buses scheduled in each time slot (x).

# Data
demands = [4, 8, 10, 7, 12, 4]  # Demand for buses in each 4-hour time slot
# End of data

# Import libraries
from cpmpy import *
import json

# Parameters
slots = len(demands)

# Decision Variables
# x[i] represents the number of buses scheduled to start working in the i-th 4-hour slot
x = intvar(0, sum(demands), shape=slots, name="x")

# Model the constraints
# Each bus operates for 8 hours, so it covers two consecutive 4-hour slots
constraints = []
for i in range(slots):
    # The number of buses covering the i-th and (i+1)-th slot should meet the demand of the (i+1)-th slot
    constraints.append(x[i] + x[(i + 1) % slots] >= demands[(i + 1) % slots])

# Create the model with constraints
model = Model(constraints)

# Objective: Minimize the total number of buses used
model.minimize(sum(x))

# Solve the model
model.solve()

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


# Source description: https://developers.google.com/optimization/scheduling/job_shop

Job Shop Scheduling Problem  One common scheduling problem is the job shop, in which multiple jobs are processed on several machines.  Each job consists of a sequence of tasks, which must be performed in a given order, and each task must be processed on a specific machine. For example, the job could be the manufacture of a single consumer item, such as an automobile. The problem is to schedule the tasks on the machines so as to minimize the length of the schedule—the time it takes for all the jobs to be completed.  There are several constraints for the job shop problem: - No task for a job can be started until the previous task for that job is completed. - A machine can only work on one task at a time. - A task, once started, must run to completion.  Print the optimal makespan (makespan), which is the total time taken to complete all jobs.

# Data
jobs_data = [  # (job_id, task_id) -> (machine_id, duration)
    [(0, 3), (1, 2), (2, 2)],  # Job 0
    [(0, 2), (2, 1), (1, 4)],  # Job 1
    [(1, 4), (2, 3)]           # Job 2
]
# End of data

# Import libraries
from cpmpy import *
import json
from itertools import combinations
import builtins  # To access the original built-in functions (because of the import * above)

# Extract job data
n_jobs = len(jobs_data)
n_tasks = builtins.sum(len(job) for job in jobs_data)
machines = set(task[0] for job in jobs_data for task in job)
n_machines = len(machines)

# Create variables for start and end times
max_duration = builtins.sum(task[1] for job in jobs_data for task in job)
max_tasks_per_job = builtins.max(len(job) for job in jobs_data)
start_times = intvar(0, max_duration, shape=(n_jobs, max_tasks_per_job), name="start_times")
end_times = intvar(0, max_duration, shape=(n_jobs, max_tasks_per_job), name="end_times")
makespan = intvar(0, max_duration, name="makespan")

# Create the model
model = Model()

# Add constraints for each task
for job_id, job in enumerate(jobs_data):
    for task_id, (machine, duration) in enumerate(job):
        model += (end_times[job_id, task_id] == start_times[job_id, task_id] + duration)

        # No task can start before the previous one ends
        if task_id > 0:
            model += (start_times[job_id, task_id] >= end_times[job_id, task_id - 1])

# Add machine constraints
for machine in machines:
    machine_tasks = [(job_id, task_id) for job_id, job in enumerate(jobs_data) for task_id, (m, duration) in enumerate(job) if m == machine]
    for (job1, task1), (job2, task2) in combinations(machine_tasks, 2):
        model += (start_times[job1, task1] >= end_times[job2, task2]) | (start_times[job2, task2] >= end_times[job1, task1])

# Objective: Minimize makespan
model += (makespan == max(end_times.flatten()))
model.minimize(makespan)

# Solve the model
model.solve()

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


# Source description: https://minesweeper.online

Minesweeper rules are very simple. The board is divided into cells, with mines randomly distributed. To win, you need to open all the cells. The number on a cell shows the number of mines adjacent to it. Using this information, you can determine cells that are safe, and cells that contain mines. Cells suspected of being mines can be marked with a flag using the right mouse button.  The task is to determine which 'X' fields contain mines based on the provided numerical clues.  Print whether each cell is a mine or not (mines).

# Data
X = -1
game_data = [  # 0-8: number of mines around, -1: not opened
    [2, 3, X, 2, 2, X, 2, 1],
    [X, X, 4, X, X, 4, X, 2],
    [X, X, X, X, X, X, 4, X],
    [X, 5, X, 6, X, X, X, 2],
    [2, X, X, X, 5, 5, X, 2],
    [1, 3, 4, X, X, X, 4, X],
    [0, 1, X, 4, X, X, X, 3],
    [0, 1, 2, X, 2, 3, X, 2]
]
# End of data

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

# Parameters
game = np.array(game_data)
rows, cols = game.shape
S = [-1, 0, 1]  # for the neighbors of a cell

# Decision Variables
mines = boolvar(shape=game.shape)  # True: mine, False: not mine

model = Model()
for (r, c), val in np.ndenumerate(game):
    if val != X:
        # This cell cannot be a mine
        model += mines[r, c] == 0
        # Count neighbors
        model += (sum(mines[r + a, c + b]
                      for a in S for b in S
                      if 0 <= r + a < rows and 0 <= c + b < cols and (a, b) != (0, 0))
                  == val)

# Solve
model.solve()

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


# Source Description: https://github.com/Alexander-Schiendorfer/cp-examples/tree/main/packing

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

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

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

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

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

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

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

    m = Model()

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

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

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

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

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

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

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

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

# Print
solution = {
    "pos_x": pos_x.value().tolist(),
    "pos_y": pos_y.value().tolist(),
    "total_x": total_x.value(),
    "total_y": total_y.value()
}
print(json.dumps(solution))
# End of CPMPy script


The resource-constrained project scheduling problem involves scheduling a set of jobs, each with a specific duration and resource requirement, such that the total project duration (makespan) is minimized. Each job may have precedence constraints, meaning certain jobs must be completed before others can start. Additionally, there are multiple types of resources, each with a limited capacity that must not be exceeded at any time.  Given the durations of the jobs, their resource requirements, the capacities of the resources, and the precedence constraints between the jobs, determine the start times of the jobs that minimize the makespan while satisfying all constraints.  Print the start times of the jobs (start_time).

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

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

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

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

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

model = Model()

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

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

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

# Solve
model.solve()

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


The room assignment problem involves assigning a set of requests to a limited number of rooms such that each request is assigned to one room for its entire duration. Some requests may already have a room pre-assigned. The main constraint is that a room can only serve one request at a time, meaning no overlapping requests can be assigned to the same room.  Print the room assignments for the requests (room_assignments).

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

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

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

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

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

    m = Model()

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

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

    return m, requestvars

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

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


Send More Money Puzzle  The "Send More Money" puzzle is a classic cryptarithmetic problem where each letter represents a unique digit. The goal is to assign digits to the letters such that the following equation holds true:     SEND  + MORE  ------   MONEY  Each letter must be assigned a unique digit from 0 to 9, and the first letter of each word cannot be zero.  Print the values assigned to each letter (s, e, n, d, m, o, r, y).

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

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

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

# Solve
model.solve()

# Print
solution = {
    "s": s.value(),
    "e": e.value(),
    "n": n.value(),
    "d": d.value(),
    "m": m.value(),
    "o": o.value(),
    "r": r.value(),
    "y": y.value()
}
print(json.dumps(solution))
# End of CPMPy script


Set (stylized as SET or SET!) is a real-time card game designed by Marsha Falco in 1974 and published by Set Enterprises in 1991. The deck consists of 81 unique cards that vary in four features across three possibilities for each kind of feature: number of shapes (one, two, or three), shape (diamond, squiggle, oval), shading (solid, striped, or open), and color (red, green, or purple).[2] Each possible combination of features (e.g. a card with three striped green diamonds) appears as a card precisely once in the deck.  In the game, certain combinations of three cards are said to make up a "set". For each one of the four categories of features—color, number, shape, and shading—the three cards must display that feature as either a) all the same, or b) all different. Put another way: For each feature the three cards must avoid having two cards showing one version of the feature and the remaining card showing a different version.  For example, 3 solid red diamonds, 2 solid green squiggles, and 1 solid purple oval form a set, because the shadings of the three cards are all the same, while the numbers, the colors, and the shapes among the three cards are all different.  For any set, the number of features that are constant (the same on all three cards) and the number of features that differ (different on all three cards) may break down as: all 4 features differing; or 1 feature being constant and 3 differing; or 2 constant and 2 differing; or 3 constant and 1 differing. (All 4 features being constant would imply that the three cards in the set are identical, which is impossible since no cards in the Set deck are identical.)  Print the indices of the winning set of cards (winning_cards).

# Data
# Constants
numbers = ONE, TWO, THREE = 1, 2, 3
colors = RED, PURPLE, GREEN = 1, 2, 3
shapes = DIAMOND, RECT, ELLIPSE = 1, 2, 3
fills = FULL, EMPTY, STRIPED = 1, 2, 3

# Make the card deck
cards_data = [
    [ONE, EMPTY, GREEN, DIAMOND],
    [TWO, STRIPED, RED, RECT],
    [THREE, STRIPED, GREEN, DIAMOND],
    [THREE, FULL, RED, DIAMOND],
    [ONE, STRIPED, GREEN, DIAMOND],
    [ONE, EMPTY, RED, DIAMOND],
    [TWO, FULL, PURPLE, DIAMOND],
    [THREE, FULL, PURPLE, ELLIPSE],
    [THREE, FULL, GREEN, RECT],
    [ONE, FULL, PURPLE, DIAMOND],
    [ONE, STRIPED, PURPLE, DIAMOND],
    [ONE, FULL, GREEN, RECT]
]
# End of data

# Import libraries
from cpmpy import *
import json

# Parameters
cards = cpm_array(cards_data)
given_numbers, given_fills, given_colors, given_shapes = cards[:, 0], cards[:, 1], cards[:, 2], cards[:, 3]

# decision variables
n_cards = len(cards)
winning_cards = intvar(0, n_cards - 1, shape=3, name="winning")  # indices of the winning cards
c1, c2, c3 = winning_cards

# constraints
model = Model()
model += AllDifferent(winning_cards)

# numbers must be all equal or all different
winning_nbs = [given_numbers[c1], given_numbers[c2], given_numbers[c3]]
model += AllEqual(winning_nbs) | AllDifferent(winning_nbs)

# filling pattern must be all equal or all different
winning_fills = [given_fills[c1], given_fills[c2], given_fills[c3]]
model += AllEqual(winning_fills) | AllDifferent(winning_fills)

# colors must be all equal or all different
winning_colors = [given_colors[c1], given_colors[c2], given_colors[c3]]
model += AllEqual(winning_colors) | AllDifferent(winning_colors)

# shapes must be all equal or all different
winning_shapes = [given_shapes[c1], given_shapes[c2], given_shapes[c3]]
model += AllEqual(winning_shapes) | AllDifferent(winning_shapes)

# Solve
model.solve()

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


The goal of Sudoku is to fill a 9x9 grid with numbers so that each row, column and 3x3 section contain all the digits between 1 and 9.  Input is a grid with some cells filled with numbers and some empty.  Print the solved grid (grid).

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

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

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

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

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

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

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

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

# Solve
model.solve()

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


Someone in Dreadsbury Mansion killed Aunt Agatha. Agatha, the butler, and Charles live in Dreadsbury Mansion, and are the only ones to live there. A killer always hates, and is no richer than his victim. Charles hates noone that Agatha hates. Agatha hates everybody except the butler. The butler hates everyone not richer than Aunt Agatha. The butler hates everyone whom Agatha hates. Noone hates everyone. Who killed Agatha?  Print the index of the killer (killer).

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

# Import libraries
from cpmpy import *
import json

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

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

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

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

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

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

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

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

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

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

# Solve
model.solve()

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


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

# Data
stage = 8  # Number of stages
# End of data

# Import libraries
from cpmpy import *
import json

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

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

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

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

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

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

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

# Solve
model.solve()

# Print
solution = {"wolf_pos": wolf_pos.value().tolist(), "goat_pos": goat_pos.value().tolist(),
            "cabbage_pos": cabbage_pos.value().tolist(), "boat_pos": boat_pos.value().tolist()}
print(json.dumps(solution))
# End of CPMPy script


# Misc: Based on PyCSP3's Zebra.py by C. Lecoutre, https://github.com/xcsp3team/pycsp3/blob/master/problems/csp/single/Zebra.py

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

# Import libraries
from cpmpy import *
import json

n_houses = 5

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

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

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

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

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

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

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

# Print
solution = {
    "colors": colors.value().tolist(),
    "nations": nations.value().tolist(),
    "jobs": jobs.value().tolist(),
    "pets": pets.value().tolist(),
    "drinks": drinks.value().tolist(),
}
print(json.dumps(solution))
# End of CPMPy script


My bank card has a 4 digit pin, abcd. I use the following facts to help me remember it:  - no two digits are the same - the 2-digit number cd is 3 times the 2-digit number ab - the 2-digit number da is 2 times the 2-digit number bc  Print the PIN (a, b, c, d).

# Import libraries
from cpmpy import *
import json

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

# Constraints
model = Model()

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

# Solve
model.solve()

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


We want to climb a stair of n steps with [m1, m2] steps at a time. For example a stair of 4 steps with m1 = 1, and m2 = 2 can be climbed with a sequence of four one-step moves or with two two-steps moves. Find a way to climb a stair of 20 steps with m1 = 3 and m2 = 5, i.e. you can take only 3 or 4 or 5 steps at a time.  Print the number of steps (steps) taken at each move as a list of 20 integers, where each integer is between 3 and 5, or 0 if no steps are taken at that move, i.e. after we reach the top of the stair with the last move.

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

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

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

# Model setup
model = Model()

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

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

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

# Solve
model.solve()

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


We want to assign a different colour to the following countries: Belgium, Denmark, France, Germany, Netherlands and Luxembourg. Two neighbouring countries cannot have the same colour. You can use integers starting from 1 to represent the colours. Find a colouring that minimizes the number of colours used.  Print the colours assigned to each country as a list (colors).

# Data
graph = [  # the adjacency of the countries, (i, j) means that country i is adjacent to country j
    [3, 1],
    [3, 6],
    [3, 4],
    [6, 4],
    [6, 1],
    [1, 5],
    [1, 4],
    [4, 5],
    [4, 2]
]
# End of data

# Import libraries
from cpmpy import *
import json

num_edges = 9
num_nodes = 6

# Decision Variables
colors = intvar(1, num_nodes, shape=num_nodes)  # the colour assigned to each country

# Constraints
model = Model()

# Two neighbouring countries cannot have the same colour
for i, j in graph:
    # Python uses 0-based indexing, but the countries are 1-based, so we need to subtract 1 from the indices
    model += colors[i - 1] != colors[j - 1]

# Objective
# Find a colouring that minimizes the number of colours used
model.minimize(max(colors))

# Solve
model.solve()

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


A farmer has 25 cows numbered 1 to 25. number 1 cow gives 1kg milk, number 2 gives 2 kg... number and so on up to number 25 that gives 25 kg per day. The farmer has 5 sons and he wants to distribute his cows to them: 7 to the first, 6 to the second and so on down to 3 to the last, however, the total quantity of milk produced should be the same: how can he distribute the cows?  Print the assignment of cows to sons (cow_assignments), as a list of 25 integers from 0 to 4.

# Import libraries
from cpmpy import *
import json

# Parameters
num_cows = 25
num_sons = 5
milk_per_cow = list(range(1, num_cows + 1))
cows_per_son = [7, 6, 5, 4, 3]

total_milk = sum(milk_per_cow)  # Total milk produced by all cows
total_milk_per_son = total_milk // num_sons  # Total milk each son should get

# Decision variables
# Each cow is assigned to a son, represented by 0-4
cow_assignments = intvar(0, num_sons - 1, shape=num_cows)

# Constraints
model = Model()

# Each son gets a specific number of cows
for son in range(num_sons):
    model += sum(cow_assignments == son) == cows_per_son[son]

# The total milk production for each son is equal
for son in range(num_sons):
    model += sum(milk_per_cow[i] * (cow_assignments[i] == son) for i in range(num_cows)) == total_milk_per_son

# Solve
model.solve()

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


Baker, Cooper, Fletcher, Miller, and Smith live on the first five floors of an apartment house. Baker does not live on the fifth floor. Cooper does not live on the first floor. Fletcher does not live on either the fifth or the first floor. Miller lives on a higher floor than does Cooper. Smith does not live on a floor adjacent to Fletcher’. Fletcher does not live on a floor adjacent to Cooper’s. They all live on different floors. Find the floors where these people live.  Print the floors where each person lives (B, C, F, M, S).

# Import libraries
from cpmpy import *
import json

# Decision Variables
B = intvar(1, 5)  # Baker
C = intvar(1, 5)  # Cooper
F = intvar(1, 5)  # Fletcher
M = intvar(1, 5)  # Miller
S = intvar(1, 5)  # Smith

# Constraints
model = Model()

model += B != 5  # Baker does not live on the fifth floor
model += C != 1  # Cooper does not live on the first floor
model += (F != 5) & (F != 1)  # Fletcher does not live on either the fifth or the first floor
model += M > C  # Miller lives on a higher floor than does Cooper
model += abs(S - F) != 1  # Smith does not live on a floor adjacent to Fletcher
model += abs(F - C) != 1  # Fletcher does not live on a floor adjacent to Cooper
model += AllDifferent([B, C, F, M, S])  # They all live on different floors

# Solve
model.solve()

# Print the solution
solution = {"B": B.value(), "C": C.value(), "F": F.value(), "M": M.value(), "S": S.value()}
print(json.dumps(solution))
# End of CPMPy script


A kid goes into a grocery store and buys four items. The cashier charges $7.11, the kid pays and is about to leave when the cashier calls the kid back, and says "Hold on, I multiplied the four items instead of adding them; I’ll try again; Hah, with adding them the price still comes to $7.11". What were the prices of the four items?  Print the prices of the four items (prices) in cents.

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

# Parameters
total_price = 711  # total price in cents
num_items = 4

# Decision variables (prices are considered in cents)
prices = intvar(1, total_price, shape=num_items)

# Constraints
model = Model()

# The sum of the prices in cents is equal to the total price in cents
model += sum(prices) == total_price

# The product of the prices should equal to the scaled total price in cents (to account for the multiplication)
model += np.prod(prices) == total_price * (100 ** (num_items - 1))

# Solve
model.solve()

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


A boy wants to give an apple to a girl. To get to her, he has to pass through five gates, each with a guard. He bribes each guard with half of his apples, plus one. The boy does not have a knife, therefore he gives the guard an integer number of apples. After he’s given the apple to the girl, he has no apples left.  Print a list of 6 numbers (apples), containing the number of apples before each gate, plus the number of apples after the last gate.

# Import libraries
from cpmpy import *
import json

# Parameters
num_gates = 5

# Decision Variables
apples = intvar(0, 100, shape=num_gates + 1)  # the number of apples before each gate plus after the last gate

# Constraints
model = Model()

# The boy is left with no apples after giving the apple to the girl, so he has 1 apple after the last gate.
model += apples[-1] == 1

# At each guard, the boy gives half of his apples, plus one.
for i in range(1, num_gates + 1):
    has_before = apples[i - 1]
    has_after = apples[i]
    model += has_before == 2 * (has_after + 1)

# Solve
model.solve()

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


Find a combination of 4 different numbers between 1 and 100, such that the sum of the squares of the two first numbers is equal to the sum of the squares of the other two numbers, i.e. a^2 + b^2 = c^2 + d^2 for some a, b, c, d in {1, 100}, a != b != c != d.  Print the numbers (a, b, c, d).

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

range_min = 1
range_max = 100

# Decision variables
a, b, c, d = intvar(range_min, range_max, shape=4)

# Constraints
model = Model()

# Sum of squares of any two numbers is equal to the sum of squares of the other two numbers
model += (a**2 + b**2) == (c**2 + d**2)

# Constraints to ensure all variables are distinct
model += AllDifferent([a, b, c, d])

# Solve
model.solve()

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