Repository: cirKITers/masKIT

Issue:
Enable setting a preconfigured mask
It might be useful to add a preconfigured mask to the `MaskedCircuit`. This might even be required in our test method to evaluate the performance of iris learning.

Discussion:
codecov[bot]: # [Codecov](https://codecov.io/gh/cirKITers/masKIT/pull/25?src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=cirKITers) Report
> Merging [#25](https://codecov.io/gh/cirKITers/masKIT/pull/25?src=pr&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=cirKITers) (71ba79b) into [main](https://codecov.io/gh/cirKITers/masKIT/commit/ccaff9bd4a5d2733d9f111ac0cdd78a7c9730ae6?el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=cirKITers) (ccaff9b) will **decrease** coverage by `0.04%`.
> The diff coverage is `66.66%`.

[![Impacted file tree graph](https://codecov.io/gh/cirKITers/masKIT/pull/25/graphs/tree.svg?width=650&height=150&src=pr&token=8Or2uHjmFE&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=cirKITers)](https://codecov.io/gh/cirKITers/masKIT/pull/25?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=cirKITers)

```diff
@@            Coverage Diff             @@
##             main      #25      +/-   ##
==========================================
- Coverage   74.54%   74.49%   -0.05%     
==========================================
  Files          15       15              
  Lines        1096     1098       +2     
  Branches      105      105              
==========================================
+ Hits          817      818       +1     
- Misses        275      276       +1     
  Partials        4        4              
```


| [Impacted Files](https://codecov.io/gh/cirKITers/masKIT/pull/25?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=cirKITers) | Coverage Δ | |
|---|---|---|
| [main.py](https://codecov.io/gh/cirKITers/masKIT/pull/25/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=cirKITers#diff-bWFpbi5weQ==) | `0.00% <0.00%> (ø)` | |
| [maskit/masks.py](https://codecov.io/gh/cirKITers/masKIT/pull/25/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=cirKITers#diff-bWFza2l0L21hc2tzLnB5) | `99.48% <80.00%> (-0.52%)` | :arrow_down: |

------

[Continue to review full report at Codecov](https://codecov.io/gh/cirKITers/masKIT/pull/25?src=pr&el=continue&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=cirKITers).
> **Legend** - [Click here to learn more](https://docs.codecov.io/docs/codecov-delta?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=cirKITers)
> `Δ = absolute <relative> (impact)`, `ø = not affected`, `? = missing data`
> Powered by [Codecov](https://codecov.io/gh/cirKITers/masKIT/pull/25?src=pr&el=footer&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=cirKITers). Last update [ccaff9b...71ba79b](https://codecov.io/gh/cirKITers/masKIT/pull/25?src=pr&el=lastupdated&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=cirKITers). Read the [comment docs](https://docs.codecov.io/docs/pull-request-comments?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=cirKITers).

eileen-kuehn: @all-contributors please add @maxfischer2781 for review
allcontributors[bot]: @eileen-kuehn 

I've put up [a pull request](https://github.com/cirKITers/masKIT/pull/27) to add @maxfischer2781! :tada:

Patch:
diff --git a/.all-contributorsrc b/.all-contributorsrc
new file mode 100644
index 0000000..ce35457
--- /dev/null
+++ b/.all-contributorsrc
@@ -0,0 +1,47 @@
+{
+  "files": [
+    "README.md"
+  ],
+  "imageSize": 100,
+  "commit": false,
+  "contributors": [
+    {
+      "login": "cDenius",
+      "name": "cDenius",
+      "avatar_url": "https://avatars.githubusercontent.com/u/28619054?v=4",
+      "profile": "https://github.com/cDenius",
+      "contributions": [
+        "code",
+        "ideas",
+        "maintenance"
+      ]
+    },
+    {
+      "login": "eileen-kuehn",
+      "name": "Eileen Kuehn",
+      "avatar_url": "https://avatars.githubusercontent.com/u/8090701?v=4",
+      "profile": "https://github.com/eileen-kuehn",
+      "contributions": [
+        "code",
+        "ideas",
+        "maintenance",
+        "test"
+      ]
+    },
+    {
+      "login": "maxfischer2781",
+      "name": "Max Fischer",
+      "avatar_url": "https://avatars.githubusercontent.com/u/5708444?v=4",
+      "profile": "https://github.com/maxfischer2781",
+      "contributions": [
+        "review"
+      ]
+    }
+  ],
+  "contributorsPerLine": 7,
+  "projectName": "masKIT",
+  "projectOwner": "cirKITers",
+  "repoType": "github",
+  "repoHost": "https://github.com",
+  "skipCi": true
+}
diff --git a/README.md b/README.md
index 2ee835c..1cca5cc 100644
--- a/README.md
+++ b/README.md
@@ -1,3 +1,28 @@
 # QHACK 2021
+<!-- ALL-CONTRIBUTORS-BADGE:START - Do not remove or modify this section -->
+[![All Contributors](https://img.shields.io/badge/all_contributors-3-orange.svg?style=flat-square)](#contributors-)
+<!-- ALL-CONTRIBUTORS-BADGE:END -->
 
 submitted: Tag 1.0
+
+## Contributors ✨
+
+Thanks goes to these wonderful people ([emoji key](https://allcontributors.org/docs/en/emoji-key)):
+
+<!-- ALL-CONTRIBUTORS-LIST:START - Do not remove or modify this section -->
+<!-- prettier-ignore-start -->
+<!-- markdownlint-disable -->
+<table>
+  <tr>
+    <td align="center"><a href="https://github.com/cDenius"><img src="https://avatars.githubusercontent.com/u/28619054?v=4?s=100" width="100px;" alt=""/><br /><sub><b>cDenius</b></sub></a><br /><a href="https://github.com/cirKITers/masKIT/commits?author=cDenius" title="Code">💻</a> <a href="#ideas-cDenius" title="Ideas, Planning, & Feedback">🤔</a> <a href="#maintenance-cDenius" title="Maintenance">🚧</a></td>
+    <td align="center"><a href="https://github.com/eileen-kuehn"><img src="https://avatars.githubusercontent.com/u/8090701?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Eileen Kuehn</b></sub></a><br /><a href="https://github.com/cirKITers/masKIT/commits?author=eileen-kuehn" title="Code">💻</a> <a href="#ideas-eileen-kuehn" title="Ideas, Planning, & Feedback">🤔</a> <a href="#maintenance-eileen-kuehn" title="Maintenance">🚧</a> <a href="https://github.com/cirKITers/masKIT/commits?author=eileen-kuehn" title="Tests">⚠️</a></td>
+    <td align="center"><a href="https://github.com/maxfischer2781"><img src="https://avatars.githubusercontent.com/u/5708444?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Max Fischer</b></sub></a><br /><a href="https://github.com/cirKITers/masKIT/pulls?q=is%3Apr+reviewed-by%3Amaxfischer2781" title="Reviewed Pull Requests">👀</a></td>
+  </tr>
+</table>
+
+<!-- markdownlint-restore -->
+<!-- prettier-ignore-end -->
+
+<!-- ALL-CONTRIBUTORS-LIST:END -->
+
+This project follows the [all-contributors](https://github.com/all-contributors/all-contributors) specification. Contributions of any kind welcome!
\ No newline at end of file
diff --git a/main.py b/main.py
index 952d6b7..25b123d 100644
--- a/main.py
+++ b/main.py
@@ -169,6 +169,9 @@ def train(
         "final_layers": current_layers,
         "params": masked_circuit.parameters.unwrap(),
         "mask": masked_circuit.mask.unwrap(),
+        "__wire_mask": masked_circuit.wire_mask.mask,
+        "__layer_mask": masked_circuit.layer_mask.mask,
+        "__parameter_mask": masked_circuit.parameter_mask.mask,
         "__rotations": rotations,
     }
 
@@ -176,7 +179,9 @@ def train(
 def test(
     train_params,
     params,
-    mask,
+    wire_mask,
+    layer_mask,
+    parameter_mask,
     layers: int,
     rotations: List,
     test_data: Optional[List] = None,
@@ -191,17 +196,24 @@ def test(
         correct = 0
         N = len(test_data)
         costs = []
-        masked_circuit = MaskedCircuit(parameters=params, layers=layers, wires=wires)
+        masked_circuit = MaskedCircuit(
+            parameters=params,
+            layers=layers,
+            wires=wires,
+            wire_mask=wire_mask,
+            layer_mask=layer_mask,
+            parameter_mask=parameter_mask,
+        )
         for _step, (data, target) in enumerate(zip(test_data, test_target)):
             output = circuit(
-                params,
+                masked_circuit.differentiable_parameters,
                 data,
                 rotations,
                 masked_circuit,
             )
             c = cost_iris(
                 circuit,
-                params,
+                masked_circuit.differentiable_parameters,
                 data,
                 target,
                 rotations,
@@ -274,7 +286,9 @@ if __name__ == "__main__":
         test(
             train_params,
             result["params"],
-            result["mask"],
+            result["__wire_mask"],
+            result["__layer_mask"],
+            result["__parameter_mask"],
             result["final_layers"],
             result["__rotations"],
             test_data=test_data,
diff --git a/maskit/masks.py b/maskit/masks.py
index 313ce1e..bf9ef3f 100644
--- a/maskit/masks.py
+++ b/maskit/masks.py
@@ -29,15 +29,26 @@ class Mask(object):
     A Mask encapsulates a :py:attr:`~.mask` storing boolean value if a specific value
     is masked or not. In case a specific position is `True`, the according value is
     masked, otherwise it is not.
+
+    :param shape: The shape of the mask
+    :param parent: `MaskedCircuit` that owns the mask
+    :param mask: Preset of values that is taken by mask
     """
 
     __slots__ = ("mask", "_parent")
 
     def __init__(
-        self, shape: Tuple[int, ...], parent: Optional["MaskedCircuit"] = None
+        self,
+        shape: Tuple[int, ...],
+        parent: Optional["MaskedCircuit"] = None,
+        mask: Optional[np.ndarray] = None,
     ):
         super().__init__()
         self.mask = np.zeros(shape, dtype=bool, requires_grad=False)
+        if mask is not None:
+            assert mask.dtype == bool, "Mask must be of type bool"
+            assert mask.shape == shape, "Shape of mask must be equal to shape"
+            self.mask[:] = mask
         self._parent = parent
 
     def __len__(self) -> int:
@@ -172,6 +183,15 @@ class MaskedCircuit(object):
     """
     A MaskedCircuit supports masking of different components including wires, layers,
     and parameters.
+
+    :param parameters: Initial parameter set for circuit
+    :param layers: Number of layers
+    :param wires: Number of wires
+    :param default_value: Default value for gates that are added back in. In case of
+        `None` that is also the default, the last known value is assumed
+    :param parameter_mask: Initialization values of paramater mask, defaults to `None`
+    :param layer_mask: Initialization values of layer mask, defaults to `None`
+    :param wire_mask: Initialization values of wire mask, defaults to `None`
     """
 
     __slots__ = (
@@ -188,6 +208,9 @@ class MaskedCircuit(object):
         layers: int,
         wires: int,
         default_value: Optional[float] = None,
+        parameter_mask: Optional[np.ndarray] = None,
+        layer_mask: Optional[np.ndarray] = None,
+        wire_mask: Optional[np.ndarray] = None,
     ):
         assert (
             layers == parameters.shape[0]
@@ -196,9 +219,11 @@ class MaskedCircuit(object):
             wires == parameters.shape[1]
         ), "Second dimension of parameters shape must be equal to number of wires"
         self.parameters = parameters
-        self._parameter_mask = Mask(shape=parameters.shape, parent=self)
-        self._layer_mask = Mask(shape=(layers,), parent=self)
-        self._wire_mask = Mask(shape=(wires,), parent=self)
+        self._parameter_mask = Mask(
+            shape=parameters.shape, parent=self, mask=parameter_mask
+        )
+        self._layer_mask = Mask(shape=(layers,), parent=self, mask=layer_mask)
+        self._wire_mask = Mask(shape=(wires,), parent=self, mask=wire_mask)
         self.default_value = default_value
 
     @property


Test patch:
diff --git a/tests/test_masks.py b/tests/test_masks.py
index fc6b580..1c5cbca 100644
--- a/tests/test_masks.py
+++ b/tests/test_masks.py
@@ -47,6 +47,13 @@ class TestMaskedCircuits:
                 layers=size,
                 wires=size + 1,
             )
+        mc = MaskedCircuit(
+            parameters=pnp.random.uniform(low=0, high=1, size=(size, size)),
+            layers=size,
+            wires=size,
+            wire_mask=pnp.ones((size,), dtype=bool),
+        )
+        assert pnp.array_equal(mc.wire_mask, pnp.ones((size,), dtype=bool))
 
     def test_wrong_mode(self):
         mp = self._create_circuit(3)
@@ -306,6 +313,16 @@ class TestFreezableMaskedCircuit:
 
 
 class TestMask:
+    def test_init(self):
+        size = 3
+        with pytest.raises(AssertionError):
+            Mask((size,), mask=pnp.array([True, True, False, False]))
+        with pytest.raises(AssertionError):
+            Mask((size,), mask=pnp.array([0, 1, 3]))
+        preset = [False, True, False]
+        mp = Mask((size,), mask=pnp.array(preset))
+        assert pnp.array_equal(mp.mask, preset)
+
     def test_setting(self):
         size = 3
         mp = Mask((size,))