We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
Document how to use a quantizer as an activation function
`tf.keras.layers.Activation("ste_sign")`

--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `larq/quantizers.py`
Content:
```
1 """A Quantizer defines the way of transforming a full precision input to a
2 quantized output and the pseudo-gradient method used for the backwards pass."""
3 
4 import tensorflow as tf
5 from larq import utils, math
6 
7 
8 @tf.custom_gradient
9 def _binarize_with_identity_grad(x):
10     def grad(dy):
11         return dy
12 
13     return math.sign(x), grad
14 
15 
16 @tf.custom_gradient
17 def _binarize_with_weighted_grad(x):
18     def grad(dy):
19         return (1 - tf.abs(x)) * 2 * dy
20 
21     return math.sign(x), grad
22 
23 
24 @utils.register_keras_custom_object
25 @utils.set_precision(1)
26 def ste_sign(x):
27     r"""
28     Sign binarization function.
29     \\[
30     q(x) = \begin{cases}
31       -1 & x < 0 \\\
32       1 & x \geq 0
33     \end{cases}
34     \\]
35 
36     The gradient is estimated using the Straight-Through Estimator
37     (essentially the binarization is replaced by a clipped identity on the
38     backward pass).
39     \\[\frac{\partial q(x)}{\partial x} = \begin{cases}
40       1 & \left|x\right| \leq 1 \\\
41       0 & \left|x\right| > 1
42     \end{cases}\\]
43 
44     ```plot-activation
45     quantizers.ste_sign
46     ```
47 
48     # Arguments
49     x: Input tensor.
50 
51     # Returns
52     Binarized tensor.
53 
54     # References
55     - [Binarized Neural Networks: Training Deep Neural Networks with Weights and
56       Activations Constrained to +1 or -1](http://arxiv.org/abs/1602.02830)
57     """
58 
59     x = tf.clip_by_value(x, -1, 1)
60 
61     return _binarize_with_identity_grad(x)
62 
63 
64 @utils.register_keras_custom_object
65 @utils.set_precision(1)
66 def magnitude_aware_sign(x):
67     r"""
68     Magnitude-aware sign for Bi-Real Net.
69 
70     ```plot-activation
71     quantizers.magnitude_aware_sign
72     ```
73 
74     # Arguments
75     x: Input tensor
76 
77     # Returns
78     Scaled binarized tensor (with values in $\{-a, a\}$, where $a$ is a float).
79 
80     # References
81     - [Bi-Real Net: Enhancing the Performance of 1-bit CNNs With Improved
82       Representational Capability and Advanced Training
83       Algorithm](https://arxiv.org/abs/1808.00278)
84 
85     """
86     scale_factor = tf.reduce_mean(tf.abs(x), axis=list(range(len(x.shape) - 1)))
87 
88     return tf.stop_gradient(scale_factor) * ste_sign(x)
89 
90 
91 @utils.register_keras_custom_object
92 @utils.set_precision(1)
93 def approx_sign(x):
94     r"""
95     Sign binarization function.
96     \\[
97     q(x) = \begin{cases}
98       -1 & x < 0 \\\
99       1 & x \geq 0
100     \end{cases}
101     \\]
102 
103     The gradient is estimated using the ApproxSign method.
104     \\[\frac{\partial q(x)}{\partial x} = \begin{cases}
105       (2 - 2 \left|x\right|) & \left|x\right| \leq 1 \\\
106       0 & \left|x\right| > 1
107     \end{cases}
108     \\]
109 
110     ```plot-activation
111     quantizers.approx_sign
112     ```
113 
114     # Arguments
115     x: Input tensor.
116 
117     # Returns
118     Binarized tensor.
119 
120     # References
121     - [Bi-Real Net: Enhancing the Performance of 1-bit CNNs With Improved
122       Representational Capability and Advanced
123       Training Algorithm](http://arxiv.org/abs/1808.00278)
124     """
125 
126     x = tf.clip_by_value(x, -1, 1)
127 
128     return _binarize_with_weighted_grad(x)
129 
130 
131 @utils.register_keras_custom_object
132 @utils.set_precision(2)
133 class SteTern:
134     r"""
135     Ternarization function.
136     \\[
137     q(x) = \begin{cases}
138     +1 & x > \Delta \\\
139     0 & |x| < \Delta \\\
140      -1 & x < - \Delta
141     \end{cases}
142     \\]
143 
144     where $\Delta$ is defined as the threshold and can be passed as an argument,
145     or can be calculated as per the Ternary Weight Networks original paper, such that
146 
147     \\[
148     \Delta = \frac{0.7}{n} \sum_{i=1}^{n} |W_i|
149     \\]
150     where we assume that $W_i$ is generated from a normal distribution.
151 
152     The gradient is estimated using the Straight-Through Estimator
153     (essentially the Ternarization is replaced by a clipped identity on the
154     backward pass).
155     \\[\frac{\partial q(x)}{\partial x} = \begin{cases}
156     1 & \left|x\right| \leq 1 \\\
157     0 & \left|x\right| > 1
158     \end{cases}\\]
159 
160     ```plot-activation
161     quantizers.SteTern
162     ```
163 
164     # Arguments
165     x: Input tensor.
166     threshold_value: The value for the threshold, $\Delta$.
167     ternary_weight_networks: Boolean of whether to use the Ternary Weight Networks threshold calculation.
168 
169     # Returns
170     Ternarized tensor.
171 
172     # References
173     - [Ternary Weight Networks](http://arxiv.org/abs/1605.04711)
174     """
175 
176     def __init__(self, threshold_value=0.1, ternary_weight_networks=False):
177         self.threshold_value = threshold_value
178         self.ternary_weight_networks = ternary_weight_networks
179 
180     def __call__(self, x):
181         x = tf.clip_by_value(x, -1, 1)
182         if self.ternary_weight_networks:
183             threshold = self.threshold_twn(x)
184         else:
185             threshold = self.threshold_value
186 
187         @tf.custom_gradient
188         def _ternarize_with_identity_grad(x):
189             def grad(dy):
190                 return dy
191 
192             return (tf.sign(tf.sign(x + threshold) + tf.sign(x - threshold)), grad)
193 
194         return _ternarize_with_identity_grad(x)
195 
196     def threshold_twn(self, x):
197         return 0.7 * tf.reduce_sum(tf.abs(x)) / tf.cast(tf.size(x), x.dtype)
198 
199     def get_config(self):
200         return {
201             "threshold_value": self.threshold_value,
202             "ternary_weight_networks": self.ternary_weight_networks,
203         }
204 
205 
206 def serialize(initializer):
207     return tf.keras.utils.serialize_keras_object(initializer)
208 
209 
210 def deserialize(name, custom_objects=None):
211     return tf.keras.utils.deserialize_keras_object(
212         name,
213         module_objects=globals(),
214         custom_objects=custom_objects,
215         printable_module_name="quantization function",
216     )
217 
218 
219 def get(identifier):
220     if identifier is None:
221         return None
222     if isinstance(identifier, str):
223         return deserialize(str(identifier))
224     if callable(identifier):
225         return identifier
226     raise ValueError(
227         f"Could not interpret quantization function identifier: {identifier}"
228     )
229
```
--- END FILES ---

Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.

