solver.press

Random-key optimization can improve the efficiency of mixed-integer programs used in financial portfolio optimization.

PhysicsMar 11, 2026Evaluation Score: 63%

Adversarial Debate Score

63% survival rate under critique

Model Critiques

google: The hypothesis is falsifiable and directly addressed by one of the papers, suggesting potential support, but the effectiveness likely depends heavily on specific portfolio characteristics and implementation. The other papers are only tangentially related, discussing optimization in other contexts.
openai: It’s falsifiable (e.g., compare solve time/optimality gap with and without random-key methods on standard portfolio MIPs), and the “Applying a Random-Key Optimizer on Mixed Integer Programs” paper plausibly supports the general MIP-efficiency claim. However, the provided other excerpts don’t dire...
anthropic: The hypothesis has direct support from the random-key optimizer paper applied to MIPs, which is relevant, but the connection to financial portfolio optimization specifically is only implied by the paper's mention of "finance" as one application domain. The remaining papers are largely irrelevant ...

Supporting Research Papers

Formal Verification

Z3 logical consistency:✅ Consistent

Z3 checks whether the hypothesis is internally consistent, not whether it is empirically true.

Experimental Validation Package

This discovery has a Claude-generated validation package with a full experimental design.

Precise Hypothesis

Random-key optimization (RKO), when applied to mixed-integer programming (MIP) formulations of financial portfolio optimization problems, reduces computational solve time by ≥20% and/or improves solution quality (Sharpe ratio or objective value) by ≥5% compared to standard branch-and-bound MIP solvers (e.g., Gurobi, CPLEX) on benchmark portfolio instances with N ≥ 50 assets, cardinality constraints, and transaction cost terms, within a fixed computational budget of 3600 seconds per instance.

Disproof criteria:
  1. RKO fails to achieve ≥20% reduction in solve time AND fails to achieve ≥5% improvement in objective value vs. Gurobi/CPLEX on ≥70% of benchmark instances.
  2. RKO solutions are infeasible (violate budget, cardinality, or weight constraints) on >5% of test instances after decoding.
  3. RKO exhibits worse scaling behavior than MIP solvers: if RKO runtime grows faster than O(N²) while MIP grows as O(N²·log N) or better for N > 200.
  4. Statistical tests (Wilcoxon signed-rank, α=0.05) show no significant difference in solution quality between RKO and exact MIP solver across 30+ instances.
  5. RKO solution quality degrades below 95% of optimal (as verified by MIP optimality gap) on instances where exact solution is known.
  6. Variance of RKO solution quality across 10 independent runs exceeds 10% of mean objective value, indicating unreliable performance.

Experimental Protocol

Minimum Viable Test (MVT): Compare RKO vs. Gurobi on 3 portfolio sizes (N=50, 200, 500) × 3 constraint types (cardinality-only, cardinality+transaction costs, cardinality+minimum lots) = 9 instance classes, 10 random instances each = 90 total instances. Measure: (a) solve time to 1% optimality gap, (b) best objective value at 3600s, (c) feasibility rate. Full Validation: Extend to N ∈ {50,100,200,500,1000}, 5 constraint types, 30 instances each = 750 instances. Include out-of-sample backtesting on real financial data (2010–2023 S&P 500 constituents). Statistical validation via bootstrap confidence intervals and Wilcoxon signed-rank tests.

Required datasets:
  1. OR-Library portfolio instances (Beasley 1990, freely available): 5 standard instances, N=31–225 assets, Hang Seng/DAX/FTSE/S&P100/Nikkei.
  2. Synthetic covariance matrices: Generated via Wishart distribution, N ∈ {50,100,200,500,1000}, 10 seeds each; expected returns from CAPM simulation.
  3. Real market data: S&P 500 daily returns 2010–2023 from Yahoo Finance/WRDS (≈$500/year academic license or free via yfinance); compute rolling 252-day covariance matrices.
  4. MIPLIB 2017 portfolio-adjacent instances: For cross-domain validation of RKO on generic MIP structure.
  5. Transaction cost schedules: Quadratic transaction cost parameters from published literature (DeMiguel et al. 2009, Journal of Finance).
  6. Benchmark RKO implementation: Reference implementation from Gonçalves & Resende (2011) random-key genetic algorithm (RKGA) codebase (publicly available on GitHub).
