Dynamic-Calibration/utils/YALMIP-master/@sdpvar/mtimes.m

873 lines
32 KiB
Matlab
Executable File

function Z = mtimes(X,Y)
%MTIMES (overloaded)
% Cannot use isa here since blkvar is marked as sdpvar
X_class = class(X);
Y_class = class(Y);
X_is_spdvar = strcmp(X_class,'sdpvar');
Y_is_spdvar = strcmp(Y_class,'sdpvar');
% Convert block objects
if ~X_is_spdvar
if isa(X,'blkvar')
X = sdpvar(X);
X_is_spdvar = isa(X,'sdpvar');
end
end
if ~Y_is_spdvar
if isa(Y,'blkvar')
Y = sdpvar(Y);
Y_is_spdvar = isa(Y,'sdpvar');
end
end
% Lame special cases, make sure to reurn
% empty matrices in the sense that the
% used MATLAB version
if isempty(X)
YY = full(reshape(Y.basis(:,1),Y.dim(1),Y.dim(2)));
Z = X*YY;
return
elseif isempty(Y)
XX = full(reshape(X.basis(:,1),X.dim(1),X.dim(2)));
Z = XX*Y;
return
end
% Optimized calls in different order?
if X_is_spdvar && Y_is_spdvar
manytimesfew = length(X.lmi_variables) > 5*length(Y.lmi_variables);
if manytimesfew
Z = (Y'*X')'; % Optimized for this order (few variables * many variables)
return
end
end
if ~X_is_spdvar
if any(isnan(X))
error('Multiplying NaN with an SDPVAR makes no sense.');
end
end
if ~Y_is_spdvar
if any(isnan(Y))
error('Multiplying NaN with an SDPVAR makes no sense.');
end
end
% Different code for
% 1 : SDPVAR * DOUBLE
% 2 : DOUBLE * SDPVAR
% 3 : SDPVAR * SDPVAR
switch 2*X_is_spdvar+Y_is_spdvar
case 3
if ~isempty(X.midfactors)
X = flush(X);
end
if ~isempty(Y.midfactors)
Y = flush(Y);
end
try
% HACK: Return entropy when user types x*log(x), plog for
% x*log(y/x) and -plog for x*log(x/y)
if isequal(Y.extra.opname,'log')
Z = check_for_special_case(Y,X);
if ~isempty(Z)
return
end
elseif isequal(X.extra.opname,'log')
Z = check_for_special_case(X,Y);
if ~isempty(Z)
return
end
end
ny = Y.dim(1);
my = Y.dim(2);
nx = X.dim(1);
mx = X.dim(2);
x_isscalar = nx*mx==1;
y_isscalar = ny*my==1;
[mt,oldvariabletype,mt_hash,hash] = yalmip('monomtable');
% Super-special hack to speed up QP formulations
% Requires that the involved variables never have been used
% before in a nonlinear expression. By exploiting this fact, we
% can avoid using findhash, which typically is the bottle-neck.
if (nx == 1) && (my == 1) && isequal(X.lmi_variables,Y.lmi_variables)
% Looks like w'Qw or similiar
% Check that no nonlinear have been defined before, and that
% the arguments are linear.
if all(oldvariabletype(X.lmi_variables)==0) && nnz(mt(find(oldvariabletype),X.lmi_variables)) == 0
Z = super_fast_quadratic_multiplication(X,Y,mt,oldvariabletype,mt_hash,hash);
return
end
end
% Are the involved variables disjoint
if X.lmi_variables(end) < Y.lmi_variables(1)
disconnected = 1;
elseif Y.lmi_variables(end) < X.lmi_variables(1)
disconnected = 2;
elseif isequal(Y.lmi_variables,X.lmi_variables)
disconnected = -1; % Same
else
disconnected = 0; % Tangled up
end
% Optimized unique
switch disconnected
case -1
all_lmi_variables = [X.lmi_variables];
case 1
all_lmi_variables = [X.lmi_variables Y.lmi_variables];
case 2
all_lmi_variables = [Y.lmi_variables X.lmi_variables];
otherwise
all_lmi_variables = uniquestripped([X.lmi_variables Y.lmi_variables]);
end
% Create clean SDPVAR object
Z = X;Z.dim(1) = 1;Z.dim(2) = 1;Z.lmi_variables = all_lmi_variables;Z.basis = [];
% Awkward code due to bug in ML6.5
Xbase = reshape(X.basis(:,1),X.dim(1),X.dim(2));
Ybase = reshape(Y.basis(:,1),Y.dim(1),Y.dim(2));
if x_isscalar
Xbase = sparse(full(Xbase));
end
if y_isscalar
Ybase = sparse(full(Ybase));
end
switch disconnected
case -1
index_X = [ones(1,length(X.lmi_variables))];
index_Y = index_X;
case 1
oX = ones(1,length(X.lmi_variables));
oY = ones(1,length(Y.lmi_variables));
index_X = [oX 0*oY];
index_Y = [oX*0 oY];
case 2
index_X = [zeros(1,length(Y.lmi_variables)) ones(1,length(X.lmi_variables))];
index_Y = [ones(1,length(Y.lmi_variables)) zeros(1,length(X.lmi_variables))];
otherwise
index_X = double(ismembcYALMIP(all_lmi_variables,X.lmi_variables));
index_Y = double(ismembcYALMIP(all_lmi_variables,Y.lmi_variables));
end
iX=find(index_X);
iY=find(index_Y);
index_X(iX)=1:length(iX);index_X=index_X(:);
index_Y(iY)=1:length(iY);index_Y=index_Y(:);
% Pre-allocate under assumption that product is fairly sparse
% If more is required later, it will be expanded
Z.lmi_variables = [Z.lmi_variables zeros(1,length(X.lmi_variables)+length(Y.lmi_variables))];
%Z.lmi_variables = [Z.lmi_variables zeros(1,length(X.lmi_variables)*length(Y.lmi_variables))];
% Pre-calc identity (used a lot
speyemy = sparse(1:my,1:my,1,my,my);
% Linear terms
inner_vector_product = (nx==1 && my==1 && (mx == ny));
if inner_vector_product
base1=Xbase*Y.basis;base1=base1(2:end);
base2=Ybase.'*X.basis;base2=base2(2:end);
[i1,j1,k1]=find(base1);
[i2,j2,k2]=find(base2);
base1 = sparse(i1,iY(j1),k1,1,length(all_lmi_variables));
base2 = sparse(i2,iX(j2),k2,1,length(all_lmi_variables));
Z.basis = [Xbase*Ybase base1+base2];
else
base0 = Xbase*Ybase;
if x_isscalar
base1 = Xbase*Y.basis(:,2:end);
base2 = Ybase(:)*X.basis(:,2:end);
elseif y_isscalar
base1 = Xbase(:)*Y.basis;base1=base1(:,2:end);
base2 = X.basis*Ybase;base2=base2(:,2:end);
else
base1 = kron(speyemy,Xbase)*Y.basis(:,2:end);
base2 = kron(Ybase.',speye(nx))*X.basis(:,2:end);
end
[i1,j1,k1]=find(base1);
[i2,j2,k2]=find(base2);
base1 = sparse(i1,iY(j1),k1,size(base0(:),1),length(all_lmi_variables));
base2 = sparse(i2,iX(j2),k2,size(base0(:),1),length(all_lmi_variables));
Z.basis = [base0(:) base1+base2];
end
% Loop start for nonlinear terms
i = length(all_lmi_variables)+1;
% [mt,oldvariabletype,mt_hash,hash] = yalmip('monomtable');
% Check if problem is bilinear. We can exploit this later
% to improve performance significantly...
bilinearproduct = 0;
candofastlocation = 0;
if all(oldvariabletype(X.lmi_variables)==0) && all(oldvariabletype(Y.lmi_variables)==0)
% if isempty(intersect(X.lmi_variables,Y.lmi_variables))
% members = ismembcYALMIP(X.lmi_variables,Y.lmi_variables);
if disconnected == 1 || disconnected == 2%~any(members)
bilinearproduct = 1;
try
dummy = ismembc2(1,1); % Not available in all versions (needed in ismember)
candofastlocation = 1;
catch
end
end
end
oldmt = mt;
local_mt = mt(all_lmi_variables,:);
used_variables = any(local_mt,1);
local_mt = local_mt(:,used_variables);
possibleOld = find(any(mt(:,used_variables),2));
if all(oldvariabletype <=3)
% All monomials have non-negative integer powers
% no chance of x^2*x^-1, hence all products
% are nonlinear
possibleOld = possibleOld(find(oldvariabletype(possibleOld)));
if size(possibleOld,1)==0
possibleOld = [];
end
% x_degrees = sum(mt(X.lmi_variables,:),2);
% y_degrees = sum(mt(Y.lmi_variables,:),2);
% if ~any(diff(x_degrees)) && ~any(diff(y_degrees)) && x_degrees(1)==y_degrees(1)
% % x and y are monomials of same order d. Hence, we only
% % have to took for old monomials of order 2d
% possibleOld = possibleOld(find(sum(mt(possibleOld,:),2) == 2*x_degrees(1)));
% end
end
if bilinearproduct && ~isempty(possibleOld)
if length(X.lmi_variables)<=length(Y.lmi_variables)
temp = mt(:,X.lmi_variables);
temp = temp(possibleOld,:);
possibleOld=possibleOld(find(any(temp,2)));
else
temp = mt(:,Y.lmi_variables);
temp = temp(possibleOld,:);
possibleOld=possibleOld(find(any(temp,2)));
end
end
theyvars = find(index_Y);
thexvars = find(index_X);
% We work with three sets of hashed data.
% 1. The hashes that were available from the start. These are
% sorted
possibleOldHash = mt_hash(possibleOld);
[possibleOldHashSorted, sortedHashLocs] = sort(possibleOldHash);
oldhash = hash;
hash = hash(used_variables);
% 2. The hashes that were introduced in the previous outer
% iteration, i.e. those generated when multiplying x(ix) with
% all y. These are sorted every iteration
new_mt_hash = [];
%new_mt_hash_aux = spalloc(length(X.lmi_variables)*length(Y.lmi_variables),1,0);
new_mt_hash_aux = zeros(min(1e3,length(X.lmi_variables)*length(Y.lmi_variables)),1);
new_mt_hash_counter = 0;
% % 3. Those that are generated at the current iteration. These
% % are not sorted
% currwent_new_mt_hash_aux = zeros(length(X.lmi_variables)*length(Y.lmi_variables),1);
% current_new_mt_hash_counter = 0;
new_mt = [];
changed_mt = 0;
local_mt = local_mt';
nvar = size(mt,1);
old_new_mt_hash_counter = new_mt_hash_counter;
possibleNewHashSorted = {};
sortedNewHashLocs = {};
possibleOldBlocked{1} = possibleOld;
sortedHashLocsBlocked{1} = sortedHashLocs;
possibleOldHashSortedBlocked{1} = possibleOldHashSorted;
possibleOldHashSortedBlockedFull{1} = full(possibleOldHashSorted);
current_offset = size(mt_hash,1);
YB = Y.basis(:,1+index_Y(theyvars(:)'));
if isequal(YB,speye(size(YB,1)))
simpleMultiplicationwithI = 1;
else
simpleMultiplicationwithI = 0;
end
for ix = thexvars(:)'
mt_x = local_mt(:,ix);
testthese = theyvars(:)';
% Compute [vec(Xbasis*Ybasis1) vec(Xbasis*Ybasis2) ...]
% in one shot using vectorization and Kronecker tricks
% Optimized and treat special case scalar*matrix etc
if x_isscalar
allprodbase = X.basis(:,1+index_X(ix));
%allprodbase = Xibase * Y.basis(:,1+index_Y(testthese));
allprodbase = allprodbase * YB;
elseif y_isscalar
allprodbase = X.basis(:,1+index_X(ix));
%allprodbase = Xibase * Y.basis(:,1+index_Y(testthese));
allprodbase = allprodbase * YB;
elseif inner_vector_product
allprodbase = X.basis(:,1+index_X(ix)).';
%allprodbase = Xibase*Y.basis(:,1+index_Y(testthese));
if ~simpleMultiplicationwithI
allprodbase = allprodbase*YB;
end
else
allprodbase = reshape(X.basis(:,1+index_X(ix)),nx,mx);
allprodbase = kron(speyemy,allprodbase);
%allprodbase = temp * Y.basis(:,1+index_Y(testthese));
allprodbase = allprodbase * YB;
end
% Keep non-zero matrices
if size(allprodbase,1)==1
nonzeromatrices = find(allprodbase);
nonzeromatrices = nonzeromatrices(find(abs(allprodbase(nonzeromatrices))>1e-12));
else
nonzeromatrices = find(sum(abs(allprodbase),1)>1e-12);
end
testthese = testthese(nonzeromatrices);
allprodbase = allprodbase(:,nonzeromatrices);
% Some data for vectorization
nyvars = length(testthese);
if prod(size(mt_x))==1 % Bug in Solaris and Linux, ML 6.X
allmt_xplusy = local_mt(:,testthese) + sparse(repmat(full(mt_x),1,nyvars));
else
allmt_xplusy = bsxfun(@plus,local_mt(:,testthese),mt_x);
% allmt_xplusy = local_mt(:,testthese) + repmat(mt_x,1,nyvars);
end
allhash = allmt_xplusy'*hash;
thesewhereactuallyused = zeros(1,nyvars);
copytofrom = ones(1,nyvars);
acounter = 0;
% Special case : x*inv(x) and similiar...
sum_to_constant = abs(allhash)<eps;
add_these = find(sum_to_constant);
if ~isempty(add_these)
prodbase = allprodbase(:,add_these);
Z.basis(:,1) = Z.basis(:,1) + sum(prodbase,2);
copytofrom(add_these) = 0;
end
indicies = find(~sum_to_constant);
indicies = indicies(:)';
allbefore_in_old = 1;
if bilinearproduct && candofastlocation
[dummy,allbefore_in_old] = ismember(allhash,possibleOldHash);
end
if bilinearproduct && candofastlocation && (nnz(allbefore_in_old)==0)
% All nonlinear variables are new, so we can create them at once
changed_mt=1;
thesewhereactuallyused = thesewhereactuallyused+1;
Z.lmi_variables = expandAllocation(Z.lmi_variables,(i+length(indicies)-1));
Z.lmi_variables(i:(i+length(indicies)-1)) = (nvar+1):(nvar+length(indicies));
nvar = nvar + length(indicies);
i = i + length(indicies);
else
for acounter = indicies
current_hash = allhash(acounter);
% Ok, braze your self for some horrible special case
% treatment etc...
if (new_mt_hash_counter == 0) || bilinearproduct % only search among old monomials
if bilinearproduct && candofastlocation
before = allbefore_in_old(acounter);
if before==0
before = [];
else
before = possibleOld(before);
end
else
before = possibleOld(sortedHashLocs(findhashsorted(possibleOldHashSorted,current_hash)));
end
else
% length(new_mt_hash_aux)
before = findhash(new_mt_hash_aux,current_hash,new_mt_hash_counter); % first among new monomials
if before
before=before+current_offset;
else
sb = 1;
cth = full(current_hash);
while isempty(before) && sb <= length(possibleOldBlocked)
testitfull = possibleOldHashSortedBlockedFull{sb};
mmm=findhashsorted(testitfull,cth);
if mmm
mmm=sortedHashLocsBlocked{sb}(mmm);
before = possibleOldBlocked{sb}(mmm);
end
sb = sb+1;
end
end
end
if before
nn = length(Z.lmi_variables);
if nn < i
Z.lmi_variables = [Z.lmi_variables zeros(1,2*i-nn)];
end
% Z.lmi_variables = expandAllocation(Z.lmi_variables,i);
Z.lmi_variables(i) = before;
else
changed_mt=1;
thesewhereactuallyused(acounter) = 1;
new_mt_hash_counter = new_mt_hash_counter + 1;
if new_mt_hash_counter>length(new_mt_hash_aux)
new_mt_hash_aux = [new_mt_hash_aux;zeros(length(new_mt_hash_aux),1)];
end
new_mt_hash_aux(new_mt_hash_counter) = current_hash;
nvar = nvar + 1;
Z.lmi_variables = expandAllocation(Z.lmi_variables,i);
Z.lmi_variables(i) = nvar;
end
i = i+1;
end
end % End y-variables
if all(copytofrom)
Z.basis = [Z.basis allprodbase];
else
Z.basis = [Z.basis allprodbase(:,find(copytofrom))];
end
if all(thesewhereactuallyused)
new_mt = [new_mt allmt_xplusy];
else
new_mt = [new_mt allmt_xplusy(:,find(thesewhereactuallyused))];
end
bsize = 100;
if new_mt_hash_counter>5
bsize = new_mt_hash_counter-1;
end
if new_mt_hash_counter > bsize
ship = new_mt_hash_aux(1:bsize);
mt_hash = [mt_hash;ship];
new_mt_hash_aux = new_mt_hash_aux(bsize+1:new_mt_hash_counter);
new_mt_hash_counter = nnz(new_mt_hash_aux);
[newHashSorted, sortednewHashLocs] = sort(ship);
possibleOldBlocked{end+1} = (1:bsize)+current_offset;
sortedHashLocsBlocked{end+1} = sortednewHashLocs;
possibleOldHashSortedBlocked{end+1} = (newHashSorted);
possibleOldHashSortedBlockedFull{end+1} = full(newHashSorted);
current_offset = current_offset + bsize;
end
end % End x-variables
if ~isempty(new_mt)
[i1,j1,k1] = find(mt);
[ii1,jj1,kk1] = find(new_mt');
uv = find(used_variables);uv=uv(:);
mt = sparse([i1(:);ii1(:)+size(mt,1)],[j1(:);uv(jj1(:))],[k1(:);kk1(:)],size(mt,1)+size(new_mt,2),size(mt,2));
end
% We pre-allocated a sufficiently long, now pick the ones we
% actually filled with values
Z.lmi_variables = Z.lmi_variables(1:i-1);
% Fucked up order (lmi_variables should be sorted)
Z = fix_variable_order(Z);
if changed_mt%~isequal(mt,oldmt)
newmt = mt(size(oldmt,1)+1:end,:);
nonlinear = ~(sum(newmt,2)==1 & sum(newmt~=0,2)==1);
newvariabletype = spalloc(size(newmt,1),1,nnz(nonlinear))';
nonlinearvariables = find(nonlinear);
newvariabletype = sparse(nonlinearvariables,ones(length(nonlinearvariables),1),3,size(newmt,1),1)';
if ~isempty(nonlinear)
%mt = internal_sdpvarstate.monomtable;
%newvariabletype(nonlinear) = 3;
quadratic = sum(newmt,2)==2;
newvariabletype(quadratic) = 2;
bilinear = max(newmt,[],2)<=1;
newvariabletype(bilinear & quadratic) = 1;
sigmonial = any(0>newmt,2) | any(newmt-fix(newmt),2);
newvariabletype(sigmonial) = 4;
end
% yalmip('setmonomtable',mt,[oldvariabletype newvariabletype],[mt_hash;new_mt_hash],oldhash);
yalmip('setmonomtable',mt,[oldvariabletype newvariabletype],[mt_hash;new_mt_hash_aux(1:new_mt_hash_counter)],oldhash);
end
if ~(x_isscalar || y_isscalar)
Z.dim(1) = X.dim(1);
Z.dim(2) = Y.dim(2);
else
Z.dim(1) = max(X.dim(1),Y.dim(1));
Z.dim(2) = max(X.dim(2),Y.dim(2));
end
catch
error(lasterr)
end
% Reset info about conic terms
Z.conicinfo = [0 0];
Z.extra.opname='';
Z.extra.createTime = definecreationtime;
Z = clean(Z);
case 2
n_X = X.dim(1);
m_X = X.dim(2);
[n_Y,m_Y] = size(Y);
x_isscalar = (n_X*m_X==1);
y_isscalar = (n_Y*m_Y==1);
if ~x_isscalar
if ((m_X~= n_Y && ~y_isscalar))
error('Inner matrix dimensions must agree.')
end
end
n = n_X;
m = m_Y;
Z = X;
if x_isscalar
if y_isscalar
if Y==0
Z = 0;
return
else
Z.basis = Z.basis*Y;
% Reset info about conic terms
Z.conicinfo = [0 0];
Z.extra.opname='';
Z = addrightfactor(Z,Y);
return
end
else
if isa(Y,'uint8') || isa(Y,'uint16') || isa(Y,'uint32') || isa(Y,'uint64')
Y = double(Y);
end
Z.dim(1) = n_Y;
Z.dim(2) = m_Y;
Z.basis = kron(Z.basis,Y(:));
Z.conicinfo = [0 0];
Z.extra.opname='';
Z = addrightfactor(Z,Y);
Z = addleftfactor(Z,speye(size(Y,1)));
Z = clean(Z);
if length(size(Y))>2
Z = reshape(reshape(Z,[],1),size(Y));
end
return
end
elseif y_isscalar
if isa(Y,'uint8') || isa(Y,'uint16') || isa(Y,'uint32') || isa(Y,'uint64')
Y = double(Y);
end
Z.dim(1) = n_X;
Z.dim(2) = m_X;
Z.basis = Z.basis*Y;
Z.conicinfo = [0 0];
Z.extra.opname='';
Z.extra.createTime = definecreationtime;
Z = addrightfactor(Z,Y);
Z = addleftfactor(Z,speye(size(Y,1)));
Z = clean(Z);
return
end
Z.dim(1) = n;
Z.dim(2) = m;
if (n_X==1) && is(X,'lpcone') && (n_Y == m_Y) && (size(X.basis,1)==size(X.basis,2-1)) && isequal(X.basis*[0 1:size(X.basis,2)-1]',(1:size(X.basis,2)-1)')
% special case to speed up x'*Q, Q square. typically
% encountered in large-scale QPs
Z.basis = [X.basis(:,1) Y.'];
else
if isa(Y,'uint8') || isa(Y,'uint16') || isa(Y,'uint32') || isa(Y,'uint64')
Y = double(Y);
end
Z.basis = kron(Y.',speye(n_X))*X.basis;
end
Z.conicinfo = [0 0];
Z.extra.opname='';
Z.extra.createTime = definecreationtime;
Z = addrightfactor(Z,Y);
Z = clean(Z);
case 1
n_Y = Y.dim(1);
m_Y = Y.dim(2);
[n_X,m_X] = size(X);
x_isscalar = (n_X*m_X==1);
y_isscalar = (n_Y*m_Y==1);
if ~x_isscalar
if ((m_X~= n_Y && ~y_isscalar))
error('Inner matrix dimensions must agree.')
end
end
n = n_X;
m = m_Y;
Z = Y;
% Special cases
if x_isscalar
if y_isscalar
if X==0
Z = 0;
return
else
Z.basis = Z.basis*X;
Z.conicinfo = [0 0];
Z.extra.opname='';
Z = addleftfactor(Z,X);
return
end
else
try
Z.basis = X*Z.basis;
catch
% This works better when low on memory in some cases
[i,j,k] = find(Y.basis);
Z.basis = sparse(i,j,X*k,size(Y.basis,1),size(Y.basis,2));
end
Z.conicinfo = [0 0];
Z.extra.opname='';
Z.extra.createTime = definecreationtime;
Z = addleftfactor(Z,X);
if X==0
Z = clean(Z);
end
return
end
elseif y_isscalar
Z.dim(1) = n_X;
Z.dim(2) = m_X;
Z.basis = X(:)*Y.basis;
Z.extra.createTime = definecreationtime;
Z = addleftfactor(Z,X);
Z = addrightfactor(Z,speye(size(X,2)));
Z = clean(Z);
if length(size(X))>2
Z = reshape(reshape(Z,[],1),size(X));
end
return
end
if m_Y==1
if issparse(X)
Z.basis = X*Y.basis;
else
if (size(X,1) > 100000) && isdiagonal(X)
try
Z.basis = bsxfun(@times,Y.basis,sparse(diag(X)));
catch
Z.basis = sparse(X)*Y.basis;
end
else
Z.basis = sparse(X)*Y.basis;
end
end
else
try
if isa(X,'uint8') || isa(X,'uint16') || isa(X,'uint32') || isa(X,'uint64')
X = double(X);
end
speyemy = speye(m_Y);
kronX = kron(speyemy,X);
Z.basis = kronX*Y.basis;
catch
disp('Multiplication of SDPVAR object caused memory error');
disp('Continuing using unvectorized version which is extremely slow');
Z.basis = [];
if isa(X,'uint8') || isa(X,'uint16') || isa(X,'uint32') || isa(X,'uint64')
X = double(X);
end
for i = 1:size(Y.basis,2);
dummy = X*reshape(Y.basis(:,i),Y.dim(1),Y.dim(2));
Z.basis = [Z.basis dummy(:)];
end
end
end
Z.dim(1) = n;
Z.dim(2) = m;
Z.conicinfo = [0 0];
Z.extra.opname='';
Z.extra.createTime = definecreationtime;
Z = addleftfactor(Z,X);
Z = clean(Z);
otherwise
error('Logical error in mtimes. Report bug')
end
function Z=clean(X)
temp = any(X.basis,1);
temp = temp(2:end);
index = find(temp);
if ~isempty(index)
Z = X;
if length(index)~=length(Z.lmi_variables)
Z.basis = Z.basis(:,[1 1+index]);
Z.lmi_variables = Z.lmi_variables(index);
end
else
Z = full(reshape(X.basis(:,1),X.dim(1),X.dim(2)));
end
function Z = fix_variable_order(Z)
% Fucked up order (lmi_variables should be sorted)
if any(diff(Z.lmi_variables)<0)
[i,j]=sort(Z.lmi_variables);
Z.basis = [Z.basis(1:end,1) Z.basis(:,j+1)];
Z.lmi_variables = Z.lmi_variables(j);
end
[un_Z_vars2] = uniquestripped(Z.lmi_variables);
if length(un_Z_vars2) < length(Z.lmi_variables)
[un_Z_vars,hh,jj] = unique(Z.lmi_variables);
if length(Z.lmi_variables) ~=length(un_Z_vars)
Z.basis = Z.basis*sparse([1 1+jj(:)'],[1 1+(1:length(jj))],ones(1,1+length(jj)))';
Z.lmi_variables = un_Z_vars;
end
end
function Z = super_fast_quadratic_multiplication(X,Y,mt,oldvariabletype,mt_hash,hash);
Q = X.basis(:,2:end);
R = Y.basis(:,2:end);
Q = (Q.')*R;
Q = Q + Q.' - diag(diag(Q));
if nnz(Q-diag(diag(Q)))==0
% Special case, only quadratic terms
% Exploit this!
n = length(X.lmi_variables);
new_mt = sparse(1:n,X.lmi_variables,2*ones(n,1),n,size(mt,2));
newvariabletype = ones(n,1)*2;
Q = diag(Q);Q = Q(:)';
else
indicies = find(tril(ones(length(Q))));
Q = Q(indicies);
Q = Q(:).';
n = length(X.lmi_variables);
V = mt(X.lmi_variables,:);
if 1
m1 = kron((1:n)',ones(n,1));
m2 = kron(ones(n,1),(1:n)');
r = reshape(1:n^2,n,n);
r = r(find(tril(r)));
m1 = m1(r);
m2 = m2(r);
VV = V';
VV = VV(:,m1) + VV(:,m2);
new_mt = VV';
else
new_mt = kron(V,ones(n,1)) + kron(ones(n,1),V);
r = reshape(1:n^2,n,n);
new_mt = new_mt(r(find(tril(r))),:);
end
newvariabletype = max((new_mt),[],2);
end
yalmip('setmonomtable',[mt;new_mt],[oldvariabletype newvariabletype'],[mt_hash;new_mt*hash],hash);
Z = X;
varbase = [(X.basis(:,1).')*Y.basis(:,2:end)+(Y.basis(:,1).')*X.basis(:,2:end) Q];
Z.basis = [(X.basis(:,1).')*Y.basis(:,1) varbase(find(varbase))];
Z.lmi_variables = [X.lmi_variables size(mt,1) + (1:length(Q))];
Z.lmi_variables = Z.lmi_variables(find(varbase));
Z.dim(1) = 1;
Z.dim(2) = 1;
Z.conicinfo = [0 0];
Z.extra.opname='';
function Z = check_for_special_case(Y,X)
% X*Y = X*log(?)
args = yalmip('getarguments',Y);
args = args.arg{1};
if isequal(X,args)
B = getbase(Y);
Z = X*B(1)-B(2)*entropy(X);
return
end
if 0%isequal(getvariables(args),getvariables(X))
% Possible f(x)*log(f(x))
[i,j,k] = find(getbase(args));
[ii,jj,kk] = find(getbase(X));
if isequal(i,ii) && isequal(j,jj)
scale = kk(1)./k(1)
if all(abs(kk./k - kk(1)./k(1)) <= 1e-12)
Z = -scale*entropy(args);
return
end
end
end
if isequal(getbase(args),[0 1]) && isequal(getbase(X),[0 1])
mt = yalmip('monomtable');
v = mt(getvariables(args),:);
vb = v(find(v));
if v(getvariables(X))==1 && min(vb)==-1 && max(vb)==1
% X * log(X / Y) = -plog(X,Y)
Z = -plog([X;recover(find(v==-1))]);
elseif v(getvariables(X))==-1 && min(vb)==-1 && max(vb)==1
% X * log(Y / X) = plot(X,Y)
Z = plog([X;recover(find(v==1))]);
else
Z = [];
end
else
Z = [];
end
function yes = isdiagonal(X)
yes = 0;
if size(X,1) == size(X,2)
[i,j] = find(X);
if all(i==j)
yes = 1;
end
end
function x = expandAllocation(x,n)
if length(x) < n
x = [x zeros(1,2*n-length(x))];
end