Skip to content

Commit

Permalink
Updated kwargs handling
Browse files Browse the repository at this point in the history
  • Loading branch information
caph1993 committed Mar 18, 2024
1 parent 0c31419 commit 7caf924
Show file tree
Hide file tree
Showing 12 changed files with 518 additions and 94 deletions.
155 changes: 155 additions & 0 deletions demo-2.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,155 @@
<!DOCTYPE html>
<html>

<head>
<title>Numpy JS Demo 2</title>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width,initial-scale=1,maximum-scale=1,user-scalable=no">

<script src="https://d3js.org/d3.v7.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/@observablehq/plot@0.6/dist/plot.umd.min.js"></script>
<script src="dist/caph1993-numpy-js.js"></script>

<script src="https://code.jquery.com/jquery-3.6.0.min.js"></script>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.62.0/codemirror.min.css">
<script src="https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.62.0/codemirror.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.62.0/mode/javascript/javascript.min.js"></script>
<script>
var __console_log = console.log;
var __log_elem = null;
console.log = (...args) => {
__console_log(...args);
if (__log_elem !== null) {
$(__log_elem).append(args.join(' ') + '\n');
}
}
</script>
<style>
.hbox {
display: flex;
flex-direction: row;
justify-content: space-between;
}
.vbox {
display: flex;
flex-direction: column;
justify-content: space-between;
}
body{
display: flex;
flex-direction: row;
flex-wrap: wrap;
font-family: 'Courier New', Courier, monospace;
}
.justify-start {
justify-content: flex-start;
}
h3{
margin: 0;
}
</style>
</head>

<body>
<div class="vbox" style="width:100%">
<h1>Demo of caph1993-numpy-js</h1>

<h3>Header:</h3>
<div style="border: solid 1px black; height: fit-content;">
<textarea id="codeHeader" disabled="true" style="width: 90vw; height:6.5em"></textarea>
<script>
(()=>{
$('#codeHeader').text(`
${'<'}script src="https://d3js.org/d3.v7.min.js">${'<'}/script>
${'<'}script src="dist/caph1993-numpy-js.js">${'<'}/script>
// Or
let d3 = require('d3');
let np = require('caph1993-numpy-js');
`.trim());
})()
</script>
</div>
<h3>Your code (modify at will):</h3>
<div style="border: solid 1px black; height: fit-content;">
<textarea id="codeInput" style="height: fit-content !important;">
// Part 1: data creation

var XY = np.random.randn([1000, 2])
var norm = XY.pow(2).sum({ axis: -1, keepdims: true }).pow(0.5)
var XY_unit = XY.op('/', norm);

console.log(`First five points:\n${XY.index(`0:5`)}`);
console.log(`Norm of the first five points (before and after):`);
console.log(np.stack([norm.index(':', 0), XY_unit.norm(-1)], -1).index(`0:5`));

var svg = Plot.plot({
grid: true,
aspectRatio: 1, // undefined
marks: [
Plot.dot(XY.tolist().map(([x,y])=>({x, y})), {x: "x", y: "y", r:1, fill:"#69b369"}),
Plot.dot(XY_unit.tolist().map(([x,y])=>({x, y})), {x: "x", y: "y", r:1, fill:"#b36969"}),
]
});
$(document.body).append(svg);

</textarea>
</div>
<div class="hbox" style="width:100%">
<div></div>
<button onclick="executeCode()" style="padding:1em; margin:1em; font-size: large;">Execute</button>
<div></div>
</div>
</div>
</div>
<div class="vbox justify-start">
<div class="vbox">
<h3>Errors:</h3>
<textarea disabled="true" id="stderr" cols="50" style="border:none; min-height: 20vh;">
(error logs will appear here)
</textarea>
</div>
<div class="vbox">
<h3>Console output:</h3>
<textarea disabled="true" id="stdout" cols="50" style="border:none; min-height: 20vh;">
(console.log logs will appear here)
</textarea>
</div>
</div>
<script>
$('#stderr').parent().hide();
var codeInput = document.getElementById("codeInput");
var codeEditor = CodeMirror.fromTextArea(codeInput, {
mode: "javascript",
lineNumbers: true,
theme: "default",
autoCloseBrackets: true,
matchBrackets: true,
height: '60vh',
});

