Fetching contributors…
Cannot retrieve contributors at this time
143 lines (116 sloc) 4.69 KB
function M = positivefactory(m, n)
% Manifold of m-by-n matrices with positive entries; scale invariant metric
% function M = positivefactory(m)
% function M = positivefactory(m, n)
% A point X on the manifold M is represented as a matrix X of size mxn with
% all individual entries real, strictly positive. By default, n = 1.
% A tangent vector at X is represented as a matrix of the same size as X.
% Entries of tangent vectors are free (in particular, not necessarily
% positive.)
% The Riemannian metric for each individual entry is the bi-invariant
% metric for positive scalars, as a particular case of the bi-invariant
% metric for positive definite matrices studied in Chapter 6 of the book
% "Positive definite matrices" by Rajendra Bhatia,
% Princeton University Press, 2007.
% The Riemannian structure of M is obtained as the Cartesian product of the
% geometry for mxn positive real numbers.
% It should be stressed that matrices with one or more zero entries do not
% belong to this manifold: they appear to be infinitely far away as a
% result of the metric scaling like X.^(-1). Thus, if the solutions of an
% optimization problem have entries equal to zero, these solutions are not
% attainable on the manifold, which is likely to create serious numerical
% issues. This geometry is best used when the solutions of the optimization
% problem are indeed entry-wise positive, yet may have very different
% scales (with some entries being very small, and some entries being very
% large, relatively.)
% See also: sympositivedefinitefactory
% This file is part of Manopt:
% Original author: Bamdev Mishra, Dec 03, 2017.
if ~exist('n', 'var') || isempty(n)
n = 1;
end = @() sprintf('Element-wise positive %dx%d matrices', m, n);
M.dim = @() m*n;
% The metric is the scale invariant metric for scalars.
M.inner = @myinner;
function innerproduct = myinner(X, eta, zeta)
innerproduct = (eta(:)./X(:))'*(zeta(:)./X(:));
M.norm = @(X, eta) sqrt(myinner(X, eta, eta));
M.dist = @(X, Y) norm(log(Y./X), 'fro');
M.typicaldist = @() sqrt(m*n);
M.egrad2rgrad = @egrad2rgrad;
function eta = egrad2rgrad(X, eta)
eta = X.*(eta).*X;
M.ehess2rhess = @ehess2rhess;
function Hess = ehess2rhess(X, egrad, ehess, eta)
% Directional derivatives of the Riemannian gradient
Hess = X.*(ehess).*X + 2*(eta.*(egrad).*X);
% Correction factor for the non-constant metric
Hess = Hess - (eta.*(egrad).*X);
% Since this manifold is an open subset of R^(nxm), the tangent space
% at any X on M is all of R^(nxm).
M.proj = @(X, eta) eta;
M.tangent = M.proj;
M.tangent2ambient = @(X, eta) eta;
M.retr = @exponential;
M.exp = @exponential;
function Y = exponential(X, eta, t)
if nargin < 3
t = 1.0;
% It is unclear whether this is the numerically most stable way to
% implement this operation. If you run into trouble with this
% factory, please get in touch on the forum.
Y = (X.*(exp((t*eta)./X)));
M.log = @logarithm;
function H = logarithm(X, Y)
% Same comment about numerical stability as for exp.
H = (X.*(log(Y./X)));
M.hash = @(X) ['z' hashmd5(X(:))];
% Generate a random element-wise positive matrix following a
% certain distribution. The particular choice of a distribution is of
% course arbitrary, and specific applications might require different
% ones.
M.rand = @random;
function X = random()
X = exp(randn(m, n));
% Generate a uniformly random unit-norm tangent vector at X.
M.randvec = @randomvec;
function eta = randomvec(X)
eta = randn(m, n).*X;
nrm = M.norm(X, eta);
eta = eta / nrm;
M.lincomb = @matrixlincomb;
M.zerovec = @(X) zeros(m, n);
M.transp = @(X1, X2, eta) eta;
% For reference, a proper vector transport is given here, following
% work by Sra and Hosseini: "Conic geometric optimisation on the
% manifold of positive definite matrices".
% This is not used by default. To force the use of this transport,
% execute "M.transp = M.paralleltransp;" on your M returned by the
% present factory.
M.paralleltransp = @parallel_transport;
function zeta = parallel_transport(X, Y, eta)
E = sqrt(Y./X);
zeta = E.*eta.*E;
% vec and mat are not isometries, because of the unusual inner metric.
M.vec = @(X, U) U(:);
M.mat = @(X, u) reshape(u, m, n);
M.vecmatareisometries = @() true;