diff --git a/environments/javascript/@orbitmines/rays/src/Ray.ts b/environments/javascript/@orbitmines/rays/src/Ray.ts index 63dcad3..f04db39 100644 --- a/environments/javascript/@orbitmines/rays/src/Ray.ts +++ b/environments/javascript/@orbitmines/rays/src/Ray.ts @@ -1,615 +1,615 @@ -// import JS, {NotImplementedError} from "@orbitmines/js"; -// -// namespace Ray { -// -// export type Any = -// { -// /** Ray is a function (.next) */ -// (...other: JS.Recursive): Ray.Any, -// -// /** Ray is a constructor - TODO: Copy? */ -// new (...other: JS.Recursive): Ray.Any, -// } -// /** JavaScript runtime conversions. */ -// & Symbol -// & Iterable -// & AsyncIterable -// -// & Pick -// -// /** Preconfigured functions defined for Rays. */ -// & { -// -readonly [TKey in keyof typeof Ray.Function.All]: typeof Ray.Function.All[TKey] extends Ray.Any -// ? Ray.Any -// : never; -// } -// -// /** Storage/Movement operations which need to be implemented. */ -// & { [TKey in keyof Ray.Op.Impl]: Ray.Any } -// -// export namespace Debug { -// export type Event = { event: string, self: Ray.Any, context: any }; -// export type Listener = (event: Event) => void; -// } -// -// /** -// * An uninitialized empty Ray, which caches itself once initialized. -// */ -// // export const None: Ray.FunctionConstructor = Ray.Function.CachedAfterUse(Ray.New); -// -// // export class Object { -// // -// // // TODO: Could copy? -// // get initial(): Ray.Any { return this._initial(); } set initial(initial: Ray.Any) { this._initial = initial; } protected _initial: Ray.Any; -// // get self(): Ray.Any { return this._self(); } set self(self: Ray.Any) { this._self = self; } protected _self: Ray.Any; -// // get terminal(): Ray.Any { return this._terminal(); } set terminal(terminal: Ray.Any) { this._terminal = terminal; } protected _terminal: Ray.Any; -// // -// // protected constructor({ initial, self, terminal }: { initial?: Ray.Any, self?: Ray.Any, terminal?: Ray.Any } = {}) { -// // -// // this._initial = initial ?? Ray.none.memoized; -// // this._self = self ?? Ray.self_reference; -// // this._terminal = terminal ?? Ray.none.memoized; -// // } -// // } -// -// /** A simplistic compiler for Ray */ -// export namespace Compiler { // TODO Ray is Compiler -// -// /** -// * In the case of Rays, whether something is a vertex/initial/terminal is only inferred from surrounding context. And these checks only need to happen locally in order to decide how to traverse arbitrary structure (as in - I only need to check the presence of something next to me, not traverse the whole direction recursively in order to decide what to do). -// * -// * How about treating something like something which the context says it's not? (Could apply this sort of thing in some fidelity/consistency checking mechanism as a way of fuzzing the fidelity mechanism) -// * -// * TODO: Compiler could have things like other composed rays which tell it cares about the other (even if that's correct or not??) -// * -// * -// * TODO: Do I want to keep the is_equiv/is_composed pattern? Or simplify to one of the two? -// * -// * // TODO: NEVER DIRECTLY EXECUTE, ONLY AFTER CHAIN OF FUNCS, possibly arbitrarily LAZY -// * -// * TODO -// * - Compose empty as first element? Disregard none to first elemn? Or not?? -// * -// * TODO; Impl -// * - Generally, return something which knows where all continuations are. -// * - Generator/step function/... ; with no assumption of halting, but allow to hook into any step. -// * - Cache: -// * - Control of (non-/)lazyness of functions -// * - None as, first time called, memoize func. -// * - Perhaps locally cache (for stuff like count?) - no way to ensure globally coherence -// * - a.orbit() -> a.orbit(a) -// * // TODO: Should be automatic? is_orbit() or any method without arguments is a.is_orbit(a.self()) ?? not a.is_orbit(a) ; ??? -// * -// * - .initial/.terminal can be seen as a particular connection on .self, which .self ignores? -// * -// * TODO: Allow hooking -// * -// * TODO: Testing -// * - Test if references hold after equivalence/composition... -// * -// * TODO: After initial demo: -// * - Allow mapping/finding of other implementations regarding some equiv funcs (like different ways of implementing using NAND etc...) -// * -// * Arbitrary. -// * -// * TODO Pass a single ray to the runtime, and run that way? Access runtime as variable from ray? That would have programs accessing possible runtimes? -// */ -// } -// -// export type Constructor = { proxy?: ProxyHandler, debug?: Debug.Listener, } -// -// export class Instance extends JS.Class.Instance { -// -// listeners: Debug.Listener[]; -// -// constructor({ -// proxy, -// debug, -// }: Ray.Constructor = {}) { -// super({ proxy: proxy ?? Ray.ProxyHandlers.Default }); -// -// this.listeners = debug ? [debug] : []; -// } -// -// /** Used to jump out of the proxy. */ -// get ___instance(): Instance { return this; } -// -// debug = ( -// on: Debug.Listener, -// func: JS.ParameterlessFunction -// ): T => { -// this.listeners.push(on); -// const ret = func(); -// this.listeners.pop(); // TODO? -// -// return ret; -// } -// -// /** Simple debug/traversal mechanism. */ -// on = (event: Omit) => { -// if (!this.listeners) return; -// -// this.listeners.forEach(listener => listener({ ...event, self: this.proxy })); -// } -// -// } -// -// -// export namespace ProxyHandlers { -// -// export const Default: ProxyHandler = JS.Class.Handler({ -// /** ray.property */ -// get: (self: Ray.Any, instance: Ray.Instance, property: string | symbol): any => { -// instance.on({ event: 'PROXY.GET', context: { property } }); -// -// /** Use any field on {Ray.Instance}, which we want to delegate to, first. */ -// if (property === '___instance' || property === 'debug' || property === 'on') { return instance[property]; } -// -// /** Otherwise, switch to functions defined on {Ray.Functions} */ -// const func = Ray.Function.Get(property as any); -// if (func) { return func.as_method({ self, property }); } -// -// if (property === Symbol.toPrimitive) -// return (hint: string) => { return 100; }; // TODO: Can be used to setup label generation through javascript objects if we want to ? + allow search on this -// // throw new NotImplementedError(``); -// -// /** Not implemented. */ -// throw new NotImplementedError(`Called property '${String(property)}' on Ray, which has not been implemented.`); -// }, -// -// /** ray.property = something; */ -// set: (self: Ray.Any, instance: Ray.Instance, property: string | symbol, value: any): boolean => { -// instance.on({ event: 'PROXY.SET', context: { property, value } }); -// -// throw new NotImplementedError(); -// }, -// -// /** delete ray.property; */ -// deleteProperty: (self: Ray.Any, instance: Ray.Instance, property: string | symbol): boolean => { -// instance.on({ event: 'PROXY.DELETE', context: { property } }); -// -// throw new NotImplementedError(); -// }, -// -// /** ray() is called. */ -// apply: (self: Ray.Any, instance: Ray.Instance, args: any[]): any => { -// instance.on({ event: 'PROXY.APPLY', context: { args } }); -// -// throw new NotImplementedError(`${self}`); -// }, -// -// /** new ray() */ -// construct: (self: Ray.Any, instance: Ray.Instance, args: any[]): object => { -// instance.on({ event: 'PROXY.NEW', context: { args } }); -// -// throw new NotImplementedError(`${args.length} ${self}`); -// }, -// -// /** property in ray; */ -// has: (self: Ray.Any, instance: Ray.Instance, property: string | symbol): boolean => { -// instance.on({ event: 'PROXY.HAS', context: { property } }); -// -// throw new NotImplementedError(`${String(property)}`); -// }, -// }); -// } -// -// /** Ray is an Enum(eration) */ -// export namespace Enum { -// -// export type Type> = { -// [TKey in T[number]]: Ray.Any -// } -// -// export const Impl = >(...enumeration: T): Type => { -// return Object.fromEntries(enumeration.map(value => -// [value, Ray.Function.None.Impl((): Ray.Any => { -// throw new NotImplementedError(); // TODO -// })] -// )) as Type; -// // TODO: ONE OF 4 SELECTION RAY for the case of type. -// } -// -// } -// -// /** Ray is a function (.next) */ -// export namespace Function { -// -// /** {T} is just an example/desired use case. But it generalizes to any function. */ -// export type Type = T | Ray.Any; -// -// /** From which perspective the Function is implemented. */ -// enum Perspective { -// None, Self, /** Ref, */ -// } -// -// // /** -// // * Implement a function from the perspective of 'this' for 'this.self'. -// // */ -// // // static Ref = (impl: (ref: Ray.Any) => TResult): Function => Ray.Function.Self(self => impl(self.as_reference())); -// -// /** Implement a function from no (or: an ignorant) perspective. */ -// export namespace None { -// -// export const Impl = (impl: Op.Zeroary.Type): Ray.Any => { -// throw new NotImplementedError(); -// // return new Ray.Any({ perspective: Perspective.None, impl }); -// } -// -// } -// -// /** Implement a function from the perspective of 'this' */ -// export namespace Self { -// export const Impl = (impl: Op.Unary.Type): Ray.Any => { -// throw new NotImplementedError(); -// // return new Ray.Any({ perspective: Perspective.Self, impl }); -// } -// -// export const Binary = (impl: Op.Binary.Type): Ray.Any => { -// throw new NotImplementedError(); -// // return new Ray.Any({ perspective: Perspective.Self, impl }); // TODO: Good way to deal with arity -// } -// -// // export const If = (impl: Op.Unary.Type): Ray.Any => { -// // return Impl(impl); // TODO: GENERIC collapse to boolean implemented and overridable -// // } -// // TODO: GENERIC collapse to boolean implemented and overridable -// -// /** -// * TODO: -// * - Maybe replace switch with 'zip'?, What are the practical differences? -// */ -// // export const Match = (cases: MatchCases): Ray.Any => { -// // return Impl(self => self); // TODO -// // } -// } -// } -// -// export namespace Op { -// -// /** -// * TODO: readonly setup, where only traversal ops are allowed. Of course these are writing in some sense, bit those writings aren't directly accessible from this perspective -// * -// */ -// -// export type Impl = -// { [TKey in keyof (typeof Op.Zeroary.All)]: Op.Zeroary.Type } -// & { [TKey in keyof (typeof Op.Unary.All)]: Op.Unary.Type } -// & { [TKey in keyof (typeof Op.Binary.All)]: Op.Binary.Type } -// -// export const all = (ops: TOps): { -// [TOP in keyof TOps]: Ray.Any -// } => ({}); -// -// export namespace Zeroary { -// export type Type = () => T; -// export type Any = (keyof typeof Op.Zeroary.All) | Type; -// -// // TODO: set = none; -// // TODO: Destroy the current thing, connect .initial & .terminal ? (can do just direct connection, preserves 'could have been something here') - then something like [self.initial, self, self.terminal].pop(). -// // TODO: Leave behind --] [-- or connect them basically.. -// export const All = Op.all({ -// // @alias('alloc', 'new', 'create', 'initialize') -// none: 'none', -// }); -// } -// export namespace Unary { -// export type Type = (a: T) => T; -// export type Any = (keyof typeof Op.Unary.All) | Type; -// -// export const All = Op.all({ -// -// // @alias('free', 'destroy', 'clear', 'delete', 'pop') -// free: 'free', -// -// /** An arbitrary Ray, defining what continuing in the reverse of this direction is equivalent to. */ -// initial: 'initial', // a.initial -// -// /** -// * An arbitrary Ray, defining what our current position is equivalent to. -// * -// * Moving to the intersecting Ray at `.self` - as a way of going an abstraction layer (lower), and asking what's inside. -// */ -// // @alias('dereference', 'self') -// self: 'self', // a.self -// -// /** An arbitrary Ray, defining what continuing in this direction is equivalent to. */ -// terminal: 'terminal', // a.terminal -// }); -// } -// export namespace Binary { -// export type Type = (a: T, b: T) => T; -// export type Any = (keyof typeof Op.Binary.All) | Type; -// -// export const All = Op.all({ -// initial: 'initial', // a.initial = b -// self: 'self', // a.self = b -// terminal: 'terminal', // a.terminal = b -// -// /** -// * Concretely, we use here "whatever the JavaScript engine run on" as the thing which has power over the equivalence assumption we use to halt programs. - The asymmetry which allows the engine to make a distinction between each object. -// * -// * @see https://orbitmines.com/papers/on-orbits-equivalence-and-inconsistencies#:~:text=And%20there%20we%20have%20it%2C%20an%20infinity%2C%20loop%2C%20...%2C%20orbit%20if%20we%20ignore%20the%20difference. -// */ -// // @alias('is_none') -// is_orbit: 'is_orbit', // a.___instance === b.___instance -// }); -// } -// } -// -// export namespace Function { -// export const Get = ( -// property: TProperty -// ): (typeof Ray.Function.All)[TProperty] | undefined => Ray.Function.All[property as TProperty] ?? undefined; -// -// export namespace All { -// -// /** [ |-?] */ export const is_initial = Ray.Function.Self.Impl( -// self => self.initial().is_none() -// ); -// /** [--|--] */ export const is_vertex = Ray.Function.Self.Impl( -// self => self.is_initial().nor(self.is_terminal()) -// ); -// /** [?-| ] */ export const is_terminal = Ray.Function.Self.Impl( -// self => self.terminal().is_none() -// ); -// /** [ | ] */ export const is_reference = Ray.Function.Self.Impl( -// self => self.is_initial().and(self.is_terminal()) -// ); -// /** [?-| ] or [ |-?] */ export const is_boundary = Ray.Function.Self.Impl( -// self => self.is_initial().xor(self.is_terminal()) -// ); -// -// /** -// * This is basically what breaks the recursive structure. -// * -// * Tries for "global coherence", practically this just means self-reference, were no change is (inconsistently) assumed... -// * -// * --- -// * -// * Another way of interpreting a possible way of implementing it, is no matter how much more detail we would like to ask, the only thing we ever see is the same structure again (if we ignore the difference of us asking about that additional structure, that's still a possible handle on some difference). -// * -// * As a way of saying/.../assuming: I only 'infinitely' assume it's only this structure, "it seems to halt here". Note that this is necessarily an assumption. No guarantee of this can be made. This is necessarily an equivalence, ..., ignorance. -// * -// * @see https://orbitmines.com/papers/on-orbits-equivalence-and-inconsistencies#:~:text=Quite%20similarly%20to%20the%20loops%2C%20I%20could%20be%20ignorant%20of%20additional%20structure%20by%20assuming%20it%27s%20not%20there. -// */ -// // TODO: is none, ref, init terminal as global equiv check kn the structure? as generalization ; yep, is_orbit. -// export const is_none = Ray.Op.Binary.All.is_orbit; -// export const is_some = Ray.Function.Self.Impl( -// self => self.is_none().not() -// ); -// -// /** -// * @see "Continuations as Equivalence (can often be done in parallel - not generally)": https://orbitmines.com/papers/on-orbits-equivalence-and-inconsistencies#:~:text=Constructing%20Continuations%20%2D%20Continuations%20as%20Equivalence -// */ -// // @alias('merge, 'continues_with', 'compose') -// export const compose = Ray.Function.Self.Binary( -// (a, b) => a.terminal().equivalent(b.initial()) -// ); -// -// /** -// * "Composing an terminal & initial boundary" -// * - TODO: Note that an orbit is reversibility. ? -// * - TODO: Could represent this abstraction in another layer what we want to accomplish while the actual search is still taking place. -// * -// * - Like with 'copy' and all concepts: Note that we're only after reversibility after ignoring some difference. -// * -// * @see "Reversibility is necessarily inconsistent": https://orbitmines.com/papers/on-orbits-equivalence-and-inconsistencies#:~:text=Another%20example%20of%20this%20is%20reversibility -// */ -// // @alias('modular', 'modulus', 'orbit', 'circle', 'repeats', 'infinitely') -// export const orbit = Ray.Function.Self.Binary( -// /** -// * - TODO: If we're only doing one end: This already assumes they are connected on the other end. -// * - TODO: should be a connection here, with is_composed ; or "reference.is_equivalent" so that you can drop one of the sides, or both. -// */ -// (a, b) => { -// b.last().compose(a.first()); -// a.first().compose(b.last()); -// -// return a; // ? -// } -// ); -// -// /** -// * Equivalence as "Composition of Ray." -// * -// * NOTE: -// * - An equivalence, is only a local equivalence, no global coherence of it can be guaranteed. And it is expensive work to edge towards global coherence. -// * - Though changes are only applied locally, their effects can be global (Take for instance, the example of adding to one Ray, which changes the perspective of everything connected to that Ray if they were to traverse the connection). -// * -// * @see https://orbitmines.com/papers/on-orbits-equivalence-and-inconsistencies#:~:text=On%20Equivalences%20%26%20Inconsistencies -// */ -// export const equivalent = Ray.Function.Self.Binary( -// (a, b) => { -// // throw new NotImplementedError(); -// -// // TODO: This is close but not quite, use the shifting thing I wrote down a few days ago: (And then use something to break the self-reference) - Either on this side. compose, or outside the definitions -// // This one harder to do in parallel? -// return a.self().compose(b.self()); -// }); -// -// /** -// * If there exists an orbit between A & B anywhere on their equivalency functions - or: their .self - (except for the directions through which we're referencing them) -// * -// * Note the connection between 'is_orbit' vs 'is_equivalence'. They're essentially the same thing, but: -// * - in the case of 'is_equivalence' we directly have access to their difference but are explicitly ignoring them - in the context in which this functionality is called. -// * - in the case of 'is_orbit', we might need to do more complicated things to acknowledge their differences - we don't have direct access to them. -// * -// * TODO: (so dually connected, what if only one is aware??) Or basically just ; the answer in this particular instance is just if either end can find the other only once. Consistency of it defined on a more abstract level... -// * -// * a.self().traverse().is_orbit(b.self().traverse()) -// */ -// // @alias('includes', 'contains') ; (slightly different variants?) -// export const is_equivalent = is_composed.from_perspective_of(self => self.self()); -// // TODO: Either is_equiv or is_composed will likely change?. -// /** -// * a.traverse().is_orbit(b.traverse()) // Basically: does there exist a single connection between the two? -// */ -// export const is_composed = is_orbit.from_perspective_of(self => self.traverse()); // Needs some ref from Ray.Function.Self. -// -// /** -// * "Applying the same thing in a different context" -// * TODO: Somewhat related to Functors? -// */ -// export const from_perspective_of = Ray.Function.Self.Binary((a, b) => { throw new NotImplementedError(); }); -// -// export const traverse = Ray.Function.Self.Impl( -// (a) => { throw new NotImplementedError(); } -// ); -// -// // TODO: .terminal/.initial is self() vs self.not() -// export const next = Ray.Function.Self.Impl((self) => { -// throw new NotImplementedError(); -// return self; -// }); -// export const has_next = Ray.Function.Self.Impl((self) => self.next().is_some()); -// // @alias('end', 'result', 'back', 'output') -// export const last = Ray.Function.Self.Impl((self) => { -// throw new NotImplementedError(); -// return self; -// }); -// -// export const previous = Ray.Function.Self.Impl((self) => self.not().next()); -// // @alias() -// export const has_previous = Ray.Function.Self.Impl((self) => self.not().has_next()); -// // @alias('beginning', 'front') -// export const first = Ray.Function.Self.Impl((self) => self.not().last()); -// -// // @alias('not', 'reverse', 'swap', 'converse', 'negative', 'neg') -// // export const not = Ray.Function.Self.Impl(self => ); -// // self.terminal = self.initial(); -// // self.initial = self.terminal(); -// // self.not().not() = self -// // TODO: What's the difference in context between stepping after not / or not doing so. -// // TODO; OR BINARY? - "It's just always .next from some perspective?" -// // TODO: Level at which "not" is applied. -// export const not = Ray.Function.Self.Binary((a, b) => b); -// -// export const and = Ray.Function.Self.Binary((a, b) => { throw new NotImplementedError(); -// } ); -// export const or = Ray.Function.Self.Binary((a, b) => { -// throw new NotImplementedError(); -// return self; -// }); -// export const xor = Ray.Function.Self.Binary((a, b) => a.xnor(b).not()); -// export const xnor = Ray.Function.Self.Binary((a, b) => a.is_orbit(b)); // TODO: Could be 'is_equivalent' too? -// export const nand = Ray.Function.Self.Binary((a, b) => a.and(b).not()); -// export const nor = Ray.Function.Self.Binary((a, b) => a.or(b).not()); -// -// -// // @alias(`push_{last.alias}`) -// export const push_back = Ray.Function.Self.Binary( -// (a, b) => a.last().compose(b) -// ); -// // @alias(`push_{first.alias}`) -// export const push_front = Ray.Function.Self.Binary( -// (a, b) => b.compose(a.first()) -// ); -// -// /** -// * Performing a copy (realizing it) can be conceptualized as traversing the entire structure. (Where the 'entire structure' means the current instantiation of it - with many ignorances attached) -// * -// * - A problem with a copy, is that in or to be generalizable, it needs to alter all references to the thing it's copying to itself - this cannot be done with certainty. -// * - This copy does not do that. Instead, it is ignorant of other things referencing the thing it's copying. -// * - Additionally, a copy necessarily has some non-redundancy to it: -// * -// * @see "A copy is necessarily inconsistent": https://orbitmines.com/papers/on-orbits-equivalence-and-inconsistencies#:~:text=If%20I%20have%20one%20thing%20and%20I%20make%20a%20perfect%20copy -// */ -// // @alias('clone', 'duplicate') -// export const copy = Ray.Function.Self.Impl( -// // or -// // (self) => self.self.copy.reference() -// (self) => none().self = self.self.copy() -// -// // TODO Relies heavily on the execution layer to copy initial/terminal etc... ; and an is_orbit check before calling copy again. - Then again on the execution layer it can lazily do this copy (by not evaluating (i.e.) traversing everywhere), or it first does this traversing directly. -// ); -// -// /** -// * Placing existing structure on a new Reference, Boundary or Vertex: -// */ -// // TODO: These should allow as_vertex as zero-ary, but that means self = self_reference, not none. ? -// /** -// * Moving `self` to `.self` on an abstraction layer (higher). As a way of being able to describe `self`. -// * -// * TODO: the .reference might need two levels of abstraction higher, one to put it at the .self, another to reference that thing? (Depends a bit on the execution layer) -// */ -// export const as_reference = Ray.Function.Self.Impl( -// self => none().self = self -// ); -// -// // TODO as_reference.as_vertex instead of as_vertex ignorant by default? -// -// export const as_vertex = Ray.Function.Self.Impl((self) => { -// const vertex = Ray.Op.Zeroary.All.none(); -// vertex.initial = self_reference; -// vertex.terminal = self_reference; -// vertex.self = self; // TODO: Like this, ignorant vs non-ignorant? What to do here? -// -// return vertex; -// }); -// export const as_terminal = Ray.Function.Self.Impl((self) => { -// const terminal = Ray.Op.Zeroary.All.none(); -// terminal.initial = self_reference; -// // terminal.terminal = none; -// terminal.self = self; -// -// return terminal; -// }); -// export const as_initial = Ray.Function.Self.Impl((self) => { -// const initial = Ray.Op.Zeroary.All.none(); -// // initial.initial = none; -// initial.terminal = self_reference; -// initial.self = self; -// -// return initial; -// }); -// -// /** -// * Any arbitrary direction, where .not (or reversing the direction) relies on some memory mechanism -// */ -// export const memoized = Ray.Function.Self.Impl( -// self => { throw new NotImplementedError(); } -// ); -// } -// } -// -// /** JavaScript runtime conversions */ -// export const array = (array: T[]): Ray.Any => Ray.iterable(array); -// export const iterable = (iterable: Iterable): Ray.Any => Ray.iterator(iterable[Symbol.iterator]()); -// export const async_iterable = (async_iterable: AsyncIterable): Ray.Any => Ray.async_iterator(async_iterable[Symbol.asyncIterator]()); -// export const iterator = (iterator: Iterator): Ray.Any => { throw new NotImplementedError(); } -// export const async_iterator = (async_iterator: AsyncIterator): Ray.Any => { throw new NotImplementedError(); } -// export const generator = (generator: Generator): Ray.Any => Ray.iterator(generator); -// export const async_generator = (generator: AsyncGenerator): Ray.Any => Ray.async_iterator(generator); -// -// export const number = (number: number) => { throw new NotImplementedError(); } -// -// export const self_reference = Ray.Function.Self.Impl( -// self => self -// ); -// export const none = Ray.Op.Zeroary.All.none; // TODO FOR ALL OPS, ? automatic, or just put them here. -// -// // export const Boundary = { INITIAL: Type.INITIAL, TERMINAL: Type.TERMINAL }; TODO: LIST O -// -// // TODO CAN ALSO BE ZERO-ARY.. -// // @alias('resize', 'size', 'structure', 'length', 'duplicate', 'copy') -> Should be generalized as any kind of structure, but with this thing repeated on it. ; use traversal or ... -// export const size = Ray.Function.Self.Binary( -// (a, size) => { throw new NotImplementedError(); } -// ); -// // @alias('bit') -// export const boolean = size(2); -// -// export const vertex = Ray.Function.Self.Impl( -// (self) => { throw new NotImplementedError(); } -// ); -// export const initial = Ray.Function.Self.Impl( -// (self) => { throw new NotImplementedError(); } -// ); -// export const terminal = Ray.Function.Self.Impl( -// (self) => { throw new NotImplementedError(); } -// ); -// } -// -// export default Ray; \ No newline at end of file +import JS, {NotImplementedError} from "@orbitmines/js"; + +namespace Ray { + + export type Any = + { + /** Ray is a function (.next) */ + (...other: JS.Recursive): Ray.Any, + + /** Ray is a constructor - TODO: Copy? */ + new (...other: JS.Recursive): Ray.Any, + } + /** JavaScript runtime conversions. */ + & Symbol + & Iterable + & AsyncIterable + + & Pick + + /** Preconfigured functions defined for Rays. */ + & { + -readonly [TKey in keyof typeof Ray.Function.All]: typeof Ray.Function.All[TKey] extends Ray.Any + ? Ray.Any + : never; + } + + /** Storage/Movement operations which need to be implemented. */ + & { [TKey in keyof Ray.Op.Impl]: Ray.Any } + + export namespace Debug { + export type Event = { event: string, self: Ray.Any, context: any }; + export type Listener = (event: Event) => void; + } + + /** + * An uninitialized empty Ray, which caches itself once initialized. + */ + // export const None: Ray.FunctionConstructor = Ray.Function.CachedAfterUse(Ray.New); + + // export class Object { + // + // // TODO: Could copy? + // get initial(): Ray.Any { return this._initial(); } set initial(initial: Ray.Any) { this._initial = initial; } protected _initial: Ray.Any; + // get self(): Ray.Any { return this._self(); } set self(self: Ray.Any) { this._self = self; } protected _self: Ray.Any; + // get terminal(): Ray.Any { return this._terminal(); } set terminal(terminal: Ray.Any) { this._terminal = terminal; } protected _terminal: Ray.Any; + // + // protected constructor({ initial, self, terminal }: { initial?: Ray.Any, self?: Ray.Any, terminal?: Ray.Any } = {}) { + // + // this._initial = initial ?? Ray.none.memoized; + // this._self = self ?? Ray.self_reference; + // this._terminal = terminal ?? Ray.none.memoized; + // } + // } + + /** A simplistic compiler for Ray */ + export namespace Compiler { // TODO Ray is Compiler + + /** + * In the case of Rays, whether something is a vertex/initial/terminal is only inferred from surrounding context. And these checks only need to happen locally in order to decide how to traverse arbitrary structure (as in - I only need to check the presence of something next to me, not traverse the whole direction recursively in order to decide what to do). + * + * How about treating something like something which the context says it's not? (Could apply this sort of thing in some fidelity/consistency checking mechanism as a way of fuzzing the fidelity mechanism) + * + * TODO: Compiler could have things like other composed rays which tell it cares about the other (even if that's correct or not??) + * + * + * TODO: Do I want to keep the is_equiv/is_composed pattern? Or simplify to one of the two? + * + * // TODO: NEVER DIRECTLY EXECUTE, ONLY AFTER CHAIN OF FUNCS, possibly arbitrarily LAZY + * + * TODO + * - Compose empty as first element? Disregard none to first elemn? Or not?? + * + * TODO; Impl + * - Generally, return something which knows where all continuations are. + * - Generator/step function/... ; with no assumption of halting, but allow to hook into any step. + * - Cache: + * - Control of (non-/)lazyness of functions + * - None as, first time called, memoize func. + * - Perhaps locally cache (for stuff like count?) - no way to ensure globally coherence + * - a.orbit() -> a.orbit(a) + * // TODO: Should be automatic? is_orbit() or any method without arguments is a.is_orbit(a.self()) ?? not a.is_orbit(a) ; ??? + * + * - .initial/.terminal can be seen as a particular connection on .self, which .self ignores? + * + * TODO: Allow hooking + * + * TODO: Testing + * - Test if references hold after equivalence/composition... + * + * TODO: After initial demo: + * - Allow mapping/finding of other implementations regarding some equiv funcs (like different ways of implementing using NAND etc...) + * + * Arbitrary. + * + * TODO Pass a single ray to the runtime, and run that way? Access runtime as variable from ray? That would have programs accessing possible runtimes? + */ + } + + export type Constructor = { proxy?: ProxyHandler, debug?: Debug.Listener, } + + export class Instance extends JS.Class.Instance { + + listeners: Debug.Listener[]; + + constructor({ + proxy, + debug, + }: Ray.Constructor = {}) { + super({ proxy: proxy ?? Ray.ProxyHandlers.Default }); + + this.listeners = debug ? [debug] : []; + } + + /** Used to jump out of the proxy. */ + get ___instance(): Instance { return this; } + + debug = ( + on: Debug.Listener, + func: JS.ParameterlessFunction + ): T => { + this.listeners.push(on); + const ret = func(); + this.listeners.pop(); // TODO? + + return ret; + } + + /** Simple debug/traversal mechanism. */ + on = (event: Omit) => { + if (!this.listeners) return; + + this.listeners.forEach(listener => listener({ ...event, self: this.proxy })); + } + + } + + + export namespace ProxyHandlers { + + export const Default: ProxyHandler = JS.Class.Handler({ + /** ray.property */ + get: (self: Ray.Any, instance: Ray.Instance, property: string | symbol): any => { + instance.on({ event: 'PROXY.GET', context: { property } }); + + /** Use any field on {Ray.Instance}, which we want to delegate to, first. */ + if (property === '___instance' || property === 'debug' || property === 'on') { return instance[property]; } + + /** Otherwise, switch to functions defined on {Ray.Functions} */ + const func = Ray.Function.Get(property as any); + if (func) { return func.as_method({ self, property }); } + + if (property === Symbol.toPrimitive) + return (hint: string) => { return 100; }; // TODO: Can be used to setup label generation through javascript objects if we want to ? + allow search on this + // throw new NotImplementedError(``); + + /** Not implemented. */ + throw new NotImplementedError(`Called property '${String(property)}' on Ray, which has not been implemented.`); + }, + + /** ray.property = something; */ + set: (self: Ray.Any, instance: Ray.Instance, property: string | symbol, value: any): boolean => { + instance.on({ event: 'PROXY.SET', context: { property, value } }); + + throw new NotImplementedError(); + }, + + /** delete ray.property; */ + deleteProperty: (self: Ray.Any, instance: Ray.Instance, property: string | symbol): boolean => { + instance.on({ event: 'PROXY.DELETE', context: { property } }); + + throw new NotImplementedError(); + }, + + /** ray() is called. */ + apply: (self: Ray.Any, instance: Ray.Instance, args: any[]): any => { + instance.on({ event: 'PROXY.APPLY', context: { args } }); + + throw new NotImplementedError(`${self}`); + }, + + /** new ray() */ + construct: (self: Ray.Any, instance: Ray.Instance, args: any[]): object => { + instance.on({ event: 'PROXY.NEW', context: { args } }); + + throw new NotImplementedError(`${args.length} ${self}`); + }, + + /** property in ray; */ + has: (self: Ray.Any, instance: Ray.Instance, property: string | symbol): boolean => { + instance.on({ event: 'PROXY.HAS', context: { property } }); + + throw new NotImplementedError(`${String(property)}`); + }, + }); + } + + /** Ray is an Enum(eration) */ + export namespace Enum { + + export type Type> = { + [TKey in T[number]]: Ray.Any + } + + export const Impl = >(...enumeration: T): Type => { + return Object.fromEntries(enumeration.map(value => + [value, Ray.Function.None.Impl((): Ray.Any => { + throw new NotImplementedError(); // TODO + })] + )) as Type; + // TODO: ONE OF 4 SELECTION RAY for the case of type. + } + + } + + /** Ray is a function (.next) */ + export namespace Function { + + /** {T} is just an example/desired use case. But it generalizes to any function. */ + export type Type = T | Ray.Any; + + /** From which perspective the Function is implemented. */ + enum Perspective { + None, Self, /** Ref, */ + } + + // /** + // * Implement a function from the perspective of 'this' for 'this.self'. + // */ + // // static Ref = (impl: (ref: Ray.Any) => TResult): Function => Ray.Function.Self(self => impl(self.as_reference())); + + /** Implement a function from no (or: an ignorant) perspective. */ + export namespace None { + + export const Impl = (impl: Op.Zeroary.Type): Ray.Any => { + throw new NotImplementedError(); + // return new Ray.Any({ perspective: Perspective.None, impl }); + } + + } + + /** Implement a function from the perspective of 'this' */ + export namespace Self { + export const Impl = (impl: Op.Unary.Type): Ray.Any => { + throw new NotImplementedError(); + // return new Ray.Any({ perspective: Perspective.Self, impl }); + } + + export const Binary = (impl: Op.Binary.Type): Ray.Any => { + throw new NotImplementedError(); + // return new Ray.Any({ perspective: Perspective.Self, impl }); // TODO: Good way to deal with arity + } + + // export const If = (impl: Op.Unary.Type): Ray.Any => { + // return Impl(impl); // TODO: GENERIC collapse to boolean implemented and overridable + // } + // TODO: GENERIC collapse to boolean implemented and overridable + + /** + * TODO: + * - Maybe replace switch with 'zip'?, What are the practical differences? + */ + // export const Match = (cases: MatchCases): Ray.Any => { + // return Impl(self => self); // TODO + // } + } + } + + export namespace Op { + + /** + * TODO: readonly setup, where only traversal ops are allowed. Of course these are writing in some sense, bit those writings aren't directly accessible from this perspective + * + */ + + export type Impl = + { [TKey in keyof (typeof Op.Zeroary.All)]: Op.Zeroary.Type } + & { [TKey in keyof (typeof Op.Unary.All)]: Op.Unary.Type } + & { [TKey in keyof (typeof Op.Binary.All)]: Op.Binary.Type } + + export const all = (ops: TOps): { + [TOP in keyof TOps]: Ray.Any + } => ({}); + + export namespace Zeroary { + export type Type = () => T; + export type Any = (keyof typeof Op.Zeroary.All) | Type; + + // TODO: set = none; + // TODO: Destroy the current thing, connect .initial & .terminal ? (can do just direct connection, preserves 'could have been something here') - then something like [self.initial, self, self.terminal].pop(). + // TODO: Leave behind --] [-- or connect them basically.. + export const All = Op.all({ + // @alias('alloc', 'new', 'create', 'initialize') + none: 'none', + }); + } + export namespace Unary { + export type Type = (a: T) => T; + export type Any = (keyof typeof Op.Unary.All) | Type; + + export const All = Op.all({ + + // @alias('free', 'destroy', 'clear', 'delete', 'pop') + free: 'free', + + /** An arbitrary Ray, defining what continuing in the reverse of this direction is equivalent to. */ + initial: 'initial', // a.initial + + /** + * An arbitrary Ray, defining what our current position is equivalent to. + * + * Moving to the intersecting Ray at `.self` - as a way of going an abstraction layer (lower), and asking what's inside. + */ + // @alias('dereference', 'self') + self: 'self', // a.self + + /** An arbitrary Ray, defining what continuing in this direction is equivalent to. */ + terminal: 'terminal', // a.terminal + }); + } + export namespace Binary { + export type Type = (a: T, b: T) => T; + export type Any = (keyof typeof Op.Binary.All) | Type; + + export const All = Op.all({ + initial: 'initial', // a.initial = b + self: 'self', // a.self = b + terminal: 'terminal', // a.terminal = b + + /** + * Concretely, we use here "whatever the JavaScript engine run on" as the thing which has power over the equivalence assumption we use to halt programs. - The asymmetry which allows the engine to make a distinction between each object. + * + * @see https://orbitmines.com/papers/on-orbits-equivalence-and-inconsistencies#:~:text=And%20there%20we%20have%20it%2C%20an%20infinity%2C%20loop%2C%20...%2C%20orbit%20if%20we%20ignore%20the%20difference. + */ + // @alias('is_none') + is_orbit: 'is_orbit', // a.___instance === b.___instance + }); + } + } + + export namespace Function { + export const Get = ( + property: TProperty + ): (typeof Ray.Function.All)[TProperty] | undefined => Ray.Function.All[property as TProperty] ?? undefined; + + export namespace All { + + /** [ |-?] */ export const is_initial = Ray.Function.Self.Impl( + self => self.initial().is_none() + ); + /** [--|--] */ export const is_vertex = Ray.Function.Self.Impl( + self => self.is_initial().nor(self.is_terminal()) + ); + /** [?-| ] */ export const is_terminal = Ray.Function.Self.Impl( + self => self.terminal().is_none() + ); + /** [ | ] */ export const is_reference = Ray.Function.Self.Impl( + self => self.is_initial().and(self.is_terminal()) + ); + /** [?-| ] or [ |-?] */ export const is_boundary = Ray.Function.Self.Impl( + self => self.is_initial().xor(self.is_terminal()) + ); + + /** + * This is basically what breaks the recursive structure. + * + * Tries for "global coherence", practically this just means self-reference, were no change is (inconsistently) assumed... + * + * --- + * + * Another way of interpreting a possible way of implementing it, is no matter how much more detail we would like to ask, the only thing we ever see is the same structure again (if we ignore the difference of us asking about that additional structure, that's still a possible handle on some difference). + * + * As a way of saying/.../assuming: I only 'infinitely' assume it's only this structure, "it seems to halt here". Note that this is necessarily an assumption. No guarantee of this can be made. This is necessarily an equivalence, ..., ignorance. + * + * @see https://orbitmines.com/papers/on-orbits-equivalence-and-inconsistencies#:~:text=Quite%20similarly%20to%20the%20loops%2C%20I%20could%20be%20ignorant%20of%20additional%20structure%20by%20assuming%20it%27s%20not%20there. + */ + // TODO: is none, ref, init terminal as global equiv check kn the structure? as generalization ; yep, is_orbit. + export const is_none = Ray.Op.Binary.All.is_orbit; + export const is_some = Ray.Function.Self.Impl( + self => self.is_none().not() + ); + + /** + * @see "Continuations as Equivalence (can often be done in parallel - not generally)": https://orbitmines.com/papers/on-orbits-equivalence-and-inconsistencies#:~:text=Constructing%20Continuations%20%2D%20Continuations%20as%20Equivalence + */ + // @alias('merge, 'continues_with', 'compose') + export const compose = Ray.Function.Self.Binary( + (a, b) => a.terminal().equivalent(b.initial()) + ); + + /** + * "Composing an terminal & initial boundary" + * - TODO: Note that an orbit is reversibility. ? + * - TODO: Could represent this abstraction in another layer what we want to accomplish while the actual search is still taking place. + * + * - Like with 'copy' and all concepts: Note that we're only after reversibility after ignoring some difference. + * + * @see "Reversibility is necessarily inconsistent": https://orbitmines.com/papers/on-orbits-equivalence-and-inconsistencies#:~:text=Another%20example%20of%20this%20is%20reversibility + */ + // @alias('modular', 'modulus', 'orbit', 'circle', 'repeats', 'infinitely') + export const orbit = Ray.Function.Self.Binary( + /** + * - TODO: If we're only doing one end: This already assumes they are connected on the other end. + * - TODO: should be a connection here, with is_composed ; or "reference.is_equivalent" so that you can drop one of the sides, or both. + */ + (a, b) => { + b.last().compose(a.first()); + a.first().compose(b.last()); + + return a; // ? + } + ); + + /** + * Equivalence as "Composition of Ray." + * + * NOTE: + * - An equivalence, is only a local equivalence, no global coherence of it can be guaranteed. And it is expensive work to edge towards global coherence. + * - Though changes are only applied locally, their effects can be global (Take for instance, the example of adding to one Ray, which changes the perspective of everything connected to that Ray if they were to traverse the connection). + * + * @see https://orbitmines.com/papers/on-orbits-equivalence-and-inconsistencies#:~:text=On%20Equivalences%20%26%20Inconsistencies + */ + export const equivalent = Ray.Function.Self.Binary( + (a, b) => { + // throw new NotImplementedError(); + + // TODO: This is close but not quite, use the shifting thing I wrote down a few days ago: (And then use something to break the self-reference) - Either on this side. compose, or outside the definitions + // This one harder to do in parallel? + return a.self().compose(b.self()); + }); + + /** + * If there exists an orbit between A & B anywhere on their equivalency functions - or: their .self - (except for the directions through which we're referencing them) + * + * Note the connection between 'is_orbit' vs 'is_equivalence'. They're essentially the same thing, but: + * - in the case of 'is_equivalence' we directly have access to their difference but are explicitly ignoring them - in the context in which this functionality is called. + * - in the case of 'is_orbit', we might need to do more complicated things to acknowledge their differences - we don't have direct access to them. + * + * TODO: (so dually connected, what if only one is aware??) Or basically just ; the answer in this particular instance is just if either end can find the other only once. Consistency of it defined on a more abstract level... + * + * a.self().traverse().is_orbit(b.self().traverse()) + */ + // @alias('includes', 'contains') ; (slightly different variants?) + export const is_equivalent = is_composed.from_perspective_of(self => self.self()); + // TODO: Either is_equiv or is_composed will likely change?. + /** + * a.traverse().is_orbit(b.traverse()) // Basically: does there exist a single connection between the two? + */ + export const is_composed = is_orbit.from_perspective_of(self => self.traverse()); // Needs some ref from Ray.Function.Self. + + /** + * "Applying the same thing in a different context" + * TODO: Somewhat related to Functors? + */ + export const from_perspective_of = Ray.Function.Self.Binary((a, b) => { throw new NotImplementedError(); }); + + export const traverse = Ray.Function.Self.Impl( + (a) => { throw new NotImplementedError(); } + ); + + // TODO: .terminal/.initial is self() vs self.not() + export const next = Ray.Function.Self.Impl((self) => { + throw new NotImplementedError(); + return self; + }); + export const has_next = Ray.Function.Self.Impl((self) => self.next().is_some()); + // @alias('end', 'result', 'back', 'output') + export const last = Ray.Function.Self.Impl((self) => { + throw new NotImplementedError(); + return self; + }); + + export const previous = Ray.Function.Self.Impl((self) => self.not().next()); + // @alias() + export const has_previous = Ray.Function.Self.Impl((self) => self.not().has_next()); + // @alias('beginning', 'front') + export const first = Ray.Function.Self.Impl((self) => self.not().last()); + + // @alias('not', 'reverse', 'swap', 'converse', 'negative', 'neg') + // export const not = Ray.Function.Self.Impl(self => ); + // self.terminal = self.initial(); + // self.initial = self.terminal(); + // self.not().not() = self + // TODO: What's the difference in context between stepping after not / or not doing so. + // TODO; OR BINARY? - "It's just always .next from some perspective?" + // TODO: Level at which "not" is applied. + export const not = Ray.Function.Self.Binary((a, b) => b); + + export const and = Ray.Function.Self.Binary((a, b) => { throw new NotImplementedError(); + } ); + export const or = Ray.Function.Self.Binary((a, b) => { + throw new NotImplementedError(); + return self; + }); + export const xor = Ray.Function.Self.Binary((a, b) => a.xnor(b).not()); + export const xnor = Ray.Function.Self.Binary((a, b) => a.is_orbit(b)); // TODO: Could be 'is_equivalent' too? + export const nand = Ray.Function.Self.Binary((a, b) => a.and(b).not()); + export const nor = Ray.Function.Self.Binary((a, b) => a.or(b).not()); + + + // @alias(`push_{last.alias}`) + export const push_back = Ray.Function.Self.Binary( + (a, b) => a.last().compose(b) + ); + // @alias(`push_{first.alias}`) + export const push_front = Ray.Function.Self.Binary( + (a, b) => b.compose(a.first()) + ); + + /** + * Performing a copy (realizing it) can be conceptualized as traversing the entire structure. (Where the 'entire structure' means the current instantiation of it - with many ignorances attached) + * + * - A problem with a copy, is that in or to be generalizable, it needs to alter all references to the thing it's copying to itself - this cannot be done with certainty. + * - This copy does not do that. Instead, it is ignorant of other things referencing the thing it's copying. + * - Additionally, a copy necessarily has some non-redundancy to it: + * + * @see "A copy is necessarily inconsistent": https://orbitmines.com/papers/on-orbits-equivalence-and-inconsistencies#:~:text=If%20I%20have%20one%20thing%20and%20I%20make%20a%20perfect%20copy + */ + // @alias('clone', 'duplicate') + export const copy = Ray.Function.Self.Impl( + // or + // (self) => self.self.copy.reference() + (self) => none().self = self.self.copy() + + // TODO Relies heavily on the execution layer to copy initial/terminal etc... ; and an is_orbit check before calling copy again. - Then again on the execution layer it can lazily do this copy (by not evaluating (i.e.) traversing everywhere), or it first does this traversing directly. + ); + + /** + * Placing existing structure on a new Reference, Boundary or Vertex: + */ + // TODO: These should allow as_vertex as zero-ary, but that means self = self_reference, not none. ? + /** + * Moving `self` to `.self` on an abstraction layer (higher). As a way of being able to describe `self`. + * + * TODO: the .reference might need two levels of abstraction higher, one to put it at the .self, another to reference that thing? (Depends a bit on the execution layer) + */ + export const as_reference = Ray.Function.Self.Impl( + self => none().self = self + ); + + // TODO as_reference.as_vertex instead of as_vertex ignorant by default? + + export const as_vertex = Ray.Function.Self.Impl((self) => { + const vertex = Ray.Op.Zeroary.All.none(); + vertex.initial = self_reference; + vertex.terminal = self_reference; + vertex.self = self; // TODO: Like this, ignorant vs non-ignorant? What to do here? + + return vertex; + }); + export const as_terminal = Ray.Function.Self.Impl((self) => { + const terminal = Ray.Op.Zeroary.All.none(); + terminal.initial = self_reference; + // terminal.terminal = none; + terminal.self = self; + + return terminal; + }); + export const as_initial = Ray.Function.Self.Impl((self) => { + const initial = Ray.Op.Zeroary.All.none(); + // initial.initial = none; + initial.terminal = self_reference; + initial.self = self; + + return initial; + }); + + /** + * Any arbitrary direction, where .not (or reversing the direction) relies on some memory mechanism + */ + export const memoized = Ray.Function.Self.Impl( + self => { throw new NotImplementedError(); } + ); + } + } + + /** JavaScript runtime conversions */ + export const array = (array: T[]): Ray.Any => Ray.iterable(array); + export const iterable = (iterable: Iterable): Ray.Any => Ray.iterator(iterable[Symbol.iterator]()); + export const async_iterable = (async_iterable: AsyncIterable): Ray.Any => Ray.async_iterator(async_iterable[Symbol.asyncIterator]()); + export const iterator = (iterator: Iterator): Ray.Any => { throw new NotImplementedError(); } + export const async_iterator = (async_iterator: AsyncIterator): Ray.Any => { throw new NotImplementedError(); } + export const generator = (generator: Generator): Ray.Any => Ray.iterator(generator); + export const async_generator = (generator: AsyncGenerator): Ray.Any => Ray.async_iterator(generator); + + export const number = (number: number) => { throw new NotImplementedError(); } + + export const self_reference = Ray.Function.Self.Impl( + self => self + ); + export const none = Ray.Op.Zeroary.All.none; // TODO FOR ALL OPS, ? automatic, or just put them here. + + // export const Boundary = { INITIAL: Type.INITIAL, TERMINAL: Type.TERMINAL }; TODO: LIST O + + // TODO CAN ALSO BE ZERO-ARY.. + // @alias('resize', 'size', 'structure', 'length', 'duplicate', 'copy') -> Should be generalized as any kind of structure, but with this thing repeated on it. ; use traversal or ... + export const size = Ray.Function.Self.Binary( + (a, size) => { throw new NotImplementedError(); } + ); + // @alias('bit') + export const boolean = size(2); + + export const vertex = Ray.Function.Self.Impl( + (self) => { throw new NotImplementedError(); } + ); + export const initial = Ray.Function.Self.Impl( + (self) => { throw new NotImplementedError(); } + ); + export const terminal = Ray.Function.Self.Impl( + (self) => { throw new NotImplementedError(); } + ); +} + +export default Ray; \ No newline at end of file diff --git a/environments/python/_temp/JS2.ts b/environments/python/_temp/JS2.ts new file mode 100644 index 0000000..b3194a7 --- /dev/null +++ b/environments/python/_temp/JS2.ts @@ -0,0 +1,217 @@ +// +// +// +// /** +// * Puts the Ray this is called with on a new Ray [initial = ref, ???, ???]. Then it places any structure it's applying a method to, on the terminal of this new Ray [initial = ref, ???, terminal = any] +// */ +// static Ref = (impl: Ray.FunctionImpl): Function => { +// return new Function(impl); // TODO: THIS SHOULD CHANGE, TO ON VERTEX. +// } +// static Impl = (impl: (initial: T, terminal: T) => T): Function => { +// return Function.Ref((ref: T) => impl(ref.initial, ref.terminal)); +// } +// // static IgnorantOfInitial = = Ray>(impl: (terminal: T) => T): Function => Function.Impl((_, terminal) => impl(terminal)); +// // static IgnorantOfTerminal = = Ray>(impl: (initial: T) => T): Function => Function.Impl((initial, _) => impl(initial)); +// // static Ignorant = = Ray>(impl: ParameterlessFunction): Function => Function.Impl(impl); +// +// /** +// * TODO: Reversible through memory... +// */ +// static WithMemory = ( +// apply: (previous: Ray.Any) => Ray.Any | any +// ): Function => { +// // return Function.Ref((ref: T) => impl(ref.initial, ref.terminal)); +// +// return { +// as_ray: (ref: Ray.Any = Ray.None()) => { +// const next = (previous: Ray.Any, first: boolean = false): Ray.Any => { +// const result = apply(previous); +// const is_terminal = result instanceof Ray ? +// result.is_none() || (result.is_terminal() && result.self.is_none()) +// : false; +// +// // Clear the terminal function attached to the Iterator. +// // TODO: In case of 'is_terminal = true': Could also leave this be, in case JavaScript allows for adding stuff to the iterator even after .done === true; +// previous.self.terminal = previous.self.___empty_terminal(); +// +// if (is_terminal) { +// // We're done, this is the end of the iterator +// +// return Ray.None(); +// } +// +// const current = Ray +// .vertex(() => result instanceof Ray ? result.self : JS.Any(result)) // TODO test +// .o(result instanceof Ray ? {} : { js: result }) +// .as_reference(); +// +// // Move the iterator to the terminal. +// current.self.terminal = () => next(current); +// +// if (first) { +// // Move the iterator's terminal to current. +// previous.self.terminal = () => current.self; +// +// current.self.initial = () => previous.self; +// +// return current; // Answer to INITIAL.terminal is a VERTEX. +// } +// +// // // TODO: This is just compose, but without .type checks.. ; FIX ON COMPOSE END for is_reference etc.. +// // if (previous.follow().type !== RayType.TERMINAL) +// // throw new PreventsImplementationBug(); +// // if (current.follow(Ray.directions.previous).type !== RayType.INITIAL) +// // throw new PreventsImplementationBug(); +// // +// // previous.follow().equivalent(current.follow(Ray.directions.previous)); +// previous.compose(current); +// +// return current.self.initial; // Answer to VERTEX.terminal is an INITIAL. +// } +// +// if (ref.is_none()) { +// const ray: Ray.Any = new Ray({ +// vertex: Ray.Any.None, +// terminal: () => next(ray, true), +// }).as_reference(); +// +// return ray; +// } else { +// const initial_vertex = Ray.vertex(() => ref.self).as_reference(); +// +// initial_vertex.self.terminal = () => next(initial_vertex); +// +// return initial_vertex; +// } +// } +// } as Function; +// } +// +// static Reversible = = Ray>( +// // @alias('backward') +// initial: (ref: Ray.Any) => Ray.Any | any, +// // @alias('forward') +// terminal: (ref: Ray.Any) => Ray.Any | any, +// ): Function => { +// // return Function.Ref((ref: T) => impl(ref.initial, ref.terminal)); +// +// return { +// as_ray: (ref: Ray.Any = Ray.None()): Ray.Any => { +// if (ref.is_none()) +// throw new NotImplementedError(); +// +// const next = (previous: Ray.Any, direction: (ref: Ray.Any) => Ray.Any | any): Ray.Any => { +// const result = direction(previous); +// +// // TODO: COuld do this in place. +// const current = Ray +// .vertex(() => result instanceof Ray ? result.self : JS.Any(result)) +// .o(result instanceof Ray ? {} : { js: result }) +// .as_reference(); +// +// current.self.initial.self = () => next(current, initial); +// current.self.terminal.self = () => next(current, terminal); +// +// return current.self.initial; +// } +// +// +// const initial_vertex = Ray.vertex(() => ref.self).as_reference(); +// +// // const initial_vertex = Ray.vertex(() => ref.self).as_reference(); +// initial_vertex.self.initial.self = () => next(initial_vertex, initial); +// initial_vertex.self.terminal.self = () => next(initial_vertex, terminal); +// +// return initial_vertex; +// } +// } as Function; +// } +// +// /** +// * Constructs a class method accepting arbitrary structure. +// * +// * a.compose(b).compose(c) = [a, b, c].compose = abc.compose = [[a1, a2], b, c].compose = [[a1, a2], b, [c1, c2]].compose = [[a1, [[[a2]]], [[[[]]], []]], b, [[[]], [], [c]]].compose = ... +// */ +// as_method = (ref: Ray.Any): Method => ((...any: Recursive): TResult => { +// if (any === undefined || any.length === 0) +// return this.step(ref); +// +// // TODO: This can be much better... +// const first = (recursive?: Recursive): T | undefined => { +// if (recursive === undefined) return; +// // if (_.isObject(recursive)) return recursive as unknown as Ray; +// +// for (let r of recursive) { +// if (r === undefined) continue; +// if (_.isObject(r)) return r as unknown as T; +// +// // if (r instanceof Ray) +// // throw new PreventsImplementationBug(); +// +// // @ts-ignore +// const _first = first(r); +// if (_first) +// return _first; +// } +// } +// +// const _first = first(any); +// +// if (_first === undefined) +// return this.step(ref); +// +// const pointer = (new Ray({ +// // @ts-ignore +// initial: () => ref, +// // @ts-ignore +// terminal: () => _first +// })) as unknown as T; +// +// return this.step(pointer); +// +// // TODO: ANY CASE +// // if (any.length === 1) { +// // } +// }) +// +// as_ray = (initial: Ray.Any = Ray.None()): Ray.Any => { +// throw new NotImplementedError(); +// } +// +// as_generator = (): Generator => { +// throw new NotImplementedError(); +// } +// +// } +// +// +// +// export const Iterator = (iterator: Iterator): Ray.Any => { +// // [ |--] +// +// return Ray.Function.WithMemory(previous => { +// const iterator_result = iterator.next(); +// +// return iterator_result.done !== true ? iterator_result.value : Ray.Any.None(); +// }).as_ray(); +// } +// + +// +// // TODO +// export const Object = (object: object): Ray.Any => Ray.Any.vertex().o(object).as_reference(); +// +// export const Any = (any: any): Ray.Any => { +// if (any === null || any === undefined) return JS.Any(any); +// if (JS.is_boolean(any)) return JS.Boolean(any); +// // if (JS.is_number(any)) return JS.Number(any); TODO +// if (JS.is_iterable(any)) return JS.Iterable(any); // || is_array(any)) +// if (JS.is_function(any)) return Ray.Function.Any(any).as_ray(); +// if (JS.is_object(any)) return JS.Object(any); +// +// // TODO +// // return JS.Any(any); +// return Ray.vertex().o({js: any}).as_reference(); +// } +// +// } diff --git a/environments/python/_temp/OrbitMinesExplorer.tsx b/environments/python/_temp/OrbitMinesExplorer.tsx new file mode 100644 index 0000000..68f5ae3 --- /dev/null +++ b/environments/python/_temp/OrbitMinesExplorer.tsx @@ -0,0 +1,292 @@ +import React from 'react'; +import {Ray, RayType} from "./Ray"; +import {VisualizationCanvas} from "./Visualization"; +import {CatmullRomLine, Circle, CubicBezierLine, QuadraticBezierLine, Torus} from "@react-three/drei"; +import _ from "lodash"; +import IEventListener from "../js/react/IEventListener"; +import {Children} from "../../lib/typescript/React"; +import {NotImplementedError} from "./errors/errors"; + +// TODO, All this should be automatic through Ray +export const add_ = (...a: number[][]): [number, number, number] => { + let res = a[0]; + for (let i = 1; i < a.length; i++) { + res = add(res, a[i]); + } + return res as [number, number, number]; +} +export const sub = (a: number[], b: number[]): [number, number, number] => [a[0] - b[0], a[1] - b[1], a[2] - b[2]]; + +export const Curve = ( + { color = "#FFFF55", + position = [0, 0, 0], + initial = position, + terminal = add(position, [0, 30, 0]), + scale = 1.5 }: any) => { + const radius = 15; + + return + {/**/} + + +} + +const BinaryValue = ({ boolean, position }: any) => { + if (boolean) + return + + const halfTorus = (torus.radius + (torus.tube.width / 2)); + + const up = add(position, [0, 60, 0]); + const middle = add(position, [0, 20, 0]); + const down = add(position, [0, -20, 0]); + + return <> + + + + + + + + + + + + +} + +/** + * Temporary Ray visualization till the visualization is incorporated into the editor (Basically when Visualization = Ray) + * + * TODO; Generalize to Ray - should be embedded on the vertex, or on another layer of description, where the interface is a rewrite rule + */ +export type InterfaceOptions = { + position?: [number, number, number], + rotation?: [number, number, number], + scale?: number, + color?: string +} +type Options = { + initial?: InterfaceOptions, + vertex?: InterfaceOptions, + terminal?: InterfaceOptions, +} + +type RenderContext = Compiler; // Rendering is Compiling - Something which holds equivalences and ignores/shuts down self-referential structures. +export type Compiler = { + coverage(ray: Ray.Any): Ray.Any, // Disallow dedups + covered_by(cover: Ray.Any, ray: Ray.Any): Compiler +} + +class TempCompiler implements Compiler { + coverage(ray: Ray.Any): Ray.Any { + return Ray.None(); + } + covered_by(cover: Ray.Any, ray: Ray.Any): Compiler { + return this; + } +} + +export const AutoRay = ( + { ray, + compiler, + initial: _default_initial, + terminal: _default_terminal, + ...options + }: { ray: Ray.Any, compiler?: Compiler } & Omit & InterfaceOptions +) => { + compiler ??= new TempCompiler(); + + + const o = (ray: Ray.Any, defaults: InterfaceOptions = {}): Required => { + const { + position = defaults.position ?? [0, 0, 0], + rotation = defaults.rotation ?? [0, 0, 0], + scale = defaults.scale ?? 1.5, + color = defaults.color ?? 'orange' + } = ray.any; + return ({ position, rotation, scale, color, }) + }; + + // Move to a layer of abstraction above what is passed to us - this way we can start describing it. + const ref = { // TODO; This general pattern is probably worth abstracting somewhere. + initial: Ray.Any.initial.as_reference(), + vertex: Ray.Any.as_reference(), + terminal: Ray.Any.terminal.as_reference() + } + + if (compiler.coverage(ray).is_some()) + return <>; + + // console.log(ref.vertex.type) + + compiler.covered_by(ref.vertex, ray); + + const interface_options = { // TODO: See here again + initial: o(ray.initial, { ..._default_initial }), + vertex: o(ray, { ...options }), + terminal: o(ray.terminal, { ..._default_terminal }), + } + + // + // switch (ref.vertex.type) { + // case RayType.REFERENCE: + // break; + // case RayType.INITIAL: + // break; + // case RayType.TERMINAL: + // break; + // case RayType.VERTEX: + // break; + // } + // + + // const _default = { scale: 1.5 } + // // + // const map: { [type: string]: { [type: string]: Pick & InterfaceOptions }} = { + // [RayType.INITIAL]: { + // [RayType.INITIAL]: { type: Ray.AnyType.INITIAL, position: [-20 * _default.scale, 0, 0] }, + // [RayType.VERTEX]: { type: Ray.AnyType.VERTEX, position: [-20 * _default.scale, 0, 0], rotation: [0, 0, Math.PI / 2] }, + // [RayType.TERMINAL]: { type: Ray.AnyType.TERMINAL }, + // } + // } + // const initial_op = map[RayType.INITIAL][ref.initial.type]; + + return + {/*{ref.initial.is_none() ? : }*/} + + {/**/} + +} + +export const AutoVertex = (ray: Omit & InterfaceOptions & { + length?: number // basically .length + children?: any +}) => { + const { + length = 1, + children + } = ray; + const _default: Required = { + position: [0, 0, 0], + rotation: [0, 0, 0], + scale: 1, + color: 'orange', + // ..._.pick(ray.vertex, 'position', 'rotation', 'scale', 'color'), + ..._.pick(ray, 'position', 'rotation', 'scale', 'color') + } + + const initial: Required = {..._default, position: [-20 * _default.scale, 0, 0], ...ray.initial}; + + // Vertex as the origin for rotation + const vertex: Required = {..._default, position: [0, 0, 0]} //, ...ray.vertex }; + const terminal: Required = {..._default, position: [20 * _default.scale, 0, 0], ...ray.terminal}; + + + if (length > 1) // TODO, currently rotates around each vertex individually + return {[...Array(length)] + .map(((_, i) => ))} + + return + + + + + {children} + +} +export const _Continuation = ({color = torus.color, ...options }: InterfaceOptions) => + +export const _Vertex = ({ color = circle.color, ...options }: any) => + +export const SimpleRenderedRay = ( + ray: Pick + & Options // Relative options to other rays + & InterfaceOptions // Default options +) => { + const _default: Required = { + position: [0, 0, 0], + rotation: [0, 0, 0], + scale: 1, + color: 'orange', + ..._.pick(ray, 'position', 'rotation', 'scale', 'color') + } + + const initial: Required = { ..._default, ...ray.initial }; + const vertex: Required = { ..._default, ...ray.vertex }; + const terminal: Required = { ..._default, ...ray.terminal }; + const { type } = ray; + + switch (type) { + case RayType.REFERENCE: + throw new NotImplementedError(); + case RayType.INITIAL: { + return <> + + <_Continuation {...vertex} /> + + } + case RayType.TERMINAL: { + return <> + + <_Continuation {...vertex} /> + + } + case RayType.VERTEX: { + return <_Vertex {...vertex} /> + } + } +} + + + + +const OrbitMinesExplorer = ( + { + listeners = [], + }: { + listeners?: IEventListener[], + } +) => { + + return ( + + + + ); +}; + +export default OrbitMinesExplorer; \ No newline at end of file diff --git a/environments/python/_temp/REFACTOR_AFTER_WORKING_TRAVERSE.md b/environments/python/_temp/REFACTOR_AFTER_WORKING_TRAVERSE.md new file mode 100644 index 0000000..bed6cf6 --- /dev/null +++ b/environments/python/_temp/REFACTOR_AFTER_WORKING_TRAVERSE.md @@ -0,0 +1,343 @@ + +// NOT NEEDED FOR INITIAL DEMONSTRATION: + +```ts + + get count(): Ray.Any { return JS.Number(this.as_array().length); } + +// TODO: For chyp used to compare [vtype, size] as domains, just type matching on the vertex. ; each individually, again, additional structure... + +// TODO: Ignore the connection between the two, say a.equiv(b) within some Rule [a,b], ignore the existing of the connection in the Rule? What does it mean not to??? +is_vertex_equivalent = (b: Arbitrary.Any) => { + +} + + +// none_or = (arbitrary: Implementation): Ray.Any => this.is_none() ? Ray.None() : arbitrary(this); + +/** + * https://en.wikipedia.org/wiki/Homoiconicity + */ +export interface PossiblyHomoiconic> { + get self(): T; + is_reference: () => boolean + as_reference: () => T +} + + + // zip also compose??? + // [a, b, c] zip [d, e, f] zip [g, h, i] ... + // [[a,d,g],[b,e,h],[c,f,i]] +static zip = Ray.Function.Impl((initial, terminal) => { + + if (initial.as_reference().type !== RayType.REFERENCE || terminal.as_reference().type !== RayType.REFERENCE) + throw new PreventsImplementationBug('TODO: Implement'); + + if (initial.type !== RayType.VERTEX || terminal.type !== RayType.VERTEX) + throw new PreventsImplementationBug('TODO: Implement'); + + throw new NotImplementedError(); + // initial.traverse() + // return new Ray({ + // + // }); +}); +zip = Ray.zip.as_method(this); + + /** + * Used for chaining JavaScript-provided properties + * + * TODO: DOESNT FOLLOW .ANY PATTERN? + */ +o = (object: { [key: string | symbol]: any }): Ray.Any => { + _.keys(object).forEach(key => this.proxy()[key] = object[key]); // TODO: Can be prettier, TODO: map to Ray equivalents and add to vertices.. + return this; +} + +// All these are dirty +o2 = ({ initial, vertex, terminal }: any): Ray.Any => { + if (initial) this.initial.o(initial); + if (vertex) this.o(vertex); + if (terminal) this.terminal.o(terminal); + + return this; +} + + // TODO: Cast to Any JS Class wrap/cast? + test self-referentially with Rays??? + cast = (): T => { throw new NotImplementedError(); } // TODO this.proxy(); + +as_string = (): string => this.as_array().map(ref => ref.any.js).join(','); // TODO: PROPER + +as_number = this.as_int; + + // TODO: FIND OUT IF SOMEONE HAS A NAME FOR THIS + // apply = (func: Ray.Any) => { + +// TODO: Combine into generalized [x, min/max()] - preserve terminal/initial structure +// TODO: Ray.Any#apply. +// TODO: FROM COMPOSER +/** + * const func = [min(), '', max()] + * + * const [min_x, max_x] = [ + * // Compute the min x-coordinate of the edges and vertices in the other graph. + * compose.terminal.x.min(), // min_other + * + * // Compute the max x-coordinate of the edges and vertices in this graph. + * compose.initial.x.max(), // max_self + * ] + */ +// } + + // TODO: +default, in the case of Initial/Terminal = Ray.None, to which the default sometimes is nothing. Or in the case of min/max it's 0. + + // [this.vertices().x.max(), this.edges().x.max()].max() + // [this.vertices().x.min(), this.edges().x.max()].max() + // TODO: Indicies not corresponding the the directionality defined, are probably on another abstraction layer described this way. More accurately, they're directly connected, and on a separate layer with more stuff in between... +# Index with respect to 'something' - i.e. distance. +# The problem with index is that this, in respect to the context defined the direction of the Ray. Necessitates a notion of distance, which necessitates the notion of an orbit, ..., loop to be an ignorant one. +get index(): Ray.Any { throw new NotImplementedError(); } +// TODO: Can probably generate these on the fly, or cache them automatically +// TODO: being called min.x needs to return the min value within that entire structure. + +min = (_default: 0): Ray.Any => { throw new NotImplementedError(); } +max = (_default: 0): Ray.Any => { throw new NotImplementedError(); } + + map = (mapping: (ray: Ray.Any) => Ray.Any | any): Ray.Any => { throw new NotImplementedError(); } +// filter = (mapping: (ray: Ray.Any) => Ray.Any | any): Ray.Any => { throw new NotImplementedError(); } + +// @alias('length', 'of_length') +static size = (of: number, value: any = undefined): Ray.Any => { + let ret: Ray.Any | undefined; + let current: Ray.Any | undefined; + // TODO: Actual good implementation: Should be lazy + for (let i = 0; i < of; i++) { + const vertex = Ray.vertex().o({js: value}).as_reference(); + + if (!ret) { + current = ret = vertex; + } else { + current = current?.compose(vertex); + } + } + + if (!ret) + return Ray.None(); + + return ret; +} +static at = (index: number, of: number, value: any = undefined): Ray.Any => { + return Ray.size(of, value).at(index); +} +/** + * Just uses length/size for permutation. TODO: More complex permutation/enumeration implementation should follow at some point. (@see https://orbitmines.com/papers/on-orbits-equivalence-and-inconsistencies#:~:text=One%20of%20them%20could%20even%20be%20putting%20both%20our%20points%20on%20our%20selection for an example) + * + * @see "Combinatorics as Equivalence": https://orbitmines.com/papers/on-orbits-equivalence-and-inconsistencies#:~:text=Constructing%20Combinatorics%20%2D%20Combinatorics%20as%20Equivalence + */ +static permutation = (permutation: number | undefined, of: number): Ray.Any => Ray.Any.at( + // In the case of a bit: 2nd value for '1' (but could be the reverse, if our interpreter does this) + permutation ?? 0, + // In the case of a bit: Either |-*-| if no bit or |-*->-*-| if a bit. + permutation === undefined ? 1 : of +) + +at = (steps: number | Ray | JS.ParameterlessFunction): Ray.Any => { + if (!JS.is_number(steps)) + throw new NotImplementedError('Not yet implemented for Ray.'); + + // TODO: Actual good implementation - also doesn't support modular like this + const array = [...this.traverse( + steps < 0 ? Ray.directions.previous : Ray.Any.directions.next + )]; + + steps = Math.abs(steps); + + return array.length > steps && steps >= 0 ? ( + array[steps] ?? Ray.None() // TODO FIX: Probably a JavaScript quirck with some weird numbers, just failsafe to None. + ) : Ray.Any.None(); +} + +// export const hexadecimal = (hexadecimal?: string): Arbitrary> => permutation(hexadecimal ? parseInt(hexadecimal, 16) : undefined, 16); + +``` + + +export class Ray + // implements + // AsyncIterable, + // Iterable + // Array + // Dict?? +{ + + /** + * JavaScript Array + */ + // [n: number]: Ray.Any; + // + // readonly [Symbol.unscopables]: { [K in keyof any[]]?: boolean }; + // length: number; + // + // [Symbol.iterator](): IterableIterator { + // return undefined; + // } + // + // at(index: number): Ray.Any | undefined { + // return undefined; + // } + // + // concat(...items: ConcatArray[]): Ray.Any[]; + // concat(...items: (ConcatArray | Ray)[]): Ray.Any[]; + // concat(...items: (ConcatArray | Ray)[]): Ray.Any[] { + // return []; + // } + // + // copyWithin(target: number, start: number, end?: number): this { + // return undefined; + // } + // + // entries(): IterableIterator<[number, Ray]> { + // return undefined; + // } + // + // every(predicate: (value: Ray.Any, index: number, array: Ray.Any[]) => value is S, thisArg?: any): this is S[]; + // every(predicate: (value: Ray.Any, index: number, array: Ray.Any[]) => unknown, thisArg?: any): boolean; + // every(predicate, thisArg?: any): any { + // } + // + // fill(value: Ray.Any, start?: number, end?: number): this { + // return undefined; + // } + // + // filter(predicate: (value: Ray.Any, index: number, array: Ray.Any[]) => value is S, thisArg?: any): S[]; + // filter(predicate: (value: Ray.Any, index: number, array: Ray.Any[]) => unknown, thisArg?: any): Ray.Any[]; + // filter(predicate, thisArg?: any): any { + // } + // + // find(predicate: (value: Ray.Any, index: number, obj: Ray.Any[]) => value is S, thisArg?: any): S | undefined; + // find(predicate: (value: Ray.Any, index: number, obj: Ray.Any[]) => unknown, thisArg?: any): Ray.Any | undefined; + // find(predicate, thisArg?: any): any { + // } + // + // findIndex(predicate: (value: Ray.Any, index: number, obj: Ray.Any[]) => unknown, thisArg?: any): number { + // return 0; + // } + // + // findLast(predicate: (value: Ray.Any, index: number, array: Ray.Any[]) => value is S, thisArg?: any): S | undefined; + // findLast(predicate: (value: Ray.Any, index: number, array: Ray.Any[]) => unknown, thisArg?: any): Ray.Any | undefined; + // findLast(predicate, thisArg?: any): any { + // } + // + // findLastIndex(predicate: (value: Ray.Any, index: number, array: Ray.Any[]) => unknown, thisArg?: any): number { + // return 0; + // } + // + // flat(depth?: D): FlatArray[] { + // return []; + // } + // + // flatMap(callback: (this: This, value: Ray.Any, index: number, array: Ray.Any[]) => (ReadonlyArray | U), thisArg?: This): U[] { + // return []; + // } + // + // forEach(callbackfn: (value: Ray.Any, index: number, array: Ray.Any[]) => void, thisArg?: any): void { + // } + // + // includes(searchElement: Ray.Any, fromIndex?: number): boolean { + // return false; + // } + // + // indexOf(searchElement: Ray.Any, fromIndex?: number): number { + // return 0; + // } + // + // join(separator?: string): string { + // return ""; + // } + // + // keys(): IterableIterator { + // return undefined; + // } + // + // lastIndexOf(searchElement: Ray.Any, fromIndex?: number): number { + // return 0; + // } + // + // map(callbackfn: (value: Ray.Any, index: number, array: Ray.Any[]) => U, thisArg?: any): U[] { + // return []; + // } + // + // pop(): Ray.Any | undefined { + // return undefined; + // } + // + // push(...items: Ray.Any[]): number { + // return 0; + // } + // + // reduce(callbackfn: (previousValue: Ray.Any, currentValue: Ray.Any, currentIndex: number, array: Ray.Any[]) => Ray.Any): Ray.Any; + // reduce(callbackfn: (previousValue: Ray.Any, currentValue: Ray.Any, currentIndex: number, array: Ray.Any[]) => Ray.Any, initialValue: Ray.Any): Ray.Any; + // reduce(callbackfn: (previousValue: U, currentValue: Ray.Any, currentIndex: number, array: Ray.Any[]) => U, initialValue: U): U; + // reduce(callbackfn, initialValue?): any { + // } + // + // reduceRight(callbackfn: (previousValue: Ray.Any, currentValue: Ray.Any, currentIndex: number, array: Ray.Any[]) => Ray.Any): Ray.Any; + // reduceRight(callbackfn: (previousValue: Ray.Any, currentValue: Ray.Any, currentIndex: number, array: Ray.Any[]) => Ray.Any, initialValue: Ray.Any): Ray.Any; + // reduceRight(callbackfn: (previousValue: U, currentValue: Ray.Any, currentIndex: number, array: Ray.Any[]) => U, initialValue: U): U; + // reduceRight(callbackfn, initialValue?): any { + // } + // + // reverse(): Ray.Any[] { + // return []; + // } + // + // shift(): Ray.Any | undefined { + // return undefined; + // } + // + // slice(start?: number, end?: number): Ray.Any[] { + // return []; + // } + // + // some(predicate: (value: Ray.Any, index: number, array: Ray.Any[]) => unknown, thisArg?: any): boolean { + // return false; + // } + // + // sort(compareFn?: (a: Ray.Any, b: Arbitrary.Any) => number): this { + // return undefined; + // } + // + // splice(start: number, deleteCount?: number): Ray.Any[]; + // splice(start: number, deleteCount: number, ...items: Ray.Any[]): Ray.Any[]; + // splice(start: number, deleteCount?: number, ...items: Ray.Any[]): Ray.Any[] { + // return []; + // } + // + // toReversed(): Ray.Any[] { + // return []; + // } + // + // toSorted(compareFn?: (a: Ray.Any, b: Arbitrary.Any) => number): Ray.Any[] { + // return []; + // } + // + // toSpliced(start: number, deleteCount: number, ...items: Ray.Any[]): Ray.Any[]; + // toSpliced(start: number, deleteCount?: number): Ray.Any[]; + // toSpliced(start: number, deleteCount?: number, ...items: Ray.Any[]): Ray.Any[] { + // return []; + // } + // + // unshift(...items: Ray.Any[]): number { + // return 0; + // } + // + // values(): IterableIterator { + // return undefined; + // } + // + // with(index: number, value: Ray.Any): Ray.Any[] { + // return []; + // } + +} + diff --git a/environments/python/_temp/REFACTOR_RENDER.md b/environments/python/_temp/REFACTOR_RENDER.md new file mode 100644 index 0000000..3128710 --- /dev/null +++ b/environments/python/_temp/REFACTOR_RENDER.md @@ -0,0 +1,59 @@ + + +```ts + +//TODO USED FOR DEBUG NOW +move = (func: (self: Ray.Any) => Ray.Any, memory: boolean, Interface: Ray.Any): Ray.Any => { + const target_ray = func(this.self); + + const target = target_ray.as_reference().o({ + ...this._dirty_store, + position: + target_ray.any.position + ?? this.any.position + ?? Ray.POSITION_OF_DOOM + }); + console.log('move', `${this.self.label.split(' ')[0]} -> ${target.self.label.split(' ')[0]}`); + + if (memory) { + if (!target_ray.any.traversed) { + Interface.any.Ray.compose(target); + target_ray.any.traversed = true; + } + } else { + Interface.any.rays = [target]; + } + + return target; +} + +static POSITION_OF_DOOM = [0, 100, 0] + +// TODO: Abstract away as compilation +render_options = (Interface: Ray.Any): Required => { + return ({ + position: + this.any.position + ?? (this.is_none() ? Ray.POSITION_OF_DOOM : Ray.Any.POSITION_OF_DOOM), + rotation: + this.any.rotation + ?? [0, 0, 0], + scale: + this.any.scale + ?? (this.is_none() ? 1.5 : 1.5), + color: + (Ray.is_orbit(Interface.any.selection.self, this.self) && Interface.any.cursor.tick) ? '#AAAAAA' // TODO: Should do lines as well, line render should prefer based on level of description.. (flat line only vertices, then render for the vertex?) + : ( + this.any.color + ?? (this.is_none() ? 'red' : { + [RayType.VERTEX]: 'orange', + [RayType.TERMINAL]: '#FF5555', + [RayType.INITIAL]: '#5555FF', + [RayType.REFERENCE]: '#555555', + }[this.type] + ) + ) + }); +} + +``` \ No newline at end of file diff --git a/environments/python/_temp/Ray.ts b/environments/python/_temp/Ray.ts new file mode 100644 index 0000000..969731b --- /dev/null +++ b/environments/python/_temp/Ray.ts @@ -0,0 +1,342 @@ +import JS, {NotImplementedError} from "@orbitmines/js"; + +namespace Ray { + + export type Any = + { + /** Ray is a constructor - TODO: Copy? */ + new (...other: JS.Recursive): Ray.Any, + } + /** JavaScript runtime conversions. */ + & Symbol + + & Pick + + /** Preconfigured functions defined for Rays. */ + & { + -readonly [TKey in keyof typeof Ray.Function.All]: typeof Ray.Function.All[TKey] extends Ray.Any + ? Ray.Any + : never; + } + + /** Storage/Movement operations which need to be implemented. */ + & { [TKey in keyof Ray.Op.Impl]: Ray.Any } + + export namespace Debug { + export type Event = { event: string, self: Ray.Any, context: any }; + export type Listener = (event: Event) => void; + } + + /** + * An uninitialized empty Ray, which caches itself once initialized. + */ + // export const None: Ray.FunctionConstructor = Ray.Function.CachedAfterUse(Ray.New); + + // export class Object { + // + // // TODO: Could copy? + // get initial(): Ray.Any { return this._initial(); } set initial(initial: Ray.Any) { this._initial = initial; } protected _initial: Ray.Any; + // get self(): Ray.Any { return this._self(); } set self(self: Ray.Any) { this._self = self; } protected _self: Ray.Any; + // get terminal(): Ray.Any { return this._terminal(); } set terminal(terminal: Ray.Any) { this._terminal = terminal; } protected _terminal: Ray.Any; + // + // protected constructor({ initial, self, terminal }: { initial?: Ray.Any, self?: Ray.Any, terminal?: Ray.Any } = {}) { + // + // this._initial = initial ?? Ray.none.memoized; + // this._self = self ?? Ray.self_reference; + // this._terminal = terminal ?? Ray.none.memoized; + // } + // } + + /** A simplistic compiler for Ray */ + export namespace Compiler { // TODO Ray is Compiler + + /** + * In the case of Rays, whether something is a vertex/initial/terminal is only inferred from surrounding context. And these checks only need to happen locally in order to decide how to traverse arbitrary structure (as in - I only need to check the presence of something next to me, not traverse the whole direction recursively in order to decide what to do). + * + * How about treating something like something which the context says it's not? (Could apply this sort of thing in some fidelity/consistency checking mechanism as a way of fuzzing the fidelity mechanism) + * + * TODO: Reference maybe as an orbit at the point is the thing ignorant + * + * TODO: Compiler could have things like other composed rays which tell it cares about the other (even if that's correct or not??) + * + * + * TODO: Do I want to keep the is_equiv/is_composed pattern? Or simplify to one of the two? + * + * // TODO: NEVER DIRECTLY EXECUTE, ONLY AFTER CHAIN OF FUNCS, possibly arbitrarily LAZY + * + * TODO + * - Compose empty as first element? Disregard none to first elemn? Or not?? + * + * TODO; Impl + * - Generally, return something which knows where all continuations are. + * - Generator/step function/... ; with no assumption of halting, but allow to hook into any step. + * - Cache: + * - Control of (non-/)lazyness of functions + * - None as, first time called, memoize func. + * - Perhaps locally cache (for stuff like count?) - no way to ensure globally coherence + * - a.orbit() -> a.orbit(a) + * // TODO: Should be automatic? is_orbit() or any method without arguments is a.is_orbit(a.self()) ?? not a.is_orbit(a) ; ??? + * + * - .initial/.terminal can be seen as a particular connection on .self, which .self ignores? + * + * TODO: Allow hooking + * + * TODO: Testing + * - Test if references hold after equivalence/composition... + * + * TODO: After initial demo: + * - Allow mapping/finding of other implementations regarding some equiv funcs (like different ways of implementing using NAND etc...) + * + * Arbitrary. + * + * TODO Pass a single ray to the runtime, and run that way? Access runtime as variable from ray? That would have programs accessing possible runtimes? + */ + } + + export type Constructor = { proxy?: ProxyHandler, debug?: Debug.Listener, } + + export class Instance extends JS.Class.Instance { + + listeners: Debug.Listener[]; + + constructor({ + proxy, + debug, + }: Ray.Constructor = {}) { + super({ proxy: proxy ?? Ray.ProxyHandlers.Default }); + + this.listeners = debug ? [debug] : []; + } + + /** Used to jump out of the proxy. */ + get ___instance(): Instance { return this; } + + debug = ( + on: Debug.Listener, + func: JS.ParameterlessFunction + ): T => { + this.listeners.push(on); + const ret = func(); + this.listeners.pop(); // TODO? + + return ret; + } + + /** Simple debug/traversal mechanism. */ + on = (event: Omit) => { + if (!this.listeners) return; + + this.listeners.forEach(listener => listener({ ...event, self: this.proxy })); + } + + } + + + export namespace ProxyHandlers { + + export const Default: ProxyHandler = JS.Class.Handler({ + /** ray.property */ + get: (self: Ray.Any, instance: Ray.Instance, property: string | symbol): any => { + instance.on({ event: 'PROXY.GET', context: { property } }); + + /** Use any field on {Ray.Instance}, which we want to delegate to, first. */ + if (property === '___instance' || property === 'debug' || property === 'on') { return instance[property]; } + + /** Otherwise, switch to functions defined on {Ray.Functions} */ + const func = Ray.Function.Get(property as any); + if (func) { return func.as_method({ self, property }); } + + if (property === Symbol.toPrimitive) + return (hint: string) => { return 100; }; // TODO: Can be used to setup label generation through javascript objects if we want to ? + allow search on this + // throw new NotImplementedError(``); + + /** Not implemented. */ + throw new NotImplementedError(`Called property '${String(property)}' on Ray, which has not been implemented.`); + }, + + /** ray.property = something; */ + set: (self: Ray.Any, instance: Ray.Instance, property: string | symbol, value: any): boolean => { + instance.on({ event: 'PROXY.SET', context: { property, value } }); + + throw new NotImplementedError(); + }, + + /** delete ray.property; */ + deleteProperty: (self: Ray.Any, instance: Ray.Instance, property: string | symbol): boolean => { + instance.on({ event: 'PROXY.DELETE', context: { property } }); + + throw new NotImplementedError(); + }, + + /** ray() is called. */ + apply: (self: Ray.Any, instance: Ray.Instance, args: any[]): any => { + instance.on({ event: 'PROXY.APPLY', context: { args } }); + + throw new NotImplementedError(`${self}`); + }, + + /** new ray() */ + construct: (self: Ray.Any, instance: Ray.Instance, args: any[]): object => { + instance.on({ event: 'PROXY.NEW', context: { args } }); + + throw new NotImplementedError(`${args.length} ${self}`); + }, + + /** property in ray; */ + has: (self: Ray.Any, instance: Ray.Instance, property: string | symbol): boolean => { + instance.on({ event: 'PROXY.HAS', context: { property } }); + + throw new NotImplementedError(`${String(property)}`); + }, + }); + } + + /** Ray is an Enum(eration) */ + export namespace Enum { + + export type Type> = { + [TKey in T[number]]: Ray.Any + } + + export const Impl = >(...enumeration: T): Type => { + return Object.fromEntries(enumeration.map(value => + [value, Ray.Function.None.Impl((): Ray.Any => { + throw new NotImplementedError(); // TODO + })] + )) as Type; + // TODO: ONE OF 4 SELECTION RAY for the case of type. + } + + } + + /** Ray is a function (.next) */ + export namespace Function { + + /** {T} is just an example/desired use case. But it generalizes to any function. */ + export type Type = T | Ray.Any; + + /** From which perspective the Function is implemented. */ + enum Perspective { + None, Self, /** Ref, */ + } + + // /** + // * Implement a function from the perspective of 'this' for 'this.self'. + // */ + // // static Ref = (impl: (ref: Ray.Any) => TResult): Function => Ray.Function.Self(self => impl(self.as_reference())); + + /** Implement a function from no (or: an ignorant) perspective. */ + export namespace None { + + export const Impl = (impl: Op.Zeroary.Type): Ray.Any => { + throw new NotImplementedError(); + // return new Ray.Any({ perspective: Perspective.None, impl }); + } + + } + + /** Implement a function from the perspective of 'this' */ + export namespace Self { + export const Impl = (impl: Op.Unary.Type): Ray.Any => { + throw new NotImplementedError(); + // return new Ray.Any({ perspective: Perspective.Self, impl }); + } + + export const Binary = (impl: Op.Binary.Type): Ray.Any => { + throw new NotImplementedError(); + // return new Ray.Any({ perspective: Perspective.Self, impl }); // TODO: Good way to deal with arity + } + + // export const If = (impl: Op.Unary.Type): Ray.Any => { + // return Impl(impl); // TODO: GENERIC collapse to boolean implemented and overridable + // } + // TODO: GENERIC collapse to boolean implemented and overridable + + /** + * TODO: + * - Maybe replace switch with 'zip'?, What are the practical differences? + */ + // export const Match = (cases: MatchCases): Ray.Any => { + // return Impl(self => self); // TODO + // } + } + } + + export namespace Function { + export const Get = ( + property: TProperty + ): (typeof Ray.Function.All)[TProperty] | undefined => Ray.Function.All[property as TProperty] ?? undefined; + + export namespace All { + + export const traverse = Ray.Function.Self.Impl( + (a) => { throw new NotImplementedError(); } + ); + + // export const not = Ray.Function.Self.Impl(self => ); + // self.terminal = self.initial(); + // self.initial = self.terminal(); + // self.not().not() = self + // TODO: What's the difference in context between stepping after not / or not doing so. + // TODO; OR BINARY? - "It's just always .next from some perspective?" + // TODO: Level at which "not" is applied. + export const not = Ray.Function.Self.Binary((a, b) => b); + + /** + * Placing existing structure on a new Reference, Boundary or Vertex: + */ + // TODO: These should allow as_vertex as zero-ary, but that means self = self_reference, not none. ? + /** + * Moving `self` to `.self` on an abstraction layer (higher). As a way of being able to describe `self`. + * + * TODO: the .reference might need two levels of abstraction higher, one to put it at the .self, another to reference that thing? (Depends a bit on the execution layer) + */ + export const as_reference = Ray.Function.Self.Impl( + self => none().self = self + ); + + // TODO as_reference.as_vertex instead of as_vertex ignorant by default? + + export const as_vertex = Ray.Function.Self.Impl((self) => { + const vertex = Ray.Op.Zeroary.All.none(); + vertex.initial = self_reference; + vertex.terminal = self_reference; + vertex.self = self; // TODO: Like this, ignorant vs non-ignorant? What to do here? + + return vertex; + }); + export const as_terminal = Ray.Function.Self.Impl((self) => { + const terminal = Ray.Op.Zeroary.All.none(); + terminal.initial = self_reference; + // terminal.terminal = none; + terminal.self = self; + + return terminal; + }); + export const as_initial = Ray.Function.Self.Impl((self) => { + const initial = Ray.Op.Zeroary.All.none(); + // initial.initial = none; + initial.terminal = self_reference; + initial.self = self; + + return initial; + }); + + /** + * Any arbitrary direction, where .not (or reversing the direction) relies on some memory mechanism + */ + export const memoized = Ray.Function.Self.Impl( + self => { throw new NotImplementedError(); } + ); + } + } + + export const self_reference = Ray.Function.Self.Impl( + self => self + ); + + // export const Boundary = { INITIAL: Type.INITIAL, TERMINAL: Type.TERMINAL }; TODO: LIST O + +} + +export default Ray; \ No newline at end of file diff --git a/environments/python/_temp/Ray2.ts b/environments/python/_temp/Ray2.ts new file mode 100644 index 0000000..3a1d57d --- /dev/null +++ b/environments/python/_temp/Ray2.ts @@ -0,0 +1,545 @@ +import _ from "lodash"; +import {NotImplementedError, PreventsImplementationBug} from "./errors/errors"; +import {InterfaceOptions} from "./OrbitMinesExplorer"; +import JS from "./JS"; + +export class Ray { + // /** + // * Moves `this.self` and `this.self.self` to a new line. + // * + // * [ |--] this.self ----- this.self.self [--|--] + // * ______ (<- initial pointer) + // */ + // as_initial = (): Ray.Any => { + // if (this.is_none()) { + // throw new PreventsImplementationBug('Should be implemented at some point ; Just return an empty vertex'); + // } + // if (this.dereference.is_none()) { + // // TODO: Need some intuition for this check + // const vertex = this.___as_vertex();1 + // + // if (vertex.type !== RayType.VERTEX) + // throw new PreventsImplementationBug(); + // + // return vertex.follow(Ray.directions.previous); + // } + // + // const [terminal_vertex, initial_vertex] = this.___as_vertices(); + // + // if (initial_vertex.type !== RayType.VERTEX) + // throw new PreventsImplementationBug(); + // if (terminal_vertex.type !== RayType.VERTEX) + // throw new PreventsImplementationBug(); + // + // initial_vertex.compose(terminal_vertex); + // + // // TODO BETTER DEBUG + // + // return initial_vertex.follow(Ray.directions.previous); + // } + /** + * Moves `this.self` and `this.self.self` to a new line. + * + * [ |--] this.self.self ----- this.self [--|--] + * _____ (<- terminal pointer) + */ + as_terminal = (): Ray.Any => { + if (this.is_none()) { + throw new PreventsImplementationBug('Should be implemented at some point ; Just return an empty vertex'); + } + if (this.dereference.is_none()) { + throw new PreventsImplementationBug(); + } + + const [terminal_vertex, initial_vertex] = this.___as_vertices(); + + if (initial_vertex.type !== RayType.VERTEX) + throw new PreventsImplementationBug(); + if (terminal_vertex.type !== RayType.VERTEX) + throw new PreventsImplementationBug(); + + initial_vertex.compose(terminal_vertex); + + return Ray.directions.next(terminal_vertex); + } + private ___as_vertices = (): [Ray, Ray] => { + if (!Ray.is_orbit(this.self, this.self.self.self)) + throw new PreventsImplementationBug('Is there a use-case for this? Probably not?'); //TODO + + // TODO NOTE: THE ORDER OF `this.self` first matters here. + return [this.self.___as_vertex(), this.___as_vertex()]; + } + private ___as_vertex = (): Ray.Any => { + const vertex = Ray.vertex().o({ js: '___as_vertex' }).as_reference().o({ js: '___as_vertex.#' }); + + return this.___ignorantly_equivalent(vertex); + } + ___ignorantly_equivalent = (ref: Ray.Any): Ray.Any => { + ref.self.self = this.self.as_arbitrary(); + this.self.self = ref.self.as_arbitrary(); + + return ref; + } + + /** [ ] */ static None = () => new Ray({ }); + /** [--?--] */ static vertex = (value: JS.ParameterlessFunction = Ray.None) => { + /** [ ] */ const vertex = Ray.None(); + /** [-- ] */ vertex.initial = vertex.___empty_initial(); + /** [ ? ] */ vertex.vertex = value; + /** [ --] */ vertex.terminal = vertex.___empty_terminal(); + + /** [--?--] */ return vertex; + } + /** [ |-?] */ static initial = () => Ray.Any.vertex().initial; + /** [?-| ] */ static terminal = () => Ray.Any.vertex().terminal; + + // TODO; Temp placeholders for now - & BETTER DEBUG + ___empty_initial = () => new Ray({ vertex: Ray.Any.None, terminal: this.as_arbitrary() }).o({ debug: 'initial ref'}).as_arbitrary(); + ___empty_terminal = () => new Ray({ vertex: Ray.Any.None, initial: this.as_arbitrary() }).o({ debug: 'terminal ref'}).as_arbitrary(); + + + + // TODO: Returns the ref, since it still holds the information on how they're not the same??? - Need some intuitive way of doing this? + + static equivalent = Ray.Function.Impl((initial, terminal) => { + + /** + * The simplest case, is where both sides are only aware of themselves (on .vertex). The only thing we need to do is turn an Orbit, to an Orbit which repeats every 2 steps, the intermediate step being the other thing. + * + * Or in textual terms something like: + * - A single Orbit: `(A.self = A) | (B.self = B)` (i.e. A.is_none && B.is_none) + * - To: `(A.self = B) | (B.self = A)` + * + * Basically turns `A` into a reference to `B`, and `B` into a reference to `A`. + */ + const ignorant_equivalence = (): Ray.Any => { + return initial.___ignorantly_equivalent(terminal); + } + + // 2x Ray.None -> Turn into 2 empty references, referencing each-other. + if ( + initial.dereference.is_none() && terminal.dereference.is_none() + && !(initial.type === RayType.VERTEX && terminal.type === RayType.VERTEX) + ) { + // throw new PreventsImplementationBug(`${initial.type} / ${terminal.type}`) + return ignorant_equivalence(); + } + + // Two structures, which have `ref.self = Ray.None` -> Turn into two structures which are on a line in between them. + if (initial.dereference.is_none()) { + const vertex = Ray.vertex().o({ js: '___as_vertex' }).as_reference().o({ js: '___as_vertex.#' }); + vertex.self.self = initial.self.as_arbitrary(); + initial.self.self = vertex.self.as_arbitrary(); + + // initial.equivalent(terminal); + // return terminal; + } + if (terminal.dereference.is_none()) { + const vertex = Ray.vertex().o({ js: '___as_vertex' }).as_reference().o({ js: '___as_vertex.#' }); + vertex.self.self = terminal.self.as_arbitrary(); + terminal.self.self = vertex.self.as_arbitrary(); + + // initial.equivalent(terminal.___as_vertex()); + // return terminal; + } + + if (initial.is_boundary() && initial.dereference.is_boundary()) { + throw new NotImplementedError(); + initial.as_terminal(); + //.follow(Ray.directions.previous).compose(terminal.dereference); + // return terminal; + } + + if ( + initial.dereference.type !== RayType.VERTEX + || terminal.dereference.type !== RayType.VERTEX + || initial.dereference.self === initial.self + || terminal.dereference.self === terminal.self + ) { + throw new PreventsImplementationBug(`[${initial.type}]`) + } + + if (Ray.directions.next(initial).type !== RayType.TERMINAL || Ray.directions.previous(terminal).type !== RayType.INITIAL) { + throw new NotImplementedError(); + initial.dereference.push_back(terminal.dereference); // TODO: NON-PUSH-BACK VARIANT? (Just local splits?) + return terminal; + // throw new PreventsImplementationBug(` + // [${initial.type}](${initial.follow_direction().any.js}) + // / [${initial.dereference.type}] {${[...initial.dereference.traverse()].map(ref => ref.self.follow_direction().any.js)}} + // -> ${terminal.type} (${terminal.follow_direction().any.js}) + // / [${terminal.dereference.type}]`) + } + + // if (terminal.self.any.js === 'D') + // throw new PreventsImplementationBug(); + + initial.dereference.compose(terminal.dereference); + + return terminal; + + // initial.dereference.compose() + // return terminal; + }); + equivalent = Ray.equivalent.as_method(this); + + + /** + * .next + */ + next = (step: Ray.FunctionImpl = Ray.directions.next): Ray.Any => { + if (step(this).self.self.is_none()) + return Ray.None(); + + let current = true; + for (let ray of this.traverse(step)) { + if (current) { + current = false; + continue; + } + return ray; + } + // return [...this.traverse(step)][1] ?? Ray.None(); // TODO BAD + return Ray.None(); + } + // @alias('end', 'result', 'back') + last = (step: Ray.FunctionImpl = Ray.directions.next): Ray.Any => { + const next = this.next(step); + return next.is_some() ? next.last(step) : this; + } + + + get reverse(): Ray.Any { + const copy = this;//TODO.copy(); + + // TODO: Do we do this lazy by default? Just using refs??? - Or abstract this elsewhere to decide what to do + const swap = copy.initial; + copy.initial = copy.terminal.as_arbitrary(); + copy.terminal = swap.as_arbitrary(); + // TODO: This doesn't actually work + + return copy; + } + + *___next({ + step = Ray.directions.next, + } = {}): Generator { + for (let current of Ray.traverse({ + initial: this.as_arbitrary(), + step + })) { + + // TODO: You can do this non-locally with a pass over the history. This way it's local, but we''ll have to find a good example of why this might not go that well. (As this would match to any empty vertices, and maybe more) + + // TODO: Could also check for none.. + const previous = current.previous(); + + if (previous.is_vertex() && !Ray.is_orbit(previous.self, current)) { + yield previous; + } + } + } + *___map(map: (vertex: Ray.Any) => T, { + step = Ray.directions.next, + } = {}): Generator { + for (let vertex of this.___next({step})) { + yield map(vertex); + } + } + + static step_function = ( + step: Ray.FunctionImpl + ): Ray.Function => { + const step_from_boundary = (from: Ray.Any, to: Ray.Any): Ray.Any => { + switch (to.type) { + /** + * Dereferencing is likely in many cases quickly subject to infinite stepping (similar to INITIAL -> INITIAL, TERMINAL -> TERMINAL, VERTEX -> VERTEX. (Could be that this means that there's no continuation, a self-reference defined here, or it's some mechanism of halting.) + * + * - TODO: Simple example of infinitely finding terminals, or a reference to 'nothing - infinitely'. + * - TODO: If both are references, allow deref of both? + */ + case RayType.REFERENCE: { + throw new NotImplementedError(); + } + + /** + * A possible continuation + * INITIAL/TERMINAL -> possible previous - TERMINAL.self.initial (pass to step) + * TERMINAL/INITIAL -> possible next - INITIAL.self.terminal (pass to step) + */ + case RayType.TERMINAL: { + return Ray.follow_direction[RayType.TERMINAL](to); + } + case RayType.INITIAL: { + return Ray.follow_direction[RayType.INITIAL](to); + } + + /** + * This is the most interesting case: Many possible continuations (from the perspective of a boundary (INITIAL/TERMINAL)). + * + * NOTE: + * - There are many ways of actually implementing this. This is one which ensures the checks needed to traverse arbitrary continuations is always local (with the trade-off that you can't disambiguate between structure on edges vs structure on vertices by default). Though this can be imposed with something else. (TODO) + * + * @see = TODO + */ + case RayType.VERTEX: { + // TODO: Could check if self.self is Orbit. + + /** + * Simplest check, ensure we're coming from some place which splits into many branches + * @see = TODO + */ + if (Ray.is_orbit(from.self, to.self.self)) { + // TODO: Branch to previous.next + // this.next_pointer(Ray.directions.previous), this.next_pointer(Ray.directions.next) + + throw new NotImplementedError(); + } + + /** + * This is the one which disallows structure on edges, and assumes a vertex it finds, necessarily as additional vertices we're looking for. (But we don't need to keep track of where we are like this ; TODO: Implement variant which checks back over branch.previous() to allow for this) + * @see = TODO + */ + if ( + to.dereference.is_boundary() // Whether there's a continuation defined on the vertex. + && Ray.is_orbit(to.self, to.self.self.self) + ) { + // default pointer + // const default_pointer = (): Ray.Any[] => { + // return pointer.terminal.is_none() ? [] : [pointer]; + // } + // TODO: Split of options.step(terminal) & new Ray({ + // initial: terminal.as_arbitrary(), + // vertex: pointer.as_arbitrary(), + // terminal: () => terminal.dereference + // }) + throw new NotImplementedError(); + } + + return step(to); + } + } + } + + return Ray.Function.WithMemory( + to_step => { + const to = to_step.dereference; + + throw new NotImplementedError(); + /** + * Cannot determine what to do without context of where we are. + */ + if (to.is_none()) { + return Ray.None(); + } + + const from_step = to_step.previous(); // TODO ONLY NEEDS ONE-STEP MEMORY ; Or could be implemented as two values as each step, or??? + + const from = from_step.dereference; + + switch (from.type) { + case RayType.REFERENCE: { + throw new NotImplementedError();// previous.dereference, + } + case RayType.INITIAL: + case RayType.TERMINAL: { + return step_from_boundary(from, to); + } + case RayType.VERTEX: { + return step(to); + } + } + } + ); + } + + static *traverse(options = { + initial: Ray.Any.None, + step: Ray.Any.directions.next, + // branch: { + // // @alias('pruning', 'mapping', 'filtering') + // prune: (branches: Ray.Any): Ray.Any => branches, + // // @alias('next', 'selection', 'tactic', 'strategy') + // next: (branches: Ray.Any): Ray.Any => branches.first(), + // } + }): Generator { + + /** + * An arbitrary Ray of (accessible) (possible) next steps to perform in traversal. + */ + // @alias('cursor(s)', 'branch(es)', 'selection(s)') + let branches: Ray.Any = Ray.step_function(options.step).as_ray(options.initial()); // TODO; This can be used to copy? + let branch = branches; + + while (true) { + // /** + // * Branch *Pruning, Mapping, ..., Filtering* + // */ + // branches = options.branch.prune(branches); // TODO: Could hold history + // + // /** + // * Branch *Selection, Tactic, ..., Strategy* + // */ + // + // /** + // * An arbitrary Ray of (requested) next steps to perform in parallel/.../superposition (with respect to all the other branches). + // */ + // const branches_to_traverse: Ray.Any = options.branch.next(branches); + // + // /** + // * Make copies of our traversal for each selected branch + // */ + // // TODO + // + // /** + // * Split off traversal to each branch, selecting their respective . + // */ + // + // let branch: Ray.Any = branches_to_traverse; //TODO + + branch.self = branch.next().as_arbitrary(); + + yield branch; + + if (branch.self.is_terminal()) + break; + + // After step, if IS_TERMINAL, nothing. assume halting (!!!at prune step ?) + + // const terminal = branch.follow(); + + // branch.self = terminal; + + /** + * if (branches.length === 0) { + * remove(pointers); // pointer, + * } else { + * ref.self = branches[0].as_arbitrary(); + * + * if (branches.length !== 1) { + * pointers.push(...branches.slice(1).map(b => Ray.Any.vertex(b.as_arbitrary()))); + * } + * } + */ + } + + // if (pointers.length !== 0) + // throw new PreventsImplementationBug(`${pointers.length} left`) + } + + all = (step: Ray.FunctionImpl = Ray.directions.next): { [key: string | symbol]: Ray.Any } & any => { + return new Proxy(this, { + + get(self: Ray.Any, p: string | symbol, receiver: any): any { + + // TODO: Could return arbitrary structure (or in other method than .all?) + return self.___map(ref => ref.any[p], {step}); + }, + + /** + * Can't overload things like '-=' for anything but things that return numbers... ; So just apply a general function instead. + */ + set(self: Ray.Any, p: string | symbol, newValue: any, receiver: any): boolean { + for (let ref of self.___next({step})) { // TODO; This needs to either be dynamically, or just a simple shut-off for circular ones. + ref.any[p] = JS.is_function(newValue) ? newValue(ref.any[p]) : newValue; + } + + return true; + }, + + + deleteProperty(self: Ray.Any, p: string | symbol): boolean { + throw new NotImplementedError(); + + return true; + } + // TODO: What do these other methods on Proxy do??? + }); + + } + + /** + * Move to a JavaScript object, which will handle any complexity of existing JavaScript objects, and allows one to abstract any values contained in the {vertex} to the usual JavaScript interface. - More usual to how one thinks about functions, ..., properties. + */ + get any(): { [key: string | symbol]: Ray.Any } & any { return this.self.proxy(); } + get ___any(): { [key: string | symbol]: Ray.Any } & any { return this.proxy(); } + + protected _proxy: any; + protected _dirty_store: { [key: string | symbol]: object } = {} + protected proxy = (constructor?: JS.ParameterlessConstructor): T & { [key: string | symbol]: Ray.Any } => { // TODO: + // TODO: IMPLEMENT SPLAT... {...ray.any} + return this._proxy ??= new Proxy(this, { + + get(self: Ray.Any, p: string | symbol, receiver: any): any { + + // throw new NotImplementedError(); + return self._dirty_store[p]; + // return self.as_arbitrary(); + }, + set(self: Ray.Any, p: string | symbol, newValue: any, receiver: any): boolean { + // TODO: + // self._dirty_store[p] = JS.is_function(newValue) ? newValue(self._dirty_store[p]) : newValue; + // throw new NotImplementedError(); + self._dirty_store[p] = newValue; + + return true; + }, + + deleteProperty(self: Ray.Any, p: string | symbol): boolean { + if (!(p in self._dirty_store)) { + return false; + } + + delete self._dirty_store[p]; + return true; + } + // TODO: What do these other methods on Proxy do??? + }) as T; + } + + /** + * + * - Don't assume we can track back any reference to this thing. Just destroy it, set everything to None. And let anything else deal with the consequences of the deletion. + * + * TODO: + * - TODO: Maybe want a way to destroy from one end, so that if other references try to look, they won't find additional structure. - More as a javascript implementation quick if anything? + * - Could lazily try to find references. + * - Implement on proxy for 'delete ray' + */ + delete = (): Ray.Any => { + this.self.initial = Ray.None; + this.self.self = this.self.self_reference; + this.self.terminal = Ray.None; + // TODO: REMOVE THESE + this.self._proxy = undefined; + this.self._dirty_store = {}; + + // Removes the current reference to it. + this.self = this.self_reference; + + return this; + } + + ___dirty_all(c: Ray.Any[]): Ray.Any[] { + if (c.filter(a => a.label === this.label).length !== 0) { + return c; + } + + c.push(this); + + if (this.initial.as_reference().is_some()) + this.initial.___dirty_all(c); + if (this.vertex.as_reference().is_some()) + this.vertex.___dirty_all(c); + if (this.terminal.as_reference().is_some()) + this.terminal.___dirty_all(c); + + return c; + } + +} + +// default = (fn: () => any): any => self.match({ +// Some: (a) => a, +// None: () => fn() +// }) +// diff --git a/environments/python/_temp/Visualization.tsx b/environments/python/_temp/Visualization.tsx new file mode 100644 index 0000000..11ae1c3 --- /dev/null +++ b/environments/python/_temp/Visualization.tsx @@ -0,0 +1,306 @@ +import React, {useCallback, useEffect, useRef, useState} from 'react'; +import {Canvas} from "@react-three/fiber"; +import {ACESFilmicToneMapping, SRGBColorSpace} from "three"; +import WEBGL from "three/examples/jsm/capabilities/WebGL"; +import {Children, value} from "../../lib/typescript/React"; +import isWebGLAvailable = WEBGL.isWebGLAvailable; +import {Ray} from "./Ray"; +import {useSearchParams} from "react-router-dom"; +import {PaperProps} from "../../lib/paper/Paper"; +import {toJpeg, toPng} from "html-to-image"; +import {Col, Row} from "../../lib/layout/flexbox"; +import {Button} from "@blueprintjs/core"; + +export const NoWebGL = () => { + return
No WebGL
+} + +export type VisualizationProps = { + context: Omit, + alt: string, +} + +const Visualization = ({ray}: { ray: Ray.Any }) => { + return + + +} + +export const CachedVisualizationCanvas = ( + { + alt, + context, + children, + ...props + }: React.HTMLAttributes & Children & VisualizationProps +) => { + const ref = useRef(null); + const [useImage, setUseImage] = useState(true); + let generate; + try { + const [params] = useSearchParams(); + + generate = params.get('generate'); + } catch (e) { + generate = 'pdf'; + } + + const exportPng = useCallback(() => { + if (ref === null) + return; + + toPng(ref.current, { + cacheBust: true, + // backgroundColor: '#1C2127' + }) + .then((dataUrl) => { + const link = document.createElement('a') + link.download = `${alt}.png` + link.href = dataUrl + link.click() + }) + .catch((err) => { + console.log(err) + }); + }, [ref]); + + if (context.link === undefined) + throw 'Cannot have the paper\'s link be undefined when using a cached canvas.' + + const canvasUrl = `${context.link.replace("https://orbitmines.com", "")}/images/${alt}.png`; + + useEffect(() => { + // Just a quick hack to check if it's loaded (could draw it on the canvas from here, ?) + + const img = new Image(); + img.src = canvasUrl; + img.onerror = () => { + setUseImage(false); + }; + }, []); + + if (useImage) { + return + + + } + + return
+
+ + {children} + +
+ {generate === 'canvas' ? + {/* eslint-disable-next-line react/jsx-no-undef */} +
+} + +export const CanvasContainer = ({ + children, + ...props + }: React.HTMLAttributes & Children) => (
+ {children} +
) + +export const ThreeJS = ({ref, children}: Children & { ref?: React.MutableRefObject}) => { + // https://threejs.org/docs/#manual/en/introduction/WebGL-compatibility-check + if (!isWebGLAvailable()) + return ; + + // console.log('webgl2', isWebGLAvailable()); + + /* + https://docs.pmnd.rs/react-three-fiber/api/canvas + - Sets up a Scene and a Camera, the basic building blocks necessary for rendering + - Renders our scene every frame, you do not need a traditional render-loop + + - Canvas is responsive to fit the parent node, so you can control how big it is by changing the parents width and height, in this case #canvas-container. + */ + return + {children} + + {/*/!* https://threejs.org/docs/#api/en/objects/Mesh *!/*/} + {/**/} + {/* /!**/} + {/* https://threejs.org/docs/#api/en/lights/AmbientLight*/} + {/* - This light globally illuminates all objects in the scene equally.*/} + {/* - This light cannot be used to cast shadows as it does not have a direction.*/} + {/* *!/*/} + + {/* /!* https://threejs.org/docs/#api/en/lights/DirectionalLight *!/*/} + {/* /!**!/*/} + + {/* /!* https://threejs.org/docs/#api/en/geometries/BoxGeometry *!/*/} + {/* */} + + {/* /!* https://threejs.org/docs/#api/en/materials/MeshStandardMaterial *!/*/} + {/* */} + {/**/} + + {/**/} + + {/* https://docs.pmnd.rs/react-postprocessing/selection */} + {/**/} + {/* /!* https://docs.pmnd.rs/react-postprocessing/effect-composer *!/*/} + {/* */} + {/* */} + {/* */} + {/**/} + + {/**/} + + {/**/} + + {/**/} + + {/**/} + + {/**/} + {/* /!* https://threejs.org/docs/#api/en/materials/MeshStandardMaterial *!/*/} + {/* */} + {/**/} + {/**/} + {/* /!* https://threejs.org/docs/#api/en/materials/MeshStandardMaterial *!/*/} + {/* */} + {/**/} + {/**/} + {/* /!* https://threejs.org/docs/#api/en/materials/MeshStandardMaterial *!/*/} + {/* */} + {/**/} + + {/**/} + {/* /!* https://threejs.org/docs/#api/en/materials/MeshStandardMaterial *!/*/} + {/* */} + {/**/} + + {/**/} + + +} + +export const VisualizationCanvas = ( + { + children, + ...props + }: React.HTMLAttributes & Children +) => { + + return ( + // ThreeJS: https://threejs.org/ + // React Three Fiber: https://docs.pmnd.rs/react-three-fiber/api/objects + // https://github.com/pmndrs/drei#readme + + {children} + + ); +}; + +export default Visualization; \ No newline at end of file diff --git a/environments/python/_temp/debug/DebugCanvas.tsx b/environments/python/_temp/debug/DebugCanvas.tsx new file mode 100644 index 0000000..ad9345b --- /dev/null +++ b/environments/python/_temp/debug/DebugCanvas.tsx @@ -0,0 +1,230 @@ +import IEventListener from "../../js/react/IEventListener"; +import {VisualizationCanvas} from "../Visualization"; +import React, {useEffect, useReducer, useRef, useState} from "react"; +import {useHotkeys} from "../../js/react/hooks/useHotkeys"; +import {_Continuation, _Vertex, add, AutoVertex, circle, InterfaceOptions, Line, torus} from "../OrbitMinesExplorer"; +import {HotkeyConfig} from "@blueprintjs/core/src/hooks/hotkeys/hotkeyConfig"; +import {useThree} from "@react-three/fiber"; +import {Stats, StatsGl} from "@react-three/drei"; +import _ from "lodash"; +import Ray from "@orbitmines/rays" + +// TODO: What about showing disconnect when multiple things are rendered at the same position?? +// TODO: It's, rende rboth draw equivalence, then ignore the difference from either perspective or take some middle thing. - Line from both ends, also vertex? (or take the pos, take the x from one/other, y from the other/..) + +// TODO: Could be a function on Ray (any func really) +export const Render = ({ ray, Interface }: { ray: Ray.Any, Interface: Ray.Any }) => { + const initial: Required = ray.follow(Ray.directions.previous).render_options(Interface); + const vertex: Required = ray.render_options(Interface); + const terminal: Required = ray.follow().render_options(Interface); + + switch (ray.type) { + case RayType.REFERENCE: + return <_Vertex position={vertex.position} rotation={[0, 0, Math.PI / 2]} scale={vertex.scale / 2} color="#555555" /> + case RayType.INITIAL: { + + return + + <_Continuation {...vertex} /> + + } + case RayType.TERMINAL: { + // TODO; Rotation like this will not be picked up upon by other thing, calculate on the fly?? + return + + <_Continuation {...vertex} /> + + } + case RayType.VERTEX: { + return <_Vertex {...vertex} /> + } + } +} + +export const DebugInterface = ({ scale = 1.5 }: InterfaceOptions) => { + const ref = useRef(); + const hotkeyConfig = useHotkeys(); + + const { + gl: renderer, + camera, + scene, + raycaster + } = useThree(); + + const space_between = 20 * scale; + + const memory = false; + + const [Interface] = useState(Ray.vertex().o({ + selection: Ray.Any.vertex( + // () => Ray.Any.vertex().o2({ + // initial: { position: [-space_between, 0, 0], scale, rotation: [0, 0, Math.PI / 2] }, + // vertex: { position: [0, 0, 0], scale, color: '#FF55FF' }, + // terminal: { position: [space_between, 0, 0 ], scale, rotation: [0, 0, Math.PI / 2] } + // }) + ).o2({ + initial: { position: [-space_between, 0, 0], scale }, + vertex: { position: [0, 0, 0], scale, color: '#FF55FF' }, + terminal: { position: [space_between, 0, 0 ], scale } + }).as_reference().o({ + position: [0, 0, 0], + scale, + rotation: [0, 0, Math.PI / 6 ], + color: '#555555' + }), + rays: [] as Ray[], + stats: false, + cursor: { tick: false }, + controls: Ray.Any.vertex().o({ + hotkeys: [ + { + combo: ["a", "arrowleft"], global: true, label: "", onKeyDown: () => { + Interface.___any.selection = Interface.___any.selection.move((self: Ray.Any) => self.initial, memory, Interface); + } + }, + { + combo: ["d", "arrowright"], global: true, label: "", onKeyDown: () => { + Interface.___any.selection = Interface.___any.selection.move((self: Ray.Any) => self.terminal, memory, Interface); + } + }, + { + combo: ["delete"], global: true, label: "", onKeyDown: () => { + Interface.___any.rays = Interface.___any.Ray.filter((ray: Ray.Any) => Ray.Any.self.label !== Interface.___any.selection.self.label); // Should be automatic, this sort of thing + Interface.___any.selection = Interface.___any.selection.delete; + } + }, + { + combo: ["w", "arrowup"], global: true, label: "", onKeyDown: () => { + // TODO SHOULD BE ANOTHER DIRECTION AT THE FRAME? + Interface.___any.selection = Interface.___any.selection.move((self: Ray.Any) => self.self, memory, Interface); + } + }, + { + combo: ["e"], global: true, label: "", onKeyDown: () => { + const change = [0, 0, Math.PI / 10]; + console.log(Interface.___any.selection.self.initial.label) + + Interface.___any.selection = Interface.___any.selection.self.o2({ + initial: { rotation: add(Interface.___any.selection.self.initial.___any.rotation ?? [0, 0, 0], change) }, + terminal: { rotation: add(Interface.___any.selection.self.terminal.___any.rotation ?? [0, 0, 0], change) }, + }) + } + }, + { + combo: ["space"], global: true, label: "", onKeyDown: (e) => { + e.preventDefault(); + Interface.___any.rays = Interface.___any.selection.self.___dirty_all([]).map((ray: Ray.Any) => { + ray.___any.traversed = true; + return ray.as_reference(); + }); + } + }, + // { + // combo: ["s", "arrowdown"], global: true, label: "", onKeyDown: () => { + // Interface.___any.selection = Interface.___any.selection.move((self: Ray.Any) => self.as_reference().as_reference(), memory, Interface); + // } + // }, + { + combo: "/", global: true, label: "", onKeyDown: () => { + console.log('---------') + console.log(`Debugging: ${Interface.___any.selection.self.label} (type=${Interface.___any.selection.type})`) + console.log(`Ray.length at pos=[${Interface.___any.selection.render_options.position}]: ${Interface.___any.Ray.filter((ray: Ray.Any) => + _.isEqual( + Interface.___any.selection.render_options.position, + ray.render_options(Interface).position + ) + ).length} / ${Interface.___any.Ray.length}`) + console.log('ref', Interface.___any.selection) + console.log('ref.self', Interface.___any.selection.self) + + const debug: DebugResult = {}; + Interface.___any.selection.self.debug(debug); + console.log('ref.debug', debug); + Interface.___any.Ray.forEach((ray: Ray.Any) => Ray.Any.debug(debug)); + console.log('Ray.debug', debug); + + } + }, + { + combo: "f3", global: true, label: "Show stats Panel", onKeyDown: (e) => { + e.preventDefault(); + Interface.___any.stats = !Interface.___any.stats; + } + }, + ] as HotkeyConfig[] + }) + })); + + // useEffect(() => { + // TODO: Eventually goes over maximum size of react-debug callstack when updates each frame like this. + hotkeyConfig.set(...Interface.___any.controls.___any.hotkeys); + // }, [Interface.___any.controls.___any.hotkeys]); + + return <> + {Interface.___any.stats ? : <>} + + + + {/*{Interface.___any.Ray.map((ray: Ray.Any) => )}*/} + {Interface.___any.Ray.map((ray: Ray.Any) => )} + +} + +export const StatsPanels = () => { + const [_, forceUpdate] = useReducer((x) => !x, false); + + const parent = useRef(document.createElement('div')); + + useEffect(() => { + document.body.appendChild(parent.current); + + for (let i = 0; i < parent.current.children.length; i++) { + (parent.current.children[i] as any).style.cssText = `position: fixed; top: 0px; left: ${(i !== 0 ? 190 : 0) + 80 * i}px; cursor: pointer; opacity: 0.9; z-index: 10000;`; + } + + if (parent.current.children.length < 3) + forceUpdate(); + }, [_]); + + return <> + + {/**/} + + + +} + +export const DebugCanvas = ( + { + listeners = [], + }: { + listeners?: IEventListener[], + } +) => { + const listener: IEventListener = { + + } + + return
+
+ + + +
+} + +export const DebugExplorer = ( + { + listeners = [], + }: { + listeners?: IEventListener[], + } +) => { + + const listener: IEventListener = {} + + return +} diff --git a/environments/python/_temp/debug/QuickVisualizationInterface.tsx b/environments/python/_temp/debug/QuickVisualizationInterface.tsx new file mode 100644 index 0000000..64847c4 --- /dev/null +++ b/environments/python/_temp/debug/QuickVisualizationInterface.tsx @@ -0,0 +1,490 @@ +import React, {useRef, useState} from "react"; +import IEventListener from "../../js/react/IEventListener"; +import {VisualizationCanvas} from "../Visualization"; +import { + _Continuation, + _Vertex, + add, + add_, + AutoVertex, + circle, + InterfaceOptions, + Line, + torus +} from "../OrbitMinesExplorer"; +import {HotkeyEventOptions, useHotkeys} from "../../js/react/hooks/useHotkeys"; +import {Ray, RayType} from "../Ray"; +import {HotkeyConfig} from "@blueprintjs/core/src/hooks/hotkeys/hotkeyConfig"; +import _ from "lodash"; +import {useThree} from "@react-three/fiber"; +import {StatsPanels} from "./DebugCanvas"; +import JS from "../JS"; + +const ___index = (ray: Ray.Any): number => { + switch (ray.type) { + case RayType.REFERENCE: + return ray.___any.index ?? 0; + case RayType.INITIAL: + return ray.follow().___any.index ?? 0; + case RayType.TERMINAL: + return ray.follow(Ray.directions.previous).___any.index ?? 0; + case RayType.VERTEX: + return ray.___any.index ?? 0; + } +} + +export const Render2 = ({ ray, Interface, show = { initial: true, terminal: true } }: { ray: Ray.Any, Interface: Ray.Any, index: number, show?: { initial: boolean, terminal: boolean } }) => { + const index = ___index(ray); + let added = [15 * index, 60 * index, 0]; + + const initial: Required = ray.follow(Ray.directions.previous).render_options(Interface); + initial.position = add_(initial.position, added); + const vertex: Required = ray.render_options(Interface); + vertex.position = add_(vertex.position, added); + const terminal: Required = ray.follow().render_options(Interface); + terminal.position = add_(terminal.position, added); + + switch (ray.type) { + case RayType.REFERENCE: { + return <_Vertex position={vertex.position} rotation={[0, 0, Math.PI / 2]} scale={vertex.scale / 2} + color="#555555"/>; + } + case RayType.VERTEX: { + return + + <_Vertex {...vertex} /> + + + } + case RayType.INITIAL: { + return + + + <_Continuation {...vertex} + position={add_(vertex.position, !show.initial && ray.type === RayType.INITIAL ? [-15, -15, 0] : [0, 0, 0])}/> + + } + case RayType.TERMINAL: { + return + + <_Continuation {...vertex} + position={add_(vertex.position, !show.terminal && ray.type === RayType.TERMINAL ? [15, 15, 0] : [0, 0, 0])}/> + + } + default: { + return + } + } + switch (ray.type) { + case RayType.REFERENCE: + return <_Vertex position={vertex.position} rotation={[0, 0, Math.PI / 2]} scale={vertex.scale / 2} + color="#555555"/> + case RayType.INITIAL: { + const diff = [-15, -15, 0]; + + return + {!show.initial ? : + + + + + + } + + + + + + + + <_Continuation {...vertex} position={add_(vertex.position, !show.initial && ray.type === RayType.INITIAL ? [-15, -15, 0] : [0, 0, 0])} /> + + + {!show.initial ? : + + <_Continuation {...vertex} position={add_(vertex.position, diff)} color="#FF5555"/> + <_Continuation {...vertex} position={add_(vertex.position, diff, [-30, 0, 0])} scale={vertex.scale / 3 * 2} + color="#AA0000"/> + } + + } + case RayType.TERMINAL: { + const diff = [15, 15, 0]; + + return + {!show.terminal ? : + + + + + + + + } + + + + + + <_Continuation {...vertex} position={add_(vertex.position, !show.terminal && ray.type === RayType.TERMINAL ? [15, 15, 0] : [0, 0, 0])} /> + + + {!show.terminal ? : + + <_Continuation {...vertex} position={add_(vertex.position, diff)} color="#5555FF" /> + <_Continuation {...vertex} position={add_(vertex.position, diff, [30, 0, 0])} scale={vertex.scale / 3 * 2} color="#AA0000"/> + } + + } + } +} + +export const QuickVisualizationInterface = ({scale = 1.5}: InterfaceOptions) => { + const ref = useRef(); + const hotkeyConfig = useHotkeys(); + + const { + gl: renderer, + camera, + scene, + raycaster + } = useThree(); + + const space_between = 20 * scale; + + const [Interface] = useState(Ray.vertex().o({ + selection: + // The things selected + Ray.None() + // Our cursor + .as_reference().o({ + position: [0, 0, 0], + scale, + rotation: [0, 0, Math.PI / 6 ], + color: '#555555' + }) + // A reference to our cursor + .as_reference(), + rays: [] as Ray[], + stats: false, + cursor: { + tick: false + }, + add: (ray: Ray.Any) => { + (Interface.any.selection as Ray).self.self = ray.self.as_arbitrary(); + Interface.any.Ray.push(...[ray.follow(Ray.directions.previous), ray, ray.follow()]); + }, + controls: { + hotkeys: [ + { + + combo: ["space"], global: true, label: "", onKeyDown: () => { + const { selection, rays } = Interface.any; + + if (selection.self.is_none()) { + console.log('hi'); + + Interface.any.add( + Ray.vertex().o2({ + initial: { position: [-space_between, 0, 0], scale, color: 'orange' }, + vertex: { index: 0, position: [0, 0, 0], scale, color: 'orange' }, + terminal: { position: [space_between, 0, 0 ], scale, color: 'orange' }, + }).as_reference() + ); + + } + } + }, + { + combo: [ + "w", + "a", + "s", + "d" + ], global: true, label: "", preventDefault: true, onKeyDown: (event: any, { pressed }: HotkeyEventOptions) => { + const selection = Interface.any.selection as Ray; + + // TODO These should be Rays at some point + const directions = [ + [["a"], ["d"]], // TODO These superpositions on initial/terminal side + [["s"], ["w"]], + // TODO Then expand these to any-dimensional + ]; + + const isFollowing = directions.map( + ([initial, terminal]) => { + const toInitial = initial.some(option => pressed.includes(option)); + const toTerminal = terminal.some(option => pressed.includes(option)); + + if (toInitial === toTerminal) + return 0; + + return toInitial ? -1 : 1; + } + ); + + // TODO: .all(), which includes the reference?? ; since selection can be arbitrary structure too. + // selection.any.position = add(selection.any.position, [ + // space_between * isFollowing[0], + // space_between * isFollowing[1], + // 0 + // ]); + // selection.all().position = (position: [number, number, number]) => add(position, [ + // space_between * isFollowing[0], // TODO These selections automatic + // space_between * isFollowing[1], + // 0 + // ]); + // selection.self.any.position = add(selection.self.any.position, [ + // (space_between / 2) * isFollowing[0], // TODO These selections automatic + // (space_between / 2) * isFollowing[1], + // 0 + // ]); + if (isFollowing[0] !== 0) { + // TODO: Should be on here compose(terminal).all().position = + const position = Interface.any.selection.dereference.any.position; + const to_add = [(space_between * 2) * isFollowing[0], 0, 0]; + + const ref = Ray.vertex().o2({ + initial: { position: add_(position, to_add, [-space_between, 0, 0]), scale, color: 'orange' }, + vertex: { index: 0, position: add_(position, to_add, [0, 0, 0]), scale, color: 'orange' }, + terminal: { position: add_(position, to_add, [space_between, 0, 0 ]), scale, color: 'orange' }, + }).as_reference(); + + const [initial, terminal] = isFollowing[0] === -1 ? + [ref, selection.dereference,] : [selection.dereference, ref]; + + initial.compose(terminal); + + Interface.any.add(ref); + } + } + }, + { + combo: [ + "arrowright", + "arrowleft", + "arrowup", + "arrowdown" + ], global: true, label: "", preventDefault: true, onKeyDown: (event: any, { pressed }: HotkeyEventOptions) => { + const selection = Interface.any.selection as Ray; + + // TODO These should be Rays at some point + const directions = [ + [["arrowleft"], ["arrowright"]], // TODO These superpositions on initial/terminal side + [["arrowdown"], ["arrowup"]], + // TODO Then expand these to any-dimensional + ]; + + const isFollowing = directions.map( + ([initial, terminal]) => { + const toInitial = initial.some(option => pressed.includes(option)); + const toTerminal = terminal.some(option => pressed.includes(option)); + + if (toInitial === toTerminal) + return 0; + + return toInitial ? -1 : 1; + } + ); + + // TODO: .all(), which includes the reference?? ; since selection can be arbitrary structure too. + // selection.any.position = add(selection.any.position, [ + // space_between * isFollowing[0], + // space_between * isFollowing[1], + // 0 + // ]); + // selection.all().position = (position: [number, number, number]) => add(position, [ + // space_between * isFollowing[0], // TODO These selections automatic + // space_between * isFollowing[1], + // 0 + // ]); + selection.self.any.position = add(selection.self.any.position, [ + (space_between / 2) * isFollowing[0], // TODO These selections automatic + (space_between / 2) * isFollowing[1], + 0 + ]); + } + }, + { + combo: [ + "ctrl + arrowright", + "ctrl + arrowleft", + "ctrl + arrowup", + "ctrl + arrowdown" + ], global: true, label: "", preventDefault: true, onKeyDown: (event: any, { pressed }: HotkeyEventOptions) => { + const { selection, rays } = Interface.any; + + // TODO These should be Rays at some point + const directions = [ + [["arrowleft"], ["arrowright"]], // TODO These superpositions on initial/terminal side + [["arrowdown"], ["arrowup"]], + // TODO Then expand these to any-dimensional + ]; + const isFollowing = directions.map( + ([initial, terminal]): Ray.FunctionImpl => { + const toInitial = initial.some(option => pressed.includes(option)); + const toTerminal = terminal.some(option => pressed.includes(option)); + + if (toInitial === toTerminal) + return Ray.directions.none; + + return toInitial ? Ray.directions.previous : Ray.Any.directions.next; + } + ); + + // TODO ; ANY NUMBER/structure CURSOR + const next = (Interface.any.selection as Ray).dereference.follow(isFollowing[0]); + if (next.self.is_some()) { + console.log('a'); + (Interface.any.selection as Ray).self.self = next.self.as_arbitrary(); + } + + console.log(isFollowing) + + // selection.all().position = (position: [number, number, number]) => add(position, [ + // space_between * isFollowing[0], // TODO These selections automatic + // space_between * isFollowing[1], + // 0 + // ]); + } + }, + { + combo: "/", global: true, label: "", preventDefault: true, onKeyDown: () => { + console.log(Interface.any.Ray.map((ray: Ray.Any) => Ray.Any.render_options(Interface))) + // console.log('---------') + // console.log(`Debugging: ${Interface.any.selection.self.label} (type=${Interface.any.selection.type})`) + // console.log(`Ray.length at pos=[${Interface.any.selection.render_options(Interface).position}]: ${Interface.any.Ray.filter((ray: Ray.Any) => + // _.isEqual( + // Interface.any.selection.render_options.position, + // ray.render_options(Interface).position + // ) + // ).length} / ${Interface.any.Ray.length}`) + // console.log('ref', Interface.any.selection) + // console.log('ref.self', Interface.any.selection.self) + // + // const debug: DebugResult = {}; + // Interface.any.selection.self.debug(debug); + // console.log('ref.debug', debug); + // Interface.any.Ray.forEach((ray: Ray.Any) => Ray.Any.debug(debug)); + // console.log('Ray.debug', debug); + + } + }, + { + combo: "f3", global: true, label: "Show stats Panel", preventDefault: true, onKeyDown: (e) => { + e.preventDefault(); + Interface.any.stats = !Interface.any.stats; + } + }, + ] as HotkeyConfig[] + } + }).as_reference()); + + // useEffect(() => { + // TODO: Eventually goes over maximum size of react-debug callstack when updates each frame like this. + hotkeyConfig.set(...Interface.any.controls.hotkeys); + // }, [Interface.any.controls.___any.hotkeys]); + + const index = ___index(Interface.any.selection); + const added = [15 * index, 60 * (index + 1), 0]; + + return <> + {Interface.any.stats ? : <>} + + + + {/*{Interface.any.Ray.map((ray: Ray.Any) => )}*/} + + {Interface.any.Ray.map((ray: Ray.Any) => )} + + {/**/} + {/* */} + {/* */} + {/* {Interface.any.Ray.___map((ray: Ray.Any, index: number) => )}*/} + {/* */} + {/**/} + + {/**/} + {/* */} + {/* */} + {/* {Interface.any.Ray.___map((ray: Ray.Any, index: number) => )}*/} + {/* */} + {/**/} + +} + +const QuickVisualizationCanvas = ( + { + listeners = [], + }: { + listeners?: IEventListener[], + } +) => { + const listener: IEventListener = { + + } + + return
+ {/**/} + {/* */} + {/* */} + {/**/} + + + + +
+} + +export const QuickVisualizationExplorer = ( + { + listeners = [], + }: { + listeners?: IEventListener[], + } +) => { + + const listener: IEventListener = {} + + return +} + +export default QuickVisualizationCanvas; \ No newline at end of file diff --git a/environments/python/_temp/external/chyp/Chyp.spec.ts b/environments/python/_temp/external/chyp/Chyp.spec.ts new file mode 100644 index 0000000..c98e088 --- /dev/null +++ b/environments/python/_temp/external/chyp/Chyp.spec.ts @@ -0,0 +1,28 @@ + +describe("Chyp", () => { + describe(".Graph", () => { + test(".set_inputs", () => { + // const graph = Chyp.Graph.new() + // .set_inputs(Ray.vertex().o({ js: 'B' }).as_arbitrary()) + // .set_inputs(Ray.vertex().o({ js: 'A' }).as_arbitrary()); + // + // expect(graph.inputs.as_reference().any.js).toBe('A'); + // expect(graph.initial.as_reference().any.js).toBe('A'); + // expect(graph.inputs.as_reference().any.js).toBe(graph.initial.as_reference().any.js); + // expect(graph.inputs).toBe(graph.initial); + }); + // + }); + // describe(".Rule", () => { + // test(".name", () => { + // let rule = Chyp.Rule.new(); + // rule.name = 'test'; + // + // expect(rule.name).toBe('test'); + // expect(rule.reverse.any.name).toBe('-test'); + // // expect(rule.reverse.reverse.any.name).toBe('test'); + // + // }); + // }); +}); + diff --git a/environments/python/_temp/external/chyp/Chyp.ts b/environments/python/_temp/external/chyp/Chyp.ts new file mode 100644 index 0000000..8c5f5d6 --- /dev/null +++ b/environments/python/_temp/external/chyp/Chyp.ts @@ -0,0 +1,155 @@ +import {Ray} from "../../explorer/Ray"; +import {NotImplementedError} from "../../explorer/errors/errors"; +import JS from "../../explorer/JS"; + +/** + * An interface from Aleks Kissinger's [Chyp (Cospans of HYPergraphs)](https://github.com/akissinger/chyp) to (OrbitMines') Ray. + * + * **What is this?, What are Rays?** + * + * See the `README.md` at [github.com/orbitmines/orbitmines.com](https://github.com/orbitmines/orbitmines.com). Or locally: [README.md](../../../../README.md) + * + * **Why is this interface here?** + * + * Note that this is mainly here for reference to the existing Chyp codebase - for anyone who understands that structure, to quickly translate that knowledge into how Rays work. - Other than that functionality, everything here should be considered as deprecated. Or considered as a legacy translation, which will be accessibly through OrbitMines. + */ + +/** TODO errors probably hooked as a boolean, one successful the other not */ +export class GraphError extends Error {} +export class RuleError extends Error {} +export class RuntimeError extends Error {} +export class StopIteration extends Error {} + +export namespace Chyp { + + export type VData = Vertex; export class Vertex extends Ray { + + static new = (): Vertex => { + return new Vertex(); + } + + protected constructor() { + super(); + } + + // Vertex.in_edges = Ray.initial + get in_edges(): Ray.Any { return this.initial; } set in_edges(ray: JS.ParameterlessFunction) { this.initial = ray; } + + // Vertex.out_edges = Ray.terminal + get out_edges(): Ray.Any { return this.terminal; } set out_edges(ray: JS.ParameterlessFunction) { this.terminal = ray; } + + } + + export type EData = Edge; export class Edge extends Ray { + + // Edge.source = Ray.initial + get source(): Ray.Any { return this.initial; } set source(ray: JS.ParameterlessFunction) { this.initial = ray; } + get s(): Ray.Any { return this.initial; } set s(ray: JS.ParameterlessFunction) { this.initial = ray; } + + // Edge.targets = Ray.terminal + get target(): Ray.Any { return this.terminal; } set target(ray: JS.ParameterlessFunction) { this.terminal = ray; } + get t(): Ray.Any { return this.terminal; } set t(ray: JS.ParameterlessFunction) { this.terminal = ray; } + + } + + export class Match extends Ray { + + /** + * | <-- (vertex/edge)_map to (vertex/edge)_image + * _____|_____ + * / | \ + * Domain: [--|--][--|--][--|--] <-- Any of the matching structure on 'domain' is '_map'. + * | | | (is_total = true, if everything is on '_map') + * | | | + * Codomain: [--|--][--|--][--|--] <-- Any of the matching structure on 'codomain' is '_image'. + * | | | (is_surjective = true, if everything is on '_image') + */ + + static new = (): Match => { + return new Match(); + } + + protected constructor() { + super(); + } + + // Match.domain = Ray.initial + get domain(): Ray.Any { return this.initial; } set domain(ray: JS.ParameterlessFunction) { this.initial = ray; } + + // Match.codomain = Ray.terminal + get codomain(): Ray.Any { return this.terminal; } set codomain(ray: JS.ParameterlessFunction) { this.terminal = ray; } + + } + + export class Rule extends Ray { + + static new = (): Rule => { + return new Rule(); + } + + protected constructor() { + super(); + } + + // Rule.lhs = Ray.initial + get lhs(): Ray.Any { return this.initial; } set lhs(ray: JS.ParameterlessFunction) { this.initial = ray; } + + // Rule.rhs = Ray.terminal + get rhs(): Ray.Any { return this.terminal; } set rhs(ray: JS.ParameterlessFunction) { this.terminal = ray; } + + /** + * TODO: We can use the same implementation to rewrite where there's not necessarily a match between initial/terminal side of a rule. + * + * TODO: In general, we can just assume each lhs, has some link to some rhs, and not go through 'The Rule', and simply if lhs terminates, it terminates, if additional ones are initialized through rhs, so be it. - Set this up after this implementation where we reference an entire thing from a single perspective. + */ + validate = () => { + // TODO: Call from somewhere. + + if (!this.initial.is_equivalent(this.terminal)) + throw new RuleError('LHS (Initial) !== RHS (Terminal)'); + + // TODO: Or each check separately + // if (!this.initial.initial.is_equivalent(this.terminal.initial)) + // throw new RuleError('Initial.Initial (LHS.Domain) !== Terminal.Initial (RHS.Domain)'); + // if (!this.initial.terminal.is_equivalent(this.terminal.terminal)) + // throw new RuleError('Initial.Terminal (LHS.Codomain) !== Terminal.Terminal (RHS.Codomain)'); + } + + name = this.property('name', ''); + + override get reverse(): Ray.Any { + // TODO remove this hacky thing - just tries to us -a / a + + const name = this.name.startsWith('-') ? this.name.substring(1) : `-${this.name}`; + + return super.reverse.o({ name }); + } + + dpo = (match: Match): Ray.Any => { + throw new NotImplementedError(); + } + + } + + export class Graph extends Ray { + + static new = (): Graph => { + return new Graph(); + } + + protected constructor() { + super(); + } + + // Graph.inputs = Ray.initial + get inputs(): Ray.Any { return this.initial; } set inputs(ray: JS.ParameterlessFunction) { this.initial = ray; } + set_inputs = (ray: JS.ParameterlessFunction): Graph => { this.inputs = ray; return this; } + add_inputs = (ray: Ray.Any): Graph => { this.inputs.compose(ray); return this; } + // Graph.inputs = Ray.terminal + get outputs(): Ray.Any { return this.terminal; } set outputs(ray: JS.ParameterlessFunction) { this.terminal = ray; } + set_outputs = (ray: JS.ParameterlessFunction): Graph => { this.outputs = ray; return this; } + add_outputs = (ray: Ray.Any): Graph => { this.outputs.compose(ray); return this; } + + } + +} \ No newline at end of file diff --git a/environments/python/_temp/external/chyp/ChypCanvas.tsx b/environments/python/_temp/external/chyp/ChypCanvas.tsx new file mode 100644 index 0000000..0d032f6 --- /dev/null +++ b/environments/python/_temp/external/chyp/ChypCanvas.tsx @@ -0,0 +1,684 @@ +import React, {useEffect, useRef, useState} from "react"; +import IEventListener from "../../js/react/IEventListener"; +import {VisualizationCanvas} from "../../explorer/Visualization"; +import { + _Continuation, + _Vertex, + add, + add_, + AutoVertex, + circle, + InterfaceOptions, + Line, + torus +} from "../../explorer/OrbitMinesExplorer"; +import {useHotkeys} from "../../js/react/hooks/useHotkeys"; +import {DebugResult, Ray, RayType} from "../../explorer/Ray"; +import {HotkeyConfig} from "@blueprintjs/core/src/hooks/hotkeys/hotkeyConfig"; +import _ from "lodash"; +import {useThree} from "@react-three/fiber"; +import {Render, StatsPanels} from "../../explorer/debug/DebugCanvas"; + +// TODO: Put the graphs setc at the top, invis lines, then draw them on hover, and maybe make surrounding stuff less visiable. +// TODO: make some function which uses a custom input like position of the interface as the thing which breaks equivalences - ignorances. Basically a custom "equivalency function" +// TODO; Key to output javascript compilation targets in console, array, .. etc. +// const Debug = () => { +// const groups: string[][] = []; +// _.values(debug).forEach(ray => { +// for (let group of groups) { +// if (group.includes(ray.label) || group.includes(ray.vertex)) { +// group.push(...[ray.label, ray.vertex].filter(l => l !== 'None')); +// return; +// } +// } +// +// groups.push([ray.label, ray.vertex].filter(l => l !== 'None')); +// }); +// +// const group = (l: string): string[] => groups.find(group => group.includes(l))!; +// const group_index = (l: string): number => groups.indexOf(group(l)); +// const index_in_group = (l: string): number => group(l).indexOf(l); +// +// +// //TODO: do the same for group in initial/yerminal +// return <> +// {/*
*/} +// {_.values(debug).map(((_ray, index) => { +// let ray = { +// ...debug[_ray.label] +// } +// +// const color = { +// [RayType.VERTEX]: 'orange', +// [RayType.TERMINAL]: '#FF5555', +// [RayType.INITIAL]: '#5555FF', +// [RayType.REFERENCE]: '#555555', +// }[ray.type]; +// +// const group_x = _.compact(group(ray.label).map(l => (debug[l] as InterfaceOptions).position)).map(position => position[0])[0]; +// // console.log(_.compact(group(ray.label).map(l => (debug[l] as InterfaceOptions).position))) +// +// ray = { +// ...ray, +// ...({ +// color, +// scale: 1.5, +// // rotation: Ray.Any.type == RayType.REFERENCE ? [0, 0, Math.PI / 6] : [0, 0, 0], +// position: [ +// group_x ?? group_index(ray.label) * 20 * 1.5, +// // (index_in_group(ray.label) + group_index(ray.label) + (group_x ? 0: 1)) * 30 * 1.5, +// index * 20 * 1.5, +// 0 +// ] +// } as InterfaceOptions) +// } +// +// debug[_ray.label] = ray; +// +// const _default: Required = { +// position: [0, 0, 0], +// rotation: [0, 0, 0], +// scale: 1, +// color: 'orange', +// ..._.pick(ray, 'position', 'rotation', 'scale', 'color'), +// } +// +// // console.log(ray.label, [ray.initial, ray.vertex, ray.terminal].toString()) +// +// const initial: Required = { ..._default, position: [-20 * _default.scale, 0, 0] }; +// +// // Vertex as the origin for rotation +// const vertex: Required = { ..._default, position: [0, 0, 0] } //, ...ray.vertex }; +// const terminal: Required = { ..._default, position: [20 * _default.scale, 0, 0] }; +// +// const Group = ({ children }: Children) => { +// return +// {children} +// +// } +// +// const None = (options: InterfaceOptions) => (<_Continuation {...options} color="#AA0000" scale={1} />) +// +// const Extreme = ({type}: { type: Ray.AnyType.INITIAL | RayType.TERMINAL }) => { +// const options = type === RayType.INITIAL ? initial : terminal; +// +// switch (ray.type) { +// case RayType.REFERENCE: +// case RayType.VERTEX: { +// const a = type === RayType.INITIAL ? { terminal: vertex } : { initial: vertex }; +// return ; +// } +// case type: { +// return ; +// } +// case (type === RayType.INITIAL ? RayType.TERMINAL : Ray.AnyType.INITIAL): { +// return <> +// } +// } +// } +// +// return +// +// +// +// +// }))} +// +// {groups.map(group => <> +// {group.map(l => )} +// )} +// {/*
*/} +// +// } + +const ___index = (ray: Ray.Any): number => { + switch (ray.type) { + case RayType.REFERENCE: + return ray.___any.index ?? 0; + case RayType.INITIAL: + return ray.follow().___any.index ?? 0; + case RayType.TERMINAL: + return ray.follow(Ray.directions.previous).___any.index ?? 0; + case RayType.VERTEX: + return ray.___any.index ?? 0; + } +} + +export const Render2 = ({ ray, Interface, show = { initial: true, terminal: true } }: { ray: Ray.Any, Interface: Ray.Any, index: number, show?: { initial: boolean, terminal: boolean } }) => { + const index = ___index(ray); + let added = [15 * index, 60 * index, 0]; + + const initial: Required = ray.follow(Ray.directions.previous).render_options(Interface); + initial.position = add_(initial.position, added); + const vertex: Required = ray.render_options(Interface); + vertex.position = add_(vertex.position, added); + const terminal: Required = ray.follow().render_options(Interface); + terminal.position = add_(terminal.position, added); + + switch (ray.type) { + case RayType.REFERENCE: + return <_Vertex position={vertex.position} rotation={[0, 0, Math.PI / 2]} scale={vertex.scale / 2} color="#555555" /> + case RayType.INITIAL: { + const diff = [-15, -15, 0]; + + return + {!show.initial ? : + + + + + + } + + + + + + + + <_Continuation {...vertex} position={add_(vertex.position, !show.initial && ray.type === RayType.INITIAL ? [-15, -15, 0] : [0, 0, 0])} /> + + + {!show.initial ? : + + <_Continuation {...vertex} position={add_(vertex.position, diff)} color="#FF5555"/> + <_Continuation {...vertex} position={add_(vertex.position, diff, [-30, 0, 0])} scale={vertex.scale / 3 * 2} + color="#AA0000"/> + } + + } + case RayType.TERMINAL: { + const diff = [15, 15, 0]; + + return + {!show.terminal ? : + + + + + + + + } + + + + + + <_Continuation {...vertex} position={add_(vertex.position, !show.terminal && ray.type === RayType.TERMINAL ? [15, 15, 0] : [0, 0, 0])} /> + + + {!show.terminal ? : + + <_Continuation {...vertex} position={add_(vertex.position, diff)} color="#5555FF" /> + <_Continuation {...vertex} position={add_(vertex.position, diff, [30, 0, 0])} scale={vertex.scale / 3 * 2} color="#AA0000"/> + } + + } + case RayType.VERTEX: { + return <_Vertex {...vertex} /> + } + } +} + +export const DebugInterface2 = ({scale = 1.5}: InterfaceOptions) => { + const ref = useRef(); + const hotkeyConfig = useHotkeys(); + + const { + gl: renderer, + camera, + scene, + raycaster + } = useThree(); + + const space_between = 20 * scale; + + const [Interface] = useState(Ray.vertex().o({ + selection: Ray.Any.vertex().o2({ + initial: { position: [-space_between, 0, 0], scale, color: 'orange' }, + vertex: { index: 0, position: [0, 0, 0], scale, color: 'orange' }, + terminal: { position: [space_between, 0, 0 ], scale, color: 'orange' }, + }).as_reference().o({ + position: [0, 0, 0], + scale, + rotation: [0, 0, Math.PI / 6 ], + color: '#555555' + }), + rays: [] as Ray[], + stats: false, + controls: { + hotkeys: [ + { + combo: ["d", "arrowright"], global: true, label: "", onKeyDown: () => { + const { selection, rays } = Interface.___any; + + const current = Interface.___any.selection.render_options(Interface); + + const next = Ray.vertex().o2({ + initial: { position: add_(current.position, [(space_between * 2) - space_between, 0, 0]), scale, color: 'orange' }, + vertex: { index: Interface.___any.selection.___any.index + 1, position: add_(current.position, [(space_between * 2), 0, 0]), scale, color: 'orange' }, + terminal: { position: add_(current.position, [(space_between * 2) + space_between, 0, 0 ]), scale, color: 'orange' } + }).as_reference().o({ + ...selection.as_reference().render_options(Interface), + position: add(selection.___any.position ?? [0, 0, 0], [space_between * 2, 0, 0]) + }); + + Interface.___any.selection = selection.compose(next); + Interface.___any.rays = Interface.___any.selection.self.___dirty_all([]).map((ray: Ray.Any) => { + ray.___any.traversed = true; + return ray.as_reference(); + }); + + } + }, + { + combo: ["a", "arrowleft"], global: true, label: "", onKeyDown: () => { + if (Interface.___any.Ray.length === 0) + return; + + Interface.___any.selection = Interface.___any.selection.pop(); + Interface.___any.selection.o({ + position: Interface.___any.selection.render_options(Interface).position + }); // TODO, Same with this. + Interface.___any.selection.self.terminal.o({ + position: add(Interface.___any.selection.render_options(Interface).position, [space_between, 0, 0]), scale, color: 'orange' + }); // TODO: The continues_with function doesn't persist the options, as they are ignored on the equivalency. Probably need some better way to deal with this kind of thing. + + Interface.___any.rays = Interface.___any.selection.self.___dirty_all([]).map((ray: Ray.Any) => { + ray.___any.traversed = true; + return ray.as_reference(); + }); + + if (Interface.___any.length === 0) { + Interface.___any.selection.___any.position = [0, 0, 0] + return; + } + } + }, + { + combo: ["w", "arrowup"], global: true, label: "", onKeyDown: () => { + const { selection, rays } = Interface.___any; + + Interface.___any.rays = Ray.flatMap((ray: Ray.Any) => [ + ray, + // Ray.js("A").as_reference().o({ + // // ...ray.o, + // position: add(ray.___any.position ?? [0, 0, 0], [0, i * 2, 0]) + // }), + // Ray.js("A").as_reference().o({ + // // ...ray.o, + // position: Ray.Any.___any.position, + // rotation: [0, 0, Math.PI / 2] + // }), + // Ray.js("A").as_reference().o({ + // // ...ray.o, + // position: add(ray.___any.position ?? [0, 0, 0], [0, i * 2, 0]), + // rotation: [0, 0, Math.PI / 2] + // }) + ]); + + // Chyp_naieve_pass.___any.selection = Chyp_naieve_pass; + + // selection.o({...selection.o, position: add(selection.___any.position ?? [0, 0, 0], [0, i * 2, 0])}) + + // selection.compose( + + // ); + } + }, + { + combo: "/", global: true, label: "", onKeyDown: () => { + console.log('---------') + console.log(`Debugging: ${Interface.___any.selection.self.label} (type=${Interface.___any.selection.type})`) + console.log(`Ray.length at pos=[${Interface.___any.selection.render_options(Interface).position}]: ${Interface.___any.Ray.filter((ray: Ray.Any) => + _.isEqual( + Interface.___any.selection.render_options.position, + ray.render_options(Interface).position + ) + ).length} / ${Interface.___any.Ray.length}`) + console.log('ref', Interface.___any.selection) + console.log('ref.self', Interface.___any.selection.self) + + const debug: DebugResult = {}; + Interface.___any.selection.self.debug(debug); + console.log('ref.debug', debug); + Interface.___any.Ray.forEach((ray: Ray.Any) => Ray.Any.debug(debug)); + console.log('Ray.debug', debug); + + } + }, + { + combo: "f3", global: true, label: "Show stats Panel", onKeyDown: (e) => { + e.preventDefault(); + Interface.___any.stats = !Interface.___any.stats; + } + }, + ] as HotkeyConfig[] + } + })); + + // useEffect(() => { + // TODO: Eventually goes over maximum size of react-debug callstack when updates each frame like this. + hotkeyConfig.set(...Interface.___any.controls.hotkeys); + // }, [Interface.___any.controls.___any.hotkeys]); + + const index = ___index(Interface.___any.selection); + const added = [15 * index, 60 * (index + 1), 0]; + + return <> + {Interface.___any.stats ? : <>} + + + + {/*{Interface.___any.Ray.map((ray: Ray.Any) => )}*/} + + {Interface.___any.Ray.map((ray: Ray.Any) => )} + + + + + {Interface.___any.Ray.map((ray: Ray.Any, index: number) => )} + + + + + + + {Interface.___any.Ray.map((ray: Ray.Any, index: number) => )} + + + + {/**/} + {/* {[*/} + {/* Ray.vertex().o2({*/} + {/* initial: { position: [(-space_between / 1.5) - (space_between / 1.5) * 2, -140, 0], scale: scale / 1.5, color: '#FF5555' },*/} + {/* vertex: { index: 0, position: [0 - (space_between / 1.5) * 2, -140, 0], scale: scale / 1.5, color: '#FF5555' },*/} + {/* terminal: { position: [(space_between / 1.5) - (space_between / 1.5) * 2, -140, 0 ], scale: scale / 1.5, color: '#FF5555' },*/} + {/* }),*/} + {/* Ray.vertex().o2({*/} + {/* initial: { position: [(-space_between / 1.5) + (space_between / 1.5) * 2, -120, 0], scale: scale / 1.5, color: '#FF5555' },*/} + {/* vertex: { index: 0, position: [0 + (space_between / 1.5) * 2, -120, 0], scale: scale / 1.5, color: '#FF5555' },*/} + {/* terminal: { position: [(space_between / 1.5) + (space_between / 1.5) * 2, -120, 0 ], scale: scale / 1.5, color: '#FF5555' },*/} + {/* }),*/} + {/* Ray.vertex().o2({*/} + {/* initial: { position: [(-space_between / 1.5), -140, 0], scale: scale / 1.5, color: '#FF5555' },*/} + {/* vertex: { index: 0, position: [0, -120, 0], scale: scale / 1.5, color: '#FF5555' },*/} + {/* terminal: { position: [(space_between / 1.5), -120, 0 ], scale: scale / 1.5, color: '#FF5555' },*/} + {/* }),*/} + {/* Ray.vertex().o2({*/} + {/* initial: { position: [(-space_between / 1.5), -140, 0], scale: scale / 1.5, color: '#FF5555' },*/} + {/* vertex: { index: 0, position: [0, -140, 0], scale: scale / 1.5, color: '#FF5555' },*/} + {/* terminal: { position: [(space_between / 1.5), -120, 0 ], scale: scale / 1.5, color: '#FF5555' },*/} + {/* }),*/} + {/* Ray.vertex().o2({*/} + {/* initial: { position: [(-space_between / 1.5), -140, 0], scale: scale / 1.5, color: '#FF5555' },*/} + {/* vertex: { index: 0, position: [0, -160, 0], scale: scale / 1.5, color: '#FF5555' },*/} + {/* terminal: { position: [(space_between / 1.5), -160, 0 ], scale: scale / 1.5, color: '#FF5555' },*/} + {/* }),*/} + {/* ]*/} + {/* .flatMap(ray => [ray.initial.as_reference(), ray.as_reference(), ray.terminal.as_reference()])*/} + {/* .map(ray => )*/} + {/* }*/} + {/* /!**!/*/} + {/* /!**!/*/} + {/**/} + +} + +export const DebugInterface3 = ({scale = 1.5}: InterfaceOptions) => { + const ref = useRef(); + const hotkeyConfig = useHotkeys(); + + const { + gl: renderer, + camera, + scene, + raycaster + } = useThree(); + + const space_between = 20 * scale; + + const [Interface] = useState(Ray.vertex().o({ + selection: Ray.Any.vertex().o2({ + initial: { position: [-space_between, 0, 0], scale, color: 'orange' }, + vertex: { index: 0, position: [0, 0, 0], scale, color: 'orange' }, + terminal: { position: [space_between, 0, 0 ], scale, color: 'orange' }, + }).as_reference().o({ + position: [0, 0, 0], + scale, + rotation: [0, 0, Math.PI / 6 ], + color: '#555555' + }), + rays: [] as Ray[], + stats: false, + cursor: { + tick: false + }, + controls: { + hotkeys: [ + { + combo: ["d", "arrowright"], global: true, label: "", onKeyDown: () => { + const { selection, rays } = Interface.___any; + + const current = Interface.___any.selection.render_options(Interface); + + const next = Ray.vertex().o2({ + initial: { position: add_(current.position, [(space_between * 2) - space_between, 0, 0]), scale, color: 'orange' }, + vertex: { index: Interface.___any.selection.___any.index + 1, position: add_(current.position, [(space_between * 2), 0, 0]), scale, color: 'orange' }, + terminal: { position: add_(current.position, [(space_between * 2) + space_between, 0, 0 ]), scale, color: 'orange' } + }).as_reference().o({ + ...selection.as_reference().render_options(Interface), + position: add(selection.___any.position ?? [0, 0, 0], [space_between * 2, 0, 0]) + }); + + Interface.___any.selection = selection.compose(next); + Interface.___any.rays = Interface.___any.selection.self.___dirty_all([]).map((ray: Ray.Any) => { + ray.___any.traversed = true; + return ray.as_reference(); + }); + + } + }, + { + combo: ["a", "arrowleft"], global: true, label: "", onKeyDown: () => { + if (Interface.___any.Ray.length === 0) + return; + + Interface.___any.selection = Interface.___any.selection.pop(); + Interface.___any.selection.o({ + position: Interface.___any.selection.render_options(Interface).position + }); // TODO, Same with this. + Interface.___any.selection.self.terminal.o({ + position: add(Interface.___any.selection.render_options(Interface).position, [space_between, 0, 0]), scale, color: 'orange' + }); // TODO: The continues_with function doesn't persist the options, as they are ignored on the equivalency. Probably need some better way to deal with this kind of thing. + + Interface.___any.rays = Interface.___any.selection.self.___dirty_all([]).map((ray: Ray.Any) => { + ray.___any.traversed = true; + return ray.as_reference(); + }); + + if (Interface.___any.length === 0) { + Interface.___any.selection.___any.position = [0, 0, 0] + return; + } + } + }, + { + combo: ["w", "arrowup"], global: true, label: "", onKeyDown: () => { + const { selection, rays } = Interface.___any; + + Interface.___any.rays = Ray.flatMap((ray: Ray.Any) => [ + ray, + // Ray.js("A").as_reference().o({ + // // ...ray.o, + // position: add(ray.___any.position ?? [0, 0, 0], [0, i * 2, 0]) + // }), + // Ray.js("A").as_reference().o({ + // // ...ray.o, + // position: Ray.Any.___any.position, + // rotation: [0, 0, Math.PI / 2] + // }), + // Ray.js("A").as_reference().o({ + // // ...ray.o, + // position: add(ray.___any.position ?? [0, 0, 0], [0, i * 2, 0]), + // rotation: [0, 0, Math.PI / 2] + // }) + ]); + + // Chyp_naieve_pass.___any.selection = Chyp_naieve_pass; + + // selection.o({...selection.o, position: add(selection.___any.position ?? [0, 0, 0], [0, i * 2, 0])}) + + // selection.compose( + + // ); + } + }, + { + combo: "/", global: true, label: "", onKeyDown: () => { + console.log('---------') + console.log(`Debugging: ${Interface.___any.selection.self.label} (type=${Interface.___any.selection.type})`) + console.log(`Ray.length at pos=[${Interface.___any.selection.render_options(Interface).position}]: ${Interface.___any.Ray.filter((ray: Ray.Any) => + _.isEqual( + Interface.___any.selection.render_options(Interface).position, + ray.render_options(Interface).position + ) + ).length} / ${Interface.___any.Ray.length}`) + console.log('ref', Interface.___any.selection) + console.log('ref.self', Interface.___any.selection.self) + + const debug: DebugResult = {}; + Interface.___any.selection.self.debug(debug); + console.log('ref.debug', debug); + Interface.___any.Ray.forEach((ray: Ray.Any) => Ray.Any.debug(debug)); + console.log('Ray.debug', debug); + } + }, + { + combo: "f3", global: true, label: "Show stats Panel", onKeyDown: (e) => { + e.preventDefault(); + Interface.___any.stats = !Interface.___any.stats; + } + }, + ] as HotkeyConfig[] + } + })); + + // useEffect(() => { + // TODO: Eventually goes over maximum size of react-debug callstack when updates each frame like this. + hotkeyConfig.set(...Interface.___any.controls.hotkeys); + // }, [Interface.___any.controls.___any.hotkeys]); + + useEffect(() => { + + setInterval(() => { + Interface.___any.cursor.tick = !Interface.___any.cursor.tick; + + // TODO THIS NEEDS TO BE EASIER + }, 500); // TODO; On react reload interval is not destroyed + }, []); + + return <> + {Interface.___any.stats ? : <>} + + + + {Interface.___any.Ray.map((ray: Ray.Any) => )} + +} + +// const Interface = () => { +// // TODO: Direct call to rerender on change, now there's lag +// +// const Rendered = ({ ray, ...options }: { ray: Ray.Any } & InterfaceOptions) => { +// const { position = options.position, rotation = options.rotation, scale = options.scale, color = options.color } = ray.___any; +// return ; +// } +// +// const DEBUG = true; +// +// const debug: DebugResult = {}; +// selection.debug(debug); +// +// return <> +//
+// {/**/} +// {/**/} +// {/**/} +// +// {/**/} +// +// {/**/} +// {/**/} +// +// +// +// {Ray.map((ray: Ray.Any) => )} +// +// {/**/} +//
+// +// } + +/** + * TODO: Import .chyp files + * TODO: Export to .chyp files + */ +const ChypCanvas = ( + { + listeners = [], + }: { + listeners?: IEventListener[], + } +) => { + const listener: IEventListener = { + + } + + return
+ {/**/} + {/* */} + {/* */} + {/**/} + + + + +
+} + +export const ChypExplorer = ( + { + listeners = [], + }: { + listeners?: IEventListener[], + } +) => { + + const listener: IEventListener = {} + + return +} + +export default ChypCanvas; \ No newline at end of file diff --git a/environments/python/_temp/external/chyp/Chyp_naive_pass.ts b/environments/python/_temp/external/chyp/Chyp_naive_pass.ts new file mode 100644 index 0000000..52d3057 --- /dev/null +++ b/environments/python/_temp/external/chyp/Chyp_naive_pass.ts @@ -0,0 +1,1665 @@ +// import {JS, Ray} from "../../explorer/Ray"; +// import {NotImplementedError} from "../../explorer/errors/errors"; +// +// /** +// * - The .copy()'s are implemented on Ray. +// * +// * TODO: There's a lot of duplicate code, unnecessary documentation and non-generality in Chyp. It was probably developed as a proof of concept? - Expecting that to be addressed in the projects Aleks Kissinger is currently setting up. +// * +// * TODO: Probably want all these types at runtime, to display them +// * +// * TODO: Graph boundary is automatic with this structure? +// * +// * TODO: merging vertex, just drawinf that one equivalence between initial/terminal etc..?? just leave the vertex? +// * +// * TODO: Methods, files, as wealonry selectuon along some linez simple name above, .. +// * +// * TODO: Can just move the terminal which holds the oointer to the boundary +// * +// * TODO: Automatically generate visual examples of all the methods +// * +// * TODO: Runtime errors as rays +// * +// * TODO: All the more complicated methods should be simply implemented in a ray which walks an arbitary graph +// */ +// /** +// * TODO: These coordinates used for inferences? +// * - If not then this is probably a hack and should be interpreted as "on another layer of description which is the GUI" +// */ +// +// export const int = (t1?: any, t2?: any, t3?: any): Ray.Any => { throw new NotImplementedError() }; +// export const list = (t1?: any, t2?: any, t3?: any): Ray.Any => { throw new NotImplementedError() }; +// export const Iterable = (t1?: any, t2?: any, t3?: any): Ray.Any => { throw new NotImplementedError() }; +// export const set = (t1?: any, t2?: any, t3?: any): Ray.Any => { throw new NotImplementedError() }; +// export const str = (t1?: any, t2?: any, t3?: any): Ray.Any => { throw new NotImplementedError() }; +// export const QKeyEvent = (t1?: any, t2?: any, t3?: any): Ray.Any => { throw new NotImplementedError() }; +// export const Optional = (t1?: any, t2?: any, t3?: any): Ray.Any => { throw new NotImplementedError() }; +// export const Tuple = (t1?: any, t2?: any, t3?: any): Ray.Any => { throw new NotImplementedError() }; +// export const QObject = (t1?: any, t2?: any, t3?: any): Ray.Any => { throw new NotImplementedError() }; +// export const Union = (t1?: any, t2?: any, t3?: any): Ray.Any => { throw new NotImplementedError() }; +// export const QModelIndex = (t1?: any, t2?: any, t3?: any): Ray.Any => { throw new NotImplementedError() }; +// export const QPersistentModelIndex = (t1?: any, t2?: any, t3?: any): Ray.Any => { throw new NotImplementedError() }; +// export const QWidget = (t1?: any, t2?: any, t3?: any): Ray.Any => { throw new NotImplementedError() }; +// export const List = (t1?: any, t2?: any, t3?: any): Ray.Any => { throw new NotImplementedError() }; +// export const Qt = (t1?: any, t2?: any, t3?: any): Ray.Any => { throw new NotImplementedError() }; +// export const Orientation = (t1?: any, t2?: any, t3?: any): Ray.Any => { throw new NotImplementedError() }; +// export const QPainter = (t1?: any, t2?: any, t3?: any): Ray.Any => { throw new NotImplementedError() }; +// export const QStyleOptionGraphicsItem = (t1?: any, t2?: any, t3?: any): Ray.Any => { throw new NotImplementedError() }; +// export const bool = (t1?: any, t2?: any, t3?: any): Ray.Any => { throw new NotImplementedError() }; +// export const False = (t1?: any, t2?: any, t3?: any): Ray.Any => { throw new NotImplementedError() }; +// export const True = (t1?: any, t2?: any, t3?: any): Ray.Any => { throw new NotImplementedError() }; +// export const QGraphicsSceneMouseEvent = (t1?: any, t2?: any, t3?: any): Ray.Any => { throw new NotImplementedError() }; +// export const QTextDocument = (t1?: any, t2?: any, t3?: any): Ray.Any => { throw new NotImplementedError() }; +// export const editor = (t1?: any, t2?: any, t3?: any): Ray.Any => { throw new NotImplementedError() }; +// export const QCloseEvent = (t1?: any, t2?: any, t3?: any): Ray.Any => { throw new NotImplementedError() }; +// export const Any = (t1?: any, t2?: any, t3?: any): Ray.Any => { throw new NotImplementedError() }; +// export const tuple = (t1?: any, t2?: any, t3?: any): Ray.Any => { throw new NotImplementedError() }; +// export const None = (t1?: any, t2?: any, t3?: any): Ray.Any => { throw new NotImplementedError() }; +// export const state = (t1?: any, t2?: any, t3?: any): Ray.Any => { throw new NotImplementedError() }; +// export const Callable = (t1?: any, t2?: any, t3?: any): Ray.Any => { throw new NotImplementedError() }; +// export const Set = (t1?: any, t2?: any, t3?: any): Ray.Any => { throw new NotImplementedError() }; +// +// export const Color = (t1: Ray.Any = str('')): Ray.Any => { throw new NotImplementedError() }; +// +// export class ValueError extends Error {} +// +// /** +// * Non-default vertex types are identified by a string label +// * +// * str() | None() - No overloading in TypeScript ;( +// */ +// export const VType = JS.Iterable([str, None]); +// +// /** +// * Used for debugging (the matcher) +// */ +// const DEBUG = true; // TODO; Generalize +// const log = (s: string) => { +// if (!DEBUG) +// return; +// +// console.log(s); +// } +// +// /** +// * Data associated with a single vertex. +// */ +// export class VData extends Ray { +// // The vertex type. +// vtype = this.property('vtype', None); +// +// // The register size (number of bundled parallel wires) of the vertex. +// size = this.count; // Implemented on Ray.count +// +// // TODO: These infers will probably be removed? +// // Whether to infer the vertex type during composition. Used for special generators (identities, permutations, redistributers). +// infer_type = this.property('infer_type', bool(False)) +// // Whether to infer the vertex size during composition. Used for special generators (identities, permutations, redistributers). +// infer_size = this.property('infer_size', bool(False)) +// +// // x-coordinate at which to draw the vertex. +// x = this.property('y', int(0)) +// // y-coordinate at which to draw the vertex. +// y = this.property('y', int(0)) +// +// highlight = this.property('highlight', bool(False)) +// value = this.property('value') +// +// /** +// * Indices (if any) where this vertex occurs in the input and output lists of the hypergraph. +// */ +// get in_indices(): Ray.Any { return this.initial } // +// get out_indices(): Ray.Any { throw new NotImplementedError(); } +// +// is_input = (): Ray.Any => this.in_indices.count.as_int() > 0; +// is_output = (): Ray.Any => this.out_indices.count.as_int() > 0; +// is_boundary = (): Ray.Any => this.is_input() || this.is_output(); +// +// // TODO: Probably generalizable +// +// /** +// * Form the quotient of the graph by identifying v with w. Afterwards, the quotiented vertex will be have integer identifier `v`. +// * +// * TODO; particular sort of composing +// */ +// merge = (other: VData): Ray.Any => { +// throw new NotImplementedError(); +// } +// +// fresh = (): VData => { +// // TODO: This is just a copy where this initial/terminal directionlaity is ignored. Only a copy of the vertex. +// +// // vtype=vd.vtype, size=vd.size, +// // x=vd.x, y=vd.y, value=vd.value +// +// } +// +// // TODO: Shouldn't be here, this should be implemented on Ray if it's general enough +// get domain(): Ray.Any { return JS.Iterable([this.vtype, this.size]) }; +// +// } +// +// export class EData extends Ray { +// +// fg = this.property('bg', Color()); +// bg = this.property('bg', Color()); +// x = this.property('y', int(0)); +// // y-coordinate at which to draw the vertex. +// y = this.property('y', int(0)); +// highlight = this.property('highlight', bool(False)) +// hyper = this.property('value', bool(True)) +// value = this.property('value') +// +// __repr__ = (): Ray.Any => { throw new NotImplementedError(); +// // TODO: return f'Edge: {self.value} ({self.x}, {self.y})' +// } +// +// // TODO: More stuff to relate it to the screen that shouldnt be here. +// /** +// * Return how many width 'units' this box needs to display nicely. +// * +// * This uses a simple rule: +// * - If the number of inputs and outputs are both <= 1, draw as a small (1 width unit) box. +// * - Otherwise draw as a larger (size 2) box. +// */ +// box_size = (): Ray.Any => JS.Number((this.s.count.as_int() <= 1 && this.t.count.as_int() <= 1) ? 1 : 2); +// +// domain = (): Ray.Any => this.source.cast().domain; +// codomain = (): Ray.Any => this.target.cast().domain; +// +// toString = (): string => this.__repr__().as_string(); // TODO; FOR ALL +// } +// +// /** +// * A hypergraph with boundaries. +// * +// * This is the main data structure used by Chyp. It represents a directed +// * hypergraph (which we call simply a "graph") as two dictionaries for +// * vertices and (hyper)edges, respectively. Each vertex is associated with a +// * `VData` object and edge edge with an `EData` object, which stores +// * information about adjacency, position, label, etc. +// * +// * The particular flavor of hypergraphs we use associate to each hyperedge a +// * list of source vertices and a list of target vertices. The hypergraph +// * itself also has a list of input vertices and a list of output vertices, +// * which are used for sequential composition and rewriting. +// */ +// export class Graph extends Ray { +// +// get vindex(): Ray.Any { return this.vdata.index.max(0); } +// get eindex(): Ray.Any { return this.edata.index.max(0); } +// +// // Mapping from integer identifiers of each vertex to its data. +// vdata = this.property('vertices'); +// // Mapping from integer identifiers of each hyperedge to its data. +// edata = this.property('edges'); +// +// // TODO .keys +// vertices = this.property('vertices'); +// edges = this.property('edges'); +// +// /** +// * Return the domain of the graph. +// * +// * This consists of a list of pairs (vertex type, register size) corresponding to each input vertex. +// */ +// // TODO: Domain/Codmain is just the initial/terminal side (possibly typed) where the direction which is what defines what it itself is connected to, is ignored. +// get domain(): Ray.Any { return this.inputs.cast().domain; }; +// +// /** +// * Return the domain of the graph. +// * +// * This consists of a list of pairs (vertex type, register size) corresponding to each output vertex. +// */ +// get codomain(): Ray.Any { return this.outputs.cast().domain; }; +// +// vertex_data = (v = int): VData => this.vertices().at(v).cast(); +// edge_data = (e = int): EData => this.edges().at(e).cast(); +// +// // TODO: Shouldnt be here +// ___next_index = (name = int(-1), index: Ray.Any): Ray.Any => { +// // TODO: This is definitely going to be buggy if '-1' and certain specific values are used. (Hence the note above add_vertex & add_edge) +// const current = name === -1 ? index : Math.max(name, index); +// return current + 1; +// } +// +// /** +// * Add a new vertex to the graph. +// * +// * @param name The value carried by this vertex (currently unused). +// * TODO: Generally this is just additional structure at the vertex, rays just implement this generally. +// * @param vertex The integer identifier to use for this vertex. If this is set to -1, the identifier is set automatically. (Note: no checks are currently made to ensure the identifier is not already in use). +// */ +// add_vertex = (name = int(-1), vertex: VData): VData => { +// this.vindex = this.___next_index(name, this.vindex); +// +// this.vdata[this.vindex - 1] = vertex; +// return vertex; +// } +// +// /** +// * Add a new hyperedge to the graph. +// * +// * @param s +// * @param t +// */ +// add_edge = (name = int(-1), edge: EData): EData => { +// this.eindex = this.___next_index(name, this.eindex); +// +// this.edata[this.eindex - 1] = edge; +// +// // TODO: Syncs the initial/terminal to the vertices (basically non-ignorant connection) +// // for v in s: +// // self.vdata[v].out_edges.add(e) +// // for v in t: +// // self.vdata[v].in_edges.add(e) +// +// return edge; +// } +// // add_simple_edge - is automatically handled: Ray.Anys can disambiguate between one/multiple values for certain purposes. +// +// /** +// * Remove a vertex from the graph. +// * +// * This removes a single vertex. +// * - If `strict` is set to True, then the vertex must have no adjacent edges nor be a boundary vertex. +// * - If `strict` is False, then `v` will be removed from the source/target list of all adjacent edges and removed from the boundaries, if applicable. +// * +// * @param v +// * @param strict If True, require the vertex to have no adjacent edges and not be a boundary vertex. +// */ +// remove_vertex = (v = int, strict: boolean = false): Ray.Any => { +// // TODO: as delegation +// +// if (strict) { +// if (this.vertex_data(v).in_edges.count.as_int() > 0 || this.vertex_data(v).out_edges > 0) { +// throw new ValueError('Attempting to remove vertex with adjacent' +// + 'edges while strict == True.'); +// } +// +// if (this.inputs.includes(v) || this.outputs.includes(v)) { +// throw new ValueError('Attempting to remove boundary vertex while' +// + 'strict == True.'); +// +// }// TODO CAN BE SIMPLIFIED +// } +// +// // in/out edges from all vertices +// delete this.vdata[v]; +// } +// +// /** +// * Remove an edge from the graph. +// * +// * @param e Integer identifier of the edge to remove. +// */ +// remove_edge = (e = int): Ray.Any => { +// // in/out edges from all vertices +// delete this.edata[e]; +// } +// +// // TODO: Can these be overlaoded in properties using -=, += in TS? +// +// +// +// +// // TODO: These are just one possibly ignorant ray through the initial/terminal ends which aren't matched - could just generate these on the fly, or similar to chyp add when added. +// +// +// +// // set function: +// // TODO: Clears the matched in/out indices, then sets them to the ones found in in/outputs +// // inputs =, outputs = +// // for the add functions: // TODO: Perhaps splat +// // also for add // TODO; these are then again duplicated to self.vdata[v].out_indices.add(i) +// +// +// /** +// * Return vertices that lie on a directed path from any of `vs`. +// * // TODO: Just traverse the rays, deduplicate using the index +// */ +// successors = (ray: Ray.Any): Ray.Any => Ray.Any.next; +// +// /** +// * Split a vertex into copies for each input, output, and tentacle. +// * +// * This is used for computing pushout complements of rules that aren't left-linear. +// * (See arXiv:2012.01847 Section 3.3 for definition of left-linear). +// * +// * Returns: A pair of lists containing the new input-like and output-like vertices, respectively. +// * +// * @param v Integer identifier of vertex to be exploded. +// */ +// explode_vertex = (v = int): Ray.Any => { +// const vertex = this.vertex_data(v); +// +// // TODO; This just seems like another copy which minor changes +// +// const next_inputs = empty(); +// const next_outputs = empty(); +// +// const __temp = ( // TODO This whole bit of code will definitely be reduced to one line at some point in this process +// vertex: VData, +// boundary: (vertex: VData) => Ray.Any, +// next_boundary: Ray.Any +// ) => { +// // TODO: It's just a duplicated process for vertex/edge since their definition is separataed +// +// // Replace any occurrences of the original vertex in the graph inputs with a new input-like vertex. +// // self.set_inputs([v1 if v1 != v else fresh(0) for v1 in self.inputs]) +// // TODO: Basically a copy, and replace this one vertex +// +// // Where the original vertex is the target of a hyperedge, replace its occurrence in the hyperedge's target list with a new input-like vertex and register this with the new vertex's data instance. +// boundary(vertex).all(e => { +// const edge: EData = e.cast(); +// +// edge.t = edge.t +// .map(target => { +// if (target === v) +// return target; +// +// const fresh_vertex = vertex.fresh(); +// next_boundary.add(this.add_vertex(fresh_vertex)) +// boundary(fresh_vertex).add(edge); +// }) +// }); +// +// } +// +// // TODO: It's always just duplicated for both ends, +// __temp(vertex, (vertex) => vertex.in_edges, next_inputs); +// __temp(vertex, (vertex) => vertex.out_edges, next_outputs); +// +// // Register the fact that `v` no longer occurs in as a source or target of any hyperedge. +// vertex.in_edges.clear(); // TODO; Should basically just reset initial/terminal +// vertex.out_edges.clear(); +// +// // Remove `v` from the hypergraph, using strict == True to catch any errors (no errors should be raised with current code). +// this.remove_vertex(vertex, true); +// +// return [next_inputs, next_outputs]; +// } +// +// +// /** +// * Insert a new identity hyperedge after the given vertex. +// * +// * A new vertex is also created, which replaces the original vertex as the source of any edges in graph as well as any occurrences of the original vertex in the graph outputs. +// * +// * @param reverse +// * - If `False`, the original vertex becomes the source of the ew identity hyperedge while the new vertex becomes the target. +// * - If `True`, the source and target of the new identity are flipped. This can be used to break directed cycles, by neffectively introducing a cap and cup. +// */ +// insert_id_after = (v = int, reverse = bool(False)): Ray.Any => { +// const vertex = this.vertex_data(v); +// +// // Create a new vertex with the same vtype and size +// // //TODO ; Which is this .fresh +// const new_vertex = vertex.fresh(); +// new_vertex.x += 3; +// // The new vertex is highlighted whenever the orignal vertex is. +// new_vertex.highlight = vertex.highlight; // TODO: This not done by .fresh? +// +// /** +// * //TODO: BEcause it's inserted after, set the output (generalize to just moving the reference to the terminal. +// * # Replace any occurences of the original vertex in the graph outputs +// * # with the new vertex. +// * self.set_outputs([x if x != v else w for x in self.outputs()]) +// */ +// +// // Where the original vertex is the source of a hyperedge, replace it with the new vertex and register this change with the data instance of each vertex +// // TODO syncs the out_edges fields, wd.out_edges.add(e) vd.out_edges.clear() +// // TODO replace out_edges.source to new_vertex again +// +// // Assign the orignal and new vertex as source or target of the new identity edge, based on the `reverse` argument. +// // TODO: Sets reverse just flipping around the initial/terminal.. +// // TODO: s, t = ([v], [w]) if not reverse else ([w], [v]) +// +// // Create the new identity edge. +// const edge = this.add_edge() // s, t, 'id', vd.x + 1.5, vd.y) +// edge.hightlight = vertex.highlight; // The new edge is highlighted whenever the original vertex is. +// +// return edge; +// } +// +// /** +// * Take the monoidal product of this graph in-place with another. +// * +// * Calling g.tensor(h) will turn g into g ⊗ h, performing the operation in-place. Use the infix version `g @ h` to simply return the tensor product without changing g. +// * +// * @param other +// * @param layout If `True`, compute new y-coordinates of the vertices and edges of the resulting graph so that the two graphs in the tensor product are adjacent with no overlap in the y-direction. +// */ +// tensor = (other: Graph, layout: boolean = true): Ray.Any => { +// +// const a = this; +// const b = other; +// +// const tensor: Ray.Any = new Ray(); // TODO: [initial = a.outputs, terminal = b.inputs] +// +// tensor.initial.any.y -= +// tensor.initial.any.y.max(); // max_self +// +// tensor.terminal.any.y -= +// (layout ? tensor.terminal.any.y.min() : 0) + 1; // min_other TODO: Why + 1 ? +// +// /** +// * # self.set_inputs(self.inputs + [vmap[v] for v in other.inputs]) +// * # self.set_outputs(self.outputs + [vmap[v] for v in other.outputs]) +// * +// * # Add the inputs and outputs of the other graph to this one. +// * self.add_inputs([vmap[v] for v in other.inputs]) +// * self.add_outputs([vmap[v] for v in other.outputs]) +// */ +// // TODO: Add equivalence/reference on the inputs/output extremes. +// } +// +// +// // Merge = compose.. +// // /** +// // * Compose structures defined at .initial with those at .terminal, dropping the vertex. ; or if done non-ignorantly. Composing them on another level, ignoring to this vertex. TODO: Allow this freedom +// // */ +// // get merge(): Ray.Any { +// // // concat initial.a + initial.b, terminal.a + terminal.b +// // // then: either destroy a/b, merge structure... etc.. +// // // Chyp: destroy b +// // +// // // this.initial.equivalent(other.initial); +// // // this.terminal.equivalent(other.terminal); +// // +// // /* +// // +// // vd = self.vertex_data(v) +// // # print("merging %s <- %s" % (v, w)) +// // +// // # Where vertex `w` occurs as an edge target, replace it with `v` +// // for e in self.in_edges(w): +// // ed = self.edge_data(e) +// // ed.t = [v if x == w else x for x in ed.t] +// // vd.in_edges.add(e) +// // +// // # Where vertex `w` occurs as an edge source, replace it with `v` +// // for e in self.out_edges(w): +// // ed = self.edge_data(e) +// // ed.s = [v if x == w else x for x in ed.s] +// // vd.out_edges.add(e) +// // +// // # Wherever `w` occurs on the graph boundary, replace it with `v` +// // self.set_inputs([v if x == w else x for x in self.inputs]) +// // self.set_outputs([v if x == w else x for x in self.outputs]) +// // +// // # Remove references to `w` from the graph +// // self.remove_vertex(w) +// // +// // */ +// // throw new NotImplementedError(); +// // } +// +// +// // @alias('merge') ?? TODO +// // static compose = Ray.___func(ref => { +// // const { initial, terminal } = ref.self; +// // +// // return initial.___primitive_switch({ +// // [RayType.VERTEX]: () => { +// // const vertex = initial.self; +// // +// // // TODO; Implement as restrictive case for Chyp +// // { +// // // if (this.self.initial.count.as_int() !== this.terminal.count.as_int()) +// // // throw new NotImplementedError(`Initial (Graph.Domain) does not match Terminal (Graph.Codomain)`); +// // +// // // Check that codomain of this graph matches the domain of the other: this is required for valid sequential composition. +// // // if (this.self.initial.is_equivalent(this.self.terminal)) +// // // throw new NotImplementedError('Initial (Graph.Domain) does not match Terminal (Graph.Codomain) types.'); // TODO; Should take care of vtype/size matches at input/output +// // } +// // +// // /** +// // * A simple case of what we want to solve here. +// // * +// // * Initial Terminal +// // * ... ... +// // * [--| ] [ |--] +// // * [--| ] [ |--] +// // * [--| ] [ |--] +// // * [ |--] [--|--] [--| ] +// // * vertex +// // * +// // * And recursively (arbitrarily) match initial/terminal structures. +// // */ +// // +// // throw new NotImplementedError(); +// // } +// // }) +// // }); +// // compose = Ray.compose(this); +// +// /** +// * Sequentially compose this graph in-place with another. +// * +// * Calling g.compose(h) will turn g into g ; h, performing the operation in-place. Use the infix version `g >> h` to simply return the sequential composition without changing g. +// */ +// compose = (other: Graph): Ray.Any => { +// // TODO Just matching outputs and inputs.. +// +// // TODO: Simply visualized, as a single thing "us composing this thing", where on the initial side, we have the outputs of one thing, which we're trying to one-to-one match to the terminal side" +// +// // TODO: min/max needs to be on vertices/edges. Not necessarilyt outputs/inputs +// +// /** +// * Basically, seeing the {compose} line x=0, and them moving outputs (left) to the negative x. And moving the inputs (right) to the positive x. +// */ +// +// // Compute the max x-coordinate of the edges and vertices in this graph. +// +// // TODO: Again, recursively going through everything defined on the initial side (outputs) +// // Shift all vertices and edges of this graph below the x-axis. +// compose.initial.any.x -= +// compose.initial.any.x.max(); // max_self +// +// // TODO: It's indeed copying here, as b_copy, abstract this away [SHOULD BE A COPY] +// compose.terminal.any.x -= +// compose.terminal.any.x.min(); // min_other +// +// // TODO: This check in the Chyp code is just done after the [terminal].copy() which we haven't implemented yet. +// /** +// * plug1 = self.outputs +// * plug2 = [vmap[v] for v in other.inputs] +// * +// * if len(plug1) != len(plug2): +// * raise GraphError(f'Attempting to plug a graph with {len(plug1)} ' +// * + f'outputs into one with {len(plug2)} inputs') +// * +// * self.set_outputs([vmap[v] for v in other.outputs]) +// */ +// +// // [outputs to inputs] +// // Go through pairs of vertices from each plug +// compose.zip().all(([input, output]) => { +// /** +// * While vertex currently assigned to p1 has already been merged into another vertex, repeatedly replace it with the vertex it was merged into until p1 is a vertex that has not already been merged. Vice versa for p2. +// */ +// +// // TODO: does this ever happen??? +// // while p1 in quotient: +// // p1 = quotient[p1] +// // while p2 in quotient: +// // p2 = quotient[p2] +// +// +// // TODO, Again the same equivalence check for loops etc.. +// { +// // If the resulting p1 and p2 are not the same vertex, merge them. +// if (input === output) +// return; +// +// // TODO; DO this on the vertices; +// // data_1 = self.vertex_data(p1) data_2 = self.vertex_data(p2) +// +// // If both vertices have flexible types that are not equal, raise an error due to ambiguity. +// // TODO: Basically, if we're assuming there could be ignorance here, which we wouldn't do on the types. Again, the types necessarily have ambiguity as well, it's just ignored in that instance. +// +// +// // TODO: From here assumes same types, now just checking size (size should again be generalized to type), it's just structure. +// +// ['vtype', 'size'].forEach(structure => { +// const infer = `infer_${structure}`; +// +// if (input[infer] && output[infer] && input[structure] !== output[structure]) { +// throw GraphError(`Ambiguous vertex ${structure} during composition.`) +// +// } else if (input[infer]) { +// // Otherwise, if one vertex has a flexible type, ensure the vertex types match. +// // TODO: Infer type = true, basically means, ignorant ambiguous type, which will just change to whatever it matched to it +// // TODO: Again both sides same thing.. +// input[structure] = output[structure]; // TODO: Generalized structure +// input[infer] = false; +// } else if (output.infer_type) { +// output[structure] = input[structure]; +// output[infer] = false; +// } +// }); +// +// input.merge(output); +// // # Register than p2 has been merged into p1. +// // quotient[p2] = p1 +// } +// }); +// } +// +// /** +// * Return the tensor product of this graph with another. +// * +// * This does not modify either of the original graphs. TODO: Again this sort of thing should be abstracted elsewhere on what to do with them +// */ +// __mul__ = (other: Graph): Ray.Any => this.___something_something_copy(other, (a, b) => a.tensor(b)); +// +// /** +// * Return the composition of the current graph with `other`. +// * +// * Composition is done in diagram order (`other` comes after `self`), and neither of the two graphs are modified. +// * @param other +// */ +// __rshift__ = (other: Graph): Ray.Any => this.___something_something_copy(other, (a, b) => a.compose(b)); +// +// /** +// * TODO: These are just simple delegations of a single method on copies.. ; requires a nice abstraction layer +// */ +// ___something_something_copy = (b: Graph, something: (a: Graph, b: Graph) => void): Ray.Any => { +// const a_copy: Graph = this.copy().cast(); // TODO: Here preferring copy, do this everywhere? / Depending on forgetful preference.. +// something(a_copy, b); // TODO: Chyp just assumes b to be copied at the other end here??? +// return a_copy; // Could generalize to either end +// } +// +// /** +// * Set the `highlight` flag for a set of vertices and edges. +// * +// * This tells the GUI to visually highlight a set of vertices/edges, e.g. by drawing them in bold. Any vertices/edges not in the sets provided will be un-highlighted. +// * +// * @param vertices A set of vertices to highlight. +// * @param edges A set of edges to highlight. +// */ +// highlight = (vertices = set(int), edges = set(int)): Ray.Any => { +// // TODO Again, these could be merged +// this.vdata +// .filter(vertex => vertices.includes(vertex)) +// .all(vertex => vertex.cast().highlight = bool(true)); +// this.edata +// .filter(edge => edges.includes(edge)) +// .all(edge => edge.cast().highlight = bool(true)); +// +// } +// +// /** +// * Clear the `highlight` flag for all vertices/edges. +// * +// * This is equivalent to calling :func:`highlight` with empty sets of vertices/edges. +// */ +// unhighlight = (): Ray.Any => { +// // TODO: These could be merged +// this.vdata.highlight = false; +// this.edata.highlight = false; +// } +// +// /** +// * Return matches of domain into codomain. +// */ +// match = (other: Graph, convex: boolean = true): Matches => new Matches(this, other, convex); +// +// /** +// * Return an isomorphism between graphs g and h if found, otherwise `None`. +// * +// * If found, the isomorphism is returned as a :py:class:`Matches` instance, whose vertex and edge maps are bijections. +// */ +// find_iso = (other: Graph): Match => { // TODO | NONE +// +// // TODO: Again, more equivalence checks +// // First, check the domains and codomains are equal between the two graphs. +// if (this.domain !== other.domain || this.codomain !== other.codomain) +// return None; //TODO +// +// // Try to find an initial match mapping one of the boundary vertices of the domain graph to the corresponding boundary vertex (the vertex in the same input/output location) of the codomain graph. If no initial match is found, return `None`. +// const initial_match = new Match(this, other); +// +// const ___temp = (func: (ray: Graph) => Ray.Any) => { +// ([func(this), func(other)] as Ray).zip(([initial, terminal]) => { +// if (!initial_match.try_add_vertex(initial, terminal)) +// return None; //TODO, DOESNT ACTUALLY RETURN THE FUNCTION HERE. Wait till matcher is abstracted to fix this +// }); +// } +// +// ___temp(ray => Ray.Any.inputs); +// ___temp(ray => Ray.Any.outputs); // TODO: See again, the same pattern over and over again, same on both sides. +// +// // If an initial match is found, try to find a total and surjective match of the domain graph into the codomain graph. +// +// return new Matches(this, other, initial_match, false) +// .filter(match => match.is_surjective()); // TODO; With this filter it should allow you to keep looking +// +// // TODO::: Automatic?? +// // If a total surjective match is not found, return `None`. +// return None; +// } +// } +// +// export class Chyp extends Ray { +// __init__ = (): Ray.Any => { throw new NotImplementedError(); } +// +// /** +// * Load a .chyp graph file from the given path. +// */ +// load_graph = (path = str) => { +// // TODO: From localStorage for now? +// // with open(path) as f: +// // g = graph_from_json(f.read()) +// } +// +// /** +// * Return a graph corresponding to the identity map. +// * +// * This graph has a single vertex which is both an input and an output. +// */ +// identity = (vertex: VData): Graph => { +// const graph = new Graph(); +// +// vertex.x = 0; // TODO automatic>? +// vertex.y = 0; +// graph.add_vertex(int(-1), vertex); +// // TODO synce input/output automatically? +// // g.set_inputs([v]) +// // g.set_outputs([v]) +// +// return graph; +// } +// +// +// ___map_domain = (domain: Ray.Any, _default: VData): Ray.Any => { +// return domain.map(([vtype, size], i) => { +// const vertex: VData = _default.copy().cast(); +// // TODO: These should be automatic somewhere, again abstract the place where it's displayed elsewhere +// vertex.x = -1.5; +// vertex.y = i - (i-1) / 2; +// +// return vertex; +// }) +// } +// +// /** +// * Return a graph with one hyperedge and given domain and codomain. +// * +// * @param _default +// * @param domain - A list of pairs (vertex type, register size) corresponding to each input vertex. +// * @param codomain - A list of pairs (vertex type, register size) corresponding to each output vertex. +// */ +// gen = (_default: VData, domain: Ray.Any, codomain: Ray.Any): Graph => { +// const graph = new Graph(); +// +// const inputs = this.___map_domain(domain, _default) +// .map(vertex => graph.add_vertex(vertex)); +// const outputs = this.___map_domain(codomain, _default) +// .map(vertex => graph.add_vertex(vertex)); +// +// // TODO This is probably automatic at some point, remove +// const edge = new EData(); +// edge.s = inputs; +// edge.t = outputs; +// graph.add_edge(int(-1), edge); +// // g.set_inputs(inputs) +// // g.set_outputs(outputs) +// +// return graph; +// } +// +// /** +// * Return a graph corresponding to the given permutation. +// * +// * This takes a permution, given as a list [x0,..,x(n-1)], which is interpreted as the permutation { x0 -> 0, x1 -> 1, ..., x(n-1) -> n-1 }. It produces a graph consisting just of vertices, where input xj is mapped to the same vertex as output j, representing an identity wire connecting input xj to output j. +// * +// * Note this is one of two reasonable conventions for specifying a permutation as a list of numbers. This one has the property, e.g. for graphs aj : 0 -> 1, we have: (a0 * a1 * a2) >> perm([2, 0, 1]) = a2 * a0 * a1. +// * +// * @param p A permutation given as an n-element list of integers from 0 to n-1. +// * @param domain The domain type of the permutation. This consists of a list of pairs (vertex type, register size) corresponding to each input vertex of the edge. If `None`, the domain is assumed to be the appropriate number of default type vertices all with register size 1. +// */ +// perm = (p = list(int), domain: Ray.Any, _default: VData) => { +// +// const graph = new Graph(); +// const num_wires = p.count.as_int(); +// +// if (num_wires !== domain.count.as_int()) +// throw new GraphError(`Domain ${domain} does not match length of permutation.`) +// +// // TODO use ___map_domain +// const inputs = domain.map(([vtype, size], i) => { +// const vertex: VData = _default.copy().cast(); +// // TODO: These should be automatic somewhere, again abstract the place where it's displayed elsewhere +// vertex.x = 0; +// vertex.y = i - (num_wires - 1) / 2; +// +// return vertex; +// }) +// .map(vertex => graph.add_vertex(vertex)); +// // const outputs = num_wires.range(i => [inputs[p[i]]) +// +// // TODO Should be automatic +// // g.set_inputs(inputs) +// // g.set_outputs(outputs) +// return graph; +// } +// +// graph_from_json = (json_string = str): Graph => { +// const json = JSON.parse(json_string().as_string()); // TODO +// +// const graph = new Graph(); +// +// // TODO: Don't do this so naively +// // g.add_vertex(x=float(vd["x"] if "x" in vd else 0.0), +// // y=float(vd["y"] if "y" in vd else 0.0), +// // value=vd["value"] if "value" in vd else "", +// // name=int(v)) +// // for e, ed in j["edges"].items(): +// // g.add_edge(s=[int(v) for v in ed["s"]], +// // t=[int(v) for v in ed["t"]], +// // value=ed["value"] if "value" in ed else "", +// // x=float(ed["x"]) if "x" in ed else 0.0, +// // y=float(ed["y"]) if "y" in ed else 0.0, +// // hyper=bool(ed["hyper"]) if "hyper" in ed else True, +// // name=int(e)) +// // +// // g.set_inputs([int(v) for v in j["inputs"]]) +// // g.set_outputs([int(v) for v in j["outputs"]]) +// // json.vertices.forEach(((vertex, i) => graph.add_vertex( +// // i, +// // new VData() +// // )); +// +// return graph; +// } +// +// /** +// * # def wide_id() -> Graph: +// * # return gen("id", 1, 1) +// * +// * # def id_perm(p: List[int]) -> Graph: +// * # g = Graph() +// * # size = len(p) +// * # inputs = [g.add_vertex(-1.5, i - (size-1)/2) for i in range(size)] +// * # outputs = [g.add_vertex(1.5, i - (size-1)/2) for i in range(size)] +// * +// * # for i in range(size): +// * # y = i - (size-1)/2 +// * # g.add_edge([inputs[i]], [outputs[p[i]]], "id", 0, y) +// * +// * # g.set_inputs(inputs) +// * # g.set_outputs(outputs) +// * +// * # return g +// */ +// +// /** +// * Return a graph corresponding to a vertex size redistribution. +// * +// * A specific case of this family of graphs are 'dividers', which split a vertex of some type and size into multiple size 1 vertices of the same type. Conversely, 'gatherers' bundle multiple vertices of the same type into a single vertex of the same type and size the sum of the individual input vertex sizes. +// * +// * More generally, a conversion can be done between different lists of sizes, for some vertex type. +// * +// * @param domain A list of pairs (vertex type, register size) corresponding to each input vertex. +// * @param codomain A list of pairs (vertex type, register size) corresponding to each output vertex. +// */ +// redistributer = (domain = list, codomain = list) => { +// /** +// * vtypes = set(vtype for vtype, _ in domain) +// * vtypes.update(vtype for vtype, _ in codomain) +// * if len(vtypes) > 1: +// * raise GraphError('Size conversion cannot mix vertex types.') +// * +// * # Raise error if size conservation is violated +// * domain_size = sum(size for _, size in domain) +// * codomain_size = sum(size for _, size in codomain) +// * if domain_size != codomain_size: +// * raise GraphError(f'Sum of domain sizes ({domain_size}) does not equal' +// * + f'sum of codomain sizes ({codomain_size}).') +// * +// * return gen('_redistributer', domain, codomain) +// */ +// +// } +// +// /** +// * Some more delegations here +// */ +// match_graph = (domain: Graph, codomain: Graph, convex: boolean = true): Matches => domain.match(codomain, convex); +// match_rule = (rule: Rule, codomain: Graph, convex: boolean = true): Matches => rule.match(codomain, convex); +// find_iso = (domain: Graph, codomain: Graph): Match => domain.find_iso(codomain); +// } +// +// export class CodeView extends Ray { +// __init__ = (): Ray.Any => { throw new NotImplementedError(); } +// popup_visible = (): Ray.Any => { throw new NotImplementedError(); } +// set_completions = (completions = Iterable(str)): Ray.Any => { throw new NotImplementedError(); } +// ident_at_cursor = (): Ray.Any => { throw new NotImplementedError(); } +// insert_completion = (completion = str): Ray.Any => { throw new NotImplementedError(); } +// keyPressEvent = (e = QKeyEvent): Ray.Any => { throw new NotImplementedError(); } +// set_current_region = (region = Optional(Tuple(int,int))): Ray.Any => { throw new NotImplementedError(); } +// add_line_below = (text = str): Ray.Any => { throw new NotImplementedError(); } +// } +// +// export class CodeCompletionModel extends Ray { +// __init__ = (parent = QObject): Ray.Any => { throw new NotImplementedError(); } +// set_completions = (completions = Iterable(str)): Ray.Any => { throw new NotImplementedError(); } +// data = (index = Union(QModelIndex, QPersistentModelIndex)): Ray.Any => { throw new NotImplementedError(); } +// rowCount = (): Ray.Any => { throw new NotImplementedError(); } +// } +// +// export class ChypDocument extends Ray { +// __init__ = (parent = QWidget): Ray.Any => { throw new NotImplementedError(); } +// confirm_close = (): Ray.Any => { throw new NotImplementedError(); } +// add_to_recent_files = (file_name = str): Ray.Any => { throw new NotImplementedError(); } +// open = (file_name = str): Ray.Any => { throw new NotImplementedError(); } +// save = (): Ray.Any => { throw new NotImplementedError(); } +// save_as = (): Ray.Any => { throw new NotImplementedError(); } +// } +// +// export class Editor extends Ray { +// __init__ = (): Ray.Any => { throw new NotImplementedError(); } +// title = (): Ray.Any => { throw new NotImplementedError(); } +// reset_state = (): Ray.Any => { throw new NotImplementedError(); } +// invalidate_text = (): Ray.Any => { throw new NotImplementedError(); } +// next_part = (): Ray.Any => { throw new NotImplementedError(); } +// jump_to_error = (): Ray.Any => { throw new NotImplementedError(); } +// show_errors = (): Ray.Any => { throw new NotImplementedError(); } +// show_at_cursor = (): Ray.Any => { throw new NotImplementedError(); } +// next_rewrite_at_cursor = (): Ray.Any => { throw new NotImplementedError(); } +// repeat_step_at_cursor = (): Ray.Any => { throw new NotImplementedError(); } +// update_state = (): Ray.Any => { throw new NotImplementedError(); } +// import_at_cursor = (): Ray.Any => { throw new NotImplementedError(); } +// } +// +// export class CheckThread extends Ray { +// __init__ = (rw = RewriteState): Ray.Any => { throw new NotImplementedError(); } +// run = (): Ray.Any => { throw new NotImplementedError(); } +// } +// +// export class ErrorListModel extends Ray { +// __init__ = (): Ray.Any => { throw new NotImplementedError(); } +// set_errors = (errors = List(Tuple(str, int, str))): Ray.Any => { throw new NotImplementedError(); } +// data = (index = Union(QModelIndex, QPersistentModelIndex)): Ray.Any => { throw new NotImplementedError(); } +// headerData = (section = int, orientation = Orientation): Ray.Any => { throw new NotImplementedError(); } +// index = (row = int, column = int): Ray.Any => { throw new NotImplementedError(); } +// columnCount = (): Ray.Any => { throw new NotImplementedError(); } +// rowCount = (): Ray.Any => { throw new NotImplementedError(); } +// parent = (): Ray.Any => { throw new NotImplementedError(); } +// } +// +// export class EItem extends Ray { +// __init__ = (g = Graph, e = int): Ray.Any => { throw new NotImplementedError(); } +// paint = (painter = QPainter, option = QStyleOptionGraphicsItem): Ray.Any => { throw new NotImplementedError(); } +// } +// +// export class VItem extends Ray { +// __init__ = (g = Graph, v = int): Ray.Any => { throw new NotImplementedError(); } +// refresh = (): Ray.Any => { throw new NotImplementedError(); } +// } +// +// export class TItem extends Ray { +// __init__ = (vitem = VItem, eitem = EItem, i = int, src = bool): Ray.Any => { throw new NotImplementedError(); } +// refresh = (): Ray.Any => { throw new NotImplementedError(); } +// } +// +// export class GraphScene extends Ray { +// __init__ = (): Ray.Any => { throw new NotImplementedError(); } +// set_graph = (g = Graph): Ray.Any => { throw new NotImplementedError(); } +// add_items = (): Ray.Any => { throw new NotImplementedError(); } +// mousePressEvent = (e = QGraphicsSceneMouseEvent): Ray.Any => { throw new NotImplementedError(); } +// mouseMoveEvent = (e = QGraphicsSceneMouseEvent): Ray.Any => { throw new NotImplementedError(); } +// mouseReleaseEvent = (_ = QGraphicsSceneMouseEvent): Ray.Any => { throw new NotImplementedError(); } +// } +// +// export class GraphView extends Ray { +// __init__ = (): Ray.Any => { throw new NotImplementedError(); } +// set_graph = (g = Graph): Ray.Any => { throw new NotImplementedError(); } +// } +// +// export class ChypHighlighter extends Ray { +// __init__ = (doc = QTextDocument): Ray.Any => { throw new NotImplementedError(); } +// set_current_region = (region = Optional(Tuple(int,int)), status = int): Ray.Any => { throw new NotImplementedError(); } +// highlightBlock = (text = str): Ray.Any => { throw new NotImplementedError(); } +// } +// +// export class MainWindow extends Ray { +// __init__ = (): Ray.Any => { throw new NotImplementedError(); } +// remove_empty_editor = (): Ray.Any => { throw new NotImplementedError(); } +// update_file_name = (): Ray.Any => { throw new NotImplementedError(); } +// tab_changed = (i = int): Ray.Any => { throw new NotImplementedError(); } +// update_themes = (): Ray.Any => { throw new NotImplementedError(); } +// recent_files = (): Ray.Any => { throw new NotImplementedError(); } +// update_recent_files = (): Ray.Any => { throw new NotImplementedError(); } +// add_tab = (ed = Editor, title = str): Ray.Any => { throw new NotImplementedError(); } +// close_tab = (): Ray.Any => { throw new NotImplementedError(); } +// new = (): Ray.Any => { throw new NotImplementedError(); } +// open = (): Ray.Any => { throw new NotImplementedError(); } +// save = (): Ray.Any => { throw new NotImplementedError(); } +// save_as = (): Ray.Any => { throw new NotImplementedError(); } +// undo = (): Ray.Any => { throw new NotImplementedError(); } +// redo = (): Ray.Any => { throw new NotImplementedError(); } +// show_errors = (): Ray.Any => { throw new NotImplementedError(); } +// add_rewrite_step = (): Ray.Any => { throw new NotImplementedError(); } +// repeat_rewrite_step = (): Ray.Any => { throw new NotImplementedError(); } +// next_rewrite = (): Ray.Any => { throw new NotImplementedError(); } +// next_part = (): Ray.Any => { throw new NotImplementedError(); } +// previous_part = (): Ray.Any => { throw new NotImplementedError(); } +// next_tab = (): Ray.Any => { throw new NotImplementedError(); } +// previous_tab = (): Ray.Any => { throw new NotImplementedError(); } +// goto_import = (): Ray.Any => { throw new NotImplementedError(); } +// closeEvent = (e = QCloseEvent): Ray.Any => { throw new NotImplementedError(); } +// build_menu = (): Ray.Any => { throw new NotImplementedError(); } +// } +// +// // TODO ISNT A MATCH JUST AN IGNORANT COPY ON BOTH SIDES??? +// export class Match extends Ray { +// +// get vertex_map(): Ray.Any { throw new NotImplementedError(); } +// get vertex_image(): Ray.Any { throw new NotImplementedError(); } +// get edge_map(): Ray.Any { throw new NotImplementedError(); } +// get edge_image(): Ray.Any { throw new NotImplementedError(); } +// +// __str__ = (): Ray.Any => { +// // (f'\tVertex map: {str(self.vertex_map)}' +// // + f'\n\tEdge map: {str(self.edge_map)}') +// // TODO +// throw new NotImplementedError(); } +// +// // Implemented on Ray +// // TODO; doesnt copy the graphs at domain/codomain +// // copy = (): Ray.Any => { throw new NotImplementedError(); } +// +// /** +// * Try to map `domain_vertex` to `codomain_vertex`. +// * +// * This map must satisfy various conditions, such as only being allowed to be non-injective on the boundary domain vertices. +// * +// * Returns: `True` if either a consistent map from `domain_vertex` to already exists or the new map is consistent and satisfies the gluing conditions, otherwise `False`. +// */ +// try_add_vertex = (domain_vertex: VData, codomain_vertex: VData): Ray.Any => { +// +// // If the vertex is already mapped, only check the new mapping is consistent with the current match. +// if (this.vertex_map.includes(domain_vertex)) { +// log(`Vertex already mapped to ${this.vertex_map[domain_vertex]}.`) +// return this.vertex_map[domain_vertex] === codomain_vertex; +// } +// +// // TODO: ALL THESE SHOULD JUST BE AN EQUIVALENCY CHECK, JUST LIKE MATCHING/// - You can't actually guarantee consistency, it's just a simple ignorant check. - We can probably just remove this as soon as the equivalency check is implemented. +// // TODO: This should all be redundant, probably removed ... +// +// // Ensure the mapping preserves vertex type. +// if (domain_vertex.vtype !== codomain_vertex.vtype) { +// log(`Vertex failed: vtypes ${domain_vertex.vtype} != ${codomain_vertex.vtype} do not match.`); +// +// return bool(False); +// } +// +// // Ensure the mapping preserves vertex size. +// if (domain_vertex.size !== codomain_vertex.size) { +// log(`Vertex failed: sizes ${domain_vertex.size} != ${codomain_vertex.size} do not match.`) +// return bool(False); +// } +// +// // Ensure non-boundary vertices in the domain are not mapped to boundary vertices in the codomain. +// if (!codomain_vertex.is_boundary() && !domain_vertex.is_boundary()) { +// log('Vertex failed: codomain vertex is boundary but domain vertex is not.') +// return bool(False); +// } +// +// // Matches must be injective everywhere except the boundary, so if the domain vertex is already mapped to another codomain vertex, check whether this non-injective mapping is permitted. +// if (this.vertex_image.includes(codomain_vertex)) { +// // If the domain vertex we are trying to add is not a boundary vertex, it cannot be used in a non-injective mapping. +// if (!domain_vertex.is_boundary()) { +// log('Vertex failed: non-injective on interior vertex.') +// return bool(False); +// } +// +// // If any vertices already mapped to the codomain vertex, they must also be boundary vertices for an allowed non-injective mapping. +// if (this.vertex_image.any(([mapped_vertex, image_vertex]) => +// image_vertex === codomain_vertex && !mapped_vertex.is_boundary() // TODO mapped_vertex on domain!! +// )) { +// log('Vertex failed: non-injective on interior vertex.') +// return bool(False); +// } +// +// return bool(False); +// } +// +// // If a new and consistent map is found, add it to the vertex map of this match. +// this.vertex_map[domain_vertex] = codomain_vertex +// this.vertex_image.add(codomain_vertex) +// +// // Unless the domain vertex is a boundary vertex, check that the number of adjacent edges of the codomain vertex is the same as the number for the domain vertex. Because matchings are required to be injective on edges, this will guarantee that the gluing conditions are satisfied. +// +// if (!domain_vertex.is_boundary()) { +// if (domain_vertex.in_edges.count.as_int() !== codomain_vertex.in_edges.count.as_int()) { +// log('Vertex failed: in_edges cannot satisfy gluing conditions.') +// return bool(False) +// } +// if (domain_vertex.out_edges.count.as_int() !== codomain_vertex.out_edges.count.as_int()) { +// log('Vertex failed: in_edges cannot satisfy gluing conditions.') +// return bool(False) +// } +// } +// +// // If a new consistent map is added that satisfies the gluing conditions, we are successful. +// log('Vertex success.') +// return bool(True); +// } +// +// /** +// * Try to map `domain_edge` to `codomain_edge`. +// * +// * This must satisfy certain conditions, such as being injective and having consistency with the vertex map. +// * +// * @param domain_edge +// * @param codomain_edge +// * +// * `True` if a consistent match is found mapping `domain_edge` to `codomain_edge`, otherwise `False`. +// */ +// try_add_edge = (initial: EData, terminal: EData): Ray.Any => { +// log(`Trying to add edge ${initial} -> ${terminal} to match:`); +// log(this.toString()); +// +// // TODO: Again an equivalence +// // Check the values of the domain and codomain edges match. +// if (initial.value !== terminal.value) { +// log(`Edge failed: values ${initial.value} != ${terminal.value}`) +// return false; +// } +// +// // The edge map must be injective. +// if (this.edge_image.includes(terminal)) { +// console.log('Edge failed: the map would become non-injective.'); +// return false; +// } +// +// // If the values match and the codomain edge has not already been mapped to, map domain edge to codomain edge. +// this.edge_map[initial] = terminal; +// this.edge_image.add(terminal); +// +// // Domain sources must match codomain sources and domain targets must match codomain targets. +// +// // TODO: Again more equivalences, just to log - debug mode +// { +// // initial = preimg +// // terminal = image +// +// // Domain sources must match codomain sources and domain targets must match codomain targets. +// if (initial.domain !== terminal.domain) { +// log(`Edge domain ${initial.domain} does not match image domain ${terminal.domain}.`); +// } +// +// // TODO, again both sides +// if (initial.codomain !== terminal.codomain) { +// log(`Edge codomain ${initial.codomain} does not match image codomain ${terminal.codomain}.`); +// } +// } +// +// // TODO: This too probably general pattern to extract +// // Check a vertex map consistent with this edge pairing exists. +// // TODO: + here is just concat, so that zip can easily match it +// ([initial.source + initial.target, terminal.source + terminal.target] as Ray).zip(([initial_vertex, terminal_vertex]) => { +// // Each vertex that is already mapped needs to be consistent. +// // TODO: More equivlaency, could be on the vertecies themselves +// if (this.vertex_map.includes(initial_vertex) && this.vertex_image[initial_vertex] !== terminal_vertex) { +// log('Edge failed: inconsistent with previously mapped vertex.'); +// return false; // TODO: NOT RETURNING AGAIN +// } +// +// // Otherwise, a consistent match must be found vertex for unmapped source and target vertices. +// +// if (!this.try_add_vertex(initial_vertex, terminal_vertex)) { +// log('Edge failed: couldn\'t add a source or target vertex.'); +// return false; +// } +// }); +// +// log('Edge success.'); +// return true; +// } +// +// /** +// * Return whether all adjacent edges of a domain vertex are mapped. +// */ +// domain_neighbourhood_mapped = (vertex: VData): Ray.Any => +// ([vertex.in_edges + vertex.out_edges] as Ray).all(edge => this.edge_map.includes(edge)); +// +// /** +// * # def cod_nhd_mapped(self, cod_v: int): +// * # """Returns True if nhd(cod_v) is the range of emap""" +// * # return (all(e in self.eimg for e in self.cod.in_edges(cod_v)) and +// * # all(e in self.eimg for e in self.cod.out_edges(cod_v))) +// */ +// +// /** +// * Try to extend the match by mapping all scalars (i.e. 0 -> 0 edges). +// * +// * Note that any matchings of scalars will yield isomorphic results under rewriting, so we don't return a list of all the possible matchings. +// * +// * Returns: `True` if all scalars in the domain are mapped injectively to scalars in the codomain, otherwise `False`. +// */ +// map_scalars = (): Ray.Any => { +// //TODO WHAT'S A SCALAR HERE? +// // TODO: Again same pattern for (co)domain - flipped, two dimensional +// const ___scalars = (domain: Graph, reverse: boolean) => { +// const is = (ray: Ray.Any) => reverse ? ray.count !== 0 : Ray.Any.count === 0; // TODO: Should be easier to just .not this +// +// return domain.edges +// .filter((edge: EData) => this.is(edge.source) && this.is(edge.target)); +// } +// +// const terminal = ___scalars(this.codomain, false); +// const initial = ___scalars(this.domain, true); +// +// // Greedily try to map scalar edges in the domain to scalar edges in the codomain with the same value. +// initial.all((initial_edge: EData) => { +// // TODO: Put initial/terminal edge on a ray and then apply funcitons +// +// log(`Trying to map scalar edge ${initial_edge}`) +// +// let found_match = false; +// +// // TODO .enumerate?? +// terminal.all(([i, terminal_edge]) => { +// if (initial_edge.value !== terminal_edge.value) // ANother equiv +// return; // TODO continue; +// +// // Map the domain scalar to the first codomain scalar available with the same value. +// this.edge_map[initial_edge] = terminal_edge; +// this.edge_image.add(terminal_edge); // TODO: Again map/image is just initial/terminal.. same as domain/codomain,, +// +// found_match = true; +// +// // Since the edge map must be injective, if a scalar in the codomain is mapped to, remove it from the list of candidates for future domain scalars to be mapped to. +// terminal.pop(i); // TODO: ??? +// +// log(`Successfully mapped scalar ${initial_edge} -> ${terminal_edge}`) +// +// // TODO: BREAK ; any?? +// }); +// +// if (!found_match) { +// log(`Match failed: could not map scalar edge ${initial_edge}.`) +// return false; // TODO DOESNT RETURN +// } +// }); +// +// return true; +// } +// +// +// // TODO: IF MORE JUST ADDS MORE MATCHES IN THIS DIRECTION, THAT SHOULD BE AUTOMATIC ON .match ... ? +// /** +// * Return any matches extending `self` by a single vertex or edge. +// */ +// more = (): Ray.Any => { +// // First, try to add an edge adjacent to any domain vertices that have already been matched. +// this.vertex_map +// // If all the edges adjacent to the current vertex have already been matched, continue. TODO: Could just be on the .vertex +// .filter(initial_vertex => this.domain_neighbourhood_mapped(initial_vertex)) +// .all((initial_vertex: VData) => { +// // TODO: AS ZIp or something +// const terminal_vertex = this.vertex_map[initial_vertex]; +// +// const ___test = (boundary: (ray: Graph) => Ray.Any): Ray.Any => { +// // Try to extend the match by mapping an adjacent source edge. +// boundary(this.domain) // TODO: AGAIN SAME THING +// // If the edge has already been matched, continue. +// .filter(edge => !this.edge_map.includes(edge)) +// .all((initial_edge: EData) => { +// +// // Otherwise, try to map this edge into the codomain graph. +// return boundary(this.codomain).map(terminal_edge => { +// const potential_new_match = this.copy(); +// +// // If the edge is successfully mapped to an edge in the codomain graph, extend the match with this mapping. +// if (potential_new_match.try_add_edge(initial_edge, terminal_edge)) +// return potential_new_match; +// +// return NONE;//todo +// }); // TODO REMOVE NON-ADDED_ONES, doesnt actually reutn either +// }) +// +// } +// +// ___test(ray => Ray.Any.in_edges); +// ___test(ray => Ray.Any.out_edges); +// +// }); +// +// // If all domain edges adjacent to matched domain vertices have already been matched, try to match an unmatched domain vertex. +// this.domain.vertices +// // If the vertex has already been matched into the codomain graph, continue. (Note we have looked at the edge-neighbourhood of these vertices above) +// .filter(initial_vertex => this.vertex_map.includes(initial_vertex)) // TODO: THIS THING IS JUST A COPY, AGAIN FROM THE VERTEX/EDGE DIFFERENTIATION FROM ABOVE +// .all(initial_vertex => { +// +// // Try to map the current domain vertex to any of the codomain vertices, extending the current match with this map when successful. +// return this.codomain.vertices.map((terminal_vertex: VData) => { +// const potential_new_match = this.copy(); +// +// // If the edge is successfully mapped to an edge in the codomain graph, extend the match with this mapping. +// if (potential_new_match.try_add_vertex(initial_edge, terminal_edge)) +// return potential_new_match; +// +// return NONE;//todo +// }); // TODO: AGAIN DOESNT RETURN +// }) +// +// return []; +// } +// +// // TODO: Total=surjective on another level of description ??? +// ___defuq = (string: 'image' | 'map', domain) => +// this[`vertex_${string}`].count === domain.vertices.count +// && this[`edge_${string}`].count === domain.edges.count; // TODO: Again vertex/edge pattern, just collapse to one check +// +// /** +// * Return whether all domain vertices and edges have been mapped. +// */ +// is_total = (): Ray.Any => this.___defuq('map', this.domain); +// /** +// * Return whether the vertex and edge maps are surjective. +// */ +// is_surjective = (): Ray.Any => this.___defuq('image', this.codomain); +// +// /** +// * Return whether the vertex and edge maps are injective. +// */ +// is_injective = (): Ray.Any => { +// // Since the edge map is always injective, we only need to check the vertex map is injective. TODO (GENERALIZE) +// return this.vertex_map.count === this.vertex_image.count; +// } +// +// /** +// * Return whether this match is convex. +// * +// * A match is convex if: +// * - It is injective. +// * - Its image in the codomain hypergraph is a convex sub-hypergraph. This means that for any two nodes in the sub-hypergraph and any path between these two nodes, every hyperedge along the path is also in the sub-hypergraph. +// * +// * TODO: Just no overlap?? +// */ +// is_convex = (): Ray.Any => { +// if (!this.is_injective()) { //TODO: WHY? +// return false; +// } +// +// +// // Check that the image sub-hypergraph is convex. Get all successors of vertices in the image of the output of the domain graph. +// const output_image_successors = this.codomain.successors( +// this.domain.outputs +// .filter(vertex => this.vertex_map[vertex]) // TODO INCLUDES +// ); // TODO, Why search in codomain fromi these, ?? this must be easier +// +// // Check there is no path from any vertices in the image of the domain outputs to a vertex in the image of the domain inputs. +// this.domain.inputs +// .filter(vertex => this.vertex_map.includes(vertex) && output_image_successors.includes(this.vertex_map[vertex])) +// .any(() => { return false }) // TODO DOESNT ACTUALLY RETURNM +// +// return true; +// } +// +// /** +// * TODO Not implemented;; +// * # def cod_nhd_mapped(self, cod_v: int): +// * # """Returns True if nhd(cod_v) is the range of emap""" +// * # return (all(e in self.eimg for e in self.cod.in_edges(cod_v)) and +// * # all(e in self.eimg for e in self.cod.out_edges(cod_v))) +// */ +// } +// +// /** +// * An iterator over matches of one graph into another. +// * +// * This class can be used to iterate over total matches of a graph into another, optionally requiring these matches to be convex. +// * +// * TODO: SUPPORT THIS/?::: +// * A class instance works by keeping a stack of partial or total matches. +// * When it is iterated over, it pops a match from its match stack if it is +// * non-empty, otherwise the iteration is stopped. If a match has been popped, +// * the instance returns the match if it is total (and convex if required). +// * Otherwise, the instance tries to extend the match and add any extended +// * matches to the stack, then continues this process of popping off the match +// * stack and extending if possible until a valid match is found and returned. +// */ +// export class Matches extends Ray { +// __init__ = (domain = Graph, codomain = Graph): Ray.Any => { +// /** +// * TODO +// * if initial_match is None: +// * initial_match = Match(domain=domain, codomain=codomain) +// * self.convex = convex +// * +// * # Try to map scalars on the initial match. +// * if initial_match.map_scalars(): +// * self.match_stack = [initial_match] +// * # If the scalars could not be mapped, set the match +// * # stack to be empty. This means that not suitable matches +// * # will be found by this class instance. +// * else: +// * self.match_stack = [] +// */ +// throw new NotImplementedError(); } +// __iter__ = (): Ray.Any => { throw new NotImplementedError(); } +// +// /** +// * Return the next suitable match found. +// * +// * A 'suitable' match is one that is total and, if `self.convex == True`, convex. +// */ +// __next__ = (): Ray.Any => { +// // TODO Like expected this, class can probably be dropped this just becomes +// +// return this.match_stack.dont_check_for_convex_or.is_convex; // TODO: Just generalize these on functions +// +// /** +// * while len(self.match_stack) > 0: +// * # Pop the match at the top of the match stack +// * m = self.match_stack.pop() +// * # If the match is total (and convex if required), return it. +// * if m.is_total(): +// * match_log("got successful match:\n" + str(m)) +// * if self.convex: +// * if m.is_convex(): +// * match_log("match is convex, returning") +// * return m +// * else: +// * match_log("match is not convex, dropping") +// * else: +// * return m +// * # If the match at the top of the match stack was not total +// * # (and convex if required), try to extend the match at the +// * # top of the stack and add the results to the match stack. +// * else: +// * self.match_stack += m.more() +// * # If a suitable match was not found, stop the iteration. +// * raise StopIteration +// */ +// } +// } +// +// export class Rule extends Ray { +// +// get equiv(): Ray.Any { throw new NotImplementedError(); } +// +// +// /** +// * Returns True if boundary on lhs embeds injectively +// */ +// is_left_linear = (): Ray.Any => { +// // TODO, needs to implement splat and stuff? or by default, could be done smarter, but again no overloading - Or should just equivalence a copy?? +// return !JS.Iterable([...this.lhs.inputs, ...this.rhs.outputs]).as_ray() +// .has_duplicates() // TODO; This thing is basically asking whether any input is used twice, whether any output is used twice, or there's a circle between in/output? Basically: NO SELF-REFERENCE, this should be a very sikmple check whether any frame is used twice here - or some loop is found basically. +// } +// +// /** +// * Do double-pushout rewriting +// * +// * Given a rule r and match of r.lhs into a graph, return a match of r.rhs into the rewritten graph. +// * @param match +// */ +// dpo = (match: Match): Ray.Any => { +// // if (!this.is_left_linear()) +// // throw new NotImplementedError("Only left linear rules are supported for now") +// +// const rewritten_graph: Graph = match.codomain.copy(); +// +// // TODO: This will definitely be rewritten, this is an ugly implementation. Just draw the lines and be ignorant in that direction ??? Probably just implement it as additional filtering on .copy()??? +// +// // Computes the push complement ?? (just the thing we're replacing right?) +// // TODO: Removes the match from the copy? +// this.lhs.edges.all(edge => rewritten_graph.remove_edge(match.edge_map(edge))); +// +// const inputs = []; +// const outputs = []; +// +// this.lhs.vertices.all(rule_vertex => { +// const match_vertex = match.vertex_map[rule_vertex]; +// +// if (!this.lhs.is_boundary(rule_vertex)) { +// rewritten_graph.remove_vertex(match_vertex); +// return; +// } +// +// const rule_input = rule_vertex.in_indices; +// const rule_output = rule_vertex.out_indices; +// +// if (rule_input.count.as_int() === 1 && rule_output === 1) { +// const [match_input, match_output] = rewritten_graph.explode_vertex(match_vertex); +// +// if (match_input.count.as_int() !== 1 && match_output.count.as_int() !== 1) +// throw new NotImplementedError("Rewriting modulo Frobenius not yet supported."); +// +// inputs[rule_vertex] = match_input[0]; +// outputs[rule_vertex] = match_output[0]; +// } else if (rule_input.count.as_int() > 1 || rule_output.count.as_int() > 1) { +// throw new NotImplementedError("Rewriting modulo Frobenius not yet supported."); +// } +// }) +// +// // Embed Rule.rhs into rewritten_graph +// const replacement = new Match(this.rhs, rewritten_graph); +// +// // TODO: This is probably another of these geeneral patterns, same thing on either ends composed as a single ray., +// const input = new Ray(); // [this.lhs.inputs, this.rhs.inputs] +// +// // first map the inputs, using the matching of the lhs +// input.zip().all(([initial, terminal]) => { +// match.vertex_map[terminal] = inputs.includes(initial) ? inputs[initial] : match.vertex_map[initial]; +// }); +// +// const output = new Ray(); // [this.lhs.outputs, this.rhs.outputs] +// +// // next map the outputs. if the same vertex is an input and an output in r.rhs, then merge them in h. +// output.zip().all(([initial, terminal]) => { +// // TODO: Again, both ends are very similar, there should probably be a direct flip in these, small difference at the end +// const temp = outputs.includes(initial) ? outputs[initial] : match.vertex_map[initial]; +// +// if (match.vertex_map.includes(terminal)) { +// rewritten_graph.merge_vertices(match.vertex_map[terminal], temp) +// } else { +// match.vertex_map[terminal] = temp; +// } +// }); +// +// // then map the interior to new, fresh vertices +// this.rhs.vertices +// .filter(vertex => !vertex.is_boundary()) +// .all((vertex: VData) => { +// // TODO Again the fresh thing repeated here - just because it's moving between graphs +// const new_vertex = rewritten_graph.add_vertex(vertex.fresh()); +// +// match.vertex_map[vertex] = new_vertex; +// match.vertex_image.add(new_vertex); +// }); +// +// // now add the edges from rhs to h and connect them using vmap1 +// // TODO: Again this should be the same thing as the vertices +// this.rhs.edges. +// all((edge: EData) => { +// const new_edge = rewritten_graph.add_edge(); +// /** +// * e1 = h.add_edge([m1.vertex_map[v] for v in ed.s], +// * [m1.vertex_map[v] for v in ed.t], +// * ed.value, ed.x, ed.y, ed.fg, ed.bg, ed.hyper) +// */ +// +// match.edge_map[edge] = new_edge; +// match.edge_image.add(new_edge); +// }) +// +// return replacement; +// } +// +// // TODO: Can probably just match Rule=Graph, and use only one of these methods?? +// /** +// * Return matches of the left side of `rule` into `graph`. +// */ +// match = (other: Graph, convex: boolean = true): Matches => new Matches(this.lhs, other, convex); +// +// /** +// * Apply the given rewrite r to at match m and return the first result +// * +// * This is a convenience wrapper for `dpo` for when the extra rewrite data isn't needed. +// */ +// rewrite = (match: Match): Ray.Any => this.dpo(match).first.terminal; // .first.codomain +// // TODO; Though .first is used here, something like .any is more appropriate in the sense of: Don't care which one first, just something. +// } +// +// export class RewriteState extends Ray { +// __init__ = (sequence = int, state = State): Ray.Any => { throw new NotImplementedError(); } +// check = (): Ray.Any => { throw new NotImplementedError(); } +// } +// +// export class State extends Ray { +// __init__ = (): Ray.Any => { throw new NotImplementedError(); } +// part_with_index_at = (pos = int): Ray.Any => { throw new NotImplementedError(); } +// part_at = (pos = int): Ray.Any => { throw new NotImplementedError(); } +// var = (items = List(Any)): Ray.Any => { throw new NotImplementedError(); } +// module_name = (items = List(Any)): Ray.Any => { throw new NotImplementedError(); } +// num = (items = List(Any)): Ray.Any => { throw new NotImplementedError(); } +// type_element = (items = list(Any)): Ray.Any => { throw new NotImplementedError(); } +// type_term = (items = list(JS.Iterable([tuple(JS.Iterable([str, None]), int), None]))): Ray.Any => { throw new NotImplementedError(); } +// id = (items = list(Any)): Ray.Any => { throw new NotImplementedError(); } +// id0 = (_ = List(Any)): Ray.Any => { throw new NotImplementedError(); } +// eq = (_ = List(Any)): Ray.Any => { throw new NotImplementedError(); } +// le = (_ = List(Any)): Ray.Any => { throw new NotImplementedError(); } +// perm_indices = (items = list(int)): Ray.Any => { throw new NotImplementedError(); } +// size_list = (items = list(int)): Ray.Any => { throw new NotImplementedError(); } +// par = (items = List(Any)): Ray.Any => { throw new NotImplementedError(); } +// gen_color = (items = List(Any)): Ray.Any => { throw new NotImplementedError(); } +// color = (items = List(Any)): Ray.Any => { throw new NotImplementedError(); } +// import_let = (items = List(Any)): Ray.Any => { throw new NotImplementedError(); } +// tactic = (items = List(Any)): Ray.Any => { throw new NotImplementedError(); } +// nested_term = (items = List(Any)): Ray.Any => { throw new NotImplementedError(); } +// } +// +// export class Tactic extends Ray { +// __init__ = (local_state = RewriteState, args = List(str)): Ray.Any => { throw new NotImplementedError(); } +// repeat = (rw = Callable([str], bool), rules = List(str)): Ray.Any => { throw new NotImplementedError(); } +// error = (message = str): Ray.Any => { throw new NotImplementedError(); } +// has_goal = (): Ray.Any => { throw new NotImplementedError(); } +// global_rules = (): Ray.Any => { throw new NotImplementedError(); } +// lookup_rule = (rule_expr = str): Ray.Any => { throw new NotImplementedError(); } +// add_refl_to_context = (graph = Graph, ident = str): Ray.Any => { throw new NotImplementedError(); } +// add_rule_to_context = (rule_name = str): Ray.Any => { throw new NotImplementedError(); } +// __lhs = (target = str): Ray.Any => { throw new NotImplementedError(); } +// __rhs = (target = str): Ray.Any => { throw new NotImplementedError(); } +// __set_lhs = (target = str, graph = Graph): Ray.Any => { throw new NotImplementedError(); } +// __set_rhs = (target = str, graph = Graph): Ray.Any => { throw new NotImplementedError(); } +// rewrite_lhs = (rule_expr = str): Ray.Any => { throw new NotImplementedError(); } +// rewrite_rhs = (rule_expr = str): Ray.Any => { throw new NotImplementedError(); } +// rewrite_lhs1 = (rule_expr = str): Ray.Any => { throw new NotImplementedError(); } +// rewrite_rhs1 = (rule_expr = str): Ray.Any => { throw new NotImplementedError(); } +// validate_goal = (): Ray.Any => { throw new NotImplementedError(); } +// lhs = (): Ray.Any => { throw new NotImplementedError(); } +// rhs = (): Ray.Any => { throw new NotImplementedError(); } +// lhs_size = (): Ray.Any => { throw new NotImplementedError(); } +// rhs_size = (): Ray.Any => { throw new NotImplementedError(); } +// highlight_lhs = (vertices = Set(int), edges = Set(int)): Ray.Any => { throw new NotImplementedError(); } +// highlight_rhs = (vertices = Set(int), edges = Set(int)): Ray.Any => { throw new NotImplementedError(); } +// __reset = (): Ray.Any => { throw new NotImplementedError(); } +// next_rhs = (current = str): Ray.Any => { throw new NotImplementedError(); } +// run_check = (): Ray.Any => { throw new NotImplementedError(); } +// name = (): Ray.Any => { throw new NotImplementedError(); } +// check = (): Ray.Any => { throw new NotImplementedError(); } +// make_rhs = (): Ray.Any => { throw new NotImplementedError(); } +// } +// +// export class RuleTac extends Ray { +// name = (): Ray.Any => { throw new NotImplementedError(); } +// make_rhs = (): Ray.Any => { throw new NotImplementedError(); } +// check = (): Ray.Any => { throw new NotImplementedError(); } +// } +// +// export class SimpTac extends Ray { +// name = (): Ray.Any => { throw new NotImplementedError(); } +// __prepare_rules = (): Ray.Any => { throw new NotImplementedError(); } +// make_rhs = (): Ray.Any => { throw new NotImplementedError(); } +// check = (): Ray.Any => { throw new NotImplementedError(); } +// } +// diff --git a/environments/python/_temp/external/chyp/README.md b/environments/python/_temp/external/chyp/README.md new file mode 100644 index 0000000..15b77d3 --- /dev/null +++ b/environments/python/_temp/external/chyp/README.md @@ -0,0 +1,9 @@ +An interface from Aleks Kissinger's [Chyp (Cospans of HYPergraphs)](https://github.com/akissinger/chyp) to (OrbitMines') Ray. + +**What is this?, What are Rays?** + +See the `README.md` at [github.com/orbitmines/orbitmines.com](https://github.com/orbitmines/orbitmines.com). Or locally: [README.md](../../../../README.md) + +**Why is this interface here?** + +Note that this is mainly here for reference to the existing Chyp codebase - for anyone who understands that structure, to quickly translate that knowledge into how Rays work. - Other than that functionality, everything here should be considered as deprecated. Or considered as a legacy translation, which will be accessibly through OrbitMines. \ No newline at end of file diff --git a/environments/python/_temp/external/chyp/examples.ts b/environments/python/_temp/external/chyp/examples.ts new file mode 100644 index 0000000..581b15c --- /dev/null +++ b/environments/python/_temp/external/chyp/examples.ts @@ -0,0 +1,76 @@ +{/* Iterator */} +{/**/} +{/* */} +{/* */} +{/**/} + +{/* Grid/Dictionary/... */} +{/**/} +{/* */} +{/* */} +{/* */} +{/* */} +{/* */} +{/**/} + +{/* Loop memory */} +{/**/} +{/* */} +{/* */} +{/**/} + + + +{/* Being able to reference any part from within the structure - self-compiling */} +{/**/} +{/* */} +{/* */} +{/* /!**!/*/} +{/**/} + +{/* Continuation expanded */} +{/**/} +{/* */} + +{/* */} +{/* */} +{/* */} +{/* /!**!/*/} +{/**/} + + +{/**/} +{/* */} +{/* */} +{/* */} +{/* */} + +{/* */} +{/* */} +{/* */} +{/* */} + +{/* */} +{/* */} + +{/* */} + +{/**/} + +{/**/} +{/* */} +{/* */} +{/* */} + +{/* */} +{/* */} +{/* */} +{/**/} diff --git a/environments/python/examples/example.py b/environments/python/examples/example.py deleted file mode 100644 index e69de29..0000000 diff --git a/environments/python/orbitmines/example.py b/environments/python/orbitmines/example.py index 3a622af..0654430 100644 --- a/environments/python/orbitmines/example.py +++ b/environments/python/orbitmines/example.py @@ -1,10 +1,29 @@ from ray import Ray -# with python: -a = Ray.boolean -b = Ray.boolean +print('test') -a, b = Ray.boolean * 2 +# Target first implementation -# TODO: This is only if Ray.boolean is .orbit, otherwise reverse would be different. -b, a = -(Ray.boolean * 2) +@Ray.function +def test() -> Ray: + a = Ray.boolean + b = Ray.boolean + + a, b = Ray.boolean * 2 + + # TODO: This is only if Ray.boolean is .orbit, otherwise reverse would be different. + b, a = -(Ray.boolean * 2) + +# Compile the function to javascript +print(test.as_javascript()) +# Compile the Compiler (i.e. Ray functionality) to javascript +print(Ray.as_javascript()) # Ray.compiler = test.compiler = Ray.compiler.compiler + +# test.runtimes.javascript() +test.run(lambda ray: ray) +# Ray.runtime(lambda ray: ray).run(test) + +test.compile(lambda ray: ray) +Ray.compile(lambda ray: ray) + +# test.compile(python).run(python) diff --git a/environments/python/orbitmines/ray.py b/environments/python/orbitmines/ray.py index 216f3a1..3c040d0 100644 --- a/environments/python/orbitmines/ray.py +++ b/environments/python/orbitmines/ray.py @@ -1,10 +1,10 @@ from __future__ import annotations import inspect -from typing import Iterator, AsyncIterator, Union, Callable, Any, Iterable +from typing import Iterator, AsyncIterator, Union, Callable, Any, Iterable, AsyncIterable + # TODO: Restrictive cases: -# - TODO: readonly setup, where only traversal ops are allowed. Of course these are writing in some sense, bit those writings aren't directly accessible from this perspective # - TODO: Tensor as restrictive case # # @@ -89,6 +89,8 @@ def is_boundary(self) -> Ray: return self.is_initial() ^ self.is_terminal() # TODO: .terminal/.initial is self() vs self.not() @ray def next(self) -> Ray: raise NotImplementedError + forward \ + = next @ray def has_next(self) -> Ray: return self.next().is_some @ray @@ -103,6 +105,21 @@ def compose(a, b: Arbitrary) -> Ray: return a.terminal().equivalent(b.initial()) continues_with \ = compose + # Equivalence as "Composition of Ray." + # + # NOTE: + # - An equivalence, is only a local equivalence, no global coherence of it can be guaranteed. And it is expensive work to edge towards global coherence. + # - Though changes are only applied locally, their effects can be global (Take for instance, the example of adding to one Ray, which changes the perspective of everything connected to that Ray if they were to traverse the connection). + # + # @see https://orbitmines.com/papers/on-orbits-equivalence-and-inconsistencies#:~:text=On%20Equivalences%20%26%20Inconsistencies + @ray + def equivalent(a, b: Arbitrary) -> Ray: + # raise NotImplementedError + + # TODO: This is close but not quite, use the shifting thing I wrote down a few days ago: (And then use something to break the self-reference) - Either on this side. compose, or outside the definitions + # This one harder to do in parallel? + return a.self().compose(b.self()) + # "Composing an terminal & initial boundary" # - TODO: Note that an orbit is reversibility. ? # - TODO: Could represent this abstraction in another layer what we want to accomplish while the actual search is still taking place. @@ -122,21 +139,6 @@ def orbit(a, b: Arbitrary) -> Ray: circle = repeats = infinitely \ = orbit - # Equivalence as "Composition of Ray." - # - # NOTE: - # - An equivalence, is only a local equivalence, no global coherence of it can be guaranteed. And it is expensive work to edge towards global coherence. - # - Though changes are only applied locally, their effects can be global (Take for instance, the example of adding to one Ray, which changes the perspective of everything connected to that Ray if they were to traverse the connection). - # - # @see https://orbitmines.com/papers/on-orbits-equivalence-and-inconsistencies#:~:text=On%20Equivalences%20%26%20Inconsistencies - @ray - def equivalent(a, b: Arbitrary) -> Ray: - # raise NotImplementedError - - # TODO: This is close but not quite, use the shifting thing I wrote down a few days ago: (And then use something to break the self-reference) - Either on this side. compose, or outside the definitions - # This one harder to do in parallel? - return a.self().compose(b.self()) - # "Applying the same thing in a different context" # TODO: Somewhat related to Functors? @ray @@ -165,6 +167,8 @@ def is_equivalent(self) -> Ray: return self.is_composed.from_perspective_of(self @ray def is_composed(self) -> Ray: return self.is_orbit.from_perspective_of(self.traverse) # Needs some ref from Ray.Function.Self. + # @see "Reversibility after ignoring some difference": https://orbitmines.com/papers/on-orbits-equivalence-and-inconsistencies#:~:text=Another%20example%20of%20this%20is%20reversibility + # @see "More accurately phrased as the assumption of Reversibility: with the potential of being violated.": https://orbitmines.com/papers/on-orbits-equivalence-and-inconsistencies#:~:text=On%20Assumptions%20%26%20Assumption%20Violation @ray # TODO @alias swap ? def reverse(self) -> Ray: raise NotImplementedError @@ -196,6 +200,7 @@ def size(self, b: Arbitrary) -> Ray: # // TODO Relies heavily on the execution layer to copy initial/terminal etc... ; and an is_orbit check before calling copy again. - Then again on the execution layer it can lazily do this copy (by not evaluating (i.e.) traversing everywhere), or it first does this traversing directly. pass + # size = length = no params different behavior # resize = structure mul = __mul__ = times \ = size @@ -263,10 +268,23 @@ def self_reference(self) -> Ray: return self def as_iterator(self) -> Iterator[Ray]: return self def as_async_iterator(self) -> AsyncIterator[Ray]: return self + def as_iterable(self) -> Iterable[Ray]: return self + def as_async_iterable(self) -> AsyncIterable[Ray]: return self def as_string(self) -> str: raise NotImplementedError def as_int(self) -> int: raise NotImplementedError def as_list(self) -> list: raise NotImplementedError def as_tuple(self) -> tuple: raise NotImplementedError + @staticmethod + def as_javascript() -> str: raise NotImplementedError + + @staticmethod + @ray + def runtimes() -> Ray: raise NotImplementedError + + # TODO: Any function calls which do not return or are convertable to ray, convert as an operator. + @staticmethod + @ray + def compiler() -> Ray: raise NotImplementedError # Ray is a function (.next) # TODO: In the case of tinygrad this is similar to .realize() ? @@ -274,6 +292,8 @@ def __call__(self, *args, **kwargs) -> Ray: print(f'__call__ {args} {kwargs}') # raise NotImplementedError return self + map = render = compile = run \ + = __call__ def __get__(self, instance, owner) -> Ray: print(f'__get__ {instance} {owner}') @@ -329,6 +349,8 @@ def __ne__(a, b: Arbitrary) -> Ray: raise NotImplementedError @ray def previous(self) -> Ray: return (-self).next + backward \ + = previous @ray def has_previous(self) -> Ray: return (-self).has_next @ray @@ -360,26 +382,30 @@ def __rfloordiv__(a, b: Arbitrary) -> Ray: return Ray(b).__floordiv__(a) # @staticmethod - def from_function(func: Callable[[Any, ...], Any]) -> Ray: + def function(func: Callable[[Any, ...], Any]) -> Ray: return Ray() @staticmethod - def from_int(val: int) -> Ray: raise NotImplementedError + def integer(val: int) -> Ray: raise NotImplementedError @staticmethod - def from_iterator(val: Iterator[Any]) -> Ray: raise NotImplementedError + def iterator(val: Iterator[Any]) -> Ray: raise NotImplementedError @staticmethod - def from_iterable(val: Iterable[Any]) -> Ray: raise NotImplementedError + def iterable(val: Iterable[Any]) -> Ray: raise NotImplementedError @staticmethod - def from_boolean(val: bool) -> Ray: raise NotImplementedError + def boolean(val: bool) -> Ray: raise NotImplementedError @staticmethod @ray - def false(): return Ray.from_boolean(False) + def false(): return Ray.boolean(False) @staticmethod @ray - def true(): return Ray.from_boolean(True) + def true(): return Ray.boolean(True) @staticmethod - def from_object(val: object) -> Ray: raise NotImplementedError + def obj(val: object) -> Ray: raise NotImplementedError + @staticmethod + def arbitrary(val: Arbitrary) -> Ray: raise NotImplementedError + @staticmethod - def from_arbitrary(val: Arbitrary) -> Ray: raise NotImplementedError + # - TODO: readonly setup, where only traversal ops are allowed. Of course these are writing in some sense, but those writings aren't directly accessible from this perspective + def readonly() -> Ray: raise NotImplementedError # print(f'{type(func)}') # def method(*args, **kwargs) -> Ray: @@ -388,12 +414,16 @@ def from_arbitrary(val: Arbitrary) -> Ray: raise NotImplementedError # return await func(self, *args, **kwargs) # TODO: Binary on self is (a, a) like is_orbit(a, a) ? + # By default a = -b is -b = a + # __sub__ = -__add__ + # __add__ = -__sub__ for name, fn in inspect.getmembers(Ray, inspect.isfunction): if name.startswith('__'): continue print(f'{name}') - setattr(Ray, name, Ray.from_function(fn)) + setattr(Ray, name, Ray.function(fn)) -setattr(Ray, '__mul__', Ray.from_function(Ray.size)) +# a: Callable[[Ray], Ray] = lambda self: self.is_terminal +setattr(Ray, '__mul__', Ray.function(Ray.size)) Arbitrary = Union[int, Ray] diff --git a/external/github.com/tinygrad/tinygrad b/external/github.com/tinygrad/tinygrad index 5647148..ddec76e 160000 --- a/external/github.com/tinygrad/tinygrad +++ b/external/github.com/tinygrad/tinygrad @@ -1 +1 @@ -Subproject commit 5647148937f9b7abc886ff91bff6ce0e687d908e +Subproject commit ddec76e9c4dbe587676801cc4d3e53afc32fcb0d