Пример #1
0
 def get_output_datatype(self):
     """Returns FINN DataType of output."""
     # we need to set output datatype to the next larger int or uint
     # enhancement: consider specifying w/ explicit outputDataType attribute
     # to allow overflow and use the same idt if user wants
     idt = DataType[self.get_nodeattr("inputDataType")]
     if idt.signed():
         return DataType.get_smallest_possible(2 * idt.min())
     else:
         return DataType.get_smallest_possible(2 * idt.max())
Пример #2
0
    def generate_params(self, model, path):
        code_gen_dir = path
        # save thresholds in thresh.h
        thresholds = model.get_initializer(self.onnx_node.input[1])

        threshold_tensor = self.get_hls_compatible_threshold_tensor(thresholds)

        min_threshold = thresholds.min()
        max_threshold = thresholds.max()
        min_input = self.get_input_datatype().min()
        max_input = self.get_input_datatype().max()
        # get range required by threshold values
        tdt_min = min(min_input, min_threshold)
        tdt_max = max(max_input, max_threshold)
        if tdt_min < 0:
            if abs(tdt_min) > tdt_max:
                tdt = DataType.get_smallest_possible(tdt_min)
            else:
                tdt = DataType.get_smallest_possible(0 - tdt_max - 1)
        else:
            tdt = DataType.get_smallest_possible(tdt_max)
        assert np.vectorize(tdt.allowed)(
            threshold_tensor
        ).all(), "Thresholds can't be expressed with type %s" % str(tdt)

        thresholds_hls_code = numpy_to_hls_code(
            threshold_tensor, tdt, "thresholds", False, True
        )
        # write thresholds into thresh.h
        f_thresh = open("{}/thresh.h".format(code_gen_dir), "w")
        tdt_hls = tdt.get_hls_datatype_str()
        # use binary to export bipolar activations
        export_odt = self.get_output_datatype()
        if self.get_output_datatype() == DataType.BIPOLAR:
            export_odt = DataType.BINARY
        odt_hls = export_odt.get_hls_datatype_str()
        f_thresh.write(
            "static ThresholdsActivation<{},{},{},{},{},{},{}> threshs \
            = ".format(
                self.calc_tmem(),
                self.get_nodeattr("PE"),
                threshold_tensor.shape[-1],
                tdt_hls,
                odt_hls,
                self.get_nodeattr("ActVal"),
                "std::less_equal<%s>" % tdt_hls,
            )
        )
        f_thresh.write(thresholds_hls_code)
        f_thresh.close()
def make_labelselect_modelwrapper(labels, pe, k, idt):
    inp = helper.make_tensor_value_info("inp", TensorProto.FLOAT, [1, labels])
    outp = helper.make_tensor_value_info("outp", TensorProto.FLOAT, [1, k])

    labelselect_node = helper.make_node(
        "LabelSelect_Batch",
        ["inp"],
        ["outp"],
        domain="finn",
        backend="fpgadataflow",
        Labels=labels,
        PE=pe,
        K=k,
        inputDataType=idt.name,
    )
    graph = helper.make_graph(
        nodes=[labelselect_node],
        name="graph",
        inputs=[inp],
        outputs=[outp],
    )

    model = helper.make_model(graph, producer_name="thresholding-model")
    model = ModelWrapper(model)

    model.set_tensor_datatype("inp", idt)
    odt = DataType.get_smallest_possible(labels - 1)
    model.set_tensor_datatype("outp", odt)

    return model
Пример #4
0
def get_smallest_possible(vals):
    """Returns smallest (fewest bits) possible DataType that can represent
    value. Prefers unsigned integers where possible."""
    vals = np.array(vals)
    for v in vals:
        assert int(v) == v, "Error float value"

    for k in DataType.get_accumulator_dt_cands():
        dt = DataType[k]

        if dt in [DataType["BIPOLAR"], DataType["TERNARY"], DataType["FLOAT32"]]:
            # not currently supported
            continue

        if (dt.min() <= vals).all() and (vals <= dt.max()).all():
            return dt

    warnings.warn(
        """InferChannelwiseLinearLayer: Output values may not be
    representable with supported data types.
    Setting maximum width data type available.
    This will lead to errors if there are no constrains on the input
    """
    )

    if (0 <= vals).all():
        return DataType["UINT64"]
    else:
        return DataType["INT64"]