Here is an example:

```diff
diff --git a/examples/server_async.py b/examples/server_async.py
--- a/examples/server_async.py
+++ b/examples/server_async.py
@@ -313,4 +313,4 @@
 
 
 if __name__ == "__main__":
-    asyncio.run(run_async_server("."), debug=True)
+    asyncio.run(run_async_server(), debug=True)
diff --git a/examples/server_sync.py b/examples/server_sync.py
--- a/examples/server_sync.py
+++ b/examples/server_sync.py
@@ -313,5 +313,5 @@
 
 
 if __name__ == "__main__":
-    server = run_sync_server(".")
+    server = run_sync_server()
     server.shutdown()

```

diff --git a/larq/quantizers.py b/larq/quantizers.py
--- a/larq/quantizers.py
+++ b/larq/quantizers.py
@@ -1,5 +1,39 @@
 """A Quantizer defines the way of transforming a full precision input to a
-quantized output and the pseudo-gradient method used for the backwards pass."""
+quantized output and the pseudo-gradient method used for the backwards pass.
+
+Quantizers can either be used through quantizer arguments that are supported
+for Larq layers, such as `input_quantizer` and `kernel_quantizer`; or they
+can be used similar to activations, i.e. either through an `Activation` layer,
+or through the `activation` argument supported by all forward layer:
+
+```python
+import tensorflow as tf
+import larq as lq
+...
+x = lq.layers.QuantDense(64, activation=None)(x)
+x = lq.layers.QuantDense(64, input_quantizer="ste_sign")(x)
+```
+
+is equivalent to:
+
+```python
+x = lq.layers.QuantDense(64)(x)
+x = tf.keras.layers.Activation("ste_sign")(x)
+x = lq.layers.QuantDense(64)(x)
+```
+
+as well as:
+
+```python
+x = lq.layers.QuantDense(64, activation="ste_sign")(x)
+x = lq.layers.QuantDense(64)(x)
+```
+
+We highly recommend using the first of these formulations: for the
+other two formulations, intermediate layers - like batch normalization or
+average pooling - and shortcut connections may result in non-binary input
+to the convolutions.
+"""
 
 import tensorflow as tf
 from larq import utils, math

{"golden_diff": "diff --git a/larq/quantizers.py b/larq/quantizers.py\n--- a/larq/quantizers.py\n+++ b/larq/quantizers.py\n@@ -1,5 +1,39 @@\n \"\"\"A Quantizer defines the way of transforming a full precision input to a\n-quantized output and the pseudo-gradient method used for the backwards pass.\"\"\"\n+quantized output and the pseudo-gradient method used for the backwards pass.\n+\n+Quantizers can either be used through quantizer arguments that are supported\n+for Larq layers, such as `input_quantizer` and `kernel_quantizer`; or they\n+can be used similar to activations, i.e. either through an `Activation` layer,\n+or through the `activation` argument supported by all forward layer:\n+\n+```python\n+import tensorflow as tf\n+import larq as lq\n+...\n+x = lq.layers.QuantDense(64, activation=None)(x)\n+x = lq.layers.QuantDense(64, input_quantizer=\"ste_sign\")(x)\n+```\n+\n+is equivalent to:\n+\n+```python\n+x = lq.layers.QuantDense(64)(x)\n+x = tf.keras.layers.Activation(\"ste_sign\")(x)\n+x = lq.layers.QuantDense(64)(x)\n+```\n+\n+as well as:\n+\n+```python\n+x = lq.layers.QuantDense(64, activation=\"ste_sign\")(x)\n+x = lq.layers.QuantDense(64)(x)\n+```\n+\n+We highly recommend using the first of these formulations: for the\n+other two formulations, intermediate layers - like batch normalization or\n+average pooling - and shortcut connections may result in non-binary input\n+to the convolutions.\n+\"\"\"\n \n import tensorflow as tf\n from larq import utils, math\n", "issue": "Document how to use a quantizer as an activation function\n`tf.keras.layers.Activation(\"ste_sign\")`\n", "before_files": [{"content": "\"\"\"A Quantizer defines the way of transforming a full precision input to a\nquantized output and the pseudo-gradient method used for the backwards pass.\"\"\"\n\nimport tensorflow as tf\nfrom larq import utils, math\n\n\n@tf.custom_gradient\ndef _binarize_with_identity_grad(x):\n    def grad(dy):\n        return dy\n\n    return math.sign(x), grad\n\n\n@tf.custom_gradient\ndef _binarize_with_weighted_grad(x):\n    def grad(dy):\n        return (1 - tf.abs(x)) * 2 * dy\n\n    return math.sign(x), grad\n\n\n@utils.register_keras_custom_object\n@utils.set_precision(1)\ndef ste_sign(x):\n    r\"\"\"\n    Sign binarization function.\n    \\\\[\n    q(x) = \\begin{cases}\n      -1 & x < 0 \\\\\\\n      1 & x \\geq 0\n    \\end{cases}\n    \\\\]\n\n    The gradient is estimated using the Straight-Through Estimator\n    (essentially the binarization is replaced by a clipped identity on the\n    backward pass).\n    \\\\[\\frac{\\partial q(x)}{\\partial x} = \\begin{cases}\n      1 & \\left|x\\right| \\leq 1 \\\\\\\n      0 & \\left|x\\right| > 1\n    \\end{cases}\\\\]\n\n    ```plot-activation\n    quantizers.ste_sign\n    ```\n\n    # Arguments\n    x: Input tensor.\n\n    # Returns\n    Binarized tensor.\n\n    # References\n    - [Binarized Neural Networks: Training Deep Neural Networks with Weights and\n      Activations Constrained to +1 or -1](http://arxiv.org/abs/1602.02830)\n    \"\"\"\n\n    x = tf.clip_by_value(x, -1, 1)\n\n    return _binarize_with_identity_grad(x)\n\n\n@utils.register_keras_custom_object\n@utils.set_precision(1)\ndef magnitude_aware_sign(x):\n    r\"\"\"\n    Magnitude-aware sign for Bi-Real Net.\n\n    ```plot-activation\n    quantizers.magnitude_aware_sign\n    ```\n\n    # Arguments\n    x: Input tensor\n\n    # Returns\n    Scaled binarized tensor (with values in $\\{-a, a\\}$, where $a$ is a float).\n\n    # References\n    - [Bi-Real Net: Enhancing the Performance of 1-bit CNNs With Improved\n      Representational Capability and Advanced Training\n      Algorithm](https://arxiv.org/abs/1808.00278)\n\n    \"\"\"\n    scale_factor = tf.reduce_mean(tf.abs(x), axis=list(range(len(x.shape) - 1)))\n\n    return tf.stop_gradient(scale_factor) * ste_sign(x)\n\n\n@utils.register_keras_custom_object\n@utils.set_precision(1)\ndef approx_sign(x):\n    r\"\"\"\n    Sign binarization function.\n    \\\\[\n    q(x) = \\begin{cases}\n      -1 & x < 0 \\\\\\\n      1 & x \\geq 0\n    \\end{cases}\n    \\\\]\n\n    The gradient is estimated using the ApproxSign method.\n    \\\\[\\frac{\\partial q(x)}{\\partial x} = \\begin{cases}\n      (2 - 2 \\left|x\\right|) & \\left|x\\right| \\leq 1 \\\\\\\n      0 & \\left|x\\right| > 1\n    \\end{cases}\n    \\\\]\n\n    ```plot-activation\n    quantizers.approx_sign\n    ```\n\n    # Arguments\n    x: Input tensor.\n\n    # Returns\n    Binarized tensor.\n\n    # References\n    - [Bi-Real Net: Enhancing the Performance of 1-bit CNNs With Improved\n      Representational Capability and Advanced\n      Training Algorithm](http://arxiv.org/abs/1808.00278)\n    \"\"\"\n\n    x = tf.clip_by_value(x, -1, 1)\n\n    return _binarize_with_weighted_grad(x)\n\n\n@utils.register_keras_custom_object\n@utils.set_precision(2)\nclass SteTern:\n    r\"\"\"\n    Ternarization function.\n    \\\\[\n    q(x) = \\begin{cases}\n    +1 & x > \\Delta \\\\\\\n    0 & |x| < \\Delta \\\\\\\n     -1 & x < - \\Delta\n    \\end{cases}\n    \\\\]\n\n    where $\\Delta$ is defined as the threshold and can be passed as an argument,\n    or can be calculated as per the Ternary Weight Networks original paper, such that\n\n    \\\\[\n    \\Delta = \\frac{0.7}{n} \\sum_{i=1}^{n} |W_i|\n    \\\\]\n    where we assume that $W_i$ is generated from a normal distribution.\n\n    The gradient is estimated using the Straight-Through Estimator\n    (essentially the Ternarization is replaced by a clipped identity on the\n    backward pass).\n    \\\\[\\frac{\\partial q(x)}{\\partial x} = \\begin{cases}\n    1 & \\left|x\\right| \\leq 1 \\\\\\\n    0 & \\left|x\\right| > 1\n    \\end{cases}\\\\]\n\n    ```plot-activation\n    quantizers.SteTern\n    ```\n\n    # Arguments\n    x: Input tensor.\n    threshold_value: The value for the threshold, $\\Delta$.\n    ternary_weight_networks: Boolean of whether to use the Ternary Weight Networks threshold calculation.\n\n    # Returns\n    Ternarized tensor.\n\n    # References\n    - [Ternary Weight Networks](http://arxiv.org/abs/1605.04711)\n    \"\"\"\n\n    def __init__(self, threshold_value=0.1, ternary_weight_networks=False):\n        self.threshold_value = threshold_value\n        self.ternary_weight_networks = ternary_weight_networks\n\n    def __call__(self, x):\n        x = tf.clip_by_value(x, -1, 1)\n        if self.ternary_weight_networks:\n            threshold = self.threshold_twn(x)\n        else:\n            threshold = self.threshold_value\n\n        @tf.custom_gradient\n        def _ternarize_with_identity_grad(x):\n            def grad(dy):\n                return dy\n\n            return (tf.sign(tf.sign(x + threshold) + tf.sign(x - threshold)), grad)\n\n        return _ternarize_with_identity_grad(x)\n\n    def threshold_twn(self, x):\n        return 0.7 * tf.reduce_sum(tf.abs(x)) / tf.cast(tf.size(x), x.dtype)\n\n    def get_config(self):\n        return {\n            \"threshold_value\": self.threshold_value,\n            \"ternary_weight_networks\": self.ternary_weight_networks,\n        }\n\n\ndef serialize(initializer):\n    return tf.keras.utils.serialize_keras_object(initializer)\n\n\ndef deserialize(name, custom_objects=None):\n    return tf.keras.utils.deserialize_keras_object(\n        name,\n        module_objects=globals(),\n        custom_objects=custom_objects,\n        printable_module_name=\"quantization function\",\n    )\n\n\ndef get(identifier):\n    if identifier is None:\n        return None\n    if isinstance(identifier, str):\n        return deserialize(str(identifier))\n    if callable(identifier):\n        return identifier\n    raise ValueError(\n        f\"Could not interpret quantization function identifier: {identifier}\"\n    )\n", "path": "larq/quantizers.py"}], "after_files": [{"content": "\"\"\"A Quantizer defines the way of transforming a full precision input to a\nquantized output and the pseudo-gradient method used for the backwards pass.\n\nQuantizers can either be used through quantizer arguments that are supported\nfor Larq layers, such as `input_quantizer` and `kernel_quantizer`; or they\ncan be used similar to activations, i.e. either through an `Activation` layer,\nor through the `activation` argument supported by all forward layer:\n\n```python\nimport tensorflow as tf\nimport larq as lq\n...\nx = lq.layers.QuantDense(64, activation=None)(x)\nx = lq.layers.QuantDense(64, input_quantizer=\"ste_sign\")(x)\n```\n\nis equivalent to:\n\n```python\nx = lq.layers.QuantDense(64)(x)\nx = tf.keras.layers.Activation(\"ste_sign\")(x)\nx = lq.layers.QuantDense(64)(x)\n```\n\nas well as:\n\n```python\nx = lq.layers.QuantDense(64, activation=\"ste_sign\")(x)\nx = lq.layers.QuantDense(64)(x)\n```\n\nWe highly recommend using the first of these formulations: for the\nother two formulations, intermediate layers - like batch normalization or\naverage pooling - and shortcut connections may result in non-binary input\nto the convolutions.\n\"\"\"\n\nimport tensorflow as tf\nfrom larq import utils, math\n\n\n@tf.custom_gradient\ndef _binarize_with_identity_grad(x):\n    def grad(dy):\n        return dy\n\n    return math.sign(x), grad\n\n\n@tf.custom_gradient\ndef _binarize_with_weighted_grad(x):\n    def grad(dy):\n        return (1 - tf.abs(x)) * 2 * dy\n\n    return math.sign(x), grad\n\n\n@utils.register_keras_custom_object\n@utils.set_precision(1)\ndef ste_sign(x):\n    r\"\"\"\n    Sign binarization function.\n    \\\\[\n    q(x) = \\begin{cases}\n      -1 & x < 0 \\\\\\\n      1 & x \\geq 0\n    \\end{cases}\n    \\\\]\n\n    The gradient is estimated using the Straight-Through Estimator\n    (essentially the binarization is replaced by a clipped identity on the\n    backward pass).\n    \\\\[\\frac{\\partial q(x)}{\\partial x} = \\begin{cases}\n      1 & \\left|x\\right| \\leq 1 \\\\\\\n      0 & \\left|x\\right| > 1\n    \\end{cases}\\\\]\n\n    ```plot-activation\n    quantizers.ste_sign\n    ```\n\n    # Arguments\n    x: Input tensor.\n\n    # Returns\n    Binarized tensor.\n\n    # References\n    - [Binarized Neural Networks: Training Deep Neural Networks with Weights and\n      Activations Constrained to +1 or -1](http://arxiv.org/abs/1602.02830)\n    \"\"\"\n\n    x = tf.clip_by_value(x, -1, 1)\n\n    return _binarize_with_identity_grad(x)\n\n\n@utils.register_keras_custom_object\n@utils.set_precision(1)\ndef magnitude_aware_sign(x):\n    r\"\"\"\n    Magnitude-aware sign for Bi-Real Net.\n\n    ```plot-activation\n    quantizers.magnitude_aware_sign\n    ```\n\n    # Arguments\n    x: Input tensor\n\n    # Returns\n    Scaled binarized tensor (with values in $\\{-a, a\\}$, where $a$ is a float).\n\n    # References\n    - [Bi-Real Net: Enhancing the Performance of 1-bit CNNs With Improved\n      Representational Capability and Advanced Training\n      Algorithm](https://arxiv.org/abs/1808.00278)\n\n    \"\"\"\n    scale_factor = tf.reduce_mean(tf.abs(x), axis=list(range(len(x.shape) - 1)))\n\n    return tf.stop_gradient(scale_factor) * ste_sign(x)\n\n\n@utils.register_keras_custom_object\n@utils.set_precision(1)\ndef approx_sign(x):\n    r\"\"\"\n    Sign binarization function.\n    \\\\[\n    q(x) = \\begin{cases}\n      -1 & x < 0 \\\\\\\n      1 & x \\geq 0\n    \\end{cases}\n    \\\\]\n\n    The gradient is estimated using the ApproxSign method.\n    \\\\[\\frac{\\partial q(x)}{\\partial x} = \\begin{cases}\n      (2 - 2 \\left|x\\right|) & \\left|x\\right| \\leq 1 \\\\\\\n      0 & \\left|x\\right| > 1\n    \\end{cases}\n    \\\\]\n\n    ```plot-activation\n    quantizers.approx_sign\n    ```\n\n    # Arguments\n    x: Input tensor.\n\n    # Returns\n    Binarized tensor.\n\n    # References\n    - [Bi-Real Net: Enhancing the Performance of 1-bit CNNs With Improved\n      Representational Capability and Advanced\n      Training Algorithm](http://arxiv.org/abs/1808.00278)\n    \"\"\"\n\n    x = tf.clip_by_value(x, -1, 1)\n\n    return _binarize_with_weighted_grad(x)\n\n\n@utils.register_keras_custom_object\n@utils.set_precision(2)\nclass SteTern:\n    r\"\"\"\n    Ternarization function.\n    \\\\[\n    q(x) = \\begin{cases}\n    +1 & x > \\Delta \\\\\\\n    0 & |x| < \\Delta \\\\\\\n     -1 & x < - \\Delta\n    \\end{cases}\n    \\\\]\n\n    where $\\Delta$ is defined as the threshold and can be passed as an argument,\n    or can be calculated as per the Ternary Weight Networks original paper, such that\n\n    \\\\[\n    \\Delta = \\frac{0.7}{n} \\sum_{i=1}^{n} |W_i|\n    \\\\]\n    where we assume that $W_i$ is generated from a normal distribution.\n\n    The gradient is estimated using the Straight-Through Estimator\n    (essentially the Ternarization is replaced by a clipped identity on the\n    backward pass).\n    \\\\[\\frac{\\partial q(x)}{\\partial x} = \\begin{cases}\n    1 & \\left|x\\right| \\leq 1 \\\\\\\n    0 & \\left|x\\right| > 1\n    \\end{cases}\\\\]\n\n    ```plot-activation\n    quantizers.SteTern\n    ```\n\n    # Arguments\n    x: Input tensor.\n    threshold_value: The value for the threshold, $\\Delta$.\n    ternary_weight_networks: Boolean of whether to use the Ternary Weight Networks threshold calculation.\n\n    # Returns\n    Ternarized tensor.\n\n    # References\n    - [Ternary Weight Networks](http://arxiv.org/abs/1605.04711)\n    \"\"\"\n\n    def __init__(self, threshold_value=0.1, ternary_weight_networks=False):\n        self.threshold_value = threshold_value\n        self.ternary_weight_networks = ternary_weight_networks\n\n    def __call__(self, x):\n        x = tf.clip_by_value(x, -1, 1)\n        if self.ternary_weight_networks:\n            threshold = self.threshold_twn(x)\n        else:\n            threshold = self.threshold_value\n\n        @tf.custom_gradient\n        def _ternarize_with_identity_grad(x):\n            def grad(dy):\n                return dy\n\n            return (tf.sign(tf.sign(x + threshold) + tf.sign(x - threshold)), grad)\n\n        return _ternarize_with_identity_grad(x)\n\n    def threshold_twn(self, x):\n        return 0.7 * tf.reduce_sum(tf.abs(x)) / tf.cast(tf.size(x), x.dtype)\n\n    def get_config(self):\n        return {\n            \"threshold_value\": self.threshold_value,\n            \"ternary_weight_networks\": self.ternary_weight_networks,\n        }\n\n\ndef serialize(initializer):\n    return tf.keras.utils.serialize_keras_object(initializer)\n\n\ndef deserialize(name, custom_objects=None):\n    return tf.keras.utils.deserialize_keras_object(\n        name,\n        module_objects=globals(),\n        custom_objects=custom_objects,\n        printable_module_name=\"quantization function\",\n    )\n\n\ndef get(identifier):\n    if identifier is None:\n        return None\n    if isinstance(identifier, str):\n        return deserialize(str(identifier))\n    if callable(identifier):\n        return identifier\n    raise ValueError(\n        f\"Could not interpret quantization function identifier: {identifier}\"\n    )\n", "path": "larq/quantizers.py"}]}