Optimal way for calc histogram in tensorflow.js - tensorflow.js

I need to calc histogram from big 1D tensor with tensorflow.js. I found way to do it, but its not optimal because of hi memory cosumtion in oneHot operation. My code example below:
for (let index = 0; index < 50; index++) { //repeat some times to ensure no memory leaks
const hist = getnormalHist(index);
const histArray = (await hist.array()) as number[];
const values = histArray.map((v, i) => ({ index: i, value: v }));
await tfvis.render.barchart({ name: "hist" }, values);
}
function getnormalHist(seed: number) {
return tf.tidy(() => {
const rand = tf
.randomNormal([100000], 0, 100, "int32", seed) //Generates long array of normal distributed randoms
.add(500)
.toInt();
const oneHot = rand.oneHot(1000, 1, 0); // convert to oneHot makes it x1000 bigger
const hist = oneHot.transpose().sum(1); // finally get histogram tensor
return hist;
});
}
I need to make this code faster and with less memory consumption, but I don't understand how.

tf.randomNormal([100000], 0, 100, "int32", seed) //Generates long array of normal distributed randoms
.add(500)
.toInt()
The above operation generates a sample distribution with mean around 500. It can be generated using mean 500 instead of 0. The add operation is not necessary for that. Additionnally it is redundant to use toInt since the dtype is already int32. Thus it can be simplified this way:
tf.randomNormal([100000], 500, 100, "int32", seed)
It is redundant to specify 0 and 1 onvalue and offvalue as they are default value.
rand.oneHot(1000)
There is no need to transpose before computing the sum for each indice. Computing the sum over the 0 axis will count each index. An intermediate matrix of size 100000 * 1000 will no longer be used.
oneHot.sum(0)
As a summary, getNormalHist will look as the following:
function getnormalHist(seed: number) {
return tf.tidy(() => {
const rand = tf
.randomNormal([100000], 500, 100, "int32", seed) //Generates long array of normal distributed randoms
const oneHot = rand.oneHot(1000); // convert to oneHot makes it x1000 bigger
const hist = oneHot.sum(0); // finally get histogram tensor
return hist;
});
}

My solution is create custom webGL operation.
class HistProgram {
variableNames = ["X"];
outputShape: number[];
userCode: string;
constructor(numIndices: number, counts: number) {
this.outputShape = [numIndices];
this.userCode = `
void main() {
int ch = getOutputCoords();
int c = 0;
for (int i = 0; i < ${counts}; i++){
if(ch == int(getX(i))){
c++;
}
}
setOutput(float(c));
}
`;
}
}
Now i can use it in this way:
const counts = 100000;
const channels = 1000;
const rand = tf.randomNormal([counts], 500, 100, "int32")
const prog = new HistProgram(channels, counts);
const hist = await tf.backend().compileAndRun(prog, [rand]).array();

Now the best solution that i found is:
const counts = 100000;
const channels = 1000;
const rand = tf.randomNormal([counts], 500, 100, "int32");
const hist = tf.sparseToDense(rand, tf.onesLike(rand), [channels], 0);

Related

how to shuffle an array except for the item in the middle?

