ModelCoefficients.java

package com.morphiqlabs.wavelet.performance;

import java.io.Serializable;

/**
 * Polynomial coefficients for performance modeling.
 * 
 * <p>Represents a quadratic model: time = a + b*n + c*n^2</p>
 * 
 * <p>This class supports online learning through incremental updates,
 * allowing the model to adapt as new measurements are collected.</p>
 * 
 * <p>Thread-safe implementation using synchronized methods for all operations
 * that read or modify the coefficients.</p>
 */
public class ModelCoefficients implements Serializable {
    private static final long serialVersionUID = 1L;
    
    private double a; // Constant term
    private double b; // Linear coefficient
    private double c; // Quadratic coefficient
    
    // Online learning parameters
    private double learningRate = 0.01;
    private int updateCount = 0;
    
    /**
     * Creates coefficients with default values.
     */
    public ModelCoefficients() {
        this(0.1, 0.0001, 0);
    }
    
    /**
     * Creates coefficients with specified values.
     * 
     * @param a Constant term
     * @param b Linear coefficient
     * @param c Quadratic coefficient
     */
    public ModelCoefficients(double a, double b, double c) {
        this.a = a;
        this.b = b;
        this.c = c;
    }
    
    /**
     * Evaluates the polynomial for a given input size.
     * Thread-safe evaluation that ensures consistent read of coefficients.
     * 
     * @param n Input size
     * @return Predicted time
     */
    public synchronized double evaluate(int n) {
        return a + b * n + c * n * n;
    }
    
    /**
     * Updates coefficients using gradient descent.
     * Thread-safe update that ensures atomic modification of all coefficients.
     * 
     * @param inputSize The input size
     * @param actualTime The actual execution time
     */
    public synchronized void updateWithMeasurement(int inputSize, double actualTime) {
        double predicted = evaluate(inputSize);
        double error = actualTime - predicted;
        
        // Adaptive learning rate that decreases with more updates
        double adaptiveLearningRate = learningRate / (1 + updateCount * 0.1);
        
        // Gradient descent update
        double gradA = -2 * error;
        double gradB = -2 * error * inputSize;
        double gradC = -2 * error * inputSize * inputSize;
        
        // Update coefficients
        a -= adaptiveLearningRate * gradA;
        b -= adaptiveLearningRate * gradB;
        c -= adaptiveLearningRate * gradC * 0.1; // Smaller update for quadratic term
        
        // Ensure coefficients remain in reasonable bounds
        a = Math.max(0, a); // Time cannot be negative
        b = Math.max(0, b); // Larger inputs should not be faster
        c = Math.max(0, c); // No negative quadratic effects
        
        updateCount++;
    }
    
    /**
     * Gets the constant term.
     * Thread-safe getter.
     * 
     * @return Constant coefficient
     */
    public synchronized double getA() {
        return a;
    }
    
    /**
     * Gets the linear coefficient.
     * Thread-safe getter.
     * 
     * @return Linear coefficient
     */
    public synchronized double getB() {
        return b;
    }
    
    /**
     * Gets the quadratic coefficient.
     * Thread-safe getter.
     * 
     * @return Quadratic coefficient
     */
    public synchronized double getC() {
        return c;
    }
    
    /**
     * Creates a copy of these coefficients.
     * Thread-safe copy that ensures consistent snapshot of coefficients.
     * 
     * @return New instance with same values
     */
    public synchronized ModelCoefficients copy() {
        return new ModelCoefficients(a, b, c);
    }
    
    @Override
    public synchronized String toString() {
        return String.format("%.4f + %.6f*n + %.9f*n²", a, b, c);
    }
}