Skip to content

Instantly share code, notes, and snippets.

@stemcstudio
Last active August 4, 2020 17:49
Show Gist options
  • Save stemcstudio/415ca00b9b434f453ac9052911048196 to your computer and use it in GitHub Desktop.
Save stemcstudio/415ca00b9b434f453ac9052911048196 to your computer and use it in GitHub Desktop.
GeoCAS

GeoCAS

Overview

Musings on a Computer Algebra System for Geometry and Geometric Algebra.

The basic idea is to construct an expression tree, simplify the expression tree by making canonical transformations, stop when no more transformations occur.

A general multivector is going to be stored as an array of basis blades, with a canonical ordering.

Scalars should be hoisted up to leave expressions involving only basis blades. When blades are ordered, computations can proceed. When tree is just a sum of basis blades with scalar coefficients it can be sorted.

Copyright (c) 2016 David Geo Holmes.

function wedge(a: number[], b: number[]): number[] {
const result: number[] = [];
const aLen = a.length;
const bLen = b.length;
for (let i = 0; i < aLen; i++) {
result.push(a[i]);
}
for (let i = 0; i < bLen; i++) {
result.push(b[i]);
}
return result;
}
interface Metric {
(u: BasisBladeExpr, v: BasisBladeExpr): ScalarExpr;
}
export class Expr {
constructor(public g: Metric, public type: string) {
}
isChanged(): boolean {
throw new Error(`${this.type}.isChanged is not implemented.`);
}
copy(dirty: boolean): Expr {
throw new Error(`${this.type}.copy is not implemented.`);
}
reset(): Expr {
throw new Error(`${this.type}.reset is not implemented.`);
}
simplify(): Expr {
throw new Error(`${this.type}.simplify is not implemented.`);
}
toPrefix(): string {
throw new Error(`${this.type}.toPrefix is not implemented.`);
}
toString(): string {
throw new Error(`${this.type}.toString is not implemented.`);
}
__add__(rhs: Expr | number): Expr {
if (rhs instanceof Expr) {
return new AddExpr(this, rhs);
}
else if (typeof rhs === 'number') {
return new AddExpr(this, new ScalarExpr(this.g, rhs));
}
else {
return void 0;
}
}
__radd__(lhs: Expr | number): Expr {
if (lhs instanceof Expr) {
return new AddExpr(lhs, this);
}
else if (typeof lhs === 'number') {
return new AddExpr(new ScalarExpr(this.g, lhs), this);
}
else {
return void 0;
}
}
__mul__(rhs: number | Expr): Expr {
if (rhs instanceof Expr) {
return new MulExpr(this, rhs);
}
else if (typeof rhs === 'number') {
return new MulExpr(this, new ScalarExpr(this.g, rhs));
}
else {
return void 0;
}
}
__rmul__(lhs: Expr | number): Expr {
if (lhs instanceof Expr) {
return new MulExpr(lhs, this);
}
else if (typeof lhs === 'number') {
return new MulExpr(new ScalarExpr(this.g, lhs), this);
}
else {
return void 0;
}
}
__vbar__(rhs: Expr | number): Expr {
if (rhs instanceof Expr) {
return new VBarExpr(this, rhs);
}
else if (typeof rhs === 'number') {
return new VBarExpr(this, new ScalarExpr(this.g, rhs));
}
else {
return void 0;
}
}
__rvbar__(lhs: Expr | number): Expr {
if (lhs instanceof Expr) {
return new VBarExpr(lhs, this);
}
else if (typeof lhs === 'number') {
return new VBarExpr(new ScalarExpr(this.g, lhs), this);
}
else {
return void 0;
}
}
__wedge__(rhs: Expr | number): Expr {
if (rhs instanceof Expr) {
return new WedgeExpr(this, rhs);
}
else if (typeof rhs === 'number') {
return new WedgeExpr(this, new ScalarExpr(this.g, rhs));
}
else {
return void 0;
}
}
__rwedge__(lhs: Expr | number): Expr {
if (lhs instanceof Expr) {
return new VBarExpr(lhs, this);
}
else if (typeof lhs === 'number') {
return new VBarExpr(new ScalarExpr(this.g, lhs), this);
}
else {
return void 0;
}
}
}
export class BinaryExpr extends Expr {
constructor(public lhs: Expr, public rhs: Expr, type: string) {
super(lhs.g, type);
if (!(lhs instanceof Expr)) {
throw new Error(`${type}.lhs must be an Expr: ${typeof lhs}`);
}
if (!(rhs instanceof Expr)) {
throw new Error(`${type}.rhs must be an Expr: ${typeof rhs}`);
}
}
}
export class AddExpr extends BinaryExpr {
constructor(lhs: Expr, rhs: Expr, public dirty = false) {
super(lhs, rhs, 'AddExpr');
}
isChanged(): boolean {
return this.dirty || this.lhs.isChanged() || this.rhs.isChanged();
}
copy(dirty: boolean): Expr {
return new AddExpr(this.lhs, this.rhs, dirty);
}
reset(): Expr {
return new AddExpr(this.lhs.reset(), this.rhs.reset());
}
simplify(): Expr {
const a = this.lhs.simplify();
const b = this.rhs.simplify();
if (b instanceof ScalarExpr && typeof b.value === 'number' && b.value === 0) {
return a.copy(true);
}
else if (a instanceof MulExpr && b instanceof MulExpr) {
if (a.lhs instanceof ScalarExpr && b.lhs instanceof ScalarExpr && a.rhs === b.rhs) {
const sa: ScalarExpr = <ScalarExpr>a.lhs;
const sb: ScalarExpr = <ScalarExpr>b.lhs;
if (typeof sa.value === 'number' && typeof sb.value === 'number') {
const s = new ScalarExpr(this.g, <number>sa.value + <number>sb.value);
return new MulExpr(s, a.rhs, true);
}
else {
return new AddExpr(a, b);
}
}
else {
return new AddExpr(a, b);
}
}
else if (a instanceof ScalarExpr && b instanceof ScalarExpr) {
if (typeof a.value === 'number' && typeof b.value === 'number') {
return new ScalarExpr(this.g, <number>a.value + <number>b.value, true);
}
else {
return new AddExpr(a, b);
}
}
else {
return new AddExpr(a, b);
}
}
toPrefix(): string {
return `+(${this.lhs.toPrefix()}, ${this.rhs.toPrefix()})`;
}
toString() {
return `${this.lhs} + ${this.rhs}`;
}
}
export class MulExpr extends BinaryExpr {
constructor(lhs: Expr, rhs: Expr, public dirty = false) {
super(lhs, rhs, 'MultiplyExpr');
}
isChanged(): boolean {
return this.dirty || this.lhs.isChanged() || this.rhs.isChanged();
}
copy(dirty: boolean): Expr {
return new MulExpr(this.lhs, this.rhs, dirty);
}
reset(): Expr {
return new MulExpr(this.lhs.reset(), this.rhs.reset());
}
simplify(): Expr {
const a = this.lhs.simplify();
const b = this.rhs.simplify();
if (a instanceof ScalarExpr) {
if (typeof a.value === 'number' && a.value === 0) {
return a.copy(true);
}
else if (typeof a.value === 'number' && a.value === 1) {
return b.copy(true);
}
else if (b instanceof ScalarExpr) {
if (typeof a.value === 'number' && a.value === 1) {
return b;
}
else if (typeof a.value === 'number' && typeof b.value === 'number') {
return new ScalarExpr(this.g, <number>a.value * <number>b.value, true);
}
else if (typeof a.value !== 'number' && typeof b.value === 'number') {
return new MulExpr(b, a, true);
}
else {
return new MulExpr(a, b);
}
}
else {
return new MulExpr(a, b);
}
}
else if (a instanceof BasisBladeExpr) {
if (b instanceof MulExpr) {
const bL = b.lhs;
const bR = b.rhs;
if (bL instanceof BasisBladeExpr) {
if (a.vectors[0] === bL.vectors[0]) {
return new MulExpr(new MulExpr(a, bL), bR, true);
}
else {
return new MulExpr(a, b);
}
}
else {
return new MulExpr(a, b);
}
}
else if (b instanceof BasisBladeExpr) {
if (a === b) {
return this.g(a, b);
}
else {
return new MulExpr(a, b);
}
}
else if (b instanceof ScalarExpr) {
return new MulExpr(b, a, true);
}
else {
return new MulExpr(a, b);
}
}
else {
return new MulExpr(a, b);
}
}
toPrefix() {
return `*(${this.lhs.toPrefix()}, ${this.rhs.toPrefix()})`;
}
toString() {
return `${this.lhs} * ${this.rhs}`;
}
}
/**
* A blade is the outer (wedge) product of a list of vectors.
* An empty list of vectors corresponds to the unit scalar.
*/
export class BasisBladeExpr extends Expr {
constructor(g: Metric, public vectors: number[], public dirty = false) {
super(g, 'BasisBladeExpr');
}
isChanged(): boolean {
return this.dirty;
}
copy(dirty: boolean): Expr {
return new BasisBladeExpr(this.g, this.vectors, dirty);
}
reset(): Expr {
if (this.dirty) {
return this.copy(false);
}
else {
return this;
}
}
simplify(): Expr {
// console.log(`${this.type}, ${this.dirty}`)
return this;
}
toPrefix(): string {
if (this.vectors.length > 0) {
return this.vectors.map((i) => `e${i + 1}`).join(' ^ ');
}
else {
return "1";
}
}
toString(): string {
if (this.vectors.length > 0) {
return this.vectors.map((i) => `e${i + 1}`).join(' ^ ');
}
else {
return "1";
}
}
}
export class ScalarExpr extends Expr {
constructor(g: Metric, public value: number | string, public dirty = false) {
super(g, 'ScalarExpr');
}
isChanged(): boolean {
return false;
}
copy(dirty: boolean): Expr {
return new ScalarExpr(this.g, this.value, dirty);
}
reset(): Expr {
if (this.dirty) {
return this.copy(false);
}
else {
return this;
}
}
simplify(): Expr {
return this;
}
toPrefix(): string {
return `${this.value}`;
}
toPrefixLong(): string {
return `ScalarExpr('${this.value}')`;
}
toString(): string {
return `${this.value}`;
}
}
export class VBarExpr extends BinaryExpr {
constructor(lhs: Expr, rhs: Expr, public dirty = false) {
super(lhs, rhs, 'VBarExpr');
}
isChanged(): boolean {
return this.dirty || this.lhs.isChanged() || this.rhs.isChanged();
}
reset(): Expr {
return new VBarExpr(this.lhs.reset(), this.rhs.reset());
}
simplify(): Expr {
const a = this.lhs.simplify();
const b = this.rhs.simplify();
if (a instanceof BasisBladeExpr && b instanceof BasisBladeExpr) {
return this.g(a, b);
}
else if (a instanceof AddExpr && b instanceof BasisBladeExpr) {
const aL = a.lhs;
const aR = a.rhs;
return new AddExpr(new VBarExpr(aL, b), new VBarExpr(aR, b), true);
}
else if (a instanceof BasisBladeExpr && b instanceof AddExpr) {
const bL = b.lhs;
const bR = b.rhs;
return new AddExpr(new VBarExpr(a, bL), new VBarExpr(a, bR), true);
}
else if (a instanceof MulExpr && b instanceof Expr) {
const aL = a.lhs;
const aR = a.rhs;
if (aL instanceof ScalarExpr && aR instanceof BasisBladeExpr) {
return new MulExpr(aL, new VBarExpr(aR, b), true);
}
else {
return new VBarExpr(a, b);
}
}
else if (a instanceof BasisBladeExpr && b instanceof MulExpr) {
const bL = b.lhs;
const bR = b.rhs;
if (bL instanceof ScalarExpr && bR instanceof BasisBladeExpr) {
return new MulExpr(bL, new VBarExpr(a, bR), true);
}
else {
return new VBarExpr(a, b);
}
}
else {
return new VBarExpr(a, b);
}
}
toPrefix(): string {
return `scp(${this.lhs.toPrefix()}, ${this.rhs.toPrefix()})`;
}
toString() {
return `${this.lhs} | ${this.rhs}`;
}
}
export class WedgeExpr extends BinaryExpr {
constructor(lhs: Expr, rhs: Expr, public dirty = false) {
super(lhs, rhs, 'WedgeExpr');
}
isChanged(): boolean {
return this.dirty || this.lhs.isChanged() || this.rhs.isChanged();
}
reset(): Expr {
return new WedgeExpr(this.lhs.reset(), this.rhs.reset());
}
simplify(): Expr {
const a = this.lhs.simplify();
const b = this.rhs.simplify();
if (a instanceof ScalarExpr) {
if (b instanceof ScalarExpr) {
return new ScalarExpr(this.g, 0, true);
}
else if (typeof a.value === 'number' && a.value === 1) {
// return new WedgeExpr(a, b);
// return a.copy(true)
return b.copy(true);
}
else {
return new WedgeExpr(a, b);
}
}
else if (b instanceof ScalarExpr) {
if (a instanceof ScalarExpr) {
return new ScalarExpr(this.g, 0, true);
}
else if (typeof b.value === 'number' && b.value === 1) {
if (a.hasOwnProperty('copy')) {
return a.copy(true);
}
else {
if (a instanceof BasisBladeExpr) {
console.log(`a ${a.type} does not have copy property (I) ${Object.keys(a)}`);
// return new BasisBladeExpr(this.g, a.vectors, true);
return new WedgeExpr(a, b);
}
else {
console.log(`a ${a.type} does not have copy property (II)`);
return new WedgeExpr(a, b);
}
}
// return a.copy(true)
// return a.copy(true);
}
else {
return new WedgeExpr(a, b);
}
}
else if (a instanceof BasisBladeExpr && b instanceof BasisBladeExpr) {
return new BasisBladeExpr(this.g, wedge(a.vectors, b.vectors), true);
}
else if (a instanceof BasisBladeExpr && b instanceof BasisBladeExpr) {
if (a === b) {
return new ScalarExpr(this.g, 0, true);
}
else {
return new AddExpr(new MulExpr(a, b), new MulExpr(new ScalarExpr(this.g, -1), new VBarExpr(a, b)), true);
}
}
else {
return new WedgeExpr(a, b);
}
}
toPrefix() {
return `^(${this.lhs}, ${this.rhs})`;
}
toString() {
return `${this.lhs} ^ ${this.rhs}`;
}
}
export default class Algebra {
basis: { [index: number]: Expr } = {};
index: { [name: string]: number } = {};
private metric: Metric;
constructor(names: string[], g: number[][]) {
this.metric = (u: BasisBladeExpr, v: BasisBladeExpr): ScalarExpr => {
const i = u.vectors[0];
const j = v.vectors[0];
return new ScalarExpr(this.metric, g[i][j]);
};
// Insert the basis blade corresponding to unity.
this.basis[0] = new BasisBladeExpr(this.metric, []);
this.index['1'] = 0;
// Insert the basis blades corresponding to the basis vectors.
for (let i = 0; i < names.length; i++) {
const name = names[i];
const index = Math.pow(2, i);
this.basis[index] = new BasisBladeExpr(this.metric, [i]);
this.index[name] = index;
}
}
scalar(value: number | string): Expr {
return new ScalarExpr(this.metric, value);
}
simplify(expr: Expr): Expr {
if (expr instanceof Expr) {
debugger;
expr = expr.reset();
expr = expr.simplify();
while (expr.isChanged()) {
console.log("simplify")
expr = expr.simplify();
}
return expr;
}
else {
throw new Error("expr must be an Expr");
}
}
}
import GeoCAS from './GeoCAS';
export default function() {
const ga = new GeoCAS(['i','j', 'k'], [[1,0,0],[0,1,0],[0,0,1]]);
const s5 = ga.scalar(5);
const e1 = ga.basis[0]
const e2 = ga.basis[1]
const e3 = ga.basis[2]
describe("scalar", function() {
it("toPrefix should be correct", function() {
expect(s5.toPrefix()).toBe("5")
})
it("toString should be correct", function() {
expect(s5.toString()).toBe("5")
})
})
describe("basis", function() {
describe("vector", function() {
it("toPrefix should be correct", function() {
expect(e1.toPrefix()).toBe("i")
expect(e2.toPrefix()).toBe("j")
expect(e3.toPrefix()).toBe("k")
})
it("toString should be defined", function() {
expect(e1.toString()).toBe("i")
expect(e2.toString()).toBe("j")
expect(e3.toString()).toBe("k")
})
})
})
describe("+", function() {
describe("(vector, vector)", function() {
const M = e1 + e2
it("toPrefix should be defined", function() {
expect(M.toPrefix()).toBe("+(i, j)")
})
it("toString should be defined", function() {
expect(M.toString()).toBe("i + j")
})
})
})
describe("^", function() {
const M = e1 ^ e2
it("toPrefix should be defined", function() {
expect(M.toPrefix()).toBe("^(i, j)")
})
it("toString should be defined", function() {
expect(M.toString()).toBe("i ^ j")
})
})
describe("*", function() {
describe("(scalar, vector)", function() {
const M = 5 * e1
it("toPrefix should be defined", function() {
expect(M.toPrefix()).toBe("*(5, i)")
})
it("toString should be defined", function() {
expect(M.toString()).toBe("5 * i")
})
})
describe("(vector, vector)", function() {
const M = e1 * e2
it("toPrefix should be defined", function() {
expect(M.toPrefix()).toBe("*(i, j)")
})
it("toString should be defined", function() {
expect(M.toString()).toBe("i * j")
})
})
})
}
interface Metric {
(u: VectorExpr, v: VectorExpr): ScalarExpr
}
export class Expr {
constructor(public g: Metric, public type: string) {
}
get changed(): boolean {
throw new Error(`${this.type}.changed is not implemented.`)
}
copy(dirty: boolean): Expr {
throw new Error(`${this.type}.copy is not implemented.`)
}
reset(): Expr {
throw new Error(`${this.type}.reset is not implemented.`)
}
simplify(): Expr {
throw new Error(`${this.type}.simplify is not implemented.`)
}
toPrefix(): string {
throw new Error(`${this.type}.toPrefix is not implemented.`)
}
toString(): string {
throw new Error(`${this.type}.toString is not implemented.`)
}
__add__(rhs: Expr) {
throw new Error(`${this.type}.__add__ is not implemented.`)
}
__radd__(rhs: Expr) {
throw new Error(`${this.type}.__radd__ is not implemented.`)
}
__mul__(rhs: Expr) {
throw new Error(`${this.type}.__mul__ is not implemented.`)
}
__rmul__(rhs: Expr) {
throw new Error(`${this.type}.__rmul__ is not implemented.`)
}
__vbar__(rhs: Expr) {
throw new Error(`${this.type}.__vbar__ is not implemented.`)
}
__rvbar__(rhs: Expr) {
throw new Error(`${this.type}.__rvbar__ is not implemented.`)
}
}
class UnaryExpr extends Expr {
constructor(g: Metric, type: string) {
super(g, type);
}
__add__(rhs: Expr| number): Expr {
if (rhs instanceof Expr) {
return new AddExpr(this, rhs)
}
else if (typeof rhs === 'number') {
return new AddExpr(this, new ScalarExpr(this.g, rhs))
}
else {
return void 0
}
}
__radd__(lhs: Expr) {
if (lhs instanceof Expr) {
return new AddExpr(lhs, this)
}
else {
return void 0;
}
}
}
class BinaryExpr extends Expr {
constructor(public lhs: Expr, public rhs: Expr, type: string) {
super(lhs.g, type);
if (!(lhs instanceof Expr)) {
throw new Error(`${type}.lhs must be an Expr: ${typeof lhs}`);
}
if (!(rhs instanceof Expr)) {
throw new Error(`${type}.rhs must be an Expr: ${typeof rhs}`);
}
}
__add__(rhs: Expr): Expr {
if (rhs instanceof Expr) {
return new AddExpr(this, rhs)
}
else {
return void 0;
}
}
__radd__(lhs: Expr | number) {
if (lhs instanceof Expr) {
return new AddExpr(lhs, this)
}
else if (typeof lhs === 'number') {
return new AddExpr(new ScalarExpr(this.g, lhs), this)
}
else {
return void 0;
}
}
__mul__(rhs: Expr | number) {
if (rhs instanceof Expr) {
return new MulExpr(this, rhs)
}
else if (typeof rhs === 'number') {
return new MulExpr(this, new ScalarExpr(this.g, rhs))
}
else {
return void 0;
}
}
__rmul__(lhs: Expr | number) {
if (lhs instanceof Expr) {
return new MulExpr(lhs, this)
}
else if (typeof lhs === 'number') {
return new MulExpr(new ScalarExpr(this.g, lhs), this)
}
else {
return void 0;
}
}
__vbar__(rhs: Expr) {
if (rhs instanceof Expr) {
return new VBarExpr(this, rhs)
}
else {
return void 0;
}
}
}
class AddExpr extends BinaryExpr {
constructor(lhs: Expr, rhs: Expr, public dirty = false) {
super(lhs, rhs, 'AddExpr');
}
__add__(rhs: Expr) {
if (rhs instanceof Expr) {
return new AddExpr(this, rhs)
}
else {
return void 0;
}
}
__vbar__(rhs: Expr) {
if (rhs instanceof Expr) {
return new VBarExpr(this, rhs)
}
else {
return void 0;
}
}
get changed(): boolean {
return this.dirty || this.lhs.changed || this.rhs.changed;
}
copy(dirty: boolean): Expr {
return new AddExpr(this.lhs, this.rhs, dirty);
}
reset(): Expr {
return new AddExpr(this.lhs.reset(), this.rhs.reset())
}
simplify(): Expr {
const a = this.lhs.simplify();
const b = this.rhs.simplify();
if (b instanceof ScalarExpr && typeof b.value === 'number' && b.value === 0) {
return a.copy(true);
}
else if (a instanceof MulExpr && b instanceof MulExpr) {
if (a.lhs instanceof ScalarExpr && b.lhs instanceof ScalarExpr && a.rhs === b.rhs) {
const sa: ScalarExpr = <ScalarExpr>a.lhs;
const sb: ScalarExpr = <ScalarExpr>b.lhs;
const s = new ScalarExpr(this.g, sa.value + sb.value);
return new MulExpr(s, a.rhs, true);
}
else {
return new AddExpr(a, b);
}
}
else if (a instanceof ScalarExpr && b instanceof ScalarExpr) {
if (typeof a.value === 'number' && typeof b.value === 'number') {
return new ScalarExpr(this.g, a.value + b.value, true);
}
else {
return new AddExpr(a, b);
}
}
else {
return new AddExpr(a, b);
}
}
toPrefix(): string {
return `+(${this.lhs.toPrefix()}, ${this.rhs.toPrefix()})`;
}
toString() {
return `${this.lhs} + ${this.rhs}`;
}
}
class MulExpr extends BinaryExpr {
constructor(lhs: Expr, rhs: Expr, public dirty = false) {
super(lhs, rhs, 'MultiplyExpr');
}
get changed(): boolean {
return this.dirty || this.lhs.changed || this.rhs.changed;
}
copy(dirty: boolean): Expr {
return new MulExpr(this.lhs, this.rhs, dirty);
}
reset(): Expr {
return new MulExpr(this.lhs.reset(), this.rhs.reset())
}
simplify(): Expr {
const a = this.lhs.simplify();
const b = this.rhs.simplify();
if (a instanceof ScalarExpr) {
if (typeof a.value === 'number' && a.value === 0) {
return a.copy(true);
}
else if (typeof a.value === 'number' && a.value === 1) {
return b.copy(true);
}
else if (b instanceof ScalarExpr) {
if (typeof a.value === 'number' && a.value === 1) {
return b;
}
else if (typeof a.value === 'number' && typeof b.value === 'number') {
return new ScalarExpr(this.g, a.value * b.value, true);
}
else if (typeof a.value !== 'number' && typeof b.value === 'number') {
return new MulExpr(b, a, true);
}
else {
return new MulExpr(a, b);
}
}
else {
return new MulExpr(a, b);
}
}
else if (a instanceof VectorExpr) {
if (b instanceof MulExpr) {
const bL = b.lhs;
const bR = b.rhs;
if (bL instanceof VectorExpr) {
if (a.name === bL.name) {
return new MulExpr(new MulExpr(a, bL), bR, true);
}
else {
return new MulExpr(a, b);
}
}
else {
return new MulExpr(a, b);
}
}
else if (b instanceof VectorExpr) {
if (a === b) {
return this.g(a, b);
}
else {
return new MulExpr(a, b);
}
}
else if (b instanceof ScalarExpr) {
return new MulExpr(b, a, true);
}
else {
return new MulExpr(a, b);
}
}
else {
return new MulExpr(a, b);
}
}
toPrefix() {
return `*(${this.lhs.toPrefix()}, ${this.rhs.toPrefix()})`;
}
toString() {
return `${this.lhs} * ${this.rhs}`;
}
}
class ScalarExpr extends UnaryExpr {
constructor(g: Metric, public value: number | string, public dirty = false) {
super(g, 'ScalarExpr');
}
get changed(): boolean {
return false;
}
copy(dirty: boolean): Expr {
return new ScalarExpr(this.g, this.value, dirty);
}
reset(): Expr {
if (this.dirty) {
return new ScalarExpr(this.g, this.value);
}
else {
return this;
}
}
simplify(): Expr {
return this;
}
toPrefix(): string {
return `${this.value}`;
}
toPrefixLong(): string {
return `ScalarExpr('${this.value}')`;
}
toString(): string {
return `${this.value}`;
}
__mul__(rhs: Expr| number): Expr {
if (rhs instanceof Expr) {
return new MulExpr(this, rhs)
}
else if (typeof rhs === 'number') {
return new MulExpr(this, new ScalarExpr(this.g, rhs))
}
else {
return void 0
}
}
__rmul__(lhs: Expr| number): Expr {
if (lhs instanceof Expr) {
return new MulExpr(lhs, this)
}
else if (typeof lhs === 'number') {
return new MulExpr(new ScalarExpr(this.g, lhs), this)
}
else {
return void 0
}
}
}
class VBarExpr extends BinaryExpr {
constructor(lhs: Expr, rhs: Expr, public dirty = false) {
super(lhs, rhs, 'VBarExpr');
}
get changed(): boolean {
return this.dirty || this.lhs.changed || this.rhs.changed;
}
reset(): Expr {
return new VBarExpr(this.lhs.reset(), this.rhs.reset())
}
simplify(): Expr {
const a = this.lhs.simplify();
const b = this.rhs.simplify();
if (a instanceof VectorExpr && b instanceof VectorExpr) {
return this.g(a, b);
}
else if (a instanceof AddExpr && b instanceof VectorExpr) {
const aL = a.lhs;
const aR = a.rhs;
return new AddExpr(new VBarExpr(aL, b), new VBarExpr(aR, b), true);
}
else if (a instanceof VectorExpr && b instanceof AddExpr) {
const bL = b.lhs;
const bR = b.rhs;
return new AddExpr(new VBarExpr(a, bL), new VBarExpr(a, bR), true);
}
else if (a instanceof MulExpr && b instanceof Expr) {
const aL = a.lhs;
const aR = a.rhs;
if (aL instanceof ScalarExpr && aR instanceof VectorExpr) {
return new MulExpr(aL, new VBarExpr(aR, b), true);
}
else {
return new VBarExpr(a, b);
}
}
else if (a instanceof VectorExpr && b instanceof MulExpr) {
const bL = b.lhs;
const bR = b.rhs;
if (bL instanceof ScalarExpr && bR instanceof VectorExpr) {
return new MulExpr(bL, new VBarExpr(a, bR), true);
}
else {
return new VBarExpr(a, b);
}
}
else {
return new VBarExpr(a, b);
}
}
toPrefix(): string {
return `scp(${this.lhs.toPrefix()}, ${this.rhs.toPrefix()})`;
}
toString() {
return `${this.lhs} | ${this.rhs}`;
}
}
class VectorExpr extends UnaryExpr {
constructor(g: Metric, public name: string, public dirty = false) {
super(g, 'VectorExpr');
}
__mul__(rhs: number | Expr) {
if (rhs instanceof Expr) {
return new MulExpr(this, rhs)
}
else if (typeof rhs === 'number') {
return new MulExpr(this, new ScalarExpr(this.g, rhs))
}
else {
return void 0;
}
}
__rmul__(lhs: number | Expr) {
if (lhs instanceof Expr) {
return new MulExpr(lhs, this)
}
else if (typeof lhs === 'number') {
return new MulExpr(new ScalarExpr(this.g, lhs), this)
}
else {
return void 0;
}
}
__vbar__(rhs: Expr) {
if (rhs instanceof Expr) {
return new VBarExpr(this, rhs)
}
else {
return void 0;
}
}
__wedge__(rhs: Expr) {
if (rhs instanceof Expr) {
return new WedgeExpr(this, rhs)
}
else {
return void 0;
}
}
get changed(): boolean {
return false;
}
copy(dirty: boolean): Expr {
return new VectorExpr(this.g, this.name, dirty);
}
reset(): Expr {
return this;
}
simplify(): Expr {
return this;
}
toPrefix() {
return `${this.name}`;
}
toPrefixLong() {
return `VectorExpr('${this.name}')`;
}
toString() {
return this.name;
}
}
class WedgeExpr extends BinaryExpr {
constructor(lhs: Expr, rhs: Expr, public dirty = false) {
super(lhs, rhs, 'WedgeExpr');
}
get changed(): boolean {
return this.dirty || this.lhs.changed || this.rhs.changed;
}
reset(): Expr {
return new WedgeExpr(this.lhs.reset(), this.rhs.reset())
}
simplify(): Expr {
const a = this.lhs.simplify();
const b = this.rhs.simplify();
if (a instanceof VectorExpr && b instanceof VectorExpr) {
if (a === b) {
return new ScalarExpr(this.g, 0, true);
}
else {
return new AddExpr(new MulExpr(a, b), new MulExpr(new ScalarExpr(this.g, -1), new VBarExpr(a, b)), true);
}
}
else {
return new WedgeExpr(a, b);
}
}
toPrefix() {
return `^(${this.lhs}, ${this.rhs})`;
}
toString() {
return `${this.lhs} ^ ${this.rhs}`;
}
}
export default class GeoCAS {
basis: {[index: number]: Expr} = {};
index: {[name: string]: number} = {};
private metric: Metric;
constructor(names: string[], g: number[][]) {
this.metric = (u: VectorExpr, v: VectorExpr): ScalarExpr => {
const i = this.index[u.name];
const j = this.index[v.name];
return new ScalarExpr(this.metric, g[i][j]);
}
for (let i = 0; i < names.length; i++) {
const name = names[i];
this.basis[i] = new VectorExpr(this.metric, name);
this.index[name] = i;
}
}
scalar(value: number | string): Expr {
return new ScalarExpr(this.metric, value);
}
simplify(expr: Expr): Expr {
if (expr instanceof Expr) {
expr = expr.reset();
expr = expr.simplify();
while(expr.changed) {
expr = expr.simplify();
}
return expr;
}
else {
throw new Error("expr must be an Expr")
}
}
}
<!DOCTYPE html>
<html>
<head>
<!-- STYLES-MARKER -->
<style>
/* STYLE-MARKER */
</style>
<script src='https://jspm.io/system.js'></script>
<!-- SHADERS-MARKER -->
<!-- SCRIPTS-MARKER -->
</head>
<body>
<pre id='info'></pre>
<script>
// CODE-MARKER
</script>
<script>
System.import('./index.js')
</script>
</body>
</html>
const ga = new GeoCAS.Algebra([[1,0,0],[0,1,0],[0,0,1]],['i','j','k']);
const one = ga.basis[0]
const e1 = ga.basis[1]
const e2 = ga.basis[2]
const e12 = ga.basis[3]
const e3 = ga.basis[4]
const x = ga.scalar('x')
const y = ga.scalar('y')
const z = ga.scalar('z')
const zero = ga.scalar(0)
const X = x * e1 + y * e2 + z * e3
simplify(e1 * e2 * 2)
/**
* Print the HTML string without a line ending.
*/
export function print(html: string): void {
const element = document.getElementById('info');
element.innerHTML = element.innerHTML + html;
}
/**
* Print the HTML string and go to the next line.
*/
export function println(html: string): void {
print(html + '\n');
}
/**
* Print the value of a variable, coerced to string
*/
export function printvar(name: string, value): void {
println('<b>' + name + '</b> => ' + value);
}
export function infix(name: string, expr: GeoCAS.Expr): void {
println('<b>' + name + '</b> => ' + expr.toString());
}
export function prefix(name: string, expr: GeoCAS.Expr): void {
println('<b>' + name + '</b> => ' + expr.toPrefix());
}
export function simplify(expr: GeoCAS.Expr): void {
try {
const simple = ga.simplify(expr)
println('<b>' + expr.toString() + '</b> => ' + simple.toString());
}
catch(e) {
if (expr) {
println('<b>' + expr.toString() + '</b> => ' + e);
}
else {
println(e);
}
}
}
{
"description": "GeoCAS",
"dependencies": {
"DomReady": "1.0.0",
"jasmine": "3.4.0"
},
"name": "",
"version": "",
"operatorOverloading": true,
"author": "David Geo Holmes",
"keywords": [
"CAS",
"Geometric",
"Algebra",
"Symbolic"
]
}
body {
background-color: #000000;
}
#info {
position: absolute;
left: 20px;
top: 20px;
font-size: 26px;
color: #00FF00;
}
<!DOCTYPE html>
<html>
<head>
<!-- STYLES-MARKER -->
<style>
/* STYLE-MARKER */
</style>
<script src='https://jspm.io/system.js'></script>
<!-- SCRIPTS-MARKER -->
</head>
<body>
<script>
// CODE-MARKER
</script>
<script>
System.import('./tests.js')
</script>
</body>
</html>
import GeoCAS from './GeoCAS.spec'
window['jasmine'] = jasmineRequire.core(jasmineRequire)
jasmineRequire.html(window['jasmine'])
const env = jasmine.getEnv()
const jasmineInterface = jasmineRequire.interface(window['jasmine'], env)
extend(window, jasmineInterface)
const htmlReporter = new jasmine.HtmlReporter({
env: env,
getContainer: function() { return document.body },
createElement: function() { return document.createElement.apply(document, arguments) },
createTextNode: function() { return document.createTextNode.apply(document, arguments) },
timer: new jasmine.Timer()
})
env.addReporter(htmlReporter)
DomReady.ready(function() {
htmlReporter.initialize()
describe("GeoCAS", GeoCAS)
env.execute()
})
/*
* Helper function for extending the properties on objects.
*/
export default function extend<T>(destination: T, source: any): T {
for (let property in source) {
destination[property] = source[property]
}
return destination
}
{
"allowJs": true,
"checkJs": true,
"declaration": true,
"emitDecoratorMetadata": true,
"experimentalDecorators": true,
"jsx": "react",
"module": "system",
"noImplicitAny": true,
"noImplicitReturns": true,
"noImplicitThis": true,
"noUnusedLocals": true,
"noUnusedParameters": true,
"preserveConstEnums": true,
"removeComments": false,
"skipLibCheck": true,
"sourceMap": true,
"strictNullChecks": true,
"suppressImplicitAnyIndexErrors": true,
"target": "es5",
"traceResolution": true
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment