Garden Optimization Example
The Garden Optimization problem finds the optimal placement of plants in a rectangular garden modeled as a graph. Plants have friendly, neutral, or antagonistic relationships, and the goal is to maximize friendly adjacencies while respecting species counts and size-based placement constraints.
import getpass
import os
from dotenv import load_dotenv
from luna_quantum.algorithms import SCIP
from luna_usecases.garden_optimization import (
GardenOptimizationCollection,
GardenOptimizationData,
GardenOptimizationFormulation,
GardenOptimizationInstance,
)
load_dotenv()
if "LUNA_API_KEY" not in os.environ:
os.environ["LUNA_API_KEY"] = getpass.getpass("Enter your Luna API key: ")
Create Data
Generate a small garden optimization instance with a 2x3 grid and 4 plant species using the generate_random() factory method.
data = GardenOptimizationData.generate_random(
n_rows=2,
n_cols=3,
n_species=4,
seed=42,
)
print(data.to_string())
Plot Data
Visualize the garden graph layout.
Create Formulation
Minimize antagonistic adjacencies while satisfying placement constraints: one plant per pot, species counts, and size-based row assignment.
Garden Optimization Formulation:
Pots: 6
Species: 4
Decision Variables:
x[p,s] in {0,1} for p = 0, ..., 5, s = 0, ..., 3
x[p,s] = 1 if a plant of species s is placed in pot p
Total: 24 binary variables
Objective:
minimize sum_{(p1,p2) in E} sum_{s1,s2} compatibility[s1][s2] * x[p1,s1] * x[p2,s2]
Constraints:
1. One plant per pot (6 constraints):
sum_s x[p,s] == 1 for all p = 0, ..., 5
2. Species count met (4 constraints):
sum_p x[p,s] == count[s] for all s = 0, ..., 3
Create Instance
Combine data and formulation into a solvable instance.
instance = GardenOptimizationInstance(data=data, formulation=formulation)
print(instance.to_string())
Data:Garden Optimization Data:
Pots: 6
Edges: 7
Species: 4
Counts: [1, 2, 1, 2]
Formulation:Garden Optimization Formulation:
Pots: 6
Species: 4
Decision Variables:
x[p,s] in {0,1} for p = 0, ..., 5, s = 0, ..., 3
x[p,s] = 1 if a plant of species s is placed in pot p
Total: 24 binary variables
Objective:
minimize sum_{(p1,p2) in E} sum_{s1,s2} compatibility[s1][s2] * x[p1,s1] * x[p2,s2]
Constraints:
1. One plant per pot (6 constraints):
sum_s x[p,s] == 1 for all p = 0, ..., 5
2. Species count met (4 constraints):
sum_p x[p,s] == count[s] for all s = 0, ..., 3
Formulate Model
Translate the instance into a mathematical optimization model.
Solve and Interpret
Solve the model with SCIP and interpret the raw result into a use-case-specific solution.
scip = SCIP()
job = scip.run(model)
sol = job.result()
uc_solution = instance.interpret(sol)
print(uc_solution.to_string())
/Users/maximilianjanetschek/PycharmProjects/luna-usecases/.venv/lib/python3.13/site-packages/rich/live.py:260:
UserWarning: install "ipywidgets" for Jupyter support
warnings.warn('install "ipywidgets" for Jupyter support')
2026-05-29 11:33:29 INFO Sleeping for 5.0 seconds. Waiting and checking a function in a loop.
2026-05-29 11:33:36 INFO Sleeping for 10.0 seconds. Waiting and checking a function in a loop.
2026-05-29 11:33:47 INFO Sleeping for 15.0 seconds. Waiting and checking a function in a loop.
Garden Optimization Solution:
Friendly pairs: 5
Antagonistic pairs: 0
Valid: True
Assignment: {'(0, 0)': 2, '(0, 1)': 1, '(0, 2)': 3, '(1, 0)': 1, '(1, 1)': 0, '(1, 2)': 3}
Plot Solution
Visualize the plant placement in the garden.
Collections
Generate benchmark collections of garden optimization instances for batch processing.
collection = GardenOptimizationCollection.from_random(
min_rows=2,
max_rows=4,
n_cols=3,
n_species=3,
num_instances=2,
seed=42,
)
model = collection.instances[0].formulate()
print(model)
Model: garden_optimization<garden_optimization>
Minimize
-x_0_0 * x_1_1 + x_0_0 * x_1_2 - x_0_0 * x_3_1 + x_0_0 * x_3_2 - x_0_1 * x_1_0
+ x_0_1 * x_1_2 - x_0_1 * x_3_0 + x_0_1 * x_3_2 + x_0_2 * x_1_0
+ x_0_2 * x_1_1 + x_0_2 * x_3_0 + x_0_2 * x_3_1 - x_1_0 * x_2_1
+ x_1_0 * x_2_2 - x_1_0 * x_4_1 + x_1_0 * x_4_2 - x_1_1 * x_2_0
+ x_1_1 * x_2_2 - x_1_1 * x_4_0 + x_1_1 * x_4_2 + x_1_2 * x_2_0
+ x_1_2 * x_2_1 + x_1_2 * x_4_0 + x_1_2 * x_4_1 - x_2_0 * x_5_1
+ x_2_0 * x_5_2 - x_2_1 * x_5_0 + x_2_1 * x_5_2 + x_2_2 * x_5_0
+ x_2_2 * x_5_1 - x_3_0 * x_4_1 + x_3_0 * x_4_2 - x_3_1 * x_4_0
+ x_3_1 * x_4_2 + x_3_2 * x_4_0 + x_3_2 * x_4_1 - x_4_0 * x_5_1
+ x_4_0 * x_5_2 - x_4_1 * x_5_0 + x_4_1 * x_5_2 + x_4_2 * x_5_0
+ x_4_2 * x_5_1
Subject To
one_plant_pot_0: x_0_0 + x_0_1 + x_0_2 == 1
one_plant_pot_1: x_1_0 + x_1_1 + x_1_2 == 1
one_plant_pot_2: x_2_0 + x_2_1 + x_2_2 == 1
one_plant_pot_3: x_3_0 + x_3_1 + x_3_2 == 1
one_plant_pot_4: x_4_0 + x_4_1 + x_4_2 == 1
one_plant_pot_5: x_5_0 + x_5_1 + x_5_2 == 1
species_count_0: x_0_0 + x_1_0 + x_2_0 + x_3_0 + x_4_0 + x_5_0 == 4
species_count_1: x_0_1 + x_1_1 + x_2_1 + x_3_1 + x_4_1 + x_5_1 == 1
species_count_2: x_0_2 + x_1_2 + x_2_2 + x_3_2 + x_4_2 + x_5_2 == 1
Binary
x_0_0 x_0_1 x_0_2 x_1_0 x_1_1 x_1_2 x_2_0 x_2_1 x_2_2 x_3_0 x_3_1 x_3_2 x_4_0
x_4_1 x_4_2 x_5_0 x_5_1 x_5_2