femtocrux 1.2.1__py3-none-any.whl → 2.0.1__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 CHANGED
@@ -1 +1 @@
1
- 1.2.1
1
+ 2.0.1
@@ -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, Union
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 numpy_to_ndarray, ndarray_to_numpy, get_channel_options
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: Union[np.array, Iterable[np.array]],
218
- quantize_inputs: bool = False,
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
- :type inputs: array or iterable of arrays, required
226
- :param inputs: The input tensors.
227
-
228
- :type quantize_inputs: bool, optional
229
- :param quantize_inputs: If true, quantizes each input from float to integer
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=cs_pb2.simulation_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 [ndarray_to_numpy(x) for x in response.data], response.report
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())
@@ -2,7 +2,7 @@
2
2
  # Generated by the protocol buffer compiler. DO NOT EDIT!
3
3
  # NO CHECKED-IN PROTOBUF GENCODE
4
4
  # source: compiler_service.proto
5
- # Protobuf Python Version: 5.29.0
5
+ # Protobuf Python Version: 6.30.0
6
6
  """Generated protocol buffer code."""
7
7
  from google.protobuf import descriptor as _descriptor
8
8
  from google.protobuf import descriptor_pool as _descriptor_pool
@@ -11,8 +11,8 @@ from google.protobuf import symbol_database as _symbol_database
11
11
  from google.protobuf.internal import builder as _builder
12
12
  _runtime_version.ValidateProtobufRuntimeVersion(
13
13
  _runtime_version.Domain.PUBLIC,
14
- 5,
15
- 29,
14
+ 6,
15
+ 30,
16
16
  0,
17
17
  '',
18
18
  'compiler_service.proto'
@@ -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\"&\n\x07ndarray\x12\x0c\n\x04\x64\x61ta\x18\x01 \x03(\x02\x12\r\n\x05shape\x18\x02 \x03(\x03\"\x14\n\x04\x64\x61ta\x12\x0c\n\x04\x64\x61ta\x18\x01 \x01(\x0c\"\xc1\x01\n\x0fsimulation_data\x12!\n\x04\x64\x61ta\x18\x01 \x03(\x0b\x32\x13.fscompiler.ndarray\x12\x17\n\x0fquantize_inputs\x18\x02 \x01(\x08\x12\x1a\n\x12\x64\x65quantize_outputs\x18\x03 \x01(\x08\x12\x19\n\x0csim_duration\x18\x04 \x01(\x02H\x00\x88\x01\x01\x12\x19\n\x0cinput_period\x18\x05 \x01(\x02H\x01\x88\x01\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\"j\n\x11simulation_output\x12!\n\x04\x64\x61ta\x18\x01 \x03(\x0b\x32\x13.fscompiler.ndarray\x12\x0e\n\x06report\x18\x02 \x01(\t\x12\"\n\x06status\x18\x03 \x01(\x0b\x32\x12.fscompiler.status\"\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')
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=575
48
- _globals['_DATA']._serialized_start=577
49
- _globals['_DATA']._serialized_end=597
50
- _globals['_SIMULATION_DATA']._serialized_start=600
51
- _globals['_SIMULATION_DATA']._serialized_end=793
52
- _globals['_SIMULATION_INPUT']._serialized_start=795
53
- _globals['_SIMULATION_INPUT']._serialized_end=911
54
- _globals['_SIMULATION_OUTPUT']._serialized_start=913
55
- _globals['_SIMULATION_OUTPUT']._serialized_end=1019
56
- _globals['_VERSION_INFO']._serialized_start=1021
57
- _globals['_VERSION_INFO']._serialized_end=1052
58
- _globals['_COMPILE']._serialized_start=1055
59
- _globals['_COMPILE']._serialized_end=1316
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)
@@ -6,7 +6,7 @@ import warnings
6
6
  import compiler_service_pb2 as compiler__service__pb2
7
7
  from google.protobuf import empty_pb2 as google_dot_protobuf_dot_empty__pb2
8
8
 
9
- GRPC_GENERATED_VERSION = '1.71.0'
9
+ GRPC_GENERATED_VERSION = '1.72.1'
10
10
  GRPC_VERSION = grpc.__version__
11
11
  _version_not_supported = False
12
12
 
@@ -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
+ )
@@ -1,28 +1,29 @@
1
1
  import argparse
2
2
  from collections.abc import Iterable
3
3
  import concurrent
4
- import google.protobuf.json_format
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.femtostack.common import CompilerFrontend
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 = google.protobuf.json_format.MessageToDict(options_struct)
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 not data_request.WhichOneof("model_or_data") == "data":
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
- # Convert input data
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
- outputs, metrics = compiler.run_behavioral_simulator(
158
- data,
159
- quantize_inputs=sim_data.quantize_inputs,
160
- dequantize_outputs=sim_data.dequantize_outputs,
161
- input_period=field_or_none(sim_data, "input_period"),
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
- yield cs_pb2.simulation_output(
175
- data=[numpy_to_ndarray(x) for x in outputs],
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()}
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: femtocrux
3
- Version: 1.2.1
3
+ Version: 2.0.1
4
4
  Summary: Femtosense Compiler
5
5
  Home-page: https://github.com/femtosense/femtocrux
6
6
  Author: Femtosense
@@ -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=_oQPUvtVeNP3frSXEV6wLGA2YBw6U-YN95lccna6p3o,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=Wjq_-lMs6nC91HOwDIc8Ts68luvYXnNfqtgRy3CuIAI,5494
10
+ femtocrux/grpc/compiler_service_pb2_grpc.py,sha256=9zj8QkrCUHa7xtvZhwD6dz3PujdO3g3GZnUM2aYLLGU,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.1.dist-info/licenses/LICENSE,sha256=eN9ZI1xHjUmFvN3TEeop5kBGXRUBfbsl55KBNBYYFqI,36
19
+ femtocrux-2.0.1.dist-info/METADATA,sha256=1dynaWUv10G69trHGW4ZswFz-BCqQS9QNckb6OgX2HI,2763
20
+ femtocrux-2.0.1.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
21
+ femtocrux-2.0.1.dist-info/top_level.txt,sha256=BkTttlioC3je__8577wxRieZqY3Abu7FOOdMnmYbcNI,10
22
+ femtocrux-2.0.1.dist-info/RECORD,,
@@ -1,5 +1,5 @@
1
1
  Wheel-Version: 1.0
2
- Generator: setuptools (78.1.0)
2
+ Generator: setuptools (80.9.0)
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any
5
5
 
@@ -1,7 +0,0 @@
1
- def _get_dir():
2
- import pathlib
3
-
4
- return pathlib.Path(__file__).parent.parent.resolve()
5
-
6
-
7
- __version__ = (_get_dir() / "VERSION").read_text(encoding="utf-8").strip()
@@ -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