Repository: PennyLaneAI/pennylane
Pull request #4913 (2023-12-03 16:05:37)

Add ability to scale Shots object with __mul__ and *
Towards #4910 

So, this is the first draft towards the assigned issue given above, Implemented `__mul__` and `__rmul__` .

Now, let me know two things first:
scenario 1:

```
>> sh1=Shots()
>> sh1*k # k is some scalar
>> TypeError: unsupported operand type(s) for *: 'NoneType' and 'int'
```

scneario 2:

```
>> sh2=Shots(100)
>> x =sh2*0
>> print(x)
>> ValueError: Shots must be a single positive integer, a tuple pair of the form (shots, copies), or a sequence of these types.
```

So, for these border cases i have not wrapped them explicitly, please let me know if you want to handle them any differently.

codecov[bot]: ## [Codecov](https://app.codecov.io/gh/PennyLaneAI/pennylane/pull/4913?src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=PennyLaneAI) Report
Attention: `2 lines` in your changes are missing coverage. Please review.
> Comparison is base [(`6ad2de6`)](https://app.codecov.io/gh/PennyLaneAI/pennylane/commit/6ad2de6980b572e65fc88054429112e97fb366d5?el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=PennyLaneAI) 99.65% compared to head [(`b00cda7`)](https://app.codecov.io/gh/PennyLaneAI/pennylane/pull/4913?src=pr&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=PennyLaneAI) 99.64%.
> Report is 1 commits behind head on master.

| [Files](https://app.codecov.io/gh/PennyLaneAI/pennylane/pull/4913?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=PennyLaneAI) | Patch % | Lines |
|---|---|---|
| [pennylane/measurements/shots.py](https://app.codecov.io/gh/PennyLaneAI/pennylane/pull/4913?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=PennyLaneAI#diff-cGVubnlsYW5lL21lYXN1cmVtZW50cy9zaG90cy5weQ==) | 80.00% | [2 Missing :warning: ](https://app.codecov.io/gh/PennyLaneAI/pennylane/pull/4913?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=PennyLaneAI) |

<details><summary>Additional details and impacted files</summary>


```diff
@@            Coverage Diff             @@
##           master    #4913      +/-   ##
==========================================
- Coverage   99.65%   99.64%   -0.02%     
==========================================
  Files         387      387              
  Lines       35049    34794     -255     
==========================================
- Hits        34929    34671     -258     
- Misses        120      123       +3     
```



</details>

[:umbrella: View full report in Codecov by Sentry](https://app.codecov.io/gh/PennyLaneAI/pennylane/pull/4913?src=pr&el=continue&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=PennyLaneAI).   
:loudspeaker: Have feedback on the report? [Share it here](https://about.codecov.io/codecov-pr-comment-feedback/?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=PennyLaneAI).

AnuravModak: Heyy @albi3ro , can u check the tests here? and let me know if i have to change anything here because code bot is not happy about it
AnuravModak: > heyy please let me know why ithe particular test is failing with the below error message? I tried bunch of stuffs is there anything that i am missing? please advise.
> 
> `FAILED tests/measurements/test_shots.py::TestProperties::test_invalid_scalar_type - TypeError: 'Scalar must be a number' object (type: <class 'str'>) must be callable.`

no worries i git it :)

Patch:
diff --git a/doc/releases/changelog-dev.md b/doc/releases/changelog-dev.md
index af9fe4030..3e827ef94 100644
--- a/doc/releases/changelog-dev.md
+++ b/doc/releases/changelog-dev.md
@@ -328,6 +328,9 @@
 * A new function called `ops.functions.assert_valid` has been added for checking if an `Operator` class is defined correctly.
   [(#4764)](https://github.com/PennyLaneAI/pennylane/pull/4764)
 
+* `Shots` can now be scaled with `*` via the `__mul__` and `__rmul__` dunders.
+  [(#4913)](https://github.com/PennyLaneAI/pennylane/pull/4913)
+
 * `GlobalPhase` now decomposes to nothing in case devices do not support global phases.
   [(#4855)](https://github.com/PennyLaneAI/pennylane/pull/4855)
 
diff --git a/pennylane/measurements/shots.py b/pennylane/measurements/shots.py
index 2d4a4bafe..f5220f77e 100644
--- a/pennylane/measurements/shots.py
+++ b/pennylane/measurements/shots.py
@@ -222,6 +222,21 @@ class Shots:
     def __bool__(self):
         return self.total_shots is not None
 
+    def __mul__(self, scalar):
+        if not isinstance(scalar, (int, float)):
+            raise TypeError("Can't multiply Shots with non-integer or float type.")
+        if self.total_shots is None:
+            return self
+
+        scaled_shot_vector = tuple(
+            ShotCopies(int(i.shots * scalar), i.copies) for i in self.shot_vector
+        )
+
+        return self.__class__(scaled_shot_vector)
+
+    def __rmul__(self, scalar):
+        return self.__mul__(scalar)
+
     @property
     def has_partitioned_shots(self):
         """


Test patch:
diff --git a/tests/measurements/test_shots.py b/tests/measurements/test_shots.py
index abc709071..7aebda3db 100644
--- a/tests/measurements/test_shots.py
+++ b/tests/measurements/test_shots.py
@@ -250,3 +250,36 @@ class TestProperties:
     def test_num_copies(self, shots, expected):
         """Tests the num_copies property."""
         assert Shots(shots).num_copies == expected
+
+    def test_shot_mul(self):
+        """Test the __mul__ method for multiplying a number by a shot object."""
+        sh1, sh2, sh3 = Shots(100), Shots((100, 100)), Shots(5)
+        sh5 = Shots()
+        scaled_sh1 = sh1 * 2
+        scaled_sh2 = sh2 * 2
+        scaled_sh3 = sh3 * 0.5
+        scaled_sh4 = 2 * sh2
+        scaled_sh5 = sh5 * 8
+
+        assert scaled_sh1.total_shots == 200
+        assert scaled_sh2.total_shots == 400
+        assert scaled_sh2.shot_vector[0].shots == 200
+        assert scaled_sh2.shot_vector[0].copies == 2
+        assert scaled_sh3.total_shots == 2
+        assert scaled_sh4.total_shots == 400
+        assert scaled_sh4.shot_vector[0].shots == 200
+        assert scaled_sh4.shot_vector[0].copies == 2
+        assert scaled_sh5 == sh5
+
+    def test_invalid_scalar_type(self):
+        """Test that __mul__ raises a TypeError for an invalid scalar type."""
+        shots = Shots(100)
+        with pytest.raises(TypeError, match="Can't multiply Shots with non-integer or float type."):
+            _ = shots * "invalid scalar type"
+
+    def test_shots_rmul(self):
+        """Test the __rmul__ method for multiplying a number by a shot object."""
+        sh1 = Shots(200)
+        scaled_sh1 = 2 * sh1
+        rev_scaled_sh1 = sh1 * 2
+        assert scaled_sh1.total_shots == rev_scaled_sh1.total_shots