christinadionysio commented on code in PR #2614:
URL: https://github.com/apache/systemds/pull/2614#discussion_r4347263592
##########
src/main/python/tests/scuro/test_fusion_orders.py:
##########
@@ -19,77 +19,80 @@
#
# -------------------------------------------------------------
-import os
-import shutil
import unittest
import numpy as np
from systemds.scuro import Concatenation, RowMax, Hadamard
-from systemds.scuro.modality.unimodal_modality import UnimodalModality
-from systemds.scuro.representations.bert import Bert
-from systemds.scuro.representations.mel_spectrogram import MelSpectrogram
from systemds.scuro.representations.average import Average
from tests.scuro.data_generator import ModalityRandomDataGenerator
from systemds.scuro.modality.type import ModalityType
class TestFusionOrders(unittest.TestCase):
+ """
+ Tests the order properties of the fusion operators: commutativity, whether
+ the order of a pairwise chain matters, and whether a pairwise chain gives
+ the same result as the n-ary call.
+ """
+
+ # (operator, chain_order_independent, chain_equals_nary)
+ #
+ # Commutativity is not in the table. Every Fusion operator has a
+ # "commutative" attribute. It is False on the base class and a subclass
+ # can override it. The test compares the measured result with that
+ # attribute, so an operator whose attribute does not match its
+ # implementation fails here.
+ #
+ # Combining a pair is never the same as combining all three. That is
+ # checked for every operator, so it is not in the table either.
+ FUSION_PROPERTIES = [
Review Comment:
Keeping this table here means we need to adopt it for every new fusion
operator. If possible use the properties for the operators themselves.
##########
src/main/python/tests/scuro/test_fusion_orders.py:
##########
@@ -19,77 +19,80 @@
#
# -------------------------------------------------------------
-import os
-import shutil
import unittest
import numpy as np
from systemds.scuro import Concatenation, RowMax, Hadamard
-from systemds.scuro.modality.unimodal_modality import UnimodalModality
-from systemds.scuro.representations.bert import Bert
-from systemds.scuro.representations.mel_spectrogram import MelSpectrogram
from systemds.scuro.representations.average import Average
from tests.scuro.data_generator import ModalityRandomDataGenerator
from systemds.scuro.modality.type import ModalityType
class TestFusionOrders(unittest.TestCase):
+ """
+ Tests the order properties of the fusion operators: commutativity, whether
+ the order of a pairwise chain matters, and whether a pairwise chain gives
+ the same result as the n-ary call.
+ """
+
+ # (operator, chain_order_independent, chain_equals_nary)
+ #
+ # Commutativity is not in the table. Every Fusion operator has a
+ # "commutative" attribute. It is False on the base class and a subclass
+ # can override it. The test compares the measured result with that
+ # attribute, so an operator whose attribute does not match its
+ # implementation fails here.
+ #
+ # Combining a pair is never the same as combining all three. That is
+ # checked for every operator, so it is not in the table either.
+ FUSION_PROPERTIES = [
+ (Average, True, False),
+ (Concatenation, False, True),
+ (RowMax, True, True),
+ (Hadamard, True, True),
+ ]
+
@classmethod
def setUpClass(cls):
- cls.num_instances = 40
+ # These properties do not depend on the input shape, so the data can
+ # be small.
+ cls.num_instances = 4
+ cls.num_features = 8
cls.data_generator = ModalityRandomDataGenerator()
- cls.r_1 = cls.data_generator.create1DModality(40, 100,
ModalityType.AUDIO)
- cls.r_2 = cls.data_generator.create1DModality(40, 100,
ModalityType.TEXT)
- cls.r_3 = cls.data_generator.create1DModality(40, 100,
ModalityType.TEXT)
-
- def test_fusion_order_avg(self):
- r_1_r_2 = self.r_1.combine(self.r_2, Average())
- r_2_r_1 = self.r_2.combine(self.r_1, Average())
- r_1_r_2_r_3 = r_1_r_2.combine(self.r_3, Average())
- r_2_r_1_r_3 = r_2_r_1.combine(self.r_3, Average())
-
- r1_r2_r3 = self.r_1.combine([self.r_2, self.r_3], Average())
-
- self.assertTrue(np.array_equal(r_1_r_2.data, r_2_r_1.data))
- self.assertTrue(np.array_equal(r_1_r_2_r_3.data, r_2_r_1_r_3.data))
- self.assertFalse(np.array_equal(r_1_r_2_r_3.data, r1_r2_r3.data))
- self.assertFalse(np.array_equal(r_1_r_2.data, r1_r2_r3.data))
-
- def test_fusion_order_concat(self):
- r_1_r_2 = self.r_1.combine(self.r_2, Concatenation())
- r_2_r_1 = self.r_2.combine(self.r_1, Concatenation())
- r_1_r_2_r_3 = r_1_r_2.combine(self.r_3, Concatenation())
- r_2_r_1_r_3 = r_2_r_1.combine(self.r_3, Concatenation())
-
- r1_r2_r3 = self.r_1.combine([self.r_2, self.r_3], Concatenation())
-
- self.assertFalse(np.array_equal(r_1_r_2.data, r_2_r_1.data))
- self.assertFalse(np.array_equal(r_1_r_2_r_3.data, r_2_r_1_r_3.data))
- self.assertFalse(np.array_equal(r_2_r_1.data, r1_r2_r3.data))
- self.assertFalse(np.array_equal(r_1_r_2.data, r1_r2_r3.data))
-
- def test_fusion_order_max(self):
- r_1_r_2 = self.r_1.combine(self.r_2, RowMax())
- r_2_r_1 = self.r_2.combine(self.r_1, RowMax())
- r_1_r_2_r_3 = r_1_r_2.combine(self.r_3, RowMax())
- r_2_r_1_r_3 = r_2_r_1.combine(self.r_3, RowMax())
-
- r1_r2_r3 = self.r_1.combine([self.r_2, self.r_3], RowMax())
-
- self.assertTrue(np.array_equal(r_1_r_2.data, r_2_r_1.data))
- self.assertTrue(np.array_equal(r_1_r_2_r_3.data, r_2_r_1_r_3.data))
- self.assertTrue(np.array_equal(r_1_r_2_r_3.data, r1_r2_r3.data))
- self.assertFalse(np.array_equal(r_1_r_2.data, r1_r2_r3.data))
-
- def test_fusion_order_hadamard(self):
- r_1_r_2 = self.r_1.combine(self.r_2, Hadamard())
- r_2_r_1 = self.r_2.combine(self.r_1, Hadamard())
- r_1_r_2_r_3 = r_1_r_2.combine(self.r_3, Hadamard())
- r_2_r_1_r_3 = r_2_r_1.combine(self.r_3, Hadamard())
-
- r1_r2_r3 = self.r_1.combine([self.r_2, self.r_3], Hadamard())
- self.assertTrue(np.array_equal(r_1_r_2.data, r_2_r_1.data))
- self.assertTrue(np.array_equal(r_1_r_2_r_3.data, r_2_r_1_r_3.data))
- self.assertTrue(np.array_equal(r_1_r_2_r_3.data, r1_r2_r3.data))
- self.assertFalse(np.array_equal(r_1_r_2.data, r1_r2_r3.data))
+ def setUp(self):
+ self.r_1 = self.data_generator.create1DModality(
+ self.num_instances, self.num_features, ModalityType.AUDIO
+ )
+ self.r_2 = self.data_generator.create1DModality(
+ self.num_instances, self.num_features, ModalityType.TEXT
+ )
+ self.r_3 = self.data_generator.create1DModality(
+ self.num_instances, self.num_features, ModalityType.TEXT
+ )
+
+ @staticmethod
+ def _equal(left, right):
+ return np.array_equal(np.asarray(left.data), np.asarray(right.data))
+
+ def test_fusion_order_properties(self):
+ for (
+ fusion_operator,
+ chain_order_independent,
+ chain_equals_nary,
+ ) in self.FUSION_PROPERTIES:
Review Comment:
In order to keep the information which operator failed the test please add a
message including the operator name to each assertion.
##########
src/main/python/tests/scuro/test_window_operations.py:
##########
@@ -111,67 +111,60 @@ def test_dynamic_window(self):
for i in range(0, self.num_instances):
assert len(aggregated_window.data[i]) == num_windows
- def test_window_aggregation_on_audio_representations(self):
+ def test_window_aggregation_on_1d_modalities(self):
+ # create1DModality returns the same shape and dtype for all three
+ # modality types. window_aggregation looks at the data layout and not
+ # at the modality type, so the result should be the same for all of
+ # them.
window_size = 10
- self.run_window_aggregation_for_modality(ModalityType.AUDIO,
window_size)
- def test_window_operations_on_video_representations(self):
- window_size = 10
- self.run_window_aggregation_for_modality(ModalityType.VIDEO,
window_size)
-
- def test_window_operations_on_text_representations(self):
- window_size = 10
-
- self.run_window_aggregation_for_modality(ModalityType.TEXT,
window_size)
-
- def run_window_aggregation_for_modality(self, modality_type, window_size):
- r = self.data_generator.create1DModality(self.num_instances, 200,
modality_type)
- for aggregation in self.aggregations:
- windowed_modality = r.window_aggregation(window_size, aggregation)
-
- self.verify_window_operation(aggregation, r, windowed_modality,
window_size)
-
- def test_window_aggregation_on_3d_modality(self):
- data, _ = self.data_generator.create_3d_modality(
- self.num_instances, (100, 8, 8)
- )
- embedding_modality = TransformedModality(
- self.data_generator, "test_transformation"
- )
- embedding_modality.data = data
- embedding_modality.stats = RepresentationStats(self.num_instances,
(100, 8, 8))
- num_windows = 10
-
- for window_operator in [
- StaticWindow(num_windows=num_windows),
- DynamicWindow(num_windows=num_windows),
- WindowAggregation(window_size=10),
+ for modality_type in [
+ ModalityType.AUDIO,
+ ModalityType.VIDEO,
+ ModalityType.TEXT,
]:
- stats = window_operator.get_output_stats(embedding_modality.stats)
- assert stats.num_instances == self.num_instances
- assert stats.output_shape == (num_windows, 8, 8)
-
- windowed_modality = embedding_modality.context(window_operator)
+ r = self.data_generator.create1DModality(
+ self.num_instances, 200, modality_type
+ )
+ for aggregation in self.aggregations:
+ with self.subTest(modality=modality_type.name,
aggregation=aggregation):
+ windowed_modality = r.window_aggregation(window_size,
aggregation)
+ self.verify_window_operation(
+ aggregation, r, windowed_modality, window_size
+ )
- def test_window_aggregation_on_2d_modality(self):
- data, _ = self.data_generator.create_2d_modality(self.num_instances,
(100, 8))
- embedding_modality = TransformedModality(
- self.data_generator, "test_transformation"
- )
- embedding_modality.data = data
- embedding_modality.stats = RepresentationStats(self.num_instances,
(100, 8))
+ def test_window_aggregation_on_nd_modality(self):
+ # Window aggregation only changes the first (time) axis and keeps the
+ # feature axes as they are. The expected shape is therefore
+ # (num_windows,) + dims[1:] for any number of dimensions.
num_windows = 10
- for window_operator in [
- StaticWindow(num_windows=num_windows),
- DynamicWindow(num_windows=num_windows),
- WindowAggregation(window_size=10),
- ]:
- stats = window_operator.get_output_stats(embedding_modality.stats)
- assert stats.num_instances == self.num_instances
- assert stats.output_shape == (num_windows, 8)
-
- windowed_modality = embedding_modality.context(window_operator)
+ for dims in [(100, 8, 8), (100, 8)]:
+ if len(dims) == 3:
+ data, _ = self.data_generator.create_3d_modality(
+ self.num_instances, dims
+ )
+ else:
+ data, _ = self.data_generator.create_2d_modality(
+ self.num_instances, dims
+ )
+ embedding_modality = TransformedModality(
+ self.data_generator, "test_transformation"
+ )
+ embedding_modality.data = data
+ embedding_modality.stats = RepresentationStats(self.num_instances,
dims)
+
+ for window_operator in [
+ StaticWindow(num_windows=num_windows),
+ DynamicWindow(num_windows=num_windows),
+ WindowAggregation(window_size=10),
+ ]:
+ with self.subTest(dims=dims,
operator=type(window_operator).__name__):
Review Comment:
Please add a message with the operator name to the assertion so we know
which one fails.
##########
src/main/python/tests/scuro/test_window_operations.py:
##########
@@ -111,67 +111,60 @@ def test_dynamic_window(self):
for i in range(0, self.num_instances):
assert len(aggregated_window.data[i]) == num_windows
- def test_window_aggregation_on_audio_representations(self):
+ def test_window_aggregation_on_1d_modalities(self):
+ # create1DModality returns the same shape and dtype for all three
+ # modality types. window_aggregation looks at the data layout and not
+ # at the modality type, so the result should be the same for all of
+ # them.
window_size = 10
- self.run_window_aggregation_for_modality(ModalityType.AUDIO,
window_size)
- def test_window_operations_on_video_representations(self):
- window_size = 10
- self.run_window_aggregation_for_modality(ModalityType.VIDEO,
window_size)
-
- def test_window_operations_on_text_representations(self):
- window_size = 10
-
- self.run_window_aggregation_for_modality(ModalityType.TEXT,
window_size)
-
- def run_window_aggregation_for_modality(self, modality_type, window_size):
- r = self.data_generator.create1DModality(self.num_instances, 200,
modality_type)
- for aggregation in self.aggregations:
- windowed_modality = r.window_aggregation(window_size, aggregation)
-
- self.verify_window_operation(aggregation, r, windowed_modality,
window_size)
-
- def test_window_aggregation_on_3d_modality(self):
- data, _ = self.data_generator.create_3d_modality(
- self.num_instances, (100, 8, 8)
- )
- embedding_modality = TransformedModality(
- self.data_generator, "test_transformation"
- )
- embedding_modality.data = data
- embedding_modality.stats = RepresentationStats(self.num_instances,
(100, 8, 8))
- num_windows = 10
-
- for window_operator in [
- StaticWindow(num_windows=num_windows),
- DynamicWindow(num_windows=num_windows),
- WindowAggregation(window_size=10),
+ for modality_type in [
+ ModalityType.AUDIO,
+ ModalityType.VIDEO,
+ ModalityType.TEXT,
]:
- stats = window_operator.get_output_stats(embedding_modality.stats)
- assert stats.num_instances == self.num_instances
- assert stats.output_shape == (num_windows, 8, 8)
-
- windowed_modality = embedding_modality.context(window_operator)
+ r = self.data_generator.create1DModality(
+ self.num_instances, 200, modality_type
+ )
+ for aggregation in self.aggregations:
+ with self.subTest(modality=modality_type.name,
aggregation=aggregation):
+ windowed_modality = r.window_aggregation(window_size,
aggregation)
+ self.verify_window_operation(
+ aggregation, r, windowed_modality, window_size
+ )
- def test_window_aggregation_on_2d_modality(self):
- data, _ = self.data_generator.create_2d_modality(self.num_instances,
(100, 8))
- embedding_modality = TransformedModality(
- self.data_generator, "test_transformation"
- )
- embedding_modality.data = data
- embedding_modality.stats = RepresentationStats(self.num_instances,
(100, 8))
+ def test_window_aggregation_on_nd_modality(self):
+ # Window aggregation only changes the first (time) axis and keeps the
+ # feature axes as they are. The expected shape is therefore
+ # (num_windows,) + dims[1:] for any number of dimensions.
num_windows = 10
- for window_operator in [
- StaticWindow(num_windows=num_windows),
- DynamicWindow(num_windows=num_windows),
- WindowAggregation(window_size=10),
- ]:
- stats = window_operator.get_output_stats(embedding_modality.stats)
- assert stats.num_instances == self.num_instances
- assert stats.output_shape == (num_windows, 8)
-
- windowed_modality = embedding_modality.context(window_operator)
+ for dims in [(100, 8, 8), (100, 8)]:
+ if len(dims) == 3:
+ data, _ = self.data_generator.create_3d_modality(
+ self.num_instances, dims
+ )
+ else:
+ data, _ = self.data_generator.create_2d_modality(
+ self.num_instances, dims
+ )
+ embedding_modality = TransformedModality(
+ self.data_generator, "test_transformation"
+ )
+ embedding_modality.data = data
+ embedding_modality.stats = RepresentationStats(self.num_instances,
dims)
+
+ for window_operator in [
+ StaticWindow(num_windows=num_windows),
+ DynamicWindow(num_windows=num_windows),
+ WindowAggregation(window_size=10),
+ ]:
+ with self.subTest(dims=dims,
operator=type(window_operator).__name__):
+ stats =
window_operator.get_output_stats(embedding_modality.stats)
+ self.assertEqual(stats.num_instances, self.num_instances)
+ self.assertEqual(stats.output_shape, (num_windows,) +
dims[1:])
+
+ embedding_modality.context(window_operator)
Review Comment:
Check the result of the context operation, i.e.: assert the actual shape of
the output matches the statistics.
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: [email protected]
For queries about this service, please contact Infrastructure at:
[email protected]