function executeCode() {
var code = codeEditor.getValue();
$('svg').remove();
$('#stdout').html('');
$('#stderr').html('');
$('#stderr').parent().hide();
__log_elem = $('#stdout');
try{
eval(code);
} catch(e){
$('#stderr').parent().show();
$('#stderr').append(e.stack)
$('#stderr').append('Press F12 for more details')
throw e;
}
}
</script>
<!-- <div id="myplot"></div> -->
<script>
// const plot = Plot.rectY({length: 10000}, Plot.binX({y: "count"}, {x: Math.random})).plot();
// const div = document.querySelector("#myplot");
// div.append(plot);
</script>
</body>

</html>
2 changes: 1 addition & 1 deletion dist/caph1993-numpy-js.js

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion dist/caph1993-numpy-js.js.map

Large diffs are not rendered by default.

94 changes: 58 additions & 36 deletions src/NDArray-class.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,17 +13,20 @@ class NDArray {

modules: typeof import("./NDArray").modules;

any: (axis?: AxisArg, keepdims?: boolean) => NDArray | boolean;
all: (axis?: AxisArg, keepdims?: boolean) => NDArray | boolean;
sum: (axis?: AxisArg, keepdims?: boolean) => NDArray | number;
product: (axis?: AxisArg, keepdims?: boolean) => NDArray | number;
max: (axis?: AxisArg, keepdims?: boolean) => NDArray | number;
min: (axis?: AxisArg, keepdims?: boolean) => NDArray | number;
argmax: (axis?: AxisArg, keepdims?: boolean) => NDArray | number;
argmin: (axis?: AxisArg, keepdims?: boolean) => NDArray | number;
mean: (axis?: AxisArg, keepdims?: boolean) => NDArray | number;
var: (axis?: AxisArg, keepdims?: boolean) => NDArray | number;
std: (axis?: AxisArg, keepdims?: boolean) => NDArray | number;
any: ReduceSignatureBool;
all: ReduceSignatureBool;
sum: ReduceSignature;
product: ReduceSignature;
max: ReduceSignature;
min: ReduceSignature;
argmax: ReduceSignature;
argmin: ReduceSignature;
mean: ReduceSignature;

var: ReduceSignature;
std: ReduceStdSignature;
norm: ReduceNormSignature;

add: SelfBinaryOperator;
subtract: SelfBinaryOperator;
multiply: SelfBinaryOperator;
Expand All @@ -32,12 +35,13 @@ class NDArray {
divide_int: SelfBinaryOperator;
pow: SelfBinaryOperator;
maximum: SelfBinaryOperator;
minimum: SelfBinaryOperator;
bitwise_or: SelfBinaryOperator;
bitwise_and: SelfBinaryOperator;
bitwise_shift_right: SelfBinaryOperator;
logical_xor: SelfBinaryOperator;
logical_or: SelfBinaryOperator;
minimum: SelfBinaryOperator;

logical_and: SelfBinaryOperator;
greater: SelfBinaryOperator;
less: SelfBinaryOperator;
Expand All @@ -47,6 +51,9 @@ class NDArray {
not_equal: SelfBinaryOperator;
isclose: (A: any, B: any, rtol?: number, atol?: number, equal_nan?: boolean) => number | boolean | NDArray;
allclose: (A: any, B: any, rtol?: number, atol?: number, equal_nan?: boolean) => boolean;

abs: SelfUnaryOperator;

assign: SelfAssignmentOperator;
add_assign: SelfAssignmentOperator;
subtract_assign: SelfAssignmentOperator;
Expand All @@ -66,7 +73,7 @@ class NDArray {

tolist: () => any;
// fromJS: (A: any) => NDArray;
round: (decimals?: number) => NDArray;
round: RoundSignature;
sort: (axis?: number) => NDArray;
transpose: (axes?: number[]) => NDArray;
op: (...args: any[]) => NDArray;
Expand Down Expand Up @@ -131,8 +138,9 @@ import { GLOBALS } from './_globals';
GLOBALS.NDArray = NDArray;

import { modules } from "./NDArray";
import { SelfAssignmentOperator, SelfBinaryOperator } from './NDArray/operators';
import { AxisArg } from './NDArray/reduce';
import { SelfAssignmentOperator, SelfBinaryOperator, SelfUnaryOperator } from './NDArray/operators';
// import { AxisArg, ReduceKwArgs } from './NDArray/reduce';
import { AxisArg, KwParser, ReduceKwargs, ReduceNormSignature, ReduceSignature, ReduceSignatureBool, ReduceStdSignature, RoundKwargs, RoundParsedKwargs, RoundSignature } from './NDArray/kwargs';
NDArray.prototype.modules = modules;


Expand Down Expand Up @@ -181,24 +189,39 @@ NDArray.prototype.toString = function () {
// ==============================


function reduceDecorator(func) {
return function (axis: import("./NDArray/reduce").AxisArg = null, keepdims: boolean = false) {
({ axis, keepdims } = Object.assign({ axis, keepdims }, this.__popKwArgs()));
return func(this, axis, keepdims);
}
}

NDArray.prototype.sum = reduceDecorator(modules.reduce.reducers.sum);
NDArray.prototype.product = reduceDecorator(modules.reduce.reducers.product);
NDArray.prototype.any = reduceDecorator(modules.reduce.reducers.any);
NDArray.prototype.all = reduceDecorator(modules.reduce.reducers.all);
NDArray.prototype.max = reduceDecorator(modules.reduce.reducers.max);
NDArray.prototype.min = reduceDecorator(modules.reduce.reducers.min);
NDArray.prototype.argmax = reduceDecorator(modules.reduce.reducers.argmax);
NDArray.prototype.argmin = reduceDecorator(modules.reduce.reducers.argmin);
NDArray.prototype.mean = reduceDecorator(modules.reduce.reducers.mean);
NDArray.prototype.var = reduceDecorator(modules.reduce.reducers.var);
NDArray.prototype.std = reduceDecorator(modules.reduce.reducers.std);
NDArray.prototype.any = modules.reduce.self_reducers.any;
NDArray.prototype.all = modules.reduce.self_reducers.all;

NDArray.prototype.sum = modules.reduce.self_reducers.sum;
NDArray.prototype.product = modules.reduce.self_reducers.product;
NDArray.prototype.max = modules.reduce.self_reducers.max;
NDArray.prototype.min = modules.reduce.self_reducers.min;
NDArray.prototype.argmax = modules.reduce.self_reducers.argmax;
NDArray.prototype.argmin = modules.reduce.self_reducers.argmin;
NDArray.prototype.mean = modules.reduce.self_reducers.mean;

NDArray.prototype.var = modules.reduce.self_reducers.var;
NDArray.prototype.std = modules.reduce.self_reducers.std;


// function reduceDecorator(func) {
// return function (axis: import("./NDArray/reduce").AxisArg = null, keepdims: boolean = false) {
// ({ axis, keepdims } = Object.assign({ axis, keepdims }, this.__popKwArgs()));
// return func(this, axis, keepdims);
// }
// }
// reducersExtra
// NDArray.prototype.var = reduceDecorator(modules.reduce.self_reducers.var);

// NDArray.prototype.norm = function (axis: import("./NDArray/reduce").AxisArg = null, keepdims: boolean = false, ord = 2) {
// ({ axis, keepdims, ord } = Object.assign({ axis, keepdims, ord }, this.__popKwArgs()));
// return modules.reduce.reducers.norm(this, axis, keepdims, ord);
// } as any;
// NDArray.prototype.std = function (axis: import("./NDArray/reduce").AxisArg = null, keepdims: boolean = false, ddof = 0) {
// ({ axis, keepdims, ddof } = Object.assign({ axis, keepdims, ddof }, this.__popKwArgs()));
// return modules.reduce.reducers.std(this, axis, keepdims, ddof);
// }



Expand Down Expand Up @@ -253,6 +276,8 @@ function unaryOpDecorator(func: import("./NDArray/operators").UnaryOperator): im
// Unary operations: only boolean_not. Positive is useless and negative is almost useless
NDArray.prototype.bitwise_or = unaryOpDecorator(modules.operators.op_unary["~"]);
NDArray.prototype.logical_or = unaryOpDecorator(modules.operators.op_unary["not"]);
NDArray.prototype.abs = unaryOpDecorator(modules.operators.op_unary["abs"]);



NDArray.prototype.isclose = modules.operators.isclose;
Expand Down Expand Up @@ -300,10 +325,7 @@ NDArray.prototype.tolist = function () {
// elementwise methods
// ==============================

NDArray.prototype.round = function (decimals = 0) {
({ decimals } = Object.assign({ decimals }, this.__popKwArgs()));
return modules.elementwise.round(this, decimals);
};
NDArray.prototype.round = modules.elementwise.round_kw.as_method;

// ==============================
// transform methods
Expand Down
7 changes: 4 additions & 3 deletions src/NDArray/elementwise.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,19 +2,20 @@

import { asarray, new_NDArray } from './basic';
import type NDArray from "../NDArray-class";
import { KwParser, RoundParsedKwargs, RoundSignature } from './kwargs';

// Here, we declare only the core functions (those that are methods)

export function elementwise(A, func, dtype) {
export function elementwise(A: NDArray, func, dtype) {
A = asarray(A);
return new_NDArray(A.flat.map(func), A.shape, dtype);
}


export function round(A, decimals = 0) {
export function round(A: NDArray, decimals: number) {
if (decimals == 0) elementwise(A, Math.round, Number);
return elementwise(A, x => parseFloat(x.toFixed(decimals)), Number);
};
export const round_kw = new KwParser<RoundSignature, RoundParsedKwargs>([["decimals", 0]]).decorators(round)

export function bitwise_not(A: NDArray) {
return elementwise(A, x => ~x, Number);
Expand Down
61 changes: 61 additions & 0 deletions src/NDArray/kwargs.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
import { NDArray } from "../NDArray-class";

export type AxisArg = null | number;

export type ReduceKwargs = { axis?: number, keepdims?: boolean };
export type ReduceSignature<T = number> = (axis?: AxisArg | ReduceKwargs, keepdims?: boolean | ReduceKwargs) => NDArray | T;
export type ReduceSignatureBool = ReduceSignature<boolean>;
export type ReduceParsedKwargs = [number, boolean];

export type ReduceStdKwargs = { axis?: number, keepdims?: boolean, ddof?: number };
export type ReduceStdSignature = (axis?: AxisArg | ReduceStdKwargs, keepdims?: boolean | ReduceStdKwargs, ddof?: number | ReduceStdKwargs) => NDArray | number;
export type ReduceStdParsedKwargs = [number, boolean, number];

export type ReduceNormKwargs = { axis?: number, keepdims?: boolean, ord?: number };
export type ReduceNormSignature = (axis?: AxisArg | ReduceNormKwargs, keepdims?: boolean | ReduceNormKwargs, ord?: number | ReduceNormKwargs) => NDArray | number;
export type ReduceNormParsedKwargs = [number, boolean, number];


export type RoundKwargs = { decimals?: number };
export type RoundSignature = (decimals?: number) => NDArray;
export type RoundParsedKwargs = [number];

type WithArray<Signature extends (...args: any[]) => any> = (arr: NDArray, ...args: Parameters<Signature>) => ReturnType<Signature>;

export class KwParser<Signature extends (...args: any[]) => any, Parsed extends any[]> {
defaults: [string, any][];

constructor(defaults: [string, any][]) {
this.defaults = defaults;
}

parse(...args: Parameters<Signature>): Parsed {
let defaults = this.defaults;
let kwargs = Object.assign(Object.fromEntries(defaults));
for (let i = 0; i < args.length; i++) {
let value = args[i];
if (value instanceof Object) Object.assign(kwargs, value);
else if (value !== undefined) kwargs[defaults[i][0]] = value;
}
let sortedArgs = defaults.map(([key, _]) => kwargs[key]);
return sortedArgs as Parsed;
}

decorator_func<F extends (arr: NDArray, ...args: Parsed) => ReturnType<Signature>>(func: F): WithArray<Signature> {
return function (arr: NDArray, ...args: Parameters<Signature>) {
const parsed = this.parse(...args);
return func(arr, ...parsed);
};
}

decorator_method<F extends (arr: NDArray, ...args: Parsed) => ReturnType<Signature>>(func: F): Signature {
let self = this;
return function (...args: Parameters<Signature>) {
const parsed = self.parse(...args);
return func(this, ...parsed);
} as any;
}
decorators<F extends (arr: NDArray, ...args: Parsed) => ReturnType<Signature>>(func: F) {
return { as_function: this.decorator_func(func), as_method: this.decorator_method(func) };
}
}
Loading

0 comments on commit 7caf924

Please sign in to comment.