Success:
  1. Primary: RKO achieves ≥20% reduction in mean solve time to 1% optimality gap vs. Gurobi across N ≥ 200 instances (p < 0.05, Wilcoxon test).
  2. Secondary: RKO best objective value within 2% of Gurobi optimal on ≥80% of instances where Gurobi finds proven optimal within 3600s.
  3. Tertiary: Out-of-sample Sharpe ratio of RKO portfolios ≥ Gurobi portfolios on ≥7/12 rolling windows (binomial test, p < 0.10).
  4. Scalability: RKO runtime scales as O(N^1.5) or better vs. Gurobi O(N^2.5) or worse for N ∈ [200, 1000].
  5. Feasibility: RKO decoded solutions satisfy all constraints on ≥98% of runs.
  6. Robustness: Coefficient of variation of RKO objective across 10 seeds < 5% for N ≤ 500.
Failure:
  1. RKO solve time exceeds Gurobi solve time on >50% of instances at any tested N.
  2. RKO objective value is >5% worse than Gurobi on >30% of instances where Gurobi finds proven optimal.
  3. Feasibility rate of RKO decoded solutions falls below 90% on any instance class.
  4. Out-of-sample Sharpe ratio of RKO portfolios is statistically significantly worse than Gurobi portfolios (p < 0.05).
  5. RKO shows no improvement over random portfolio selection (baseline sanity check fails).
  6. Wilcoxon signed-rank test fails to reject null hypothesis of equal performance at α=0.10 for primary metric.

4

GPU hours

45d

Time to result

$320

Min cost

$2,800

Full cost

ROI Projection

Commercial:
  1. Immediate: Integration into portfolio management platforms (Bloomberg PORT, FactSet, Aladdin) as a faster optimization engine; market size ~$4B for portfolio analytics software.
  2. Medium-term: Robo-advisor platforms (Betterment, Wealthfront, Schwab Intelligent Portfolios) serving 10M+ accounts could use RKO for personalized cardinality-constrained optimization at scale, reducing cloud compute costs by estimated 15–30%.
  3. Long-term: Foundation for real-time adaptive portfolio optimization in high-frequency rebalancing contexts; potential $50M+ market for latency-sensitive portfolio optimization tools.
  4. Cross-domain: RKO methodology validated here transfers to supply chain optimization, energy portfolio management, and insurance asset-liability matching — combined addressable market >$500M.
  5. Open-source value: A well-documented RKO portfolio library on GitHub could attract 1,000+ stars and become a standard benchmark tool, driving academic and practitioner adoption.
Research:
  1. Quantitative trading firms: 20% reduction in portfolio rebalancing computation time translates to ~$50K–$500K annual savings per trading desk (assuming 10,000 rebalancing events/year at $5–$50 compute cost each).
  2. Asset management: Enables real-time portfolio optimization for N > 500 assets, opening new product categories (e.g., custom index funds with complex ESG constraints) worth estimated $2–10B AUM expansion industry-wide.
  3. Academic impact: Expected 50–150 citations within 5 years if published in Management Science or Operations Research; h-index contribution ≈ 2–5 for lead authors.
  4. Software licensing: RKO portfolio optimization library could command $10K–$100K/year enterprise licensing fees.
  5. Regulatory compliance: Faster optimization enables more frequent constraint-compliant rebalancing, reducing regulatory risk exposure estimated at $1M–$10M per compliance failure avoided.

🔓 If proven, this unlocks

Proving this hypothesis is a prerequisite for the following downstream discoveries and applications:

  • 1RKO_multi_period_portfolio_optimization_004
  • 2RKO_factor_model_portfolio_005
  • 3hybrid_RKO_MIP_warm_start_006
  • 4RKO_robust_portfolio_optimization_007
  • 5RKO_ESG_constrained_portfolio_008

Prerequisites

These must be validated before this hypothesis can be confirmed:

  • RKO_BRKGA_framework_validation_001
  • MIP_portfolio_formulation_benchmark_002
  • cardinality_constrained_portfolio_NP_hardness_003

Implementation Sketch

# RKO Portfolio Optimization - Core Architecture

import numpy as np
from scipy.optimize import minimize
from dataclasses import dataclass
from typing import Tuple, List

@dataclass
class PortfolioInstance:
    mu: np.ndarray          # Expected returns [N]
    Sigma: np.ndarray       # Covariance matrix [N x N]
    k: int                  # Cardinality constraint
    w_min: float = 0.01     # Minimum weight per selected asset
    w_max: float = 0.30     # Maximum weight per selected asset
    transaction_costs: np.ndarray = None  # [N]
    w0: np.ndarray = None   # Current portfolio weights

