Skip to content

Commit

Permalink
Merge pull request #187 from Phuire-Research/carousel
Browse files Browse the repository at this point in the history
Carousel (v0.1.4)
  • Loading branch information
REllEK-IO authored Mar 29, 2024
2 parents 223d0f6 + b9f5b05 commit 76b8083
Show file tree
Hide file tree
Showing 63 changed files with 2,166 additions and 801 deletions.
2 changes: 1 addition & 1 deletion Axium.md
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,7 @@ Please avoid using these qualities, but are providing explanations to understand
* clearDialog - Clears the currently stored Stratimux dialog, may be used within a strategy.
* clearBadActionTypeFromBadActionList - This is to allow for plans to take into account for expired actions and clear such.
* clearBadStrategyTopicFromBadActionList - Allows plans to accounts for specific ActionStrategy topics that might find themselves in badActions and clear such.
* clearBadPlanFromBadPlanList - This additionally allows for concepts to take into account potentially failed plans that are set by axium.stage(). Via their topic as payload and clears such.
* clearBadPlanFromBadPlanList - This additionally allows for concepts to take into account potentially failed plans that are set by axium.plan(). Via their topic as payload and clears such.
* kick - This is a pure action that will just trigger the next function via the UnifiedSubject to prime subscribers or stages. Noting that the downside of Stratimux's halting quality, is you have to kick it into gear if it hasn't received an action recently for your staged Plans to operate as intended.

