Skip to content

Commit 60da4d7

Browse files
authored
Merge pull request #20 from gem-library/issue#19
Issue#19
2 parents 1aa4c5c + 6c67178 commit 60da4d7

10 files changed

Lines changed: 222 additions & 18 deletions

File tree

CHANGELOG.md

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,19 @@
1-
##
1+
## [2.0.2] - 2021-07-29
22
- Added gem2 and sgem2 binary conversion tools
3+
- Added support for builtin pinv
4+
- Improved subsasgn
5+
- Improved times
6+
37

48
## [2.0.1] - 2020-12-19
59
- Compatibility with GNU Octave 6.1.0
610
- Updated MOxUnit and MOcov submodule
711
- Improved subsref
812
- Improved null
9-
- improved svds
10-
- improved eigs
11-
- improved mldivide
12-
- improved mrdivide
13+
- Improved svds
14+
- Improved eigs
15+
- Improved mldivide
16+
- Improved mrdivide
1317

1418
## [2.0.0] - 2020-05-08
1519
- Support for GNU Octave 4.2

docs/docs/functions.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -115,6 +115,7 @@ The following function can be applied to gem/sgem objects directly.
115115
| numel ||| |
116116
| num2str ||| through matlab's implementation and sprintf |
117117
| or ||| |
118+
| pinv ||| through matlab's implementation |
118119
| plot ||| |
119120
| plus ||| |
120121
| power ||| |

gem/@gem/subsasgn.m

Lines changed: 22 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -43,17 +43,36 @@
4343

4444

4545
%% If needed, we prepare a copy of the object, only this object will be modified
46-
if (length(indices) == 1) && (isempty(indices{1}))
46+
if ((length(indices) == 1) && (isempty(indices{1}))) || ...
47+
((length(indices) == 2) && (isempty(indices{1}) || isempty(indices{2})))
4748
% There is nothing to assign. But we check that the rhs does not
4849
% contain more than 1 element
4950
if numel(values) > 1
5051
error('In an assignment A(I) = B in gem::subsasgn, the number of elements in B and I must be the same');
5152
end
5253
result = this;
5354
return;
54-
else
55-
result = copy(this);
55+
elseif (length(indices) == 2) && (numel(values) == 0)
56+
% We are deleting elements
57+
58+
% Sanity check
59+
if isempty(indices{1}) || isempty(indices{2}) || (min(indices{1}) < 1) || (min(indices{2}) < 1) || (max(indices{1}) > s(1)) || (max(indices{2}) > s(2))
60+
error('Indices out of bound in gem::subsasgn')
61+
end
62+
63+
if (length(indices{1}) < s(1)) && (length(indices{2}) < s(2))
64+
error('Null assignments are only possible for full lines or columns');
65+
elseif (length(indices{1}) < s(1))
66+
% We are deleting lines
67+
subs.subs{1} = setdiff(1:s(1), indices{1});
68+
else
69+
% We are deleting columns
70+
subs.subs{2} = setdiff(1:s(2), indices{2});
71+
end
72+
result = subsref(this, subs);
73+
return;
5674
end
75+
result = copy(this);
5776

5877

5978
%% Let's make some more checks

gem/@gem/times.m

Lines changed: 29 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,8 +19,35 @@
1919
size1 = size(this);
2020
size2 = size(varargin{1});
2121

22-
if (~isequal(size1, size2)) && (prod(size1) ~= 1) && (prod(size2) ~= 1) && (prod(size1)+prod(size2) > 0)
23-
error('Incompatible size for element-wise matrix multiplication');
22+
if (~isequal(size1, size2)) && (prod(size1) ~= 1) && (prod(size2) ~= 1)
23+
if (size1(1) == size2(1)) && ((size1(2) == 1) || (size2(2) == 1))
24+
% Product of a matrix with a vertical vector, we expand the
25+
% vector to a matrix
26+
if size1(2) == 1
27+
% v.*M
28+
result = (this*ones(1, size2(2))).*varargin{1};
29+
return;
30+
else
31+
% M.*v
32+
result = this.*(varargin{1}*ones(1, size1(2)));
33+
return;
34+
end
35+
elseif (size1(2) == size2(2)) && ((size1(1) == 1) || (size2(1) == 1))
36+
% Product of a matrix with a horizontal vector, we expand the
37+
% vector to a matrix
38+
if size1(1) == 1
39+
% v.*M
40+
result = (ones(size2(1), 1)*this).*varargin{1};
41+
return;
42+
else
43+
% M.*v
44+
result = this.*(ones(size1(1), 1)*varargin{1});
45+
return;
46+
end
47+
end
48+
if prod(size1)+prod(size2) > 0
49+
error('Incompatible size for element-wise matrix multiplication');
50+
end
2451
end
2552

