Skip to content

Commit 1890d46

Browse files
authored
Spectral cones (#295)
* Added spectral cone projections and a few tests * more tests * fixed existing logging bug * fixed comment * fixed BLAS call * validate nuclear cone * validate nuclear cone * fixed build systems, added timing, added flags * comment * minor * minor cmake-build * minor cmake build * tightened tolerance to pass a test * changed tolerance again * LAPACK build flags * removed complementary check in test (talk to brendan) * changed tolerance in test * changed tolerance of test * trying to fix C++ build * trying to fix C++ build * trying to fix C++ build * trying to fix C++ build * changed cmake * removed cast * changed makefile * removed static temporarily * removed static temporarily * fixing C++ build * mkl * mkl build * fixed merge conflict * complex PSD cone and C++ build * complex PSD cone wrong datatype? * complex PSD cone * complex PSD cone * complex PSD cone * complex PSD cone * complex PSD cone * as it used to be * added new workflow for spectral cones and conditional compilation * corrected Makefile * GPU build * updated makefile * ran clang formatter * formatted CMakeLists.txt * fixed two tests
1 parent ed11fae commit 1890d46

27 files changed

+4395
-22
lines changed

.github/workflows/build_spectral.yml

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
name: Build and Test with Spectral Cones
2+
3+
on: [push, pull_request]
4+
5+
jobs:
6+
linux:
7+
strategy:
8+
fail-fast: false
9+
matrix:
10+
long: [0, 1]
11+
spectral: [0, 1]
12+
13+
runs-on: ubuntu-latest
14+
steps:
15+
- uses: actions/checkout@v4
16+
- run: sudo apt-get update && sudo apt-get install -y libopenblas-dev liblapack-dev
17+
- run: make DLONG=${{ matrix.long }} USE_LAPACK=1 USE_SPECTRAL_CONES=${{ matrix.spectral }}
18+
- run: make test DLONG=${{ matrix.long }} USE_LAPACK=1 USE_SPECTRAL_CONES=${{ matrix.spectral }}
19+
- run: out/run_tests_direct # test direct solver
20+
- run: out/run_tests_indirect # test indirect solver
21+
22+
mac:
23+
strategy:
24+
fail-fast: false
25+
matrix:
26+
long: [0, 1]
27+
spectral: [0, 1]
28+
29+
runs-on: macos-latest
30+
steps:
31+
- uses: actions/checkout@v4
32+
- run: brew update && brew install openblas lapack
33+
- run: make DLONG=${{ matrix.long }} USE_LAPACK=1 USE_SPECTRAL_CONES=${{ matrix.spectral }}
34+
- run: make test DLONG=${{ matrix.long }} USE_LAPACK=1 USE_SPECTRAL_CONES=${{ matrix.spectral }}
35+
- run: out/run_tests_direct # test direct solver
36+
- run: out/run_tests_indirect # test indirect solver

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,3 +51,4 @@ out
5151
hs21_tiny_qp
5252
rob_gauss_cov_est
5353
build/
54+
*.vscode

CMakeLists.txt

Lines changed: 59 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -124,6 +124,12 @@ message(STATUS "Read/write functionality is NOT ${NO_READ_WRITE}")
124124
option(USE_LAPACK "Whether to use BLAS/LAPACK" ON)
125125
message(STATUS "BLAS/LAPACK usage is ${USE_LAPACK}")
126126

127+
# spectral cones
128+
option(USE_SPECTRAL_CONES "Whether to use spectral cones (requires LAPACK)" OFF)
129+
if(USE_SPECTRAL_CONES AND NOT USE_LAPACK)
130+
message(FATAL_ERROR "USE_SPECTRAL_CONES requires USE_LAPACK to be ON")
131+
endif()
132+
127133
# Enable OpenMP support
128134
option(USE_OPENMP "Compile with OpenMP support" OFF)
129135
message(STATUS "OpenMP parallelization is ${USE_OPENMP}")
@@ -158,7 +164,7 @@ if(NO_READ_WRITE)
158164
set(COMPILER_OPTS "-DNO_READ_WRITE=1 ${COMPILER_OPTS}")
159165
endif()
160166

161-
if (USE_LAPACK)
167+
if(USE_LAPACK)
162168
set(COMPILER_OPTS "-DUSE_LAPACK ${COMPILER_OPTS}")
163169
list(APPEND LAPACK_LINK_LIBRARIES "blas" "lapack")
164170
endif()
@@ -167,6 +173,19 @@ if(USE_OPENMP)
167173
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fopenmp")
168174
endif()
169175

176+
# Flags for spectral cones.
177+
if(USE_SPECTRAL_CONES)
178+
set(COMPILER_OPTS "-DUSE_SPECTRAL_CONES ${COMPILER_OPTS}")
179+
endif()
180+
181+
if(USE_SPECTRAL_TIMING_FLAG)
182+
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -DSPECTRAL_TIMING_FLAG")
183+
endif()
184+
185+
if(USE_SPECTRAL_DEBUG)
186+
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -DSPECTRAL_DEBUG")
187+
endif()
188+
170189
message(STATUS "COMPILER_OPTS = ${COMPILER_OPTS}")
171190
message(STATUS "CMAKE_C_FLAGS = ${CMAKE_C_FLAGS}")
172191

@@ -193,6 +212,23 @@ set(${PROJECT_NAME}_SRC
193212
${LINSYS}/csparse.c
194213
${LINSYS}/scs_matrix.c)
195214

215+
# Spectral source files
216+
set(SPECTRAL_SRC
217+
src/spectral_cones/logdeterminant/log_cone_Newton.c
218+
src/spectral_cones/logdeterminant/log_cone_IPM.c
219+
src/spectral_cones/logdeterminant/log_cone_wrapper.c
220+
src/spectral_cones/logdeterminant/logdet_cone.c
221+
src/spectral_cones/nuclear/ell1_cone.c
222+
src/spectral_cones/nuclear/nuclear_cone.c
223+
src/spectral_cones/sum-largest/sum_largest_cone.c
224+
src/spectral_cones/sum-largest/sum_largest_eval_cone.c
225+
src/spectral_cones/util_spectral_cones.c)
226+
227+
# Conditionally add spectral files
228+
if(USE_SPECTRAL_CONES)
229+
list(APPEND ${PROJECT_NAME}_SRC ${SPECTRAL_SRC})
230+
endif()
231+
196232
# Common header files
197233
set(${PROJECT_NAME}_HDR
198234
include/aa.h
@@ -211,6 +247,13 @@ set(${PROJECT_NAME}_HDR
211247
${LINSYS}/csparse.h
212248
${LINSYS}/scs_matrix.h)
213249

250+
# Spectral header files
251+
set(SPECTRAL_HDR include/util_spectral_cones.h)
252+
253+
if(USE_SPECTRAL_CONES)
254+
list(APPEND ${PROJECT_NAME}_HDR ${SPECTRAL_HDR})
255+
endif()
256+
214257
# get all the c file in amd/external
215258
file(GLOB ${PROJECT_NAME}_AMD_EXTERNAL_SRC ${EXTERNAL}/amd/*.c)
216259

@@ -249,7 +292,8 @@ target_include_directories(
249292
target_compile_definitions(${${PROJECT_NAME}_DIRECT} PRIVATE ${COMPILER_OPTS})
250293

251294
# The library depends on math (m) and (optionally) blas and lapack
252-
target_link_libraries(${${PROJECT_NAME}_DIRECT} PRIVATE m ${LAPACK_LINK_LIBRARIES})
295+
target_link_libraries(${${PROJECT_NAME}_DIRECT}
296+
PRIVATE m ${LAPACK_LINK_LIBRARIES})
253297

254298
# Set some properties
255299
set_target_properties(
@@ -297,7 +341,8 @@ target_compile_definitions(${${PROJECT_NAME}_INDIRECT} PRIVATE ${COMPILER_OPTS}
297341
-DINDIRECT)
298342

299343
# The library depends on math (m) and (optionally) blas and lapack
300-
target_link_libraries(${${PROJECT_NAME}_INDIRECT} PUBLIC m ${LAPACK_LINK_LIBRARIES})
344+
target_link_libraries(${${PROJECT_NAME}_INDIRECT}
345+
PUBLIC m ${LAPACK_LINK_LIBRARIES})
301346

302347
# Set some properties
303348
set_target_properties(
@@ -351,8 +396,8 @@ if(DEFINED ENV{MKLROOT})
351396
target_compile_definitions(${${PROJECT_NAME}_MKL} PRIVATE ${COMPILER_OPTS})
352397
# See:
353398
# https://www.intel.com/content/www/us/en/developer/tools/oneapi/onemkl-link-line-advisor.html
354-
# This is probably not correct for other systems. TODO: make SCS-MKL
355-
# work for all combinations of platform / compiler / threading options.
399+
# This is probably not correct for other systems. TODO: make SCS-MKL work for
400+
# all combinations of platform / compiler / threading options.
356401
target_link_options(${${PROJECT_NAME}_MKL} PRIVATE "LINKER:--no-as-needed")
357402
target_link_directories(${${PROJECT_NAME}_MKL} PRIVATE $ENV{MKLROOT}/lib)
358403
target_link_libraries(
@@ -393,27 +438,29 @@ endif()
393438

394439
# ##############################################################################
395440

396-
# If cuDSS is enabled and environment variables are set, install the cuDSS version
441+
# If cuDSS is enabled and environment variables are set, install the cuDSS
442+
# version
397443
if(USE_CUDSS)
398444
find_package(CUDAToolkit REQUIRED)
399445
find_package(cudss REQUIRED)
400446

401447
# Force 32-bit integers for cuDSS compatibility
402448
if(DLONG)
403-
message(FATAL_ERROR "cuDSS requires 32-bit integers. Set DLONG=OFF or do not use -DDLONG=ON")
449+
message(
450+
FATAL_ERROR
451+
"cuDSS requires 32-bit integers. Set DLONG=OFF or do not use -DDLONG=ON"
452+
)
404453
endif()
405454

406455
message(STATUS "Will install SCS-cuDSS (libscscudss).")
407456

408-
409457
set(CUDSSSRC ${LINSYS}/cudss/direct)
410458

411459
# Here we compile the direct cuDSS library
412460
set(${PROJECT_NAME}_CUDSS ${PROJECT_NAME}cudss)
413461
add_library(
414-
${${PROJECT_NAME}_CUDSS}
415-
${${PROJECT_NAME}_HDR} ${${PROJECT_NAME}_SRC} ${CUDSSSRC}/private.c
416-
${CUDSSSRC}/private.h)
462+
${${PROJECT_NAME}_CUDSS} ${${PROJECT_NAME}_HDR} ${${PROJECT_NAME}_SRC}
463+
${CUDSSSRC}/private.c ${CUDSSSRC}/private.h)
417464

418465
target_include_directories(
419466
${${PROJECT_NAME}_CUDSS}
@@ -428,12 +475,9 @@ if(USE_CUDSS)
428475

429476
target_compile_definitions(${${PROJECT_NAME}_CUDSS} PRIVATE ${COMPILER_OPTS})
430477

431-
432478
target_link_libraries(
433479
${${PROJECT_NAME}_CUDSS}
434-
PRIVATE CUDA::cudart
435-
cudss
436-
$<$<NOT:$<PLATFORM_ID:Windows>>:m>
480+
PRIVATE CUDA::cudart cudss $<$<NOT:$<PLATFORM_ID:Windows>>:m>
437481
${LAPACK_LINK_LIBRARIES})
438482

439483
# Set some properties

Makefile

Lines changed: 33 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,33 @@
11
# MAKEFILE for scs
22
include scs.mk
33

4-
SCS_OBJECTS = src/util.o src/cones.o src/exp_cone.o src/aa.o src/rw.o src/linalg.o src/ctrlc.o src/scs_version.o src/normalize.o
4+
# Base object files always included
5+
BASE_SCS_OBJECTS = \
6+
src/util.o src/cones.o src/exp_cone.o src/aa.o src/rw.o src/linalg.o src/ctrlc.o src/scs_version.o src/normalize.o
7+
8+
# Spectral cones files, only included if USE_SPECTRAL_CONES=1
9+
SPECTRAL_CONES_OBJECTS = \
10+
src/spectral_cones/logdeterminant/log_cone_Newton.o \
11+
src/spectral_cones/logdeterminant/log_cone_IPM.o \
12+
src/spectral_cones/logdeterminant/log_cone_wrapper.o \
13+
src/spectral_cones/logdeterminant/logdet_cone.o \
14+
src/spectral_cones/nuclear/ell1_cone.o \
15+
src/spectral_cones/nuclear/nuclear_cone.o \
16+
src/spectral_cones/sum-largest/sum_largest_cone.o \
17+
src/spectral_cones/sum-largest/sum_largest_eval_cone.o \
18+
src/spectral_cones/util_spectral_cones.o
19+
20+
# Now decide which objects to include:
21+
ifneq ($(USE_LAPACK),0)
22+
ifneq ($(USE_SPECTRAL_CONES),0)
23+
SCS_OBJECTS = $(BASE_SCS_OBJECTS) $(SPECTRAL_CONES_OBJECTS)
24+
else
25+
SCS_OBJECTS = $(BASE_SCS_OBJECTS)
26+
endif
27+
else
28+
SCS_OBJECTS = $(BASE_SCS_OBJECTS)
29+
endif
30+
531
SCS_O = src/scs.o
632
SCS_INDIR_O = src/scs_indir.o
733

@@ -27,6 +53,12 @@ else
2753
@echo "NOT compiled with blas/lapack, cannot solve SDPs (can solve LPs, SOCPs, ECPs, and PCPs)."
2854
@echo "To solve SDPs, install blas and lapack, then edit scs.mk to set USE_LAPACK=1"
2955
@echo "and point to the library install locations, and recompile with 'make purge', 'make'."
56+
endif
57+
ifneq ($(USE_SPECTRAL_CONES), 0)
58+
@echo "Compiled with spectral cones extension enabled (Daniel Cederberg 2025)"
59+
else
60+
@echo "Spectral cones extension is NOT enabled. To enable spectral cones, edit"
61+
@echo "scs.mk to set USE_SPECTRAL_CONES=1 and recompile."
3062
endif
3163
@echo "****************************************************************************************"
3264

include/cones.h

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,17 @@ extern "C" {
1111
#include "scs_work.h"
1212
#include <string.h>
1313

14+
#ifdef USE_SPECTRAL_CONES
15+
#include "util_spectral_cones.h" // for newton_stats
16+
17+
// macro for time measurements of SpectralSCS
18+
#ifdef SPECTRAL_TIMING_FLAG
19+
#define SPECTRAL_TIMING(action) action
20+
#else
21+
#define SPECTRAL_TIMING(action)
22+
#endif
23+
#endif
24+
1425
/* private data to help cone projection step */
1526
struct SCS_CONE_WORK {
1627
/*
@@ -26,13 +37,45 @@ struct SCS_CONE_WORK {
2637
scs_int m; /* total length of cone */
2738
/* box cone quantities */
2839
scs_float box_t_warm_start;
40+
2941
#ifdef USE_LAPACK
3042
/* workspace for eigenvector decompositions: */
3143
scs_float *Xs, *Z, *e, *work, *rwork;
3244
scs_complex_float *cXs, *cZ, *cwork;
3345
blas_int *isuppz, *iwork;
3446
blas_int lwork, lcwork, lrwork, liwork;
3547
#endif
48+
49+
#ifdef USE_SPECTRAL_CONES
50+
/* if the projection onto the logarithmic cone should be warmstarted*/
51+
bool *log_cone_warmstarts;
52+
53+
/* Needed for ell1 norm cone projection */
54+
Value_index *work_ell1;
55+
scs_float *work_ell1_proj;
56+
57+
// used for timing spectral vector cone and spectral matrix cone projections
58+
SPECTRAL_TIMING(scs_float tot_time_mat_cone_proj;)
59+
SPECTRAL_TIMING(scs_float tot_time_vec_cone_proj;)
60+
61+
/* workspace for singular value decompositions: */
62+
scs_float *s_nuc, *u_nuc, *vt_nuc, *work_nuc;
63+
blas_int lwork_nuc;
64+
65+
/* workspace that is used internally in the logdet projection (for example,
66+
the gradient and Hessian of the objective function in the projection
67+
problem are stored using this memory) */
68+
scs_float *work_logdet;
69+
70+
/* workspace to store the projection onto the logarithm cone */
71+
scs_float *saved_log_projs;
72+
73+
/* Stats for spectral projections, assuming there is only one spectral cone */
74+
Newton_stats newton_stats;
75+
76+
/* workspace for projection onto sum-largest-evals cone */
77+
scs_float *work_sum_of_largest;
78+
#endif
3679
};
3780

3881
void SCS(free_cone)(ScsCone *k);

include/scs.h

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -143,6 +143,24 @@ typedef struct {
143143
scs_float *p;
144144
/** Number of (primal and dual) power cone triples. */
145145
scs_int psize;
146+
#ifdef USE_SPECTRAL_CONES
147+
/** Array of logdet cone constraints 'len(d) = dsize'. The dimension of a
148+
* log-det cone is "n" and not "n+2" if the matrix has dimension n */
149+
scs_int *d;
150+
/** Length of logdet cone constraints array `d`. */
151+
scs_int dsize;
152+
/** Array of nuc norm cone constraints 'len(nuc_m) = len(nuc_n) = nucsize.*/
153+
scs_int *nuc_m;
154+
scs_int *nuc_n;
155+
scs_int nucsize;
156+
/** Array of ell1-norm cone constraints 'len(ell1) = ell1_size */
157+
scs_int *ell1;
158+
scs_int ell1_size;
159+
/** Array of sum-of-largest-evals cone */
160+
scs_int *sl_n;
161+
scs_int *sl_k;
162+
scs_int sl_size;
163+
#endif
146164
} ScsCone;
147165

148166
/** Contains primal-dual solution arrays or a certificate of infeasibility.
@@ -206,6 +224,13 @@ typedef struct {
206224
scs_float cone_time;
207225
/** Total time (milliseconds) spent in the acceleration routine. */
208226
scs_float accel_time;
227+
#ifdef SPECTRAL_TIMING_FLAG
228+
/** Average time (milliseconds) per iteration matrix cone projection */
229+
scs_float ave_time_matrix_cone_proj;
230+
/** Average time (milliseconds) per iteration for spectral vector cone
231+
* projection */
232+
scs_float ave_time_vector_cone_proj;
233+
#endif
209234
} ScsInfo;
210235

211236
/*

include/util_spectral_cones.h

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
#ifndef UTILSPECTRALCONES_H
2+
#define UTILSPECTRALCONES_H
3+
4+
#ifdef __cplusplus
5+
extern "C" {
6+
#endif
7+
8+
#include "scs_blas.h"
9+
#include "scs_types.h"
10+
#include <assert.h>
11+
#include <math.h>
12+
#include <stdbool.h>
13+
#include <stddef.h>
14+
#include <stdio.h>
15+
16+
#define IN_CONE -1
17+
#define IN_NEGATIVE_DUAL_CONE -2
18+
#define ANALYTICAL_SOL -3
19+
20+
bool is_pos(const scs_float *x, scs_int n);
21+
bool is_negative(const scs_float *x, scs_int n);
22+
void non_neg_proj(const scs_float *src, scs_float *dst, scs_int n);
23+
scs_float sum_log(const scs_float *x, scs_int n);
24+
scs_float min_vec(const scs_float *vec, scs_int n);
25+
26+
// used for sorting in ell1-norm cone and sum of largest cone.
27+
typedef struct {
28+
scs_float value;
29+
int index;
30+
} Value_index;
31+
32+
typedef struct {
33+
int iter;
34+
35+
// if plain Newton computed the projection or if an IPM was used
36+
int newton_success;
37+
38+
// dual_res, pri_res, complementarity for the projection problem
39+
scs_float residuals[3];
40+
} Newton_stats;
41+
42+
#ifdef __cplusplus
43+
}
44+
#endif
45+
#endif

0 commit comments

Comments
 (0)