Basic Usage¶
This guide covers the fundamental usage patterns for POGS.
Problem Formulation¶
POGS solves problems in graph form:
where: - \(f:\mathbb{R}^m \to \mathbb{R}\) and \(g:\mathbb{R}^n \to \mathbb{R}\) are convex, separable functions - \(A \in \mathbb{R}^{m \times n}\) is the data matrix - \(x \in \mathbb{R}^n\) are the optimization variables - \(y \in \mathbb{R}^m\) are auxiliary variables
Separability means: $$ f(y) = \sum_{i=1}^m f_i(y_i), \quad g(x) = \sum_{j=1}^n g_j(x_j) $$
C++ Interface¶
1. Include Headers and Create Matrix¶
#include "pogs.h"
#include "matrix/matrix_dense.h"
const size_t m = 100; // Number of samples
const size_t n = 50; // Number of features
// Create matrix A (row-major, m x n)
std::vector<double> A_data(m * n);
// ... fill A_data ...
pogs::MatrixDense<double> A('r', m, n, A_data.data());
2. Create the Solver¶
// PogsDirect - uses direct factorization (dense problems)
pogs::PogsDirect<double, pogs::MatrixDense<double>> solver(A);
// Alternative: PogsCgls - uses iterative method (large/sparse problems)
// pogs::PogsCgls<double, pogs::MatrixDense<double>> solver(A);
3. Define Objective Functions¶
// f_i(y_i) and g_j(x_j) are defined using FunctionObj
std::vector<FunctionObj<double>> f(m);
std::vector<FunctionObj<double>> g(n);
// Example: Lasso (least squares + L1 regularization)
// f_i(y_i) = (1/2) * y_i^2 - b_i * y_i (for ||Ax - b||^2)
for (size_t i = 0; i < m; ++i) {
f[i].h = kSquare; // Base function: (1/2) x^2
f[i].c = 1.0; // Scaling
f[i].d = -b[i]; // Linear term: creates ||y - b||^2
}
// g_j(x_j) = lambda * |x_j| (L1 regularization)
double lambda = 0.1;
for (size_t j = 0; j < n; ++j) {
g[j].h = kAbs; // Base function: |x|
g[j].c = lambda; // Regularization weight
}
4. Configure the Solver¶
solver.SetRho(1.0); // ADMM penalty parameter
solver.SetAbsTol(1e-4); // Absolute tolerance
solver.SetRelTol(1e-3); // Relative tolerance
solver.SetMaxIter(1000); // Maximum iterations
solver.SetVerbose(2); // Show progress
solver.SetAdaptiveRho(true); // Enable adaptive rho
5. Solve and Get Results¶
PogsStatus status = solver.Solve(f, g);
if (status == POGS_SUCCESS) {
printf("Converged in %u iterations\n", solver.GetFinalIter());
printf("Optimal value: %f\n", solver.GetOptval());
// Access solution
const double* x = solver.GetX();
for (size_t j = 0; j < n; ++j) {
printf("x[%zu] = %f\n", j, x[j]);
}
}
Complete Example¶
#include "pogs.h"
#include "matrix/matrix_dense.h"
#include <vector>
#include <cstdio>
#include <cstdlib>
int main() {
const size_t m = 100; // samples
const size_t n = 50; // features
const double lambda = 0.1;
// Generate random data
std::vector<double> A_data(m * n);
std::vector<double> b(m);
for (size_t i = 0; i < m * n; ++i)
A_data[i] = (double)rand() / RAND_MAX - 0.5;
for (size_t i = 0; i < m; ++i)
b[i] = (double)rand() / RAND_MAX - 0.5;
// Create matrix and solver
pogs::MatrixDense<double> A('r', m, n, A_data.data());
pogs::PogsDirect<double, pogs::MatrixDense<double>> solver(A);
// Configure
solver.SetAbsTol(1e-4);
solver.SetRelTol(1e-3);
solver.SetMaxIter(1000);
solver.SetVerbose(2);
// Define objective functions
std::vector<FunctionObj<double>> f(m), g(n);
for (size_t i = 0; i < m; ++i) {
f[i].h = kSquare;
f[i].d = -b[i];
}
for (size_t j = 0; j < n; ++j) {
g[j].h = kAbs;
g[j].c = lambda;
}
// Solve
PogsStatus status = solver.Solve(f, g);
if (status == POGS_SUCCESS) {
printf("Converged! Optimal value: %f\n", solver.GetOptval());
} else {
printf("Failed with status: %d\n", status);
}
return 0;
}
Function Types¶
POGS supports many common functions via the Function enum:
Norms and Regularization¶
| Enum | Mathematical Form | Use Case |
|---|---|---|
kAbs |
\(\|x\|\) | L1 regularization (Lasso) |
kSquare |
\((1/2)x^2\) | Least squares, L2 penalty |
kHuber |
Huber loss | Robust regression |
Indicator Functions (Constraints)¶
| Enum | Mathematical Form | Use Case |
|---|---|---|
kIndBox01 |
\(I_{[0,1]}(x)\) | Box constraints |
kIndEq0 |
\(I_{\{0\}}(x)\) | Equality to zero |
kIndGe0 |
\(I_{[0,\infty)}(x)\) | Non-negativity |
kIndLe0 |
\(I_{(-\infty,0]}(x)\) | Non-positivity |
Nonlinear Functions¶
| Enum | Mathematical Form | Use Case |
|---|---|---|
kLogistic |
\(\log(1 + e^x)\) | Logistic regression |
kNegLog |
\(-\log(x)\) | Barrier functions |
kExp |
\(e^x\) | Exponential objectives |
kIdentity |
\(x\) | Linear terms |
kZero |
\(0\) | Unconstrained |
FunctionObj Parameters¶
Each FunctionObj<T> represents:
| Field | Default | Description |
|---|---|---|
h |
kZero |
Base function type |
a |
1.0 |
Input scaling |
b |
0.0 |
Input shift |
c |
1.0 |
Output scaling |
d |
0.0 |
Linear term coefficient |
e |
0.0 |
Quadratic term coefficient |
Example:
FunctionObj<double> obj;
obj.h = kSquare; // h(x) = (1/2)x^2
obj.c = 0.5; // Scale by 0.5
obj.d = -b[i]; // Add linear term -b[i]*x
// Result: 0.5 * (1/2) * x^2 - b[i] * x = (1/4)x^2 - b[i]*x
Common Problem Types¶
Lasso Regression¶
for (size_t i = 0; i < m; ++i) {
f[i].h = kSquare;
f[i].d = -b[i];
}
for (size_t j = 0; j < n; ++j) {
g[j].h = kAbs;
g[j].c = lambda;
}
Ridge Regression¶
for (size_t i = 0; i < m; ++i) {
f[i].h = kSquare;
f[i].d = -2.0 * b[i];
}
for (size_t j = 0; j < n; ++j) {
g[j].h = kSquare;
g[j].c = lambda;
}
Non-Negative Least Squares¶
for (size_t i = 0; i < m; ++i) {
f[i].h = kSquare;
f[i].d = -2.0 * b[i];
}
for (size_t j = 0; j < n; ++j) {
g[j].h = kIndGe0; // Non-negativity constraint
}
Status Codes¶
| Status | Meaning |
|---|---|
POGS_SUCCESS |
Converged successfully |
POGS_MAX_ITER |
Maximum iterations reached |
POGS_NAN_FOUND |
Numerical error (NaN) |
POGS_INFEASIBLE |
Problem likely infeasible |
POGS_UNBOUNDED |
Problem likely unbounded |
POGS_ERROR |
Generic error |
Solver Parameters¶
Penalty Parameter (rho)¶
- Controls the weight of the augmented Lagrangian term
- Default:
1.0 - Larger rho: Faster convergence but potentially less accurate
- Smaller rho: More accurate but slower convergence
Tolerances¶
Absolute tolerance (abs_tol): Default 1e-4
Relative tolerance (rel_tol): Default 1e-3
The solver stops when primal and dual residuals satisfy: $$ |r|2 \leq \epsilon \cdot \max(|Ax|_2, |y|_2) $$}} + \epsilon_{\text{rel}
Adaptive Rho¶
When SetAdaptiveRho(true), rho is automatically adjusted:
- If primal residual >> dual residual: increase rho
- If dual residual >> primal residual: decrease rho
Next Steps¶
- Advanced Features - Warm starting, Anderson acceleration
- Cone Problems - LP, QP, SOCP, SDP formulations
- Examples - Complete working examples