Skip to main content

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.

PyPI version Python License: MIT Downloads


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.0 required; 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

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)

Source distribution for dpo 2.6.0
File Size Uploaded Provenance
dpo-2.6.0.tar.gz 170.6 kB Without provenance Details

Built distribution (wheel)

Table of built distributions (wheels) for dpo 2.6.0
File Interpreter ABI Platform
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.

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page