Commit 60ef579d authored by Cianciosa, Mark's avatar Cianciosa, Mark
Browse files

Add the ability to have multiple input arrays in the mlx models. Enhance...

Add the ability to have multiple input arrays in the mlx models. Enhance fortran and C bindings to allow this.
parent 3f2fa89d
Loading
Loading
Loading
Loading
+161 −32
Original line number Diff line number Diff line
@@ -30,42 +30,105 @@ ml_model mlce_init_custom_pytorch(const char *weights_filename,
}

//------------------------------------------------------------------------------
///  @brief Initalize a mlx model.
///  @brief Create a shapes object.
///
///  @param[in] filename Path to the mlx model.
///  @param[in] in_shape Input dimension shapes.
///  @param[in] shape    Array dimension shapes.
///  @param[in] num_dims Number of dimensions in shape.
///  @returns An input shapes object.
//------------------------------------------------------------------------------
    ml_shapes mlce_init_shapes(const int *shape,
                               const size_t num_dims) {
        ml_embedder::model::shape temp_shape(num_dims);
        std::memcpy(temp_shape.data(), shape, num_dims*sizeof(int));
        return reinterpret_cast<ml_shapes> (new ml_embedder::model::shapes ({temp_shape}));
    }

//------------------------------------------------------------------------------
///  @brief Add a shape to the shapes object.
///
///  @param[in,out] s        Shape objects.
///  @param[in]     shape    Input dimension shapes.
///  @param[in]     num_dims Number of input dimensions.
//------------------------------------------------------------------------------
ml_model mlce_init_mlx(const char *filename,
                       const int *in_shape,
    void mlce_add_shape(ml_shapes s,
                        const int *shape,
                        const size_t num_dims) {
    mlx::core::SmallVector<int> shape(num_dims);
    std::memcpy(shape.data(), in_shape, num_dims*sizeof(int));
        ml_embedder::model::shapes *temp_shapes =
            reinterpret_cast<ml_embedder::model::shapes *> (s);
        ml_embedder::model::shape temp(num_dims);
        std::memcpy(temp.data(), shape, num_dims*sizeof(int));
        temp_shapes->push_back(temp);
    }

//------------------------------------------------------------------------------
///  @brief Finalize a shapes object.
///
///  @note Only finalize a shape object constructed from @ref mlce_init_shape.
///
///  @param[in,out] is Input shapes object.
//------------------------------------------------------------------------------
    void nlce_finalize_shapes(ml_shapes s) {
        delete reinterpret_cast<ml_embedder::model::shapes *> (s);
    }

//------------------------------------------------------------------------------
///  @brief Initialize a mlx model.
///
///  @param[in] filename Path to the mlx model.
///  @param[in] is       Input shapes object.
//------------------------------------------------------------------------------
ml_model mlce_init_mlx(const char *filename,
                       const ml_shapes s) {
    ml_embedder::model::shapes *input_shapes =
        reinterpret_cast<ml_embedder::model::shapes *> (s);

    return reinterpret_cast<ml_model> (new ml_embedder::mlx_model(std::string(filename),
                                                                  shape));
                                                                  *input_shapes));
}

//------------------------------------------------------------------------------
///  @brief Finalize a model.
///
///  @param[in] m The model model instance.
///  @param[in,out] m The model model instance.
//------------------------------------------------------------------------------
void mlce_finalize(ml_model m) {
    delete reinterpret_cast<ml_embedder::model *> (m);
}

//------------------------------------------------------------------------------
///  @brief Get the number of inputs.
///
///  @param[in] m The model model instance.
///  @returns The number of input dimensions.
//------------------------------------------------------------------------------
size_t mlce_num_inputs(ml_model m) {
    ml_embedder::model *model = reinterpret_cast<ml_embedder::model *> (m);
    return model->input_shapes.size();
}

