From 2b30d4d2bfb258e0ef51dfa263b441469f5ab17c Mon Sep 17 00:00:00 2001
From: "github-actions[bot]"
 <41898282+github-actions[bot]@users.noreply.github.com>
Date: Thu, 4 Apr 2024 15:17:07 +0000
Subject: [PATCH] Create rule S6929: The axis argument should be specified when
 using TensorFlow's reduction operations (#3644)

* Create rule S6929

* Create rule S6929: The axis argument should be specified when using TensorFlow's reduction operations

* Fix after review

---------

Co-authored-by: joke1196 <joke1196@users.noreply.github.com>
Co-authored-by: David Kunzmann <david.kunzmann@sonarsource.com>
---
 rules/S6929/metadata.json        |   2 +
 rules/S6929/python/metadata.json |  24 +++++++
 rules/S6929/python/rule.adoc     | 104 +++++++++++++++++++++++++++++++
 3 files changed, 130 insertions(+)
 create mode 100644 rules/S6929/metadata.json
 create mode 100644 rules/S6929/python/metadata.json
 create mode 100644 rules/S6929/python/rule.adoc

diff --git a/rules/S6929/metadata.json b/rules/S6929/metadata.json
new file mode 100644
index 00000000000..2c63c085104
--- /dev/null
+++ b/rules/S6929/metadata.json
@@ -0,0 +1,2 @@
+{
+}
diff --git a/rules/S6929/python/metadata.json b/rules/S6929/python/metadata.json
new file mode 100644
index 00000000000..3b898974b13
--- /dev/null
+++ b/rules/S6929/python/metadata.json
@@ -0,0 +1,24 @@
+{
+  "title": "The axis argument should be specified when using TensorFlow's reduction operations",
+  "type": "CODE_SMELL",
+  "status": "ready",
+  "remediation": {
+    "func": "Constant\/Issue",
+    "constantCost": "5min"
+  },
+  "tags": [
+  ],
+  "defaultSeverity": "Major",
+  "ruleSpecification": "RSPEC-6929",
+  "sqKey": "S6929",
+  "scope": "All",
+  "defaultQualityProfiles": ["Sonar way"],
+  "quickfix": "unknown",
+  "code": {
+    "impacts": {
+      "MAINTAINABILITY": "MEDIUM",
+      "RELIABILITY": "HIGH"
+    },
+    "attribute": "CLEAR"
+  }
+}
diff --git a/rules/S6929/python/rule.adoc b/rules/S6929/python/rule.adoc
new file mode 100644
index 00000000000..83c46a414ef
--- /dev/null
+++ b/rules/S6929/python/rule.adoc
@@ -0,0 +1,104 @@
+This rule raises an issue when the axis argument is not provided to TensorFlow's reduction operations.
+
+== Why is this an issue?
+
+The result of TensorFlow's reduction operations (i.e. ``tf.math.reduce_sum``, ``tf.math.reduce_std``), 
+highly depends on the shape of the Tensor provided. 
+
+[source,python]
+----
+import tensorflow as tf
+
+x = tf.constant([[1, 1, 1], [1, 1, 1]])
+tf.math.reduce_sum(x)
+----
+
+In the example above the reduction of the 2 dimensional array will return the value `6` as all the elements are added together.
+By default TensorFlow's reduction operations are applied across all axis. When specifying an axis the result will be completely different.
+
+[source,python]
+----
+import tensorflow as tf
+
+x = tf.constant([[1, 1, 1], [1, 1, 1]])
+tf.math.reduce_sum(x, axis=0)
+----
+
+Here the result will be `[2,2,2]` as the reduction is applied only on the axis 0.
+
+TensorFlow's default behavior can be confusing, especially when the reducing array of different shapes. 
+
+Considering the following example:
+
+[source,python]
+----
+import tensorflow as tf
+
+x = tf.constant([[1], [2]])
+y = tf.constant([1, 2])
+tf.math.reduce_sum(x + y)
+----
+
+Here the result will be `12` instead of the `6` that could be expected. This is because the implicit broadcasting reshapes the 
+first array to `[[1,1], [2,2]]` which is then added to the `y` array `[1,2]` resulting in ``[[2,3], [3,4]]``. As the 
+reduction happen across all dimensions the result is then ``2 + 3 + 3 + 4 = 12``. It is not clear by looking at the example 
+if this was intentional or if the user made a mistake.
+
+This is why a good practice is to always specify the axis on which to perform the reduction.
+
+For example: 
+
+[source,python]
+----
+import tensorflow as tf
+
+x = tf.constant([[1], [2]])
+y = tf.constant([1, 2])
+tf.math.reduce_sum(x + y, axis=0)
+----
+
+In the example above, specifying the axis clarifies the intent, as the result now is ``[5, 7]``. If the intent was to effectively
+reduce across all dimensions the user should provide the list of axis `axis=[0,1]` 
+or clearly state the default behavior should be applied with ``axis=None``.
+
+== How to fix it
+
+To fix this issue provide the axis argument when using a TensorFlow reduction operation such as ``tf.math.reduce_sum``, ``tf.math.reduce_prod``, ``tf.math.reduce_mean``, etc...
+
+=== Code examples
+
+==== Noncompliant code example
+
+[source,python,diff-id=1,diff-type=noncompliant]
+----
+import tensorflow as tf
+
+x = tf.constant([[1, 1, 1], [1, 1, 1]])
+tf.math.reduce_sum(x) # Noncompliant: the axis arguments defaults to None
+----
+
+==== Compliant solution
+
+[source,python,diff-id=1,diff-type=compliant]
+----
+import tensorflow as tf
+
+x = tf.constant([[1, 1, 1], [1, 1, 1]])
+tf.math.reduce_sum(x, axis=0) # Compliant: the reduction will happen only on the axis 0, resulting in `[2,2,2]` 
+----
+
+
+== Resources
+=== Documentation
+
+* TensorFlow Documentation - https://www.tensorflow.org/api_docs/python/tf/math/reduce_max[tf.math.reduce_max reference]
+* TensorFlow Documentation - https://www.tensorflow.org/api_docs/python/tf/math/reduce_mean[tf.math.reduce_mean reference]
+* TensorFlow Documentation - https://www.tensorflow.org/api_docs/python/tf/math/reduce_min[tf.math.reduce_min reference]
+* TensorFlow Documentation - https://www.tensorflow.org/api_docs/python/tf/math/reduce_prod[tf.math.reduce_prod reference]
+* TensorFlow Documentation - https://www.tensorflow.org/api_docs/python/tf/math/reduce_std[tf.math.reduce_std reference]
+* TensorFlow Documentation - https://www.tensorflow.org/api_docs/python/tf/math/reduce_sum[tf.math.reduce_sum reference]
+* TensorFlow Documentation - https://www.tensorflow.org/api_docs/python/tf/math/reduce_variance[tf.math.reduce_variance reference]
+
+=== Articles & blog posts
+
+* Vahidk Developers Guide - https://github.com/vahidk/EffectiveTensorflow?tab=readme-ov-file#broadcasting-the-good-and-the-ugly[Broadcasting the good and the ugly]