Iยดm creating a Bingo board and I need that the one in the middle always stays the same even when shuffleing this array:
const bbb = [
"๐Ÿ˜‹",
"๐Ÿ˜",
"๐Ÿคฃ",
"๐Ÿ˜ƒ",
"๐Ÿ˜„",
"๐Ÿ˜…",
"๐Ÿ˜†",
"๐Ÿ˜‰",
"๐Ÿ˜Š",
"๐Ÿ˜Š",
"๐Ÿ˜Ž ",
"๐Ÿคฉ",
"๐ŸŽฏ",
"๐Ÿ˜ถ",
"๐Ÿ˜ซ",
"๐Ÿ˜ด",
"๐Ÿค ",
"๐Ÿ™„ ",
"๐Ÿ˜‘",
"๐Ÿ˜ฏ",
"๐Ÿ˜š",
"๐Ÿ˜ฅ",
"๐Ÿ˜ฎ ",
"๐Ÿ˜›",
"๐Ÿ˜"
];
const data = arrayShuffle(bbb).reduce(
(data, value, index) => ({ ...data, [index]: value }),
{}
);
and then Im maping the array to display the Tiles and create the board like this:
{Object.keys(data).map(id => (
<Tile
key={id}
id={id}
isSet={state.checked[id]}
onToggle={() => toggle(id)}
>
{data[id]}
</Tile>
))}
Remove the middle item from the array initially. Then do the in-place randomizing of items and finally attach the middle item to the array.
This runs in O(n) time complexity where n is the size of your array and you always get a uniform random permutation.
const bbb = [ "๐Ÿ˜‹", "๐Ÿ˜", "๐Ÿคฃ", "๐Ÿ˜ƒ", "๐Ÿ˜„", "๐Ÿ˜…", "๐Ÿ˜†", "๐Ÿ˜‰", "๐Ÿ˜Š", "๐Ÿ˜Š", "๐Ÿ˜Ž", "๐Ÿคฉ", "๐ŸŽฏ", "๐Ÿ˜ถ", "๐Ÿ˜ซ", "๐Ÿ˜ด", "๐Ÿค", "๐Ÿ™„", "๐Ÿ˜‘", "๐Ÿ˜ฏ", "๐Ÿ˜š", "๐Ÿ˜ฅ", "๐Ÿ˜ฎ", "๐Ÿ˜›", "๐Ÿ˜", ];
const getRandomInt = (min, max) => {
min = Math.ceil(min);
max = Math.floor(max);
return Math.floor(Math.random() * (max - min)) + min;
};
const arrayShuffleInplaceExceptMiddle = (A) => {
const middle = A.splice(A.length/2, 1);
const n = A.length;
const middleIndex = Math.floor(n / 2);
for (let i = 0; i < n; i++) {
let swapIndex = getRandomInt(i, n);
let a = A[i];
A[i] = A[swapIndex];
A[swapIndex] = a;
}
A.splice(n/2, 0, ...middle)
};
// test runs
Array.from({length: 10}, () => {
arrayShuffleInplaceExceptMiddle(bbb);
console.log(bbb.join(""));
})
Just shuffle the array normally, but remove the the value before the shuffle and insert it back afterward:
/**
* Durstenfeld shuffle
*
* - https://stackoverflow.com/a/12646864/438273
* - https://en.wikipedia.org/wiki/Fisher%E2%80%93Yates_shuffle#The_modern_algorithm
*
* #param {unknown[]} array
*/
function shuffleArray (array) {
for (let i = array.length - 1; i > 0; i--) {
const j = Math.floor(Math.random() * (i + 1));
[array[i], array[j]] = [array[j], array[i]];
}
}
/**
* Like a normal shuffle, but for a bingo board
*
* #param {unknown[]} array
*/
function bingoShuffle (array) {
const index = Math.floor((array.length - 1) / 2);
const [value] = array.splice(index, 1);
shuffleArray(array);
array.splice(index, 0, value);
}
// Let's keep the board small for this demo:
const board = [
"๐Ÿ˜Š",
"๐Ÿ˜Š",
"๐Ÿ˜Ž",
"๐Ÿคฉ",
"๐ŸŽฏ",
"๐Ÿ˜ถ",
"๐Ÿ˜ซ",
"๐Ÿ˜ด",
"๐Ÿค",
];
console.log(board.join(' '));
// Shuffle it a few times and look at the results:
for (let i = 0; i < 10; i += 1) {
bingoShuffle(board);
console.log(board.join(' '));
}
And because you tagged this with reactjs, I'm guessing this is (immutable) state, so you'll need to get a new array when shuffling, like this:
const updatedBoard = bingoShuffle([...board]);
// ^^^^^^^^^^
// Shallow copy into new array so you don't mutate React state

Using p5 inside React [closed]

