From fc7fae3236e0570c79f680a3fadb9a804fd78a04 Mon Sep 17 00:00:00 2001 From: REllEK-IO Date: Tue, 28 Nov 2023 06:16:42 -0700 Subject: [PATCH 1/2] Experimental: Plan Beats --- README.md | 5 +++ package.json | 2 +- src/model/stagePlanner.ts | 41 ++++++++++++++++---- src/test/stagePlannerBeat.test.ts | 64 +++++++++++++++++++++++++++++++ 4 files changed, 103 insertions(+), 9 deletions(-) create mode 100644 src/test/stagePlannerBeat.test.ts diff --git a/README.md b/README.md index df13fb2..3bded6a 100644 --- a/README.md +++ b/README.md @@ -20,6 +20,11 @@ User Interface Proof of Concept and Demo: [logixUX](https://github.com/Phuire-Re *Note if you notice a strange any capitalization, this is a new format that is being formalized as conceptual logic. Where we capitalize not just people, places, and things, but concepts as well. In addition, there was no generative intelligence used in the creation of this framework or documentation. This is **100% hand written.*** ## Change Log ![Tests](https://github.com/Phuire-Research/Stratimux/actions/workflows/node.js.yml/badge.svg) +### 11/27/23 +* Added a new experimental parameter to staging. Beat, which is a duration that will "Throttle and debounce," state notifications to that specific plan. +*Note: This becomes necessary to set once your application grows in complexity. As the fundamental issue with graph programming, is that at a sufficient scale. You are fighting your computers branch prediction. You can think of the run time of Stratimux like a balloon. In theory as long as your logic is clean, you shouldn't run into race conditions. But the unfortunate truth is that the more logic that your balloon if filled with. The higher likelihood your balloon will pop. So to avoid this, you need to supply a beat to complex plans that might overinflate your balloon.* + +*This is counter intuitive to most programming, but is the trade off for this new dynamic paradigm. As computers have been designed for transactional object oriented programming. This paradigm moves beyond that comfortable scale of complexity and reveals the generally good enough design of the computers we rely upon. Funny to say that you can write by hand a program that hallucinates. **hint** **hint*** ### 11/26/23 * Updated naming conventions throughout. Counter is now CounterState. Strategies now export with their associated concept's prepended. * Added parsing tokens. If curious about this functionality see the logixUX project. These tokens in combination with that project will upon its release. Allow for the ease of parsing Stratimux or other TypeScript projects into high quality training data. diff --git a/package.json b/package.json index 0805036..a40855c 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "stratimux", "license": "GPL-3.0", - "version": "0.0.75", + "version": "0.0.76", "description": "Unified Turing Machine", "main": "dist/index.js", "module": "dist/index.mjs", diff --git a/src/model/stagePlanner.ts b/src/model/stagePlanner.ts index df6de7e..3cc83a7 100644 --- a/src/model/stagePlanner.ts +++ b/src/model/stagePlanner.ts @@ -20,6 +20,9 @@ export type Plan = { stages: Staging[], stage: number; stageFailed: number; + beat: number; + offBeat: number; + timer: NodeJS.Timeout[] } export type NamedStagePlanner = { @@ -200,13 +203,14 @@ export class UnifiedSubject extends Subject { private planId = 0; private currentStages: Map = new Map(); private stageDelimiters: Map = new Map(); + private concepts: Concepts = {}; constructor() { super(); } - stage(title: string, stages: Staging[]): StagePlanner { + stage(title: string, stages: Staging[], beat?: number): StagePlanner { const planId = this.planId; this.planId++; - this.currentStages.set(planId, {title, stages, stage: 0, stageFailed: -1}); + this.currentStages.set(planId, {title, stages, stage: 0, stageFailed: -1, beat: beat ? beat : -1, offBeat: -1, timer: []}); const conclude = () => { this.currentStages.delete(planId); }; @@ -287,7 +291,7 @@ export class UnifiedSubject extends Subject { } next(value: Concepts) { - const concepts = { + this.concepts = { ...value }; if (!this.closed) { @@ -295,12 +299,33 @@ export class UnifiedSubject extends Subject { // Where Dispatcher would be (action$: Subject) => {}(); const axiumState = value[0].state as AxiumState; this.currentStages.forEach((plan, key) => { - const dispatcher: Dispatcher = (action: Action, options: dispatchOptions) => { - this._dispatch(axiumState, key, plan, concepts, action, options); - }; const index = plan.stage; if (index < plan.stages.length) { - plan.stages[index](concepts, dispatcher); + const timer = plan.timer; + const now = Date.now(); + if (plan.beat > -1) { + if (plan.offBeat < now) { + plan.offBeat = Date.now() + plan.beat; + const dispatcher: Dispatcher = (action: Action, options: dispatchOptions) => { + this._dispatch(axiumState, key, plan, this.concepts, action, options); + }; + plan.stages[index](this.concepts, dispatcher); + } else if (timer.length === 0 && plan.offBeat > now) { + timer.push(setTimeout(() => { + plan.timer = []; + plan.offBeat = Date.now() + plan.beat; + const dispatcher: Dispatcher = (() => (action: Action, options: dispatchOptions) => { + this._dispatch(axiumState, key, plan, this.concepts, action, options); + }).bind(this)(); + plan.stages[index](this.concepts, dispatcher); + }, plan.offBeat - Date.now())); + } + } else { + const dispatcher: Dispatcher = (action: Action, options: dispatchOptions) => { + this._dispatch(axiumState, key, plan, this.concepts, action, options); + }; + plan.stages[index](this.concepts, dispatcher); + } } }); const {observers} = this; @@ -308,7 +333,7 @@ export class UnifiedSubject extends Subject { const len = observers.length; for (let i = 0; i < len; i++) { if (observers[i]) { - observers[i].next(concepts); + observers[i].next(this.concepts); } } } diff --git a/src/test/stagePlannerBeat.test.ts b/src/test/stagePlannerBeat.test.ts new file mode 100644 index 0000000..7c2c6f1 --- /dev/null +++ b/src/test/stagePlannerBeat.test.ts @@ -0,0 +1,64 @@ + +/*<$ +For the graph programming framework Stratimux, generate a test to ensure that you can create a stage planner that sets the beat parameter +of the plan. The beat will ensure that within a span of time the first notification of state change will be observed. +But any new additional changes to state will be debounced for the beat duration. +But if the beat has not been notified for a period. The first notification will go through and start this process over again. +This is a combination of throttle and debounce. +$>*/ +/*<#*/ +import { createAxium } from '../model/axium'; +import { selectState } from '../model/selector'; +import { axiumSelectOpen } from '../concepts/axium/axium.selector'; +import { axiumPreClose } from '../concepts/axium/qualities/preClose.quality'; +import { axiumKick } from '../concepts/axium/qualities/kick.quality'; +import { CounterState, counterName, createCounterConcept } from '../concepts/counter/counter.concept'; +import { counterAdd } from '../concepts/counter/qualities/add.quality'; + +test('Stage Planner Beat Test', (done) => { + let timerActive = false; + const axium = createAxium('axium test stage planner beat', [ + createCounterConcept() + ], true, true); + const plan = axium.stage('Stage Planner Beat Test', [ + (___, dispatch) => { + timerActive = true; + setTimeout(() => { + timerActive = false; + }, 1000); + dispatch(axiumKick(), { + iterateStage: true, + on: { + selector: axiumSelectOpen, + expected: true + }, + }); + }, + (___, dispatch) => { + dispatch(counterAdd(), { + iterateStage: true + }); + }, + (concepts, dispatch) => { + if (!timerActive) { + const state = selectState(concepts, counterName); + if (state) { + expect(state.count).toBe(10); + setTimeout(() => done(), 1000); + dispatch(axiumPreClose({exit: false}), { + iterateStage: true + }); + plan.conclude(); + } + } else { + dispatch(counterAdd(), { + throttle: 1 + }); + } + }, + () => { + // + } + ], 90); +}); +/*#>*/ \ No newline at end of file From ca4f9272bcdad8fda72bbdd2415910376d869c3c Mon Sep 17 00:00:00 2001 From: REllEK-IO Date: Tue, 28 Nov 2023 06:21:16 -0700 Subject: [PATCH 2/2] Finding CI server timer --- src/test/stagePlannerBeat.test.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/test/stagePlannerBeat.test.ts b/src/test/stagePlannerBeat.test.ts index 7c2c6f1..35c5b3a 100644 --- a/src/test/stagePlannerBeat.test.ts +++ b/src/test/stagePlannerBeat.test.ts @@ -59,6 +59,6 @@ test('Stage Planner Beat Test', (done) => { () => { // } - ], 90); + ], 93); }); /*#>*/ \ No newline at end of file