Reference implementation of SEFOP for Python in a simplified manner
This repo solves a knapsack problem. Given a budget and a weight limit, pick the combination of products that maximizes total calories.
Sets
-
$I$ : set of candidate products, indexed by$i \in I$
Parameters
-
$\text{price}_i$ ,$\text{weight}_i$ ,$\text{calories}_i$ : unit price (USD), unit weight (kg), and nutritional value of product$i \in I$ -
$B$ : budget, in USD (max_budget_usd) -
$W$ : weight limit, in kg (max_weight_kg)
Decision variable
-
$x_i \in \mathbb{Z}_{\ge 0}$ for each$i \in I$ : number of units of product$i$ selected
Objective — maximize total calories:
Constraints
Because
Example — Data from data/2/data.json
| Product | Price | Weight | Calories |
|---|---|---|---|
| Apple | $1.00 | 0.50 kg | 100 |
| Chocolate | $5.00 | 1.00 kg | 50 |
Constraints: budget $10.00 and weight limit 2.00 kg
Optimal solution:
| Product | Units | Cost | Weight | Calories |
|---|---|---|---|---|
| Apple | 4 | $4.00 | 2.00 kg | 400 |
| Chocolate | 0 | $0.00 | 0.00 kg | 0 |
| Total calories | Total cost / Budget | Total weight / Max weight |
|---|---|---|
| 400 | $4.00 / $10.00 | 2.00 kg / 2.00 kg |
The solver picks 4 apples — chocolate costs 10× more per calorie, so it never appears in the optimal solution.
src/
domain/ # pure entities — Product, Request, Recommendation
services/ # data loading and application service
engine/
orchestrator.py # pipeline coordinator: pre → strategy → post
preprocessing/ # filter infeasible products before solving
optimization/ # MIP solver and greedy heuristic
postprocessing/ # sort and refine the recommendation
cli.py # CLI entry point
tests/ # mirrors src/ structure
data/ # sample problem instances
git clone https://github.com/sefop/sefop-python-starter.git
cd sefop-python-starterpy -3.12 -m venv .venv
.venv\Scripts\activate # On Windows
# source .venv/bin/activate # On macOS/Linuxpip install -r requirements.txt
pip install -e .The -e flag installs the package in editable mode, making your source code directly importable. This is the standard Python development practice — no need to set PYTHONPATH or reinstall when you edit code.
What breaks if you skip this step? pytest still works, since pyproject.toml adds src to the path just for pytest. But python -m cli 1 will fail with an import error — cli.py imports domain, engine, etc. as top-level packages, and without the editable install Python has no way to find them under src/ outside of pytest.
pytestpytest -m "not integration"pytest -m integrationEach integration test drives the real CLI end-to-end against a pre-built "situation" (tests/resources/<situation>/) and checks two things: that the generated formulation (model.lp) matches a golden file, so an unintended change to the model is caught even though the formulation is otherwise only unit-tested in isolation; and situation-specific conditions on the outcome — e.g. the expected optimal calories, that an infeasible request exits non-zero and writes no solution, that tied optimal solutions still report the shared optimal total, or that a large instance correctly routes to the heuristic instead of the MIP solver.
pytest tests/domain/ # domain unit tests only
pytest tests/services/ # service tests only
pytest tests/engine/ # engine, strategies, pre/postprocessing testsCI runs two checks before the test suite; both are fast, so run them locally before pushing to avoid a red PR.
black --check src tests # verify formatting only, no changes written
black src tests # reformat in placeConfiguration (line length, target Python version) lives in pyproject.toml's [tool.black] section, so the local command and CI always agree.
mypyConfiguration lives in pyproject.toml's [tool.mypy] section.
python -m cli 1 # solve request from data/1/data.json
python -m cli 2 # solve request from data/2/data.jsonThis project demonstrates Clean Architecture applied to optimization:
domain/— Pure business logic (Product, Request, Recommendation) with no external dependenciesservices/— Data loading (JsonDataLoader) and the application service that wires loading to the engineengine/orchestrator.py— Pipeline coordinator: runs preprocessing → picks a strategy → runs postprocessingengine/preprocessing/— Filters out products that can never be selected (individually infeasible)engine/optimization/— Solver implementations:- Greedy heuristic — fast, approximate solution for large problems
- MIP solver — first assembles a solver-agnostic model (the formulation: variables, constraints, objective, in
model_abstraction/, built fromcomponents/) using pure Python with no solver dependency. Only once that model exists is it handed to a technology-specific solver (solvers/highs_solver.py) to actually optimize. Because the formulation is a plain Python object, it can be unit-tested on its own — seetests/engine/optimization_strategy/mip/model_abstraction/and.../components/— independent of whether HiGHS or any other solver is installed.
engine/postprocessing/— Refines the recommendation (e.g., sorts products by quantity)cli.py— Entry point that loads data and calls the solver