class RKODecoder:
    """Maps continuous random keys in [0,1]^N to feasible portfolio"""
    
    def decode(self, keys: np.ndarray, instance: PortfolioInstance) -> np.ndarray:
        N = len(keys)
        # Step 1: Rank-based asset selection
        ranked_assets = np.argsort(keys)[::-1]  # Descending order
        selected = ranked_assets[:instance.k]
        
        # Step 2: Solve continuous QP for weights on selected assets
        weights = self._solve_subproblem(selected, instance)
        
        # Step 3: Map back to full weight vector
        w_full = np.zeros(N)
        w_full[selected] = weights
        return w_full
    
    def _solve_subproblem(self, selected: np.ndarray, 
                          instance: PortfolioInstance) -> np.ndarray:
        k = len(selected)
        Sigma_sub = instance.Sigma[np.ix_(selected, selected)]
        mu_sub = instance.mu[selected]
        lambda_risk = 0.5  # Risk aversion parameter
        
        def objective(w):
            portfolio_var = w @ Sigma_sub @ w
            portfolio_ret = mu_sub @ w
            tc = 0.0
            if instance.transaction_costs is not None:
                w0_sub = instance.w0[selected] if instance.w0 is not None \
                         else np.zeros(k)
                tc = instance.transaction_costs[selected] @ np.abs(w - w0_sub)
            return lambda_risk * portfolio_var - portfolio_ret + tc
        
        constraints = [{'type': 'eq', 'fun': lambda w: np.sum(w) - 1.0}]
        bounds = [(instance.w_min, instance.w_max)] * k
        w0 = np.ones(k) / k
        
        result = minimize(objective, w0, method='SLSQP',
                         bounds=bounds, constraints=constraints,
                         options={'ftol': 1e-9, 'maxiter': 1000})
        return result.x if result.success else w0

class BRKGA:
    """Biased Random-Key Genetic Algorithm for Portfolio Optimization"""
    
    def __init__(self, N: int, P: int = 100, rho_e: float = 0.15,
                 rho_m: float = 0.10, p_e: float = 0.70):
        self.N = N          # Number of assets (chromosome length)
        self.P = P          # Population size
        self.rho_e = rho_e  # Elite fraction
        self.rho_m = rho_m  # Mutant fraction
        self.p_e = p_e      # Elite inheritance probability
        self.n_elite = max(1, int(P * rho_e))
        self.n_mutant = max(1, int(P * rho_m))
        self.n_crossover = P - self.n_elite - self.n_mutant
        self.decoder = RKODecoder()
    
    def initialize_population(self) -> np.ndarray:
        return np.random.uniform(0, 1, (self.P, self.N))
    
    def evaluate_population(self, population: np.ndarray,
                            instance: PortfolioInstance) -> np.ndarray:
        fitness = np.zeros(self.P)
        for i in range(self.P):
            w = self.decoder.decode(population[i], instance)
            fitness[i] = self._compute_fitness(w, instance)
        return fitness
    
    def _compute_fitness(self, w: np.ndarray, 
                         instance: PortfolioInstance) -> float:
        # Negative because BRKGA minimizes
        portfolio_var = w @ instance.Sigma @ w
        portfolio_ret = instance.mu @ w
        return -(portfolio_ret - 0.5 * portfolio_var)  # Minimize negative utility
    
    def evolve(self, population: np.ndarray, 
               fitness: np.ndarray) -> np.ndarray:
        # Sort by fitness (ascending = better for minimization)
        sorted_idx = np.argsort(fitness)
        elite = population[sorted_idx[:self.n_elite]]
        
        new_pop = np.zeros_like(population)
        # Keep elite
        new_pop[:self.n_elite] = elite
        # Add mutants
        new_pop[self.n_elite:self.n_elite+self.n_mutant] = \
            np.random.uniform(0, 1, (self.n_mutant, self.N))
        # Biased crossover
        for i in range(self.n_crossover):
            elite_parent = elite[np.random.randint(self.n_elite)]
            non_elite_idx = sorted_idx[self.n_elite + 
                                       np.random.randint(self.P - self.n_elite)]
            non_elite_parent = population[non_elite_idx]
            mask = np.random.uniform(0, 1, self.N) < self.p_e
            child = np.where(mask, elite_parent, non_elite_parent)
            new_pop[self.n_elite + self.n_mutant + i] = child
        return new_pop
    
    def optimize(self, instance: PortfolioInstance, 
                 max_time: float = 3600.0,
                 max_generations: int = 10000,
                 stagnation_limit: int = 50) -> Tuple[np.ndarray, float, List]:
        import time
        population = self.initialize_population()
        fitness = self.evaluate_population(population, instance)
        
        best_fitness = np.min(fitness)
        best_weights = self.decoder.decode(
            population[np.argmin(fitness)], instance)
        convergence_curve = [(0.0, best_fitness)]
        stagnation_count = 0
        start_time = time.time()
        
        for gen in range(max_generations):
            elapsed = time.time() - start_time
            if elapsed >= max_time:
                break
            
            population = self.evolve(population, fitness)
            fitness = self.evaluate_population(population, instance)
            
            gen_best = np.min(fitness)
            if gen_best < best_fitness - 1e-8:
                best_fitness = gen_best
                best_weights = self.decoder.decode(
                    population[np.argmin(fitness)], instance)
                stagnation_count = 0
            else:
                stagnation_count += 1
            
            convergence_curve.append((elapsed, best_fitness))
            
            # Restart on stagnation
            if stagnation_count >= stagnation_limit:
                population[self.n_elite:] = np.random.uniform(
                    0, 1, (self.P - self.n_elite, self.N))
                stagnation_count = 0
        
        return best_weights, best_fitness, convergence_curve

