diff --git a/CMakeLists.txt b/CMakeLists.txt index 1aa4d05..9486c12 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -3,6 +3,11 @@ # ----------------------------------------------------------------------------- cmake_minimum_required(VERSION 3.20) +set(_PDHCG_CUDA_ARCHITECTURES_EXPLICIT FALSE) +if(DEFINED CMAKE_CUDA_ARCHITECTURES OR DEFINED ENV{CUDAARCHS}) + set(_PDHCG_CUDA_ARCHITECTURES_EXPLICIT TRUE) +endif() + project(pdhcg LANGUAGES C CXX CUDA) set(PDHCG_VERSION_MAJOR 0) @@ -26,9 +31,17 @@ if(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES) set_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS "Debug" "Release" "MinSizeRel" "RelWithDebInfo") endif() -if(NOT DEFINED CMAKE_CUDA_ARCHITECTURES) - set(CMAKE_CUDA_ARCHITECTURES 60 70 75 80 86 89 90) +if(NOT _PDHCG_CUDA_ARCHITECTURES_EXPLICIT) + if(CMAKE_CUDA_COMPILER_VERSION VERSION_GREATER_EQUAL 13.0) + set(_PDHCG_DEFAULT_CUDA_ARCHITECTURES all) + else() + set(_PDHCG_DEFAULT_CUDA_ARCHITECTURES 60 70 75 80 86 89 90) + endif() + set(CMAKE_CUDA_ARCHITECTURES "${_PDHCG_DEFAULT_CUDA_ARCHITECTURES}" + CACHE STRING "CUDA architectures to build" FORCE) + unset(_PDHCG_DEFAULT_CUDA_ARCHITECTURES) endif() +unset(_PDHCG_CUDA_ARCHITECTURES_EXPLICIT) # Global Compile flags (corresponding to CFLAGS/NVCCFLAGS) add_compile_options(-fPIC -O3 -Wall -Wextra -g) @@ -162,6 +175,7 @@ endif() set(CORE_LINK_LIBS PUBLIC CUDA::cudart CUDA::cublas + CUDA::cusolver CUDA::cusparse ZLIB::ZLIB ) @@ -323,7 +337,7 @@ if(PDHCG_BUILD_TESTS) # Input-driven inspection/export tools are built but require explicit # command-line data, so they are not zero-argument CTest cases. - if(TEST_NAME STREQUAL "test_distributed_conic" AND PDHCG_COMPILE_DISTRIBUTED) + if(TEST_NAME MATCHES "^test_distributed_(conic|psd)$" AND PDHCG_COMPILE_DISTRIBUTED) add_test(NAME ${TEST_NAME} COMMAND ${MPIEXEC_EXECUTABLE} ${MPIEXEC_NUMPROC_FLAG} 2 ${MPIEXEC_PREFLAGS} $ ${MPIEXEC_POSTFLAGS}) diff --git a/README.md b/README.md index 5890c9e..d06a585 100644 --- a/README.md +++ b/README.md @@ -27,7 +27,7 @@ PDHCG solves convex quadratic conic programs in the following form, with a spars - $Q$ is the sparse symmetric quadratic component (optional). - $R \in \mathbb{R}^{k\times n}$ is a tall low-rank factor (optional, $k$ = rank). - $D \in \mathbb{R}^{k\times k}$ is an optional middle matrix that scales / weights / signs the low-rank term. When omitted it defaults to the identity, recovering the standard $Q + R^\top R$ formulation. $D$ may be **diagonal, sparse, dense, or indefinite** — the backend auto-detects the cheapest runtime representation. -- Standard SOC, Rotated SOC, Exponential, and Power cones are supported both on variable blocks and through native affine constraints $Fx + g \in \mathcal{K}_a$. +- Standard SOC, Rotated SOC, Exponential, Power, and positive-semidefinite (PSD) cones are supported both on variable blocks and through native affine constraints $Fx + g \in \mathcal{K}_a$. ## Installation (C++ Executable) @@ -99,7 +99,7 @@ Solver Parameters: | --pock_chambolle_alpha | double | Value for Pock-Chambolle step size parameter $\alpha$. | 1.0 | | --no_pock_chambolle | flag | Disable Pock-Chambolle rescaling (enabled by default). | false | | --no_bound_obj_rescaling | flag | Disable bound objective rescaling (enabled by default). | false | -| --no_cone_preserving_scaling | flag | Keep coordinate-wise scaling within cone blocks. | false | +| --no_cone_preserving_scaling | flag | Disable cone-preserving scaling. | false | | --sv_max_iter | int | Max iterations for singular value estimation (Power Method). | 5000 | | --sv_tol | double | Tolerance for singular value estimation. | 1e-4 | | --eval_freq | int | Frequency of termination criteria evaluation (in iterations). | 200 | @@ -132,7 +132,9 @@ The block scale is | Pock-Chambolle | `d_rms` | `sqrt(d_max * d_rms)` | PDHCG applies the rule to both variable and affine cone blocks. Setting -`--no_cone_preserving_scaling` bypasses block aggregation. +`--no_cone_preserving_scaling` bypasses block aggregation. PSD blocks +remain cone-compatible through a structured element-wise scale +$s_{ij}=d_i d_j$. **Distributed Options** (only available when built with `-DPDHCG_COMPILE_DISTRIBUTED=ON`): | Option | Type | Description | Default | @@ -238,9 +240,8 @@ problem = cp.Problem(cp.Minimize(x), [x >= 1]) problem.solve(solver="PDHCG", eps=1e-6) ``` -The backend supports quadratic objectives and CVXPY Zero, NonNeg, SOC, -ExpCone, and PowCone3D constraints. PSD and mixed-integer models are not -supported. +The backend supports quadratic objectives and CVXPY Zero, NonNeg, SOC, PSD, +ExpCone, and PowCone3D constraints. ## Citation If you use this software or method in your research, please cite our paper: diff --git a/distributed/distributed_solver.cu b/distributed/distributed_solver.cu index caef8c4..9d10c45 100644 --- a/distributed/distributed_solver.cu +++ b/distributed/distributed_solver.cu @@ -19,7 +19,7 @@ limitations under the License. #include "distributed_utils.h" #include "internal_types.h" #include "pdhcg.h" -#include "pdhcg_kernels.cuh" +#include "pdhcg_kernels.h" #include "pdhg_core_op.h" #include "permute.h" #include "preconditioner.h" diff --git a/docs/C_API.md b/docs/C_API.md index 3b0148f..6f4fa09 100644 --- a/docs/C_API.md +++ b/docs/C_API.md @@ -46,7 +46,7 @@ The objective minimized is `0.5 * x^T (Q + R^T D R) x + c^T x + c0`. `Q`, `R`, a - `var_lb`: Variable lower bounds. If `NULL`, defaults to all `-INFINITY`. - `var_ub`: Variable upper bounds. If `NULL`, defaults to all `+INFINITY`. - `objective_constant`: Scalar constant term added to the objective value. If `NULL`, defaults to `0.0`. -- `num_var_cones`, `var_cones`: Optional conic variable blocks. Supported types are SOC, rotated SOC, exponential, and power cones. Pass `0` and `NULL` when no variable cones are present. See [Types](c/types.md) for slot layouts and `set_cone_fixed` in [Functions](c/functions.md) for pinning individual slots. +- `num_var_cones`, `var_cones`: Optional conic variable blocks. Supported types are SOC, rotated SOC, exponential, power, and positive-semidefinite cones. Pass `0` and `NULL` when no variable cones are present. See [Types](c/types.md) for slot layouts and `set_cone_fixed` in [Functions](c/functions.md) for pinning individual non-PSD slots. - `affine_cone_matrix_desc`: Matrix descriptor for `F` in the native constraint `F x + affine_cone_offset in K`. Pass `NULL` when no affine cone rows are present. - `affine_cone_offset`: Offset vector with one entry per row of `F`. Pass `NULL` for zero offsets. - `num_affine_cones`, `affine_cones`: Cone blocks covering every row of `F`. Each `start_idx` is relative to `F`; blocks must be disjoint. Affine cone descriptors must set `is_fixed = NULL`. diff --git a/docs/c/functions.md b/docs/c/functions.md index a46e79a..6905d1f 100644 --- a/docs/c/functions.md +++ b/docs/c/functions.md @@ -92,7 +92,7 @@ int set_cone_fixed( Pins one slot of cone `cone_idx` to `value`. Allocates the `is_fixed` flag array on first use and also writes `primal_start[start_idx + slot] = value` so the projection sees the constant. During preprocessing, that slot is also converted to equal lower and upper bounds. Typical use: fix the `y` slot of an exponential cone (e.g. Fisher-market entropy term with `y = 1`). -Variable SOC, rotated-SOC, exponential, and power cones support every fixed-slot pattern whose intersection with the cone is nonempty. The solver validates the section before preprocessing and rejects empty or non-finite sections. Projection and stationarity residuals use the same weighted fixed-section operator, including diagonal quadratic objectives and large SOC/rotated-SOC blocks. +Variable SOC, rotated-SOC, exponential, and power cones support every fixed-slot pattern whose intersection with the cone is nonempty. The solver validates the section before preprocessing and rejects empty or non-finite sections. Projection and stationarity residuals use the same weighted fixed-section operator, including diagonal quadratic objectives and large SOC/rotated-SOC blocks. `set_cone_fixed` rejects PSD blocks. **Parameters:** diff --git a/docs/c/types.md b/docs/c/types.md index 301b2eb..d0eeaa3 100644 --- a/docs/c/types.md +++ b/docs/c/types.md @@ -103,7 +103,9 @@ typedef enum { CONE_ROTATED_SOC = 0, CONE_STANDARD_SOC = 1, CONE_EXPONENTIAL = 2, - CONE_POWER = 3 + CONE_POWER = 3, + CONE_PSD = 4, + NUM_CONE_TYPES = 5 } cone_type_t; ``` @@ -113,6 +115,12 @@ typedef enum { | `CONE_ROTATED_SOC` | `\|\|v\|\|^2 <= 2 s t`, `s, t >= 0` | `v` (`v_dim`), `s`, `t` | | `CONE_EXPONENTIAL` | `y * exp(x / y) <= z`, `y > 0` | `x`, `y`, `z` (`v_dim` must be 1) | | `CONE_POWER` | `x^alpha * y^(1-alpha) >= \|z\|`, `x,y >= 0` | `x`, `y`, `z` (`v_dim` must be 1) | +| `CONE_PSD` | Symmetric matrix `X` is positive semidefinite | `svec(X)` (`v_dim` is the matrix order; length `v_dim * (v_dim + 1) / 2`) | + +PSD `svec` stores the lower triangle in column-major order. Diagonal entries +are unchanged and off-diagonal entries are multiplied by `sqrt(2)`. +Distributed partitioning keeps each PSD block on one GPU and never splits its +`svec` coordinates across devices. ## Cone Spec @@ -128,12 +136,12 @@ typedef struct { Input descriptor for a single cone block. In `var_cones`, `start_idx` indexes the variable vector; in `affine_cones`, it indexes rows of the separately -supplied affine matrix `F`. The -slot count is `v_dim + 2` for SOC/RSOC and `3` for -exponential/power cones. Power cones require `power_alpha` in `(0,1)`. -Variable cones may provide an `is_fixed` array of `slot_count` bytes. Every -mathematically nonempty fixed-slot pattern is supported for all four cone -types. Affine cones must set `is_fixed` to NULL. +supplied affine matrix `F`. The slot count is `v_dim + 2` for SOC/RSOC, `3` +for exponential/power cones, and `v_dim * (v_dim + 1) / 2` for PSD cones. +Power cones require `power_alpha` in `(0,1)`. Variable non-PSD cones may +provide an `is_fixed` array of `slot_count` bytes. Every mathematically +nonempty fixed-slot pattern is supported for those cone types. PSD blocks do +not support fixed slots, and affine cones must set `is_fixed` to NULL. ## Cone Blocks diff --git a/docs/index.md b/docs/index.md index faf5621..35d385f 100644 --- a/docs/index.md +++ b/docs/index.md @@ -26,13 +26,13 @@ Where: - $c$ is the linear objective vector - $\ell_c, u_c$ are constraint bounds - $\ell_v, u_v$ are variable bounds -- $\mathcal{K}_a$ and $\mathcal{K}_v$ are products of Standard SOC, Rotated SOC, Exponential, or Power cones +- $\mathcal{K}_a$ and $\mathcal{K}_v$ are products of Standard SOC, Rotated SOC, Exponential, Power, or positive-semidefinite cones ## Key Features - **GPU Acceleration**: Fully leverages NVIDIA CUDA for extreme-scale QP problems - **Flexible Problem Structure**: Supports sparse, low-rank, and middle-weighted low-rank ($R^\top D R$) quadratic terms — alone or combined -- **Conic constraints**: fully GPU-accelerated SOC, Rotated SOC, Exponential, and Power cone projection on variable blocks or native affine maps $Fx + g$ +- **Conic constraints**: fully GPU-accelerated SOC, Rotated SOC, Exponential, Power, and positive-semidefinite cone projection on variable blocks or native affine maps $Fx + g$ - **High Performance**: Competitive with commercial solvers on large-scale problems - **SpMVOp Auto-Detection**: Automatically uses cuSPARSE SpMVOp on CUDA 13+ while falling back to standard SpMV on CUDA 12.x - **Multi-GPU Distributed Solving**: Supports parallel solving across multiple GPUs via MPI and NCCL (optional, enabled at compile time) diff --git a/docs/python/model.md b/docs/python/model.md index 669cfe1..5a3421c 100644 --- a/docs/python/model.md +++ b/docs/python/model.md @@ -36,9 +36,9 @@ require one Python dict per cone. | Field | Type | Notes | |---|---|---| -| `types` | scalar or `int32[K]` | `ConeType.SOC`, `RSOC`, `EXP`, or `POWER`. | +| `types` | scalar or `int32[K]` | `ConeType.SOC`, `RSOC`, `EXP`, `POWER`, or `PSD`. | | `starts` | `int32[K]` | First variable index or affine row of each block. | -| `v_dims` | scalar or `int32[K]` | Length of `v`; defaults to 1. | +| `v_dims` | scalar or `int32[K]` | Length of `v`, or matrix order for PSD; defaults to 1. | | `power_alphas` | scalar or `float64[K]` | Required in `(0, 1)` for power cones. | | `fixed_mask` | optional `uint8[N]` | Ambient-coordinate mask for fixed variable slots. Values come from the primal warm start. | @@ -48,6 +48,14 @@ Slot layout per cone: - `rsoc`: `v[0..v_dim-1], s, t` with `||v||^2 <= 2 s t`, `s, t >= 0`. - `exp`: `x, y, z` with `y * exp(x / y) <= z`, `y > 0`. - `power`: `x, y, z` with `x^alpha * y^(1-alpha) >= |z|`, `x, y >= 0`. +- `psd`: `svec(X)` for an order-`v_dim` symmetric matrix `X >= 0`. `svec` + stores the lower triangle in column-major order, leaves diagonal entries + unchanged, and multiplies off-diagonal entries by `sqrt(2)`. + +PSD blocks do not support entries in `fixed_mask`; express fixed matrix entries +as ordinary linear equalities instead. +In distributed solves, each PSD block remains on one GPU; permutation and +partitioning never split its `svec` coordinates across devices. ```python import numpy as np diff --git a/docs/python/quickstart.md b/docs/python/quickstart.md index 166cb4f..303cb08 100644 --- a/docs/python/quickstart.md +++ b/docs/python/quickstart.md @@ -100,8 +100,8 @@ print(problem.status, value, x.value) ``` The backend preserves CVXPY's primal and dual conventions. It supports -quadratic objectives and Zero, NonNeg, SOC, ExpCone, and PowCone3D -constraints. PSD and mixed-integer models are not supported. +quadratic objectives and Zero, NonNeg, SOC, PSD, ExpCone, and PowCone3D +constraints. Mixed-integer models are not supported. ## Model Creation diff --git a/include/pdhcg_types.h b/include/pdhcg_types.h index 3152397..8ecabb8 100644 --- a/include/pdhcg_types.h +++ b/include/pdhcg_types.h @@ -66,14 +66,15 @@ extern "C" CONE_STANDARD_SOC = 1, CONE_EXPONENTIAL = 2, CONE_POWER = 3, /* 3-dim: x^alpha * y^(1-alpha) >= |z|, x,y >= 0 */ - NUM_CONE_TYPES = 4 + CONE_PSD = 4, /* svec(X), X symmetric positive semidefinite */ + NUM_CONE_TYPES = 5 } cone_type_t; typedef struct { int num_cones; int *start_idx; /* [num_cones] */ - int *v_dim; /* [num_cones] */ + int *v_dim; /* [num_cones]; PSD stores the matrix order */ cone_type_t *type; /* [num_cones] */ double *power_alpha; /* [num_cones]; alpha in (0,1) for CONE_POWER, else unused */ int fixed_mask_size; /* number of entries in is_fixed; zero when no mask is stored */ @@ -83,10 +84,10 @@ extern "C" typedef struct { cone_type_t type; - int start_idx; /* variable index, or row of F for affine cones */ - int v_dim; + int start_idx; /* variable index, or row of F for affine cones */ + int v_dim; /* vector dimension for SOC/RSOC; matrix order for PSD */ double power_alpha; /* required for CONE_POWER (in (0,1)); ignored otherwise */ - const char *is_fixed; /* variable cones only; must be NULL for affine cones */ + const char *is_fixed; /* variable non-PSD cones only; must be NULL for affine cones */ } cone_spec_t; typedef struct diff --git a/internal/cone_kernel_ops.h b/internal/cone_kernel_ops.h new file mode 100644 index 0000000..d1c839a --- /dev/null +++ b/internal/cone_kernel_ops.h @@ -0,0 +1,108 @@ +/* +Copyright 2026 Hongpei Li + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +#pragma once + +#include "internal_types.h" + +typedef void (*cone_proj_launcher_t)(double *primal, + const double *variable_rescaling, + double *warm_start, + const int *start_idx, + const int *v_dim, + const double *power_alpha, + const char *is_fixed, + int count); + +typedef void (*cone_dual_res_launcher_t)(double *dual_residual, + double *complementarity_residual, + const double *objective_vector, + const double *dual_product, + const double *variable_rescaling, + const double *primal_solution, + double *warm_start, + const int *start_idx, + const int *v_dim, + const double *power_alpha, + const char *is_fixed, + int count); + +typedef void (*cone_proj_diag_q_launcher_t)(double *pdhg_primal, + double *reflected_primal, + const double *current_primal, + const double *variable_rescaling, + const double *q_diag, + double tau, + double *warm_start, + const int *start_idx, + const int *v_dim, + const double *power_alpha, + const char *is_fixed, + int count); + +typedef struct +{ + cone_proj_launcher_t project[NUM_PROJ_METHODS]; + cone_proj_diag_q_launcher_t project_diag_q[NUM_PROJ_METHODS]; + cone_dual_res_launcher_t dual_residual[NUM_PROJ_METHODS]; +} cone_kernel_ops_t; + +extern const cone_kernel_ops_t pdhcg_rsoc_cone_kernel_ops; +extern const cone_kernel_ops_t pdhcg_soc_cone_kernel_ops; +extern const cone_kernel_ops_t pdhcg_exp_cone_kernel_ops; +extern const cone_kernel_ops_t pdhcg_power_cone_kernel_ops; + +void launch_block_projected_mapping_only_dual(double *dual_residual, + double *complementarity_residual, + const double *objective_vector, + const double *dual_product, + const double *variable_rescaling, + const double *primal_solution, + double *warm_start, + const int *start_idx, + const int *v_dim, + const double *power_alpha, + const char *is_fixed, + int count); + +void launch_grid_projected_mapping_only_dual(double *dual_residual, + double *complementarity_residual, + const double *objective_vector, + const double *dual_product, + const double *variable_rescaling, + const double *primal_solution, + double *warm_start, + const int *start_idx, + const int *v_dim, + const double *power_alpha, + const char *is_fixed, + int count); + +void launch_cone_reflection(cone_proj_method_t method, + double *reflected_primal, + const double *pdhg_primal, + const double *current_primal, + const int *start_idx, + const int *v_dim, + int count); + +void launch_cone_dual_slack(cone_proj_method_t method, + double *dual_slack, + const double *objective_vector, + const double *dual_product, + const int *start_idx, + const int *v_dim, + int count); diff --git a/internal/cone_kernel_reductions.h b/internal/cone_kernel_reductions.h new file mode 100644 index 0000000..2b8588e --- /dev/null +++ b/internal/cone_kernel_reductions.h @@ -0,0 +1,134 @@ +/* +Copyright 2026 Hongpei Li + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +#pragma once + +#include +#include + +__device__ static inline void cone_block_sum3(double *first, double *second, double *third, double scratch[96]) +{ + int lane = threadIdx.x & 31; + int warp = threadIdx.x >> 5; + unsigned mask = __activemask(); + double a = *first; + double b = *second; + double c = *third; + for (int offset = 16; offset > 0; offset >>= 1) + { + a += __shfl_down_sync(mask, a, offset); + b += __shfl_down_sync(mask, b, offset); + c += __shfl_down_sync(mask, c, offset); + } + if (lane == 0) + { + scratch[3 * warp + 0] = a; + scratch[3 * warp + 1] = b; + scratch[3 * warp + 2] = c; + } + __syncthreads(); + + int num_warps = (blockDim.x + 31) >> 5; + if (warp == 0) + { + a = lane < num_warps ? scratch[3 * lane + 0] : 0.0; + b = lane < num_warps ? scratch[3 * lane + 1] : 0.0; + c = lane < num_warps ? scratch[3 * lane + 2] : 0.0; + for (int offset = 16; offset > 0; offset >>= 1) + { + a += __shfl_down_sync(0xffffffffu, a, offset); + b += __shfl_down_sync(0xffffffffu, b, offset); + c += __shfl_down_sync(0xffffffffu, c, offset); + } + if (lane == 0) + { + scratch[0] = a; + scratch[1] = b; + scratch[2] = c; + } + } + __syncthreads(); + *first = scratch[0]; + *second = scratch[1]; + *third = scratch[2]; + __syncthreads(); +} + +__device__ static inline double cone_block_max(double value, double scratch[96]) +{ + int lane = threadIdx.x & 31; + int warp = threadIdx.x >> 5; + unsigned mask = __activemask(); + for (int offset = 16; offset > 0; offset >>= 1) + value = fmax(value, __shfl_down_sync(mask, value, offset)); + if (lane == 0) + scratch[warp] = value; + __syncthreads(); + + int num_warps = (blockDim.x + 31) >> 5; + if (warp == 0) + { + value = lane < num_warps ? scratch[lane] : 0.0; + for (int offset = 16; offset > 0; offset >>= 1) + value = fmax(value, __shfl_down_sync(0xffffffffu, value, offset)); + if (lane == 0) + scratch[0] = value; + } + __syncthreads(); + value = scratch[0]; + __syncthreads(); + return value; +} + +__device__ static inline void cone_atomic_max_positive(double *address, double value) +{ + atomicMax(reinterpret_cast(address), + static_cast(__double_as_longlong(value))); +} + +enum standard_soc_block_mode +{ + SOC_BLOCK_IDENTITY = 0, + SOC_BLOCK_ZERO_FREE = 1, + SOC_BLOCK_APEX = 2, + SOC_BLOCK_SCALAR_Z = 3, + SOC_BLOCK_FIXED_Z_ROOT = 4, + SOC_BLOCK_FREE_Z_ROOT = 5, + SOC_BLOCK_ZERO_Z_ROOT = 6 +}; + +static __device__ __forceinline__ double large_cone_block_sum(double value) +{ + __shared__ double warp_sums[32]; + const unsigned mask = 0xffffffffu; + const int lane = threadIdx.x & 31; + const int warp = threadIdx.x >> 5; + const int num_warps = (blockDim.x + 31) >> 5; + + for (int offset = 16; offset > 0; offset >>= 1) + value += __shfl_down_sync(mask, value, offset); + if (lane == 0) + warp_sums[warp] = value; + __syncthreads(); + + value = (warp == 0 && lane < num_warps) ? warp_sums[lane] : 0.0; + if (warp == 0) + { + for (int offset = 16; offset > 0; offset >>= 1) + value += __shfl_down_sync(mask, value, offset); + } + return value; +} diff --git a/internal/cone_projection_utils.h b/internal/cone_projection_utils.h new file mode 100644 index 0000000..273242a --- /dev/null +++ b/internal/cone_projection_utils.h @@ -0,0 +1,64 @@ +/* +Copyright 2026 Hongpei Li + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +#pragma once + +#include +#include + +__device__ static inline double +cone_section_weight(const double *rescaling, const double *q_diag, double tau, int index) +{ + double metric = q_diag ? 1.0 + tau * q_diag[index] : 1.0; + double d = rescaling[index]; + return fmax(metric * d * d, DBL_MIN); +} + +__device__ static inline bool cone_section_has_fixed(const char *is_fixed, int start, int length) +{ + if (!is_fixed) + return false; + for (int slot = 0; slot < length; ++slot) + if (is_fixed[start + slot]) + return true; + return false; +} + +__device__ static inline double cone_section_actual(const double *point, const double *rescaling, int index) +{ + return point[index] / rescaling[index]; +} + +/* For the negative scalar branch of a weighted SOC projection, return a + multiplier at which the root residual is nonnegative. */ +__device__ static inline double cone_section_negative_soc_upper( + double singular_metric, double endpoint_polar, double fixed_norm2, double polar_norm2, double max_vector_metric) +{ + double upper; + if (fixed_norm2 > 0.0) + { + upper = singular_metric + endpoint_polar / sqrt(fixed_norm2); + } + else + { + double polar_norm = sqrt(polar_norm2); + double gap = polar_norm - endpoint_polar; + if (!(gap > 0.0)) + return NAN; + upper = (polar_norm / gap) * singular_metric + (endpoint_polar / gap) * max_vector_metric; + } + return upper * (1.0 + 64.0 * DBL_EPSILON); +} diff --git a/internal/cone_section_projection.cuh b/internal/cone_section_projection.cuh deleted file mode 100644 index ad614cb..0000000 --- a/internal/cone_section_projection.cuh +++ /dev/null @@ -1,746 +0,0 @@ -/* -Copyright 2026 Hongpei Li - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -#pragma once - -#include -#include - -__device__ static inline double -cone_section_weight(const double *rescaling, const double *q_diag, double tau, int index) -{ - double metric = q_diag ? 1.0 + tau * q_diag[index] : 1.0; - double d = rescaling[index]; - return fmax(metric * d * d, DBL_MIN); -} - -__device__ static inline bool cone_section_has_fixed(const char *is_fixed, int start, int length) -{ - if (!is_fixed) - return false; - for (int slot = 0; slot < length; ++slot) - if (is_fixed[start + slot]) - return true; - return false; -} - -__device__ static inline double cone_section_actual(const double *point, const double *rescaling, int index) -{ - return point[index] / rescaling[index]; -} - -/* For the negative scalar branch of a weighted SOC projection, return a - multiplier at which the root residual is nonnegative. */ -__device__ static inline double cone_section_negative_soc_upper( - double singular_metric, double endpoint_polar, double fixed_norm2, double polar_norm2, double max_vector_metric) -{ - double upper; - if (fixed_norm2 > 0.0) - { - upper = singular_metric + endpoint_polar / sqrt(fixed_norm2); - } - else - { - double polar_norm = sqrt(polar_norm2); - double gap = polar_norm - endpoint_polar; - if (!(gap > 0.0)) - return NAN; - upper = (polar_norm / gap) * singular_metric + (endpoint_polar / gap) * max_vector_metric; - } - return upper * (1.0 + 64.0 * DBL_EPSILON); -} - -/* Map a weighted rotated SOC to a weighted standard SOC in sum/difference - endpoint coordinates, then reuse its negative-branch bracket. */ -__device__ static inline double cone_section_negative_rsoc_upper(double omega_s, - double omega_t, - double s, - double t, - double fixed_norm2, - double polar_norm2, - double max_vector_metric) -{ - const double inv_sqrt2 = 0.70710678118654752440; - double sqrt_omega_s = sqrt(omega_s); - double sqrt_omega_t = sqrt(omega_t); - double root_metric = sqrt_omega_s * sqrt_omega_t; - double scaled_s = sqrt_omega_s * s; - double scaled_t = sqrt_omega_t * t; - double transformed_w = (scaled_s - scaled_t) * inv_sqrt2; - double endpoint_polar = -(scaled_s + scaled_t) * inv_sqrt2; - double transformed_fixed_norm2 = root_metric * fixed_norm2; - double transformed_polar_norm2 = polar_norm2 / root_metric + transformed_w * transformed_w; - double transformed_max_metric = fmax(1.0, max_vector_metric / root_metric); - double transformed_upper = cone_section_negative_soc_upper( - 1.0, endpoint_polar, transformed_fixed_norm2, transformed_polar_norm2, transformed_max_metric); - return root_metric * transformed_upper; -} - -/* Weighted projection onto an arbitrary nonempty fixed section of - { (u,z) : ||u||_2 <= z }. The first k+1 coordinates form u. */ -__device__ static inline void project_standard_soc_section_serial(double *point, - const double *rescaling, - const double *q_diag, - double tau, - double *warm_start, - int start, - int k, - const char *is_fixed) -{ - int u_length = k + 1; - int z_index = start + u_length; - bool fixed_z = is_fixed[z_index] != 0; - double fixed_norm2 = 0.0; - double free_norm2 = 0.0; - double polar_norm2 = 0.0; - double max_omega = 0.0; - int free_count = 0; - for (int slot = 0; slot < u_length; ++slot) - { - int index = start + slot; - double value = cone_section_actual(point, rescaling, index); - if (is_fixed[index]) - fixed_norm2 += value * value; - else - { - double omega = cone_section_weight(rescaling, q_diag, tau, index); - free_norm2 += value * value; - polar_norm2 += (omega * value) * (omega * value); - max_omega = fmax(max_omega, omega); - ++free_count; - } - } - - double z_input = cone_section_actual(point, rescaling, z_index); - if (fixed_z) - { - double radius2 = fmax(0.0, z_input * z_input - fixed_norm2); - if (free_count == 0 || free_norm2 <= radius2) - return; - if (!(radius2 > 0.0)) - { - for (int slot = 0; slot < u_length; ++slot) - if (!is_fixed[start + slot]) - point[start + slot] = 0.0; - return; - } - - double lo = 0.0; - double hi = sqrt(polar_norm2) / sqrt(radius2) * (1.0 + 64.0 * DBL_EPSILON); - if (!(hi > 0.0) || !isfinite(hi)) - { - hi = warm_start && *warm_start > 0.0 && isfinite(*warm_start) ? *warm_start : 1.0; - for (int expansion = 0; expansion < 100; ++expansion) - { - double norm2 = 0.0; - for (int slot = 0; slot < u_length; ++slot) - { - int index = start + slot; - if (is_fixed[index]) - continue; - double omega = cone_section_weight(rescaling, q_diag, tau, index); - double value = cone_section_actual(point, rescaling, index) * omega / (omega + hi); - norm2 += value * value; - } - if (norm2 <= radius2) - break; - hi *= 2.0; - } - } - for (int iteration = 0; iteration < 80; ++iteration) - { - double lambda = 0.5 * (lo + hi); - double norm2 = 0.0; - for (int slot = 0; slot < u_length; ++slot) - { - int index = start + slot; - if (is_fixed[index]) - continue; - double omega = cone_section_weight(rescaling, q_diag, tau, index); - double value = cone_section_actual(point, rescaling, index) * omega / (omega + lambda); - norm2 += value * value; - } - if (norm2 > radius2) - lo = lambda; - else - hi = lambda; - if ((hi - lo) <= 1e-13 * (1.0 + hi + lo)) - break; - } - double lambda = 0.5 * (lo + hi); - if (warm_start) - *warm_start = lambda; - for (int slot = 0; slot < u_length; ++slot) - { - int index = start + slot; - if (!is_fixed[index]) - { - double omega = cone_section_weight(rescaling, q_diag, tau, index); - point[index] *= omega / (omega + lambda); - } - } - return; - } - - double total_norm2 = fixed_norm2 + free_norm2; - if (z_input >= 0.0 && total_norm2 <= z_input * z_input) - return; - if (free_count == 0) - { - double projected_z = fmax(z_input, sqrt(fixed_norm2)); - point[z_index] = projected_z * rescaling[z_index]; - return; - } - - double omega_z = cone_section_weight(rescaling, q_diag, tau, z_index); - if (fixed_norm2 == 0.0) - { - if (-omega_z * z_input >= sqrt(polar_norm2)) - { - for (int slot = 0; slot < u_length; ++slot) - if (!is_fixed[start + slot]) - point[start + slot] = 0.0; - point[z_index] = 0.0; - return; - } - } - - double lambda; - if (z_input == 0.0) - { - lambda = omega_z; - double norm2 = fixed_norm2; - for (int slot = 0; slot < u_length; ++slot) - { - int index = start + slot; - if (is_fixed[index]) - continue; - double omega = cone_section_weight(rescaling, q_diag, tau, index); - double value = cone_section_actual(point, rescaling, index) * omega / (omega + lambda); - norm2 += value * value; - } - point[z_index] = sqrt(norm2) * rescaling[z_index]; - } - else - { - bool lower_branch = z_input > 0.0; - double lo; - double hi; - if (lower_branch) - { - lo = 0.0; - hi = omega_z * (1.0 - 1e-14); - } - else - { - lo = omega_z * (1.0 + 1e-14); - hi = cone_section_negative_soc_upper(omega_z, -omega_z * z_input, fixed_norm2, polar_norm2, max_omega); - if (!(hi > lo) || !isfinite(hi)) - { - hi = 2.0 * omega_z; - for (int expansion = 0; expansion < 100; ++expansion) - { - double norm2 = fixed_norm2; - for (int slot = 0; slot < u_length; ++slot) - { - int index = start + slot; - if (is_fixed[index]) - continue; - double omega = cone_section_weight(rescaling, q_diag, tau, index); - double value = cone_section_actual(point, rescaling, index) * omega / (omega + hi); - norm2 += value * value; - } - double z = omega_z * z_input / (omega_z - hi); - if (norm2 >= z * z) - break; - hi *= 2.0; - } - } - } - - if (warm_start && *warm_start > lo && *warm_start < hi && isfinite(*warm_start)) - { - double norm2 = fixed_norm2; - for (int slot = 0; slot < u_length; ++slot) - { - int index = start + slot; - if (is_fixed[index]) - continue; - double omega = cone_section_weight(rescaling, q_diag, tau, index); - double value = cone_section_actual(point, rescaling, index) * omega / (omega + *warm_start); - norm2 += value * value; - } - double z = omega_z * z_input / (omega_z - *warm_start); - double f = norm2 - z * z; - if ((lower_branch && f > 0.0) || (!lower_branch && f < 0.0)) - lo = *warm_start; - else - hi = *warm_start; - } - - for (int iteration = 0; iteration < 80; ++iteration) - { - double trial = 0.5 * (lo + hi); - double norm2 = fixed_norm2; - for (int slot = 0; slot < u_length; ++slot) - { - int index = start + slot; - if (is_fixed[index]) - continue; - double omega = cone_section_weight(rescaling, q_diag, tau, index); - double value = cone_section_actual(point, rescaling, index) * omega / (omega + trial); - norm2 += value * value; - } - double z = omega_z * z_input / (omega_z - trial); - double f = norm2 - z * z; - if ((lower_branch && f > 0.0) || (!lower_branch && f < 0.0)) - lo = trial; - else - hi = trial; - if ((hi - lo) <= 1e-13 * (1.0 + hi + lo)) - break; - } - lambda = 0.5 * (lo + hi); - point[z_index] *= omega_z / (omega_z - lambda); - } - - if (warm_start) - *warm_start = lambda; - for (int slot = 0; slot < u_length; ++slot) - { - int index = start + slot; - if (!is_fixed[index]) - { - double omega = cone_section_weight(rescaling, q_diag, tau, index); - point[index] *= omega / (omega + lambda); - } - } -} - -__device__ static inline double rotated_soc_smooth_objective(const double *point, - const double *rescaling, - const double *q_diag, - double tau, - int start, - int k, - const char *is_fixed, - double lambda, - double s, - double t) -{ - double objective = 0.0; - for (int slot = 0; slot < k; ++slot) - { - int index = start + slot; - if (is_fixed[index]) - continue; - double omega = cone_section_weight(rescaling, q_diag, tau, index); - double input = cone_section_actual(point, rescaling, index); - double value = input * omega / (omega + lambda); - double delta = value - input; - objective += omega * delta * delta; - } - int s_index = start + k; - int t_index = s_index + 1; - double omega_s = cone_section_weight(rescaling, q_diag, tau, s_index); - double omega_t = cone_section_weight(rescaling, q_diag, tau, t_index); - double ds = s - cone_section_actual(point, rescaling, s_index); - double dt = t - cone_section_actual(point, rescaling, t_index); - return objective + omega_s * ds * ds + omega_t * dt * dt; -} - -/* Weighted projection onto an arbitrary nonempty fixed section of - { (v,s,t) : ||v||_2^2 <= 2 s t, s >= 0, t >= 0 }. */ -__device__ static inline void project_rotated_soc_section_serial(double *point, - const double *rescaling, - const double *q_diag, - double tau, - double *warm_start, - int start, - int k, - const char *is_fixed) -{ - int s_index = start + k; - int t_index = s_index + 1; - bool fixed_s = is_fixed[s_index] != 0; - bool fixed_t = is_fixed[t_index] != 0; - double s_input = cone_section_actual(point, rescaling, s_index); - double t_input = cone_section_actual(point, rescaling, t_index); - double fixed_norm2 = 0.0; - double free_norm2 = 0.0; - double polar_norm2 = 0.0; - double max_omega = 0.0; - int free_count = 0; - for (int slot = 0; slot < k; ++slot) - { - int index = start + slot; - double value = cone_section_actual(point, rescaling, index); - if (is_fixed[index]) - fixed_norm2 += value * value; - else - { - double omega = cone_section_weight(rescaling, q_diag, tau, index); - free_norm2 += value * value; - polar_norm2 += (omega * value) * (omega * value); - max_omega = fmax(max_omega, omega); - ++free_count; - } - } - - if (fixed_s && fixed_t) - { - double radius2 = fmax(0.0, 2.0 * s_input * t_input - fixed_norm2); - if (free_count == 0 || free_norm2 <= radius2) - return; - if (!(radius2 > 0.0)) - { - for (int slot = 0; slot < k; ++slot) - if (!is_fixed[start + slot]) - point[start + slot] = 0.0; - return; - } - - double lo = 0.0; - double hi = sqrt(polar_norm2) / sqrt(radius2) * (1.0 + 64.0 * DBL_EPSILON); - if (!(hi > 0.0) || !isfinite(hi)) - { - hi = warm_start && *warm_start > 0.0 && isfinite(*warm_start) ? *warm_start : 1.0; - for (int expansion = 0; expansion < 100; ++expansion) - { - double norm2 = 0.0; - for (int slot = 0; slot < k; ++slot) - { - int index = start + slot; - if (is_fixed[index]) - continue; - double omega = cone_section_weight(rescaling, q_diag, tau, index); - double value = cone_section_actual(point, rescaling, index) * omega / (omega + hi); - norm2 += value * value; - } - if (norm2 <= radius2) - break; - hi *= 2.0; - } - } - for (int iteration = 0; iteration < 80; ++iteration) - { - double lambda = 0.5 * (lo + hi); - double norm2 = 0.0; - for (int slot = 0; slot < k; ++slot) - { - int index = start + slot; - if (is_fixed[index]) - continue; - double omega = cone_section_weight(rescaling, q_diag, tau, index); - double value = cone_section_actual(point, rescaling, index) * omega / (omega + lambda); - norm2 += value * value; - } - if (norm2 > radius2) - lo = lambda; - else - hi = lambda; - if ((hi - lo) <= 1e-13 * (1.0 + hi + lo)) - break; - } - double lambda = 0.5 * (lo + hi); - if (warm_start) - *warm_start = lambda; - for (int slot = 0; slot < k; ++slot) - { - int index = start + slot; - if (!is_fixed[index]) - { - double omega = cone_section_weight(rescaling, q_diag, tau, index); - point[index] *= omega / (omega + lambda); - } - } - return; - } - - if (fixed_s || fixed_t) - { - int free_endpoint_index = fixed_s ? t_index : s_index; - double fixed_endpoint = fixed_s ? s_input : t_input; - double free_endpoint_input = fixed_s ? t_input : s_input; - double omega_endpoint = cone_section_weight(rescaling, q_diag, tau, free_endpoint_index); - if (!(fixed_endpoint > 0.0)) - { - for (int slot = 0; slot < k; ++slot) - if (!is_fixed[start + slot]) - point[start + slot] = 0.0; - point[free_endpoint_index] = fmax(free_endpoint_input, 0.0) * rescaling[free_endpoint_index]; - return; - } - if (free_endpoint_input >= 0.0 && fixed_norm2 + free_norm2 <= 2.0 * fixed_endpoint * free_endpoint_input) - return; - if (free_count == 0) - { - double lower_bound = fixed_norm2 / (2.0 * fixed_endpoint); - point[free_endpoint_index] = fmax(free_endpoint_input, lower_bound) * rescaling[free_endpoint_index]; - return; - } - - double lo = 0.0; - double violation = fixed_norm2 + free_norm2 - 2.0 * fixed_endpoint * free_endpoint_input; - double hi = omega_endpoint * violation / (2.0 * fixed_endpoint * fixed_endpoint); - hi *= 1.0 + 64.0 * DBL_EPSILON; - if (!(hi > 0.0) || !isfinite(hi)) - { - hi = warm_start && *warm_start > 0.0 && isfinite(*warm_start) ? *warm_start : omega_endpoint; - for (int expansion = 0; expansion < 100; ++expansion) - { - double norm2 = fixed_norm2; - for (int slot = 0; slot < k; ++slot) - { - int index = start + slot; - if (is_fixed[index]) - continue; - double omega = cone_section_weight(rescaling, q_diag, tau, index); - double value = cone_section_actual(point, rescaling, index) * omega / (omega + hi); - norm2 += value * value; - } - double endpoint = free_endpoint_input + hi * fixed_endpoint / omega_endpoint; - if (norm2 <= 2.0 * fixed_endpoint * endpoint) - break; - hi *= 2.0; - } - } - for (int iteration = 0; iteration < 80; ++iteration) - { - double lambda = 0.5 * (lo + hi); - double norm2 = fixed_norm2; - for (int slot = 0; slot < k; ++slot) - { - int index = start + slot; - if (is_fixed[index]) - continue; - double omega = cone_section_weight(rescaling, q_diag, tau, index); - double value = cone_section_actual(point, rescaling, index) * omega / (omega + lambda); - norm2 += value * value; - } - double endpoint = free_endpoint_input + lambda * fixed_endpoint / omega_endpoint; - if (norm2 > 2.0 * fixed_endpoint * endpoint) - lo = lambda; - else - hi = lambda; - if ((hi - lo) <= 1e-13 * (1.0 + hi + lo)) - break; - } - double lambda = 0.5 * (lo + hi); - if (warm_start) - *warm_start = lambda; - for (int slot = 0; slot < k; ++slot) - { - int index = start + slot; - if (!is_fixed[index]) - { - double omega = cone_section_weight(rescaling, q_diag, tau, index); - point[index] *= omega / (omega + lambda); - } - } - point[free_endpoint_index] = - (free_endpoint_input + lambda * fixed_endpoint / omega_endpoint) * rescaling[free_endpoint_index]; - return; - } - - double total_norm2 = fixed_norm2 + free_norm2; - if (s_input >= 0.0 && t_input >= 0.0 && total_norm2 <= 2.0 * s_input * t_input) - return; - - double omega_s = cone_section_weight(rescaling, q_diag, tau, s_index); - double omega_t = cone_section_weight(rescaling, q_diag, tau, t_index); - if (fixed_norm2 == 0.0) - { - double bs = omega_s * s_input; - double bt = omega_t * t_input; - if (bs <= 0.0 && bt <= 0.0 && polar_norm2 <= 2.0 * bs * bt) - { - for (int slot = 0; slot < k; ++slot) - if (!is_fixed[start + slot]) - point[start + slot] = 0.0; - point[s_index] = 0.0; - point[t_index] = 0.0; - return; - } - } - - double root_metric = sqrt(omega_s) * sqrt(omega_t); - double balance = sqrt(omega_s) * s_input + sqrt(omega_t) * t_input; - double balance_scale = 1.0 + fabs(sqrt(omega_s) * s_input) + fabs(sqrt(omega_t) * t_input); - double lambda = root_metric; - double projected_s = 0.0; - double projected_t = 0.0; - bool smooth_valid = true; - - if (fabs(balance) <= 64.0 * DBL_EPSILON * balance_scale) - { - double norm2 = fixed_norm2; - for (int slot = 0; slot < k; ++slot) - { - int index = start + slot; - if (is_fixed[index]) - continue; - double omega = cone_section_weight(rescaling, q_diag, tau, index); - double value = cone_section_actual(point, rescaling, index) * omega / (omega + lambda); - norm2 += value * value; - } - double product = 0.5 * root_metric * norm2; - double delta = sqrt(omega_s) * s_input; - double scaled_t = 0.5 * (-delta + sqrt(fmax(0.0, delta * delta + 4.0 * product))); - double scaled_s = scaled_t + delta; - projected_s = scaled_s / sqrt(omega_s); - projected_t = scaled_t / sqrt(omega_t); - smooth_valid = projected_s >= 0.0 && projected_t >= 0.0; - } - else - { - bool lower_branch = balance > 0.0; - double lo = lower_branch ? 0.0 : root_metric * (1.0 + 1e-14); - double hi = lower_branch ? root_metric * (1.0 - 1e-14) : 2.0 * root_metric; - - if (!lower_branch) - { - hi = cone_section_negative_rsoc_upper( - omega_s, omega_t, s_input, t_input, fixed_norm2, polar_norm2, max_omega); - if (!(hi > lo) || !isfinite(hi)) - { - hi = 2.0 * root_metric; - for (int expansion = 0; expansion < 100; ++expansion) - { - double determinant = omega_s * omega_t - hi * hi; - double s = omega_t * (omega_s * s_input + hi * t_input) / determinant; - double t = omega_s * (omega_t * t_input + hi * s_input) / determinant; - double f = INFINITY; - if (s >= 0.0 && t >= 0.0) - { - double norm2 = fixed_norm2; - for (int slot = 0; slot < k; ++slot) - { - int index = start + slot; - if (is_fixed[index]) - continue; - double omega = cone_section_weight(rescaling, q_diag, tau, index); - double value = cone_section_actual(point, rescaling, index) * omega / (omega + hi); - norm2 += value * value; - } - f = norm2 - 2.0 * s * t; - } - if (f >= 0.0) - break; - hi *= 2.0; - } - } - } - - for (int iteration = 0; iteration < 90; ++iteration) - { - double trial = 0.5 * (lo + hi); - double determinant = omega_s * omega_t - trial * trial; - double s = omega_t * (omega_s * s_input + trial * t_input) / determinant; - double t = omega_s * (omega_t * t_input + trial * s_input) / determinant; - double f = INFINITY; - if (s >= 0.0 && t >= 0.0) - { - double norm2 = fixed_norm2; - for (int slot = 0; slot < k; ++slot) - { - int index = start + slot; - if (is_fixed[index]) - continue; - double omega = cone_section_weight(rescaling, q_diag, tau, index); - double value = cone_section_actual(point, rescaling, index) * omega / (omega + trial); - norm2 += value * value; - } - f = norm2 - 2.0 * s * t; - } - if ((lower_branch && f > 0.0) || (!lower_branch && f < 0.0)) - lo = trial; - else - hi = trial; - if ((hi - lo) <= 1e-13 * (1.0 + hi + lo)) - break; - } - lambda = 0.5 * (lo + hi); - double determinant = omega_s * omega_t - lambda * lambda; - projected_s = omega_t * (omega_s * s_input + lambda * t_input) / determinant; - projected_t = omega_s * (omega_t * t_input + lambda * s_input) / determinant; - smooth_valid = isfinite(projected_s) && isfinite(projected_t) && projected_s >= 0.0 && projected_t >= 0.0; - } - - double best_objective = smooth_valid - ? rotated_soc_smooth_objective( - point, rescaling, q_diag, tau, start, k, is_fixed, lambda, projected_s, projected_t) - : INFINITY; - int mode = smooth_valid ? 0 : 1; - if (fixed_norm2 == 0.0) - { - double vector_objective = 0.0; - for (int slot = 0; slot < k; ++slot) - { - int index = start + slot; - if (!is_fixed[index]) - { - double omega = cone_section_weight(rescaling, q_diag, tau, index); - double value = cone_section_actual(point, rescaling, index); - vector_objective += omega * value * value; - } - } - double s_axis = fmax(s_input, 0.0); - double s_axis_objective = - vector_objective + omega_s * (s_axis - s_input) * (s_axis - s_input) + omega_t * t_input * t_input; - if (s_axis_objective < best_objective) - { - best_objective = s_axis_objective; - projected_s = s_axis; - projected_t = 0.0; - mode = 1; - } - double t_axis = fmax(t_input, 0.0); - double t_axis_objective = - vector_objective + omega_s * s_input * s_input + omega_t * (t_axis - t_input) * (t_axis - t_input); - if (t_axis_objective < best_objective) - { - projected_s = 0.0; - projected_t = t_axis; - mode = 1; - } - } - - if (mode == 0) - { - for (int slot = 0; slot < k; ++slot) - { - int index = start + slot; - if (!is_fixed[index]) - { - double omega = cone_section_weight(rescaling, q_diag, tau, index); - point[index] *= omega / (omega + lambda); - } - } - if (warm_start) - *warm_start = lambda; - } - else - { - for (int slot = 0; slot < k; ++slot) - if (!is_fixed[start + slot]) - point[start + slot] = 0.0; - if (warm_start) - *warm_start = 0.0; - } - point[s_index] = projected_s * rescaling[s_index]; - point[t_index] = projected_t * rescaling[t_index]; -} diff --git a/internal/internal_types.h b/internal/internal_types.h index 92babac..d35674f 100644 --- a/internal/internal_types.h +++ b/internal/internal_types.h @@ -91,10 +91,10 @@ typedef struct double min_tol; int iteration_limit; int total_count; - int has_inner_loop; } inner_solver_t; typedef struct distributed_cone_split_s distributed_cone_split_t; +typedef struct psd_projection_runtime_s psd_projection_runtime_t; struct cone_bucket_s; typedef enum @@ -121,6 +121,8 @@ typedef struct struct cone_bucket_s *buckets; int num_buckets; bool has_power_cones; + bool has_psd_cones; + psd_projection_runtime_t *psd; distributed_cone_split_t *split; } cone_runtime_t; diff --git a/internal/pdhcg_affine_cone_kernels.h b/internal/pdhcg_affine_cone_kernels.h new file mode 100644 index 0000000..1910134 --- /dev/null +++ b/internal/pdhcg_affine_cone_kernels.h @@ -0,0 +1,59 @@ +/* +Copyright 2025-2026 Haihao Lu +Copyright 2026 Hongpei Li + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +#pragma once + +#include + +#ifdef __cplusplus +extern "C" +{ +#endif + __global__ void finish_affine_cone_residuals_kernel(double *primal_residual, + const double *primal_product, + const double *affine_cone_offset, + const double *constraint_rescaling, + double *dual_membership, + const double *dual_membership_rescaling, + int n); + + __global__ void prepare_affine_cone_residuals_kernel(double *projection_point, + double *complementarity_residual, + const double *primal_product, + const double *affine_cone_offset, + const double *dual_solution, + const int *start_idx, + const int *v_dim, + double constraint_bound_rescaling, + int num_cones); + + __global__ void prepare_affine_cone_residuals_grid_kernel(double *projection_point, + double *complementarity_accumulator, + const double *primal_product, + const double *affine_cone_offset, + const double *dual_solution, + const int *start_idx, + const int *v_dim, + int num_cones, + int blocks_per_cone); + + __global__ void finish_affine_cone_complementarity_kernel(double *complementarity_residual, + double constraint_bound_rescaling, + int num_cones); +#ifdef __cplusplus +} +#endif diff --git a/internal/pdhcg_cone_common_kernels.h b/internal/pdhcg_cone_common_kernels.h new file mode 100644 index 0000000..7930ba8 --- /dev/null +++ b/internal/pdhcg_cone_common_kernels.h @@ -0,0 +1,79 @@ +/* +Copyright 2026 Hongpei Li + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +#pragma once + +#include + +#ifdef __cplusplus +extern "C" +{ +#endif + + __global__ void set_cone_dual_slack_kernel(double *dual_slack, + const double *objective_vector, + const double *dual_product, + const int *start_idx, + const int *v_dim, + int num_blocks); + + __global__ void set_cone_dual_slack_warp_kernel(double *dual_slack, + const double *objective_vector, + const double *dual_product, + const int *start_idx, + const int *v_dim, + int num_cones); + + __global__ void set_cone_dual_slack_grid_kernel(double *dual_slack, + const double *objective_vector, + const double *dual_product, + const int *start_idx, + const int *v_dim, + int num_cones, + int blocks_per_cone); + + __global__ void recompute_reflected_at_cone_kernel(double *reflected_primal, + const double *pdhg_primal, + const double *current_primal, + const int *start_idx, + const int *v_dim, + int num_blocks); + + __global__ void recompute_reflected_at_cone_warp_kernel(double *reflected_primal, + const double *pdhg_primal, + const double *current_primal, + const int *start_idx, + const int *v_dim, + int num_cones); + + __global__ void recompute_reflected_at_cone_block_kernel(double *reflected_primal, + const double *pdhg_primal, + const double *current_primal, + const int *start_idx, + const int *v_dim, + int num_cones); + + __global__ void recompute_reflected_at_cone_grid_kernel(double *reflected_primal, + const double *pdhg_primal, + const double *current_primal, + const int *start_idx, + const int *v_dim, + int num_cones, + int blocks_per_cone); + +#ifdef __cplusplus +} +#endif diff --git a/internal/pdhcg_exp_cone_kernels.h b/internal/pdhcg_exp_cone_kernels.h new file mode 100644 index 0000000..5e49a17 --- /dev/null +++ b/internal/pdhcg_exp_cone_kernels.h @@ -0,0 +1,59 @@ +/* +Copyright 2025 Haihao Lu +Copyright 2026 Hongpei Li + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +#pragma once + +#include + +#ifdef __cplusplus +extern "C" +{ +#endif + __global__ void project_exp_cone_kernel(double *__restrict__ primal_solution, + const double *__restrict__ variable_rescaling, + double *__restrict__ warm_start, + const int *__restrict__ start_idx, + const int *__restrict__ v_dim, + const char *__restrict__ is_fixed, + int num_blocks); + + __global__ void compute_cone_dual_residual_exp_kernel(double *__restrict__ dual_residual, + double *__restrict__ complementarity_residual, + const double *__restrict__ objective_vector, + const double *__restrict__ dual_product, + const double *__restrict__ variable_rescaling, + const double *__restrict__ primal_solution, + double *__restrict__ warm_start, + const int *__restrict__ start_idx, + const int *__restrict__ v_dim, + const char *__restrict__ is_fixed, + int num_blocks); + + __global__ void project_exp_cone_diag_q_kernel(double *__restrict__ pdhg_primal, + double *__restrict__ reflected_primal, + const double *__restrict__ current_primal, + const double *__restrict__ variable_rescaling, + const double *__restrict__ Q_diag, + double tau, + double *__restrict__ warm_start, + const int *__restrict__ start_idx, + const int *__restrict__ v_dim, + const char *__restrict__ is_fixed, + int num_blocks); +#ifdef __cplusplus +} +#endif diff --git a/internal/pdhcg_kernels.cuh b/internal/pdhcg_kernels.cuh deleted file mode 100644 index 23ddc90..0000000 --- a/internal/pdhcg_kernels.cuh +++ /dev/null @@ -1,923 +0,0 @@ -/* -Copyright 2025 Haihao Lu -Copyright 2026 Hongpei Li - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -#ifndef PDHCG_KERNELS_CUH -#define PDHCG_KERNELS_CUH - -#include -#include - -#ifdef __cplusplus -extern "C" -{ -#endif - // ====================================================================== - // Utility Operations - // ====================================================================== - - __global__ void - element_wise_mul_kernel(const double *__restrict__ A, const double *__restrict__ B, double *__restrict__ C, int n); - - __global__ void element_wise_mul_inplace_kernel(double *__restrict__ x, const double *__restrict__ d, int n); - - __global__ void vector_sub_kernel(double *__restrict__ direction, - const double *__restrict__ a, - const double *__restrict__ b, - int n); - - __global__ void - vector_add_kernel(const double *__restrict__ a, const double *__restrict__ b, double *__restrict__ out, int n); - - __global__ void project_primal_onto_bounds_kernel(double *__restrict__ primal_solution, - const double *__restrict__ variable_lower_bound, - const double *__restrict__ variable_upper_bound, - int num_variables); - - __global__ void prepare_projected_gradient_point_kernel(double *__restrict__ projected_point, - const double *__restrict__ primal_solution, - const double *__restrict__ effective_objective, - const double *__restrict__ dual_product, - const double *__restrict__ variable_lower_bound, - const double *__restrict__ variable_upper_bound, - double step_size, - int num_variables); - - __global__ void augment_projected_gradient_residual_kernel(double *__restrict__ dual_residual, - const double *__restrict__ primal_solution, - const double *__restrict__ projected_point, - const double *__restrict__ variable_rescaling, - double step_size, - int num_variables); - - // ====================================================================== - // Advanced Metrics & Reduced Costs - // ====================================================================== - - __global__ void compute_and_rescale_reduced_cost_kernel(double *reduced_cost, - const double *objective, - const double *dual_product, - const double *variable_rescaling, - const double objective_vector_rescaling, - const double constraint_bound_rescaling, - int n_vars); - - // ====================================================================== - // Primal Updates - // ====================================================================== - - __global__ void compute_lp_next_pdhg_primal_solution_kernel(const double *current_primal, - double *reflected_primal, - const double *dual_product, - const double *objective, - const double *var_lb, - const double *var_ub, - int n, - double step_size); - - __global__ void compute_lp_next_pdhg_primal_solution_major_kernel(const double *current_primal, - double *pdhg_primal, - double *reflected_primal, - const double *dual_product, - const double *objective, - const double *var_lb, - const double *var_ub, - int n, - double step_size, - double *dual_slack); - - __global__ void compute_diagonal_q_next_pdhg_primal_solution_kernel(const double *current_primal, - double *reflected_primal, - double *objective_product, - const double *dual_product, - const double *objective, - const double *var_lb, - const double *var_ub, - int n, - double step_size); - - __global__ void compute_diagonal_q_next_pdhg_primal_solution_major_kernel(const double *current_primal, - double *pdhg_primal, - double *reflected_primal, - double *objective_product, - const double *dual_product, - const double *objective, - const double *var_lb, - const double *var_ub, - int n, - double step_size); - - // ====================================================================== - // Dual Updates - // ====================================================================== - - __global__ void compute_next_pdhg_dual_solution_kernel(const double *current_dual, - double *reflected_dual, - const double *primal_product, - const double *affine_cone_offset, - const double *constraint_lower_bound, - const double *constraint_upper_bound, - int n, - double step_size); - - __global__ void compute_next_pdhg_dual_solution_major_kernel(const double *current_dual, - double *pdhg_dual, - double *reflected_dual, - const double *primal_product, - const double *affine_cone_offset, - const double *constraint_lower_bound, - const double *constraint_upper_bound, - int n, - double step_size); - - __global__ void prepare_constraint_dual_update_kernel(const double *current_dual, - const double *primal_product, - const double *affine_cone_offset, - const double *constraint_lower_bound, - const double *constraint_upper_bound, - double *projected_constraint_value, - int n, - double step_size); - - __global__ void finish_constraint_dual_update_kernel(const double *current_dual, - const double *primal_product, - const double *affine_cone_offset, - const double *projected_constraint_value, - double *pdhg_dual, - double *reflected_dual, - int n, - double step_size); - - // ====================================================================== - // Halpern & Solution Management - // ====================================================================== - - __global__ void halpern_update_kernel(const double *initial_primal, - double *current_primal, - const double *reflected_primal, - const double *initial_dual, - double *current_dual, - const double *reflected_dual, - int n_vars, - int n_cons, - double weight, - double reflection_coeff); - - __global__ void rescale_solution_kernel(double *primal_solution, - double *dual_solution, - const double *variable_rescaling, - const double *constraint_rescaling, - const double objective_vector_rescaling, - const double constraint_bound_rescaling, - int n_vars, - int n_cons); - - __global__ void compute_delta_solution_kernel(const double *initial_primal, - const double *pdhg_primal, - double *delta_primal, - const double *initial_dual, - const double *pdhg_dual, - double *delta_dual, - int n_vars, - int n_cons); - - // ====================================================================== - // Primal Inner Solver (Gradient Descent & Barzilai-Borwein) - // ====================================================================== - - __global__ void primal_gradient_descent_kernel(const double *dual_product, - const double *current_primal_solution, - double *reflected_primal, - const double *objective_vector, - const double *objective_product, - const double *var_lb, - const double *var_ub, - const double stepsize, - const int n_vars); - - __global__ void primal_gradient_descent_kernel_major(const double *dual_product, - const double *current_primal_solution, - double *reflected_primal, - double *pdhg_primal_solution, - const double *objective_vector, - const double *objective_product, - const double *var_lb, - const double *var_ub, - const double stepsize, - const int n_vars); - - __global__ void compute_bb_alpha_safeguard_kernel(const double *d_norm_gtg, const double *d_tmp, double *d_alpha); - - __global__ void compute_bb_alpha_M_kernel(const double *d_stMs, const double *d_tmp, double *d_alpha); - - __global__ void scalar_sqrt_copy_kernel(const double *src, double *dst); - - __global__ void - compute_csr_diag_kernel(const int *row_ptr, const int *col_ind, const double *val, double *diag, int num_rows); - - __global__ void compute_csr_row_sq_norm_kernel(const int *row_ptr, const double *val, double *out, int num_rows); - - __global__ void compute_csr_row_sq_norm_weighted_kernel( - const int *row_ptr, const int *col_ind, const double *val, const double *weights, double *out, int num_rows); - - __global__ void compute_csr_row_quad_form_dense_kernel(const int *row_ptr, - const int *col_ind, - const double *val, - const double *D_dense, - int rank, - double *out, - int num_rows); - - __global__ void refresh_inner_precond_kernel( - const double *diag_h_static, double inv_tau, double *m_diag, double *m_inv, int n_vars); - - __global__ void primal_gradient_descent_kernel_bb_init(const double *dual_product, - double *gradient, - double *direction, - const double *current_primal_solution, - double *pdhg_primal_solution, - const double *objective_vector, - const double *objective_product, - const double *var_lb, - const double *var_ub, - const double stepsize, - const int n_vars); - - __global__ void primal_bb_update_gradient_kernel(double *pdhg_primal_solution, - const double *current_primal_solution, - const double *objective_vector, - const double *dual_product, - const double *objective_product, - double *gradient, - double *delta_gradient, - const double inv_step_size, - const int n_vars); - - __global__ void primal_bb_update_direction_kernel(double *pdhg_primal_solution, - const double *gradient, - double *direction, - const double *var_lb, - const double *var_ub, - const double *d_alpha, - const int n_vars); - - __global__ void primal_gradient_descent_kernel_bb_init_precond(const double *dual_product, - double *gradient, - double *direction, - const double *current_primal_solution, - double *pdhg_primal_solution, - const double *objective_vector, - const double *objective_product, - const double *var_lb, - const double *var_ub, - const double *m_inv, - const double stepsize, - const int n_vars); - - __global__ void primal_bb_update_direction_kernel_precond(double *pdhg_primal_solution, - const double *gradient, - double *direction, - const double *var_lb, - const double *var_ub, - const double *m_inv, - const double *d_alpha, - const int n_vars); - - __global__ void primal_bb_final_kernel(const double *current_primal_solution, - const double *pdhg_primal_solution, - double *reflected_primal_solution, - const int n_vars); - - // ====================================================================== - // Residuals & Infeasibility Metrics - // ====================================================================== - - __global__ void compute_lp_residual_kernel(double *primal_residual, - const double *primal_product, - const double *affine_cone_offset, - const double *constraint_lower_bound, - const double *constraint_upper_bound, - const double *dual_solution, - double *dual_residual, - const double *dual_product, - const double *dual_slack, - const double *objective_vector, - const double *constraint_rescaling, - const double *variable_rescaling, - double *affine_dual_membership, - double *dual_obj_contribution, - const double *const_lb_finite, - const double *const_ub_finite, - bool defer_constraint_projection, - int num_constraints, - int num_variables); - - __global__ void compute_qp_residual_kernel(double *primal_residual, - const double *primal_product, - const double *affine_cone_offset, - const double *primal_obj_product, - const double *primal_solution, - const double *constraint_lower_bound, - const double *constraint_upper_bound, - const double *variable_lower_bound, - const double *variable_upper_bound, - const double *dual_solution, - double *dual_residual, - const double *dual_product, - double *dual_slack, - const double *objective_vector, - const double *constraint_rescaling, - const double *variable_rescaling, - double *affine_dual_membership, - double *dual_obj_contribution, - const double *const_lb_finite, - const double *const_ub_finite, - const double step_size, - bool defer_constraint_projection, - int num_constraints, - int num_variables); - - __global__ void finish_affine_cone_residuals_kernel(double *primal_residual, - const double *primal_product, - const double *affine_cone_offset, - const double *constraint_rescaling, - double *dual_membership, - const double *dual_membership_rescaling, - int n); - - __global__ void prepare_affine_cone_residuals_kernel(double *projection_point, - double *complementarity_residual, - const double *primal_product, - const double *affine_cone_offset, - const double *dual_solution, - const int *start_idx, - const int *v_dim, - double constraint_bound_rescaling, - int num_cones); - - __global__ void prepare_affine_cone_residuals_grid_kernel(double *projection_point, - double *complementarity_accumulator, - const double *primal_product, - const double *affine_cone_offset, - const double *dual_solution, - const int *start_idx, - const int *v_dim, - int num_cones, - int blocks_per_cone); - - __global__ void finish_affine_cone_complementarity_kernel(double *complementarity_residual, - double constraint_bound_rescaling, - int num_cones); - - __global__ void recover_primal_obj_dual_product(double *dual_product, - double *primal_obj_product, - const double *variable_rescaling, - int num_variables); - - __global__ void primal_infeasibility_project_kernel(double *primal_ray_estimate, - const double *variable_lower_bound, - const double *variable_upper_bound, - int num_variables); - - __global__ void dual_infeasibility_project_kernel(double *dual_ray_estimate, - const double *constraint_lower_bound, - const double *constraint_upper_bound, - int num_constraints); - - __global__ void compute_primal_infeasibility_kernel(const double *primal_product, - const double *const_lb, - const double *const_ub, - int num_constraints, - double *primal_infeasibility, - const double *constraint_rescaling); - - __global__ void compute_dual_infeasibility_kernel(const double *dual_product, - const double *var_lb, - const double *var_ub, - int num_variables, - double *dual_infeasibility, - const double *variable_rescaling); - - __global__ void - dual_solution_dual_objective_contribution_kernel(const double *constraint_lower_bound_finite_val, - const double *constraint_upper_bound_finite_val, - const double *affine_cone_offset, - const double *dual_solution, - int num_constraints, - double *dual_objective_dual_solution_contribution_array); - - __global__ void - dual_objective_dual_slack_contribution_array_kernel(const double *dual_slack, - double *dual_objective_dual_slack_contribution_array, - const double *variable_lower_bound_finite_val, - const double *variable_upper_bound_finite_val, - int num_variables); - - __global__ void compute_and_rescale_reduced_cost_qp_kernel(double *__restrict__ reduced_cost, - const double *__restrict__ objective, - const double *__restrict__ quadratic_product, - const double *__restrict__ dual_product, - const double *__restrict__ variable_rescaling, - const double objective_vector_rescaling, - const double constraint_bound_rescaling, - const double *__restrict__ variable_lower_bound, - const double *__restrict__ variable_upper_bound, - int n_vars); - - __global__ void project_rotated_soc_kernel(double *__restrict__ primal_solution, - const double *__restrict__ variable_rescaling, - double *__restrict__ warm_start, - const int *__restrict__ start_idx, - const int *__restrict__ v_dim, - const char *__restrict__ is_fixed, - int num_blocks); - - __global__ void project_rotated_soc_warp_kernel(double *__restrict__ primal_solution, - const double *__restrict__ variable_rescaling, - double *__restrict__ warm_start, - const int *__restrict__ start_idx, - const int *__restrict__ v_dim, - const char *__restrict__ is_fixed, - int num_blocks); - - __global__ void project_rotated_soc_block_kernel(double *__restrict__ primal_solution, - const double *__restrict__ variable_rescaling, - const double *__restrict__ Q_diag, - double tau, - double *__restrict__ warm_start, - const int *__restrict__ start_idx, - const int *__restrict__ v_dim, - const char *__restrict__ is_fixed, - int num_blocks); - - __global__ void initialize_rotated_soc_grid_weighted_kernel(const double *__restrict__ primal_solution, - const double *__restrict__ variable_rescaling, - const double *__restrict__ Q_diag, - double tau, - double *__restrict__ workspace, - const int *__restrict__ start_idx, - const int *__restrict__ v_dim, - const char *__restrict__ is_fixed, - int num_cones, - int blocks_per_cone); - - __global__ void - finalize_rotated_soc_grid_weighted_initialization_kernel(const double *__restrict__ primal_solution, - const double *__restrict__ variable_rescaling, - const double *__restrict__ Q_diag, - double tau, - double *__restrict__ workspace, - const int *__restrict__ start_idx, - const int *__restrict__ v_dim, - const char *__restrict__ is_fixed, - int num_cones); - - __global__ void reduce_rotated_soc_grid_weighted_root_kernel(const double *__restrict__ primal_solution, - const double *__restrict__ variable_rescaling, - const double *__restrict__ Q_diag, - double tau, - double *__restrict__ workspace, - const int *__restrict__ start_idx, - const int *__restrict__ v_dim, - const char *__restrict__ is_fixed, - int num_cones, - int blocks_per_cone); - - __global__ void finalize_rotated_soc_grid_weighted_root_kernel(const double *__restrict__ primal_solution, - const double *__restrict__ variable_rescaling, - const double *__restrict__ Q_diag, - double tau, - double *__restrict__ workspace, - const int *__restrict__ start_idx, - const int *__restrict__ v_dim, - const char *__restrict__ is_fixed, - int num_cones); - - __global__ void reduce_rotated_soc_grid_axis_objective_kernel(const double *__restrict__ primal_solution, - const double *__restrict__ variable_rescaling, - const double *__restrict__ Q_diag, - double tau, - double *__restrict__ workspace, - const int *__restrict__ start_idx, - const int *__restrict__ v_dim, - const char *__restrict__ is_fixed, - int num_cones, - int blocks_per_cone); - - __global__ void finalize_rotated_soc_grid_axis_objective_kernel(const double *__restrict__ primal_solution, - const double *__restrict__ variable_rescaling, - const double *__restrict__ Q_diag, - double tau, - double *__restrict__ workspace, - const int *__restrict__ start_idx, - const int *__restrict__ v_dim, - int num_cones); - - __global__ void apply_rotated_soc_grid_weighted_kernel(double *__restrict__ primal_solution, - const double *__restrict__ variable_rescaling, - const double *__restrict__ Q_diag, - double tau, - const double *__restrict__ workspace, - const int *__restrict__ start_idx, - const int *__restrict__ v_dim, - const char *__restrict__ is_fixed, - int num_cones, - int blocks_per_cone); - - __global__ void project_rotated_soc_grid_reduce_kernel(double *__restrict__ primal_solution, - double *__restrict__ workspace, - const int *__restrict__ start_idx, - const int *__restrict__ v_dim, - int num_cones, - int blocks_per_cone); - - __global__ void project_rotated_soc_grid_finalize_kernel(double *__restrict__ primal_solution, - double *__restrict__ workspace, - const int *__restrict__ start_idx, - const int *__restrict__ v_dim, - int num_cones); - - __global__ void project_rotated_soc_grid_apply_kernel(double *__restrict__ primal_solution, - const double *__restrict__ workspace, - const int *__restrict__ start_idx, - const int *__restrict__ v_dim, - int num_cones, - int blocks_per_cone); - - __global__ void project_standard_soc_kernel(double *__restrict__ primal_solution, - const double *__restrict__ variable_rescaling, - double *__restrict__ warm_start, - const int *__restrict__ start_idx, - const int *__restrict__ v_dim, - const char *__restrict__ is_fixed, - int num_blocks); - - __global__ void project_standard_soc_warp_kernel(double *__restrict__ primal_solution, - const double *__restrict__ variable_rescaling, - double *__restrict__ warm_start, - const int *__restrict__ start_idx, - const int *__restrict__ v_dim, - const char *__restrict__ is_fixed, - int num_blocks); - - __global__ void project_standard_soc_block_kernel(double *__restrict__ primal_solution, - const double *__restrict__ variable_rescaling, - const double *__restrict__ Q_diag, - double tau, - double *__restrict__ warm_start, - const int *__restrict__ start_idx, - const int *__restrict__ v_dim, - const char *__restrict__ is_fixed, - int num_blocks); - - __global__ void initialize_standard_soc_grid_weighted_kernel(const double *__restrict__ primal_solution, - const double *__restrict__ variable_rescaling, - const double *__restrict__ Q_diag, - double tau, - double *__restrict__ workspace, - const int *__restrict__ start_idx, - const int *__restrict__ v_dim, - const char *__restrict__ is_fixed, - int num_cones, - int blocks_per_cone); - - __global__ void - finalize_standard_soc_grid_weighted_initialization_kernel(const double *__restrict__ primal_solution, - const double *__restrict__ variable_rescaling, - const double *__restrict__ Q_diag, - double tau, - double *__restrict__ workspace, - const int *__restrict__ start_idx, - const int *__restrict__ v_dim, - const char *__restrict__ is_fixed, - int num_cones); - - __global__ void reduce_standard_soc_grid_weighted_root_kernel(const double *__restrict__ primal_solution, - const double *__restrict__ variable_rescaling, - const double *__restrict__ Q_diag, - double tau, - double *__restrict__ workspace, - const int *__restrict__ start_idx, - const int *__restrict__ v_dim, - const char *__restrict__ is_fixed, - int num_cones, - int blocks_per_cone); - - __global__ void finalize_standard_soc_grid_weighted_root_kernel(double *__restrict__ primal_solution, - const double *__restrict__ variable_rescaling, - const double *__restrict__ Q_diag, - double tau, - double *__restrict__ workspace, - const int *__restrict__ start_idx, - const int *__restrict__ v_dim, - int num_cones); - - __global__ void apply_standard_soc_grid_weighted_kernel(double *__restrict__ primal_solution, - const double *__restrict__ variable_rescaling, - const double *__restrict__ Q_diag, - double tau, - const double *__restrict__ workspace, - const int *__restrict__ start_idx, - const int *__restrict__ v_dim, - const char *__restrict__ is_fixed, - int num_cones, - int blocks_per_cone); - - __global__ void project_standard_soc_grid_reduce_kernel(double *__restrict__ primal_solution, - double *__restrict__ workspace, - const int *__restrict__ start_idx, - const int *__restrict__ v_dim, - int num_cones, - int blocks_per_cone); - - __global__ void project_standard_soc_grid_finalize_kernel(double *__restrict__ primal_solution, - double *__restrict__ workspace, - const int *__restrict__ start_idx, - const int *__restrict__ v_dim, - int num_cones); - - __global__ void project_standard_soc_grid_apply_kernel(double *__restrict__ primal_solution, - const double *__restrict__ workspace, - const int *__restrict__ start_idx, - const int *__restrict__ v_dim, - int num_cones, - int blocks_per_cone); - - __global__ void compute_cone_dual_residual_kernel(double *__restrict__ dual_residual, - double *__restrict__ complementarity_residual, - const double *__restrict__ objective_vector, - const double *__restrict__ dual_product, - const double *__restrict__ variable_rescaling, - const double *__restrict__ primal_solution, - double *__restrict__ warm_start, - const int *__restrict__ start_idx, - const int *__restrict__ v_dim, - const char *__restrict__ is_fixed, - int num_blocks); - - __global__ void compute_cone_dual_residual_warp_kernel(double *__restrict__ dual_residual, - double *__restrict__ complementarity_residual, - const double *__restrict__ objective_vector, - const double *__restrict__ dual_product, - const double *__restrict__ variable_rescaling, - const double *__restrict__ primal_solution, - double *__restrict__ warm_start, - const int *__restrict__ start_idx, - const int *__restrict__ v_dim, - const char *__restrict__ is_fixed, - int num_blocks); - - __global__ void compute_cone_dual_residual_grid_reduce_kernel(const double *__restrict__ objective_vector, - const double *__restrict__ dual_product, - double *__restrict__ workspace, - const int *__restrict__ start_idx, - const int *__restrict__ v_dim, - int num_cones, - int blocks_per_cone); - - __global__ void compute_cone_dual_residual_grid_finalize_kernel(double *__restrict__ dual_residual, - const double *__restrict__ objective_vector, - const double *__restrict__ dual_product, - const double *__restrict__ variable_rescaling, - double *__restrict__ workspace, - const int *__restrict__ start_idx, - const int *__restrict__ v_dim, - int num_cones); - - __global__ void compute_cone_dual_residual_grid_apply_kernel(double *__restrict__ dual_residual, - const double *__restrict__ objective_vector, - const double *__restrict__ dual_product, - const double *__restrict__ variable_rescaling, - const double *__restrict__ workspace, - const int *__restrict__ start_idx, - const int *__restrict__ v_dim, - int num_cones, - int blocks_per_cone); - - __global__ void compute_cone_dual_residual_standard_warp_kernel(double *__restrict__ dual_residual, - double *__restrict__ complementarity_residual, - const double *__restrict__ objective_vector, - const double *__restrict__ dual_product, - const double *__restrict__ variable_rescaling, - const double *__restrict__ primal_solution, - double *__restrict__ warm_start, - const int *__restrict__ start_idx, - const int *__restrict__ v_dim, - const char *__restrict__ is_fixed, - int num_blocks); - - __global__ void compute_cone_dual_residual_standard_grid_reduce_kernel(const double *__restrict__ objective_vector, - const double *__restrict__ dual_product, - double *__restrict__ workspace, - const int *__restrict__ start_idx, - const int *__restrict__ v_dim, - int num_cones, - int blocks_per_cone); - - __global__ void - compute_cone_dual_residual_standard_grid_finalize_kernel(double *__restrict__ dual_residual, - const double *__restrict__ objective_vector, - const double *__restrict__ dual_product, - const double *__restrict__ variable_rescaling, - double *__restrict__ workspace, - const int *__restrict__ start_idx, - const int *__restrict__ v_dim, - int num_cones); - - __global__ void compute_cone_dual_residual_standard_grid_apply_kernel(double *__restrict__ dual_residual, - const double *__restrict__ objective_vector, - const double *__restrict__ dual_product, - const double *__restrict__ variable_rescaling, - const double *__restrict__ workspace, - const int *__restrict__ start_idx, - const int *__restrict__ v_dim, - int num_cones, - int blocks_per_cone); - - __global__ void project_exp_cone_kernel(double *__restrict__ primal_solution, - const double *__restrict__ variable_rescaling, - double *__restrict__ warm_start, - const int *__restrict__ start_idx, - const int *__restrict__ v_dim, - const char *__restrict__ is_fixed, - int num_blocks); - - __global__ void compute_cone_dual_residual_exp_kernel(double *__restrict__ dual_residual, - double *__restrict__ complementarity_residual, - const double *__restrict__ objective_vector, - const double *__restrict__ dual_product, - const double *__restrict__ variable_rescaling, - const double *__restrict__ primal_solution, - double *__restrict__ warm_start, - const int *__restrict__ start_idx, - const int *__restrict__ v_dim, - const char *__restrict__ is_fixed, - int num_blocks); - - __global__ void project_power_cone_kernel(double *__restrict__ primal_solution, - const double *__restrict__ variable_rescaling, - double *__restrict__ warm_start, - const int *__restrict__ start_idx, - const int *__restrict__ v_dim, - const double *__restrict__ power_alpha, - const char *__restrict__ is_fixed, - int num_blocks); - - __global__ void compute_cone_dual_residual_power_kernel(double *__restrict__ dual_residual, - double *__restrict__ complementarity_residual, - const double *__restrict__ objective_vector, - const double *__restrict__ dual_product, - const double *__restrict__ variable_rescaling, - const double *__restrict__ primal_solution, - double *__restrict__ warm_start, - const int *__restrict__ start_idx, - const int *__restrict__ v_dim, - const double *__restrict__ power_alpha, - const char *__restrict__ is_fixed, - int num_blocks); - - __global__ void compute_power_cone_primal_violation_kernel(double *__restrict__ absolute_violation, - double *__restrict__ relative_violation, - const double *__restrict__ primal_solution, - const double *__restrict__ variable_rescaling, - const int *__restrict__ start_idx, - const double *__restrict__ power_alpha, - double homogeneous_scale, - int num_blocks); - - __global__ void project_power_cone_diag_q_kernel(double *__restrict__ pdhg_primal, - double *__restrict__ reflected_primal, - const double *__restrict__ current_primal, - const double *__restrict__ variable_rescaling, - const double *__restrict__ Q_diag, - double tau, - double *__restrict__ warm_start, - const int *__restrict__ start_idx, - const int *__restrict__ v_dim, - const double *__restrict__ power_alpha, - const char *__restrict__ is_fixed, - int num_blocks); - - __global__ void compute_cone_dual_residual_standard_kernel(double *__restrict__ dual_residual, - double *__restrict__ complementarity_residual, - const double *__restrict__ objective_vector, - const double *__restrict__ dual_product, - const double *__restrict__ variable_rescaling, - const double *__restrict__ primal_solution, - double *__restrict__ warm_start, - const int *__restrict__ start_idx, - const int *__restrict__ v_dim, - const char *__restrict__ is_fixed, - int num_blocks); - - __global__ void set_cone_dual_slack_kernel(double *__restrict__ dual_slack, - const double *__restrict__ objective_vector, - const double *__restrict__ dual_product, - const int *__restrict__ start_idx, - const int *__restrict__ v_dim, - int num_blocks); - - __global__ void set_cone_dual_slack_warp_kernel(double *__restrict__ dual_slack, - const double *__restrict__ objective_vector, - const double *__restrict__ dual_product, - const int *__restrict__ start_idx, - const int *__restrict__ v_dim, - int num_cones); - - __global__ void set_cone_dual_slack_grid_kernel(double *__restrict__ dual_slack, - const double *__restrict__ objective_vector, - const double *__restrict__ dual_product, - const int *__restrict__ start_idx, - const int *__restrict__ v_dim, - int num_cones, - int blocks_per_cone); - - __global__ void recompute_reflected_at_cone_kernel(double *__restrict__ reflected_primal, - const double *__restrict__ pdhg_primal, - const double *__restrict__ current_primal, - const int *__restrict__ start_idx, - const int *__restrict__ v_dim, - int num_blocks); - - __global__ void recompute_reflected_at_cone_warp_kernel(double *__restrict__ reflected_primal, - const double *__restrict__ pdhg_primal, - const double *__restrict__ current_primal, - const int *__restrict__ start_idx, - const int *__restrict__ v_dim, - int num_cones); - - __global__ void recompute_reflected_at_cone_block_kernel(double *__restrict__ reflected_primal, - const double *__restrict__ pdhg_primal, - const double *__restrict__ current_primal, - const int *__restrict__ start_idx, - const int *__restrict__ v_dim, - int num_cones); - - __global__ void recompute_reflected_at_cone_grid_kernel(double *__restrict__ reflected_primal, - const double *__restrict__ pdhg_primal, - const double *__restrict__ current_primal, - const int *__restrict__ start_idx, - const int *__restrict__ v_dim, - int num_cones, - int blocks_per_cone); - - __global__ void clear_cone_residual_grid_kernel(double *__restrict__ dual_residual, - const int *__restrict__ start_idx, - const int *__restrict__ v_dim, - int num_cones, - int blocks_per_cone); - - __global__ void project_rotated_soc_diag_q_kernel(double *__restrict__ pdhg_primal, - double *__restrict__ reflected_primal, - const double *__restrict__ current_primal, - const double *__restrict__ variable_rescaling, - const double *__restrict__ Q_diag, - double tau, - double *__restrict__ warm_start, - const int *__restrict__ start_idx, - const int *__restrict__ v_dim, - const char *__restrict__ is_fixed, - int num_blocks); - - __global__ void project_standard_soc_diag_q_kernel(double *__restrict__ pdhg_primal, - double *__restrict__ reflected_primal, - const double *__restrict__ current_primal, - const double *__restrict__ variable_rescaling, - const double *__restrict__ Q_diag, - double tau, - double *__restrict__ warm_start, - const int *__restrict__ start_idx, - const int *__restrict__ v_dim, - const char *__restrict__ is_fixed, - int num_blocks); - - __global__ void project_exp_cone_diag_q_kernel(double *__restrict__ pdhg_primal, - double *__restrict__ reflected_primal, - const double *__restrict__ current_primal, - const double *__restrict__ variable_rescaling, - const double *__restrict__ Q_diag, - double tau, - double *__restrict__ warm_start, - const int *__restrict__ start_idx, - const int *__restrict__ v_dim, - const char *__restrict__ is_fixed, - int num_blocks); - -#ifdef __cplusplus -} -#endif - -#endif // PDHCG_KERNELS_CUH diff --git a/internal/pdhcg_kernels.h b/internal/pdhcg_kernels.h new file mode 100644 index 0000000..d73aa6c --- /dev/null +++ b/internal/pdhcg_kernels.h @@ -0,0 +1,412 @@ +/* +Copyright 2025 Haihao Lu +Copyright 2026 Hongpei Li + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +#ifndef PDHCG_KERNELS_H +#define PDHCG_KERNELS_H + +#include +#include + +#ifdef __cplusplus +extern "C" +{ +#endif + // ====================================================================== + // Utility Operations + // ====================================================================== + + __global__ void + element_wise_mul_kernel(const double *__restrict__ A, const double *__restrict__ B, double *__restrict__ C, int n); + + __global__ void element_wise_mul_inplace_kernel(double *__restrict__ x, const double *__restrict__ d, int n); + + __global__ void vector_sub_kernel(double *__restrict__ direction, + const double *__restrict__ a, + const double *__restrict__ b, + int n); + + __global__ void + vector_add_kernel(const double *__restrict__ a, const double *__restrict__ b, double *__restrict__ out, int n); + + __global__ void project_primal_onto_bounds_kernel(double *__restrict__ primal_solution, + const double *__restrict__ variable_lower_bound, + const double *__restrict__ variable_upper_bound, + int num_variables); + + __global__ void prepare_projected_gradient_point_kernel(double *__restrict__ projected_point, + const double *__restrict__ primal_solution, + const double *__restrict__ effective_objective, + const double *__restrict__ dual_product, + const double *__restrict__ variable_lower_bound, + const double *__restrict__ variable_upper_bound, + double step_size, + int num_variables); + + __global__ void augment_projected_gradient_residual_kernel(double *__restrict__ dual_residual, + const double *__restrict__ primal_solution, + const double *__restrict__ projected_point, + const double *__restrict__ variable_rescaling, + double step_size, + int num_variables); + + // ====================================================================== + // Advanced Metrics & Reduced Costs + // ====================================================================== + + __global__ void compute_and_rescale_reduced_cost_kernel(double *reduced_cost, + const double *objective, + const double *dual_product, + const double *variable_rescaling, + const double objective_vector_rescaling, + const double constraint_bound_rescaling, + int n_vars); + + // ====================================================================== + // Primal Updates + // ====================================================================== + + __global__ void compute_lp_next_pdhg_primal_solution_kernel(const double *current_primal, + double *reflected_primal, + const double *dual_product, + const double *objective, + const double *var_lb, + const double *var_ub, + int n, + double step_size); + + __global__ void compute_lp_next_pdhg_primal_solution_major_kernel(const double *current_primal, + double *pdhg_primal, + double *reflected_primal, + const double *dual_product, + const double *objective, + const double *var_lb, + const double *var_ub, + int n, + double step_size, + double *dual_slack); + + __global__ void compute_diagonal_q_next_pdhg_primal_solution_kernel(const double *current_primal, + double *reflected_primal, + double *objective_product, + const double *dual_product, + const double *objective, + const double *var_lb, + const double *var_ub, + int n, + double step_size); + + __global__ void compute_diagonal_q_next_pdhg_primal_solution_major_kernel(const double *current_primal, + double *pdhg_primal, + double *reflected_primal, + double *objective_product, + const double *dual_product, + const double *objective, + const double *var_lb, + const double *var_ub, + int n, + double step_size); + + // ====================================================================== + // Dual Updates + // ====================================================================== + + __global__ void compute_next_pdhg_dual_solution_kernel(const double *current_dual, + double *reflected_dual, + const double *primal_product, + const double *affine_cone_offset, + const double *constraint_lower_bound, + const double *constraint_upper_bound, + int n, + double step_size); + + __global__ void compute_next_pdhg_dual_solution_major_kernel(const double *current_dual, + double *pdhg_dual, + double *reflected_dual, + const double *primal_product, + const double *affine_cone_offset, + const double *constraint_lower_bound, + const double *constraint_upper_bound, + int n, + double step_size); + + __global__ void prepare_constraint_dual_update_kernel(const double *current_dual, + const double *primal_product, + const double *affine_cone_offset, + const double *constraint_lower_bound, + const double *constraint_upper_bound, + double *projected_constraint_value, + int n, + double step_size); + + __global__ void finish_constraint_dual_update_kernel(const double *current_dual, + const double *primal_product, + const double *affine_cone_offset, + const double *projected_constraint_value, + double *pdhg_dual, + double *reflected_dual, + int n, + double step_size); + + // ====================================================================== + // Halpern & Solution Management + // ====================================================================== + + __global__ void halpern_update_kernel(const double *initial_primal, + double *current_primal, + const double *reflected_primal, + const double *initial_dual, + double *current_dual, + const double *reflected_dual, + int n_vars, + int n_cons, + double weight, + double reflection_coeff); + + __global__ void rescale_solution_kernel(double *primal_solution, + double *dual_solution, + const double *variable_rescaling, + const double *constraint_rescaling, + const double objective_vector_rescaling, + const double constraint_bound_rescaling, + int n_vars, + int n_cons); + + __global__ void compute_delta_solution_kernel(const double *initial_primal, + const double *pdhg_primal, + double *delta_primal, + const double *initial_dual, + const double *pdhg_dual, + double *delta_dual, + int n_vars, + int n_cons); + + // ====================================================================== + // Primal Inner Solver (Gradient Descent & Barzilai-Borwein) + // ====================================================================== + + __global__ void primal_gradient_descent_kernel(const double *dual_product, + const double *current_primal_solution, + double *reflected_primal, + const double *objective_vector, + const double *objective_product, + const double *var_lb, + const double *var_ub, + const double stepsize, + const int n_vars); + + __global__ void primal_gradient_descent_kernel_major(const double *dual_product, + const double *current_primal_solution, + double *reflected_primal, + double *pdhg_primal_solution, + const double *objective_vector, + const double *objective_product, + const double *var_lb, + const double *var_ub, + const double stepsize, + const int n_vars); + + __global__ void compute_bb_alpha_safeguard_kernel(const double *d_norm_gtg, const double *d_tmp, double *d_alpha); + + __global__ void compute_bb_alpha_M_kernel(const double *d_stMs, const double *d_tmp, double *d_alpha); + + __global__ void scalar_sqrt_copy_kernel(const double *src, double *dst); + + __global__ void + compute_csr_diag_kernel(const int *row_ptr, const int *col_ind, const double *val, double *diag, int num_rows); + + __global__ void compute_csr_row_sq_norm_kernel(const int *row_ptr, const double *val, double *out, int num_rows); + + __global__ void compute_csr_row_sq_norm_weighted_kernel( + const int *row_ptr, const int *col_ind, const double *val, const double *weights, double *out, int num_rows); + + __global__ void compute_csr_row_quad_form_dense_kernel(const int *row_ptr, + const int *col_ind, + const double *val, + const double *D_dense, + int rank, + double *out, + int num_rows); + + __global__ void refresh_inner_precond_kernel( + const double *diag_h_static, double inv_tau, double *m_diag, double *m_inv, int n_vars); + + __global__ void primal_gradient_descent_kernel_bb_init(const double *dual_product, + double *gradient, + double *direction, + const double *current_primal_solution, + double *pdhg_primal_solution, + const double *objective_vector, + const double *objective_product, + const double *var_lb, + const double *var_ub, + const double stepsize, + const int n_vars); + + __global__ void primal_bb_update_gradient_kernel(double *pdhg_primal_solution, + const double *current_primal_solution, + const double *objective_vector, + const double *dual_product, + const double *objective_product, + double *gradient, + double *delta_gradient, + const double inv_step_size, + const int n_vars); + + __global__ void primal_bb_update_direction_kernel(double *pdhg_primal_solution, + const double *gradient, + double *direction, + const double *var_lb, + const double *var_ub, + const double *d_alpha, + const int n_vars); + + __global__ void primal_gradient_descent_kernel_bb_init_precond(const double *dual_product, + double *gradient, + double *direction, + const double *current_primal_solution, + double *pdhg_primal_solution, + const double *objective_vector, + const double *objective_product, + const double *var_lb, + const double *var_ub, + const double *m_inv, + const double stepsize, + const int n_vars); + + __global__ void primal_bb_update_direction_kernel_precond(double *pdhg_primal_solution, + const double *gradient, + double *direction, + const double *var_lb, + const double *var_ub, + const double *m_inv, + const double *d_alpha, + const int n_vars); + + __global__ void primal_bb_final_kernel(const double *current_primal_solution, + const double *pdhg_primal_solution, + double *reflected_primal_solution, + const int n_vars); + + // ====================================================================== + // Residuals & Infeasibility Metrics + // ====================================================================== + + __global__ void compute_lp_residual_kernel(double *primal_residual, + const double *primal_product, + const double *affine_cone_offset, + const double *constraint_lower_bound, + const double *constraint_upper_bound, + const double *dual_solution, + double *dual_residual, + const double *dual_product, + const double *dual_slack, + const double *objective_vector, + const double *constraint_rescaling, + const double *variable_rescaling, + double *affine_dual_membership, + double *dual_obj_contribution, + const double *const_lb_finite, + const double *const_ub_finite, + bool defer_constraint_projection, + int num_constraints, + int num_variables); + + __global__ void compute_qp_residual_kernel(double *primal_residual, + const double *primal_product, + const double *affine_cone_offset, + const double *primal_obj_product, + const double *primal_solution, + const double *constraint_lower_bound, + const double *constraint_upper_bound, + const double *variable_lower_bound, + const double *variable_upper_bound, + const double *dual_solution, + double *dual_residual, + const double *dual_product, + double *dual_slack, + const double *objective_vector, + const double *constraint_rescaling, + const double *variable_rescaling, + double *affine_dual_membership, + double *dual_obj_contribution, + const double *const_lb_finite, + const double *const_ub_finite, + const double step_size, + bool defer_constraint_projection, + int num_constraints, + int num_variables); + + __global__ void recover_primal_obj_dual_product(double *dual_product, + double *primal_obj_product, + const double *variable_rescaling, + int num_variables); + + __global__ void primal_infeasibility_project_kernel(double *primal_ray_estimate, + const double *variable_lower_bound, + const double *variable_upper_bound, + int num_variables); + + __global__ void dual_infeasibility_project_kernel(double *dual_ray_estimate, + const double *constraint_lower_bound, + const double *constraint_upper_bound, + int num_constraints); + + __global__ void compute_primal_infeasibility_kernel(const double *primal_product, + const double *const_lb, + const double *const_ub, + int num_constraints, + double *primal_infeasibility, + const double *constraint_rescaling); + + __global__ void compute_dual_infeasibility_kernel(const double *dual_product, + const double *var_lb, + const double *var_ub, + int num_variables, + double *dual_infeasibility, + const double *variable_rescaling); + + __global__ void + dual_solution_dual_objective_contribution_kernel(const double *constraint_lower_bound_finite_val, + const double *constraint_upper_bound_finite_val, + const double *affine_cone_offset, + const double *dual_solution, + int num_constraints, + double *dual_objective_dual_solution_contribution_array); + + __global__ void + dual_objective_dual_slack_contribution_array_kernel(const double *dual_slack, + double *dual_objective_dual_slack_contribution_array, + const double *variable_lower_bound_finite_val, + const double *variable_upper_bound_finite_val, + int num_variables); + + __global__ void compute_and_rescale_reduced_cost_qp_kernel(double *__restrict__ reduced_cost, + const double *__restrict__ objective, + const double *__restrict__ quadratic_product, + const double *__restrict__ dual_product, + const double *__restrict__ variable_rescaling, + const double objective_vector_rescaling, + const double constraint_bound_rescaling, + const double *__restrict__ variable_lower_bound, + const double *__restrict__ variable_upper_bound, + int n_vars); + +#ifdef __cplusplus +} +#endif + +#endif // PDHCG_KERNELS_H diff --git a/internal/pdhcg_power_cone_kernels.h b/internal/pdhcg_power_cone_kernels.h new file mode 100644 index 0000000..064b857 --- /dev/null +++ b/internal/pdhcg_power_cone_kernels.h @@ -0,0 +1,81 @@ +/* +Copyright 2025 Haihao Lu +Copyright 2026 Hongpei Li + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +#pragma once + +#include + +#ifdef __cplusplus +extern "C" +{ +#endif + __global__ void project_power_cone_kernel(double *__restrict__ primal_solution, + const double *__restrict__ variable_rescaling, + double *__restrict__ warm_start, + const int *__restrict__ start_idx, + const int *__restrict__ v_dim, + const double *__restrict__ power_alpha, + const char *__restrict__ is_fixed, + int num_blocks); + + __global__ void compute_cone_dual_residual_power_kernel(double *__restrict__ dual_residual, + double *__restrict__ complementarity_residual, + const double *__restrict__ objective_vector, + const double *__restrict__ dual_product, + const double *__restrict__ variable_rescaling, + const double *__restrict__ primal_solution, + double *__restrict__ warm_start, + const int *__restrict__ start_idx, + const int *__restrict__ v_dim, + const double *__restrict__ power_alpha, + const char *__restrict__ is_fixed, + int num_blocks); + + __global__ void compute_power_cone_primal_violation_kernel(double *__restrict__ absolute_violation, + double *__restrict__ relative_violation, + const double *__restrict__ primal_solution, + const double *__restrict__ variable_rescaling, + const int *__restrict__ start_idx, + const double *__restrict__ power_alpha, + double homogeneous_scale, + int num_blocks); + + __global__ void project_power_cone_diag_q_kernel(double *__restrict__ pdhg_primal, + double *__restrict__ reflected_primal, + const double *__restrict__ current_primal, + const double *__restrict__ variable_rescaling, + const double *__restrict__ Q_diag, + double tau, + double *__restrict__ warm_start, + const int *__restrict__ start_idx, + const int *__restrict__ v_dim, + const double *__restrict__ power_alpha, + const char *__restrict__ is_fixed, + int num_blocks); + + void launch_power_cone_primal_violation(double *absolute_violation, + double *relative_violation, + const double *primal_solution, + const double *variable_rescaling, + const int *start_idx, + const double *power_alpha, + double homogeneous_scale, + int count); + +#ifdef __cplusplus +} +#endif diff --git a/internal/pdhcg_psd_cone.h b/internal/pdhcg_psd_cone.h new file mode 100644 index 0000000..1fec526 --- /dev/null +++ b/internal/pdhcg_psd_cone.h @@ -0,0 +1,54 @@ +/* +Copyright 2026 Hongpei Li + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +#pragma once + +#include "internal_types.h" + +#ifdef __cplusplus +extern "C" +{ +#endif + psd_projection_runtime_t *create_psd_projection_runtime(const int *start_idx, + const int *matrix_order, + int num_blocks, + int complementarity_offset); + void free_psd_projection_runtime(psd_projection_runtime_t *runtime); + + void project_psd_cones(psd_projection_runtime_t *runtime, double *vector); + void compute_psd_cone_dual_residual(psd_projection_runtime_t *runtime, + double *dual_residual, + const double *objective_vector, + const double *dual_product, + const double *variable_rescaling); + void recompute_psd_cone_reflection(psd_projection_runtime_t *runtime, + double *reflected_primal, + const double *pdhg_primal, + const double *current_primal); + void set_psd_cone_dual_slack(psd_projection_runtime_t *runtime, + double *dual_slack, + const double *objective_vector, + const double *dual_product); + void prepare_psd_affine_cone_residuals(psd_projection_runtime_t *runtime, + double *projection_point, + double *complementarity_residual, + const double *primal_product, + const double *affine_cone_offset, + const double *dual_solution, + double constraint_bound_rescaling); +#ifdef __cplusplus +} +#endif diff --git a/internal/pdhcg_rsoc_cone_kernels.h b/internal/pdhcg_rsoc_cone_kernels.h new file mode 100644 index 0000000..9a608be --- /dev/null +++ b/internal/pdhcg_rsoc_cone_kernels.h @@ -0,0 +1,211 @@ +/* +Copyright 2025 Haihao Lu +Copyright 2026 Hongpei Li + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +#pragma once + +#include + +#ifdef __cplusplus +extern "C" +{ +#endif + __global__ void project_rotated_soc_kernel(double *__restrict__ primal_solution, + const double *__restrict__ variable_rescaling, + double *__restrict__ warm_start, + const int *__restrict__ start_idx, + const int *__restrict__ v_dim, + const char *__restrict__ is_fixed, + int num_blocks); + + __global__ void project_rotated_soc_warp_kernel(double *__restrict__ primal_solution, + const double *__restrict__ variable_rescaling, + double *__restrict__ warm_start, + const int *__restrict__ start_idx, + const int *__restrict__ v_dim, + const char *__restrict__ is_fixed, + int num_blocks); + + __global__ void project_rotated_soc_block_kernel(double *__restrict__ primal_solution, + const double *__restrict__ variable_rescaling, + const double *__restrict__ Q_diag, + double tau, + double *__restrict__ warm_start, + const int *__restrict__ start_idx, + const int *__restrict__ v_dim, + const char *__restrict__ is_fixed, + int num_blocks); + + __global__ void initialize_rotated_soc_grid_weighted_kernel(const double *__restrict__ primal_solution, + const double *__restrict__ variable_rescaling, + const double *__restrict__ Q_diag, + double tau, + double *__restrict__ workspace, + const int *__restrict__ start_idx, + const int *__restrict__ v_dim, + const char *__restrict__ is_fixed, + int num_cones, + int blocks_per_cone); + + __global__ void + finalize_rotated_soc_grid_weighted_initialization_kernel(const double *__restrict__ primal_solution, + const double *__restrict__ variable_rescaling, + const double *__restrict__ Q_diag, + double tau, + double *__restrict__ workspace, + const int *__restrict__ start_idx, + const int *__restrict__ v_dim, + const char *__restrict__ is_fixed, + int num_cones); + + __global__ void reduce_rotated_soc_grid_weighted_root_kernel(const double *__restrict__ primal_solution, + const double *__restrict__ variable_rescaling, + const double *__restrict__ Q_diag, + double tau, + double *__restrict__ workspace, + const int *__restrict__ start_idx, + const int *__restrict__ v_dim, + const char *__restrict__ is_fixed, + int num_cones, + int blocks_per_cone); + + __global__ void finalize_rotated_soc_grid_weighted_root_kernel(const double *__restrict__ primal_solution, + const double *__restrict__ variable_rescaling, + const double *__restrict__ Q_diag, + double tau, + double *__restrict__ workspace, + const int *__restrict__ start_idx, + const int *__restrict__ v_dim, + const char *__restrict__ is_fixed, + int num_cones); + + __global__ void reduce_rotated_soc_grid_axis_objective_kernel(const double *__restrict__ primal_solution, + const double *__restrict__ variable_rescaling, + const double *__restrict__ Q_diag, + double tau, + double *__restrict__ workspace, + const int *__restrict__ start_idx, + const int *__restrict__ v_dim, + const char *__restrict__ is_fixed, + int num_cones, + int blocks_per_cone); + + __global__ void finalize_rotated_soc_grid_axis_objective_kernel(const double *__restrict__ primal_solution, + const double *__restrict__ variable_rescaling, + const double *__restrict__ Q_diag, + double tau, + double *__restrict__ workspace, + const int *__restrict__ start_idx, + const int *__restrict__ v_dim, + int num_cones); + + __global__ void apply_rotated_soc_grid_weighted_kernel(double *__restrict__ primal_solution, + const double *__restrict__ variable_rescaling, + const double *__restrict__ Q_diag, + double tau, + const double *__restrict__ workspace, + const int *__restrict__ start_idx, + const int *__restrict__ v_dim, + const char *__restrict__ is_fixed, + int num_cones, + int blocks_per_cone); + + __global__ void project_rotated_soc_grid_reduce_kernel(double *__restrict__ primal_solution, + double *__restrict__ workspace, + const int *__restrict__ start_idx, + const int *__restrict__ v_dim, + int num_cones, + int blocks_per_cone); + + __global__ void project_rotated_soc_grid_finalize_kernel(double *__restrict__ primal_solution, + double *__restrict__ workspace, + const int *__restrict__ start_idx, + const int *__restrict__ v_dim, + int num_cones); + + __global__ void project_rotated_soc_grid_apply_kernel(double *__restrict__ primal_solution, + const double *__restrict__ workspace, + const int *__restrict__ start_idx, + const int *__restrict__ v_dim, + int num_cones, + int blocks_per_cone); + + __global__ void compute_cone_dual_residual_kernel(double *__restrict__ dual_residual, + double *__restrict__ complementarity_residual, + const double *__restrict__ objective_vector, + const double *__restrict__ dual_product, + const double *__restrict__ variable_rescaling, + const double *__restrict__ primal_solution, + double *__restrict__ warm_start, + const int *__restrict__ start_idx, + const int *__restrict__ v_dim, + const char *__restrict__ is_fixed, + int num_blocks); + + __global__ void compute_cone_dual_residual_warp_kernel(double *__restrict__ dual_residual, + double *__restrict__ complementarity_residual, + const double *__restrict__ objective_vector, + const double *__restrict__ dual_product, + const double *__restrict__ variable_rescaling, + const double *__restrict__ primal_solution, + double *__restrict__ warm_start, + const int *__restrict__ start_idx, + const int *__restrict__ v_dim, + const char *__restrict__ is_fixed, + int num_blocks); + + __global__ void compute_cone_dual_residual_grid_reduce_kernel(const double *__restrict__ objective_vector, + const double *__restrict__ dual_product, + double *__restrict__ workspace, + const int *__restrict__ start_idx, + const int *__restrict__ v_dim, + int num_cones, + int blocks_per_cone); + + __global__ void compute_cone_dual_residual_grid_finalize_kernel(double *__restrict__ dual_residual, + const double *__restrict__ objective_vector, + const double *__restrict__ dual_product, + const double *__restrict__ variable_rescaling, + double *__restrict__ workspace, + const int *__restrict__ start_idx, + const int *__restrict__ v_dim, + int num_cones); + + __global__ void compute_cone_dual_residual_grid_apply_kernel(double *__restrict__ dual_residual, + const double *__restrict__ objective_vector, + const double *__restrict__ dual_product, + const double *__restrict__ variable_rescaling, + const double *__restrict__ workspace, + const int *__restrict__ start_idx, + const int *__restrict__ v_dim, + int num_cones, + int blocks_per_cone); + + __global__ void project_rotated_soc_diag_q_kernel(double *__restrict__ pdhg_primal, + double *__restrict__ reflected_primal, + const double *__restrict__ current_primal, + const double *__restrict__ variable_rescaling, + const double *__restrict__ Q_diag, + double tau, + double *__restrict__ warm_start, + const int *__restrict__ start_idx, + const int *__restrict__ v_dim, + const char *__restrict__ is_fixed, + int num_blocks); + +#ifdef __cplusplus +} +#endif diff --git a/internal/pdhcg_soc_cone_kernels.h b/internal/pdhcg_soc_cone_kernels.h new file mode 100644 index 0000000..e6367a8 --- /dev/null +++ b/internal/pdhcg_soc_cone_kernels.h @@ -0,0 +1,191 @@ +/* +Copyright 2025 Haihao Lu +Copyright 2026 Hongpei Li + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +#pragma once + +#include + +#ifdef __cplusplus +extern "C" +{ +#endif + __global__ void project_standard_soc_kernel(double *__restrict__ primal_solution, + const double *__restrict__ variable_rescaling, + double *__restrict__ warm_start, + const int *__restrict__ start_idx, + const int *__restrict__ v_dim, + const char *__restrict__ is_fixed, + int num_blocks); + + __global__ void project_standard_soc_warp_kernel(double *__restrict__ primal_solution, + const double *__restrict__ variable_rescaling, + double *__restrict__ warm_start, + const int *__restrict__ start_idx, + const int *__restrict__ v_dim, + const char *__restrict__ is_fixed, + int num_blocks); + + __global__ void project_standard_soc_block_kernel(double *__restrict__ primal_solution, + const double *__restrict__ variable_rescaling, + const double *__restrict__ Q_diag, + double tau, + double *__restrict__ warm_start, + const int *__restrict__ start_idx, + const int *__restrict__ v_dim, + const char *__restrict__ is_fixed, + int num_blocks); + + __global__ void initialize_standard_soc_grid_weighted_kernel(const double *__restrict__ primal_solution, + const double *__restrict__ variable_rescaling, + const double *__restrict__ Q_diag, + double tau, + double *__restrict__ workspace, + const int *__restrict__ start_idx, + const int *__restrict__ v_dim, + const char *__restrict__ is_fixed, + int num_cones, + int blocks_per_cone); + + __global__ void + finalize_standard_soc_grid_weighted_initialization_kernel(const double *__restrict__ primal_solution, + const double *__restrict__ variable_rescaling, + const double *__restrict__ Q_diag, + double tau, + double *__restrict__ workspace, + const int *__restrict__ start_idx, + const int *__restrict__ v_dim, + const char *__restrict__ is_fixed, + int num_cones); + + __global__ void reduce_standard_soc_grid_weighted_root_kernel(const double *__restrict__ primal_solution, + const double *__restrict__ variable_rescaling, + const double *__restrict__ Q_diag, + double tau, + double *__restrict__ workspace, + const int *__restrict__ start_idx, + const int *__restrict__ v_dim, + const char *__restrict__ is_fixed, + int num_cones, + int blocks_per_cone); + + __global__ void finalize_standard_soc_grid_weighted_root_kernel(double *__restrict__ primal_solution, + const double *__restrict__ variable_rescaling, + const double *__restrict__ Q_diag, + double tau, + double *__restrict__ workspace, + const int *__restrict__ start_idx, + const int *__restrict__ v_dim, + int num_cones); + + __global__ void apply_standard_soc_grid_weighted_kernel(double *__restrict__ primal_solution, + const double *__restrict__ variable_rescaling, + const double *__restrict__ Q_diag, + double tau, + const double *__restrict__ workspace, + const int *__restrict__ start_idx, + const int *__restrict__ v_dim, + const char *__restrict__ is_fixed, + int num_cones, + int blocks_per_cone); + + __global__ void project_standard_soc_grid_reduce_kernel(double *__restrict__ primal_solution, + double *__restrict__ workspace, + const int *__restrict__ start_idx, + const int *__restrict__ v_dim, + int num_cones, + int blocks_per_cone); + + __global__ void project_standard_soc_grid_finalize_kernel(double *__restrict__ primal_solution, + double *__restrict__ workspace, + const int *__restrict__ start_idx, + const int *__restrict__ v_dim, + int num_cones); + + __global__ void project_standard_soc_grid_apply_kernel(double *__restrict__ primal_solution, + const double *__restrict__ workspace, + const int *__restrict__ start_idx, + const int *__restrict__ v_dim, + int num_cones, + int blocks_per_cone); + + __global__ void compute_cone_dual_residual_standard_warp_kernel(double *__restrict__ dual_residual, + double *__restrict__ complementarity_residual, + const double *__restrict__ objective_vector, + const double *__restrict__ dual_product, + const double *__restrict__ variable_rescaling, + const double *__restrict__ primal_solution, + double *__restrict__ warm_start, + const int *__restrict__ start_idx, + const int *__restrict__ v_dim, + const char *__restrict__ is_fixed, + int num_blocks); + + __global__ void compute_cone_dual_residual_standard_grid_reduce_kernel(const double *__restrict__ objective_vector, + const double *__restrict__ dual_product, + double *__restrict__ workspace, + const int *__restrict__ start_idx, + const int *__restrict__ v_dim, + int num_cones, + int blocks_per_cone); + + __global__ void + compute_cone_dual_residual_standard_grid_finalize_kernel(double *__restrict__ dual_residual, + const double *__restrict__ objective_vector, + const double *__restrict__ dual_product, + const double *__restrict__ variable_rescaling, + double *__restrict__ workspace, + const int *__restrict__ start_idx, + const int *__restrict__ v_dim, + int num_cones); + + __global__ void compute_cone_dual_residual_standard_grid_apply_kernel(double *__restrict__ dual_residual, + const double *__restrict__ objective_vector, + const double *__restrict__ dual_product, + const double *__restrict__ variable_rescaling, + const double *__restrict__ workspace, + const int *__restrict__ start_idx, + const int *__restrict__ v_dim, + int num_cones, + int blocks_per_cone); + + __global__ void compute_cone_dual_residual_standard_kernel(double *__restrict__ dual_residual, + double *__restrict__ complementarity_residual, + const double *__restrict__ objective_vector, + const double *__restrict__ dual_product, + const double *__restrict__ variable_rescaling, + const double *__restrict__ primal_solution, + double *__restrict__ warm_start, + const int *__restrict__ start_idx, + const int *__restrict__ v_dim, + const char *__restrict__ is_fixed, + int num_blocks); + + __global__ void project_standard_soc_diag_q_kernel(double *__restrict__ pdhg_primal, + double *__restrict__ reflected_primal, + const double *__restrict__ current_primal, + const double *__restrict__ variable_rescaling, + const double *__restrict__ Q_diag, + double tau, + double *__restrict__ warm_start, + const int *__restrict__ start_idx, + const int *__restrict__ v_dim, + const char *__restrict__ is_fixed, + int num_blocks); + +#ifdef __cplusplus +} +#endif diff --git a/pyproject.toml b/pyproject.toml index e7d9c98..c4e6eb4 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -26,7 +26,6 @@ wheel.packages = ["python/pdhcg"] sdist.include = ["tests/**", "CHANGELOG.md", "pyproject.toml", "README.md", "LICENSE"] [tool.scikit-build.cmake.define] -CMAKE_CUDA_ARCHITECTURES = "all" CMAKE_CUDA_STANDARD = "17" PDHCG_BUILD_PYTHON = "ON" PDHCG_BUILD_STATIC_LIB = "ON" diff --git a/python/README.md b/python/README.md index 8f18967..409f57d 100644 --- a/python/README.md +++ b/python/README.md @@ -115,8 +115,8 @@ print("Dual solution:", m.Pi) ## Cone Constraints -Second-order, rotated second-order, exponential, and power cone constraints use -the columnar `ConeSpec` API: +Second-order, rotated second-order, exponential, power, and positive-semidefinite +cone constraints use the columnar `ConeSpec` API: ```python import numpy as np @@ -150,8 +150,8 @@ problem = cp.Problem(cp.Minimize(x), [x >= 1]) problem.solve(solver="PDHCG", eps=1e-6) ``` -Quadratic objectives and Zero, NonNeg, SOC, ExpCone, and PowCone3D -constraints are supported. PSD and mixed-integer models are not supported. +Quadratic objectives and Zero, NonNeg, SOC, PSD, ExpCone, and PowCone3D +constraints are supported. Mixed-integer models are not supported. ## Modeling @@ -238,7 +238,7 @@ Below is a list of commonly used parameters, their internal keys, and descriptio | `UsePCAlpha` | `has_pock_chambolle_alpha` | bool | `True` | Whether to use the Pock–Chambolle α step size adjustment. | | `PCAlpha` | `pock_chambolle_alpha` | float | `1.0` | Value of the Pock–Chambolle α parameter. | | `BoundObjRescaling` | `bound_objective_rescaling` | bool | `True` | Whether to rescale the objective vector during preprocessing. | -| `UseConePreservingScaling` | `use_cone_preserving_scaling` | bool | `True` | Whether to broadcast one scaling value over every cone block. | +| `UseConePreservingScaling` | `use_cone_preserving_scaling` | bool | `True` | Whether to broadcast one scale over each cone block. | | `RestartArtificialThresh` | `artificial_restart_threshold` | float | `0.36` | Threshold for artificial restart. | | `RestartSufficientReduction` | `sufficient_reduction_for_restart` | float | `0.2` | Sufficient reduction factor to justify a restart. | | `RestartNecessaryReduction` | `necessary_reduction_for_restart` | float | `0.8` | Necessary reduction factor required for a restart. | diff --git a/python/pdhcg/cones.py b/python/pdhcg/cones.py index a0d6a76..42e583f 100644 --- a/python/pdhcg/cones.py +++ b/python/pdhcg/cones.py @@ -28,6 +28,7 @@ class ConeType(IntEnum): SOC = 1 EXP = 2 POWER = 3 + PSD = 4 ROTATED_SOC = RSOC STANDARD_SOC = SOC @@ -39,6 +40,7 @@ class ConeType(IntEnum): "soc": ConeType.SOC, "exp": ConeType.EXP, "power": ConeType.POWER, + "psd": ConeType.PSD, } @@ -47,11 +49,11 @@ def _cone_type_code(value: Any) -> int: try: return int(_CONE_TYPE_NAMES[value.lower()]) except KeyError as exc: - raise ValueError("cone type must be 'soc', 'rsoc', 'exp', or 'power'") from exc + raise ValueError("cone type must be 'soc', 'rsoc', 'exp', 'power', or 'psd'") from exc if not isinstance(value, Integral): raise TypeError("cone type must be a ConeType, integer code, or string") code = int(value) - if code < int(ConeType.RSOC) or code > int(ConeType.POWER): + if code < int(ConeType.RSOC) or code > int(ConeType.PSD): raise ValueError(f"invalid cone type code {code}") return code @@ -97,7 +99,7 @@ def _as_type_vector(value: Any, count: int) -> np.ndarray: if array.ndim != 1 or array.size != count: raise ValueError(f"types must be a scalar or a 1D array of length {count}") if array.dtype.kind in "iu": - if array.size and (array.min() < int(ConeType.RSOC) or array.max() > int(ConeType.POWER)): + if array.size and (array.min() < int(ConeType.RSOC) or array.max() > int(ConeType.PSD)): raise ValueError("types contains an invalid cone type code") return np.ascontiguousarray(array, dtype=np.int32) return np.fromiter((_cone_type_code(item) for item in array), dtype=np.int32, count=count) @@ -109,7 +111,8 @@ class ConeSpec: ``types``, ``v_dims``, and ``power_alphas`` may be scalars and are then broadcast to all entries in ``starts``. For variable cones, ``starts`` are variable indices. For affine cones, they are rows of the separately supplied - affine map ``F``. + affine map ``F``. For PSD cones, ``v_dims`` stores the matrix order and each + block occupies lower-triangular column-major ``svec`` coordinates. """ __slots__ = ("types", "starts", "v_dims", "power_alphas", "fixed_mask") @@ -169,11 +172,12 @@ def validate_ambient( ambient_dimension = int(ambient_dimension) if ambient_dimension < 0: raise ValueError("ambient_dimension must be nonnegative") - lengths = np.where( - (self.types == int(ConeType.EXP)) | (self.types == int(ConeType.POWER)), - 3, - self.v_dims.astype(np.int64) + 2, - ) + v_dims_i64 = self.v_dims.astype(np.int64) + lengths = v_dims_i64 + 2 + three_dimensional = (self.types == int(ConeType.EXP)) | (self.types == int(ConeType.POWER)) + lengths = np.where(three_dimensional, 3, lengths) + psd = self.types == int(ConeType.PSD) + lengths = np.where(psd, v_dims_i64 * (v_dims_i64 + 1) // 2, lengths) ends = self.starts.astype(np.int64) + lengths if np.any(ends > ambient_dimension): raise ValueError("a cone block extends beyond the ambient dimension") @@ -187,6 +191,9 @@ def validate_ambient( "fixed_mask length " f"{self.fixed_mask.size} != ambient dimension {ambient_dimension}" ) + for start, length, is_psd in zip(self.starts, lengths, psd): + if is_psd and np.any(self.fixed_mask[int(start) : int(start + length)]): + raise ValueError("PSD cones do not support fixed slots") @classmethod def from_columnar(cls, payload: Mapping[str, Any]) -> "ConeSpec": diff --git a/python/pdhcg/cvxpy_backend.py b/python/pdhcg/cvxpy_backend.py index dbfdd20..b58491c 100644 --- a/python/pdhcg/cvxpy_backend.py +++ b/python/pdhcg/cvxpy_backend.py @@ -9,8 +9,8 @@ register ``PDHCG`` under ``cvxpy.settings.SOLVER_MAP_CONIC`` so that ``problem.solve(solver='PDHCG')`` works. -Supported CVXPY constraints: Zero, NonNeg, SOC, ExpCone, PowCone3D. -Not supported: PSD, integer variables. +Supported CVXPY constraints: Zero, NonNeg, SOC, PSD, ExpCone, PowCone3D. +Not supported: integer variables. """ from __future__ import annotations @@ -21,7 +21,7 @@ import cvxpy.settings as _cvx_s import numpy as np import scipy.sparse as sp -from cvxpy.constraints import SOC, ExpCone, NonNeg, PowCone3D, Zero +from cvxpy.constraints import PSD, SOC, ExpCone, NonNeg, PowCone3D, Zero from cvxpy.reductions.solution import Solution, failure_solution from cvxpy.reductions.solvers import utilities from cvxpy.reductions.solvers.conic_solvers.conic_solver import ConicSolver @@ -45,7 +45,7 @@ class PDHCG(ConicSolver): """PDHCG conic-solver plugin for CVXPY.""" MIP_CAPABLE = False - SUPPORTED_CONSTRAINTS = [Zero, NonNeg, SOC, ExpCone, PowCone3D] + SUPPORTED_CONSTRAINTS = [Zero, NonNeg, SOC, PSD, ExpCone, PowCone3D] # CVXPY's ExpCone convention is (x, y, z) with z >= y * exp(x/y), y > 0. # PDHCG's internal exp cone convention is (r1, r2, r3) with r3 >= r2 * exp(r1/r2). @@ -61,6 +61,55 @@ def import_solver(self) -> None: def supports_quad_obj(self) -> bool: return True + @staticmethod + def psd_format_mat(constr): + """Map a symmetric matrix to lower-triangular column-major svec.""" + order = constr.expr.shape[0] + packed_length = order * (order + 1) // 2 + + lower = np.tril_indices(order) + columns = np.sort(np.ravel_multi_index(lower, (order, order), order="F")) + values = np.zeros((order, order), dtype=np.float64) + values[lower] = np.sqrt(2.0) + np.fill_diagonal(values, 1.0) + values = values.ravel(order="F") + values = values[values != 0.0] + packed = sp.csc_array( + (values, (np.arange(packed_length), columns)), + shape=(packed_length, order * order), + ) + + indices = np.arange(order * order) + matrix_indices = indices.reshape((order, order)) + symmetrize = sp.csc_array( + ( + np.full(2 * order * order, 0.5), + ( + np.concatenate((indices, matrix_indices.ravel(order="F"))), + np.concatenate((indices, matrix_indices.T.ravel(order="F"))), + ), + ), + shape=(order * order, order * order), + ) + return packed @ symmetrize + + @staticmethod + def extract_dual_value(result_vec, offset, constraint): + """Expand a PSD svec dual before CVXPY restores constraint shapes.""" + if not isinstance(constraint, PSD): + return utilities.extract_dual_value(result_vec, offset, constraint) + + order = constraint.shape[0] + packed_length = order * (order + 1) // 2 + new_offset = offset + packed_length + full = np.zeros((order, order), dtype=np.float64) + full[np.triu_indices(order)] = result_vec[offset:new_offset] + full += full.T + full[np.diag_indices(order)] *= 0.5 + full[np.tril_indices(order, k=-1)] /= np.sqrt(2.0) + full[np.triu_indices(order, k=1)] /= np.sqrt(2.0) + return full.ravel(order="F"), new_offset + def cite(self, data): return ( "@misc{pdhcg,\n" @@ -82,12 +131,12 @@ def invert(self, solution, inverse_data): primal_vars = {inverse_data[self.VAR_ID]: solution["primal"]} eq_dual = utilities.get_dual_values( solution["eq_dual"], - utilities.extract_dual_value, + self.extract_dual_value, inverse_data[self.EQ_CONSTR], ) ineq_dual = utilities.get_dual_values( solution["ineq_dual"], - utilities.extract_dual_value, + self.extract_dual_value, inverse_data[self.NEQ_CONSTR], ) dual_vars = {**eq_dual, **ineq_dual} @@ -106,16 +155,16 @@ def solve_via_data( n_zero = int(cone_dims.zero) n_nonneg = int(cone_dims.nonneg) soc_dims = list(cone_dims.soc) + psd_dims = list(cone_dims.psd) n_exp = int(cone_dims.exp) pow_alphas = list(cone_dims.p3d) - if cone_dims.psd: - raise ValueError("PDHCG does not support PSD constraints.") n = c.size soc_total = sum(soc_dims) + psd_total = sum(order * (order + 1) // 2 for order in psd_dims) exp_total = 3 * n_exp pow_total = 3 * len(pow_alphas) - n_cone_rows = soc_total + exp_total + pow_total + n_cone_rows = soc_total + psd_total + exp_total + pow_total n_total_rows = n_zero + n_nonneg + n_cone_rows assert A_cvx.shape == (n_total_rows, n), ( @@ -123,10 +172,12 @@ def solve_via_data( ) # Internal slack layout: one SOC needs (v_dim + 2) slots = (k - 1) + 2 = k + 1 - # (extra "phantom" w-slot pinned to 0). EXP and POWER need 3 slots each. + # (extra "phantom" w-slot pinned to 0). PSD uses lower-triangular svec; + # EXP and POWER need 3 slots each. n_soc_blocks = len(soc_dims) + n_psd_blocks = len(psd_dims) n_pow_blocks = len(pow_alphas) - n_slack = soc_total + n_soc_blocks + 3 * n_exp + 3 * n_pow_blocks + n_slack = soc_total + n_soc_blocks + psd_total + 3 * n_exp + 3 * n_pow_blocks n_vars_total = n + n_slack if n_vars_total > np.iinfo(np.int32).max or n_total_rows > np.iinfo(np.int32).max: raise ValueError("PDHCG dimensions must fit signed 32-bit indices.") @@ -135,7 +186,7 @@ def solve_via_data( # column map directly; row indices are simply arange(n_cone_rows). S_cols = np.empty(n_cone_rows, dtype=np.int64) - n_cones = n_soc_blocks + n_exp + n_pow_blocks + n_cones = n_soc_blocks + n_psd_blocks + n_exp + n_pow_blocks cone_types = np.empty(n_cones, dtype=np.int32) cone_starts = np.empty(n_cones, dtype=np.int32) cone_v_dims = np.ones(n_cones, dtype=np.int32) @@ -161,6 +212,21 @@ def solve_via_data( slack_off += k + 1 cvx_row_off += k + # --- PSD blocks --- + # CVXPY uses lower-triangular column-major svec with sqrt(2)-scaled + # off-diagonal entries, which is PDHCG's native PSD representation. + for order in psd_dims: + packed_length = order * (order + 1) // 2 + S_cols[cvx_row_off : cvx_row_off + packed_length] = np.arange( + slack_off, slack_off + packed_length, dtype=np.int64 + ) + cone_types[cone_idx] = int(ConeType.PSD) + cone_starts[cone_idx] = n + slack_off + cone_v_dims[cone_idx] = order + cone_idx += 1 + slack_off += packed_length + cvx_row_off += packed_length + # --- EXP blocks --- if n_exp: cone_slice = slice(cone_idx, cone_idx + n_exp) @@ -290,7 +356,7 @@ def solve_via_data( primal = x_full[:n] if x_full is not None else None # PDHCG's row multiplier convention is the negative of CVXPY's canonical # A*x + s = b convention. Convert once before splitting Zero and inequality - # cone duals so equality, NonNeg, SOC, Exp, and Power duals agree with CVXPY. + # cone duals so equality, NonNeg, SOC, PSD, Exp, and Power duals agree with CVXPY. cvxpy_dual = -y_full if y_full is not None else None eq_dual = cvxpy_dual[:n_zero] if cvxpy_dual is not None else None ineq_dual = cvxpy_dual[n_zero:] if cvxpy_dual is not None else None diff --git a/python_bindings/_core_bindings.cpp b/python_bindings/_core_bindings.cpp index 818875b..951a3c9 100644 --- a/python_bindings/_core_bindings.cpp +++ b/python_bindings/_core_bindings.cpp @@ -596,7 +596,7 @@ static ParsedConeSpecs parse_columnar_cone_specs(py::object cones, bool affine, for (py::ssize_t cone = 0; cone < count; ++cone) { int type_code = type_data[cone]; - if (type_code < CONE_ROTATED_SOC || type_code > CONE_POWER) + if (type_code < CONE_ROTATED_SOC || type_code >= NUM_CONE_TYPES) throw std::invalid_argument(std::string(kind) + " has an invalid type code"); cone_spec_t &spec = out.specs[(size_t)cone]; spec.type = static_cast(type_code); diff --git a/src/cli.c b/src/cli.c index 85d2c13..50da5fc 100644 --- a/src/cli.c +++ b/src/cli.c @@ -177,7 +177,7 @@ void print_usage(const char *prog_name) fprintf(stderr, " --no_pock_chambolle Disable Pock-Chambolle rescaling (default: enabled).\n"); fprintf(stderr, " --pock_chambolle_alpha Value for Pock-Chambolle alpha (default: 1.0).\n"); fprintf(stderr, " --no_bound_obj_rescaling Disable bound objective rescaling.\n"); - fprintf(stderr, " --no_cone_preserving_scaling Keep coordinate-wise cone scaling.\n"); + fprintf(stderr, " --no_cone_preserving_scaling Disable cone-preserving scaling.\n"); fprintf(stderr, " --eval_freq Termination evaluation frequency (default: 200).\n"); fprintf(stderr, " --artificial_restart_threshold Artificial restart threshold (default: 0.36).\n"); fprintf(stderr, diff --git a/src/cone_dispatch.cu b/src/cone_dispatch.cu index 5cdb8a3..eb31f30 100644 --- a/src/cone_dispatch.cu +++ b/src/cone_dispatch.cu @@ -15,667 +15,24 @@ limitations under the License. */ #include "cone_dispatch.h" +#include "cone_kernel_ops.h" #include "distributed_conic.h" -#include "pdhcg_kernels.cuh" +#include "pdhcg_psd_cone.h" #include "utils.h" -#include -typedef void (*cone_proj_launcher_t)(double *primal, - const double *var_rescale, - double *warm_start, - const int *start_idx, - const int *v_dim, - const double *power_alpha, - const char *is_fixed, - int count); - -typedef void (*cone_dual_res_launcher_t)(double *dual_residual, - double *complementarity_residual, - const double *objective_vector, - const double *dual_product, - const double *var_rescale, - const double *primal_solution, - double *warm_start, - const int *start_idx, - const int *v_dim, - const double *power_alpha, - const char *is_fixed, - int count); - -static void launch_rotated_thread_proj( - double *p, const double *vr, double *ws, const int *si, const int *vd, const double *pa, const char *isf, int n) -{ - (void)pa; - int t = THREADS_PER_BLOCK; - int b = (n + t - 1) / t; - project_rotated_soc_kernel<<>>(p, vr, ws, si, vd, isf, n); -} -static void launch_rotated_warp_proj( - double *p, const double *vr, double *ws, const int *si, const int *vd, const double *pa, const char *isf, int n) -{ - (void)pa; - int t = THREADS_PER_BLOCK; - int b = (n * 32 + t - 1) / t; - project_rotated_soc_warp_kernel<<>>(p, vr, ws, si, vd, isf, n); -} -static void launch_rotated_block_proj( - double *p, const double *vr, double *ws, const int *si, const int *vd, const double *pa, const char *isf, int n) -{ - (void)pa; - project_rotated_soc_block_kernel<<>>(p, vr, NULL, 0.0, ws, si, vd, isf, n); -} -static void launch_rotated_grid_weighted_impl(double *p, - const double *vr, - const double *qd, - double tau, - double *ws, - const int *si, - const int *vd, - const char *isf, - int n) -{ - int threads = THREADS_PER_BLOCK; - int blocks_per_cone = PDHCG_LARGE_CONE_BLOCKS_PER_CONE; - int blocks = n * blocks_per_cone; - CUDA_CHECK(cudaMemsetAsync(ws + n, 0, (size_t)5 * n * sizeof(double))); - initialize_rotated_soc_grid_weighted_kernel<<>>( - p, vr, qd, tau, ws, si, vd, isf, n, blocks_per_cone); - finalize_rotated_soc_grid_weighted_initialization_kernel<<<(n + threads - 1) / threads, threads>>>( - p, vr, qd, tau, ws, si, vd, isf, n); - for (int iteration = 0; iteration < PDHCG_CONE_GRID_ROOT_ITERATIONS; ++iteration) - { - CUDA_CHECK(cudaMemsetAsync(ws + n, 0, (size_t)2 * n * sizeof(double))); - reduce_rotated_soc_grid_weighted_root_kernel<<>>( - p, vr, qd, tau, ws, si, vd, isf, n, blocks_per_cone); - finalize_rotated_soc_grid_weighted_root_kernel<<<(n + threads - 1) / threads, threads>>>( - p, vr, qd, tau, ws, si, vd, isf, n); - } - CUDA_CHECK(cudaMemsetAsync(ws + n, 0, (size_t)2 * n * sizeof(double))); - reduce_rotated_soc_grid_axis_objective_kernel<<>>( - p, vr, qd, tau, ws, si, vd, isf, n, blocks_per_cone); - finalize_rotated_soc_grid_axis_objective_kernel<<<(n + threads - 1) / threads, threads>>>( - p, vr, qd, tau, ws, si, vd, n); - apply_rotated_soc_grid_weighted_kernel<<>>(p, vr, qd, tau, ws, si, vd, isf, n, blocks_per_cone); -} -static void launch_rotated_grid_weighted_proj( - double *p, const double *vr, double *ws, const int *si, const int *vd, const double *pa, const char *isf, int n) -{ - (void)pa; - launch_rotated_grid_weighted_impl(p, vr, NULL, 0.0, ws, si, vd, isf, n); -} -static void launch_rotated_grid_proj( - double *p, const double *vr, double *ws, const int *si, const int *vd, const double *pa, const char *isf, int n) -{ - (void)vr; - (void)pa; - (void)isf; - int t = THREADS_PER_BLOCK; - int blocks_per_cone = PDHCG_LARGE_CONE_BLOCKS_PER_CONE; - int b = n * blocks_per_cone; - CUDA_CHECK(cudaMemsetAsync(ws, 0, (size_t)n * sizeof(double))); - project_rotated_soc_grid_reduce_kernel<<>>(p, ws, si, vd, n, blocks_per_cone); - project_rotated_soc_grid_finalize_kernel<<<(n + t - 1) / t, t>>>(p, ws, si, vd, n); - project_rotated_soc_grid_apply_kernel<<>>(p, ws, si, vd, n, blocks_per_cone); -} -static void launch_standard_thread_proj( - double *p, const double *vr, double *ws, const int *si, const int *vd, const double *pa, const char *isf, int n) -{ - (void)pa; - int t = THREADS_PER_BLOCK; - int b = (n + t - 1) / t; - project_standard_soc_kernel<<>>(p, vr, ws, si, vd, isf, n); -} -static void launch_standard_warp_proj( - double *p, const double *vr, double *ws, const int *si, const int *vd, const double *pa, const char *isf, int n) -{ - (void)pa; - int t = THREADS_PER_BLOCK; - int b = (n * 32 + t - 1) / t; - project_standard_soc_warp_kernel<<>>(p, vr, ws, si, vd, isf, n); -} -static void launch_standard_block_proj( - double *p, const double *vr, double *ws, const int *si, const int *vd, const double *pa, const char *isf, int n) -{ - (void)pa; - project_standard_soc_block_kernel<<>>(p, vr, NULL, 0.0, ws, si, vd, isf, n); -} -static void launch_standard_grid_weighted_impl(double *p, - const double *vr, - const double *qd, - double tau, - double *ws, - const int *si, - const int *vd, - const char *isf, - int n) -{ - int threads = THREADS_PER_BLOCK; - int blocks_per_cone = PDHCG_LARGE_CONE_BLOCKS_PER_CONE; - int blocks = n * blocks_per_cone; - CUDA_CHECK(cudaMemsetAsync(ws + n, 0, (size_t)5 * n * sizeof(double))); - initialize_standard_soc_grid_weighted_kernel<<>>( - p, vr, qd, tau, ws, si, vd, isf, n, blocks_per_cone); - finalize_standard_soc_grid_weighted_initialization_kernel<<<(n + threads - 1) / threads, threads>>>( - p, vr, qd, tau, ws, si, vd, isf, n); - for (int iteration = 0; iteration < PDHCG_CONE_GRID_ROOT_ITERATIONS; ++iteration) - { - CUDA_CHECK(cudaMemsetAsync(ws + n, 0, (size_t)2 * n * sizeof(double))); - reduce_standard_soc_grid_weighted_root_kernel<<>>( - p, vr, qd, tau, ws, si, vd, isf, n, blocks_per_cone); - finalize_standard_soc_grid_weighted_root_kernel<<<(n + threads - 1) / threads, threads>>>( - p, vr, qd, tau, ws, si, vd, n); - } - apply_standard_soc_grid_weighted_kernel<<>>(p, vr, qd, tau, ws, si, vd, isf, n, blocks_per_cone); -} -static void launch_standard_grid_weighted_proj( - double *p, const double *vr, double *ws, const int *si, const int *vd, const double *pa, const char *isf, int n) -{ - (void)pa; - launch_standard_grid_weighted_impl(p, vr, NULL, 0.0, ws, si, vd, isf, n); -} -static void launch_standard_grid_proj( - double *p, const double *vr, double *ws, const int *si, const int *vd, const double *pa, const char *isf, int n) -{ - (void)vr; - (void)pa; - (void)isf; - int t = THREADS_PER_BLOCK; - int blocks_per_cone = PDHCG_LARGE_CONE_BLOCKS_PER_CONE; - int b = n * blocks_per_cone; - CUDA_CHECK(cudaMemsetAsync(ws, 0, (size_t)n * sizeof(double))); - project_standard_soc_grid_reduce_kernel<<>>(p, ws, si, vd, n, blocks_per_cone); - project_standard_soc_grid_finalize_kernel<<<(n + t - 1) / t, t>>>(p, ws, si, vd, n); - project_standard_soc_grid_apply_kernel<<>>(p, ws, si, vd, n, blocks_per_cone); -} -static void launch_exp_thread_proj( - double *p, const double *vr, double *ws, const int *si, const int *vd, const double *pa, const char *isf, int n) -{ - (void)pa; - int t = THREADS_PER_BLOCK; - int b = (n + t - 1) / t; - project_exp_cone_kernel<<>>(p, vr, ws, si, vd, isf, n); -} -static void launch_power_thread_proj( - double *p, const double *vr, double *ws, const int *si, const int *vd, const double *pa, const char *isf, int n) -{ - int t = THREADS_PER_BLOCK; - int b = (n + t - 1) / t; - project_power_cone_kernel<<>>(p, vr, ws, si, vd, pa, isf, n); -} - -static const cone_proj_launcher_t proj_launch_table[NUM_CONE_TYPES][NUM_PROJ_METHODS] = { - [CONE_ROTATED_SOC] = - { - [PROJ_METHOD_THREAD] = launch_rotated_thread_proj, - [PROJ_METHOD_WARP] = launch_rotated_warp_proj, - [PROJ_METHOD_BLOCK] = launch_rotated_block_proj, - [PROJ_METHOD_GRID] = launch_rotated_grid_proj, - [PROJ_METHOD_GRID_WEIGHTED] = launch_rotated_grid_weighted_proj, - }, - [CONE_STANDARD_SOC] = - { - [PROJ_METHOD_THREAD] = launch_standard_thread_proj, - [PROJ_METHOD_WARP] = launch_standard_warp_proj, - [PROJ_METHOD_BLOCK] = launch_standard_block_proj, - [PROJ_METHOD_GRID] = launch_standard_grid_proj, - [PROJ_METHOD_GRID_WEIGHTED] = launch_standard_grid_weighted_proj, - }, - [CONE_EXPONENTIAL] = - { - [PROJ_METHOD_THREAD] = launch_exp_thread_proj, - [PROJ_METHOD_WARP] = NULL, - [PROJ_METHOD_BLOCK] = NULL, - [PROJ_METHOD_GRID] = NULL, - [PROJ_METHOD_GRID_WEIGHTED] = NULL, - }, - [CONE_POWER] = - { - [PROJ_METHOD_THREAD] = launch_power_thread_proj, - [PROJ_METHOD_WARP] = NULL, - [PROJ_METHOD_BLOCK] = NULL, - [PROJ_METHOD_GRID] = NULL, - [PROJ_METHOD_GRID_WEIGHTED] = NULL, - }, -}; - -static void launch_rotated_thread_dual(double *dr, - double *cr, - const double *obj, - const double *dp, - const double *vr, - const double *ps, - double *ws, - const int *si, - const int *vd, - const double *pa, - const char *isf, - int n) -{ - (void)pa; - int t = THREADS_PER_BLOCK; - int b = (n + t - 1) / t; - compute_cone_dual_residual_kernel<<>>(dr, cr, obj, dp, vr, ps, ws, si, vd, isf, n); -} -static void launch_rotated_warp_dual(double *dr, - double *cr, - const double *obj, - const double *dp, - const double *vr, - const double *ps, - double *ws, - const int *si, - const int *vd, - const double *pa, - const char *isf, - int n) -{ - (void)pa; - int t = THREADS_PER_BLOCK; - int b = (n * 32 + t - 1) / t; - compute_cone_dual_residual_warp_kernel<<>>(dr, cr, obj, dp, vr, ps, ws, si, vd, isf, n); -} -static void launch_rotated_grid_dual(double *dr, - double *cr, - const double *obj, - const double *dp, - const double *vr, - const double *ps, - double *ws, - const int *si, - const int *vd, - const double *pa, - const char *isf, - int n) -{ - (void)cr; - (void)ps; - (void)pa; - (void)isf; - int t = THREADS_PER_BLOCK; - int blocks_per_cone = PDHCG_LARGE_CONE_BLOCKS_PER_CONE; - int b = n * blocks_per_cone; - CUDA_CHECK(cudaMemsetAsync(ws, 0, (size_t)n * sizeof(double))); - compute_cone_dual_residual_grid_reduce_kernel<<>>(obj, dp, ws, si, vd, n, blocks_per_cone); - compute_cone_dual_residual_grid_finalize_kernel<<<(n + t - 1) / t, t>>>(dr, obj, dp, vr, ws, si, vd, n); - compute_cone_dual_residual_grid_apply_kernel<<>>(dr, obj, dp, vr, ws, si, vd, n, blocks_per_cone); -} -static void launch_standard_thread_dual(double *dr, - double *cr, - const double *obj, - const double *dp, - const double *vr, - const double *ps, - double *ws, - const int *si, - const int *vd, - const double *pa, - const char *isf, - int n) -{ - (void)pa; - int t = THREADS_PER_BLOCK; - int b = (n + t - 1) / t; - compute_cone_dual_residual_standard_kernel<<>>(dr, cr, obj, dp, vr, ps, ws, si, vd, isf, n); -} -static void launch_standard_warp_dual(double *dr, - double *cr, - const double *obj, - const double *dp, - const double *vr, - const double *ps, - double *ws, - const int *si, - const int *vd, - const double *pa, - const char *isf, - int n) -{ - (void)pa; - int t = THREADS_PER_BLOCK; - int b = (n * 32 + t - 1) / t; - compute_cone_dual_residual_standard_warp_kernel<<>>(dr, cr, obj, dp, vr, ps, ws, si, vd, isf, n); -} -static void launch_standard_grid_dual(double *dr, - double *cr, - const double *obj, - const double *dp, - const double *vr, - const double *ps, - double *ws, - const int *si, - const int *vd, - const double *pa, - const char *isf, - int n) -{ - (void)cr; - (void)ps; - (void)pa; - (void)isf; - int t = THREADS_PER_BLOCK; - int blocks_per_cone = PDHCG_LARGE_CONE_BLOCKS_PER_CONE; - int b = n * blocks_per_cone; - CUDA_CHECK(cudaMemsetAsync(ws, 0, (size_t)n * sizeof(double))); - compute_cone_dual_residual_standard_grid_reduce_kernel<<>>(obj, dp, ws, si, vd, n, blocks_per_cone); - compute_cone_dual_residual_standard_grid_finalize_kernel<<<(n + t - 1) / t, t>>>(dr, obj, dp, vr, ws, si, vd, n); - compute_cone_dual_residual_standard_grid_apply_kernel<<>>(dr, obj, dp, vr, ws, si, vd, n, blocks_per_cone); -} -static void launch_exp_thread_dual(double *dr, - double *cr, - const double *obj, - const double *dp, - const double *vr, - const double *ps, - double *ws, - const int *si, - const int *vd, - const double *pa, - const char *isf, - int n) -{ - (void)pa; - int t = THREADS_PER_BLOCK; - int b = (n + t - 1) / t; - compute_cone_dual_residual_exp_kernel<<>>(dr, cr, obj, dp, vr, ps, ws, si, vd, isf, n); -} -static void launch_power_thread_dual(double *dr, - double *cr, - const double *obj, - const double *dp, - const double *vr, - const double *ps, - double *ws, - const int *si, - const int *vd, - const double *pa, - const char *isf, - int n) -{ - int t = THREADS_PER_BLOCK; - int b = (n + t - 1) / t; - compute_cone_dual_residual_power_kernel<<>>(dr, cr, obj, dp, vr, ps, ws, si, vd, pa, isf, n); -} - -static void launch_projected_mapping_only_dual_impl( - double *dual_residual, const int *start_idx, const int *v_dim, int count, int blocks_per_cone) -{ - clear_cone_residual_grid_kernel<<>>( - dual_residual, start_idx, v_dim, count, blocks_per_cone); -} - -static void launch_block_projected_mapping_only_dual(double *dr, - double *cr, - const double *obj, - const double *dp, - const double *vr, - const double *ps, - double *ws, - const int *si, - const int *vd, - const double *pa, - const char *isf, - int n) -{ - (void)cr; - (void)obj; - (void)dp; - (void)vr; - (void)ps; - (void)ws; - (void)pa; - (void)isf; - launch_projected_mapping_only_dual_impl(dr, si, vd, n, 1); -} - -static void launch_grid_projected_mapping_only_dual(double *dr, - double *cr, - const double *obj, - const double *dp, - const double *vr, - const double *ps, - double *ws, - const int *si, - const int *vd, - const double *pa, - const char *isf, - int n) -{ - (void)cr; - (void)obj; - (void)dp; - (void)vr; - (void)ps; - (void)ws; - (void)pa; - (void)isf; - launch_projected_mapping_only_dual_impl(dr, si, vd, n, PDHCG_LARGE_CONE_BLOCKS_PER_CONE); -} - -typedef void (*cone_proj_diag_q_launcher_t)(double *pdhg_primal, - double *reflected_primal, - const double *current_primal, - const double *var_rescale, - const double *Q_diag, - double tau, - double *warm_start, - const int *start_idx, - const int *v_dim, - const double *power_alpha, - const char *is_fixed, - int count); - -static void launch_rotated_thread_proj_diag_q(double *pp, - double *rp, - const double *cp, - const double *vr, - const double *qd, - double tau, - double *ws, - const int *si, - const int *vd, - const double *pa, - const char *isf, - int n) -{ - (void)pa; - int t = THREADS_PER_BLOCK; - int b = (n + t - 1) / t; - project_rotated_soc_diag_q_kernel<<>>(pp, rp, cp, vr, qd, tau, ws, si, vd, isf, n); -} -static void launch_rotated_block_proj_diag_q(double *pp, - double *rp, - const double *cp, - const double *vr, - const double *qd, - double tau, - double *ws, - const int *si, - const int *vd, - const double *pa, - const char *isf, - int n) -{ - (void)pa; - project_rotated_soc_block_kernel<<>>(pp, vr, qd, tau, ws, si, vd, isf, n); - recompute_reflected_at_cone_block_kernel<<>>(rp, pp, cp, si, vd, n); -} -static void launch_rotated_grid_weighted_proj_diag_q(double *pp, - double *rp, - const double *cp, - const double *vr, - const double *qd, - double tau, - double *ws, - const int *si, - const int *vd, - const double *pa, - const char *isf, - int n) -{ - (void)pa; - launch_rotated_grid_weighted_impl(pp, vr, qd, tau, ws, si, vd, isf, n); - int blocks_per_cone = PDHCG_LARGE_CONE_BLOCKS_PER_CONE; - recompute_reflected_at_cone_grid_kernel<<>>( - rp, pp, cp, si, vd, n, blocks_per_cone); -} -static void launch_standard_thread_proj_diag_q(double *pp, - double *rp, - const double *cp, - const double *vr, - const double *qd, - double tau, - double *ws, - const int *si, - const int *vd, - const double *pa, - const char *isf, - int n) -{ - (void)pa; - int t = THREADS_PER_BLOCK; - int b = (n + t - 1) / t; - project_standard_soc_diag_q_kernel<<>>(pp, rp, cp, vr, qd, tau, ws, si, vd, isf, n); -} -static void launch_standard_block_proj_diag_q(double *pp, - double *rp, - const double *cp, - const double *vr, - const double *qd, - double tau, - double *ws, - const int *si, - const int *vd, - const double *pa, - const char *isf, - int n) -{ - (void)pa; - project_standard_soc_block_kernel<<>>(pp, vr, qd, tau, ws, si, vd, isf, n); - recompute_reflected_at_cone_block_kernel<<>>(rp, pp, cp, si, vd, n); -} -static void launch_standard_grid_weighted_proj_diag_q(double *pp, - double *rp, - const double *cp, - const double *vr, - const double *qd, - double tau, - double *ws, - const int *si, - const int *vd, - const double *pa, - const char *isf, - int n) -{ - (void)pa; - launch_standard_grid_weighted_impl(pp, vr, qd, tau, ws, si, vd, isf, n); - int blocks_per_cone = PDHCG_LARGE_CONE_BLOCKS_PER_CONE; - recompute_reflected_at_cone_grid_kernel<<>>( - rp, pp, cp, si, vd, n, blocks_per_cone); -} -static void launch_exp_thread_proj_diag_q(double *pp, - double *rp, - const double *cp, - const double *vr, - const double *qd, - double tau, - double *ws, - const int *si, - const int *vd, - const double *pa, - const char *isf, - int n) -{ - (void)pa; - int t = THREADS_PER_BLOCK; - int b = (n + t - 1) / t; - project_exp_cone_diag_q_kernel<<>>(pp, rp, cp, vr, qd, tau, ws, si, vd, isf, n); -} -static void launch_power_thread_proj_diag_q(double *pp, - double *rp, - const double *cp, - const double *vr, - const double *qd, - double tau, - double *ws, - const int *si, - const int *vd, - const double *pa, - const char *isf, - int n) -{ - int t = THREADS_PER_BLOCK; - int b = (n + t - 1) / t; - project_power_cone_diag_q_kernel<<>>(pp, rp, cp, vr, qd, tau, ws, si, vd, pa, isf, n); -} - -static const cone_proj_diag_q_launcher_t proj_diag_q_launch_table[NUM_CONE_TYPES][NUM_PROJ_METHODS] = { - [CONE_ROTATED_SOC] = - { - [PROJ_METHOD_THREAD] = launch_rotated_thread_proj_diag_q, - [PROJ_METHOD_WARP] = NULL, - [PROJ_METHOD_BLOCK] = launch_rotated_block_proj_diag_q, - [PROJ_METHOD_GRID] = NULL, - [PROJ_METHOD_GRID_WEIGHTED] = launch_rotated_grid_weighted_proj_diag_q, - }, - [CONE_STANDARD_SOC] = - { - [PROJ_METHOD_THREAD] = launch_standard_thread_proj_diag_q, - [PROJ_METHOD_WARP] = NULL, - [PROJ_METHOD_BLOCK] = launch_standard_block_proj_diag_q, - [PROJ_METHOD_GRID] = NULL, - [PROJ_METHOD_GRID_WEIGHTED] = launch_standard_grid_weighted_proj_diag_q, - }, - [CONE_EXPONENTIAL] = - { - [PROJ_METHOD_THREAD] = launch_exp_thread_proj_diag_q, - [PROJ_METHOD_WARP] = NULL, - [PROJ_METHOD_BLOCK] = NULL, - [PROJ_METHOD_GRID] = NULL, - [PROJ_METHOD_GRID_WEIGHTED] = NULL, - }, - [CONE_POWER] = - { - [PROJ_METHOD_THREAD] = launch_power_thread_proj_diag_q, - [PROJ_METHOD_WARP] = NULL, - [PROJ_METHOD_BLOCK] = NULL, - [PROJ_METHOD_GRID] = NULL, - [PROJ_METHOD_GRID_WEIGHTED] = NULL, - }, -}; - -static const cone_dual_res_launcher_t dual_res_launch_table[NUM_CONE_TYPES][NUM_PROJ_METHODS] = { - [CONE_ROTATED_SOC] = - { - [PROJ_METHOD_THREAD] = launch_rotated_thread_dual, - [PROJ_METHOD_WARP] = launch_rotated_warp_dual, - [PROJ_METHOD_BLOCK] = launch_block_projected_mapping_only_dual, - [PROJ_METHOD_GRID] = launch_rotated_grid_dual, - [PROJ_METHOD_GRID_WEIGHTED] = launch_grid_projected_mapping_only_dual, - }, - [CONE_STANDARD_SOC] = - { - [PROJ_METHOD_THREAD] = launch_standard_thread_dual, - [PROJ_METHOD_WARP] = launch_standard_warp_dual, - [PROJ_METHOD_BLOCK] = launch_block_projected_mapping_only_dual, - [PROJ_METHOD_GRID] = launch_standard_grid_dual, - [PROJ_METHOD_GRID_WEIGHTED] = launch_grid_projected_mapping_only_dual, - }, - [CONE_EXPONENTIAL] = - { - [PROJ_METHOD_THREAD] = launch_exp_thread_dual, - [PROJ_METHOD_WARP] = NULL, - [PROJ_METHOD_BLOCK] = NULL, - [PROJ_METHOD_GRID] = NULL, - [PROJ_METHOD_GRID_WEIGHTED] = NULL, - }, - [CONE_POWER] = - { - [PROJ_METHOD_THREAD] = launch_power_thread_dual, - [PROJ_METHOD_WARP] = NULL, - [PROJ_METHOD_BLOCK] = NULL, - [PROJ_METHOD_GRID] = NULL, - [PROJ_METHOD_GRID_WEIGHTED] = NULL, - }, +static_assert(CONE_ROTATED_SOC == 0 && CONE_STANDARD_SOC == 1 && CONE_EXPONENTIAL == 2 && CONE_POWER == 3 && + CONE_PSD == 4 && NUM_CONE_TYPES == 5, + "cone kernel dispatch must match cone_type_t"); +static_assert(PROJ_METHOD_THREAD == 0 && PROJ_METHOD_WARP == 1 && PROJ_METHOD_BLOCK == 2 && PROJ_METHOD_GRID == 3 && + PROJ_METHOD_GRID_WEIGHTED == 4 && NUM_PROJ_METHODS == 5, + "cone kernel dispatch must match cone_proj_method_t"); + +static const cone_kernel_ops_t *const cone_kernel_ops_by_type[NUM_CONE_TYPES] = { + &pdhcg_rsoc_cone_kernel_ops, + &pdhcg_soc_cone_kernel_ops, + &pdhcg_exp_cone_kernel_ops, + &pdhcg_power_cone_kernel_ops, + NULL, }; void project_cone_runtime(pdhg_solver_state_t *state, cone_runtime_t *runtime, double *vector, double *warm_start) @@ -686,15 +43,16 @@ void project_cone_runtime(pdhg_solver_state_t *state, cone_runtime_t *runtime, d { const cone_bucket_t *bk = &runtime->buckets[b]; const double *pa = runtime->power_alpha ? runtime->power_alpha + bk->offset : NULL; - proj_launch_table[bk->type][bk->method](vector, - coordinate_rescaling, - warm_start + PDHCG_CONE_WORKSPACE_STRIDE * bk->offset, - runtime->start_idx + bk->offset, - runtime->v_dim + bk->offset, - pa, - runtime->is_fixed, - bk->count); + cone_kernel_ops_by_type[bk->type]->project[bk->method](vector, + coordinate_rescaling, + warm_start + PDHCG_CONE_WORKSPACE_STRIDE * bk->offset, + runtime->start_idx + bk->offset, + runtime->v_dim + bk->offset, + pa, + runtime->is_fixed, + bk->count); } + project_psd_cones(runtime->psd, vector); project_split_cones(state, runtime, vector); } @@ -709,28 +67,19 @@ void project_cone_runtime_diag_q(pdhg_solver_state_t *state, cone_runtime_t *run { const cone_bucket_t *bk = &runtime->buckets[b]; const double *pa = runtime->power_alpha ? runtime->power_alpha + bk->offset : NULL; - cone_proj_method_t method = PROJ_METHOD_THREAD; - if (bk->type == CONE_STANDARD_SOC && bk->method != PROJ_METHOD_THREAD) - method = bk->method == PROJ_METHOD_GRID || bk->method == PROJ_METHOD_GRID_WEIGHTED - ? PROJ_METHOD_GRID_WEIGHTED - : PROJ_METHOD_BLOCK; - else if (bk->type == CONE_ROTATED_SOC && bk->method != PROJ_METHOD_THREAD) - method = bk->method == PROJ_METHOD_GRID || bk->method == PROJ_METHOD_GRID_WEIGHTED - ? PROJ_METHOD_GRID_WEIGHTED - : PROJ_METHOD_BLOCK; - proj_diag_q_launch_table[bk->type][method](pdhg_primal, - reflected_primal, - current_primal, - state->variable_rescaling, - Q_diag, - primal_step_size, - runtime->projection_warm_start + - PDHCG_CONE_WORKSPACE_STRIDE * bk->offset, - runtime->start_idx + bk->offset, - runtime->v_dim + bk->offset, - pa, - runtime->is_fixed, - bk->count); + cone_kernel_ops_by_type[bk->type]->project_diag_q[bk->method](pdhg_primal, + reflected_primal, + current_primal, + state->variable_rescaling, + Q_diag, + primal_step_size, + runtime->projection_warm_start + + PDHCG_CONE_WORKSPACE_STRIDE * bk->offset, + runtime->start_idx + bk->offset, + runtime->v_dim + bk->offset, + pa, + runtime->is_fixed, + bk->count); } project_split_cones(state, runtime, pdhg_primal); recompute_split_cone_reflected(state, reflected_primal, pdhg_primal, current_primal); @@ -747,123 +96,59 @@ void compute_cone_dual_residual(pdhg_solver_state_t *state, const double *effect { const cone_bucket_t *bk = &state->cones.buckets[b]; const double *pa = state->cones.power_alpha ? state->cones.power_alpha + bk->offset : NULL; - dual_res_launch_table[bk->type][bk->method](state->dual_residual, - state->cones.complementarity_residual + bk->offset, - effective_obj, - state->dual_product, - state->variable_rescaling, - state->pdhg_primal_solution, - state->cones.residual_warm_start + - PDHCG_CONE_WORKSPACE_STRIDE * bk->offset, - state->cones.start_idx + bk->offset, - state->cones.v_dim + bk->offset, - pa, - state->cones.is_fixed, - bk->count); + cone_kernel_ops_by_type[bk->type]->dual_residual[bk->method](state->dual_residual, + state->cones.complementarity_residual + bk->offset, + effective_obj, + state->dual_product, + state->variable_rescaling, + state->pdhg_primal_solution, + state->cones.residual_warm_start + + PDHCG_CONE_WORKSPACE_STRIDE * bk->offset, + state->cones.start_idx + bk->offset, + state->cones.v_dim + bk->offset, + pa, + state->cones.is_fixed, + bk->count); } + compute_psd_cone_dual_residual( + state->cones.psd, state->dual_residual, effective_obj, state->dual_product, state->variable_rescaling); compute_split_cone_dual_residual(state, effective_obj); } void recompute_cone_reflection(pdhg_solver_state_t *state) { - int threads = THREADS_PER_BLOCK; for (int b = 0; b < state->cones.num_buckets; ++b) { const cone_bucket_t *bk = &state->cones.buckets[b]; - if (bk->method == PROJ_METHOD_GRID || bk->method == PROJ_METHOD_GRID_WEIGHTED) - { - int blocks_per_cone = PDHCG_LARGE_CONE_BLOCKS_PER_CONE; - recompute_reflected_at_cone_grid_kernel<<count * blocks_per_cone, threads>>>( - state->reflected_primal_solution, - state->pdhg_primal_solution, - state->current_primal_solution, - state->cones.start_idx + bk->offset, - state->cones.v_dim + bk->offset, - bk->count, - blocks_per_cone); - } - else if (bk->method == PROJ_METHOD_BLOCK) - { - recompute_reflected_at_cone_block_kernel<<count, threads>>>(state->reflected_primal_solution, - state->pdhg_primal_solution, - state->current_primal_solution, - state->cones.start_idx + bk->offset, - state->cones.v_dim + bk->offset, - bk->count); - } - else if (bk->method == PROJ_METHOD_WARP) - { - int blocks = (bk->count * 32 + threads - 1) / threads; - recompute_reflected_at_cone_warp_kernel<<>>(state->reflected_primal_solution, - state->pdhg_primal_solution, - state->current_primal_solution, - state->cones.start_idx + bk->offset, - state->cones.v_dim + bk->offset, - bk->count); - } - else - { - int blocks = (bk->count + threads - 1) / threads; - recompute_reflected_at_cone_kernel<<>>(state->reflected_primal_solution, - state->pdhg_primal_solution, - state->current_primal_solution, - state->cones.start_idx + bk->offset, - state->cones.v_dim + bk->offset, - bk->count); - } + launch_cone_reflection(bk->method, + state->reflected_primal_solution, + state->pdhg_primal_solution, + state->current_primal_solution, + state->cones.start_idx + bk->offset, + state->cones.v_dim + bk->offset, + bk->count); } + recompute_psd_cone_reflection(state->cones.psd, + state->reflected_primal_solution, + state->pdhg_primal_solution, + state->current_primal_solution); recompute_split_cone_reflected( state, state->reflected_primal_solution, state->pdhg_primal_solution, state->current_primal_solution); } void set_cone_dual_slack(pdhg_solver_state_t *state, const double *effective_obj) { - int threads = THREADS_PER_BLOCK; for (int b = 0; b < state->cones.num_buckets; ++b) { const cone_bucket_t *bk = &state->cones.buckets[b]; - if (bk->method == PROJ_METHOD_GRID || bk->method == PROJ_METHOD_GRID_WEIGHTED) - { - int blocks_per_cone = PDHCG_LARGE_CONE_BLOCKS_PER_CONE; - set_cone_dual_slack_grid_kernel<<count * blocks_per_cone, threads>>>(state->dual_slack, - effective_obj, - state->dual_product, - state->cones.start_idx + - bk->offset, - state->cones.v_dim + bk->offset, - bk->count, - blocks_per_cone); - } - else if (bk->method == PROJ_METHOD_BLOCK) - { - set_cone_dual_slack_grid_kernel<<count, threads>>>(state->dual_slack, - effective_obj, - state->dual_product, - state->cones.start_idx + bk->offset, - state->cones.v_dim + bk->offset, - bk->count, - 1); - } - else if (bk->method == PROJ_METHOD_WARP) - { - int blocks = (bk->count * 32 + threads - 1) / threads; - set_cone_dual_slack_warp_kernel<<>>(state->dual_slack, - effective_obj, - state->dual_product, - state->cones.start_idx + bk->offset, - state->cones.v_dim + bk->offset, - bk->count); - } - else - { - int blocks = (bk->count + threads - 1) / threads; - set_cone_dual_slack_kernel<<>>(state->dual_slack, - effective_obj, - state->dual_product, - state->cones.start_idx + bk->offset, - state->cones.v_dim + bk->offset, - bk->count); - } + launch_cone_dual_slack(bk->method, + state->dual_slack, + effective_obj, + state->dual_product, + state->cones.start_idx + bk->offset, + state->cones.v_dim + bk->offset, + bk->count); } + set_psd_cone_dual_slack(state->cones.psd, state->dual_slack, effective_obj, state->dual_product); set_split_cone_dual_slack(state, state->dual_slack, effective_obj, state->dual_product); } diff --git a/src/cone_utils.c b/src/cone_utils.c index 8212414..3844d46 100644 --- a/src/cone_utils.c +++ b/src/cone_utils.c @@ -29,6 +29,11 @@ int cone_length(cone_type_t type, int v_dim) return 3; if (type == CONE_STANDARD_SOC || type == CONE_ROTATED_SOC) return v_dim >= 0 && v_dim <= INT_MAX - 2 ? v_dim + 2 : -1; + if (type == CONE_PSD && v_dim > 0) + { + long long length = (long long)v_dim * (v_dim + 1LL) / 2LL; + return length <= INT_MAX ? (int)length : -1; + } return -1; } @@ -92,6 +97,18 @@ int cone_blocks_init_from_specs(cone_blocks_t *blocks, free(owner); return -1; } + if (specs[cone].type == CONE_PSD && specs[cone].is_fixed) + { + int has_fixed = 0; + for (int slot = 0; slot < length; ++slot) + has_fixed |= specs[cone].is_fixed[slot] != 0; + if (has_fixed) + { + fprintf(stderr, "[create_qp_problem] %s PSD cone %d does not support fixed slots.\n", kind, cone); + free(owner); + return -1; + } + } if (specs[cone].type == CONE_POWER && !(isfinite(specs[cone].power_alpha) && specs[cone].power_alpha > 0.0 && specs[cone].power_alpha < 1.0)) { diff --git a/src/kernels/pdhcg_affine_cone_kernels.cu b/src/kernels/pdhcg_affine_cone_kernels.cu new file mode 100644 index 0000000..4d7052b --- /dev/null +++ b/src/kernels/pdhcg_affine_cone_kernels.cu @@ -0,0 +1,122 @@ +/* +Copyright 2025-2026 Haihao Lu +Copyright 2026 Hongpei Li + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +#include "pdhcg_affine_cone_kernels.h" + +#include +#include + +__global__ void finish_affine_cone_residuals_kernel(double *primal_residual, + const double *primal_product, + const double *affine_cone_offset, + const double *constraint_rescaling, + double *dual_membership, + const double *dual_membership_rescaling, + int n) +{ + int i = blockIdx.x * blockDim.x + threadIdx.x; + if (i < n) + { + double value = primal_product[i] + affine_cone_offset[i]; + primal_residual[i] = (value - primal_residual[i]) * constraint_rescaling[i]; + dual_membership[i] *= dual_membership_rescaling[i]; + } +} + +__global__ void prepare_affine_cone_residuals_kernel(double *projection_point, + double *complementarity_residual, + const double *primal_product, + const double *affine_cone_offset, + const double *dual_solution, + const int *start_idx, + const int *v_dim, + double constraint_bound_rescaling, + int num_cones) +{ + int cone = blockIdx.x; + if (cone >= num_cones) + return; + int start = start_idx[cone]; + int length = v_dim[cone] + 2; + double dot = 0.0; + for (int slot = threadIdx.x; slot < length; slot += blockDim.x) + { + int i = start + slot; + double dual = dual_solution[i]; + projection_point[i] = -dual; + dot += dual * (primal_product[i] + affine_cone_offset[i]); + } + + extern __shared__ double partial_sum[]; + partial_sum[threadIdx.x] = dot; + __syncthreads(); + for (int stride = blockDim.x / 2; stride > 0; stride >>= 1) + { + if (threadIdx.x < stride) + partial_sum[threadIdx.x] += partial_sum[threadIdx.x + stride]; + __syncthreads(); + } + if (threadIdx.x == 0) + complementarity_residual[cone] = fabs(partial_sum[0]) / constraint_bound_rescaling; +} + +__global__ void prepare_affine_cone_residuals_grid_kernel(double *projection_point, + double *complementarity_accumulator, + const double *primal_product, + const double *affine_cone_offset, + const double *dual_solution, + const int *start_idx, + const int *v_dim, + int num_cones, + int blocks_per_cone) +{ + int cone = blockIdx.x / blocks_per_cone; + if (cone >= num_cones) + return; + int part = blockIdx.x - cone * blocks_per_cone; + int start = start_idx[cone]; + int length = v_dim[cone] + 2; + double dot = 0.0; + for (int slot = part * blockDim.x + threadIdx.x; slot < length; slot += blocks_per_cone * blockDim.x) + { + int index = start + slot; + double dual = dual_solution[index]; + projection_point[index] = -dual; + dot += dual * (primal_product[index] + affine_cone_offset[index]); + } + + extern __shared__ double partial_sum[]; + partial_sum[threadIdx.x] = dot; + __syncthreads(); + for (int stride = blockDim.x / 2; stride > 0; stride >>= 1) + { + if (threadIdx.x < stride) + partial_sum[threadIdx.x] += partial_sum[threadIdx.x + stride]; + __syncthreads(); + } + if (threadIdx.x == 0) + atomicAdd(complementarity_accumulator + cone, partial_sum[0]); +} + +__global__ void finish_affine_cone_complementarity_kernel(double *complementarity_residual, + double constraint_bound_rescaling, + int num_cones) +{ + int cone = blockIdx.x * blockDim.x + threadIdx.x; + if (cone < num_cones) + complementarity_residual[cone] = fabs(complementarity_residual[cone]) / constraint_bound_rescaling; +} diff --git a/src/kernels/pdhcg_cone_common_kernels.cu b/src/kernels/pdhcg_cone_common_kernels.cu new file mode 100644 index 0000000..283af51 --- /dev/null +++ b/src/kernels/pdhcg_cone_common_kernels.cu @@ -0,0 +1,305 @@ +/* +Copyright 2026 Hongpei Li + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +#include "cone_kernel_ops.h" +#include "pdhcg_cone_common_kernels.h" +#include "utils.h" + +#include + +__global__ void set_cone_dual_slack_kernel(double *__restrict__ dual_slack, + const double *__restrict__ objective_vector, + const double *__restrict__ dual_product, + const int *__restrict__ start_idx, + const int *__restrict__ v_dim, + int num_blocks) +{ + int blk = blockIdx.x * blockDim.x + threadIdx.x; + if (blk >= num_blocks) + return; + int start = start_idx[blk]; + int k = v_dim[blk]; + for (int m = 0; m < k + 2; ++m) + { + int idx = start + m; + dual_slack[idx] = objective_vector[idx] - dual_product[idx]; + } +} + +__global__ void set_cone_dual_slack_grid_kernel(double *__restrict__ dual_slack, + const double *__restrict__ objective_vector, + const double *__restrict__ dual_product, + const int *__restrict__ start_idx, + const int *__restrict__ v_dim, + int num_cones, + int blocks_per_cone) +{ + int cone = blockIdx.x / blocks_per_cone; + if (cone >= num_cones) + return; + + int part = blockIdx.x - cone * blocks_per_cone; + int start = start_idx[cone]; + int n = v_dim[cone] + 2; + for (int m = part * blockDim.x + threadIdx.x; m < n; m += blocks_per_cone * blockDim.x) + { + int idx = start + m; + dual_slack[idx] = objective_vector[idx] - dual_product[idx]; + } +} + +__global__ void set_cone_dual_slack_warp_kernel(double *__restrict__ dual_slack, + const double *__restrict__ objective_vector, + const double *__restrict__ dual_product, + const int *__restrict__ start_idx, + const int *__restrict__ v_dim, + int num_cones) +{ + int global_thread = blockIdx.x * blockDim.x + threadIdx.x; + int cone = global_thread >> 5; + if (cone >= num_cones) + return; + + int lane = global_thread & 31; + int start = start_idx[cone]; + int n = v_dim[cone] + 2; + for (int m = lane; m < n; m += 32) + { + int idx = start + m; + dual_slack[idx] = objective_vector[idx] - dual_product[idx]; + } +} + +__global__ void recompute_reflected_at_cone_kernel(double *__restrict__ reflected_primal, + const double *__restrict__ pdhg_primal, + const double *__restrict__ current_primal, + const int *__restrict__ start_idx, + const int *__restrict__ v_dim, + int num_blocks) +{ + int blk = blockIdx.x * blockDim.x + threadIdx.x; + if (blk >= num_blocks) + return; + int start = start_idx[blk]; + int k = v_dim[blk]; + for (int m = 0; m < k + 2; ++m) + { + int idx = start + m; + reflected_primal[idx] = 2.0 * pdhg_primal[idx] - current_primal[idx]; + } +} + +__global__ void recompute_reflected_at_cone_warp_kernel(double *__restrict__ reflected_primal, + const double *__restrict__ pdhg_primal, + const double *__restrict__ current_primal, + const int *__restrict__ start_idx, + const int *__restrict__ v_dim, + int num_cones) +{ + int global_thread = blockIdx.x * blockDim.x + threadIdx.x; + int cone = global_thread >> 5; + if (cone >= num_cones) + return; + + int lane = global_thread & 31; + int start = start_idx[cone]; + int n = v_dim[cone] + 2; + for (int m = lane; m < n; m += 32) + { + int idx = start + m; + reflected_primal[idx] = 2.0 * pdhg_primal[idx] - current_primal[idx]; + } +} + +__global__ void recompute_reflected_at_cone_grid_kernel(double *__restrict__ reflected_primal, + const double *__restrict__ pdhg_primal, + const double *__restrict__ current_primal, + const int *__restrict__ start_idx, + const int *__restrict__ v_dim, + int num_cones, + int blocks_per_cone) +{ + int cone = blockIdx.x / blocks_per_cone; + if (cone >= num_cones) + return; + + int part = blockIdx.x - cone * blocks_per_cone; + int start = start_idx[cone]; + int n = v_dim[cone] + 2; + for (int m = part * blockDim.x + threadIdx.x; m < n; m += blocks_per_cone * blockDim.x) + { + int idx = start + m; + reflected_primal[idx] = 2.0 * pdhg_primal[idx] - current_primal[idx]; + } +} + +__global__ void recompute_reflected_at_cone_block_kernel(double *__restrict__ reflected_primal, + const double *__restrict__ pdhg_primal, + const double *__restrict__ current_primal, + const int *__restrict__ start_idx, + const int *__restrict__ v_dim, + int num_cones) +{ + int cone = blockIdx.x; + if (cone >= num_cones) + return; + int start = start_idx[cone]; + int length = v_dim[cone] + 2; + for (int slot = threadIdx.x; slot < length; slot += blockDim.x) + { + int index = start + slot; + reflected_primal[index] = 2.0 * pdhg_primal[index] - current_primal[index]; + } +} + +__global__ void clear_cone_residual_grid_kernel(double *__restrict__ dual_residual, + const int *__restrict__ start_idx, + const int *__restrict__ v_dim, + int num_cones, + int blocks_per_cone) +{ + int cone = blockIdx.x / blocks_per_cone; + if (cone >= num_cones) + return; + int part = blockIdx.x - cone * blocks_per_cone; + int start = start_idx[cone]; + int length = v_dim[cone] + 2; + for (int slot = part * blockDim.x + threadIdx.x; slot < length; slot += blocks_per_cone * blockDim.x) + dual_residual[start + slot] = 0.0; +} + +static void launch_projected_mapping_only_dual_impl( + double *dual_residual, const int *start_idx, const int *v_dim, int count, int blocks_per_cone) +{ + clear_cone_residual_grid_kernel<<>>( + dual_residual, start_idx, v_dim, count, blocks_per_cone); +} + +void launch_block_projected_mapping_only_dual(double *dr, + double *cr, + const double *obj, + const double *dp, + const double *vr, + const double *ps, + double *ws, + const int *si, + const int *vd, + const double *pa, + const char *isf, + int n) +{ + (void)cr; + (void)obj; + (void)dp; + (void)vr; + (void)ps; + (void)ws; + (void)pa; + (void)isf; + launch_projected_mapping_only_dual_impl(dr, si, vd, n, 1); +} + +void launch_grid_projected_mapping_only_dual(double *dr, + double *cr, + const double *obj, + const double *dp, + const double *vr, + const double *ps, + double *ws, + const int *si, + const int *vd, + const double *pa, + const char *isf, + int n) +{ + (void)cr; + (void)obj; + (void)dp; + (void)vr; + (void)ps; + (void)ws; + (void)pa; + (void)isf; + launch_projected_mapping_only_dual_impl(dr, si, vd, n, PDHCG_LARGE_CONE_BLOCKS_PER_CONE); +} + +void launch_cone_reflection(cone_proj_method_t method, + double *reflected_primal, + const double *pdhg_primal, + const double *current_primal, + const int *start_idx, + const int *v_dim, + int count) +{ + int threads = THREADS_PER_BLOCK; + if (method == PROJ_METHOD_GRID || method == PROJ_METHOD_GRID_WEIGHTED) + { + int blocks_per_cone = PDHCG_LARGE_CONE_BLOCKS_PER_CONE; + recompute_reflected_at_cone_grid_kernel<<>>( + reflected_primal, pdhg_primal, current_primal, start_idx, v_dim, count, blocks_per_cone); + } + else if (method == PROJ_METHOD_BLOCK) + { + recompute_reflected_at_cone_block_kernel<<>>( + reflected_primal, pdhg_primal, current_primal, start_idx, v_dim, count); + } + else if (method == PROJ_METHOD_WARP) + { + int blocks = (count * 32 + threads - 1) / threads; + recompute_reflected_at_cone_warp_kernel<<>>( + reflected_primal, pdhg_primal, current_primal, start_idx, v_dim, count); + } + else + { + int blocks = (count + threads - 1) / threads; + recompute_reflected_at_cone_kernel<<>>( + reflected_primal, pdhg_primal, current_primal, start_idx, v_dim, count); + } +} + +void launch_cone_dual_slack(cone_proj_method_t method, + double *dual_slack, + const double *objective_vector, + const double *dual_product, + const int *start_idx, + const int *v_dim, + int count) +{ + int threads = THREADS_PER_BLOCK; + if (method == PROJ_METHOD_GRID || method == PROJ_METHOD_GRID_WEIGHTED) + { + int blocks_per_cone = PDHCG_LARGE_CONE_BLOCKS_PER_CONE; + set_cone_dual_slack_grid_kernel<<>>( + dual_slack, objective_vector, dual_product, start_idx, v_dim, count, blocks_per_cone); + } + else if (method == PROJ_METHOD_BLOCK) + { + set_cone_dual_slack_grid_kernel<<>>( + dual_slack, objective_vector, dual_product, start_idx, v_dim, count, 1); + } + else if (method == PROJ_METHOD_WARP) + { + int blocks = (count * 32 + threads - 1) / threads; + set_cone_dual_slack_warp_kernel<<>>( + dual_slack, objective_vector, dual_product, start_idx, v_dim, count); + } + else + { + int blocks = (count + threads - 1) / threads; + set_cone_dual_slack_kernel<<>>( + dual_slack, objective_vector, dual_product, start_idx, v_dim, count); + } +} diff --git a/src/kernels/pdhcg_conic_block_kernels.cu b/src/kernels/pdhcg_conic_block_kernels.cu deleted file mode 100644 index 13ec9c8..0000000 --- a/src/kernels/pdhcg_conic_block_kernels.cu +++ /dev/null @@ -1,2053 +0,0 @@ -/* -Copyright 2026 Hongpei Li - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -#include "cone_section_projection.cuh" -#include "pdhcg_kernels.cuh" - -#include -#include -#include - -__device__ static inline void cone_block_sum3(double *first, double *second, double *third, double scratch[96]) -{ - int lane = threadIdx.x & 31; - int warp = threadIdx.x >> 5; - unsigned mask = __activemask(); - double a = *first; - double b = *second; - double c = *third; - for (int offset = 16; offset > 0; offset >>= 1) - { - a += __shfl_down_sync(mask, a, offset); - b += __shfl_down_sync(mask, b, offset); - c += __shfl_down_sync(mask, c, offset); - } - if (lane == 0) - { - scratch[3 * warp + 0] = a; - scratch[3 * warp + 1] = b; - scratch[3 * warp + 2] = c; - } - __syncthreads(); - - int num_warps = (blockDim.x + 31) >> 5; - if (warp == 0) - { - a = lane < num_warps ? scratch[3 * lane + 0] : 0.0; - b = lane < num_warps ? scratch[3 * lane + 1] : 0.0; - c = lane < num_warps ? scratch[3 * lane + 2] : 0.0; - for (int offset = 16; offset > 0; offset >>= 1) - { - a += __shfl_down_sync(0xffffffffu, a, offset); - b += __shfl_down_sync(0xffffffffu, b, offset); - c += __shfl_down_sync(0xffffffffu, c, offset); - } - if (lane == 0) - { - scratch[0] = a; - scratch[1] = b; - scratch[2] = c; - } - } - __syncthreads(); - *first = scratch[0]; - *second = scratch[1]; - *third = scratch[2]; - __syncthreads(); -} - -__device__ static inline double cone_block_max(double value, double scratch[96]) -{ - int lane = threadIdx.x & 31; - int warp = threadIdx.x >> 5; - unsigned mask = __activemask(); - for (int offset = 16; offset > 0; offset >>= 1) - value = fmax(value, __shfl_down_sync(mask, value, offset)); - if (lane == 0) - scratch[warp] = value; - __syncthreads(); - - int num_warps = (blockDim.x + 31) >> 5; - if (warp == 0) - { - value = lane < num_warps ? scratch[lane] : 0.0; - for (int offset = 16; offset > 0; offset >>= 1) - value = fmax(value, __shfl_down_sync(0xffffffffu, value, offset)); - if (lane == 0) - scratch[0] = value; - } - __syncthreads(); - value = scratch[0]; - __syncthreads(); - return value; -} - -__device__ static inline void cone_atomic_max_positive(double *address, double value) -{ - atomicMax(reinterpret_cast(address), - static_cast(__double_as_longlong(value))); -} - -enum standard_soc_block_mode -{ - SOC_BLOCK_IDENTITY = 0, - SOC_BLOCK_ZERO_FREE = 1, - SOC_BLOCK_APEX = 2, - SOC_BLOCK_SCALAR_Z = 3, - SOC_BLOCK_FIXED_Z_ROOT = 4, - SOC_BLOCK_FREE_Z_ROOT = 5, - SOC_BLOCK_ZERO_Z_ROOT = 6 -}; - -__global__ void project_standard_soc_block_kernel(double *__restrict__ point, - const double *__restrict__ rescaling, - const double *__restrict__ q_diag, - double tau, - double *__restrict__ warm_start, - const int *__restrict__ start_idx, - const int *__restrict__ v_dim, - const char *__restrict__ is_fixed, - int num_cones) -{ - int cone = blockIdx.x; - if (cone >= num_cones) - return; - - __shared__ double scratch[96]; - __shared__ double fixed_norm2; - __shared__ double radius2; - __shared__ double lambda; - __shared__ double lo; - __shared__ double hi; - __shared__ double z_input; - __shared__ double omega_z; - __shared__ int mode; - __shared__ int lower_branch; - __shared__ int done; - - int start = start_idx[cone]; - int k = v_dim[cone]; - int u_length = k + 1; - int z_index = start + u_length; - bool fixed_z = is_fixed && is_fixed[z_index]; - - double local_fixed_norm2 = 0.0; - double local_free_norm2 = 0.0; - double local_polar_norm2 = 0.0; - double local_max_omega = 0.0; - for (int slot = threadIdx.x; slot < u_length; slot += blockDim.x) - { - int index = start + slot; - double value = point[index] / rescaling[index]; - if (is_fixed && is_fixed[index]) - local_fixed_norm2 += value * value; - else - { - double omega = cone_section_weight(rescaling, q_diag, tau, index); - local_free_norm2 += value * value; - local_polar_norm2 += (omega * value) * (omega * value); - local_max_omega = fmax(local_max_omega, omega); - } - } - cone_block_sum3(&local_fixed_norm2, &local_free_norm2, &local_polar_norm2, scratch); - local_max_omega = cone_block_max(local_max_omega, scratch); - - if (threadIdx.x == 0) - { - fixed_norm2 = local_fixed_norm2; - z_input = point[z_index] / rescaling[z_index]; - omega_z = cone_section_weight(rescaling, q_diag, tau, z_index); - int free_count = 0; - for (int slot = 0; slot < u_length; ++slot) - free_count += !(is_fixed && is_fixed[start + slot]); - - if (fixed_z) - { - radius2 = fmax(0.0, z_input * z_input - fixed_norm2); - if (free_count == 0 || local_free_norm2 <= radius2) - mode = SOC_BLOCK_IDENTITY; - else if (!(radius2 > 0.0)) - mode = SOC_BLOCK_ZERO_FREE; - else - { - mode = SOC_BLOCK_FIXED_Z_ROOT; - hi = sqrt(local_polar_norm2) / sqrt(radius2) * (1.0 + 64.0 * DBL_EPSILON); - } - } - else if (z_input >= 0.0 && fixed_norm2 + local_free_norm2 <= z_input * z_input) - { - mode = SOC_BLOCK_IDENTITY; - } - else if (free_count == 0) - { - mode = SOC_BLOCK_SCALAR_Z; - } - else if (fixed_norm2 == 0.0 && -omega_z * z_input >= sqrt(local_polar_norm2)) - { - mode = SOC_BLOCK_APEX; - } - else if (z_input == 0.0) - { - mode = SOC_BLOCK_ZERO_Z_ROOT; - lambda = omega_z; - } - else - { - mode = SOC_BLOCK_FREE_Z_ROOT; - lower_branch = z_input > 0.0; - lo = lower_branch ? 0.0 : omega_z * (1.0 + 1e-14); - hi = lower_branch ? omega_z * (1.0 - 1e-14) - : cone_section_negative_soc_upper( - omega_z, -omega_z * z_input, fixed_norm2, local_polar_norm2, local_max_omega); - } - } - __syncthreads(); - - if (mode == SOC_BLOCK_IDENTITY) - return; - if (mode == SOC_BLOCK_ZERO_FREE || mode == SOC_BLOCK_APEX) - { - for (int slot = threadIdx.x; slot < u_length; slot += blockDim.x) - if (!(is_fixed && is_fixed[start + slot])) - point[start + slot] = 0.0; - if (mode == SOC_BLOCK_APEX && threadIdx.x == 0) - point[z_index] = 0.0; - return; - } - if (mode == SOC_BLOCK_SCALAR_Z) - { - if (threadIdx.x == 0) - point[z_index] = fmax(z_input, sqrt(fixed_norm2)) * rescaling[z_index]; - return; - } - - if (mode == SOC_BLOCK_FIXED_Z_ROOT) - { - if (threadIdx.x == 0) - { - lo = 0.0; - done = hi > 0.0 && isfinite(hi); - if (!done) - hi = warm_start && warm_start[cone] > 0.0 && isfinite(warm_start[cone]) ? warm_start[cone] : 1.0; - } - __syncthreads(); - for (int expansion = 0; expansion < 80; ++expansion) - { - if (done) - break; - double norm2 = 0.0; - double unused = 0.0; - double unused2 = 0.0; - for (int slot = threadIdx.x; slot < u_length; slot += blockDim.x) - { - int index = start + slot; - if (is_fixed && is_fixed[index]) - continue; - double omega = cone_section_weight(rescaling, q_diag, tau, index); - double value = (point[index] / rescaling[index]) * omega / (omega + hi); - norm2 += value * value; - } - cone_block_sum3(&norm2, &unused, &unused2, scratch); - if (threadIdx.x == 0) - { - done = norm2 <= radius2; - if (!done) - hi *= 2.0; - } - __syncthreads(); - if (done) - break; - } - __syncthreads(); - - if (threadIdx.x == 0) - { - double warm = warm_start ? warm_start[cone] : 0.0; - lambda = warm > lo && warm < hi && isfinite(warm) ? warm : 0.5 * (lo + hi); - done = 0; - } - __syncthreads(); - for (int iteration = 0; iteration < 30; ++iteration) - { - double norm2 = 0.0; - double derivative = 0.0; - double unused = 0.0; - for (int slot = threadIdx.x; slot < u_length; slot += blockDim.x) - { - int index = start + slot; - if (is_fixed && is_fixed[index]) - continue; - double omega = cone_section_weight(rescaling, q_diag, tau, index); - double value = (point[index] / rescaling[index]) * omega / (omega + lambda); - norm2 += value * value; - derivative -= 2.0 * value * value / (omega + lambda); - } - cone_block_sum3(&norm2, &derivative, &unused, scratch); - if (threadIdx.x == 0) - { - double f = norm2 - radius2; - if (f > 0.0) - lo = lambda; - else - hi = lambda; - double next = lambda - f / derivative; - if (!isfinite(next) || !(next > lo && next < hi)) - next = 0.5 * (lo + hi); - done = fabs(f) <= 1e-13 * (1.0 + radius2) || hi - lo <= 1e-13 * (1.0 + hi + lo); - if (!done) - lambda = next; - } - __syncthreads(); - if (done) - break; - } - } - else if (mode == SOC_BLOCK_ZERO_Z_ROOT) - { - double norm2 = 0.0; - double unused = 0.0; - double unused2 = 0.0; - for (int slot = threadIdx.x; slot < u_length; slot += blockDim.x) - { - int index = start + slot; - if (is_fixed && is_fixed[index]) - continue; - double omega = cone_section_weight(rescaling, q_diag, tau, index); - double value = (point[index] / rescaling[index]) * omega / (omega + lambda); - norm2 += value * value; - } - cone_block_sum3(&norm2, &unused, &unused2, scratch); - if (threadIdx.x == 0) - point[z_index] = sqrt(fixed_norm2 + norm2) * rescaling[z_index]; - } - else - { - if (!lower_branch) - { - if (threadIdx.x == 0) - { - done = hi > lo && isfinite(hi); - if (!done) - hi = 2.0 * omega_z; - } - __syncthreads(); - for (int expansion = 0; expansion < 80; ++expansion) - { - if (done) - break; - double norm2 = 0.0; - double unused = 0.0; - double unused2 = 0.0; - for (int slot = threadIdx.x; slot < u_length; slot += blockDim.x) - { - int index = start + slot; - if (is_fixed && is_fixed[index]) - continue; - double omega = cone_section_weight(rescaling, q_diag, tau, index); - double value = (point[index] / rescaling[index]) * omega / (omega + hi); - norm2 += value * value; - } - cone_block_sum3(&norm2, &unused, &unused2, scratch); - if (threadIdx.x == 0) - { - double z = omega_z * z_input / (omega_z - hi); - done = fixed_norm2 + norm2 >= z * z; - if (!done) - hi *= 2.0; - } - __syncthreads(); - if (done) - break; - } - } - __syncthreads(); - if (threadIdx.x == 0) - { - double warm = warm_start ? warm_start[cone] : 0.0; - lambda = warm > lo && warm < hi && isfinite(warm) ? warm : 0.5 * (lo + hi); - done = 0; - } - __syncthreads(); - for (int iteration = 0; iteration < 35; ++iteration) - { - double norm2 = 0.0; - double derivative = 0.0; - double unused = 0.0; - for (int slot = threadIdx.x; slot < u_length; slot += blockDim.x) - { - int index = start + slot; - if (is_fixed && is_fixed[index]) - continue; - double omega = cone_section_weight(rescaling, q_diag, tau, index); - double value = (point[index] / rescaling[index]) * omega / (omega + lambda); - norm2 += value * value; - derivative -= 2.0 * value * value / (omega + lambda); - } - cone_block_sum3(&norm2, &derivative, &unused, scratch); - if (threadIdx.x == 0) - { - double z = omega_z * z_input / (omega_z - lambda); - double f = fixed_norm2 + norm2 - z * z; - derivative -= 2.0 * z * z / (omega_z - lambda); - if ((lower_branch && f > 0.0) || (!lower_branch && f < 0.0)) - lo = lambda; - else - hi = lambda; - double next = lambda - f / derivative; - if (!isfinite(next) || !(next > lo && next < hi)) - next = 0.5 * (lo + hi); - done = fabs(f) <= 1e-13 * (1.0 + fixed_norm2 + norm2 + z * z) || hi - lo <= 1e-13 * (1.0 + hi + lo); - if (!done) - lambda = next; - } - __syncthreads(); - if (done) - break; - } - if (threadIdx.x == 0) - point[z_index] *= omega_z / (omega_z - lambda); - } - - if (warm_start && threadIdx.x == 0) - warm_start[cone] = lambda; - for (int slot = threadIdx.x; slot < u_length; slot += blockDim.x) - { - int index = start + slot; - if (!(is_fixed && is_fixed[index])) - { - double omega = cone_section_weight(rescaling, q_diag, tau, index); - point[index] *= omega / (omega + lambda); - } - } -} - -enum standard_soc_grid_weighted_mode -{ - SOC_GRID_IDENTITY = 0, - SOC_GRID_ZERO_FREE = 1, - SOC_GRID_APEX = 2, - SOC_GRID_SCALAR_Z = 3, - SOC_GRID_FIXED_EXPAND = 4, - SOC_GRID_FIXED_ROOT = 5, - SOC_GRID_FREE_EXPAND = 6, - SOC_GRID_FREE_ROOT = 7, - SOC_GRID_ZERO_Z_EVAL = 8, - SOC_GRID_FIXED_APPLY = 9, - SOC_GRID_FREE_APPLY = 10, - SOC_GRID_ZERO_Z_APPLY = 11 -}; - -__global__ void initialize_standard_soc_grid_weighted_kernel(const double *__restrict__ point, - const double *__restrict__ rescaling, - const double *__restrict__ q_diag, - double tau, - double *__restrict__ workspace, - const int *__restrict__ start_idx, - const int *__restrict__ v_dim, - const char *__restrict__ is_fixed, - int num_cones, - int blocks_per_cone) -{ - int cone = blockIdx.x / blocks_per_cone; - if (cone >= num_cones) - return; - int part = blockIdx.x - cone * blocks_per_cone; - int start = start_idx[cone]; - int u_length = v_dim[cone] + 1; - double fixed_norm2 = 0.0; - double free_norm2 = 0.0; - double polar_norm2 = 0.0; - double free_count = 0.0; - double max_omega = 0.0; - for (int slot = part * blockDim.x + threadIdx.x; slot < u_length; slot += blocks_per_cone * blockDim.x) - { - int index = start + slot; - double value = point[index] / rescaling[index]; - if (is_fixed && is_fixed[index]) - fixed_norm2 += value * value; - else - { - double omega = cone_section_weight(rescaling, q_diag, tau, index); - free_norm2 += value * value; - polar_norm2 += (omega * value) * (omega * value); - free_count += 1.0; - max_omega = fmax(max_omega, omega); - } - } - __shared__ double scratch[96]; - cone_block_sum3(&fixed_norm2, &free_norm2, &polar_norm2, scratch); - double unused = 0.0; - double unused2 = 0.0; - cone_block_sum3(&free_count, &unused, &unused2, scratch); - max_omega = cone_block_max(max_omega, scratch); - if (threadIdx.x == 0) - { - atomicAdd(workspace + num_cones + cone, fixed_norm2); - atomicAdd(workspace + 2 * num_cones + cone, free_norm2); - atomicAdd(workspace + 3 * num_cones + cone, polar_norm2); - atomicAdd(workspace + 4 * num_cones + cone, free_count); - cone_atomic_max_positive(workspace + 5 * num_cones + cone, max_omega); - } -} - -__global__ void finalize_standard_soc_grid_weighted_initialization_kernel(const double *__restrict__ point, - const double *__restrict__ rescaling, - const double *__restrict__ q_diag, - double tau, - double *__restrict__ workspace, - const int *__restrict__ start_idx, - const int *__restrict__ v_dim, - const char *__restrict__ is_fixed, - int num_cones) -{ - int cone = blockIdx.x * blockDim.x + threadIdx.x; - if (cone >= num_cones) - return; - int start = start_idx[cone]; - int z_index = start + v_dim[cone] + 1; - double warm = workspace[cone]; - double fixed_norm2 = workspace[num_cones + cone]; - double free_norm2 = workspace[2 * num_cones + cone]; - double polar_norm2 = workspace[3 * num_cones + cone]; - int free_count = (int)workspace[4 * num_cones + cone]; - double max_omega = workspace[5 * num_cones + cone]; - double z = point[z_index] / rescaling[z_index]; - double omega_z_value = cone_section_weight(rescaling, q_diag, tau, z_index); - bool fixed_z = is_fixed && is_fixed[z_index]; - int selected_mode; - double constant = fixed_norm2; - double lower = 0.0; - double upper = 0.0; - double trial = warm; - - if (fixed_z) - { - constant = fmax(0.0, z * z - fixed_norm2); - if (free_count == 0 || free_norm2 <= constant) - selected_mode = SOC_GRID_IDENTITY; - else if (!(constant > 0.0)) - selected_mode = SOC_GRID_ZERO_FREE; - else - { - lower = 0.0; - upper = sqrt(polar_norm2) / sqrt(constant) * (1.0 + 64.0 * DBL_EPSILON); - if (upper > 0.0 && isfinite(upper)) - { - selected_mode = SOC_GRID_FIXED_ROOT; - trial = warm > lower && warm < upper && isfinite(warm) ? warm : 0.5 * upper; - } - else - { - selected_mode = SOC_GRID_FIXED_EXPAND; - trial = warm > 0.0 && isfinite(warm) ? warm : 1.0; - upper = trial; - } - } - } - else if (z >= 0.0 && fixed_norm2 + free_norm2 <= z * z) - { - selected_mode = SOC_GRID_IDENTITY; - } - else if (free_count == 0) - { - selected_mode = SOC_GRID_SCALAR_Z; - } - else if (fixed_norm2 == 0.0 && -omega_z_value * z >= sqrt(polar_norm2)) - { - selected_mode = SOC_GRID_APEX; - } - else if (z == 0.0) - { - selected_mode = SOC_GRID_ZERO_Z_EVAL; - trial = omega_z_value; - } - else if (z > 0.0) - { - selected_mode = SOC_GRID_FREE_ROOT; - lower = 0.0; - upper = omega_z_value * (1.0 - 1e-14); - trial = warm > lower && warm < upper && isfinite(warm) ? warm : 0.5 * (lower + upper); - } - else - { - lower = omega_z_value * (1.0 + 1e-14); - double endpoint_polar = -omega_z_value * z; - upper = cone_section_negative_soc_upper(omega_z_value, endpoint_polar, fixed_norm2, polar_norm2, max_omega); - if (upper > lower && isfinite(upper)) - { - selected_mode = SOC_GRID_FREE_ROOT; - trial = warm > lower && warm < upper && isfinite(warm) ? warm : 0.5 * (lower + upper); - } - else - { - selected_mode = SOC_GRID_FREE_EXPAND; - trial = warm > lower && isfinite(warm) ? warm : 2.0 * omega_z_value; - upper = trial; - } - } - - workspace[cone] = trial; - workspace[4 * num_cones + cone] = (double)selected_mode; - workspace[5 * num_cones + cone] = constant; - workspace[6 * num_cones + cone] = lower; - workspace[7 * num_cones + cone] = upper; -} - -__global__ void reduce_standard_soc_grid_weighted_root_kernel(const double *__restrict__ point, - const double *__restrict__ rescaling, - const double *__restrict__ q_diag, - double tau, - double *__restrict__ workspace, - const int *__restrict__ start_idx, - const int *__restrict__ v_dim, - const char *__restrict__ is_fixed, - int num_cones, - int blocks_per_cone) -{ - int cone = blockIdx.x / blocks_per_cone; - if (cone >= num_cones) - return; - int selected_mode = (int)workspace[4 * num_cones + cone]; - if (selected_mode < SOC_GRID_FIXED_EXPAND || selected_mode > SOC_GRID_ZERO_Z_EVAL) - return; - int part = blockIdx.x - cone * blocks_per_cone; - int start = start_idx[cone]; - int u_length = v_dim[cone] + 1; - double lambda_value = workspace[cone]; - double norm2 = 0.0; - double derivative = 0.0; - for (int slot = part * blockDim.x + threadIdx.x; slot < u_length; slot += blocks_per_cone * blockDim.x) - { - int index = start + slot; - if (is_fixed && is_fixed[index]) - continue; - double omega = cone_section_weight(rescaling, q_diag, tau, index); - double value = (point[index] / rescaling[index]) * omega / (omega + lambda_value); - norm2 += value * value; - derivative -= 2.0 * value * value / (omega + lambda_value); - } - __shared__ double scratch[96]; - double unused = 0.0; - cone_block_sum3(&norm2, &derivative, &unused, scratch); - if (threadIdx.x == 0) - { - atomicAdd(workspace + num_cones + cone, norm2); - atomicAdd(workspace + 2 * num_cones + cone, derivative); - } -} - -__global__ void finalize_standard_soc_grid_weighted_root_kernel(double *__restrict__ point, - const double *__restrict__ rescaling, - const double *__restrict__ q_diag, - double tau, - double *__restrict__ workspace, - const int *__restrict__ start_idx, - const int *__restrict__ v_dim, - int num_cones) -{ - int cone = blockIdx.x * blockDim.x + threadIdx.x; - if (cone >= num_cones) - return; - int selected_mode = (int)workspace[4 * num_cones + cone]; - if (selected_mode < SOC_GRID_FIXED_EXPAND || selected_mode > SOC_GRID_ZERO_Z_EVAL) - return; - int start = start_idx[cone]; - int z_index = start + v_dim[cone] + 1; - double lambda_value = workspace[cone]; - double sum = workspace[num_cones + cone]; - double derivative = workspace[2 * num_cones + cone]; - double constant = workspace[5 * num_cones + cone]; - double lower = workspace[6 * num_cones + cone]; - double upper = workspace[7 * num_cones + cone]; - - if (selected_mode == SOC_GRID_ZERO_Z_EVAL) - { - point[z_index] = sqrt(constant + sum) * rescaling[z_index]; - workspace[4 * num_cones + cone] = (double)SOC_GRID_ZERO_Z_APPLY; - return; - } - - double f; - if (selected_mode == SOC_GRID_FIXED_EXPAND || selected_mode == SOC_GRID_FIXED_ROOT) - { - f = sum - constant; - if (selected_mode == SOC_GRID_FIXED_EXPAND) - { - if (f > 0.0) - { - lower = lambda_value; - lambda_value *= 2.0; - } - else - { - upper = lambda_value; - selected_mode = SOC_GRID_FIXED_ROOT; - lambda_value = 0.5 * (lower + upper); - } - } - else - { - if (f > 0.0) - lower = lambda_value; - else - upper = lambda_value; - bool converged = fabs(f) <= 1e-13 * (1.0 + constant) || upper - lower <= 1e-13 * (1.0 + upper + lower); - if (converged) - selected_mode = SOC_GRID_FIXED_APPLY; - else - { - double next = lambda_value - f / derivative; - lambda_value = isfinite(next) && next > lower && next < upper ? next : 0.5 * (lower + upper); - } - } - } - else - { - double z_input = point[z_index] / rescaling[z_index]; - double omega_z_value = cone_section_weight(rescaling, q_diag, tau, z_index); - double z = omega_z_value * z_input / (omega_z_value - lambda_value); - f = constant + sum - z * z; - derivative -= 2.0 * z * z / (omega_z_value - lambda_value); - if (selected_mode == SOC_GRID_FREE_EXPAND) - { - if (f < 0.0) - { - lower = lambda_value; - lambda_value *= 2.0; - } - else - { - upper = lambda_value; - selected_mode = SOC_GRID_FREE_ROOT; - lambda_value = 0.5 * (lower + upper); - } - } - else - { - bool lower_branch_value = z_input > 0.0; - if ((lower_branch_value && f > 0.0) || (!lower_branch_value && f < 0.0)) - lower = lambda_value; - else - upper = lambda_value; - bool converged = - fabs(f) <= 1e-13 * (1.0 + constant + sum + z * z) || upper - lower <= 1e-13 * (1.0 + upper + lower); - if (converged) - selected_mode = SOC_GRID_FREE_APPLY; - else - { - double next = lambda_value - f / derivative; - lambda_value = isfinite(next) && next > lower && next < upper ? next : 0.5 * (lower + upper); - } - } - } - workspace[cone] = lambda_value; - workspace[4 * num_cones + cone] = (double)selected_mode; - workspace[6 * num_cones + cone] = lower; - workspace[7 * num_cones + cone] = upper; -} - -__global__ void apply_standard_soc_grid_weighted_kernel(double *__restrict__ point, - const double *__restrict__ rescaling, - const double *__restrict__ q_diag, - double tau, - const double *__restrict__ workspace, - const int *__restrict__ start_idx, - const int *__restrict__ v_dim, - const char *__restrict__ is_fixed, - int num_cones, - int blocks_per_cone) -{ - int cone = blockIdx.x / blocks_per_cone; - if (cone >= num_cones) - return; - int selected_mode = (int)workspace[4 * num_cones + cone]; - if (selected_mode == SOC_GRID_IDENTITY || selected_mode == SOC_GRID_SCALAR_Z) - { - if (selected_mode == SOC_GRID_SCALAR_Z && blockIdx.x % blocks_per_cone == 0 && threadIdx.x == 0) - { - int z_index = start_idx[cone] + v_dim[cone] + 1; - double z = point[z_index] / rescaling[z_index]; - point[z_index] = fmax(z, sqrt(workspace[5 * num_cones + cone])) * rescaling[z_index]; - } - return; - } - int part = blockIdx.x - cone * blocks_per_cone; - int start = start_idx[cone]; - int u_length = v_dim[cone] + 1; - if (selected_mode == SOC_GRID_ZERO_FREE || selected_mode == SOC_GRID_APEX) - { - for (int slot = part * blockDim.x + threadIdx.x; slot < u_length; slot += blocks_per_cone * blockDim.x) - if (!(is_fixed && is_fixed[start + slot])) - point[start + slot] = 0.0; - if (selected_mode == SOC_GRID_APEX && part == 0 && threadIdx.x == 0) - point[start + u_length] = 0.0; - return; - } - - double lambda_value = workspace[cone]; - for (int slot = part * blockDim.x + threadIdx.x; slot < u_length; slot += blocks_per_cone * blockDim.x) - { - int index = start + slot; - if (!(is_fixed && is_fixed[index])) - { - double omega = cone_section_weight(rescaling, q_diag, tau, index); - point[index] *= omega / (omega + lambda_value); - } - } - bool free_z_mode = selected_mode == SOC_GRID_FREE_EXPAND || selected_mode == SOC_GRID_FREE_ROOT || - selected_mode == SOC_GRID_FREE_APPLY; - if (free_z_mode && part == 0 && threadIdx.x == 0) - { - int z_index = start + u_length; - double omega_z_value = cone_section_weight(rescaling, q_diag, tau, z_index); - point[z_index] *= omega_z_value / (omega_z_value - lambda_value); - } -} - -enum rotated_soc_block_mode -{ - RSOC_BLOCK_IDENTITY = 0, - RSOC_BLOCK_ZERO_FREE = 1, - RSOC_BLOCK_FIXED_ENDPOINTS_ROOT = 2, - RSOC_BLOCK_ONE_ENDPOINT_ZERO = 3, - RSOC_BLOCK_ONE_ENDPOINT_SCALAR = 4, - RSOC_BLOCK_ONE_ENDPOINT_ROOT = 5, - RSOC_BLOCK_APEX = 6, - RSOC_BLOCK_BALANCED = 7, - RSOC_BLOCK_FREE_ROOT = 8, - RSOC_BLOCK_AXIS = 9 -}; - -__global__ void project_rotated_soc_block_kernel(double *__restrict__ point, - const double *__restrict__ rescaling, - const double *__restrict__ q_diag, - double tau, - double *__restrict__ warm_start, - const int *__restrict__ start_idx, - const int *__restrict__ v_dim, - const char *__restrict__ is_fixed, - int num_cones) -{ - int cone = blockIdx.x; - if (cone >= num_cones) - return; - - __shared__ double scratch[96]; - __shared__ double fixed_norm2; - __shared__ double radius2; - __shared__ double lambda; - __shared__ double lo; - __shared__ double hi; - __shared__ double s_input; - __shared__ double t_input; - __shared__ double omega_s; - __shared__ double omega_t; - __shared__ double projected_s; - __shared__ double projected_t; - __shared__ double free_objective; - __shared__ int mode; - __shared__ int lower_branch; - __shared__ int done; - - int start = start_idx[cone]; - int k = v_dim[cone]; - int s_index = start + k; - int t_index = s_index + 1; - bool fixed_s = is_fixed && is_fixed[s_index]; - bool fixed_t = is_fixed && is_fixed[t_index]; - - double local_fixed_norm2 = 0.0; - double local_free_norm2 = 0.0; - double local_polar_norm2 = 0.0; - double local_free_objective = 0.0; - double local_max_omega = 0.0; - for (int slot = threadIdx.x; slot < k; slot += blockDim.x) - { - int index = start + slot; - double value = point[index] / rescaling[index]; - if (is_fixed && is_fixed[index]) - local_fixed_norm2 += value * value; - else - { - double omega = cone_section_weight(rescaling, q_diag, tau, index); - local_free_norm2 += value * value; - local_polar_norm2 += (omega * value) * (omega * value); - local_free_objective += omega * value * value; - local_max_omega = fmax(local_max_omega, omega); - } - } - cone_block_sum3(&local_fixed_norm2, &local_free_norm2, &local_polar_norm2, scratch); - double unused = 0.0; - double unused2 = 0.0; - cone_block_sum3(&local_free_objective, &unused, &unused2, scratch); - local_max_omega = cone_block_max(local_max_omega, scratch); - - if (threadIdx.x == 0) - { - fixed_norm2 = local_fixed_norm2; - free_objective = local_free_objective; - s_input = point[s_index] / rescaling[s_index]; - t_input = point[t_index] / rescaling[t_index]; - omega_s = cone_section_weight(rescaling, q_diag, tau, s_index); - omega_t = cone_section_weight(rescaling, q_diag, tau, t_index); - int free_count = 0; - for (int slot = 0; slot < k; ++slot) - free_count += !(is_fixed && is_fixed[start + slot]); - - if (fixed_s && fixed_t) - { - radius2 = fmax(0.0, 2.0 * s_input * t_input - fixed_norm2); - if (free_count == 0 || local_free_norm2 <= radius2) - mode = RSOC_BLOCK_IDENTITY; - else if (!(radius2 > 0.0)) - mode = RSOC_BLOCK_ZERO_FREE; - else - { - mode = RSOC_BLOCK_FIXED_ENDPOINTS_ROOT; - hi = sqrt(local_polar_norm2) / sqrt(radius2) * (1.0 + 64.0 * DBL_EPSILON); - } - } - else if (fixed_s || fixed_t) - { - double fixed_endpoint = fixed_s ? s_input : t_input; - double free_endpoint = fixed_s ? t_input : s_input; - if (!(fixed_endpoint > 0.0)) - mode = RSOC_BLOCK_ONE_ENDPOINT_ZERO; - else if (free_endpoint >= 0.0 && fixed_norm2 + local_free_norm2 <= 2.0 * fixed_endpoint * free_endpoint) - mode = RSOC_BLOCK_IDENTITY; - else if (free_count == 0) - mode = RSOC_BLOCK_ONE_ENDPOINT_SCALAR; - else - { - mode = RSOC_BLOCK_ONE_ENDPOINT_ROOT; - double metric = fixed_s ? omega_t : omega_s; - double violation = fixed_norm2 + local_free_norm2 - 2.0 * fixed_endpoint * free_endpoint; - hi = metric * violation / (2.0 * fixed_endpoint * fixed_endpoint); - hi *= 1.0 + 64.0 * DBL_EPSILON; - } - } - else if (s_input >= 0.0 && t_input >= 0.0 && fixed_norm2 + local_free_norm2 <= 2.0 * s_input * t_input) - { - mode = RSOC_BLOCK_IDENTITY; - } - else - { - double bs = omega_s * s_input; - double bt = omega_t * t_input; - if (fixed_norm2 == 0.0 && bs <= 0.0 && bt <= 0.0 && local_polar_norm2 <= 2.0 * bs * bt) - { - mode = RSOC_BLOCK_APEX; - } - else - { - double root_metric = sqrt(omega_s) * sqrt(omega_t); - double balance = sqrt(omega_s) * s_input + sqrt(omega_t) * t_input; - double balance_scale = 1.0 + fabs(sqrt(omega_s) * s_input) + fabs(sqrt(omega_t) * t_input); - lambda = root_metric; - if (fabs(balance) <= 64.0 * DBL_EPSILON * balance_scale) - mode = RSOC_BLOCK_BALANCED; - else - { - mode = RSOC_BLOCK_FREE_ROOT; - lower_branch = balance > 0.0; - lo = lower_branch ? 0.0 : root_metric * (1.0 + 1e-14); - if (lower_branch) - { - hi = root_metric * (1.0 - 1e-14); - } - else - { - hi = cone_section_negative_rsoc_upper( - omega_s, omega_t, s_input, t_input, fixed_norm2, local_polar_norm2, local_max_omega); - } - } - } - } - } - __syncthreads(); - - if (mode == RSOC_BLOCK_IDENTITY) - return; - if (mode == RSOC_BLOCK_ZERO_FREE || mode == RSOC_BLOCK_APEX) - { - for (int slot = threadIdx.x; slot < k; slot += blockDim.x) - if (!(is_fixed && is_fixed[start + slot])) - point[start + slot] = 0.0; - if (mode == RSOC_BLOCK_APEX && threadIdx.x == 0) - { - point[s_index] = 0.0; - point[t_index] = 0.0; - } - return; - } - if (mode == RSOC_BLOCK_ONE_ENDPOINT_ZERO) - { - for (int slot = threadIdx.x; slot < k; slot += blockDim.x) - if (!(is_fixed && is_fixed[start + slot])) - point[start + slot] = 0.0; - if (threadIdx.x == 0) - { - if (fixed_s) - point[t_index] = fmax(t_input, 0.0) * rescaling[t_index]; - else - point[s_index] = fmax(s_input, 0.0) * rescaling[s_index]; - } - return; - } - if (mode == RSOC_BLOCK_ONE_ENDPOINT_SCALAR) - { - if (threadIdx.x == 0) - { - double fixed_endpoint = fixed_s ? s_input : t_input; - double free_endpoint = fixed_s ? t_input : s_input; - double projected = fmax(free_endpoint, fixed_norm2 / (2.0 * fixed_endpoint)); - if (fixed_s) - point[t_index] = projected * rescaling[t_index]; - else - point[s_index] = projected * rescaling[s_index]; - } - return; - } - - if (mode == RSOC_BLOCK_FIXED_ENDPOINTS_ROOT || mode == RSOC_BLOCK_ONE_ENDPOINT_ROOT) - { - if (threadIdx.x == 0) - { - lo = 0.0; - double metric = mode == RSOC_BLOCK_ONE_ENDPOINT_ROOT ? (fixed_s ? omega_t : omega_s) : 1.0; - done = hi > 0.0 && isfinite(hi); - if (!done) - hi = warm_start && warm_start[cone] > 0.0 && isfinite(warm_start[cone]) ? warm_start[cone] : metric; - } - __syncthreads(); - for (int expansion = 0; expansion < 80; ++expansion) - { - if (done) - break; - double norm2 = 0.0; - double dummy = 0.0; - double dummy2 = 0.0; - for (int slot = threadIdx.x; slot < k; slot += blockDim.x) - { - int index = start + slot; - if (is_fixed && is_fixed[index]) - continue; - double omega = cone_section_weight(rescaling, q_diag, tau, index); - double value = (point[index] / rescaling[index]) * omega / (omega + hi); - norm2 += value * value; - } - cone_block_sum3(&norm2, &dummy, &dummy2, scratch); - if (threadIdx.x == 0) - { - if (mode == RSOC_BLOCK_FIXED_ENDPOINTS_ROOT) - done = norm2 <= radius2; - else - { - double fixed_endpoint = fixed_s ? s_input : t_input; - double free_endpoint = fixed_s ? t_input : s_input; - double omega_endpoint = fixed_s ? omega_t : omega_s; - double endpoint = free_endpoint + hi * fixed_endpoint / omega_endpoint; - done = fixed_norm2 + norm2 <= 2.0 * fixed_endpoint * endpoint; - } - if (!done) - hi *= 2.0; - } - __syncthreads(); - if (done) - break; - } - __syncthreads(); - - if (threadIdx.x == 0) - { - double warm = warm_start ? warm_start[cone] : 0.0; - lambda = warm > lo && warm < hi && isfinite(warm) ? warm : 0.5 * (lo + hi); - done = 0; - } - __syncthreads(); - for (int iteration = 0; iteration < 30; ++iteration) - { - double norm2 = 0.0; - double derivative = 0.0; - double dummy = 0.0; - for (int slot = threadIdx.x; slot < k; slot += blockDim.x) - { - int index = start + slot; - if (is_fixed && is_fixed[index]) - continue; - double omega = cone_section_weight(rescaling, q_diag, tau, index); - double value = (point[index] / rescaling[index]) * omega / (omega + lambda); - norm2 += value * value; - derivative -= 2.0 * value * value / (omega + lambda); - } - cone_block_sum3(&norm2, &derivative, &dummy, scratch); - if (threadIdx.x == 0) - { - double target; - double f; - if (mode == RSOC_BLOCK_FIXED_ENDPOINTS_ROOT) - { - target = radius2; - f = norm2 - target; - } - else - { - double fixed_endpoint = fixed_s ? s_input : t_input; - double free_endpoint = fixed_s ? t_input : s_input; - double omega_endpoint = fixed_s ? omega_t : omega_s; - double endpoint = free_endpoint + lambda * fixed_endpoint / omega_endpoint; - target = 2.0 * fixed_endpoint * endpoint; - f = fixed_norm2 + norm2 - target; - derivative -= 2.0 * fixed_endpoint * fixed_endpoint / omega_endpoint; - } - if (f > 0.0) - lo = lambda; - else - hi = lambda; - double next = lambda - f / derivative; - if (!isfinite(next) || !(next > lo && next < hi)) - next = 0.5 * (lo + hi); - done = fabs(f) <= 1e-13 * (1.0 + target) || hi - lo <= 1e-13 * (1.0 + hi + lo); - if (!done) - lambda = next; - } - __syncthreads(); - if (done) - break; - } - - if (threadIdx.x == 0 && mode == RSOC_BLOCK_ONE_ENDPOINT_ROOT) - { - double fixed_endpoint = fixed_s ? s_input : t_input; - double free_endpoint = fixed_s ? t_input : s_input; - double omega_endpoint = fixed_s ? omega_t : omega_s; - double projected = free_endpoint + lambda * fixed_endpoint / omega_endpoint; - if (fixed_s) - point[t_index] = projected * rescaling[t_index]; - else - point[s_index] = projected * rescaling[s_index]; - } - } - else if (mode == RSOC_BLOCK_BALANCED) - { - double norm2 = 0.0; - double dummy = 0.0; - double dummy2 = 0.0; - for (int slot = threadIdx.x; slot < k; slot += blockDim.x) - { - int index = start + slot; - if (is_fixed && is_fixed[index]) - continue; - double omega = cone_section_weight(rescaling, q_diag, tau, index); - double value = (point[index] / rescaling[index]) * omega / (omega + lambda); - norm2 += value * value; - } - cone_block_sum3(&norm2, &dummy, &dummy2, scratch); - if (threadIdx.x == 0) - { - double root_metric = sqrt(omega_s) * sqrt(omega_t); - double product = 0.5 * root_metric * (fixed_norm2 + norm2); - double delta = sqrt(omega_s) * s_input; - double scaled_t = 0.5 * (-delta + sqrt(fmax(0.0, delta * delta + 4.0 * product))); - double scaled_s = scaled_t + delta; - projected_s = scaled_s / sqrt(omega_s); - projected_t = scaled_t / sqrt(omega_t); - } - __syncthreads(); - } - else - { - if (!lower_branch) - { - if (threadIdx.x == 0) - { - done = hi > lo && isfinite(hi); - if (!done) - hi = 2.0 * sqrt(omega_s) * sqrt(omega_t); - } - __syncthreads(); - for (int expansion = 0; expansion < 80; ++expansion) - { - if (done) - break; - double norm2 = 0.0; - double dummy = 0.0; - double dummy2 = 0.0; - for (int slot = threadIdx.x; slot < k; slot += blockDim.x) - { - int index = start + slot; - if (is_fixed && is_fixed[index]) - continue; - double omega = cone_section_weight(rescaling, q_diag, tau, index); - double value = (point[index] / rescaling[index]) * omega / (omega + hi); - norm2 += value * value; - } - cone_block_sum3(&norm2, &dummy, &dummy2, scratch); - if (threadIdx.x == 0) - { - double determinant = omega_s * omega_t - hi * hi; - double s = omega_t * (omega_s * s_input + hi * t_input) / determinant; - double t = omega_s * (omega_t * t_input + hi * s_input) / determinant; - double f = (s >= 0.0 && t >= 0.0) ? fixed_norm2 + norm2 - 2.0 * s * t : INFINITY; - done = f >= 0.0; - if (!done) - hi *= 2.0; - } - __syncthreads(); - if (done) - break; - } - } - __syncthreads(); - - if (threadIdx.x == 0) - { - double warm = warm_start ? warm_start[cone] : 0.0; - lambda = warm > lo && warm < hi && isfinite(warm) ? warm : 0.5 * (lo + hi); - done = 0; - } - __syncthreads(); - for (int iteration = 0; iteration < 40; ++iteration) - { - double norm2 = 0.0; - double derivative = 0.0; - double dummy = 0.0; - for (int slot = threadIdx.x; slot < k; slot += blockDim.x) - { - int index = start + slot; - if (is_fixed && is_fixed[index]) - continue; - double omega = cone_section_weight(rescaling, q_diag, tau, index); - double value = (point[index] / rescaling[index]) * omega / (omega + lambda); - norm2 += value * value; - derivative -= 2.0 * value * value / (omega + lambda); - } - cone_block_sum3(&norm2, &derivative, &dummy, scratch); - if (threadIdx.x == 0) - { - double determinant = omega_s * omega_t - lambda * lambda; - double s = omega_t * (omega_s * s_input + lambda * t_input) / determinant; - double t = omega_s * (omega_t * t_input + lambda * s_input) / determinant; - double f = INFINITY; - if (s >= 0.0 && t >= 0.0) - { - f = fixed_norm2 + norm2 - 2.0 * s * t; - double ds = (omega_t * t + lambda * s) / determinant; - double dt = (lambda * t + omega_s * s) / determinant; - derivative -= 2.0 * (ds * t + s * dt); - } - if ((lower_branch && f > 0.0) || (!lower_branch && f < 0.0)) - lo = lambda; - else - hi = lambda; - double next = lambda - f / derivative; - if (!isfinite(next) || !(next > lo && next < hi)) - next = 0.5 * (lo + hi); - done = isfinite(f) && - (fabs(f) <= 1e-13 * (1.0 + fixed_norm2 + norm2 + 2.0 * s * t) || - hi - lo <= 1e-13 * (1.0 + hi + lo)); - if (!done) - lambda = next; - } - __syncthreads(); - if (done) - break; - } - if (threadIdx.x == 0) - { - double determinant = omega_s * omega_t - lambda * lambda; - projected_s = omega_t * (omega_s * s_input + lambda * t_input) / determinant; - projected_t = omega_s * (omega_t * t_input + lambda * s_input) / determinant; - } - __syncthreads(); - } - - if (mode == RSOC_BLOCK_BALANCED || mode == RSOC_BLOCK_FREE_ROOT) - { - double smooth_vector_objective = 0.0; - double dummy = 0.0; - double dummy2 = 0.0; - for (int slot = threadIdx.x; slot < k; slot += blockDim.x) - { - int index = start + slot; - if (is_fixed && is_fixed[index]) - continue; - double omega = cone_section_weight(rescaling, q_diag, tau, index); - double input = point[index] / rescaling[index]; - double value = input * omega / (omega + lambda); - double delta = value - input; - smooth_vector_objective += omega * delta * delta; - } - cone_block_sum3(&smooth_vector_objective, &dummy, &dummy2, scratch); - if (threadIdx.x == 0 && fixed_norm2 == 0.0) - { - double smooth_objective = smooth_vector_objective + - omega_s * (projected_s - s_input) * (projected_s - s_input) + - omega_t * (projected_t - t_input) * (projected_t - t_input); - double s_axis = fmax(s_input, 0.0); - double s_axis_objective = - free_objective + omega_s * (s_axis - s_input) * (s_axis - s_input) + omega_t * t_input * t_input; - double t_axis = fmax(t_input, 0.0); - double t_axis_objective = - free_objective + omega_s * s_input * s_input + omega_t * (t_axis - t_input) * (t_axis - t_input); - if (s_axis_objective < smooth_objective && s_axis_objective <= t_axis_objective) - { - projected_s = s_axis; - projected_t = 0.0; - mode = RSOC_BLOCK_AXIS; - } - else if (t_axis_objective < smooth_objective) - { - projected_s = 0.0; - projected_t = t_axis; - mode = RSOC_BLOCK_AXIS; - } - } - __syncthreads(); - } - - if (mode == RSOC_BLOCK_AXIS) - { - for (int slot = threadIdx.x; slot < k; slot += blockDim.x) - if (!(is_fixed && is_fixed[start + slot])) - point[start + slot] = 0.0; - } - else - { - for (int slot = threadIdx.x; slot < k; slot += blockDim.x) - { - int index = start + slot; - if (!(is_fixed && is_fixed[index])) - { - double omega = cone_section_weight(rescaling, q_diag, tau, index); - point[index] *= omega / (omega + lambda); - } - } - if (warm_start && threadIdx.x == 0) - warm_start[cone] = lambda; - } - if (threadIdx.x == 0 && (mode == RSOC_BLOCK_BALANCED || mode == RSOC_BLOCK_FREE_ROOT || mode == RSOC_BLOCK_AXIS)) - { - point[s_index] = projected_s * rescaling[s_index]; - point[t_index] = projected_t * rescaling[t_index]; - } -} - -enum rotated_soc_grid_weighted_mode -{ - RSOC_GRID_IDENTITY = 0, - RSOC_GRID_ZERO_FREE = 1, - RSOC_GRID_ONE_ENDPOINT_ZERO = 2, - RSOC_GRID_ONE_ENDPOINT_SCALAR = 3, - RSOC_GRID_APEX = 4, - RSOC_GRID_FIXED_EXPAND = 5, - RSOC_GRID_FIXED_ROOT = 6, - RSOC_GRID_FIXED_APPLY = 7, - RSOC_GRID_ONE_EXPAND = 8, - RSOC_GRID_ONE_ROOT = 9, - RSOC_GRID_ONE_APPLY = 10, - RSOC_GRID_BALANCED_EVAL = 11, - RSOC_GRID_FREE_EXPAND = 12, - RSOC_GRID_FREE_ROOT = 13, - RSOC_GRID_FREE_APPLY = 14, - RSOC_GRID_BALANCED_APPLY = 15, - RSOC_GRID_AXIS = 16 -}; - -__device__ static inline void rotated_soc_grid_free_endpoints(const double *point, - const double *rescaling, - const double *q_diag, - double tau, - int s_index, - int t_index, - double lambda, - double *projected_s, - double *projected_t) -{ - double s = point[s_index] / rescaling[s_index]; - double t = point[t_index] / rescaling[t_index]; - double omega_s = cone_section_weight(rescaling, q_diag, tau, s_index); - double omega_t = cone_section_weight(rescaling, q_diag, tau, t_index); - double determinant = omega_s * omega_t - lambda * lambda; - *projected_s = omega_t * (omega_s * s + lambda * t) / determinant; - *projected_t = omega_s * (omega_t * t + lambda * s) / determinant; -} - -__global__ void initialize_rotated_soc_grid_weighted_kernel(const double *__restrict__ point, - const double *__restrict__ rescaling, - const double *__restrict__ q_diag, - double tau, - double *__restrict__ workspace, - const int *__restrict__ start_idx, - const int *__restrict__ v_dim, - const char *__restrict__ is_fixed, - int num_cones, - int blocks_per_cone) -{ - int cone = blockIdx.x / blocks_per_cone; - if (cone >= num_cones) - return; - int part = blockIdx.x - cone * blocks_per_cone; - int start = start_idx[cone]; - int k = v_dim[cone]; - double fixed_norm2 = 0.0; - double free_norm2 = 0.0; - double polar_norm2 = 0.0; - double free_count = 0.0; - double max_omega = 0.0; - for (int slot = part * blockDim.x + threadIdx.x; slot < k; slot += blocks_per_cone * blockDim.x) - { - int index = start + slot; - double value = point[index] / rescaling[index]; - if (is_fixed && is_fixed[index]) - fixed_norm2 += value * value; - else - { - double omega = cone_section_weight(rescaling, q_diag, tau, index); - free_norm2 += value * value; - polar_norm2 += (omega * value) * (omega * value); - free_count += 1.0; - max_omega = fmax(max_omega, omega); - } - } - __shared__ double scratch[96]; - cone_block_sum3(&fixed_norm2, &free_norm2, &polar_norm2, scratch); - double unused = 0.0; - double unused2 = 0.0; - cone_block_sum3(&free_count, &unused, &unused2, scratch); - max_omega = cone_block_max(max_omega, scratch); - if (threadIdx.x == 0) - { - atomicAdd(workspace + num_cones + cone, fixed_norm2); - atomicAdd(workspace + 2 * num_cones + cone, free_norm2); - atomicAdd(workspace + 3 * num_cones + cone, polar_norm2); - atomicAdd(workspace + 4 * num_cones + cone, free_count); - cone_atomic_max_positive(workspace + 5 * num_cones + cone, max_omega); - } -} - -__global__ void finalize_rotated_soc_grid_weighted_initialization_kernel(const double *__restrict__ point, - const double *__restrict__ rescaling, - const double *__restrict__ q_diag, - double tau, - double *__restrict__ workspace, - const int *__restrict__ start_idx, - const int *__restrict__ v_dim, - const char *__restrict__ is_fixed, - int num_cones) -{ - int cone = blockIdx.x * blockDim.x + threadIdx.x; - if (cone >= num_cones) - return; - int start = start_idx[cone]; - int k = v_dim[cone]; - int s_index = start + k; - int t_index = s_index + 1; - bool fixed_s = is_fixed && is_fixed[s_index]; - bool fixed_t = is_fixed && is_fixed[t_index]; - double warm = workspace[cone]; - double fixed_norm2 = workspace[num_cones + cone]; - double free_norm2 = workspace[2 * num_cones + cone]; - double polar_norm2 = workspace[3 * num_cones + cone]; - int free_count = (int)workspace[4 * num_cones + cone]; - double max_omega = workspace[5 * num_cones + cone]; - double s = point[s_index] / rescaling[s_index]; - double t = point[t_index] / rescaling[t_index]; - double omega_s_value = cone_section_weight(rescaling, q_diag, tau, s_index); - double omega_t_value = cone_section_weight(rescaling, q_diag, tau, t_index); - int selected_mode; - double constant = fixed_norm2; - double lower = 0.0; - double upper = 0.0; - double trial = warm; - - if (fixed_s && fixed_t) - { - constant = fmax(0.0, 2.0 * s * t - fixed_norm2); - if (free_count == 0 || free_norm2 <= constant) - selected_mode = RSOC_GRID_IDENTITY; - else if (!(constant > 0.0)) - selected_mode = RSOC_GRID_ZERO_FREE; - else - { - upper = sqrt(polar_norm2) / sqrt(constant) * (1.0 + 64.0 * DBL_EPSILON); - if (upper > 0.0 && isfinite(upper)) - { - selected_mode = RSOC_GRID_FIXED_ROOT; - trial = warm > 0.0 && warm < upper && isfinite(warm) ? warm : 0.5 * upper; - } - else - { - selected_mode = RSOC_GRID_FIXED_EXPAND; - trial = warm > 0.0 && isfinite(warm) ? warm : 1.0; - upper = trial; - } - } - } - else if (fixed_s || fixed_t) - { - double fixed_endpoint = fixed_s ? s : t; - double free_endpoint = fixed_s ? t : s; - if (!(fixed_endpoint > 0.0)) - selected_mode = RSOC_GRID_ONE_ENDPOINT_ZERO; - else if (free_endpoint >= 0.0 && fixed_norm2 + free_norm2 <= 2.0 * fixed_endpoint * free_endpoint) - selected_mode = RSOC_GRID_IDENTITY; - else if (free_count == 0) - selected_mode = RSOC_GRID_ONE_ENDPOINT_SCALAR; - else - { - double metric = fixed_s ? omega_t_value : omega_s_value; - double violation = fixed_norm2 + free_norm2 - 2.0 * fixed_endpoint * free_endpoint; - upper = metric * violation / (2.0 * fixed_endpoint * fixed_endpoint); - upper *= 1.0 + 64.0 * DBL_EPSILON; - if (upper > 0.0 && isfinite(upper)) - { - selected_mode = RSOC_GRID_ONE_ROOT; - trial = warm > 0.0 && warm < upper && isfinite(warm) ? warm : 0.5 * upper; - } - else - { - selected_mode = RSOC_GRID_ONE_EXPAND; - trial = warm > 0.0 && isfinite(warm) ? warm : metric; - upper = trial; - } - } - } - else if (s >= 0.0 && t >= 0.0 && fixed_norm2 + free_norm2 <= 2.0 * s * t) - { - selected_mode = RSOC_GRID_IDENTITY; - } - else - { - double bs = omega_s_value * s; - double bt = omega_t_value * t; - if (fixed_norm2 == 0.0 && bs <= 0.0 && bt <= 0.0 && polar_norm2 <= 2.0 * bs * bt) - { - selected_mode = RSOC_GRID_APEX; - } - else - { - double sqrt_omega_s = sqrt(omega_s_value); - double sqrt_omega_t = sqrt(omega_t_value); - double root_metric = sqrt_omega_s * sqrt_omega_t; - double scaled_s = sqrt_omega_s * s; - double scaled_t = sqrt_omega_t * t; - double balance = scaled_s + scaled_t; - double balance_scale = 1.0 + fabs(scaled_s) + fabs(scaled_t); - if (fabs(balance) <= 64.0 * DBL_EPSILON * balance_scale) - { - selected_mode = RSOC_GRID_BALANCED_EVAL; - trial = root_metric; - } - else if (balance > 0.0) - { - selected_mode = RSOC_GRID_FREE_ROOT; - lower = 0.0; - upper = root_metric * (1.0 - 1e-14); - trial = warm > lower && warm < upper && isfinite(warm) ? warm : 0.5 * (lower + upper); - } - else - { - lower = root_metric * (1.0 + 1e-14); - upper = cone_section_negative_rsoc_upper( - omega_s_value, omega_t_value, s, t, fixed_norm2, polar_norm2, max_omega); - if (upper > lower && isfinite(upper)) - { - selected_mode = RSOC_GRID_FREE_ROOT; - trial = warm > lower && warm < upper && isfinite(warm) ? warm : 0.5 * (lower + upper); - } - else - { - selected_mode = RSOC_GRID_FREE_EXPAND; - trial = warm > lower && isfinite(warm) ? warm : 2.0 * root_metric; - upper = trial; - } - } - } - } - - workspace[cone] = trial; - workspace[4 * num_cones + cone] = (double)selected_mode; - workspace[5 * num_cones + cone] = constant; - workspace[6 * num_cones + cone] = lower; - workspace[7 * num_cones + cone] = upper; -} - -__global__ void reduce_rotated_soc_grid_weighted_root_kernel(const double *__restrict__ point, - const double *__restrict__ rescaling, - const double *__restrict__ q_diag, - double tau, - double *__restrict__ workspace, - const int *__restrict__ start_idx, - const int *__restrict__ v_dim, - const char *__restrict__ is_fixed, - int num_cones, - int blocks_per_cone) -{ - int cone = blockIdx.x / blocks_per_cone; - if (cone >= num_cones) - return; - int selected_mode = (int)workspace[4 * num_cones + cone]; - bool active = selected_mode == RSOC_GRID_FIXED_EXPAND || selected_mode == RSOC_GRID_FIXED_ROOT || - selected_mode == RSOC_GRID_ONE_EXPAND || selected_mode == RSOC_GRID_ONE_ROOT || - selected_mode == RSOC_GRID_BALANCED_EVAL || selected_mode == RSOC_GRID_FREE_EXPAND || - selected_mode == RSOC_GRID_FREE_ROOT; - if (!active) - return; - int part = blockIdx.x - cone * blocks_per_cone; - int start = start_idx[cone]; - int k = v_dim[cone]; - double lambda_value = workspace[cone]; - double norm2 = 0.0; - double derivative = 0.0; - for (int slot = part * blockDim.x + threadIdx.x; slot < k; slot += blocks_per_cone * blockDim.x) - { - int index = start + slot; - if (is_fixed && is_fixed[index]) - continue; - double omega = cone_section_weight(rescaling, q_diag, tau, index); - double value = (point[index] / rescaling[index]) * omega / (omega + lambda_value); - norm2 += value * value; - derivative -= 2.0 * value * value / (omega + lambda_value); - } - __shared__ double scratch[96]; - double unused = 0.0; - cone_block_sum3(&norm2, &derivative, &unused, scratch); - if (threadIdx.x == 0) - { - atomicAdd(workspace + num_cones + cone, norm2); - atomicAdd(workspace + 2 * num_cones + cone, derivative); - } -} - -__global__ void finalize_rotated_soc_grid_weighted_root_kernel(const double *__restrict__ point, - const double *__restrict__ rescaling, - const double *__restrict__ q_diag, - double tau, - double *__restrict__ workspace, - const int *__restrict__ start_idx, - const int *__restrict__ v_dim, - const char *__restrict__ is_fixed, - int num_cones) -{ - int cone = blockIdx.x * blockDim.x + threadIdx.x; - if (cone >= num_cones) - return; - int selected_mode = (int)workspace[4 * num_cones + cone]; - bool active = selected_mode == RSOC_GRID_FIXED_EXPAND || selected_mode == RSOC_GRID_FIXED_ROOT || - selected_mode == RSOC_GRID_ONE_EXPAND || selected_mode == RSOC_GRID_ONE_ROOT || - selected_mode == RSOC_GRID_BALANCED_EVAL || selected_mode == RSOC_GRID_FREE_EXPAND || - selected_mode == RSOC_GRID_FREE_ROOT; - if (!active) - return; - int start = start_idx[cone]; - int k = v_dim[cone]; - int s_index = start + k; - int t_index = s_index + 1; - bool fixed_s = is_fixed && is_fixed[s_index]; - double s_input_value = point[s_index] / rescaling[s_index]; - double t_input_value = point[t_index] / rescaling[t_index]; - double omega_s_value = cone_section_weight(rescaling, q_diag, tau, s_index); - double omega_t_value = cone_section_weight(rescaling, q_diag, tau, t_index); - double lambda_value = workspace[cone]; - double sum = workspace[num_cones + cone]; - double derivative = workspace[2 * num_cones + cone]; - double constant = workspace[5 * num_cones + cone]; - double lower = workspace[6 * num_cones + cone]; - double upper = workspace[7 * num_cones + cone]; - double f = 0.0; - - if (selected_mode == RSOC_GRID_FIXED_EXPAND || selected_mode == RSOC_GRID_FIXED_ROOT) - { - f = sum - constant; - if (selected_mode == RSOC_GRID_FIXED_EXPAND) - { - if (f > 0.0) - { - lower = lambda_value; - lambda_value *= 2.0; - } - else - { - upper = lambda_value; - selected_mode = RSOC_GRID_FIXED_ROOT; - lambda_value = 0.5 * (lower + upper); - } - } - else - { - if (f > 0.0) - lower = lambda_value; - else - upper = lambda_value; - bool converged = fabs(f) <= 1e-13 * (1.0 + constant) || upper - lower <= 1e-13 * (1.0 + upper + lower); - if (converged) - selected_mode = RSOC_GRID_FIXED_APPLY; - else - { - double next = lambda_value - f / derivative; - lambda_value = isfinite(next) && next > lower && next < upper ? next : 0.5 * (lower + upper); - } - } - } - else if (selected_mode == RSOC_GRID_ONE_EXPAND || selected_mode == RSOC_GRID_ONE_ROOT) - { - double fixed_endpoint = fixed_s ? s_input_value : t_input_value; - double free_endpoint = fixed_s ? t_input_value : s_input_value; - double omega_endpoint = fixed_s ? omega_t_value : omega_s_value; - double projected_endpoint = free_endpoint + lambda_value * fixed_endpoint / omega_endpoint; - f = constant + sum - 2.0 * fixed_endpoint * projected_endpoint; - derivative -= 2.0 * fixed_endpoint * fixed_endpoint / omega_endpoint; - if (selected_mode == RSOC_GRID_ONE_EXPAND) - { - if (f > 0.0) - { - lower = lambda_value; - lambda_value *= 2.0; - } - else - { - upper = lambda_value; - selected_mode = RSOC_GRID_ONE_ROOT; - lambda_value = 0.5 * (lower + upper); - } - } - else - { - if (f > 0.0) - lower = lambda_value; - else - upper = lambda_value; - bool converged = - fabs(f) <= 1e-13 * (1.0 + constant + sum) || upper - lower <= 1e-13 * (1.0 + upper + lower); - if (converged) - selected_mode = RSOC_GRID_ONE_APPLY; - else - { - double next = lambda_value - f / derivative; - lambda_value = isfinite(next) && next > lower && next < upper ? next : 0.5 * (lower + upper); - } - } - } - else if (selected_mode == RSOC_GRID_BALANCED_EVAL) - { - double root_metric = sqrt(omega_s_value) * sqrt(omega_t_value); - double product = 0.5 * root_metric * (constant + sum); - double delta = sqrt(omega_s_value) * s_input_value; - double scaled_t = 0.5 * (-delta + sqrt(fmax(0.0, delta * delta + 4.0 * product))); - workspace[6 * num_cones + cone] = (scaled_t + delta) / sqrt(omega_s_value); - workspace[7 * num_cones + cone] = scaled_t / sqrt(omega_t_value); - selected_mode = RSOC_GRID_BALANCED_APPLY; - } - else - { - double determinant = omega_s_value * omega_t_value - lambda_value * lambda_value; - double projected_s_value; - double projected_t_value; - rotated_soc_grid_free_endpoints( - point, rescaling, q_diag, tau, s_index, t_index, lambda_value, &projected_s_value, &projected_t_value); - f = projected_s_value >= 0.0 && projected_t_value >= 0.0 - ? constant + sum - 2.0 * projected_s_value * projected_t_value - : INFINITY; - if (isfinite(f)) - { - double ds = (omega_t_value * projected_t_value + lambda_value * projected_s_value) / determinant; - double dt = (lambda_value * projected_t_value + omega_s_value * projected_s_value) / determinant; - derivative -= 2.0 * (ds * projected_t_value + projected_s_value * dt); - } - if (selected_mode == RSOC_GRID_FREE_EXPAND) - { - if (f < 0.0) - { - lower = lambda_value; - lambda_value *= 2.0; - } - else - { - upper = lambda_value; - selected_mode = RSOC_GRID_FREE_ROOT; - lambda_value = 0.5 * (lower + upper); - } - } - else - { - bool lower_branch_value = sqrt(omega_s_value) * s_input_value + sqrt(omega_t_value) * t_input_value > 0.0; - if ((lower_branch_value && f > 0.0) || (!lower_branch_value && f < 0.0)) - lower = lambda_value; - else - upper = lambda_value; - bool converged = isfinite(f) && - (fabs(f) <= 1e-13 * (1.0 + constant + sum + 2.0 * projected_s_value * projected_t_value) || - upper - lower <= 1e-13 * (1.0 + upper + lower)); - if (converged) - selected_mode = RSOC_GRID_FREE_APPLY; - else - { - double next = lambda_value - f / derivative; - lambda_value = isfinite(next) && next > lower && next < upper ? next : 0.5 * (lower + upper); - } - } - } - - workspace[cone] = lambda_value; - workspace[4 * num_cones + cone] = (double)selected_mode; - if (selected_mode != RSOC_GRID_BALANCED_APPLY) - { - workspace[6 * num_cones + cone] = lower; - workspace[7 * num_cones + cone] = upper; - } -} - -__global__ void reduce_rotated_soc_grid_axis_objective_kernel(const double *__restrict__ point, - const double *__restrict__ rescaling, - const double *__restrict__ q_diag, - double tau, - double *__restrict__ workspace, - const int *__restrict__ start_idx, - const int *__restrict__ v_dim, - const char *__restrict__ is_fixed, - int num_cones, - int blocks_per_cone) -{ - int cone = blockIdx.x / blocks_per_cone; - if (cone >= num_cones || workspace[5 * num_cones + cone] != 0.0) - return; - int selected_mode = (int)workspace[4 * num_cones + cone]; - if (selected_mode != RSOC_GRID_FREE_EXPAND && selected_mode != RSOC_GRID_FREE_ROOT && - selected_mode != RSOC_GRID_FREE_APPLY && selected_mode != RSOC_GRID_BALANCED_APPLY) - return; - int part = blockIdx.x - cone * blocks_per_cone; - int start = start_idx[cone]; - int k = v_dim[cone]; - double lambda_value = workspace[cone]; - double smooth_objective = 0.0; - double axis_objective = 0.0; - for (int slot = part * blockDim.x + threadIdx.x; slot < k; slot += blocks_per_cone * blockDim.x) - { - int index = start + slot; - if (is_fixed && is_fixed[index]) - continue; - double omega = cone_section_weight(rescaling, q_diag, tau, index); - double input = point[index] / rescaling[index]; - double projected = input * omega / (omega + lambda_value); - double delta = projected - input; - smooth_objective += omega * delta * delta; - axis_objective += omega * input * input; - } - __shared__ double scratch[96]; - double unused = 0.0; - cone_block_sum3(&smooth_objective, &axis_objective, &unused, scratch); - if (threadIdx.x == 0) - { - atomicAdd(workspace + num_cones + cone, smooth_objective); - atomicAdd(workspace + 2 * num_cones + cone, axis_objective); - } -} - -__global__ void finalize_rotated_soc_grid_axis_objective_kernel(const double *__restrict__ point, - const double *__restrict__ rescaling, - const double *__restrict__ q_diag, - double tau, - double *__restrict__ workspace, - const int *__restrict__ start_idx, - const int *__restrict__ v_dim, - int num_cones) -{ - int cone = blockIdx.x * blockDim.x + threadIdx.x; - if (cone >= num_cones || workspace[5 * num_cones + cone] != 0.0) - return; - int selected_mode = (int)workspace[4 * num_cones + cone]; - if (selected_mode != RSOC_GRID_FREE_EXPAND && selected_mode != RSOC_GRID_FREE_ROOT && - selected_mode != RSOC_GRID_FREE_APPLY && selected_mode != RSOC_GRID_BALANCED_APPLY) - return; - int start = start_idx[cone]; - int k = v_dim[cone]; - int s_index = start + k; - int t_index = s_index + 1; - double s_input_value = point[s_index] / rescaling[s_index]; - double t_input_value = point[t_index] / rescaling[t_index]; - double omega_s_value = cone_section_weight(rescaling, q_diag, tau, s_index); - double omega_t_value = cone_section_weight(rescaling, q_diag, tau, t_index); - double projected_s_value; - double projected_t_value; - if (selected_mode == RSOC_GRID_BALANCED_APPLY) - { - projected_s_value = workspace[6 * num_cones + cone]; - projected_t_value = workspace[7 * num_cones + cone]; - } - else - { - rotated_soc_grid_free_endpoints( - point, rescaling, q_diag, tau, s_index, t_index, workspace[cone], &projected_s_value, &projected_t_value); - } - double smooth_objective = workspace[num_cones + cone] + - omega_s_value * (projected_s_value - s_input_value) * (projected_s_value - s_input_value) + - omega_t_value * (projected_t_value - t_input_value) * (projected_t_value - t_input_value); - double vector_axis_objective = workspace[2 * num_cones + cone]; - double s_axis = fmax(s_input_value, 0.0); - double s_axis_objective = vector_axis_objective + - omega_s_value * (s_axis - s_input_value) * (s_axis - s_input_value) + - omega_t_value * t_input_value * t_input_value; - double t_axis = fmax(t_input_value, 0.0); - double t_axis_objective = vector_axis_objective + omega_s_value * s_input_value * s_input_value + - omega_t_value * (t_axis - t_input_value) * (t_axis - t_input_value); - if (s_axis_objective < smooth_objective && s_axis_objective <= t_axis_objective) - { - workspace[6 * num_cones + cone] = s_axis; - workspace[7 * num_cones + cone] = 0.0; - workspace[4 * num_cones + cone] = (double)RSOC_GRID_AXIS; - } - else if (t_axis_objective < smooth_objective) - { - workspace[6 * num_cones + cone] = 0.0; - workspace[7 * num_cones + cone] = t_axis; - workspace[4 * num_cones + cone] = (double)RSOC_GRID_AXIS; - } -} - -__global__ void apply_rotated_soc_grid_weighted_kernel(double *__restrict__ point, - const double *__restrict__ rescaling, - const double *__restrict__ q_diag, - double tau, - const double *__restrict__ workspace, - const int *__restrict__ start_idx, - const int *__restrict__ v_dim, - const char *__restrict__ is_fixed, - int num_cones, - int blocks_per_cone) -{ - int cone = blockIdx.x / blocks_per_cone; - if (cone >= num_cones) - return; - int selected_mode = (int)workspace[4 * num_cones + cone]; - if (selected_mode == RSOC_GRID_IDENTITY) - return; - int part = blockIdx.x - cone * blocks_per_cone; - int start = start_idx[cone]; - int k = v_dim[cone]; - int s_index = start + k; - int t_index = s_index + 1; - bool fixed_s = is_fixed && is_fixed[s_index]; - bool fixed_t = is_fixed && is_fixed[t_index]; - - bool zero_vector = selected_mode == RSOC_GRID_ZERO_FREE || selected_mode == RSOC_GRID_ONE_ENDPOINT_ZERO || - selected_mode == RSOC_GRID_APEX || selected_mode == RSOC_GRID_AXIS; - if (zero_vector) - { - for (int slot = part * blockDim.x + threadIdx.x; slot < k; slot += blocks_per_cone * blockDim.x) - if (!(is_fixed && is_fixed[start + slot])) - point[start + slot] = 0.0; - } - else if (selected_mode != RSOC_GRID_ONE_ENDPOINT_SCALAR) - { - double lambda_value = workspace[cone]; - for (int slot = part * blockDim.x + threadIdx.x; slot < k; slot += blocks_per_cone * blockDim.x) - { - int index = start + slot; - if (!(is_fixed && is_fixed[index])) - { - double omega = cone_section_weight(rescaling, q_diag, tau, index); - point[index] *= omega / (omega + lambda_value); - } - } - } - - if (part == 0 && threadIdx.x == 0) - { - if (selected_mode == RSOC_GRID_ONE_ENDPOINT_ZERO) - { - if (fixed_s) - point[t_index] = fmax(point[t_index] / rescaling[t_index], 0.0) * rescaling[t_index]; - else - point[s_index] = fmax(point[s_index] / rescaling[s_index], 0.0) * rescaling[s_index]; - } - else if (selected_mode == RSOC_GRID_ONE_ENDPOINT_SCALAR) - { - double fixed_endpoint = fixed_s ? point[s_index] / rescaling[s_index] : point[t_index] / rescaling[t_index]; - int free_index = fixed_s ? t_index : s_index; - double input = point[free_index] / rescaling[free_index]; - point[free_index] = - fmax(input, workspace[5 * num_cones + cone] / (2.0 * fixed_endpoint)) * rescaling[free_index]; - } - else if (selected_mode == RSOC_GRID_APEX) - { - point[s_index] = 0.0; - point[t_index] = 0.0; - } - else if (selected_mode == RSOC_GRID_ONE_EXPAND || selected_mode == RSOC_GRID_ONE_ROOT || - selected_mode == RSOC_GRID_ONE_APPLY) - { - double lambda_value = workspace[cone]; - double fixed_endpoint = fixed_s ? point[s_index] / rescaling[s_index] : point[t_index] / rescaling[t_index]; - int free_index = fixed_s ? t_index : s_index; - double input = point[free_index] / rescaling[free_index]; - double omega = cone_section_weight(rescaling, q_diag, tau, free_index); - point[free_index] = (input + lambda_value * fixed_endpoint / omega) * rescaling[free_index]; - } - else if (selected_mode == RSOC_GRID_BALANCED_APPLY || selected_mode == RSOC_GRID_AXIS) - { - if (!fixed_s) - point[s_index] = workspace[6 * num_cones + cone] * rescaling[s_index]; - if (!fixed_t) - point[t_index] = workspace[7 * num_cones + cone] * rescaling[t_index]; - } - else if (selected_mode == RSOC_GRID_FREE_EXPAND || selected_mode == RSOC_GRID_FREE_ROOT || - selected_mode == RSOC_GRID_FREE_APPLY) - { - double projected_s; - double projected_t; - rotated_soc_grid_free_endpoints( - point, rescaling, q_diag, tau, s_index, t_index, workspace[cone], &projected_s, &projected_t); - point[s_index] = projected_s * rescaling[s_index]; - point[t_index] = projected_t * rescaling[t_index]; - } - } -} - -__global__ void recompute_reflected_at_cone_block_kernel(double *__restrict__ reflected_primal, - const double *__restrict__ pdhg_primal, - const double *__restrict__ current_primal, - const int *__restrict__ start_idx, - const int *__restrict__ v_dim, - int num_cones) -{ - int cone = blockIdx.x; - if (cone >= num_cones) - return; - int start = start_idx[cone]; - int length = v_dim[cone] + 2; - for (int slot = threadIdx.x; slot < length; slot += blockDim.x) - { - int index = start + slot; - reflected_primal[index] = 2.0 * pdhg_primal[index] - current_primal[index]; - } -} - -__global__ void clear_cone_residual_grid_kernel(double *__restrict__ dual_residual, - const int *__restrict__ start_idx, - const int *__restrict__ v_dim, - int num_cones, - int blocks_per_cone) -{ - int cone = blockIdx.x / blocks_per_cone; - if (cone >= num_cones) - return; - int part = blockIdx.x - cone * blocks_per_cone; - int start = start_idx[cone]; - int length = v_dim[cone] + 2; - for (int slot = part * blockDim.x + threadIdx.x; slot < length; slot += blocks_per_cone * blockDim.x) - dual_residual[start + slot] = 0.0; -} diff --git a/src/kernels/pdhcg_conic_kernels.cu b/src/kernels/pdhcg_conic_kernels.cu deleted file mode 100644 index e31dea0..0000000 --- a/src/kernels/pdhcg_conic_kernels.cu +++ /dev/null @@ -1,4627 +0,0 @@ -/* -Copyright 2026 Hongpei Li - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -#include "cone_section_projection.cuh" -#include "pdhcg_kernels.cuh" -#include -#include -#include - -__global__ void project_rotated_soc_kernel(double *__restrict__ primal_solution, - const double *__restrict__ variable_rescaling, - double *__restrict__ warm_start, - const int *__restrict__ start_idx, - const int *__restrict__ v_dim, - const char *__restrict__ is_fixed, - int num_blocks) -{ - int blk = blockIdx.x * blockDim.x + threadIdx.x; - if (blk >= num_blocks) - return; - - const double INV_SQRT2 = 0.7071067811865475; - - int start = start_idx[blk]; - int k = v_dim[blk]; - if (cone_section_has_fixed(is_fixed, start, k + 2)) - { - project_rotated_soc_section_serial( - primal_solution, variable_rescaling, NULL, 0.0, warm_start + blk, start, k, is_fixed); - return; - } - double *v = primal_solution + start; - double *sptr = primal_solution + start + k; - double *tptr = primal_solution + start + k + 1; - - double s = *sptr; - double t = *tptr; - - double w = (s - t) * INV_SQRT2; - double z = (s + t) * INV_SQRT2; - - double d_s = variable_rescaling[start + k]; - double d_t = variable_rescaling[start + k + 1]; - double d_st = sqrt(d_s * d_t); - - bool diag_uniform = true; - for (int m = 0; m < k && diag_uniform; ++m) - { - if (variable_rescaling[start + m] != d_st) - diag_uniform = false; - } - if (diag_uniform) - { - double sumsq = w * w; - for (int m = 0; m < k; ++m) - sumsq += v[m] * v[m]; - double r = sqrt(sumsq); - if (r <= z) - return; - if (r <= -z) - { - for (int m = 0; m < k; ++m) - v[m] = 0.0; - *sptr = 0.0; - *tptr = 0.0; - return; - } - double scale = (z + r) / (2.0 * r); - for (int m = 0; m < k; ++m) - v[m] *= scale; - double w_new = scale * w; - double z_new = scale * r; - *sptr = (z_new + w_new) * INV_SQRT2; - *tptr = (z_new - w_new) * INV_SQRT2; - return; - } - - double r_inv_sq = w * w; - double r_pos_sq = w * w; - for (int m = 0; m < k; ++m) - { - double dh = variable_rescaling[start + m] / d_st; - double v_m = v[m]; - r_inv_sq += (v_m / dh) * (v_m / dh); - r_pos_sq += (v_m * dh) * (v_m * dh); - } - double r_inv = sqrt(r_inv_sq); - if (r_inv <= z) - return; - double r_pos = sqrt(r_pos_sq); - if (r_pos <= -z) - { - for (int m = 0; m < k; ++m) - v[m] = 0.0; - *sptr = 0.0; - *tptr = 0.0; - return; - } - - double lo, hi; - bool z_pos = (z > 0.0); - if (z_pos) - { - lo = 0.0; - hi = 0.5 - 1e-14; - } - else - { - lo = 0.5 + 1e-14; - hi = 1.0; - for (int doubling = 0; doubling < 60; ++doubling) - { - double sum_hi = 0.0; - for (int m = 0; m < k; ++m) - { - double dh = variable_rescaling[start + m] / d_st; - double dh2 = dh * dh; - double tt = v[m] * dh / (dh2 + 2.0 * hi); - sum_hi += tt * tt; - } - double tw_hi = w / (1.0 + 2.0 * hi); - sum_hi += tw_hi * tw_hi; - double zt_hi = z / (1.0 - 2.0 * hi); - double f_hi = sum_hi - zt_hi * zt_hi; - if (f_hi > 0.0) - break; - lo = hi; - hi *= 2.0; - } - } - - double warm_lam = warm_start[blk]; - if (warm_lam > lo && warm_lam < hi) - { - double sum_w = 0.0; - for (int m = 0; m < k; ++m) - { - double dh = variable_rescaling[start + m] / d_st; - double dh2 = dh * dh; - double tt = v[m] * dh / (dh2 + 2.0 * warm_lam); - sum_w += tt * tt; - } - double tw = w / (1.0 + 2.0 * warm_lam); - sum_w += tw * tw; - double zt = z / (1.0 - 2.0 * warm_lam); - double f = sum_w - zt * zt; - if (fabs(f) < 1e-12) - { - double w_new = w / (1.0 + 2.0 * warm_lam); - double z_new = z / (1.0 - 2.0 * warm_lam); - for (int m = 0; m < k; ++m) - { - double dh = variable_rescaling[start + m] / d_st; - double dh2 = dh * dh; - v[m] = v[m] * dh2 / (dh2 + 2.0 * warm_lam); - } - *sptr = (z_new + w_new) * INV_SQRT2; - *tptr = (z_new - w_new) * INV_SQRT2; - return; - } - if (z_pos) - { - if (f > 0.0) - lo = warm_lam; - else - hi = warm_lam; - } - else - { - if (f > 0.0) - hi = warm_lam; - else - lo = warm_lam; - } - } - - for (int it = 0; it < 60; ++it) - { - double lam = 0.5 * (lo + hi); - double sum = 0.0; - for (int m = 0; m < k; ++m) - { - double dh = variable_rescaling[start + m] / d_st; - double dh2 = dh * dh; - double tt = v[m] * dh / (dh2 + 2.0 * lam); - sum += tt * tt; - } - double tw = w / (1.0 + 2.0 * lam); - sum += tw * tw; - double zt = z / (1.0 - 2.0 * lam); - double f = sum - zt * zt; - if (z_pos) - { - if (f > 0.0) - lo = lam; - else - hi = lam; - } - else - { - if (f > 0.0) - hi = lam; - else - lo = lam; - } - if ((hi - lo) / (1.0 + hi + lo) < 1e-13) - break; - } - double lam = 0.5 * (lo + hi); - warm_start[blk] = lam; - - double w_new = w / (1.0 + 2.0 * lam); - double z_new = z / (1.0 - 2.0 * lam); - for (int m = 0; m < k; ++m) - { - double dh = variable_rescaling[start + m] / d_st; - double dh2 = dh * dh; - v[m] = v[m] * dh2 / (dh2 + 2.0 * lam); - } - *sptr = (z_new + w_new) * INV_SQRT2; - *tptr = (z_new - w_new) * INV_SQRT2; -} - -__global__ void compute_cone_dual_residual_kernel(double *__restrict__ dual_residual, - double *__restrict__ complementarity_residual, - const double *__restrict__ objective_vector, - const double *__restrict__ dual_product, - const double *__restrict__ variable_rescaling, - const double *__restrict__ primal_solution, - double *__restrict__ warm_start, - const int *__restrict__ start_idx, - const int *__restrict__ v_dim, - const char *__restrict__ is_fixed, - int num_blocks) -{ - int blk = blockIdx.x * blockDim.x + threadIdx.x; - if (blk >= num_blocks) - return; - - const double INV_SQRT2 = 0.7071067811865475; - int start = start_idx[blk]; - int k = v_dim[blk]; - - if (cone_section_has_fixed(is_fixed, start, k + 2)) - { - for (int slot = 0; slot < k + 2; ++slot) - { - int index = start + slot; - double residual = objective_vector[index] - dual_product[index]; - dual_residual[index] = is_fixed[index] ? primal_solution[index] : primal_solution[index] - residual; - } - project_rotated_soc_section_serial( - dual_residual, variable_rescaling, NULL, 0.0, warm_start + blk, start, k, is_fixed); - for (int slot = 0; slot < k + 2; ++slot) - { - int index = start + slot; - dual_residual[index] = - is_fixed[index] ? 0.0 : (primal_solution[index] - dual_residual[index]) * variable_rescaling[index]; - } - complementarity_residual[blk] = 0.0; - return; - } - - double r_s = objective_vector[start + k] - dual_product[start + k]; - double r_t = objective_vector[start + k + 1] - dual_product[start + k + 1]; - double r_w = (r_s - r_t) * INV_SQRT2; - double r_z = (r_s + r_t) * INV_SQRT2; - - double d_s = variable_rescaling[start + k]; - double d_t = variable_rescaling[start + k + 1]; - double d_st = sqrt(d_s * d_t); - - bool diag_uniform = true; - for (int m = 0; m < k && diag_uniform; ++m) - { - if (variable_rescaling[start + m] != d_st) - diag_uniform = false; - } - - if (diag_uniform) - { - double sumsq = r_w * r_w; - for (int m = 0; m < k; ++m) - { - double v_m = objective_vector[start + m] - dual_product[start + m]; - sumsq += v_m * v_m; - } - double r_norm = sqrt(sumsq); - - double v_factor, p_s, p_t; - if (r_norm <= r_z) - { - v_factor = 0.0; - p_s = r_s; - p_t = r_t; - } - else if (r_norm <= -r_z) - { - v_factor = 1.0; - p_s = 0.0; - p_t = 0.0; - } - else - { - double scale = (r_z + r_norm) / (2.0 * r_norm); - v_factor = 1.0 - scale; - double w_new = scale * r_w; - double z_new = scale * r_norm; - p_s = (z_new + w_new) * INV_SQRT2; - p_t = (z_new - w_new) * INV_SQRT2; - } - for (int m = 0; m < k; ++m) - { - double v_m = objective_vector[start + m] - dual_product[start + m]; - dual_residual[start + m] = v_m * v_factor * variable_rescaling[start + m]; - } - dual_residual[start + k] = (r_s - p_s) * variable_rescaling[start + k]; - dual_residual[start + k + 1] = (r_t - p_t) * variable_rescaling[start + k + 1]; - return; - } - - double r_inv_sq = r_w * r_w; - double r_pos_sq = r_w * r_w; - for (int m = 0; m < k; ++m) - { - double e_m = d_st / variable_rescaling[start + m]; - double rc_m = objective_vector[start + m] - dual_product[start + m]; - r_inv_sq += (rc_m / e_m) * (rc_m / e_m); - r_pos_sq += (rc_m * e_m) * (rc_m * e_m); - } - double r_inv = sqrt(r_inv_sq); - double r_pos = sqrt(r_pos_sq); - - if (r_inv <= r_z) - { - for (int m = 0; m < k; ++m) - dual_residual[start + m] = 0.0; - dual_residual[start + k] = 0.0; - dual_residual[start + k + 1] = 0.0; - return; - } - if (r_pos <= -r_z) - { - for (int m = 0; m < k; ++m) - { - double rc_m = objective_vector[start + m] - dual_product[start + m]; - dual_residual[start + m] = rc_m * variable_rescaling[start + m]; - } - dual_residual[start + k] = r_s * variable_rescaling[start + k]; - dual_residual[start + k + 1] = r_t * variable_rescaling[start + k + 1]; - return; - } - - double lo, hi; - bool z_pos = (r_z > 0.0); - if (z_pos) - { - lo = 0.0; - hi = 0.5 - 1e-14; - } - else - { - lo = 0.5 + 1e-14; - hi = 1.0; - for (int doubling = 0; doubling < 60; ++doubling) - { - double sum_hi = 0.0; - for (int m = 0; m < k; ++m) - { - double e_m = d_st / variable_rescaling[start + m]; - double e_m2 = e_m * e_m; - double rc_m = objective_vector[start + m] - dual_product[start + m]; - double tt = rc_m * e_m / (e_m2 + 2.0 * hi); - sum_hi += tt * tt; - } - double tw_hi = r_w / (1.0 + 2.0 * hi); - sum_hi += tw_hi * tw_hi; - double zt_hi = r_z / (1.0 - 2.0 * hi); - double f_hi = sum_hi - zt_hi * zt_hi; - if (f_hi > 0.0) - break; - lo = hi; - hi *= 2.0; - } - } - - double warm_lam = warm_start[blk]; - if (warm_lam > lo && warm_lam < hi) - { - double sum_w = 0.0; - for (int m = 0; m < k; ++m) - { - double e_m = d_st / variable_rescaling[start + m]; - double e_m2 = e_m * e_m; - double rc_m = objective_vector[start + m] - dual_product[start + m]; - double tt = rc_m * e_m / (e_m2 + 2.0 * warm_lam); - sum_w += tt * tt; - } - double tw = r_w / (1.0 + 2.0 * warm_lam); - sum_w += tw * tw; - double zt = r_z / (1.0 - 2.0 * warm_lam); - double f = sum_w - zt * zt; - if (fabs(f) < 1e-12) - { - double p_w_w = r_w / (1.0 + 2.0 * warm_lam); - double p_z_w = r_z / (1.0 - 2.0 * warm_lam); - double p_s_w = (p_z_w + p_w_w) * INV_SQRT2; - double p_t_w = (p_z_w - p_w_w) * INV_SQRT2; - for (int m = 0; m < k; ++m) - { - double e_m = d_st / variable_rescaling[start + m]; - double e_m2 = e_m * e_m; - double rc_m = objective_vector[start + m] - dual_product[start + m]; - double p_m = rc_m * e_m2 / (e_m2 + 2.0 * warm_lam); - dual_residual[start + m] = (rc_m - p_m) * variable_rescaling[start + m]; - } - dual_residual[start + k] = (r_s - p_s_w) * variable_rescaling[start + k]; - dual_residual[start + k + 1] = (r_t - p_t_w) * variable_rescaling[start + k + 1]; - return; - } - if (z_pos) - { - if (f > 0.0) - lo = warm_lam; - else - hi = warm_lam; - } - else - { - if (f > 0.0) - hi = warm_lam; - else - lo = warm_lam; - } - } - - for (int it = 0; it < 60; ++it) - { - double lam = 0.5 * (lo + hi); - double sum = 0.0; - for (int m = 0; m < k; ++m) - { - double e_m = d_st / variable_rescaling[start + m]; - double e_m2 = e_m * e_m; - double rc_m = objective_vector[start + m] - dual_product[start + m]; - double tt = rc_m * e_m / (e_m2 + 2.0 * lam); - sum += tt * tt; - } - double tw = r_w / (1.0 + 2.0 * lam); - sum += tw * tw; - double zt = r_z / (1.0 - 2.0 * lam); - double f = sum - zt * zt; - if (z_pos) - { - if (f > 0.0) - lo = lam; - else - hi = lam; - } - else - { - if (f > 0.0) - hi = lam; - else - lo = lam; - } - if ((hi - lo) / (1.0 + hi + lo) < 1e-13) - break; - } - double lam = 0.5 * (lo + hi); - warm_start[blk] = lam; - - double p_w = r_w / (1.0 + 2.0 * lam); - double p_z = r_z / (1.0 - 2.0 * lam); - double p_s = (p_z + p_w) * INV_SQRT2; - double p_t = (p_z - p_w) * INV_SQRT2; - - for (int m = 0; m < k; ++m) - { - double e_m = d_st / variable_rescaling[start + m]; - double e_m2 = e_m * e_m; - double rc_m = objective_vector[start + m] - dual_product[start + m]; - double p_m = rc_m * e_m2 / (e_m2 + 2.0 * lam); - dual_residual[start + m] = (rc_m - p_m) * variable_rescaling[start + m]; - } - dual_residual[start + k] = (r_s - p_s) * variable_rescaling[start + k]; - dual_residual[start + k + 1] = (r_t - p_t) * variable_rescaling[start + k + 1]; -} - -__global__ void project_standard_soc_kernel(double *__restrict__ primal_solution, - const double *__restrict__ variable_rescaling, - double *__restrict__ warm_start, - const int *__restrict__ start_idx, - const int *__restrict__ v_dim, - const char *__restrict__ is_fixed, - int num_blocks) -{ - int blk = blockIdx.x * blockDim.x + threadIdx.x; - if (blk >= num_blocks) - return; - - int start = start_idx[blk]; - int k = v_dim[blk]; - if (cone_section_has_fixed(is_fixed, start, k + 2)) - { - project_standard_soc_section_serial( - primal_solution, variable_rescaling, NULL, 0.0, warm_start + blk, start, k, is_fixed); - return; - } - double *v = primal_solution + start; - double *wptr = primal_solution + start + k; - double *zptr = primal_solution + start + k + 1; - - double w = *wptr; - double z = *zptr; - - double d_z = variable_rescaling[start + k + 1]; - double dhat_w = variable_rescaling[start + k] / d_z; - double dhat_w2 = dhat_w * dhat_w; - - bool diag_uniform = (dhat_w == 1.0); - for (int m = 0; m < k && diag_uniform; ++m) - { - if (variable_rescaling[start + m] != d_z) - diag_uniform = false; - } - - if (diag_uniform) - { - double sumsq = w * w; - for (int m = 0; m < k; ++m) - sumsq += v[m] * v[m]; - double r = sqrt(sumsq); - if (r <= z) - return; - if (r <= -z) - { - for (int m = 0; m < k; ++m) - v[m] = 0.0; - *wptr = 0.0; - *zptr = 0.0; - return; - } - double scale = (z + r) / (2.0 * r); - for (int m = 0; m < k; ++m) - v[m] *= scale; - *wptr = scale * w; - *zptr = scale * r; - return; - } - - double r_inv_sq = (w / dhat_w) * (w / dhat_w); - double r_pos_sq = (w * dhat_w) * (w * dhat_w); - for (int m = 0; m < k; ++m) - { - double dh = variable_rescaling[start + m] / d_z; - double v_m = v[m]; - r_inv_sq += (v_m / dh) * (v_m / dh); - r_pos_sq += (v_m * dh) * (v_m * dh); - } - double r_inv = sqrt(r_inv_sq); - if (r_inv <= z) - return; - double r_pos = sqrt(r_pos_sq); - if (r_pos <= -z) - { - for (int m = 0; m < k; ++m) - v[m] = 0.0; - *wptr = 0.0; - *zptr = 0.0; - return; - } - - double lo, hi; - bool z_pos = (z > 0.0); - if (z_pos) - { - lo = 0.0; - hi = 0.5 - 1e-14; - } - else - { - lo = 0.5 + 1e-14; - hi = 1.0; - for (int doubling = 0; doubling < 60; ++doubling) - { - double sum_hi = 0.0; - for (int m = 0; m < k; ++m) - { - double dh = variable_rescaling[start + m] / d_z; - double dh2 = dh * dh; - double t = v[m] * dh / (dh2 + 2.0 * hi); - sum_hi += t * t; - } - double tw_hi = w * dhat_w / (dhat_w2 + 2.0 * hi); - sum_hi += tw_hi * tw_hi; - double zt_hi = z / (1.0 - 2.0 * hi); - double f_hi = sum_hi - zt_hi * zt_hi; - if (f_hi > 0.0) - break; - lo = hi; - hi *= 2.0; - } - } - - double warm_lam = warm_start[blk]; - if (warm_lam > lo && warm_lam < hi) - { - double sum_w = 0.0; - for (int m = 0; m < k; ++m) - { - double dh = variable_rescaling[start + m] / d_z; - double dh2 = dh * dh; - double t = v[m] * dh / (dh2 + 2.0 * warm_lam); - sum_w += t * t; - } - double tw = w * dhat_w / (dhat_w2 + 2.0 * warm_lam); - sum_w += tw * tw; - double zt = z / (1.0 - 2.0 * warm_lam); - double f = sum_w - zt * zt; - if (fabs(f) < 1e-12) - { - *zptr = z / (1.0 - 2.0 * warm_lam); - *wptr = w * dhat_w2 / (dhat_w2 + 2.0 * warm_lam); - for (int m = 0; m < k; ++m) - { - double dh = variable_rescaling[start + m] / d_z; - double dh2 = dh * dh; - v[m] = v[m] * dh2 / (dh2 + 2.0 * warm_lam); - } - return; - } - if (z_pos) - { - if (f > 0.0) - lo = warm_lam; - else - hi = warm_lam; - } - else - { - if (f > 0.0) - hi = warm_lam; - else - lo = warm_lam; - } - } - - for (int it = 0; it < 60; ++it) - { - double lam = 0.5 * (lo + hi); - double sum = 0.0; - for (int m = 0; m < k; ++m) - { - double dh = variable_rescaling[start + m] / d_z; - double dh2 = dh * dh; - double t = v[m] * dh / (dh2 + 2.0 * lam); - sum += t * t; - } - double tw = w * dhat_w / (dhat_w2 + 2.0 * lam); - sum += tw * tw; - double zt = z / (1.0 - 2.0 * lam); - double f = sum - zt * zt; - if (z_pos) - { - if (f > 0.0) - lo = lam; - else - hi = lam; - } - else - { - if (f > 0.0) - hi = lam; - else - lo = lam; - } - if ((hi - lo) / (1.0 + hi + lo) < 1e-13) - break; - } - double lam = 0.5 * (lo + hi); - warm_start[blk] = lam; - - *zptr = z / (1.0 - 2.0 * lam); - *wptr = w * dhat_w2 / (dhat_w2 + 2.0 * lam); - for (int m = 0; m < k; ++m) - { - double dh = variable_rescaling[start + m] / d_z; - double dh2 = dh * dh; - v[m] = v[m] * dh2 / (dh2 + 2.0 * lam); - } -} - -__global__ void compute_cone_dual_residual_standard_kernel(double *__restrict__ dual_residual, - double *__restrict__ complementarity_residual, - const double *__restrict__ objective_vector, - const double *__restrict__ dual_product, - const double *__restrict__ variable_rescaling, - const double *__restrict__ primal_solution, - double *__restrict__ warm_start, - const int *__restrict__ start_idx, - const int *__restrict__ v_dim, - const char *__restrict__ is_fixed, - int num_blocks) -{ - int blk = blockIdx.x * blockDim.x + threadIdx.x; - if (blk >= num_blocks) - return; - - int start = start_idx[blk]; - int k = v_dim[blk]; - - if (cone_section_has_fixed(is_fixed, start, k + 2)) - { - for (int slot = 0; slot < k + 2; ++slot) - { - int index = start + slot; - double residual = objective_vector[index] - dual_product[index]; - dual_residual[index] = is_fixed[index] ? primal_solution[index] : primal_solution[index] - residual; - } - project_standard_soc_section_serial( - dual_residual, variable_rescaling, NULL, 0.0, warm_start + blk, start, k, is_fixed); - for (int slot = 0; slot < k + 2; ++slot) - { - int index = start + slot; - dual_residual[index] = - is_fixed[index] ? 0.0 : (primal_solution[index] - dual_residual[index]) * variable_rescaling[index]; - } - complementarity_residual[blk] = 0.0; - return; - } - - double r_w = objective_vector[start + k] - dual_product[start + k]; - double r_z = objective_vector[start + k + 1] - dual_product[start + k + 1]; - - double d_z = variable_rescaling[start + k + 1]; - double e_w = d_z / variable_rescaling[start + k]; - double e_w2 = e_w * e_w; - - bool diag_uniform = (e_w == 1.0); - for (int m = 0; m < k && diag_uniform; ++m) - { - if (variable_rescaling[start + m] != d_z) - diag_uniform = false; - } - - if (diag_uniform) - { - double sumsq = r_w * r_w; - for (int m = 0; m < k; ++m) - { - double rc_m = objective_vector[start + m] - dual_product[start + m]; - sumsq += rc_m * rc_m; - } - double r = sqrt(sumsq); - double v_factor, p_w, p_z; - if (r <= r_z) - { - v_factor = 0.0; - p_w = r_w; - p_z = r_z; - } - else if (r <= -r_z) - { - v_factor = 1.0; - p_w = 0.0; - p_z = 0.0; - } - else - { - double scale = (r_z + r) / (2.0 * r); - v_factor = 1.0 - scale; - p_w = scale * r_w; - p_z = scale * r; - } - for (int m = 0; m < k; ++m) - { - double rc_m = objective_vector[start + m] - dual_product[start + m]; - dual_residual[start + m] = rc_m * v_factor * variable_rescaling[start + m]; - } - dual_residual[start + k] = (r_w - p_w) * variable_rescaling[start + k]; - dual_residual[start + k + 1] = (r_z - p_z) * variable_rescaling[start + k + 1]; - return; - } - - double r_inv_sq = (r_w / e_w) * (r_w / e_w); - double r_pos_sq = (r_w * e_w) * (r_w * e_w); - for (int m = 0; m < k; ++m) - { - double e_m = d_z / variable_rescaling[start + m]; - double rc_m = objective_vector[start + m] - dual_product[start + m]; - r_inv_sq += (rc_m / e_m) * (rc_m / e_m); - r_pos_sq += (rc_m * e_m) * (rc_m * e_m); - } - double r_inv = sqrt(r_inv_sq); - double r_pos = sqrt(r_pos_sq); - - if (r_inv <= r_z) - { - for (int m = 0; m < k; ++m) - dual_residual[start + m] = 0.0; - dual_residual[start + k] = 0.0; - dual_residual[start + k + 1] = 0.0; - return; - } - if (r_pos <= -r_z) - { - for (int m = 0; m < k; ++m) - { - double rc_m = objective_vector[start + m] - dual_product[start + m]; - dual_residual[start + m] = rc_m * variable_rescaling[start + m]; - } - dual_residual[start + k] = r_w * variable_rescaling[start + k]; - dual_residual[start + k + 1] = r_z * variable_rescaling[start + k + 1]; - return; - } - - double lo, hi; - bool z_pos = (r_z > 0.0); - if (z_pos) - { - lo = 0.0; - hi = 0.5 - 1e-14; - } - else - { - lo = 0.5 + 1e-14; - hi = 1.0; - for (int doubling = 0; doubling < 60; ++doubling) - { - double sum_hi = 0.0; - for (int m = 0; m < k; ++m) - { - double e_m = d_z / variable_rescaling[start + m]; - double e_m2 = e_m * e_m; - double rc_m = objective_vector[start + m] - dual_product[start + m]; - double t = rc_m * e_m / (e_m2 + 2.0 * hi); - sum_hi += t * t; - } - double tw_hi = r_w * e_w / (e_w2 + 2.0 * hi); - sum_hi += tw_hi * tw_hi; - double zt_hi = r_z / (1.0 - 2.0 * hi); - double f_hi = sum_hi - zt_hi * zt_hi; - if (f_hi > 0.0) - break; - lo = hi; - hi *= 2.0; - } - } - - double warm_lam = warm_start[blk]; - if (warm_lam > lo && warm_lam < hi) - { - double sum_w = 0.0; - for (int m = 0; m < k; ++m) - { - double e_m = d_z / variable_rescaling[start + m]; - double e_m2 = e_m * e_m; - double rc_m = objective_vector[start + m] - dual_product[start + m]; - double t = rc_m * e_m / (e_m2 + 2.0 * warm_lam); - sum_w += t * t; - } - double tw = r_w * e_w / (e_w2 + 2.0 * warm_lam); - sum_w += tw * tw; - double zt = r_z / (1.0 - 2.0 * warm_lam); - double f = sum_w - zt * zt; - if (fabs(f) < 1e-12) - { - double p_z_w = r_z / (1.0 - 2.0 * warm_lam); - double p_w_w = r_w * e_w2 / (e_w2 + 2.0 * warm_lam); - for (int m = 0; m < k; ++m) - { - double e_m = d_z / variable_rescaling[start + m]; - double e_m2 = e_m * e_m; - double rc_m = objective_vector[start + m] - dual_product[start + m]; - double p_m = rc_m * e_m2 / (e_m2 + 2.0 * warm_lam); - dual_residual[start + m] = (rc_m - p_m) * variable_rescaling[start + m]; - } - dual_residual[start + k] = (r_w - p_w_w) * variable_rescaling[start + k]; - dual_residual[start + k + 1] = (r_z - p_z_w) * variable_rescaling[start + k + 1]; - return; - } - if (z_pos) - { - if (f > 0.0) - lo = warm_lam; - else - hi = warm_lam; - } - else - { - if (f > 0.0) - hi = warm_lam; - else - lo = warm_lam; - } - } - - for (int it = 0; it < 60; ++it) - { - double lam = 0.5 * (lo + hi); - double sum = 0.0; - for (int m = 0; m < k; ++m) - { - double e_m = d_z / variable_rescaling[start + m]; - double e_m2 = e_m * e_m; - double rc_m = objective_vector[start + m] - dual_product[start + m]; - double t = rc_m * e_m / (e_m2 + 2.0 * lam); - sum += t * t; - } - double tw = r_w * e_w / (e_w2 + 2.0 * lam); - sum += tw * tw; - double zt = r_z / (1.0 - 2.0 * lam); - double f = sum - zt * zt; - if (z_pos) - { - if (f > 0.0) - lo = lam; - else - hi = lam; - } - else - { - if (f > 0.0) - hi = lam; - else - lo = lam; - } - if ((hi - lo) / (1.0 + hi + lo) < 1e-13) - break; - } - double lam = 0.5 * (lo + hi); - warm_start[blk] = lam; - - double p_z = r_z / (1.0 - 2.0 * lam); - double p_w = r_w * e_w2 / (e_w2 + 2.0 * lam); - - for (int m = 0; m < k; ++m) - { - double e_m = d_z / variable_rescaling[start + m]; - double e_m2 = e_m * e_m; - double rc_m = objective_vector[start + m] - dual_product[start + m]; - double p_m = rc_m * e_m2 / (e_m2 + 2.0 * lam); - dual_residual[start + m] = (rc_m - p_m) * variable_rescaling[start + m]; - } - dual_residual[start + k] = (r_w - p_w) * variable_rescaling[start + k]; - dual_residual[start + k + 1] = (r_z - p_z) * variable_rescaling[start + k + 1]; -} - -static __device__ __forceinline__ double large_cone_block_sum(double value) -{ - __shared__ double warp_sums[32]; - const unsigned mask = 0xffffffffu; - const int lane = threadIdx.x & 31; - const int warp = threadIdx.x >> 5; - const int num_warps = (blockDim.x + 31) >> 5; - - for (int offset = 16; offset > 0; offset >>= 1) - value += __shfl_down_sync(mask, value, offset); - if (lane == 0) - warp_sums[warp] = value; - __syncthreads(); - - value = (warp == 0 && lane < num_warps) ? warp_sums[lane] : 0.0; - if (warp == 0) - { - for (int offset = 16; offset > 0; offset >>= 1) - value += __shfl_down_sync(mask, value, offset); - } - return value; -} - -__global__ void project_rotated_soc_grid_reduce_kernel(double *__restrict__ primal_solution, - double *__restrict__ workspace, - const int *__restrict__ start_idx, - const int *__restrict__ v_dim, - int num_cones, - int blocks_per_cone) -{ - int cone = blockIdx.x / blocks_per_cone; - int part = blockIdx.x - cone * blocks_per_cone; - if (cone >= num_cones) - return; - - int start = start_idx[cone]; - int k = v_dim[cone]; - double sum = 0.0; - for (int m = part * blockDim.x + threadIdx.x; m < k; m += blocks_per_cone * blockDim.x) - { - double value = primal_solution[start + m]; - sum += value * value; - } - sum = large_cone_block_sum(sum); - if (threadIdx.x == 0) - atomicAdd(workspace + cone, sum); -} - -__global__ void project_rotated_soc_grid_finalize_kernel(double *__restrict__ primal_solution, - double *__restrict__ workspace, - const int *__restrict__ start_idx, - const int *__restrict__ v_dim, - int num_cones) -{ - int cone = blockIdx.x * blockDim.x + threadIdx.x; - if (cone >= num_cones) - return; - - const double INV_SQRT2 = 0.7071067811865475; - int start = start_idx[cone]; - int k = v_dim[cone]; - double s = primal_solution[start + k]; - double t = primal_solution[start + k + 1]; - double w = (s - t) * INV_SQRT2; - double z = (s + t) * INV_SQRT2; - double radius = sqrt(fmax(0.0, workspace[cone] + w * w)); - - if (radius <= z) - { - workspace[cone] = 1.0; - return; - } - if (radius <= -z) - { - workspace[cone] = 0.0; - primal_solution[start + k] = 0.0; - primal_solution[start + k + 1] = 0.0; - return; - } - - double scale = (z + radius) / (2.0 * radius); - double w_new = scale * w; - double z_new = scale * radius; - workspace[cone] = scale; - primal_solution[start + k] = (z_new + w_new) * INV_SQRT2; - primal_solution[start + k + 1] = (z_new - w_new) * INV_SQRT2; -} - -__global__ void project_rotated_soc_grid_apply_kernel(double *__restrict__ primal_solution, - const double *__restrict__ workspace, - const int *__restrict__ start_idx, - const int *__restrict__ v_dim, - int num_cones, - int blocks_per_cone) -{ - int cone = blockIdx.x / blocks_per_cone; - int part = blockIdx.x - cone * blocks_per_cone; - if (cone >= num_cones) - return; - - double scale = workspace[cone]; - if (scale == 1.0) - return; - - int start = start_idx[cone]; - int k = v_dim[cone]; - for (int m = part * blockDim.x + threadIdx.x; m < k; m += blocks_per_cone * blockDim.x) - { - primal_solution[start + m] *= scale; - } -} - -__global__ void compute_cone_dual_residual_grid_reduce_kernel(const double *__restrict__ objective_vector, - const double *__restrict__ dual_product, - double *__restrict__ workspace, - const int *__restrict__ start_idx, - const int *__restrict__ v_dim, - int num_cones, - int blocks_per_cone) -{ - int cone = blockIdx.x / blocks_per_cone; - int part = blockIdx.x - cone * blocks_per_cone; - if (cone >= num_cones) - return; - - int start = start_idx[cone]; - int k = v_dim[cone]; - double sum = 0.0; - for (int m = part * blockDim.x + threadIdx.x; m < k; m += blocks_per_cone * blockDim.x) - { - double residual = objective_vector[start + m] - dual_product[start + m]; - sum += residual * residual; - } - sum = large_cone_block_sum(sum); - if (threadIdx.x == 0) - atomicAdd(workspace + cone, sum); -} - -__global__ void compute_cone_dual_residual_grid_finalize_kernel(double *__restrict__ dual_residual, - const double *__restrict__ objective_vector, - const double *__restrict__ dual_product, - const double *__restrict__ variable_rescaling, - double *__restrict__ workspace, - const int *__restrict__ start_idx, - const int *__restrict__ v_dim, - int num_cones) -{ - int cone = blockIdx.x * blockDim.x + threadIdx.x; - if (cone >= num_cones) - return; - - const double INV_SQRT2 = 0.7071067811865475; - int start = start_idx[cone]; - int k = v_dim[cone]; - double r_s = objective_vector[start + k] - dual_product[start + k]; - double r_t = objective_vector[start + k + 1] - dual_product[start + k + 1]; - double r_w = (r_s - r_t) * INV_SQRT2; - double r_z = (r_s + r_t) * INV_SQRT2; - double norm = sqrt(fmax(0.0, workspace[cone] + r_w * r_w)); - double factor; - double p_s; - double p_t; - - if (norm <= r_z) - { - factor = 0.0; - p_s = r_s; - p_t = r_t; - } - else if (norm <= -r_z) - { - factor = 1.0; - p_s = 0.0; - p_t = 0.0; - } - else - { - double scale = (r_z + norm) / (2.0 * norm); - double w_new = scale * r_w; - double z_new = scale * norm; - factor = 1.0 - scale; - p_s = (z_new + w_new) * INV_SQRT2; - p_t = (z_new - w_new) * INV_SQRT2; - } - - workspace[cone] = factor; - dual_residual[start + k] = (r_s - p_s) * variable_rescaling[start + k]; - dual_residual[start + k + 1] = (r_t - p_t) * variable_rescaling[start + k + 1]; -} - -__global__ void compute_cone_dual_residual_grid_apply_kernel(double *__restrict__ dual_residual, - const double *__restrict__ objective_vector, - const double *__restrict__ dual_product, - const double *__restrict__ variable_rescaling, - const double *__restrict__ workspace, - const int *__restrict__ start_idx, - const int *__restrict__ v_dim, - int num_cones, - int blocks_per_cone) -{ - int cone = blockIdx.x / blocks_per_cone; - int part = blockIdx.x - cone * blocks_per_cone; - if (cone >= num_cones) - return; - - double factor = workspace[cone]; - int start = start_idx[cone]; - int k = v_dim[cone]; - for (int m = part * blockDim.x + threadIdx.x; m < k; m += blocks_per_cone * blockDim.x) - { - int idx = start + m; - double residual = objective_vector[idx] - dual_product[idx]; - dual_residual[idx] = residual * factor * variable_rescaling[idx]; - } -} - -__global__ void project_standard_soc_grid_reduce_kernel(double *__restrict__ primal_solution, - double *__restrict__ workspace, - const int *__restrict__ start_idx, - const int *__restrict__ v_dim, - int num_cones, - int blocks_per_cone) -{ - int cone = blockIdx.x / blocks_per_cone; - int part = blockIdx.x - cone * blocks_per_cone; - if (cone >= num_cones) - return; - - int start = start_idx[cone]; - int k = v_dim[cone]; - double sum = 0.0; - for (int m = part * blockDim.x + threadIdx.x; m < k; m += blocks_per_cone * blockDim.x) - { - double value = primal_solution[start + m]; - sum += value * value; - } - sum = large_cone_block_sum(sum); - if (threadIdx.x == 0) - atomicAdd(workspace + cone, sum); -} - -__global__ void project_standard_soc_grid_finalize_kernel(double *__restrict__ primal_solution, - double *__restrict__ workspace, - const int *__restrict__ start_idx, - const int *__restrict__ v_dim, - int num_cones) -{ - int cone = blockIdx.x * blockDim.x + threadIdx.x; - if (cone >= num_cones) - return; - - int start = start_idx[cone]; - int k = v_dim[cone]; - double w = primal_solution[start + k]; - double z = primal_solution[start + k + 1]; - double radius = sqrt(fmax(0.0, workspace[cone] + w * w)); - - if (radius <= z) - { - workspace[cone] = 1.0; - return; - } - if (radius <= -z) - { - workspace[cone] = 0.0; - primal_solution[start + k] = 0.0; - primal_solution[start + k + 1] = 0.0; - return; - } - - double scale = (z + radius) / (2.0 * radius); - workspace[cone] = scale; - primal_solution[start + k] = scale * w; - primal_solution[start + k + 1] = scale * radius; -} - -__global__ void project_standard_soc_grid_apply_kernel(double *__restrict__ primal_solution, - const double *__restrict__ workspace, - const int *__restrict__ start_idx, - const int *__restrict__ v_dim, - int num_cones, - int blocks_per_cone) -{ - int cone = blockIdx.x / blocks_per_cone; - int part = blockIdx.x - cone * blocks_per_cone; - if (cone >= num_cones) - return; - - double scale = workspace[cone]; - if (scale == 1.0) - return; - - int start = start_idx[cone]; - int k = v_dim[cone]; - for (int m = part * blockDim.x + threadIdx.x; m < k; m += blocks_per_cone * blockDim.x) - { - primal_solution[start + m] *= scale; - } -} - -__global__ void compute_cone_dual_residual_standard_grid_reduce_kernel(const double *__restrict__ objective_vector, - const double *__restrict__ dual_product, - double *__restrict__ workspace, - const int *__restrict__ start_idx, - const int *__restrict__ v_dim, - int num_cones, - int blocks_per_cone) -{ - int cone = blockIdx.x / blocks_per_cone; - int part = blockIdx.x - cone * blocks_per_cone; - if (cone >= num_cones) - return; - - int start = start_idx[cone]; - int k = v_dim[cone]; - double sum = 0.0; - for (int m = part * blockDim.x + threadIdx.x; m < k; m += blocks_per_cone * blockDim.x) - { - double residual = objective_vector[start + m] - dual_product[start + m]; - sum += residual * residual; - } - sum = large_cone_block_sum(sum); - if (threadIdx.x == 0) - atomicAdd(workspace + cone, sum); -} - -__global__ void compute_cone_dual_residual_standard_grid_finalize_kernel(double *__restrict__ dual_residual, - const double *__restrict__ objective_vector, - const double *__restrict__ dual_product, - const double *__restrict__ variable_rescaling, - double *__restrict__ workspace, - const int *__restrict__ start_idx, - const int *__restrict__ v_dim, - int num_cones) -{ - int cone = blockIdx.x * blockDim.x + threadIdx.x; - if (cone >= num_cones) - return; - - int start = start_idx[cone]; - int k = v_dim[cone]; - double r_w = objective_vector[start + k] - dual_product[start + k]; - double r_z = objective_vector[start + k + 1] - dual_product[start + k + 1]; - double radius = sqrt(fmax(0.0, workspace[cone] + r_w * r_w)); - double factor; - double p_w; - double p_z; - - if (radius <= r_z) - { - factor = 0.0; - p_w = r_w; - p_z = r_z; - } - else if (radius <= -r_z) - { - factor = 1.0; - p_w = 0.0; - p_z = 0.0; - } - else - { - double scale = (r_z + radius) / (2.0 * radius); - factor = 1.0 - scale; - p_w = scale * r_w; - p_z = scale * radius; - } - - workspace[cone] = factor; - dual_residual[start + k] = (r_w - p_w) * variable_rescaling[start + k]; - dual_residual[start + k + 1] = (r_z - p_z) * variable_rescaling[start + k + 1]; -} - -__global__ void compute_cone_dual_residual_standard_grid_apply_kernel(double *__restrict__ dual_residual, - const double *__restrict__ objective_vector, - const double *__restrict__ dual_product, - const double *__restrict__ variable_rescaling, - const double *__restrict__ workspace, - const int *__restrict__ start_idx, - const int *__restrict__ v_dim, - int num_cones, - int blocks_per_cone) -{ - int cone = blockIdx.x / blocks_per_cone; - int part = blockIdx.x - cone * blocks_per_cone; - if (cone >= num_cones) - return; - - double factor = workspace[cone]; - int start = start_idx[cone]; - int k = v_dim[cone]; - for (int m = part * blockDim.x + threadIdx.x; m < k; m += blocks_per_cone * blockDim.x) - { - int idx = start + m; - double residual = objective_vector[idx] - dual_product[idx]; - dual_residual[idx] = residual * factor * variable_rescaling[idx]; - } -} - -__global__ void project_rotated_soc_warp_kernel(double *__restrict__ primal_solution, - const double *__restrict__ variable_rescaling, - double *__restrict__ warm_start, - const int *__restrict__ start_idx, - const int *__restrict__ v_dim, - const char *__restrict__ is_fixed, - int num_blocks) -{ - int tid = blockIdx.x * blockDim.x + threadIdx.x; - int blk = tid >> 5; - int lane = tid & 31; - if (blk >= num_blocks) - return; - - const double INV_SQRT2 = 0.7071067811865475; - const unsigned MASK = 0xffffffffu; - - int start = start_idx[blk]; - int k = v_dim[blk]; - - int has_fixed = lane == 0 ? cone_section_has_fixed(is_fixed, start, k + 2) : 0; - has_fixed = __shfl_sync(MASK, has_fixed, 0); - if (has_fixed) - { - if (lane == 0) - project_rotated_soc_section_serial( - primal_solution, variable_rescaling, NULL, 0.0, warm_start + blk, start, k, is_fixed); - return; - } - - double s_val = primal_solution[start + k]; - double t_val = primal_solution[start + k + 1]; - - double w = (s_val - t_val) * INV_SQRT2; - double z = (s_val + t_val) * INV_SQRT2; - - double d_s = variable_rescaling[start + k]; - double d_t = variable_rescaling[start + k + 1]; - double d_st = sqrt(d_s * d_t); - - int my_diff = 0; - for (int m = lane; m < k; m += 32) - { - if (variable_rescaling[start + m] != d_st) - my_diff = 1; - } - for (int o = 16; o > 0; o >>= 1) - my_diff |= __shfl_xor_sync(MASK, my_diff, o); - - if (my_diff == 0) - { - double my_sumsq = (lane == 0) ? w * w : 0.0; - for (int m = lane; m < k; m += 32) - { - double v_m = primal_solution[start + m]; - my_sumsq += v_m * v_m; - } - for (int o = 16; o > 0; o >>= 1) - my_sumsq += __shfl_xor_sync(MASK, my_sumsq, o); - double r = sqrt(my_sumsq); - if (r <= z) - return; - if (r <= -z) - { - for (int m = lane; m < k; m += 32) - primal_solution[start + m] = 0.0; - if (lane == 0) - { - primal_solution[start + k] = 0.0; - primal_solution[start + k + 1] = 0.0; - } - return; - } - double scale = (z + r) / (2.0 * r); - for (int m = lane; m < k; m += 32) - primal_solution[start + m] *= scale; - double w_new = scale * w; - double z_new = scale * r; - if (lane == 0) - { - primal_solution[start + k] = (z_new + w_new) * INV_SQRT2; - primal_solution[start + k + 1] = (z_new - w_new) * INV_SQRT2; - } - return; - } - - double my_inv = (lane == 0) ? w * w : 0.0; - double my_pos = (lane == 0) ? w * w : 0.0; - for (int m = lane; m < k; m += 32) - { - double dh = variable_rescaling[start + m] / d_st; - double v_m = primal_solution[start + m]; - my_inv += (v_m / dh) * (v_m / dh); - my_pos += (v_m * dh) * (v_m * dh); - } - for (int o = 16; o > 0; o >>= 1) - { - my_inv += __shfl_xor_sync(MASK, my_inv, o); - my_pos += __shfl_xor_sync(MASK, my_pos, o); - } - double r_inv = sqrt(my_inv); - if (r_inv <= z) - return; - double r_pos = sqrt(my_pos); - if (r_pos <= -z) - { - for (int m = lane; m < k; m += 32) - primal_solution[start + m] = 0.0; - if (lane == 0) - { - primal_solution[start + k] = 0.0; - primal_solution[start + k + 1] = 0.0; - } - return; - } - - double lo, hi; - bool z_pos = (z > 0.0); - if (z_pos) - { - lo = 0.0; - hi = 0.5 - 1e-14; - } - else - { - lo = 0.5 + 1e-14; - hi = 1.0; - for (int doubling = 0; doubling < 60; ++doubling) - { - double my_sum = (lane == 0) ? (w / (1.0 + 2.0 * hi)) * (w / (1.0 + 2.0 * hi)) : 0.0; - for (int m = lane; m < k; m += 32) - { - double dh = variable_rescaling[start + m] / d_st; - double dh2 = dh * dh; - double tt = primal_solution[start + m] * dh / (dh2 + 2.0 * hi); - my_sum += tt * tt; - } - for (int o = 16; o > 0; o >>= 1) - my_sum += __shfl_xor_sync(MASK, my_sum, o); - double zt_hi = z / (1.0 - 2.0 * hi); - double f_hi = my_sum - zt_hi * zt_hi; - if (f_hi > 0.0) - break; - lo = hi; - hi *= 2.0; - } - } - - double warm_lam = warm_start[blk]; - if (warm_lam > lo && warm_lam < hi) - { - double my_sum = (lane == 0) ? (w / (1.0 + 2.0 * warm_lam)) * (w / (1.0 + 2.0 * warm_lam)) : 0.0; - for (int m = lane; m < k; m += 32) - { - double dh = variable_rescaling[start + m] / d_st; - double dh2 = dh * dh; - double tt = primal_solution[start + m] * dh / (dh2 + 2.0 * warm_lam); - my_sum += tt * tt; - } - for (int o = 16; o > 0; o >>= 1) - my_sum += __shfl_xor_sync(MASK, my_sum, o); - double zt = z / (1.0 - 2.0 * warm_lam); - double f = my_sum - zt * zt; - if (fabs(f) < 1e-12) - { - double w_new = w / (1.0 + 2.0 * warm_lam); - double z_new = z / (1.0 - 2.0 * warm_lam); - for (int m = lane; m < k; m += 32) - { - double dh = variable_rescaling[start + m] / d_st; - double dh2 = dh * dh; - primal_solution[start + m] = primal_solution[start + m] * dh2 / (dh2 + 2.0 * warm_lam); - } - if (lane == 0) - { - primal_solution[start + k] = (z_new + w_new) * INV_SQRT2; - primal_solution[start + k + 1] = (z_new - w_new) * INV_SQRT2; - } - return; - } - if (z_pos) - { - if (f > 0.0) - lo = warm_lam; - else - hi = warm_lam; - } - else - { - if (f > 0.0) - hi = warm_lam; - else - lo = warm_lam; - } - } - - for (int it = 0; it < 60; ++it) - { - double lam = 0.5 * (lo + hi); - double my_sum = (lane == 0) ? (w / (1.0 + 2.0 * lam)) * (w / (1.0 + 2.0 * lam)) : 0.0; - for (int m = lane; m < k; m += 32) - { - double dh = variable_rescaling[start + m] / d_st; - double dh2 = dh * dh; - double tt = primal_solution[start + m] * dh / (dh2 + 2.0 * lam); - my_sum += tt * tt; - } - for (int o = 16; o > 0; o >>= 1) - my_sum += __shfl_xor_sync(MASK, my_sum, o); - double zt = z / (1.0 - 2.0 * lam); - double f = my_sum - zt * zt; - if (z_pos) - { - if (f > 0.0) - lo = lam; - else - hi = lam; - } - else - { - if (f > 0.0) - hi = lam; - else - lo = lam; - } - if ((hi - lo) / (1.0 + hi + lo) < 1e-13) - break; - } - double lam = 0.5 * (lo + hi); - if (lane == 0) - warm_start[blk] = lam; - - double w_new = w / (1.0 + 2.0 * lam); - double z_new = z / (1.0 - 2.0 * lam); - for (int m = lane; m < k; m += 32) - { - double dh = variable_rescaling[start + m] / d_st; - double dh2 = dh * dh; - primal_solution[start + m] = primal_solution[start + m] * dh2 / (dh2 + 2.0 * lam); - } - if (lane == 0) - { - primal_solution[start + k] = (z_new + w_new) * INV_SQRT2; - primal_solution[start + k + 1] = (z_new - w_new) * INV_SQRT2; - } -} - -__global__ void compute_cone_dual_residual_warp_kernel(double *__restrict__ dual_residual, - double *__restrict__ complementarity_residual, - const double *__restrict__ objective_vector, - const double *__restrict__ dual_product, - const double *__restrict__ variable_rescaling, - const double *__restrict__ primal_solution, - double *__restrict__ warm_start, - const int *__restrict__ start_idx, - const int *__restrict__ v_dim, - const char *__restrict__ is_fixed, - int num_blocks) -{ - int tid = blockIdx.x * blockDim.x + threadIdx.x; - int blk = tid >> 5; - int lane = tid & 31; - if (blk >= num_blocks) - return; - - const double INV_SQRT2 = 0.7071067811865475; - const unsigned MASK = 0xffffffffu; - - int start = start_idx[blk]; - int k = v_dim[blk]; - - int has_fixed = lane == 0 ? cone_section_has_fixed(is_fixed, start, k + 2) : 0; - has_fixed = __shfl_sync(MASK, has_fixed, 0); - if (has_fixed) - { - if (lane == 0) - { - for (int slot = 0; slot < k + 2; ++slot) - { - int index = start + slot; - double residual = objective_vector[index] - dual_product[index]; - dual_residual[index] = is_fixed[index] ? primal_solution[index] : primal_solution[index] - residual; - } - project_rotated_soc_section_serial( - dual_residual, variable_rescaling, NULL, 0.0, warm_start + blk, start, k, is_fixed); - for (int slot = 0; slot < k + 2; ++slot) - { - int index = start + slot; - dual_residual[index] = - is_fixed[index] ? 0.0 : (primal_solution[index] - dual_residual[index]) * variable_rescaling[index]; - } - complementarity_residual[blk] = 0.0; - } - return; - } - - double r_s = objective_vector[start + k] - dual_product[start + k]; - double r_t = objective_vector[start + k + 1] - dual_product[start + k + 1]; - double r_w = (r_s - r_t) * INV_SQRT2; - double r_z = (r_s + r_t) * INV_SQRT2; - - double d_s = variable_rescaling[start + k]; - double d_t = variable_rescaling[start + k + 1]; - double d_st = sqrt(d_s * d_t); - - int my_diff = 0; - for (int m = lane; m < k; m += 32) - { - if (variable_rescaling[start + m] != d_st) - my_diff = 1; - } - for (int o = 16; o > 0; o >>= 1) - my_diff |= __shfl_xor_sync(MASK, my_diff, o); - - if (my_diff == 0) - { - double my_sumsq = (lane == 0) ? r_w * r_w : 0.0; - for (int m = lane; m < k; m += 32) - { - double rc_m = objective_vector[start + m] - dual_product[start + m]; - my_sumsq += rc_m * rc_m; - } - for (int o = 16; o > 0; o >>= 1) - my_sumsq += __shfl_xor_sync(MASK, my_sumsq, o); - double r_norm = sqrt(my_sumsq); - - double v_factor, p_s, p_t; - if (r_norm <= r_z) - { - v_factor = 0.0; - p_s = r_s; - p_t = r_t; - } - else if (r_norm <= -r_z) - { - v_factor = 1.0; - p_s = 0.0; - p_t = 0.0; - } - else - { - double scale = (r_z + r_norm) / (2.0 * r_norm); - v_factor = 1.0 - scale; - double w_new = scale * r_w; - double z_new = scale * r_norm; - p_s = (z_new + w_new) * INV_SQRT2; - p_t = (z_new - w_new) * INV_SQRT2; - } - - for (int m = lane; m < k; m += 32) - { - double rc_m = objective_vector[start + m] - dual_product[start + m]; - dual_residual[start + m] = rc_m * v_factor * variable_rescaling[start + m]; - } - if (lane == 0) - { - dual_residual[start + k] = (r_s - p_s) * variable_rescaling[start + k]; - dual_residual[start + k + 1] = (r_t - p_t) * variable_rescaling[start + k + 1]; - } - return; - } - - double my_inv = (lane == 0) ? r_w * r_w : 0.0; - double my_pos = (lane == 0) ? r_w * r_w : 0.0; - for (int m = lane; m < k; m += 32) - { - double e_m = d_st / variable_rescaling[start + m]; - double rc_m = objective_vector[start + m] - dual_product[start + m]; - my_inv += (rc_m / e_m) * (rc_m / e_m); - my_pos += (rc_m * e_m) * (rc_m * e_m); - } - for (int o = 16; o > 0; o >>= 1) - { - my_inv += __shfl_xor_sync(MASK, my_inv, o); - my_pos += __shfl_xor_sync(MASK, my_pos, o); - } - double r_inv = sqrt(my_inv); - double r_pos = sqrt(my_pos); - - if (r_inv <= r_z) - { - for (int m = lane; m < k; m += 32) - dual_residual[start + m] = 0.0; - if (lane == 0) - { - dual_residual[start + k] = 0.0; - dual_residual[start + k + 1] = 0.0; - } - return; - } - if (r_pos <= -r_z) - { - for (int m = lane; m < k; m += 32) - { - double rc_m = objective_vector[start + m] - dual_product[start + m]; - dual_residual[start + m] = rc_m * variable_rescaling[start + m]; - } - if (lane == 0) - { - dual_residual[start + k] = r_s * variable_rescaling[start + k]; - dual_residual[start + k + 1] = r_t * variable_rescaling[start + k + 1]; - } - return; - } - - double lo, hi; - bool z_pos = (r_z > 0.0); - if (z_pos) - { - lo = 0.0; - hi = 0.5 - 1e-14; - } - else - { - lo = 0.5 + 1e-14; - hi = 1.0; - for (int doubling = 0; doubling < 60; ++doubling) - { - double my_sum = (lane == 0) ? (r_w / (1.0 + 2.0 * hi)) * (r_w / (1.0 + 2.0 * hi)) : 0.0; - for (int m = lane; m < k; m += 32) - { - double e_m = d_st / variable_rescaling[start + m]; - double e_m2 = e_m * e_m; - double rc_m = objective_vector[start + m] - dual_product[start + m]; - double tt = rc_m * e_m / (e_m2 + 2.0 * hi); - my_sum += tt * tt; - } - for (int o = 16; o > 0; o >>= 1) - my_sum += __shfl_xor_sync(MASK, my_sum, o); - double zt_hi = r_z / (1.0 - 2.0 * hi); - double f_hi = my_sum - zt_hi * zt_hi; - if (f_hi > 0.0) - break; - lo = hi; - hi *= 2.0; - } - } - - double warm_lam = warm_start[blk]; - if (warm_lam > lo && warm_lam < hi) - { - double my_sum = (lane == 0) ? (r_w / (1.0 + 2.0 * warm_lam)) * (r_w / (1.0 + 2.0 * warm_lam)) : 0.0; - for (int m = lane; m < k; m += 32) - { - double e_m = d_st / variable_rescaling[start + m]; - double e_m2 = e_m * e_m; - double rc_m = objective_vector[start + m] - dual_product[start + m]; - double tt = rc_m * e_m / (e_m2 + 2.0 * warm_lam); - my_sum += tt * tt; - } - for (int o = 16; o > 0; o >>= 1) - my_sum += __shfl_xor_sync(MASK, my_sum, o); - double zt = r_z / (1.0 - 2.0 * warm_lam); - double f = my_sum - zt * zt; - if (fabs(f) < 1e-12) - { - double p_w_w = r_w / (1.0 + 2.0 * warm_lam); - double p_z_w = r_z / (1.0 - 2.0 * warm_lam); - double p_s_w = (p_z_w + p_w_w) * INV_SQRT2; - double p_t_w = (p_z_w - p_w_w) * INV_SQRT2; - for (int m = lane; m < k; m += 32) - { - double e_m = d_st / variable_rescaling[start + m]; - double e_m2 = e_m * e_m; - double rc_m = objective_vector[start + m] - dual_product[start + m]; - double p_m = rc_m * e_m2 / (e_m2 + 2.0 * warm_lam); - dual_residual[start + m] = (rc_m - p_m) * variable_rescaling[start + m]; - } - if (lane == 0) - { - dual_residual[start + k] = (r_s - p_s_w) * variable_rescaling[start + k]; - dual_residual[start + k + 1] = (r_t - p_t_w) * variable_rescaling[start + k + 1]; - } - return; - } - if (z_pos) - { - if (f > 0.0) - lo = warm_lam; - else - hi = warm_lam; - } - else - { - if (f > 0.0) - hi = warm_lam; - else - lo = warm_lam; - } - } - - for (int it = 0; it < 60; ++it) - { - double lam = 0.5 * (lo + hi); - double my_sum = (lane == 0) ? (r_w / (1.0 + 2.0 * lam)) * (r_w / (1.0 + 2.0 * lam)) : 0.0; - for (int m = lane; m < k; m += 32) - { - double e_m = d_st / variable_rescaling[start + m]; - double e_m2 = e_m * e_m; - double rc_m = objective_vector[start + m] - dual_product[start + m]; - double tt = rc_m * e_m / (e_m2 + 2.0 * lam); - my_sum += tt * tt; - } - for (int o = 16; o > 0; o >>= 1) - my_sum += __shfl_xor_sync(MASK, my_sum, o); - double zt = r_z / (1.0 - 2.0 * lam); - double f = my_sum - zt * zt; - if (z_pos) - { - if (f > 0.0) - lo = lam; - else - hi = lam; - } - else - { - if (f > 0.0) - hi = lam; - else - lo = lam; - } - if ((hi - lo) / (1.0 + hi + lo) < 1e-13) - break; - } - double lam = 0.5 * (lo + hi); - if (lane == 0) - warm_start[blk] = lam; - - double p_w = r_w / (1.0 + 2.0 * lam); - double p_z = r_z / (1.0 - 2.0 * lam); - double p_s = (p_z + p_w) * INV_SQRT2; - double p_t = (p_z - p_w) * INV_SQRT2; - - for (int m = lane; m < k; m += 32) - { - double e_m = d_st / variable_rescaling[start + m]; - double e_m2 = e_m * e_m; - double rc_m = objective_vector[start + m] - dual_product[start + m]; - double p_m = rc_m * e_m2 / (e_m2 + 2.0 * lam); - dual_residual[start + m] = (rc_m - p_m) * variable_rescaling[start + m]; - } - if (lane == 0) - { - dual_residual[start + k] = (r_s - p_s) * variable_rescaling[start + k]; - dual_residual[start + k + 1] = (r_t - p_t) * variable_rescaling[start + k + 1]; - } -} - -__global__ void project_standard_soc_warp_kernel(double *__restrict__ primal_solution, - const double *__restrict__ variable_rescaling, - double *__restrict__ warm_start, - const int *__restrict__ start_idx, - const int *__restrict__ v_dim, - const char *__restrict__ is_fixed, - int num_blocks) -{ - int tid = blockIdx.x * blockDim.x + threadIdx.x; - int blk = tid >> 5; - int lane = tid & 31; - if (blk >= num_blocks) - return; - - const unsigned MASK = 0xffffffffu; - - int start = start_idx[blk]; - int k = v_dim[blk]; - - int has_fixed = lane == 0 ? cone_section_has_fixed(is_fixed, start, k + 2) : 0; - has_fixed = __shfl_sync(MASK, has_fixed, 0); - if (has_fixed) - { - if (lane == 0) - project_standard_soc_section_serial( - primal_solution, variable_rescaling, NULL, 0.0, warm_start + blk, start, k, is_fixed); - return; - } - - double w = primal_solution[start + k]; - double z = primal_solution[start + k + 1]; - - double d_z = variable_rescaling[start + k + 1]; - double dhat_w = variable_rescaling[start + k] / d_z; - double dhat_w2 = dhat_w * dhat_w; - - int my_diff = (lane == 0 && dhat_w != 1.0) ? 1 : 0; - for (int m = lane; m < k; m += 32) - { - if (variable_rescaling[start + m] != d_z) - my_diff = 1; - } - for (int o = 16; o > 0; o >>= 1) - my_diff |= __shfl_xor_sync(MASK, my_diff, o); - - if (my_diff == 0) - { - double my_sumsq = (lane == 0) ? w * w : 0.0; - for (int m = lane; m < k; m += 32) - { - double v_m = primal_solution[start + m]; - my_sumsq += v_m * v_m; - } - for (int o = 16; o > 0; o >>= 1) - my_sumsq += __shfl_xor_sync(MASK, my_sumsq, o); - double r = sqrt(my_sumsq); - if (r <= z) - return; - if (r <= -z) - { - for (int m = lane; m < k; m += 32) - primal_solution[start + m] = 0.0; - if (lane == 0) - { - primal_solution[start + k] = 0.0; - primal_solution[start + k + 1] = 0.0; - } - return; - } - double scale = (z + r) / (2.0 * r); - for (int m = lane; m < k; m += 32) - primal_solution[start + m] *= scale; - if (lane == 0) - { - primal_solution[start + k] = scale * w; - primal_solution[start + k + 1] = scale * r; - } - return; - } - - double my_inv = (lane == 0) ? (w / dhat_w) * (w / dhat_w) : 0.0; - double my_pos = (lane == 0) ? (w * dhat_w) * (w * dhat_w) : 0.0; - for (int m = lane; m < k; m += 32) - { - double dh = variable_rescaling[start + m] / d_z; - double v_m = primal_solution[start + m]; - my_inv += (v_m / dh) * (v_m / dh); - my_pos += (v_m * dh) * (v_m * dh); - } - for (int o = 16; o > 0; o >>= 1) - { - my_inv += __shfl_xor_sync(MASK, my_inv, o); - my_pos += __shfl_xor_sync(MASK, my_pos, o); - } - double r_inv = sqrt(my_inv); - if (r_inv <= z) - return; - double r_pos = sqrt(my_pos); - if (r_pos <= -z) - { - for (int m = lane; m < k; m += 32) - primal_solution[start + m] = 0.0; - if (lane == 0) - { - primal_solution[start + k] = 0.0; - primal_solution[start + k + 1] = 0.0; - } - return; - } - - double lo, hi; - bool z_pos = (z > 0.0); - if (z_pos) - { - lo = 0.0; - hi = 0.5 - 1e-14; - } - else - { - lo = 0.5 + 1e-14; - hi = 1.0; - for (int doubling = 0; doubling < 60; ++doubling) - { - double my_sum = - (lane == 0) ? (w * dhat_w / (dhat_w2 + 2.0 * hi)) * (w * dhat_w / (dhat_w2 + 2.0 * hi)) : 0.0; - for (int m = lane; m < k; m += 32) - { - double dh = variable_rescaling[start + m] / d_z; - double dh2 = dh * dh; - double tt = primal_solution[start + m] * dh / (dh2 + 2.0 * hi); - my_sum += tt * tt; - } - for (int o = 16; o > 0; o >>= 1) - my_sum += __shfl_xor_sync(MASK, my_sum, o); - double zt_hi = z / (1.0 - 2.0 * hi); - double f_hi = my_sum - zt_hi * zt_hi; - if (f_hi > 0.0) - break; - lo = hi; - hi *= 2.0; - } - } - - double warm_lam = warm_start[blk]; - if (warm_lam > lo && warm_lam < hi) - { - double my_sum = - (lane == 0) ? (w * dhat_w / (dhat_w2 + 2.0 * warm_lam)) * (w * dhat_w / (dhat_w2 + 2.0 * warm_lam)) : 0.0; - for (int m = lane; m < k; m += 32) - { - double dh = variable_rescaling[start + m] / d_z; - double dh2 = dh * dh; - double tt = primal_solution[start + m] * dh / (dh2 + 2.0 * warm_lam); - my_sum += tt * tt; - } - for (int o = 16; o > 0; o >>= 1) - my_sum += __shfl_xor_sync(MASK, my_sum, o); - double zt = z / (1.0 - 2.0 * warm_lam); - double f = my_sum - zt * zt; - if (fabs(f) < 1e-12) - { - for (int m = lane; m < k; m += 32) - { - double dh = variable_rescaling[start + m] / d_z; - double dh2 = dh * dh; - primal_solution[start + m] = primal_solution[start + m] * dh2 / (dh2 + 2.0 * warm_lam); - } - if (lane == 0) - { - primal_solution[start + k + 1] = z / (1.0 - 2.0 * warm_lam); - primal_solution[start + k] = w * dhat_w2 / (dhat_w2 + 2.0 * warm_lam); - } - return; - } - if (z_pos) - { - if (f > 0.0) - lo = warm_lam; - else - hi = warm_lam; - } - else - { - if (f > 0.0) - hi = warm_lam; - else - lo = warm_lam; - } - } - - for (int it = 0; it < 60; ++it) - { - double lam = 0.5 * (lo + hi); - double my_sum = (lane == 0) ? (w * dhat_w / (dhat_w2 + 2.0 * lam)) * (w * dhat_w / (dhat_w2 + 2.0 * lam)) : 0.0; - for (int m = lane; m < k; m += 32) - { - double dh = variable_rescaling[start + m] / d_z; - double dh2 = dh * dh; - double tt = primal_solution[start + m] * dh / (dh2 + 2.0 * lam); - my_sum += tt * tt; - } - for (int o = 16; o > 0; o >>= 1) - my_sum += __shfl_xor_sync(MASK, my_sum, o); - double zt = z / (1.0 - 2.0 * lam); - double f = my_sum - zt * zt; - if (z_pos) - { - if (f > 0.0) - lo = lam; - else - hi = lam; - } - else - { - if (f > 0.0) - hi = lam; - else - lo = lam; - } - if ((hi - lo) / (1.0 + hi + lo) < 1e-13) - break; - } - double lam = 0.5 * (lo + hi); - if (lane == 0) - warm_start[blk] = lam; - - for (int m = lane; m < k; m += 32) - { - double dh = variable_rescaling[start + m] / d_z; - double dh2 = dh * dh; - primal_solution[start + m] = primal_solution[start + m] * dh2 / (dh2 + 2.0 * lam); - } - if (lane == 0) - { - primal_solution[start + k + 1] = z / (1.0 - 2.0 * lam); - primal_solution[start + k] = w * dhat_w2 / (dhat_w2 + 2.0 * lam); - } -} - -__global__ void compute_cone_dual_residual_standard_warp_kernel(double *__restrict__ dual_residual, - double *__restrict__ complementarity_residual, - const double *__restrict__ objective_vector, - const double *__restrict__ dual_product, - const double *__restrict__ variable_rescaling, - const double *__restrict__ primal_solution, - double *__restrict__ warm_start, - const int *__restrict__ start_idx, - const int *__restrict__ v_dim, - const char *__restrict__ is_fixed, - int num_blocks) -{ - int tid = blockIdx.x * blockDim.x + threadIdx.x; - int blk = tid >> 5; - int lane = tid & 31; - if (blk >= num_blocks) - return; - - const unsigned MASK = 0xffffffffu; - - int start = start_idx[blk]; - int k = v_dim[blk]; - - int has_fixed = lane == 0 ? cone_section_has_fixed(is_fixed, start, k + 2) : 0; - has_fixed = __shfl_sync(MASK, has_fixed, 0); - if (has_fixed) - { - if (lane == 0) - { - for (int slot = 0; slot < k + 2; ++slot) - { - int index = start + slot; - double residual = objective_vector[index] - dual_product[index]; - dual_residual[index] = is_fixed[index] ? primal_solution[index] : primal_solution[index] - residual; - } - project_standard_soc_section_serial( - dual_residual, variable_rescaling, NULL, 0.0, warm_start + blk, start, k, is_fixed); - for (int slot = 0; slot < k + 2; ++slot) - { - int index = start + slot; - dual_residual[index] = - is_fixed[index] ? 0.0 : (primal_solution[index] - dual_residual[index]) * variable_rescaling[index]; - } - complementarity_residual[blk] = 0.0; - } - return; - } - - double r_w = objective_vector[start + k] - dual_product[start + k]; - double r_z = objective_vector[start + k + 1] - dual_product[start + k + 1]; - - double d_z = variable_rescaling[start + k + 1]; - double e_w = d_z / variable_rescaling[start + k]; - double e_w2 = e_w * e_w; - - int my_diff = (lane == 0 && e_w != 1.0) ? 1 : 0; - for (int m = lane; m < k; m += 32) - { - if (variable_rescaling[start + m] != d_z) - my_diff = 1; - } - for (int o = 16; o > 0; o >>= 1) - my_diff |= __shfl_xor_sync(MASK, my_diff, o); - - if (my_diff == 0) - { - double my_sumsq = (lane == 0) ? r_w * r_w : 0.0; - for (int m = lane; m < k; m += 32) - { - double rc_m = objective_vector[start + m] - dual_product[start + m]; - my_sumsq += rc_m * rc_m; - } - for (int o = 16; o > 0; o >>= 1) - my_sumsq += __shfl_xor_sync(MASK, my_sumsq, o); - double r = sqrt(my_sumsq); - double v_factor, p_w, p_z; - if (r <= r_z) - { - v_factor = 0.0; - p_w = r_w; - p_z = r_z; - } - else if (r <= -r_z) - { - v_factor = 1.0; - p_w = 0.0; - p_z = 0.0; - } - else - { - double scale = (r_z + r) / (2.0 * r); - v_factor = 1.0 - scale; - p_w = scale * r_w; - p_z = scale * r; - } - for (int m = lane; m < k; m += 32) - { - double rc_m = objective_vector[start + m] - dual_product[start + m]; - dual_residual[start + m] = rc_m * v_factor * variable_rescaling[start + m]; - } - if (lane == 0) - { - dual_residual[start + k] = (r_w - p_w) * variable_rescaling[start + k]; - dual_residual[start + k + 1] = (r_z - p_z) * variable_rescaling[start + k + 1]; - } - return; - } - - double my_inv = (lane == 0) ? (r_w / e_w) * (r_w / e_w) : 0.0; - double my_pos = (lane == 0) ? (r_w * e_w) * (r_w * e_w) : 0.0; - for (int m = lane; m < k; m += 32) - { - double e_m = d_z / variable_rescaling[start + m]; - double rc_m = objective_vector[start + m] - dual_product[start + m]; - my_inv += (rc_m / e_m) * (rc_m / e_m); - my_pos += (rc_m * e_m) * (rc_m * e_m); - } - for (int o = 16; o > 0; o >>= 1) - { - my_inv += __shfl_xor_sync(MASK, my_inv, o); - my_pos += __shfl_xor_sync(MASK, my_pos, o); - } - double r_inv = sqrt(my_inv); - double r_pos = sqrt(my_pos); - - if (r_inv <= r_z) - { - for (int m = lane; m < k; m += 32) - dual_residual[start + m] = 0.0; - if (lane == 0) - { - dual_residual[start + k] = 0.0; - dual_residual[start + k + 1] = 0.0; - } - return; - } - if (r_pos <= -r_z) - { - for (int m = lane; m < k; m += 32) - { - double rc_m = objective_vector[start + m] - dual_product[start + m]; - dual_residual[start + m] = rc_m * variable_rescaling[start + m]; - } - if (lane == 0) - { - dual_residual[start + k] = r_w * variable_rescaling[start + k]; - dual_residual[start + k + 1] = r_z * variable_rescaling[start + k + 1]; - } - return; - } - - double lo, hi; - bool z_pos = (r_z > 0.0); - if (z_pos) - { - lo = 0.0; - hi = 0.5 - 1e-14; - } - else - { - lo = 0.5 + 1e-14; - hi = 1.0; - for (int doubling = 0; doubling < 60; ++doubling) - { - double my_sum = (lane == 0) ? (r_w * e_w / (e_w2 + 2.0 * hi)) * (r_w * e_w / (e_w2 + 2.0 * hi)) : 0.0; - for (int m = lane; m < k; m += 32) - { - double e_m = d_z / variable_rescaling[start + m]; - double e_m2 = e_m * e_m; - double rc_m = objective_vector[start + m] - dual_product[start + m]; - double tt = rc_m * e_m / (e_m2 + 2.0 * hi); - my_sum += tt * tt; - } - for (int o = 16; o > 0; o >>= 1) - my_sum += __shfl_xor_sync(MASK, my_sum, o); - double zt_hi = r_z / (1.0 - 2.0 * hi); - double f_hi = my_sum - zt_hi * zt_hi; - if (f_hi > 0.0) - break; - lo = hi; - hi *= 2.0; - } - } - - double warm_lam = warm_start[blk]; - if (warm_lam > lo && warm_lam < hi) - { - double my_sum = - (lane == 0) ? (r_w * e_w / (e_w2 + 2.0 * warm_lam)) * (r_w * e_w / (e_w2 + 2.0 * warm_lam)) : 0.0; - for (int m = lane; m < k; m += 32) - { - double e_m = d_z / variable_rescaling[start + m]; - double e_m2 = e_m * e_m; - double rc_m = objective_vector[start + m] - dual_product[start + m]; - double tt = rc_m * e_m / (e_m2 + 2.0 * warm_lam); - my_sum += tt * tt; - } - for (int o = 16; o > 0; o >>= 1) - my_sum += __shfl_xor_sync(MASK, my_sum, o); - double zt = r_z / (1.0 - 2.0 * warm_lam); - double f = my_sum - zt * zt; - if (fabs(f) < 1e-12) - { - double p_z_w = r_z / (1.0 - 2.0 * warm_lam); - double p_w_w = r_w * e_w2 / (e_w2 + 2.0 * warm_lam); - for (int m = lane; m < k; m += 32) - { - double e_m = d_z / variable_rescaling[start + m]; - double e_m2 = e_m * e_m; - double rc_m = objective_vector[start + m] - dual_product[start + m]; - double p_m = rc_m * e_m2 / (e_m2 + 2.0 * warm_lam); - dual_residual[start + m] = (rc_m - p_m) * variable_rescaling[start + m]; - } - if (lane == 0) - { - dual_residual[start + k] = (r_w - p_w_w) * variable_rescaling[start + k]; - dual_residual[start + k + 1] = (r_z - p_z_w) * variable_rescaling[start + k + 1]; - } - return; - } - if (z_pos) - { - if (f > 0.0) - lo = warm_lam; - else - hi = warm_lam; - } - else - { - if (f > 0.0) - hi = warm_lam; - else - lo = warm_lam; - } - } - - for (int it = 0; it < 60; ++it) - { - double lam = 0.5 * (lo + hi); - double my_sum = (lane == 0) ? (r_w * e_w / (e_w2 + 2.0 * lam)) * (r_w * e_w / (e_w2 + 2.0 * lam)) : 0.0; - for (int m = lane; m < k; m += 32) - { - double e_m = d_z / variable_rescaling[start + m]; - double e_m2 = e_m * e_m; - double rc_m = objective_vector[start + m] - dual_product[start + m]; - double tt = rc_m * e_m / (e_m2 + 2.0 * lam); - my_sum += tt * tt; - } - for (int o = 16; o > 0; o >>= 1) - my_sum += __shfl_xor_sync(MASK, my_sum, o); - double zt = r_z / (1.0 - 2.0 * lam); - double f = my_sum - zt * zt; - if (z_pos) - { - if (f > 0.0) - lo = lam; - else - hi = lam; - } - else - { - if (f > 0.0) - hi = lam; - else - lo = lam; - } - if ((hi - lo) / (1.0 + hi + lo) < 1e-13) - break; - } - double lam = 0.5 * (lo + hi); - if (lane == 0) - warm_start[blk] = lam; - - double p_z = r_z / (1.0 - 2.0 * lam); - double p_w = r_w * e_w2 / (e_w2 + 2.0 * lam); - - for (int m = lane; m < k; m += 32) - { - double e_m = d_z / variable_rescaling[start + m]; - double e_m2 = e_m * e_m; - double rc_m = objective_vector[start + m] - dual_product[start + m]; - double p_m = rc_m * e_m2 / (e_m2 + 2.0 * lam); - dual_residual[start + m] = (rc_m - p_m) * variable_rescaling[start + m]; - } - if (lane == 0) - { - dual_residual[start + k] = (r_w - p_w) * variable_rescaling[start + k]; - dual_residual[start + k + 1] = (r_z - p_z) * variable_rescaling[start + k + 1]; - } -} - -/* Project onto D K_exp via Parikh-Boyd Newton on rho = u_1/u_2 (u = D^{-1} x). */ -__device__ static inline void project_exp_cone_point( - double r1, double r2, double r3, double d1, double d2, double d3, double *xo, double *yo, double *zo) -{ - const double E_CONST = 2.718281828459045; - double rr1 = r1 / d1, rr2 = r2 / d2, rr3 = r3 / d3; - - if (rr2 > 0.0) - { - double ratio = rr1 / rr2; - if (ratio < 700.0 && rr2 * exp(ratio) <= rr3) - { - *xo = r1; - *yo = r2; - *zo = r3; - return; - } - } - else if (rr2 == 0.0 && rr1 <= 0.0 && rr3 >= 0.0) - { - *xo = r1; - *yo = r2; - *zo = r3; - return; - } - - if (r1 > 0.0) - { - double ratio = (d2 * r2) / (d1 * r1); - if (ratio < 700.0 && d1 * r1 * exp(ratio) + E_CONST * d3 * r3 <= 0.0) - { - *xo = 0.0; - *yo = 0.0; - *zo = 0.0; - return; - } - } - else if (r1 == 0.0 && r2 <= 0.0 && r3 <= 0.0) - { - *xo = 0.0; - *yo = 0.0; - *zo = 0.0; - return; - } - - if (rr1 <= 0.0 && rr2 <= 0.0) - { - *xo = r1; - *yo = 0.0; - *zo = (rr3 < 0.0) ? 0.0 : r3; - return; - } - - double alpha = (d3 / d1) * (d3 / d1); - double beta = (d3 / d2) * (d3 / d2); - double rho = 0.0; - bool diverged = false; - for (int it = 0; it < 100; ++it) - { - if (rho >= 299.0 || rho <= -299.0) - { - diverged = true; - break; - } - double e_rho = exp(rho); - double e_2rho = e_rho * e_rho; - double one_m_rho = 1.0 - rho; - - double a_term = alpha - beta * rho * one_m_rho; - double b_term = beta * rr1 * one_m_rho - alpha * rr2; - double f = rr1 - rr2 * rho + rr3 * e_rho * a_term + e_2rho * b_term; - - double da_drho = -beta * (1.0 - 2.0 * rho); - double db_drho = -beta * rr1; - double df = -rr2 + rr3 * e_rho * (a_term + da_drho) + e_2rho * (2.0 * b_term + db_drho); - - if (fabs(df) < 1e-300) - break; - double step = f / df; - if (step > 10.0) - step = 10.0; - if (step < -10.0) - step = -10.0; - rho -= step; - if (fabs(step) < 1e-13 * (1.0 + fabs(rho))) - break; - } - - double e_rho = exp(rho); - double denom = rho + alpha * e_rho * e_rho; - double u2 = (rr1 + alpha * rr3 * e_rho) / denom; - - if (diverged || !isfinite(u2) || u2 <= 0.0) - { - *xo = (r1 < 0.0) ? r1 : 0.0; - *yo = 0.0; - *zo = (r3 > 0.0) ? r3 : 0.0; - return; - } - - double u1 = rho * u2; - double u3 = u2 * e_rho; - - *xo = d1 * u1; - *yo = d2 * u2; - *zo = d3 * u3; -} - -/* y-fixed cross-section of D K_exp: weighted 1D Newton-bisection on u = exp((rz/d_r)/y_eff). */ -__device__ static inline void project_2d_exp_persp( - double rz0, double ry, double rt0, double d_r, double d_y, double d_t, double *warm_start, double *rzo, double *rto) -{ - if (d_r <= 0.0 || d_y <= 0.0 || d_t <= 0.0) - { - *rzo = rz0; - *rto = rt0; - return; - } - double y_eff = ry / d_y; - if (y_eff <= 0.0) - { - *rzo = (rz0 < 0.0) ? rz0 : 0.0; - *rto = (rt0 > 0.0) ? rt0 : 0.0; - return; - } - - double arg = (rz0 / d_r) / y_eff; - if (arg < 700.0) - { - double rhs = y_eff * d_t * exp(arg); - if (rhs <= rt0) - { - *rzo = rz0; - *rto = rt0; - return; - } - } - - double a = d_t * d_t * y_eff; - double b = d_t * rt0; - double c = d_r * d_r * y_eff; - double e = d_r * rz0; - - double u = *warm_start; - double u_lo = 1e-30; - double u_hi = 1.0; - for (int g = 0; g < 200; ++g) - { - double lu = log(u_hi); - double f_hi = a * u_hi * u_hi - b * u_hi + c * lu - e; - if (isfinite(f_hi) && f_hi > 0.0) - break; - u_hi *= 4.0; - if (u_hi > 1e150) - break; - } - for (int g = 0; g < 200; ++g) - { - double lu = log(u_lo); - double f_lo = a * u_lo * u_lo - b * u_lo + c * lu - e; - if (isfinite(f_lo) && f_lo < 0.0) - break; - u_lo *= 0.25; - if (u_lo < 1e-300) - break; - } - if (u_lo >= u_hi) - { - *rzo = rz0; - *rto = rt0; - return; - } - - if (!(u > u_lo && u < u_hi) || !isfinite(u)) - u = exp(0.5 * (log(u_lo) + log(u_hi))); - - for (int it = 0; it < 80; ++it) - { - double lu = log(u); - double f = a * u * u - b * u + c * lu - e; - double df = 2.0 * a * u - b + c / u; - if (f > 0.0) - u_hi = u; - else - u_lo = u; - double u_new; - if (df > 1e-300 && isfinite(df) && isfinite(f)) - { - u_new = u - f / df; - if (!isfinite(u_new) || u_new <= u_lo || u_new >= u_hi) - u_new = exp(0.5 * (log(u_lo) + log(u_hi))); - } - else - { - u_new = exp(0.5 * (log(u_lo) + log(u_hi))); - } - if (fabs(u_new - u) < 1e-14 * (1.0 + fabs(u_new))) - { - u = u_new; - break; - } - u = u_new; - } - *warm_start = u; - *rzo = d_r * y_eff * log(u); - *rto = d_t * y_eff * u; -} - -__device__ static inline double exp_cone_boundary(double x, double y) -{ - if (!(y > 0.0)) - return x <= 0.0 ? 0.0 : INFINITY; - double exponent = x / y; - double log_value = log(y) + exponent; - if (log_value >= log(DBL_MAX)) - return INFINITY; - if (log_value <= log(DBL_MIN)) - return 0.0; - return exp(log_value); -} - -__device__ static inline bool exp_cone_contains_point(double x, double y, double z) -{ - if (y > 0.0 && z > 0.0) - { - double lhs = log(y) + x / y; - double rhs = log(z); - double tolerance = 64.0 * DBL_EPSILON * (1.0 + fabs(lhs) + fabs(rhs)); - return lhs <= rhs + tolerance; - } - return y == 0.0 && x <= 0.0 && z >= 0.0; -} - -__device__ static inline double -exp_fixed_x_objective(double y, double x, double input_y, double input_z, double weight_y, double weight_z) -{ - double z = exp_cone_boundary(x, y); - if (!isfinite(z)) - return INFINITY; - double dy = y - input_y; - double dz = z - input_z; - return weight_y * dy * dy + weight_z * dz * dz; -} - -__device__ static inline double -exp_fixed_z_objective(double y, double z, double input_x, double input_y, double weight_x, double weight_y) -{ - double x = y > 0.0 ? y * (log(z) - log(y)) : 0.0; - double dx = x - input_x; - double dy = y - input_y; - return weight_x * dx * dx + weight_y * dy * dy; -} - -__device__ static inline double exp_xz_log_violation(double y, double x, double z) -{ - if (!(y > 0.0) || !(z > 0.0)) - return x <= 0.0 ? -INFINITY : INFINITY; - return log(y) + x / y - log(z); -} - -__device__ static inline void project_exp_cone_section(double *point, - const double *rescaling, - const double *q_diag, - double tau, - double *warm_start, - int start, - const char *is_fixed) -{ - bool fixed_x = is_fixed[start + 0] != 0; - bool fixed_y = is_fixed[start + 1] != 0; - bool fixed_z = is_fixed[start + 2] != 0; - double input_x = point[start + 0] / rescaling[start + 0]; - double input_y = point[start + 1] / rescaling[start + 1]; - double input_z = point[start + 2] / rescaling[start + 2]; - - if (exp_cone_contains_point(input_x, input_y, input_z) || (fixed_x && fixed_y && fixed_z)) - return; - - double weight_x = cone_section_weight(rescaling, q_diag, tau, start + 0); - double weight_y = cone_section_weight(rescaling, q_diag, tau, start + 1); - double weight_z = cone_section_weight(rescaling, q_diag, tau, start + 2); - double output_x = input_x; - double output_y = input_y; - double output_z = input_z; - - if (fixed_x && fixed_y) - { - output_z = fmax(input_z, exp_cone_boundary(input_x, input_y)); - } - else if (fixed_y && fixed_z) - { - if (input_y == 0.0) - output_x = fmin(input_x, 0.0); - else - output_x = fmin(input_x, input_y * (log(input_z) - log(input_y))); - } - else if (fixed_x && fixed_z) - { - if (input_z == 0.0) - { - output_y = 0.0; - } - else if (input_x > 0.0) - { - double center = input_x; - double left = fmax(DBL_MIN, input_x / 1024.0); - while (exp_xz_log_violation(left, input_x, input_z) <= 0.0 && left > DBL_MIN) - left *= 0.5; - double lo = left; - double hi = center; - for (int iteration = 0; iteration < 100; ++iteration) - { - double mid = 0.5 * (lo + hi); - if (exp_xz_log_violation(mid, input_x, input_z) > 0.0) - lo = mid; - else - hi = mid; - } - double lower = 0.5 * (lo + hi); - - lo = center; - hi = fmax(2.0 * center, input_z); - while (exp_xz_log_violation(hi, input_x, input_z) < 0.0 && hi < DBL_MAX / 4.0) - hi *= 2.0; - for (int iteration = 0; iteration < 100; ++iteration) - { - double mid = 0.5 * (lo + hi); - if (exp_xz_log_violation(mid, input_x, input_z) <= 0.0) - lo = mid; - else - hi = mid; - } - double upper = 0.5 * (lo + hi); - output_y = fmin(fmax(input_y, lower), upper); - } - else - { - double lo = 0.0; - double hi = fmax(1.0, fmax(input_z, fabs(input_x))); - while (exp_xz_log_violation(hi, input_x, input_z) < 0.0 && hi < DBL_MAX / 4.0) - hi *= 2.0; - for (int iteration = 0; iteration < 100; ++iteration) - { - double mid = 0.5 * (lo + hi); - if (exp_xz_log_violation(mid, input_x, input_z) <= 0.0) - lo = mid; - else - hi = mid; - } - output_y = fmin(fmax(input_y, 0.0), 0.5 * (lo + hi)); - } - } - else if (fixed_y) - { - if (!(input_y > 0.0)) - { - output_x = fmin(input_x, 0.0); - output_z = fmax(input_z, 0.0); - } - else - { - double effective_x = sqrt(weight_x); - double effective_y = sqrt(weight_y); - double effective_z = sqrt(weight_z); - double scaled_x; - double scaled_z; - project_2d_exp_persp(effective_x * input_x, - effective_y * input_y, - effective_z * input_z, - effective_x, - effective_y, - effective_z, - warm_start, - &scaled_x, - &scaled_z); - output_x = scaled_x / effective_x; - output_z = scaled_z / effective_z; - } - } - else if (fixed_x) - { - double scale = 1.0 + fabs(input_x) + fabs(input_y) + fabs(input_z); - double lo = input_x > 0.0 ? fmax(DBL_MIN, input_x / 700.0) : 0.0; - double hi = scale; - double previous = exp_fixed_x_objective(0.5 * hi, input_x, input_y, input_z, weight_y, weight_z); - double current = exp_fixed_x_objective(hi, input_x, input_y, input_z, weight_y, weight_z); - for (int expansion = 0; expansion < 80 && current < previous && hi < DBL_MAX / 4.0; ++expansion) - { - previous = current; - hi *= 2.0; - current = exp_fixed_x_objective(hi, input_x, input_y, input_z, weight_y, weight_z); - } - const double ratio = 0.6180339887498948482; - double a = lo; - double b = hi; - double c = b - ratio * (b - a); - double d = a + ratio * (b - a); - double fc = exp_fixed_x_objective(c, input_x, input_y, input_z, weight_y, weight_z); - double fd = exp_fixed_x_objective(d, input_x, input_y, input_z, weight_y, weight_z); - for (int iteration = 0; iteration < 100; ++iteration) - { - if (fc <= fd) - { - b = d; - d = c; - fd = fc; - c = b - ratio * (b - a); - fc = exp_fixed_x_objective(c, input_x, input_y, input_z, weight_y, weight_z); - } - else - { - a = c; - c = d; - fc = fd; - d = a + ratio * (b - a); - fd = exp_fixed_x_objective(d, input_x, input_y, input_z, weight_y, weight_z); - } - } - output_y = 0.5 * (a + b); - output_z = exp_cone_boundary(input_x, output_y); - if (input_x <= 0.0) - { - double closure_z = fmax(input_z, 0.0); - double closure_objective = - weight_y * input_y * input_y + weight_z * (closure_z - input_z) * (closure_z - input_z); - double smooth_objective = exp_fixed_x_objective(output_y, input_x, input_y, input_z, weight_y, weight_z); - if (closure_objective <= smooth_objective) - { - output_y = 0.0; - output_z = closure_z; - } - } - } - else if (fixed_z) - { - if (input_z == 0.0) - { - output_x = fmin(input_x, 0.0); - output_y = 0.0; - } - else - { - double scale = 1.0 + fabs(input_x) + fabs(input_y) + input_z; - double lo = 0.0; - double hi = scale; - double previous = exp_fixed_z_objective(0.5 * hi, input_z, input_x, input_y, weight_x, weight_y); - double current = exp_fixed_z_objective(hi, input_z, input_x, input_y, weight_x, weight_y); - for (int expansion = 0; expansion < 80 && current < previous && hi < DBL_MAX / 4.0; ++expansion) - { - previous = current; - hi *= 2.0; - current = exp_fixed_z_objective(hi, input_z, input_x, input_y, weight_x, weight_y); - } - const double ratio = 0.6180339887498948482; - double a = lo; - double b = hi; - double c = b - ratio * (b - a); - double d = a + ratio * (b - a); - double fc = exp_fixed_z_objective(c, input_z, input_x, input_y, weight_x, weight_y); - double fd = exp_fixed_z_objective(d, input_z, input_x, input_y, weight_x, weight_y); - for (int iteration = 0; iteration < 100; ++iteration) - { - if (fc <= fd) - { - b = d; - d = c; - fd = fc; - c = b - ratio * (b - a); - fc = exp_fixed_z_objective(c, input_z, input_x, input_y, weight_x, weight_y); - } - else - { - a = c; - c = d; - fc = fd; - d = a + ratio * (b - a); - fd = exp_fixed_z_objective(d, input_z, input_x, input_y, weight_x, weight_y); - } - } - output_y = 0.5 * (a + b); - output_x = output_y > 0.0 ? output_y * (log(input_z) - log(output_y)) : 0.0; - double closure_x = fmin(input_x, 0.0); - double closure_objective = - weight_x * (closure_x - input_x) * (closure_x - input_x) + weight_y * input_y * input_y; - double smooth_objective = exp_fixed_z_objective(output_y, input_z, input_x, input_y, weight_x, weight_y); - if (closure_objective <= smooth_objective) - { - output_x = closure_x; - output_y = 0.0; - } - } - } - - if (!fixed_x) - point[start + 0] = output_x * rescaling[start + 0]; - if (!fixed_y) - point[start + 1] = output_y * rescaling[start + 1]; - if (!fixed_z) - point[start + 2] = output_z * rescaling[start + 2]; -} - -__global__ void project_exp_cone_kernel(double *__restrict__ primal_solution, - const double *__restrict__ variable_rescaling, - double *__restrict__ warm_start, - const int *__restrict__ start_idx, - const int *__restrict__ v_dim, - const char *__restrict__ is_fixed, - int num_blocks) -{ - (void)v_dim; - int blk = blockIdx.x * blockDim.x + threadIdx.x; - if (blk >= num_blocks) - return; - - int s_idx = start_idx[blk]; - double r1 = primal_solution[s_idx + 0]; - double r2 = primal_solution[s_idx + 1]; - double r3 = primal_solution[s_idx + 2]; - - double d1 = variable_rescaling[s_idx + 0]; - double d2 = variable_rescaling[s_idx + 1]; - double d3 = variable_rescaling[s_idx + 2]; - - if (cone_section_has_fixed(is_fixed, s_idx, 3)) - { - project_exp_cone_section(primal_solution, variable_rescaling, NULL, 0.0, warm_start + blk, s_idx, is_fixed); - return; - } - - double xo, yo, zo; - project_exp_cone_point(r1, r2, r3, d1, d2, d3, &xo, &yo, &zo); - - primal_solution[s_idx + 0] = xo; - primal_solution[s_idx + 1] = yo; - primal_solution[s_idx + 2] = zo; -} - -__global__ void compute_cone_dual_residual_exp_kernel(double *__restrict__ dual_residual, - double *__restrict__ complementarity_residual, - const double *__restrict__ objective_vector, - const double *__restrict__ dual_product, - const double *__restrict__ variable_rescaling, - const double *__restrict__ primal_solution, - double *__restrict__ warm_start, - const int *__restrict__ start_idx, - const int *__restrict__ v_dim, - const char *__restrict__ is_fixed, - int num_blocks) -{ - (void)v_dim; - int blk = blockIdx.x * blockDim.x + threadIdx.x; - if (blk >= num_blocks) - return; - - int s_idx = start_idx[blk]; - double r1 = objective_vector[s_idx + 0] - dual_product[s_idx + 0]; - double r2 = objective_vector[s_idx + 1] - dual_product[s_idx + 1]; - double r3 = objective_vector[s_idx + 2] - dual_product[s_idx + 2]; - - if (cone_section_has_fixed(is_fixed, s_idx, 3)) - { - const double residual[3] = {r1, r2, r3}; - for (int slot = 0; slot < 3; ++slot) - { - int index = s_idx + slot; - dual_residual[index] = is_fixed[index] ? primal_solution[index] : primal_solution[index] - residual[slot]; - } - project_exp_cone_section(dual_residual, variable_rescaling, NULL, 0.0, warm_start + blk, s_idx, is_fixed); - for (int slot = 0; slot < 3; ++slot) - { - int index = s_idx + slot; - dual_residual[index] = - is_fixed[index] ? 0.0 : (primal_solution[index] - dual_residual[index]) * variable_rescaling[index]; - } - complementarity_residual[blk] = 0.0; - return; - } - - double d1 = 1.0 / variable_rescaling[s_idx + 0]; - double d2 = 1.0 / variable_rescaling[s_idx + 1]; - double d3 = 1.0 / variable_rescaling[s_idx + 2]; - - /* Moreau: dist(r, K_exp^*) = ||-proj_{K_exp}(-r)|| with inverse-scaled d. */ - double xo, yo, zo; - project_exp_cone_point(-r1, -r2, -r3, d1, d2, d3, &xo, &yo, &zo); - - dual_residual[s_idx + 0] = -xo * variable_rescaling[s_idx + 0]; - dual_residual[s_idx + 1] = -yo * variable_rescaling[s_idx + 1]; - dual_residual[s_idx + 2] = -zo * variable_rescaling[s_idx + 2]; -} - -/* 3-dim alpha-power cone K_a = {(x,y,z) : x >= 0, y >= 0, x^a * y^(1-a) >= |z|}. - Weighted projection: solves - min_{(x,y,z) in K_a} 0.5 * ( wx*(x-rx)^2 + wy*(y-ry)^2 + wz*(z-rz)^2 ) - with wi > 0. In-cone test is metric-independent; opposite-cone test is not. - Bisection on rho = |z_proj| in [0, |r_z|] using KKT-derived formulas - x(rho) = 0.5 (rx + sqrt(rx^2 + 4 a (wz/wx) rho (|rz|-rho))) - y(rho) = 0.5 (ry + sqrt(ry^2 + 4 (1-a) (wz/wy) rho (|rz|-rho))) - G(rho) = x^a y^(1-a) - rho. */ -__device__ static inline double positive_quadratic_root(double r, double q) -{ - if (!(q > 0.0)) - return fmax(r, 0.0); - double disc = hypot(r, 2.0 * sqrt(q)); - if (r >= 0.0) - return 0.5 * (r + disc); - return (2.0 * q) / (disc - r); -} - -__device__ static inline void project_power_cone_point_normalized( - double rx, double ry, double rz, double wx, double wy, double wz, double alpha, double *xo, double *yo, double *zo) -{ - double abs_rz = fabs(rz); - double sgn_rz = (rz >= 0.0) ? 1.0 : -1.0; - double om = 1.0 - alpha; - - if (abs_rz == 0.0) - { - *xo = fmax(rx, 0.0); - *yo = fmax(ry, 0.0); - *zo = 0.0; - return; - } - - if (rx > 0.0 && ry > 0.0) - { - if (alpha * log(rx) + om * log(ry) >= log(abs_rz)) - { - *xo = rx; - *yo = ry; - *zo = rz; - return; - } - } - - /* Opposite cone under weighted inner product: - proj^w(r) = 0 iff (wx*rx, wy*ry, wz*rz) in -K_a^*, i.e., - (-wx*rx)/a)^a * ((-wy*ry)/(1-a))^(1-a) >= wz*|rz|, rx <= 0, ry <= 0. */ - if (rx <= 0.0 && ry <= 0.0) - { - double u = (rx < 0.0) ? (-wx * rx) / alpha : 0.0; - double v = (ry < 0.0) ? (-wy * ry) / om : 0.0; - if (u > 0.0 && v > 0.0 && alpha * log(u) + om * log(v) >= log(wz) + log(abs_rz)) - { - *xo = 0.0; - *yo = 0.0; - *zo = 0.0; - return; - } - } - - double c_x = 4.0 * alpha * (wz / wx); - double c_y = 4.0 * om * (wz / wy); - - /* - * Bisect in log(rho). When one input axis is negative and alpha is close - * to an endpoint, the positive root can be many orders of magnitude below - * |r_z|. A linear relative floor would then converge to an infeasible - * point instead of the nonzero root. - */ - double lo = log(DBL_MIN); - double hi = log(abs_rz); - if (!(hi > lo)) - { - *xo = fmax(rx, 0.0); - *yo = fmax(ry, 0.0); - *zo = 0.0; - return; - } - - for (int it = 0; it < 80; ++it) - { - double log_rho = lo + 0.5 * (hi - lo); - double rho = exp(log_rho); - double x = positive_quadratic_root(rx, 0.25 * c_x * rho * (abs_rz - rho)); - double y = positive_quadratic_root(ry, 0.25 * c_y * rho * (abs_rz - rho)); - bool above_boundary = x > 0.0 && y > 0.0 && alpha * log(x) + om * log(y) > log_rho; - if (above_boundary) - lo = log_rho; - else - hi = log_rho; - } - double rho = exp(lo + 0.5 * (hi - lo)); - *xo = positive_quadratic_root(rx, 0.25 * c_x * rho * (abs_rz - rho)); - *yo = positive_quadratic_root(ry, 0.25 * c_y * rho * (abs_rz - rho)); - double log_bound = alpha * log(*xo) + om * log(*yo); - *zo = sgn_rz * fmin(rho, exp(log_bound)); -} - -__device__ static inline void project_power_cone_point( - double rx, double ry, double rz, double wx, double wy, double wz, double alpha, double *xo, double *yo, double *zo) -{ - /* The cone and weighted projection are positively homogeneous. Normalize - the point so products such as rho * (|r_z| - rho) cannot overflow. */ - double scale = fmax(fabs(rx), fmax(fabs(ry), fabs(rz))); - if (!(scale > 0.0) || !isfinite(scale)) - { - project_power_cone_point_normalized(rx, ry, rz, wx, wy, wz, alpha, xo, yo, zo); - return; - } - - double xn, yn, zn; - project_power_cone_point_normalized(rx / scale, ry / scale, rz / scale, wx, wy, wz, alpha, &xn, &yn, &zn); - *xo = xn * scale; - *yo = yn * scale; - *zo = zn * scale; -} - -/* Project x,y while z is fixed. The active boundary is x^a y^(1-a) = |z|. */ -__device__ static inline double -power_xy_log_boundary(double lambda, double rx, double ry, double wx, double wy, double alpha) -{ - double om = 1.0 - alpha; - double x = positive_quadratic_root(rx, (lambda / wx) * alpha); - double y = positive_quadratic_root(ry, (lambda / wy) * om); - if (!(x > 0.0) || !(y > 0.0)) - return -INFINITY; - return alpha * log(x) + om * log(y); -} - -__device__ static inline void project_power_xy_fixed_z_normalized( - double rx, double ry, double fixed_z, double wx, double wy, double alpha, double *xo, double *yo) -{ - double c = fabs(fixed_z); - double om = 1.0 - alpha; - if (c == 0.0) - { - *xo = fmax(rx, 0.0); - *yo = fmax(ry, 0.0); - return; - } - - if (rx > 0.0 && ry > 0.0 && alpha * log(rx) + om * log(ry) >= log(c)) - { - *xo = rx; - *yo = ry; - return; - } - - double target = log(c); - double hi = fmin(wx, wy); - if (!(hi > 0.0) || !isfinite(hi)) - hi = 1.0; - for (int it = 0; it < 2048; ++it) - { - double log_boundary = power_xy_log_boundary(hi, rx, ry, wx, wy, alpha); - if (log_boundary >= target || isnan(log_boundary)) - break; - if (hi >= 0.5 * DBL_MAX) - { - hi = DBL_MAX; - break; - } - hi *= 2.0; - } - - double lambda; - double floor_log_boundary = power_xy_log_boundary(DBL_MIN, rx, ry, wx, wy, alpha); - if (floor_log_boundary >= target) - { - double lo = 0.0; - double floor_hi = DBL_MIN; - for (int it = 0; it < 80; ++it) - { - double candidate = lo + 0.5 * (floor_hi - lo); - if (power_xy_log_boundary(candidate, rx, ry, wx, wy, alpha) < target) - lo = candidate; - else - floor_hi = candidate; - } - lambda = lo + 0.5 * (floor_hi - lo); - } - else - { - double log_lo = log(DBL_MIN); - double log_hi = log(hi); - for (int it = 0; it < 96; ++it) - { - double log_lambda = log_lo + 0.5 * (log_hi - log_lo); - double candidate = exp(log_lambda); - if (power_xy_log_boundary(candidate, rx, ry, wx, wy, alpha) < target) - log_lo = log_lambda; - else - log_hi = log_lambda; - } - lambda = exp(log_lo + 0.5 * (log_hi - log_lo)); - } - *xo = positive_quadratic_root(rx, (lambda / wx) * alpha); - *yo = positive_quadratic_root(ry, (lambda / wy) * om); -} - -__device__ static inline void project_power_xy_fixed_z( - double rx, double ry, double fixed_z, double wx, double wy, double alpha, double *xo, double *yo) -{ - double scale = fmax(fabs(rx), fmax(fabs(ry), fabs(fixed_z))); - if (!(scale > 0.0) || !isfinite(scale)) - { - project_power_xy_fixed_z_normalized(rx, ry, fixed_z, wx, wy, alpha, xo, yo); - return; - } - - double xn, yn; - project_power_xy_fixed_z_normalized(rx / scale, ry / scale, fixed_z / scale, wx, wy, alpha, &xn, &yn); - *xo = xn * scale; - *yo = yn * scale; -} - -/* With one nonnegative axis fixed, project the other axis and z onto - |z| <= fixed_axis^fixed_exp * other^other_exp. On the active boundary, - direct bisection in other is stable even when the KKT multiplier is tiny. */ -__device__ static inline double power_exp_from_log(double log_value) -{ - if (log_value >= log(DBL_MAX)) - return INFINITY; - if (log_value <= log(DBL_MIN)) - return 0.0; - return exp(log_value); -} - -__device__ static inline double power_section_derivative( - double other, double r_other, double abs_rz, double w_other, double wz, double log_coefficient, double other_exp) -{ - if (!(other > 0.0)) - return -INFINITY; - - double log_other = log(other); - double bound = power_exp_from_log(log_coefficient + other_exp * log_other); - double slope = power_exp_from_log(log_coefficient + log(other_exp) + (other_exp - 1.0) * log_other); - double linear_term = w_other * (other - r_other); - double gap = bound - abs_rz; - if (gap == 0.0 || slope == 0.0) - return linear_term; - if (!isfinite(slope)) - return copysign(INFINITY, gap); - return linear_term + wz * gap * slope; -} - -__device__ static inline void project_power_section_fixed_axis_normalized(double fixed_axis, - double r_other, - double rz, - double w_other, - double wz, - double fixed_exp, - double other_exp, - double *other_out, - double *z_out) -{ - double abs_rz = fabs(rz); - if (!(fixed_axis > 0.0) || abs_rz == 0.0) - { - *other_out = fmax(r_other, 0.0); - *z_out = 0.0; - return; - } - double log_coefficient = fixed_exp * log(fixed_axis); - - if (r_other > 0.0 && log_coefficient + other_exp * log(r_other) >= log(abs_rz)) - { - *other_out = r_other; - *z_out = rz; - return; - } - - double log_feasible_other = (log(abs_rz) - log_coefficient) / other_exp; - double feasible_other = power_exp_from_log(log_feasible_other); - if (feasible_other == 0.0) - { - *other_out = 0.0; - *z_out = 0.0; - return; - } - - double lo = 0.0; - double hi = fmax(1.0, fmax(r_other, 0.0)); - if (isfinite(feasible_other)) - hi = fmin(hi, feasible_other); - for (int it = 0; it < 1024; ++it) - { - double derivative = power_section_derivative(hi, r_other, abs_rz, w_other, wz, log_coefficient, other_exp); - if (!(derivative < 0.0)) - break; - if (isfinite(feasible_other) && hi >= feasible_other) - break; - double next_hi = hi * 2.0; - if (!isfinite(next_hi)) - { - hi = isfinite(feasible_other) ? feasible_other : DBL_MAX; - break; - } - hi = isfinite(feasible_other) ? fmin(next_hi, feasible_other) : next_hi; - } - - for (int it = 0; it < 80; ++it) - { - double other = lo + 0.5 * (hi - lo); - if (other == 0.0) - break; - double derivative = power_section_derivative(other, r_other, abs_rz, w_other, wz, log_coefficient, other_exp); - if (derivative < 0.0) - lo = other; - else - hi = other; - } - double other = lo + 0.5 * (hi - lo); - double projected_abs_z = other > 0.0 ? power_exp_from_log(log_coefficient + other_exp * log(other)) : 0.0; - *other_out = other; - *z_out = copysign(fmin(projected_abs_z, abs_rz), rz); -} - -__device__ static inline void project_power_section_fixed_axis(double fixed_axis, - double r_other, - double rz, - double w_other, - double wz, - double fixed_exp, - double other_exp, - double *other_out, - double *z_out) -{ - double scale = fmax(fixed_axis, fmax(fabs(r_other), fabs(rz))); - if (!(scale > 0.0) || !isfinite(scale)) - { - project_power_section_fixed_axis_normalized( - fixed_axis, r_other, rz, w_other, wz, fixed_exp, other_exp, other_out, z_out); - return; - } - - double normalized_other, normalized_z; - project_power_section_fixed_axis_normalized(fixed_axis / scale, - r_other / scale, - rz / scale, - w_other, - wz, - fixed_exp, - other_exp, - &normalized_other, - &normalized_z); - *other_out = normalized_other * scale; - *z_out = normalized_z * scale; -} - -__device__ static inline void project_power_cone_point_with_fixed(double rx, - double ry, - double rz, - double wx, - double wy, - double wz, - double alpha, - bool fixed_x, - bool fixed_y, - bool fixed_z, - double *xo, - double *yo, - double *zo) -{ - double om = 1.0 - alpha; - *xo = rx; - *yo = ry; - *zo = rz; - - if (!fixed_x && !fixed_y && !fixed_z) - { - project_power_cone_point(rx, ry, rz, wx, wy, wz, alpha, xo, yo, zo); - return; - } - - if (fixed_z) - { - if (fixed_x && fixed_y) - return; - if (fixed_x) - { - double lower = fabs(rz) == 0.0 ? 0.0 : exp((log(fabs(rz)) - alpha * log(rx)) / om); - *yo = fmax(ry, lower); - return; - } - if (fixed_y) - { - double lower = fabs(rz) == 0.0 ? 0.0 : exp((log(fabs(rz)) - om * log(ry)) / alpha); - *xo = fmax(rx, lower); - return; - } - project_power_xy_fixed_z(rx, ry, rz, wx, wy, alpha, xo, yo); - return; - } - - if (fixed_x && fixed_y) - { - double bound = 0.0; - if (rx > 0.0 && ry > 0.0) - { - double log_bound = alpha * log(rx) + om * log(ry); - bound = log_bound < log(DBL_MAX) ? exp(log_bound) : INFINITY; - } - *zo = fmax(-bound, fmin(rz, bound)); - return; - } - if (fixed_x) - { - project_power_section_fixed_axis(rx, ry, rz, wy, wz, alpha, om, yo, zo); - return; - } - if (fixed_y) - { - project_power_section_fixed_axis(ry, rx, rz, wx, wz, om, alpha, xo, zo); - return; - } -} - -__global__ void project_power_cone_kernel(double *__restrict__ primal_solution, - const double *__restrict__ variable_rescaling, - double *__restrict__ warm_start, - const int *__restrict__ start_idx, - const int *__restrict__ v_dim, - const double *__restrict__ power_alpha, - const char *__restrict__ is_fixed, - int num_blocks) -{ - (void)v_dim; - int blk = blockIdx.x * blockDim.x + threadIdx.x; - if (blk >= num_blocks) - return; - - int s_idx = start_idx[blk]; - double r1 = primal_solution[s_idx + 0]; - double r2 = primal_solution[s_idx + 1]; - double r3 = primal_solution[s_idx + 2]; - - double d1 = variable_rescaling[s_idx + 0]; - double d2 = variable_rescaling[s_idx + 1]; - double d3 = variable_rescaling[s_idx + 2]; - double alpha = power_alpha[blk]; - - /* Prox in scaled space with metric I equals prox in actual space with metric diag(d^2). */ - double rx = r1 / d1; - double ry = r2 / d2; - double rz = r3 / d3; - double wx = d1 * d1; - double wy = d2 * d2; - double wz = d3 * d3; - double xo, yo, zo; - project_power_cone_point_with_fixed(rx, - ry, - rz, - wx, - wy, - wz, - alpha, - is_fixed && is_fixed[s_idx + 0], - is_fixed && is_fixed[s_idx + 1], - is_fixed && is_fixed[s_idx + 2], - &xo, - &yo, - &zo); - primal_solution[s_idx + 0] = xo * d1; - primal_solution[s_idx + 1] = yo * d2; - primal_solution[s_idx + 2] = zo * d3; -} - -__global__ void compute_cone_dual_residual_power_kernel(double *__restrict__ dual_residual, - double *__restrict__ complementarity_residual, - const double *__restrict__ objective_vector, - const double *__restrict__ dual_product, - const double *__restrict__ variable_rescaling, - const double *__restrict__ primal_solution, - double *__restrict__ warm_start, - const int *__restrict__ start_idx, - const int *__restrict__ v_dim, - const double *__restrict__ power_alpha, - const char *__restrict__ is_fixed, - int num_blocks) -{ - (void)warm_start; - (void)v_dim; - int blk = blockIdx.x * blockDim.x + threadIdx.x; - if (blk >= num_blocks) - return; - - int s_idx = start_idx[blk]; - double r1 = objective_vector[s_idx + 0] - dual_product[s_idx + 0]; - double r2 = objective_vector[s_idx + 1] - dual_product[s_idx + 1]; - double r3 = objective_vector[s_idx + 2] - dual_product[s_idx + 2]; - double alpha = power_alpha[blk]; - - bool fixed_x = is_fixed && is_fixed[s_idx + 0]; - bool fixed_y = is_fixed && is_fixed[s_idx + 1]; - bool fixed_z = is_fixed && is_fixed[s_idx + 2]; - if (fixed_x || fixed_y || fixed_z) - { - double d1 = variable_rescaling[s_idx + 0]; - double d2 = variable_rescaling[s_idx + 1]; - double d3 = variable_rescaling[s_idx + 2]; - double x = primal_solution[s_idx + 0] / d1; - double y = primal_solution[s_idx + 1] / d2; - double z = primal_solution[s_idx + 2] / d3; - double q1 = r1 * d1; - double q2 = r2 * d2; - double q3 = r3 * d3; - - dual_residual[s_idx + 0] = fixed_x ? 0.0 : q1; - dual_residual[s_idx + 1] = fixed_y ? 0.0 : q2; - dual_residual[s_idx + 2] = fixed_z ? 0.0 : q3; - if (fixed_x && fixed_y && fixed_z) - return; - - double abs_z = fabs(z); - double bound = 0.0; - bool regular = x > 0.0 && y > 0.0 && isfinite(x) && isfinite(y) && isfinite(z); - if (regular) - { - double log_bound = alpha * log(x) + (1.0 - alpha) * log(y); - bound = exp(log_bound); - regular = isfinite(bound) && bound > 0.0; - } - - if (regular && abs_z > 0.0) - { - double normal[3] = { - -alpha * bound / x, - -(1.0 - alpha) * bound / y, - copysign(1.0, z), - }; - double q[3] = {q1, q2, q3}; - bool fixed[3] = {fixed_x, fixed_y, fixed_z}; - double normal_scale = 0.0; - for (int i = 0; i < 3; ++i) - { - if (!fixed[i]) - normal_scale = fmax(normal_scale, fabs(normal[i])); - } - if (!(normal_scale > 0.0) || !isfinite(normal_scale)) - { - regular = false; - } - - double dot = 0.0; - double normal2 = 0.0; - for (int i = 0; i < 3 && regular; ++i) - { - if (!fixed[i]) - { - double scaled_normal = normal[i] / normal_scale; - dot += q[i] * scaled_normal; - normal2 += scaled_normal * scaled_normal; - } - } - if (regular) - { - double scaled_lambda = (dot < 0.0 && normal2 > 0.0) ? -dot / normal2 : 0.0; - double lambda = scaled_lambda / normal_scale; - for (int i = 0; i < 3; ++i) - { - if (!fixed[i]) - dual_residual[s_idx + i] = q[i] + scaled_lambda * (normal[i] / normal_scale); - } - double slack_scale = fmax(1.0, fmax(bound, abs_z)); - double complementarity = lambda * (fmax(bound - abs_z, 0.0) / slack_scale); - complementarity_residual[blk] = complementarity; - return; - } - } - - if (regular) - return; - - /* Degenerate axes are nonsmooth. A unit metric projection supplies a - scale-independent KKT guard without changing the adaptive mapping. */ - double rx = x - (fixed_x ? 0.0 : r1 / d1); - double ry = y - (fixed_y ? 0.0 : r2 / d2); - double rz = z - (fixed_z ? 0.0 : r3 / d3); - double xo, yo, zo; - project_power_cone_point_with_fixed( - rx, ry, rz, d1 * d1, d2 * d2, d3 * d3, alpha, fixed_x, fixed_y, fixed_z, &xo, &yo, &zo); - if (!fixed_x) - dual_residual[s_idx + 0] = (x - xo) * d1 * d1; - if (!fixed_y) - dual_residual[s_idx + 1] = (y - yo) * d2 * d2; - if (!fixed_z) - dual_residual[s_idx + 2] = (z - zo) * d3 * d3; - return; - } - - double vr1 = variable_rescaling[s_idx + 0]; - double vr2 = variable_rescaling[s_idx + 1]; - double vr3 = variable_rescaling[s_idx + 2]; - - /* Moreau via primal projection: dual_res = -Proj_K(-r * vr). */ - double xo, yo, zo; - project_power_cone_point(-r1 * vr1, -r2 * vr2, -r3 * vr3, 1.0, 1.0, 1.0, alpha, &xo, &yo, &zo); - - dual_residual[s_idx + 0] = -xo; - dual_residual[s_idx + 1] = -yo; - dual_residual[s_idx + 2] = -zo; -} - -__global__ void compute_power_cone_primal_violation_kernel(double *__restrict__ absolute_violation, - double *__restrict__ relative_violation, - const double *__restrict__ primal_solution, - const double *__restrict__ variable_rescaling, - const int *__restrict__ start_idx, - const double *__restrict__ power_alpha, - double homogeneous_scale, - int num_blocks) -{ - int blk = blockIdx.x * blockDim.x + threadIdx.x; - if (blk >= num_blocks) - return; - - int start = start_idx[blk]; - double x = primal_solution[start + 0] / variable_rescaling[start + 0]; - double y = primal_solution[start + 1] / variable_rescaling[start + 1]; - double z = primal_solution[start + 2] / variable_rescaling[start + 2]; - if (!isfinite(x) || !isfinite(y) || !isfinite(z)) - { - absolute_violation[blk] = INFINITY; - relative_violation[blk] = INFINITY; - return; - } - double violation = fmax(-x, -y); - double abs_z = fabs(z); - if (abs_z > 0.0) - { - double bound = 0.0; - if (x > 0.0 && y > 0.0) - { - double alpha = power_alpha[blk]; - double log_bound = alpha * log(x) + (1.0 - alpha) * log(y); - double log_abs_z = log(abs_z); - double roundoff_tolerance = 64.0 * DBL_EPSILON * (1.0 + fabs(log_bound) + fabs(log_abs_z)); - if (log_bound + roundoff_tolerance >= log_abs_z) - { - violation = fmax(violation, 0.0); - absolute_violation[blk] = violation; - relative_violation[blk] = violation / (homogeneous_scale + fmax(fabs(x), fmax(fabs(y), abs_z))); - return; - } - bound = exp(log_bound); - } - violation = fmax(violation, abs_z - bound); - } - violation = fmax(violation, 0.0); - absolute_violation[blk] = violation; - relative_violation[blk] = violation / (homogeneous_scale + fmax(fabs(x), fmax(fabs(y), abs_z))); -} - -__global__ void project_power_cone_diag_q_kernel(double *__restrict__ pdhg_primal, - double *__restrict__ reflected_primal, - const double *__restrict__ current_primal, - const double *__restrict__ variable_rescaling, - const double *__restrict__ Q_diag, - double tau, - double *__restrict__ warm_start, - const int *__restrict__ start_idx, - const int *__restrict__ v_dim, - const double *__restrict__ power_alpha, - const char *__restrict__ is_fixed, - int num_blocks) -{ - (void)warm_start; - (void)v_dim; - int blk = blockIdx.x * blockDim.x + threadIdx.x; - if (blk >= num_blocks) - return; - - int s_idx = start_idx[blk]; - double r1 = pdhg_primal[s_idx + 0]; - double r2 = pdhg_primal[s_idx + 1]; - double r3 = pdhg_primal[s_idx + 2]; - - double d1 = variable_rescaling[s_idx + 0]; - double d2 = variable_rescaling[s_idx + 1]; - double d3 = variable_rescaling[s_idx + 2]; - double alpha = power_alpha[blk]; - - /* Effective weight in actual space: omega_i = (1 + tau*Q_ii) * d_i^2. */ - double w1 = 1.0 + tau * Q_diag[s_idx + 0]; - double w2 = 1.0 + tau * Q_diag[s_idx + 1]; - double w3 = 1.0 + tau * Q_diag[s_idx + 2]; - double om_x = w1 * d1 * d1; - double om_y = w2 * d2 * d2; - double om_z = w3 * d3 * d3; - double rx = r1 / d1; - double ry = r2 / d2; - double rz = r3 / d3; - double xo, yo, zo; - project_power_cone_point_with_fixed(rx, - ry, - rz, - om_x, - om_y, - om_z, - alpha, - is_fixed && is_fixed[s_idx + 0], - is_fixed && is_fixed[s_idx + 1], - is_fixed && is_fixed[s_idx + 2], - &xo, - &yo, - &zo); - pdhg_primal[s_idx + 0] = xo * d1; - pdhg_primal[s_idx + 1] = yo * d2; - pdhg_primal[s_idx + 2] = zo * d3; - for (int m = 0; m < 3; ++m) - { - int idx = s_idx + m; - reflected_primal[idx] = 2.0 * pdhg_primal[idx] - current_primal[idx]; - } -} - -__global__ void set_cone_dual_slack_kernel(double *__restrict__ dual_slack, - const double *__restrict__ objective_vector, - const double *__restrict__ dual_product, - const int *__restrict__ start_idx, - const int *__restrict__ v_dim, - int num_blocks) -{ - int blk = blockIdx.x * blockDim.x + threadIdx.x; - if (blk >= num_blocks) - return; - int start = start_idx[blk]; - int k = v_dim[blk]; - for (int m = 0; m < k + 2; ++m) - { - int idx = start + m; - dual_slack[idx] = objective_vector[idx] - dual_product[idx]; - } -} - -__global__ void set_cone_dual_slack_grid_kernel(double *__restrict__ dual_slack, - const double *__restrict__ objective_vector, - const double *__restrict__ dual_product, - const int *__restrict__ start_idx, - const int *__restrict__ v_dim, - int num_cones, - int blocks_per_cone) -{ - int cone = blockIdx.x / blocks_per_cone; - if (cone >= num_cones) - return; - - int part = blockIdx.x - cone * blocks_per_cone; - int start = start_idx[cone]; - int n = v_dim[cone] + 2; - for (int m = part * blockDim.x + threadIdx.x; m < n; m += blocks_per_cone * blockDim.x) - { - int idx = start + m; - dual_slack[idx] = objective_vector[idx] - dual_product[idx]; - } -} - -__global__ void set_cone_dual_slack_warp_kernel(double *__restrict__ dual_slack, - const double *__restrict__ objective_vector, - const double *__restrict__ dual_product, - const int *__restrict__ start_idx, - const int *__restrict__ v_dim, - int num_cones) -{ - int global_thread = blockIdx.x * blockDim.x + threadIdx.x; - int cone = global_thread >> 5; - if (cone >= num_cones) - return; - - int lane = global_thread & 31; - int start = start_idx[cone]; - int n = v_dim[cone] + 2; - for (int m = lane; m < n; m += 32) - { - int idx = start + m; - dual_slack[idx] = objective_vector[idx] - dual_product[idx]; - } -} - -__global__ void recompute_reflected_at_cone_kernel(double *__restrict__ reflected_primal, - const double *__restrict__ pdhg_primal, - const double *__restrict__ current_primal, - const int *__restrict__ start_idx, - const int *__restrict__ v_dim, - int num_blocks) -{ - int blk = blockIdx.x * blockDim.x + threadIdx.x; - if (blk >= num_blocks) - return; - int start = start_idx[blk]; - int k = v_dim[blk]; - for (int m = 0; m < k + 2; ++m) - { - int idx = start + m; - reflected_primal[idx] = 2.0 * pdhg_primal[idx] - current_primal[idx]; - } -} - -__global__ void recompute_reflected_at_cone_warp_kernel(double *__restrict__ reflected_primal, - const double *__restrict__ pdhg_primal, - const double *__restrict__ current_primal, - const int *__restrict__ start_idx, - const int *__restrict__ v_dim, - int num_cones) -{ - int global_thread = blockIdx.x * blockDim.x + threadIdx.x; - int cone = global_thread >> 5; - if (cone >= num_cones) - return; - - int lane = global_thread & 31; - int start = start_idx[cone]; - int n = v_dim[cone] + 2; - for (int m = lane; m < n; m += 32) - { - int idx = start + m; - reflected_primal[idx] = 2.0 * pdhg_primal[idx] - current_primal[idx]; - } -} - -__global__ void recompute_reflected_at_cone_grid_kernel(double *__restrict__ reflected_primal, - const double *__restrict__ pdhg_primal, - const double *__restrict__ current_primal, - const int *__restrict__ start_idx, - const int *__restrict__ v_dim, - int num_cones, - int blocks_per_cone) -{ - int cone = blockIdx.x / blocks_per_cone; - if (cone >= num_cones) - return; - - int part = blockIdx.x - cone * blocks_per_cone; - int start = start_idx[cone]; - int n = v_dim[cone] + 2; - for (int m = part * blockDim.x + threadIdx.x; m < n; m += blocks_per_cone * blockDim.x) - { - int idx = start + m; - reflected_primal[idx] = 2.0 * pdhg_primal[idx] - current_primal[idx]; - } -} - -/* Weighted prox onto D K_soc; effective rescaling e_i = sqrt(w_i) d_i, w_i = 1 + tau Q_i. */ -__global__ void project_standard_soc_diag_q_kernel(double *__restrict__ pdhg_primal, - double *__restrict__ reflected_primal, - const double *__restrict__ current_primal, - const double *__restrict__ variable_rescaling, - const double *__restrict__ Q_diag, - double tau, - double *__restrict__ warm_start, - const int *__restrict__ start_idx, - const int *__restrict__ v_dim, - const char *__restrict__ is_fixed, - int num_blocks) -{ - int blk = blockIdx.x * blockDim.x + threadIdx.x; - if (blk >= num_blocks) - return; - - int start = start_idx[blk]; - int k = v_dim[blk]; - int w_off = start + k; - int z_off = start + k + 1; - - if (cone_section_has_fixed(is_fixed, start, k + 2)) - { - project_standard_soc_section_serial( - pdhg_primal, variable_rescaling, Q_diag, tau, warm_start + blk, start, k, is_fixed); - for (int slot = 0; slot < k + 2; ++slot) - { - int index = start + slot; - reflected_primal[index] = 2.0 * pdhg_primal[index] - current_primal[index]; - } - return; - } - - double r_w = pdhg_primal[w_off]; - double r_z = pdhg_primal[z_off]; - - double d_z = variable_rescaling[z_off]; - double w_w = 1.0 + tau * Q_diag[w_off]; - double w_z = 1.0 + tau * Q_diag[z_off]; - double sqrt_w_w = sqrt(w_w); - double sqrt_w_z = sqrt(w_z); - double e_z = sqrt_w_z * d_z; - double e_w = sqrt_w_w * variable_rescaling[w_off]; - double eh_w = e_w / e_z; - double eh_w2 = eh_w * eh_w; - - double r_inv_sq = w_w * (r_w / eh_w) * (r_w / eh_w); - double r_pos_sq = w_w * (r_w * eh_w) * (r_w * eh_w); - for (int m = 0; m < k; ++m) - { - double w_m = 1.0 + tau * Q_diag[start + m]; - double e_m = sqrt(w_m) * variable_rescaling[start + m]; - double eh_m = e_m / e_z; - double r_m = pdhg_primal[start + m]; - r_inv_sq += w_m * (r_m / eh_m) * (r_m / eh_m); - r_pos_sq += w_m * (r_m * eh_m) * (r_m * eh_m); - } - double w_z_r_z_sq = w_z * r_z * r_z; - - if (r_inv_sq <= w_z_r_z_sq && r_z >= 0.0) - { - for (int m = 0; m < k; ++m) - { - int idx = start + m; - reflected_primal[idx] = 2.0 * pdhg_primal[idx] - current_primal[idx]; - } - reflected_primal[w_off] = 2.0 * r_w - current_primal[w_off]; - reflected_primal[z_off] = 2.0 * r_z - current_primal[z_off]; - return; - } - - if (r_pos_sq <= w_z_r_z_sq && r_z <= 0.0) - { - for (int m = 0; m < k; ++m) - { - int idx = start + m; - pdhg_primal[idx] = 0.0; - reflected_primal[idx] = -current_primal[idx]; - } - pdhg_primal[w_off] = 0.0; - pdhg_primal[z_off] = 0.0; - reflected_primal[w_off] = -current_primal[w_off]; - reflected_primal[z_off] = -current_primal[z_off]; - return; - } - - /* Fast path: no Q on cone slots and uniform d_v = d_z (LP-style symmetric case). */ - if (Q_diag[w_off] == 0.0 && Q_diag[z_off] == 0.0) - { - bool no_cone_Q = true; - bool d_uniform = (variable_rescaling[w_off] == d_z); - for (int m = 0; m < k; ++m) - { - if (Q_diag[start + m] != 0.0) - { - no_cone_Q = false; - break; - } - if (variable_rescaling[start + m] != d_z) - { - d_uniform = false; - break; - } - } - if (no_cone_Q && d_uniform) - { - double sumsq = r_w * r_w; - for (int m = 0; m < k; ++m) - { - double vm = pdhg_primal[start + m]; - sumsq += vm * vm; - } - double rnorm = sqrt(sumsq); - /* in-cone (rnorm <= r_z, r_z >= 0) and at-origin (rnorm <= -r_z, r_z <= 0) handled above */ - double scale = (r_z + rnorm) / (2.0 * rnorm); - for (int m = 0; m < k; ++m) - { - double v_new = scale * pdhg_primal[start + m]; - pdhg_primal[start + m] = v_new; - int idx = start + m; - reflected_primal[idx] = 2.0 * v_new - current_primal[idx]; - } - double w_new = scale * r_w; - double z_new = scale * rnorm; - pdhg_primal[w_off] = w_new; - pdhg_primal[z_off] = z_new; - reflected_primal[w_off] = 2.0 * w_new - current_primal[w_off]; - reflected_primal[z_off] = 2.0 * z_new - current_primal[z_off]; - return; - } - } - - double lo, hi; - bool z_pos = (r_z > 0.0); - if (z_pos) - { - lo = 0.0; - hi = 0.5 - 1e-14; - } - else - { - lo = 0.5 + 1e-14; - hi = 1.0; - for (int doubling = 0; doubling < 60; ++doubling) - { - double sum_hi = 0.0; - for (int m = 0; m < k; ++m) - { - double w_m = 1.0 + tau * Q_diag[start + m]; - double e_m = sqrt(w_m) * variable_rescaling[start + m]; - double eh_m = e_m / e_z; - double eh_m2 = eh_m * eh_m; - double r_m = pdhg_primal[start + m]; - double t = sqrt(w_m) * r_m * eh_m / (eh_m2 + 2.0 * hi); - sum_hi += t * t; - } - double tw_hi = sqrt_w_w * r_w * eh_w / (eh_w2 + 2.0 * hi); - sum_hi += tw_hi * tw_hi; - double tz_hi = sqrt_w_z * r_z / (1.0 - 2.0 * hi); - double f_hi = sum_hi - tz_hi * tz_hi; - if (f_hi > 0.0) - break; - lo = hi; - hi *= 2.0; - } - } - - double warm_lam = warm_start[blk]; - if (warm_lam > lo && warm_lam < hi) - { - double sum_w = 0.0; - for (int m = 0; m < k; ++m) - { - double w_m = 1.0 + tau * Q_diag[start + m]; - double e_m = sqrt(w_m) * variable_rescaling[start + m]; - double eh_m = e_m / e_z; - double eh_m2 = eh_m * eh_m; - double r_m = pdhg_primal[start + m]; - double t = sqrt(w_m) * r_m * eh_m / (eh_m2 + 2.0 * warm_lam); - sum_w += t * t; - } - double tw = sqrt_w_w * r_w * eh_w / (eh_w2 + 2.0 * warm_lam); - sum_w += tw * tw; - double tz = sqrt_w_z * r_z / (1.0 - 2.0 * warm_lam); - double f = sum_w - tz * tz; - if (fabs(f) < 1e-12) - { - double new_z = r_z / (1.0 - 2.0 * warm_lam); - double new_w = r_w * eh_w2 / (eh_w2 + 2.0 * warm_lam); - pdhg_primal[z_off] = new_z; - pdhg_primal[w_off] = new_w; - reflected_primal[z_off] = 2.0 * new_z - current_primal[z_off]; - reflected_primal[w_off] = 2.0 * new_w - current_primal[w_off]; - for (int m = 0; m < k; ++m) - { - int idx = start + m; - double w_m = 1.0 + tau * Q_diag[idx]; - double e_m = sqrt(w_m) * variable_rescaling[idx]; - double eh_m = e_m / e_z; - double eh_m2 = eh_m * eh_m; - double r_m = pdhg_primal[idx]; - double new_m = r_m * eh_m2 / (eh_m2 + 2.0 * warm_lam); - pdhg_primal[idx] = new_m; - reflected_primal[idx] = 2.0 * new_m - current_primal[idx]; - } - return; - } - if (z_pos) - { - if (f > 0.0) - lo = warm_lam; - else - hi = warm_lam; - } - else - { - if (f > 0.0) - hi = warm_lam; - else - lo = warm_lam; - } - } - - for (int it = 0; it < 60; ++it) - { - double lam = 0.5 * (lo + hi); - double sum = 0.0; - for (int m = 0; m < k; ++m) - { - double w_m = 1.0 + tau * Q_diag[start + m]; - double e_m = sqrt(w_m) * variable_rescaling[start + m]; - double eh_m = e_m / e_z; - double eh_m2 = eh_m * eh_m; - double r_m = pdhg_primal[start + m]; - double t = sqrt(w_m) * r_m * eh_m / (eh_m2 + 2.0 * lam); - sum += t * t; - } - double tw = sqrt_w_w * r_w * eh_w / (eh_w2 + 2.0 * lam); - sum += tw * tw; - double tz = sqrt_w_z * r_z / (1.0 - 2.0 * lam); - double f = sum - tz * tz; - if (z_pos) - { - if (f > 0.0) - lo = lam; - else - hi = lam; - } - else - { - if (f > 0.0) - hi = lam; - else - lo = lam; - } - if ((hi - lo) / (1.0 + hi + lo) < 1e-13) - break; - } - double lam = 0.5 * (lo + hi); - warm_start[blk] = lam; - - double new_z = r_z / (1.0 - 2.0 * lam); - double new_w = r_w * eh_w2 / (eh_w2 + 2.0 * lam); - pdhg_primal[z_off] = new_z; - pdhg_primal[w_off] = new_w; - reflected_primal[z_off] = 2.0 * new_z - current_primal[z_off]; - reflected_primal[w_off] = 2.0 * new_w - current_primal[w_off]; - for (int m = 0; m < k; ++m) - { - int idx = start + m; - double w_m = 1.0 + tau * Q_diag[idx]; - double e_m = sqrt(w_m) * variable_rescaling[idx]; - double eh_m = e_m / e_z; - double eh_m2 = eh_m * eh_m; - double r_m = pdhg_primal[idx]; - double new_m = r_m * eh_m2 / (eh_m2 + 2.0 * lam); - pdhg_primal[idx] = new_m; - reflected_primal[idx] = 2.0 * new_m - current_primal[idx]; - } -} - -/* Weighted prox onto D K_exp; coordinate change y_i = sqrt(w_i) x_i gives e_i = sqrt(w_i) d_i. */ -__global__ void project_exp_cone_diag_q_kernel(double *__restrict__ pdhg_primal, - double *__restrict__ reflected_primal, - const double *__restrict__ current_primal, - const double *__restrict__ variable_rescaling, - const double *__restrict__ Q_diag, - double tau, - double *__restrict__ warm_start, - const int *__restrict__ start_idx, - const int *__restrict__ v_dim, - const char *__restrict__ is_fixed, - int num_blocks) -{ - (void)v_dim; - int blk = blockIdx.x * blockDim.x + threadIdx.x; - if (blk >= num_blocks) - return; - - int s_idx = start_idx[blk]; - - if (cone_section_has_fixed(is_fixed, s_idx, 3)) - { - project_exp_cone_section(pdhg_primal, variable_rescaling, Q_diag, tau, warm_start + blk, s_idx, is_fixed); - for (int slot = 0; slot < 3; ++slot) - { - int index = s_idx + slot; - reflected_primal[index] = 2.0 * pdhg_primal[index] - current_primal[index]; - } - return; - } - - double r1 = pdhg_primal[s_idx + 0]; - double r2 = pdhg_primal[s_idx + 1]; - double r3 = pdhg_primal[s_idx + 2]; - - double d1 = variable_rescaling[s_idx + 0]; - double d2 = variable_rescaling[s_idx + 1]; - double d3 = variable_rescaling[s_idx + 2]; - - double w1 = 1.0 + tau * Q_diag[s_idx + 0]; - double w2 = 1.0 + tau * Q_diag[s_idx + 1]; - double w3 = 1.0 + tau * Q_diag[s_idx + 2]; - - /* Clamp guards against negative drift in Q_diag invalidating sqrt(w_i). */ - if (!(w1 > 0.0)) - w1 = 1.0; - if (!(w2 > 0.0)) - w2 = 1.0; - if (!(w3 > 0.0)) - w3 = 1.0; - - double sw1 = sqrt(w1); - double sw2 = sqrt(w2); - double sw3 = sqrt(w3); - - double e1 = sw1 * d1; - double e2 = sw2 * d2; - double e3 = sw3 * d3; - - double u1 = sw1 * r1; - double u2 = sw2 * r2; - double u3 = sw3 * r3; - double y1_out, y2_out, y3_out; - project_exp_cone_point(u1, u2, u3, e1, e2, e3, &y1_out, &y2_out, &y3_out); - double x1 = y1_out / sw1; - double x2 = y2_out / sw2; - double x3 = y3_out / sw3; - - pdhg_primal[s_idx + 0] = x1; - pdhg_primal[s_idx + 1] = x2; - pdhg_primal[s_idx + 2] = x3; - - reflected_primal[s_idx + 0] = 2.0 * x1 - current_primal[s_idx + 0]; - reflected_primal[s_idx + 1] = 2.0 * x2 - current_primal[s_idx + 1]; - reflected_primal[s_idx + 2] = 2.0 * x3 - current_primal[s_idx + 2]; -} - -/* Direct (s,t) bisection in zeta = xi/sqrt(w_s w_t); alpha = sqrt(w_t/w_s) carries asymmetry. */ -__global__ void project_rotated_soc_diag_q_kernel(double *__restrict__ pdhg_primal, - double *__restrict__ reflected_primal, - const double *__restrict__ current_primal, - const double *__restrict__ variable_rescaling, - const double *__restrict__ Q_diag, - double tau, - double *__restrict__ warm_start, - const int *__restrict__ start_idx, - const int *__restrict__ v_dim, - const char *__restrict__ is_fixed, - int num_blocks) -{ - int blk = blockIdx.x * blockDim.x + threadIdx.x; - if (blk >= num_blocks) - return; - - const double W_FLOOR = 1e-300; - - int start = start_idx[blk]; - int k = v_dim[blk]; - int len = k + 2; - - if (cone_section_has_fixed(is_fixed, start, len)) - { - project_rotated_soc_section_serial( - pdhg_primal, variable_rescaling, Q_diag, tau, warm_start + blk, start, k, is_fixed); - for (int slot = 0; slot < len; ++slot) - { - int index = start + slot; - reflected_primal[index] = 2.0 * pdhg_primal[index] - current_primal[index]; - } - return; - } - - double r_s = pdhg_primal[start + k]; - double r_t = pdhg_primal[start + k + 1]; - - double q_s = Q_diag[start + k]; - double q_t = Q_diag[start + k + 1]; - double w_s = 1.0 + tau * q_s; - double w_t = 1.0 + tau * q_t; - if (!(w_s > W_FLOOR)) - w_s = W_FLOOR; - if (!(w_t > W_FLOOR)) - w_t = W_FLOOR; - double sigma = sqrt(w_s * w_t); - double alpha = sqrt(w_t / w_s); - double inv_alpha = 1.0 / alpha; - - double d_s = variable_rescaling[start + k]; - double d_t = variable_rescaling[start + k + 1]; - double d_st = sqrt(d_s * d_t); - - const double INV_SQRT2 = 0.7071067811865475; - /* Fast path: no Q on cone slots (w_s = w_t = 1 and all w_v_i = 1) and uniform d_v = d_st. - This is the COMMON case for QCQP transform aux vars. Reduces to LP-style RSOC closed form. */ - if (q_s == 0.0 && q_t == 0.0) - { - bool no_cone_Q = true; - bool d_uniform = true; - for (int m = 0; m < k; ++m) - { - if (Q_diag[start + m] != 0.0) - { - no_cone_Q = false; - break; - } - if (variable_rescaling[start + m] != d_st) - { - d_uniform = false; - break; - } - } - if (no_cone_Q && d_uniform) - { - double w_val = (r_s - r_t) * INV_SQRT2; - double z_val = (r_s + r_t) * INV_SQRT2; - double sumsq = w_val * w_val; - for (int m = 0; m < k; ++m) - { - double vm = pdhg_primal[start + m]; - sumsq += vm * vm; - } - double rnorm = sqrt(sumsq); - if (rnorm <= z_val) - { - for (int m = 0; m < len; ++m) - { - int idx = start + m; - reflected_primal[idx] = 2.0 * pdhg_primal[idx] - current_primal[idx]; - } - return; - } - if (rnorm <= -z_val) - { - for (int m = 0; m < k; ++m) - { - pdhg_primal[start + m] = 0.0; - int idx = start + m; - reflected_primal[idx] = -current_primal[idx]; - } - pdhg_primal[start + k] = 0.0; - pdhg_primal[start + k + 1] = 0.0; - reflected_primal[start + k] = -current_primal[start + k]; - reflected_primal[start + k + 1] = -current_primal[start + k + 1]; - return; - } - double scale = (z_val + rnorm) / (2.0 * rnorm); - double w_new = scale * w_val; - double z_new = scale * rnorm; - for (int m = 0; m < k; ++m) - { - double v_new = scale * pdhg_primal[start + m]; - pdhg_primal[start + m] = v_new; - int idx = start + m; - reflected_primal[idx] = 2.0 * v_new - current_primal[idx]; - } - double s_new = (z_new + w_new) * INV_SQRT2; - double t_new = (z_new - w_new) * INV_SQRT2; - pdhg_primal[start + k] = s_new; - pdhg_primal[start + k + 1] = t_new; - reflected_primal[start + k] = 2.0 * s_new - current_primal[start + k]; - reflected_primal[start + k + 1] = 2.0 * t_new - current_primal[start + k + 1]; - return; - } - } - - { - double lhs = 0.0; - for (int m = 0; m < k; ++m) - { - double d_m = variable_rescaling[start + m]; - double Ds = d_st / d_m; - double rv = pdhg_primal[start + m]; - double term = Ds * rv; - lhs += term * term; - } - if (r_s >= 0.0 && r_t >= 0.0 && lhs <= 2.0 * r_s * r_t) - { - for (int m = 0; m < len; ++m) - { - int idx = start + m; - double pv = pdhg_primal[idx]; - reflected_primal[idx] = 2.0 * pv - current_primal[idx]; - } - return; - } - } - - if (r_s <= 0.0 && r_t <= 0.0) - { - double rhs = 2.0 * sigma * sigma * r_s * r_t; - double lhs = 0.0; - for (int m = 0; m < k; ++m) - { - double d_m = variable_rescaling[start + m]; - double q_m = Q_diag[start + m]; - double w_m = 1.0 + tau * q_m; - if (!(w_m > W_FLOOR)) - w_m = W_FLOOR; - double rv = pdhg_primal[start + m]; - double term = d_m * w_m * rv / d_st; - lhs += term * term; - } - if (lhs <= rhs) - { - for (int m = 0; m < k; ++m) - pdhg_primal[start + m] = 0.0; - pdhg_primal[start + k] = 0.0; - pdhg_primal[start + k + 1] = 0.0; - for (int m = 0; m < len; ++m) - { - int idx = start + m; - reflected_primal[idx] = -current_primal[idx]; - } - return; - } - } - - double lo, hi; - int bracket_kind; /* 0: f increasing on bracket; 1: f decreasing. */ - bool need_doubling = false; - double sum_alpha = r_s + alpha * r_t; - - if (r_s > 0.0 && r_t > 0.0) - { - lo = 0.0; - hi = 1.0 - 1e-14; - bracket_kind = 1; - } - else if (r_s < 0.0 && r_t < 0.0) - { - lo = 1.0 + 1e-14; - hi = 2.0; - bracket_kind = 0; - need_doubling = true; - } - else if (r_s <= 0.0 && r_t >= 0.0) - { - if (sum_alpha <= 0.0) - { - lo = 1.0 + 1e-14; - if (r_t == 0.0) - { - hi = 2.0; - need_doubling = true; - } - else - { - hi = -r_s / (alpha * r_t); - if (!(hi > lo)) - hi = lo + 1.0; - } - bracket_kind = 0; - } - else - { - lo = (r_t > 0.0) ? (-r_s / (alpha * r_t)) : 0.0; - if (!(lo >= 0.0)) - lo = 0.0; - hi = 1.0 - 1e-14; - if (!(lo < hi)) - lo = hi - 1e-7; - bracket_kind = 1; - } - } - else - { - if (sum_alpha <= 0.0) - { - lo = 1.0 + 1e-14; - if (r_s == 0.0) - { - hi = 2.0; - need_doubling = true; - } - else - { - hi = -alpha * r_t / r_s; - if (!(hi > lo)) - hi = lo + 1.0; - } - bracket_kind = 0; - } - else - { - lo = (r_s > 0.0) ? (-alpha * r_t / r_s) : 0.0; - if (!(lo >= 0.0)) - lo = 0.0; - hi = 1.0 - 1e-14; - if (!(lo < hi)) - lo = hi - 1e-7; - bracket_kind = 1; - } - } - -#define ORACLE_EVAL(ZETA, F_OUT) \ - do \ - { \ - double _zeta = (ZETA); \ - double _denom = 1.0 - _zeta * _zeta; \ - double _s = (r_s + _zeta * alpha * r_t) / _denom; \ - double _t = (r_t + _zeta * inv_alpha * r_s) / _denom; \ - double _sv = 0.0; \ - for (int _m = 0; _m < k; ++_m) \ - { \ - double _dm = variable_rescaling[start + _m]; \ - double _Ds = d_st / _dm; \ - double _qm = Q_diag[start + _m]; \ - double _wm = 1.0 + tau * _qm; \ - if (!(_wm > W_FLOOR)) \ - _wm = W_FLOOR; \ - double _Dh2 = _Ds * _Ds * sigma / _wm; \ - double _rv = pdhg_primal[start + _m]; \ - double _vz = _rv / (1.0 + _zeta * _Dh2); \ - double _tm = _Ds * _vz; \ - _sv += _tm * _tm; \ - } \ - (F_OUT) = _sv - 2.0 * _s * _t; \ - } while (0) - - if (need_doubling) - { - double f_hi; - for (int dbl = 0; dbl < 60; ++dbl) - { - ORACLE_EVAL(hi, f_hi); - if (f_hi >= 0.0) - break; - lo = hi; - hi *= 2.0; - } - } - - double warm_zeta = warm_start[blk]; - if (warm_zeta > lo && warm_zeta < hi) - { - double f_w; - ORACLE_EVAL(warm_zeta, f_w); - if (fabs(f_w) < 1e-12) - { - double zeta = warm_zeta; - double denom = 1.0 - zeta * zeta; - double s_new = (r_s + zeta * alpha * r_t) / denom; - double t_new = (r_t + zeta * inv_alpha * r_s) / denom; - for (int m = 0; m < k; ++m) - { - double d_m = variable_rescaling[start + m]; - double Ds = d_st / d_m; - double q_m = Q_diag[start + m]; - double w_m = 1.0 + tau * q_m; - if (!(w_m > W_FLOOR)) - w_m = W_FLOOR; - double Dh2 = Ds * Ds * sigma / w_m; - double rv = pdhg_primal[start + m]; - pdhg_primal[start + m] = rv / (1.0 + zeta * Dh2); - } - pdhg_primal[start + k] = s_new; - pdhg_primal[start + k + 1] = t_new; - for (int m = 0; m < len; ++m) - { - int idx = start + m; - double pv = pdhg_primal[idx]; - reflected_primal[idx] = 2.0 * pv - current_primal[idx]; - } - return; - } - if (bracket_kind == 0) - { - if (f_w < 0.0) - lo = warm_zeta; - else - hi = warm_zeta; - } - else - { - if (f_w > 0.0) - lo = warm_zeta; - else - hi = warm_zeta; - } - } - - for (int it = 0; it < 80; ++it) - { - double mid = 0.5 * (lo + hi); - double f_m; - ORACLE_EVAL(mid, f_m); - if (bracket_kind == 0) - { - if (f_m < 0.0) - lo = mid; - else - hi = mid; - } - else - { - if (f_m > 0.0) - lo = mid; - else - hi = mid; - } - if ((hi - lo) / (1.0 + fabs(hi) + fabs(lo)) < 1e-13) - break; - } - double zeta = 0.5 * (lo + hi); - warm_start[blk] = zeta; - - double denom = 1.0 - zeta * zeta; - double s_new = (r_s + zeta * alpha * r_t) / denom; - double t_new = (r_t + zeta * inv_alpha * r_s) / denom; - for (int m = 0; m < k; ++m) - { - double d_m = variable_rescaling[start + m]; - double Ds = d_st / d_m; - double q_m = Q_diag[start + m]; - double w_m = 1.0 + tau * q_m; - if (!(w_m > W_FLOOR)) - w_m = W_FLOOR; - double Dh2 = Ds * Ds * sigma / w_m; - double rv = pdhg_primal[start + m]; - pdhg_primal[start + m] = rv / (1.0 + zeta * Dh2); - } - pdhg_primal[start + k] = s_new; - pdhg_primal[start + k + 1] = t_new; - - for (int m = 0; m < len; ++m) - { - int idx = start + m; - double pv = pdhg_primal[idx]; - reflected_primal[idx] = 2.0 * pv - current_primal[idx]; - } -#undef ORACLE_EVAL -} diff --git a/src/kernels/pdhcg_exp_cone_kernels.cu b/src/kernels/pdhcg_exp_cone_kernels.cu new file mode 100644 index 0000000..c71da78 --- /dev/null +++ b/src/kernels/pdhcg_exp_cone_kernels.cu @@ -0,0 +1,769 @@ +/* +Copyright 2026 Hongpei Li + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +#include "cone_kernel_ops.h" +#include "cone_projection_utils.h" +#include "pdhcg_exp_cone_kernels.h" +#include "utils.h" + +#include +#include +#include + +__device__ static inline void project_exp_cone_point( + double r1, double r2, double r3, double d1, double d2, double d3, double *xo, double *yo, double *zo) +{ + const double E_CONST = 2.718281828459045; + double rr1 = r1 / d1, rr2 = r2 / d2, rr3 = r3 / d3; + + if (rr2 > 0.0) + { + double ratio = rr1 / rr2; + if (ratio < 700.0 && rr2 * exp(ratio) <= rr3) + { + *xo = r1; + *yo = r2; + *zo = r3; + return; + } + } + else if (rr2 == 0.0 && rr1 <= 0.0 && rr3 >= 0.0) + { + *xo = r1; + *yo = r2; + *zo = r3; + return; + } + + if (r1 > 0.0) + { + double ratio = (d2 * r2) / (d1 * r1); + if (ratio < 700.0 && d1 * r1 * exp(ratio) + E_CONST * d3 * r3 <= 0.0) + { + *xo = 0.0; + *yo = 0.0; + *zo = 0.0; + return; + } + } + else if (r1 == 0.0 && r2 <= 0.0 && r3 <= 0.0) + { + *xo = 0.0; + *yo = 0.0; + *zo = 0.0; + return; + } + + if (rr1 <= 0.0 && rr2 <= 0.0) + { + *xo = r1; + *yo = 0.0; + *zo = (rr3 < 0.0) ? 0.0 : r3; + return; + } + + double alpha = (d3 / d1) * (d3 / d1); + double beta = (d3 / d2) * (d3 / d2); + double rho = 0.0; + bool diverged = false; + for (int it = 0; it < 100; ++it) + { + if (rho >= 299.0 || rho <= -299.0) + { + diverged = true; + break; + } + double e_rho = exp(rho); + double e_2rho = e_rho * e_rho; + double one_m_rho = 1.0 - rho; + + double a_term = alpha - beta * rho * one_m_rho; + double b_term = beta * rr1 * one_m_rho - alpha * rr2; + double f = rr1 - rr2 * rho + rr3 * e_rho * a_term + e_2rho * b_term; + + double da_drho = -beta * (1.0 - 2.0 * rho); + double db_drho = -beta * rr1; + double df = -rr2 + rr3 * e_rho * (a_term + da_drho) + e_2rho * (2.0 * b_term + db_drho); + + if (fabs(df) < 1e-300) + break; + double step = f / df; + if (step > 10.0) + step = 10.0; + if (step < -10.0) + step = -10.0; + rho -= step; + if (fabs(step) < 1e-13 * (1.0 + fabs(rho))) + break; + } + + double e_rho = exp(rho); + double denom = rho + alpha * e_rho * e_rho; + double u2 = (rr1 + alpha * rr3 * e_rho) / denom; + + if (diverged || !isfinite(u2) || u2 <= 0.0) + { + *xo = (r1 < 0.0) ? r1 : 0.0; + *yo = 0.0; + *zo = (r3 > 0.0) ? r3 : 0.0; + return; + } + + double u1 = rho * u2; + double u3 = u2 * e_rho; + + *xo = d1 * u1; + *yo = d2 * u2; + *zo = d3 * u3; +} + +/* y-fixed cross-section of D K_exp: weighted 1D Newton-bisection on u = exp((rz/d_r)/y_eff). */ +__device__ static inline void project_2d_exp_persp( + double rz0, double ry, double rt0, double d_r, double d_y, double d_t, double *warm_start, double *rzo, double *rto) +{ + if (d_r <= 0.0 || d_y <= 0.0 || d_t <= 0.0) + { + *rzo = rz0; + *rto = rt0; + return; + } + double y_eff = ry / d_y; + if (y_eff <= 0.0) + { + *rzo = (rz0 < 0.0) ? rz0 : 0.0; + *rto = (rt0 > 0.0) ? rt0 : 0.0; + return; + } + + double arg = (rz0 / d_r) / y_eff; + if (arg < 700.0) + { + double rhs = y_eff * d_t * exp(arg); + if (rhs <= rt0) + { + *rzo = rz0; + *rto = rt0; + return; + } + } + + double a = d_t * d_t * y_eff; + double b = d_t * rt0; + double c = d_r * d_r * y_eff; + double e = d_r * rz0; + + double u = *warm_start; + double u_lo = 1e-30; + double u_hi = 1.0; + for (int g = 0; g < 200; ++g) + { + double lu = log(u_hi); + double f_hi = a * u_hi * u_hi - b * u_hi + c * lu - e; + if (isfinite(f_hi) && f_hi > 0.0) + break; + u_hi *= 4.0; + if (u_hi > 1e150) + break; + } + for (int g = 0; g < 200; ++g) + { + double lu = log(u_lo); + double f_lo = a * u_lo * u_lo - b * u_lo + c * lu - e; + if (isfinite(f_lo) && f_lo < 0.0) + break; + u_lo *= 0.25; + if (u_lo < 1e-300) + break; + } + if (u_lo >= u_hi) + { + *rzo = rz0; + *rto = rt0; + return; + } + + if (!(u > u_lo && u < u_hi) || !isfinite(u)) + u = exp(0.5 * (log(u_lo) + log(u_hi))); + + for (int it = 0; it < 80; ++it) + { + double lu = log(u); + double f = a * u * u - b * u + c * lu - e; + double df = 2.0 * a * u - b + c / u; + if (f > 0.0) + u_hi = u; + else + u_lo = u; + double u_new; + if (df > 1e-300 && isfinite(df) && isfinite(f)) + { + u_new = u - f / df; + if (!isfinite(u_new) || u_new <= u_lo || u_new >= u_hi) + u_new = exp(0.5 * (log(u_lo) + log(u_hi))); + } + else + { + u_new = exp(0.5 * (log(u_lo) + log(u_hi))); + } + if (fabs(u_new - u) < 1e-14 * (1.0 + fabs(u_new))) + { + u = u_new; + break; + } + u = u_new; + } + *warm_start = u; + *rzo = d_r * y_eff * log(u); + *rto = d_t * y_eff * u; +} + +__device__ static inline double exp_cone_boundary(double x, double y) +{ + if (!(y > 0.0)) + return x <= 0.0 ? 0.0 : INFINITY; + double exponent = x / y; + double log_value = log(y) + exponent; + if (log_value >= log(DBL_MAX)) + return INFINITY; + if (log_value <= log(DBL_MIN)) + return 0.0; + return exp(log_value); +} + +__device__ static inline bool exp_cone_contains_point(double x, double y, double z) +{ + if (y > 0.0 && z > 0.0) + { + double lhs = log(y) + x / y; + double rhs = log(z); + double tolerance = 64.0 * DBL_EPSILON * (1.0 + fabs(lhs) + fabs(rhs)); + return lhs <= rhs + tolerance; + } + return y == 0.0 && x <= 0.0 && z >= 0.0; +} + +__device__ static inline double +exp_fixed_x_objective(double y, double x, double input_y, double input_z, double weight_y, double weight_z) +{ + double z = exp_cone_boundary(x, y); + if (!isfinite(z)) + return INFINITY; + double dy = y - input_y; + double dz = z - input_z; + return weight_y * dy * dy + weight_z * dz * dz; +} + +__device__ static inline double +exp_fixed_z_objective(double y, double z, double input_x, double input_y, double weight_x, double weight_y) +{ + double x = y > 0.0 ? y * (log(z) - log(y)) : 0.0; + double dx = x - input_x; + double dy = y - input_y; + return weight_x * dx * dx + weight_y * dy * dy; +} + +__device__ static inline double exp_xz_log_violation(double y, double x, double z) +{ + if (!(y > 0.0) || !(z > 0.0)) + return x <= 0.0 ? -INFINITY : INFINITY; + return log(y) + x / y - log(z); +} + +__device__ static inline void project_exp_cone_section(double *point, + const double *rescaling, + const double *q_diag, + double tau, + double *warm_start, + int start, + const char *is_fixed) +{ + bool fixed_x = is_fixed[start + 0] != 0; + bool fixed_y = is_fixed[start + 1] != 0; + bool fixed_z = is_fixed[start + 2] != 0; + double input_x = point[start + 0] / rescaling[start + 0]; + double input_y = point[start + 1] / rescaling[start + 1]; + double input_z = point[start + 2] / rescaling[start + 2]; + + if (exp_cone_contains_point(input_x, input_y, input_z) || (fixed_x && fixed_y && fixed_z)) + return; + + double weight_x = cone_section_weight(rescaling, q_diag, tau, start + 0); + double weight_y = cone_section_weight(rescaling, q_diag, tau, start + 1); + double weight_z = cone_section_weight(rescaling, q_diag, tau, start + 2); + double output_x = input_x; + double output_y = input_y; + double output_z = input_z; + + if (fixed_x && fixed_y) + { + output_z = fmax(input_z, exp_cone_boundary(input_x, input_y)); + } + else if (fixed_y && fixed_z) + { + if (input_y == 0.0) + output_x = fmin(input_x, 0.0); + else + output_x = fmin(input_x, input_y * (log(input_z) - log(input_y))); + } + else if (fixed_x && fixed_z) + { + if (input_z == 0.0) + { + output_y = 0.0; + } + else if (input_x > 0.0) + { + double center = input_x; + double left = fmax(DBL_MIN, input_x / 1024.0); + while (exp_xz_log_violation(left, input_x, input_z) <= 0.0 && left > DBL_MIN) + left *= 0.5; + double lo = left; + double hi = center; + for (int iteration = 0; iteration < 100; ++iteration) + { + double mid = 0.5 * (lo + hi); + if (exp_xz_log_violation(mid, input_x, input_z) > 0.0) + lo = mid; + else + hi = mid; + } + double lower = 0.5 * (lo + hi); + + lo = center; + hi = fmax(2.0 * center, input_z); + while (exp_xz_log_violation(hi, input_x, input_z) < 0.0 && hi < DBL_MAX / 4.0) + hi *= 2.0; + for (int iteration = 0; iteration < 100; ++iteration) + { + double mid = 0.5 * (lo + hi); + if (exp_xz_log_violation(mid, input_x, input_z) <= 0.0) + lo = mid; + else + hi = mid; + } + double upper = 0.5 * (lo + hi); + output_y = fmin(fmax(input_y, lower), upper); + } + else + { + double lo = 0.0; + double hi = fmax(1.0, fmax(input_z, fabs(input_x))); + while (exp_xz_log_violation(hi, input_x, input_z) < 0.0 && hi < DBL_MAX / 4.0) + hi *= 2.0; + for (int iteration = 0; iteration < 100; ++iteration) + { + double mid = 0.5 * (lo + hi); + if (exp_xz_log_violation(mid, input_x, input_z) <= 0.0) + lo = mid; + else + hi = mid; + } + output_y = fmin(fmax(input_y, 0.0), 0.5 * (lo + hi)); + } + } + else if (fixed_y) + { + if (!(input_y > 0.0)) + { + output_x = fmin(input_x, 0.0); + output_z = fmax(input_z, 0.0); + } + else + { + double effective_x = sqrt(weight_x); + double effective_y = sqrt(weight_y); + double effective_z = sqrt(weight_z); + double scaled_x; + double scaled_z; + project_2d_exp_persp(effective_x * input_x, + effective_y * input_y, + effective_z * input_z, + effective_x, + effective_y, + effective_z, + warm_start, + &scaled_x, + &scaled_z); + output_x = scaled_x / effective_x; + output_z = scaled_z / effective_z; + } + } + else if (fixed_x) + { + double scale = 1.0 + fabs(input_x) + fabs(input_y) + fabs(input_z); + double lo = input_x > 0.0 ? fmax(DBL_MIN, input_x / 700.0) : 0.0; + double hi = scale; + double previous = exp_fixed_x_objective(0.5 * hi, input_x, input_y, input_z, weight_y, weight_z); + double current = exp_fixed_x_objective(hi, input_x, input_y, input_z, weight_y, weight_z); + for (int expansion = 0; expansion < 80 && current < previous && hi < DBL_MAX / 4.0; ++expansion) + { + previous = current; + hi *= 2.0; + current = exp_fixed_x_objective(hi, input_x, input_y, input_z, weight_y, weight_z); + } + const double ratio = 0.6180339887498948482; + double a = lo; + double b = hi; + double c = b - ratio * (b - a); + double d = a + ratio * (b - a); + double fc = exp_fixed_x_objective(c, input_x, input_y, input_z, weight_y, weight_z); + double fd = exp_fixed_x_objective(d, input_x, input_y, input_z, weight_y, weight_z); + for (int iteration = 0; iteration < 100; ++iteration) + { + if (fc <= fd) + { + b = d; + d = c; + fd = fc; + c = b - ratio * (b - a); + fc = exp_fixed_x_objective(c, input_x, input_y, input_z, weight_y, weight_z); + } + else + { + a = c; + c = d; + fc = fd; + d = a + ratio * (b - a); + fd = exp_fixed_x_objective(d, input_x, input_y, input_z, weight_y, weight_z); + } + } + output_y = 0.5 * (a + b); + output_z = exp_cone_boundary(input_x, output_y); + if (input_x <= 0.0) + { + double closure_z = fmax(input_z, 0.0); + double closure_objective = + weight_y * input_y * input_y + weight_z * (closure_z - input_z) * (closure_z - input_z); + double smooth_objective = exp_fixed_x_objective(output_y, input_x, input_y, input_z, weight_y, weight_z); + if (closure_objective <= smooth_objective) + { + output_y = 0.0; + output_z = closure_z; + } + } + } + else if (fixed_z) + { + if (input_z == 0.0) + { + output_x = fmin(input_x, 0.0); + output_y = 0.0; + } + else + { + double scale = 1.0 + fabs(input_x) + fabs(input_y) + input_z; + double lo = 0.0; + double hi = scale; + double previous = exp_fixed_z_objective(0.5 * hi, input_z, input_x, input_y, weight_x, weight_y); + double current = exp_fixed_z_objective(hi, input_z, input_x, input_y, weight_x, weight_y); + for (int expansion = 0; expansion < 80 && current < previous && hi < DBL_MAX / 4.0; ++expansion) + { + previous = current; + hi *= 2.0; + current = exp_fixed_z_objective(hi, input_z, input_x, input_y, weight_x, weight_y); + } + const double ratio = 0.6180339887498948482; + double a = lo; + double b = hi; + double c = b - ratio * (b - a); + double d = a + ratio * (b - a); + double fc = exp_fixed_z_objective(c, input_z, input_x, input_y, weight_x, weight_y); + double fd = exp_fixed_z_objective(d, input_z, input_x, input_y, weight_x, weight_y); + for (int iteration = 0; iteration < 100; ++iteration) + { + if (fc <= fd) + { + b = d; + d = c; + fd = fc; + c = b - ratio * (b - a); + fc = exp_fixed_z_objective(c, input_z, input_x, input_y, weight_x, weight_y); + } + else + { + a = c; + c = d; + fc = fd; + d = a + ratio * (b - a); + fd = exp_fixed_z_objective(d, input_z, input_x, input_y, weight_x, weight_y); + } + } + output_y = 0.5 * (a + b); + output_x = output_y > 0.0 ? output_y * (log(input_z) - log(output_y)) : 0.0; + double closure_x = fmin(input_x, 0.0); + double closure_objective = + weight_x * (closure_x - input_x) * (closure_x - input_x) + weight_y * input_y * input_y; + double smooth_objective = exp_fixed_z_objective(output_y, input_z, input_x, input_y, weight_x, weight_y); + if (closure_objective <= smooth_objective) + { + output_x = closure_x; + output_y = 0.0; + } + } + } + + if (!fixed_x) + point[start + 0] = output_x * rescaling[start + 0]; + if (!fixed_y) + point[start + 1] = output_y * rescaling[start + 1]; + if (!fixed_z) + point[start + 2] = output_z * rescaling[start + 2]; +} + +__global__ void project_exp_cone_kernel(double *__restrict__ primal_solution, + const double *__restrict__ variable_rescaling, + double *__restrict__ warm_start, + const int *__restrict__ start_idx, + const int *__restrict__ v_dim, + const char *__restrict__ is_fixed, + int num_blocks) +{ + (void)v_dim; + int blk = blockIdx.x * blockDim.x + threadIdx.x; + if (blk >= num_blocks) + return; + + int s_idx = start_idx[blk]; + double r1 = primal_solution[s_idx + 0]; + double r2 = primal_solution[s_idx + 1]; + double r3 = primal_solution[s_idx + 2]; + + double d1 = variable_rescaling[s_idx + 0]; + double d2 = variable_rescaling[s_idx + 1]; + double d3 = variable_rescaling[s_idx + 2]; + + if (cone_section_has_fixed(is_fixed, s_idx, 3)) + { + project_exp_cone_section(primal_solution, variable_rescaling, NULL, 0.0, warm_start + blk, s_idx, is_fixed); + return; + } + + double xo, yo, zo; + project_exp_cone_point(r1, r2, r3, d1, d2, d3, &xo, &yo, &zo); + + primal_solution[s_idx + 0] = xo; + primal_solution[s_idx + 1] = yo; + primal_solution[s_idx + 2] = zo; +} + +__global__ void compute_cone_dual_residual_exp_kernel(double *__restrict__ dual_residual, + double *__restrict__ complementarity_residual, + const double *__restrict__ objective_vector, + const double *__restrict__ dual_product, + const double *__restrict__ variable_rescaling, + const double *__restrict__ primal_solution, + double *__restrict__ warm_start, + const int *__restrict__ start_idx, + const int *__restrict__ v_dim, + const char *__restrict__ is_fixed, + int num_blocks) +{ + (void)v_dim; + int blk = blockIdx.x * blockDim.x + threadIdx.x; + if (blk >= num_blocks) + return; + + int s_idx = start_idx[blk]; + double r1 = objective_vector[s_idx + 0] - dual_product[s_idx + 0]; + double r2 = objective_vector[s_idx + 1] - dual_product[s_idx + 1]; + double r3 = objective_vector[s_idx + 2] - dual_product[s_idx + 2]; + + if (cone_section_has_fixed(is_fixed, s_idx, 3)) + { + const double residual[3] = {r1, r2, r3}; + for (int slot = 0; slot < 3; ++slot) + { + int index = s_idx + slot; + dual_residual[index] = is_fixed[index] ? primal_solution[index] : primal_solution[index] - residual[slot]; + } + project_exp_cone_section(dual_residual, variable_rescaling, NULL, 0.0, warm_start + blk, s_idx, is_fixed); + for (int slot = 0; slot < 3; ++slot) + { + int index = s_idx + slot; + dual_residual[index] = + is_fixed[index] ? 0.0 : (primal_solution[index] - dual_residual[index]) * variable_rescaling[index]; + } + complementarity_residual[blk] = 0.0; + return; + } + + double d1 = 1.0 / variable_rescaling[s_idx + 0]; + double d2 = 1.0 / variable_rescaling[s_idx + 1]; + double d3 = 1.0 / variable_rescaling[s_idx + 2]; + + /* Moreau: dist(r, K_exp^*) = ||-proj_{K_exp}(-r)|| with inverse-scaled d. */ + double xo, yo, zo; + project_exp_cone_point(-r1, -r2, -r3, d1, d2, d3, &xo, &yo, &zo); + + dual_residual[s_idx + 0] = -xo * variable_rescaling[s_idx + 0]; + dual_residual[s_idx + 1] = -yo * variable_rescaling[s_idx + 1]; + dual_residual[s_idx + 2] = -zo * variable_rescaling[s_idx + 2]; +} + +/* 3-dim alpha-power cone K_a = {(x,y,z) : x >= 0, y >= 0, x^a * y^(1-a) >= |z|}. + Weighted projection: solves + min_{(x,y,z) in K_a} 0.5 * ( wx*(x-rx)^2 + wy*(y-ry)^2 + wz*(z-rz)^2 ) + with wi > 0. In-cone test is metric-independent; opposite-cone test is not. + Bisection on rho = |z_proj| in [0, |r_z|] using KKT-derived formulas + x(rho) = 0.5 (rx + sqrt(rx^2 + 4 a (wz/wx) rho (|rz|-rho))) + y(rho) = 0.5 (ry + sqrt(ry^2 + 4 (1-a) (wz/wy) rho (|rz|-rho))) + G(rho) = x^a y^(1-a) - rho. */ +__global__ void project_exp_cone_diag_q_kernel(double *__restrict__ pdhg_primal, + double *__restrict__ reflected_primal, + const double *__restrict__ current_primal, + const double *__restrict__ variable_rescaling, + const double *__restrict__ Q_diag, + double tau, + double *__restrict__ warm_start, + const int *__restrict__ start_idx, + const int *__restrict__ v_dim, + const char *__restrict__ is_fixed, + int num_blocks) +{ + (void)v_dim; + int blk = blockIdx.x * blockDim.x + threadIdx.x; + if (blk >= num_blocks) + return; + + int s_idx = start_idx[blk]; + + if (cone_section_has_fixed(is_fixed, s_idx, 3)) + { + project_exp_cone_section(pdhg_primal, variable_rescaling, Q_diag, tau, warm_start + blk, s_idx, is_fixed); + for (int slot = 0; slot < 3; ++slot) + { + int index = s_idx + slot; + reflected_primal[index] = 2.0 * pdhg_primal[index] - current_primal[index]; + } + return; + } + + double r1 = pdhg_primal[s_idx + 0]; + double r2 = pdhg_primal[s_idx + 1]; + double r3 = pdhg_primal[s_idx + 2]; + + double d1 = variable_rescaling[s_idx + 0]; + double d2 = variable_rescaling[s_idx + 1]; + double d3 = variable_rescaling[s_idx + 2]; + + double w1 = 1.0 + tau * Q_diag[s_idx + 0]; + double w2 = 1.0 + tau * Q_diag[s_idx + 1]; + double w3 = 1.0 + tau * Q_diag[s_idx + 2]; + + /* Clamp guards against negative drift in Q_diag invalidating sqrt(w_i). */ + if (!(w1 > 0.0)) + w1 = 1.0; + if (!(w2 > 0.0)) + w2 = 1.0; + if (!(w3 > 0.0)) + w3 = 1.0; + + double sw1 = sqrt(w1); + double sw2 = sqrt(w2); + double sw3 = sqrt(w3); + + double e1 = sw1 * d1; + double e2 = sw2 * d2; + double e3 = sw3 * d3; + + double u1 = sw1 * r1; + double u2 = sw2 * r2; + double u3 = sw3 * r3; + double y1_out, y2_out, y3_out; + project_exp_cone_point(u1, u2, u3, e1, e2, e3, &y1_out, &y2_out, &y3_out); + double x1 = y1_out / sw1; + double x2 = y2_out / sw2; + double x3 = y3_out / sw3; + + pdhg_primal[s_idx + 0] = x1; + pdhg_primal[s_idx + 1] = x2; + pdhg_primal[s_idx + 2] = x3; + + reflected_primal[s_idx + 0] = 2.0 * x1 - current_primal[s_idx + 0]; + reflected_primal[s_idx + 1] = 2.0 * x2 - current_primal[s_idx + 1]; + reflected_primal[s_idx + 2] = 2.0 * x3 - current_primal[s_idx + 2]; +} + +/* Direct (s,t) bisection in zeta = xi/sqrt(w_s w_t); alpha = sqrt(w_t/w_s) carries asymmetry. */ + +static void launch_exp_thread_proj( + double *p, const double *vr, double *ws, const int *si, const int *vd, const double *pa, const char *isf, int n) +{ + (void)pa; + int t = THREADS_PER_BLOCK; + int b = (n + t - 1) / t; + project_exp_cone_kernel<<>>(p, vr, ws, si, vd, isf, n); +} +static void launch_exp_thread_dual(double *dr, + double *cr, + const double *obj, + const double *dp, + const double *vr, + const double *ps, + double *ws, + const int *si, + const int *vd, + const double *pa, + const char *isf, + int n) +{ + (void)pa; + int t = THREADS_PER_BLOCK; + int b = (n + t - 1) / t; + compute_cone_dual_residual_exp_kernel<<>>(dr, cr, obj, dp, vr, ps, ws, si, vd, isf, n); +} +static void launch_exp_thread_proj_diag_q(double *pp, + double *rp, + const double *cp, + const double *vr, + const double *qd, + double tau, + double *ws, + const int *si, + const int *vd, + const double *pa, + const char *isf, + int n) +{ + (void)pa; + int t = THREADS_PER_BLOCK; + int b = (n + t - 1) / t; + project_exp_cone_diag_q_kernel<<>>(pp, rp, cp, vr, qd, tau, ws, si, vd, isf, n); +} + +extern const cone_kernel_ops_t pdhcg_exp_cone_kernel_ops = { + { + launch_exp_thread_proj, + NULL, + NULL, + NULL, + NULL, + }, + { + launch_exp_thread_proj_diag_q, + NULL, + NULL, + NULL, + NULL, + }, + { + launch_exp_thread_dual, + NULL, + NULL, + NULL, + NULL, + }, +}; diff --git a/src/kernels/pdhcg_kernels.cu b/src/kernels/pdhcg_kernels.cu index ef1ff81..3be3bf4 100644 --- a/src/kernels/pdhcg_kernels.cu +++ b/src/kernels/pdhcg_kernels.cu @@ -15,7 +15,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -#include "pdhcg_kernels.cuh" +#include "pdhcg_kernels.h" #include #include __global__ void compute_and_rescale_reduced_cost_kernel(double *reduced_cost, @@ -737,107 +737,6 @@ __global__ void compute_qp_residual_kernel(double *primal_residual, } } -__global__ void finish_affine_cone_residuals_kernel(double *primal_residual, - const double *primal_product, - const double *affine_cone_offset, - const double *constraint_rescaling, - double *dual_membership, - const double *dual_membership_rescaling, - int n) -{ - int i = blockIdx.x * blockDim.x + threadIdx.x; - if (i < n) - { - double value = primal_product[i] + affine_cone_offset[i]; - primal_residual[i] = (value - primal_residual[i]) * constraint_rescaling[i]; - dual_membership[i] *= dual_membership_rescaling[i]; - } -} - -__global__ void prepare_affine_cone_residuals_kernel(double *projection_point, - double *complementarity_residual, - const double *primal_product, - const double *affine_cone_offset, - const double *dual_solution, - const int *start_idx, - const int *v_dim, - double constraint_bound_rescaling, - int num_cones) -{ - int cone = blockIdx.x; - if (cone >= num_cones) - return; - int start = start_idx[cone]; - int length = v_dim[cone] + 2; - double dot = 0.0; - for (int slot = threadIdx.x; slot < length; slot += blockDim.x) - { - int i = start + slot; - double dual = dual_solution[i]; - projection_point[i] = -dual; - dot += dual * (primal_product[i] + affine_cone_offset[i]); - } - - extern __shared__ double partial_sum[]; - partial_sum[threadIdx.x] = dot; - __syncthreads(); - for (int stride = blockDim.x / 2; stride > 0; stride >>= 1) - { - if (threadIdx.x < stride) - partial_sum[threadIdx.x] += partial_sum[threadIdx.x + stride]; - __syncthreads(); - } - if (threadIdx.x == 0) - complementarity_residual[cone] = fabs(partial_sum[0]) / constraint_bound_rescaling; -} - -__global__ void prepare_affine_cone_residuals_grid_kernel(double *projection_point, - double *complementarity_accumulator, - const double *primal_product, - const double *affine_cone_offset, - const double *dual_solution, - const int *start_idx, - const int *v_dim, - int num_cones, - int blocks_per_cone) -{ - int cone = blockIdx.x / blocks_per_cone; - if (cone >= num_cones) - return; - int part = blockIdx.x - cone * blocks_per_cone; - int start = start_idx[cone]; - int length = v_dim[cone] + 2; - double dot = 0.0; - for (int slot = part * blockDim.x + threadIdx.x; slot < length; slot += blocks_per_cone * blockDim.x) - { - int index = start + slot; - double dual = dual_solution[index]; - projection_point[index] = -dual; - dot += dual * (primal_product[index] + affine_cone_offset[index]); - } - - extern __shared__ double partial_sum[]; - partial_sum[threadIdx.x] = dot; - __syncthreads(); - for (int stride = blockDim.x / 2; stride > 0; stride >>= 1) - { - if (threadIdx.x < stride) - partial_sum[threadIdx.x] += partial_sum[threadIdx.x + stride]; - __syncthreads(); - } - if (threadIdx.x == 0) - atomicAdd(complementarity_accumulator + cone, partial_sum[0]); -} - -__global__ void finish_affine_cone_complementarity_kernel(double *complementarity_residual, - double constraint_bound_rescaling, - int num_cones) -{ - int cone = blockIdx.x * blockDim.x + threadIdx.x; - if (cone < num_cones) - complementarity_residual[cone] = fabs(complementarity_residual[cone]) / constraint_bound_rescaling; -} - __global__ void recover_primal_obj_dual_product(double *dual_product, double *primal_obj_product, const double *variable_rescaling, diff --git a/src/kernels/pdhcg_power_cone_kernels.cu b/src/kernels/pdhcg_power_cone_kernels.cu new file mode 100644 index 0000000..2a04233 --- /dev/null +++ b/src/kernels/pdhcg_power_cone_kernels.cu @@ -0,0 +1,821 @@ +/* +Copyright 2026 Hongpei Li + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +#include "cone_kernel_ops.h" +#include "pdhcg_power_cone_kernels.h" +#include "utils.h" + +#include +#include +#include + +__device__ static inline double positive_quadratic_root(double r, double q) +{ + if (!(q > 0.0)) + return fmax(r, 0.0); + double disc = hypot(r, 2.0 * sqrt(q)); + if (r >= 0.0) + return 0.5 * (r + disc); + return (2.0 * q) / (disc - r); +} + +__device__ static inline void project_power_cone_point_normalized( + double rx, double ry, double rz, double wx, double wy, double wz, double alpha, double *xo, double *yo, double *zo) +{ + double abs_rz = fabs(rz); + double sgn_rz = (rz >= 0.0) ? 1.0 : -1.0; + double om = 1.0 - alpha; + + if (abs_rz == 0.0) + { + *xo = fmax(rx, 0.0); + *yo = fmax(ry, 0.0); + *zo = 0.0; + return; + } + + if (rx > 0.0 && ry > 0.0) + { + if (alpha * log(rx) + om * log(ry) >= log(abs_rz)) + { + *xo = rx; + *yo = ry; + *zo = rz; + return; + } + } + + /* Opposite cone under weighted inner product: + proj^w(r) = 0 iff (wx*rx, wy*ry, wz*rz) in -K_a^*, i.e., + (-wx*rx)/a)^a * ((-wy*ry)/(1-a))^(1-a) >= wz*|rz|, rx <= 0, ry <= 0. */ + if (rx <= 0.0 && ry <= 0.0) + { + double u = (rx < 0.0) ? (-wx * rx) / alpha : 0.0; + double v = (ry < 0.0) ? (-wy * ry) / om : 0.0; + if (u > 0.0 && v > 0.0 && alpha * log(u) + om * log(v) >= log(wz) + log(abs_rz)) + { + *xo = 0.0; + *yo = 0.0; + *zo = 0.0; + return; + } + } + + double c_x = 4.0 * alpha * (wz / wx); + double c_y = 4.0 * om * (wz / wy); + + /* + * Bisect in log(rho). When one input axis is negative and alpha is close + * to an endpoint, the positive root can be many orders of magnitude below + * |r_z|. A linear relative floor would then converge to an infeasible + * point instead of the nonzero root. + */ + double lo = log(DBL_MIN); + double hi = log(abs_rz); + if (!(hi > lo)) + { + *xo = fmax(rx, 0.0); + *yo = fmax(ry, 0.0); + *zo = 0.0; + return; + } + + for (int it = 0; it < 80; ++it) + { + double log_rho = lo + 0.5 * (hi - lo); + double rho = exp(log_rho); + double x = positive_quadratic_root(rx, 0.25 * c_x * rho * (abs_rz - rho)); + double y = positive_quadratic_root(ry, 0.25 * c_y * rho * (abs_rz - rho)); + bool above_boundary = x > 0.0 && y > 0.0 && alpha * log(x) + om * log(y) > log_rho; + if (above_boundary) + lo = log_rho; + else + hi = log_rho; + } + double rho = exp(lo + 0.5 * (hi - lo)); + *xo = positive_quadratic_root(rx, 0.25 * c_x * rho * (abs_rz - rho)); + *yo = positive_quadratic_root(ry, 0.25 * c_y * rho * (abs_rz - rho)); + double log_bound = alpha * log(*xo) + om * log(*yo); + *zo = sgn_rz * fmin(rho, exp(log_bound)); +} + +__device__ static inline void project_power_cone_point( + double rx, double ry, double rz, double wx, double wy, double wz, double alpha, double *xo, double *yo, double *zo) +{ + /* The cone and weighted projection are positively homogeneous. Normalize + the point so products such as rho * (|r_z| - rho) cannot overflow. */ + double scale = fmax(fabs(rx), fmax(fabs(ry), fabs(rz))); + if (!(scale > 0.0) || !isfinite(scale)) + { + project_power_cone_point_normalized(rx, ry, rz, wx, wy, wz, alpha, xo, yo, zo); + return; + } + + double xn, yn, zn; + project_power_cone_point_normalized(rx / scale, ry / scale, rz / scale, wx, wy, wz, alpha, &xn, &yn, &zn); + *xo = xn * scale; + *yo = yn * scale; + *zo = zn * scale; +} + +/* Project x,y while z is fixed. The active boundary is x^a y^(1-a) = |z|. */ +__device__ static inline double +power_xy_log_boundary(double lambda, double rx, double ry, double wx, double wy, double alpha) +{ + double om = 1.0 - alpha; + double x = positive_quadratic_root(rx, (lambda / wx) * alpha); + double y = positive_quadratic_root(ry, (lambda / wy) * om); + if (!(x > 0.0) || !(y > 0.0)) + return -INFINITY; + return alpha * log(x) + om * log(y); +} + +__device__ static inline void project_power_xy_fixed_z_normalized( + double rx, double ry, double fixed_z, double wx, double wy, double alpha, double *xo, double *yo) +{ + double c = fabs(fixed_z); + double om = 1.0 - alpha; + if (c == 0.0) + { + *xo = fmax(rx, 0.0); + *yo = fmax(ry, 0.0); + return; + } + + if (rx > 0.0 && ry > 0.0 && alpha * log(rx) + om * log(ry) >= log(c)) + { + *xo = rx; + *yo = ry; + return; + } + + double target = log(c); + double hi = fmin(wx, wy); + if (!(hi > 0.0) || !isfinite(hi)) + hi = 1.0; + for (int it = 0; it < 2048; ++it) + { + double log_boundary = power_xy_log_boundary(hi, rx, ry, wx, wy, alpha); + if (log_boundary >= target || isnan(log_boundary)) + break; + if (hi >= 0.5 * DBL_MAX) + { + hi = DBL_MAX; + break; + } + hi *= 2.0; + } + + double lambda; + double floor_log_boundary = power_xy_log_boundary(DBL_MIN, rx, ry, wx, wy, alpha); + if (floor_log_boundary >= target) + { + double lo = 0.0; + double floor_hi = DBL_MIN; + for (int it = 0; it < 80; ++it) + { + double candidate = lo + 0.5 * (floor_hi - lo); + if (power_xy_log_boundary(candidate, rx, ry, wx, wy, alpha) < target) + lo = candidate; + else + floor_hi = candidate; + } + lambda = lo + 0.5 * (floor_hi - lo); + } + else + { + double log_lo = log(DBL_MIN); + double log_hi = log(hi); + for (int it = 0; it < 96; ++it) + { + double log_lambda = log_lo + 0.5 * (log_hi - log_lo); + double candidate = exp(log_lambda); + if (power_xy_log_boundary(candidate, rx, ry, wx, wy, alpha) < target) + log_lo = log_lambda; + else + log_hi = log_lambda; + } + lambda = exp(log_lo + 0.5 * (log_hi - log_lo)); + } + *xo = positive_quadratic_root(rx, (lambda / wx) * alpha); + *yo = positive_quadratic_root(ry, (lambda / wy) * om); +} + +__device__ static inline void project_power_xy_fixed_z( + double rx, double ry, double fixed_z, double wx, double wy, double alpha, double *xo, double *yo) +{ + double scale = fmax(fabs(rx), fmax(fabs(ry), fabs(fixed_z))); + if (!(scale > 0.0) || !isfinite(scale)) + { + project_power_xy_fixed_z_normalized(rx, ry, fixed_z, wx, wy, alpha, xo, yo); + return; + } + + double xn, yn; + project_power_xy_fixed_z_normalized(rx / scale, ry / scale, fixed_z / scale, wx, wy, alpha, &xn, &yn); + *xo = xn * scale; + *yo = yn * scale; +} + +/* With one nonnegative axis fixed, project the other axis and z onto + |z| <= fixed_axis^fixed_exp * other^other_exp. On the active boundary, + direct bisection in other is stable even when the KKT multiplier is tiny. */ +__device__ static inline double power_exp_from_log(double log_value) +{ + if (log_value >= log(DBL_MAX)) + return INFINITY; + if (log_value <= log(DBL_MIN)) + return 0.0; + return exp(log_value); +} + +__device__ static inline double power_section_derivative( + double other, double r_other, double abs_rz, double w_other, double wz, double log_coefficient, double other_exp) +{ + if (!(other > 0.0)) + return -INFINITY; + + double log_other = log(other); + double bound = power_exp_from_log(log_coefficient + other_exp * log_other); + double slope = power_exp_from_log(log_coefficient + log(other_exp) + (other_exp - 1.0) * log_other); + double linear_term = w_other * (other - r_other); + double gap = bound - abs_rz; + if (gap == 0.0 || slope == 0.0) + return linear_term; + if (!isfinite(slope)) + return copysign(INFINITY, gap); + return linear_term + wz * gap * slope; +} + +__device__ static inline void project_power_section_fixed_axis_normalized(double fixed_axis, + double r_other, + double rz, + double w_other, + double wz, + double fixed_exp, + double other_exp, + double *other_out, + double *z_out) +{ + double abs_rz = fabs(rz); + if (!(fixed_axis > 0.0) || abs_rz == 0.0) + { + *other_out = fmax(r_other, 0.0); + *z_out = 0.0; + return; + } + double log_coefficient = fixed_exp * log(fixed_axis); + + if (r_other > 0.0 && log_coefficient + other_exp * log(r_other) >= log(abs_rz)) + { + *other_out = r_other; + *z_out = rz; + return; + } + + double log_feasible_other = (log(abs_rz) - log_coefficient) / other_exp; + double feasible_other = power_exp_from_log(log_feasible_other); + if (feasible_other == 0.0) + { + *other_out = 0.0; + *z_out = 0.0; + return; + } + + double lo = 0.0; + double hi = fmax(1.0, fmax(r_other, 0.0)); + if (isfinite(feasible_other)) + hi = fmin(hi, feasible_other); + for (int it = 0; it < 1024; ++it) + { + double derivative = power_section_derivative(hi, r_other, abs_rz, w_other, wz, log_coefficient, other_exp); + if (!(derivative < 0.0)) + break; + if (isfinite(feasible_other) && hi >= feasible_other) + break; + double next_hi = hi * 2.0; + if (!isfinite(next_hi)) + { + hi = isfinite(feasible_other) ? feasible_other : DBL_MAX; + break; + } + hi = isfinite(feasible_other) ? fmin(next_hi, feasible_other) : next_hi; + } + + for (int it = 0; it < 80; ++it) + { + double other = lo + 0.5 * (hi - lo); + if (other == 0.0) + break; + double derivative = power_section_derivative(other, r_other, abs_rz, w_other, wz, log_coefficient, other_exp); + if (derivative < 0.0) + lo = other; + else + hi = other; + } + double other = lo + 0.5 * (hi - lo); + double projected_abs_z = other > 0.0 ? power_exp_from_log(log_coefficient + other_exp * log(other)) : 0.0; + *other_out = other; + *z_out = copysign(fmin(projected_abs_z, abs_rz), rz); +} + +__device__ static inline void project_power_section_fixed_axis(double fixed_axis, + double r_other, + double rz, + double w_other, + double wz, + double fixed_exp, + double other_exp, + double *other_out, + double *z_out) +{ + double scale = fmax(fixed_axis, fmax(fabs(r_other), fabs(rz))); + if (!(scale > 0.0) || !isfinite(scale)) + { + project_power_section_fixed_axis_normalized( + fixed_axis, r_other, rz, w_other, wz, fixed_exp, other_exp, other_out, z_out); + return; + } + + double normalized_other, normalized_z; + project_power_section_fixed_axis_normalized(fixed_axis / scale, + r_other / scale, + rz / scale, + w_other, + wz, + fixed_exp, + other_exp, + &normalized_other, + &normalized_z); + *other_out = normalized_other * scale; + *z_out = normalized_z * scale; +} + +__device__ static inline void project_power_cone_point_with_fixed(double rx, + double ry, + double rz, + double wx, + double wy, + double wz, + double alpha, + bool fixed_x, + bool fixed_y, + bool fixed_z, + double *xo, + double *yo, + double *zo) +{ + double om = 1.0 - alpha; + *xo = rx; + *yo = ry; + *zo = rz; + + if (!fixed_x && !fixed_y && !fixed_z) + { + project_power_cone_point(rx, ry, rz, wx, wy, wz, alpha, xo, yo, zo); + return; + } + + if (fixed_z) + { + if (fixed_x && fixed_y) + return; + if (fixed_x) + { + double lower = fabs(rz) == 0.0 ? 0.0 : exp((log(fabs(rz)) - alpha * log(rx)) / om); + *yo = fmax(ry, lower); + return; + } + if (fixed_y) + { + double lower = fabs(rz) == 0.0 ? 0.0 : exp((log(fabs(rz)) - om * log(ry)) / alpha); + *xo = fmax(rx, lower); + return; + } + project_power_xy_fixed_z(rx, ry, rz, wx, wy, alpha, xo, yo); + return; + } + + if (fixed_x && fixed_y) + { + double bound = 0.0; + if (rx > 0.0 && ry > 0.0) + { + double log_bound = alpha * log(rx) + om * log(ry); + bound = log_bound < log(DBL_MAX) ? exp(log_bound) : INFINITY; + } + *zo = fmax(-bound, fmin(rz, bound)); + return; + } + if (fixed_x) + { + project_power_section_fixed_axis(rx, ry, rz, wy, wz, alpha, om, yo, zo); + return; + } + if (fixed_y) + { + project_power_section_fixed_axis(ry, rx, rz, wx, wz, om, alpha, xo, zo); + return; + } +} + +__global__ void project_power_cone_kernel(double *__restrict__ primal_solution, + const double *__restrict__ variable_rescaling, + double *__restrict__ warm_start, + const int *__restrict__ start_idx, + const int *__restrict__ v_dim, + const double *__restrict__ power_alpha, + const char *__restrict__ is_fixed, + int num_blocks) +{ + (void)v_dim; + int blk = blockIdx.x * blockDim.x + threadIdx.x; + if (blk >= num_blocks) + return; + + int s_idx = start_idx[blk]; + double r1 = primal_solution[s_idx + 0]; + double r2 = primal_solution[s_idx + 1]; + double r3 = primal_solution[s_idx + 2]; + + double d1 = variable_rescaling[s_idx + 0]; + double d2 = variable_rescaling[s_idx + 1]; + double d3 = variable_rescaling[s_idx + 2]; + double alpha = power_alpha[blk]; + + /* Prox in scaled space with metric I equals prox in actual space with metric diag(d^2). */ + double rx = r1 / d1; + double ry = r2 / d2; + double rz = r3 / d3; + double wx = d1 * d1; + double wy = d2 * d2; + double wz = d3 * d3; + double xo, yo, zo; + project_power_cone_point_with_fixed(rx, + ry, + rz, + wx, + wy, + wz, + alpha, + is_fixed && is_fixed[s_idx + 0], + is_fixed && is_fixed[s_idx + 1], + is_fixed && is_fixed[s_idx + 2], + &xo, + &yo, + &zo); + primal_solution[s_idx + 0] = xo * d1; + primal_solution[s_idx + 1] = yo * d2; + primal_solution[s_idx + 2] = zo * d3; +} + +__global__ void compute_cone_dual_residual_power_kernel(double *__restrict__ dual_residual, + double *__restrict__ complementarity_residual, + const double *__restrict__ objective_vector, + const double *__restrict__ dual_product, + const double *__restrict__ variable_rescaling, + const double *__restrict__ primal_solution, + double *__restrict__ warm_start, + const int *__restrict__ start_idx, + const int *__restrict__ v_dim, + const double *__restrict__ power_alpha, + const char *__restrict__ is_fixed, + int num_blocks) +{ + (void)warm_start; + (void)v_dim; + int blk = blockIdx.x * blockDim.x + threadIdx.x; + if (blk >= num_blocks) + return; + + int s_idx = start_idx[blk]; + double r1 = objective_vector[s_idx + 0] - dual_product[s_idx + 0]; + double r2 = objective_vector[s_idx + 1] - dual_product[s_idx + 1]; + double r3 = objective_vector[s_idx + 2] - dual_product[s_idx + 2]; + double alpha = power_alpha[blk]; + + bool fixed_x = is_fixed && is_fixed[s_idx + 0]; + bool fixed_y = is_fixed && is_fixed[s_idx + 1]; + bool fixed_z = is_fixed && is_fixed[s_idx + 2]; + if (fixed_x || fixed_y || fixed_z) + { + double d1 = variable_rescaling[s_idx + 0]; + double d2 = variable_rescaling[s_idx + 1]; + double d3 = variable_rescaling[s_idx + 2]; + double x = primal_solution[s_idx + 0] / d1; + double y = primal_solution[s_idx + 1] / d2; + double z = primal_solution[s_idx + 2] / d3; + double q1 = r1 * d1; + double q2 = r2 * d2; + double q3 = r3 * d3; + + dual_residual[s_idx + 0] = fixed_x ? 0.0 : q1; + dual_residual[s_idx + 1] = fixed_y ? 0.0 : q2; + dual_residual[s_idx + 2] = fixed_z ? 0.0 : q3; + if (fixed_x && fixed_y && fixed_z) + return; + + double abs_z = fabs(z); + double bound = 0.0; + bool regular = x > 0.0 && y > 0.0 && isfinite(x) && isfinite(y) && isfinite(z); + if (regular) + { + double log_bound = alpha * log(x) + (1.0 - alpha) * log(y); + bound = exp(log_bound); + regular = isfinite(bound) && bound > 0.0; + } + + if (regular && abs_z > 0.0) + { + double normal[3] = { + -alpha * bound / x, + -(1.0 - alpha) * bound / y, + copysign(1.0, z), + }; + double q[3] = {q1, q2, q3}; + bool fixed[3] = {fixed_x, fixed_y, fixed_z}; + double normal_scale = 0.0; + for (int i = 0; i < 3; ++i) + { + if (!fixed[i]) + normal_scale = fmax(normal_scale, fabs(normal[i])); + } + if (!(normal_scale > 0.0) || !isfinite(normal_scale)) + { + regular = false; + } + + double dot = 0.0; + double normal2 = 0.0; + for (int i = 0; i < 3 && regular; ++i) + { + if (!fixed[i]) + { + double scaled_normal = normal[i] / normal_scale; + dot += q[i] * scaled_normal; + normal2 += scaled_normal * scaled_normal; + } + } + if (regular) + { + double scaled_lambda = (dot < 0.0 && normal2 > 0.0) ? -dot / normal2 : 0.0; + double lambda = scaled_lambda / normal_scale; + for (int i = 0; i < 3; ++i) + { + if (!fixed[i]) + dual_residual[s_idx + i] = q[i] + scaled_lambda * (normal[i] / normal_scale); + } + double slack_scale = fmax(1.0, fmax(bound, abs_z)); + double complementarity = lambda * (fmax(bound - abs_z, 0.0) / slack_scale); + complementarity_residual[blk] = complementarity; + return; + } + } + + if (regular) + return; + + /* Degenerate axes are nonsmooth. A unit metric projection supplies a + scale-independent KKT guard without changing the adaptive mapping. */ + double rx = x - (fixed_x ? 0.0 : r1 / d1); + double ry = y - (fixed_y ? 0.0 : r2 / d2); + double rz = z - (fixed_z ? 0.0 : r3 / d3); + double xo, yo, zo; + project_power_cone_point_with_fixed( + rx, ry, rz, d1 * d1, d2 * d2, d3 * d3, alpha, fixed_x, fixed_y, fixed_z, &xo, &yo, &zo); + if (!fixed_x) + dual_residual[s_idx + 0] = (x - xo) * d1 * d1; + if (!fixed_y) + dual_residual[s_idx + 1] = (y - yo) * d2 * d2; + if (!fixed_z) + dual_residual[s_idx + 2] = (z - zo) * d3 * d3; + return; + } + + double vr1 = variable_rescaling[s_idx + 0]; + double vr2 = variable_rescaling[s_idx + 1]; + double vr3 = variable_rescaling[s_idx + 2]; + + /* Moreau via primal projection: dual_res = -Proj_K(-r * vr). */ + double xo, yo, zo; + project_power_cone_point(-r1 * vr1, -r2 * vr2, -r3 * vr3, 1.0, 1.0, 1.0, alpha, &xo, &yo, &zo); + + dual_residual[s_idx + 0] = -xo; + dual_residual[s_idx + 1] = -yo; + dual_residual[s_idx + 2] = -zo; +} + +__global__ void compute_power_cone_primal_violation_kernel(double *__restrict__ absolute_violation, + double *__restrict__ relative_violation, + const double *__restrict__ primal_solution, + const double *__restrict__ variable_rescaling, + const int *__restrict__ start_idx, + const double *__restrict__ power_alpha, + double homogeneous_scale, + int num_blocks) +{ + int blk = blockIdx.x * blockDim.x + threadIdx.x; + if (blk >= num_blocks) + return; + + int start = start_idx[blk]; + double x = primal_solution[start + 0] / variable_rescaling[start + 0]; + double y = primal_solution[start + 1] / variable_rescaling[start + 1]; + double z = primal_solution[start + 2] / variable_rescaling[start + 2]; + if (!isfinite(x) || !isfinite(y) || !isfinite(z)) + { + absolute_violation[blk] = INFINITY; + relative_violation[blk] = INFINITY; + return; + } + double violation = fmax(-x, -y); + double abs_z = fabs(z); + if (abs_z > 0.0) + { + double bound = 0.0; + if (x > 0.0 && y > 0.0) + { + double alpha = power_alpha[blk]; + double log_bound = alpha * log(x) + (1.0 - alpha) * log(y); + double log_abs_z = log(abs_z); + double roundoff_tolerance = 64.0 * DBL_EPSILON * (1.0 + fabs(log_bound) + fabs(log_abs_z)); + if (log_bound + roundoff_tolerance >= log_abs_z) + { + violation = fmax(violation, 0.0); + absolute_violation[blk] = violation; + relative_violation[blk] = violation / (homogeneous_scale + fmax(fabs(x), fmax(fabs(y), abs_z))); + return; + } + bound = exp(log_bound); + } + violation = fmax(violation, abs_z - bound); + } + violation = fmax(violation, 0.0); + absolute_violation[blk] = violation; + relative_violation[blk] = violation / (homogeneous_scale + fmax(fabs(x), fmax(fabs(y), abs_z))); +} + +__global__ void project_power_cone_diag_q_kernel(double *__restrict__ pdhg_primal, + double *__restrict__ reflected_primal, + const double *__restrict__ current_primal, + const double *__restrict__ variable_rescaling, + const double *__restrict__ Q_diag, + double tau, + double *__restrict__ warm_start, + const int *__restrict__ start_idx, + const int *__restrict__ v_dim, + const double *__restrict__ power_alpha, + const char *__restrict__ is_fixed, + int num_blocks) +{ + (void)warm_start; + (void)v_dim; + int blk = blockIdx.x * blockDim.x + threadIdx.x; + if (blk >= num_blocks) + return; + + int s_idx = start_idx[blk]; + double r1 = pdhg_primal[s_idx + 0]; + double r2 = pdhg_primal[s_idx + 1]; + double r3 = pdhg_primal[s_idx + 2]; + + double d1 = variable_rescaling[s_idx + 0]; + double d2 = variable_rescaling[s_idx + 1]; + double d3 = variable_rescaling[s_idx + 2]; + double alpha = power_alpha[blk]; + + /* Effective weight in actual space: omega_i = (1 + tau*Q_ii) * d_i^2. */ + double w1 = 1.0 + tau * Q_diag[s_idx + 0]; + double w2 = 1.0 + tau * Q_diag[s_idx + 1]; + double w3 = 1.0 + tau * Q_diag[s_idx + 2]; + double om_x = w1 * d1 * d1; + double om_y = w2 * d2 * d2; + double om_z = w3 * d3 * d3; + double rx = r1 / d1; + double ry = r2 / d2; + double rz = r3 / d3; + double xo, yo, zo; + project_power_cone_point_with_fixed(rx, + ry, + rz, + om_x, + om_y, + om_z, + alpha, + is_fixed && is_fixed[s_idx + 0], + is_fixed && is_fixed[s_idx + 1], + is_fixed && is_fixed[s_idx + 2], + &xo, + &yo, + &zo); + pdhg_primal[s_idx + 0] = xo * d1; + pdhg_primal[s_idx + 1] = yo * d2; + pdhg_primal[s_idx + 2] = zo * d3; + for (int m = 0; m < 3; ++m) + { + int idx = s_idx + m; + reflected_primal[idx] = 2.0 * pdhg_primal[idx] - current_primal[idx]; + } +} + +static void launch_power_thread_proj( + double *p, const double *vr, double *ws, const int *si, const int *vd, const double *pa, const char *isf, int n) +{ + int t = THREADS_PER_BLOCK; + int b = (n + t - 1) / t; + project_power_cone_kernel<<>>(p, vr, ws, si, vd, pa, isf, n); +} + +static void launch_power_thread_dual(double *dr, + double *cr, + const double *obj, + const double *dp, + const double *vr, + const double *ps, + double *ws, + const int *si, + const int *vd, + const double *pa, + const char *isf, + int n) +{ + int t = THREADS_PER_BLOCK; + int b = (n + t - 1) / t; + compute_cone_dual_residual_power_kernel<<>>(dr, cr, obj, dp, vr, ps, ws, si, vd, pa, isf, n); +} + +static void launch_power_thread_proj_diag_q(double *pp, + double *rp, + const double *cp, + const double *vr, + const double *qd, + double tau, + double *ws, + const int *si, + const int *vd, + const double *pa, + const char *isf, + int n) +{ + int t = THREADS_PER_BLOCK; + int b = (n + t - 1) / t; + project_power_cone_diag_q_kernel<<>>(pp, rp, cp, vr, qd, tau, ws, si, vd, pa, isf, n); +} + +void launch_power_cone_primal_violation(double *absolute_violation, + double *relative_violation, + const double *primal_solution, + const double *variable_rescaling, + const int *start_idx, + const double *power_alpha, + double homogeneous_scale, + int count) +{ + int threads = THREADS_PER_BLOCK; + int blocks = (count + threads - 1) / threads; + compute_power_cone_primal_violation_kernel<<>>(absolute_violation, + relative_violation, + primal_solution, + variable_rescaling, + start_idx, + power_alpha, + homogeneous_scale, + count); +} + +extern const cone_kernel_ops_t pdhcg_power_cone_kernel_ops = { + { + launch_power_thread_proj, + NULL, + NULL, + NULL, + NULL, + }, + { + launch_power_thread_proj_diag_q, + NULL, + NULL, + NULL, + NULL, + }, + { + launch_power_thread_dual, + NULL, + NULL, + NULL, + NULL, + }, +}; diff --git a/src/kernels/pdhcg_psd_cone_kernels.cu b/src/kernels/pdhcg_psd_cone_kernels.cu new file mode 100644 index 0000000..72a896d --- /dev/null +++ b/src/kernels/pdhcg_psd_cone_kernels.cu @@ -0,0 +1,574 @@ +/* +Copyright 2026 Hongpei Li + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +#include "pdhcg_psd_cone.h" +#include "utils.h" + +#include +#include +#include +#include +#include +#include + +#define PDHCG_PSD_BATCHED_MAX_ORDER 32 +#define CUSOLVER_CHECK(call) \ + do \ + { \ + cusolverStatus_t status = call; \ + if (status != CUSOLVER_STATUS_SUCCESS) \ + { \ + fprintf(stderr, "cuSOLVER Error at %s:%d: status %d\n", __FILE__, __LINE__, (int)status); \ + exit(EXIT_FAILURE); \ + } \ + } while (0) + +typedef struct +{ + int matrix_order; + int packed_length; + int count; + int *start_idx; + int *block_idx; + int *packed_row; + int *packed_col; + double *matrices; + double *eigenvalues; + double *workspace; + int workspace_size; + int *info; + bool use_batched_jacobi; +} psd_order_bucket_t; + +struct psd_projection_runtime_s +{ + int complementarity_offset; + int num_buckets; + psd_order_bucket_t *buckets; + cusolverDnHandle_t solver_handle; + syevjInfo_t jacobi_params; +}; + +__global__ static void gather_svec_matrices_kernel(double *matrices, + const double *vector, + const int *start_idx, + const int *packed_row, + const int *packed_col, + int matrix_order, + int packed_length, + int count) +{ + int matrix = blockIdx.x; + if (matrix >= count) + return; + + const double inv_sqrt_two = 0.70710678118654752440; + size_t matrix_offset = (size_t)matrix * (size_t)matrix_order * (size_t)matrix_order; + int vector_offset = start_idx[matrix]; + for (int slot = threadIdx.x; slot < packed_length; slot += blockDim.x) + { + int row = packed_row[slot]; + int col = packed_col[slot]; + double value = vector[vector_offset + slot]; + if (row != col) + value *= inv_sqrt_two; + matrices[matrix_offset + row + col * matrix_order] = value; + matrices[matrix_offset + col + row * matrix_order] = value; + } +} + +__global__ static void scatter_positive_eigenspace_kernel(double *vector, + const double *eigenvectors, + const double *eigenvalues, + const int *info, + const int *start_idx, + const int *packed_row, + const int *packed_col, + int matrix_order, + int packed_length, + int count) +{ + int matrix = blockIdx.x; + if (matrix >= count) + return; + + const double sqrt_two = 1.41421356237309504880; + int vector_offset = start_idx[matrix]; + size_t matrix_offset = (size_t)matrix * (size_t)matrix_order * (size_t)matrix_order; + size_t eigenvalue_offset = (size_t)matrix * (size_t)matrix_order; + bool valid = info[matrix] == 0; + for (int slot = threadIdx.x; slot < packed_length; slot += blockDim.x) + { + double value = 0.0; + int row = packed_row[slot]; + int col = packed_col[slot]; + if (valid) + { + for (int eigenvector = 0; eigenvector < matrix_order; ++eigenvector) + { + double eigenvalue = fmax(eigenvalues[eigenvalue_offset + eigenvector], 0.0); + double u_row = eigenvectors[matrix_offset + row + eigenvector * matrix_order]; + double u_col = eigenvectors[matrix_offset + col + eigenvector * matrix_order]; + value += eigenvalue * u_row * u_col; + } + if (row != col) + value *= sqrt_two; + } + else + { + value = NAN; + } + vector[vector_offset + slot] = value; + } +} + +__global__ static void project_psd_scalars_kernel(double *vector, const int *start_idx, int count) +{ + int block = blockIdx.x * blockDim.x + threadIdx.x; + if (block < count) + { + int index = start_idx[block]; + vector[index] = fmax(vector[index], 0.0); + } +} + +__global__ static void prepare_psd_dual_residual_kernel(double *dual_residual, + const double *objective_vector, + const double *dual_product, + const int *start_idx, + int packed_length, + int count) +{ + int block = blockIdx.x; + if (block >= count) + return; + int start = start_idx[block]; + for (int slot = threadIdx.x; slot < packed_length; slot += blockDim.x) + { + int index = start + slot; + dual_residual[index] = objective_vector[index] - dual_product[index]; + } +} + +__global__ static void finish_psd_dual_residual_kernel(double *dual_residual, + const double *objective_vector, + const double *dual_product, + const double *variable_rescaling, + const int *start_idx, + int packed_length, + int count) +{ + int block = blockIdx.x; + if (block >= count) + return; + int start = start_idx[block]; + for (int slot = threadIdx.x; slot < packed_length; slot += blockDim.x) + { + int index = start + slot; + double residual = objective_vector[index] - dual_product[index]; + dual_residual[index] = (residual - dual_residual[index]) * variable_rescaling[index]; + } +} + +__global__ static void recompute_psd_reflection_kernel(double *reflected_primal, + const double *pdhg_primal, + const double *current_primal, + const int *start_idx, + int packed_length, + int count) +{ + int block = blockIdx.x; + if (block >= count) + return; + int start = start_idx[block]; + for (int slot = threadIdx.x; slot < packed_length; slot += blockDim.x) + { + int index = start + slot; + reflected_primal[index] = 2.0 * pdhg_primal[index] - current_primal[index]; + } +} + +__global__ static void set_psd_dual_slack_kernel(double *dual_slack, + const double *objective_vector, + const double *dual_product, + const int *start_idx, + int packed_length, + int count) +{ + int block = blockIdx.x; + if (block >= count) + return; + int start = start_idx[block]; + for (int slot = threadIdx.x; slot < packed_length; slot += blockDim.x) + { + int index = start + slot; + dual_slack[index] = objective_vector[index] - dual_product[index]; + } +} + +__global__ static void prepare_psd_affine_residuals_kernel(double *projection_point, + double *complementarity_residual, + const double *primal_product, + const double *affine_cone_offset, + const double *dual_solution, + const int *start_idx, + const int *block_idx, + int packed_length, + int complementarity_offset, + double constraint_bound_rescaling, + int count) +{ + int block = blockIdx.x; + if (block >= count) + return; + int start = start_idx[block]; + double dot = 0.0; + for (int slot = threadIdx.x; slot < packed_length; slot += blockDim.x) + { + int index = start + slot; + double dual = dual_solution[index]; + projection_point[index] = -dual; + dot += dual * (primal_product[index] + affine_cone_offset[index]); + } + + extern __shared__ double partial_sum[]; + partial_sum[threadIdx.x] = dot; + __syncthreads(); + for (int stride = blockDim.x / 2; stride > 0; stride >>= 1) + { + if (threadIdx.x < stride) + partial_sum[threadIdx.x] += partial_sum[threadIdx.x + stride]; + __syncthreads(); + } + if (threadIdx.x == 0) + { + int output = complementarity_offset + block_idx[block]; + complementarity_residual[output] = fabs(partial_sum[0]) / constraint_bound_rescaling; + } +} + +static void initialize_bucket(psd_projection_runtime_t *runtime, + psd_order_bucket_t *bucket, + int matrix_order, + int count, + const int *start_idx, + const int *block_idx) +{ + bucket->matrix_order = matrix_order; + bucket->packed_length = (int)((long long)matrix_order * (matrix_order + 1LL) / 2LL); + bucket->count = count; + + size_t block_bytes = (size_t)count * sizeof(int); + ALLOC_AND_COPY(bucket->start_idx, start_idx, block_bytes); + ALLOC_AND_COPY(bucket->block_idx, block_idx, block_bytes); + + if (matrix_order == 1) + return; + + int *packed_row = (int *)safe_malloc((size_t)bucket->packed_length * sizeof(int)); + int *packed_col = (int *)safe_malloc((size_t)bucket->packed_length * sizeof(int)); + int slot = 0; + for (int col = 0; col < matrix_order; ++col) + { + for (int row = col; row < matrix_order; ++row) + { + packed_row[slot] = row; + packed_col[slot] = col; + ++slot; + } + } + size_t packed_index_bytes = (size_t)bucket->packed_length * sizeof(int); + ALLOC_AND_COPY(bucket->packed_row, packed_row, packed_index_bytes); + ALLOC_AND_COPY(bucket->packed_col, packed_col, packed_index_bytes); + free(packed_row); + free(packed_col); + + size_t matrix_entries = (size_t)count * (size_t)matrix_order * (size_t)matrix_order; + size_t eigenvalue_entries = (size_t)count * (size_t)matrix_order; + CUDA_CHECK(cudaMalloc(&bucket->matrices, matrix_entries * sizeof(double))); + CUDA_CHECK(cudaMalloc(&bucket->eigenvalues, eigenvalue_entries * sizeof(double))); + CUDA_CHECK(cudaMalloc(&bucket->info, block_bytes)); + + bucket->use_batched_jacobi = matrix_order <= PDHCG_PSD_BATCHED_MAX_ORDER; + if (bucket->use_batched_jacobi) + { + CUSOLVER_CHECK(cusolverDnDsyevjBatched_bufferSize(runtime->solver_handle, + CUSOLVER_EIG_MODE_VECTOR, + CUBLAS_FILL_MODE_LOWER, + matrix_order, + bucket->matrices, + matrix_order, + bucket->eigenvalues, + &bucket->workspace_size, + runtime->jacobi_params, + count)); + } + else + { + CUSOLVER_CHECK(cusolverDnDsyevd_bufferSize(runtime->solver_handle, + CUSOLVER_EIG_MODE_VECTOR, + CUBLAS_FILL_MODE_LOWER, + matrix_order, + bucket->matrices, + matrix_order, + bucket->eigenvalues, + &bucket->workspace_size)); + } + if (bucket->workspace_size > 0) + CUDA_CHECK(cudaMalloc(&bucket->workspace, (size_t)bucket->workspace_size * sizeof(double))); +} + +psd_projection_runtime_t * +create_psd_projection_runtime(const int *start_idx, const int *matrix_order, int num_blocks, int complementarity_offset) +{ + if (num_blocks <= 0) + return NULL; + + psd_projection_runtime_t *runtime = (psd_projection_runtime_t *)safe_calloc(1, sizeof(psd_projection_runtime_t)); + runtime->complementarity_offset = complementarity_offset; + CUSOLVER_CHECK(cusolverDnCreate(&runtime->solver_handle)); + CUSOLVER_CHECK(cusolverDnCreateSyevjInfo(&runtime->jacobi_params)); + CUSOLVER_CHECK(cusolverDnXsyevjSetTolerance(runtime->jacobi_params, 1e-12)); + CUSOLVER_CHECK(cusolverDnXsyevjSetMaxSweeps(runtime->jacobi_params, 100)); + CUSOLVER_CHECK(cusolverDnXsyevjSetSortEig(runtime->jacobi_params, 1)); + + int *unique_orders = (int *)safe_malloc((size_t)num_blocks * sizeof(int)); + int *bucket_counts = (int *)safe_calloc((size_t)num_blocks, sizeof(int)); + for (int block = 0; block < num_blocks; ++block) + { + int bucket = 0; + while (bucket < runtime->num_buckets && unique_orders[bucket] != matrix_order[block]) + ++bucket; + if (bucket == runtime->num_buckets) + unique_orders[runtime->num_buckets++] = matrix_order[block]; + ++bucket_counts[bucket]; + } + + runtime->buckets = (psd_order_bucket_t *)safe_calloc((size_t)runtime->num_buckets, sizeof(psd_order_bucket_t)); + for (int bucket = 0; bucket < runtime->num_buckets; ++bucket) + { + int count = bucket_counts[bucket]; + int *starts = (int *)safe_malloc((size_t)count * sizeof(int)); + int *indices = (int *)safe_malloc((size_t)count * sizeof(int)); + int output = 0; + for (int block = 0; block < num_blocks; ++block) + { + if (matrix_order[block] == unique_orders[bucket]) + { + starts[output] = start_idx[block]; + indices[output] = block; + ++output; + } + } + initialize_bucket(runtime, &runtime->buckets[bucket], unique_orders[bucket], count, starts, indices); + free(starts); + free(indices); + } + free(unique_orders); + free(bucket_counts); + return runtime; +} + +void free_psd_projection_runtime(psd_projection_runtime_t *runtime) +{ + if (!runtime) + return; + for (int bucket_idx = 0; bucket_idx < runtime->num_buckets; ++bucket_idx) + { + psd_order_bucket_t *bucket = &runtime->buckets[bucket_idx]; + if (bucket->start_idx) + CUDA_CHECK(cudaFree(bucket->start_idx)); + if (bucket->block_idx) + CUDA_CHECK(cudaFree(bucket->block_idx)); + if (bucket->packed_row) + CUDA_CHECK(cudaFree(bucket->packed_row)); + if (bucket->packed_col) + CUDA_CHECK(cudaFree(bucket->packed_col)); + if (bucket->matrices) + CUDA_CHECK(cudaFree(bucket->matrices)); + if (bucket->eigenvalues) + CUDA_CHECK(cudaFree(bucket->eigenvalues)); + if (bucket->workspace) + CUDA_CHECK(cudaFree(bucket->workspace)); + if (bucket->info) + CUDA_CHECK(cudaFree(bucket->info)); + } + free(runtime->buckets); + CUSOLVER_CHECK(cusolverDnDestroySyevjInfo(runtime->jacobi_params)); + CUSOLVER_CHECK(cusolverDnDestroy(runtime->solver_handle)); + free(runtime); +} + +static void project_psd_bucket(psd_projection_runtime_t *runtime, psd_order_bucket_t *bucket, double *vector) +{ + int threads = THREADS_PER_BLOCK; + if (bucket->matrix_order == 1) + { + int blocks = (bucket->count + threads - 1) / threads; + project_psd_scalars_kernel<<>>(vector, bucket->start_idx, bucket->count); + return; + } + + gather_svec_matrices_kernel<<count, threads>>>(bucket->matrices, + vector, + bucket->start_idx, + bucket->packed_row, + bucket->packed_col, + bucket->matrix_order, + bucket->packed_length, + bucket->count); + if (bucket->use_batched_jacobi) + { + CUSOLVER_CHECK(cusolverDnDsyevjBatched(runtime->solver_handle, + CUSOLVER_EIG_MODE_VECTOR, + CUBLAS_FILL_MODE_LOWER, + bucket->matrix_order, + bucket->matrices, + bucket->matrix_order, + bucket->eigenvalues, + bucket->workspace, + bucket->workspace_size, + bucket->info, + runtime->jacobi_params, + bucket->count)); + } + else + { + size_t matrix_stride = (size_t)bucket->matrix_order * (size_t)bucket->matrix_order; + for (int matrix = 0; matrix < bucket->count; ++matrix) + { + CUSOLVER_CHECK(cusolverDnDsyevd(runtime->solver_handle, + CUSOLVER_EIG_MODE_VECTOR, + CUBLAS_FILL_MODE_LOWER, + bucket->matrix_order, + bucket->matrices + matrix * matrix_stride, + bucket->matrix_order, + bucket->eigenvalues + (size_t)matrix * (size_t)bucket->matrix_order, + bucket->workspace, + bucket->workspace_size, + bucket->info + matrix)); + } + } + scatter_positive_eigenspace_kernel<<count, threads>>>(vector, + bucket->matrices, + bucket->eigenvalues, + bucket->info, + bucket->start_idx, + bucket->packed_row, + bucket->packed_col, + bucket->matrix_order, + bucket->packed_length, + bucket->count); +} + +void project_psd_cones(psd_projection_runtime_t *runtime, double *vector) +{ + if (!runtime) + return; + for (int bucket = 0; bucket < runtime->num_buckets; ++bucket) + project_psd_bucket(runtime, &runtime->buckets[bucket], vector); + CUDA_CHECK(cudaGetLastError()); +} + +void compute_psd_cone_dual_residual(psd_projection_runtime_t *runtime, + double *dual_residual, + const double *objective_vector, + const double *dual_product, + const double *variable_rescaling) +{ + if (!runtime) + return; + for (int bucket_idx = 0; bucket_idx < runtime->num_buckets; ++bucket_idx) + { + psd_order_bucket_t *bucket = &runtime->buckets[bucket_idx]; + prepare_psd_dual_residual_kernel<<count, THREADS_PER_BLOCK>>>( + dual_residual, objective_vector, dual_product, bucket->start_idx, bucket->packed_length, bucket->count); + } + project_psd_cones(runtime, dual_residual); + for (int bucket_idx = 0; bucket_idx < runtime->num_buckets; ++bucket_idx) + { + psd_order_bucket_t *bucket = &runtime->buckets[bucket_idx]; + finish_psd_dual_residual_kernel<<count, THREADS_PER_BLOCK>>>(dual_residual, + objective_vector, + dual_product, + variable_rescaling, + bucket->start_idx, + bucket->packed_length, + bucket->count); + } + CUDA_CHECK(cudaGetLastError()); +} + +void recompute_psd_cone_reflection(psd_projection_runtime_t *runtime, + double *reflected_primal, + const double *pdhg_primal, + const double *current_primal) +{ + if (!runtime) + return; + for (int bucket_idx = 0; bucket_idx < runtime->num_buckets; ++bucket_idx) + { + psd_order_bucket_t *bucket = &runtime->buckets[bucket_idx]; + recompute_psd_reflection_kernel<<count, THREADS_PER_BLOCK>>>( + reflected_primal, pdhg_primal, current_primal, bucket->start_idx, bucket->packed_length, bucket->count); + } + CUDA_CHECK(cudaGetLastError()); +} + +void set_psd_cone_dual_slack(psd_projection_runtime_t *runtime, + double *dual_slack, + const double *objective_vector, + const double *dual_product) +{ + if (!runtime) + return; + for (int bucket_idx = 0; bucket_idx < runtime->num_buckets; ++bucket_idx) + { + psd_order_bucket_t *bucket = &runtime->buckets[bucket_idx]; + set_psd_dual_slack_kernel<<count, THREADS_PER_BLOCK>>>( + dual_slack, objective_vector, dual_product, bucket->start_idx, bucket->packed_length, bucket->count); + } + CUDA_CHECK(cudaGetLastError()); +} + +void prepare_psd_affine_cone_residuals(psd_projection_runtime_t *runtime, + double *projection_point, + double *complementarity_residual, + const double *primal_product, + const double *affine_cone_offset, + const double *dual_solution, + double constraint_bound_rescaling) +{ + if (!runtime) + return; + for (int bucket_idx = 0; bucket_idx < runtime->num_buckets; ++bucket_idx) + { + psd_order_bucket_t *bucket = &runtime->buckets[bucket_idx]; + prepare_psd_affine_residuals_kernel<<count, THREADS_PER_BLOCK, THREADS_PER_BLOCK * sizeof(double)>>>( + projection_point, + complementarity_residual, + primal_product, + affine_cone_offset, + dual_solution, + bucket->start_idx, + bucket->block_idx, + bucket->packed_length, + runtime->complementarity_offset, + constraint_bound_rescaling, + bucket->count); + } + CUDA_CHECK(cudaGetLastError()); +} diff --git a/src/kernels/pdhcg_rsoc_cone_kernels.cu b/src/kernels/pdhcg_rsoc_cone_kernels.cu new file mode 100644 index 0000000..c3c0d8c --- /dev/null +++ b/src/kernels/pdhcg_rsoc_cone_kernels.cu @@ -0,0 +1,3493 @@ +/* +Copyright 2026 Hongpei Li + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +#include "cone_kernel_ops.h" +#include "cone_kernel_reductions.h" +#include "cone_projection_utils.h" +#include "pdhcg_rsoc_cone_kernels.h" +/* Map a weighted rotated SOC to a weighted standard SOC in sum/difference + endpoint coordinates, then reuse its negative-branch bracket. */ +__device__ static inline double cone_section_negative_rsoc_upper(double omega_s, + double omega_t, + double s, + double t, + double fixed_norm2, + double polar_norm2, + double max_vector_metric) +{ + const double inv_sqrt2 = 0.70710678118654752440; + double sqrt_omega_s = sqrt(omega_s); + double sqrt_omega_t = sqrt(omega_t); + double root_metric = sqrt_omega_s * sqrt_omega_t; + double scaled_s = sqrt_omega_s * s; + double scaled_t = sqrt_omega_t * t; + double transformed_w = (scaled_s - scaled_t) * inv_sqrt2; + double endpoint_polar = -(scaled_s + scaled_t) * inv_sqrt2; + double transformed_fixed_norm2 = root_metric * fixed_norm2; + double transformed_polar_norm2 = polar_norm2 / root_metric + transformed_w * transformed_w; + double transformed_max_metric = fmax(1.0, max_vector_metric / root_metric); + double transformed_upper = cone_section_negative_soc_upper( + 1.0, endpoint_polar, transformed_fixed_norm2, transformed_polar_norm2, transformed_max_metric); + return root_metric * transformed_upper; +} +__device__ static inline double rotated_soc_smooth_objective(const double *point, + const double *rescaling, + const double *q_diag, + double tau, + int start, + int k, + const char *is_fixed, + double lambda, + double s, + double t) +{ + double objective = 0.0; + for (int slot = 0; slot < k; ++slot) + { + int index = start + slot; + if (is_fixed[index]) + continue; + double omega = cone_section_weight(rescaling, q_diag, tau, index); + double input = cone_section_actual(point, rescaling, index); + double value = input * omega / (omega + lambda); + double delta = value - input; + objective += omega * delta * delta; + } + int s_index = start + k; + int t_index = s_index + 1; + double omega_s = cone_section_weight(rescaling, q_diag, tau, s_index); + double omega_t = cone_section_weight(rescaling, q_diag, tau, t_index); + double ds = s - cone_section_actual(point, rescaling, s_index); + double dt = t - cone_section_actual(point, rescaling, t_index); + return objective + omega_s * ds * ds + omega_t * dt * dt; +} + +/* Weighted projection onto an arbitrary nonempty fixed section of + { (v,s,t) : ||v||_2^2 <= 2 s t, s >= 0, t >= 0 }. */ +__device__ static inline void project_rotated_soc_section_serial(double *point, + const double *rescaling, + const double *q_diag, + double tau, + double *warm_start, + int start, + int k, + const char *is_fixed) +{ + int s_index = start + k; + int t_index = s_index + 1; + bool fixed_s = is_fixed[s_index] != 0; + bool fixed_t = is_fixed[t_index] != 0; + double s_input = cone_section_actual(point, rescaling, s_index); + double t_input = cone_section_actual(point, rescaling, t_index); + double fixed_norm2 = 0.0; + double free_norm2 = 0.0; + double polar_norm2 = 0.0; + double max_omega = 0.0; + int free_count = 0; + for (int slot = 0; slot < k; ++slot) + { + int index = start + slot; + double value = cone_section_actual(point, rescaling, index); + if (is_fixed[index]) + fixed_norm2 += value * value; + else + { + double omega = cone_section_weight(rescaling, q_diag, tau, index); + free_norm2 += value * value; + polar_norm2 += (omega * value) * (omega * value); + max_omega = fmax(max_omega, omega); + ++free_count; + } + } + + if (fixed_s && fixed_t) + { + double radius2 = fmax(0.0, 2.0 * s_input * t_input - fixed_norm2); + if (free_count == 0 || free_norm2 <= radius2) + return; + if (!(radius2 > 0.0)) + { + for (int slot = 0; slot < k; ++slot) + if (!is_fixed[start + slot]) + point[start + slot] = 0.0; + return; + } + + double lo = 0.0; + double hi = sqrt(polar_norm2) / sqrt(radius2) * (1.0 + 64.0 * DBL_EPSILON); + if (!(hi > 0.0) || !isfinite(hi)) + { + hi = warm_start && *warm_start > 0.0 && isfinite(*warm_start) ? *warm_start : 1.0; + for (int expansion = 0; expansion < 100; ++expansion) + { + double norm2 = 0.0; + for (int slot = 0; slot < k; ++slot) + { + int index = start + slot; + if (is_fixed[index]) + continue; + double omega = cone_section_weight(rescaling, q_diag, tau, index); + double value = cone_section_actual(point, rescaling, index) * omega / (omega + hi); + norm2 += value * value; + } + if (norm2 <= radius2) + break; + hi *= 2.0; + } + } + for (int iteration = 0; iteration < 80; ++iteration) + { + double lambda = 0.5 * (lo + hi); + double norm2 = 0.0; + for (int slot = 0; slot < k; ++slot) + { + int index = start + slot; + if (is_fixed[index]) + continue; + double omega = cone_section_weight(rescaling, q_diag, tau, index); + double value = cone_section_actual(point, rescaling, index) * omega / (omega + lambda); + norm2 += value * value; + } + if (norm2 > radius2) + lo = lambda; + else + hi = lambda; + if ((hi - lo) <= 1e-13 * (1.0 + hi + lo)) + break; + } + double lambda = 0.5 * (lo + hi); + if (warm_start) + *warm_start = lambda; + for (int slot = 0; slot < k; ++slot) + { + int index = start + slot; + if (!is_fixed[index]) + { + double omega = cone_section_weight(rescaling, q_diag, tau, index); + point[index] *= omega / (omega + lambda); + } + } + return; + } + + if (fixed_s || fixed_t) + { + int free_endpoint_index = fixed_s ? t_index : s_index; + double fixed_endpoint = fixed_s ? s_input : t_input; + double free_endpoint_input = fixed_s ? t_input : s_input; + double omega_endpoint = cone_section_weight(rescaling, q_diag, tau, free_endpoint_index); + if (!(fixed_endpoint > 0.0)) + { + for (int slot = 0; slot < k; ++slot) + if (!is_fixed[start + slot]) + point[start + slot] = 0.0; + point[free_endpoint_index] = fmax(free_endpoint_input, 0.0) * rescaling[free_endpoint_index]; + return; + } + if (free_endpoint_input >= 0.0 && fixed_norm2 + free_norm2 <= 2.0 * fixed_endpoint * free_endpoint_input) + return; + if (free_count == 0) + { + double lower_bound = fixed_norm2 / (2.0 * fixed_endpoint); + point[free_endpoint_index] = fmax(free_endpoint_input, lower_bound) * rescaling[free_endpoint_index]; + return; + } + + double lo = 0.0; + double violation = fixed_norm2 + free_norm2 - 2.0 * fixed_endpoint * free_endpoint_input; + double hi = omega_endpoint * violation / (2.0 * fixed_endpoint * fixed_endpoint); + hi *= 1.0 + 64.0 * DBL_EPSILON; + if (!(hi > 0.0) || !isfinite(hi)) + { + hi = warm_start && *warm_start > 0.0 && isfinite(*warm_start) ? *warm_start : omega_endpoint; + for (int expansion = 0; expansion < 100; ++expansion) + { + double norm2 = fixed_norm2; + for (int slot = 0; slot < k; ++slot) + { + int index = start + slot; + if (is_fixed[index]) + continue; + double omega = cone_section_weight(rescaling, q_diag, tau, index); + double value = cone_section_actual(point, rescaling, index) * omega / (omega + hi); + norm2 += value * value; + } + double endpoint = free_endpoint_input + hi * fixed_endpoint / omega_endpoint; + if (norm2 <= 2.0 * fixed_endpoint * endpoint) + break; + hi *= 2.0; + } + } + for (int iteration = 0; iteration < 80; ++iteration) + { + double lambda = 0.5 * (lo + hi); + double norm2 = fixed_norm2; + for (int slot = 0; slot < k; ++slot) + { + int index = start + slot; + if (is_fixed[index]) + continue; + double omega = cone_section_weight(rescaling, q_diag, tau, index); + double value = cone_section_actual(point, rescaling, index) * omega / (omega + lambda); + norm2 += value * value; + } + double endpoint = free_endpoint_input + lambda * fixed_endpoint / omega_endpoint; + if (norm2 > 2.0 * fixed_endpoint * endpoint) + lo = lambda; + else + hi = lambda; + if ((hi - lo) <= 1e-13 * (1.0 + hi + lo)) + break; + } + double lambda = 0.5 * (lo + hi); + if (warm_start) + *warm_start = lambda; + for (int slot = 0; slot < k; ++slot) + { + int index = start + slot; + if (!is_fixed[index]) + { + double omega = cone_section_weight(rescaling, q_diag, tau, index); + point[index] *= omega / (omega + lambda); + } + } + point[free_endpoint_index] = + (free_endpoint_input + lambda * fixed_endpoint / omega_endpoint) * rescaling[free_endpoint_index]; + return; + } + + double total_norm2 = fixed_norm2 + free_norm2; + if (s_input >= 0.0 && t_input >= 0.0 && total_norm2 <= 2.0 * s_input * t_input) + return; + + double omega_s = cone_section_weight(rescaling, q_diag, tau, s_index); + double omega_t = cone_section_weight(rescaling, q_diag, tau, t_index); + if (fixed_norm2 == 0.0) + { + double bs = omega_s * s_input; + double bt = omega_t * t_input; + if (bs <= 0.0 && bt <= 0.0 && polar_norm2 <= 2.0 * bs * bt) + { + for (int slot = 0; slot < k; ++slot) + if (!is_fixed[start + slot]) + point[start + slot] = 0.0; + point[s_index] = 0.0; + point[t_index] = 0.0; + return; + } + } + + double root_metric = sqrt(omega_s) * sqrt(omega_t); + double balance = sqrt(omega_s) * s_input + sqrt(omega_t) * t_input; + double balance_scale = 1.0 + fabs(sqrt(omega_s) * s_input) + fabs(sqrt(omega_t) * t_input); + double lambda = root_metric; + double projected_s = 0.0; + double projected_t = 0.0; + bool smooth_valid = true; + + if (fabs(balance) <= 64.0 * DBL_EPSILON * balance_scale) + { + double norm2 = fixed_norm2; + for (int slot = 0; slot < k; ++slot) + { + int index = start + slot; + if (is_fixed[index]) + continue; + double omega = cone_section_weight(rescaling, q_diag, tau, index); + double value = cone_section_actual(point, rescaling, index) * omega / (omega + lambda); + norm2 += value * value; + } + double product = 0.5 * root_metric * norm2; + double delta = sqrt(omega_s) * s_input; + double scaled_t = 0.5 * (-delta + sqrt(fmax(0.0, delta * delta + 4.0 * product))); + double scaled_s = scaled_t + delta; + projected_s = scaled_s / sqrt(omega_s); + projected_t = scaled_t / sqrt(omega_t); + smooth_valid = projected_s >= 0.0 && projected_t >= 0.0; + } + else + { + bool lower_branch = balance > 0.0; + double lo = lower_branch ? 0.0 : root_metric * (1.0 + 1e-14); + double hi = lower_branch ? root_metric * (1.0 - 1e-14) : 2.0 * root_metric; + + if (!lower_branch) + { + hi = cone_section_negative_rsoc_upper( + omega_s, omega_t, s_input, t_input, fixed_norm2, polar_norm2, max_omega); + if (!(hi > lo) || !isfinite(hi)) + { + hi = 2.0 * root_metric; + for (int expansion = 0; expansion < 100; ++expansion) + { + double determinant = omega_s * omega_t - hi * hi; + double s = omega_t * (omega_s * s_input + hi * t_input) / determinant; + double t = omega_s * (omega_t * t_input + hi * s_input) / determinant; + double f = INFINITY; + if (s >= 0.0 && t >= 0.0) + { + double norm2 = fixed_norm2; + for (int slot = 0; slot < k; ++slot) + { + int index = start + slot; + if (is_fixed[index]) + continue; + double omega = cone_section_weight(rescaling, q_diag, tau, index); + double value = cone_section_actual(point, rescaling, index) * omega / (omega + hi); + norm2 += value * value; + } + f = norm2 - 2.0 * s * t; + } + if (f >= 0.0) + break; + hi *= 2.0; + } + } + } + + for (int iteration = 0; iteration < 90; ++iteration) + { + double trial = 0.5 * (lo + hi); + double determinant = omega_s * omega_t - trial * trial; + double s = omega_t * (omega_s * s_input + trial * t_input) / determinant; + double t = omega_s * (omega_t * t_input + trial * s_input) / determinant; + double f = INFINITY; + if (s >= 0.0 && t >= 0.0) + { + double norm2 = fixed_norm2; + for (int slot = 0; slot < k; ++slot) + { + int index = start + slot; + if (is_fixed[index]) + continue; + double omega = cone_section_weight(rescaling, q_diag, tau, index); + double value = cone_section_actual(point, rescaling, index) * omega / (omega + trial); + norm2 += value * value; + } + f = norm2 - 2.0 * s * t; + } + if ((lower_branch && f > 0.0) || (!lower_branch && f < 0.0)) + lo = trial; + else + hi = trial; + if ((hi - lo) <= 1e-13 * (1.0 + hi + lo)) + break; + } + lambda = 0.5 * (lo + hi); + double determinant = omega_s * omega_t - lambda * lambda; + projected_s = omega_t * (omega_s * s_input + lambda * t_input) / determinant; + projected_t = omega_s * (omega_t * t_input + lambda * s_input) / determinant; + smooth_valid = isfinite(projected_s) && isfinite(projected_t) && projected_s >= 0.0 && projected_t >= 0.0; + } + + double best_objective = smooth_valid + ? rotated_soc_smooth_objective( + point, rescaling, q_diag, tau, start, k, is_fixed, lambda, projected_s, projected_t) + : INFINITY; + int mode = smooth_valid ? 0 : 1; + if (fixed_norm2 == 0.0) + { + double vector_objective = 0.0; + for (int slot = 0; slot < k; ++slot) + { + int index = start + slot; + if (!is_fixed[index]) + { + double omega = cone_section_weight(rescaling, q_diag, tau, index); + double value = cone_section_actual(point, rescaling, index); + vector_objective += omega * value * value; + } + } + double s_axis = fmax(s_input, 0.0); + double s_axis_objective = + vector_objective + omega_s * (s_axis - s_input) * (s_axis - s_input) + omega_t * t_input * t_input; + if (s_axis_objective < best_objective) + { + best_objective = s_axis_objective; + projected_s = s_axis; + projected_t = 0.0; + mode = 1; + } + double t_axis = fmax(t_input, 0.0); + double t_axis_objective = + vector_objective + omega_s * s_input * s_input + omega_t * (t_axis - t_input) * (t_axis - t_input); + if (t_axis_objective < best_objective) + { + projected_s = 0.0; + projected_t = t_axis; + mode = 1; + } + } + + if (mode == 0) + { + for (int slot = 0; slot < k; ++slot) + { + int index = start + slot; + if (!is_fixed[index]) + { + double omega = cone_section_weight(rescaling, q_diag, tau, index); + point[index] *= omega / (omega + lambda); + } + } + if (warm_start) + *warm_start = lambda; + } + else + { + for (int slot = 0; slot < k; ++slot) + if (!is_fixed[start + slot]) + point[start + slot] = 0.0; + if (warm_start) + *warm_start = 0.0; + } + point[s_index] = projected_s * rescaling[s_index]; + point[t_index] = projected_t * rescaling[t_index]; +} +#include "utils.h" + +#include +#include +#include + +__global__ void project_rotated_soc_kernel(double *__restrict__ primal_solution, + const double *__restrict__ variable_rescaling, + double *__restrict__ warm_start, + const int *__restrict__ start_idx, + const int *__restrict__ v_dim, + const char *__restrict__ is_fixed, + int num_blocks) +{ + int blk = blockIdx.x * blockDim.x + threadIdx.x; + if (blk >= num_blocks) + return; + + const double INV_SQRT2 = 0.7071067811865475; + + int start = start_idx[blk]; + int k = v_dim[blk]; + if (cone_section_has_fixed(is_fixed, start, k + 2)) + { + project_rotated_soc_section_serial( + primal_solution, variable_rescaling, NULL, 0.0, warm_start + blk, start, k, is_fixed); + return; + } + double *v = primal_solution + start; + double *sptr = primal_solution + start + k; + double *tptr = primal_solution + start + k + 1; + + double s = *sptr; + double t = *tptr; + + double w = (s - t) * INV_SQRT2; + double z = (s + t) * INV_SQRT2; + + double d_s = variable_rescaling[start + k]; + double d_t = variable_rescaling[start + k + 1]; + double d_st = sqrt(d_s * d_t); + + bool diag_uniform = true; + for (int m = 0; m < k && diag_uniform; ++m) + { + if (variable_rescaling[start + m] != d_st) + diag_uniform = false; + } + if (diag_uniform) + { + double sumsq = w * w; + for (int m = 0; m < k; ++m) + sumsq += v[m] * v[m]; + double r = sqrt(sumsq); + if (r <= z) + return; + if (r <= -z) + { + for (int m = 0; m < k; ++m) + v[m] = 0.0; + *sptr = 0.0; + *tptr = 0.0; + return; + } + double scale = (z + r) / (2.0 * r); + for (int m = 0; m < k; ++m) + v[m] *= scale; + double w_new = scale * w; + double z_new = scale * r; + *sptr = (z_new + w_new) * INV_SQRT2; + *tptr = (z_new - w_new) * INV_SQRT2; + return; + } + + double r_inv_sq = w * w; + double r_pos_sq = w * w; + for (int m = 0; m < k; ++m) + { + double dh = variable_rescaling[start + m] / d_st; + double v_m = v[m]; + r_inv_sq += (v_m / dh) * (v_m / dh); + r_pos_sq += (v_m * dh) * (v_m * dh); + } + double r_inv = sqrt(r_inv_sq); + if (r_inv <= z) + return; + double r_pos = sqrt(r_pos_sq); + if (r_pos <= -z) + { + for (int m = 0; m < k; ++m) + v[m] = 0.0; + *sptr = 0.0; + *tptr = 0.0; + return; + } + + double lo, hi; + bool z_pos = (z > 0.0); + if (z_pos) + { + lo = 0.0; + hi = 0.5 - 1e-14; + } + else + { + lo = 0.5 + 1e-14; + hi = 1.0; + for (int doubling = 0; doubling < 60; ++doubling) + { + double sum_hi = 0.0; + for (int m = 0; m < k; ++m) + { + double dh = variable_rescaling[start + m] / d_st; + double dh2 = dh * dh; + double tt = v[m] * dh / (dh2 + 2.0 * hi); + sum_hi += tt * tt; + } + double tw_hi = w / (1.0 + 2.0 * hi); + sum_hi += tw_hi * tw_hi; + double zt_hi = z / (1.0 - 2.0 * hi); + double f_hi = sum_hi - zt_hi * zt_hi; + if (f_hi > 0.0) + break; + lo = hi; + hi *= 2.0; + } + } + + double warm_lam = warm_start[blk]; + if (warm_lam > lo && warm_lam < hi) + { + double sum_w = 0.0; + for (int m = 0; m < k; ++m) + { + double dh = variable_rescaling[start + m] / d_st; + double dh2 = dh * dh; + double tt = v[m] * dh / (dh2 + 2.0 * warm_lam); + sum_w += tt * tt; + } + double tw = w / (1.0 + 2.0 * warm_lam); + sum_w += tw * tw; + double zt = z / (1.0 - 2.0 * warm_lam); + double f = sum_w - zt * zt; + if (fabs(f) < 1e-12) + { + double w_new = w / (1.0 + 2.0 * warm_lam); + double z_new = z / (1.0 - 2.0 * warm_lam); + for (int m = 0; m < k; ++m) + { + double dh = variable_rescaling[start + m] / d_st; + double dh2 = dh * dh; + v[m] = v[m] * dh2 / (dh2 + 2.0 * warm_lam); + } + *sptr = (z_new + w_new) * INV_SQRT2; + *tptr = (z_new - w_new) * INV_SQRT2; + return; + } + if (z_pos) + { + if (f > 0.0) + lo = warm_lam; + else + hi = warm_lam; + } + else + { + if (f > 0.0) + hi = warm_lam; + else + lo = warm_lam; + } + } + + for (int it = 0; it < 60; ++it) + { + double lam = 0.5 * (lo + hi); + double sum = 0.0; + for (int m = 0; m < k; ++m) + { + double dh = variable_rescaling[start + m] / d_st; + double dh2 = dh * dh; + double tt = v[m] * dh / (dh2 + 2.0 * lam); + sum += tt * tt; + } + double tw = w / (1.0 + 2.0 * lam); + sum += tw * tw; + double zt = z / (1.0 - 2.0 * lam); + double f = sum - zt * zt; + if (z_pos) + { + if (f > 0.0) + lo = lam; + else + hi = lam; + } + else + { + if (f > 0.0) + hi = lam; + else + lo = lam; + } + if ((hi - lo) / (1.0 + hi + lo) < 1e-13) + break; + } + double lam = 0.5 * (lo + hi); + warm_start[blk] = lam; + + double w_new = w / (1.0 + 2.0 * lam); + double z_new = z / (1.0 - 2.0 * lam); + for (int m = 0; m < k; ++m) + { + double dh = variable_rescaling[start + m] / d_st; + double dh2 = dh * dh; + v[m] = v[m] * dh2 / (dh2 + 2.0 * lam); + } + *sptr = (z_new + w_new) * INV_SQRT2; + *tptr = (z_new - w_new) * INV_SQRT2; +} + +__global__ void compute_cone_dual_residual_kernel(double *__restrict__ dual_residual, + double *__restrict__ complementarity_residual, + const double *__restrict__ objective_vector, + const double *__restrict__ dual_product, + const double *__restrict__ variable_rescaling, + const double *__restrict__ primal_solution, + double *__restrict__ warm_start, + const int *__restrict__ start_idx, + const int *__restrict__ v_dim, + const char *__restrict__ is_fixed, + int num_blocks) +{ + int blk = blockIdx.x * blockDim.x + threadIdx.x; + if (blk >= num_blocks) + return; + + const double INV_SQRT2 = 0.7071067811865475; + int start = start_idx[blk]; + int k = v_dim[blk]; + + if (cone_section_has_fixed(is_fixed, start, k + 2)) + { + for (int slot = 0; slot < k + 2; ++slot) + { + int index = start + slot; + double residual = objective_vector[index] - dual_product[index]; + dual_residual[index] = is_fixed[index] ? primal_solution[index] : primal_solution[index] - residual; + } + project_rotated_soc_section_serial( + dual_residual, variable_rescaling, NULL, 0.0, warm_start + blk, start, k, is_fixed); + for (int slot = 0; slot < k + 2; ++slot) + { + int index = start + slot; + dual_residual[index] = + is_fixed[index] ? 0.0 : (primal_solution[index] - dual_residual[index]) * variable_rescaling[index]; + } + complementarity_residual[blk] = 0.0; + return; + } + + double r_s = objective_vector[start + k] - dual_product[start + k]; + double r_t = objective_vector[start + k + 1] - dual_product[start + k + 1]; + double r_w = (r_s - r_t) * INV_SQRT2; + double r_z = (r_s + r_t) * INV_SQRT2; + + double d_s = variable_rescaling[start + k]; + double d_t = variable_rescaling[start + k + 1]; + double d_st = sqrt(d_s * d_t); + + bool diag_uniform = true; + for (int m = 0; m < k && diag_uniform; ++m) + { + if (variable_rescaling[start + m] != d_st) + diag_uniform = false; + } + + if (diag_uniform) + { + double sumsq = r_w * r_w; + for (int m = 0; m < k; ++m) + { + double v_m = objective_vector[start + m] - dual_product[start + m]; + sumsq += v_m * v_m; + } + double r_norm = sqrt(sumsq); + + double v_factor, p_s, p_t; + if (r_norm <= r_z) + { + v_factor = 0.0; + p_s = r_s; + p_t = r_t; + } + else if (r_norm <= -r_z) + { + v_factor = 1.0; + p_s = 0.0; + p_t = 0.0; + } + else + { + double scale = (r_z + r_norm) / (2.0 * r_norm); + v_factor = 1.0 - scale; + double w_new = scale * r_w; + double z_new = scale * r_norm; + p_s = (z_new + w_new) * INV_SQRT2; + p_t = (z_new - w_new) * INV_SQRT2; + } + for (int m = 0; m < k; ++m) + { + double v_m = objective_vector[start + m] - dual_product[start + m]; + dual_residual[start + m] = v_m * v_factor * variable_rescaling[start + m]; + } + dual_residual[start + k] = (r_s - p_s) * variable_rescaling[start + k]; + dual_residual[start + k + 1] = (r_t - p_t) * variable_rescaling[start + k + 1]; + return; + } + + double r_inv_sq = r_w * r_w; + double r_pos_sq = r_w * r_w; + for (int m = 0; m < k; ++m) + { + double e_m = d_st / variable_rescaling[start + m]; + double rc_m = objective_vector[start + m] - dual_product[start + m]; + r_inv_sq += (rc_m / e_m) * (rc_m / e_m); + r_pos_sq += (rc_m * e_m) * (rc_m * e_m); + } + double r_inv = sqrt(r_inv_sq); + double r_pos = sqrt(r_pos_sq); + + if (r_inv <= r_z) + { + for (int m = 0; m < k; ++m) + dual_residual[start + m] = 0.0; + dual_residual[start + k] = 0.0; + dual_residual[start + k + 1] = 0.0; + return; + } + if (r_pos <= -r_z) + { + for (int m = 0; m < k; ++m) + { + double rc_m = objective_vector[start + m] - dual_product[start + m]; + dual_residual[start + m] = rc_m * variable_rescaling[start + m]; + } + dual_residual[start + k] = r_s * variable_rescaling[start + k]; + dual_residual[start + k + 1] = r_t * variable_rescaling[start + k + 1]; + return; + } + + double lo, hi; + bool z_pos = (r_z > 0.0); + if (z_pos) + { + lo = 0.0; + hi = 0.5 - 1e-14; + } + else + { + lo = 0.5 + 1e-14; + hi = 1.0; + for (int doubling = 0; doubling < 60; ++doubling) + { + double sum_hi = 0.0; + for (int m = 0; m < k; ++m) + { + double e_m = d_st / variable_rescaling[start + m]; + double e_m2 = e_m * e_m; + double rc_m = objective_vector[start + m] - dual_product[start + m]; + double tt = rc_m * e_m / (e_m2 + 2.0 * hi); + sum_hi += tt * tt; + } + double tw_hi = r_w / (1.0 + 2.0 * hi); + sum_hi += tw_hi * tw_hi; + double zt_hi = r_z / (1.0 - 2.0 * hi); + double f_hi = sum_hi - zt_hi * zt_hi; + if (f_hi > 0.0) + break; + lo = hi; + hi *= 2.0; + } + } + + double warm_lam = warm_start[blk]; + if (warm_lam > lo && warm_lam < hi) + { + double sum_w = 0.0; + for (int m = 0; m < k; ++m) + { + double e_m = d_st / variable_rescaling[start + m]; + double e_m2 = e_m * e_m; + double rc_m = objective_vector[start + m] - dual_product[start + m]; + double tt = rc_m * e_m / (e_m2 + 2.0 * warm_lam); + sum_w += tt * tt; + } + double tw = r_w / (1.0 + 2.0 * warm_lam); + sum_w += tw * tw; + double zt = r_z / (1.0 - 2.0 * warm_lam); + double f = sum_w - zt * zt; + if (fabs(f) < 1e-12) + { + double p_w_w = r_w / (1.0 + 2.0 * warm_lam); + double p_z_w = r_z / (1.0 - 2.0 * warm_lam); + double p_s_w = (p_z_w + p_w_w) * INV_SQRT2; + double p_t_w = (p_z_w - p_w_w) * INV_SQRT2; + for (int m = 0; m < k; ++m) + { + double e_m = d_st / variable_rescaling[start + m]; + double e_m2 = e_m * e_m; + double rc_m = objective_vector[start + m] - dual_product[start + m]; + double p_m = rc_m * e_m2 / (e_m2 + 2.0 * warm_lam); + dual_residual[start + m] = (rc_m - p_m) * variable_rescaling[start + m]; + } + dual_residual[start + k] = (r_s - p_s_w) * variable_rescaling[start + k]; + dual_residual[start + k + 1] = (r_t - p_t_w) * variable_rescaling[start + k + 1]; + return; + } + if (z_pos) + { + if (f > 0.0) + lo = warm_lam; + else + hi = warm_lam; + } + else + { + if (f > 0.0) + hi = warm_lam; + else + lo = warm_lam; + } + } + + for (int it = 0; it < 60; ++it) + { + double lam = 0.5 * (lo + hi); + double sum = 0.0; + for (int m = 0; m < k; ++m) + { + double e_m = d_st / variable_rescaling[start + m]; + double e_m2 = e_m * e_m; + double rc_m = objective_vector[start + m] - dual_product[start + m]; + double tt = rc_m * e_m / (e_m2 + 2.0 * lam); + sum += tt * tt; + } + double tw = r_w / (1.0 + 2.0 * lam); + sum += tw * tw; + double zt = r_z / (1.0 - 2.0 * lam); + double f = sum - zt * zt; + if (z_pos) + { + if (f > 0.0) + lo = lam; + else + hi = lam; + } + else + { + if (f > 0.0) + hi = lam; + else + lo = lam; + } + if ((hi - lo) / (1.0 + hi + lo) < 1e-13) + break; + } + double lam = 0.5 * (lo + hi); + warm_start[blk] = lam; + + double p_w = r_w / (1.0 + 2.0 * lam); + double p_z = r_z / (1.0 - 2.0 * lam); + double p_s = (p_z + p_w) * INV_SQRT2; + double p_t = (p_z - p_w) * INV_SQRT2; + + for (int m = 0; m < k; ++m) + { + double e_m = d_st / variable_rescaling[start + m]; + double e_m2 = e_m * e_m; + double rc_m = objective_vector[start + m] - dual_product[start + m]; + double p_m = rc_m * e_m2 / (e_m2 + 2.0 * lam); + dual_residual[start + m] = (rc_m - p_m) * variable_rescaling[start + m]; + } + dual_residual[start + k] = (r_s - p_s) * variable_rescaling[start + k]; + dual_residual[start + k + 1] = (r_t - p_t) * variable_rescaling[start + k + 1]; +} + +__global__ void project_rotated_soc_grid_reduce_kernel(double *__restrict__ primal_solution, + double *__restrict__ workspace, + const int *__restrict__ start_idx, + const int *__restrict__ v_dim, + int num_cones, + int blocks_per_cone) +{ + int cone = blockIdx.x / blocks_per_cone; + int part = blockIdx.x - cone * blocks_per_cone; + if (cone >= num_cones) + return; + + int start = start_idx[cone]; + int k = v_dim[cone]; + double sum = 0.0; + for (int m = part * blockDim.x + threadIdx.x; m < k; m += blocks_per_cone * blockDim.x) + { + double value = primal_solution[start + m]; + sum += value * value; + } + sum = large_cone_block_sum(sum); + if (threadIdx.x == 0) + atomicAdd(workspace + cone, sum); +} + +__global__ void project_rotated_soc_grid_finalize_kernel(double *__restrict__ primal_solution, + double *__restrict__ workspace, + const int *__restrict__ start_idx, + const int *__restrict__ v_dim, + int num_cones) +{ + int cone = blockIdx.x * blockDim.x + threadIdx.x; + if (cone >= num_cones) + return; + + const double INV_SQRT2 = 0.7071067811865475; + int start = start_idx[cone]; + int k = v_dim[cone]; + double s = primal_solution[start + k]; + double t = primal_solution[start + k + 1]; + double w = (s - t) * INV_SQRT2; + double z = (s + t) * INV_SQRT2; + double radius = sqrt(fmax(0.0, workspace[cone] + w * w)); + + if (radius <= z) + { + workspace[cone] = 1.0; + return; + } + if (radius <= -z) + { + workspace[cone] = 0.0; + primal_solution[start + k] = 0.0; + primal_solution[start + k + 1] = 0.0; + return; + } + + double scale = (z + radius) / (2.0 * radius); + double w_new = scale * w; + double z_new = scale * radius; + workspace[cone] = scale; + primal_solution[start + k] = (z_new + w_new) * INV_SQRT2; + primal_solution[start + k + 1] = (z_new - w_new) * INV_SQRT2; +} + +__global__ void project_rotated_soc_grid_apply_kernel(double *__restrict__ primal_solution, + const double *__restrict__ workspace, + const int *__restrict__ start_idx, + const int *__restrict__ v_dim, + int num_cones, + int blocks_per_cone) +{ + int cone = blockIdx.x / blocks_per_cone; + int part = blockIdx.x - cone * blocks_per_cone; + if (cone >= num_cones) + return; + + double scale = workspace[cone]; + if (scale == 1.0) + return; + + int start = start_idx[cone]; + int k = v_dim[cone]; + for (int m = part * blockDim.x + threadIdx.x; m < k; m += blocks_per_cone * blockDim.x) + { + primal_solution[start + m] *= scale; + } +} + +__global__ void compute_cone_dual_residual_grid_reduce_kernel(const double *__restrict__ objective_vector, + const double *__restrict__ dual_product, + double *__restrict__ workspace, + const int *__restrict__ start_idx, + const int *__restrict__ v_dim, + int num_cones, + int blocks_per_cone) +{ + int cone = blockIdx.x / blocks_per_cone; + int part = blockIdx.x - cone * blocks_per_cone; + if (cone >= num_cones) + return; + + int start = start_idx[cone]; + int k = v_dim[cone]; + double sum = 0.0; + for (int m = part * blockDim.x + threadIdx.x; m < k; m += blocks_per_cone * blockDim.x) + { + double residual = objective_vector[start + m] - dual_product[start + m]; + sum += residual * residual; + } + sum = large_cone_block_sum(sum); + if (threadIdx.x == 0) + atomicAdd(workspace + cone, sum); +} + +__global__ void compute_cone_dual_residual_grid_finalize_kernel(double *__restrict__ dual_residual, + const double *__restrict__ objective_vector, + const double *__restrict__ dual_product, + const double *__restrict__ variable_rescaling, + double *__restrict__ workspace, + const int *__restrict__ start_idx, + const int *__restrict__ v_dim, + int num_cones) +{ + int cone = blockIdx.x * blockDim.x + threadIdx.x; + if (cone >= num_cones) + return; + + const double INV_SQRT2 = 0.7071067811865475; + int start = start_idx[cone]; + int k = v_dim[cone]; + double r_s = objective_vector[start + k] - dual_product[start + k]; + double r_t = objective_vector[start + k + 1] - dual_product[start + k + 1]; + double r_w = (r_s - r_t) * INV_SQRT2; + double r_z = (r_s + r_t) * INV_SQRT2; + double norm = sqrt(fmax(0.0, workspace[cone] + r_w * r_w)); + double factor; + double p_s; + double p_t; + + if (norm <= r_z) + { + factor = 0.0; + p_s = r_s; + p_t = r_t; + } + else if (norm <= -r_z) + { + factor = 1.0; + p_s = 0.0; + p_t = 0.0; + } + else + { + double scale = (r_z + norm) / (2.0 * norm); + double w_new = scale * r_w; + double z_new = scale * norm; + factor = 1.0 - scale; + p_s = (z_new + w_new) * INV_SQRT2; + p_t = (z_new - w_new) * INV_SQRT2; + } + + workspace[cone] = factor; + dual_residual[start + k] = (r_s - p_s) * variable_rescaling[start + k]; + dual_residual[start + k + 1] = (r_t - p_t) * variable_rescaling[start + k + 1]; +} + +__global__ void compute_cone_dual_residual_grid_apply_kernel(double *__restrict__ dual_residual, + const double *__restrict__ objective_vector, + const double *__restrict__ dual_product, + const double *__restrict__ variable_rescaling, + const double *__restrict__ workspace, + const int *__restrict__ start_idx, + const int *__restrict__ v_dim, + int num_cones, + int blocks_per_cone) +{ + int cone = blockIdx.x / blocks_per_cone; + int part = blockIdx.x - cone * blocks_per_cone; + if (cone >= num_cones) + return; + + double factor = workspace[cone]; + int start = start_idx[cone]; + int k = v_dim[cone]; + for (int m = part * blockDim.x + threadIdx.x; m < k; m += blocks_per_cone * blockDim.x) + { + int idx = start + m; + double residual = objective_vector[idx] - dual_product[idx]; + dual_residual[idx] = residual * factor * variable_rescaling[idx]; + } +} + +__global__ void project_rotated_soc_warp_kernel(double *__restrict__ primal_solution, + const double *__restrict__ variable_rescaling, + double *__restrict__ warm_start, + const int *__restrict__ start_idx, + const int *__restrict__ v_dim, + const char *__restrict__ is_fixed, + int num_blocks) +{ + int tid = blockIdx.x * blockDim.x + threadIdx.x; + int blk = tid >> 5; + int lane = tid & 31; + if (blk >= num_blocks) + return; + + const double INV_SQRT2 = 0.7071067811865475; + const unsigned MASK = 0xffffffffu; + + int start = start_idx[blk]; + int k = v_dim[blk]; + + int has_fixed = lane == 0 ? cone_section_has_fixed(is_fixed, start, k + 2) : 0; + has_fixed = __shfl_sync(MASK, has_fixed, 0); + if (has_fixed) + { + if (lane == 0) + project_rotated_soc_section_serial( + primal_solution, variable_rescaling, NULL, 0.0, warm_start + blk, start, k, is_fixed); + return; + } + + double s_val = primal_solution[start + k]; + double t_val = primal_solution[start + k + 1]; + + double w = (s_val - t_val) * INV_SQRT2; + double z = (s_val + t_val) * INV_SQRT2; + + double d_s = variable_rescaling[start + k]; + double d_t = variable_rescaling[start + k + 1]; + double d_st = sqrt(d_s * d_t); + + int my_diff = 0; + for (int m = lane; m < k; m += 32) + { + if (variable_rescaling[start + m] != d_st) + my_diff = 1; + } + for (int o = 16; o > 0; o >>= 1) + my_diff |= __shfl_xor_sync(MASK, my_diff, o); + + if (my_diff == 0) + { + double my_sumsq = (lane == 0) ? w * w : 0.0; + for (int m = lane; m < k; m += 32) + { + double v_m = primal_solution[start + m]; + my_sumsq += v_m * v_m; + } + for (int o = 16; o > 0; o >>= 1) + my_sumsq += __shfl_xor_sync(MASK, my_sumsq, o); + double r = sqrt(my_sumsq); + if (r <= z) + return; + if (r <= -z) + { + for (int m = lane; m < k; m += 32) + primal_solution[start + m] = 0.0; + if (lane == 0) + { + primal_solution[start + k] = 0.0; + primal_solution[start + k + 1] = 0.0; + } + return; + } + double scale = (z + r) / (2.0 * r); + for (int m = lane; m < k; m += 32) + primal_solution[start + m] *= scale; + double w_new = scale * w; + double z_new = scale * r; + if (lane == 0) + { + primal_solution[start + k] = (z_new + w_new) * INV_SQRT2; + primal_solution[start + k + 1] = (z_new - w_new) * INV_SQRT2; + } + return; + } + + double my_inv = (lane == 0) ? w * w : 0.0; + double my_pos = (lane == 0) ? w * w : 0.0; + for (int m = lane; m < k; m += 32) + { + double dh = variable_rescaling[start + m] / d_st; + double v_m = primal_solution[start + m]; + my_inv += (v_m / dh) * (v_m / dh); + my_pos += (v_m * dh) * (v_m * dh); + } + for (int o = 16; o > 0; o >>= 1) + { + my_inv += __shfl_xor_sync(MASK, my_inv, o); + my_pos += __shfl_xor_sync(MASK, my_pos, o); + } + double r_inv = sqrt(my_inv); + if (r_inv <= z) + return; + double r_pos = sqrt(my_pos); + if (r_pos <= -z) + { + for (int m = lane; m < k; m += 32) + primal_solution[start + m] = 0.0; + if (lane == 0) + { + primal_solution[start + k] = 0.0; + primal_solution[start + k + 1] = 0.0; + } + return; + } + + double lo, hi; + bool z_pos = (z > 0.0); + if (z_pos) + { + lo = 0.0; + hi = 0.5 - 1e-14; + } + else + { + lo = 0.5 + 1e-14; + hi = 1.0; + for (int doubling = 0; doubling < 60; ++doubling) + { + double my_sum = (lane == 0) ? (w / (1.0 + 2.0 * hi)) * (w / (1.0 + 2.0 * hi)) : 0.0; + for (int m = lane; m < k; m += 32) + { + double dh = variable_rescaling[start + m] / d_st; + double dh2 = dh * dh; + double tt = primal_solution[start + m] * dh / (dh2 + 2.0 * hi); + my_sum += tt * tt; + } + for (int o = 16; o > 0; o >>= 1) + my_sum += __shfl_xor_sync(MASK, my_sum, o); + double zt_hi = z / (1.0 - 2.0 * hi); + double f_hi = my_sum - zt_hi * zt_hi; + if (f_hi > 0.0) + break; + lo = hi; + hi *= 2.0; + } + } + + double warm_lam = warm_start[blk]; + if (warm_lam > lo && warm_lam < hi) + { + double my_sum = (lane == 0) ? (w / (1.0 + 2.0 * warm_lam)) * (w / (1.0 + 2.0 * warm_lam)) : 0.0; + for (int m = lane; m < k; m += 32) + { + double dh = variable_rescaling[start + m] / d_st; + double dh2 = dh * dh; + double tt = primal_solution[start + m] * dh / (dh2 + 2.0 * warm_lam); + my_sum += tt * tt; + } + for (int o = 16; o > 0; o >>= 1) + my_sum += __shfl_xor_sync(MASK, my_sum, o); + double zt = z / (1.0 - 2.0 * warm_lam); + double f = my_sum - zt * zt; + if (fabs(f) < 1e-12) + { + double w_new = w / (1.0 + 2.0 * warm_lam); + double z_new = z / (1.0 - 2.0 * warm_lam); + for (int m = lane; m < k; m += 32) + { + double dh = variable_rescaling[start + m] / d_st; + double dh2 = dh * dh; + primal_solution[start + m] = primal_solution[start + m] * dh2 / (dh2 + 2.0 * warm_lam); + } + if (lane == 0) + { + primal_solution[start + k] = (z_new + w_new) * INV_SQRT2; + primal_solution[start + k + 1] = (z_new - w_new) * INV_SQRT2; + } + return; + } + if (z_pos) + { + if (f > 0.0) + lo = warm_lam; + else + hi = warm_lam; + } + else + { + if (f > 0.0) + hi = warm_lam; + else + lo = warm_lam; + } + } + + for (int it = 0; it < 60; ++it) + { + double lam = 0.5 * (lo + hi); + double my_sum = (lane == 0) ? (w / (1.0 + 2.0 * lam)) * (w / (1.0 + 2.0 * lam)) : 0.0; + for (int m = lane; m < k; m += 32) + { + double dh = variable_rescaling[start + m] / d_st; + double dh2 = dh * dh; + double tt = primal_solution[start + m] * dh / (dh2 + 2.0 * lam); + my_sum += tt * tt; + } + for (int o = 16; o > 0; o >>= 1) + my_sum += __shfl_xor_sync(MASK, my_sum, o); + double zt = z / (1.0 - 2.0 * lam); + double f = my_sum - zt * zt; + if (z_pos) + { + if (f > 0.0) + lo = lam; + else + hi = lam; + } + else + { + if (f > 0.0) + hi = lam; + else + lo = lam; + } + if ((hi - lo) / (1.0 + hi + lo) < 1e-13) + break; + } + double lam = 0.5 * (lo + hi); + if (lane == 0) + warm_start[blk] = lam; + + double w_new = w / (1.0 + 2.0 * lam); + double z_new = z / (1.0 - 2.0 * lam); + for (int m = lane; m < k; m += 32) + { + double dh = variable_rescaling[start + m] / d_st; + double dh2 = dh * dh; + primal_solution[start + m] = primal_solution[start + m] * dh2 / (dh2 + 2.0 * lam); + } + if (lane == 0) + { + primal_solution[start + k] = (z_new + w_new) * INV_SQRT2; + primal_solution[start + k + 1] = (z_new - w_new) * INV_SQRT2; + } +} + +__global__ void compute_cone_dual_residual_warp_kernel(double *__restrict__ dual_residual, + double *__restrict__ complementarity_residual, + const double *__restrict__ objective_vector, + const double *__restrict__ dual_product, + const double *__restrict__ variable_rescaling, + const double *__restrict__ primal_solution, + double *__restrict__ warm_start, + const int *__restrict__ start_idx, + const int *__restrict__ v_dim, + const char *__restrict__ is_fixed, + int num_blocks) +{ + int tid = blockIdx.x * blockDim.x + threadIdx.x; + int blk = tid >> 5; + int lane = tid & 31; + if (blk >= num_blocks) + return; + + const double INV_SQRT2 = 0.7071067811865475; + const unsigned MASK = 0xffffffffu; + + int start = start_idx[blk]; + int k = v_dim[blk]; + + int has_fixed = lane == 0 ? cone_section_has_fixed(is_fixed, start, k + 2) : 0; + has_fixed = __shfl_sync(MASK, has_fixed, 0); + if (has_fixed) + { + if (lane == 0) + { + for (int slot = 0; slot < k + 2; ++slot) + { + int index = start + slot; + double residual = objective_vector[index] - dual_product[index]; + dual_residual[index] = is_fixed[index] ? primal_solution[index] : primal_solution[index] - residual; + } + project_rotated_soc_section_serial( + dual_residual, variable_rescaling, NULL, 0.0, warm_start + blk, start, k, is_fixed); + for (int slot = 0; slot < k + 2; ++slot) + { + int index = start + slot; + dual_residual[index] = + is_fixed[index] ? 0.0 : (primal_solution[index] - dual_residual[index]) * variable_rescaling[index]; + } + complementarity_residual[blk] = 0.0; + } + return; + } + + double r_s = objective_vector[start + k] - dual_product[start + k]; + double r_t = objective_vector[start + k + 1] - dual_product[start + k + 1]; + double r_w = (r_s - r_t) * INV_SQRT2; + double r_z = (r_s + r_t) * INV_SQRT2; + + double d_s = variable_rescaling[start + k]; + double d_t = variable_rescaling[start + k + 1]; + double d_st = sqrt(d_s * d_t); + + int my_diff = 0; + for (int m = lane; m < k; m += 32) + { + if (variable_rescaling[start + m] != d_st) + my_diff = 1; + } + for (int o = 16; o > 0; o >>= 1) + my_diff |= __shfl_xor_sync(MASK, my_diff, o); + + if (my_diff == 0) + { + double my_sumsq = (lane == 0) ? r_w * r_w : 0.0; + for (int m = lane; m < k; m += 32) + { + double rc_m = objective_vector[start + m] - dual_product[start + m]; + my_sumsq += rc_m * rc_m; + } + for (int o = 16; o > 0; o >>= 1) + my_sumsq += __shfl_xor_sync(MASK, my_sumsq, o); + double r_norm = sqrt(my_sumsq); + + double v_factor, p_s, p_t; + if (r_norm <= r_z) + { + v_factor = 0.0; + p_s = r_s; + p_t = r_t; + } + else if (r_norm <= -r_z) + { + v_factor = 1.0; + p_s = 0.0; + p_t = 0.0; + } + else + { + double scale = (r_z + r_norm) / (2.0 * r_norm); + v_factor = 1.0 - scale; + double w_new = scale * r_w; + double z_new = scale * r_norm; + p_s = (z_new + w_new) * INV_SQRT2; + p_t = (z_new - w_new) * INV_SQRT2; + } + + for (int m = lane; m < k; m += 32) + { + double rc_m = objective_vector[start + m] - dual_product[start + m]; + dual_residual[start + m] = rc_m * v_factor * variable_rescaling[start + m]; + } + if (lane == 0) + { + dual_residual[start + k] = (r_s - p_s) * variable_rescaling[start + k]; + dual_residual[start + k + 1] = (r_t - p_t) * variable_rescaling[start + k + 1]; + } + return; + } + + double my_inv = (lane == 0) ? r_w * r_w : 0.0; + double my_pos = (lane == 0) ? r_w * r_w : 0.0; + for (int m = lane; m < k; m += 32) + { + double e_m = d_st / variable_rescaling[start + m]; + double rc_m = objective_vector[start + m] - dual_product[start + m]; + my_inv += (rc_m / e_m) * (rc_m / e_m); + my_pos += (rc_m * e_m) * (rc_m * e_m); + } + for (int o = 16; o > 0; o >>= 1) + { + my_inv += __shfl_xor_sync(MASK, my_inv, o); + my_pos += __shfl_xor_sync(MASK, my_pos, o); + } + double r_inv = sqrt(my_inv); + double r_pos = sqrt(my_pos); + + if (r_inv <= r_z) + { + for (int m = lane; m < k; m += 32) + dual_residual[start + m] = 0.0; + if (lane == 0) + { + dual_residual[start + k] = 0.0; + dual_residual[start + k + 1] = 0.0; + } + return; + } + if (r_pos <= -r_z) + { + for (int m = lane; m < k; m += 32) + { + double rc_m = objective_vector[start + m] - dual_product[start + m]; + dual_residual[start + m] = rc_m * variable_rescaling[start + m]; + } + if (lane == 0) + { + dual_residual[start + k] = r_s * variable_rescaling[start + k]; + dual_residual[start + k + 1] = r_t * variable_rescaling[start + k + 1]; + } + return; + } + + double lo, hi; + bool z_pos = (r_z > 0.0); + if (z_pos) + { + lo = 0.0; + hi = 0.5 - 1e-14; + } + else + { + lo = 0.5 + 1e-14; + hi = 1.0; + for (int doubling = 0; doubling < 60; ++doubling) + { + double my_sum = (lane == 0) ? (r_w / (1.0 + 2.0 * hi)) * (r_w / (1.0 + 2.0 * hi)) : 0.0; + for (int m = lane; m < k; m += 32) + { + double e_m = d_st / variable_rescaling[start + m]; + double e_m2 = e_m * e_m; + double rc_m = objective_vector[start + m] - dual_product[start + m]; + double tt = rc_m * e_m / (e_m2 + 2.0 * hi); + my_sum += tt * tt; + } + for (int o = 16; o > 0; o >>= 1) + my_sum += __shfl_xor_sync(MASK, my_sum, o); + double zt_hi = r_z / (1.0 - 2.0 * hi); + double f_hi = my_sum - zt_hi * zt_hi; + if (f_hi > 0.0) + break; + lo = hi; + hi *= 2.0; + } + } + + double warm_lam = warm_start[blk]; + if (warm_lam > lo && warm_lam < hi) + { + double my_sum = (lane == 0) ? (r_w / (1.0 + 2.0 * warm_lam)) * (r_w / (1.0 + 2.0 * warm_lam)) : 0.0; + for (int m = lane; m < k; m += 32) + { + double e_m = d_st / variable_rescaling[start + m]; + double e_m2 = e_m * e_m; + double rc_m = objective_vector[start + m] - dual_product[start + m]; + double tt = rc_m * e_m / (e_m2 + 2.0 * warm_lam); + my_sum += tt * tt; + } + for (int o = 16; o > 0; o >>= 1) + my_sum += __shfl_xor_sync(MASK, my_sum, o); + double zt = r_z / (1.0 - 2.0 * warm_lam); + double f = my_sum - zt * zt; + if (fabs(f) < 1e-12) + { + double p_w_w = r_w / (1.0 + 2.0 * warm_lam); + double p_z_w = r_z / (1.0 - 2.0 * warm_lam); + double p_s_w = (p_z_w + p_w_w) * INV_SQRT2; + double p_t_w = (p_z_w - p_w_w) * INV_SQRT2; + for (int m = lane; m < k; m += 32) + { + double e_m = d_st / variable_rescaling[start + m]; + double e_m2 = e_m * e_m; + double rc_m = objective_vector[start + m] - dual_product[start + m]; + double p_m = rc_m * e_m2 / (e_m2 + 2.0 * warm_lam); + dual_residual[start + m] = (rc_m - p_m) * variable_rescaling[start + m]; + } + if (lane == 0) + { + dual_residual[start + k] = (r_s - p_s_w) * variable_rescaling[start + k]; + dual_residual[start + k + 1] = (r_t - p_t_w) * variable_rescaling[start + k + 1]; + } + return; + } + if (z_pos) + { + if (f > 0.0) + lo = warm_lam; + else + hi = warm_lam; + } + else + { + if (f > 0.0) + hi = warm_lam; + else + lo = warm_lam; + } + } + + for (int it = 0; it < 60; ++it) + { + double lam = 0.5 * (lo + hi); + double my_sum = (lane == 0) ? (r_w / (1.0 + 2.0 * lam)) * (r_w / (1.0 + 2.0 * lam)) : 0.0; + for (int m = lane; m < k; m += 32) + { + double e_m = d_st / variable_rescaling[start + m]; + double e_m2 = e_m * e_m; + double rc_m = objective_vector[start + m] - dual_product[start + m]; + double tt = rc_m * e_m / (e_m2 + 2.0 * lam); + my_sum += tt * tt; + } + for (int o = 16; o > 0; o >>= 1) + my_sum += __shfl_xor_sync(MASK, my_sum, o); + double zt = r_z / (1.0 - 2.0 * lam); + double f = my_sum - zt * zt; + if (z_pos) + { + if (f > 0.0) + lo = lam; + else + hi = lam; + } + else + { + if (f > 0.0) + hi = lam; + else + lo = lam; + } + if ((hi - lo) / (1.0 + hi + lo) < 1e-13) + break; + } + double lam = 0.5 * (lo + hi); + if (lane == 0) + warm_start[blk] = lam; + + double p_w = r_w / (1.0 + 2.0 * lam); + double p_z = r_z / (1.0 - 2.0 * lam); + double p_s = (p_z + p_w) * INV_SQRT2; + double p_t = (p_z - p_w) * INV_SQRT2; + + for (int m = lane; m < k; m += 32) + { + double e_m = d_st / variable_rescaling[start + m]; + double e_m2 = e_m * e_m; + double rc_m = objective_vector[start + m] - dual_product[start + m]; + double p_m = rc_m * e_m2 / (e_m2 + 2.0 * lam); + dual_residual[start + m] = (rc_m - p_m) * variable_rescaling[start + m]; + } + if (lane == 0) + { + dual_residual[start + k] = (r_s - p_s) * variable_rescaling[start + k]; + dual_residual[start + k + 1] = (r_t - p_t) * variable_rescaling[start + k + 1]; + } +} + +__global__ void project_rotated_soc_diag_q_kernel(double *__restrict__ pdhg_primal, + double *__restrict__ reflected_primal, + const double *__restrict__ current_primal, + const double *__restrict__ variable_rescaling, + const double *__restrict__ Q_diag, + double tau, + double *__restrict__ warm_start, + const int *__restrict__ start_idx, + const int *__restrict__ v_dim, + const char *__restrict__ is_fixed, + int num_blocks) +{ + int blk = blockIdx.x * blockDim.x + threadIdx.x; + if (blk >= num_blocks) + return; + + const double W_FLOOR = 1e-300; + + int start = start_idx[blk]; + int k = v_dim[blk]; + int len = k + 2; + + if (cone_section_has_fixed(is_fixed, start, len)) + { + project_rotated_soc_section_serial( + pdhg_primal, variable_rescaling, Q_diag, tau, warm_start + blk, start, k, is_fixed); + for (int slot = 0; slot < len; ++slot) + { + int index = start + slot; + reflected_primal[index] = 2.0 * pdhg_primal[index] - current_primal[index]; + } + return; + } + + double r_s = pdhg_primal[start + k]; + double r_t = pdhg_primal[start + k + 1]; + + double q_s = Q_diag[start + k]; + double q_t = Q_diag[start + k + 1]; + double w_s = 1.0 + tau * q_s; + double w_t = 1.0 + tau * q_t; + if (!(w_s > W_FLOOR)) + w_s = W_FLOOR; + if (!(w_t > W_FLOOR)) + w_t = W_FLOOR; + double sigma = sqrt(w_s * w_t); + double alpha = sqrt(w_t / w_s); + double inv_alpha = 1.0 / alpha; + + double d_s = variable_rescaling[start + k]; + double d_t = variable_rescaling[start + k + 1]; + double d_st = sqrt(d_s * d_t); + + const double INV_SQRT2 = 0.7071067811865475; + /* Fast path: no Q on cone slots (w_s = w_t = 1 and all w_v_i = 1) and uniform d_v = d_st. + This is the COMMON case for QCQP transform aux vars. Reduces to LP-style RSOC closed form. */ + if (q_s == 0.0 && q_t == 0.0) + { + bool no_cone_Q = true; + bool d_uniform = true; + for (int m = 0; m < k; ++m) + { + if (Q_diag[start + m] != 0.0) + { + no_cone_Q = false; + break; + } + if (variable_rescaling[start + m] != d_st) + { + d_uniform = false; + break; + } + } + if (no_cone_Q && d_uniform) + { + double w_val = (r_s - r_t) * INV_SQRT2; + double z_val = (r_s + r_t) * INV_SQRT2; + double sumsq = w_val * w_val; + for (int m = 0; m < k; ++m) + { + double vm = pdhg_primal[start + m]; + sumsq += vm * vm; + } + double rnorm = sqrt(sumsq); + if (rnorm <= z_val) + { + for (int m = 0; m < len; ++m) + { + int idx = start + m; + reflected_primal[idx] = 2.0 * pdhg_primal[idx] - current_primal[idx]; + } + return; + } + if (rnorm <= -z_val) + { + for (int m = 0; m < k; ++m) + { + pdhg_primal[start + m] = 0.0; + int idx = start + m; + reflected_primal[idx] = -current_primal[idx]; + } + pdhg_primal[start + k] = 0.0; + pdhg_primal[start + k + 1] = 0.0; + reflected_primal[start + k] = -current_primal[start + k]; + reflected_primal[start + k + 1] = -current_primal[start + k + 1]; + return; + } + double scale = (z_val + rnorm) / (2.0 * rnorm); + double w_new = scale * w_val; + double z_new = scale * rnorm; + for (int m = 0; m < k; ++m) + { + double v_new = scale * pdhg_primal[start + m]; + pdhg_primal[start + m] = v_new; + int idx = start + m; + reflected_primal[idx] = 2.0 * v_new - current_primal[idx]; + } + double s_new = (z_new + w_new) * INV_SQRT2; + double t_new = (z_new - w_new) * INV_SQRT2; + pdhg_primal[start + k] = s_new; + pdhg_primal[start + k + 1] = t_new; + reflected_primal[start + k] = 2.0 * s_new - current_primal[start + k]; + reflected_primal[start + k + 1] = 2.0 * t_new - current_primal[start + k + 1]; + return; + } + } + + { + double lhs = 0.0; + for (int m = 0; m < k; ++m) + { + double d_m = variable_rescaling[start + m]; + double Ds = d_st / d_m; + double rv = pdhg_primal[start + m]; + double term = Ds * rv; + lhs += term * term; + } + if (r_s >= 0.0 && r_t >= 0.0 && lhs <= 2.0 * r_s * r_t) + { + for (int m = 0; m < len; ++m) + { + int idx = start + m; + double pv = pdhg_primal[idx]; + reflected_primal[idx] = 2.0 * pv - current_primal[idx]; + } + return; + } + } + + if (r_s <= 0.0 && r_t <= 0.0) + { + double rhs = 2.0 * sigma * sigma * r_s * r_t; + double lhs = 0.0; + for (int m = 0; m < k; ++m) + { + double d_m = variable_rescaling[start + m]; + double q_m = Q_diag[start + m]; + double w_m = 1.0 + tau * q_m; + if (!(w_m > W_FLOOR)) + w_m = W_FLOOR; + double rv = pdhg_primal[start + m]; + double term = d_m * w_m * rv / d_st; + lhs += term * term; + } + if (lhs <= rhs) + { + for (int m = 0; m < k; ++m) + pdhg_primal[start + m] = 0.0; + pdhg_primal[start + k] = 0.0; + pdhg_primal[start + k + 1] = 0.0; + for (int m = 0; m < len; ++m) + { + int idx = start + m; + reflected_primal[idx] = -current_primal[idx]; + } + return; + } + } + + double lo, hi; + int bracket_kind; /* 0: f increasing on bracket; 1: f decreasing. */ + bool need_doubling = false; + double sum_alpha = r_s + alpha * r_t; + + if (r_s > 0.0 && r_t > 0.0) + { + lo = 0.0; + hi = 1.0 - 1e-14; + bracket_kind = 1; + } + else if (r_s < 0.0 && r_t < 0.0) + { + lo = 1.0 + 1e-14; + hi = 2.0; + bracket_kind = 0; + need_doubling = true; + } + else if (r_s <= 0.0 && r_t >= 0.0) + { + if (sum_alpha <= 0.0) + { + lo = 1.0 + 1e-14; + if (r_t == 0.0) + { + hi = 2.0; + need_doubling = true; + } + else + { + hi = -r_s / (alpha * r_t); + if (!(hi > lo)) + hi = lo + 1.0; + } + bracket_kind = 0; + } + else + { + lo = (r_t > 0.0) ? (-r_s / (alpha * r_t)) : 0.0; + if (!(lo >= 0.0)) + lo = 0.0; + hi = 1.0 - 1e-14; + if (!(lo < hi)) + lo = hi - 1e-7; + bracket_kind = 1; + } + } + else + { + if (sum_alpha <= 0.0) + { + lo = 1.0 + 1e-14; + if (r_s == 0.0) + { + hi = 2.0; + need_doubling = true; + } + else + { + hi = -alpha * r_t / r_s; + if (!(hi > lo)) + hi = lo + 1.0; + } + bracket_kind = 0; + } + else + { + lo = (r_s > 0.0) ? (-alpha * r_t / r_s) : 0.0; + if (!(lo >= 0.0)) + lo = 0.0; + hi = 1.0 - 1e-14; + if (!(lo < hi)) + lo = hi - 1e-7; + bracket_kind = 1; + } + } + +#define ORACLE_EVAL(ZETA, F_OUT) \ + do \ + { \ + double _zeta = (ZETA); \ + double _denom = 1.0 - _zeta * _zeta; \ + double _s = (r_s + _zeta * alpha * r_t) / _denom; \ + double _t = (r_t + _zeta * inv_alpha * r_s) / _denom; \ + double _sv = 0.0; \ + for (int _m = 0; _m < k; ++_m) \ + { \ + double _dm = variable_rescaling[start + _m]; \ + double _Ds = d_st / _dm; \ + double _qm = Q_diag[start + _m]; \ + double _wm = 1.0 + tau * _qm; \ + if (!(_wm > W_FLOOR)) \ + _wm = W_FLOOR; \ + double _Dh2 = _Ds * _Ds * sigma / _wm; \ + double _rv = pdhg_primal[start + _m]; \ + double _vz = _rv / (1.0 + _zeta * _Dh2); \ + double _tm = _Ds * _vz; \ + _sv += _tm * _tm; \ + } \ + (F_OUT) = _sv - 2.0 * _s * _t; \ + } while (0) + + if (need_doubling) + { + double f_hi; + for (int dbl = 0; dbl < 60; ++dbl) + { + ORACLE_EVAL(hi, f_hi); + if (f_hi >= 0.0) + break; + lo = hi; + hi *= 2.0; + } + } + + double warm_zeta = warm_start[blk]; + if (warm_zeta > lo && warm_zeta < hi) + { + double f_w; + ORACLE_EVAL(warm_zeta, f_w); + if (fabs(f_w) < 1e-12) + { + double zeta = warm_zeta; + double denom = 1.0 - zeta * zeta; + double s_new = (r_s + zeta * alpha * r_t) / denom; + double t_new = (r_t + zeta * inv_alpha * r_s) / denom; + for (int m = 0; m < k; ++m) + { + double d_m = variable_rescaling[start + m]; + double Ds = d_st / d_m; + double q_m = Q_diag[start + m]; + double w_m = 1.0 + tau * q_m; + if (!(w_m > W_FLOOR)) + w_m = W_FLOOR; + double Dh2 = Ds * Ds * sigma / w_m; + double rv = pdhg_primal[start + m]; + pdhg_primal[start + m] = rv / (1.0 + zeta * Dh2); + } + pdhg_primal[start + k] = s_new; + pdhg_primal[start + k + 1] = t_new; + for (int m = 0; m < len; ++m) + { + int idx = start + m; + double pv = pdhg_primal[idx]; + reflected_primal[idx] = 2.0 * pv - current_primal[idx]; + } + return; + } + if (bracket_kind == 0) + { + if (f_w < 0.0) + lo = warm_zeta; + else + hi = warm_zeta; + } + else + { + if (f_w > 0.0) + lo = warm_zeta; + else + hi = warm_zeta; + } + } + + for (int it = 0; it < 80; ++it) + { + double mid = 0.5 * (lo + hi); + double f_m; + ORACLE_EVAL(mid, f_m); + if (bracket_kind == 0) + { + if (f_m < 0.0) + lo = mid; + else + hi = mid; + } + else + { + if (f_m > 0.0) + lo = mid; + else + hi = mid; + } + if ((hi - lo) / (1.0 + fabs(hi) + fabs(lo)) < 1e-13) + break; + } + double zeta = 0.5 * (lo + hi); + warm_start[blk] = zeta; + + double denom = 1.0 - zeta * zeta; + double s_new = (r_s + zeta * alpha * r_t) / denom; + double t_new = (r_t + zeta * inv_alpha * r_s) / denom; + for (int m = 0; m < k; ++m) + { + double d_m = variable_rescaling[start + m]; + double Ds = d_st / d_m; + double q_m = Q_diag[start + m]; + double w_m = 1.0 + tau * q_m; + if (!(w_m > W_FLOOR)) + w_m = W_FLOOR; + double Dh2 = Ds * Ds * sigma / w_m; + double rv = pdhg_primal[start + m]; + pdhg_primal[start + m] = rv / (1.0 + zeta * Dh2); + } + pdhg_primal[start + k] = s_new; + pdhg_primal[start + k + 1] = t_new; + + for (int m = 0; m < len; ++m) + { + int idx = start + m; + double pv = pdhg_primal[idx]; + reflected_primal[idx] = 2.0 * pv - current_primal[idx]; + } +#undef ORACLE_EVAL +} + +enum rotated_soc_block_mode +{ + RSOC_BLOCK_IDENTITY = 0, + RSOC_BLOCK_ZERO_FREE = 1, + RSOC_BLOCK_FIXED_ENDPOINTS_ROOT = 2, + RSOC_BLOCK_ONE_ENDPOINT_ZERO = 3, + RSOC_BLOCK_ONE_ENDPOINT_SCALAR = 4, + RSOC_BLOCK_ONE_ENDPOINT_ROOT = 5, + RSOC_BLOCK_APEX = 6, + RSOC_BLOCK_BALANCED = 7, + RSOC_BLOCK_FREE_ROOT = 8, + RSOC_BLOCK_AXIS = 9 +}; + +__global__ void project_rotated_soc_block_kernel(double *__restrict__ point, + const double *__restrict__ rescaling, + const double *__restrict__ q_diag, + double tau, + double *__restrict__ warm_start, + const int *__restrict__ start_idx, + const int *__restrict__ v_dim, + const char *__restrict__ is_fixed, + int num_cones) +{ + int cone = blockIdx.x; + if (cone >= num_cones) + return; + + __shared__ double scratch[96]; + __shared__ double fixed_norm2; + __shared__ double radius2; + __shared__ double lambda; + __shared__ double lo; + __shared__ double hi; + __shared__ double s_input; + __shared__ double t_input; + __shared__ double omega_s; + __shared__ double omega_t; + __shared__ double projected_s; + __shared__ double projected_t; + __shared__ double free_objective; + __shared__ int mode; + __shared__ int lower_branch; + __shared__ int done; + + int start = start_idx[cone]; + int k = v_dim[cone]; + int s_index = start + k; + int t_index = s_index + 1; + bool fixed_s = is_fixed && is_fixed[s_index]; + bool fixed_t = is_fixed && is_fixed[t_index]; + + double local_fixed_norm2 = 0.0; + double local_free_norm2 = 0.0; + double local_polar_norm2 = 0.0; + double local_free_objective = 0.0; + double local_max_omega = 0.0; + for (int slot = threadIdx.x; slot < k; slot += blockDim.x) + { + int index = start + slot; + double value = point[index] / rescaling[index]; + if (is_fixed && is_fixed[index]) + local_fixed_norm2 += value * value; + else + { + double omega = cone_section_weight(rescaling, q_diag, tau, index); + local_free_norm2 += value * value; + local_polar_norm2 += (omega * value) * (omega * value); + local_free_objective += omega * value * value; + local_max_omega = fmax(local_max_omega, omega); + } + } + cone_block_sum3(&local_fixed_norm2, &local_free_norm2, &local_polar_norm2, scratch); + double unused = 0.0; + double unused2 = 0.0; + cone_block_sum3(&local_free_objective, &unused, &unused2, scratch); + local_max_omega = cone_block_max(local_max_omega, scratch); + + if (threadIdx.x == 0) + { + fixed_norm2 = local_fixed_norm2; + free_objective = local_free_objective; + s_input = point[s_index] / rescaling[s_index]; + t_input = point[t_index] / rescaling[t_index]; + omega_s = cone_section_weight(rescaling, q_diag, tau, s_index); + omega_t = cone_section_weight(rescaling, q_diag, tau, t_index); + int free_count = 0; + for (int slot = 0; slot < k; ++slot) + free_count += !(is_fixed && is_fixed[start + slot]); + + if (fixed_s && fixed_t) + { + radius2 = fmax(0.0, 2.0 * s_input * t_input - fixed_norm2); + if (free_count == 0 || local_free_norm2 <= radius2) + mode = RSOC_BLOCK_IDENTITY; + else if (!(radius2 > 0.0)) + mode = RSOC_BLOCK_ZERO_FREE; + else + { + mode = RSOC_BLOCK_FIXED_ENDPOINTS_ROOT; + hi = sqrt(local_polar_norm2) / sqrt(radius2) * (1.0 + 64.0 * DBL_EPSILON); + } + } + else if (fixed_s || fixed_t) + { + double fixed_endpoint = fixed_s ? s_input : t_input; + double free_endpoint = fixed_s ? t_input : s_input; + if (!(fixed_endpoint > 0.0)) + mode = RSOC_BLOCK_ONE_ENDPOINT_ZERO; + else if (free_endpoint >= 0.0 && fixed_norm2 + local_free_norm2 <= 2.0 * fixed_endpoint * free_endpoint) + mode = RSOC_BLOCK_IDENTITY; + else if (free_count == 0) + mode = RSOC_BLOCK_ONE_ENDPOINT_SCALAR; + else + { + mode = RSOC_BLOCK_ONE_ENDPOINT_ROOT; + double metric = fixed_s ? omega_t : omega_s; + double violation = fixed_norm2 + local_free_norm2 - 2.0 * fixed_endpoint * free_endpoint; + hi = metric * violation / (2.0 * fixed_endpoint * fixed_endpoint); + hi *= 1.0 + 64.0 * DBL_EPSILON; + } + } + else if (s_input >= 0.0 && t_input >= 0.0 && fixed_norm2 + local_free_norm2 <= 2.0 * s_input * t_input) + { + mode = RSOC_BLOCK_IDENTITY; + } + else + { + double bs = omega_s * s_input; + double bt = omega_t * t_input; + if (fixed_norm2 == 0.0 && bs <= 0.0 && bt <= 0.0 && local_polar_norm2 <= 2.0 * bs * bt) + { + mode = RSOC_BLOCK_APEX; + } + else + { + double root_metric = sqrt(omega_s) * sqrt(omega_t); + double balance = sqrt(omega_s) * s_input + sqrt(omega_t) * t_input; + double balance_scale = 1.0 + fabs(sqrt(omega_s) * s_input) + fabs(sqrt(omega_t) * t_input); + lambda = root_metric; + if (fabs(balance) <= 64.0 * DBL_EPSILON * balance_scale) + mode = RSOC_BLOCK_BALANCED; + else + { + mode = RSOC_BLOCK_FREE_ROOT; + lower_branch = balance > 0.0; + lo = lower_branch ? 0.0 : root_metric * (1.0 + 1e-14); + if (lower_branch) + { + hi = root_metric * (1.0 - 1e-14); + } + else + { + hi = cone_section_negative_rsoc_upper( + omega_s, omega_t, s_input, t_input, fixed_norm2, local_polar_norm2, local_max_omega); + } + } + } + } + } + __syncthreads(); + + if (mode == RSOC_BLOCK_IDENTITY) + return; + if (mode == RSOC_BLOCK_ZERO_FREE || mode == RSOC_BLOCK_APEX) + { + for (int slot = threadIdx.x; slot < k; slot += blockDim.x) + if (!(is_fixed && is_fixed[start + slot])) + point[start + slot] = 0.0; + if (mode == RSOC_BLOCK_APEX && threadIdx.x == 0) + { + point[s_index] = 0.0; + point[t_index] = 0.0; + } + return; + } + if (mode == RSOC_BLOCK_ONE_ENDPOINT_ZERO) + { + for (int slot = threadIdx.x; slot < k; slot += blockDim.x) + if (!(is_fixed && is_fixed[start + slot])) + point[start + slot] = 0.0; + if (threadIdx.x == 0) + { + if (fixed_s) + point[t_index] = fmax(t_input, 0.0) * rescaling[t_index]; + else + point[s_index] = fmax(s_input, 0.0) * rescaling[s_index]; + } + return; + } + if (mode == RSOC_BLOCK_ONE_ENDPOINT_SCALAR) + { + if (threadIdx.x == 0) + { + double fixed_endpoint = fixed_s ? s_input : t_input; + double free_endpoint = fixed_s ? t_input : s_input; + double projected = fmax(free_endpoint, fixed_norm2 / (2.0 * fixed_endpoint)); + if (fixed_s) + point[t_index] = projected * rescaling[t_index]; + else + point[s_index] = projected * rescaling[s_index]; + } + return; + } + + if (mode == RSOC_BLOCK_FIXED_ENDPOINTS_ROOT || mode == RSOC_BLOCK_ONE_ENDPOINT_ROOT) + { + if (threadIdx.x == 0) + { + lo = 0.0; + double metric = mode == RSOC_BLOCK_ONE_ENDPOINT_ROOT ? (fixed_s ? omega_t : omega_s) : 1.0; + done = hi > 0.0 && isfinite(hi); + if (!done) + hi = warm_start && warm_start[cone] > 0.0 && isfinite(warm_start[cone]) ? warm_start[cone] : metric; + } + __syncthreads(); + for (int expansion = 0; expansion < 80; ++expansion) + { + if (done) + break; + double norm2 = 0.0; + double dummy = 0.0; + double dummy2 = 0.0; + for (int slot = threadIdx.x; slot < k; slot += blockDim.x) + { + int index = start + slot; + if (is_fixed && is_fixed[index]) + continue; + double omega = cone_section_weight(rescaling, q_diag, tau, index); + double value = (point[index] / rescaling[index]) * omega / (omega + hi); + norm2 += value * value; + } + cone_block_sum3(&norm2, &dummy, &dummy2, scratch); + if (threadIdx.x == 0) + { + if (mode == RSOC_BLOCK_FIXED_ENDPOINTS_ROOT) + done = norm2 <= radius2; + else + { + double fixed_endpoint = fixed_s ? s_input : t_input; + double free_endpoint = fixed_s ? t_input : s_input; + double omega_endpoint = fixed_s ? omega_t : omega_s; + double endpoint = free_endpoint + hi * fixed_endpoint / omega_endpoint; + done = fixed_norm2 + norm2 <= 2.0 * fixed_endpoint * endpoint; + } + if (!done) + hi *= 2.0; + } + __syncthreads(); + if (done) + break; + } + __syncthreads(); + + if (threadIdx.x == 0) + { + double warm = warm_start ? warm_start[cone] : 0.0; + lambda = warm > lo && warm < hi && isfinite(warm) ? warm : 0.5 * (lo + hi); + done = 0; + } + __syncthreads(); + for (int iteration = 0; iteration < 30; ++iteration) + { + double norm2 = 0.0; + double derivative = 0.0; + double dummy = 0.0; + for (int slot = threadIdx.x; slot < k; slot += blockDim.x) + { + int index = start + slot; + if (is_fixed && is_fixed[index]) + continue; + double omega = cone_section_weight(rescaling, q_diag, tau, index); + double value = (point[index] / rescaling[index]) * omega / (omega + lambda); + norm2 += value * value; + derivative -= 2.0 * value * value / (omega + lambda); + } + cone_block_sum3(&norm2, &derivative, &dummy, scratch); + if (threadIdx.x == 0) + { + double target; + double f; + if (mode == RSOC_BLOCK_FIXED_ENDPOINTS_ROOT) + { + target = radius2; + f = norm2 - target; + } + else + { + double fixed_endpoint = fixed_s ? s_input : t_input; + double free_endpoint = fixed_s ? t_input : s_input; + double omega_endpoint = fixed_s ? omega_t : omega_s; + double endpoint = free_endpoint + lambda * fixed_endpoint / omega_endpoint; + target = 2.0 * fixed_endpoint * endpoint; + f = fixed_norm2 + norm2 - target; + derivative -= 2.0 * fixed_endpoint * fixed_endpoint / omega_endpoint; + } + if (f > 0.0) + lo = lambda; + else + hi = lambda; + double next = lambda - f / derivative; + if (!isfinite(next) || !(next > lo && next < hi)) + next = 0.5 * (lo + hi); + done = fabs(f) <= 1e-13 * (1.0 + target) || hi - lo <= 1e-13 * (1.0 + hi + lo); + if (!done) + lambda = next; + } + __syncthreads(); + if (done) + break; + } + + if (threadIdx.x == 0 && mode == RSOC_BLOCK_ONE_ENDPOINT_ROOT) + { + double fixed_endpoint = fixed_s ? s_input : t_input; + double free_endpoint = fixed_s ? t_input : s_input; + double omega_endpoint = fixed_s ? omega_t : omega_s; + double projected = free_endpoint + lambda * fixed_endpoint / omega_endpoint; + if (fixed_s) + point[t_index] = projected * rescaling[t_index]; + else + point[s_index] = projected * rescaling[s_index]; + } + } + else if (mode == RSOC_BLOCK_BALANCED) + { + double norm2 = 0.0; + double dummy = 0.0; + double dummy2 = 0.0; + for (int slot = threadIdx.x; slot < k; slot += blockDim.x) + { + int index = start + slot; + if (is_fixed && is_fixed[index]) + continue; + double omega = cone_section_weight(rescaling, q_diag, tau, index); + double value = (point[index] / rescaling[index]) * omega / (omega + lambda); + norm2 += value * value; + } + cone_block_sum3(&norm2, &dummy, &dummy2, scratch); + if (threadIdx.x == 0) + { + double root_metric = sqrt(omega_s) * sqrt(omega_t); + double product = 0.5 * root_metric * (fixed_norm2 + norm2); + double delta = sqrt(omega_s) * s_input; + double scaled_t = 0.5 * (-delta + sqrt(fmax(0.0, delta * delta + 4.0 * product))); + double scaled_s = scaled_t + delta; + projected_s = scaled_s / sqrt(omega_s); + projected_t = scaled_t / sqrt(omega_t); + } + __syncthreads(); + } + else + { + if (!lower_branch) + { + if (threadIdx.x == 0) + { + done = hi > lo && isfinite(hi); + if (!done) + hi = 2.0 * sqrt(omega_s) * sqrt(omega_t); + } + __syncthreads(); + for (int expansion = 0; expansion < 80; ++expansion) + { + if (done) + break; + double norm2 = 0.0; + double dummy = 0.0; + double dummy2 = 0.0; + for (int slot = threadIdx.x; slot < k; slot += blockDim.x) + { + int index = start + slot; + if (is_fixed && is_fixed[index]) + continue; + double omega = cone_section_weight(rescaling, q_diag, tau, index); + double value = (point[index] / rescaling[index]) * omega / (omega + hi); + norm2 += value * value; + } + cone_block_sum3(&norm2, &dummy, &dummy2, scratch); + if (threadIdx.x == 0) + { + double determinant = omega_s * omega_t - hi * hi; + double s = omega_t * (omega_s * s_input + hi * t_input) / determinant; + double t = omega_s * (omega_t * t_input + hi * s_input) / determinant; + double f = (s >= 0.0 && t >= 0.0) ? fixed_norm2 + norm2 - 2.0 * s * t : INFINITY; + done = f >= 0.0; + if (!done) + hi *= 2.0; + } + __syncthreads(); + if (done) + break; + } + } + __syncthreads(); + + if (threadIdx.x == 0) + { + double warm = warm_start ? warm_start[cone] : 0.0; + lambda = warm > lo && warm < hi && isfinite(warm) ? warm : 0.5 * (lo + hi); + done = 0; + } + __syncthreads(); + for (int iteration = 0; iteration < 40; ++iteration) + { + double norm2 = 0.0; + double derivative = 0.0; + double dummy = 0.0; + for (int slot = threadIdx.x; slot < k; slot += blockDim.x) + { + int index = start + slot; + if (is_fixed && is_fixed[index]) + continue; + double omega = cone_section_weight(rescaling, q_diag, tau, index); + double value = (point[index] / rescaling[index]) * omega / (omega + lambda); + norm2 += value * value; + derivative -= 2.0 * value * value / (omega + lambda); + } + cone_block_sum3(&norm2, &derivative, &dummy, scratch); + if (threadIdx.x == 0) + { + double determinant = omega_s * omega_t - lambda * lambda; + double s = omega_t * (omega_s * s_input + lambda * t_input) / determinant; + double t = omega_s * (omega_t * t_input + lambda * s_input) / determinant; + double f = INFINITY; + if (s >= 0.0 && t >= 0.0) + { + f = fixed_norm2 + norm2 - 2.0 * s * t; + double ds = (omega_t * t + lambda * s) / determinant; + double dt = (lambda * t + omega_s * s) / determinant; + derivative -= 2.0 * (ds * t + s * dt); + } + if ((lower_branch && f > 0.0) || (!lower_branch && f < 0.0)) + lo = lambda; + else + hi = lambda; + double next = lambda - f / derivative; + if (!isfinite(next) || !(next > lo && next < hi)) + next = 0.5 * (lo + hi); + done = isfinite(f) && + (fabs(f) <= 1e-13 * (1.0 + fixed_norm2 + norm2 + 2.0 * s * t) || + hi - lo <= 1e-13 * (1.0 + hi + lo)); + if (!done) + lambda = next; + } + __syncthreads(); + if (done) + break; + } + if (threadIdx.x == 0) + { + double determinant = omega_s * omega_t - lambda * lambda; + projected_s = omega_t * (omega_s * s_input + lambda * t_input) / determinant; + projected_t = omega_s * (omega_t * t_input + lambda * s_input) / determinant; + } + __syncthreads(); + } + + if (mode == RSOC_BLOCK_BALANCED || mode == RSOC_BLOCK_FREE_ROOT) + { + double smooth_vector_objective = 0.0; + double dummy = 0.0; + double dummy2 = 0.0; + for (int slot = threadIdx.x; slot < k; slot += blockDim.x) + { + int index = start + slot; + if (is_fixed && is_fixed[index]) + continue; + double omega = cone_section_weight(rescaling, q_diag, tau, index); + double input = point[index] / rescaling[index]; + double value = input * omega / (omega + lambda); + double delta = value - input; + smooth_vector_objective += omega * delta * delta; + } + cone_block_sum3(&smooth_vector_objective, &dummy, &dummy2, scratch); + if (threadIdx.x == 0 && fixed_norm2 == 0.0) + { + double smooth_objective = smooth_vector_objective + + omega_s * (projected_s - s_input) * (projected_s - s_input) + + omega_t * (projected_t - t_input) * (projected_t - t_input); + double s_axis = fmax(s_input, 0.0); + double s_axis_objective = + free_objective + omega_s * (s_axis - s_input) * (s_axis - s_input) + omega_t * t_input * t_input; + double t_axis = fmax(t_input, 0.0); + double t_axis_objective = + free_objective + omega_s * s_input * s_input + omega_t * (t_axis - t_input) * (t_axis - t_input); + if (s_axis_objective < smooth_objective && s_axis_objective <= t_axis_objective) + { + projected_s = s_axis; + projected_t = 0.0; + mode = RSOC_BLOCK_AXIS; + } + else if (t_axis_objective < smooth_objective) + { + projected_s = 0.0; + projected_t = t_axis; + mode = RSOC_BLOCK_AXIS; + } + } + __syncthreads(); + } + + if (mode == RSOC_BLOCK_AXIS) + { + for (int slot = threadIdx.x; slot < k; slot += blockDim.x) + if (!(is_fixed && is_fixed[start + slot])) + point[start + slot] = 0.0; + } + else + { + for (int slot = threadIdx.x; slot < k; slot += blockDim.x) + { + int index = start + slot; + if (!(is_fixed && is_fixed[index])) + { + double omega = cone_section_weight(rescaling, q_diag, tau, index); + point[index] *= omega / (omega + lambda); + } + } + if (warm_start && threadIdx.x == 0) + warm_start[cone] = lambda; + } + if (threadIdx.x == 0 && (mode == RSOC_BLOCK_BALANCED || mode == RSOC_BLOCK_FREE_ROOT || mode == RSOC_BLOCK_AXIS)) + { + point[s_index] = projected_s * rescaling[s_index]; + point[t_index] = projected_t * rescaling[t_index]; + } +} + +enum rotated_soc_grid_weighted_mode +{ + RSOC_GRID_IDENTITY = 0, + RSOC_GRID_ZERO_FREE = 1, + RSOC_GRID_ONE_ENDPOINT_ZERO = 2, + RSOC_GRID_ONE_ENDPOINT_SCALAR = 3, + RSOC_GRID_APEX = 4, + RSOC_GRID_FIXED_EXPAND = 5, + RSOC_GRID_FIXED_ROOT = 6, + RSOC_GRID_FIXED_APPLY = 7, + RSOC_GRID_ONE_EXPAND = 8, + RSOC_GRID_ONE_ROOT = 9, + RSOC_GRID_ONE_APPLY = 10, + RSOC_GRID_BALANCED_EVAL = 11, + RSOC_GRID_FREE_EXPAND = 12, + RSOC_GRID_FREE_ROOT = 13, + RSOC_GRID_FREE_APPLY = 14, + RSOC_GRID_BALANCED_APPLY = 15, + RSOC_GRID_AXIS = 16 +}; + +__device__ static inline void rotated_soc_grid_free_endpoints(const double *point, + const double *rescaling, + const double *q_diag, + double tau, + int s_index, + int t_index, + double lambda, + double *projected_s, + double *projected_t) +{ + double s = point[s_index] / rescaling[s_index]; + double t = point[t_index] / rescaling[t_index]; + double omega_s = cone_section_weight(rescaling, q_diag, tau, s_index); + double omega_t = cone_section_weight(rescaling, q_diag, tau, t_index); + double determinant = omega_s * omega_t - lambda * lambda; + *projected_s = omega_t * (omega_s * s + lambda * t) / determinant; + *projected_t = omega_s * (omega_t * t + lambda * s) / determinant; +} + +__global__ void initialize_rotated_soc_grid_weighted_kernel(const double *__restrict__ point, + const double *__restrict__ rescaling, + const double *__restrict__ q_diag, + double tau, + double *__restrict__ workspace, + const int *__restrict__ start_idx, + const int *__restrict__ v_dim, + const char *__restrict__ is_fixed, + int num_cones, + int blocks_per_cone) +{ + int cone = blockIdx.x / blocks_per_cone; + if (cone >= num_cones) + return; + int part = blockIdx.x - cone * blocks_per_cone; + int start = start_idx[cone]; + int k = v_dim[cone]; + double fixed_norm2 = 0.0; + double free_norm2 = 0.0; + double polar_norm2 = 0.0; + double free_count = 0.0; + double max_omega = 0.0; + for (int slot = part * blockDim.x + threadIdx.x; slot < k; slot += blocks_per_cone * blockDim.x) + { + int index = start + slot; + double value = point[index] / rescaling[index]; + if (is_fixed && is_fixed[index]) + fixed_norm2 += value * value; + else + { + double omega = cone_section_weight(rescaling, q_diag, tau, index); + free_norm2 += value * value; + polar_norm2 += (omega * value) * (omega * value); + free_count += 1.0; + max_omega = fmax(max_omega, omega); + } + } + __shared__ double scratch[96]; + cone_block_sum3(&fixed_norm2, &free_norm2, &polar_norm2, scratch); + double unused = 0.0; + double unused2 = 0.0; + cone_block_sum3(&free_count, &unused, &unused2, scratch); + max_omega = cone_block_max(max_omega, scratch); + if (threadIdx.x == 0) + { + atomicAdd(workspace + num_cones + cone, fixed_norm2); + atomicAdd(workspace + 2 * num_cones + cone, free_norm2); + atomicAdd(workspace + 3 * num_cones + cone, polar_norm2); + atomicAdd(workspace + 4 * num_cones + cone, free_count); + cone_atomic_max_positive(workspace + 5 * num_cones + cone, max_omega); + } +} + +__global__ void finalize_rotated_soc_grid_weighted_initialization_kernel(const double *__restrict__ point, + const double *__restrict__ rescaling, + const double *__restrict__ q_diag, + double tau, + double *__restrict__ workspace, + const int *__restrict__ start_idx, + const int *__restrict__ v_dim, + const char *__restrict__ is_fixed, + int num_cones) +{ + int cone = blockIdx.x * blockDim.x + threadIdx.x; + if (cone >= num_cones) + return; + int start = start_idx[cone]; + int k = v_dim[cone]; + int s_index = start + k; + int t_index = s_index + 1; + bool fixed_s = is_fixed && is_fixed[s_index]; + bool fixed_t = is_fixed && is_fixed[t_index]; + double warm = workspace[cone]; + double fixed_norm2 = workspace[num_cones + cone]; + double free_norm2 = workspace[2 * num_cones + cone]; + double polar_norm2 = workspace[3 * num_cones + cone]; + int free_count = (int)workspace[4 * num_cones + cone]; + double max_omega = workspace[5 * num_cones + cone]; + double s = point[s_index] / rescaling[s_index]; + double t = point[t_index] / rescaling[t_index]; + double omega_s_value = cone_section_weight(rescaling, q_diag, tau, s_index); + double omega_t_value = cone_section_weight(rescaling, q_diag, tau, t_index); + int selected_mode; + double constant = fixed_norm2; + double lower = 0.0; + double upper = 0.0; + double trial = warm; + + if (fixed_s && fixed_t) + { + constant = fmax(0.0, 2.0 * s * t - fixed_norm2); + if (free_count == 0 || free_norm2 <= constant) + selected_mode = RSOC_GRID_IDENTITY; + else if (!(constant > 0.0)) + selected_mode = RSOC_GRID_ZERO_FREE; + else + { + upper = sqrt(polar_norm2) / sqrt(constant) * (1.0 + 64.0 * DBL_EPSILON); + if (upper > 0.0 && isfinite(upper)) + { + selected_mode = RSOC_GRID_FIXED_ROOT; + trial = warm > 0.0 && warm < upper && isfinite(warm) ? warm : 0.5 * upper; + } + else + { + selected_mode = RSOC_GRID_FIXED_EXPAND; + trial = warm > 0.0 && isfinite(warm) ? warm : 1.0; + upper = trial; + } + } + } + else if (fixed_s || fixed_t) + { + double fixed_endpoint = fixed_s ? s : t; + double free_endpoint = fixed_s ? t : s; + if (!(fixed_endpoint > 0.0)) + selected_mode = RSOC_GRID_ONE_ENDPOINT_ZERO; + else if (free_endpoint >= 0.0 && fixed_norm2 + free_norm2 <= 2.0 * fixed_endpoint * free_endpoint) + selected_mode = RSOC_GRID_IDENTITY; + else if (free_count == 0) + selected_mode = RSOC_GRID_ONE_ENDPOINT_SCALAR; + else + { + double metric = fixed_s ? omega_t_value : omega_s_value; + double violation = fixed_norm2 + free_norm2 - 2.0 * fixed_endpoint * free_endpoint; + upper = metric * violation / (2.0 * fixed_endpoint * fixed_endpoint); + upper *= 1.0 + 64.0 * DBL_EPSILON; + if (upper > 0.0 && isfinite(upper)) + { + selected_mode = RSOC_GRID_ONE_ROOT; + trial = warm > 0.0 && warm < upper && isfinite(warm) ? warm : 0.5 * upper; + } + else + { + selected_mode = RSOC_GRID_ONE_EXPAND; + trial = warm > 0.0 && isfinite(warm) ? warm : metric; + upper = trial; + } + } + } + else if (s >= 0.0 && t >= 0.0 && fixed_norm2 + free_norm2 <= 2.0 * s * t) + { + selected_mode = RSOC_GRID_IDENTITY; + } + else + { + double bs = omega_s_value * s; + double bt = omega_t_value * t; + if (fixed_norm2 == 0.0 && bs <= 0.0 && bt <= 0.0 && polar_norm2 <= 2.0 * bs * bt) + { + selected_mode = RSOC_GRID_APEX; + } + else + { + double sqrt_omega_s = sqrt(omega_s_value); + double sqrt_omega_t = sqrt(omega_t_value); + double root_metric = sqrt_omega_s * sqrt_omega_t; + double scaled_s = sqrt_omega_s * s; + double scaled_t = sqrt_omega_t * t; + double balance = scaled_s + scaled_t; + double balance_scale = 1.0 + fabs(scaled_s) + fabs(scaled_t); + if (fabs(balance) <= 64.0 * DBL_EPSILON * balance_scale) + { + selected_mode = RSOC_GRID_BALANCED_EVAL; + trial = root_metric; + } + else if (balance > 0.0) + { + selected_mode = RSOC_GRID_FREE_ROOT; + lower = 0.0; + upper = root_metric * (1.0 - 1e-14); + trial = warm > lower && warm < upper && isfinite(warm) ? warm : 0.5 * (lower + upper); + } + else + { + lower = root_metric * (1.0 + 1e-14); + upper = cone_section_negative_rsoc_upper( + omega_s_value, omega_t_value, s, t, fixed_norm2, polar_norm2, max_omega); + if (upper > lower && isfinite(upper)) + { + selected_mode = RSOC_GRID_FREE_ROOT; + trial = warm > lower && warm < upper && isfinite(warm) ? warm : 0.5 * (lower + upper); + } + else + { + selected_mode = RSOC_GRID_FREE_EXPAND; + trial = warm > lower && isfinite(warm) ? warm : 2.0 * root_metric; + upper = trial; + } + } + } + } + + workspace[cone] = trial; + workspace[4 * num_cones + cone] = (double)selected_mode; + workspace[5 * num_cones + cone] = constant; + workspace[6 * num_cones + cone] = lower; + workspace[7 * num_cones + cone] = upper; +} + +__global__ void reduce_rotated_soc_grid_weighted_root_kernel(const double *__restrict__ point, + const double *__restrict__ rescaling, + const double *__restrict__ q_diag, + double tau, + double *__restrict__ workspace, + const int *__restrict__ start_idx, + const int *__restrict__ v_dim, + const char *__restrict__ is_fixed, + int num_cones, + int blocks_per_cone) +{ + int cone = blockIdx.x / blocks_per_cone; + if (cone >= num_cones) + return; + int selected_mode = (int)workspace[4 * num_cones + cone]; + bool active = selected_mode == RSOC_GRID_FIXED_EXPAND || selected_mode == RSOC_GRID_FIXED_ROOT || + selected_mode == RSOC_GRID_ONE_EXPAND || selected_mode == RSOC_GRID_ONE_ROOT || + selected_mode == RSOC_GRID_BALANCED_EVAL || selected_mode == RSOC_GRID_FREE_EXPAND || + selected_mode == RSOC_GRID_FREE_ROOT; + if (!active) + return; + int part = blockIdx.x - cone * blocks_per_cone; + int start = start_idx[cone]; + int k = v_dim[cone]; + double lambda_value = workspace[cone]; + double norm2 = 0.0; + double derivative = 0.0; + for (int slot = part * blockDim.x + threadIdx.x; slot < k; slot += blocks_per_cone * blockDim.x) + { + int index = start + slot; + if (is_fixed && is_fixed[index]) + continue; + double omega = cone_section_weight(rescaling, q_diag, tau, index); + double value = (point[index] / rescaling[index]) * omega / (omega + lambda_value); + norm2 += value * value; + derivative -= 2.0 * value * value / (omega + lambda_value); + } + __shared__ double scratch[96]; + double unused = 0.0; + cone_block_sum3(&norm2, &derivative, &unused, scratch); + if (threadIdx.x == 0) + { + atomicAdd(workspace + num_cones + cone, norm2); + atomicAdd(workspace + 2 * num_cones + cone, derivative); + } +} + +__global__ void finalize_rotated_soc_grid_weighted_root_kernel(const double *__restrict__ point, + const double *__restrict__ rescaling, + const double *__restrict__ q_diag, + double tau, + double *__restrict__ workspace, + const int *__restrict__ start_idx, + const int *__restrict__ v_dim, + const char *__restrict__ is_fixed, + int num_cones) +{ + int cone = blockIdx.x * blockDim.x + threadIdx.x; + if (cone >= num_cones) + return; + int selected_mode = (int)workspace[4 * num_cones + cone]; + bool active = selected_mode == RSOC_GRID_FIXED_EXPAND || selected_mode == RSOC_GRID_FIXED_ROOT || + selected_mode == RSOC_GRID_ONE_EXPAND || selected_mode == RSOC_GRID_ONE_ROOT || + selected_mode == RSOC_GRID_BALANCED_EVAL || selected_mode == RSOC_GRID_FREE_EXPAND || + selected_mode == RSOC_GRID_FREE_ROOT; + if (!active) + return; + int start = start_idx[cone]; + int k = v_dim[cone]; + int s_index = start + k; + int t_index = s_index + 1; + bool fixed_s = is_fixed && is_fixed[s_index]; + double s_input_value = point[s_index] / rescaling[s_index]; + double t_input_value = point[t_index] / rescaling[t_index]; + double omega_s_value = cone_section_weight(rescaling, q_diag, tau, s_index); + double omega_t_value = cone_section_weight(rescaling, q_diag, tau, t_index); + double lambda_value = workspace[cone]; + double sum = workspace[num_cones + cone]; + double derivative = workspace[2 * num_cones + cone]; + double constant = workspace[5 * num_cones + cone]; + double lower = workspace[6 * num_cones + cone]; + double upper = workspace[7 * num_cones + cone]; + double f = 0.0; + + if (selected_mode == RSOC_GRID_FIXED_EXPAND || selected_mode == RSOC_GRID_FIXED_ROOT) + { + f = sum - constant; + if (selected_mode == RSOC_GRID_FIXED_EXPAND) + { + if (f > 0.0) + { + lower = lambda_value; + lambda_value *= 2.0; + } + else + { + upper = lambda_value; + selected_mode = RSOC_GRID_FIXED_ROOT; + lambda_value = 0.5 * (lower + upper); + } + } + else + { + if (f > 0.0) + lower = lambda_value; + else + upper = lambda_value; + bool converged = fabs(f) <= 1e-13 * (1.0 + constant) || upper - lower <= 1e-13 * (1.0 + upper + lower); + if (converged) + selected_mode = RSOC_GRID_FIXED_APPLY; + else + { + double next = lambda_value - f / derivative; + lambda_value = isfinite(next) && next > lower && next < upper ? next : 0.5 * (lower + upper); + } + } + } + else if (selected_mode == RSOC_GRID_ONE_EXPAND || selected_mode == RSOC_GRID_ONE_ROOT) + { + double fixed_endpoint = fixed_s ? s_input_value : t_input_value; + double free_endpoint = fixed_s ? t_input_value : s_input_value; + double omega_endpoint = fixed_s ? omega_t_value : omega_s_value; + double projected_endpoint = free_endpoint + lambda_value * fixed_endpoint / omega_endpoint; + f = constant + sum - 2.0 * fixed_endpoint * projected_endpoint; + derivative -= 2.0 * fixed_endpoint * fixed_endpoint / omega_endpoint; + if (selected_mode == RSOC_GRID_ONE_EXPAND) + { + if (f > 0.0) + { + lower = lambda_value; + lambda_value *= 2.0; + } + else + { + upper = lambda_value; + selected_mode = RSOC_GRID_ONE_ROOT; + lambda_value = 0.5 * (lower + upper); + } + } + else + { + if (f > 0.0) + lower = lambda_value; + else + upper = lambda_value; + bool converged = + fabs(f) <= 1e-13 * (1.0 + constant + sum) || upper - lower <= 1e-13 * (1.0 + upper + lower); + if (converged) + selected_mode = RSOC_GRID_ONE_APPLY; + else + { + double next = lambda_value - f / derivative; + lambda_value = isfinite(next) && next > lower && next < upper ? next : 0.5 * (lower + upper); + } + } + } + else if (selected_mode == RSOC_GRID_BALANCED_EVAL) + { + double root_metric = sqrt(omega_s_value) * sqrt(omega_t_value); + double product = 0.5 * root_metric * (constant + sum); + double delta = sqrt(omega_s_value) * s_input_value; + double scaled_t = 0.5 * (-delta + sqrt(fmax(0.0, delta * delta + 4.0 * product))); + workspace[6 * num_cones + cone] = (scaled_t + delta) / sqrt(omega_s_value); + workspace[7 * num_cones + cone] = scaled_t / sqrt(omega_t_value); + selected_mode = RSOC_GRID_BALANCED_APPLY; + } + else + { + double determinant = omega_s_value * omega_t_value - lambda_value * lambda_value; + double projected_s_value; + double projected_t_value; + rotated_soc_grid_free_endpoints( + point, rescaling, q_diag, tau, s_index, t_index, lambda_value, &projected_s_value, &projected_t_value); + f = projected_s_value >= 0.0 && projected_t_value >= 0.0 + ? constant + sum - 2.0 * projected_s_value * projected_t_value + : INFINITY; + if (isfinite(f)) + { + double ds = (omega_t_value * projected_t_value + lambda_value * projected_s_value) / determinant; + double dt = (lambda_value * projected_t_value + omega_s_value * projected_s_value) / determinant; + derivative -= 2.0 * (ds * projected_t_value + projected_s_value * dt); + } + if (selected_mode == RSOC_GRID_FREE_EXPAND) + { + if (f < 0.0) + { + lower = lambda_value; + lambda_value *= 2.0; + } + else + { + upper = lambda_value; + selected_mode = RSOC_GRID_FREE_ROOT; + lambda_value = 0.5 * (lower + upper); + } + } + else + { + bool lower_branch_value = sqrt(omega_s_value) * s_input_value + sqrt(omega_t_value) * t_input_value > 0.0; + if ((lower_branch_value && f > 0.0) || (!lower_branch_value && f < 0.0)) + lower = lambda_value; + else + upper = lambda_value; + bool converged = isfinite(f) && + (fabs(f) <= 1e-13 * (1.0 + constant + sum + 2.0 * projected_s_value * projected_t_value) || + upper - lower <= 1e-13 * (1.0 + upper + lower)); + if (converged) + selected_mode = RSOC_GRID_FREE_APPLY; + else + { + double next = lambda_value - f / derivative; + lambda_value = isfinite(next) && next > lower && next < upper ? next : 0.5 * (lower + upper); + } + } + } + + workspace[cone] = lambda_value; + workspace[4 * num_cones + cone] = (double)selected_mode; + if (selected_mode != RSOC_GRID_BALANCED_APPLY) + { + workspace[6 * num_cones + cone] = lower; + workspace[7 * num_cones + cone] = upper; + } +} + +__global__ void reduce_rotated_soc_grid_axis_objective_kernel(const double *__restrict__ point, + const double *__restrict__ rescaling, + const double *__restrict__ q_diag, + double tau, + double *__restrict__ workspace, + const int *__restrict__ start_idx, + const int *__restrict__ v_dim, + const char *__restrict__ is_fixed, + int num_cones, + int blocks_per_cone) +{ + int cone = blockIdx.x / blocks_per_cone; + if (cone >= num_cones || workspace[5 * num_cones + cone] != 0.0) + return; + int selected_mode = (int)workspace[4 * num_cones + cone]; + if (selected_mode != RSOC_GRID_FREE_EXPAND && selected_mode != RSOC_GRID_FREE_ROOT && + selected_mode != RSOC_GRID_FREE_APPLY && selected_mode != RSOC_GRID_BALANCED_APPLY) + return; + int part = blockIdx.x - cone * blocks_per_cone; + int start = start_idx[cone]; + int k = v_dim[cone]; + double lambda_value = workspace[cone]; + double smooth_objective = 0.0; + double axis_objective = 0.0; + for (int slot = part * blockDim.x + threadIdx.x; slot < k; slot += blocks_per_cone * blockDim.x) + { + int index = start + slot; + if (is_fixed && is_fixed[index]) + continue; + double omega = cone_section_weight(rescaling, q_diag, tau, index); + double input = point[index] / rescaling[index]; + double projected = input * omega / (omega + lambda_value); + double delta = projected - input; + smooth_objective += omega * delta * delta; + axis_objective += omega * input * input; + } + __shared__ double scratch[96]; + double unused = 0.0; + cone_block_sum3(&smooth_objective, &axis_objective, &unused, scratch); + if (threadIdx.x == 0) + { + atomicAdd(workspace + num_cones + cone, smooth_objective); + atomicAdd(workspace + 2 * num_cones + cone, axis_objective); + } +} + +__global__ void finalize_rotated_soc_grid_axis_objective_kernel(const double *__restrict__ point, + const double *__restrict__ rescaling, + const double *__restrict__ q_diag, + double tau, + double *__restrict__ workspace, + const int *__restrict__ start_idx, + const int *__restrict__ v_dim, + int num_cones) +{ + int cone = blockIdx.x * blockDim.x + threadIdx.x; + if (cone >= num_cones || workspace[5 * num_cones + cone] != 0.0) + return; + int selected_mode = (int)workspace[4 * num_cones + cone]; + if (selected_mode != RSOC_GRID_FREE_EXPAND && selected_mode != RSOC_GRID_FREE_ROOT && + selected_mode != RSOC_GRID_FREE_APPLY && selected_mode != RSOC_GRID_BALANCED_APPLY) + return; + int start = start_idx[cone]; + int k = v_dim[cone]; + int s_index = start + k; + int t_index = s_index + 1; + double s_input_value = point[s_index] / rescaling[s_index]; + double t_input_value = point[t_index] / rescaling[t_index]; + double omega_s_value = cone_section_weight(rescaling, q_diag, tau, s_index); + double omega_t_value = cone_section_weight(rescaling, q_diag, tau, t_index); + double projected_s_value; + double projected_t_value; + if (selected_mode == RSOC_GRID_BALANCED_APPLY) + { + projected_s_value = workspace[6 * num_cones + cone]; + projected_t_value = workspace[7 * num_cones + cone]; + } + else + { + rotated_soc_grid_free_endpoints( + point, rescaling, q_diag, tau, s_index, t_index, workspace[cone], &projected_s_value, &projected_t_value); + } + double smooth_objective = workspace[num_cones + cone] + + omega_s_value * (projected_s_value - s_input_value) * (projected_s_value - s_input_value) + + omega_t_value * (projected_t_value - t_input_value) * (projected_t_value - t_input_value); + double vector_axis_objective = workspace[2 * num_cones + cone]; + double s_axis = fmax(s_input_value, 0.0); + double s_axis_objective = vector_axis_objective + + omega_s_value * (s_axis - s_input_value) * (s_axis - s_input_value) + + omega_t_value * t_input_value * t_input_value; + double t_axis = fmax(t_input_value, 0.0); + double t_axis_objective = vector_axis_objective + omega_s_value * s_input_value * s_input_value + + omega_t_value * (t_axis - t_input_value) * (t_axis - t_input_value); + if (s_axis_objective < smooth_objective && s_axis_objective <= t_axis_objective) + { + workspace[6 * num_cones + cone] = s_axis; + workspace[7 * num_cones + cone] = 0.0; + workspace[4 * num_cones + cone] = (double)RSOC_GRID_AXIS; + } + else if (t_axis_objective < smooth_objective) + { + workspace[6 * num_cones + cone] = 0.0; + workspace[7 * num_cones + cone] = t_axis; + workspace[4 * num_cones + cone] = (double)RSOC_GRID_AXIS; + } +} + +__global__ void apply_rotated_soc_grid_weighted_kernel(double *__restrict__ point, + const double *__restrict__ rescaling, + const double *__restrict__ q_diag, + double tau, + const double *__restrict__ workspace, + const int *__restrict__ start_idx, + const int *__restrict__ v_dim, + const char *__restrict__ is_fixed, + int num_cones, + int blocks_per_cone) +{ + int cone = blockIdx.x / blocks_per_cone; + if (cone >= num_cones) + return; + int selected_mode = (int)workspace[4 * num_cones + cone]; + if (selected_mode == RSOC_GRID_IDENTITY) + return; + int part = blockIdx.x - cone * blocks_per_cone; + int start = start_idx[cone]; + int k = v_dim[cone]; + int s_index = start + k; + int t_index = s_index + 1; + bool fixed_s = is_fixed && is_fixed[s_index]; + bool fixed_t = is_fixed && is_fixed[t_index]; + + bool zero_vector = selected_mode == RSOC_GRID_ZERO_FREE || selected_mode == RSOC_GRID_ONE_ENDPOINT_ZERO || + selected_mode == RSOC_GRID_APEX || selected_mode == RSOC_GRID_AXIS; + if (zero_vector) + { + for (int slot = part * blockDim.x + threadIdx.x; slot < k; slot += blocks_per_cone * blockDim.x) + if (!(is_fixed && is_fixed[start + slot])) + point[start + slot] = 0.0; + } + else if (selected_mode != RSOC_GRID_ONE_ENDPOINT_SCALAR) + { + double lambda_value = workspace[cone]; + for (int slot = part * blockDim.x + threadIdx.x; slot < k; slot += blocks_per_cone * blockDim.x) + { + int index = start + slot; + if (!(is_fixed && is_fixed[index])) + { + double omega = cone_section_weight(rescaling, q_diag, tau, index); + point[index] *= omega / (omega + lambda_value); + } + } + } + + if (part == 0 && threadIdx.x == 0) + { + if (selected_mode == RSOC_GRID_ONE_ENDPOINT_ZERO) + { + if (fixed_s) + point[t_index] = fmax(point[t_index] / rescaling[t_index], 0.0) * rescaling[t_index]; + else + point[s_index] = fmax(point[s_index] / rescaling[s_index], 0.0) * rescaling[s_index]; + } + else if (selected_mode == RSOC_GRID_ONE_ENDPOINT_SCALAR) + { + double fixed_endpoint = fixed_s ? point[s_index] / rescaling[s_index] : point[t_index] / rescaling[t_index]; + int free_index = fixed_s ? t_index : s_index; + double input = point[free_index] / rescaling[free_index]; + point[free_index] = + fmax(input, workspace[5 * num_cones + cone] / (2.0 * fixed_endpoint)) * rescaling[free_index]; + } + else if (selected_mode == RSOC_GRID_APEX) + { + point[s_index] = 0.0; + point[t_index] = 0.0; + } + else if (selected_mode == RSOC_GRID_ONE_EXPAND || selected_mode == RSOC_GRID_ONE_ROOT || + selected_mode == RSOC_GRID_ONE_APPLY) + { + double lambda_value = workspace[cone]; + double fixed_endpoint = fixed_s ? point[s_index] / rescaling[s_index] : point[t_index] / rescaling[t_index]; + int free_index = fixed_s ? t_index : s_index; + double input = point[free_index] / rescaling[free_index]; + double omega = cone_section_weight(rescaling, q_diag, tau, free_index); + point[free_index] = (input + lambda_value * fixed_endpoint / omega) * rescaling[free_index]; + } + else if (selected_mode == RSOC_GRID_BALANCED_APPLY || selected_mode == RSOC_GRID_AXIS) + { + if (!fixed_s) + point[s_index] = workspace[6 * num_cones + cone] * rescaling[s_index]; + if (!fixed_t) + point[t_index] = workspace[7 * num_cones + cone] * rescaling[t_index]; + } + else if (selected_mode == RSOC_GRID_FREE_EXPAND || selected_mode == RSOC_GRID_FREE_ROOT || + selected_mode == RSOC_GRID_FREE_APPLY) + { + double projected_s; + double projected_t; + rotated_soc_grid_free_endpoints( + point, rescaling, q_diag, tau, s_index, t_index, workspace[cone], &projected_s, &projected_t); + point[s_index] = projected_s * rescaling[s_index]; + point[t_index] = projected_t * rescaling[t_index]; + } + } +} + +static void launch_rotated_thread_proj( + double *p, const double *vr, double *ws, const int *si, const int *vd, const double *pa, const char *isf, int n) +{ + (void)pa; + int t = THREADS_PER_BLOCK; + int b = (n + t - 1) / t; + project_rotated_soc_kernel<<>>(p, vr, ws, si, vd, isf, n); +} +static void launch_rotated_warp_proj( + double *p, const double *vr, double *ws, const int *si, const int *vd, const double *pa, const char *isf, int n) +{ + (void)pa; + int t = THREADS_PER_BLOCK; + int b = (n * 32 + t - 1) / t; + project_rotated_soc_warp_kernel<<>>(p, vr, ws, si, vd, isf, n); +} +static void launch_rotated_block_proj( + double *p, const double *vr, double *ws, const int *si, const int *vd, const double *pa, const char *isf, int n) +{ + (void)pa; + project_rotated_soc_block_kernel<<>>(p, vr, NULL, 0.0, ws, si, vd, isf, n); +} +static void launch_rotated_grid_weighted_impl(double *p, + const double *vr, + const double *qd, + double tau, + double *ws, + const int *si, + const int *vd, + const char *isf, + int n) +{ + int threads = THREADS_PER_BLOCK; + int blocks_per_cone = PDHCG_LARGE_CONE_BLOCKS_PER_CONE; + int blocks = n * blocks_per_cone; + CUDA_CHECK(cudaMemsetAsync(ws + n, 0, (size_t)5 * n * sizeof(double))); + initialize_rotated_soc_grid_weighted_kernel<<>>( + p, vr, qd, tau, ws, si, vd, isf, n, blocks_per_cone); + finalize_rotated_soc_grid_weighted_initialization_kernel<<<(n + threads - 1) / threads, threads>>>( + p, vr, qd, tau, ws, si, vd, isf, n); + for (int iteration = 0; iteration < PDHCG_CONE_GRID_ROOT_ITERATIONS; ++iteration) + { + CUDA_CHECK(cudaMemsetAsync(ws + n, 0, (size_t)2 * n * sizeof(double))); + reduce_rotated_soc_grid_weighted_root_kernel<<>>( + p, vr, qd, tau, ws, si, vd, isf, n, blocks_per_cone); + finalize_rotated_soc_grid_weighted_root_kernel<<<(n + threads - 1) / threads, threads>>>( + p, vr, qd, tau, ws, si, vd, isf, n); + } + CUDA_CHECK(cudaMemsetAsync(ws + n, 0, (size_t)2 * n * sizeof(double))); + reduce_rotated_soc_grid_axis_objective_kernel<<>>( + p, vr, qd, tau, ws, si, vd, isf, n, blocks_per_cone); + finalize_rotated_soc_grid_axis_objective_kernel<<<(n + threads - 1) / threads, threads>>>( + p, vr, qd, tau, ws, si, vd, n); + apply_rotated_soc_grid_weighted_kernel<<>>(p, vr, qd, tau, ws, si, vd, isf, n, blocks_per_cone); +} +static void launch_rotated_grid_weighted_proj( + double *p, const double *vr, double *ws, const int *si, const int *vd, const double *pa, const char *isf, int n) +{ + (void)pa; + launch_rotated_grid_weighted_impl(p, vr, NULL, 0.0, ws, si, vd, isf, n); +} +static void launch_rotated_grid_proj( + double *p, const double *vr, double *ws, const int *si, const int *vd, const double *pa, const char *isf, int n) +{ + (void)vr; + (void)pa; + (void)isf; + int t = THREADS_PER_BLOCK; + int blocks_per_cone = PDHCG_LARGE_CONE_BLOCKS_PER_CONE; + int b = n * blocks_per_cone; + CUDA_CHECK(cudaMemsetAsync(ws, 0, (size_t)n * sizeof(double))); + project_rotated_soc_grid_reduce_kernel<<>>(p, ws, si, vd, n, blocks_per_cone); + project_rotated_soc_grid_finalize_kernel<<<(n + t - 1) / t, t>>>(p, ws, si, vd, n); + project_rotated_soc_grid_apply_kernel<<>>(p, ws, si, vd, n, blocks_per_cone); +} +static void launch_rotated_thread_dual(double *dr, + double *cr, + const double *obj, + const double *dp, + const double *vr, + const double *ps, + double *ws, + const int *si, + const int *vd, + const double *pa, + const char *isf, + int n) +{ + (void)pa; + int t = THREADS_PER_BLOCK; + int b = (n + t - 1) / t; + compute_cone_dual_residual_kernel<<>>(dr, cr, obj, dp, vr, ps, ws, si, vd, isf, n); +} +static void launch_rotated_warp_dual(double *dr, + double *cr, + const double *obj, + const double *dp, + const double *vr, + const double *ps, + double *ws, + const int *si, + const int *vd, + const double *pa, + const char *isf, + int n) +{ + (void)pa; + int t = THREADS_PER_BLOCK; + int b = (n * 32 + t - 1) / t; + compute_cone_dual_residual_warp_kernel<<>>(dr, cr, obj, dp, vr, ps, ws, si, vd, isf, n); +} +static void launch_rotated_grid_dual(double *dr, + double *cr, + const double *obj, + const double *dp, + const double *vr, + const double *ps, + double *ws, + const int *si, + const int *vd, + const double *pa, + const char *isf, + int n) +{ + (void)cr; + (void)ps; + (void)pa; + (void)isf; + int t = THREADS_PER_BLOCK; + int blocks_per_cone = PDHCG_LARGE_CONE_BLOCKS_PER_CONE; + int b = n * blocks_per_cone; + CUDA_CHECK(cudaMemsetAsync(ws, 0, (size_t)n * sizeof(double))); + compute_cone_dual_residual_grid_reduce_kernel<<>>(obj, dp, ws, si, vd, n, blocks_per_cone); + compute_cone_dual_residual_grid_finalize_kernel<<<(n + t - 1) / t, t>>>(dr, obj, dp, vr, ws, si, vd, n); + compute_cone_dual_residual_grid_apply_kernel<<>>(dr, obj, dp, vr, ws, si, vd, n, blocks_per_cone); +} +static void launch_rotated_thread_proj_diag_q(double *pp, + double *rp, + const double *cp, + const double *vr, + const double *qd, + double tau, + double *ws, + const int *si, + const int *vd, + const double *pa, + const char *isf, + int n) +{ + (void)pa; + int t = THREADS_PER_BLOCK; + int b = (n + t - 1) / t; + project_rotated_soc_diag_q_kernel<<>>(pp, rp, cp, vr, qd, tau, ws, si, vd, isf, n); +} +static void launch_rotated_block_proj_diag_q(double *pp, + double *rp, + const double *cp, + const double *vr, + const double *qd, + double tau, + double *ws, + const int *si, + const int *vd, + const double *pa, + const char *isf, + int n) +{ + (void)pa; + project_rotated_soc_block_kernel<<>>(pp, vr, qd, tau, ws, si, vd, isf, n); + launch_cone_reflection(PROJ_METHOD_BLOCK, rp, pp, cp, si, vd, n); +} +static void launch_rotated_grid_weighted_proj_diag_q(double *pp, + double *rp, + const double *cp, + const double *vr, + const double *qd, + double tau, + double *ws, + const int *si, + const int *vd, + const double *pa, + const char *isf, + int n) +{ + (void)pa; + launch_rotated_grid_weighted_impl(pp, vr, qd, tau, ws, si, vd, isf, n); + launch_cone_reflection(PROJ_METHOD_GRID, rp, pp, cp, si, vd, n); +} + +extern const cone_kernel_ops_t pdhcg_rsoc_cone_kernel_ops = { + { + launch_rotated_thread_proj, + launch_rotated_warp_proj, + launch_rotated_block_proj, + launch_rotated_grid_proj, + launch_rotated_grid_weighted_proj, + }, + { + launch_rotated_thread_proj_diag_q, + launch_rotated_block_proj_diag_q, + launch_rotated_block_proj_diag_q, + launch_rotated_grid_weighted_proj_diag_q, + launch_rotated_grid_weighted_proj_diag_q, + }, + { + launch_rotated_thread_dual, + launch_rotated_warp_dual, + launch_block_projected_mapping_only_dual, + launch_rotated_grid_dual, + launch_grid_projected_mapping_only_dual, + }, +}; diff --git a/src/kernels/pdhcg_soc_cone_kernels.cu b/src/kernels/pdhcg_soc_cone_kernels.cu new file mode 100644 index 0000000..f3a6f40 --- /dev/null +++ b/src/kernels/pdhcg_soc_cone_kernels.cu @@ -0,0 +1,2650 @@ +/* +Copyright 2026 Hongpei Li + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +#include "cone_kernel_ops.h" +#include "cone_kernel_reductions.h" +#include "cone_projection_utils.h" +#include "pdhcg_soc_cone_kernels.h" +/* Weighted projection onto an arbitrary nonempty fixed section of + { (u,z) : ||u||_2 <= z }. The first k+1 coordinates form u. */ +__device__ static inline void project_standard_soc_section_serial(double *point, + const double *rescaling, + const double *q_diag, + double tau, + double *warm_start, + int start, + int k, + const char *is_fixed) +{ + int u_length = k + 1; + int z_index = start + u_length; + bool fixed_z = is_fixed[z_index] != 0; + double fixed_norm2 = 0.0; + double free_norm2 = 0.0; + double polar_norm2 = 0.0; + double max_omega = 0.0; + int free_count = 0; + for (int slot = 0; slot < u_length; ++slot) + { + int index = start + slot; + double value = cone_section_actual(point, rescaling, index); + if (is_fixed[index]) + fixed_norm2 += value * value; + else + { + double omega = cone_section_weight(rescaling, q_diag, tau, index); + free_norm2 += value * value; + polar_norm2 += (omega * value) * (omega * value); + max_omega = fmax(max_omega, omega); + ++free_count; + } + } + + double z_input = cone_section_actual(point, rescaling, z_index); + if (fixed_z) + { + double radius2 = fmax(0.0, z_input * z_input - fixed_norm2); + if (free_count == 0 || free_norm2 <= radius2) + return; + if (!(radius2 > 0.0)) + { + for (int slot = 0; slot < u_length; ++slot) + if (!is_fixed[start + slot]) + point[start + slot] = 0.0; + return; + } + + double lo = 0.0; + double hi = sqrt(polar_norm2) / sqrt(radius2) * (1.0 + 64.0 * DBL_EPSILON); + if (!(hi > 0.0) || !isfinite(hi)) + { + hi = warm_start && *warm_start > 0.0 && isfinite(*warm_start) ? *warm_start : 1.0; + for (int expansion = 0; expansion < 100; ++expansion) + { + double norm2 = 0.0; + for (int slot = 0; slot < u_length; ++slot) + { + int index = start + slot; + if (is_fixed[index]) + continue; + double omega = cone_section_weight(rescaling, q_diag, tau, index); + double value = cone_section_actual(point, rescaling, index) * omega / (omega + hi); + norm2 += value * value; + } + if (norm2 <= radius2) + break; + hi *= 2.0; + } + } + for (int iteration = 0; iteration < 80; ++iteration) + { + double lambda = 0.5 * (lo + hi); + double norm2 = 0.0; + for (int slot = 0; slot < u_length; ++slot) + { + int index = start + slot; + if (is_fixed[index]) + continue; + double omega = cone_section_weight(rescaling, q_diag, tau, index); + double value = cone_section_actual(point, rescaling, index) * omega / (omega + lambda); + norm2 += value * value; + } + if (norm2 > radius2) + lo = lambda; + else + hi = lambda; + if ((hi - lo) <= 1e-13 * (1.0 + hi + lo)) + break; + } + double lambda = 0.5 * (lo + hi); + if (warm_start) + *warm_start = lambda; + for (int slot = 0; slot < u_length; ++slot) + { + int index = start + slot; + if (!is_fixed[index]) + { + double omega = cone_section_weight(rescaling, q_diag, tau, index); + point[index] *= omega / (omega + lambda); + } + } + return; + } + + double total_norm2 = fixed_norm2 + free_norm2; + if (z_input >= 0.0 && total_norm2 <= z_input * z_input) + return; + if (free_count == 0) + { + double projected_z = fmax(z_input, sqrt(fixed_norm2)); + point[z_index] = projected_z * rescaling[z_index]; + return; + } + + double omega_z = cone_section_weight(rescaling, q_diag, tau, z_index); + if (fixed_norm2 == 0.0) + { + if (-omega_z * z_input >= sqrt(polar_norm2)) + { + for (int slot = 0; slot < u_length; ++slot) + if (!is_fixed[start + slot]) + point[start + slot] = 0.0; + point[z_index] = 0.0; + return; + } + } + + double lambda; + if (z_input == 0.0) + { + lambda = omega_z; + double norm2 = fixed_norm2; + for (int slot = 0; slot < u_length; ++slot) + { + int index = start + slot; + if (is_fixed[index]) + continue; + double omega = cone_section_weight(rescaling, q_diag, tau, index); + double value = cone_section_actual(point, rescaling, index) * omega / (omega + lambda); + norm2 += value * value; + } + point[z_index] = sqrt(norm2) * rescaling[z_index]; + } + else + { + bool lower_branch = z_input > 0.0; + double lo; + double hi; + if (lower_branch) + { + lo = 0.0; + hi = omega_z * (1.0 - 1e-14); + } + else + { + lo = omega_z * (1.0 + 1e-14); + hi = cone_section_negative_soc_upper(omega_z, -omega_z * z_input, fixed_norm2, polar_norm2, max_omega); + if (!(hi > lo) || !isfinite(hi)) + { + hi = 2.0 * omega_z; + for (int expansion = 0; expansion < 100; ++expansion) + { + double norm2 = fixed_norm2; + for (int slot = 0; slot < u_length; ++slot) + { + int index = start + slot; + if (is_fixed[index]) + continue; + double omega = cone_section_weight(rescaling, q_diag, tau, index); + double value = cone_section_actual(point, rescaling, index) * omega / (omega + hi); + norm2 += value * value; + } + double z = omega_z * z_input / (omega_z - hi); + if (norm2 >= z * z) + break; + hi *= 2.0; + } + } + } + + if (warm_start && *warm_start > lo && *warm_start < hi && isfinite(*warm_start)) + { + double norm2 = fixed_norm2; + for (int slot = 0; slot < u_length; ++slot) + { + int index = start + slot; + if (is_fixed[index]) + continue; + double omega = cone_section_weight(rescaling, q_diag, tau, index); + double value = cone_section_actual(point, rescaling, index) * omega / (omega + *warm_start); + norm2 += value * value; + } + double z = omega_z * z_input / (omega_z - *warm_start); + double f = norm2 - z * z; + if ((lower_branch && f > 0.0) || (!lower_branch && f < 0.0)) + lo = *warm_start; + else + hi = *warm_start; + } + + for (int iteration = 0; iteration < 80; ++iteration) + { + double trial = 0.5 * (lo + hi); + double norm2 = fixed_norm2; + for (int slot = 0; slot < u_length; ++slot) + { + int index = start + slot; + if (is_fixed[index]) + continue; + double omega = cone_section_weight(rescaling, q_diag, tau, index); + double value = cone_section_actual(point, rescaling, index) * omega / (omega + trial); + norm2 += value * value; + } + double z = omega_z * z_input / (omega_z - trial); + double f = norm2 - z * z; + if ((lower_branch && f > 0.0) || (!lower_branch && f < 0.0)) + lo = trial; + else + hi = trial; + if ((hi - lo) <= 1e-13 * (1.0 + hi + lo)) + break; + } + lambda = 0.5 * (lo + hi); + point[z_index] *= omega_z / (omega_z - lambda); + } + + if (warm_start) + *warm_start = lambda; + for (int slot = 0; slot < u_length; ++slot) + { + int index = start + slot; + if (!is_fixed[index]) + { + double omega = cone_section_weight(rescaling, q_diag, tau, index); + point[index] *= omega / (omega + lambda); + } + } +} +#include "utils.h" + +#include +#include +#include + +__global__ void project_standard_soc_kernel(double *__restrict__ primal_solution, + const double *__restrict__ variable_rescaling, + double *__restrict__ warm_start, + const int *__restrict__ start_idx, + const int *__restrict__ v_dim, + const char *__restrict__ is_fixed, + int num_blocks) +{ + int blk = blockIdx.x * blockDim.x + threadIdx.x; + if (blk >= num_blocks) + return; + + int start = start_idx[blk]; + int k = v_dim[blk]; + if (cone_section_has_fixed(is_fixed, start, k + 2)) + { + project_standard_soc_section_serial( + primal_solution, variable_rescaling, NULL, 0.0, warm_start + blk, start, k, is_fixed); + return; + } + double *v = primal_solution + start; + double *wptr = primal_solution + start + k; + double *zptr = primal_solution + start + k + 1; + + double w = *wptr; + double z = *zptr; + + double d_z = variable_rescaling[start + k + 1]; + double dhat_w = variable_rescaling[start + k] / d_z; + double dhat_w2 = dhat_w * dhat_w; + + bool diag_uniform = (dhat_w == 1.0); + for (int m = 0; m < k && diag_uniform; ++m) + { + if (variable_rescaling[start + m] != d_z) + diag_uniform = false; + } + + if (diag_uniform) + { + double sumsq = w * w; + for (int m = 0; m < k; ++m) + sumsq += v[m] * v[m]; + double r = sqrt(sumsq); + if (r <= z) + return; + if (r <= -z) + { + for (int m = 0; m < k; ++m) + v[m] = 0.0; + *wptr = 0.0; + *zptr = 0.0; + return; + } + double scale = (z + r) / (2.0 * r); + for (int m = 0; m < k; ++m) + v[m] *= scale; + *wptr = scale * w; + *zptr = scale * r; + return; + } + + double r_inv_sq = (w / dhat_w) * (w / dhat_w); + double r_pos_sq = (w * dhat_w) * (w * dhat_w); + for (int m = 0; m < k; ++m) + { + double dh = variable_rescaling[start + m] / d_z; + double v_m = v[m]; + r_inv_sq += (v_m / dh) * (v_m / dh); + r_pos_sq += (v_m * dh) * (v_m * dh); + } + double r_inv = sqrt(r_inv_sq); + if (r_inv <= z) + return; + double r_pos = sqrt(r_pos_sq); + if (r_pos <= -z) + { + for (int m = 0; m < k; ++m) + v[m] = 0.0; + *wptr = 0.0; + *zptr = 0.0; + return; + } + + double lo, hi; + bool z_pos = (z > 0.0); + if (z_pos) + { + lo = 0.0; + hi = 0.5 - 1e-14; + } + else + { + lo = 0.5 + 1e-14; + hi = 1.0; + for (int doubling = 0; doubling < 60; ++doubling) + { + double sum_hi = 0.0; + for (int m = 0; m < k; ++m) + { + double dh = variable_rescaling[start + m] / d_z; + double dh2 = dh * dh; + double t = v[m] * dh / (dh2 + 2.0 * hi); + sum_hi += t * t; + } + double tw_hi = w * dhat_w / (dhat_w2 + 2.0 * hi); + sum_hi += tw_hi * tw_hi; + double zt_hi = z / (1.0 - 2.0 * hi); + double f_hi = sum_hi - zt_hi * zt_hi; + if (f_hi > 0.0) + break; + lo = hi; + hi *= 2.0; + } + } + + double warm_lam = warm_start[blk]; + if (warm_lam > lo && warm_lam < hi) + { + double sum_w = 0.0; + for (int m = 0; m < k; ++m) + { + double dh = variable_rescaling[start + m] / d_z; + double dh2 = dh * dh; + double t = v[m] * dh / (dh2 + 2.0 * warm_lam); + sum_w += t * t; + } + double tw = w * dhat_w / (dhat_w2 + 2.0 * warm_lam); + sum_w += tw * tw; + double zt = z / (1.0 - 2.0 * warm_lam); + double f = sum_w - zt * zt; + if (fabs(f) < 1e-12) + { + *zptr = z / (1.0 - 2.0 * warm_lam); + *wptr = w * dhat_w2 / (dhat_w2 + 2.0 * warm_lam); + for (int m = 0; m < k; ++m) + { + double dh = variable_rescaling[start + m] / d_z; + double dh2 = dh * dh; + v[m] = v[m] * dh2 / (dh2 + 2.0 * warm_lam); + } + return; + } + if (z_pos) + { + if (f > 0.0) + lo = warm_lam; + else + hi = warm_lam; + } + else + { + if (f > 0.0) + hi = warm_lam; + else + lo = warm_lam; + } + } + + for (int it = 0; it < 60; ++it) + { + double lam = 0.5 * (lo + hi); + double sum = 0.0; + for (int m = 0; m < k; ++m) + { + double dh = variable_rescaling[start + m] / d_z; + double dh2 = dh * dh; + double t = v[m] * dh / (dh2 + 2.0 * lam); + sum += t * t; + } + double tw = w * dhat_w / (dhat_w2 + 2.0 * lam); + sum += tw * tw; + double zt = z / (1.0 - 2.0 * lam); + double f = sum - zt * zt; + if (z_pos) + { + if (f > 0.0) + lo = lam; + else + hi = lam; + } + else + { + if (f > 0.0) + hi = lam; + else + lo = lam; + } + if ((hi - lo) / (1.0 + hi + lo) < 1e-13) + break; + } + double lam = 0.5 * (lo + hi); + warm_start[blk] = lam; + + *zptr = z / (1.0 - 2.0 * lam); + *wptr = w * dhat_w2 / (dhat_w2 + 2.0 * lam); + for (int m = 0; m < k; ++m) + { + double dh = variable_rescaling[start + m] / d_z; + double dh2 = dh * dh; + v[m] = v[m] * dh2 / (dh2 + 2.0 * lam); + } +} + +__global__ void compute_cone_dual_residual_standard_kernel(double *__restrict__ dual_residual, + double *__restrict__ complementarity_residual, + const double *__restrict__ objective_vector, + const double *__restrict__ dual_product, + const double *__restrict__ variable_rescaling, + const double *__restrict__ primal_solution, + double *__restrict__ warm_start, + const int *__restrict__ start_idx, + const int *__restrict__ v_dim, + const char *__restrict__ is_fixed, + int num_blocks) +{ + int blk = blockIdx.x * blockDim.x + threadIdx.x; + if (blk >= num_blocks) + return; + + int start = start_idx[blk]; + int k = v_dim[blk]; + + if (cone_section_has_fixed(is_fixed, start, k + 2)) + { + for (int slot = 0; slot < k + 2; ++slot) + { + int index = start + slot; + double residual = objective_vector[index] - dual_product[index]; + dual_residual[index] = is_fixed[index] ? primal_solution[index] : primal_solution[index] - residual; + } + project_standard_soc_section_serial( + dual_residual, variable_rescaling, NULL, 0.0, warm_start + blk, start, k, is_fixed); + for (int slot = 0; slot < k + 2; ++slot) + { + int index = start + slot; + dual_residual[index] = + is_fixed[index] ? 0.0 : (primal_solution[index] - dual_residual[index]) * variable_rescaling[index]; + } + complementarity_residual[blk] = 0.0; + return; + } + + double r_w = objective_vector[start + k] - dual_product[start + k]; + double r_z = objective_vector[start + k + 1] - dual_product[start + k + 1]; + + double d_z = variable_rescaling[start + k + 1]; + double e_w = d_z / variable_rescaling[start + k]; + double e_w2 = e_w * e_w; + + bool diag_uniform = (e_w == 1.0); + for (int m = 0; m < k && diag_uniform; ++m) + { + if (variable_rescaling[start + m] != d_z) + diag_uniform = false; + } + + if (diag_uniform) + { + double sumsq = r_w * r_w; + for (int m = 0; m < k; ++m) + { + double rc_m = objective_vector[start + m] - dual_product[start + m]; + sumsq += rc_m * rc_m; + } + double r = sqrt(sumsq); + double v_factor, p_w, p_z; + if (r <= r_z) + { + v_factor = 0.0; + p_w = r_w; + p_z = r_z; + } + else if (r <= -r_z) + { + v_factor = 1.0; + p_w = 0.0; + p_z = 0.0; + } + else + { + double scale = (r_z + r) / (2.0 * r); + v_factor = 1.0 - scale; + p_w = scale * r_w; + p_z = scale * r; + } + for (int m = 0; m < k; ++m) + { + double rc_m = objective_vector[start + m] - dual_product[start + m]; + dual_residual[start + m] = rc_m * v_factor * variable_rescaling[start + m]; + } + dual_residual[start + k] = (r_w - p_w) * variable_rescaling[start + k]; + dual_residual[start + k + 1] = (r_z - p_z) * variable_rescaling[start + k + 1]; + return; + } + + double r_inv_sq = (r_w / e_w) * (r_w / e_w); + double r_pos_sq = (r_w * e_w) * (r_w * e_w); + for (int m = 0; m < k; ++m) + { + double e_m = d_z / variable_rescaling[start + m]; + double rc_m = objective_vector[start + m] - dual_product[start + m]; + r_inv_sq += (rc_m / e_m) * (rc_m / e_m); + r_pos_sq += (rc_m * e_m) * (rc_m * e_m); + } + double r_inv = sqrt(r_inv_sq); + double r_pos = sqrt(r_pos_sq); + + if (r_inv <= r_z) + { + for (int m = 0; m < k; ++m) + dual_residual[start + m] = 0.0; + dual_residual[start + k] = 0.0; + dual_residual[start + k + 1] = 0.0; + return; + } + if (r_pos <= -r_z) + { + for (int m = 0; m < k; ++m) + { + double rc_m = objective_vector[start + m] - dual_product[start + m]; + dual_residual[start + m] = rc_m * variable_rescaling[start + m]; + } + dual_residual[start + k] = r_w * variable_rescaling[start + k]; + dual_residual[start + k + 1] = r_z * variable_rescaling[start + k + 1]; + return; + } + + double lo, hi; + bool z_pos = (r_z > 0.0); + if (z_pos) + { + lo = 0.0; + hi = 0.5 - 1e-14; + } + else + { + lo = 0.5 + 1e-14; + hi = 1.0; + for (int doubling = 0; doubling < 60; ++doubling) + { + double sum_hi = 0.0; + for (int m = 0; m < k; ++m) + { + double e_m = d_z / variable_rescaling[start + m]; + double e_m2 = e_m * e_m; + double rc_m = objective_vector[start + m] - dual_product[start + m]; + double t = rc_m * e_m / (e_m2 + 2.0 * hi); + sum_hi += t * t; + } + double tw_hi = r_w * e_w / (e_w2 + 2.0 * hi); + sum_hi += tw_hi * tw_hi; + double zt_hi = r_z / (1.0 - 2.0 * hi); + double f_hi = sum_hi - zt_hi * zt_hi; + if (f_hi > 0.0) + break; + lo = hi; + hi *= 2.0; + } + } + + double warm_lam = warm_start[blk]; + if (warm_lam > lo && warm_lam < hi) + { + double sum_w = 0.0; + for (int m = 0; m < k; ++m) + { + double e_m = d_z / variable_rescaling[start + m]; + double e_m2 = e_m * e_m; + double rc_m = objective_vector[start + m] - dual_product[start + m]; + double t = rc_m * e_m / (e_m2 + 2.0 * warm_lam); + sum_w += t * t; + } + double tw = r_w * e_w / (e_w2 + 2.0 * warm_lam); + sum_w += tw * tw; + double zt = r_z / (1.0 - 2.0 * warm_lam); + double f = sum_w - zt * zt; + if (fabs(f) < 1e-12) + { + double p_z_w = r_z / (1.0 - 2.0 * warm_lam); + double p_w_w = r_w * e_w2 / (e_w2 + 2.0 * warm_lam); + for (int m = 0; m < k; ++m) + { + double e_m = d_z / variable_rescaling[start + m]; + double e_m2 = e_m * e_m; + double rc_m = objective_vector[start + m] - dual_product[start + m]; + double p_m = rc_m * e_m2 / (e_m2 + 2.0 * warm_lam); + dual_residual[start + m] = (rc_m - p_m) * variable_rescaling[start + m]; + } + dual_residual[start + k] = (r_w - p_w_w) * variable_rescaling[start + k]; + dual_residual[start + k + 1] = (r_z - p_z_w) * variable_rescaling[start + k + 1]; + return; + } + if (z_pos) + { + if (f > 0.0) + lo = warm_lam; + else + hi = warm_lam; + } + else + { + if (f > 0.0) + hi = warm_lam; + else + lo = warm_lam; + } + } + + for (int it = 0; it < 60; ++it) + { + double lam = 0.5 * (lo + hi); + double sum = 0.0; + for (int m = 0; m < k; ++m) + { + double e_m = d_z / variable_rescaling[start + m]; + double e_m2 = e_m * e_m; + double rc_m = objective_vector[start + m] - dual_product[start + m]; + double t = rc_m * e_m / (e_m2 + 2.0 * lam); + sum += t * t; + } + double tw = r_w * e_w / (e_w2 + 2.0 * lam); + sum += tw * tw; + double zt = r_z / (1.0 - 2.0 * lam); + double f = sum - zt * zt; + if (z_pos) + { + if (f > 0.0) + lo = lam; + else + hi = lam; + } + else + { + if (f > 0.0) + hi = lam; + else + lo = lam; + } + if ((hi - lo) / (1.0 + hi + lo) < 1e-13) + break; + } + double lam = 0.5 * (lo + hi); + warm_start[blk] = lam; + + double p_z = r_z / (1.0 - 2.0 * lam); + double p_w = r_w * e_w2 / (e_w2 + 2.0 * lam); + + for (int m = 0; m < k; ++m) + { + double e_m = d_z / variable_rescaling[start + m]; + double e_m2 = e_m * e_m; + double rc_m = objective_vector[start + m] - dual_product[start + m]; + double p_m = rc_m * e_m2 / (e_m2 + 2.0 * lam); + dual_residual[start + m] = (rc_m - p_m) * variable_rescaling[start + m]; + } + dual_residual[start + k] = (r_w - p_w) * variable_rescaling[start + k]; + dual_residual[start + k + 1] = (r_z - p_z) * variable_rescaling[start + k + 1]; +} + +__global__ void project_standard_soc_grid_reduce_kernel(double *__restrict__ primal_solution, + double *__restrict__ workspace, + const int *__restrict__ start_idx, + const int *__restrict__ v_dim, + int num_cones, + int blocks_per_cone) +{ + int cone = blockIdx.x / blocks_per_cone; + int part = blockIdx.x - cone * blocks_per_cone; + if (cone >= num_cones) + return; + + int start = start_idx[cone]; + int k = v_dim[cone]; + double sum = 0.0; + for (int m = part * blockDim.x + threadIdx.x; m < k; m += blocks_per_cone * blockDim.x) + { + double value = primal_solution[start + m]; + sum += value * value; + } + sum = large_cone_block_sum(sum); + if (threadIdx.x == 0) + atomicAdd(workspace + cone, sum); +} + +__global__ void project_standard_soc_grid_finalize_kernel(double *__restrict__ primal_solution, + double *__restrict__ workspace, + const int *__restrict__ start_idx, + const int *__restrict__ v_dim, + int num_cones) +{ + int cone = blockIdx.x * blockDim.x + threadIdx.x; + if (cone >= num_cones) + return; + + int start = start_idx[cone]; + int k = v_dim[cone]; + double w = primal_solution[start + k]; + double z = primal_solution[start + k + 1]; + double radius = sqrt(fmax(0.0, workspace[cone] + w * w)); + + if (radius <= z) + { + workspace[cone] = 1.0; + return; + } + if (radius <= -z) + { + workspace[cone] = 0.0; + primal_solution[start + k] = 0.0; + primal_solution[start + k + 1] = 0.0; + return; + } + + double scale = (z + radius) / (2.0 * radius); + workspace[cone] = scale; + primal_solution[start + k] = scale * w; + primal_solution[start + k + 1] = scale * radius; +} + +__global__ void project_standard_soc_grid_apply_kernel(double *__restrict__ primal_solution, + const double *__restrict__ workspace, + const int *__restrict__ start_idx, + const int *__restrict__ v_dim, + int num_cones, + int blocks_per_cone) +{ + int cone = blockIdx.x / blocks_per_cone; + int part = blockIdx.x - cone * blocks_per_cone; + if (cone >= num_cones) + return; + + double scale = workspace[cone]; + if (scale == 1.0) + return; + + int start = start_idx[cone]; + int k = v_dim[cone]; + for (int m = part * blockDim.x + threadIdx.x; m < k; m += blocks_per_cone * blockDim.x) + { + primal_solution[start + m] *= scale; + } +} + +__global__ void compute_cone_dual_residual_standard_grid_reduce_kernel(const double *__restrict__ objective_vector, + const double *__restrict__ dual_product, + double *__restrict__ workspace, + const int *__restrict__ start_idx, + const int *__restrict__ v_dim, + int num_cones, + int blocks_per_cone) +{ + int cone = blockIdx.x / blocks_per_cone; + int part = blockIdx.x - cone * blocks_per_cone; + if (cone >= num_cones) + return; + + int start = start_idx[cone]; + int k = v_dim[cone]; + double sum = 0.0; + for (int m = part * blockDim.x + threadIdx.x; m < k; m += blocks_per_cone * blockDim.x) + { + double residual = objective_vector[start + m] - dual_product[start + m]; + sum += residual * residual; + } + sum = large_cone_block_sum(sum); + if (threadIdx.x == 0) + atomicAdd(workspace + cone, sum); +} + +__global__ void compute_cone_dual_residual_standard_grid_finalize_kernel(double *__restrict__ dual_residual, + const double *__restrict__ objective_vector, + const double *__restrict__ dual_product, + const double *__restrict__ variable_rescaling, + double *__restrict__ workspace, + const int *__restrict__ start_idx, + const int *__restrict__ v_dim, + int num_cones) +{ + int cone = blockIdx.x * blockDim.x + threadIdx.x; + if (cone >= num_cones) + return; + + int start = start_idx[cone]; + int k = v_dim[cone]; + double r_w = objective_vector[start + k] - dual_product[start + k]; + double r_z = objective_vector[start + k + 1] - dual_product[start + k + 1]; + double radius = sqrt(fmax(0.0, workspace[cone] + r_w * r_w)); + double factor; + double p_w; + double p_z; + + if (radius <= r_z) + { + factor = 0.0; + p_w = r_w; + p_z = r_z; + } + else if (radius <= -r_z) + { + factor = 1.0; + p_w = 0.0; + p_z = 0.0; + } + else + { + double scale = (r_z + radius) / (2.0 * radius); + factor = 1.0 - scale; + p_w = scale * r_w; + p_z = scale * radius; + } + + workspace[cone] = factor; + dual_residual[start + k] = (r_w - p_w) * variable_rescaling[start + k]; + dual_residual[start + k + 1] = (r_z - p_z) * variable_rescaling[start + k + 1]; +} + +__global__ void compute_cone_dual_residual_standard_grid_apply_kernel(double *__restrict__ dual_residual, + const double *__restrict__ objective_vector, + const double *__restrict__ dual_product, + const double *__restrict__ variable_rescaling, + const double *__restrict__ workspace, + const int *__restrict__ start_idx, + const int *__restrict__ v_dim, + int num_cones, + int blocks_per_cone) +{ + int cone = blockIdx.x / blocks_per_cone; + int part = blockIdx.x - cone * blocks_per_cone; + if (cone >= num_cones) + return; + + double factor = workspace[cone]; + int start = start_idx[cone]; + int k = v_dim[cone]; + for (int m = part * blockDim.x + threadIdx.x; m < k; m += blocks_per_cone * blockDim.x) + { + int idx = start + m; + double residual = objective_vector[idx] - dual_product[idx]; + dual_residual[idx] = residual * factor * variable_rescaling[idx]; + } +} + +__global__ void project_standard_soc_warp_kernel(double *__restrict__ primal_solution, + const double *__restrict__ variable_rescaling, + double *__restrict__ warm_start, + const int *__restrict__ start_idx, + const int *__restrict__ v_dim, + const char *__restrict__ is_fixed, + int num_blocks) +{ + int tid = blockIdx.x * blockDim.x + threadIdx.x; + int blk = tid >> 5; + int lane = tid & 31; + if (blk >= num_blocks) + return; + + const unsigned MASK = 0xffffffffu; + + int start = start_idx[blk]; + int k = v_dim[blk]; + + int has_fixed = lane == 0 ? cone_section_has_fixed(is_fixed, start, k + 2) : 0; + has_fixed = __shfl_sync(MASK, has_fixed, 0); + if (has_fixed) + { + if (lane == 0) + project_standard_soc_section_serial( + primal_solution, variable_rescaling, NULL, 0.0, warm_start + blk, start, k, is_fixed); + return; + } + + double w = primal_solution[start + k]; + double z = primal_solution[start + k + 1]; + + double d_z = variable_rescaling[start + k + 1]; + double dhat_w = variable_rescaling[start + k] / d_z; + double dhat_w2 = dhat_w * dhat_w; + + int my_diff = (lane == 0 && dhat_w != 1.0) ? 1 : 0; + for (int m = lane; m < k; m += 32) + { + if (variable_rescaling[start + m] != d_z) + my_diff = 1; + } + for (int o = 16; o > 0; o >>= 1) + my_diff |= __shfl_xor_sync(MASK, my_diff, o); + + if (my_diff == 0) + { + double my_sumsq = (lane == 0) ? w * w : 0.0; + for (int m = lane; m < k; m += 32) + { + double v_m = primal_solution[start + m]; + my_sumsq += v_m * v_m; + } + for (int o = 16; o > 0; o >>= 1) + my_sumsq += __shfl_xor_sync(MASK, my_sumsq, o); + double r = sqrt(my_sumsq); + if (r <= z) + return; + if (r <= -z) + { + for (int m = lane; m < k; m += 32) + primal_solution[start + m] = 0.0; + if (lane == 0) + { + primal_solution[start + k] = 0.0; + primal_solution[start + k + 1] = 0.0; + } + return; + } + double scale = (z + r) / (2.0 * r); + for (int m = lane; m < k; m += 32) + primal_solution[start + m] *= scale; + if (lane == 0) + { + primal_solution[start + k] = scale * w; + primal_solution[start + k + 1] = scale * r; + } + return; + } + + double my_inv = (lane == 0) ? (w / dhat_w) * (w / dhat_w) : 0.0; + double my_pos = (lane == 0) ? (w * dhat_w) * (w * dhat_w) : 0.0; + for (int m = lane; m < k; m += 32) + { + double dh = variable_rescaling[start + m] / d_z; + double v_m = primal_solution[start + m]; + my_inv += (v_m / dh) * (v_m / dh); + my_pos += (v_m * dh) * (v_m * dh); + } + for (int o = 16; o > 0; o >>= 1) + { + my_inv += __shfl_xor_sync(MASK, my_inv, o); + my_pos += __shfl_xor_sync(MASK, my_pos, o); + } + double r_inv = sqrt(my_inv); + if (r_inv <= z) + return; + double r_pos = sqrt(my_pos); + if (r_pos <= -z) + { + for (int m = lane; m < k; m += 32) + primal_solution[start + m] = 0.0; + if (lane == 0) + { + primal_solution[start + k] = 0.0; + primal_solution[start + k + 1] = 0.0; + } + return; + } + + double lo, hi; + bool z_pos = (z > 0.0); + if (z_pos) + { + lo = 0.0; + hi = 0.5 - 1e-14; + } + else + { + lo = 0.5 + 1e-14; + hi = 1.0; + for (int doubling = 0; doubling < 60; ++doubling) + { + double my_sum = + (lane == 0) ? (w * dhat_w / (dhat_w2 + 2.0 * hi)) * (w * dhat_w / (dhat_w2 + 2.0 * hi)) : 0.0; + for (int m = lane; m < k; m += 32) + { + double dh = variable_rescaling[start + m] / d_z; + double dh2 = dh * dh; + double tt = primal_solution[start + m] * dh / (dh2 + 2.0 * hi); + my_sum += tt * tt; + } + for (int o = 16; o > 0; o >>= 1) + my_sum += __shfl_xor_sync(MASK, my_sum, o); + double zt_hi = z / (1.0 - 2.0 * hi); + double f_hi = my_sum - zt_hi * zt_hi; + if (f_hi > 0.0) + break; + lo = hi; + hi *= 2.0; + } + } + + double warm_lam = warm_start[blk]; + if (warm_lam > lo && warm_lam < hi) + { + double my_sum = + (lane == 0) ? (w * dhat_w / (dhat_w2 + 2.0 * warm_lam)) * (w * dhat_w / (dhat_w2 + 2.0 * warm_lam)) : 0.0; + for (int m = lane; m < k; m += 32) + { + double dh = variable_rescaling[start + m] / d_z; + double dh2 = dh * dh; + double tt = primal_solution[start + m] * dh / (dh2 + 2.0 * warm_lam); + my_sum += tt * tt; + } + for (int o = 16; o > 0; o >>= 1) + my_sum += __shfl_xor_sync(MASK, my_sum, o); + double zt = z / (1.0 - 2.0 * warm_lam); + double f = my_sum - zt * zt; + if (fabs(f) < 1e-12) + { + for (int m = lane; m < k; m += 32) + { + double dh = variable_rescaling[start + m] / d_z; + double dh2 = dh * dh; + primal_solution[start + m] = primal_solution[start + m] * dh2 / (dh2 + 2.0 * warm_lam); + } + if (lane == 0) + { + primal_solution[start + k + 1] = z / (1.0 - 2.0 * warm_lam); + primal_solution[start + k] = w * dhat_w2 / (dhat_w2 + 2.0 * warm_lam); + } + return; + } + if (z_pos) + { + if (f > 0.0) + lo = warm_lam; + else + hi = warm_lam; + } + else + { + if (f > 0.0) + hi = warm_lam; + else + lo = warm_lam; + } + } + + for (int it = 0; it < 60; ++it) + { + double lam = 0.5 * (lo + hi); + double my_sum = (lane == 0) ? (w * dhat_w / (dhat_w2 + 2.0 * lam)) * (w * dhat_w / (dhat_w2 + 2.0 * lam)) : 0.0; + for (int m = lane; m < k; m += 32) + { + double dh = variable_rescaling[start + m] / d_z; + double dh2 = dh * dh; + double tt = primal_solution[start + m] * dh / (dh2 + 2.0 * lam); + my_sum += tt * tt; + } + for (int o = 16; o > 0; o >>= 1) + my_sum += __shfl_xor_sync(MASK, my_sum, o); + double zt = z / (1.0 - 2.0 * lam); + double f = my_sum - zt * zt; + if (z_pos) + { + if (f > 0.0) + lo = lam; + else + hi = lam; + } + else + { + if (f > 0.0) + hi = lam; + else + lo = lam; + } + if ((hi - lo) / (1.0 + hi + lo) < 1e-13) + break; + } + double lam = 0.5 * (lo + hi); + if (lane == 0) + warm_start[blk] = lam; + + for (int m = lane; m < k; m += 32) + { + double dh = variable_rescaling[start + m] / d_z; + double dh2 = dh * dh; + primal_solution[start + m] = primal_solution[start + m] * dh2 / (dh2 + 2.0 * lam); + } + if (lane == 0) + { + primal_solution[start + k + 1] = z / (1.0 - 2.0 * lam); + primal_solution[start + k] = w * dhat_w2 / (dhat_w2 + 2.0 * lam); + } +} + +__global__ void compute_cone_dual_residual_standard_warp_kernel(double *__restrict__ dual_residual, + double *__restrict__ complementarity_residual, + const double *__restrict__ objective_vector, + const double *__restrict__ dual_product, + const double *__restrict__ variable_rescaling, + const double *__restrict__ primal_solution, + double *__restrict__ warm_start, + const int *__restrict__ start_idx, + const int *__restrict__ v_dim, + const char *__restrict__ is_fixed, + int num_blocks) +{ + int tid = blockIdx.x * blockDim.x + threadIdx.x; + int blk = tid >> 5; + int lane = tid & 31; + if (blk >= num_blocks) + return; + + const unsigned MASK = 0xffffffffu; + + int start = start_idx[blk]; + int k = v_dim[blk]; + + int has_fixed = lane == 0 ? cone_section_has_fixed(is_fixed, start, k + 2) : 0; + has_fixed = __shfl_sync(MASK, has_fixed, 0); + if (has_fixed) + { + if (lane == 0) + { + for (int slot = 0; slot < k + 2; ++slot) + { + int index = start + slot; + double residual = objective_vector[index] - dual_product[index]; + dual_residual[index] = is_fixed[index] ? primal_solution[index] : primal_solution[index] - residual; + } + project_standard_soc_section_serial( + dual_residual, variable_rescaling, NULL, 0.0, warm_start + blk, start, k, is_fixed); + for (int slot = 0; slot < k + 2; ++slot) + { + int index = start + slot; + dual_residual[index] = + is_fixed[index] ? 0.0 : (primal_solution[index] - dual_residual[index]) * variable_rescaling[index]; + } + complementarity_residual[blk] = 0.0; + } + return; + } + + double r_w = objective_vector[start + k] - dual_product[start + k]; + double r_z = objective_vector[start + k + 1] - dual_product[start + k + 1]; + + double d_z = variable_rescaling[start + k + 1]; + double e_w = d_z / variable_rescaling[start + k]; + double e_w2 = e_w * e_w; + + int my_diff = (lane == 0 && e_w != 1.0) ? 1 : 0; + for (int m = lane; m < k; m += 32) + { + if (variable_rescaling[start + m] != d_z) + my_diff = 1; + } + for (int o = 16; o > 0; o >>= 1) + my_diff |= __shfl_xor_sync(MASK, my_diff, o); + + if (my_diff == 0) + { + double my_sumsq = (lane == 0) ? r_w * r_w : 0.0; + for (int m = lane; m < k; m += 32) + { + double rc_m = objective_vector[start + m] - dual_product[start + m]; + my_sumsq += rc_m * rc_m; + } + for (int o = 16; o > 0; o >>= 1) + my_sumsq += __shfl_xor_sync(MASK, my_sumsq, o); + double r = sqrt(my_sumsq); + double v_factor, p_w, p_z; + if (r <= r_z) + { + v_factor = 0.0; + p_w = r_w; + p_z = r_z; + } + else if (r <= -r_z) + { + v_factor = 1.0; + p_w = 0.0; + p_z = 0.0; + } + else + { + double scale = (r_z + r) / (2.0 * r); + v_factor = 1.0 - scale; + p_w = scale * r_w; + p_z = scale * r; + } + for (int m = lane; m < k; m += 32) + { + double rc_m = objective_vector[start + m] - dual_product[start + m]; + dual_residual[start + m] = rc_m * v_factor * variable_rescaling[start + m]; + } + if (lane == 0) + { + dual_residual[start + k] = (r_w - p_w) * variable_rescaling[start + k]; + dual_residual[start + k + 1] = (r_z - p_z) * variable_rescaling[start + k + 1]; + } + return; + } + + double my_inv = (lane == 0) ? (r_w / e_w) * (r_w / e_w) : 0.0; + double my_pos = (lane == 0) ? (r_w * e_w) * (r_w * e_w) : 0.0; + for (int m = lane; m < k; m += 32) + { + double e_m = d_z / variable_rescaling[start + m]; + double rc_m = objective_vector[start + m] - dual_product[start + m]; + my_inv += (rc_m / e_m) * (rc_m / e_m); + my_pos += (rc_m * e_m) * (rc_m * e_m); + } + for (int o = 16; o > 0; o >>= 1) + { + my_inv += __shfl_xor_sync(MASK, my_inv, o); + my_pos += __shfl_xor_sync(MASK, my_pos, o); + } + double r_inv = sqrt(my_inv); + double r_pos = sqrt(my_pos); + + if (r_inv <= r_z) + { + for (int m = lane; m < k; m += 32) + dual_residual[start + m] = 0.0; + if (lane == 0) + { + dual_residual[start + k] = 0.0; + dual_residual[start + k + 1] = 0.0; + } + return; + } + if (r_pos <= -r_z) + { + for (int m = lane; m < k; m += 32) + { + double rc_m = objective_vector[start + m] - dual_product[start + m]; + dual_residual[start + m] = rc_m * variable_rescaling[start + m]; + } + if (lane == 0) + { + dual_residual[start + k] = r_w * variable_rescaling[start + k]; + dual_residual[start + k + 1] = r_z * variable_rescaling[start + k + 1]; + } + return; + } + + double lo, hi; + bool z_pos = (r_z > 0.0); + if (z_pos) + { + lo = 0.0; + hi = 0.5 - 1e-14; + } + else + { + lo = 0.5 + 1e-14; + hi = 1.0; + for (int doubling = 0; doubling < 60; ++doubling) + { + double my_sum = (lane == 0) ? (r_w * e_w / (e_w2 + 2.0 * hi)) * (r_w * e_w / (e_w2 + 2.0 * hi)) : 0.0; + for (int m = lane; m < k; m += 32) + { + double e_m = d_z / variable_rescaling[start + m]; + double e_m2 = e_m * e_m; + double rc_m = objective_vector[start + m] - dual_product[start + m]; + double tt = rc_m * e_m / (e_m2 + 2.0 * hi); + my_sum += tt * tt; + } + for (int o = 16; o > 0; o >>= 1) + my_sum += __shfl_xor_sync(MASK, my_sum, o); + double zt_hi = r_z / (1.0 - 2.0 * hi); + double f_hi = my_sum - zt_hi * zt_hi; + if (f_hi > 0.0) + break; + lo = hi; + hi *= 2.0; + } + } + + double warm_lam = warm_start[blk]; + if (warm_lam > lo && warm_lam < hi) + { + double my_sum = + (lane == 0) ? (r_w * e_w / (e_w2 + 2.0 * warm_lam)) * (r_w * e_w / (e_w2 + 2.0 * warm_lam)) : 0.0; + for (int m = lane; m < k; m += 32) + { + double e_m = d_z / variable_rescaling[start + m]; + double e_m2 = e_m * e_m; + double rc_m = objective_vector[start + m] - dual_product[start + m]; + double tt = rc_m * e_m / (e_m2 + 2.0 * warm_lam); + my_sum += tt * tt; + } + for (int o = 16; o > 0; o >>= 1) + my_sum += __shfl_xor_sync(MASK, my_sum, o); + double zt = r_z / (1.0 - 2.0 * warm_lam); + double f = my_sum - zt * zt; + if (fabs(f) < 1e-12) + { + double p_z_w = r_z / (1.0 - 2.0 * warm_lam); + double p_w_w = r_w * e_w2 / (e_w2 + 2.0 * warm_lam); + for (int m = lane; m < k; m += 32) + { + double e_m = d_z / variable_rescaling[start + m]; + double e_m2 = e_m * e_m; + double rc_m = objective_vector[start + m] - dual_product[start + m]; + double p_m = rc_m * e_m2 / (e_m2 + 2.0 * warm_lam); + dual_residual[start + m] = (rc_m - p_m) * variable_rescaling[start + m]; + } + if (lane == 0) + { + dual_residual[start + k] = (r_w - p_w_w) * variable_rescaling[start + k]; + dual_residual[start + k + 1] = (r_z - p_z_w) * variable_rescaling[start + k + 1]; + } + return; + } + if (z_pos) + { + if (f > 0.0) + lo = warm_lam; + else + hi = warm_lam; + } + else + { + if (f > 0.0) + hi = warm_lam; + else + lo = warm_lam; + } + } + + for (int it = 0; it < 60; ++it) + { + double lam = 0.5 * (lo + hi); + double my_sum = (lane == 0) ? (r_w * e_w / (e_w2 + 2.0 * lam)) * (r_w * e_w / (e_w2 + 2.0 * lam)) : 0.0; + for (int m = lane; m < k; m += 32) + { + double e_m = d_z / variable_rescaling[start + m]; + double e_m2 = e_m * e_m; + double rc_m = objective_vector[start + m] - dual_product[start + m]; + double tt = rc_m * e_m / (e_m2 + 2.0 * lam); + my_sum += tt * tt; + } + for (int o = 16; o > 0; o >>= 1) + my_sum += __shfl_xor_sync(MASK, my_sum, o); + double zt = r_z / (1.0 - 2.0 * lam); + double f = my_sum - zt * zt; + if (z_pos) + { + if (f > 0.0) + lo = lam; + else + hi = lam; + } + else + { + if (f > 0.0) + hi = lam; + else + lo = lam; + } + if ((hi - lo) / (1.0 + hi + lo) < 1e-13) + break; + } + double lam = 0.5 * (lo + hi); + if (lane == 0) + warm_start[blk] = lam; + + double p_z = r_z / (1.0 - 2.0 * lam); + double p_w = r_w * e_w2 / (e_w2 + 2.0 * lam); + + for (int m = lane; m < k; m += 32) + { + double e_m = d_z / variable_rescaling[start + m]; + double e_m2 = e_m * e_m; + double rc_m = objective_vector[start + m] - dual_product[start + m]; + double p_m = rc_m * e_m2 / (e_m2 + 2.0 * lam); + dual_residual[start + m] = (rc_m - p_m) * variable_rescaling[start + m]; + } + if (lane == 0) + { + dual_residual[start + k] = (r_w - p_w) * variable_rescaling[start + k]; + dual_residual[start + k + 1] = (r_z - p_z) * variable_rescaling[start + k + 1]; + } +} + +/* Project onto D K_exp via Parikh-Boyd Newton on rho = u_1/u_2 (u = D^{-1} x). */ + +__global__ void project_standard_soc_diag_q_kernel(double *__restrict__ pdhg_primal, + double *__restrict__ reflected_primal, + const double *__restrict__ current_primal, + const double *__restrict__ variable_rescaling, + const double *__restrict__ Q_diag, + double tau, + double *__restrict__ warm_start, + const int *__restrict__ start_idx, + const int *__restrict__ v_dim, + const char *__restrict__ is_fixed, + int num_blocks) +{ + int blk = blockIdx.x * blockDim.x + threadIdx.x; + if (blk >= num_blocks) + return; + + int start = start_idx[blk]; + int k = v_dim[blk]; + int w_off = start + k; + int z_off = start + k + 1; + + if (cone_section_has_fixed(is_fixed, start, k + 2)) + { + project_standard_soc_section_serial( + pdhg_primal, variable_rescaling, Q_diag, tau, warm_start + blk, start, k, is_fixed); + for (int slot = 0; slot < k + 2; ++slot) + { + int index = start + slot; + reflected_primal[index] = 2.0 * pdhg_primal[index] - current_primal[index]; + } + return; + } + + double r_w = pdhg_primal[w_off]; + double r_z = pdhg_primal[z_off]; + + double d_z = variable_rescaling[z_off]; + double w_w = 1.0 + tau * Q_diag[w_off]; + double w_z = 1.0 + tau * Q_diag[z_off]; + double sqrt_w_w = sqrt(w_w); + double sqrt_w_z = sqrt(w_z); + double e_z = sqrt_w_z * d_z; + double e_w = sqrt_w_w * variable_rescaling[w_off]; + double eh_w = e_w / e_z; + double eh_w2 = eh_w * eh_w; + + double r_inv_sq = w_w * (r_w / eh_w) * (r_w / eh_w); + double r_pos_sq = w_w * (r_w * eh_w) * (r_w * eh_w); + for (int m = 0; m < k; ++m) + { + double w_m = 1.0 + tau * Q_diag[start + m]; + double e_m = sqrt(w_m) * variable_rescaling[start + m]; + double eh_m = e_m / e_z; + double r_m = pdhg_primal[start + m]; + r_inv_sq += w_m * (r_m / eh_m) * (r_m / eh_m); + r_pos_sq += w_m * (r_m * eh_m) * (r_m * eh_m); + } + double w_z_r_z_sq = w_z * r_z * r_z; + + if (r_inv_sq <= w_z_r_z_sq && r_z >= 0.0) + { + for (int m = 0; m < k; ++m) + { + int idx = start + m; + reflected_primal[idx] = 2.0 * pdhg_primal[idx] - current_primal[idx]; + } + reflected_primal[w_off] = 2.0 * r_w - current_primal[w_off]; + reflected_primal[z_off] = 2.0 * r_z - current_primal[z_off]; + return; + } + + if (r_pos_sq <= w_z_r_z_sq && r_z <= 0.0) + { + for (int m = 0; m < k; ++m) + { + int idx = start + m; + pdhg_primal[idx] = 0.0; + reflected_primal[idx] = -current_primal[idx]; + } + pdhg_primal[w_off] = 0.0; + pdhg_primal[z_off] = 0.0; + reflected_primal[w_off] = -current_primal[w_off]; + reflected_primal[z_off] = -current_primal[z_off]; + return; + } + + /* Fast path: no Q on cone slots and uniform d_v = d_z (LP-style symmetric case). */ + if (Q_diag[w_off] == 0.0 && Q_diag[z_off] == 0.0) + { + bool no_cone_Q = true; + bool d_uniform = (variable_rescaling[w_off] == d_z); + for (int m = 0; m < k; ++m) + { + if (Q_diag[start + m] != 0.0) + { + no_cone_Q = false; + break; + } + if (variable_rescaling[start + m] != d_z) + { + d_uniform = false; + break; + } + } + if (no_cone_Q && d_uniform) + { + double sumsq = r_w * r_w; + for (int m = 0; m < k; ++m) + { + double vm = pdhg_primal[start + m]; + sumsq += vm * vm; + } + double rnorm = sqrt(sumsq); + /* in-cone (rnorm <= r_z, r_z >= 0) and at-origin (rnorm <= -r_z, r_z <= 0) handled above */ + double scale = (r_z + rnorm) / (2.0 * rnorm); + for (int m = 0; m < k; ++m) + { + double v_new = scale * pdhg_primal[start + m]; + pdhg_primal[start + m] = v_new; + int idx = start + m; + reflected_primal[idx] = 2.0 * v_new - current_primal[idx]; + } + double w_new = scale * r_w; + double z_new = scale * rnorm; + pdhg_primal[w_off] = w_new; + pdhg_primal[z_off] = z_new; + reflected_primal[w_off] = 2.0 * w_new - current_primal[w_off]; + reflected_primal[z_off] = 2.0 * z_new - current_primal[z_off]; + return; + } + } + + double lo, hi; + bool z_pos = (r_z > 0.0); + if (z_pos) + { + lo = 0.0; + hi = 0.5 - 1e-14; + } + else + { + lo = 0.5 + 1e-14; + hi = 1.0; + for (int doubling = 0; doubling < 60; ++doubling) + { + double sum_hi = 0.0; + for (int m = 0; m < k; ++m) + { + double w_m = 1.0 + tau * Q_diag[start + m]; + double e_m = sqrt(w_m) * variable_rescaling[start + m]; + double eh_m = e_m / e_z; + double eh_m2 = eh_m * eh_m; + double r_m = pdhg_primal[start + m]; + double t = sqrt(w_m) * r_m * eh_m / (eh_m2 + 2.0 * hi); + sum_hi += t * t; + } + double tw_hi = sqrt_w_w * r_w * eh_w / (eh_w2 + 2.0 * hi); + sum_hi += tw_hi * tw_hi; + double tz_hi = sqrt_w_z * r_z / (1.0 - 2.0 * hi); + double f_hi = sum_hi - tz_hi * tz_hi; + if (f_hi > 0.0) + break; + lo = hi; + hi *= 2.0; + } + } + + double warm_lam = warm_start[blk]; + if (warm_lam > lo && warm_lam < hi) + { + double sum_w = 0.0; + for (int m = 0; m < k; ++m) + { + double w_m = 1.0 + tau * Q_diag[start + m]; + double e_m = sqrt(w_m) * variable_rescaling[start + m]; + double eh_m = e_m / e_z; + double eh_m2 = eh_m * eh_m; + double r_m = pdhg_primal[start + m]; + double t = sqrt(w_m) * r_m * eh_m / (eh_m2 + 2.0 * warm_lam); + sum_w += t * t; + } + double tw = sqrt_w_w * r_w * eh_w / (eh_w2 + 2.0 * warm_lam); + sum_w += tw * tw; + double tz = sqrt_w_z * r_z / (1.0 - 2.0 * warm_lam); + double f = sum_w - tz * tz; + if (fabs(f) < 1e-12) + { + double new_z = r_z / (1.0 - 2.0 * warm_lam); + double new_w = r_w * eh_w2 / (eh_w2 + 2.0 * warm_lam); + pdhg_primal[z_off] = new_z; + pdhg_primal[w_off] = new_w; + reflected_primal[z_off] = 2.0 * new_z - current_primal[z_off]; + reflected_primal[w_off] = 2.0 * new_w - current_primal[w_off]; + for (int m = 0; m < k; ++m) + { + int idx = start + m; + double w_m = 1.0 + tau * Q_diag[idx]; + double e_m = sqrt(w_m) * variable_rescaling[idx]; + double eh_m = e_m / e_z; + double eh_m2 = eh_m * eh_m; + double r_m = pdhg_primal[idx]; + double new_m = r_m * eh_m2 / (eh_m2 + 2.0 * warm_lam); + pdhg_primal[idx] = new_m; + reflected_primal[idx] = 2.0 * new_m - current_primal[idx]; + } + return; + } + if (z_pos) + { + if (f > 0.0) + lo = warm_lam; + else + hi = warm_lam; + } + else + { + if (f > 0.0) + hi = warm_lam; + else + lo = warm_lam; + } + } + + for (int it = 0; it < 60; ++it) + { + double lam = 0.5 * (lo + hi); + double sum = 0.0; + for (int m = 0; m < k; ++m) + { + double w_m = 1.0 + tau * Q_diag[start + m]; + double e_m = sqrt(w_m) * variable_rescaling[start + m]; + double eh_m = e_m / e_z; + double eh_m2 = eh_m * eh_m; + double r_m = pdhg_primal[start + m]; + double t = sqrt(w_m) * r_m * eh_m / (eh_m2 + 2.0 * lam); + sum += t * t; + } + double tw = sqrt_w_w * r_w * eh_w / (eh_w2 + 2.0 * lam); + sum += tw * tw; + double tz = sqrt_w_z * r_z / (1.0 - 2.0 * lam); + double f = sum - tz * tz; + if (z_pos) + { + if (f > 0.0) + lo = lam; + else + hi = lam; + } + else + { + if (f > 0.0) + hi = lam; + else + lo = lam; + } + if ((hi - lo) / (1.0 + hi + lo) < 1e-13) + break; + } + double lam = 0.5 * (lo + hi); + warm_start[blk] = lam; + + double new_z = r_z / (1.0 - 2.0 * lam); + double new_w = r_w * eh_w2 / (eh_w2 + 2.0 * lam); + pdhg_primal[z_off] = new_z; + pdhg_primal[w_off] = new_w; + reflected_primal[z_off] = 2.0 * new_z - current_primal[z_off]; + reflected_primal[w_off] = 2.0 * new_w - current_primal[w_off]; + for (int m = 0; m < k; ++m) + { + int idx = start + m; + double w_m = 1.0 + tau * Q_diag[idx]; + double e_m = sqrt(w_m) * variable_rescaling[idx]; + double eh_m = e_m / e_z; + double eh_m2 = eh_m * eh_m; + double r_m = pdhg_primal[idx]; + double new_m = r_m * eh_m2 / (eh_m2 + 2.0 * lam); + pdhg_primal[idx] = new_m; + reflected_primal[idx] = 2.0 * new_m - current_primal[idx]; + } +} + +/* Weighted prox onto D K_exp; coordinate change y_i = sqrt(w_i) x_i gives e_i = sqrt(w_i) d_i. */ + +__global__ void project_standard_soc_block_kernel(double *__restrict__ point, + const double *__restrict__ rescaling, + const double *__restrict__ q_diag, + double tau, + double *__restrict__ warm_start, + const int *__restrict__ start_idx, + const int *__restrict__ v_dim, + const char *__restrict__ is_fixed, + int num_cones) +{ + int cone = blockIdx.x; + if (cone >= num_cones) + return; + + __shared__ double scratch[96]; + __shared__ double fixed_norm2; + __shared__ double radius2; + __shared__ double lambda; + __shared__ double lo; + __shared__ double hi; + __shared__ double z_input; + __shared__ double omega_z; + __shared__ int mode; + __shared__ int lower_branch; + __shared__ int done; + + int start = start_idx[cone]; + int k = v_dim[cone]; + int u_length = k + 1; + int z_index = start + u_length; + bool fixed_z = is_fixed && is_fixed[z_index]; + + double local_fixed_norm2 = 0.0; + double local_free_norm2 = 0.0; + double local_polar_norm2 = 0.0; + double local_max_omega = 0.0; + for (int slot = threadIdx.x; slot < u_length; slot += blockDim.x) + { + int index = start + slot; + double value = point[index] / rescaling[index]; + if (is_fixed && is_fixed[index]) + local_fixed_norm2 += value * value; + else + { + double omega = cone_section_weight(rescaling, q_diag, tau, index); + local_free_norm2 += value * value; + local_polar_norm2 += (omega * value) * (omega * value); + local_max_omega = fmax(local_max_omega, omega); + } + } + cone_block_sum3(&local_fixed_norm2, &local_free_norm2, &local_polar_norm2, scratch); + local_max_omega = cone_block_max(local_max_omega, scratch); + + if (threadIdx.x == 0) + { + fixed_norm2 = local_fixed_norm2; + z_input = point[z_index] / rescaling[z_index]; + omega_z = cone_section_weight(rescaling, q_diag, tau, z_index); + int free_count = 0; + for (int slot = 0; slot < u_length; ++slot) + free_count += !(is_fixed && is_fixed[start + slot]); + + if (fixed_z) + { + radius2 = fmax(0.0, z_input * z_input - fixed_norm2); + if (free_count == 0 || local_free_norm2 <= radius2) + mode = SOC_BLOCK_IDENTITY; + else if (!(radius2 > 0.0)) + mode = SOC_BLOCK_ZERO_FREE; + else + { + mode = SOC_BLOCK_FIXED_Z_ROOT; + hi = sqrt(local_polar_norm2) / sqrt(radius2) * (1.0 + 64.0 * DBL_EPSILON); + } + } + else if (z_input >= 0.0 && fixed_norm2 + local_free_norm2 <= z_input * z_input) + { + mode = SOC_BLOCK_IDENTITY; + } + else if (free_count == 0) + { + mode = SOC_BLOCK_SCALAR_Z; + } + else if (fixed_norm2 == 0.0 && -omega_z * z_input >= sqrt(local_polar_norm2)) + { + mode = SOC_BLOCK_APEX; + } + else if (z_input == 0.0) + { + mode = SOC_BLOCK_ZERO_Z_ROOT; + lambda = omega_z; + } + else + { + mode = SOC_BLOCK_FREE_Z_ROOT; + lower_branch = z_input > 0.0; + lo = lower_branch ? 0.0 : omega_z * (1.0 + 1e-14); + hi = lower_branch ? omega_z * (1.0 - 1e-14) + : cone_section_negative_soc_upper( + omega_z, -omega_z * z_input, fixed_norm2, local_polar_norm2, local_max_omega); + } + } + __syncthreads(); + + if (mode == SOC_BLOCK_IDENTITY) + return; + if (mode == SOC_BLOCK_ZERO_FREE || mode == SOC_BLOCK_APEX) + { + for (int slot = threadIdx.x; slot < u_length; slot += blockDim.x) + if (!(is_fixed && is_fixed[start + slot])) + point[start + slot] = 0.0; + if (mode == SOC_BLOCK_APEX && threadIdx.x == 0) + point[z_index] = 0.0; + return; + } + if (mode == SOC_BLOCK_SCALAR_Z) + { + if (threadIdx.x == 0) + point[z_index] = fmax(z_input, sqrt(fixed_norm2)) * rescaling[z_index]; + return; + } + + if (mode == SOC_BLOCK_FIXED_Z_ROOT) + { + if (threadIdx.x == 0) + { + lo = 0.0; + done = hi > 0.0 && isfinite(hi); + if (!done) + hi = warm_start && warm_start[cone] > 0.0 && isfinite(warm_start[cone]) ? warm_start[cone] : 1.0; + } + __syncthreads(); + for (int expansion = 0; expansion < 80; ++expansion) + { + if (done) + break; + double norm2 = 0.0; + double unused = 0.0; + double unused2 = 0.0; + for (int slot = threadIdx.x; slot < u_length; slot += blockDim.x) + { + int index = start + slot; + if (is_fixed && is_fixed[index]) + continue; + double omega = cone_section_weight(rescaling, q_diag, tau, index); + double value = (point[index] / rescaling[index]) * omega / (omega + hi); + norm2 += value * value; + } + cone_block_sum3(&norm2, &unused, &unused2, scratch); + if (threadIdx.x == 0) + { + done = norm2 <= radius2; + if (!done) + hi *= 2.0; + } + __syncthreads(); + if (done) + break; + } + __syncthreads(); + + if (threadIdx.x == 0) + { + double warm = warm_start ? warm_start[cone] : 0.0; + lambda = warm > lo && warm < hi && isfinite(warm) ? warm : 0.5 * (lo + hi); + done = 0; + } + __syncthreads(); + for (int iteration = 0; iteration < 30; ++iteration) + { + double norm2 = 0.0; + double derivative = 0.0; + double unused = 0.0; + for (int slot = threadIdx.x; slot < u_length; slot += blockDim.x) + { + int index = start + slot; + if (is_fixed && is_fixed[index]) + continue; + double omega = cone_section_weight(rescaling, q_diag, tau, index); + double value = (point[index] / rescaling[index]) * omega / (omega + lambda); + norm2 += value * value; + derivative -= 2.0 * value * value / (omega + lambda); + } + cone_block_sum3(&norm2, &derivative, &unused, scratch); + if (threadIdx.x == 0) + { + double f = norm2 - radius2; + if (f > 0.0) + lo = lambda; + else + hi = lambda; + double next = lambda - f / derivative; + if (!isfinite(next) || !(next > lo && next < hi)) + next = 0.5 * (lo + hi); + done = fabs(f) <= 1e-13 * (1.0 + radius2) || hi - lo <= 1e-13 * (1.0 + hi + lo); + if (!done) + lambda = next; + } + __syncthreads(); + if (done) + break; + } + } + else if (mode == SOC_BLOCK_ZERO_Z_ROOT) + { + double norm2 = 0.0; + double unused = 0.0; + double unused2 = 0.0; + for (int slot = threadIdx.x; slot < u_length; slot += blockDim.x) + { + int index = start + slot; + if (is_fixed && is_fixed[index]) + continue; + double omega = cone_section_weight(rescaling, q_diag, tau, index); + double value = (point[index] / rescaling[index]) * omega / (omega + lambda); + norm2 += value * value; + } + cone_block_sum3(&norm2, &unused, &unused2, scratch); + if (threadIdx.x == 0) + point[z_index] = sqrt(fixed_norm2 + norm2) * rescaling[z_index]; + } + else + { + if (!lower_branch) + { + if (threadIdx.x == 0) + { + done = hi > lo && isfinite(hi); + if (!done) + hi = 2.0 * omega_z; + } + __syncthreads(); + for (int expansion = 0; expansion < 80; ++expansion) + { + if (done) + break; + double norm2 = 0.0; + double unused = 0.0; + double unused2 = 0.0; + for (int slot = threadIdx.x; slot < u_length; slot += blockDim.x) + { + int index = start + slot; + if (is_fixed && is_fixed[index]) + continue; + double omega = cone_section_weight(rescaling, q_diag, tau, index); + double value = (point[index] / rescaling[index]) * omega / (omega + hi); + norm2 += value * value; + } + cone_block_sum3(&norm2, &unused, &unused2, scratch); + if (threadIdx.x == 0) + { + double z = omega_z * z_input / (omega_z - hi); + done = fixed_norm2 + norm2 >= z * z; + if (!done) + hi *= 2.0; + } + __syncthreads(); + if (done) + break; + } + } + __syncthreads(); + if (threadIdx.x == 0) + { + double warm = warm_start ? warm_start[cone] : 0.0; + lambda = warm > lo && warm < hi && isfinite(warm) ? warm : 0.5 * (lo + hi); + done = 0; + } + __syncthreads(); + for (int iteration = 0; iteration < 35; ++iteration) + { + double norm2 = 0.0; + double derivative = 0.0; + double unused = 0.0; + for (int slot = threadIdx.x; slot < u_length; slot += blockDim.x) + { + int index = start + slot; + if (is_fixed && is_fixed[index]) + continue; + double omega = cone_section_weight(rescaling, q_diag, tau, index); + double value = (point[index] / rescaling[index]) * omega / (omega + lambda); + norm2 += value * value; + derivative -= 2.0 * value * value / (omega + lambda); + } + cone_block_sum3(&norm2, &derivative, &unused, scratch); + if (threadIdx.x == 0) + { + double z = omega_z * z_input / (omega_z - lambda); + double f = fixed_norm2 + norm2 - z * z; + derivative -= 2.0 * z * z / (omega_z - lambda); + if ((lower_branch && f > 0.0) || (!lower_branch && f < 0.0)) + lo = lambda; + else + hi = lambda; + double next = lambda - f / derivative; + if (!isfinite(next) || !(next > lo && next < hi)) + next = 0.5 * (lo + hi); + done = fabs(f) <= 1e-13 * (1.0 + fixed_norm2 + norm2 + z * z) || hi - lo <= 1e-13 * (1.0 + hi + lo); + if (!done) + lambda = next; + } + __syncthreads(); + if (done) + break; + } + if (threadIdx.x == 0) + point[z_index] *= omega_z / (omega_z - lambda); + } + + if (warm_start && threadIdx.x == 0) + warm_start[cone] = lambda; + for (int slot = threadIdx.x; slot < u_length; slot += blockDim.x) + { + int index = start + slot; + if (!(is_fixed && is_fixed[index])) + { + double omega = cone_section_weight(rescaling, q_diag, tau, index); + point[index] *= omega / (omega + lambda); + } + } +} + +enum standard_soc_grid_weighted_mode +{ + SOC_GRID_IDENTITY = 0, + SOC_GRID_ZERO_FREE = 1, + SOC_GRID_APEX = 2, + SOC_GRID_SCALAR_Z = 3, + SOC_GRID_FIXED_EXPAND = 4, + SOC_GRID_FIXED_ROOT = 5, + SOC_GRID_FREE_EXPAND = 6, + SOC_GRID_FREE_ROOT = 7, + SOC_GRID_ZERO_Z_EVAL = 8, + SOC_GRID_FIXED_APPLY = 9, + SOC_GRID_FREE_APPLY = 10, + SOC_GRID_ZERO_Z_APPLY = 11 +}; + +__global__ void initialize_standard_soc_grid_weighted_kernel(const double *__restrict__ point, + const double *__restrict__ rescaling, + const double *__restrict__ q_diag, + double tau, + double *__restrict__ workspace, + const int *__restrict__ start_idx, + const int *__restrict__ v_dim, + const char *__restrict__ is_fixed, + int num_cones, + int blocks_per_cone) +{ + int cone = blockIdx.x / blocks_per_cone; + if (cone >= num_cones) + return; + int part = blockIdx.x - cone * blocks_per_cone; + int start = start_idx[cone]; + int u_length = v_dim[cone] + 1; + double fixed_norm2 = 0.0; + double free_norm2 = 0.0; + double polar_norm2 = 0.0; + double free_count = 0.0; + double max_omega = 0.0; + for (int slot = part * blockDim.x + threadIdx.x; slot < u_length; slot += blocks_per_cone * blockDim.x) + { + int index = start + slot; + double value = point[index] / rescaling[index]; + if (is_fixed && is_fixed[index]) + fixed_norm2 += value * value; + else + { + double omega = cone_section_weight(rescaling, q_diag, tau, index); + free_norm2 += value * value; + polar_norm2 += (omega * value) * (omega * value); + free_count += 1.0; + max_omega = fmax(max_omega, omega); + } + } + __shared__ double scratch[96]; + cone_block_sum3(&fixed_norm2, &free_norm2, &polar_norm2, scratch); + double unused = 0.0; + double unused2 = 0.0; + cone_block_sum3(&free_count, &unused, &unused2, scratch); + max_omega = cone_block_max(max_omega, scratch); + if (threadIdx.x == 0) + { + atomicAdd(workspace + num_cones + cone, fixed_norm2); + atomicAdd(workspace + 2 * num_cones + cone, free_norm2); + atomicAdd(workspace + 3 * num_cones + cone, polar_norm2); + atomicAdd(workspace + 4 * num_cones + cone, free_count); + cone_atomic_max_positive(workspace + 5 * num_cones + cone, max_omega); + } +} + +__global__ void finalize_standard_soc_grid_weighted_initialization_kernel(const double *__restrict__ point, + const double *__restrict__ rescaling, + const double *__restrict__ q_diag, + double tau, + double *__restrict__ workspace, + const int *__restrict__ start_idx, + const int *__restrict__ v_dim, + const char *__restrict__ is_fixed, + int num_cones) +{ + int cone = blockIdx.x * blockDim.x + threadIdx.x; + if (cone >= num_cones) + return; + int start = start_idx[cone]; + int z_index = start + v_dim[cone] + 1; + double warm = workspace[cone]; + double fixed_norm2 = workspace[num_cones + cone]; + double free_norm2 = workspace[2 * num_cones + cone]; + double polar_norm2 = workspace[3 * num_cones + cone]; + int free_count = (int)workspace[4 * num_cones + cone]; + double max_omega = workspace[5 * num_cones + cone]; + double z = point[z_index] / rescaling[z_index]; + double omega_z_value = cone_section_weight(rescaling, q_diag, tau, z_index); + bool fixed_z = is_fixed && is_fixed[z_index]; + int selected_mode; + double constant = fixed_norm2; + double lower = 0.0; + double upper = 0.0; + double trial = warm; + + if (fixed_z) + { + constant = fmax(0.0, z * z - fixed_norm2); + if (free_count == 0 || free_norm2 <= constant) + selected_mode = SOC_GRID_IDENTITY; + else if (!(constant > 0.0)) + selected_mode = SOC_GRID_ZERO_FREE; + else + { + lower = 0.0; + upper = sqrt(polar_norm2) / sqrt(constant) * (1.0 + 64.0 * DBL_EPSILON); + if (upper > 0.0 && isfinite(upper)) + { + selected_mode = SOC_GRID_FIXED_ROOT; + trial = warm > lower && warm < upper && isfinite(warm) ? warm : 0.5 * upper; + } + else + { + selected_mode = SOC_GRID_FIXED_EXPAND; + trial = warm > 0.0 && isfinite(warm) ? warm : 1.0; + upper = trial; + } + } + } + else if (z >= 0.0 && fixed_norm2 + free_norm2 <= z * z) + { + selected_mode = SOC_GRID_IDENTITY; + } + else if (free_count == 0) + { + selected_mode = SOC_GRID_SCALAR_Z; + } + else if (fixed_norm2 == 0.0 && -omega_z_value * z >= sqrt(polar_norm2)) + { + selected_mode = SOC_GRID_APEX; + } + else if (z == 0.0) + { + selected_mode = SOC_GRID_ZERO_Z_EVAL; + trial = omega_z_value; + } + else if (z > 0.0) + { + selected_mode = SOC_GRID_FREE_ROOT; + lower = 0.0; + upper = omega_z_value * (1.0 - 1e-14); + trial = warm > lower && warm < upper && isfinite(warm) ? warm : 0.5 * (lower + upper); + } + else + { + lower = omega_z_value * (1.0 + 1e-14); + double endpoint_polar = -omega_z_value * z; + upper = cone_section_negative_soc_upper(omega_z_value, endpoint_polar, fixed_norm2, polar_norm2, max_omega); + if (upper > lower && isfinite(upper)) + { + selected_mode = SOC_GRID_FREE_ROOT; + trial = warm > lower && warm < upper && isfinite(warm) ? warm : 0.5 * (lower + upper); + } + else + { + selected_mode = SOC_GRID_FREE_EXPAND; + trial = warm > lower && isfinite(warm) ? warm : 2.0 * omega_z_value; + upper = trial; + } + } + + workspace[cone] = trial; + workspace[4 * num_cones + cone] = (double)selected_mode; + workspace[5 * num_cones + cone] = constant; + workspace[6 * num_cones + cone] = lower; + workspace[7 * num_cones + cone] = upper; +} + +__global__ void reduce_standard_soc_grid_weighted_root_kernel(const double *__restrict__ point, + const double *__restrict__ rescaling, + const double *__restrict__ q_diag, + double tau, + double *__restrict__ workspace, + const int *__restrict__ start_idx, + const int *__restrict__ v_dim, + const char *__restrict__ is_fixed, + int num_cones, + int blocks_per_cone) +{ + int cone = blockIdx.x / blocks_per_cone; + if (cone >= num_cones) + return; + int selected_mode = (int)workspace[4 * num_cones + cone]; + if (selected_mode < SOC_GRID_FIXED_EXPAND || selected_mode > SOC_GRID_ZERO_Z_EVAL) + return; + int part = blockIdx.x - cone * blocks_per_cone; + int start = start_idx[cone]; + int u_length = v_dim[cone] + 1; + double lambda_value = workspace[cone]; + double norm2 = 0.0; + double derivative = 0.0; + for (int slot = part * blockDim.x + threadIdx.x; slot < u_length; slot += blocks_per_cone * blockDim.x) + { + int index = start + slot; + if (is_fixed && is_fixed[index]) + continue; + double omega = cone_section_weight(rescaling, q_diag, tau, index); + double value = (point[index] / rescaling[index]) * omega / (omega + lambda_value); + norm2 += value * value; + derivative -= 2.0 * value * value / (omega + lambda_value); + } + __shared__ double scratch[96]; + double unused = 0.0; + cone_block_sum3(&norm2, &derivative, &unused, scratch); + if (threadIdx.x == 0) + { + atomicAdd(workspace + num_cones + cone, norm2); + atomicAdd(workspace + 2 * num_cones + cone, derivative); + } +} + +__global__ void finalize_standard_soc_grid_weighted_root_kernel(double *__restrict__ point, + const double *__restrict__ rescaling, + const double *__restrict__ q_diag, + double tau, + double *__restrict__ workspace, + const int *__restrict__ start_idx, + const int *__restrict__ v_dim, + int num_cones) +{ + int cone = blockIdx.x * blockDim.x + threadIdx.x; + if (cone >= num_cones) + return; + int selected_mode = (int)workspace[4 * num_cones + cone]; + if (selected_mode < SOC_GRID_FIXED_EXPAND || selected_mode > SOC_GRID_ZERO_Z_EVAL) + return; + int start = start_idx[cone]; + int z_index = start + v_dim[cone] + 1; + double lambda_value = workspace[cone]; + double sum = workspace[num_cones + cone]; + double derivative = workspace[2 * num_cones + cone]; + double constant = workspace[5 * num_cones + cone]; + double lower = workspace[6 * num_cones + cone]; + double upper = workspace[7 * num_cones + cone]; + + if (selected_mode == SOC_GRID_ZERO_Z_EVAL) + { + point[z_index] = sqrt(constant + sum) * rescaling[z_index]; + workspace[4 * num_cones + cone] = (double)SOC_GRID_ZERO_Z_APPLY; + return; + } + + double f; + if (selected_mode == SOC_GRID_FIXED_EXPAND || selected_mode == SOC_GRID_FIXED_ROOT) + { + f = sum - constant; + if (selected_mode == SOC_GRID_FIXED_EXPAND) + { + if (f > 0.0) + { + lower = lambda_value; + lambda_value *= 2.0; + } + else + { + upper = lambda_value; + selected_mode = SOC_GRID_FIXED_ROOT; + lambda_value = 0.5 * (lower + upper); + } + } + else + { + if (f > 0.0) + lower = lambda_value; + else + upper = lambda_value; + bool converged = fabs(f) <= 1e-13 * (1.0 + constant) || upper - lower <= 1e-13 * (1.0 + upper + lower); + if (converged) + selected_mode = SOC_GRID_FIXED_APPLY; + else + { + double next = lambda_value - f / derivative; + lambda_value = isfinite(next) && next > lower && next < upper ? next : 0.5 * (lower + upper); + } + } + } + else + { + double z_input = point[z_index] / rescaling[z_index]; + double omega_z_value = cone_section_weight(rescaling, q_diag, tau, z_index); + double z = omega_z_value * z_input / (omega_z_value - lambda_value); + f = constant + sum - z * z; + derivative -= 2.0 * z * z / (omega_z_value - lambda_value); + if (selected_mode == SOC_GRID_FREE_EXPAND) + { + if (f < 0.0) + { + lower = lambda_value; + lambda_value *= 2.0; + } + else + { + upper = lambda_value; + selected_mode = SOC_GRID_FREE_ROOT; + lambda_value = 0.5 * (lower + upper); + } + } + else + { + bool lower_branch_value = z_input > 0.0; + if ((lower_branch_value && f > 0.0) || (!lower_branch_value && f < 0.0)) + lower = lambda_value; + else + upper = lambda_value; + bool converged = + fabs(f) <= 1e-13 * (1.0 + constant + sum + z * z) || upper - lower <= 1e-13 * (1.0 + upper + lower); + if (converged) + selected_mode = SOC_GRID_FREE_APPLY; + else + { + double next = lambda_value - f / derivative; + lambda_value = isfinite(next) && next > lower && next < upper ? next : 0.5 * (lower + upper); + } + } + } + workspace[cone] = lambda_value; + workspace[4 * num_cones + cone] = (double)selected_mode; + workspace[6 * num_cones + cone] = lower; + workspace[7 * num_cones + cone] = upper; +} + +__global__ void apply_standard_soc_grid_weighted_kernel(double *__restrict__ point, + const double *__restrict__ rescaling, + const double *__restrict__ q_diag, + double tau, + const double *__restrict__ workspace, + const int *__restrict__ start_idx, + const int *__restrict__ v_dim, + const char *__restrict__ is_fixed, + int num_cones, + int blocks_per_cone) +{ + int cone = blockIdx.x / blocks_per_cone; + if (cone >= num_cones) + return; + int selected_mode = (int)workspace[4 * num_cones + cone]; + if (selected_mode == SOC_GRID_IDENTITY || selected_mode == SOC_GRID_SCALAR_Z) + { + if (selected_mode == SOC_GRID_SCALAR_Z && blockIdx.x % blocks_per_cone == 0 && threadIdx.x == 0) + { + int z_index = start_idx[cone] + v_dim[cone] + 1; + double z = point[z_index] / rescaling[z_index]; + point[z_index] = fmax(z, sqrt(workspace[5 * num_cones + cone])) * rescaling[z_index]; + } + return; + } + int part = blockIdx.x - cone * blocks_per_cone; + int start = start_idx[cone]; + int u_length = v_dim[cone] + 1; + if (selected_mode == SOC_GRID_ZERO_FREE || selected_mode == SOC_GRID_APEX) + { + for (int slot = part * blockDim.x + threadIdx.x; slot < u_length; slot += blocks_per_cone * blockDim.x) + if (!(is_fixed && is_fixed[start + slot])) + point[start + slot] = 0.0; + if (selected_mode == SOC_GRID_APEX && part == 0 && threadIdx.x == 0) + point[start + u_length] = 0.0; + return; + } + + double lambda_value = workspace[cone]; + for (int slot = part * blockDim.x + threadIdx.x; slot < u_length; slot += blocks_per_cone * blockDim.x) + { + int index = start + slot; + if (!(is_fixed && is_fixed[index])) + { + double omega = cone_section_weight(rescaling, q_diag, tau, index); + point[index] *= omega / (omega + lambda_value); + } + } + bool free_z_mode = selected_mode == SOC_GRID_FREE_EXPAND || selected_mode == SOC_GRID_FREE_ROOT || + selected_mode == SOC_GRID_FREE_APPLY; + if (free_z_mode && part == 0 && threadIdx.x == 0) + { + int z_index = start + u_length; + double omega_z_value = cone_section_weight(rescaling, q_diag, tau, z_index); + point[z_index] *= omega_z_value / (omega_z_value - lambda_value); + } +} + +enum rotated_soc_block_mode +{ + RSOC_BLOCK_IDENTITY = 0, + RSOC_BLOCK_ZERO_FREE = 1, + RSOC_BLOCK_FIXED_ENDPOINTS_ROOT = 2, + RSOC_BLOCK_ONE_ENDPOINT_ZERO = 3, + RSOC_BLOCK_ONE_ENDPOINT_SCALAR = 4, + RSOC_BLOCK_ONE_ENDPOINT_ROOT = 5, + RSOC_BLOCK_APEX = 6, + RSOC_BLOCK_BALANCED = 7, + RSOC_BLOCK_FREE_ROOT = 8, + RSOC_BLOCK_AXIS = 9 +}; + +static void launch_standard_thread_proj( + double *p, const double *vr, double *ws, const int *si, const int *vd, const double *pa, const char *isf, int n) +{ + (void)pa; + int t = THREADS_PER_BLOCK; + int b = (n + t - 1) / t; + project_standard_soc_kernel<<>>(p, vr, ws, si, vd, isf, n); +} +static void launch_standard_warp_proj( + double *p, const double *vr, double *ws, const int *si, const int *vd, const double *pa, const char *isf, int n) +{ + (void)pa; + int t = THREADS_PER_BLOCK; + int b = (n * 32 + t - 1) / t; + project_standard_soc_warp_kernel<<>>(p, vr, ws, si, vd, isf, n); +} +static void launch_standard_block_proj( + double *p, const double *vr, double *ws, const int *si, const int *vd, const double *pa, const char *isf, int n) +{ + (void)pa; + project_standard_soc_block_kernel<<>>(p, vr, NULL, 0.0, ws, si, vd, isf, n); +} +static void launch_standard_grid_weighted_impl(double *p, + const double *vr, + const double *qd, + double tau, + double *ws, + const int *si, + const int *vd, + const char *isf, + int n) +{ + int threads = THREADS_PER_BLOCK; + int blocks_per_cone = PDHCG_LARGE_CONE_BLOCKS_PER_CONE; + int blocks = n * blocks_per_cone; + CUDA_CHECK(cudaMemsetAsync(ws + n, 0, (size_t)5 * n * sizeof(double))); + initialize_standard_soc_grid_weighted_kernel<<>>( + p, vr, qd, tau, ws, si, vd, isf, n, blocks_per_cone); + finalize_standard_soc_grid_weighted_initialization_kernel<<<(n + threads - 1) / threads, threads>>>( + p, vr, qd, tau, ws, si, vd, isf, n); + for (int iteration = 0; iteration < PDHCG_CONE_GRID_ROOT_ITERATIONS; ++iteration) + { + CUDA_CHECK(cudaMemsetAsync(ws + n, 0, (size_t)2 * n * sizeof(double))); + reduce_standard_soc_grid_weighted_root_kernel<<>>( + p, vr, qd, tau, ws, si, vd, isf, n, blocks_per_cone); + finalize_standard_soc_grid_weighted_root_kernel<<<(n + threads - 1) / threads, threads>>>( + p, vr, qd, tau, ws, si, vd, n); + } + apply_standard_soc_grid_weighted_kernel<<>>(p, vr, qd, tau, ws, si, vd, isf, n, blocks_per_cone); +} +static void launch_standard_grid_weighted_proj( + double *p, const double *vr, double *ws, const int *si, const int *vd, const double *pa, const char *isf, int n) +{ + (void)pa; + launch_standard_grid_weighted_impl(p, vr, NULL, 0.0, ws, si, vd, isf, n); +} +static void launch_standard_grid_proj( + double *p, const double *vr, double *ws, const int *si, const int *vd, const double *pa, const char *isf, int n) +{ + (void)vr; + (void)pa; + (void)isf; + int t = THREADS_PER_BLOCK; + int blocks_per_cone = PDHCG_LARGE_CONE_BLOCKS_PER_CONE; + int b = n * blocks_per_cone; + CUDA_CHECK(cudaMemsetAsync(ws, 0, (size_t)n * sizeof(double))); + project_standard_soc_grid_reduce_kernel<<>>(p, ws, si, vd, n, blocks_per_cone); + project_standard_soc_grid_finalize_kernel<<<(n + t - 1) / t, t>>>(p, ws, si, vd, n); + project_standard_soc_grid_apply_kernel<<>>(p, ws, si, vd, n, blocks_per_cone); +} +static void launch_standard_thread_dual(double *dr, + double *cr, + const double *obj, + const double *dp, + const double *vr, + const double *ps, + double *ws, + const int *si, + const int *vd, + const double *pa, + const char *isf, + int n) +{ + (void)pa; + int t = THREADS_PER_BLOCK; + int b = (n + t - 1) / t; + compute_cone_dual_residual_standard_kernel<<>>(dr, cr, obj, dp, vr, ps, ws, si, vd, isf, n); +} +static void launch_standard_warp_dual(double *dr, + double *cr, + const double *obj, + const double *dp, + const double *vr, + const double *ps, + double *ws, + const int *si, + const int *vd, + const double *pa, + const char *isf, + int n) +{ + (void)pa; + int t = THREADS_PER_BLOCK; + int b = (n * 32 + t - 1) / t; + compute_cone_dual_residual_standard_warp_kernel<<>>(dr, cr, obj, dp, vr, ps, ws, si, vd, isf, n); +} +static void launch_standard_grid_dual(double *dr, + double *cr, + const double *obj, + const double *dp, + const double *vr, + const double *ps, + double *ws, + const int *si, + const int *vd, + const double *pa, + const char *isf, + int n) +{ + (void)cr; + (void)ps; + (void)pa; + (void)isf; + int t = THREADS_PER_BLOCK; + int blocks_per_cone = PDHCG_LARGE_CONE_BLOCKS_PER_CONE; + int b = n * blocks_per_cone; + CUDA_CHECK(cudaMemsetAsync(ws, 0, (size_t)n * sizeof(double))); + compute_cone_dual_residual_standard_grid_reduce_kernel<<>>(obj, dp, ws, si, vd, n, blocks_per_cone); + compute_cone_dual_residual_standard_grid_finalize_kernel<<<(n + t - 1) / t, t>>>(dr, obj, dp, vr, ws, si, vd, n); + compute_cone_dual_residual_standard_grid_apply_kernel<<>>(dr, obj, dp, vr, ws, si, vd, n, blocks_per_cone); +} +static void launch_standard_thread_proj_diag_q(double *pp, + double *rp, + const double *cp, + const double *vr, + const double *qd, + double tau, + double *ws, + const int *si, + const int *vd, + const double *pa, + const char *isf, + int n) +{ + (void)pa; + int t = THREADS_PER_BLOCK; + int b = (n + t - 1) / t; + project_standard_soc_diag_q_kernel<<>>(pp, rp, cp, vr, qd, tau, ws, si, vd, isf, n); +} +static void launch_standard_block_proj_diag_q(double *pp, + double *rp, + const double *cp, + const double *vr, + const double *qd, + double tau, + double *ws, + const int *si, + const int *vd, + const double *pa, + const char *isf, + int n) +{ + (void)pa; + project_standard_soc_block_kernel<<>>(pp, vr, qd, tau, ws, si, vd, isf, n); + launch_cone_reflection(PROJ_METHOD_BLOCK, rp, pp, cp, si, vd, n); +} +static void launch_standard_grid_weighted_proj_diag_q(double *pp, + double *rp, + const double *cp, + const double *vr, + const double *qd, + double tau, + double *ws, + const int *si, + const int *vd, + const double *pa, + const char *isf, + int n) +{ + (void)pa; + launch_standard_grid_weighted_impl(pp, vr, qd, tau, ws, si, vd, isf, n); + launch_cone_reflection(PROJ_METHOD_GRID, rp, pp, cp, si, vd, n); +} + +extern const cone_kernel_ops_t pdhcg_soc_cone_kernel_ops = { + { + launch_standard_thread_proj, + launch_standard_warp_proj, + launch_standard_block_proj, + launch_standard_grid_proj, + launch_standard_grid_weighted_proj, + }, + { + launch_standard_thread_proj_diag_q, + launch_standard_block_proj_diag_q, + launch_standard_block_proj_diag_q, + launch_standard_grid_weighted_proj_diag_q, + launch_standard_grid_weighted_proj_diag_q, + }, + { + launch_standard_thread_dual, + launch_standard_warp_dual, + launch_block_projected_mapping_only_dual, + launch_standard_grid_dual, + launch_grid_projected_mapping_only_dual, + }, +}; diff --git a/src/pdhcg.c b/src/pdhcg.c index 1d3d266..4d0207c 100644 --- a/src/pdhcg.c +++ b/src/pdhcg.c @@ -629,6 +629,11 @@ int set_cone_fixed(qp_problem_t *prob, int cone_idx, int slot, double value) fprintf(stderr, "[set_cone_fixed] cone_idx %d out of range [0, %d)\n", cone_idx, prob->cones.num_cones); return -1; } + if (prob->cones.type[cone_idx] == CONE_PSD) + { + fprintf(stderr, "[set_cone_fixed] fixed PSD cone sections are not supported.\n"); + return -1; + } int len = cone_block_length(&prob->cones, cone_idx); if (slot < 0 || slot >= len) { diff --git a/src/pdhg_core_op.cu b/src/pdhg_core_op.cu index 44650bb..ba3e48d 100644 --- a/src/pdhg_core_op.cu +++ b/src/pdhg_core_op.cu @@ -16,11 +16,15 @@ limitations under the License. */ #include "cone_dispatch.h" +#include "cone_kernel_ops.h" #include "distributed_conic.h" #include "distributed_interface.h" #include "internal_types.h" #include "pdhcg.h" -#include "pdhcg_kernels.cuh" +#include "pdhcg_affine_cone_kernels.h" +#include "pdhcg_kernels.h" +#include "pdhcg_power_cone_kernels.h" +#include "pdhcg_psd_cone.h" #include "pdhg_core_op.h" #include "preconditioner.h" #include "solver.h" @@ -158,6 +162,13 @@ static void compute_affine_cone_residuals(pdhg_solver_state_t *state, bucket->count); } } + prepare_psd_affine_cone_residuals(state->affine_cones.psd, + projection_point, + state->affine_cones.complementarity_residual, + state->primal_product, + state->affine_cone_offset, + state->pdhg_dual_solution, + state->constraint_bound_rescaling); } prepare_split_affine_cone_residuals( state, projection_point, state->primal_product, state->affine_cone_offset, state->pdhg_dual_solution); @@ -212,23 +223,21 @@ static void compute_power_cone_primal_violation(pdhg_solver_state_t *state, double absolute_accumulator = 0.0; double relative_accumulator = 0.0; - int threads = THREADS_PER_BLOCK; for (int b = 0; b < state->cones.num_buckets; ++b) { const cone_bucket_t *bucket = &state->cones.buckets[b]; if (bucket->type != CONE_POWER) continue; - int blocks = (bucket->count + threads - 1) / threads; double *absolute_workspace = state->cones.power_violation_workspace + bucket->offset; double *relative_workspace = state->cones.power_violation_workspace + state->cones.num_blocks + bucket->offset; - compute_power_cone_primal_violation_kernel<<>>(absolute_workspace, - relative_workspace, - state->pdhg_primal_solution, - state->variable_rescaling, - state->cones.start_idx + bucket->offset, - state->cones.power_alpha + bucket->offset, - state->constraint_bound_rescaling, - bucket->count); + launch_power_cone_primal_violation(absolute_workspace, + relative_workspace, + state->pdhg_primal_solution, + state->variable_rescaling, + state->cones.start_idx + bucket->offset, + state->cones.power_alpha + bucket->offset, + state->constraint_bound_rescaling, + bucket->count); if (optimality_norm == NORM_TYPE_L_INF) { absolute_accumulator = fmax(absolute_accumulator, @@ -718,11 +727,24 @@ void pdhg_update(pdhg_solver_state_t *state) case PDHCG_NON_Q: { lp_primal_update(state, primal_step_size); + if (state->has_variable_cones) + { + project_cone_runtime( + state, &state->cones, state->pdhg_primal_solution, state->cones.projection_warm_start); + recompute_cone_reflection(state); + } break; } case PDHCG_DIAG_Q: { - diag_q_primal_update(state, primal_step_size); + if (state->cones.has_psd_cones) + primal_BB_step_size_update(state, primal_step_size); + else + { + diag_q_primal_update(state, primal_step_size); + if (state->has_variable_cones) + project_cone_runtime_diag_q(state, &state->cones, primal_step_size); + } break; } case PDHCG_SPARSE_Q: @@ -737,23 +759,6 @@ void pdhg_update(pdhg_solver_state_t *state) exit(EXIT_FAILURE); } - if (state->has_variable_cones) - { - quad_obj_type_t qt = state->quadratic_objective_term->quad_obj_type; - if (qt == PDHCG_DIAG_Q) - { - project_cone_runtime_diag_q(state, &state->cones, primal_step_size); - } - else if (qt == PDHCG_SPARSE_Q || qt == PDHCG_LOW_RANK_Q || qt == PDHCG_LOW_RANK_PLUS_SPARSE_Q) - { - } - else - { - project_cone_runtime(state, &state->cones, state->pdhg_primal_solution, state->cones.projection_warm_start); - recompute_cone_reflection(state); - } - } - state->inner_solver->total_count++; pdhcg_spmv_execute(state->sparse_handle, @@ -1021,9 +1026,7 @@ void compute_fixed_point_error(pdhg_solver_state_t *state) interaction = 2 * state->step_size * cross_term; state->fixed_point_error = sqrt(movement + interaction); - if (state->problem_type == CONVEX_QP && - (state->quadratic_objective_term->quad_obj_type != PDHCG_NON_Q && - state->quadratic_objective_term->quad_obj_type != PDHCG_DIAG_Q)) + if (state->problem_type == CONVEX_QP && state->inner_solver->bb_step_size) { state->inner_solver->tol = fmin(state->inner_solver->tol, diff --git a/src/preconditioner.c b/src/preconditioner.c index dec8b60..4fdbb59 100644 --- a/src/preconditioner.c +++ b/src/preconditioner.c @@ -24,8 +24,9 @@ limitations under the License. #include #define SCALING_EPSILON 1e-12 -#define CURTIS_REID_MIN_ABS 1e-300 -#define CURTIS_REID_LOG_SCALE_LIMIT 69.07755278982137 +#define LOG_SCALING_MIN_ABS 1e-300 +#define LOG_SCALING_LIMIT 69.07755278982137 +#define PSD_LOG_FACTOR_LIMIT (0.5 * LOG_SCALING_LIMIT) #define PHASE_TAPER_CONE_THRESHOLD 8 typedef enum @@ -34,14 +35,93 @@ typedef enum CONE_SCALING_POCK_CHAMBOLLE, } cone_scaling_phase_t; +static int max_psd_order(const cone_blocks_t *cones) +{ + int max_order = 0; + for (int block = 0; block < cones->num_cones; ++block) + if (cones->type[block] == CONE_PSD) + max_order = cones->v_dim[block] > max_order ? cones->v_dim[block] : max_order; + return max_order; +} + +static void project_psd_log_scaling(double *log_scaling, int start, int order, double *factor_log) +{ + memset(factor_log, 0, (size_t)order * sizeof(double)); + + int slot = start; + for (int column = 0; column < order; ++column) + { + for (int row = column; row < order; ++row, ++slot) + { + double value = log_scaling[slot]; + if (isnan(value)) + value = 0.0; + else if (value > LOG_SCALING_LIMIT) + value = LOG_SCALING_LIMIT; + else if (value < -LOG_SCALING_LIMIT) + value = -LOG_SCALING_LIMIT; + + if (row == column) + factor_log[row] += 2.0 * value; + else + { + factor_log[row] += value; + factor_log[column] += value; + } + } + } + + double rhs_sum = 0.0; + for (int index = 0; index < order; ++index) + rhs_sum += factor_log[index]; + const double correction = rhs_sum / (2.0 * order + 2.0); + const double diagonal = order + 2.0; + for (int index = 0; index < order; ++index) + { + factor_log[index] = (factor_log[index] - correction) / diagonal; + factor_log[index] = fmax(-PSD_LOG_FACTOR_LIMIT, fmin(PSD_LOG_FACTOR_LIMIT, factor_log[index])); + } + + slot = start; + for (int column = 0; column < order; ++column) + for (int row = column; row < order; ++row, ++slot) + log_scaling[slot] = factor_log[row] + factor_log[column]; +} + +static void project_psd_scaling(double *scaling, int start, int order, double *factor_log) +{ + const int length = cone_length(CONE_PSD, order); + for (int slot = start; slot < start + length; ++slot) + scaling[slot] = log(fmax(scaling[slot], LOG_SCALING_MIN_ABS)); + project_psd_log_scaling(scaling, start, order, factor_log); + for (int slot = start; slot < start + length; ++slot) + scaling[slot] = exp(scaling[slot]); +} + /* - * Cone-block aggregation follows HPR-SOCP's :phase_taper strategy: + * Part of PDHCG's cone-preserving scaling mode follows HPR-SOCP's :phase_taper strategy: * https://github.com/PolyU-IOR/HPR-SOCP + * + * When that mode is disabled, PSD blocks follow CARDAL's per-element scaling: + * https://github.com/Lhongpei/CARDAL */ -static void apply_cone_preserving_scaling(double *scaling, const cone_blocks_t *cones, cone_scaling_phase_t phase) +static void apply_cone_scaling_adjustment(double *scaling, + const cone_blocks_t *cones, + cone_scaling_phase_t phase, + bool use_cone_preserving_scaling) { + int workspace_order = use_cone_preserving_scaling ? 0 : max_psd_order(cones); + double *factor_log = workspace_order > 0 ? safe_malloc((size_t)workspace_order * sizeof(double)) : NULL; + for (int block = 0; block < cones->num_cones; ++block) { + if (!use_cone_preserving_scaling) + { + if (cones->type[block] == CONE_PSD) + project_psd_scaling(scaling, cones->start_idx[block], cones->v_dim[block], factor_log); + continue; + } + int start = cones->start_idx[block]; int length = cone_block_length(cones, block); double block_max = 0.0; @@ -59,16 +139,18 @@ static void apply_cone_preserving_scaling(double *scaling, const cone_blocks_t * for (int index = start; index < start + length; ++index) scaling[index] = block_scale; } + + free(factor_log); } static double curtis_reid_exp_clamped(double value) { if (isnan(value)) return 1.0; - if (value > CURTIS_REID_LOG_SCALE_LIMIT) - value = CURTIS_REID_LOG_SCALE_LIMIT; - else if (value < -CURTIS_REID_LOG_SCALE_LIMIT) - value = -CURTIS_REID_LOG_SCALE_LIMIT; + if (value > LOG_SCALING_LIMIT) + value = LOG_SCALING_LIMIT; + else if (value < -LOG_SCALING_LIMIT) + value = -LOG_SCALING_LIMIT; return exp(value); } @@ -260,6 +342,14 @@ static void curtis_reid_rescaling(qp_problem_t *problem, const int num_nonzeros = problem->constraint_matrix_num_nonzeros; double *con_rescale = safe_malloc((size_t)num_cons * sizeof(double)); double *var_rescale = safe_malloc((size_t)num_vars * sizeof(double)); + int psd_workspace_order = 0; + if (!use_cone_preserving_scaling) + { + psd_workspace_order = max_psd_order(&problem->cones); + int affine_psd_order = max_psd_order(&problem->affine_cones); + psd_workspace_order = affine_psd_order > psd_workspace_order ? affine_psd_order : psd_workspace_order; + } + double *psd_factor_log = psd_workspace_order > 0 ? safe_malloc((size_t)psd_workspace_order * sizeof(double)) : NULL; for (int row = 0; row < num_cons; ++row) con_rescale[row] = 1.0; @@ -281,7 +371,7 @@ static void curtis_reid_rescaling(qp_problem_t *problem, for (int nz = matrix->row_ptr[row]; nz < matrix->row_ptr[row + 1]; ++nz) { const int col = matrix->col_ind[nz]; - const double log_abs = log(fmax(fabs(matrix->val[nz]), CURTIS_REID_MIN_ABS)); + const double log_abs = log(fmax(fabs(matrix->val[nz]), LOG_SCALING_MIN_ABS)); row_log_abs_sum[row] += log_abs; col_log_abs_sum[col] += log_abs; ++col_count[col]; @@ -304,27 +394,36 @@ static void curtis_reid_rescaling(qp_problem_t *problem, row_log_scale[row] = (end > begin) ? sum / (double)(end - begin) : 0.0; } - if (use_cone_preserving_scaling) + for (int block = 0; block < problem->affine_cones.num_cones; ++block) { - for (int block = 0; block < problem->affine_cones.num_cones; ++block) + if (!use_cone_preserving_scaling) { - int start = problem->affine_cones.start_idx[block]; - int length = cone_block_length(&problem->affine_cones, block); - double block_sum = 0.0; - int block_count = 0; - for (int row = start; row < start + length; ++row) + if (problem->affine_cones.type[block] == CONE_PSD) { - int begin = matrix->row_ptr[row]; - int end = matrix->row_ptr[row + 1]; - block_sum += row_log_abs_sum[row]; - block_count += end - begin; - for (int nz = begin; nz < end; ++nz) - block_sum -= col_log_scale[matrix->col_ind[nz]]; + project_psd_log_scaling(row_log_scale, + problem->affine_cones.start_idx[block], + problem->affine_cones.v_dim[block], + psd_factor_log); } - double block_log_scale = block_count > 0 ? block_sum / (double)block_count : 0.0; - for (int row = start; row < start + length; ++row) - row_log_scale[row] = block_log_scale; + continue; } + + int start = problem->affine_cones.start_idx[block]; + int length = cone_block_length(&problem->affine_cones, block); + double block_sum = 0.0; + int block_count = 0; + for (int row = start; row < start + length; ++row) + { + int begin = matrix->row_ptr[row]; + int end = matrix->row_ptr[row + 1]; + block_sum += row_log_abs_sum[row]; + block_count += end - begin; + for (int nz = begin; nz < end; ++nz) + block_sum -= col_log_scale[matrix->col_ind[nz]]; + } + double block_log_scale = block_count > 0 ? block_sum / (double)block_count : 0.0; + for (int row = start; row < start + length; ++row) + row_log_scale[row] = block_log_scale; } memcpy(col_sum, col_log_abs_sum, (size_t)num_vars * sizeof(double)); @@ -336,28 +435,37 @@ static void curtis_reid_rescaling(qp_problem_t *problem, for (int col = 0; col < num_vars; ++col) col_log_scale[col] = col_count[col] > 0 ? col_sum[col] / (double)col_count[col] : 0.0; - if (use_cone_preserving_scaling) + /* + * Adding c_j = c_B for all j in cone block B gives the exact + * block minimizer below. With block aggregation disabled, PSD + * columns use the diagonal-congruence projection instead. + */ + for (int block = 0; block < problem->cones.num_cones; ++block) { - /* - * Adding c_j = c_B for all j in cone block B gives the exact - * block minimizer below. With cone-preserving scaling disabled, - * the independent column minimizers above are retained. - */ - for (int block = 0; block < problem->cones.num_cones; ++block) + if (!use_cone_preserving_scaling) { - const int start = problem->cones.start_idx[block]; - const int length = cone_block_length(&problem->cones, block); - double block_sum = 0.0; - int block_count = 0; - for (int col = start; col < start + length; ++col) + if (problem->cones.type[block] == CONE_PSD) { - block_sum += col_sum[col]; - block_count += col_count[col]; + project_psd_log_scaling(col_log_scale, + problem->cones.start_idx[block], + problem->cones.v_dim[block], + psd_factor_log); } - const double block_log_scale = block_count > 0 ? block_sum / (double)block_count : 0.0; - for (int col = start; col < start + length; ++col) - col_log_scale[col] = block_log_scale; + continue; + } + + const int start = problem->cones.start_idx[block]; + const int length = cone_block_length(&problem->cones, block); + double block_sum = 0.0; + int block_count = 0; + for (int col = start; col < start + length; ++col) + { + block_sum += col_sum[col]; + block_count += col_count[col]; } + const double block_log_scale = block_count > 0 ? block_sum / (double)block_count : 0.0; + for (int col = start; col < start + length; ++col) + col_log_scale[col] = block_log_scale; } } @@ -383,6 +491,7 @@ static void curtis_reid_rescaling(qp_problem_t *problem, free(con_rescale); free(var_rescale); + free(psd_factor_log); } static void ruiz_rescaling(qp_problem_t *problem, @@ -432,11 +541,9 @@ static void ruiz_rescaling(qp_problem_t *problem, for (int i = 0; i < num_cons; ++i) con_rescale[i] = (con_rescale[i] < SCALING_EPSILON) ? 1.0 : sqrt(con_rescale[i]); - if (use_cone_preserving_scaling) - { - apply_cone_preserving_scaling(var_rescale, &problem->cones, CONE_SCALING_RUIZ); - apply_cone_preserving_scaling(con_rescale, &problem->affine_cones, CONE_SCALING_RUIZ); - } + apply_cone_scaling_adjustment(var_rescale, &problem->cones, CONE_SCALING_RUIZ, use_cone_preserving_scaling); + apply_cone_scaling_adjustment( + con_rescale, &problem->affine_cones, CONE_SCALING_RUIZ, use_cone_preserving_scaling); scale_problem(problem, con_rescale, var_rescale); for (int i = 0; i < num_vars; ++i) @@ -477,11 +584,10 @@ static void pock_chambolle_rescaling(qp_problem_t *problem, for (int i = 0; i < num_cons; ++i) con_rescale[i] = (con_rescale[i] < SCALING_EPSILON) ? 1.0 : sqrt(con_rescale[i]); - if (use_cone_preserving_scaling) - { - apply_cone_preserving_scaling(var_rescale, &problem->cones, CONE_SCALING_POCK_CHAMBOLLE); - apply_cone_preserving_scaling(con_rescale, &problem->affine_cones, CONE_SCALING_POCK_CHAMBOLLE); - } + apply_cone_scaling_adjustment( + var_rescale, &problem->cones, CONE_SCALING_POCK_CHAMBOLLE, use_cone_preserving_scaling); + apply_cone_scaling_adjustment( + con_rescale, &problem->affine_cones, CONE_SCALING_POCK_CHAMBOLLE, use_cone_preserving_scaling); scale_problem(problem, con_rescale, var_rescale); for (int i = 0; i < num_vars; ++i) diff --git a/src/presolve_wrapper.c b/src/presolve_wrapper.c index cc3c922..2571251 100644 --- a/src/presolve_wrapper.c +++ b/src/presolve_wrapper.c @@ -332,6 +332,11 @@ static int append_fixed_cone_rows(const qp_problem_t *source, PreFOSInputAdapter return 1; } +static size_t psd_column_major_slot(size_t order, size_t row, size_t column) +{ + return column * (2 * order - column + 1) / 2 + row - column; +} + static int initialize_domains(const qp_problem_t *source, PreFOSInputAdapter *adapter) { size_t n = (size_t)source->num_variables; @@ -414,6 +419,19 @@ static int initialize_domains(const qp_problem_t *source, PreFOSInputAdapter *ad target->indices[1] = start + 1; target->indices[2] = start + 2; break; + case CONE_PSD: + { + size_t row; + size_t column; + size_t position = 0; + size_t order = (size_t)vector_dimension; + target->type = PREFOS_CONE_POSITIVE_SEMIDEFINITE; + target->matrix_order = order; + for (row = 0; row < order; ++row) + for (column = 0; column <= row; ++column) + target->indices[position++] = start + (int)psd_column_major_slot(order, row, column); + break; + } default: free(owner); return 0; @@ -505,12 +523,14 @@ static int convert_cone_to_pdhcg(const PreFOSConeBlock *source, cone_blocks_t *t size_t dimension = source->dimension; size_t i; int start; - if (!source->indices || dimension < 2) + if (!source->indices || dimension == 0) return 0; switch (source->type) { case PREFOS_CONE_SECOND_ORDER: + if (dimension < 2) + return 0; start = source->indices[1]; for (i = 1; i < dimension; ++i) if (source->indices[i] != start + (int)i - 1) @@ -543,6 +563,30 @@ static int convert_cone_to_pdhcg(const PreFOSConeBlock *source, cone_blocks_t *t if (source->type == PREFOS_CONE_POWER) target->power_alpha[cone] = source->power_alpha; break; + case PREFOS_CONE_POSITIVE_SEMIDEFINITE: + { + size_t row; + size_t column; + size_t position = 0; + size_t order = source->matrix_order; + if (order == 0 || order > (size_t)INT_MAX || order * (order + 1) / 2 != dimension) + return 0; + start = source->indices[0]; + if (start < 0) + return 0; + for (row = 0; row < order; ++row) + { + for (column = 0; column <= row; ++column) + { + size_t slot = psd_column_major_slot(order, row, column); + if ((long long)source->indices[position++] != (long long)start + (long long)slot) + return 0; + } + } + target->type[cone] = CONE_PSD; + target->v_dim[cone] = (int)order; + break; + } default: return 0; } diff --git a/src/solver_state.cu b/src/solver_state.cu index 79be4b0..2428c6d 100644 --- a/src/solver_state.cu +++ b/src/solver_state.cu @@ -19,7 +19,8 @@ limitations under the License. #include "distributed_conic.h" #include "internal_types.h" #include "pdhcg.h" -#include "pdhcg_kernels.cuh" +#include "pdhcg_kernels.h" +#include "pdhcg_psd_cone.h" #include "pdhg_core_op.h" #include "preconditioner.h" #include "solver.h" @@ -279,7 +280,6 @@ static void initialize_quadratic_obj_term(pdhg_solver_state_t *state, const proc static void initialize_inner_solver(pdhg_solver_state_t *state, const pdhg_parameters_t *params) { state->inner_solver = (inner_solver_t *)safe_calloc(1, sizeof(inner_solver_t)); - state->inner_solver->has_inner_loop = false; int iteration_limit = params->inner_solver_parameters.iteration_limit; double initial_tol = params->inner_solver_parameters.initial_tolerance; @@ -303,91 +303,69 @@ static void initialize_inner_solver(pdhg_solver_state_t *state, const pdhg_param min_tol = 1e-9; } - if (!(state->quadratic_objective_term->quad_obj_type == PDHCG_NON_Q || - state->quadratic_objective_term->quad_obj_type == PDHCG_DIAG_Q)) - { - ALLOC_ZERO(state->inner_solver->primal_buffer, state->num_variables * sizeof(double)); - ALLOC_ZERO(state->inner_solver->dual_buffer, state->num_constraints * sizeof(double)); - } + quad_obj_type_t objective_type = state->quadratic_objective_term->quad_obj_type; + if (objective_type == PDHCG_NON_Q || (objective_type == PDHCG_DIAG_Q && !state->cones.has_psd_cones)) + return; - switch (state->quadratic_objective_term->quad_obj_type) + /* A nonuniform diagonal metric has no one-EVD PSD prox, so diagonal-Q + models with PSD variables use the existing projected BB solve. */ + ALLOC_ZERO(state->inner_solver->primal_buffer, state->num_variables * sizeof(double)); + ALLOC_ZERO(state->inner_solver->dual_buffer, state->num_constraints * sizeof(double)); + state->inner_solver->bb_step_size = (bb_step_size_t *)safe_calloc(1, sizeof(bb_step_size_t)); + ALLOC_ZERO(state->inner_solver->bb_step_size->gradient, state->num_variables * sizeof(double)); + ALLOC_ZERO(state->inner_solver->bb_step_size->direction, state->num_variables * sizeof(double)); + ALLOC_ZERO(state->inner_solver->bb_step_size->scalar_buffer, 4 * sizeof(double)); + + state->inner_solver->iteration_limit = iteration_limit; + state->inner_solver->tol = initial_tol; + state->inner_solver->min_tol = min_tol; + + state->inner_solver->bb_step_size->precond_enabled = params->diag_jacobi_precond && !state->cones.has_psd_cones; + if (state->inner_solver->bb_step_size->precond_enabled) { - case PDHCG_NON_Q: - break; - case PDHCG_DIAG_Q: - break; - case PDHCG_SPARSE_Q: - case PDHCG_LOW_RANK_Q: - case PDHCG_LOW_RANK_PLUS_SPARSE_Q: - state->inner_solver->has_inner_loop = true; - state->inner_solver->bb_step_size = (bb_step_size_t *)safe_calloc(1, sizeof(bb_step_size_t)); - ALLOC_ZERO(state->inner_solver->bb_step_size->gradient, state->num_variables * sizeof(double)); - ALLOC_ZERO(state->inner_solver->bb_step_size->direction, state->num_variables * sizeof(double)); - ALLOC_ZERO(state->inner_solver->bb_step_size->scalar_buffer, 4 * sizeof(double)); - - state->inner_solver->iteration_limit = iteration_limit; - state->inner_solver->tol = initial_tol; - state->inner_solver->min_tol = min_tol; - - state->inner_solver->bb_step_size->precond_enabled = params->diag_jacobi_precond; - if (params->diag_jacobi_precond) + int n = state->num_variables; + ALLOC_ZERO(state->inner_solver->bb_step_size->diag_h_static, n * sizeof(double)); + ALLOC_ZERO(state->inner_solver->bb_step_size->m_diag, n * sizeof(double)); + ALLOC_ZERO(state->inner_solver->bb_step_size->m_inv, n * sizeof(double)); + ALLOC_ZERO(state->inner_solver->bb_step_size->Ms_buffer, n * sizeof(double)); + state->inner_solver->bb_step_size->cached_inv_tau = -1.0; + state->inner_solver->bb_step_size->tol_scale = 1.0; + + if (objective_type == PDHCG_SPARSE_Q || objective_type == PDHCG_LOW_RANK_PLUS_SPARSE_Q) + { + cu_sparse_matrix_csr_t *Q = state->quadratic_objective_term->objective_sparse_matrix; + compute_csr_diag_kernel<<num_blocks_primal, THREADS_PER_BLOCK>>>( + Q->row_ptr, Q->col_ind, Q->val, state->inner_solver->bb_step_size->diag_h_static, n); + CUDA_CHECK(cudaGetLastError()); + } + + if (objective_type == PDHCG_LOW_RANK_Q || objective_type == PDHCG_LOW_RANK_PLUS_SPARSE_Q) + { + cu_sparse_matrix_csr_t *Rt = state->quadratic_objective_term->objective_lowrank_matrix_t; + double *out = state->inner_solver->bb_step_size->Ms_buffer; + int mtype = state->quadratic_objective_term->lowrank_middle_type; + if (mtype == 1) { - int n = state->num_variables; - ALLOC_ZERO(state->inner_solver->bb_step_size->diag_h_static, n * sizeof(double)); - ALLOC_ZERO(state->inner_solver->bb_step_size->m_diag, n * sizeof(double)); - ALLOC_ZERO(state->inner_solver->bb_step_size->m_inv, n * sizeof(double)); - ALLOC_ZERO(state->inner_solver->bb_step_size->Ms_buffer, n * sizeof(double)); - state->inner_solver->bb_step_size->cached_inv_tau = -1.0; - state->inner_solver->bb_step_size->tol_scale = 1.0; - - if (state->quadratic_objective_term->quad_obj_type == PDHCG_SPARSE_Q || - state->quadratic_objective_term->quad_obj_type == PDHCG_LOW_RANK_PLUS_SPARSE_Q) - { - cu_sparse_matrix_csr_t *Q = state->quadratic_objective_term->objective_sparse_matrix; - compute_csr_diag_kernel<<num_blocks_primal, THREADS_PER_BLOCK>>>( - Q->row_ptr, Q->col_ind, Q->val, state->inner_solver->bb_step_size->diag_h_static, n); - CUDA_CHECK(cudaGetLastError()); - } - - if (state->quadratic_objective_term->quad_obj_type == PDHCG_LOW_RANK_Q || - state->quadratic_objective_term->quad_obj_type == PDHCG_LOW_RANK_PLUS_SPARSE_Q) - { - cu_sparse_matrix_csr_t *Rt = state->quadratic_objective_term->objective_lowrank_matrix_t; - double *out = state->inner_solver->bb_step_size->Ms_buffer; - int mtype = state->quadratic_objective_term->lowrank_middle_type; - if (mtype == 1) - { - compute_csr_row_sq_norm_weighted_kernel<<num_blocks_primal, THREADS_PER_BLOCK>>>( - Rt->row_ptr, Rt->col_ind, Rt->val, state->quadratic_objective_term->d_middle_diag, out, n); - } - else if (mtype == 2) - { - int rank = state->quadratic_objective_term->num_rank_lowrank_obj; - compute_csr_row_quad_form_dense_kernel<<num_blocks_primal, THREADS_PER_BLOCK>>>( - Rt->row_ptr, - Rt->col_ind, - Rt->val, - state->quadratic_objective_term->d_middle_dense, - rank, - out, - n); - } - else - { - compute_csr_row_sq_norm_kernel<<num_blocks_primal, THREADS_PER_BLOCK>>>( - Rt->row_ptr, Rt->val, out, n); - } - CUDA_CHECK(cudaGetLastError()); - const double one = 1.0; - CUBLAS_CHECK(cublasDaxpy( - state->blas_handle, n, &one, out, 1, state->inner_solver->bb_step_size->diag_h_static, 1)); - CUDA_CHECK(cudaMemset(out, 0, n * sizeof(double))); - } + compute_csr_row_sq_norm_weighted_kernel<<num_blocks_primal, THREADS_PER_BLOCK>>>( + Rt->row_ptr, Rt->col_ind, Rt->val, state->quadratic_objective_term->d_middle_diag, out, n); } - break; - default: - fprintf(stderr, "Error: Unknown Quadratic Objective Type detected.\n"); - exit(EXIT_FAILURE); + else if (mtype == 2) + { + int rank = state->quadratic_objective_term->num_rank_lowrank_obj; + compute_csr_row_quad_form_dense_kernel<<num_blocks_primal, THREADS_PER_BLOCK>>>( + Rt->row_ptr, Rt->col_ind, Rt->val, state->quadratic_objective_term->d_middle_dense, rank, out, n); + } + else + { + compute_csr_row_sq_norm_kernel<<num_blocks_primal, THREADS_PER_BLOCK>>>( + Rt->row_ptr, Rt->val, out, n); + } + CUDA_CHECK(cudaGetLastError()); + const double one = 1.0; + CUBLAS_CHECK( + cublasDaxpy(state->blas_handle, n, &one, out, 1, state->inner_solver->bb_step_size->diag_h_static, 1)); + CUDA_CHECK(cudaMemset(out, 0, n * sizeof(double))); + } } } @@ -446,7 +424,7 @@ pick_cone_proj_method(const cone_blocks_t *cones, int cone, const double *coordi { cone_type_t type = cones->type[cone]; int v_dim = cones->v_dim[cone]; - if (type == CONE_EXPONENTIAL || type == CONE_POWER) + if (type == CONE_EXPONENTIAL || type == CONE_POWER || type == CONE_PSD) return PROJ_METHOD_THREAD; if (v_dim < 32) return PROJ_METHOD_THREAD; @@ -514,6 +492,7 @@ initialize_cone_layout(cone_runtime_t *runtime, const cone_blocks_t *cones, cons bucket_count[cones->type[i]][methods[i]]++; for (int method = 0; method < NUM_PROJ_METHODS; ++method) runtime->has_power_cones |= bucket_count[CONE_POWER][method] > 0; + runtime->has_psd_cones = bucket_count[CONE_PSD][PROJ_METHOD_THREAD] > 0; cone_bucket_t buckets_tmp[NUM_CONE_TYPES * NUM_PROJ_METHODS]; int num_buckets = 0; @@ -524,7 +503,7 @@ initialize_cone_layout(cone_runtime_t *runtime, const cone_blocks_t *cones, cons for (int m = 0; m < NUM_PROJ_METHODS; ++m) { bucket_offset[t][m] = offset; - if (bucket_count[t][m] > 0) + if (bucket_count[t][m] > 0 && t != CONE_PSD) { buckets_tmp[num_buckets].type = (cone_type_t)t; buckets_tmp[num_buckets].method = (cone_proj_method_t)m; @@ -537,8 +516,11 @@ initialize_cone_layout(cone_runtime_t *runtime, const cone_blocks_t *cones, cons } runtime->num_buckets = num_buckets; - runtime->buckets = (cone_bucket_t *)safe_malloc((size_t)num_buckets * sizeof(cone_bucket_t)); - memcpy(runtime->buckets, buckets_tmp, (size_t)num_buckets * sizeof(cone_bucket_t)); + if (num_buckets > 0) + { + runtime->buckets = (cone_bucket_t *)safe_malloc((size_t)num_buckets * sizeof(cone_bucket_t)); + memcpy(runtime->buckets, buckets_tmp, (size_t)num_buckets * sizeof(cone_bucket_t)); + } size_t cb = (size_t)K * sizeof(int); int *start_perm = (int *)safe_malloc(cb); @@ -570,6 +552,13 @@ initialize_cone_layout(cone_runtime_t *runtime, const cone_blocks_t *cones, cons CUDA_CHECK(cudaMemcpy(runtime->power_alpha, alpha_perm, ab, cudaMemcpyHostToDevice)); free(alpha_perm); } + if (runtime->has_psd_cones) + { + int psd_offset = bucket_offset[CONE_PSD][PROJ_METHOD_THREAD]; + int psd_count = bucket_count[CONE_PSD][PROJ_METHOD_THREAD]; + runtime->psd = + create_psd_projection_runtime(start_perm + psd_offset, vdim_perm + psd_offset, psd_count, psd_offset); + } free(start_perm); free(vdim_perm); free(methods); @@ -614,14 +603,21 @@ static void initialize_cone_runtime(pdhg_solver_state_t *state, size_t vb = (size_t)state->num_variables * sizeof(double); if (qt != PDHCG_NON_Q) CUDA_CHECK(cudaMalloc(&state->cones.effective_objective_gradient, vb)); - if (qt == PDHCG_SPARSE_Q || qt == PDHCG_LOW_RANK_Q || qt == PDHCG_LOW_RANK_PLUS_SPARSE_Q) - CUDA_CHECK(cudaMalloc(&state->cones.bb_primal_snapshot, vb)); } initialize_cone_layout(&state->cones, &working_problem->cones, rescale_info->var_rescale); double global_has_power_cones = state->cones.has_power_cones ? 1.0 : 0.0; pdhcg_all_reduce_scalar(state->grid_context, &global_has_power_cones, PDHCG_OP_MAX, PDHCG_SCOPE_ROW, false); state->cones.has_power_cones = global_has_power_cones != 0.0; + double global_has_psd_cones = state->cones.has_psd_cones ? 1.0 : 0.0; + pdhcg_all_reduce_scalar(state->grid_context, &global_has_psd_cones, PDHCG_OP_MAX, PDHCG_SCOPE_ROW, false); + state->cones.has_psd_cones = global_has_psd_cones != 0.0; + if (state->has_variable_cones) + { + quad_obj_type_t qt = rescale_info->processed_problem ? rescale_info->processed_problem->quad_type : PDHCG_NON_Q; + if (qt != PDHCG_NON_Q && (qt != PDHCG_DIAG_Q || state->cones.has_psd_cones)) + CUDA_CHECK(cudaMalloc(&state->cones.bb_primal_snapshot, (size_t)state->num_variables * sizeof(double))); + } bool has_affine_cones = working_problem->affine_cones.num_cones > 0 || state->affine_cones.split != NULL || pdhcg_get_global_num_affine_cones(state->grid_context) > 0; @@ -667,9 +663,9 @@ static void initialize_cone_runtime(pdhg_solver_state_t *state, CUDA_CHECK(cudaMemcpy(dst + z_idx, &z_val, sizeof(double), cudaMemcpyHostToDevice)); } } - else if (cones->type[i] == CONE_EXPONENTIAL || cones->type[i] == CONE_POWER) + else if (cones->type[i] == CONE_EXPONENTIAL || cones->type[i] == CONE_POWER || cones->type[i] == CONE_PSD) { - /* Rely on the ALLOC_ZERO default (0, 0, 0), which is in-cone for both. */ + /* The zero vector is feasible for these cones. */ } else { @@ -1254,6 +1250,7 @@ void pdhg_solver_state_free(pdhg_solver_state_t *state) CUDA_CHECK(cudaFree(runtime->effective_objective_gradient)); if (runtime->bb_primal_snapshot) CUDA_CHECK(cudaFree(runtime->bb_primal_snapshot)); + free_psd_projection_runtime(runtime->psd); } free_split_cones(state); diff --git a/src/utils.cu b/src/utils.cu index e9bab4d..6b84d93 100644 --- a/src/utils.cu +++ b/src/utils.cu @@ -14,7 +14,7 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ -#include "pdhcg_kernels.cuh" +#include "pdhcg_kernels.h" #include "solver_state.h" #include "spmv_backend.h" #include "utils.h" diff --git a/test/test_cone_permutation.c b/test/test_cone_permutation.c index b89bc19..9d03fb1 100644 --- a/test/test_cone_permutation.c +++ b/test/test_cone_permutation.c @@ -1,3 +1,4 @@ +#include "cone_utils.h" #include "pdhcg.h" #include "permute.h" #include @@ -17,9 +18,9 @@ int main(void) { - const int n = 16; + const int n = 21; static const int scalar_row_ptr[] = {0, 0}; - static const int affine_row_ptr[] = {0, 0, 0, 0, 0, 0, 0}; + static const int affine_row_ptr[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; static const int col_ind[] = {0}; static const double values[] = {0.0}; double objective[n]; @@ -35,7 +36,7 @@ int main(void) A.data.csr.col_ind = col_ind; A.data.csr.vals = values; matrix_desc_t F = {0}; - F.m = 6; + F.m = 9; F.n = n; F.fmt = matrix_csr; F.data.csr.nnz = 0; @@ -44,7 +45,7 @@ int main(void) F.data.csr.vals = values; const double constraint_lower[] = {0.0}; const double constraint_upper[] = {0.0}; - static const double affine_offset[] = {10.0, 11.0, 12.0, 20.0, 21.0, 22.0}; + static const double affine_offset[] = {10.0, 11.0, 12.0, 20.0, 21.0, 22.0, 30.0, 31.0, 32.0}; const char fixed0[] = {0, 0, 1}; const char fixed1[] = {1, 0, 0}; const char fixed2[] = {0, 1, 0}; @@ -54,10 +55,12 @@ int main(void) {.type = CONE_POWER, .start_idx = 5, .v_dim = 1, .power_alpha = 0.3, .is_fixed = fixed1}, {.type = CONE_EXPONENTIAL, .start_idx = 9, .v_dim = 1, .power_alpha = 0.0, .is_fixed = fixed2}, {.type = CONE_ROTATED_SOC, .start_idx = 12, .v_dim = 2, .power_alpha = 0.0, .is_fixed = fixed3}, + {.type = CONE_PSD, .start_idx = 17, .v_dim = 2}, }; const cone_spec_t affine_cones[] = { {.type = CONE_STANDARD_SOC, .start_idx = 0, .v_dim = 1}, {.type = CONE_EXPONENTIAL, .start_idx = 3, .v_dim = 1}, + {.type = CONE_PSD, .start_idx = 6, .v_dim = 2}, }; qp_problem_t *problem = create_qp_problem(objective, @@ -70,29 +73,27 @@ int main(void) NULL, NULL, NULL, - 4, + 5, cones, &F, affine_offset, - 2, + 3, affine_cones); CHECK(problem != NULL); int permutation[n]; - int row_permutation[] = {0, 1, 2, 3, 4, 5, 6}; + int row_permutation[] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9}; srand(7); generate_cone_aware_permutation(problem, FULL_RANDOM_PERMUTATION, 1, permutation); CHECK(validate_cone_permutation(problem, permutation)); qp_problem_t *permuted = permute_problem_return_new(problem, row_permutation, permutation); CHECK(permuted != NULL); - for (int cone = 0; cone < 4; ++cone) + for (int cone = 0; cone < 5; ++cone) { int old_start = problem->cones.start_idx[cone]; int new_start = permuted->cones.start_idx[cone]; - int length = (problem->cones.type[cone] == CONE_EXPONENTIAL || problem->cones.type[cone] == CONE_POWER) - ? 3 - : problem->cones.v_dim[cone] + 2; + int length = cone_block_length(&problem->cones, cone); CHECK(permuted->cones.type[cone] == problem->cones.type[cone]); if (problem->cones.type[cone] == CONE_POWER) CHECK(permuted->cones.power_alpha[cone] == problem->cones.power_alpha[cone]); @@ -108,7 +109,7 @@ int main(void) generate_cone_aware_permutation(problem, BLOCK_RANDOM_PERMUTATION, 2, block_permutation); CHECK(validate_cone_permutation(problem, block_permutation)); - int row_cone_permutation[7]; + int row_cone_permutation[10]; int identity_columns[n]; for (int col = 0; col < n; ++col) identity_columns[col] = col; @@ -117,15 +118,16 @@ int main(void) CHECK(validate_affine_cone_row_permutation(problem, row_cone_permutation)); qp_problem_t *affine_permuted = permute_problem_return_new(problem, row_cone_permutation, identity_columns); CHECK(affine_permuted != NULL); - for (int cone = 0; cone < 2; ++cone) + for (int cone = 0; cone < 3; ++cone) { int old_start = problem->affine_cones.start_idx[cone]; int new_start = affine_permuted->affine_cones.start_idx[cone]; - for (int slot = 0; slot < 3; ++slot) + int length = cone_block_length(&problem->affine_cones, cone); + for (int slot = 0; slot < length; ++slot) CHECK(affine_permuted->affine_cone_offset[new_start + slot] == problem->affine_cone_offset[old_start + slot]); } - int invalid_rows[] = {1, 0, 2, 3, 4, 5, 6}; + int invalid_rows[] = {1, 0, 2, 3, 4, 5, 6, 7, 8, 9}; CHECK(!validate_affine_cone_row_permutation(problem, invalid_rows)); CHECK(!permute_problem(problem, invalid_rows, identity_columns)); int duplicate_columns[n]; diff --git a/test/test_curtis_reid_scaling.c b/test/test_curtis_reid_scaling.c index 0104099..b9a87cd 100644 --- a/test/test_curtis_reid_scaling.c +++ b/test/test_curtis_reid_scaling.c @@ -208,7 +208,7 @@ static void test_cone_block_scaling(void) qp_problem_free(problem); } -static qp_problem_t *make_phase_taper_problem(int length, int affine) +static qp_problem_t *make_diagonal_cone_scaling_problem(int length, int affine, cone_type_t cone_type, int v_dim) { int *row_ptr = (int *)malloc((size_t)(length + 1) * sizeof(int)); int *col_ind = (int *)malloc((size_t)length * sizeof(int)); @@ -239,9 +239,9 @@ static qp_problem_t *make_phase_taper_problem(int length, int affine) diagonal.data.csr.col_ind = col_ind; diagonal.data.csr.vals = values; cone_spec_t cone = { - .type = CONE_STANDARD_SOC, + .type = cone_type, .start_idx = 0, - .v_dim = length - 2, + .v_dim = v_dim, }; qp_problem_t *problem = NULL; @@ -278,7 +278,7 @@ static pdhg_parameters_t phase_taper_parameters(int ruiz) static void test_phase_taper_case(int length, int affine, int ruiz) { - qp_problem_t *problem = make_phase_taper_problem(length, affine); + qp_problem_t *problem = make_diagonal_cone_scaling_problem(length, affine, CONE_STANDARD_SOC, length - 2); if (!problem) { fprintf( @@ -310,6 +310,128 @@ static void test_phase_taper_case(int length, int affine, int ruiz) qp_problem_free(problem); } +static qp_problem_t *make_psd_congruence_scaling_problem(int affine) +{ + const int order = 3; + const int length = order * (order + 1) / 2; + const int rows = affine ? length : 1; + const int columns = affine ? 1 : length; + int *row_ptr = (int *)malloc((size_t)(rows + 1) * sizeof(int)); + int *col_ind = (int *)malloc((size_t)length * sizeof(int)); + double *values = (double *)malloc((size_t)length * sizeof(double)); + double *objective = (double *)calloc((size_t)columns, sizeof(double)); + double *bounds = (double *)calloc((size_t)rows, sizeof(double)); + if (!row_ptr || !col_ind || !values || !objective || !bounds) + { + free(row_ptr); + free(col_ind); + free(values); + free(objective); + free(bounds); + return NULL; + } + + if (affine) + { + for (int row = 0; row <= rows; ++row) + row_ptr[row] = row; + for (int index = 0; index < length; ++index) + col_ind[index] = 0; + } + else + { + row_ptr[0] = 0; + row_ptr[1] = length; + for (int index = 0; index < length; ++index) + col_ind[index] = index; + } + for (int index = 0; index < length; ++index) + values[index] = (double)(index + 1) * (double)(index + 1); + + matrix_desc_t matrix = {0}; + matrix.m = rows; + matrix.n = columns; + matrix.fmt = matrix_csr; + matrix.data.csr.nnz = length; + matrix.data.csr.row_ptr = row_ptr; + matrix.data.csr.col_ind = col_ind; + matrix.data.csr.vals = values; + const cone_spec_t cone = { + .type = CONE_PSD, + .start_idx = 0, + .v_dim = order, + }; + + qp_problem_t *problem = affine + ? create_qp_problem( + objective, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, 0, NULL, &matrix, NULL, 1, &cone) + : create_qp_problem( + objective, NULL, NULL, NULL, &matrix, bounds, bounds, NULL, NULL, NULL, 1, &cone, NULL, NULL, 0, NULL); + + free(row_ptr); + free(col_ind); + free(values); + free(objective); + free(bounds); + return problem; +} + +static void test_psd_scaling_case(int affine, int method) +{ + qp_problem_t *problem = make_psd_congruence_scaling_problem(affine); + if (!problem) + { + fprintf(stderr, "failed to create %s PSD scaling problem\n", affine ? "affine" : "variable"); + ++failures; + return; + } + + pdhg_parameters_t params; + if (method == 0) + params = curtis_reid_only_parameters(); + else + params = phase_taper_parameters(method == 1); + params.use_cone_preserving_scaling = false; + + rescale_info_t *info = rescale_problem(¶ms, problem); + if (!info) + { + fprintf(stderr, + "%s PSD scaling returned NULL\n", + method == 0 ? "Curtis-Reid" + : method == 1 ? "Ruiz" + : "Pock-Chambolle"); + ++failures; + qp_problem_free(problem); + return; + } + + const double *scaling = affine ? info->con_rescale : info->var_rescale; + check_close("PSD (1,0) congruence scale", scaling[1] * scaling[1], scaling[0] * scaling[3], 1e-12); + check_close("PSD (2,0) congruence scale", scaling[2] * scaling[2], scaling[0] * scaling[5], 1e-12); + check_close("PSD (2,1) congruence scale", scaling[4] * scaling[4], scaling[3] * scaling[5], 1e-12); + if (fabs(log(scaling[0] / scaling[5])) < 1e-8) + { + fprintf(stderr, + "%s %s PSD scaling unexpectedly remained uniform\n", + affine ? "affine" : "variable", + method == 0 ? "Curtis-Reid" + : method == 1 ? "Ruiz" + : "Pock-Chambolle"); + ++failures; + } + + rescale_info_free(info); + qp_problem_free(problem); +} + +static void test_psd_diagonal_congruence_scaling(void) +{ + for (int affine = 0; affine <= 1; ++affine) + for (int method = 0; method < 3; ++method) + test_psd_scaling_case(affine, method); +} + static void test_phase_taper_scaling(void) { for (int length = 8; length <= 9; ++length) @@ -340,5 +462,6 @@ int main(void) test_plain_scaling(); test_cone_block_scaling(); test_phase_taper_scaling(); + test_psd_diagonal_congruence_scaling(); return failures == 0 ? 0 : 1; } diff --git a/test/test_distributed_psd.c b/test/test_distributed_psd.c new file mode 100644 index 0000000..2b62848 --- /dev/null +++ b/test/test_distributed_psd.c @@ -0,0 +1,137 @@ +#include "pdhcg.h" + +#include +#include +#include + +#ifdef PDHCG_COMPILE_DISTRIBUTED +#include + +static qp_problem_t *make_variable_psd_problem(void) +{ + const double sqrt_two = 1.41421356237309504880; + static const int row_ptr[] = {0, 1, 2}; + static const int col_ind[] = {1, 2}; + static const double values[] = {1.0, 1.0}; + double rhs[] = {1.0, 2.0 * sqrt_two}; + static const double objective[] = {0.0, 0.0, 0.0, 1.0, 0.0, 0.0}; + static const double var_lb[] = {0.0, -INFINITY, -INFINITY, -INFINITY, 0.0, 0.0}; + static const double var_ub[] = {0.0, INFINITY, INFINITY, INFINITY, 0.0, 0.0}; + matrix_desc_t A = {0}; + A.m = 2; + A.n = 6; + A.fmt = matrix_csr; + A.data.csr.nnz = 2; + A.data.csr.row_ptr = row_ptr; + A.data.csr.col_ind = col_ind; + A.data.csr.vals = values; + cone_spec_t cone = {.type = CONE_PSD, .start_idx = 1, .v_dim = 2}; + return create_qp_problem( + objective, NULL, NULL, NULL, &A, rhs, rhs, var_lb, var_ub, NULL, 1, &cone, NULL, NULL, 0, NULL); +} + +static qp_problem_t *make_affine_psd_problem(void) +{ + const double sqrt_two = 1.41421356237309504880; + static const int a_row_ptr[] = {0, 0}; + static const double scalar_rhs[] = {0.0}; + static const int f_row_ptr[] = {0, 1, 1, 1}; + static const int f_col_ind[] = {0}; + static const double f_values[] = {1.0}; + double offset[] = {0.0, sqrt_two, 1.0}; + static const double objective[] = {1.0}; + matrix_desc_t A = {0}; + matrix_desc_t F = {0}; + A.m = 1; + A.n = 1; + A.fmt = matrix_csr; + A.data.csr.row_ptr = a_row_ptr; + F.m = 3; + F.n = 1; + F.fmt = matrix_csr; + F.data.csr.nnz = 1; + F.data.csr.row_ptr = f_row_ptr; + F.data.csr.col_ind = f_col_ind; + F.data.csr.vals = f_values; + cone_spec_t cone = {.type = CONE_PSD, .start_idx = 0, .v_dim = 2}; + return create_qp_problem( + objective, NULL, NULL, NULL, &A, scalar_rhs, scalar_rhs, NULL, NULL, NULL, 0, NULL, &F, offset, 1, &cone); +} + +static void configure(pdhg_parameters_t *params, int row_dims, int col_dims) +{ + set_default_parameters(params); + params->verbose = 0; + params->grid_size.decided = true; + params->grid_size.row_dims = row_dims; + params->grid_size.col_dims = col_dims; + params->partition_method = UNIFORM_PARTITION; + params->permute_method = NO_PERMUTATION; + params->presolve = false; + params->termination_evaluation_frequency = 10; + params->termination_criteria.eps_optimal_relative = 1e-7; + params->termination_criteria.eps_feasible_relative = 1e-7; + params->termination_criteria.time_sec_limit = 30.0; +} + +int main(int argc, char **argv) +{ + MPI_Init(&argc, &argv); + int rank = 0; + int size = 0; + MPI_Comm_rank(MPI_COMM_WORLD, &rank); + MPI_Comm_size(MPI_COMM_WORLD, &size); + if (size != 2) + { + if (rank == 0) + fprintf(stderr, "test_distributed_psd requires exactly two MPI ranks\n"); + MPI_Finalize(); + return 77; + } + + int failed = 0; + pdhg_parameters_t params; + configure(¶ms, 1, 2); + qp_problem_t *problem = rank == 0 ? make_variable_psd_problem() : NULL; + pdhcg_result_t *result = solve_qp_problem_distributed(¶ms, problem); + if (rank == 0) + { + const double sqrt_two = 1.41421356237309504880; + if (!result || result->termination_reason != TERMINATION_REASON_OPTIMAL || + fabs(result->primal_solution[1] - 1.0) > 8e-4 || fabs(result->primal_solution[2] - 2.0 * sqrt_two) > 8e-4 || + fabs(result->primal_solution[3] - 4.0) > 1e-3) + { + fprintf(stderr, "distributed variable PSD solve returned an incorrect solution\n"); + failed = 1; + } + pdhcg_result_free(result); + qp_problem_free(problem); + } + + configure(¶ms, 2, 1); + problem = rank == 0 ? make_affine_psd_problem() : NULL; + result = solve_qp_problem_distributed(¶ms, problem); + if (rank == 0) + { + if (!result || result->termination_reason != TERMINATION_REASON_OPTIMAL || + fabs(result->primal_solution[0] - 1.0) > 1e-3 || result->relative_primal_residual > 3e-6 || + result->relative_dual_residual > 3e-6) + { + fprintf(stderr, "distributed affine PSD solve returned an incorrect solution\n"); + failed = 1; + } + pdhcg_result_free(result); + qp_problem_free(problem); + } + + MPI_Bcast(&failed, 1, MPI_INT, 0, MPI_COMM_WORLD); + MPI_Finalize(); + return failed; +} + +#else +int main(void) +{ + return 0; +} +#endif diff --git a/test/test_fixed_cone_sections.cu b/test/test_fixed_cone_sections.cu index 003ded7..0fe5b62 100644 --- a/test/test_fixed_cone_sections.cu +++ b/test/test_fixed_cone_sections.cu @@ -1,5 +1,10 @@ #include "internal_types.h" -#include "pdhcg_kernels.cuh" +#include "pdhcg_affine_cone_kernels.h" +#include "pdhcg_cone_common_kernels.h" +#include "pdhcg_exp_cone_kernels.h" +#include "pdhcg_kernels.h" +#include "pdhcg_rsoc_cone_kernels.h" +#include "pdhcg_soc_cone_kernels.h" #include "pdhcg_types.h" #include diff --git a/test/test_large_rsoc_projection.cu b/test/test_large_rsoc_projection.cu index 1ebc5ea..edc923b 100644 --- a/test/test_large_rsoc_projection.cu +++ b/test/test_large_rsoc_projection.cu @@ -15,7 +15,8 @@ limitations under the License. */ #include "internal_types.h" -#include "pdhcg_kernels.cuh" +#include "pdhcg_cone_common_kernels.h" +#include "pdhcg_rsoc_cone_kernels.h" #include "utils.h" #include #include diff --git a/test/test_large_soc_projection.cu b/test/test_large_soc_projection.cu index 0f367c0..308a82a 100644 --- a/test/test_large_soc_projection.cu +++ b/test/test_large_soc_projection.cu @@ -15,7 +15,7 @@ limitations under the License. */ #include "internal_types.h" -#include "pdhcg_kernels.cuh" +#include "pdhcg_soc_cone_kernels.h" #include "utils.h" #include #include diff --git a/test/test_power_cone_projection.cu b/test/test_power_cone_projection.cu index 7f116c3..a0caa50 100644 --- a/test/test_power_cone_projection.cu +++ b/test/test_power_cone_projection.cu @@ -1,4 +1,4 @@ -#include "pdhcg_kernels.cuh" +#include "pdhcg_power_cone_kernels.h" #include #include diff --git a/test/test_prefos_integration.c b/test/test_prefos_integration.c index 230d81f..407f135 100644 --- a/test/test_prefos_integration.c +++ b/test/test_prefos_integration.c @@ -81,6 +81,7 @@ static int test_all_fixed_postsolve(void) double objective[] = {1.0, 2.0}; double row_lower[] = {1.0}; double row_upper[] = {1.0}; + double row_offset[] = {0.0}; memset(&problem, 0, sizeof(problem)); problem.num_variables = 2; @@ -98,6 +99,7 @@ static int test_all_fixed_postsolve(void) problem.objective_vector = objective; problem.constraint_lower_bound = row_lower; problem.constraint_upper_bound = row_upper; + problem.affine_cone_offset = row_offset; info = pdhcg_presolve(&problem, ¶meters); CHECK(info != NULL); @@ -237,6 +239,63 @@ static int test_power_layout_and_alpha(void) return 0; } +static int test_psd_layout_and_postsolve(void) +{ + qp_problem_t problem; + pdhg_parameters_t parameters = quiet_parameters(); + pdhcg_presolve_info_t *info; + pdhcg_result_t result; + double lower[] = {2.0, -INFINITY, -INFINITY, -INFINITY, -INFINITY, -INFINITY, -INFINITY}; + double upper[] = {2.0, INFINITY, INFINITY, INFINITY, INFINITY, INFINITY, INFINITY}; + double objective[] = {0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0}; + int cone_start[] = {1}; + int cone_v_dim[] = {3}; + cone_type_t cone_type[] = {CONE_PSD}; + const double identity_svec[] = {1.0, 0.0, 0.0, 1.0, 0.0, 1.0}; + int i; + + memset(&problem, 0, sizeof(problem)); + problem.num_variables = 7; + problem.constraint_matrix = create_csr(0, 0); + CHECK(problem.constraint_matrix != NULL); + problem.variable_lower_bound = lower; + problem.variable_upper_bound = upper; + problem.objective_vector = objective; + problem.cones.num_cones = 1; + problem.cones.start_idx = cone_start; + problem.cones.v_dim = cone_v_dim; + problem.cones.type = cone_type; + + info = pdhcg_presolve(&problem, ¶meters); + CHECK(info != NULL); + CHECK(info->presolve_status == PDHCG_PRESOLVE_STATUS_REDUCED); + CHECK(!info->problem_solved_during_presolve); + CHECK(info->reduced_problem != NULL); + CHECK(info->reduced_problem->num_variables == 6); + CHECK(info->reduced_problem->cones.num_cones == 1); + CHECK(info->reduced_problem->cones.start_idx[0] == 0); + CHECK(info->reduced_problem->cones.v_dim[0] == 3); + CHECK(info->reduced_problem->cones.type[0] == CONE_PSD); + + memset(&result, 0, sizeof(result)); + result.primal_solution = (double *)calloc(6, sizeof(double)); + result.reduced_cost = (double *)calloc(6, sizeof(double)); + CHECK(result.primal_solution && result.reduced_cost); + memcpy(result.primal_solution, identity_svec, sizeof(identity_svec)); + CHECK(pdhcg_postsolve(info, &result, &problem)); + CHECK(result.num_variables == 7); + CHECK(fabs(result.primal_solution[0] - 2.0) <= 1e-12); + for (i = 0; i < 6; ++i) + CHECK(fabs(result.primal_solution[i + 1] - identity_svec[i]) <= 1e-12); + + free(result.primal_solution); + free(result.dual_solution); + free(result.reduced_cost); + pdhcg_presolve_info_free(info); + free_csr(problem.constraint_matrix); + return 0; +} + static int test_diagonal_middle_matrix(void) { qp_problem_t problem; @@ -310,6 +369,10 @@ int main(void) fflush(stdout); if (test_power_layout_and_alpha()) return 1; + printf("PSD layout and postsolve...\n"); + fflush(stdout); + if (test_psd_layout_and_postsolve()) + return 1; printf("diagonal middle matrix...\n"); fflush(stdout); if (test_diagonal_middle_matrix()) diff --git a/test/test_psd_cones.c b/test/test_psd_cones.c new file mode 100644 index 0000000..4aca2da --- /dev/null +++ b/test/test_psd_cones.c @@ -0,0 +1,205 @@ +#include "pdhcg.h" +#include "pdhcg_types.h" +#include "utils.h" + +#include +#include +#include + +static int close_enough(double value, double expected, double tolerance) +{ + return fabs(value - expected) <= tolerance * (1.0 + fabs(expected)); +} + +static pdhcg_result_t *solve_tiny(qp_problem_t *problem) +{ + pdhg_parameters_t params; + set_default_parameters(¶ms); + params.verbose = 0; + params.presolve = false; + params.use_cone_preserving_scaling = false; + params.termination_evaluation_frequency = 10; + params.termination_criteria.eps_optimal_relative = 1e-7; + params.termination_criteria.eps_feasible_relative = 1e-7; + params.termination_criteria.time_sec_limit = 30.0; + return solve_qp_problem(problem, ¶ms); +} + +static int check_variable_psd(void) +{ + const double sqrt_two = 1.41421356237309504880; + const double objective[] = {0.0, 0.0, 1.0}; + const int row_ptr[] = {0, 1, 2}; + const int col_ind[] = {0, 1}; + const double values[] = {100.0, 0.01}; + const double rhs[] = {100.0, 0.02 * sqrt_two}; + matrix_desc_t A = {0}; + A.m = 2; + A.n = 3; + A.fmt = matrix_csr; + A.data.csr.nnz = 2; + A.data.csr.row_ptr = row_ptr; + A.data.csr.col_ind = col_ind; + A.data.csr.vals = values; + cone_spec_t cone = {.type = CONE_PSD, .start_idx = 0, .v_dim = 2}; + + qp_problem_t *problem = + create_qp_problem(objective, NULL, NULL, NULL, &A, rhs, rhs, NULL, NULL, NULL, 1, &cone, NULL, NULL, 0, NULL); + pdhcg_result_t *result = problem ? solve_tiny(problem) : NULL; + int passed = result && result->termination_reason == TERMINATION_REASON_OPTIMAL && + close_enough(result->primal_solution[0], 1.0, 2e-4) && + close_enough(result->primal_solution[1], 2.0 * sqrt_two, 2e-4) && + close_enough(result->primal_solution[2], 4.0, 3e-4) && result->relative_primal_residual < 2e-6 && + result->relative_dual_residual < 2e-6; + if (!passed && result) + { + fprintf(stderr, + "variable PSD: status=%d x=[%.9g %.9g %.9g], pr=%.3e du=%.3e gap=%.3e\n", + (int)result->termination_reason, + result->primal_solution[0], + result->primal_solution[1], + result->primal_solution[2], + result->relative_primal_residual, + result->relative_dual_residual, + result->relative_objective_gap); + } + pdhcg_result_free(result); + qp_problem_free(problem); + return passed; +} + +static int check_affine_psd(void) +{ + const double sqrt_two = 1.41421356237309504880; + const double objective[] = {1.0}; + const int row_ptr[] = {0, 1, 1, 1}; + const int col_ind[] = {0}; + const double values[] = {1.0}; + const double offset[] = {0.0, sqrt_two, 1.0}; + matrix_desc_t F = {0}; + F.m = 3; + F.n = 1; + F.fmt = matrix_csr; + F.data.csr.nnz = 1; + F.data.csr.row_ptr = row_ptr; + F.data.csr.col_ind = col_ind; + F.data.csr.vals = values; + cone_spec_t cone = {.type = CONE_PSD, .start_idx = 0, .v_dim = 2}; + + qp_problem_t *problem = create_qp_problem( + objective, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, 0, NULL, &F, offset, 1, &cone); + pdhcg_result_t *result = problem ? solve_tiny(problem) : NULL; + int passed = result && result->termination_reason == TERMINATION_REASON_OPTIMAL && + close_enough(result->primal_solution[0], 1.0, 3e-4) && result->relative_primal_residual < 2e-6 && + result->relative_dual_residual < 2e-6; + if (!passed && result) + { + fprintf(stderr, + "affine PSD: status=%d x=%.9g pr=%.3e du=%.3e gap=%.3e\n", + (int)result->termination_reason, + result->primal_solution[0], + result->relative_primal_residual, + result->relative_dual_residual, + result->relative_objective_gap); + } + pdhcg_result_free(result); + qp_problem_free(problem); + return passed; +} + +static int check_diagonal_q_psd(void) +{ + const double sqrt_two = 1.41421356237309504880; + const double objective[] = {0.0, 0.0, -3.0}; + const int a_row_ptr[] = {0, 1, 2}; + const int a_col_ind[] = {0, 1}; + const double a_values[] = {1.0, 1.0}; + const double rhs[] = {1.0, 2.0 * sqrt_two}; + const int q_row_ptr[] = {0, 0, 0, 1}; + const int q_col_ind[] = {2}; + const double q_values[] = {1.0}; + matrix_desc_t A = {0}; + matrix_desc_t Q = {0}; + A.m = 2; + A.n = 3; + A.fmt = matrix_csr; + A.data.csr.nnz = 2; + A.data.csr.row_ptr = a_row_ptr; + A.data.csr.col_ind = a_col_ind; + A.data.csr.vals = a_values; + Q.m = 3; + Q.n = 3; + Q.fmt = matrix_csr; + Q.data.csr.nnz = 1; + Q.data.csr.row_ptr = q_row_ptr; + Q.data.csr.col_ind = q_col_ind; + Q.data.csr.vals = q_values; + cone_spec_t cone = {.type = CONE_PSD, .start_idx = 0, .v_dim = 2}; + + qp_problem_t *problem = + create_qp_problem(objective, &Q, NULL, NULL, &A, rhs, rhs, NULL, NULL, NULL, 1, &cone, NULL, NULL, 0, NULL); + int uses_diagonal_q = + problem && detect_q_type(problem->objective_sparse_matrix, NULL, problem->num_variables, 0) == PDHCG_DIAG_Q; + pdhcg_result_t *result = problem ? solve_tiny(problem) : NULL; + int passed = uses_diagonal_q && result && result->termination_reason == TERMINATION_REASON_OPTIMAL && + close_enough(result->primal_solution[2], 4.0, 5e-4) && result->relative_primal_residual < 3e-6 && + result->relative_dual_residual < 3e-6; + if (!passed && result) + { + fprintf(stderr, + "diagonal-Q PSD: status=%d x=[%.9g %.9g %.9g], pr=%.3e du=%.3e gap=%.3e\n", + (int)result->termination_reason, + result->primal_solution[0], + result->primal_solution[1], + result->primal_solution[2], + result->relative_primal_residual, + result->relative_dual_residual, + result->relative_objective_gap); + } + pdhcg_result_free(result); + qp_problem_free(problem); + return passed; +} + +static int check_fixed_psd_rejected(void) +{ + const double objective[] = {0.0, 0.0, 0.0}; + const int row_ptr[] = {0, 0}; + const double rhs[] = {0.0}; + const char fixed[] = {0, 1, 0}; + matrix_desc_t A = {0}; + A.m = 1; + A.n = 3; + A.fmt = matrix_csr; + A.data.csr.row_ptr = row_ptr; + cone_spec_t cone = { + .type = CONE_PSD, + .start_idx = 0, + .v_dim = 2, + .is_fixed = fixed, + }; + qp_problem_t *problem = + create_qp_problem(objective, NULL, NULL, NULL, &A, rhs, rhs, NULL, NULL, NULL, 1, &cone, NULL, NULL, 0, NULL); + int passed = problem == NULL; + qp_problem_free(problem); + + cone.is_fixed = NULL; + problem = + create_qp_problem(objective, NULL, NULL, NULL, &A, rhs, rhs, NULL, NULL, NULL, 1, &cone, NULL, NULL, 0, NULL); + passed &= problem && set_cone_fixed(problem, 0, 1, 0.0) != 0; + qp_problem_free(problem); + return passed; +} + +int main(void) +{ + int variable = check_variable_psd(); + int affine = check_affine_psd(); + int diagonal_q = check_diagonal_q_psd(); + int fixed_rejected = check_fixed_psd_rejected(); + printf("variable PSD: %s\n", variable ? "PASS" : "FAIL"); + printf("affine PSD: %s\n", affine ? "PASS" : "FAIL"); + printf("diagonal-Q PSD: %s\n", diagonal_q ? "PASS" : "FAIL"); + printf("fixed PSD: %s\n", fixed_rejected ? "REJECTED" : "FAIL"); + return variable && affine && diagonal_q && fixed_rejected ? 0 : 1; +} diff --git a/test/test_psd_projection.cu b/test/test_psd_projection.cu new file mode 100644 index 0000000..33bff23 --- /dev/null +++ b/test/test_psd_projection.cu @@ -0,0 +1,114 @@ +#include "pdhcg_psd_cone.h" + +#include +#include +#include +#include + +static int test_batched_projection(void) +{ + const double sqrt_two = 1.41421356237309504880; + const int order[] = {2, 2, 3, 1}; + const int start[] = {0, 3, 6, 12}; + double host[] = { + 1.0, + 2.0 * sqrt_two, + 1.0, + 2.0, + -sqrt_two, + 2.0, + 1.0, + 2.0 * sqrt_two, + 3.0 * sqrt_two, + 4.0, + 6.0 * sqrt_two, + 9.0, + -3.0, + }; + const double expected[] = { + 1.5, + 1.5 * sqrt_two, + 1.5, + 2.0, + -sqrt_two, + 2.0, + 1.0, + 2.0 * sqrt_two, + 3.0 * sqrt_two, + 4.0, + 6.0 * sqrt_two, + 9.0, + 0.0, + }; + double *device = NULL; + cudaMalloc(&device, sizeof(host)); + cudaMemcpy(device, host, sizeof(host), cudaMemcpyHostToDevice); + + psd_projection_runtime_t *runtime = create_psd_projection_runtime(start, order, 4, 0); + project_psd_cones(runtime, device); + cudaMemcpy(host, device, sizeof(host), cudaMemcpyDeviceToHost); + + int passed = 1; + for (int slot = 0; slot < 13; ++slot) + if (!isfinite(host[slot]) || fabs(host[slot] - expected[slot]) > 1e-10) + passed = 0; + + free_psd_projection_runtime(runtime); + cudaFree(device); + return passed; +} + +static int test_large_diagonal_projection(void) +{ + const int order = 33; + const int packed_length = order * (order + 1) / 2; + double *host = (double *)calloc((size_t)packed_length, sizeof(double)); + double *device = NULL; + if (!host) + return 0; + + int slot = 0; + for (int col = 0; col < order; ++col) + { + for (int row = col; row < order; ++row) + { + if (row == col) + host[slot] = col % 2 == 0 ? -(double)(col + 1) : (double)(col + 1); + ++slot; + } + } + + cudaMalloc(&device, (size_t)packed_length * sizeof(double)); + cudaMemcpy(device, host, (size_t)packed_length * sizeof(double), cudaMemcpyHostToDevice); + const int start = 0; + psd_projection_runtime_t *runtime = create_psd_projection_runtime(&start, &order, 1, 0); + project_psd_cones(runtime, device); + cudaMemcpy(host, device, (size_t)packed_length * sizeof(double), cudaMemcpyDeviceToHost); + + int passed = 1; + slot = 0; + for (int col = 0; col < order; ++col) + { + for (int row = col; row < order; ++row) + { + double expected = row == col && col % 2 == 1 ? (double)(col + 1) : 0.0; + if (!isfinite(host[slot]) || fabs(host[slot] - expected) > 1e-9 * (1.0 + expected)) + passed = 0; + ++slot; + } + } + + free_psd_projection_runtime(runtime); + cudaFree(device); + free(host); + return passed; +} + +int main(void) +{ + int batched = test_batched_projection(); + int large = test_large_diagonal_projection(); + printf("PSD batched spectral projection: %s\n", batched ? "PASS" : "FAIL"); + printf("PSD order-33 spectral projection: %s\n", large ? "PASS" : "FAIL"); + return batched && large ? 0 : 1; +} diff --git a/tests/test_cones.py b/tests/test_cones.py index fd524d5..4dc9a9d 100644 --- a/tests/test_cones.py +++ b/tests/test_cones.py @@ -41,6 +41,20 @@ def test_cone_spec_validates_power_and_ambient_ranges() -> None: cones.validate_ambient(4, allow_fixed=True) +def test_psd_cone_uses_matrix_order_and_rejects_fixed_slots() -> None: + cones = ConeSpec("psd", np.array([0], dtype=np.int32), v_dims=3) + cones.validate_ambient(6, allow_fixed=True, require_cover=True) + + fixed = ConeSpec( + ConeType.PSD, + np.array([0], dtype=np.int32), + v_dims=2, + fixed_mask=np.array([0, 1, 0], dtype=np.uint8), + ) + with pytest.raises(ValueError, match="PSD"): + fixed.validate_ambient(3, allow_fixed=True) + + def test_read_problem_file_returns_columnar_cones() -> None: problem = Path(__file__).parent / "data" / "cbf_q3_smoke.cbf" raw = read_problem_file(str(problem)) diff --git a/tests/test_model_cones.py b/tests/test_model_cones.py index b79130c..b2dbd52 100644 --- a/tests/test_model_cones.py +++ b/tests/test_model_cones.py @@ -83,6 +83,43 @@ def test_model_native_affine_soc_columnar_input(sparse: bool) -> None: np.testing.assert_allclose(model.X, [5.0], atol=2e-4) +@pytest.mark.gpu +def test_model_variable_psd_svec_input() -> None: + sqrt_two = np.sqrt(2.0) + model = _quiet_model( + Model( + objective_vector=np.array([0.0, 0.0, 1.0]), + constraint_matrix=sp.csr_matrix([[1.0, 0.0, 0.0], [0.0, 1.0, 0.0]]), + constraint_lower_bound=np.array([1.0, 2.0 * sqrt_two]), + constraint_upper_bound=np.array([1.0, 2.0 * sqrt_two]), + variable_cones=ConeSpec(ConeType.PSD, np.array([0], dtype=np.int32), v_dims=2), + ) + ) + + model.optimize() + + assert model.Status == "OPTIMAL" + np.testing.assert_allclose(model.X, [1.0, 2.0 * sqrt_two, 4.0], atol=8e-4) + + +@pytest.mark.gpu +def test_model_native_affine_psd_svec_input() -> None: + sqrt_two = np.sqrt(2.0) + model = _quiet_model( + Model( + objective_vector=np.array([1.0]), + affine_cone_matrix=sp.csr_matrix([[1.0], [0.0], [0.0]]), + affine_cone_offset=np.array([0.0, sqrt_two, 1.0]), + affine_cones=ConeSpec(ConeType.PSD, np.array([0], dtype=np.int32), v_dims=2), + ) + ) + + model.optimize() + + assert model.Status == "OPTIMAL" + np.testing.assert_allclose(model.X, [1.0], atol=8e-4) + + @pytest.mark.gpu def test_cvxpy_constant_exp_rows_use_equalities_instead_of_fixed_slots() -> None: cp = pytest.importorskip("cvxpy") @@ -134,3 +171,20 @@ def test_cvxpy_soc_dual_sign_and_order() -> None: np.testing.assert_allclose(fixed.dual_value, [-0.6, -0.8], atol=5e-5) np.testing.assert_allclose(cone.dual_value[0], [1.0], atol=5e-5) np.testing.assert_allclose(cone.dual_value[1].ravel(), [-0.6, -0.8], atol=5e-5) + + +@pytest.mark.gpu +def test_cvxpy_psd_primal_and_dual_svec_order() -> None: + cp = pytest.importorskip("cvxpy") + import pdhcg.cvxpy_backend # noqa: F401 + + matrix = cp.Variable((2, 2), symmetric=True) + fixed = [matrix[0, 0] == 1.0, matrix[0, 1] == 2.0] + cone = matrix >> 0 + problem = cp.Problem(cp.Minimize(matrix[1, 1]), [*fixed, cone]) + + problem.solve(solver="PDHCG", eps=1e-6, verbose=False) + + assert problem.status == cp.OPTIMAL + np.testing.assert_allclose(matrix.value, [[1.0, 2.0], [2.0, 4.0]], atol=2e-3) + np.testing.assert_allclose(cone.dual_value, [[4.0, -2.0], [-2.0, 1.0]], atol=3e-3)