DPO — Debt-Payment Optimization
A population-based metaheuristic that intentionally accepts worse moves, records them as debt, and repays with interest to escape local optima.
What is DPO?
DPO is an optimization algorithm inspired by financial debt dynamics. Unlike simulated annealing (which forgets history) or evolutionary algorithms (which only keep elites), DPO deliberately takes worse solutions, records the degradation as debt, then repays it with interest — forcing the search to overshoot past local optima and converge aggressively toward the global best.
$$X_{\text{new}} = X + \underbrace{\beta \cdot \text{debt}}{\text{repayment}} + \underbrace{\gamma \cdot \text{debt}}{\text{overshoot}} + \underbrace{\delta \cdot (\text{gBest} - X)}_{\text{global pull}}$$
Key features:
- 5 built-in presets — NAS, HPO, Resource Allocation, Pathfinding/TSP, Scheduling
- 4 problem types — Continuous, Combinatoric, NAS, and Hybrid (mixed continuous + discrete)
- Island model with specialized sub-populations and migration
- NSGA-II multi-objective support with Pareto archiving
- Adaptive controls — mutation, acceptance, temperature, debt memory all self-tune
- One dependency — only
numpy>=1.21.0required; everything else is optional
Benchmark Results
Comprehensive benchmark across 24 datasets (NASBench-201, HPOBench, HPOLib, Synthetic, Noisy Synthetic), 14 methods, 3 seeds, 1,008 total runs:
Overall Ranking (all 24 datasets)
| Rank | Method | Mean Rank ↓ | #1 Wins | Mean AUC |
|---|---|---|---|---|
| 1 | JADE | 3.19 | 5 | 0.9581 |
| 2 | GWO | 3.83 | 4 | 0.9616 |
| 3 | DPO | 4.27 | 6 | 0.9301 |
| 4 | DE | 5.23 | 3 | 0.9264 |
| 5 | ACO | 6.02 | 0 | 0.9138 |
| 6 | GA | 6.19 | 3 | 0.9529 |
| 7 | PSO | 8.58 | 0 | 0.9206 |
| 8 | WOA | 10.12 | 1 | 0.9178 |
| 9 | FA | 11.17 | 0 | 0.9098 |
| 10 | ABC | 11.67 | 0 | 0.9013 |
| 11 | SA | 14.00 | 0 | 0.5282 |
DPO achieves the most #1 wins (6) of any method and ranks 3rd overall across all benchmarks. Ablation variants (DPO-NoDebt, DPO-NoAccept, DPO-NoRepay) consistently rank lower, confirming each component contributes to performance.
Per-Family Highlights
| Benchmark Family | DPO Rank | Datasets | Notable Results |
|---|---|---|---|
| NASBench-201 | 1st–5th | 3 | #1 on CIFAR-10 & CIFAR-100 |
| HPOBench | 4th–5th | 8 | #1 on Credit-G, Car |
| HPOLib | 1st–8th | 5 | #1 on Naval Propulsion, Slice Localization |
| Synthetic (20-D) | 4th | 6 | Strong on high-dimensional BBOB functions |
| Noisy Synthetic | 4th | 3 | Robust under evaluation noise |
NASBench-201 Detailed Results
| Dataset | DPO Score | Best Possible | DPO Rank | 95% Convergence |
|---|---|---|---|---|
| CIFAR-10 | 0.9172 | 0.9172 | 1st | iter 29.7 |
| CIFAR-100 | 0.7372 | 0.7372 | 1st | iter 34.0 |
| ImageNet-16-120 | 0.4706 | 0.4740 | 5th | iter 25.7 |
HPOBench Highlights
| Dataset | DPO Score | DPO Rank |
|---|---|---|
| Australian | 0.9682 | 4th |
| Blood Transfusion | 0.7747 | 2nd |
| Car | 1.0000 | 1st (tied) |
| Credit-G | 0.9241 | 1st |
| Segment | 0.9757 | 2nd |
Reproduce all benchmarks:
python -m dpo.benchmarks.hpo_comprehensive_benchmark --seeds 3 --population 40 --iterations 60
Installation
pip install dpo
From source (editable):
git clone https://github.com/Arya1718/dpo.git
cd dpo
pip install -e .
Optional extras:
pip install dpo[dev] # pytest, black, flake8, mypy
pip install dpo[gpu] # PyTorch support
pip install dpo[benchmarks] # NASBench, HPOBench backends
pip install dpo[docs] # Sphinx documentation
Requirements: Python ≥ 3.8, NumPy ≥ 1.21.0
Quick Start
One-Line Optimization
from dpo import dpo
result = dpo(preset='nas')
print(result['best_fitness'])
print(result['best_accuracy'])
Recommended: DPO_Universal
from dpo.core.universal import DPO_Universal, DPO_Presets
config = DPO_Presets.NAS_Config(population_size=60, max_iterations=200)
optimizer = DPO_Universal(config=config)
result = optimizer.optimize()
print(f"Best fitness: {result['best_fitness']:.6f}")
print(f"Best accuracy: {result['best_accuracy']:.4f}")
print(f"Best solution: {optimizer.get_best_solution()}")
How to Use DPO
DPO provides three levels of control, from simplest to most advanced:
Level 1 ─ dpo(preset='nas') # One function call
Level 2 ─ DPO_Universal(config=..., problem=...) # Preset + custom problem
Level 3 ─ DPO_NAS(config=..., estimator=...) # Full manual control
Available Presets
| Preset | Factory Method | Best For | Key Traits |
|---|---|---|---|
'nas' |
DPO_Presets.NAS_Config() |
Neural Architecture Search | 3 islands, NSGA-II, accuracy-dominant |
'hpo' |
DPO_Presets.HyperparameterTuning_Config() |
ML hyperparameter tuning | single island, continuous mode, long debt memory |
'resource' |
DPO_Presets.ResourceAllocation_Config() |
Cloud/network balancing | 4 islands, heaviest constraint penalty |
'pathfinding' |
DPO_Presets.Pathfinding_Config() |
TSP, routing, paths | 5 islands, highest exploration, fast migration |
'scheduling' |
DPO_Presets.Scheduling_Config() |
Job/factory scheduling | 4 islands, highest cost weight |
DPO also has built-in DPO_Config presets:
| Config Preset | Use Case | Population | Iterations |
|---|---|---|---|
DPO_Config.fast() |
Quick testing | 20 | 50 |
DPO_Config.balanced() |
General use | 40 | 150 |
DPO_Config.thorough() |
Deep search | 80 | 300 |
DPO_Config.publication() |
Reproducible research | 80 | 300 |
DPO_Config.continuous_analytic() |
Continuous benchmarks | 30 | 100 |
Problem Types & Complete Examples
1. Continuous Optimization (HPO, Calibration, Black-Box)
Use dpo_optimize() for the simplest setup, or ContinuousOptimizationProblem for more control.
Simple (one function call):
from dpo import dpo_optimize
def objective(params):
lr = params['learning_rate']
dropout = params['dropout']
# Simulated training loss
loss = (lr - 0.001)**2 + (dropout - 0.3)**2
accuracy = max(0.0, 0.95 - loss)
return loss, {
'accuracy': accuracy,
'latency_ms': 1.0,
'memory_mb': 1.0,
'flops_m': 1.0,
}
result = dpo_optimize(
objective=objective,
bounds=[(1e-5, 0.1), (0.0, 0.9)],
names=['learning_rate', 'dropout'],
preset='balanced',
max_iterations=100,
population_size=40,
)
print(f"Best params: {result['best_solution']}")
print(f"Best loss: {result['best_fitness']:.6f}")
With Problem class:
from dpo.core.problem import ContinuousOptimizationProblem
from dpo.core.universal import DPO_Presets, DPO_Universal
def objective(params):
x, y = params['x'], params['y']
fitness = x**2 + y**2
return fitness, {
'accuracy': 1.0 / (1.0 + fitness),
'latency_ms': 1.0,
'memory_mb': 1.0,
'flops_m': 1.0,
}
problem = ContinuousOptimizationProblem(
objective_fn=objective,
param_bounds=[(-5.0, 5.0), (-5.0, 5.0)],
param_names=['x', 'y'],
)
config = DPO_Presets.HyperparameterTuning_Config(
population_size=30,
max_iterations=100,
)
optimizer = DPO_Universal(problem=problem, config=config)
result = optimizer.optimize()
best = optimizer.get_best_solution()
print(f"x={best['x']:.6f}, y={best['y']:.6f}")
print(f"Minimum: {result['best_fitness']:.8f}")
2. Combinatoric Optimization (TSP, Routing, Scheduling)
TSP (one function call):
import numpy as np
from dpo import dpo_solve_tsp
n_cities = 20
coords = np.random.default_rng(42).uniform(0, 100, (n_cities, 2))
dist = np.linalg.norm(coords[:, None] - coords[None, :], axis=2)
result = dpo_solve_tsp(
distance_matrix=dist,
preset='balanced',
max_iterations=120,
population_size=60,
)
print(f"Best tour length: {result['best_fitness']:.2f}")
Job scheduling:
import numpy as np
from dpo.core.problem import CombinatoricOptimizationProblem
from dpo.core.universal import DPO_Presets, DPO_Universal
n_jobs, n_machines = 30, 4
proc_times = np.random.default_rng(0).integers(1, 20, (n_jobs, n_machines))
def makespan(seq_dict):
seq = seq_dict['sequence']
machine_time = np.zeros(n_machines)
for job_idx in seq:
m = np.argmin(machine_time)
machine_time[m] += proc_times[job_idx % n_jobs, m]
cost = float(np.max(machine_time))
return cost, {
'accuracy': 1.0 / (1.0 + cost),
'latency_ms': cost,
'memory_mb': 1.0,
'flops_m': 1.0,
}
problem = CombinatoricOptimizationProblem(
objective_fn=makespan,
problem_size=n_jobs,
)
config = DPO_Presets.Scheduling_Config(population_size=45, max_iterations=120)
optimizer = DPO_Universal(problem=problem, config=config)
result = optimizer.optimize()
print(f"Best makespan: {result['best_fitness']:.1f} time units")
3. Neural Architecture Search (NAS)
from dpo.core.problem import NASProblem
from dpo.core.universal import DPO_Presets, DPO_Universal
class MyNASEstimator:
"""Your evaluator must implement estimate() returning (fitness, metrics_dict)."""
def estimate(self, arch_dict, search_mode=True, iteration=0, **kwargs):
# arch_dict contains: operations, kernels, skip_connections,
# depth_multiplier, channel_multiplier
ops = arch_dict.get('operations', [])
depth = float(arch_dict.get('depth_multiplier', 1.0))
channels = float(arch_dict.get('channel_multiplier', 1.0))
# Replace with your real training / proxy evaluation
accuracy = 0.80 + 0.05 * (depth + channels) / 3.0
latency = 20.0 + 5.0 * depth
memory = 10.0 + 5.0 * channels
flops = 80.0 + 12.0 * depth * channels
fitness = 1.0 - accuracy # DPO minimizes fitness
return fitness, {
'accuracy': accuracy,
'latency_ms': latency,
'memory_mb': memory,
'flops_m': flops,
}
problem = NASProblem(
evaluator=MyNASEstimator(),
constraints={'latency': 100.0, 'memory': 50.0, 'flops': 300.0},
)
config = DPO_Presets.NAS_Config(population_size=60, max_iterations=200)
optimizer = DPO_Universal(problem=problem, config=config)
result = optimizer.optimize()
arch = optimizer.get_best_solution()
print(f"Best accuracy: {result['best_accuracy']:.4f}")
print(f"Operations: {arch['operations']}")
print(f"Kernels: {arch['kernels']}")
print(f"Skip connects: {arch['skip_connections']}")
print(f"Depth mult: {arch['depth_multiplier']:.2f}")
print(f"Channel mult: {arch['channel_multiplier']:.2f}")
Aggressive NAS mode (best benchmark performance):
config = DPO_Presets.NAS_Config(
population_size=80,
max_iterations=300,
aggressive_mode=True, # stronger β=1.70, γ=1.30, elite_ratio=0.25
)
config.verbose = False
optimizer = DPO_Universal(problem=problem, config=config)
result = optimizer.optimize()
4. Hybrid Optimization (Mixed Continuous + Discrete)
from dpo.core.problem import HybridProblem
from dpo.core.universal import DPO_Presets, DPO_Universal
def objective(params):
lr = params.get('num_0', 1e-3) # continuous
reg = params.get('num_1', 1e-4) # continuous
model = params.get('disc_0', 'mlp') # discrete
bias = {'mlp': 0.02, 'cnn': 0.01, 'transformer': 0.015}.get(model, 0.02)
fitness = (lr - 0.002)**2 * 1000 + (reg - 0.0002)**2 * 20000 + bias
return float(fitness), {
'accuracy': float(1.0 / (1.0 + fitness)),
'latency_ms': 30.0,
'memory_mb': 12.0,
'flops_m': 80.0,
}
problem = HybridProblem(
objective_fn=objective,
numeric_bounds=[(1e-5, 1e-2), (1e-6, 1e-3)], # 2 continuous params
discrete_options={'model_family': ['mlp', 'cnn', 'transformer']}, # 1 discrete
)
config = DPO_Presets.HyperparameterTuning_Config(population_size=30, max_iterations=100)
optimizer = DPO_Universal(problem=problem, config=config)
result = optimizer.optimize()
print(f"Best fitness: {result['best_fitness']:.6f}")
5. Resource Allocation
from dpo.core.problem import ContinuousOptimizationProblem
from dpo.core.universal import DPO_Presets, DPO_Universal
def allocation_objective(params):
cpu = params['cpu_fraction']
mem = params['mem_fraction']
bw = params['bandwidth']
imbalance = abs(cpu - 0.45) + abs(mem - 0.35) + abs(bw - 0.20)
over_commit = max(0.0, cpu + mem + bw - 1.0)
fitness = imbalance + 6.0 * over_commit
return fitness, {
'accuracy': 1.0 / (1.0 + fitness),
'latency_ms': 30.0 + 20.0 * imbalance,
'memory_mb': 8.0 + 25.0 * mem,
'flops_m': 50.0 + 40.0 * bw,
}
problem = ContinuousOptimizationProblem(
objective_fn=allocation_objective,
param_bounds=[(0.0, 1.0), (0.0, 1.0), (0.0, 1.0)],
param_names=['cpu_fraction', 'mem_fraction', 'bandwidth'],
)
config = DPO_Presets.ResourceAllocation_Config(population_size=50, max_iterations=150)
optimizer = DPO_Universal(problem=problem, config=config)
result = optimizer.optimize()
best = optimizer.get_best_solution()
print(f"CPU={best['cpu_fraction']:.3f} MEM={best['mem_fraction']:.3f} BW={best['bandwidth']:.3f}")
6. Custom Problem (Full Extensibility)
Implement the Problem abstract class for any domain:
import numpy as np
from dpo.core.problem import Problem
from dpo.core.solution import NumericSolution
from dpo.core.universal import DPO_Universal
class RosenbrockProblem(Problem):
"""Custom problem: implement evaluate() and create_solution()."""
def evaluate(self, solution, **kwargs):
p = solution.to_dict()
x, y = p['x'], p['y']
fitness = (1 - x)**2 + 100 * (y - x**2)**2
return fitness, {
'accuracy': 1.0 / (1.0 + fitness),
'latency_ms': 1.0,
'memory_mb': 1.0,
'flops_m': 1.0,
}
def create_solution(self, **kwargs):
vals = np.random.uniform(-5.0, 5.0, size=2)
return NumericSolution(vals, [(-5.0, 5.0)] * 2, ['x', 'y'])
def get_problem_info(self):
return {'name': 'Rosenbrock', 'type': 'hpo'} # auto-selects HPO preset
result = DPO_Universal(problem=RosenbrockProblem()).optimize()
print(f"Minimum: {result['best_fitness']:.6f}")
Objective Function Contract
Every objective function must return a tuple of (fitness, metrics_dict):
def my_objective(params: dict) -> tuple:
fitness = ... # float, lower is better — DPO minimizes this
metrics = {
'accuracy': ..., # float, higher is better (DPO tracks this internally)
'latency_ms': ..., # float, constraint metric
'memory_mb': ..., # float, constraint metric
'flops_m': ..., # float, constraint metric
}
return fitness, metrics
Tip: If your problem doesn't have natural latency/memory/flops metrics, just set them to
1.0. DPO will still work correctly using only the fitness value.
Reading Results
optimizer.optimize() returns a result dictionary:
result = optimizer.optimize()
# ── Core metrics ──────────────────────────────────────────────
result['best_fitness'] # float — lowest fitness found (lower is better)
result['best_accuracy'] # float — highest accuracy across all iterations
result['best_architecture'] # dict — best solution as architecture dict
result['best_metrics'] # dict — full metrics of the best solution
# ── Convergence history ───────────────────────────────────────
history = result['history']
history['best_accuracy'] # List[float] — accuracy curve (monotonic improving)
history['best_fitness'] # List[float] — fitness curve per iteration
history['avg_fitness'] # List[float] — population average
history['debt_norms'] # List[float] — mean debt magnitude per iteration
history['diversity_scores'] # List[float] — population diversity
history['acceptance_rates'] # List[float] — worse-move acceptance rates
history['auc_10'] # float — area under accuracy curve at 10%
history['auc_25'] # float — AUC at 25%
history['auc_50'] # float — AUC at 50%
history['time_to_95'] # int — iteration reaching 95% of best
history['time_to_99'] # int — iteration reaching 99% of best
# ── Acceptance statistics ─────────────────────────────────────
stats = result['acceptance_stats']
stats['total_candidates'] # total candidate solutions evaluated
stats['accepted_better'] # accepted because strictly better
stats['accepted_worse'] # accepted despite being worse (DPO debt events)
stats['rejected'] # rejected candidates
# ── Helpers on the optimizer object ───────────────────────────
optimizer.get_best_solution() # dict — best solution found
optimizer.get_history() # same as result['history']
optimizer.get_config() # DPO_Config — active configuration
Plot Convergence
import matplotlib.pyplot as plt
history = optimizer.get_history()
plt.plot(history['best_accuracy'], label='DPO', color='red', lw=2)
plt.xlabel('Iteration')
plt.ylabel('Best Accuracy')
plt.title('DPO Convergence')
plt.grid(True, alpha=0.3)
plt.legend()
plt.tight_layout()
plt.savefig('convergence.png', dpi=150)
Configuration Reference
All parameters live in DPO_Config (from dpo.core.config):
Core Algorithm
| Parameter | Default | Description |
|---|---|---|
alpha_0 |
0.15 | Exploration / mutation magnitude |
beta_0 |
1.0 | Debt repayment force |
gamma_0 |
1.0 | Overshoot multiplier |
delta_0 |
0.2 | Global-best pull strength |
decay_power |
0.5 | Power-law decay exponent |
Population & Islands
| Parameter | Default | Description |
|---|---|---|
population_size |
40 | Total agents across all islands |
max_iterations |
200 | Maximum optimization iterations |
elite_ratio |
0.10 | Fraction of elites preserved |
island_model |
True | Enable island sub-populations |
num_islands |
3 | Number of islands |
migration_freq |
10 | Iterations between island migrations |
diversity_inject_freq |
5 | Iterations between diversity injections |
Debt Memory
| Parameter | Default | Description |
|---|---|---|
debt_memory_lambda |
0.85 | EMA decay for debt accumulation (λ ∈ [0.7, 0.95]) |
min_debt_memory |
0.70 | Minimum debt retention |
max_debt_memory |
0.95 | Maximum debt retention |
debt_persistence_start |
0.80 | Debt persistence at iteration 0 |
debt_persistence_end |
0.20 | Debt persistence at final iteration |
debt_persistence_decay |
'linear' |
Schedule: 'linear', 'exponential', 'cosine' |
Temperature & Acceptance
| Parameter | Default | Description |
|---|---|---|
temperature_start |
1.0 | Initial SA temperature |
temperature_min |
0.02 | Floor temperature |
temperature_decay |
0.95 | Multiplicative cooling per iteration |
force_late_debt |
True | Force debt accumulation in late phase |
late_debt_start_ratio |
0.60 | When late-phase debt forcing begins |
Fitness Weights
| Parameter | Default | Description |
|---|---|---|
w_accuracy |
0.60 | Weight on accuracy (set to 0.95 for NAS SOTA) |
w_cost |
0.30 | Weight on cost (latency + memory + flops) |
w_penalty |
0.10 | Weight on constraint violations |
Constraints
| Parameter | Default | Description |
|---|---|---|
latency_constraint |
100.0 | Max latency (ms) |
memory_constraint |
50.0 | Max memory (MB) |
flops_constraint |
300.0 | Max FLOPs (M) |
constraint_penalty_scale |
2.0 | Multiplier on penalty terms |
Convergence
| Parameter | Default | Description |
|---|---|---|
patience |
30 | Early-stop patience (iterations) |
stagnation_threshold |
15 | Iterations before stagnation boost |
exploration_phase_ratio |
0.30 | Fraction of run in exploration mode |
adaptive_early_stop |
True | Enable adaptive early stopping |
Ablation Flags
| Parameter | Default | Description |
|---|---|---|
enable_debt_accumulation |
True | Toggle debt accumulation |
enable_worse_acceptance |
True | Toggle worse-move acceptance |
enable_debt_repayment |
True | Toggle debt repayment |
Override Any Parameter
from dpo.core.universal import DPO_Presets, DPO_Universal
config = DPO_Presets.NAS_Config(aggressive_mode=True)
config.max_iterations = 300
config.population_size = 100
config.verbose = False
optimizer = DPO_Universal(config=config)
result = optimizer.optimize()
Advanced Usage
Silence All Logging
import logging
logger = logging.getLogger("DPO-Silent")
logger.setLevel(logging.CRITICAL)
logger.disabled = True
optimizer = DPO_Universal(preset='nas', logger=logger)
Multi-Seed Reproducible Runs
import numpy as np
from dpo.core.universal import DPO_Universal
results = []
for seed in [42, 123, 999]:
np.random.seed(seed)
r = DPO_Universal(preset='nas').optimize()
results.append(r['best_accuracy'])
print(f"Accuracy: {np.mean(results):.4f} ± {np.std(results):.4f}")
Direct Access to Core Optimizer
from dpo.core.optimizer import DPO_NAS
from dpo.core.config import DPO_Config
from dpo.evaluation.ensemble import EnsembleEstimator
from dpo.constraints.handler import AdvancedConstraintHandler
config = DPO_Config(population_size=80, max_iterations=200, alpha_0=0.25)
config.validate()
optimizer = DPO_NAS(
config=config,
estimator=EnsembleEstimator(),
constraint_handler=AdvancedConstraintHandler(config),
)
optimizer.initialize_population()
result = optimizer.optimize()
print(optimizer.best_accuracy)
print(optimizer.best_agent.gene.to_architecture_dict())
print(len(optimizer.pareto_archive), "Pareto-optimal solutions")
Auto-Detection of Problem Type
If your custom Problem.get_problem_info() returns a recognizable type, DPO auto-selects the matching preset — no preset= argument needed:
class MyProblem(Problem):
def get_problem_info(self):
return {'name': 'MyTSP', 'type': 'pathfinding'} # auto-selects pathfinding preset
# ... evaluate(), create_solution() ...
optimizer = DPO_Universal(problem=MyProblem()) # preset auto-detected
result = optimizer.optimize()
type keyword |
Auto-Selected Preset |
|---|---|
'nas' / 'architecture' |
NAS |
'resource' / 'allocation' |
Resource Allocation |
'pathfinding' / 'routing' / 'tsp' / 'vrp' |
Pathfinding |
'hpo' / 'hyperparameter' / 'tuning' |
HPO |
'scheduling' / 'schedule' / 'job' |
Scheduling |
How DPO Works (Algorithm Overview)
| Step | What Happens |
|---|---|
| 1 | Evaluate a candidate solution |
| 2 | If worse than current → accept it, record debt = Δfitness |
| 3 | Debt accumulates each time the search moves downhill |
| 4 | When an improvement is found → repay debt with interest, then overshoot |
| 5 | A global-best pull (δ · (gBest − X)) stabilizes at all times |
| 6 | Late phase (t > 0.75): mutation suppressed, elites re-optimized every iteration |
Three-Phase Search Strategy
┌─────────────┬──────────────────┬────────────────────┐
│ Exploration │ Transition │ Convergence │
│ 0% ─── 30% │ 30% ─── 75% │ 75% ─── 100% │
│ │ │ │
│ High α │ Balanced α │ Suppressed α │
│ Wide search │ Debt repayment │ Elite local search │
│ Accumulate │ active, balanced │ Aggressive repay │
│ debt freely │ exploitation │ Overshoot active │
└─────────────┴──────────────────┴────────────────────┘
DPO vs Other Algorithms
| Feature | DPO | SA | GA | DE | PSO |
|---|---|---|---|---|---|
| Accepts worse moves | ✅ | ✅ | ❌ | ❌ | ❌ |
| Remembers degradation history | ✅ | ❌ | ❌ | ❌ | ❌ |
| Repays with overshoot | ✅ | ❌ | ❌ | ❌ | ❌ |
| Population-based | ✅ | ❌ | ✅ | ✅ | ✅ |
| Island model | ✅ | ❌ | Optional | ❌ | ❌ |
| Multi-objective (NSGA-II) | ✅ | ❌ | Optional | ❌ | ❌ |
| Adaptive parameters | ✅ | ❌ | ❌ | ❌ | Partial |
CLI Entry Points
After installing, three CLI commands are available:
dpo-benchmark # Run NAS example
dpo-optimize # Run HPO example
dpo-routing # Run pathfinding example
API Summary
Top-Level Functions (from dpo)
| Function | Purpose |
|---|---|
dpo(problem, config, preset) |
Universal one-call optimizer |
dpo_optimize(objective, bounds, names, preset) |
Continuous optimization shortcut |
dpo_solve_tsp(distance_matrix, preset) |
TSP solver shortcut |
dpo_solve_nas(estimator, constraints, preset) |
NAS solver shortcut |
Core Classes
| Class | Import | Purpose |
|---|---|---|
DPO_Universal |
dpo.core.universal |
High-level optimizer (recommended) |
DPO_Presets |
dpo.core.universal |
Pre-tuned config factories |
DPO_Config |
dpo.core.config |
All tunable parameters |
DPO_NAS |
dpo.core.optimizer |
Low-level core engine |
EnsembleEstimator |
dpo.evaluation.ensemble |
Default NAS evaluator |
AdvancedConstraintHandler |
dpo.constraints.handler |
Constraint penalty handler |
Problem Classes
| Class | Import | For |
|---|---|---|
Problem |
dpo.core.problem |
Abstract base — implement for custom domains |
ContinuousOptimizationProblem |
dpo.core.problem |
Real-valued optimization |
CombinatoricOptimizationProblem |
dpo.core.problem |
Permutation/sequence optimization |
NASProblem |
dpo.core.problem |
Neural architecture search |
HybridProblem |
dpo.core.problem |
Mixed continuous + discrete |
Solution Classes
| Class | Import | For |
|---|---|---|
NumericSolution |
dpo.core.solution |
Real-valued vectors with bounds |
CombinatoricSolution |
dpo.core.solution |
Permutations / sequences |
HybridSolution |
dpo.core.solution |
Mixed numeric + combinatoric |
Project Structure
dpo/
├── __init__.py # Top-level API: dpo(), dpo_optimize(), dpo_solve_tsp(), dpo_solve_nas()
├── core/
│ ├── optimizer.py # DPO_NAS — core algorithm (2200+ lines)
│ ├── universal.py # DPO_Universal + DPO_Presets (recommended entry point)
│ ├── config.py # DPO_Config dataclass (70+ tunable parameters)
│ ├── agent.py # SearchAgent with debt tracking
│ ├── problem.py # Problem ABC + 4 concrete problem classes
│ └── solution.py # Solution ABC + 3 concrete solution classes
├── architecture/
│ └── gene.py # ArchitectureGene (NAS-specific encoding)
├── evaluation/
│ ├── ensemble.py # EnsembleEstimator + ProblemBasedEstimator
│ ├── estimators.py # ZeroShotEstimator, SurrogateEstimator
│ └── cache.py # LRU evaluation cache with noise injection
├── constraints/
│ └── handler.py # AdvancedConstraintHandler (adaptive penalties)
├── utils/
│ ├── helpers.py # save_json, load_json
│ └── logger.py # get_logger
├── examples/ # 6 runnable example scripts
└── benchmarks/ # Comprehensive evaluation framework
Runnable Examples
| Script | Description | Run Command |
|---|---|---|
example_nas.py |
NAS with a mock estimator | python -m dpo.examples.example_nas |
example_hpo.py |
3 HPO scenarios | python -m dpo.examples.example_hpo |
example_tsp.py |
TSP (10 & 150 cities) | python -m dpo.examples.example_tsp |
example_pathfinding.py |
2D grid pathfinding | python -m dpo.examples.example_pathfinding |
example_resource_allocation.py |
Cloud resource balancing | python -m dpo.examples.example_resource_allocation |
example_hybrid.py |
Mixed continuous + discrete | python -m dpo.examples.example_hybrid |
Citation
If you use DPO in your research, please cite:
@software{dpo2026,
author = {Arya H},
title = {DPO: Debt-Payment Optimization},
year = {2026},
url = {https://github.com/Arya1718/dpo}
}
License
MIT License. See LICENSE for details.
Author
Arya H — arya.h1718@gmail.com
Links
- PyPI: pypi.org/project/dpo
- GitHub: github.com/Arya1718/dpo
- Docs: dpo-nas.readthedocs.io
- Issues: github.com/Arya1718/dpo/issues
Metadata
Release files for dpo 2.6.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | |
|---|---|---|---|---|
| dpo-2.6.0.tar.gz | 170.6 kB | Without provenance | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| dpo-2.6.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 349.4 kB
Release files / dpo-2.6.0.tar.gz
| Download URL | dpo-2.6.0.tar.gz |
|---|---|
| Size | 170.6 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
fcf8899703df86b7e6309c82d8ed21df38e5321828494bdd5bd726929d845ad0
|
|
BLAKE2b-256 checksum How to use checksums |
e022c9ba32103730190ad80347e856004d42c124046ae6b399c9fb08a6787b45
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.2.0 CPython/3.11.0
|
Release files / dpo-2.6.0-py3-none-any.whl
| Download URL | dpo-2.6.0-py3-none-any.whl |
|---|---|
| Size | 178.8 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
3e2715cb5699c2377485670e59f531b0f53c6c2f0252e67e94128aeece43e54c
|
|
BLAKE2b-256 checksum How to use checksums |
c27eea7b7bca8c1095d669b282538121e8dd57d32244df3f0b5e025d1d230e2a
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.2.0 CPython/3.11.0
|
Security for dpo 2.6.0
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
No provenance found
None of the files in this release were uploaded with provenance metadata.
A note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.
To learn more about protecting yourself from malicious actors, visit our package security documentation.