|
34 | 34 | EspNNQuantizer, |
35 | 35 | fused_activation_kind, |
36 | 36 | ) |
| 37 | +from executorch.backends.espressif.reference import ProgramEmulator # noqa: E402 |
37 | 38 | from executorch.backends.espressif.serialization.xts3 import ( # noqa: E402 |
38 | 39 | build_graph, |
| 40 | + check_dataflow, |
39 | 41 | check_runtime_limits, |
40 | 42 | MAX_GRAPH_IO, |
41 | 43 | MAX_GRAPH_OPERATIONS, |
@@ -171,3 +173,54 @@ def test_runtime_limits_reject_misaligned_int32_constants(): |
171 | 173 | ] |
172 | 174 | with pytest.raises(ValueError, match="4-byte aligned"): |
173 | 175 | check_runtime_limits(dataclasses.replace(graph, tensors=tensors)) |
| 176 | + |
| 177 | + |
| 178 | +class DropoutClassifier(nn.Module): |
| 179 | + """MobileNetV3's classifier: Linear, Hardswish, in-place Dropout, Linear.""" |
| 180 | + |
| 181 | + def __init__(self): |
| 182 | + super().__init__() |
| 183 | + self.head = nn.Sequential( |
| 184 | + nn.Linear(16, 32), |
| 185 | + nn.Hardswish(inplace=True), |
| 186 | + nn.Dropout(0.2, inplace=True), |
| 187 | + nn.Linear(32, 10), |
| 188 | + ) |
| 189 | + |
| 190 | + def forward(self, x): |
| 191 | + return self.head(x) |
| 192 | + |
| 193 | + |
| 194 | +def test_dropout_between_quantized_ops_reads_the_producer(): |
| 195 | + converted, program = quantize(DropoutClassifier(), [(1, 16)]) |
| 196 | + graph = build_graph(program) |
| 197 | + check_dataflow(graph) |
| 198 | + last = graph.operations[-1] |
| 199 | + assert last.opcode == XTS3OpCode.LINEAR |
| 200 | + assert last.inputs[0] in graph.operations[-2].outputs |
| 201 | + |
| 202 | + edge, report = to_espnn_edge(program, allow_cpu_fallback=False) |
| 203 | + assert report["partitions"] == 1 |
| 204 | + x = torch.randn(1, 16) |
| 205 | + expected = converted(x) |
| 206 | + actual = ProgramEmulator(edge.exported_program())(x).to(torch.float32) |
| 207 | + assert torch.allclose(actual, expected, atol=0.2) |
| 208 | + |
| 209 | + |
| 210 | +def test_dataflow_rejects_reading_an_unwritten_activation(): |
| 211 | + graph = _small_graph() |
| 212 | + unwritten = dataclasses.replace( |
| 213 | + graph.tensors[graph.operations[-1].inputs[0]], |
| 214 | + tensor_id=len(graph.tensors), |
| 215 | + ) |
| 216 | + last = dataclasses.replace( |
| 217 | + graph.operations[-1], |
| 218 | + inputs=[unwritten.tensor_id, *graph.operations[-1].inputs[1:]], |
| 219 | + ) |
| 220 | + broken = dataclasses.replace( |
| 221 | + graph, |
| 222 | + tensors=[*graph.tensors, unwritten], |
| 223 | + operations=[*graph.operations[:-1], last], |
| 224 | + ) |
| 225 | + with pytest.raises(ValueError, match="nothing writes"): |
| 226 | + check_dataflow(broken) |
0 commit comments