Skip to content

Commit

Permalink
Merge branch 'main' of github.com:vlang/vsl into feature/new-lapack
Browse files Browse the repository at this point in the history
* 'main' of github.com:vlang/vsl:
  feat: Improve machine learning models and data struct in vsl.ml
  feat: Add machine learning models and data struct to vsl.ml
  noise: add simplex noise (#207)
  poly: edit multiply and add divide functions (#215)
  • Loading branch information
ulises-jeremias committed Aug 5, 2024
2 parents d9fd254 + 665fa11 commit 79d8a8d
Show file tree
Hide file tree
Showing 15 changed files with 908 additions and 187 deletions.
16 changes: 16 additions & 0 deletions examples/noise_fractal_2d/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
# Example - noise_fractal_2d 📘

This example demonstrates the usage of the V Scientific Library for generating pink noise.

## Instructions

1. Ensure you have the V compiler installed. You can download it from [here](https://vlang.io).
2. Ensure you have the VSL installed. You can do it following the [installation guide](https://github.com/vlang/vsl?tab=readme-ov-file#-installation)!
3. Navigate to this directory.
4. Run the example using the following command:

```sh
v run main.v
```

Enjoy exploring the capabilities of the V Scientific Library! 😊
54 changes: 54 additions & 0 deletions examples/noise_fractal_2d/main.v
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
module main

import rand
import vsl.noise
import vsl.plot

fn main() {
// Creation of the noise generator.
// Other noise functions and dimensions count are available.
rand.seed([u32(3155200429), u32(3208395956)])
mut generator := noise.Generator.new()
generator.randomize()

mut x := []f64{}
mut y := []f64{}
mut z := []f64{}

// 5 layers of simplex noise
octaves := 5
persistence := 0.5

for xx in 0 .. 200 {
for yy in 0 .. 200 {
mut total := 0.0
mut frequency := 1.0
mut amplitude := 1.0
mut max_value := 0.0

for _ in 0 .. octaves {
total += generator.simplex_2d(0.03 * xx * frequency, 0.03 * yy * frequency) * amplitude
max_value += amplitude
amplitude *= persistence
frequency *= 2.0
}

// Normalize to [-1, 1]
total /= max_value
x << xx
y << yy
z << total
}
}

mut plt := plot.Plot.new()
plt.heatmap(
x: x
y: y
z: z
)
plt.layout(
title: 'Pink `fractal` noise 2d example'
)
plt.show()!
}
16 changes: 16 additions & 0 deletions examples/noise_simplex_2d/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
# Example - noise_simple_2d 📘

This example demonstrates the usage of the V Scientific Library for generating simplex noise.

## Instructions

1. Ensure you have the V compiler installed. You can download it from [here](https://vlang.io).
2. Ensure you have the VSL installed. You can do it following the [installation guide](https://github.com/vlang/vsl?tab=readme-ov-file#-installation)!
3. Navigate to this directory.
4. Run the example using the following command:

```sh
v run main.v
```

Enjoy exploring the capabilities of the V Scientific Library! 😊
38 changes: 38 additions & 0 deletions examples/noise_simplex_2d/main.v
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
module main

import rand
import vsl.noise
import vsl.plot

fn main() {
// Creation of the noise generator.
// Other noise functions and dimensions count are available.
rand.seed([u32(3155200429), u32(3208395956)])
mut generator := noise.Generator.new()
generator.randomize()

mut x := []f64{}
mut y := []f64{}
mut z := []f64{}

for xx in 0 .. 40 {
for yy in 0 .. 40 {
// We need to scale the coordinates to control the frequency of the noise.
val := generator.simplex_2d(0.03 * xx, 0.03 * yy)
x << xx
y << yy
z << val
}
}

mut plt := plot.Plot.new()
plt.heatmap(
x: x
y: y
z: z
)
plt.layout(
title: 'Simplex noise 2d example'
)
plt.show()!
}
80 changes: 80 additions & 0 deletions ml/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
# VSL Machine Learning (vsl.ml)

VSL aims to provide a robust set of tools for scientific computing with an emphasis
on performance and ease of use. In the `vsl.ml` module, some machine learning
models are designed as observers of data, meaning they re-train automatically when
data changes, while others do not require this functionality.

## Key Features

- **Observers of Data**: Some machine learning models in VSL act as observers,
re-training automatically when data changes.
- **High Performance**: Leverages V’s performance optimizations and can integrate
with C and Fortran libraries like Open BLAS and LAPACK.
- **Versatile Algorithms**: Supports a variety of machine learning algorithms and
models.

## Usage

### Loading Data

The `Data` struct in `vsl.ml` is designed to hold data in matrix format for machine
learning tasks. Here's a brief overview of how to use it:

#### Creating a Data Object

You can create a `Data` object using the following methods:

- `Data.new`: Creates a new `Data` object with specified dimensions.
- `Data.from_raw_x`: Creates a `Data` object from raw x values (without y values).
- `Data.from_raw_xy`: Creates a `Data` object from raw x and y values combined in a single matrix.
- `Data.from_raw_xy_sep`: Creates a `Data` object from separate x and y raw values.

### Data Methods

The `Data` struct has several key methods to manage and manipulate data:

- `set(x, y)`: Sets the x matrix and y vector and notifies observers.
- `set_y(y)`: Sets the y vector and notifies observers.
- `set_x(x)`: Sets the x matrix and notifies observers.
- `split(ratio)`: Splits the data into two parts based on the given ratio.
- `clone()`: Returns a deep copy of the Data object without observers.
- `clone_with_same_x()`: Returns a deep copy of the Data object but shares the same x reference.
- `add_observer(obs)`: Adds an observer to the data object.
- `notify_update()`: Notifies observers of data changes.

### Stat Observer

The `Stat` struct is an observer of `Data`, providing statistical analysis of the
data it observes. It automatically updates its statistics when the underlying data
changes.

## Observer Models

The following machine learning models in VSL are compatible with the `Observer`
pattern. This means they can observe data changes and automatically update
themselves.

### K-Means Clustering

K-Means Clustering is used for unsupervised learning to group data points into
clusters. As an observer model, it re-trains automatically when the data changes,
which is useful for dynamic datasets that require continuous updates.

### K-Nearest Neighbors (KNN)

K-Nearest Neighbors (KNN) is used for classification tasks where the target
variable is categorical. As an observer model, it re-trains automatically when the
data changes, which is beneficial for datasets that are frequently updated.

## Non-Observer Models

The following machine learning models in VSL do not require the observer pattern
and are trained once on a dataset without continuous updates.

### Linear Regression

Linear Regression is used for predicting a continuous target variable based on one
or more predictor variables. It is typically trained once on a dataset and used to
make predictions without requiring continuous updates. Hence, it is not implemented
as an observer model.
20 changes: 20 additions & 0 deletions noise/noise.v
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
module noise

import rand

// Generator is a struct holding the permutation table used in perlin and simplex noise
pub struct Generator {
mut:
perm []int = rand.shuffle_clone(permutations) or { panic(err) }
}

// new is a function that return a new Generator struct
pub fn Generator.new() Generator {
return Generator{}
}

// randomize is a function that shuffle the permutation set inside the Generator struct
// will not shuffle if rand.seed is not changed
pub fn (mut generator Generator) randomize() {
generator.perm = rand.shuffle_clone(permutations) or { panic(err) }
}
33 changes: 7 additions & 26 deletions noise/perlin2d.v
Original file line number Diff line number Diff line change
@@ -1,26 +1,7 @@
module noise

import rand

// Perlin is a struct that hold the permutation set for perlin noise
pub struct Perlin {
mut:
perm []int = rand.shuffle_clone(permutations) or { panic(err) }
}

// new is a function that return a new Perlin struct
pub fn Perlin.new() Perlin {
return Perlin{}
}

// randomize is a function that shuffle the permutation set inside the Perlin struct
// will not shuffle if rand.seed is not changed
pub fn (mut perlin Perlin) randomize() {
perlin.perm = rand.shuffle_clone(permutations) or { panic(err) }
}

// perlin2d is a function that return a single value of perlin noise for a given 2d position
pub fn (perlin Perlin) perlin2d(x f64, y f64) f64 {
pub fn (generator Generator) perlin2d(x f64, y f64) f64 {
xi := int(x) & 0xFF
yi := int(y) & 0xFF

Expand All @@ -30,13 +11,13 @@ pub fn (perlin Perlin) perlin2d(x f64, y f64) f64 {
u := fade(xf)
v := fade(yf)

pxi := perlin.perm[xi]
pxi1 := perlin.perm[xi + 1]
pxi := generator.perm[xi]
pxi1 := generator.perm[xi + 1]

aa := perlin.perm[pxi + yi]
ab := perlin.perm[pxi + yi + 1]
ba := perlin.perm[pxi1 + yi]
bb := perlin.perm[pxi1 + yi + 1]
aa := generator.perm[pxi + yi]
ab := generator.perm[pxi + yi + 1]
ba := generator.perm[pxi1 + yi]
bb := generator.perm[pxi1 + yi + 1]

x1 := lerp(grad2d(aa, xf, yf), grad2d(ba, xf - 1, yf), u)
x2 := lerp(grad2d(ab, xf, yf - 1), grad2d(bb, xf - 1, yf - 1), u)
Expand Down
2 changes: 1 addition & 1 deletion noise/perlin2d_test.v
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import vsl.float.float64
fn test_perlin2d() {
rand.seed([u32(3155200429), u32(3208395956)])

mut gen := Perlin.new()
mut gen := Generator.new()
gen.randomize()

result := gen.perlin2d(0.125, 0.125)
Expand Down
30 changes: 15 additions & 15 deletions noise/perlin3d.v
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
module noise

// perlin3d is a function that return a single value of perlin noise for a given 3d position
pub fn (perlin Perlin) perlin3d(x f64, y f64, z f64) f64 {
pub fn (generator Generator) perlin3d(x f64, y f64, z f64) f64 {
xi := int(x) & 0xFF
yi := int(y) & 0xFF
zi := int(z) & 0xFF
Expand All @@ -12,21 +12,21 @@ pub fn (perlin Perlin) perlin3d(x f64, y f64, z f64) f64 {
v := fade(yf)
w := fade(zf)

pxi := perlin.perm[xi]
pxi_yi := perlin.perm[pxi + yi]
pxi_yi1 := perlin.perm[pxi + yi + 1]
pxi1 := perlin.perm[xi + 1]
pxi1_yi := perlin.perm[pxi1 + yi]
pxi1_yi1 := perlin.perm[pxi1 + yi + 1]
pxi := generator.perm[xi]
pxi_yi := generator.perm[pxi + yi]
pxi_yi1 := generator.perm[pxi + yi + 1]
pxi1 := generator.perm[xi + 1]
pxi1_yi := generator.perm[pxi1 + yi]
pxi1_yi1 := generator.perm[pxi1 + yi + 1]

aaa := perlin.perm[pxi_yi + zi]
aba := perlin.perm[pxi_yi1 + zi]
aab := perlin.perm[pxi_yi + zi + 1]
abb := perlin.perm[pxi_yi1 + zi + 1]
baa := perlin.perm[pxi1_yi + zi]
bba := perlin.perm[pxi1_yi1 + zi]
bab := perlin.perm[pxi1_yi + zi + 1]
bbb := perlin.perm[pxi1_yi1 + zi + 1]
aaa := generator.perm[pxi_yi + zi]
aba := generator.perm[pxi_yi1 + zi]
aab := generator.perm[pxi_yi + zi + 1]
abb := generator.perm[pxi_yi1 + zi + 1]
baa := generator.perm[pxi1_yi + zi]
bba := generator.perm[pxi1_yi1 + zi]
bab := generator.perm[pxi1_yi + zi + 1]
bbb := generator.perm[pxi1_yi1 + zi + 1]

mut x1 := lerp(grad3d(aaa, xf, yf, zf), grad3d(baa, xf - 1, yf, zf), u)
mut x2 := lerp(grad3d(aba, xf, yf - 1, zf), grad3d(bba, xf - 1, yf - 1, zf), u)
Expand Down
2 changes: 1 addition & 1 deletion noise/perlin3d_test.v
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import vsl.float.float64
fn test_perlin3d() {
rand.seed([u32(3155200429), u32(3208395956)])

mut gen := Perlin.new()
mut gen := Generator.new()
gen.randomize()

result := gen.perlin3d(0.125, 0.125, 0.125)
Expand Down
Loading

0 comments on commit 79d8a8d

Please sign in to comment.