2653
% Now we also check the type of both objects

gem/@sgem/subsasgn.m

Lines changed: 22 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -43,17 +43,36 @@
4343

4444

4545
%% If needed, we prepare a copy of the object, only this object will be modified
46-
if (length(indices) == 1) && (isempty(indices{1}))
46+
if ((length(indices) == 1) && (isempty(indices{1}))) || ...
47+
((length(indices) == 2) && (isempty(indices{1}) || isempty(indices{2})))
4748
% There is nothing to assign. But we check that the rhs does not
4849
% contain more than 1 element
4950
if numel(values) > 1
5051
error('In an assignment A(I) = B in sgem::subsasgn, the number of elements in B and I must be the same');
5152
end
5253
result = this;
5354
return;
54-
else
55-
result = copy(this);
55+
elseif (length(indices) == 2) && (numel(values) == 0)
56+
% We are deleting elements
57+
58+
% Sanity check
59+
if isempty(indices{1}) || isempty(indices{2}) || (min(indices{1}) < 1) || (min(indices{2}) < 1) || (max(indices{1}) > s(1)) || (max(indices{2}) > s(2))
60+
error('Indices out of bound in sgem::subsasgn')
61+
end
62+
63+
if (length(indices{1}) < s(1)) && (length(indices{2}) < s(2))
64+
error('Null assignments are only possible for full lines or columns');
65+
elseif (length(indices{1}) < s(1))
66+
% We are deleting lines
67+
subs.subs{1} = setdiff(1:s(1), indices{1});
68+
else
69+
% We are deleting columns
70+
subs.subs{2} = setdiff(1:s(2), indices{2});
71+
end
72+
result = subsref(this, subs);
73+
return;
5674
end
75+
result = copy(this);
5776

5877

5978
%% Let's make some more checks

gem/@sgem/times.m

Lines changed: 29 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,8 +19,35 @@
1919
size1 = size(this);
2020
size2 = size(varargin{1});
2121

22-
if (~isequal(size1, size2)) && (prod(size1) ~= 1) && (prod(size2) ~= 1) && (prod(size1)+prod(size2) > 0)
23-
error('Incompatible size for element-wise matrix multiplication');
22+
if (~isequal(size1, size2)) && (prod(size1) ~= 1) && (prod(size2) ~= 1)
23+
if (size1(1) == size2(1)) && ((size1(2) == 1) || (size2(2) == 1))
24+
% Product of a matrix with a vertical vector, we expand the
25+
% vector to a matrix
26+
if size1(2) == 1
27+
% v.*M
28+
result = (this*ones(1, size2(2))).*varargin{1};
29+
return;
30+
else
31+
% M.*v
32+
result = this.*(varargin{1}*ones(1, size1(2)));
33+
return;
34+
end
35+
elseif (size1(2) == size2(2)) && ((size1(1) == 1) || (size2(1) == 1))
36+
% Product of a matrix with a horizontal vector, we expand the
37+
% vector to a matrix
38+
if size1(1) == 1
39+
% v.*M
40+
result = (ones(size2(1), 1)*this).*varargin{1};
41+
return;
42+
else
43+
% M.*v
44+
result = this.*(ones(size1(1), 1)*varargin{1});
45+
return;
46+
end
47+
end
48+
if prod(size1)+prod(size2) > 0
49+
error('Incompatible size for element-wise matrix multiplication');
50+
end
2451
end
2552

2653
% Now we also check the type of both objects

tests/gem/subsasgn_test.m

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,31 @@
6060
y(1, [1 2 3 4]) = [1 2 3 4]';
6161
z(1, [1 2 3 4]) = [1 2 3 4]';
6262
assert(abs(max(max(y-z))) <= 1e-6);
63+
64+
yy = y;
65+
zz = z;
66+
yy(:, 1) = [];
67+
zz(:, 1) = [];
68+
assert(abs(max(max(yy-zz))) <= 1e-6);
69+
70+
yy = y;
71+
zz = z;
72+
yy(:, 20:end) = [];
73+
zz(:, 20:end) = [];
74+
assert(abs(max(max(yy-zz))) <= 1e-6);
75+
76+
yy = y;
77+
zz = z;
78+
yy(1, :) = [];
79+
zz(1, :) = [];
80+
assert(abs(max(max(yy-zz))) <= 1e-6);
81+
82+
yy = y;
83+
zz = z;
84+
yy(20:end, :) = [];
85+
zz(20:end, :) = [];
86+
assert(abs(max(max(yy-zz))) <= 1e-6);
87+
6388
end
6489

