bug #51412: strcmp with multidimensional char arrays

 Submitter: Guillaume Submitted: Fri 07 Jul 2017 09:48:27 AM UTC Category: Octave Function Severity: 3 - Normal Priority: 5 - Normal Item Group: Matlab Compatibility Status: Confirmed Assigned to: None Originator Name: Guillaume Open/Closed:  * Open Release:  * dev Operating System:  * Any Fixed Release: None Planned Release: None
* Mandatory Fields

Add a New Comment Rich Markup

Discussion

Tue 13 Dec 2022 09:57:02 PM UTC, comment #10:

just providing an update with octave 8.0.1 (hg id: 24bd675bceab) and matlab 2022b:

no change in the comment #0 response except the 2D case in Octave now gives a warning:

"warning: multi-row character matrix converted to a string, only the first row is used"

adding in the Matlab behavior with string arrays:

%1D array
>> a = string('abc')
a =
"abc"
>> strcmp(a,a)
ans =
logical
1
>> strcmp(a,{a})
ans =
logical
0
>> strcmp({a},{a})
ans =
logical
0
>> strcmp(a,{a,a})
ans =
1×2 logical array
0   0
>> strcmp({a},{a,a})
ans =
1×2 logical array
0   0
>> a = ["abc","def"]
a =
1×2 string array
"abc"    "def"
>> strcmp(a,a)
ans =
1×2 logical array
1   1
>> strcmp(a,{a})
ans =
1×2 logical array
0   0
>> strcmp({a},{a})
ans =
logical
0
>> strcmp(a,{a,a})
ans =
1×2 logical array
0   0
>> strcmp({a},{a,a})
ans =
1×2 logical array
0   0

%2D array
>> a = ["abc","def"; "ghi", "jkl"]
a =
2×2 string array
"abc"    "def"
"ghi"    "jkl"
>> strcmp(a,a)
ans =
2×2 logical array
1   1
1   1
>> strcmp({a},{a})
ans =
logical
0
>> strcmp(a,{a,a})
Error using strcmp
Inputs must be the same size or either one can be a scalar.
>> strcmp({a},{a,a})
ans =
1×2 logical array
0   0

%3D array
>> a = cat(3,a,a)
2×2×2 string array
a(:,:,1) =
"abc"    "def"
"ghi"    "jkl"
a(:,:,2) =
"abc"    "def"
"ghi"    "jkl"
>> strcmp(a,a)
2×2×2 logical array
ans(:,:,1) =
1   1
1   1
ans(:,:,2) =
1   1
1   1
>> strcmp(a,{a})
2×2×2 logical array
ans(:,:,1) =
0   0
0   0
ans(:,:,2) =
0   0
0   0
>> strcmp({a},{a})
ans =
logical
0
>> strcmp(a,{a,a})
Error using strcmp
Inputs must be the same size or either one can be a scalar.
>> strcmp({a},{a,a})
ans =
1×2 logical array
0   0

also, mike's check in comment #5, in octave 8.0.1:

>> a = ['aaa'; 'bbb'];
>> string_value (a)
error: 'string_value' undefined near line 1, column 1
>> cellstr_value (a)
error: 'cellstr_value' undefined near line 1, column 1

so it seems those functions no longer exist in octave?

and Rik's comment #6 and comment #7 tests:

Matlab 2022b:

>> x = {['aa'; 'bb']; ['aa'; 'cc']}
x =
2×1 cell array
{2×2 char}
{2×2 char}
>> strcmp (x, 'aa')
ans =
2×1 logical array
0
0
>> strcmp (x, {'aa'})
ans =
2×1 logical array
0
0
>> strcmp (x, {'aa';'cc'})
ans =
2×1 logical array
0
0
>> strcmp (x, {['aa';'cc']})
ans =
2×1 logical array
0
1
>> x = {['ab']; ['cd'; 'ef']}
x =
2×1 cell array
{'ab'    }
{2×2 char}
>> iscellstr (x)
ans =
logical
1
>> y = {['ab']; ['c'; 'e']}
y =
2×1 cell array
{'ab'    }
{2×1 char}
>> iscellstr (y)
ans =
logical
1

and the same tests in Octave 8.0.1:

ﻳ誂>> x = {['aa'; 'bb']; ['aa'; 'cc']};
>> strcmp (x, 'aa')
warning: multi-row character matrix converted to a string, only the first row is used
warning: multi-row character matrix converted to a string, only the first row is used
ans =

1
1

>> strcmp (x, {'aa'})
ans =

1
1

>> strcmp (x, {'aa';'cc'})
ans =

1
0

>> strcmp (x, {['aa';'cc']})
warning: multi-row character matrix converted to a string, only the first row is used
ans =

1
1

>> x = {['ab']; ['cd'; 'ef']}
x =
{
[1,1] = ab
[2,1] =

cd
ef

}

>> iscellstr (x)
ans = 1
>> y = {['ab']; ['c'; 'e']}
y =
{
[1,1] = ab
[2,1] =

c
e

}

