Skip to content

Commit

Permalink
initial PoC
Browse files Browse the repository at this point in the history
  • Loading branch information
cztomsik committed Apr 20, 2023
1 parent 025a76a commit cdb9f9d
Show file tree
Hide file tree
Showing 11 changed files with 499 additions and 3 deletions.
5 changes: 5 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
zig-cache/
zig-out/
**/*~
**/node_modules
**/.DS_Store
6 changes: 6 additions & 0 deletions .gitmodules
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
[submodule "deps/ggml"]
path = deps/ggml
url = https://github.com/ggerganov/ggml
[submodule "deps/napigen"]
path = deps/napigen
url = https://github.com/cztomsik/zig-napigen
38 changes: 35 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,13 +1,45 @@
# ggml-js
JS bindings for the [ggml](https://github.com/ggerganov/ggml) library.
JavaScript bindings for the [ggml](https://github.com/ggerganov/ggml) library.

## Installation
You can install ggml-js via npm:

```bash
npm install ggml-js
```

## Usage
TODO
Here's an example of how to use ggml-js in your JavaScript code:

```js
import { Context } from 'ggml-js';

// Initialize the context
const ctx = Context.init();

// Create new 1D tensors a and b
const a = ctx.newTensor1D('f32', 1);
const b = ctx.newTensor1D('f32', 1);

// Perform element-wise multiplication of a and b
const ab = a.mul(b);

// Build the forward computation graph
const graph = ab.buildForward();

// Print the graph structure
graph.print();

// Set values for tensors a and b
a.set(0, 1.5);
b.set(0, 2);

// Compute the graph
graph.compute();

// Print the result of the multiplication
console.log(ab.get(0));
```

## License
MIT
This project is licensed under the MIT License.
310 changes: 310 additions & 0 deletions bindings.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,310 @@
import { createRequire } from 'node:module'
const require = createRequire(import.meta.url)
const native = require('./zig-out/lib/ggml.node')

export const GGML_TYPE = Object.fromEntries(
Object.entries(native)
.filter(([k, v]) => k.startsWith('GGML_TYPE_'))
.map(([k, v]) => [k.slice(10).toLowerCase(), v])
)

export class Context {
/**
* Initialize the context.
* @returns {Context}
*/
static init(opts = {}) {
opts = {
mem_size: BigInt(1024),
mem_buffer: null,
no_alloc: false,
...opts,
}

return wrap(native.ggml_init(opts), Context)
}

wrapTensor(obj, type) {
return (obj.context = this), (obj.type = type), wrap(obj, Tensor)
}

wrapGraph(obj) {
return (obj.context = this), wrap(obj, Graph)
}

/**
* Create a new 1D tensor.
* @returns {Tensor}
*/
newTensor1D(type, dim) {
return this.wrapTensor(native.ggml_new_tensor_1d(this, GGML_TYPE[type], big(dim)), type)
}

/**
* Create a new 2D tensor.
* @returns {Tensor}
*/
newTensor2D(type, dim1, dim2) {
return this.wrapTensor(native.ggml_new_tensor_2d(this, GGML_TYPE[type], big(dim1), big(dim2)), type)
}

/**
* Create a new 3D tensor.
* @returns {Tensor}
*/
newTensor3D(type, dim1, dim2, dim3) {
return this.wrapTensor(native.ggml_new_tensor_3d(this, GGML_TYPE[type], big(dim1), big(dim2), big(dim3)), type)
}

/**
* Create a new 4D tensor.
* @returns {Tensor}
*/
newTensor4D(type, dim1, dim2, dim3, dim4) {
return this.wrapTensor(
native.ggml_new_tensor_4d(this, GGML_TYPE[type], big(dim1), big(dim2), big(dim3), big(dim4)),
type
)
}

/**
* Create a new 32-bit integer.
* @returns {Tensor}
*/
newI32(value) {
return this.wrapTensor(native.ggml_new_i32(this, value))
}

/**
* Create a new 32-bit float.
* @returns {Tensor}
*/
newF32(value) {
return this.wrapTensor(native.ggml_new_f32(this, value))
}
}

export class Tensor {
type = null
context = null

wrapNew(obj) {
return this.context.wrapTensor(obj, this.type)
}

/**
* Set all elements to the given value.
* @returns {Tensor}
*/
setAll(value) {
if (value === 0) {
native.ggml_set_zero(this)
} else {
if (this.type === 'i32') {
native.ggml_set_i32(this, value)
} else if (this.type === 'f32') {
native.ggml_set_f32(this, value)
} else {
throw new Error(`TODO`)
}
}
return this
}

/**
* Get value at the given index.
* @returns {number}
*/
get(index) {
if (this.type === 'i32') {
return native.ggml_get_i32_1d(this, index)
} else if (this.type === 'f32') {
return native.ggml_get_f32_1d(this, index)
} else {
throw new Error(`TODO`)
}
}

/**
* Set value at the given index.
* @returns {Tensor}
*/
set(index, value) {
if (this.type === 'i32') {
native.ggml_set_i32_1d(this, index, value)
} else if (this.type === 'f32') {
native.ggml_set_f32_1d(this, index, value)
} else {
throw new Error(`TODO`)
}
return this
}

/**
* Add two tensors.
* @param {Tensor} other
* @returns {Tensor}
*/
add(other) {
return this.wrapNew(native.ggml_add(this.context, this, other))
}

/**
* Subtract two tensors.
* @param {Tensor} other
* @returns {Tensor}
*/
sub(other) {
return this.wrapNew(native.ggml_sub(this.context, this, other))
}

/**
* Multiply two tensors.
* @param {Tensor} other
* @returns {Tensor}
*/
mul(other) {
return this.wrapNew(native.ggml_mul(this.context, this, other))
}

/**
* Divide two tensors.
* @param {Tensor} other
* @returns {Tensor}
*/
div(other) {
return this.wrapNew(native.ggml_div(this.context, this, other))
}

/**
* Compute square of a tensor.
* @returns {Tensor}
*/
sqr() {
return this.wrapNew(native.ggml_sqr(this.context, this))
}

/**
* Compute square root of a tensor.
* @returns {Tensor}
*/
sqrt() {
return this.wrapNew(native.ggml_sqrt(this.context, this))
}

/**
* Compute sum of all elements.
* @returns {Tensor}
*/
sum() {
return this.wrapNew(native.ggml_sum(this.context, this))
}

/**
* Compute mean of all elements.
* @returns {Tensor}
*/
mean() {
return this.wrapNew(native.ggml_mean(this.context, this))
}

/**
* Compute absolute value of a tensor.
* @returns {Tensor}
*/
abs() {
return this.wrapNew(native.ggml_abs(this.context, this))
}

/**
* Compute sign of a tensor.
* @returns {Tensor}
*/
sgn() {
return this.wrapNew(native.ggml_sgn(this.context, this))
}

/**
* Negate a tensor.
* @returns {Tensor}
*/
neg() {
return this.wrapNew(native.ggml_neg(this.context, this))
}

/**
* Compute RELU of a tensor.
* @returns {Tensor}
*/
relu() {
return this.wrapNew(native.ggml_relu(this.context, this))
}

/**
* Compute GELU of a tensor.
* @returns {Tensor}
*/
gelu() {
return this.wrapNew(native.ggml_gelu(this.context, this))
}

/**
* Compute SiLU of a tensor.
* @returns {Tensor}
*/
silu() {
return this.wrapNew(native.ggml_silu(this.context, this))
}

/**
* Normalize a tensor.
* @returns {Tensor}
*/
norm() {
return this.wrapNew(native.ggml_norm(this.context, this))
}

/**
* Compute RMS Norm of a tensor.
* @returns {Tensor}
*/
rmsNorm() {
return this.wrapNew(native.ggml_rms_norm(this.context, this))
}

/**
* Build a graph for forward computation.
* @returns {Graph}
*/
buildForward() {
return this.context.wrapGraph(native.ggml_build_forward(this))
}
}

class Graph {
context = null

/**
* Compute the graph.
*/
compute() {
native.ggml_graph_compute(this.context, this)
}

/**
* Reset the graph.
*/
reset() {
native.ggml_graph_reset(this)
}

/**
* Print info about the graph.
*/
print() {
native.ggml_graph_print(this)
}
}

const wrap = (obj, Clz) => (Object.setPrototypeOf(obj, Clz.prototype), obj)
const big = v => BigInt(v)
Loading

0 comments on commit cdb9f9d

Please sign in to comment.