Source code for BsplineQuantRegpy.core.constraints

#!/usr/bin/env python
# -*- coding: utf-8 -*-

__author__ = "Alexandre Abbes"
__copyright__ = "Copyright 2026, Alexandre Adel Abbes"
__license__ = "GPL"
__version__ = "1.0.1"

import numpy as np
import cvxpy as cp

[docs] def apply_karlin_constraints_cubic(coeffs_cubic, sign=1): """ Applique les contraintes de Karlin pour un polynôme cubique (dérivée première) selon le théorème 3 de l'article sign > 0 : p(u) >= 0 sur [0,1] sign < 0 : p(u) <= 0 sur [0,1] (soit -p(u) >= 0) """ a, b, c, d = coeffs_cubic constraints = [] if sign > 0: # p(u) >= 0 coeffs = (a, b, c, d) final_sign = 1 else: # p(u) <= 0 <=> -p(u) >= 0 coeffs = (-a, -b, -c, -d) # Les coefficients de -p(u) final_sign = 1 # On veut -p(u) >= 0, donc signe positif a1, b1, c1, d1 = coeffs # Variables auxiliaires selon le théorème 3 y0 = cp.Variable() y1 = cp.Variable() y2 = cp.Variable() x0 = cp.Variable() x1 = cp.Variable() x2 = cp.Variable() # Équations (6a)-(6d) pour le polynôme (p ou -p selon le signe) constraints.append(d1 == y0) constraints.append(c1 == 2*y1 + x0 - y0) constraints.append(b1 == y2 + 2*x1 - 2*y1) constraints.append(a1 == x2 - y2) # Contraintes de cône second-order (6e)-(6f) constraints.append(cp.SOC(x0 + x2, cp.hstack([x0 - x2, 2*x1]))) constraints.append(cp.SOC(y0 + y2, cp.hstack([y0 - y2, 2*y1]))) # Conditions aux bornes (pour le polynôme transformé) constraints.append(d1 >= 0) constraints.append(a1 + b1 + c1 + d1 >= 0) return constraints
def apply_karlin_constraints_cubic_bak(coeffs_cubic, monot_sign=1): """ Applique les contraintes de Karlin pour un polynôme cubique (dérivée première) selon le théorème 3 de l'article """ # coeffs_cubic = [a, b, c, d] pour a*u^3 + b*u^2 + c*u + d a, b, c, d = coeffs_cubic # Variables auxiliaires selon le théorème 3 y0 = cp.Variable() y1 = cp.Variable() y2 = cp.Variable() x0 = cp.Variable() x1 = cp.Variable() x2 = cp.Variable() constraints = [] # Équations (6a)-(6d) adaptées pour un polynôme cubique constraints.append(d == y0) constraints.append(c == 2*y1 + x0 - y0) constraints.append(b == y2 + 2*x1 - 2*y1) constraints.append(a == x2 - y2) # Contraintes de cône second-order (6e)-(6f) constraints.append(cp.SOC(x0 + x2, cp.hstack([x0 - x2, 2*x1]))) constraints.append(cp.SOC(y0 + y2, cp.hstack([y0 - y2, 2*y1]))) # Pour la monotonie: si monot_sign > 0, p(u) >= 0 pour u ∈ [0,1] # si monot_sign < 0, -p(u) >= 0 pour u ∈ [0,1] if monot_sign > 0: # p(0) = d >= 0 et p(1) = a + b + c + d >= 0 constraints.append(d >= 0) constraints.append(a + b + c + d >= 0) elif monot_sign < 0: # -p(0) = -d >= 0 et -p(1) = -(a + b + c + d) >= 0 constraints.append(-d >= 0) constraints.append(-(a + b + c + d) >= 0) return constraints
[docs] def apply_karlin_constraints_quadratic(coeffs_quad, const_sign=1): """ Applique les contraintes de Karlin pour un polynôme quadratique (dérivée seconde) selon la Proposition 5 du document PDF """ # coeffs_quad = [a, b, c] pour a*u^2 + b*u + c # Note: selon la notation du PDF: p(x) = p0 + p1*x + p2*x^2 # Donc: p0 = c, p1 = b, p2 = a p0, p1, p2 = coeffs_quad[2], coeffs_quad[1], coeffs_quad[0] #Karlin's matrices K1 = np.array([[1, 0, -1, -1], [0, 1, 0, -1]]) K2 = np.array([1, 0, 1, 1]) constraints = [] if const_sign != 0: # Variable auxiliaire z0 >= 0 z0 = cp.Variable() constraints.append(z0 >= 0) P=const_sign*np.array([p0,p1,p2,const_sign*z0]) x1 = K2@P [x2,x3]=K1@P # Pour cv_sign > 0: p(x) >= 0 sur [0,1] #if cv_sign > 0: # Selon Proposition 5: (p0 + p2 + z0, p0 - p2 - z0, p1 - z0) ∈ Q3 #x1 = p0 + p2 + z0 #x2 = p0 - p2 - z0 #x3 = p1 - z0 # P=cv_sign*np.array([p0,p1,p2,cv_sign*z0]) # x1 = K2@P # [x2,x3]=K1@P # Contrainte SOC: x1 >= ||(x2, x3)||_2 constraints.append(cp.SOC(x1, cp.hstack([x2, x3]))) return constraints
[docs] def apply_val_constraints(const, sign=1): """ Applique les contraintes pour un polynôme linéaire (dérivée troisième) p(u) = a*u + b >= 0 (ou <= 0) pour u ∈ [0,1] """ constraints = [] if sign > 0: # p(u) >= 0 sur [0,1] ↔ min(p(0), p(1)) >= 0 constraints.append(const >= 0) # p(0) >= 0 #constraints.append(a + b >= 0) # p(1) >= 0 elif sign < 0: # p(u) <= 0 sur [0,1] ↔ max(p(0), p(1)) <= 0 constraints.append(const <= 0) # p(0) <= 0 #constraints.append(a + b <= 0) # p(1) <= 0 return constraints