@justinelliottcobb/amari-wasm 0.15.1 → 0.17.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,4 +1,4 @@
1
- # @justinelliottcobb/amari-wasm v0.15.1
1
+ # @justinelliottcobb/amari-wasm v0.17.0
2
2
 
3
3
  **Unified Mathematical Computing Library with High-Precision WebAssembly Support**
4
4
 
@@ -19,6 +19,8 @@ Amari is a comprehensive mathematical computing library that brings advanced alg
19
19
  - **Holographic Memory** *(v0.12.3)*: Vector Symbolic Architecture for associative memory with binding and bundling operations
20
20
  - **Functional Analysis** *(v0.15.0)*: Hilbert spaces, linear operators, spectral decomposition, and Sobolev spaces
21
21
  - **Optical Field Operations** *(v0.15.1)*: GA-native Lee hologram encoding for DMD displays and VSA-based optical processing
22
+ - **Computational Topology** *(v0.16.0)*: Simplicial complexes, homology computation, persistent homology, and Morse theory
23
+ - **Dynamical Systems** *(v0.17.0)*: ODE solvers, stability analysis, bifurcation diagrams, Lyapunov exponents, and phase portraits
22
24
  - **Probability Theory** *(v0.13.0)*: Distributions on multivector spaces, MCMC sampling, and Monte Carlo estimation
23
25
  - **Relativistic Physics**: Spacetime algebra (Cl(1,3)) with WebAssembly-compatible precision
24
26
  - **Spacecraft Orbital Mechanics**: Full-precision trajectory calculations in browsers