Closed. This question is not reproducible or was caused by typos. It is not currently accepting answers.
This question was caused by a typo or a problem that can no longer be reproduced. While similar questions may be on-topic here, this one was resolved in a way less likely to help future readers.
Closed 10 months ago.
Improve this question
I am trying to integrate a slightly modified version of Boids flocking Sim into my React website. I have tried with both wrapper and p5-React npm's, both work on a smaller scale (i.e. with the examples in the descriptions), but when I try to apply it to the boids sim it just shows "loading...". I have watched the youtube tutorial and fully understand the logic, but I am a bit new to p5. Here is what I am trying to include: https://codepen.io/stephenleemorrow/pen/QWaxKqY
Here is my VS Code:
let flock;
let img;
let bg;
p5.preload = () => {
img = p5.loadImage("src\pngbyte.com-Open-Window-Window-Png-Image-window-png-images-open-bar.png");
}
p5.setup = () => {
bg = p5.loadImage("src\pixle-cloud-landscape.webp");
p5.createCanvas(568, 320);
flock = new Flock();
// Add an initial set of boids into the system
for (let i = 0; i < 20; i++) {
let b = new Boid(p5.width / 2, p5.height / 2);
flock.addBoid(b);
}
}
p5.draw = (p5) => {
p5.background(51);
flock.run();
p5.image(img, -23, -10, 610, 340);
}
// Add a new boid into the System
p5.mouseDragged = () => {
flock.addBoid(new Boid(p5.mouseX, p5.mouseY));
}
// The Nature of Code
// Daniel Shiffman
// http://natureofcode.com
// Flock object
// Does very little, simply manages the array of all the boids
function Flock() {
// An array for all the boids
this.boids = []; // Initialize the array
}
Flock.prototype.run = function () {
for (let i = 0; i < this.boids.length; i++) {
this.boids[i].run(this.boids); // Passing the entire list of boids to each boid individually
}
}
Flock.prototype.addBoid = function (b) {
this.boids.push(b);
}
// The Nature of Code
// Daniel Shiffman
// http://natureofcode.com
// Boid class
// Methods for Separation, Cohesion, Alignment added
function Boid(x, y) {
this.acceleration = p5.createVector(0, 0);
this.velocity = p5.createVector(p5.random(-1, 1), p5.random(-1, 1));
this.position = p5.createVector(x, y);
this.r = 3.0;
this.maxspeed = 3; // Maximum speed
this.maxforce = 0.05; // Maximum steering force
}
Boid.prototype.run = function (boids) {
this.flock(boids);
this.update();
this.borders();
this.render();
}
Boid.prototype.applyForce = function (force) {
// We could add mass here if we want A = F / M
this.acceleration.add(force);
}
// We accumulate a new acceleration each time based on three rules
Boid.prototype.flock = function (boids) {
let sep = this.separate(boids); // Separation
let ali = this.align(boids); // Alignment
let coh = this.cohesion(boids); // Cohesion
// Arbitrarily weight these forces
sep.mult(1.5);
ali.mult(1.0);
coh.mult(1.0);
// Add the force vectors to acceleration
this.applyForce(sep);
this.applyForce(ali);
this.applyForce(coh);
}
// Method to update location
Boid.prototype.update = function () {
// Update velocity
this.velocity.add(this.acceleration);
// Limit speed
this.velocity.limit(this.maxspeed);
this.position.add(this.velocity);
// Reset accelertion to 0 each cycle
this.acceleration.mult(0);
}
// A method that calculates and applies a steering force towards a target
// STEER = DESIRED MINUS VELOCITY
Boid.prototype.seek = function (target) {
let desired = p5.Vector.sub(target, this.position); // A vector pointing from the location to the target
// Normalize desired and scale to maximum speed
desired.normalize();
desired.mult(this.maxspeed);
// Steering = Desired minus Velocity
let steer = p5.Vector.sub(desired, this.velocity);
steer.limit(this.maxforce); // Limit to maximum steering force
return steer;
}
Boid.prototype.render = function () {
// Draw a triangle rotated in the direction of velocity
let theta = this.velocity.heading() + p5.radians(90);
p5.fill(50);
p5.stroke(100);
p5.push();
p5.translate(this.position.x, this.position.y);
p5.rotate(theta);
p5.beginShape();
p5.vertex(0, -this.r * .5);
p5.vertex(-this.r, this.r * .5);
p5.vertex(this.r, this.r * .5);
p5.endShape(p5.CLOSE);
p5.pop();
}
// Wraparound
Boid.prototype.borders = function () {
if (this.position.x < -this.r) this.position.x = p5.width + this.r;
if (this.position.y < -this.r) this.position.y = p5.height + this.r;
if (this.position.x > p5.width + this.r) this.position.x = -this.r;
if (this.position.y > p5.height + this.r) this.position.y = -this.r;
}
// Separation
// Method checks for nearby boids and steers away
Boid.prototype.separate = function (boids, p5) {
let desiredseparation = 25.0;
let steer = p5.createVector(0, 0);
let count = 0;
// For every boid in the system, check if it's too close
for (let i = 0; i < boids.length; i++) {
let d = p5.Vector.dist(this.position, boids[i].position);
// If the distance is greater than 0 and less than an arbitrary amount (0 when you are yourself)
if ((d > 0) && (d < desiredseparation)) {
// Calculate vector pointing away from neighbor
let diff = p5.Vector.sub(this.position, boids[i].position);
diff.normalize();
diff.div(d); // Weight by distance
steer.add(diff);
count++; // Keep track of how many
}
}
// Average -- divide by how many
if (count > 0) {
steer.div(count);
}
// As long as the vector is greater than 0
if (steer.mag() > 0) {
// Implement Reynolds: Steering = Desired - Velocity
steer.normalize();
steer.mult(this.maxspeed);
steer.sub(this.velocity);
steer.limit(this.maxforce);
}
return steer;
}
// Alignment
// For every nearby boid in the system, calculate the average velocity
Boid.prototype.align = function (boids) {
let neighbordist = 50;
let sum = p5.createVector(0, 0);
let count = 0;
for (let i = 0; i < boids.length; i++) {
let d = p5.Vector.dist(this.position, boids[i].position);
if ((d > 0) && (d < neighbordist)) {
sum.add(boids[i].velocity);
count++;
}
}
if (count > 0) {
sum.div(count);
sum.normalize();
sum.mult(this.maxspeed);
let steer = p5.Vector.sub(sum, this.velocity);
steer.limit(this.maxforce);
return steer;
} else {
return p5.createVector(0, 0);
}
}
// Cohesion
// For the average location (i.e. center) of all nearby boids, calculate steering vector towards that location
Boid.prototype.cohesion = function (boids) {
let neighbordist = 50;
let sum = p5.createVector(0, 0); // Start with empty vector to accumulate all locations
let count = 0;
for (let i = 0; i < boids.length; i++) {
let d = p5.Vector.dist(this.position, boids[i].position);
if ((d > 0) && (d < neighbordist)) {
sum.add(boids[i].position); // Add location
count++;
}
}
if (count > 0) {
sum.div(count);
return this.seek(sum); // Steer towards the location
} else {
return p5.createVector(0, 0);
}
}
}
Again, the connection to App() is good cause it works with smaller sketches. I think I'm just missing something in the syntax.
Any help would be GREATLY appreciated!
I made a few changes and got this running (although with errors in the p5 code that kind of fall out of the scope of the answer).
This might be a typo, but to use this sketch with ReactP5Wrapper, you need to export it as a function, so your code above is missing
export const sketch = (p5) => {
at the start. You actually have the closing bracket at the end, so it's probably a typo in the question.
Later in the code, anywhere you are passing p5 into a function, like
Boid.prototype.separate = function (boids, p5) {
you are not passing anything in for p5 - like
let sep = this.separate(boids);
so that would cause undefined errors. The p5 passed into the sketch function can be used throughout the sketch so you can remove p5 from the parameter lists of these other functions.
The really breaking issue here though was that you were trying to load images from the /src/ folder.
Make a directory in the /public/ folder at the top level of your project, call it /assets/ and put your images in there.
Then you can reference them like:
p5.preload = () => {
img = p5.loadImage("assets/test.png");
}
After I do all that, I get the sketch starting, creating the canvas and doing some of the algorithm but it fails at various points that I'm sure will have loads of fun debugging after you get it running to begin with :)
Btw, I'm displaying the sketch in my App component like this, using the ReactP5Wrapper:
import React from 'react';
import { ReactP5Wrapper } from 'react-p5-wrapper';
import { sketch } from './sketch/sketch';
export const App = () => {
return(
<div>
<ReactP5Wrapper sketch={sketch}/>
</div>
)
}

How to get all possibilities for required and optional entries?

The title is not very explicit :')
So there is an example to understand what I want to do :
I have to put a string (as input) which have to look like:
(Hey) (how are you) !
This have to create this string array:
!
Hey !
how are you !
Hey how are you !
Based on the parentheses that have to made all possibilities of string, considering that parenthesis is "optional"
function combs(arr, depth) {
var resp = [], len = arr.length, total = (1 << len);
for (var i = 1; i < total; i++) {
var tmp = [], good = true;
for (var k = 0; k < len; k++) {
if ((i & (1 << k))) {
tmp.push(arr[k]);
}
if (depth && tmp.length > depth) {
good = false;
continue;
}
}
good && resp.push(tmp);
}
return resp;
}
const entries = [{c:"hey",r:false}, {c:" how are you",r:false}, {c:"!",r:true}];
entries.forEach((v,i)=>entries[i].i=i);
const req = entries.filter(v=>v.r);
const opt = entries.filter(v=>!v.r);
const result = [req.map(v=>v.c).join("")];
combs(opt).forEach(v=>{
result.push(req.concat(v).sort((a,b)=>a.i-b.i).map(v=>v.c).join(""))
})
console.log(result)
Firstly, I have to calculate the possibility of having only required elements,
after I calculate all possibility for optional elements only, and I merge each optional possibilities with the only required possibility.
I sort by the index in the origin array.
And I get:
[ '!', 'hey!', ' how are you!', 'hey how are you!' ]
As wanted :)

Comparing variable to Math.max(...arr) not returning accurate answer

I'm trying to complete an easy LeetCode question: https://leetcode.com/problems/kids-with-the-greatest-number-of-candies/ but cannot figure out why my code is not working correctly. Here is the question and a correct solution:
Given the array candies and the integer extraCandies, where candies[i] represents the number of candies that the ith kid has.For each kid check if there is a way to distribute extraCandies among the kids such that he or she can have the greatest number of candies among them. Notice that multiple kids can have the greatest number of candies.
Input: candies = [2,3,5,1,3], extraCandies = 3
Output: [true,true,true,false,true]
Here is my current code:
var kidsWithCandies = function(candies, extraCandies) {
let newArr = [];
const max = Math.max(...candies)
for(i=0; i<candies.length; i++) {
let newVal = candies[i] + extraCandies
if (newVal >= max) {
newArr.push('true')
} else {
newArr.push('false')
}
}
return newArr
};
My code is returning [true,true,true,true,true] instead of [true,true,true,false,true].
I've used console.log() to check the values for 'max' and 'newVal' as the loop runs, and they are all correct, so there must be something wrong with my if statement, but I can't figure out what.
Thank you for your help!
You've answered your own question. Nonetheless, this'd also pass on LeetCode:
const kidsWithCandies = (candies, extraCandies) => {
let maxCandies = 0;
const greatest = [];
for (const candy of candies) {
(candy > maxCandies) && (maxCandies = candy);
}
for (let index = 0; index < candies.length; ++index) {
greatest.push(candies[index] + extraCandies >= maxCandies);
}
return greatest;
};

How to sort an array of objects by calculated value

Hello I'm trying to sort an array of objects by value, trought a function that calculates the average of the values in that objects property, which is an array of integers, then sorts it in decreasing order.
// SORT BY SCORE BY MOST SCORED
function sortByScoreDown() {
arrayLivros.sort((a, b) => fullscoreForSort(b._scores) - fullscoreForSort(a._scores));
}
// CALCULATE FULLSCORE
function fullscoreForSort(givenScores) {
let score = 0;
let total = givenScores.length - 1; // -1 BECAUSE BOOK._SCORES STARTS WITH AN ARRAY WITH 0 AS FIRST VALUE FOR SIMPLIFICATION
if (total != 0) {
let summedScore = givenScores.reduce((sum, add) => sum + add);
let score = summedScore / total;
}
return score;
}
// VALUES
newBook1 = {_title: book1,
_scores: [0,100,50]}
newBook2 = {_title: book2,
_scores: [0,100,100]}
newBook3 = {_title: book3,
_scores: [0,50,50]}
newBook4 = {_title: book3,
_scores: [0,30,30]}
arrayBooks = [newBook1, newBook2, newBook3, newBook4];
// EXCPECTED RETURN
arrayBooks = [newBook2, newBook1, newBook3, newBook4];
Thanks in advance.

Resources