6590
x = gem([]);
@@ -76,7 +101,6 @@
76101
x(5) = 5;
77102
x(6:7) = [6 7];
78103

79-
80104
isOctave = exist('OCTAVE_VERSION', 'builtin') ~= 0;
81105
if ~isOctave
82106
% Octave incorrectly transforms empty arrays into objects, so these

tests/gem/times_test.m

Lines changed: 30 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,35 @@
3030
validateDoubleConsistency2(@(x,y) times(x(1),double(sparse(y))), y(1,:), y(2,:));
3131
validateDoubleConsistency2(@(x,y) times(double(x(1)),y), y(1,:), y(2,:));
3232
validateDoubleConsistency2(@(x,y) times(double(sparse(x(1))),y), y(1,:), y(2,:));
33+
34+
% product with a vector
35+
validateDoubleConsistency2(@(x,y) times(x,y(1,:)), y(1,:), y(2,:));
36+
validateDoubleConsistency2(@(x,y) times(x,sparse(y(1,:))), y(1,:), y(2,:));
37+
validateDoubleConsistency2(@(x,y) times(x,double(y(1,:))), y(1,:), y(2,:));
38+
validateDoubleConsistency2(@(x,y) times(x,double(sparse(y(1,:)))), y(1,:), y(2,:));
39+
validateDoubleConsistency2(@(x,y) times(double(x),y(1,:)), y(1,:), y(2,:));
40+
validateDoubleConsistency2(@(x,y) times(double(sparse(x)),y(1,:)), y(1,:), y(2,:));
41+
42+
validateDoubleConsistency2(@(x,y) times(x(1,:),y), y(1,:), y(2,:));
43+
validateDoubleConsistency2(@(x,y) times(x(1,:),sparse(y)), y(1,:), y(2,:));
44+
validateDoubleConsistency2(@(x,y) times(x(1,:),double(y)), y(1,:), y(2,:));
45+
validateDoubleConsistency2(@(x,y) times(x(1,:),double(sparse(y))), y(1,:), y(2,:));
46+
validateDoubleConsistency2(@(x,y) times(double(x(1,:)),y), y(1,:), y(2,:));
47+
validateDoubleConsistency2(@(x,y) times(double(sparse(x(1,:))),y), y(1,:), y(2,:));
48+
49+
validateDoubleConsistency2(@(x,y) times(x,y(:,1)), y(1,:), y(2,:));
50+
validateDoubleConsistency2(@(x,y) times(x,sparse(y(:,1))), y(1,:), y(2,:));
51+
validateDoubleConsistency2(@(x,y) times(x,double(y(:,1))), y(1,:), y(2,:));
52+
validateDoubleConsistency2(@(x,y) times(x,double(sparse(y(:,1)))), y(1,:), y(2,:));
53+
validateDoubleConsistency2(@(x,y) times(double(x),y(:,1)), y(1,:), y(2,:));
54+
validateDoubleConsistency2(@(x,y) times(double(sparse(x)),y(:,1)), y(1,:), y(2,:));
55+
56+
validateDoubleConsistency2(@(x,y) times(x(:,1),y), y(1,:), y(2,:));
57+
validateDoubleConsistency2(@(x,y) times(x(:,1),sparse(y)), y(1,:), y(2,:));
58+
validateDoubleConsistency2(@(x,y) times(x(:,1),double(y)), y(1,:), y(2,:));
59+
validateDoubleConsistency2(@(x,y) times(x(:,1),double(sparse(y))), y(1,:), y(2,:));
60+
validateDoubleConsistency2(@(x,y) times(double(x(:,1)),y), y(1,:), y(2,:));
61+
validateDoubleConsistency2(@(x,y) times(double(sparse(x(:,1))),y), y(1,:), y(2,:));
3362
end
3463

3564
function test_empty
@@ -44,5 +73,5 @@
4473
shouldProduceAnError(@() times(x,x,x));
4574

4675
% sizes should match
47-
shouldProduceAnError(@() times(x, [1 2 3]));
76+
shouldProduceAnError(@() times(x, [1 2]));
4877
end