@@ -570,6 +572,408 @@ async function opticalDemo() {
570
572
  opticalDemo();
571
573
  ```
572
574
 
575
+ ### Computational Topology *(v0.16.0)*
576
+
577
+ Compute homology groups, persistent homology, and analyze topological features of data:
578
+
579
+ ```typescript
580
+ import init, {
581
+ WasmSimplex,
582
+ WasmSimplicialComplex,
583
+ WasmFiltration,
584
+ WasmPersistentHomology,
585
+ ripsFromDistances,
586
+ findCriticalPoints2D,
587
+ WasmMorseComplex
588
+ } from '@justinelliottcobb/amari-wasm';
589
+
590
+ async function topologyDemo() {
591
+ await init();
592
+
593
+ // ========================================
594
+ // Simplicial Complexes and Homology
595
+ // ========================================
596
+
597
+ // Create a triangle (2-simplex with all faces)
598
+ const complex = new WasmSimplicialComplex();
599
+ complex.addSimplex([0, 1, 2]); // Triangle
600
+
601
+ // Closure property: edges and vertices automatically added
602
+ console.log(`Vertices: ${complex.vertexCount()}`); // 3
603
+ console.log(`Edges: ${complex.edgeCount()}`); // 3
604
+ console.log(`Triangles: ${complex.simplexCount(2)}`); // 1
605
+
606
+ // Compute Betti numbers (topological invariants)
607
+ const betti = complex.bettiNumbers();
608
+ console.log(`β₀ = ${betti[0]}`); // 1 (one connected component)
609
+ console.log(`β₁ = ${betti[1]}`); // 0 (no holes - filled triangle)
610
+
611
+ // Euler characteristic: χ = V - E + F
612
+ console.log(`χ = ${complex.eulerCharacteristic()}`); // 1
613
+
614
+ // ========================================
615
+ // Circle (unfilled triangle boundary)
616
+ // ========================================
617
+
618
+ const circle = new WasmSimplicialComplex();
619
+ circle.addSimplex([0, 1]); // Edge 0-1
620
+ circle.addSimplex([1, 2]); // Edge 1-2
621
+ circle.addSimplex([2, 0]); // Edge 2-0
622
+
623
+ const circleBetti = circle.bettiNumbers();
624
+ console.log(`Circle β₀ = ${circleBetti[0]}`); // 1 (connected)
625
+ console.log(`Circle β₁ = ${circleBetti[1]}`); // 1 (one hole!)
626
+
627
+ // ========================================
628
+ // Persistent Homology
629
+ // ========================================
630
+
631
+ // Build a filtration (simplices appearing over time)
632
+ const filt = new WasmFiltration();
633
+ filt.add(0.0, [0]); // Point 0 at t=0
634
+ filt.add(0.0, [1]); // Point 1 at t=0
635
+ filt.add(0.0, [2]); // Point 2 at t=0
636
+ filt.add(1.0, [0, 1]); // Edge 0-1 at t=1
637
+ filt.add(2.0, [1, 2]); // Edge 1-2 at t=2
638
+ filt.add(3.0, [0, 2]); // Edge 0-2 at t=3 (creates loop)
639
+
640
+ // Compute persistent homology
641
+ const ph = WasmPersistentHomology.compute(filt);
642
+
643
+ // Get persistence diagram as [dim, birth, death] triples
644
+ const diagram = ph.getDiagram();
645
+ for (let i = 0; i < diagram.length; i += 3) {
646
+ const dim = diagram[i];
647
+ const birth = diagram[i + 1];
648
+ const death = diagram[i + 2];
649
+ console.log(`H${dim}: born at ${birth}, dies at ${death === Infinity ? '∞' : death}`);
650
+ }
651
+
652
+ // Betti numbers at different times
653
+ console.log(`Betti at t=0.5: ${ph.bettiAt(0.5)}`); // [3, 0] - 3 components
654
+ console.log(`Betti at t=3.5: ${ph.bettiAt(3.5)}`); // [1, 1] - 1 component, 1 loop
655
+
656
+ // ========================================
657
+ // Vietoris-Rips Filtration from Point Cloud
658
+ // ========================================
659
+
660
+ // 3 points forming an equilateral triangle
661
+ // Distances: d(0,1)=1, d(1,2)=1, d(0,2)=1
662
+ const numPoints = 3;
663
+ const maxDim = 2;
664
+ // Upper triangular pairwise distances: [d(0,1), d(0,2), d(1,2)]
665
+ const distances = [1.0, 1.0, 1.0];
666
+
667
+ const ripsFilt = ripsFromDistances(numPoints, maxDim, distances);
668
+ const ripsPH = WasmPersistentHomology.compute(ripsFilt);
669
+ console.log(`Rips intervals in H0: ${ripsPH.intervalCount(0)}`);
670
+ console.log(`Rips intervals in H1: ${ripsPH.intervalCount(1)}`);
671
+
672
+ // ========================================
673
+ // Morse Theory (Critical Points)
674
+ // ========================================
675
+
676
+ // Precompute f(x,y) = x² + y² on a grid
677
+ const resolution = 20;
678
+ const values = [];
679
+ for (let i = 0; i <= resolution; i++) {
680
+ const x = -1.0 + (2.0 * i) / resolution;
681
+ for (let j = 0; j <= resolution; j++) {
682
+ const y = -1.0 + (2.0 * j) / resolution;
683
+ values.push(x * x + y * y);
684
+ }
685
+ }
686
+
687
+ const criticalPoints = findCriticalPoints2D(
688
+ resolution,
689
+ -1.0, 1.0, // x range
690
+ -1.0, 1.0, // y range
691
+ 0.1, // tolerance
692
+ values
693
+ );
694
+
695
+ console.log(`Found ${criticalPoints.length} critical points`);
696
+ for (const cp of criticalPoints) {
697
+ console.log(` ${cp.criticalType} at (${cp.position[0].toFixed(2)}, ${cp.position[1].toFixed(2)}), value=${cp.value.toFixed(2)}`);
698
+ }
699
+
700
+ // Morse complex analysis
701
+ const morse = new WasmMorseComplex(criticalPoints);
702
+ const counts = morse.countsByIndex();
703
+ console.log(`Critical points by index: ${counts}`);
704
+
705
+ // Verify Morse inequalities: c_k >= β_k
706
+ const complexBetti = complex.bettiNumbers();
707
+ console.log(`Weak Morse inequalities hold: ${morse.checkWeakMorseInequalities(complexBetti)}`);
708
+
709
+ // Clean up WASM memory
710
+ complex.free();
711
+ circle.free();
712
+ filt.free();
713
+ ph.free();
714
+ ripsFilt.free();
715
+ ripsPH.free();
716
+ morse.free();
717
+ criticalPoints.forEach(cp => cp.free());
718
+ }
719
+
720
+ topologyDemo();
721
+ ```
722
+
723
+ ### Dynamical Systems *(v0.17.0)*
724
+
725
+ Analyze chaotic systems, compute bifurcation diagrams, and explore phase space:
726
+
727
+ ```typescript
728
+ import init, {
729
+ WasmLorenzSystem,
730
+ WasmVanDerPolOscillator,
731
+ WasmDuffingOscillator,
732
+ WasmRungeKutta4,
733
+ WasmLyapunovSpectrum,
734
+ WasmBifurcationDiagram,
735
+ WasmPhasePortrait,
736
+ WasmStabilityAnalysis,
737
+ computeLyapunovExponents,
738
+ findFixedPoints
739
+ } from '@justinelliottcobb/amari-wasm';
740
+
741
+ async function dynamicsDemo() {
742
+ await init();
743
+
744
+ // ========================================
745
+ // Lorenz Attractor
746
+ // ========================================
747
+
748
+ // Create classic Lorenz system (sigma=10, rho=28, beta=8/3)
749
+ const lorenz = WasmLorenzSystem.classic();
750
+ console.log(`Lorenz parameters: σ=${lorenz.sigma}, ρ=${lorenz.rho}, β=${lorenz.beta}`);
751
+
752
+ // Create RK4 solver
753
+ const solver = new WasmRungeKutta4();
754
+
755
+ // Integrate trajectory from initial condition
756
+ const initial = [1.0, 1.0, 1.0];
757
+ const trajectory = solver.solve(lorenz, initial, 0.0, 50.0, 5000);
758
+
759
+ console.log(`Trajectory has ${trajectory.length} points`);
760
+
761
+ // Get final state
762
+ const finalState = trajectory[trajectory.length - 1];
763
+ console.log(`Final state: (${finalState[0].toFixed(3)}, ${finalState[1].toFixed(3)}, ${finalState[2].toFixed(3)})`);
764
+
765
+ // ========================================
766
+ // Van der Pol Limit Cycle
767
+ // ========================================
768
+
769
+ // Create Van der Pol oscillator with mu = 1.0
770
+ const vdp = WasmVanDerPolOscillator.new(1.0);
771
+
772
+ // Small initial displacement
773
+ const vdpTrajectory = solver.solve(vdp, [0.1, 0.0], 0.0, 50.0, 5000);
774
+
775
+ // Check limit cycle amplitude
776
+ const vdpFinal = vdpTrajectory[vdpTrajectory.length - 1];
777
+ console.log(`Van der Pol final amplitude: ${Math.abs(vdpFinal[0]).toFixed(3)}`);
778
+
779
+ // ========================================
780
+ // Lyapunov Exponents
781
+ // ========================================
782
+
783
+ // Compute Lyapunov spectrum for Lorenz system
784
+ const lyapunov = computeLyapunovExponents(lorenz, initial, 10000, 0.01);
785
+
786
+ console.log(`Lyapunov exponents: [${lyapunov.exponents.map(e => e.toFixed(4)).join(', ')}]`);
787
+ console.log(`Sum: ${lyapunov.sum().toFixed(4)} (negative = dissipative)`);
788
+
789
+ if (lyapunov.exponents[0] > 0) {
790
+ console.log('System is chaotic!');
791
+ }
792
+
793
+ // Kaplan-Yorke dimension
794
+ console.log(`Kaplan-Yorke dimension: ${lyapunov.kaplanYorkeDimension().toFixed(3)}`);
795
+
796
+ // ========================================
797
+ // Bifurcation Diagram
798
+ // ========================================
799
+
800
+ // Compute bifurcation diagram for logistic map
801
+ const bifurcation = WasmBifurcationDiagram.compute(
802
+ 'logistic',
803
+ 2.5, // r_min
804
+ 4.0, // r_max
805
+ 1000, // num_parameters
806
+ 500, // transient iterations
807
+ 100 // sample points per parameter
808
+ );
809
+
810
+ console.log(`Bifurcation diagram: ${bifurcation.parameterCount()} parameter values`);
811
+
812
+ // Get attractor points at specific parameter
813
+ const attractorAt3_5 = bifurcation.attractorPoints(3.5);
814
+ console.log(`Attractor at r=3.5: ${attractorAt3_5.length} points`);
815
+
816
+ // ========================================
817
+ // Stability Analysis
818
+ // ========================================
819
+
820
+ // Find fixed points of Van der Pol oscillator
821
+ const fixedPoints = findFixedPoints(vdp, [[0.0, 0.0]], 1e-10);
822
+
823
+ for (const fp of fixedPoints) {
824
+ console.log(`Fixed point: (${fp.point[0].toFixed(6)}, ${fp.point[1].toFixed(6)})`);
825
+
826
+ // Analyze stability
827
+ const stability = WasmStabilityAnalysis.analyze(vdp, fp.point);
828
+ console.log(` Stability: ${stability.stabilityType}`);
829
+ console.log(` Eigenvalues: ${stability.eigenvalues.map(e => `${e.real.toFixed(4)}+${e.imag.toFixed(4)}i`).join(', ')}`);
830
+ }
831
+
832
+ // ========================================
833
+ // Phase Portrait
834
+ // ========================================
835
+
836
+ // Generate phase portrait for Duffing oscillator
837
+ const duffing = WasmDuffingOscillator.new(1.0, -1.0, 0.2, 0.3, 1.2);
838
+ const portrait = WasmPhasePortrait.generate(
839
+ duffing,
840
+ [-2.0, 2.0], // x range
841
+ [-2.0, 2.0], // y range
842
+ 20, // grid resolution
843
+ 5.0, // integration time
844
+ 0.01 // dt
845
+ );
846
+
847
+ console.log(`Phase portrait: ${portrait.trajectoryCount()} trajectories`);
848
+
849
+ // Get nullclines
850
+ const nullclines = portrait.nullclines();
851
+ console.log(`x-nullcline: ${nullclines.x.length} points`);
852
+ console.log(`y-nullcline: ${nullclines.y.length} points`);
853
+
854
+ // Clean up WASM memory
855
+ lorenz.free();
856
+ vdp.free();
857
+ duffing.free();
858
+ solver.free();
859
+ bifurcation.free();
860
+ portrait.free();
861
+ fixedPoints.forEach(fp => fp.free());
862
+ }
863
+
864
+ dynamicsDemo();
865
+ ```
866
+
867
+ #### Dynamics API
868
+
869
+ **WasmLorenzSystem:**
870
+ - `classic()`: Create with σ=10, ρ=28, β=8/3
871
+ - `new(sigma, rho, beta)`: Create with custom parameters
872
+ - `sigma`, `rho`, `beta`: Parameter getters
873
+ - `vectorField(state)`: Evaluate dx/dt at state
874
+
875
+ **WasmVanDerPolOscillator:**
876
+ - `new(mu)`: Create with damping parameter μ
877
+ - `mu`: Parameter getter
878
+ - `vectorField(state)`: Evaluate dx/dt at state
879
+
880
+ **WasmDuffingOscillator:**
881
+ - `new(alpha, beta, delta, gamma, omega)`: Create driven Duffing oscillator
882
+ - `vectorField(state, t)`: Evaluate dx/dt at state and time t
883
+
884
+ **WasmRosslerSystem:**
885
+ - `new(a, b, c)`: Create Rossler attractor
886
+ - `classic()`: Create with a=0.2, b=0.2, c=5.7
887
+
888
+ **WasmHenonMap:**
889
+ - `new(a, b)`: Create Henon map
890
+ - `classic()`: Create with a=1.4, b=0.3
891
+ - `iterate(state)`: Apply one map iteration
892
+
893
+ **WasmRungeKutta4:**
894
+ - `new()`: Create RK4 solver
895
+ - `solve(system, initial, t0, t1, steps)`: Integrate trajectory
896
+ - `step(system, state, t, dt)`: Single integration step
897
+
898
+ **WasmAdaptiveSolver:**
899
+ - `rkf45()`: Create RKF45 adaptive solver
900
+ - `dormandPrince()`: Create Dormand-Prince solver
901
+ - `solve(system, initial, t0, t1, tolerance)`: Adaptive integration
902
+
903
+ **Lyapunov Functions:**
904
+ - `computeLyapunovExponents(system, initial, steps, dt)`: Compute spectrum
905
+ - Returns: `{ exponents, sum(), kaplanYorkeDimension(), isChaotic() }`
906
+
907
+ **WasmBifurcationDiagram:**
908
+ - `compute(systemType, paramMin, paramMax, numParams, transient, samples)`: Generate diagram
909
+ - `parameterCount()`: Number of parameter values
910
+ - `attractorPoints(param)`: Get attractor at specific parameter
911
+ - `branches()`: Get all (parameter, points) pairs
912
+
913
+ **WasmStabilityAnalysis:**
914
+ - `analyze(system, point)`: Analyze stability at point
915
+ - `stabilityType`: 'stable_node', 'stable_spiral', 'unstable_node', 'unstable_spiral', 'saddle', 'center'
916
+ - `eigenvalues`: Array of {real, imag} pairs
917
+ - `isStable()`: True if asymptotically stable
918
+
919
+ **findFixedPoints:**
920
+ - `findFixedPoints(system, initialGuesses, tolerance)`: Find fixed points via Newton's method
921
+ - Returns array of `{ point, converged, iterations }`
922
+
923
+ **WasmPhasePortrait:**
924
+ - `generate(system, xRange, yRange, resolution, tMax, dt)`: Generate portrait
925
+ - `trajectoryCount()`: Number of trajectories
926
+ - `trajectories()`: Get all trajectory arrays
927
+ - `nullclines()`: Get {x, y} nullcline point arrays
928
+
929
+ #### Topology API
930
+
931
+ **WasmSimplex:**
932
+ - `new(vertices)`: Create simplex from vertex indices
933
+ - `dimension()`: Get dimension (vertices - 1)
934
+ - `getVertices()`: Get sorted vertex array
935
+ - `orientation()`: Get orientation sign (+1 or -1)
936
+ - `containsVertex(v)`: Check if vertex is in simplex
937
+ - `faces(k)`: Get all k-dimensional faces
938
+ - `boundaryFaces()`: Get boundary faces with signs
939
+
940
+ **WasmSimplicialComplex:**
941
+ - `new()`: Create empty complex
942
+ - `addSimplex(vertices)`: Add simplex and all its faces
943
+ - `contains(vertices)`: Check if simplex exists
944
+ - `dimension()`: Get maximum simplex dimension
945
+ - `simplexCount(dim)`: Count simplices in dimension
946
+ - `totalSimplexCount()`: Total simplex count
947
+ - `vertexCount()`: Count 0-simplices
948
+ - `edgeCount()`: Count 1-simplices
949
+ - `bettiNumbers()`: Compute [β₀, β₁, β₂, ...]
950
+ - `eulerCharacteristic()`: Compute χ = Σ(-1)^k f_k
951
+ - `fVector()`: Get face counts [f₀, f₁, f₂, ...]
952
+ - `isConnected()`: Check if complex is connected
953
+ - `connectedComponents()`: Count components
954
+
955
+ **WasmFiltration:**
956
+ - `new()`: Create empty filtration
957
+ - `add(time, vertices)`: Add simplex at filtration time
958
+ - `isEmpty()`: Check if filtration is empty
959
+ - `complexAt(time)`: Get complex at given time
960
+ - `bettiAt(time)`: Get Betti numbers at time
961
+
962
+ **WasmPersistentHomology:**
963
+ - `compute(filtration)`: Compute persistent homology
964
+ - `getDiagram()`: Get [dim, birth, death, ...] triples
965
+ - `bettiAt(time)`: Get Betti numbers at time
966
+ - `intervalCount(dim)`: Count intervals in dimension
967
+
968
+ **Standalone Functions:**
969
+ - `ripsFromDistances(numPoints, maxDim, distances)`: Create Rips filtration
970
+ - `findCriticalPoints2D(resolution, xMin, xMax, yMin, yMax, tolerance, values)`: Find critical points
971
+
972
+ **WasmMorseComplex:**
973
+ - `new(criticalPoints)`: Create from critical points
974
+ - `countsByIndex()`: Get counts by Morse index
975
+ - `checkWeakMorseInequalities(betti)`: Verify c_k >= β_k
976
+
573
977
  #### Optical Field API
574
978
 
575
979
  **WasmOpticalRotorField:**
@@ -681,6 +1085,12 @@ opticalDemo();
681
1085
  - **Embedding Retrieval**: Content-addressable semantic search in vector databases
682
1086
  - **Holographic Displays**: Lee hologram encoding for DMD and SLM devices
683
1087
  - **Optical Computing**: Phase-encoded VSA operations for optical neural networks
1088
+ - **Topological Data Analysis**: Persistent homology for shape and feature detection
1089
+ - **Computational Biology**: Protein structure analysis via simplicial complexes
1090
+ - **Sensor Networks**: Coverage analysis using homology
1091
+ - **Chaos Theory**: Lorenz attractors, bifurcation diagrams, Lyapunov exponents
1092
+ - **Control Systems**: Stability analysis and phase portraits for dynamical systems
1093
+ - **Climate Modeling**: Sensitivity analysis via Lyapunov spectrum computation
684
1094
 
685
1095
  ## API Reference
686
1096
 
@@ -759,6 +1169,36 @@ opticalDemo();
759
1169
  - `WasmOpticalCodebook.get(symbol)`: Get field for symbol
760
1170
  - `WasmTropicalOpticalAlgebra.tropicalAdd(a, b)`: Pointwise minimum phase
761
1171
 
1172
+ ### Topology Operations
1173
+
1174
+ - `WasmSimplex.new(vertices)`: Create simplex from vertex array
1175
+ - `WasmSimplex.dimension()`: Get simplex dimension
1176
+ - `WasmSimplex.faces(k)`: Get k-dimensional faces
1177
+ - `WasmSimplicialComplex.new()`: Create empty complex
1178
+ - `WasmSimplicialComplex.addSimplex(vertices)`: Add simplex with closure
1179
+ - `WasmSimplicialComplex.bettiNumbers()`: Compute Betti numbers
1180
+ - `WasmSimplicialComplex.eulerCharacteristic()`: Compute Euler characteristic
1181
+ - `WasmFiltration.add(time, vertices)`: Add simplex at filtration time
1182
+ - `WasmPersistentHomology.compute(filtration)`: Compute persistence
1183
+ - `WasmPersistentHomology.getDiagram()`: Get persistence diagram
1184
+ - `ripsFromDistances(n, dim, distances)`: Build Rips filtration
1185
+ - `findCriticalPoints2D(...)`: Find Morse critical points
1186
+
1187
+ ### Dynamics Operations
1188
+
1189
+ - `WasmLorenzSystem.classic()`: Create classic Lorenz attractor
1190
+ - `WasmVanDerPolOscillator.new(mu)`: Create Van der Pol oscillator
1191
+ - `WasmDuffingOscillator.new(alpha, beta, delta, gamma, omega)`: Create Duffing oscillator
1192
+ - `WasmRosslerSystem.classic()`: Create Rossler attractor
1193
+ - `WasmHenonMap.classic()`: Create Henon map
1194
+ - `WasmRungeKutta4.solve(system, initial, t0, t1, steps)`: Integrate trajectory
1195
+ - `WasmAdaptiveSolver.rkf45()`: Create adaptive RKF45 solver
1196
+ - `computeLyapunovExponents(system, initial, steps, dt)`: Compute Lyapunov spectrum
1197
+ - `WasmBifurcationDiagram.compute(type, paramMin, paramMax, n, transient, samples)`: Generate bifurcation diagram
1198
+ - `WasmStabilityAnalysis.analyze(system, point)`: Analyze fixed point stability
1199
+ - `findFixedPoints(system, guesses, tolerance)`: Find fixed points
1200
+ - `WasmPhasePortrait.generate(system, xRange, yRange, res, tMax, dt)`: Generate phase portrait
1201
+
762
1202
  ## Examples
763
1203
 
764
1204
  Check out the [examples directory](https://github.com/justinelliottcobb/Amari/tree/master/examples) for more detailed usage: