150 lines
5.0 KiB
Mathematica
150 lines
5.0 KiB
Mathematica
|
|
function varargout = implies_internal(varargin)
|
||
|
|
|
||
|
|
X = varargin{1};
|
||
|
|
Y = varargin{2};
|
||
|
|
|
||
|
|
if nargin == 2
|
||
|
|
zero_tolerance = 1e-5;
|
||
|
|
else
|
||
|
|
zero_tolerance = varargin{3};
|
||
|
|
end
|
||
|
|
|
||
|
|
% Normalize
|
||
|
|
if isa(X,'constraint')
|
||
|
|
X = lmi(X,[],[],1);
|
||
|
|
end
|
||
|
|
if isa(Y,'constraint')
|
||
|
|
Y = lmi(Y,[],[],1);
|
||
|
|
end
|
||
|
|
|
||
|
|
% % Special case something implies binary == 1
|
||
|
|
% if isa(Y,'lmi') && length(Y)==1 & isa(Y,'equality')
|
||
|
|
% y = sdpvar(Y);
|
||
|
|
% if isa(y,'binary') && getbase(y)==[1 -1] | getbase(y)==[-1 1]
|
||
|
|
%
|
||
|
|
% end
|
||
|
|
% end
|
||
|
|
|
||
|
|
if isa(X,'sdpvar') & isa(Y,'sdpvar')
|
||
|
|
varargout{1} = (Y >= X);
|
||
|
|
elseif isa(X,'sdpvar') & isa(Y,'lmi')
|
||
|
|
varargout{1} = binary_implies_constraint(X,Y);
|
||
|
|
elseif isa(X,'lmi') & isa(Y,'sdpvar')
|
||
|
|
varargout{1} = constraint_implies_binary(X,Y,zero_tolerance);
|
||
|
|
elseif isa(X,'lmi') & isa(Y,'lmi')
|
||
|
|
% Glue them using a binary
|
||
|
|
binvar d
|
||
|
|
varargout{1} = [constraint_implies_binary(X,d,zero_tolerance), binary_implies_constraint(d,Y)];
|
||
|
|
end
|
||
|
|
|
||
|
|
function F = binary_implies_constraint(X,Y)
|
||
|
|
if isempty(Y) | length(Y)==0
|
||
|
|
F = [];
|
||
|
|
return
|
||
|
|
end
|
||
|
|
switch settype(Y)
|
||
|
|
case 'multiple'
|
||
|
|
% recursive call if we have a mixture
|
||
|
|
Y1 = Y(find(is(Y,'equality')));
|
||
|
|
Y2 = Y(find(is(Y,'elementwise')));
|
||
|
|
Y3 = Y(find(is(Y,'socp')));
|
||
|
|
Y4 = Y(find(is(Y,'sdp')));
|
||
|
|
F = [binary_implies_constraint(X,Y1),
|
||
|
|
binary_implies_constraint(X,Y2),
|
||
|
|
binary_implies_constraint(X,Y3),
|
||
|
|
binary_implies_constraint(X,Y4)];
|
||
|
|
|
||
|
|
case 'elementwise' % X --> (Y(:)>=0)
|
||
|
|
Y = sdpvar(Y);
|
||
|
|
Y = Y(:);
|
||
|
|
F = binary_implies_linearnegativeconstraint(-Y,X);
|
||
|
|
|
||
|
|
case 'equality' % X --> (Y(:)==0)
|
||
|
|
Y = sdpvar(Y);
|
||
|
|
Y = Y(:);
|
||
|
|
[M,m,infbound]=derivebounds(Y);
|
||
|
|
if infbound
|
||
|
|
warning('You have unbounded variables in IMPLIES leading to a lousy big-M relaxation.');
|
||
|
|
end
|
||
|
|
F = binary_implies_linearequality(Y,reshape(X,[],1),M,m);
|
||
|
|
% F = [F, binary_implies_linearnegativeconstraint(-Y,reshape(X,[],1),-m,-M)];
|
||
|
|
|
||
|
|
case 'sdp' % X --> (Y>=0)
|
||
|
|
if length(X)>1
|
||
|
|
error('IMPLIES not implemented for vector x implies lmi.');
|
||
|
|
end
|
||
|
|
Y = sdpvar(Y);
|
||
|
|
% Elements in matrix
|
||
|
|
y = Y(find(triu(ones(length(Y)))));
|
||
|
|
% Derive bounds on all elements
|
||
|
|
[M,m,infbound]=derivebounds(y);
|
||
|
|
if infbound
|
||
|
|
warning('You have unbounded variables in IMPLIES leading to a lousy big-M relaxation.');
|
||
|
|
end
|
||
|
|
% Crude lower bound eig(Y) > -max(abs(Y(:))*n*I
|
||
|
|
m=-max(abs([M;m]))*length(Y);
|
||
|
|
% Big-M relaxation...
|
||
|
|
F = [Y >= (1-X)*m*eye(length(Y))];
|
||
|
|
|
||
|
|
otherwise
|
||
|
|
error('IMPLIES only implemented for linear (in)equalities and semidefinite constraints');
|
||
|
|
end
|
||
|
|
|
||
|
|
function F = constraint_implies_binary(X,Y,zero_tolerance)
|
||
|
|
|
||
|
|
switch settype(X)
|
||
|
|
case 'multiple'
|
||
|
|
X1 = X(find(is(X,'equality')));
|
||
|
|
X2 = X(find(is(X,'elementwise')));
|
||
|
|
if length(X1)+length(X2) < length(X)
|
||
|
|
disp('Binary variables can only be activated by linear (in)equalities in IMPLIES');
|
||
|
|
end
|
||
|
|
d1 = binvar(length(Y),1);
|
||
|
|
d2 = binvar(length(Y),1);
|
||
|
|
F1 = constraint_implies_binary(X1,d1,zero_tolerance);
|
||
|
|
F2 = constraint_implies_binary(X2,d2,zero_tolerance);
|
||
|
|
F = [F1, F2, Y >= d1+d2-1];
|
||
|
|
|
||
|
|
case 'elementwise'
|
||
|
|
X = -sdpvar(X);
|
||
|
|
|
||
|
|
if length(Y)==length(X)
|
||
|
|
% Elementwise implies, either by user or internally
|
||
|
|
F = linearnegativeconstraint_implies_binary(X,Y,[],[],zero_tolerance);
|
||
|
|
elseif length(Y)== 1
|
||
|
|
% Many rows should imply one. Create intermediate and use AND
|
||
|
|
d = binvar(length(X),1);
|
||
|
|
F = [linearnegativeconstraint_implies_binary(X,d,[],[],zero_tolerance), Y >= sum(d)+1-length(X)];
|
||
|
|
else
|
||
|
|
error('Inconsistent sizes in implies_internal')
|
||
|
|
end
|
||
|
|
|
||
|
|
case 'equality'
|
||
|
|
X = sdpvar(X);X = reshape(X,[],1);
|
||
|
|
n = length(X);
|
||
|
|
if 0 % isequal(getbase(X),[-ones(n,1) eye(n)]) | isequal(getbase(X),[ones(n,1) -eye(n)]) & all(ismember(depends(X),yalmip('binvariables')));
|
||
|
|
% Smart code for X == 1 implies Y
|
||
|
|
F = [Y(:) >= recover(getvariables(X))];
|
||
|
|
else
|
||
|
|
d = binvar(length(X),2,'full');
|
||
|
|
%F = linearnegativeconstraint_implies_binary([-zero_tolerance-X;X-zero_tolerance],[d(:,1);d(:,2)],[],[],zero_tolerance);
|
||
|
|
F = linearnegativeconstraint_implies_binary([-zero_tolerance-X;X-zero_tolerance],[d(:,1);d(:,2)],[],[],zero_tolerance/100);
|
||
|
|
if length(X)==length(Y)
|
||
|
|
% elementwise version
|
||
|
|
F = [F, Y >= d(:,1)+d(:,2)-1];
|
||
|
|
elseif length(Y)==1
|
||
|
|
% All elements must be zero
|
||
|
|
F = [F, Y >= sum(sum(d))-2*length(X)+1];
|
||
|
|
else
|
||
|
|
error('Inconsistent sizes in implies_internal')
|
||
|
|
end
|
||
|
|
end
|
||
|
|
|
||
|
|
otherwise
|
||
|
|
disp('IMPLIES not implemented for this case');
|
||
|
|
error('IMPLIES not implemented for this case');
|
||
|
|
end
|
||
|
|
|
||
|
|
|
||
|
|
|