Skip to main content

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.

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 Harya.h1718@gmail.com

Links

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

dpo-2.6.0.tar.gz (170.6 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

dpo-2.6.0-py3-none-any.whl (178.8 kB view details)

Uploaded Python 3

File details

Details for the file dpo-2.6.0.tar.gz.

File metadata

  • Download URL: dpo-2.6.0.tar.gz
  • Upload date:
  • Size: 170.6 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.11.0

File hashes

Hashes for dpo-2.6.0.tar.gz
Algorithm Hash digest
SHA256 fcf8899703df86b7e6309c82d8ed21df38e5321828494bdd5bd726929d845ad0
MD5 0886afb5c844e418c6e81054de72e0c3
BLAKE2b-256 e022c9ba32103730190ad80347e856004d42c124046ae6b399c9fb08a6787b45

See more details on using hashes here.

File details

Details for the file dpo-2.6.0-py3-none-any.whl.

File metadata

  • Download URL: dpo-2.6.0-py3-none-any.whl
  • Upload date:
  • Size: 178.8 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.11.0

File hashes

Hashes for dpo-2.6.0-py3-none-any.whl
Algorithm Hash digest
SHA256 3e2715cb5699c2377485670e59f531b0f53c6c2f0252e67e94128aeece43e54c
MD5 905fe0ada075ac122d4c569fbd1cb8b0
BLAKE2b-256 c27eea7b7bca8c1095d669b282538121e8dd57d32244df3f0b5e025d1d230e2a

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page