## Axium Strategies Concept Set Transformation
Expand Down
2 changes: 1 addition & 1 deletion Index.md
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ const axium = createAxium('ownershipTest', [
createCounterConcept(),
createExperimentConcept(createExperimentState(), [checkInStrategyQuality], [experimentActionQuePrinciple])
], true, true);
const plan = axium.stage(
const plan = axium.plan(
'Testing Ownership Staging', [
(cpts, dispatch) => {
const axiumState = cpts[0].state as AxiumState;
Expand Down
62 changes: 26 additions & 36 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,9 @@
* Action Oriented
* Single Lock Graph Framework
* Composable Strategies
* Stage Planner
* Stage Planner (Hierarchal Planning/Higher Order Reasoning)
* Prioritization
* Change Detection
* Plain Text Dialog Output
* Hot Loading
* No Dependency Injection
Expand Down Expand Up @@ -42,6 +44,16 @@ When in doubt simplify.
* [Unified Turing Machine](https://github.com/Phuire-Research/Stratimux/blob/main/The-Unified-Turing-Machine.md) - The governing concept for this entire framework.

## Change Log ![Tests](https://github.com/Phuire-Research/Stratimux/actions/workflows/node.js.yml/badge.svg)
### **BREAKING Update v0.1.4** *Update 3/28/24*
* Removed the "on.expected" option from dispatch to reduce inner complexity of your stages
* Renamed **axium.stage** to **axium.plan** to be in line with industry terminology
* The new plan set up requires a staging entity or the return from the new createStage helper function
* This new entity enables you to change the priority in which your stages are informed per state change
* You may now assign each stage its own separate beat versus the entire plan
* Removes beat from the overall plan and now needs to be performed atomically
* This overall change trims the total plans that are checked per state, but may still supply plans that trigger on all changes via empty array in entity or outright ignoring the value field via the createStage function
* Added nullReducer to disallow excessive observations from being triggered
* First pass updating StagePlanner documentation
### 3/05/24
* Minor DX release, properly exporting Axium type for inclusion in other frameworks.
### 12/14/23
Expand All @@ -60,23 +72,7 @@ When in doubt simplify.
* Action Payloads must extend type: Record<string, unknown>
* This change is to provide a guarantee of advanced functionality in the current UI Proof of Concept.

## Working Branch: Carousel
While I did not want to release a major API change after an initial release. Working on logixUX revealed that there is a need to reduce the inner time complexity within the Axium itself. Carousel, is a rework on how selectors function, as well as a larger refactor on how stages work.

Effectively the UnifiedSubject will now memoize all potential state changes. And your stages will be able to pass selectors in creation to as a changed record. I'm taking my time with this update to ensure that everything is well tested within logixUX. Prior to breaking APIs.

With this change, I will be able to call the underlying pattern a solution to what bothered me about ECS(Entity Component Systems). As I designed Stratimux and the Unified Turing Machine to act as the foundation for simulation in conjunction with AI. It just so happens that it is currently simulating data transformations and through logixUX a complete UI system.

What I am currently addressing is something that has always bothered me in other frameworks and game engines. That once you have your system set up everything is always checked and has an inherit inefficiency built in due to such. With the above planned change, in O(n) I can determine what has changed and prune stages to what is needed. Likewise the same selectors keep the baseline complexity of accessing those changes to the same O(n). As selectors once defined, are a simple retrieval functions with no looping quality.

* *Update 3/05/24*
Releasing a minor update so that Stratimux can be introduced into other frameworks, as the reality of the current release. It that it is ready in a limited scale such as a simple state machine. The main issue with the current approach is a back pressure becomes an issue at higher complexity and starts interfering with branch prediction.
* *Update 12/28/23*
Now that this branch is able to break implementations, the planned scope of this refactor has expanded to move allow this framework to be multithreaded out of the box. This new plan will take some time to implement and will be merged into main when ready.
* *Update 2/14/24*
On the working branch I have included a new nullReducer. This will allow for stratimux to be able to execute as state or stateless beyond initial set up.

----
```bash
npm i stratimux
```
Expand Down Expand Up @@ -197,7 +193,7 @@ export const uXqOfUXQuality = createQuality(
/* Below are the default functions available for your quality */
// export const qOfUXQuality = createQuality(
// qOfUXType,
// defaultReducer,
// defaultReducer(Informs)/nullReducer(Doesn't Inform),
// The method is optional and is an advanced behavior
// defaultMethodCreator
// );
Expand All @@ -222,7 +218,9 @@ import {
getAxiumState,
primeAction,
selectUnifiedState,
strategyBegin
strategyBegin,
createStage,
stageWaitForOpenThenIterate
} from 'stratimux';
import { UXState, uXName } from './uX.concept';
import { uXSomeStrategy, uXSomeStrategyTopic } from './strategies/uXSome.strategy';
Expand All @@ -233,33 +231,25 @@ export const uXPrinciple: PrincipleFunction = (
concepts$: UnifiedSubject,
semaphore: number
) => {
const plan = concepts$.stage('uX Plan', [
(concepts, dispatch) => {
// This will register this plan to the axium, this allows for the axium to close or remove your concept cleanly.
dispatch(primeAction(concepts, axiumRegisterStagePlanner({conceptName: uXName, stagePlanner: plan})), {
on: {
selector: axiumSelectOpen,
expected: true,
},
iterateStage: true
});
},
(concepts, dispatch) => {
// There always needs to be atleast one subscriber or plan for the Axium to be active.
const plan = concepts$.plan('uX Plan', [
// This will register this plan to the axium, this allows for the axium to close or remove your concept cleanly.
stageWaitForOpenThenIterate(() => (axiumRegisterStagePlanner({conceptName: uXName, stagePlanner: plan}))),
createStage((concepts, dispatch) => {
const state = selectUnifiedState<UXState>(concepts, semaphore);
if (state) {
dispatch(strategyBegin(uXSomeStrategy()), {
iterateStage: true
});
}
},
(concepts) => {
}, {beat: 30}),
createStage((concepts) => {
const {lastStrategy} = getAxiumState(concepts);
if (lastStrategy === uXSomeStrategyTopic) {
plan.conclude();
}
}
// There always needs to be atleast one subscriber or plan for the Axium to be active.
], 30);
}, {beat: 30})
]);
};
```

Expand Down
112 changes: 68 additions & 44 deletions StagePlanner.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,34 +28,58 @@ export type dispatchOptions = {
throttle?: number;
setStep?: number;
iterateStep?: boolean;
on?: {
selector: KeyedSelector,
expected: any
},
}

```
* dispatchOptions
* **dispatchOptions**
* runOnce - If enabled on the dispatch options, this will permit only one dispatch of that action within its stage.
* throttle - Required to prevent the stage to be considered bad if rerunning the same action within the same stage, specific use case is tracking some position over time. If on is part of options, this will only come into play after that action is first dispatched.
* incrementStage - Will increment to the next stage index, this should be your default option for dispatching actions or strategies to prevent action overflow.
* setStage - This will set the stage to a specific stage index, useful if some strategy failed and the staging needs to be reset to prepare for that strategy again. This will always override iterateStage.
* on - Simple handler that will prevent dispatch until the selected value is set to what is expected. Keep in mind this should also be occupied by a throttle, as this dispatch will run on each successful state update. This should be utilized alongside iterateStage, setStage, or throttle to prevent action overflow.

### Stage Planner Internals
```typescript
export type Dispatcher = (action: Action, options: dispatchOptions) => void;
export type Staging = (

export type Stage = (
concepts: Concepts,
dispatch: (action: Action, options: dispatchOptions) => void
dispatch: (action: Action, options: dispatchOptions, ) => void,
changes?: KeyedSelector[]
) => void;

export type Staging = {
stage: Stage;
selectors: KeyedSelector[];
priority?: number
beat?: number,
};
export type PartialStaging = {
stage: Stage;
selectors?: KeyedSelector[];
priority?: number
beat?: number,
};

export const createStage = (stage: Stage, selector?: KeyedSelector[], priority?: number, beat?: number): Staging => {
return {
stage,
selectors: selector ? selector : [],
priority,
beat
};
};

export class UnifiedSubject extends Subject<Concepts> {
stage(title: string, stages: Staging[]) {}
plan(title: string, stages: PartialStaging[], beat?: number): StagePlanner {}
}
```
* Dispatcher - This is the supplied dispatch function that is made available each stage.
* Staging - The interface that you will be interacting with when setting up your stages, noting placement of concepts and the dispatch function.
* UnifiedSubject - This is a specialized subject for utilized within Stratimux to allow for this stage planner paradigm. This is made available via the createAxium function and likewise within your principles via the concept$ property. Note that your plan will be an array of functions even with just one stage.
* Staging - A functional interface that informs input and output of each stage in your plan.
* **concepts** - The most recent concepts
* **dispatch** - Use to dispatch new actions and strategies into your axium
* **changes** - Informs which properties of the supplied KeyedSelectors for the current stage have changed.
* createStage - Helper function that guides assembly of a Staging entity
* UnifiedSubject - This is a specialized subject for utilized within Stratimux to allow for this stage planner paradigm. This is made available via the createAxium function and likewise within your principles via the concept$ property. Note that your plan will be an array of Staging entities created manually or via the createStage function.

## Example
```typescript
Expand All @@ -65,47 +89,47 @@ const sub = axium.subscribe((concepts) => {
const axiumState = concepts[0].state as AxiumState;
if (axiumState.badPlans.length > 0) {
const badPlan = axiumState.badPlans[0];
const counter = selectState<Counter>(concepts, counterName);
console.log('Stage Ran Away, badPlans.length: ', axiumState.badPlans.length, 'Count: ', counter.count);
const counter = selectState<CounterState>(concepts, counterName);
console.log('Stage Ran Away, badPlans.length: ', axiumState.badPlans.length, 'Count: ', counter?.count);
plan.conclude();
sub.unsubscribe();
expect(badPlan.stageFailed).toBe(2);
expect(counter.count).toBe(2);
expect(counter?.count).toBe(2);
setTimeout(() => {done();}, 500);
}
});
const plan = axium.stage('Stage DispatchOptions Test',
const plan = axium.plan('Stage DispatchOptions Test',
[
(concepts, dispatch) => {
const counter = selectState<Counter>(concepts, counterName);
createStage((concepts, dispatch) => {
const counter = selectState<CounterState>(concepts, counterName);
console.log('Stage 1 ', counter, runCount);
dispatch(counterAdd(), {
iterateStage: true
});
}, (concepts, dispatch) => {
}),
createStage((concepts, dispatch) => {
runCount++;
const counter = selectState<Counter>(concepts, counterName);
const counter = selectState<CounterState>(concepts, counterName);
console.log('Stage 2 ', counter, runCount);
// Sets count to 2 and only runs once per state update
dispatch(counterAdd(), {
runOnce: true
});
// Will wait until count is set to 2, then set the Stage Explicitly to the third Step counting from 0.
dispatch(counterAdd(), {
setStage: 2,
on: {
selector: counterSelectCount,
expected: 2
},
// Requires throttle, because the previous action is of the same type, but runs only once.
throttle: 1
});
if (selectSlice(concepts, counterSelectCount) === 2) {
dispatch(counterAdd(), {
setStage: 2,
// Requires throttle, because the previous action is of the same type, but runs only once.
throttle: 1
});
}
// }
}, (concepts, dispatch) => {
}),
createStage((concepts, dispatch) => {
runCount++;
const counter = selectState<Counter>(concepts, counterName);
const counter = selectState<CounterState>(concepts, counterName);
console.log('Should run twice, Stage 3 ', counter, runCount);
// Will cause an action overflow forcing the current stage to conclude and add the plan to badPlans
// Will cause an action overflow forcing the stage to close and add itself to badPlans
dispatch(counterSubtract(), {
// Enabling will cause this test to timeout via the subscription watching for badPlans to never be ran.
// throttle: 500
Expand All @@ -117,7 +141,7 @@ const plan = axium.stage('Stage DispatchOptions Test',
'Will also run twice. 1st will be before "Stage Ran Away,"',
'and after "Should run twice." The 2nd will be final console log output.'
);
}
})
]);
```
To prevent action overflow, each stage is paying attention to consecutive actions of the same type. Noting that this can likewise be overwhelmed via a throttle set to 0. If throttle 0 is utilized, the current stage should change.
Expand All @@ -131,18 +155,18 @@ Lastly, in an action overflow state, sequentially the overflow will call the sam
## Stage Planner within your Principle
Stratimux is designed to be ran primarily through its loaded concepts and their associated principles. To prevent unexpected behaviors in your own principles. Please utilize the supplied KeyedSelector for axium's open property to begin the stage of your concepts.
```typescript
const plan = concept$.stage('Principle Stage Example', [
(___, dispatch) => {
dispatch(someAction(), {
iterateStage: true,
on: {
selector: axiumSelectOpen,
expected: true
},
});
},
(concepts, dispatch) => {
const plan = concept$.plan('Principle Stage Example', [
createStage((concepts, dispatch) => {
// If axium is currently open
if (select.slice(concepts, axiumSelectOpen)) {
dispatch(someAction(), {
iterateStage: true,
});
}
// Plan will only run if the open property on the main axium concept has changed.
}, [axiumSelectOpen]),
createStage((concepts, dispatch) => {
// Your principle's run time logic.
}
})
]);
```
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "stratimux",
"license": "GPL-3.0",
"version": "0.1.2",
"version": "0.1.4",
"description": "Unified Turing Machine",
"main": "dist/index.js",
"module": "dist/index.mjs",
Expand Down
13 changes: 7 additions & 6 deletions src/concepts/axium/axium.close.principle.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,11 @@ import { Subscriber } from 'rxjs';
import { Concepts } from '../../model/concept';
import { PrincipleFunction } from '../../model/principle';
import { selectUnifiedState } from '../../model/selector';
import { UnifiedSubject } from '../../model/stagePlanner';
import { UnifiedSubject, createStage } from '../../model/stagePlanner';
import { AxiumState } from './axium.concept';
import { axiumClose } from './qualities/close.quality';
import { Action } from '../../model/action';
import { axiumSelectPrepareClose } from './axium.selector';
/*<#*/
export const axiumClosePrinciple: PrincipleFunction = (
_: Subscriber<Action>,
Expand All @@ -17,8 +18,8 @@ export const axiumClosePrinciple: PrincipleFunction = (
semaphore: number
) => {
let init = false;
const plan = concepts$.stage('Plan Axium Close', [
(concepts, dispatch) => {
const plan = concepts$.plan('Plan Axium Close', [
createStage((concepts, dispatch) => {
const state = selectUnifiedState<AxiumState>(concepts, semaphore);
if (!init && state?.prepareClose) {
init = true;
Expand All @@ -28,10 +29,10 @@ export const axiumClosePrinciple: PrincipleFunction = (
});
plan.conclude();
}
},
() => {
}, { selectors: [axiumSelectPrepareClose], priority: Infinity}),
createStage(() => {
//
}
})
]);
};
/*#>*/
Loading

0 comments on commit 76b8083

Please sign in to comment.