# Benchmark comparison harness
def run_benchmark(instance: PortfolioInstance, 
                  time_limit: float = 3600.0) -> dict:
    import gurobipy as gp
    from gurobipy import GRB
    
    results = {}
    
    # --- RKO Run ---
    brkga = BRKGA(N=len(instance.mu), P=100)
    rko_weights, rko_obj, rko_curve = brkga.optimize(
        instance, max_time=time_limit)
    results['rko'] = {
        'weights': rko_weights,
        'objective': rko_obj,
        'convergence': rko_curve,
        'feasible': validate_feasibility(rko_weights, instance)
    }
    
    # --- Gurobi MIP Run ---
    N = len(instance.mu)
    m = gp.Model()
    m.setParam('TimeLimit', time_limit)
    m.setParam('MIPGap', 0.01)
    m.setParam('Threads', 4)
    
    w = m.addVars(N, lb=0, ub=instance.w_max, name='w')
    z = m.addVars(N, vtype=GRB.BINARY, name='z')
    
    # Objective: minimize variance - lambda * return
    obj = gp.quicksum(instance.Sigma[i,j] * w[i] * w[j] 
                      for i in range(N) for j in range(N))
    obj -= 2.0 * gp.quicksum(instance.mu[i] * w[i] for i in range(N))
    m.setObjective(obj, GRB.MINIMIZE)
    
    m.addConstr(gp.quicksum(w[i] for i in range(N)) == 1)
    m.addConstr(gp.quicksum(z[i] for i in range(N)) == instance.k)
    for i in range(N):
        m.addConstr(w[i] >= instance.w_min * z[i])
        m.addConstr(w[i] <= instance.w_max * z[i])
    
    m.optimize()
    results['gurobi'] = {
        'weights': np.array([w[i].X for i in range(N)]),
        'objective': m.ObjVal,
        'mip_gap': m.MIPGap,
        'solve_time': m.Runtime
    }
    return results

def validate_feasibility(weights: np.ndarray, 
                         instance: PortfolioInstance) -> bool:
    tol = 1e-4
    budget_ok = abs(np.sum(weights) - 1.0) < tol
    cardinality_ok = np.sum(weights > tol) == instance.k
    bounds_ok = all(w == 0 or (instance.w_min - tol <= w <= instance.w_max + tol)
                    for w in weights)
    return budget_ok and cardinality_ok and bounds_ok
Abort checkpoints:
  1. Day 7 (Instance Generation): If >10% of generated covariance matrices are not positive semi-definite after regularization, abort and revise generation procedure. Threshold: <90% valid instances.
  2. Day 10 (MIP Formulation Validation): If RKO/Gurobi solutions on OR-Library instances deviate >5% from published optimal values, abort and debug formulation. Threshold: must match published optima within 1% on ≥4/5 OR-Library instances.
  3. Day 20 (Baseline Solver Runs): If Gurobi solves all N≤200 instances to proven optimality in <60 seconds (leaving no room for RKO improvement), abort full validation and pivot to N>500 focus. Threshold: Gurobi solve time <60s on >80% of N=200 instances.
  4. Day 25 (RKO Solver Runs): If RKO feasibility rate falls below 85% across all instances, abort and fix decoder before proceeding. Threshold: feasibility rate ≥90% required to continue.
  5. Day 28 (Statistical Analysis - MVT): If Wilcoxon test p-value > 0.20 for primary metric on MVT instances (N≥200), abort full validation (750 instances) and report negative result. Threshold: p < 0.20 on MVT required to justify full study.
  6. Day 35 (Real Data Validation): If out-of-sample Sharpe ratio of RKO portfolios is statistically significantly worse than equal-weight benchmark (p < 0.05), abort commercial value claims and reframe as computational-only contribution.

Source

AegisMind Research
Need AI to work rigorously on your problems? AegisMind uses the same multi-model engine for personal and professional use. Get started