>> iscellstr (y)
ans = 1

Nicholas Jankowski <nrjank>
Tue 11 Jul 2017 04:25:48 PM UTC, comment #9:

This does need to be tackled, but it is not going to be easy.  Matlab's documentation refers to cellstr as

"A cell array of character vectors is a cell array where every cell contains a character vector."

Octave has assumed, quite reasonably, that a character vector is a 1xN char array.  But the code examples clearly show that Matlab doesn't require a vector---It only requires a char array object for each cell.

Also, Matlab has recently introduced string arrays which are yet another fundamental object type to join char arrays and cellstrs.  Eventually Octave will probably support these so it makes sense that at the same time that is_string, string_value, and cellstr_value are overhauled to make them Matlab compatible, that we also address string arrays.  In fact, we might want to reserve is_string to test for the new string array class and use another function name (maybe ischar?) to test if something is just a char array.

Some more slightly bad news.  I did work up a cset that fixes the issue with strcmp, but by avoiding cellstr it is 10X slower.

Rik <rik5>
Tue 11 Jul 2017 09:04:07 AM UTC, comment #8:

MATLAB 2016a

x = {['aa'; 'bb']; ['aa'; 'cc']};
strcmp (x, 'aa')
strcmp (x, {'aa'})
strcmp (x, {'aa';'cc'})
strcmp (x, {['aa';'cc']})
ans =
0
0
ans =
0
0
ans =
0
0
ans =
0
1

x = {['ab']; ['cd'; 'ef']}
iscellstr (x)
y = {['ab']; ['c'; 'e']}
iscellstr (y)
x =
'ab'
[2x2 char]
ans =
1
y =
'ab'
[2x1 char]
ans =
1

Ceral Paquet <octavebugs>
Mon 10 Jul 2017 08:53:14 PM UTC, comment #7:

Another test for someone with Matlab

x = {['ab']; ['cd'; 'ef']}
iscellstr (x)
y = {['ab']; ['c'; 'e']}
iscellstr (y)

If I read the documentation correctly, Matlab should return false for both cases.

Rik <rik5>
Mon 10 Jul 2017 06:39:05 PM UTC, comment #6:

Could someone with matlab test this?

x = {['aa'; 'bb']; ['aa'; 'cc']};
strcmp (x, 'aa')
strcmp (x, {'aa'})
strcmp (x, {'aa';'cc'})
strcmp (x, {['aa';'cc']})

Rik <rik5>
Mon 10 Jul 2017 05:16:57 PM UTC, comment #5:

In addition I think we need to be wary about the definitions of "string" and "cell string" in this function, and places where string_value and cellstr_value are used to compare two operands.

The is_string and iscellstr tests return true for values that cannot be exactly represented by our normal definition of what a "string" is.

Simple example of a two-dimensional character matrix:

>> a = ['aaa'; 'bbb'];
>> string_value (a)
ans = aaa
>> cellstr_value (a)
ans =
{
[1,1] = aaa
}

The string_value method truncates a multi-dimensional character vector to return just the first row (this has been discussed in other bug reports). The cellstr_value method calls string_value.

Mike Miller <mtmiller>
Mon 10 Jul 2017 04:59:17 PM UTC, comment #4:

Documentation for Matlab's behavior is at http://www.mathworks.com/help/matlab/ref/strcmp.html.

Mixed char matrix / cellstr comparisons are tricky.  In bug #51187 I had to change the ismember functionality specifically for Matlab compatibility

The do_strcmp_fun function is in libinterp/corefcn/strfns.cc.  I see that there are several cases to handle broadcasting correctly.  For example, when doing

strcmp ("b", {"a", "b", "c", "d"})

I have to rebuild Octave with debugging features in order to test what is going on, but it may be an obvious extra case in the if/else tree needs to be added to handle this.

Rik <rik5>
Mon 10 Jul 2017 04:53:47 PM UTC, comment #3:

Ok, so our definition of "iscellstr" is correct, but something for which "iscellstr" returns true cannot necessarily be turned into a one-dimensional array of strings.

Mike Miller <mtmiller>
Mon 10 Jul 2017 09:25:39 AM UTC, comment #2:

Here is the Matlab output for the same calls:

>> a = ['aa'; 'bb'];
>> strcmp (a, {a})

ans =

2x1 logical array

0
0

>> strcmp (a, {a,a})

ans =

1x2 logical array

0   0

>> a = ['aaa'; 'bbb'; 'ccc'];
>> strcmp (a, {a})

ans =

3x1 logical array

0
0
0

>> strcmp (a, {a,a})
Error using strcmp
The number of rows of the string matrix must match the number of elements in the cell vector.

>> a = repmat ('a', 3, 3, 3);
>> iscellstr({a})

ans =

logical

1

Guillaume <gyom>
Fri 07 Jul 2017 08:04:53 PM UTC, comment #1:

So to me the immediately interesting cases are

