Skip to content

Commit

Permalink
apacheGH-37472: [MATLAB] Implement the isequal() method on `arrow.t…
Browse files Browse the repository at this point in the history
…ype.Type` (apache#37474)

### Rationale for this change

Currently, it's not possible to determine if two `arrow.type.Type` instances are equal because the `isequal()` method always returns `false` by default:

```matlab

>> t = arrow.int32()

t = 

  Int32Type with properties:

    ID: Int32

% Compare a with itself
>> tf = isequal(t, t)

tf =

  logical

   0

```

### What changes are included in this PR?

1. Implemented the `isequal` method on all concrete subclasses of `arrow.type.Type` 
2. Added a new method called `isEqual()` on the `arrow::matlab::type::proxy::Type` class.

**Example Usage**

```matlab
>> t1 = arrow.timestamp(TimeUnit="Second");
>> t2 = arrow.timestamp(TimeUnit="Microsecond");
>> t3 = arrow.timestamp(TimeUnit="Second");

% Compare t1 and t2
>> tf1 = isequal(t1, t2)

tf1 =

  logical

   0

% Compare t1 and t3
>> tf2 = isequal(t1, t3)

tf2 =

  logical

   1
```

### Are these changes tested?

Yes. Added test cases verifying `isequal` behaves as expected for all concrete subclasses of `arrow.type.Type`.

### Are there any user-facing changes?

Yes. Users can now use the `isequal` method to compare `arrow.type.Type` instances for equality.

### Future Directions

1. Implement the `isequal` method for `arrow.type.Field`
2. Implement the `isequal` method for `arrow.tabular.Schema` 

* Closes: apache#37472

Authored-by: Sarah Gilmore <sgilmore@mathworks.com>
Signed-off-by: Kevin Gurney <kgurney@mathworks.com>
  • Loading branch information
sgilmore10 authored and dgreiss committed Feb 17, 2024
1 parent 183db32 commit 538fecc
Show file tree
Hide file tree
Showing 19 changed files with 686 additions and 1 deletion.
24 changes: 24 additions & 0 deletions matlab/src/cpp/arrow/matlab/type/proxy/type.cc
Original file line number Diff line number Diff line change
Expand Up @@ -17,11 +17,14 @@

#include "arrow/matlab/type/proxy/type.h"

#include "libmexclass/proxy/ProxyManager.h"

namespace arrow::matlab::type::proxy {

Type::Type(std::shared_ptr<arrow::DataType> type) : data_type{std::move(type)} {
REGISTER_METHOD(Type, typeID);
REGISTER_METHOD(Type, numFields);
REGISTER_METHOD(Type, isEqual);
}

std::shared_ptr<arrow::DataType> Type::unwrap() {
Expand All @@ -44,5 +47,26 @@ namespace arrow::matlab::type::proxy {
context.outputs[0] = num_fields_mda;
}

void Type::isEqual(libmexclass::proxy::method::Context& context) {
namespace mda = ::matlab::data;

const mda::TypedArray<uint64_t> type_proxy_ids = context.inputs[0];

bool is_equal = true;
const auto check_metadata = false;
for (const auto& type_proxy_id : type_proxy_ids) {
// Retrieve the Type proxy from the ProxyManager
auto proxy = libmexclass::proxy::ProxyManager::getProxy(type_proxy_id);
auto type_proxy = std::static_pointer_cast<proxy::Type>(proxy);
auto type_to_compare = type_proxy->unwrap();

if (!data_type->Equals(type_to_compare, check_metadata)) {
is_equal = false;
break;
}
}
mda::ArrayFactory factory;
context.outputs[0] = factory.createScalar(is_equal);
}
}

2 changes: 2 additions & 0 deletions matlab/src/cpp/arrow/matlab/type/proxy/type.h
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,8 @@ class Type : public libmexclass::proxy::Proxy {

void numFields(libmexclass::proxy::method::Context& context);

void isEqual(libmexclass::proxy::method::Context& context);

std::shared_ptr<arrow::DataType> data_type;
};

Expand Down
35 changes: 34 additions & 1 deletion matlab/src/matlab/+arrow/+type/Type.m
Original file line number Diff line number Diff line change
Expand Up @@ -48,5 +48,38 @@
propgrp = matlab.mixin.util.PropertyGroup(proplist);
end
end


methods
function tf = isequal(obj, varargin)

narginchk(2, inf);
tf = false;

proxyIDs = zeros([numel(obj) numel(varargin)], "uint64");

for ii = 1:numel(varargin)
type = varargin{ii};
if ~isa(type, "arrow.type.Type") || ~isequal(size(obj), size(type))
% Return early if type is not an arrow.type.Type or if
% the dimensions of obj and type do not match.
return;
end

% type(:) flattens N-dimensional arrays into a column
% vector before collecting the Proxy properties into a
% row vector.
proxies = [type(:).Proxy];
proxyIDs(:, ii) = [proxies.ID];
end

for ii = 1:numel(obj)
% Invoke isEqual proxy method on each Type
% in the object array
tf = obj(ii).Proxy.isEqual(proxyIDs(ii, :));
if ~tf
return;
end
end
end
end
end
36 changes: 36 additions & 0 deletions matlab/test/arrow/type/tBooleanType.m
Original file line number Diff line number Diff line change
Expand Up @@ -22,4 +22,40 @@
BitWidth = int32(1)
ClassName = "arrow.type.BooleanType"
end

methods(Test)
function IsEqualTrue(testCase)
% Verifies isequal method of arrow.type.BooleanType returns true if
% these conditions are met:
%
% 1. All input arguments have a class type arrow.type.BooleanType
% 2. All inputs have the same size

% Scalar BooleanType arrays
boolType1 = arrow.boolean();
boolType2 = arrow.boolean();
testCase.verifyTrue(isequal(boolType1, boolType2));

% Non-scalar BooleanType arrays
typeArray1 = [boolType1 boolType1];
typeArray2 = [boolType2 boolType2];
testCase.verifyTrue(isequal(typeArray1, typeArray2));
end

function IsEqualFalse(testCase)
% Verifies the isequal method of arrow.type.BooleanType returns
% false when expected.

% Pass a different arrow.type.Type subclass to isequal
boolType = arrow.boolean();
int32Type = arrow.int32();
testCase.verifyFalse(isequal(boolType, int32Type));
testCase.verifyFalse(isequal([boolType boolType], [int32Type int32Type]));

% BooleanType arrays have different sizes
typeArray1 = [boolType boolType];
typeArray2 = [boolType boolType]';
testCase.verifyFalse(isequal(typeArray1, typeArray2));
end
end
end
34 changes: 34 additions & 0 deletions matlab/test/arrow/type/tDate32Type.m
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,40 @@ function InvalidProxy(testCase)
testCase.verifyError(@() arrow.type.Date32Type(proxy), "arrow:proxy:ProxyNameMismatch");
end

function IsEqualTrue(testCase)
% Verifies isequal method of arrow.type.Date32Type returns true if
% these conditions are met:
%
% 1. All input arguments have a class type arrow.type.Date32Type
% 2. All inputs have the same size

% Scalar Date32Type arrays
date32Type1 = arrow.date32();
date32Type2 = arrow.date32();
testCase.verifyTrue(isequal(date32Type1, date32Type2));

% Non-scalar Date32Type arrays
typeArray1 = [date32Type1 date32Type1];
typeArray2 = [date32Type2 date32Type2];
testCase.verifyTrue(isequal(typeArray1, typeArray2));
end

function IsEqualFalse(testCase)
% Verifies the isequal method of arrow.type.Date32Type returns
% false when expected.

% Pass a different arrow.type.Type subclass to isequal
date32Type = arrow.date32();
int32Type = arrow.int32();
testCase.verifyFalse(isequal(date32Type, int32Type));
testCase.verifyFalse(isequal([date32Type date32Type], [int32Type int32Type]));

% Date32Type arrays have different sizes
typeArray1 = [date32Type date32Type];
typeArray2 = [date32Type date32Type]';
testCase.verifyFalse(isequal(typeArray1, typeArray2));
end

end

end
36 changes: 36 additions & 0 deletions matlab/test/arrow/type/tFloat32Type.m
Original file line number Diff line number Diff line change
Expand Up @@ -22,4 +22,40 @@
BitWidth = int32(32)
ClassName = "arrow.type.Float32Type"
end

methods(Test)
function IsEqualTrue(testCase)
% Verifies isequal method of arrow.type.Float32Type returns true if
% these conditions are met:
%
% 1. All input arguments have a class type arrow.type.Float32Type
% 2. All inputs have the same size

% Scalar Float32Type arrays
float32Type1 = arrow.float32();
float32Type2 = arrow.float32();
testCase.verifyTrue(isequal(float32Type1, float32Type2));

% Non-scalar Float32Type arrays
typeArray1 = [float32Type1 float32Type1];
typeArray2 = [float32Type2 float32Type2];
testCase.verifyTrue(isequal(typeArray1, typeArray2));
end

function IsEqualFalse(testCase)
% Verifies the isequal method of arrow.type.Float32Type returns
% false when expected.

% Pass a different arrow.type.Type subclass to isequal
float32Type = arrow.float32();
int32Type = arrow.int32();
testCase.verifyFalse(isequal(float32Type, int32Type));
testCase.verifyFalse(isequal([float32Type float32Type], [int32Type int32Type]));

% Float32Type arrays have different sizes
typeArray1 = [float32Type float32Type];
typeArray2 = [float32Type float32Type]';
testCase.verifyFalse(isequal(typeArray1, typeArray2));
end
end
end
35 changes: 35 additions & 0 deletions matlab/test/arrow/type/tFloat64Type.m
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,41 @@
TypeID = arrow.type.ID.Float64
BitWidth = int32(64)
ClassName = "arrow.type.Float64Type"
end

methods(Test)
function IsEqualTrue(testCase)
% Verifies isequal method of arrow.type.Float64Type returns true if
% these conditions are met:
%
% 1. All input arguments have a class type arrow.type.Float64Type
% 2. All inputs have the same size

% Scalar Float64Type arrays
float64Type1 = arrow.float64();
float64Type2 = arrow.float64();
testCase.verifyTrue(isequal(float64Type1, float64Type2));

% Non-scalar Float64Type arrays
typeArray1 = [float64Type1 float64Type1];
typeArray2 = [float64Type2 float64Type2];
testCase.verifyTrue(isequal(typeArray1, typeArray2));
end

function IsEqualFalse(testCase)
% Verifies the isequal method of arrow.type.Float64Type returns
% false when expected.

% Pass a different arrow.type.Type subclass to isequal
float64Type = arrow.float64();
int32Type = arrow.int32();
testCase.verifyFalse(isequal(float64Type, int32Type));
testCase.verifyFalse(isequal([float64Type float64Type], [int32Type int32Type]));

% Float64Type arrays have different sizes
typeArray1 = [float64Type float64Type];
typeArray2 = [float64Type float64Type]';
testCase.verifyFalse(isequal(typeArray1, typeArray2));
end
end
end
36 changes: 36 additions & 0 deletions matlab/test/arrow/type/tInt16Type.m
Original file line number Diff line number Diff line change
Expand Up @@ -22,4 +22,40 @@
BitWidth = int32(16)
ClassName = "arrow.type.Int16Type"
end

methods(Test)
function IsEqualTrue(testCase)
% Verifies isequal method of arrow.type.Int16Type returns true if
% these conditions are met:
%
% 1. All input arguments have a class type arrow.type.Int16Type
% 2. All inputs have the same size

% Scalar Int16Type arrays
int16Type1 = arrow.int16();
int16Type2 = arrow.int16();
testCase.verifyTrue(isequal(int16Type1, int16Type2));

% Non-scalar Int16Type arrays
typeArray1 = [int16Type1 int16Type1];
typeArray2 = [int16Type2 int16Type2];
testCase.verifyTrue(isequal(typeArray1, typeArray2));
end

function IsEqualFalse(testCase)
% Verifies the isequal method of arrow.type.Int16Type returns
% false when expected.

% Pass a different arrow.type.Type subclass to isequal
int16Type = arrow.int16();
int32Type = arrow.int32();
testCase.verifyFalse(isequal(int16Type, int32Type));
testCase.verifyFalse(isequal([int16Type int16Type], [int32Type int32Type]));

% Int16Type arrays have different sizes
typeArray1 = [int16Type int16Type];
typeArray2 = [int16Type int16Type]';
testCase.verifyFalse(isequal(typeArray1, typeArray2));
end
end
end
36 changes: 36 additions & 0 deletions matlab/test/arrow/type/tInt32Type.m
Original file line number Diff line number Diff line change
Expand Up @@ -22,4 +22,40 @@
BitWidth = int32(32)
ClassName = "arrow.type.Int32Type"
end

methods(Test)
function IsEqualTrue(testCase)
% Verifies isequal method of arrow.type.Int32Type returns true if
% these conditions are met:
%
% 1. All input arguments have a class type arrow.type.Int32Type
% 2. All inputs have the same size

% Scalar Int32Type arrays
int32Type1 = arrow.int32();
int32Type2 = arrow.int32();
testCase.verifyTrue(isequal(int32Type1, int32Type2));

% Non-scalar Int32Type arrays
typeArray1 = [int32Type1 int32Type1];
typeArray2 = [int32Type2 int32Type2];
testCase.verifyTrue(isequal(typeArray1, typeArray2));
end

function IsEqualFalse(testCase)
% Verifies the isequal method of arrow.type.Int32Type returns
% false when expected.

% Pass a different arrow.type.Type subclass to isequal
int32Type = arrow.int32();
int64Type = arrow.int64();
testCase.verifyFalse(isequal(int32Type, int64Type));
testCase.verifyFalse(isequal([int32Type int32Type], [int64Type int64Type]));

% Int32Type arrays have different sizes
typeArray1 = [int32Type int32Type];
typeArray2 = [int32Type int32Type]';
testCase.verifyFalse(isequal(typeArray1, typeArray2));
end
end
end
36 changes: 36 additions & 0 deletions matlab/test/arrow/type/tInt64Type.m
Original file line number Diff line number Diff line change
Expand Up @@ -22,4 +22,40 @@
BitWidth = int32(64)
ClassName = "arrow.type.Int64Type"
end

methods(Test)
function IsEqualTrue(testCase)
% Verifies isequal method of arrow.type.Int64Type returns true if
% these conditions are met:
%
% 1. All input arguments have a class type arrow.type.Int64Type
% 2. All inputs have the same size

% Scalar Int64Type arrays
int64Type1 = arrow.int64();
int64Type2 = arrow.int64();
testCase.verifyTrue(isequal(int64Type1, int64Type2));

% Non-scalar Int64Type arrays
typeArray1 = [int64Type1 int64Type1];
typeArray2 = [int64Type2 int64Type2];
testCase.verifyTrue(isequal(typeArray1, typeArray2));
end

function IsEqualFalse(testCase)
% Verifies the isequal method of arrow.type.Int64Type returns
% false when expected.

% Pass a different arrow.type.Type subclass to isequal
int64Type = arrow.int64();
int32Type = arrow.int32();
testCase.verifyFalse(isequal(int64Type, int32Type));
testCase.verifyFalse(isequal([int64Type int64Type], [int32Type int32Type]));

% Int64Type arrays have different sizes
typeArray1 = [int64Type int64Type];
typeArray2 = [int64Type int64Type]';
testCase.verifyFalse(isequal(typeArray1, typeArray2));
end
end
end

0 comments on commit 538fecc

Please sign in to comment.