tests/sgem/subsasgn_test.m

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,31 @@
6060
y(1, [1 2 3 4]) = [1 2 3 4]';
6161
z(1, [1 2 3 4]) = [1 2 3 4]';
6262
assert(abs(max(max(y-z))) <= 1e-6);
63+
64+
yy = y;
65+
zz = z;
66+
yy(:, 1) = [];
67+
zz(:, 1) = [];
68+
assert(abs(max(max(yy-zz))) <= 1e-6);
69+
70+
yy = y;
71+
zz = z;
72+
yy(:, 20:end) = [];
73+
zz(:, 20:end) = [];
74+
assert(abs(max(max(yy-zz))) <= 1e-6);
75+
76+
yy = y;
77+
zz = z;
78+
yy(1, :) = [];
79+
zz(1, :) = [];
80+
assert(abs(max(max(yy-zz))) <= 1e-6);
81+
82+
yy = y;
83+
zz = z;
84+
yy(20:end, :) = [];
85+
zz(20:end, :) = [];
86+
assert(abs(max(max(yy-zz))) <= 1e-6);
87+
6388
end
6489

6590
x = sgem([]);

tests/sgem/times_test.m

Lines changed: 30 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,35 @@
3030
validateDoubleConsistency2(@(x,y) times(x(1),double(full(y))), y(1,:), y(2,:));
3131
validateDoubleConsistency2(@(x,y) times(double(x(1)),y), y(1,:), y(2,:));
3232
validateDoubleConsistency2(@(x,y) times(double(full(x(1))),y), y(1,:), y(2,:));
33+
34+
% product with a vector
35+
validateDoubleConsistency2(@(x,y) times(x,y(1,:)), y(1,:), y(2,:));
36+
validateDoubleConsistency2(@(x,y) times(x,sparse(y(1,:))), y(1,:), y(2,:));
37+
validateDoubleConsistency2(@(x,y) times(x,double(y(1,:))), y(1,:), y(2,:));
38+
validateDoubleConsistency2(@(x,y) times(x,double(sparse(y(1,:)))), y(1,:), y(2,:));
39+
validateDoubleConsistency2(@(x,y) times(double(x),y(1,:)), y(1,:), y(2,:));
40+
validateDoubleConsistency2(@(x,y) times(double(sparse(x)),y(1,:)), y(1,:), y(2,:));
41+
42+
validateDoubleConsistency2(@(x,y) times(x(1,:),y), y(1,:), y(2,:));
43+
validateDoubleConsistency2(@(x,y) times(x(1,:),sparse(y)), y(1,:), y(2,:));
44+
validateDoubleConsistency2(@(x,y) times(x(1,:),double(y)), y(1,:), y(2,:));
45+
validateDoubleConsistency2(@(x,y) times(x(1,:),double(sparse(y))), y(1,:), y(2,:));
46+
validateDoubleConsistency2(@(x,y) times(double(x(1,:)),y), y(1,:), y(2,:));
47+
validateDoubleConsistency2(@(x,y) times(double(sparse(x(1,:))),y), y(1,:), y(2,:));
48+
49+
validateDoubleConsistency2(@(x,y) times(x,y(:,1)), y(1,:), y(2,:));
50+
validateDoubleConsistency2(@(x,y) times(x,sparse(y(:,1))), y(1,:), y(2,:));
51+
validateDoubleConsistency2(@(x,y) times(x,double(y(:,1))), y(1,:), y(2,:));
52+
validateDoubleConsistency2(@(x,y) times(x,double(sparse(y(:,1)))), y(1,:), y(2,:));
53+
validateDoubleConsistency2(@(x,y) times(double(x),y(:,1)), y(1,:), y(2,:));
54+
validateDoubleConsistency2(@(x,y) times(double(sparse(x)),y(:,1)), y(1,:), y(2,:));
55+
56+
validateDoubleConsistency2(@(x,y) times(x(:,1),y), y(1,:), y(2,:));
57+
validateDoubleConsistency2(@(x,y) times(x(:,1),sparse(y)), y(1,:), y(2,:));
58+
validateDoubleConsistency2(@(x,y) times(x(:,1),double(y)), y(1,:), y(2,:));
59+
validateDoubleConsistency2(@(x,y) times(x(:,1),double(sparse(y))), y(1,:), y(2,:));
60+
validateDoubleConsistency2(@(x,y) times(double(x(:,1)),y), y(1,:), y(2,:));
61+
validateDoubleConsistency2(@(x,y) times(double(sparse(x(:,1))),y), y(1,:), y(2,:));
3362
end
3463

3564
function test_empty
@@ -44,5 +73,5 @@
4473
shouldProduceAnError(@() times(x,x,x));
4574

4675
% sizes should match
47-
shouldProduceAnError(@() times(x, [1 2 3]));
76+
shouldProduceAnError(@() times(x, [1 2]));
4877
end

0 commit comments

Comments
 (0)