Debt Payment Optimization
Project description
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
Project details
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file dpo-2.5.0.tar.gz.
File metadata
- Download URL: dpo-2.5.0.tar.gz
- Upload date:
- Size: 168.5 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.11.0
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
7c61517d89a026acfcc260ca91dbd6220506a218f1552e005f1520dc9860e84b
|
|
| MD5 |
ae13aafb812542805f0e73a770b8cf6d
|
|
| BLAKE2b-256 |
a12eb254195d6d6cb12a472450e54b60fc60e1ea421f2e029cd6240ea7bd15a9
|
File details
Details for the file dpo-2.5.0-py3-none-any.whl.
File metadata
- Download URL: dpo-2.5.0-py3-none-any.whl
- Upload date:
- Size: 176.4 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.11.0
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
e79d1d26a8f493fdce995e42c55970823a582b316920ee7cc8e70f122bcc1d20
|
|
| MD5 |
c8d32f6b7187817b10739f91b322713e
|
|
| BLAKE2b-256 |
42d6d7700034ec0393a28a19a052643303635cad194939b9ba90adaf2a47079d
|