femtocrux 1.2.1__py3-none-any.whl → 2.0.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- femtocrux/VERSION +1 -1
- femtocrux/client/client.py +18 -38
- femtocrux/grpc/compiler_service_pb2.py +22 -14
- femtocrux/server/__init__.py +1 -0
- femtocrux/server/compiler_frontend.py +168 -0
- femtocrux/server/server.py +21 -34
- femtocrux/util/utils.py +71 -11
- {femtocrux-1.2.1.dist-info → femtocrux-2.0.0.dist-info}/METADATA +1 -1
- femtocrux-2.0.0.dist-info/RECORD +22 -0
- {femtocrux-1.2.1.dist-info → femtocrux-2.0.0.dist-info}/WHEEL +1 -1
- femtocrux/femtostack/__init__.py +0 -7
- femtocrux/femtostack/common/__init__.py +0 -10
- femtocrux/femtostack/common/frontend.py +0 -109
- femtocrux/femtostack/common/metrics.py +0 -184
- femtocrux/femtostack/common/sim_io.py +0 -470
- femtocrux/femtostack/tflite_api/__init__.py +0 -6
- femtocrux/femtostack/tflite_api/tflite_frontend.py +0 -6
- femtocrux/femtostack/torch_api/__init__.py +0 -0
- femtocrux/femtostack/torch_api/frontend.py +0 -136
- femtocrux-1.2.1.dist-info/RECORD +0 -30
- {femtocrux-1.2.1.dist-info → femtocrux-2.0.0.dist-info}/licenses/LICENSE +0 -0
- {femtocrux-1.2.1.dist-info → femtocrux-2.0.0.dist-info}/top_level.txt +0 -0
femtocrux/VERSION
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
|
|
1
|
+
2.0.0
|
femtocrux/client/client.py
CHANGED
|
@@ -11,12 +11,16 @@ import pickle
|
|
|
11
11
|
import queue
|
|
12
12
|
import sys
|
|
13
13
|
import time
|
|
14
|
-
from typing import Any, List, Tuple
|
|
14
|
+
from typing import Any, List, Tuple
|
|
15
15
|
from contextlib import contextmanager
|
|
16
16
|
|
|
17
17
|
from fmot.fqir import GraphProto
|
|
18
18
|
|
|
19
|
-
from femtocrux.util.utils import
|
|
19
|
+
from femtocrux.util.utils import (
|
|
20
|
+
get_channel_options,
|
|
21
|
+
serialize_sim_inputs_message,
|
|
22
|
+
deserialize_simulation_output,
|
|
23
|
+
)
|
|
20
24
|
|
|
21
25
|
# GRPC artifacts
|
|
22
26
|
import femtocrux.grpc.compiler_service_pb2 as cs_pb2
|
|
@@ -214,34 +218,17 @@ class Simulator:
|
|
|
214
218
|
|
|
215
219
|
def simulate(
|
|
216
220
|
self,
|
|
217
|
-
inputs:
|
|
218
|
-
|
|
219
|
-
dequantize_outputs: bool = False,
|
|
220
|
-
input_period: float = None,
|
|
221
|
+
inputs: Iterable[np.array],
|
|
222
|
+
input_period: float = 0.016,
|
|
221
223
|
) -> List[np.array]:
|
|
222
224
|
"""
|
|
223
225
|
Simulates the model on the given inputs.
|
|
224
226
|
|
|
225
|
-
:
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
using its scale :math:`a` and zero point :math:`z`, according to the formula
|
|
231
|
-
.. math::
|
|
232
|
-
f(x) = a (x - z)
|
|
233
|
-
|
|
234
|
-
:type dequantize_outputs: bool, optional
|
|
235
|
-
:param dequantize_outputs: If true, dequantizes each output from integer to
|
|
236
|
-
float using its scale :math:`a` and zero point :math:`z`, according to the
|
|
237
|
-
formula
|
|
238
|
-
.. math::
|
|
239
|
-
g(x) = \\frac{x}{a} + z.
|
|
240
|
-
|
|
241
|
-
:param sim_duration: The total simulation time, in seconds. Used to estimate
|
|
242
|
-
static power consumption. For example, in a streaming model, this should be the
|
|
243
|
-
time elapsed between model invocations. By default, this is just the estimated
|
|
244
|
-
latency of the model.
|
|
227
|
+
:param inputs: The input tensors in a dictionary of:
|
|
228
|
+
{
|
|
229
|
+
"input_name1": np.ndarray(int16 types ...),
|
|
230
|
+
"input_name2": np.ndarray([int16 types ...])
|
|
231
|
+
}
|
|
245
232
|
:type input_period (float, optional): Duration between each input in a sequence,
|
|
246
233
|
in seconds.
|
|
247
234
|
|
|
@@ -249,24 +236,14 @@ class Simulator:
|
|
|
249
236
|
:return: The output tensors.
|
|
250
237
|
|
|
251
238
|
"""
|
|
252
|
-
# FIXME need to handle multiple inputs
|
|
253
|
-
# Convert a single ndarray to a list containing that input.
|
|
254
|
-
# Otherwise, this will be interpreted as an iterable of (n-1)-d arrays
|
|
255
|
-
if isinstance(inputs, np.ndarray):
|
|
256
|
-
inputs = [inputs]
|
|
257
239
|
|
|
258
240
|
simulation_request = cs_pb2.simulation_input(
|
|
259
|
-
data=
|
|
260
|
-
data=[numpy_to_ndarray(x) for x in inputs],
|
|
261
|
-
quantize_inputs=quantize_inputs,
|
|
262
|
-
dequantize_outputs=dequantize_outputs,
|
|
263
|
-
input_period=input_period,
|
|
264
|
-
)
|
|
241
|
+
data=serialize_sim_inputs_message(inputs, input_period)
|
|
265
242
|
)
|
|
266
243
|
self._send_request(simulation_request)
|
|
267
244
|
response = self._get_response()
|
|
268
245
|
|
|
269
|
-
return
|
|
246
|
+
return deserialize_simulation_output(response.outputs), response.report
|
|
270
247
|
|
|
271
248
|
|
|
272
249
|
class CompilerClientImpl:
|
|
@@ -348,6 +325,9 @@ class CompilerClientImpl:
|
|
|
348
325
|
"""
|
|
349
326
|
return Simulator(client=self, model=model, options=options)
|
|
350
327
|
|
|
328
|
+
def get_simulator_object(self, model: Model, options: dict = {}) -> Simulator:
|
|
329
|
+
return self.simulate(model, options)
|
|
330
|
+
|
|
351
331
|
def _server_version(self) -> str:
|
|
352
332
|
"""Queries the femtocrux version running on the server."""
|
|
353
333
|
response = self.stub.version(google.protobuf.empty_pb2.Empty())
|
|
@@ -26,13 +26,17 @@ from google.protobuf import empty_pb2 as google_dot_protobuf_dot_empty__pb2
|
|
|
26
26
|
from google.protobuf import struct_pb2 as google_dot_protobuf_dot_struct__pb2
|
|
27
27
|
|
|
28
28
|
|
|
29
|
-
DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x16\x63ompiler_service.proto\x12\nfscompiler\x1a\x1bgoogle/protobuf/empty.proto\x1a\x1cgoogle/protobuf/struct.proto\"g\n\x04\x66qir\x12\r\n\x05model\x18\x01 \x01(\x0c\x12\x16\n\tbatch_dim\x18\x02 \x01(\x03H\x00\x88\x01\x01\x12\x19\n\x0csequence_dim\x18\x03 \x01(\x03H\x01\x88\x01\x01\x42\x0c\n\n_batch_dimB\x0f\n\r_sequence_dim\"G\n\x06tflite\x12\r\n\x05model\x18\x01 \x01(\x0c\x12\x1b\n\x0esignature_name\x18\x02 \x01(\tH\x00\x88\x01\x01\x42\x11\n\x0f_signature_name\"\x90\x01\n\x05model\x12 \n\x04\x66qir\x18\x01 \x01(\x0b\x32\x10.fscompiler.fqirH\x00\x12$\n\x06tflite\x18\x02 \x01(\x0b\x32\x12.fscompiler.tfliteH\x00\x12-\n\x07options\x18\x03 \x01(\x0b\x32\x17.google.protobuf.StructH\x01\x88\x01\x01\x42\x04\n\x02irB\n\n\x08_options\"&\n\x06status\x12\x0f\n\x07success\x18\x01 \x01(\x08\x12\x0b\n\x03msg\x18\x02 \x01(\t\"I\n\x12\x63ompiled_artifacts\x12\x0f\n\x07\x62itfile\x18\x01 \x01(\x0c\x12\"\n\x06status\x18\x02 \x01(\x0b\x32\x12.fscompiler.status\"
|
|
29
|
+
DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x16\x63ompiler_service.proto\x12\nfscompiler\x1a\x1bgoogle/protobuf/empty.proto\x1a\x1cgoogle/protobuf/struct.proto\"g\n\x04\x66qir\x12\r\n\x05model\x18\x01 \x01(\x0c\x12\x16\n\tbatch_dim\x18\x02 \x01(\x03H\x00\x88\x01\x01\x12\x19\n\x0csequence_dim\x18\x03 \x01(\x03H\x01\x88\x01\x01\x42\x0c\n\n_batch_dimB\x0f\n\r_sequence_dim\"G\n\x06tflite\x12\r\n\x05model\x18\x01 \x01(\x0c\x12\x1b\n\x0esignature_name\x18\x02 \x01(\tH\x00\x88\x01\x01\x42\x11\n\x0f_signature_name\"\x90\x01\n\x05model\x12 \n\x04\x66qir\x18\x01 \x01(\x0b\x32\x10.fscompiler.fqirH\x00\x12$\n\x06tflite\x18\x02 \x01(\x0b\x32\x12.fscompiler.tfliteH\x00\x12-\n\x07options\x18\x03 \x01(\x0b\x32\x17.google.protobuf.StructH\x01\x88\x01\x01\x42\x04\n\x02irB\n\n\x08_options\"&\n\x06status\x12\x0f\n\x07success\x18\x01 \x01(\x08\x12\x0b\n\x03msg\x18\x02 \x01(\t\"I\n\x12\x63ompiled_artifacts\x12\x0f\n\x07\x62itfile\x18\x01 \x01(\x0c\x12\"\n\x06status\x18\x02 \x01(\x0b\x32\x12.fscompiler.status\"5\n\x07ndarray\x12\x0c\n\x04\x64\x61ta\x18\x01 \x01(\x0c\x12\r\n\x05shape\x18\x02 \x03(\x03\x12\r\n\x05\x64type\x18\x03 \x01(\t\"\x14\n\x04\x64\x61ta\x12\x0c\n\x04\x64\x61ta\x18\x01 \x01(\x0c\"\xe6\x01\n\x0fsimulation_data\x12\x37\n\x06inputs\x18\x01 \x03(\x0b\x32\'.fscompiler.simulation_data.InputsEntry\x12\x19\n\x0csim_duration\x18\x02 \x01(\x02H\x00\x88\x01\x01\x12\x19\n\x0cinput_period\x18\x03 \x01(\x02H\x01\x88\x01\x01\x1a\x42\n\x0bInputsEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\"\n\x05value\x18\x02 \x01(\x0b\x32\x13.fscompiler.ndarray:\x02\x38\x01\x42\x0f\n\r_sim_durationB\x0f\n\r_input_period\"t\n\x10simulation_input\x12\"\n\x05model\x18\x01 \x01(\x0b\x32\x11.fscompiler.modelH\x00\x12+\n\x04\x64\x61ta\x18\x02 \x01(\x0b\x32\x1b.fscompiler.simulation_dataH\x00\x42\x0f\n\rmodel_or_data\"\xc9\x01\n\x11simulation_output\x12;\n\x07outputs\x18\x01 \x03(\x0b\x32*.fscompiler.simulation_output.OutputsEntry\x12\x0e\n\x06report\x18\x02 \x01(\t\x12\"\n\x06status\x18\x03 \x01(\x0b\x32\x12.fscompiler.status\x1a\x43\n\x0cOutputsEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\"\n\x05value\x18\x02 \x01(\x0b\x32\x13.fscompiler.ndarray:\x02\x38\x01\"\x1f\n\x0cversion_info\x12\x0f\n\x07version\x18\x01 \x01(\t2\x85\x02\n\x07\x43ompile\x12>\n\x07\x63ompile\x12\x11.fscompiler.model\x1a\x1e.fscompiler.compiled_artifacts\"\x00\x12,\n\x04ping\x12\x10.fscompiler.data\x1a\x10.fscompiler.data\"\x00\x12M\n\x08simulate\x12\x1c.fscompiler.simulation_input\x1a\x1d.fscompiler.simulation_output\"\x00(\x01\x30\x01\x12=\n\x07version\x12\x16.google.protobuf.Empty\x1a\x18.fscompiler.version_info\"\x00\x62\x06proto3')
|
|
30
30
|
|
|
31
31
|
_globals = globals()
|
|
32
32
|
_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals)
|
|
33
33
|
_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'compiler_service_pb2', _globals)
|
|
34
34
|
if not _descriptor._USE_C_DESCRIPTORS:
|
|
35
35
|
DESCRIPTOR._loaded_options = None
|
|
36
|
+
_globals['_SIMULATION_DATA_INPUTSENTRY']._loaded_options = None
|
|
37
|
+
_globals['_SIMULATION_DATA_INPUTSENTRY']._serialized_options = b'8\001'
|
|
38
|
+
_globals['_SIMULATION_OUTPUT_OUTPUTSENTRY']._loaded_options = None
|
|
39
|
+
_globals['_SIMULATION_OUTPUT_OUTPUTSENTRY']._serialized_options = b'8\001'
|
|
36
40
|
_globals['_FQIR']._serialized_start=97
|
|
37
41
|
_globals['_FQIR']._serialized_end=200
|
|
38
42
|
_globals['_TFLITE']._serialized_start=202
|
|
@@ -44,17 +48,21 @@ if not _descriptor._USE_C_DESCRIPTORS:
|
|
|
44
48
|
_globals['_COMPILED_ARTIFACTS']._serialized_start=462
|
|
45
49
|
_globals['_COMPILED_ARTIFACTS']._serialized_end=535
|
|
46
50
|
_globals['_NDARRAY']._serialized_start=537
|
|
47
|
-
_globals['_NDARRAY']._serialized_end=
|
|
48
|
-
_globals['_DATA']._serialized_start=
|
|
49
|
-
_globals['_DATA']._serialized_end=
|
|
50
|
-
_globals['_SIMULATION_DATA']._serialized_start=
|
|
51
|
-
_globals['_SIMULATION_DATA']._serialized_end=
|
|
52
|
-
_globals['
|
|
53
|
-
_globals['
|
|
54
|
-
_globals['
|
|
55
|
-
_globals['
|
|
56
|
-
_globals['
|
|
57
|
-
_globals['
|
|
58
|
-
_globals['
|
|
59
|
-
_globals['
|
|
51
|
+
_globals['_NDARRAY']._serialized_end=590
|
|
52
|
+
_globals['_DATA']._serialized_start=592
|
|
53
|
+
_globals['_DATA']._serialized_end=612
|
|
54
|
+
_globals['_SIMULATION_DATA']._serialized_start=615
|
|
55
|
+
_globals['_SIMULATION_DATA']._serialized_end=845
|
|
56
|
+
_globals['_SIMULATION_DATA_INPUTSENTRY']._serialized_start=745
|
|
57
|
+
_globals['_SIMULATION_DATA_INPUTSENTRY']._serialized_end=811
|
|
58
|
+
_globals['_SIMULATION_INPUT']._serialized_start=847
|
|
59
|
+
_globals['_SIMULATION_INPUT']._serialized_end=963
|
|
60
|
+
_globals['_SIMULATION_OUTPUT']._serialized_start=966
|
|
61
|
+
_globals['_SIMULATION_OUTPUT']._serialized_end=1167
|
|
62
|
+
_globals['_SIMULATION_OUTPUT_OUTPUTSENTRY']._serialized_start=1100
|
|
63
|
+
_globals['_SIMULATION_OUTPUT_OUTPUTSENTRY']._serialized_end=1167
|
|
64
|
+
_globals['_VERSION_INFO']._serialized_start=1169
|
|
65
|
+
_globals['_VERSION_INFO']._serialized_end=1200
|
|
66
|
+
_globals['_COMPILE']._serialized_start=1203
|
|
67
|
+
_globals['_COMPILE']._serialized_end=1464
|
|
60
68
|
# @@protoc_insertion_point(module_scope)
|
femtocrux/server/__init__.py
CHANGED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
from .compiler_frontend import CompilerFrontend, TorchCompiler # noqa: F401
|
|
@@ -0,0 +1,168 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Copyright Femtosense 2024
|
|
3
|
+
|
|
4
|
+
By using this software package, you agree to abide by the terms and conditions
|
|
5
|
+
in the license agreement found at https://femtosense.ai/legal/eula/
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
import numpy as np
|
|
9
|
+
import os
|
|
10
|
+
import tempfile
|
|
11
|
+
import torch
|
|
12
|
+
import zipfile
|
|
13
|
+
|
|
14
|
+
from fmot import ConvertedModel
|
|
15
|
+
from fmot.fqir import GraphProto
|
|
16
|
+
from femtomapper import MapperConf, Mapper, MapperState
|
|
17
|
+
from femtobehav.fasmir import FASMIR
|
|
18
|
+
from femtobehav.sim import SimRunner
|
|
19
|
+
from typing import Any
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
class CompilerFrontend:
|
|
23
|
+
"""A generic compiler frontend, must be subclassed for each input IR/framework"""
|
|
24
|
+
|
|
25
|
+
def __init__(self, input_ir: Any, fasmir: FASMIR = None):
|
|
26
|
+
self.input_ir = input_ir
|
|
27
|
+
self.fasmir = fasmir
|
|
28
|
+
# self.io_wrapper = io_wrapper
|
|
29
|
+
|
|
30
|
+
@property
|
|
31
|
+
def is_compiled(self):
|
|
32
|
+
return self.fasmir is not None
|
|
33
|
+
|
|
34
|
+
def _compile(self, input_ir: Any, options: dict) -> FASMIR:
|
|
35
|
+
"""
|
|
36
|
+
Runs FM compiler to generate FASMIR, and encode io information in a
|
|
37
|
+
SimIOWrapper object.
|
|
38
|
+
|
|
39
|
+
Must be implemented for each frontend subclass.
|
|
40
|
+
|
|
41
|
+
returns FASMIR
|
|
42
|
+
"""
|
|
43
|
+
raise NotImplementedError(
|
|
44
|
+
"Subclasses need to implement this based on their input ir"
|
|
45
|
+
)
|
|
46
|
+
|
|
47
|
+
def compile(self, options: dict = {}):
|
|
48
|
+
if not self.is_compiled:
|
|
49
|
+
self.fasmir = self._compile(self.input_ir, options)
|
|
50
|
+
|
|
51
|
+
def dump_bitfile(self, encrypt: bool = True) -> bytes:
|
|
52
|
+
"""Dumps a bitfile used to program the SPU."""
|
|
53
|
+
if not self.is_compiled:
|
|
54
|
+
raise RuntimeError("Model must be compiled before dumping bitfile")
|
|
55
|
+
|
|
56
|
+
with tempfile.TemporaryFile() as tmpfile:
|
|
57
|
+
with tempfile.TemporaryDirectory() as dirname:
|
|
58
|
+
# Dump memory files to a directory
|
|
59
|
+
runner = SimRunner(self.fasmir, data_dir=dirname, encrypt=encrypt)
|
|
60
|
+
runner.reset()
|
|
61
|
+
runner.finish()
|
|
62
|
+
|
|
63
|
+
# Archive the directory
|
|
64
|
+
with zipfile.ZipFile(
|
|
65
|
+
tmpfile, mode="w", compression=zipfile.ZIP_DEFLATED
|
|
66
|
+
) as archive:
|
|
67
|
+
for relpath in os.listdir(dirname):
|
|
68
|
+
abspath = os.path.join(dirname, relpath)
|
|
69
|
+
archive.write(abspath, arcname=relpath)
|
|
70
|
+
|
|
71
|
+
# Read out the bytes in the archive
|
|
72
|
+
tmpfile.seek(0)
|
|
73
|
+
bitfile = tmpfile.read()
|
|
74
|
+
|
|
75
|
+
return bitfile
|
|
76
|
+
|
|
77
|
+
def _get_padded_len(self, fasmir: FASMIR, name: str):
|
|
78
|
+
try:
|
|
79
|
+
fasmir_var = fasmir.data_vars[name]
|
|
80
|
+
except KeyError:
|
|
81
|
+
raise ValueError(
|
|
82
|
+
"Failed to find FASMIR variable corresponding to name %s" % name
|
|
83
|
+
)
|
|
84
|
+
return fasmir_var.numpy.shape[0]
|
|
85
|
+
|
|
86
|
+
def run_behavioral_simulator(
|
|
87
|
+
self, inputs: dict[str, np.ndarray], input_period: float = None, **kwargs
|
|
88
|
+
):
|
|
89
|
+
"""
|
|
90
|
+
Runs the behavioral simulator and returns outputs and metrics.
|
|
91
|
+
|
|
92
|
+
Arguments:
|
|
93
|
+
args (np.ndarray): Input tensors to the simulator, as numpy arrays. Either
|
|
94
|
+
floating-point or integer (see `quantize_inputs` for
|
|
95
|
+
more detail on input datatypes).
|
|
96
|
+
input_period (float, optional): total simulation time.
|
|
97
|
+
|
|
98
|
+
"""
|
|
99
|
+
runner = SimRunner(self.fasmir, **kwargs)
|
|
100
|
+
runner.reset()
|
|
101
|
+
outputs, __, __ = runner.run(inputs)
|
|
102
|
+
metrics = runner.get_metrics(input_period, concise=True, as_yamlable=True)
|
|
103
|
+
runner.finish()
|
|
104
|
+
return outputs, metrics
|
|
105
|
+
|
|
106
|
+
|
|
107
|
+
def _compile_fqir(graph: GraphProto, options: dict) -> FASMIR:
|
|
108
|
+
mapper_conf = MapperConf(**options)
|
|
109
|
+
mapper = Mapper(mapper_conf)
|
|
110
|
+
mapper_state = MapperState(fqir=graph)
|
|
111
|
+
|
|
112
|
+
# compile:
|
|
113
|
+
mapper_state = mapper.do(mapper_state)
|
|
114
|
+
|
|
115
|
+
# extract fasmir
|
|
116
|
+
fasmir = mapper_state.fasmir
|
|
117
|
+
return fasmir
|
|
118
|
+
|
|
119
|
+
|
|
120
|
+
class TorchCompiler(CompilerFrontend):
|
|
121
|
+
def __init__(self, graph: GraphProto, batch_dim: int = None, seq_dim: int = None):
|
|
122
|
+
assert isinstance(graph, GraphProto)
|
|
123
|
+
|
|
124
|
+
super().__init__(input_ir=graph)
|
|
125
|
+
self.batch_dim = batch_dim
|
|
126
|
+
self.seq_dim = seq_dim
|
|
127
|
+
|
|
128
|
+
def _compile(self, input_ir: GraphProto, options: dict) -> FASMIR:
|
|
129
|
+
fasmir = _compile_fqir(input_ir, options)
|
|
130
|
+
# wrapper = self._get_fqir_iowrapper(input_ir, fasmir)
|
|
131
|
+
return fasmir
|
|
132
|
+
|
|
133
|
+
@classmethod
|
|
134
|
+
def from_fqir(cls, graph: GraphProto, batch_dim: int = None, seq_dim: int = None):
|
|
135
|
+
assert isinstance(graph, GraphProto)
|
|
136
|
+
return cls(graph, batch_dim, seq_dim)
|
|
137
|
+
|
|
138
|
+
@classmethod
|
|
139
|
+
def from_converted_model(
|
|
140
|
+
cls,
|
|
141
|
+
model: ConvertedModel,
|
|
142
|
+
batch_dim: int = None,
|
|
143
|
+
seq_dim: int = None,
|
|
144
|
+
experimental_tracing=False,
|
|
145
|
+
):
|
|
146
|
+
assert isinstance(model, ConvertedModel)
|
|
147
|
+
graph = model.trace(experimental_hybrid_tracing=experimental_tracing)
|
|
148
|
+
return cls(graph, batch_dim, seq_dim)
|
|
149
|
+
|
|
150
|
+
@classmethod
|
|
151
|
+
def from_torch_module(
|
|
152
|
+
cls,
|
|
153
|
+
module: torch.nn.Module,
|
|
154
|
+
calibration_data,
|
|
155
|
+
precision: str = "double",
|
|
156
|
+
batch_dim: int = None,
|
|
157
|
+
seq_dim: int = None,
|
|
158
|
+
experimental_tracing=False,
|
|
159
|
+
conversion_kwargs: dict = {},
|
|
160
|
+
):
|
|
161
|
+
cmodel = ConvertedModel(
|
|
162
|
+
module, precision, batch_dim=batch_dim, seq_dim=seq_dim, **conversion_kwargs
|
|
163
|
+
)
|
|
164
|
+
cmodel.quantize(calibration_data)
|
|
165
|
+
|
|
166
|
+
return TorchCompiler.from_converted_model(
|
|
167
|
+
cmodel, batch_dim, seq_dim, experimental_tracing
|
|
168
|
+
)
|
femtocrux/server/server.py
CHANGED
|
@@ -1,28 +1,29 @@
|
|
|
1
1
|
import argparse
|
|
2
2
|
from collections.abc import Iterable
|
|
3
3
|
import concurrent
|
|
4
|
-
|
|
4
|
+
from google.protobuf.json_format import MessageToDict
|
|
5
5
|
import grpc
|
|
6
6
|
import logging
|
|
7
7
|
import pickle
|
|
8
8
|
import sys
|
|
9
9
|
|
|
10
|
-
from femtocrux.
|
|
11
|
-
from femtocrux.femtostack.tflite_api.tflite_frontend import TFLiteCompiler
|
|
12
|
-
from femtocrux.femtostack.torch_api.frontend import TorchCompiler
|
|
10
|
+
from femtocrux.server import CompilerFrontend, TorchCompiler
|
|
13
11
|
|
|
14
12
|
from femtocrux.server.exceptions import format_exception, format_exception_from_exc
|
|
15
13
|
from femtocrux.util.utils import (
|
|
16
|
-
numpy_to_ndarray,
|
|
17
|
-
ndarray_to_numpy,
|
|
18
14
|
field_or_none,
|
|
19
15
|
get_channel_options,
|
|
16
|
+
serialize_simulation_output,
|
|
17
|
+
deserialize_simulation_data,
|
|
20
18
|
)
|
|
21
19
|
|
|
22
20
|
# Import GRPC artifacts
|
|
23
21
|
import femtocrux.grpc.compiler_service_pb2 as cs_pb2
|
|
24
22
|
import femtocrux.grpc.compiler_service_pb2_grpc as cs_pb2_grpc
|
|
25
23
|
|
|
24
|
+
# Set recursion limit to avoid FQIR recursion bug
|
|
25
|
+
sys.setrecursionlimit(10000)
|
|
26
|
+
|
|
26
27
|
# Network parameters
|
|
27
28
|
default_port = "50051"
|
|
28
29
|
|
|
@@ -49,19 +50,11 @@ class CompileServicer(cs_pb2_grpc.CompileServicer):
|
|
|
49
50
|
seq_dim=field_or_none(fqir, "sequence_dim"),
|
|
50
51
|
)
|
|
51
52
|
|
|
52
|
-
def _get_tflite_compiler(self, model: cs_pb2.model) -> CompilerFrontend:
|
|
53
|
-
"""Get a TFLite compiler from a model message"""
|
|
54
|
-
tflite = model.tflite
|
|
55
|
-
return TFLiteCompiler(
|
|
56
|
-
tflite.model, signature=field_or_none(tflite, "signature_name")
|
|
57
|
-
)
|
|
58
|
-
|
|
59
53
|
def _compile_model(self, model: cs_pb2.model, context) -> CompilerFrontend:
|
|
60
54
|
"""Compile a model, for simulation or bitfile generation."""
|
|
61
55
|
# Get a compiler for the model
|
|
62
56
|
model_type_map = {
|
|
63
57
|
"fqir": self._get_fqir_compiler,
|
|
64
|
-
"tflite": self._get_tflite_compiler,
|
|
65
58
|
}
|
|
66
59
|
model_type = model.WhichOneof("ir")
|
|
67
60
|
compiler = model_type_map[model_type](model)
|
|
@@ -71,7 +64,7 @@ class CompileServicer(cs_pb2_grpc.CompileServicer):
|
|
|
71
64
|
if options_struct is None:
|
|
72
65
|
options = {}
|
|
73
66
|
else:
|
|
74
|
-
options =
|
|
67
|
+
options = MessageToDict(options_struct)
|
|
75
68
|
|
|
76
69
|
# Compile the model
|
|
77
70
|
compiler.compile(options=options)
|
|
@@ -138,28 +131,25 @@ class CompileServicer(cs_pb2_grpc.CompileServicer):
|
|
|
138
131
|
# Subsequent requests must be data
|
|
139
132
|
for data_request in request_iterator:
|
|
140
133
|
# Check that this is a data message
|
|
141
|
-
if
|
|
134
|
+
if data_request.WhichOneof("model_or_data") not in ["data"]:
|
|
142
135
|
yield cs_pb2.simulation_output(
|
|
143
136
|
status=cs_pb2.status(success=False, msg="Expected data message.")
|
|
144
137
|
)
|
|
145
138
|
continue
|
|
146
139
|
|
|
147
|
-
|
|
148
|
-
sim_data = data_request.data
|
|
149
|
-
data = [ndarray_to_numpy(x) for x in sim_data.data]
|
|
150
|
-
assert (
|
|
151
|
-
len(data) == 1
|
|
152
|
-
), "Behavioral sim currently only supports 1 input" # FIXME
|
|
153
|
-
data = data[0]
|
|
140
|
+
data = data_request.data
|
|
154
141
|
|
|
155
142
|
# Simulate the model
|
|
156
143
|
try:
|
|
157
|
-
|
|
158
|
-
data
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
144
|
+
if data_request.WhichOneof("model_or_data") == "data":
|
|
145
|
+
### New Path with int64 proto message holding data
|
|
146
|
+
deserialized_inputs = deserialize_simulation_data(data.inputs)
|
|
147
|
+
outputs, metrics = compiler.run_behavioral_simulator(
|
|
148
|
+
deserialized_inputs,
|
|
149
|
+
input_period=field_or_none(data, "input_period"),
|
|
150
|
+
)
|
|
151
|
+
else:
|
|
152
|
+
raise (Exception("Didn't get a model or data"))
|
|
163
153
|
except Exception as exc:
|
|
164
154
|
msg = "Simulator raised exception:\n%s" % (
|
|
165
155
|
format_exception_from_exc(exc)
|
|
@@ -171,11 +161,8 @@ class CompileServicer(cs_pb2_grpc.CompileServicer):
|
|
|
171
161
|
continue
|
|
172
162
|
|
|
173
163
|
# Respond with output data
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
report=metrics,
|
|
177
|
-
status=cs_pb2.status(success=True),
|
|
178
|
-
)
|
|
164
|
+
message = serialize_simulation_output(outputs, metrics)
|
|
165
|
+
yield message
|
|
179
166
|
|
|
180
167
|
def version(self, empty, context) -> cs_pb2.version_info:
|
|
181
168
|
"""Return the version of femtocrux running in this server."""
|
femtocrux/util/utils.py
CHANGED
|
@@ -1,21 +1,12 @@
|
|
|
1
1
|
""" Utils for client-server communication. """
|
|
2
|
-
|
|
2
|
+
import json
|
|
3
3
|
import numpy as np
|
|
4
|
+
import torch
|
|
4
5
|
from typing import Any
|
|
5
6
|
|
|
6
7
|
import femtocrux.grpc.compiler_service_pb2 as cs_pb2
|
|
7
8
|
|
|
8
9
|
|
|
9
|
-
def numpy_to_ndarray(data: np.array) -> cs_pb2.ndarray:
|
|
10
|
-
"""Convert a numpy array to an ndarray message."""
|
|
11
|
-
return cs_pb2.ndarray(data=[float(x) for x in data.ravel()], shape=data.shape)
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
def ndarray_to_numpy(data: cs_pb2.ndarray) -> np.array:
|
|
15
|
-
"""Convert an ndarray message to a numpy array."""
|
|
16
|
-
return np.array(data.data).reshape(data.shape)
|
|
17
|
-
|
|
18
|
-
|
|
19
10
|
def field_or_none(message: Any, field_name: str) -> Any:
|
|
20
11
|
"""Convert empty message fields to None."""
|
|
21
12
|
return getattr(message, field_name) if message.HasField(field_name) else None
|
|
@@ -29,3 +20,72 @@ def get_channel_options(max_message_mb: int = 32):
|
|
|
29
20
|
("grpc.max_send_message_length", max_message_size),
|
|
30
21
|
("grpc.max_receive_message_length", max_message_size),
|
|
31
22
|
]
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
def serialize_numpy_array(arr: np.ndarray) -> cs_pb2.ndarray:
|
|
26
|
+
"""Serializes a NumPy array into a NumpyArrayProto message."""
|
|
27
|
+
return cs_pb2.ndarray(
|
|
28
|
+
data=arr.tobytes(), shape=list(arr.shape), dtype=str(arr.dtype)
|
|
29
|
+
)
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
def serialize_sim_inputs_message(
|
|
33
|
+
data_dict: dict, input_period: float
|
|
34
|
+
) -> cs_pb2.simulation_data:
|
|
35
|
+
"""Serializes a dictionary where values are NumPy arrays."""
|
|
36
|
+
message = cs_pb2.simulation_data()
|
|
37
|
+
for key, array in data_dict.items():
|
|
38
|
+
if isinstance(array, torch.Tensor):
|
|
39
|
+
array_to_serialize = array.numpy()
|
|
40
|
+
elif isinstance(array, np.ndarray):
|
|
41
|
+
array_to_serialize = array
|
|
42
|
+
else:
|
|
43
|
+
raise (Exception("Input array was not of type torch.Tensor or np.ndarray"))
|
|
44
|
+
|
|
45
|
+
if not np.issubdtype(array_to_serialize.dtype, np.integer):
|
|
46
|
+
raise (
|
|
47
|
+
Exception(
|
|
48
|
+
"Input data is not an integer type. Please quantize your"
|
|
49
|
+
"data to int16 or lower."
|
|
50
|
+
)
|
|
51
|
+
)
|
|
52
|
+
|
|
53
|
+
if len(array_to_serialize.shape) > 2:
|
|
54
|
+
raise (
|
|
55
|
+
Exception(
|
|
56
|
+
"Expected 2 dimensions for input and got shape: "
|
|
57
|
+
"{array_to_serialize.shape} Your input array has too many "
|
|
58
|
+
"dimensions. When in inference mode, please remove any batch "
|
|
59
|
+
"dimensions."
|
|
60
|
+
)
|
|
61
|
+
)
|
|
62
|
+
|
|
63
|
+
message.inputs[key].CopyFrom(serialize_numpy_array(array_to_serialize))
|
|
64
|
+
message.input_period = input_period
|
|
65
|
+
return message
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
def serialize_simulation_output(data_dict: dict, report) -> cs_pb2.simulation_output:
|
|
69
|
+
"""Serializes a dictionary where values are NumPy arrays."""
|
|
70
|
+
message = cs_pb2.simulation_output()
|
|
71
|
+
for key, array in data_dict.items():
|
|
72
|
+
message.outputs[key].CopyFrom(serialize_numpy_array(array))
|
|
73
|
+
message.report = json.dumps(report)
|
|
74
|
+
message.status.CopyFrom(cs_pb2.status(success=True))
|
|
75
|
+
|
|
76
|
+
return message
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
def deserialize_numpy_array(proto: cs_pb2.ndarray) -> np.ndarray:
|
|
80
|
+
"""Deserializes a NumpyArrayProto message back into a NumPy array."""
|
|
81
|
+
return np.frombuffer(proto.data, dtype=np.dtype(proto.dtype)).reshape(proto.shape)
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
def deserialize_simulation_data(proto: cs_pb2.simulation_data) -> dict:
|
|
85
|
+
"""Deserializes a LargerMessage back into a dictionary of NumPy arrays."""
|
|
86
|
+
return {key: deserialize_numpy_array(value) for key, value in proto.items()}
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
def deserialize_simulation_output(proto: cs_pb2.simulation_output) -> dict:
|
|
90
|
+
"""Deserializes a LargerMessage back into a dictionary of NumPy arrays."""
|
|
91
|
+
return {key: deserialize_numpy_array(value) for key, value in proto.items()}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
femtocrux/ENV_REQUIREMENTS.sh,sha256=t_O1B4hJAMgxvH9gwp1qls6eVFmhSYBJe64KmuK_H-4,1389
|
|
2
|
+
femtocrux/PY_REQUIREMENTS,sha256=UwXV0o3gieruZUYdN9r2bqQ0Wcf_vosjeP6LJVx6oF0,275
|
|
3
|
+
femtocrux/VERSION,sha256=wo_MpTY3vIjhJK8XJd8Ty5jGne3v1i-zzb4c22t2BiQ,6
|
|
4
|
+
femtocrux/__init__.py,sha256=yIWd9I2PEXCn_PKIILAN3mkWeTf0tgtVualeTIHNxfQ,342
|
|
5
|
+
femtocrux/version.py,sha256=uNg2kHxQo6oUN1ah7s9_85rCZVRoTHGPD1GAQPZW4lw,164
|
|
6
|
+
femtocrux/client/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
7
|
+
femtocrux/client/client.py,sha256=jIkfdIwfTxveC4G-WtQVzumK0m_icdznHFE175P48y0,21601
|
|
8
|
+
femtocrux/grpc/__init__.py,sha256=uiMHQt5I2eAKJqI3Zh0h1Gm7cmPR4PbaGS71nCJQCGw,169
|
|
9
|
+
femtocrux/grpc/compiler_service_pb2.py,sha256=4VbEpkSn7rp1GzBEQq1GPgiuZ8A2CWwgrCupbBvvNmg,5494
|
|
10
|
+
femtocrux/grpc/compiler_service_pb2_grpc.py,sha256=twzBPI2b9BFhkn75WcopLk_Z5D-GwvHheoGOid3gpVA,8501
|
|
11
|
+
femtocrux/server/__init__.py,sha256=kpm2AGLxo3AA9zo-G8N0Dm0gmWbvAd6jfhMbtkeHcxo,77
|
|
12
|
+
femtocrux/server/compiler_frontend.py,sha256=-pfhOHDkXWReoB273Bc9s2s7BVTMZoCSyGM7pudmMhY,5472
|
|
13
|
+
femtocrux/server/exceptions.py,sha256=lI6n471n5QKf5G3aL_1kuBVEItD-jBgithVVpPDwNYc,609
|
|
14
|
+
femtocrux/server/healthcheck.py,sha256=ehqAwnv0D0zpy-AUZAPwv8rp874DZCwUmP8nzdXzZvI,1565
|
|
15
|
+
femtocrux/server/server.py,sha256=sumnTj63uOGkzwj4-xwTuN4wVB7CDopy8e7PLOpW64Q,7405
|
|
16
|
+
femtocrux/util/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
17
|
+
femtocrux/util/utils.py,sha256=HlmlqS-R7d7PaYaYi8sY6U0p9NoeHjQ1ObapBMKhCrU,3356
|
|
18
|
+
femtocrux-2.0.0.dist-info/licenses/LICENSE,sha256=eN9ZI1xHjUmFvN3TEeop5kBGXRUBfbsl55KBNBYYFqI,36
|
|
19
|
+
femtocrux-2.0.0.dist-info/METADATA,sha256=VH_1bBP3ZdU8y-zSAYW85bwR7fZjXrwFm0mjjJuTW9c,2763
|
|
20
|
+
femtocrux-2.0.0.dist-info/WHEEL,sha256=zaaOINJESkSfm_4HQVc5ssNzHCPXhJm0kEUakpsEHaU,91
|
|
21
|
+
femtocrux-2.0.0.dist-info/top_level.txt,sha256=BkTttlioC3je__8577wxRieZqY3Abu7FOOdMnmYbcNI,10
|
|
22
|
+
femtocrux-2.0.0.dist-info/RECORD,,
|
femtocrux/femtostack/__init__.py
DELETED
|
@@ -1,10 +0,0 @@
|
|
|
1
|
-
"""
|
|
2
|
-
Copyright Femtosense 2024
|
|
3
|
-
|
|
4
|
-
By using this software package, you agree to abide by the terms and conditions
|
|
5
|
-
in the license agreement found at https://femtosense.ai/legal/eula/
|
|
6
|
-
"""
|
|
7
|
-
|
|
8
|
-
from .frontend import CompilerFrontend # noqa: F401
|
|
9
|
-
from .metrics import SimMetrics # noqa: F401
|
|
10
|
-
from .sim_io import IOConfig, SimIOWrapper # noqa: F401
|