# Exemple resolu : Hierarchical CSP - Configuration PC
# ---- Catalogue de composants ----
# socket : 0 = LGA1700 (Intel), 1 = AM4 (AMD)
# brand : 0 = AMD, 1 = Intel/Nvidia
# color : 0 = noir, 1 = blanc, 2 = rouge
CPUS = [
{'name': 'Intel Core i5-12400', 'socket': 0, 'tdp': 65, 'score': 70, 'price': 200, 'brand': 1},
{'name': 'Intel Core i7-12700', 'socket': 0, 'tdp': 125, 'score': 90, 'price': 350, 'brand': 1},
{'name': 'AMD Ryzen 5 5600', 'socket': 1, 'tdp': 65, 'score': 68, 'price': 180, 'brand': 0},
{'name': 'AMD Ryzen 7 5800X', 'socket': 1, 'tdp': 105, 'score': 88, 'price': 300, 'brand': 0},
]
MOTHERBOARDS = [
{'name': 'MSI PRO B660M', 'socket': 0, 'price': 130},
{'name': 'Asus ROG Z690', 'socket': 0, 'price': 250},
{'name': 'Gigabyte B550M', 'socket': 1, 'price': 120},
{'name': 'Asus TUF B550', 'socket': 1, 'price': 160},
]
GPUS = [
{'name': 'RTX 3060', 'tdp': 170, 'score': 75, 'price': 320, 'brand': 1},
{'name': 'RTX 3080', 'tdp': 320, 'score': 95, 'price': 700, 'brand': 1},
{'name': 'RX 6600', 'tdp': 132, 'score': 70, 'price': 270, 'brand': 0},
{'name': 'RX 6700 XT', 'tdp': 230, 'score': 82, 'price': 400, 'brand': 0},
]
PSUS = [
{'name': 'Corsair CV 550W', 'wattage': 550, 'price': 75, 'warranty': 3},
{'name': 'Corsair RM 750W', 'wattage': 750, 'price': 110, 'warranty': 7},
{'name': 'BeQuiet 850W', 'wattage': 850, 'price': 140, 'warranty': 5},
]
CASES = [
{'name': 'NZXT H510', 'color': 0, 'price': 90},
{'name': 'Lian Li O11 Blanc', 'color': 1, 'price': 140},
{'name': 'Fractal Pop Rouge', 'color': 2, 'price': 100},
]
BUDGET = 820 # EUR (serre pour forcer un compromis)
MIN_PERF = 145 # score CPU + GPU minimum
PREFERRED_BRAND = 0 # 0 = AMD
PREFERRED_COLOR = 0 # 0 = noir
MIN_WARRANTY = 7 # annees (garantie longue duree)
model = cp_model.CpModel()
cpu_v = model.NewIntVar(0, len(CPUS) - 1, 'cpu')
mb_v = model.NewIntVar(0, len(MOTHERBOARDS) - 1, 'mb')
gpu_v = model.NewIntVar(0, len(GPUS) - 1, 'gpu')
psu_v = model.NewIntVar(0, len(PSUS) - 1, 'psu')
case_v = model.NewIntVar(0, len(CASES) - 1, 'case')
def make_attr(var, values, name, lo=0, hi=10000):
v = model.NewIntVar(lo, hi, name)
model.AddElement(var, values, v)
return v
cpu_socket = make_attr(cpu_v, [c['socket'] for c in CPUS], 'cpu_socket', 0, 1)
cpu_tdp = make_attr(cpu_v, [c['tdp'] for c in CPUS], 'cpu_tdp', 0, 200)
cpu_score = make_attr(cpu_v, [c['score'] for c in CPUS], 'cpu_score', 0, 100)
cpu_price = make_attr(cpu_v, [c['price'] for c in CPUS], 'cpu_price', 0, 500)
cpu_brand = make_attr(cpu_v, [c['brand'] for c in CPUS], 'cpu_brand', 0, 1)
mb_socket = make_attr(mb_v, [m['socket'] for m in MOTHERBOARDS], 'mb_socket', 0, 1)
mb_price = make_attr(mb_v, [m['price'] for m in MOTHERBOARDS], 'mb_price', 0, 400)
gpu_tdp = make_attr(gpu_v, [g['tdp'] for g in GPUS], 'gpu_tdp', 0, 400)
gpu_score = make_attr(gpu_v, [g['score'] for g in GPUS], 'gpu_score', 0, 100)
gpu_price = make_attr(gpu_v, [g['price'] for g in GPUS], 'gpu_price', 0, 800)
gpu_brand = make_attr(gpu_v, [g['brand'] for g in GPUS], 'gpu_brand', 0, 1)
psu_watt = make_attr(psu_v, [p['wattage'] for p in PSUS], 'psu_watt', 0, 1000)
psu_price = make_attr(psu_v, [p['price'] for p in PSUS], 'psu_price', 0, 200)
psu_warranty= make_attr(psu_v, [p['warranty'] for p in PSUS], 'psu_warranty',0, 10)
case_color = make_attr(case_v, [c['color'] for c in CASES], 'case_color', 0, 2)
case_price = make_attr(case_v, [c['price'] for c in CASES], 'case_price', 0, 200)
total_price = model.NewIntVar(0, 5000, 'total_price')
total_score = model.NewIntVar(0, 200, 'total_score')
total_tdp = model.NewIntVar(0, 700, 'total_tdp')
model.Add(total_price == cpu_price + mb_price + gpu_price + psu_price + case_price)
model.Add(total_score == cpu_score + gpu_score)
model.Add(total_tdp == cpu_tdp + gpu_tdp)
#contraintes mandatory
model.Add(cpu_socket == mb_socket)
model.Add(psu_watt >= total_tdp + 80)
#contraintes strong
over_budget = model.NewBoolVar('over_budget')
model.Add(total_price <= BUDGET).OnlyEnforceIf(over_budget.Not())
model.Add(total_price > BUDGET).OnlyEnforceIf(over_budget)
low_perf = model.NewBoolVar('low_perf')
model.Add(total_score >= MIN_PERF).OnlyEnforceIf(low_perf.Not())
model.Add(total_score < MIN_PERF).OnlyEnforceIf(low_perf)
#contraintes weak
wrong_cpu_brand = model.NewBoolVar('wrong_cpu_brand')
model.Add(cpu_brand == PREFERRED_BRAND).OnlyEnforceIf(wrong_cpu_brand.Not())
model.Add(cpu_brand != PREFERRED_BRAND).OnlyEnforceIf(wrong_cpu_brand)
wrong_gpu_brand = model.NewBoolVar('wrong_gpu_brand')
model.Add(gpu_brand == PREFERRED_BRAND).OnlyEnforceIf(wrong_gpu_brand.Not())
model.Add(gpu_brand != PREFERRED_BRAND).OnlyEnforceIf(wrong_gpu_brand)
wrong_color = model.NewBoolVar('wrong_color')
model.Add(case_color == PREFERRED_COLOR).OnlyEnforceIf(wrong_color.Not())
model.Add(case_color != PREFERRED_COLOR).OnlyEnforceIf(wrong_color)
short_warranty = model.NewBoolVar('short_warranty')
model.Add(psu_warranty >= MIN_WARRANTY).OnlyEnforceIf(short_warranty.Not())
model.Add(psu_warranty < MIN_WARRANTY).OnlyEnforceIf(short_warranty)
M = 5
strong_cost = over_budget + low_perf
weak_cost = wrong_cpu_brand + wrong_gpu_brand + wrong_color + short_warranty
model.Minimize(M * strong_cost + weak_cost)
solver = cp_model.CpSolver()
solver.parameters.max_time_in_seconds = 30
status = solver.Solve(model)
if status in (cp_model.OPTIMAL, cp_model.FEASIBLE):
ci, mi, gi, pi, ki = (solver.Value(cpu_v), solver.Value(mb_v),
solver.Value(gpu_v), solver.Value(psu_v), solver.Value(case_v))
brands = {0: 'AMD', 1: 'Intel/Nvidia'}
colors = ['noir', 'blanc', 'rouge']
total = CPUS[ci]['price'] + MOTHERBOARDS[mi]['price'] + GPUS[gi]['price'] + PSUS[pi]['price'] + CASES[ki]['price']
score = CPUS[ci]['score'] + GPUS[gi]['score']
tdp_tot = CPUS[ci]['tdp'] + GPUS[gi]['tdp']
print("Configuration PC optimale \n")
print(f" CPU : {CPUS[ci]['name']:<28} {CPUS[ci]['price']:>4} EUR score={CPUS[ci]['score']} {brands[CPUS[ci]['brand']]}")
print(f" Carte mere : {MOTHERBOARDS[mi]['name']:<28} {MOTHERBOARDS[mi]['price']:>4} EUR")
print(f" GPU : {GPUS[gi]['name']:<28} {GPUS[gi]['price']:>4} EUR score={GPUS[gi]['score']} {brands[GPUS[gi]['brand']]}")
print(f" Alimentation: {PSUS[pi]['name']:<27} {PSUS[pi]['price']:>4} EUR {PSUS[pi]['wattage']}W garantie {PSUS[pi]['warranty']} ans")
print(f" Boitier : {CASES[ki]['name']:<28} {CASES[ki]['price']:>4} EUR {colors[CASES[ki]['color']]}")
print(f"\n=== Bilan ===")
print(f" Prix total : {total} EUR (budget {BUDGET}) => {'OK' if total <= BUDGET else 'DEPASSE de ' + str(total - BUDGET) + ' EUR'}")
print(f" Score total : {score} (min {MIN_PERF}) => {'OK' if score >= MIN_PERF else 'INSUFFISANT'}")
print(f" TDP total : {tdp_tot}W + 80W = {tdp_tot + 80}W (alim {PSUS[pi]['wattage']}W)")
print(f"\nContraintes souples")
s_ok, s_ko = 'satisfaite', 'VIOLEE'
print(f" [STRONG] Budget : {s_ok if total <= BUDGET else s_ko}")
print(f" [STRONG] Perf min : {s_ok if score >= MIN_PERF else s_ko}")
print(f" [WEAK] Marque CPU : {brands[CPUS[ci]['brand']]} {'(prefere)' if CPUS[ci]['brand'] == PREFERRED_BRAND else '(non prefere -- compromis budget/perf)'}")
print(f" [WEAK] Marque GPU : {brands[GPUS[gi]['brand']]} {'(prefere)' if GPUS[gi]['brand'] == PREFERRED_BRAND else '(non prefere -- compromis budget/perf)'}")
print(f" [WEAK] Couleur : {colors[CASES[ki]['color']]} {'(prefere)' if CASES[ki]['color'] == PREFERRED_COLOR else '(non prefere)'}")
print(f" [WEAK] Garantie : {PSUS[pi]['warranty']} ans {'(OK)' if PSUS[pi]['warranty'] >= MIN_WARRANTY else '(courte)'}")
n_strong = solver.Value(over_budget) + solver.Value(low_perf)
n_weak = (solver.Value(wrong_cpu_brand) + solver.Value(wrong_gpu_brand)
+ solver.Value(wrong_color) + solver.Value(short_warranty))
print(f"\n Score objectif : {M} x {n_strong} (strong) + {n_weak} (weak) = {M * n_strong + n_weak}")