Пример #5
0
 def apply(self, model):
     graph = model.graph
     node_ind = 0
     graph_modified = False
     for n in graph.node:
         # search for (MultiThreshold, Add) pair
         node_ind += 1
         if (
             n.op_type == "MultiThreshold"
             and not model.is_fork_node(n)
             and not model.is_join_node(n)
         ):
             consumer = model.find_consumer(n.output[0])
             if consumer is not None and consumer.op_type == "Add":
                 mt_node = n
                 add_node = consumer
                 threshold_name = mt_node.input[1]
                 add_weight_name = add_node.input[1]
                 T = model.get_initializer(threshold_name)
                 A = model.get_initializer(add_weight_name)
                 if (A is None) or (T is None):
                     warnings.warn("Threshold or add bias not constant, skipping")
                     continue
                 end_name = add_node.output[0]
                 # we can only absorb scalar adds
                 is_scalar = A.ndim == 0 or all(x == 1 for x in A.shape)
                 if not is_scalar:
                     continue
                 bias = A.flatten()[0]
                 # set MultiThreshold bias property
                 mt_inst = getCustomOp(mt_node)
                 bias += mt_inst.get_nodeattr("out_bias")
                 mt_inst.set_nodeattr("out_bias", bias)
                 graph_modified = True
                 # compute new DataType for MultiThreshold output
                 steps = T.shape[-1]
                 new_min = bias
                 new_max = steps + bias
                 odt = DataType.get_smallest_possible(steps).name.replace(
                     "UINT", "INT"
                 )
                 odt = DataType[odt]
                 assert odt.allowed(new_max) and odt.allowed(
                     new_min
                 ), """Could
                 not compute new MultiThreshold DataType (min = %d max = %d)""" % (
                     new_min,
                     new_max,
                 )
                 mt_inst.set_nodeattr("out_dtype", odt.name)
                 # remove Add node, rewire MultiThreshold
                 graph.node.remove(add_node)
                 mt_node.output[0] = end_name
                 # set datatype
                 model.set_tensor_datatype(end_name, odt)
     if graph_modified:
         model = model.transform(InferDataTypes())
     return (model, graph_modified)
Пример #6
0
 def get_output_datatype(self):
     """Returns FINN DataType of output."""
     # determine data type from image size and input type
     idt = DataType[self.get_nodeattr("inputDataType")]
     vecs = list(self.get_nodeattr("numInputVectors"))
     npixels = vecs[-1] * vecs[-2]
     if idt.signed():
         extreme_value = npixels * idt.min()
     else:
         extreme_value = npixels * idt.max()
     return DataType.get_smallest_possible(extreme_value)
    def get_smallest_possible(self, vals):
        """Returns smallest (fewest bits) possible DataType that can represent
        value. Prefers unsigned integers where possible."""
        vals = np.array(vals)
        for v in vals:
            assert int(v) == v, "Error float value"

        cands = DataType.get_accumulator_dt_cands()
        for k in cands:
            dt = DataType[k]
            if (dt.min() <= vals).all() and (vals <= dt.max()).all():
                return dt
Пример #8
0
 def minimize_accumulator_width(self, model):
     "Minimize threshold width ('accumulator width' here due to convention)"
     thresholds = model.get_initializer(self.onnx_node.input[1])
     threshold_tensor = self.get_hls_compatible_threshold_tensor(thresholds)
     min_threshold = thresholds.min()
     max_threshold = thresholds.max()
     min_input = self.get_input_datatype().min()
     max_input = self.get_input_datatype().max()
     # get range required by threshold values
     tdt_min = min(min_input, min_threshold)
     tdt_max = max(max_input, max_threshold)
     if tdt_min < 0:
         if abs(tdt_min) > tdt_max:
             tdt = DataType.get_smallest_possible(tdt_min)
         else:
             tdt = DataType.get_smallest_possible(0 - tdt_max - 1)
     else:
         tdt = DataType.get_smallest_possible(tdt_max)
     assert np.vectorize(tdt.allowed)(threshold_tensor).all(
     ), "Thresholds can't be expressed with type %s" % str(tdt)
     self.set_nodeattr("weightDataType", tdt.name)
     return DataType[self.get_nodeattr("weightDataType")]
Пример #9
0
 def __init__(self, onnx_node):
     super().__init__(onnx_node)
     odt_name = self.get_nodeattr("outputDataType")
     if odt_name == "":
         # If not provided compute min size
         labels = self.get_nodeattr("Labels")
         odt = DataType.get_smallest_possible(labels - 1)
         # ensure a datatype divisible by 8-bits in case this is the last node
         bw = roundup_to_integer_multiple(odt.bitwidth(), 8)
         new_odt_name = odt.name.replace(str(odt.bitwidth()), str(bw))
         odt = DataType[new_odt_name]
         odt_name = odt.name
         self.set_nodeattr("outputDataType", odt_name)
