def adjoint_derivative(dx, dy, ds, **kwargs): """Applies adjoint of derivative at (A, b, c) to perturbations dx, dy, ds Args: dx: NumPy array representing perturbation in `x` dy: NumPy array representing perturbation in `y` ds: NumPy array representing perturbation in `s` Returns: (`dA`, `db`, `dc`), the result of applying the adjoint to the perturbations; the sparsity pattern of `dA` matches that of `A`. """ dw = -(x @ dx + y @ dy + s @ ds) dz = np.concatenate( [dx, D_proj_dual_cone.rmatvec(dy + ds) - ds, np.array([dw])]) if np.allclose(dz, 0): r = np.zeros(dz.shape) elif mode == "dense": r = _diffcp._solve_adjoint_derivative_dense(M, MT, dz) else: r = _diffcp.lsqr(MT, dz).solution values = pi_z[cols] * r[rows + n] - pi_z[n + rows] * r[cols] dA = sparse.csc_matrix((values, (rows, cols)), shape=A.shape) db = pi_z[n:n + m] * r[-1] - pi_z[-1] * r[n:n + m] dc = pi_z[:n] * r[-1] - pi_z[-1] * r[:n] return dA, db, dc
def derivative(dA, db, dc, **kwargs): """Applies derivative at (A, b, c) to perturbations dA, db, dc Args: dA: SciPy sparse matrix in CSC format; must have same sparsity pattern as the matrix `A` from the cone program db: NumPy array representing perturbation in `b` dc: NumPy array representing perturbation in `c` Returns: NumPy arrays dx, dy, ds, the result of applying the derivative to the perturbations. """ dQ = sparse.bmat( [[None, dA.T, np.expand_dims(dc, -1)], [-dA, None, np.expand_dims(db, -1)], [-np.expand_dims(dc, -1).T, -np.expand_dims(db, -1).T, None]]) rhs = dQ @ pi_z if np.allclose(rhs, 0): dz = np.zeros(rhs.size) elif mode == "dense": dz = _diffcp._solve_derivative_dense(M, MT, rhs) else: dz = _diffcp.lsqr(M, rhs).solution du, dv, dw = np.split(dz, [n, n + m]) dx = du - x * dw dy = D_proj_dual_cone.matvec(dv) - y * dw ds = D_proj_dual_cone.matvec(dv) - dv - s * dw return -dx, -dy, -ds