1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
//! Here is an example to describe how a typical model is formulated.
//! It is a rendition of a combinatorial optimization classic:
//! the [Knapsack problem](https://en.wikipedia.org/wiki/Knapsack_problem).
//!
//! Let's say we are building a brand new PC. We want to play AAA games without going bankrupt.

use copper::views::ViewExt;

fn main() {
    // All problem formulations will start with a model object
    let mut m = copper::Model::default();

    // How many monitors do we buy: we need at least one, but not more than three
    let n_monitors = m.new_var(1, 3).unwrap();

    // All monitors cost the same, and each additional monitor provides the same bump to our score
    let monitor_price = 100;
    let monitor_score = 250;

    // Each GPU model has a fixed price, and an associated benchmark score
    let gpu_prices = [150, 250, 500];
    let gpu_scores = [100, 400, 800];

    // We use binary decision variables to represent "do I pick this GPU?"
    let gpus: Vec<_> = m.new_vars_binary(gpu_scores.len()).collect();

    // For each potential GPU, we multiply its price (and score) by whether or not it is selected.
    // The sum of these terms gives us the price and score of the selected GPU.
    let gpu_price = m.sum_iter(gpus.iter().zip(gpu_prices).map(|(x, price)| x.times(price)));
    let gpu_score = m.sum_iter(gpus.iter().zip(gpu_scores).map(|(x, score)| x.times(score)));

    // This expression is the overall price of our build
    let price = m.add(gpu_price, n_monitors.times(monitor_price));

    // We want to maximize this score: how much we'll value this particular build
    let score = m.add(gpu_score, n_monitors.times(monitor_score));

    // Exactly one GPU: we want to run Crysis, but our case must fit under the desk
    let n_gpus = m.sum(&gpus);
    m.equals(n_gpus, 1);

    // Grandma got us some money for our birthday, that will be our budget
    m.less_than_or_equals(price, 600);

    // Let the solver find the assignment that upholds our constraints and maximizes our score
    let solution = m.maximize(score).unwrap();

    // Our optimal build has three monitors and a mid-tier GPU. We even have some left-over cash!
    assert_eq!(solution[n_monitors], 3);
    assert_eq!(solution.get_values_binary(&gpus), vec![false, true, false]);
    assert_eq!(solution[score], 1150);
    assert_eq!(solution[price], 550);
}