Пример #10
0
 def minimize_accumulator_width(self, model):
     weights = model.get_initializer(self.onnx_node.input[1])
     if len(self.onnx_node.input) > 2:
         thresholds = model.get_initializer(self.onnx_node.input[2])
     else:
         thresholds = None
     idt = self.get_input_datatype()
     # calculate minimum and maximum values of accumulator
     (acc_min, acc_max) = calculate_matvec_accumulator_range(weights, idt)
     if thresholds is not None:
         threshold_tensor = self.get_hls_compatible_threshold_tensor(thresholds)
         # set threshold datatype (and accumulator datatype implicitly)
         min_threshold = thresholds.min()
         max_threshold = thresholds.max()
         # get range required by threshold values
         tdt_min = min(acc_min, min_threshold)
         tdt_max = max(acc_max, max_threshold)
         if tdt_min < 0:
             if abs(tdt_min) > tdt_max:
                 tdt = DataType.get_smallest_possible(tdt_min)
             else:
                 tdt = DataType.get_smallest_possible(0 - tdt_max)
         else:
             tdt = DataType.get_smallest_possible(tdt_max)
         assert np.vectorize(tdt.allowed)(
             threshold_tensor
         ).all(), "Thresholds can't be expressed with type %s" % str(tdt)
         self.set_nodeattr("accDataType", tdt.name)
     else:
         if acc_min < 0:
             if abs(acc_min) > acc_max:
                 adt = DataType.get_smallest_possible(acc_min)
             else:
                 adt = DataType.get_smallest_possible(0 - acc_max)
         else:
             adt = DataType.get_smallest_possible(acc_max)
         # ensure a datatype divisible by 8-bits in case this is the last node
         bw = roundup_to_integer_multiple(adt.bitwidth(), 8)
         new_adt_name = adt.name.replace(str(adt.bitwidth()), str(bw))
         adt = DataType[new_adt_name]
         self.set_nodeattr("accDataType", adt.name)
         # for no-activation nodes, output dt = acc dt
         self.set_nodeattr("outputDataType", adt.name)
     return DataType[self.get_nodeattr("accDataType")]
Пример #11
0
def test_smallest_possible():
    assert DataType.get_smallest_possible(1) == DataType["BINARY"]
    assert DataType.get_smallest_possible(1.1) == DataType["FLOAT32"]
    assert DataType.get_smallest_possible(-1) == DataType["BIPOLAR"]
    assert DataType.get_smallest_possible(-3) == DataType["INT3"]
    assert DataType.get_smallest_possible(-3.2) == DataType["FLOAT32"]
 def minimize_accumulator_width(self, model):
     weights = model.get_initializer(self.onnx_node.input[1])
     k_h, k_w = self.get_nodeattr("Kernel")
     fm = self.get_nodeattr("Channels")
     # put weights into the shape expected by calculate_matvec_accumulator_range
     weights = weights.reshape(fm, k_h * k_w).transpose()
     if len(self.onnx_node.input) > 2:
         thresholds = model.get_initializer(self.onnx_node.input[2])
     else:
         thresholds = None
     idt = self.get_input_datatype()
     # calculate minimum and maximum values of accumulator
     (acc_min, acc_max) = calculate_matvec_accumulator_range(weights, idt)
     if thresholds is not None:
         threshold_tensor = self.get_hls_compatible_threshold_tensor(thresholds)
         # set threshold datatype (and accumulator datatype implicitly)
         min_threshold = thresholds.min()
         max_threshold = thresholds.max()
         # clip threshold values
         clip_upper = None
         clip_lower = None
         if max_threshold > acc_max + 1:
             clip_upper = acc_max + 1
         if min_threshold < acc_min:
             clip_lower = acc_min
         if (clip_lower is not None) or (clip_upper is not None):
             warnings.warn("Clipping some thresholds in %s" % self.onnx_node.name)
             thresholds = np.clip(thresholds, clip_lower, clip_upper)
             model.set_initializer(self.onnx_node.input[2], thresholds)
             threshold_tensor = self.get_hls_compatible_threshold_tensor(thresholds)
             min_threshold = thresholds.min()
             max_threshold = thresholds.max()
         # get range required by threshold values
         tdt_min = min(acc_min, min_threshold)
         tdt_max = max(acc_max, max_threshold)
         if tdt_min < 0:
             if abs(tdt_min) > tdt_max:
                 tdt = DataType.get_smallest_possible(tdt_min)
             else:
                 tdt = DataType.get_smallest_possible(0 - tdt_max)
         else:
             tdt = DataType.get_smallest_possible(tdt_max)
         assert np.vectorize(tdt.allowed)(
             threshold_tensor
         ).all(), "Thresholds in %s can't be expressed with type %s" % (
             self.onnx_node.name,
             str(tdt),
         )
         self.set_nodeattr("accDataType", tdt.name)
     else:
         if acc_min < 0:
             if abs(acc_min) > acc_max:
                 adt = DataType.get_smallest_possible(acc_min)
             else:
                 adt = DataType.get_smallest_possible(0 - acc_max)
         else:
             adt = DataType.get_smallest_possible(acc_max)
         # ensure a datatype divisible by 8-bits in case this is the last node
         bw = roundup_to_integer_multiple(adt.bitwidth(), 8)
         new_adt_name = adt.name.replace(str(adt.bitwidth()), str(bw))
         adt = DataType[new_adt_name]
         self.set_nodeattr("accDataType", adt.name)
         # for no-activation nodes, output dt = acc dt
         self.set_nodeattr("outputDataType", adt.name)
     return DataType[self.get_nodeattr("accDataType")]
Пример #13
0
def test_smallest_possible():
    assert DataType.get_smallest_possible(1) == DataType.BINARY
    assert DataType.get_smallest_possible(1.1) == DataType.FLOAT32
    assert DataType.get_smallest_possible(-1) == DataType.BIPOLAR
    assert DataType.get_smallest_possible(-3) == DataType.INT3
    assert DataType.get_smallest_possible(-3.2) == DataType.FLOAT32