From dd36b5b7b4542aba165fa71b36e68a50fab779f6 Mon Sep 17 00:00:00 2001 From: tkc <181991+tkc@users.noreply.github.com> Date: Wed, 2 Jan 2019 21:23:18 +0900 Subject: [PATCH] Add vector matrix --- lib/{matrix.spec.ts => math.spec.ts} | 2 +- lib/math.ts | 88 ++++++++++++++ lib/matrix.ts | 165 ++++++++++++++++----------- lib/neural-network.spec.ts | 2 +- lib/neural-network.ts | 2 +- lib/vactor.ts | 92 +++++++++++++++ models/index.ts | 37 ++++-- 7 files changed, 309 insertions(+), 79 deletions(-) rename lib/{matrix.spec.ts => math.spec.ts} (96%) create mode 100644 lib/math.ts create mode 100644 lib/vactor.ts diff --git a/lib/matrix.spec.ts b/lib/math.spec.ts similarity index 96% rename from lib/matrix.spec.ts rename to lib/math.spec.ts index d44206f..ecfce71 100644 --- a/lib/matrix.spec.ts +++ b/lib/math.spec.ts @@ -1,4 +1,4 @@ -import * as matrx from "./matrix"; +import * as matrx from "./math"; test("Dot", () => { const a = [[1, 1, 1, 1]]; diff --git a/lib/math.ts b/lib/math.ts new file mode 100644 index 0000000..a4219ca --- /dev/null +++ b/lib/math.ts @@ -0,0 +1,88 @@ +export function expM2(matrix: number[][]): number[][] { + return matrix.map(m1 => m1.map(m2 => Math.exp(m2))); +} + +export function MultiplyM1M1(matrix1: number[], matrix2: number[]): number[] { + if (matrix1.length === matrix2.length) { + return matrix1.map((m, i) => matrix1[i] * matrix2[i]); + } else { + if (matrix1.length < matrix2.length) { + return matrix2.map((m, i) => matrix1[0] * matrix2[i]); + } else { + return matrix1.map((m, i) => matrix1[i] * matrix2[0]); + } + } +} + +export function MultiplyM2M1(matrix1: number[][], matrix2: number[]): number[][] { + const res = []; + matrix1.forEach((m, i) => { + m.forEach((m2, k) => res.push(matrix2.map(m2 => m2 * matrix1[i][k]))); + }); + return res; +} + +export function MultiplyScalarM1(scalar: number, matrix2: number[]): number[] { + return matrix2.map(m => m * scalar); +} + +export function SumScalarM2(scalar: number, matrix2: number[][]): number[][] { + return matrix2.map(m2 => m2.map((m3, i) => m3 + scalar)); +} + +export function SumM1(matrix: number[]): number { + return matrix.reduce((p, c) => p + c); +} + +export function SumM2(matrix: number[][]): number { + return matrix.map(m => m.reduce((p, c) => p + c)).reduce((p, c) => p + c); +} + +export function SumAxis0M2(matrix: number[][]): number[] { + return matrix.map(m => m.reduce((p, c) => p + c)); +} + +export function DotDivideM2Scalar(matrix2: number[], scalar: number): number[] { + return matrix2.map(m2 => m2 / scalar); +} + +export function DotDivideScalarM1(scalar: number, matrix: number[]): number[] { + return matrix.map(m => scalar / m); +} + +export function SquareM1(matrix: number[]): number[] { + return matrix.map((m, i) => matrix[i] * matrix[i]); +} + +export function SubtractM2Scalar(matrix1: number[][], scalar: number): number[][] { + return matrix1.map(m1 => m1.map((m2, i) => m2 - scalar)); +} + +export function SubtractM1Scalar(matrix1: number[], scalar: number): number[] { + return matrix1.map(m => m - scalar); +} + +export function SubtracScalarM1(scalar: number, matrix1: number[]): number[] { + return matrix1.map(m => scalar - m); +} + +export function SubtractM1M1(matrix1: number[], matrix2: number[]): number[] { + if (matrix1.length === matrix2.length) { + return matrix1.map((m, i) => matrix1[i] - matrix2[i]); + } else { + if (matrix1.length < matrix2.length) { + return matrix2.map((m, i) => matrix1[0] - matrix2[i]); + } else { + return matrix1.map((m, i) => matrix1[i] - matrix2[0]); + } + } +} + +export function DotscalarM2(scalar: number, matrix2: number[][]): number[][] { + return matrix2.map(m2 => m2.map((m3, i) => m3 * scalar)); +} + +export function DotM2M1(matrix1: number[][], matrix2: number[]): number[] { + const res = matrix1.map(m1 => m1.map((m2, i) => m2 * matrix2[i])); + return res.map(m1 => m1.reduce((p, c) => p + c)); +} diff --git a/lib/matrix.ts b/lib/matrix.ts index a4219ca..fafd363 100644 --- a/lib/matrix.ts +++ b/lib/matrix.ts @@ -1,88 +1,115 @@ -export function expM2(matrix: number[][]): number[][] { - return matrix.map(m1 => m1.map(m2 => Math.exp(m2))); -} +import * as Models from "../models"; +import * as Vector from "./vactor"; -export function MultiplyM1M1(matrix1: number[], matrix2: number[]): number[] { - if (matrix1.length === matrix2.length) { - return matrix1.map((m, i) => matrix1[i] * matrix2[i]); - } else { - if (matrix1.length < matrix2.length) { - return matrix2.map((m, i) => matrix1[0] * matrix2[i]); - } else { - return matrix1.map((m, i) => matrix1[i] * matrix2[0]); - } +export function CreateMatrix(v: number[][]): Models.Matrix { + let matrixsInstance: number[][]; + matrixsInstance = v; + + function val(): number[][] { + return matrixsInstance; } -} -export function MultiplyM2M1(matrix1: number[][], matrix2: number[]): number[][] { - const res = []; - matrix1.forEach((m, i) => { - m.forEach((m2, k) => res.push(matrix2.map(m2 => m2 * matrix1[i][k]))); - }); - return res; -} + function row(i: number): Models.Vector { + if (i < 0 || i >= matrixsInstance.length) { + return Vector.CreateVector([0]); + } + return Vector.CreateVector(matrixsInstance[i]); + } -export function MultiplyScalarM1(scalar: number, matrix2: number[]): number[] { - return matrix2.map(m => m * scalar); -} + function column(j: number): Models.Vector { + if (j < 0 || j >= matrixsInstance[0].length) { + return Vector.CreateVector([0]); + } + const result = []; + for (let i = 0; i < matrixsInstance.length; i += 1) { + result.push(matrixsInstance[i][j]); + } + return Vector.CreateVector(result); + } -export function SumScalarM2(scalar: number, matrix2: number[][]): number[][] { - return matrix2.map(m2 => m2.map((m3, i) => m3 + scalar)); -} + function log(): Models.Matrix { + const res = matrixsInstance.map((x, i) => x.map((y, k) => Math.log(y))); + return CreateMatrix(res); + } -export function SumM1(matrix: number[]): number { - return matrix.reduce((p, c) => p + c); -} + function add(matrix: number | Models.Matrix): Models.Matrix { + if (typeof matrix === "number") { + const res = matrixsInstance.map((x: number[]) => x.map(y => (y + matrix) as number)); + return CreateMatrix(res); + } -export function SumM2(matrix: number[][]): number { - return matrix.map(m => m.reduce((p, c) => p + c)).reduce((p, c) => p + c); -} + if (!isSameSize(matrix)) { + throw new Error("Cannot operate two matrix with different dimensions."); + } -export function SumAxis0M2(matrix: number[][]): number[] { - return matrix.map(m => m.reduce((p, c) => p + c)); -} + const val: number[][] = matrix.val(); + const res = matrixsInstance.map((x, i) => x.map((y, k) => y + val[i][k])); + return CreateMatrix(res); + } -export function DotDivideM2Scalar(matrix2: number[], scalar: number): number[] { - return matrix2.map(m2 => m2 / scalar); -} + function subtract(matrix: number | Models.Matrix): Models.Matrix { + if (typeof matrix === "number") { + const res = matrixsInstance.map((x: number[]) => x.map(y => (y - matrix) as number)); + return CreateMatrix(res); + } -export function DotDivideScalarM1(scalar: number, matrix: number[]): number[] { - return matrix.map(m => scalar / m); -} + if (!isSameSize(matrix)) { + throw new Error("Cannot operate two matrix with different dimensions."); + } -export function SquareM1(matrix: number[]): number[] { - return matrix.map((m, i) => matrix[i] * matrix[i]); -} + const val: number[][] = matrix.val(); + const res = matrixsInstance.map((x, i) => x.map((y, k) => y - val[i][k])); + return CreateMatrix(res); + } -export function SubtractM2Scalar(matrix1: number[][], scalar: number): number[][] { - return matrix1.map(m1 => m1.map((m2, i) => m2 - scalar)); -} + function isSameSize(matrix: Models.Matrix): boolean { + const val: number[][] = matrix.val(); + return matrixsInstance.length === val.length && matrixsInstance[0].length === val[0].length; + } -export function SubtractM1Scalar(matrix1: number[], scalar: number): number[] { - return matrix1.map(m => m - scalar); -} + function multiply(matrix: number | Models.Matrix): Models.Matrix { + if (typeof matrix === "number") { + const result = CreateMatrix(matrixsInstance).val(); + for (let i = 0, li = result.length; i < li; i += 1) { + for (let j = 0, lj = result[0].length; j < lj; j += 1) { + result[i][j] *= matrix as number; + } + } + return CreateMatrix(matrixsInstance); + } -export function SubtracScalarM1(scalar: number, matrix1: number[]): number[] { - return matrix1.map(m => scalar - m); -} + let inputElements: number[][] = matrix.val(); + if (matrixsInstance[0].length !== inputElements.length) { + throw new Error("Number of rows of A should match number of cols of B."); + } -export function SubtractM1M1(matrix1: number[], matrix2: number[]): number[] { - if (matrix1.length === matrix2.length) { - return matrix1.map((m, i) => matrix1[i] - matrix2[i]); - } else { - if (matrix1.length < matrix2.length) { - return matrix2.map((m, i) => matrix1[0] - matrix2[i]); - } else { - return matrix1.map((m, i) => matrix1[i] - matrix2[0]); + const li = matrixsInstance.length; + const lj = inputElements[0].length; + const lk = matrixsInstance[0].length - 1; + const result = []; + for (let i = 0; i < li; i += 1) { + const currentRow = []; + for (let j = 0; j < lj; j += 1) { + let sum = 0; + for (let k = lk; k >= 0; k -= 1) { + sum += matrixsInstance[i][k] * inputElements[k][j]; + } + currentRow[j] = sum; + } + result[i] = currentRow; } - } -} -export function DotscalarM2(scalar: number, matrix2: number[][]): number[][] { - return matrix2.map(m2 => m2.map((m3, i) => m3 * scalar)); -} + return CreateMatrix(result); + } -export function DotM2M1(matrix1: number[][], matrix2: number[]): number[] { - const res = matrix1.map(m1 => m1.map((m2, i) => m2 * matrix2[i])); - return res.map(m1 => m1.reduce((p, c) => p + c)); + return { + val, + multiply, + isSameSize, + subtract, + row, + column, + log, + add, + }; } diff --git a/lib/neural-network.spec.ts b/lib/neural-network.spec.ts index 0c865f4..8083ad9 100644 --- a/lib/neural-network.spec.ts +++ b/lib/neural-network.spec.ts @@ -42,7 +42,7 @@ test("Loss", () => { test("Accuracy", () => { const x = [[1, 1, 1, 1]]; - const y = [[1, 1, 1, 1]]; + const y = [1, 1, 1, 1]; const w = [0.1, 0.1, 0.1, 0.1]; const b = 0.1; const res = nn.Accuracy(x, y, w, b); diff --git a/lib/neural-network.ts b/lib/neural-network.ts index 65508c5..0ad1e12 100644 --- a/lib/neural-network.ts +++ b/lib/neural-network.ts @@ -1,4 +1,4 @@ -import * as matrx from "./matrix"; +import * as matrx from "./math"; export interface UpdateResult { w: number[]; diff --git a/lib/vactor.ts b/lib/vactor.ts new file mode 100644 index 0000000..3af2d3f --- /dev/null +++ b/lib/vactor.ts @@ -0,0 +1,92 @@ +import * as Models from "../models"; +import * as Matrix from "./matrix"; + +export function CreateArray(num: number, v: number): number[] { + const result = []; + for (let i = 0; i < num; i += 1) { + result.push(v); + } + return result; +} + +export function Zero(num: number): Models.Vector { + return CreateVector(CreateArray(num, 0)); +} + +export function CreateVector(v: number[]): Models.Vector { + let vectorInstance: number[]; + vectorInstance = v; + + function val(): number[] { + return vectorInstance; + } + + function size(): number { + return vectorInstance.length; + } + + function log(): number[] { + return vectorInstance.map(v => Math.log(v)); + } + + function subtract(value: number | Models.Vector): Models.Vector { + const vector = []; + if (typeof value === "number") { + for (let i = 0, li = vectorInstance.length; i < li; i += 1) { + vector.push(vectorInstance[i] - value); + } + } else { + const values: number[] = value.val(); + if (vectorInstance.length !== values.length) { + throw new Error("Cannot operate two vectors with different dimensions."); + } + for (let i = 0, li = vectorInstance.length; i < li; i += 1) { + vector.push(vectorInstance[i] - values[i]); + } + } + return CreateVector(vector); + } + + function sum(): number { + return vectorInstance.reduce((p, c) => p + c); + } + + function multiply(values: number[]): Models.Vector { + const vector = []; + for (let i = 0, li = vectorInstance.length; i < li; i += 1) { + vector.push(vectorInstance[i] * values[i]); + } + return CreateVector(vector); + } + + function dot(values: number[]): number { + if (vectorInstance.length !== values.length) { + throw new Error("Cannot operate two vectors with different dimensions."); + } + let product = 0; + for (let i = 0; i < values.length; i += 1) { + product += vectorInstance[i] * values[i]; + } + return product; + } + + function map(fn: (v: number) => number): Models.Vector { + return CreateVector(vectorInstance.map(x => fn(x))); + } + + function matrix(): Models.Matrix { + return Matrix.CreateMatrix([vectorInstance]); + } + + return { + val, + size, + log, + subtract, + sum, + multiply, + dot, + map, + matrix, + }; +} diff --git a/models/index.ts b/models/index.ts index cc4a753..bd9ec1e 100755 --- a/models/index.ts +++ b/models/index.ts @@ -1,12 +1,35 @@ export type VarietyIrisSetosa = "Iris-setosa"; export type VarietyIrisVersicolor = "Iris-versicolor"; export type VarietyIrisVirginica = "Iris-virginica"; -export type VarietyType = VarietyIrisSetosa | VarietyIrisVersicolor|VarietyIrisVirginica +export type VarietyType = VarietyIrisSetosa | VarietyIrisVersicolor | VarietyIrisVirginica; export interface Iris { - sepalLength: number; - sepalWidth: number; - petalLength: number; - petalWidth: number; - variety: VarietyType; - } \ No newline at end of file + sepalLength: number; + sepalWidth: number; + petalLength: number; + petalWidth: number; + variety: VarietyType; +} + +export interface Vector { + val(): number[]; + size(): number; + log: () => number[]; + subtract: (v: number | Vector) => Vector; + sum: () => number; + multiply: (v: number[]) => Vector; + dot: (v: number[]) => number; + map(fn: (v: number) => number): Vector; + matrix(): Matrix; +} + +export interface Matrix { + val(): number[][]; + multiply: (matrix: number | Matrix) => Matrix; + isSameSize(matrix: Matrix): boolean; + subtract(matrix: number | Matrix): Matrix; + row(i: number): Vector; + column(j: number): Vector; + log(): Matrix; + add(matrix: number | Matrix): Matrix; +}