//------------------------------------------------------------------------------
///  @brief Get a the input shape.
///
///  @param[in]  m     The model model instance.
///  @param[in]  index Index to get the shape for.
///  @param[out] s     The number of elements in the shape.
///  @returns The input shape array.
//------------------------------------------------------------------------------
int *mlce_in_shape(ml_model m, size_t *s) {
int *mlce_in_shape(ml_model m, const size_t index, size_t *s) {
    ml_embedder::model *model = reinterpret_cast<ml_embedder::model *> (m);
    s[0] = model->input_shapes[index].size();
    return reinterpret_cast<int *> (model->input_shapes[index].data());
}

//------------------------------------------------------------------------------
///  @brief Get the number of outputs.
///
///  @param[in] m The model model instance.
///  @returns The number of input dimensions.
//------------------------------------------------------------------------------
size_t mlce_num_outputs(ml_model m) {
    ml_embedder::model *model = reinterpret_cast<ml_embedder::model *> (m);
    s[0] = model->input_shape.size();
    return reinterpret_cast<int *> (model->input_shape.data());
    return model->output_shapes.size();
}

//------------------------------------------------------------------------------
@@ -75,34 +138,100 @@ int *mlce_in_shape(ml_model m, size_t *s) {
///  @param[out] s The number of elements in the shape.
///  @returns The output shape array.
//------------------------------------------------------------------------------
int *mlce_out_shape(ml_model m, size_t *s) {
int *mlce_out_shape(ml_model m, const size_t index, size_t *s) {
    ml_embedder::model *model = reinterpret_cast<ml_embedder::model *> (m);
    s[0] = model->input_shape.size();
    return reinterpret_cast<int *> (model->output_shape.data());
    s[0] = model->output_shapes[index].size();
    return reinterpret_cast<int *> (model->output_shapes[index].data());
}

//------------------------------------------------------------------------------
///  @brief Initialize an arrays object.
///
///  @param[in] buffer     The array buffer.
///  @param[in] shape      The shape of the batch.
///  @param[in] num_dims   Number of input dimensions.
///  @param[in] batch_size The size of a batch.
///  @returns An arrays object.
//------------------------------------------------------------------------------
ml_arrays mlce_init_arrays_float(void *buffer, const int *shape,
                                 const size_t num_dims,
                                 const int batch_size) {
    ml_embedder::model::shape temp_shape;
    temp_shape.push_back(batch_size);
    for (size_t i = 0; i < num_dims; i++) {
        temp_shape.push_back(shape[i]);
    }

    ml_embedder::model::arrays *arrays = new ml_embedder::model::arrays();
    arrays->emplace_back((float *)buffer, temp_shape, mlx::core::float32);
    return reinterpret_cast<ml_arrays> (arrays);
}

//------------------------------------------------------------------------------
///  @brief Add an array object.
///
///  @param[in,out] a          Arrays object.
///  @param[in]     buffer     The array buffer.
///  @param[in]     shape      The shape of the batch.
///  @param[in]     num_dims   Number of input dimensions.
///  @param[in]     batch_size The size of a batch.
//------------------------------------------------------------------------------
void mlce_add_array_float(ml_arrays a,
                          void *buffer, const int *shape,
                          const size_t num_dims,
                          const int batch_size) {
    ml_embedder::model::shape temp_shape;
    temp_shape.push_back(batch_size);
    for (size_t i = 0; i < num_dims; i++) {
        temp_shape.push_back(shape[i]);
    }

    ml_embedder::model::arrays *arrays = reinterpret_cast<ml_embedder::model::arrays *> (a);
    arrays->emplace_back((float *)buffer, temp_shape, mlx::core::float32);
}

//------------------------------------------------------------------------------
///  @brief Get data for index.
///
///  FIXME: Data layout may use strides. This should be accounted for here.
///
///  @param[in,out] a     Arrays object.
///  @param[in]     index Array index.
///  @returns A pointer to the data.
//------------------------------------------------------------------------------
float *mlce_get_array_data_float(ml_arrays a, const size_t index) {
    ml_embedder::model::arrays *arrays = reinterpret_cast<ml_embedder::model::arrays *> (a);
    return arrays->operator[](index).data<float> ();
}

//------------------------------------------------------------------------------
///  @brief Finalize a input shape.
///
///  @note Only finalize a shape object constructed from @ref mlce_init_shape.
///
///  @param[in,out] a Arrays object.
//------------------------------------------------------------------------------
void mlce_finalize_arrays(ml_arrays a) {
    delete reinterpret_cast<ml_embedder::model::arrays *> (a);
}

//------------------------------------------------------------------------------
///  @brief Run a model.
///
///  @note The user is require to allocate an output buffer of the correct size.
///  FIXME: Data layout may use strides. This should be accounted for here.
///
///  @param[in] m   The model model instance.
///  @param[in] in         The input buffer.
///  @param[in] out        The output buffer.
///  @param[in] batch_size The size of a batch.
///  @param[in] input_size The size of a batch.
///  @param[in] in  The input buffers.
///  @returns The output buffers.
//------------------------------------------------------------------------------
void mlce_run_float(ml_model m, void *in, void *out, const int batch_size) {
ml_arrays mlce_run(ml_model m, ml_arrays in) {
    ml_embedder::model *model = reinterpret_cast<ml_embedder::model *> (m);
    ml_embedder::model::arrays *inputs = reinterpret_cast<ml_embedder::model::arrays *> (in);

    mlx::core::SmallVector<int> shape;
    shape.push_back(batch_size);
    for (auto i : model->input_shape) {
        shape.push_back(i);
    ml_embedder::model::arrays outputs = model->func(*inputs);
    for (mlx::core::array &out : outputs) {
        out.eval();
    }

    mlx::core::array input = mlx::core::array((float *)in, shape, mlx::core::float32);
    mlx::core::array y = model->func({input})[0];
    y.eval();
    std::memcpy(out, y.data<float> (), y.nbytes());
    return reinterpret_cast<ml_arrays> (new ml_embedder::model::arrays(std::move(outputs)));
}
+105 −16
Original line number Diff line number Diff line
@@ -16,6 +16,10 @@ extern "C" {

///  ml_model type for C interface.
    typedef void *ml_model;
///  ml_model shape objects.
    typedef void *ml_shapes;
///  ml_model array objects.
    typedef void *ml_arrays;

//------------------------------------------------------------------------------
///  @brief Initialize a keras model.
@@ -34,53 +38,138 @@ extern "C" {
                                      const char *config_filename);

//------------------------------------------------------------------------------
///  @brief Initalize a mlx model.
///  @brief Create a shapes object.
///
///  @param[in] filename Path to the mlx model.
///  @param[in] in_shape Input dimension shapes.
///  @param[in] shape    Array dimension shapes.
///  @param[in] num_dims Number of dimensions in shape.
//------------------------------------------------------------------------------
    ml_shapes mlce_init_shapes(const int *shape,
                               const size_t num_dims);

//------------------------------------------------------------------------------
///  @brief Add a shape to the shapes object.
///
///  @param[in,out] s        Shape object.
///  @param[in]     shape    Input dimension shapes.
///  @param[in]     num_dims Number of input dimensions.
//------------------------------------------------------------------------------
    ml_model mlce_init_mlx(const char *filename,
                           const int *in_shape,
    void mlce_add_shape(ml_shapes s,
                        const int *shape,
                        const size_t num_dims);

//------------------------------------------------------------------------------
///  @brief Finalize a shapes object.
///
///  @note Only finalize a shape object constructed from @ref mlce_init_shape.
///
///  @param[in,out] s Input shapes object.
//------------------------------------------------------------------------------
    void nlce_finalize_shapes(ml_shapes s);

//------------------------------------------------------------------------------
///  @brief Initialize a mlx model.
///
///  @param[in] filename Path to the mlx model.
///  @param[in] s        Input dimension shapes.
//------------------------------------------------------------------------------
    ml_model mlce_init_mlx(const char *filename,
                           const ml_shapes s);

//------------------------------------------------------------------------------
///  @brief Finalize a model.
///
///  @param[in] m The model model instance.
///  @param[in,out] m The model model instance.
//------------------------------------------------------------------------------
    void mlce_finalize(ml_model m);

//------------------------------------------------------------------------------
///  @brief Get the number of inputs.
///
///  @param[in] m The model model instance.
///  @returns The number of input dimensions.
//------------------------------------------------------------------------------
    size_t mlce_num_inputs(ml_model m);

//------------------------------------------------------------------------------
///  @brief Get a the input shape.
///
///  @param[in]  m     The model model instance.
///  @param[in]  index Index to get the shape for.
///  @param[out] s     The number of elements in the shape.
///  @returns The input shape array.
//------------------------------------------------------------------------------
    int *mlce_in_shape(ml_model m, size_t *s);
    int *mlce_in_shape(ml_model m, const size_t index, size_t *s);

//------------------------------------------------------------------------------
///  @brief Get the number of outputs.
///
///  @param[in] m The model model instance.
///  @returns The number of input dimensions.
//------------------------------------------------------------------------------
    size_t mlce_num_outputs(ml_model m);

//------------------------------------------------------------------------------
///  @brief Get a the output shape.
///
///  @param[in]  m The model model instance.
///  @param[in]  index Index to get the shape for.
///  @param[out] s The number of elements in the shape.
///  @returns The output shape array.
//------------------------------------------------------------------------------
    int *mlce_out_shape(ml_model m, size_t *s);
    int *mlce_out_shape(ml_model m, const size_t index, size_t *s);

//------------------------------------------------------------------------------
///  @brief Run a model with float inputs.
///  @brief Initialize an arrays object.
///
///  @note The user is required to allocate an output buffer of the correct
///        size.
///  @param[in] buffer     The array buffer.
///  @param[in] shape      The shape of the batch.
///  @param[in] num_dims   Number of input dimensions.
///  @param[in] batch_size The size of a batch.
//------------------------------------------------------------------------------
    ml_arrays mlce_init_arrays_float(void *buffer, const int *shape,
                                     const size_t num_dims,
                                     const int batch_size);

//------------------------------------------------------------------------------
///  @brief Add an array object.
///
///  @param[in,out] a          Arrays object.
///  @param[in]     buffer     The array buffer.
///  @param[in]     shape      The shape of the batch.
///  @param[in]     num_dims   Number of input dimensions.
///  @param[in]     batch_size The size of a batch.
//------------------------------------------------------------------------------
    void mlce_add_array_float(ml_arrays a,
                              void *buffer, const int *shape,
                              const size_t num_dims,
                              const int batch_size);

//------------------------------------------------------------------------------
///  @brief Get data for index.
///
///  @param[in,out] a     Arrays object.
///  @param[in]     index Array index.
///  @returns A pointer to the data.
//------------------------------------------------------------------------------
    float *mlce_get_array_data_float(ml_arrays a, const size_t index);

//------------------------------------------------------------------------------
///  @brief Finalize a input shape.
///
///  @note Only finalize a shape object constructed from @ref mlce_init_shape.
///
///  @param[in,out] a Arrays object.
//------------------------------------------------------------------------------
    void mlce_finalize_arrays(ml_arrays a);

//------------------------------------------------------------------------------
///  @brief Run a model with inputs.
///
///  @param[in] m          The model model instance.
///  @param[in] in         The input buffer.
///  @param[in] out        The output buffer.
///  @param[in] batch_size The size of a batch.
///  @returns The output buffers.
//------------------------------------------------------------------------------
    void mlce_run_float(ml_model m, void *in, void *out, const int batch_size);
    ml_arrays mlce_run(ml_model m, ml_arrays in);

#ifdef __cplusplus
}
+350 −47

File changed.

Preview size limit exceeded, changes collapsed.

+71.6 KiB (478 KiB)

File changed.

No diff preview for this file type.

+46 −21
Original line number Diff line number Diff line
@@ -21,6 +21,10 @@ namespace ml_embedder {
///  @brief A generic machine learning model.
//------------------------------------------------------------------------------
    class model {
    public:
///  Type alias for arrays.
        typedef std::vector<mlx::core::array> arrays;

    protected:
///  Type alias for model inputs.
        typedef const std::vector<mlx::core::array> &inputs;
@@ -28,10 +32,15 @@ namespace ml_embedder {
        typedef std::vector<mlx::core::array> outputs;

    public:
///  Type alias for shape.
        typedef mlx::core::SmallVector<int> shape;
///  Type alias for shapes.
        typedef std::vector<shape> shapes;

///  Input dimensions;
        mlx::core::SmallVector<int> input_shape;
        shapes input_shapes;
///  Output dimensions;
        mlx::core::SmallVector<int> output_shape;
        shapes output_shapes;
///  Model type
        mlx::core::Dtype type;

@@ -149,7 +158,8 @@ namespace ml_embedder {

//  FIXME: This is hard coded but it should be read from the json file.
            int last_input = 6;
            input_shape.push_back(last_input);
            input_shapes.push_back({});
            input_shapes[0].push_back(last_input);

            for (auto i : config["layers"]) {
                if (i["class_name"] == "Dense") {
@@ -164,8 +174,8 @@ namespace ml_embedder {
                }
            }

            mlx::core::array y = func({mlx::core::zeros(input_shape)})[0];
            output_shape = y.shape();
            mlx::core::array y = func({mlx::core::zeros(input_shapes[0])})[0];
            output_shapes.push_back(y.shape());
            type = y.dtype();
        }

@@ -263,6 +273,8 @@ namespace ml_embedder {
            const nlohmann::json config =
                nlohmann::json::parse(zip.get_file("config.json").get_buffer());

            input_shapes.push_back({});

            if (config["class_name"] == "Sequential") {
                zip::file::buffer weights_file =
                    zip.get_file("model.weights.h5").get_buffer();
@@ -279,7 +291,7 @@ namespace ml_embedder {
                    if (i["class_name"] == "InputLayer") {
                        for (auto j : i["config"]["batch_shape"]) {
                            if (!j.is_null()) {
                                input_shape.push_back(j);
                                input_shapes[0].push_back(j);
                            }
                        }
                    } else if (i["class_name"] == "Dense") {
@@ -299,8 +311,8 @@ namespace ml_embedder {
                exit(0);
            }

            mlx::core::array y = func({mlx::core::zeros(input_shape)})[0];
            output_shape = y.shape();
            mlx::core::array y = func({mlx::core::zeros(input_shapes[0])})[0];
            output_shapes.push_back(y.shape());
            type = y.dtype();
        }

@@ -326,15 +338,20 @@ namespace ml_embedder {
///  @brief Construct a model mlx function.
///
///  @param[in] model_file Path to the keras model.
///  @param[in] in_shape   Dimension of the inputs not including the batch size.
///  @param[in] in_shapes  Dimension of the inputs not including the batch size.
//------------------------------------------------------------------------------
        mlx_model(const std::string model_file,
                  const mlx::core::SmallVector<int> &in_shape) {
            mlx::core::SmallVector<int> temp(in_shape.size() + 1);
                  const shapes &in_shapes) {

            shapes temp_shapes;
            for (const shape &s: in_shapes) {
                shape temp(s.size() + 1);
                temp[0] = 1;
            input_shape = in_shape;
            for (size_t i = 0, ie = in_shape.size(); i < ie; i++) {
                temp[i + 1] = in_shape[0];
                for (size_t i = 0, ie = s.size(); i < ie; i++) {
                    temp[i + 1] = s[i];
                }
                temp_shapes.push_back(temp);
                input_shapes.push_back(s);
            }

            mlx::core::ImportedFunction import_f = mlx::core::import_function(model_file);
@@ -343,13 +360,21 @@ namespace ml_embedder {
                return import_f(in);
            });

            mlx::core::array y = func({mlx::core::zeros(temp)})[0];
            temp = y.shape();
            for (size_t i = 1, ie = temp.size(); i < ie; i++) {
                output_shape.push_back(temp[i]);
            arrays temp_inputs;
            for (const shape &s: temp_shapes) {
                temp_inputs.push_back(mlx::core::zeros(s));
            }

            type = y.dtype();
            outputs temp_outputs = func(temp_inputs);
            for (size_t i = 0, ie = temp_outputs.size(); i < ie; i++) {
                const shape temp = temp_outputs[i].shape();
                output_shapes.push_back({});
                for (size_t j = 1, je = temp.size(); j < je; j++) {
                    output_shapes[i].push_back(temp[j]);
                }
            }

            type = temp_outputs[0].dtype();
        }
    };
}
Loading