tessl install tessl/pypi-ecos@2.0.1Python interface to ECOS, a numerical solver for convex second-order cone programs.
Complete specification of all data types and formats used by ECOS.
cType: numpy.ndarray with dtype float64
Shape: (n,) where n is the number of optimization variables
Purpose: Coefficients for the linear objective function to minimize: c'*x
Requirements:
n columns to matchCreation:
import numpy as np
# From list
c = np.array([1.0, 2.0, 3.0])
# From explicit float values
c = np.array([-1.0])
# With specified dtype (optional but recommended)
c = np.array([1.0, 2.0, 3.0], dtype=np.float64)
# Check shape
print(c.shape) # (3,) - 1D array with 3 elementsCommon Errors:
# ERROR: 2D array instead of 1D
c = np.array([[1.0, 2.0]]) # Shape (1, 2) - WRONG
# CORRECT: 1D array
c = np.array([1.0, 2.0]) # Shape (2,) - CORRECT
# ERROR: Python list (not numpy array)
c = [1.0, 2.0] # May cause issues
# CORRECT: Convert to numpy array
c = np.array([1.0, 2.0])h and bType: numpy.ndarray with dtype float64
Shape:
h: (m,) where m is the total cone dimension (number of rows in G)b: (p,) where p is the number of equality constraints (number of rows in A)Purpose:
h: Right-hand side for inequality constraints G*x <=_K hb: Right-hand side for equality constraints A*x = bRequirements:
Creation:
import numpy as np
# Match constraint dimensions
m = 5 # Number of inequality constraints
p = 2 # Number of equality constraints
h = np.array([1.0, 2.0, 0.0, 0.0, 3.0]) # Length m
b = np.array([5.0, 10.0]) # Length p
# From zeros
h = np.zeros(m)
b = np.ones(p)G and AType: scipy.sparse.csc_matrix (Compressed Sparse Column format)
Shape:
G: (m, n) where m is total cone dimension, n is number of variablesA: (p, n) where p is number of equality constraints, n is number of variablesPurpose:
G: Left-hand side of inequality constraints G*x <=_K hA: Left-hand side of equality constraints A*x = bCritical Requirements:
cimport numpy as np
import scipy.sparse as sp
# From list of lists
G = sp.csc_matrix([[1.0, 2.0], [3.0, 4.0]])
# From numpy array
G_dense = np.array([[1.0, 2.0], [3.0, 4.0]])
G = sp.csc_matrix(G_dense)
# Single row or column
G = sp.csc_matrix([[1.0, 2.0, 3.0]]) # Shape (1, 3)
G = sp.csc_matrix([[1.0], [2.0], [3.0]]) # Shape (3, 1)import numpy as np
import scipy.sparse as sp
# CSC format: (data, row_indices, col_pointers)
# Matrix: [[1, 0], [0, 2], [3, 0]]
data = np.array([1.0, 3.0, 2.0])
row_indices = np.array([0, 2, 1]) # Row index of each nonzero
col_pointers = np.array([0, 2, 3]) # Start index of each column
G = sp.csc_matrix((data, row_indices, col_pointers), shape=(3, 2))import numpy as np
import scipy.sparse as sp
# Specify (row, col, value) for each nonzero
rows = np.array([0, 1, 2])
cols = np.array([0, 1, 0])
data = np.array([1.0, 2.0, 3.0])
G_coo = sp.coo_matrix((data, (rows, cols)), shape=(3, 2))
G = sp.csc_matrix(G_coo) # Convert to CSCimport scipy.sparse as sp
# Use LIL (List of Lists) for construction
G_lil = sp.lil_matrix((3, 4))
G_lil[0, 0] = 1.0
G_lil[1, 2] = 2.0
G_lil[2, 1] = 3.0
# Convert to CSC for ECOS
G = sp.csc_matrix(G_lil)import numpy as np
import scipy.sparse as sp
# Create separate constraint blocks
G_block1 = sp.csc_matrix([[1.0, 2.0], [3.0, 4.0]])
G_block2 = sp.csc_matrix([[5.0, 6.0]])
# Stack vertically
G = sp.vstack([G_block1, G_block2], format='csc')
# Stack horizontally (for more variables)
G_left = sp.csc_matrix([[1.0], [2.0]])
G_right = sp.csc_matrix([[3.0], [4.0]])
G = sp.hstack([G_left, G_right], format='csc')import numpy as np
import scipy.sparse as sp
# ERROR: Dense numpy array
G = np.array([[1.0, 2.0], [3.0, 4.0]])
solution = ecos.solve(c, G, h, dims) # TypeError
# ERROR: Python list
G = [[1.0, 2.0], [3.0, 4.0]]
solution = ecos.solve(c, G, h, dims) # TypeError
# WARNING: CSR format (will be converted with warning)
G = sp.csr_matrix([[1.0, 2.0], [3.0, 4.0]])
solution = ecos.solve(c, G, h, dims) # Works but prints warning
# CORRECT: CSC format
G = sp.csc_matrix([[1.0, 2.0], [3.0, 4.0]])
solution = ecos.solve(c, G, h, dims) # Correctimport scipy.sparse as sp
# Check if matrix is CSC
print(type(G)) # <class 'scipy.sparse._csc.csc_matrix'>
print(sp.isspmatrix_csc(G)) # True
# Check dimensions
print(G.shape) # (m, n)
# View as dense (for debugging only)
print(G.toarray())
# Check sparsity
print(G.nnz) # Number of non-zero elementsdimsType: dict with specific keys
Required Keys:
'l': Number of linear inequality constraints (positive orthant dimension)'q': List of second-order cone dimensionsOptional Keys:
'e': Number of exponential conesPurpose: Specifies the structure of the cone K in the constraint G*x <=_K h
dims['l']Type: int (non-negative integer)
Meaning: Number of linear inequality constraints (positive orthant constraints)
Constraint: First dims['l'] rows of G*x <= h are interpreted as element-wise inequalities
# 3 linear inequalities
dims = {'l': 3, 'q': []}
# Corresponds to:
# G[0,:] * x <= h[0]
# G[1,:] * x <= h[1]
# G[2,:] * x <= h[2]Requirements:
dims['l'] >= 0dims = {'l': 0, 'q': [...]}dims['q']Type: list of positive integers
Meaning: Each element specifies the dimension of one second-order cone
Constraint: Each cone in the list corresponds to consecutive rows in G
# One 3-dimensional SOC, one 4-dimensional SOC
dims = {'l': 0, 'q': [3, 4]}
# Corresponds to:
# (G[0,:]*x, G[1,:]*x, G[2,:]*x) in Q_3 (rows 0-2)
# (G[3,:]*x, G[4,:]*x, G[5,:]*x, G[6,:]*x) in Q_4 (rows 3-6)Second-Order Cone Definition:
Requirements:
dims = {'l': 5, 'q': []}dims = {'l': 0, 'q': 3} is WRONGdims = {'l': 0, 'q': [3]}dims['e']Type: int (non-negative integer)
Meaning: Number of exponential cones (each has dimension 3)
Constraint: Exponential cone constraints follow linear and SOC constraints in G
# 2 linear, one 3-dim SOC, one exponential cone
dims = {'l': 2, 'q': [3], 'e': 1}
# Total rows in G: 2 + 3 + 3 = 8
# Rows 0-1: linear
# Rows 2-4: SOC
# Rows 5-7: exponential coneExponential Cone Definition:
Critical Requirement: Total cone dimensions must equal number of rows in G
total_dim = dims['l'] + sum(dims['q']) + 3 * dims.get('e', 0)
assert total_dim == G.shape[0]
assert total_dim == len(h)Examples:
# Example 1: Only linear
G.shape = (5, 3)
dims = {'l': 5, 'q': []} # 5 + 0 = 5 ✓
# Example 2: Only SOCs
G.shape = (7, 3)
dims = {'l': 0, 'q': [3, 4]} # 0 + 3 + 4 = 7 ✓
# Example 3: Mixed
G.shape = (10, 3)
dims = {'l': 2, 'q': [3, 5]} # 2 + 3 + 5 = 10 ✓
# Example 4: With exponential
G.shape = (11, 3)
dims = {'l': 2, 'q': [3], 'e': 2} # 2 + 3 + 3*2 = 11 ✓
# ERROR: Dimension mismatch
G.shape = (7, 3)
dims = {'l': 5, 'q': []} # 5 ≠ 7 ✗import scipy.sparse as sp
import numpy as np
# Example 1: Pure LP
n = 3 # variables
m = 5 # constraints
c = np.zeros(n)
G = sp.csc_matrix(np.random.randn(m, n))
h = np.zeros(m)
dims = {'l': 5, 'q': []} # All linear
# Example 2: Pure SOCP
n = 4
m = 7
c = np.zeros(n)
G = sp.csc_matrix(np.random.randn(m, n))
h = np.zeros(m)
dims = {'l': 0, 'q': [3, 4]} # Two SOCs
# Example 3: Mixed LP + SOCP
n = 5
m = 10
c = np.zeros(n)
G = sp.csc_matrix(np.random.randn(m, n))
h = np.zeros(m)
dims = {'l': 3, 'q': [4, 3]} # 3 linear + two SOCs (3 + 4 + 3 = 10)solution: dict = {
'x': numpy.ndarray, # shape (n,)
'y': numpy.ndarray, # shape (p,) or (0,)
's': numpy.ndarray, # shape (m,)
'z': numpy.ndarray, # shape (m,)
'info': dict
}solution['x']: Primal Solutionnumpy.ndarray with dtype float64(n,) where n is the number of variablesoptimal_x = solution['x']solution['y']: Dual Variables (Equality)numpy.ndarray with dtype float64(p,) if equality constraints exist, (0,) otherwiseA*x = b constraintsdual_eq = solution['y']solution['s']: Slack Variablesnumpy.ndarray with dtype float64(m,) where m is total cone dimensions = h - G*x and s in Kslack = solution['s']solution['z']: Dual Variables (Cone)numpy.ndarray with dtype float64(m,) where m is total cone dimensionG*x <=_K h, lying in dual cone K*dual_cone = solution['z']solution['info']: Solver InformationdictExample Access:
info = solution['info']
exit_flag = info.get('exitFlag', None)
iterations = info.get('iter', None)
solve_time = info.get('timing', {}).get('tsolve', None)import numpy as np
import scipy.sparse as sp
import ecos
# Problem dimensions
n = 3 # Number of variables
m = 5 # Total cone dimension (inequality constraints)
p = 2 # Number of equality constraints
# Objective (1D float array)
c = np.array([1.0, 2.0, 3.0], dtype=np.float64)
assert c.shape == (n,)
assert c.dtype == np.float64
# Inequality constraint matrix (sparse CSC)
G = sp.csc_matrix(np.random.randn(m, n))
assert sp.isspmatrix_csc(G)
assert G.shape == (m, n)
# Inequality RHS (1D float array)
h = np.zeros(m, dtype=np.float64)
assert h.shape == (m,)
assert h.dtype == np.float64
# Equality constraint matrix (sparse CSC)
A = sp.csc_matrix(np.random.randn(p, n))
assert sp.isspmatrix_csc(A)
assert A.shape == (p, n)
# Equality RHS (1D float array)
b = np.ones(p, dtype=np.float64)
assert b.shape == (p,)
assert b.dtype == np.float64
# Cone dimensions (dict with specific structure)
dims = {'l': 2, 'q': [3]}
assert isinstance(dims, dict)
assert 'l' in dims and 'q' in dims
assert isinstance(dims['l'], int) and dims['l'] >= 0
assert isinstance(dims['q'], list)
assert dims['l'] + sum(dims['q']) == m
# Solve
solution = ecos.solve(c, G, h, dims, A, b)
# Validate solution types
assert isinstance(solution, dict)
assert isinstance(solution['x'], np.ndarray)
assert solution['x'].shape == (n,)
assert isinstance(solution['y'], np.ndarray)
assert solution['y'].shape == (p,)
assert isinstance(solution['s'], np.ndarray)
assert solution['s'].shape == (m,)
assert isinstance(solution['z'], np.ndarray)
assert solution['z'].shape == (m,)
assert isinstance(solution['info'], dict)import numpy as np
import scipy.sparse as sp
def ensure_csc_matrix(M):
"""Convert matrix to CSC format if needed."""
if M is None:
return None
if sp.isspmatrix_csc(M):
return M
if sp.isspmatrix(M):
return sp.csc_matrix(M)
# Assume dense array or list
return sp.csc_matrix(M)
def ensure_float_array(v):
"""Convert vector to float64 numpy array."""
if isinstance(v, np.ndarray):
return v.astype(np.float64)
return np.array(v, dtype=np.float64)
# Usage
G = ensure_csc_matrix([[1.0, 2.0], [3.0, 4.0]])
c = ensure_float_array([1.0, 2.0])
h = ensure_float_array([0.0, 0.0])