>> a = ['aa'; 'bb'];
>> strcmp (a, {a})
ans =

1
0

>> strcmp (a, {a,a})
ans =

1  0

>> a = ['aaa'; 'bbb'; 'ccc'];
>> strcmp (a, {a})
ans =

1
0
0

>> strcmp (a, {a,a})
ans = 0

The 3-dimensional cases probably fail because

>> a = repmat ('a', 3, 3, 3);
>> iscellstr({a})
ans = 1

but elsewhere in Octave's baseline, code expects that if something is a cellstr, it can be converted into a vector of strings.

What does Matlab iscellstr return in this case?

Mike Miller <mtmiller>
Fri 07 Jul 2017 09:48:27 AM UTC, original submission:

strcmp seems to differ from Matlab when the input is not a row vector, see with the following code:

% 1D array
a = char(ceil(rand(1,5)*5+'a'));
strcmp(a,a)
strcmp(a,{a})
strcmp({a},{a})
strcmp(a,{a,a})
strcmp({a},{a,a})

% 2D array
a = char(ceil(rand(2,5)*5+'a'));
strcmp(a,a)
strcmp(a,{a})
strcmp({a},{a})
strcmp(a,{a,a})
strcmp({a},{a,a})

% 3D array
a = char(ceil(rand(2,5,2)*5+'a'));
strcmp(a,a)
strcmp(a,{a})
strcmp({a},{a})
strcmp(a,{a,a})
strcmp({a},{a,a})

In Octave:

octave:1> % 1D array
octave:1> a = char(ceil(rand(1,5)*5+'a'));
octave:2> strcmp(a,a)
ans = 1
octave:3> strcmp(a,{a})
ans = 1
octave:4> strcmp({a},{a})
ans = 1
octave:5> strcmp(a,{a,a})
ans =

1  1

octave:6> strcmp({a},{a,a})
ans =

1  1

octave:7>
octave:7> % 2D array
octave:7> a = char(ceil(rand(2,5)*5+'a'));
octave:8> strcmp(a,a)
ans = 1
octave:9> strcmp(a,{a})
ans =

1
0

octave:10> strcmp({a},{a})
ans = 1
octave:11> strcmp(a,{a,a})
ans =

1  0

octave:12> strcmp({a},{a,a})
ans =

1  1

octave:13>
octave:13> % 3D array
octave:13> a = char(ceil(rand(2,5,2)*5+'a'));
octave:14> strcmp(a,a)
ans = 1
octave:15> strcmp(a,{a})
error: invalid conversion of charNDArray to string_vector
octave:15> strcmp({a},{a})
error: invalid conversion of charNDArray to string
octave:15> strcmp(a,{a,a})
error: invalid conversion of charNDArray to string_vector
octave:15> strcmp({a},{a,a})
error: invalid conversion of charNDArray to string

while in Matlab:

>> % 1D array
>> a = char(ceil(rand(1,5)*5+'a'));
>> strcmp(a,a)
ans =
logical
1
>> strcmp(a,{a})
ans =
logical
1
>> strcmp({a},{a})
ans =
logical
1
>> strcmp(a,{a,a})
ans =
1x2 logical array
1   1
>> strcmp({a},{a,a})
ans =
1x2 logical array
1   1
>>
>> % 2D array
>> a = char(ceil(rand(2,5)*5+'a'));
>> strcmp(a,a)
ans =
logical
1
>> strcmp(a,{a})
ans =
2x1 logical array
0
0
>> strcmp({a},{a})
ans =
logical
1
>> strcmp(a,{a,a})
ans =
1x2 logical array
0   0
>> strcmp({a},{a,a})
ans =
1x2 logical array
1   1
>>
>> % 3D array
>> a = char(ceil(rand(2,5,2)*5+'a'));
>> strcmp(a,a)
ans =
logical
1
>> strcmp(a,{a})
ans =
2x1 logical array
0
0
>> strcmp({a},{a})
ans =
logical
1
>> strcmp(a,{a,a})
ans =
1x2 logical array
0   0
>> strcmp({a},{a,a})
ans =
1x2 logical array
1   1

Guillaume <gyom>

Attached Files

(Note: upload size limit is set to 16384 kB, after insertion of the required escape characters.)

Attach Files:

Comment:

No files currently attached

Dependencies

Depends on the following items: None found

Items that depend on this one: None found

Mail Notification Carbon-Copy List

Carbon-Copy List
• -email is unavailable- added by nrjank (Posted a comment)
• -email is unavailable- added by octavebugs (Posted a comment)
• -email is unavailable- added by rik5 (Posted a comment)
• -email is unavailable- added by mtmiller (Posted a comment)
• -email is unavailable- added by gyom (Submitted the item)
•

There are 0 votes so far. Votes easily highlight which items people would like to see resolved in priority, independently of the priority of the item set by tracker managers.

Only group members can vote.

History

Follows 1 latest change.

Date Changed by Updated Field Previous Value => Replaced by
2017-07-11 rik5 StatusNone Confirmed