├── .gitignore ├── .project ├── FUNDING.yml ├── src ├── .properties ├── Math-ODE │ ├── package.st │ ├── PMExplicitAnnouncer.class.st │ ├── PMImplicitAnnouncer.class.st │ ├── PMExplicitMultiStepper.class.st │ ├── PMImplicitMultiStepper.class.st │ ├── PMExplicitSolver.class.st │ ├── PMImplicitSolver.class.st │ ├── PMImplicitSystem.class.st │ ├── PMSimpleSymplecticSystem.class.st │ ├── PMODESystem.class.st │ ├── PMExplicitSolverAnnouncement.class.st │ ├── PMImplicitSolverAnnouncement.class.st │ ├── PMHeunStepper.class.st │ ├── PMImplicitMidpointStepper.class.st │ ├── PMExplicitSystem.class.st │ ├── PMBDF2Stepper.class.st │ ├── PMImplicitMidpointSolver.class.st │ ├── PMMidpointStepper.class.st │ ├── PMStateTime.class.st │ ├── PMBDF3Stepper.class.st │ ├── PMMultiStepper.class.st │ ├── PMBDF4Stepper.class.st │ ├── PMAB2Stepper.class.st │ ├── PMRungeKuttaStepper.class.st │ ├── PMTranscriptRecorder.class.st │ ├── PMStepper.class.st │ ├── PMStateRecorder.class.st │ ├── PMSymplecticSystem.class.st │ ├── PMExplicitSolverSubscriber.class.st │ ├── PMAB3Stepper.class.st │ ├── PMAB4Stepper.class.st │ └── PMTrapezoidStepper.class.st ├── Math-TSNE │ └── package.st ├── Math-Complex │ ├── package.st │ ├── PMPolynomial.extension.st │ ├── Object.extension.st │ ├── PMVector.extension.st │ └── Number.extension.st ├── Math-Helpers │ ├── package.st │ ├── SequenceableCollection.extension.st │ └── PMWeightedPoint.class.st ├── Math-KDTree │ ├── package.st │ ├── PMStupidNN.class.st │ └── PMIndexedKDTree.class.st ├── Math-Series │ ├── package.st │ ├── PMContinuedFraction.class.st │ └── PMInfiniteSeries.class.st ├── Math-Chromosome │ ├── package.st │ ├── ManifestMathChromosome.class.st │ └── PMVectorChromosomeManager.class.st ├── Math-Clustering │ ├── package.st │ ├── PMCovarianceCluster.class.st │ ├── PMEuclideanCluster.class.st │ ├── PMCluster.class.st │ └── PMMahalanobisCenter.class.st ├── Math-Numerical │ ├── package.st │ ├── PMMaximizingPoint.class.st │ ├── PMBulirschStoerInterpolator.class.st │ ├── PMVectorProjectedFunction.class.st │ ├── Integer.extension.st │ ├── PMProjectedOneVariableFunction.class.st │ ├── PMCDFNewtonZeroFinder.class.st │ ├── PMMinimizingPoint.class.st │ ├── PMOptimizingBracketFinder.class.st │ ├── PMVectorAccumulator.class.st │ ├── PMLeastSquares.class.st │ ├── PMCovarianceAccumulator.class.st │ ├── PMFunctionalIterator.class.st │ ├── PMNewtonInterpolator.class.st │ ├── PMMultiVariableGeneralOptimizer.class.st │ ├── Number.extension.st │ ├── PMLanczosFormula.class.st │ └── PMBisectionZeroFinder.class.st ├── Math-Quantile │ └── package.st ├── Math-Quaternion │ ├── package.st │ ├── Object.extension.st │ ├── PMVector.extension.st │ ├── PMPolynomial.extension.st │ ├── Number.extension.st │ └── PMComplexNumber.extension.st ├── Math-Statistics │ └── package.st ├── Math-Tests-ODE │ ├── package.st │ ├── PMAbstractTest.class.st │ ├── PMStepperTest.class.st │ ├── PMImplicitMidpointSolverTest.class.st │ ├── PMODESystemTest.class.st │ ├── PMEulerSolverTest.class.st │ ├── PMBeckwardEulerSolverTest.class.st │ ├── PMAM4StepperTest.class.st │ ├── PMAB3StepperTest.class.st │ └── PMAB4StepperTest.class.st ├── Math-Tests-TSNE │ └── package.st ├── BaselineOfPolyMath │ └── package.st ├── Math-Accuracy-ODE │ └── package.st ├── Math-Core-Process │ └── package.st ├── Math-FunctionFit │ ├── package.st │ ├── Collection.extension.st │ ├── PMDataHolder.class.st │ ├── PMErrorMinimizer.class.st │ └── PMErrorAsParameterFunction.class.st ├── Math-Permutation │ └── package.st ├── Math-Polynomials │ ├── package.st │ └── PMEstimatedPolynomial.class.st ├── Math-Tests-Complex │ ├── package.st │ └── NumberTest.extension.st ├── Math-Tests-KDTree │ └── package.st ├── ExtendedNumberParser │ └── package.st ├── Math-Accuracy-Core │ └── package.st ├── Math-Benchmarks-ODE │ ├── package.st │ ├── PMImplicitBenchmark.class.st │ ├── PMExplicitMultiBenchmark.class.st │ ├── PMExplicitBenchmark.class.st │ └── PMODEBenchmark.class.st ├── Math-Distributions │ ├── package.st │ ├── PMSeriesTermServer.class.st │ ├── ManifestMathDistributions.class.st │ ├── PMSimpsonIntegrator.class.st │ ├── PMIncompleteGammaSeriesTermServer.class.st │ ├── PMIncompleteGammaFractionTermServer.class.st │ ├── PMProbabilityDistributionFunction.class.st │ ├── PMIncompleteBetaFractionTermServer.class.st │ ├── PMLaplaceGenerator.class.st │ ├── PMRombergIntegrator.class.st │ ├── PMIncompleteGammaFunction.class.st │ ├── PMChiSquareDistribution.class.st │ ├── PMIncompleteBetaFunction.class.st │ ├── PMMitchellMooreGenerator.class.st │ └── PMCongruentialRandomNumberGenerator.class.st ├── Math-Tests-Accuracy │ ├── package.st │ └── PMAccuracyFindKeyTest.class.st ├── Math-Tests-Numerical │ ├── package.st │ ├── PMExponentialDistributionTest.class.st │ ├── IntegerTest.extension.st │ ├── PMCovarianceAccumulatorTest.class.st │ ├── PMGeneticOptimizerBugsTest.class.st │ └── PMNumberExtensionsTestCase.class.st ├── Math-Tests-Quantile │ └── package.st ├── Math-UtilsDataServer │ ├── package.st │ ├── PMAbstractDataServer.class.st │ └── PMMemoryBasedDataServer.class.st ├── Math-Visualizations │ └── package.st ├── Math-Benchmarks-KDTree │ └── package.st ├── Math-Number-Extensions │ ├── package.st │ ├── Number.extension.st │ └── Float.extension.st ├── Math-Physics-Constants │ └── package.st ├── Math-Tests-Clustering │ └── package.st ├── Math-Tests-FunctionFit │ ├── package.st │ ├── PMDataHolderTest.class.st │ ├── PMErrorMinimizerTest.class.st │ ├── PMFunctionFitTest.class.st │ ├── PMErrorAsParameterFunctionTest.class.st │ └── PMSimpleParameterFunctionTest.class.st ├── Math-Tests-Permutation │ └── package.st ├── Math-Tests-Polynomials │ └── package.st ├── Math-Tests-Quaternion │ └── package.st ├── Math-Tests-Statistics │ ├── package.st │ └── PMStatisticsTests.class.st ├── Math-FastFourierTransform │ ├── package.st │ ├── Integer.extension.st │ ├── LargePositiveInteger.extension.st │ └── SmallInteger.extension.st ├── Math-StatisticalMoments │ ├── package.st │ ├── PMFixedStatisticalMoments.class.st │ └── PMFastStatisticalMoments.class.st ├── Math-Tests-Core-Process │ └── package.st ├── Math-Tests-Distributions │ ├── package.st │ ├── PMKolmogorovSmirnovSample.class.st │ ├── PMMultivariateNormalDistributionTest.class.st │ └── PMKolmogorovsDistributionTest.class.st ├── Math-ArbitraryPrecisionFloat │ ├── package.st │ ├── LargePositiveInteger.extension.st │ ├── ScaledDecimal.extension.st │ ├── Integer.extension.st │ ├── Float.extension.st │ ├── Number.extension.st │ ├── Fraction.extension.st │ └── NumberParser.extension.st ├── Math-AutomaticDifferenciation │ ├── package.st │ ├── Object.extension.st │ ├── Number.extension.st │ ├── PMGradient.class.st │ └── PMHessian.class.st ├── Math-CompatibilityUpToPharo11 │ ├── package.st │ ├── Float32Array.class.st │ ├── SequenceableCollection.extension.st │ └── Float.extension.st ├── Math-Tests-Number-Extensions │ ├── package.st │ └── PMNumberExtensionsTest.class.st ├── Math-PrincipalComponentAnalysis │ ├── package.st │ ├── PMDataTransformer.class.st │ ├── PMPrincipalComponentAnalyser.class.st │ ├── PMPrincipalComponentAnalyserSVD.class.st │ ├── PMStandardizationScaler.class.st │ └── PMSciKitLearnSVDFlipAlgorithm.class.st ├── Math-Tests-FastFourierTransform │ └── package.st ├── Math-Tests-ArbitraryPrecisionFloat │ └── package.st ├── Math-Tests-AutomaticDifferenciation │ ├── package.st │ └── PMGradientAndHessianTest.class.st ├── Math-Tests-RandomDistributionBased │ ├── package.st │ └── PMLaplaceGeneratorTest.class.st └── Math-Tests-PrincipalComponentAnalysis │ ├── package.st │ ├── PMPCAJacobiTransformationTest.class.st │ ├── PMPCASingularValueDecompositionTest.class.st │ └── PMPrincipalComponentAnalyserTest.class.st ├── assets └── logos │ ├── logo.png │ └── logo-title.png ├── .smalltalk.ston ├── .github └── workflows │ ├── smalltalk-ci.yml │ └── release.yml └── LICENSE /.gitignore: -------------------------------------------------------------------------------- 1 | **/.DS_STORE 2 | -------------------------------------------------------------------------------- /.project: -------------------------------------------------------------------------------- 1 | { 2 | 'srcDirectory' : 'src' 3 | } -------------------------------------------------------------------------------- /FUNDING.yml: -------------------------------------------------------------------------------- 1 | github: SergeStinckwich 2 | -------------------------------------------------------------------------------- /src/.properties: -------------------------------------------------------------------------------- 1 | { 2 | #format : #tonel 3 | } -------------------------------------------------------------------------------- /src/Math-ODE/package.st: -------------------------------------------------------------------------------- 1 | Package { #name : #'Math-ODE' } 2 | -------------------------------------------------------------------------------- /src/Math-TSNE/package.st: -------------------------------------------------------------------------------- 1 | Package { #name : #'Math-TSNE' } 2 | -------------------------------------------------------------------------------- /src/Math-Complex/package.st: -------------------------------------------------------------------------------- 1 | Package { #name : #'Math-Complex' } 2 | -------------------------------------------------------------------------------- /src/Math-Helpers/package.st: -------------------------------------------------------------------------------- 1 | Package { #name : #'Math-Helpers' } 2 | -------------------------------------------------------------------------------- /src/Math-KDTree/package.st: -------------------------------------------------------------------------------- 1 | Package { #name : #'Math-KDTree' } 2 | -------------------------------------------------------------------------------- /src/Math-Series/package.st: -------------------------------------------------------------------------------- 1 | Package { #name : #'Math-Series' } 2 | -------------------------------------------------------------------------------- /src/Math-Chromosome/package.st: -------------------------------------------------------------------------------- 1 | Package { #name : #'Math-Chromosome' } 2 | -------------------------------------------------------------------------------- /src/Math-Clustering/package.st: -------------------------------------------------------------------------------- 1 | Package { #name : #'Math-Clustering' } 2 | -------------------------------------------------------------------------------- /src/Math-Numerical/package.st: -------------------------------------------------------------------------------- 1 | Package { #name : #'Math-Numerical' } 2 | -------------------------------------------------------------------------------- /src/Math-Quantile/package.st: -------------------------------------------------------------------------------- 1 | Package { #name : #'Math-Quantile' } 2 | -------------------------------------------------------------------------------- /src/Math-Quaternion/package.st: -------------------------------------------------------------------------------- 1 | Package { #name : #'Math-Quaternion' } 2 | -------------------------------------------------------------------------------- /src/Math-Statistics/package.st: -------------------------------------------------------------------------------- 1 | Package { #name : #'Math-Statistics' } 2 | -------------------------------------------------------------------------------- /src/Math-Tests-ODE/package.st: -------------------------------------------------------------------------------- 1 | Package { #name : 'Math-Tests-ODE' } 2 | -------------------------------------------------------------------------------- /src/Math-Tests-TSNE/package.st: -------------------------------------------------------------------------------- 1 | Package { #name : 'Math-Tests-TSNE' } 2 | -------------------------------------------------------------------------------- /src/BaselineOfPolyMath/package.st: -------------------------------------------------------------------------------- 1 | Package { #name : #BaselineOfPolyMath } 2 | -------------------------------------------------------------------------------- /src/Math-Accuracy-ODE/package.st: -------------------------------------------------------------------------------- 1 | Package { #name : #'Math-Accuracy-ODE' } 2 | -------------------------------------------------------------------------------- /src/Math-Core-Process/package.st: -------------------------------------------------------------------------------- 1 | Package { #name : #'Math-Core-Process' } 2 | -------------------------------------------------------------------------------- /src/Math-FunctionFit/package.st: -------------------------------------------------------------------------------- 1 | Package { #name : #'Math-FunctionFit' } 2 | -------------------------------------------------------------------------------- /src/Math-Permutation/package.st: -------------------------------------------------------------------------------- 1 | Package { #name : #'Math-Permutation' } 2 | -------------------------------------------------------------------------------- /src/Math-Polynomials/package.st: -------------------------------------------------------------------------------- 1 | Package { #name : #'Math-Polynomials' } 2 | -------------------------------------------------------------------------------- /src/Math-Tests-Complex/package.st: -------------------------------------------------------------------------------- 1 | Package { #name : 'Math-Tests-Complex' } 2 | -------------------------------------------------------------------------------- /src/Math-Tests-KDTree/package.st: -------------------------------------------------------------------------------- 1 | Package { #name : #'Math-Tests-KDTree' } 2 | -------------------------------------------------------------------------------- /src/ExtendedNumberParser/package.st: -------------------------------------------------------------------------------- 1 | Package { #name : #ExtendedNumberParser } 2 | -------------------------------------------------------------------------------- /src/Math-Accuracy-Core/package.st: -------------------------------------------------------------------------------- 1 | Package { #name : #'Math-Accuracy-Core' } 2 | -------------------------------------------------------------------------------- /src/Math-Benchmarks-ODE/package.st: -------------------------------------------------------------------------------- 1 | Package { #name : #'Math-Benchmarks-ODE' } 2 | -------------------------------------------------------------------------------- /src/Math-Distributions/package.st: -------------------------------------------------------------------------------- 1 | Package { #name : #'Math-Distributions' } 2 | -------------------------------------------------------------------------------- /src/Math-Tests-Accuracy/package.st: -------------------------------------------------------------------------------- 1 | Package { #name : 'Math-Tests-Accuracy' } 2 | -------------------------------------------------------------------------------- /src/Math-Tests-Numerical/package.st: -------------------------------------------------------------------------------- 1 | Package { #name : 'Math-Tests-Numerical' } 2 | -------------------------------------------------------------------------------- /src/Math-Tests-Quantile/package.st: -------------------------------------------------------------------------------- 1 | Package { #name : #'Math-Tests-Quantile' } 2 | -------------------------------------------------------------------------------- /src/Math-UtilsDataServer/package.st: -------------------------------------------------------------------------------- 1 | Package { #name : #'Math-UtilsDataServer' } 2 | -------------------------------------------------------------------------------- /src/Math-Visualizations/package.st: -------------------------------------------------------------------------------- 1 | Package { #name : #'Math-Visualizations' } 2 | -------------------------------------------------------------------------------- /src/Math-Benchmarks-KDTree/package.st: -------------------------------------------------------------------------------- 1 | Package { #name : #'Math-Benchmarks-KDTree' } 2 | -------------------------------------------------------------------------------- /src/Math-Number-Extensions/package.st: -------------------------------------------------------------------------------- 1 | Package { #name : #'Math-Number-Extensions' } 2 | -------------------------------------------------------------------------------- /src/Math-Physics-Constants/package.st: -------------------------------------------------------------------------------- 1 | Package { #name : #'Math-Physics-Constants' } 2 | -------------------------------------------------------------------------------- /src/Math-Tests-Clustering/package.st: -------------------------------------------------------------------------------- 1 | Package { #name : #'Math-Tests-Clustering' } 2 | -------------------------------------------------------------------------------- /src/Math-Tests-FunctionFit/package.st: -------------------------------------------------------------------------------- 1 | Package { #name : #'Math-Tests-FunctionFit' } 2 | -------------------------------------------------------------------------------- /src/Math-Tests-Permutation/package.st: -------------------------------------------------------------------------------- 1 | Package { #name : #'Math-Tests-Permutation' } 2 | -------------------------------------------------------------------------------- /src/Math-Tests-Polynomials/package.st: -------------------------------------------------------------------------------- 1 | Package { #name : #'Math-Tests-Polynomials' } 2 | -------------------------------------------------------------------------------- /src/Math-Tests-Quaternion/package.st: -------------------------------------------------------------------------------- 1 | Package { #name : #'Math-Tests-Quaternion' } 2 | -------------------------------------------------------------------------------- /src/Math-Tests-Statistics/package.st: -------------------------------------------------------------------------------- 1 | Package { #name : #'Math-Tests-Statistics' } 2 | -------------------------------------------------------------------------------- /src/Math-FastFourierTransform/package.st: -------------------------------------------------------------------------------- 1 | Package { #name : #'Math-FastFourierTransform' } 2 | -------------------------------------------------------------------------------- /src/Math-StatisticalMoments/package.st: -------------------------------------------------------------------------------- 1 | Package { #name : #'Math-StatisticalMoments' } 2 | -------------------------------------------------------------------------------- /src/Math-Tests-Core-Process/package.st: -------------------------------------------------------------------------------- 1 | Package { #name : #'Math-Tests-Core-Process' } 2 | -------------------------------------------------------------------------------- /src/Math-Tests-Distributions/package.st: -------------------------------------------------------------------------------- 1 | Package { #name : 'Math-Tests-Distributions' } 2 | -------------------------------------------------------------------------------- /assets/logos/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PolyMathOrg/PolyMath/HEAD/assets/logos/logo.png -------------------------------------------------------------------------------- /src/Math-ArbitraryPrecisionFloat/package.st: -------------------------------------------------------------------------------- 1 | Package { #name : #'Math-ArbitraryPrecisionFloat' } 2 | -------------------------------------------------------------------------------- /src/Math-AutomaticDifferenciation/package.st: -------------------------------------------------------------------------------- 1 | Package { #name : #'Math-AutomaticDifferenciation' } 2 | -------------------------------------------------------------------------------- /src/Math-CompatibilityUpToPharo11/package.st: -------------------------------------------------------------------------------- 1 | Package { #name : #'Math-CompatibilityUpToPharo11' } 2 | -------------------------------------------------------------------------------- /src/Math-Tests-Number-Extensions/package.st: -------------------------------------------------------------------------------- 1 | Package { #name : #'Math-Tests-Number-Extensions' } 2 | -------------------------------------------------------------------------------- /src/Math-PrincipalComponentAnalysis/package.st: -------------------------------------------------------------------------------- 1 | Package { #name : 'Math-PrincipalComponentAnalysis' } 2 | -------------------------------------------------------------------------------- /src/Math-Tests-FastFourierTransform/package.st: -------------------------------------------------------------------------------- 1 | Package { #name : 'Math-Tests-FastFourierTransform' } 2 | -------------------------------------------------------------------------------- /assets/logos/logo-title.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PolyMathOrg/PolyMath/HEAD/assets/logos/logo-title.png -------------------------------------------------------------------------------- /src/Math-Tests-ArbitraryPrecisionFloat/package.st: -------------------------------------------------------------------------------- 1 | Package { #name : #'Math-Tests-ArbitraryPrecisionFloat' } 2 | -------------------------------------------------------------------------------- /src/Math-Tests-AutomaticDifferenciation/package.st: -------------------------------------------------------------------------------- 1 | Package { #name : #'Math-Tests-AutomaticDifferenciation' } 2 | -------------------------------------------------------------------------------- /src/Math-Tests-RandomDistributionBased/package.st: -------------------------------------------------------------------------------- 1 | Package { #name : #'Math-Tests-RandomDistributionBased' } 2 | -------------------------------------------------------------------------------- /src/Math-Tests-PrincipalComponentAnalysis/package.st: -------------------------------------------------------------------------------- 1 | Package { #name : 'Math-Tests-PrincipalComponentAnalysis' } 2 | -------------------------------------------------------------------------------- /src/Math-FunctionFit/Collection.extension.st: -------------------------------------------------------------------------------- 1 | Extension { #name : #Collection } 2 | 3 | { #category : #'*Math-FunctionFit' } 4 | Collection >> norm [ 5 | ^(self*self)sum sqrt 6 | ] 7 | -------------------------------------------------------------------------------- /src/Math-AutomaticDifferenciation/Object.extension.st: -------------------------------------------------------------------------------- 1 | Extension { #name : #Object } 2 | 3 | { #category : #'*Math-AutomaticDifferenciation' } 4 | Object >> isDualNumber [ 5 | ^ false 6 | ] 7 | -------------------------------------------------------------------------------- /src/Math-CompatibilityUpToPharo11/Float32Array.class.st: -------------------------------------------------------------------------------- 1 | Class { 2 | #name : #Float32Array, 3 | #superclass : #FloatArray, 4 | #type : #words, 5 | #category : #'Math-CompatibilityUpToPharo11' 6 | } 7 | -------------------------------------------------------------------------------- /src/Math-Tests-Complex/NumberTest.extension.st: -------------------------------------------------------------------------------- 1 | Extension { #name : 'NumberTest' } 2 | 3 | { #category : '*Math-Tests-Complex' } 4 | NumberTest >> testComplexConjugate [ 5 | 6 | self assert: 5 complexConjugate equals: 5 7 | ] 8 | -------------------------------------------------------------------------------- /src/Math-Quaternion/Object.extension.st: -------------------------------------------------------------------------------- 1 | Extension { #name : #Object } 2 | 3 | { #category : #'*Math-Quaternion-testing' } 4 | Object >> isQuaternion [ 5 | "Answer true if receiver is a Quaternion. False by default." 6 | 7 | ^ false 8 | ] 9 | -------------------------------------------------------------------------------- /src/Math-ArbitraryPrecisionFloat/LargePositiveInteger.extension.st: -------------------------------------------------------------------------------- 1 | Extension { #name : #LargePositiveInteger } 2 | 3 | { #category : #'*Math-ArbitraryPrecisionFloat' } 4 | LargePositiveInteger >> isZero [ 5 | "faster than super" 6 | 7 | ^false 8 | ] 9 | -------------------------------------------------------------------------------- /src/Math-ODE/PMExplicitAnnouncer.class.st: -------------------------------------------------------------------------------- 1 | " 2 | An ExplicitAnnouncer is used by ODESolver to announce step results (ExplicitSolverAnnouncement). 3 | " 4 | Class { 5 | #name : #PMExplicitAnnouncer, 6 | #superclass : #Announcer, 7 | #category : #'Math-ODE' 8 | } 9 | -------------------------------------------------------------------------------- /src/Math-ODE/PMImplicitAnnouncer.class.st: -------------------------------------------------------------------------------- 1 | " 2 | An ImplicitAnnouncer is used by ODESolver to announce step results (ImplicitSolverAnnouncement). 3 | " 4 | Class { 5 | #name : #PMImplicitAnnouncer, 6 | #superclass : #Announcer, 7 | #category : #'Math-ODE' 8 | } 9 | -------------------------------------------------------------------------------- /src/Math-Complex/PMPolynomial.extension.st: -------------------------------------------------------------------------------- 1 | Extension { #name : #PMPolynomial } 2 | 3 | { #category : #'*Math-Complex' } 4 | PMPolynomial >> adaptToComplex: rcvr andSend: selector [ 5 | ^(self class coefficients: (Array with: rcvr) ) perform: selector with: self 6 | ] 7 | -------------------------------------------------------------------------------- /src/Math-ArbitraryPrecisionFloat/ScaledDecimal.extension.st: -------------------------------------------------------------------------------- 1 | Extension { #name : #ScaledDecimal } 2 | 3 | { #category : #'*Math-ArbitraryPrecisionFloat' } 4 | ScaledDecimal >> asArbitraryPrecisionFloatNumBits: n [ 5 | ^self asFraction asArbitraryPrecisionFloatNumBits: n 6 | ] 7 | -------------------------------------------------------------------------------- /src/Math-ArbitraryPrecisionFloat/Integer.extension.st: -------------------------------------------------------------------------------- 1 | Extension { #name : #Integer } 2 | 3 | { #category : #'*Math-ArbitraryPrecisionFloat' } 4 | Integer >> asArbitraryPrecisionFloatNumBits: n [ 5 | ^PMArbitraryPrecisionFloat 6 | mantissa: self 7 | exponent: 0 8 | nBits: n 9 | ] 10 | -------------------------------------------------------------------------------- /src/Math-Distributions/PMSeriesTermServer.class.st: -------------------------------------------------------------------------------- 1 | Class { 2 | #name : #PMSeriesTermServer, 3 | #superclass : #Object, 4 | #instVars : [ 5 | 'x', 6 | 'lastTerm' 7 | ], 8 | #category : #'Math-Distributions-Gamma' 9 | } 10 | 11 | { #category : #initialization } 12 | PMSeriesTermServer >> setArgument: aNumber [ 13 | x := aNumber asFloat 14 | ] 15 | -------------------------------------------------------------------------------- /.smalltalk.ston: -------------------------------------------------------------------------------- 1 | SmalltalkCISpec { 2 | #loading : [ 3 | SCIMetacelloLoadSpec { 4 | #baseline : 'PolyMath', 5 | #directory : 'src', 6 | #platforms : [ #pharo ] 7 | } 8 | ], 9 | #testing : { 10 | #include : { 11 | #packages : [ 'Math-.*' ] 12 | }, 13 | #coverage : { 14 | #packages : [ 'Math-*' ] 15 | } 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/Math-ODE/PMExplicitMultiStepper.class.st: -------------------------------------------------------------------------------- 1 | " 2 | An ExplicitMultiStepper is stepper for explicit linear multistep methods. 3 | Explicit means that the new state of the ode can be computed explicitly from the current state without solving implicit equations. 4 | 5 | " 6 | Class { 7 | #name : #PMExplicitMultiStepper, 8 | #superclass : #PMMultiStepper, 9 | #category : #'Math-ODE' 10 | } 11 | -------------------------------------------------------------------------------- /src/Math-ODE/PMImplicitMultiStepper.class.st: -------------------------------------------------------------------------------- 1 | " 2 | An ImplicitMultiStepper is stepper for implicit linear multistep methods. 3 | Implicit methods find a solution by solving an equation involving the current state of the system and the previous states. 4 | 5 | 6 | " 7 | Class { 8 | #name : #PMImplicitMultiStepper, 9 | #superclass : #PMMultiStepper, 10 | #category : #'Math-ODE' 11 | } 12 | -------------------------------------------------------------------------------- /src/Math-Complex/Object.extension.st: -------------------------------------------------------------------------------- 1 | Extension { #name : #Object } 2 | 3 | { #category : #'*Math-Complex' } 4 | Object >> isComplexNumber [ 5 | "Answer true if receiver is a Complex number. False by default." 6 | 7 | ^ false 8 | ] 9 | 10 | { #category : #'*Math-Complex' } 11 | Object >> isRealNumber [ 12 | "Answer true if receiver is a real number. False by default." 13 | ^ false 14 | ] 15 | -------------------------------------------------------------------------------- /src/Math-Quaternion/PMVector.extension.st: -------------------------------------------------------------------------------- 1 | Extension { #name : #PMVector } 2 | 3 | { #category : #'*Math-Quaternion' } 4 | PMVector >> adaptToQuaternion: aQuaternion andSend: aByteSymbol [ 5 | ^ self collect: [ :ea | aQuaternion perform: aByteSymbol with: ea ] 6 | ] 7 | 8 | { #category : #'*Math-Quaternion' } 9 | PMVector >> multiplyByQuaternion: quaternion [ 10 | 11 | ^ self * quaternion 12 | ] 13 | -------------------------------------------------------------------------------- /src/Math-Complex/PMVector.extension.st: -------------------------------------------------------------------------------- 1 | Extension { #name : #PMVector } 2 | 3 | { #category : #'*Math-Complex' } 4 | PMVector >> adaptToComplex: rcvr andSend: selector [ 5 | ^ self collect: [ :ea | rcvr perform: selector with: ea ] 6 | ] 7 | 8 | { #category : #'*Math-Complex' } 9 | PMVector >> isReal [ 10 | 11 | "Answer true if all values of the vector are real numbers" 12 | 13 | ^ self allSatisfy: [ :each | each isRealNumber ] 14 | ] 15 | -------------------------------------------------------------------------------- /src/Math-Tests-FunctionFit/PMDataHolderTest.class.st: -------------------------------------------------------------------------------- 1 | Class { 2 | #name : #PMDataHolderTest, 3 | #superclass : #TestCase, 4 | #category : #'Math-Tests-FunctionFit' 5 | } 6 | 7 | { #category : #tests } 8 | PMDataHolderTest >> testpointsAndErrorsDo [ 9 | |g| 10 | g:=PMDataHolder new: 2 withAll: 2@3. 11 | g pointsAndErrorsDo: [:i|( i xValue =2)ifFalse:[self error].( i yValue =3)ifFalse:[self error].( i weight=1)ifFalse:[self error]] 12 | ] 13 | -------------------------------------------------------------------------------- /src/Math-Chromosome/ManifestMathChromosome.class.st: -------------------------------------------------------------------------------- 1 | " 2 | Please describe the package using the class comment of the included manifest class. The manifest class also includes other additional metadata for the package. These meta data are used by other tools such as the SmalllintManifestChecker and the critics Browser 3 | " 4 | Class { 5 | #name : #ManifestMathChromosome, 6 | #superclass : #PackageManifest, 7 | #category : #'Math-Chromosome-Manifest' 8 | } 9 | -------------------------------------------------------------------------------- /src/Math-Quaternion/PMPolynomial.extension.st: -------------------------------------------------------------------------------- 1 | Extension { #name : #PMPolynomial } 2 | 3 | { #category : #'*Math-Quaternion' } 4 | PMPolynomial >> adaptToQuaternion: rcvr andSend: selector [ 5 | ^(self class coefficients: (Array with: rcvr) ) perform: selector with: self 6 | ] 7 | 8 | { #category : #'*Math-Quaternion' } 9 | PMPolynomial >> multiplyByQuaternion: quaternion [ 10 | 11 | ^ (self class coefficients: (Array with: quaternion)) * self 12 | ] 13 | -------------------------------------------------------------------------------- /src/Math-Distributions/ManifestMathDistributions.class.st: -------------------------------------------------------------------------------- 1 | " 2 | Please describe the package using the class comment of the included manifest class. The manifest class also includes other additional metadata for the package. These meta data are used by other tools such as the SmalllintManifestChecker and the critics Browser 3 | " 4 | Class { 5 | #name : #ManifestMathDistributions, 6 | #superclass : #PackageManifest, 7 | #category : #'Math-Distributions-Manifest' 8 | } 9 | -------------------------------------------------------------------------------- /src/Math-Numerical/PMMaximizingPoint.class.st: -------------------------------------------------------------------------------- 1 | Class { 2 | #name : #PMMaximizingPoint, 3 | #superclass : #PMMinimizingPoint, 4 | #category : #'Math-Numerical-Math-FunctionIterator' 5 | } 6 | 7 | { #category : #information } 8 | PMMaximizingPoint >> better: anOptimizingPoint [ 9 | ^ value >= anOptimizingPoint value 10 | ] 11 | 12 | { #category : #information } 13 | PMMaximizingPoint >> betterThan: anOptimizingPoint [ 14 | ^value > anOptimizingPoint value 15 | ] 16 | -------------------------------------------------------------------------------- /src/Math-AutomaticDifferenciation/Number.extension.st: -------------------------------------------------------------------------------- 1 | Extension { #name : #Number } 2 | 3 | { #category : #'*Math-AutomaticDifferenciation' } 4 | Number >> addDualNumber: aDualNumber [ 5 | ^ aDualNumber class 6 | value: self + aDualNumber value 7 | eps: aDualNumber eps 8 | ] 9 | 10 | { #category : #'*Math-AutomaticDifferenciation' } 11 | Number >> multiplyDualNumber: aDualNumber [ 12 | ^ aDualNumber class 13 | value: aDualNumber value * self 14 | eps: aDualNumber eps * self 15 | ] 16 | -------------------------------------------------------------------------------- /src/Math-Tests-ODE/PMAbstractTest.class.st: -------------------------------------------------------------------------------- 1 | Class { 2 | #name : 'PMAbstractTest', 3 | #superclass : 'TestCase', 4 | #category : 'Math-Tests-ODE', 5 | #package : 'Math-Tests-ODE' 6 | } 7 | 8 | { #category : 'testing' } 9 | PMAbstractTest class >> isAbstract [ 10 | 11 | ^ self name == #PMAbstractTest 12 | ] 13 | 14 | { #category : 'asserting' } 15 | PMAbstractTest >> assert: actualNumber closeTo: expectedNumber [ 16 | 17 | self assert: actualNumber closeTo: expectedNumber precision: 0.001 18 | ] 19 | -------------------------------------------------------------------------------- /src/Math-ODE/PMExplicitSolver.class.st: -------------------------------------------------------------------------------- 1 | Class { 2 | #name : #PMExplicitSolver, 3 | #superclass : #PMODESolver, 4 | #category : #'Math-ODE' 5 | } 6 | 7 | { #category : #private } 8 | PMExplicitSolver class >> stepperClass [ 9 | ^ PMExplicitStepper 10 | ] 11 | 12 | { #category : #'announcement hooks' } 13 | PMExplicitSolver >> announcementClass [ 14 | ^ PMExplicitSolverAnnouncement 15 | ] 16 | 17 | { #category : #'announcement hooks' } 18 | PMExplicitSolver >> announcerClass [ 19 | 20 | ^ PMExplicitAnnouncer 21 | ] 22 | -------------------------------------------------------------------------------- /src/Math-ODE/PMImplicitSolver.class.st: -------------------------------------------------------------------------------- 1 | Class { 2 | #name : #PMImplicitSolver, 3 | #superclass : #PMODESolver, 4 | #category : #'Math-ODE' 5 | } 6 | 7 | { #category : #private } 8 | PMImplicitSolver class >> stepperClass [ 9 | ^ PMImplicitStepper 10 | ] 11 | 12 | { #category : #'announcement hooks' } 13 | PMImplicitSolver >> announcementClass [ 14 | 15 | ^ PMImplicitSolverAnnouncement 16 | ] 17 | 18 | { #category : #'announcement hooks' } 19 | PMImplicitSolver >> announcerClass [ 20 | 21 | ^ PMImplicitAnnouncer 22 | ] 23 | -------------------------------------------------------------------------------- /src/Math-Distributions/PMSimpsonIntegrator.class.st: -------------------------------------------------------------------------------- 1 | Class { 2 | #name : #PMSimpsonIntegrator, 3 | #superclass : #PMTrapezeIntegrator, 4 | #category : #'Math-Distributions-Core' 5 | } 6 | 7 | { #category : #operation } 8 | PMSimpsonIntegrator >> evaluateIteration [ 9 | | oldResult oldSum | 10 | iterations < 2 11 | ifTrue: [ self higherOrderSum. 12 | ^1 13 | ]. 14 | oldResult := result. 15 | oldSum := sum. 16 | result := (self higherOrderSum * 4 - oldSum) / 3. 17 | ^self relativePrecision: ( result - oldResult) abs 18 | ] 19 | -------------------------------------------------------------------------------- /src/Math-Tests-RandomDistributionBased/PMLaplaceGeneratorTest.class.st: -------------------------------------------------------------------------------- 1 | Class { 2 | #name : #PMLaplaceGeneratorTest, 3 | #superclass : #TestCase, 4 | #category : #'Math-Tests-RandomDistributionBased' 5 | } 6 | 7 | { #category : #'as yet unclassified' } 8 | PMLaplaceGeneratorTest >> classToTest [ 9 | ^ PMLaplaceGenerator 10 | ] 11 | 12 | { #category : #accessing } 13 | PMLaplaceGeneratorTest >> testPeekAlwaysReplyTheSameValue [ 14 | | g | 15 | g := self classToTest shape: 0.5 scale: 0.3. 16 | self assert: g peek equals: g peek 17 | ] 18 | -------------------------------------------------------------------------------- /src/Math-ArbitraryPrecisionFloat/Float.extension.st: -------------------------------------------------------------------------------- 1 | Extension { #name : #Float } 2 | 3 | { #category : #'*Math-ArbitraryPrecisionFloat' } 4 | Float >> asArbitraryPrecisionFloatNumBits: n [ 5 | self isNaN ifTrue: [self error: 'cannot convert NaN to arbitrary precision']. 6 | self isInfinite ifTrue: [self error: 'cannot convert Infinity to arbitrary precision']. 7 | self isZero ifTrue: [^0 asArbitraryPrecisionFloatNumBits: n ]. 8 | ^ PMArbitraryPrecisionFloat new 9 | mantissa: self significandAsInteger * self sign 10 | exponent: (self exponent max: -1022) - 52 11 | nBits: n 12 | ] 13 | -------------------------------------------------------------------------------- /src/Math-FunctionFit/PMDataHolder.class.st: -------------------------------------------------------------------------------- 1 | " 2 | DataHolder is an Array of Points and is used only internally by FunctionFit. 3 | " 4 | Class { 5 | #name : #PMDataHolder, 6 | #superclass : #Array, 7 | #type : #variable, 8 | #category : #'Math-FunctionFit' 9 | } 10 | 11 | { #category : #iterators } 12 | PMDataHolder >> pointsAndErrorsDo: aBlock [ 13 | "uses an unweighted approach; the weighted one does not make sense here and is dangerous, if done too naively, eg because of negative or infinite weights" 14 | self do: 15 | [ :each | aBlock value: (PMWeightedPoint point: each)] 16 | ] 17 | -------------------------------------------------------------------------------- /src/Math-CompatibilityUpToPharo11/SequenceableCollection.extension.st: -------------------------------------------------------------------------------- 1 | Extension { #name : #SequenceableCollection } 2 | 3 | { #category : #'*Math-CompatibilityUpToPharo11' } 4 | SequenceableCollection >> closeTo: aSequenceableCollection precision: aPrecision [ 5 | "Return true if all my elements are close to the elements of same index of the parameter with a certain precision" 6 | 7 | "(#(1.9283 2.3029) closeTo: #(1.9284 2.3028) precision: 0.001) >>> true" 8 | 9 | self with: aSequenceableCollection do: [ :a :b | (a closeTo: b precision: aPrecision) ifFalse: [ ^ false ] ]. 10 | ^ true 11 | ] 12 | -------------------------------------------------------------------------------- /src/Math-FastFourierTransform/Integer.extension.st: -------------------------------------------------------------------------------- 1 | Extension { #name : #Integer } 2 | 3 | { #category : #'*Math-FastFourierTransform' } 4 | Integer >> bitReverse: highBit [ 5 | "Reverse the bits of the receiver so that the lsb is the highBit'th bit of the answer." 6 | 7 | | v r s | 8 | highBit < self highBit 9 | ifTrue: [ self error: 'Not enough bits.' ]. 10 | v := self. 11 | r := v bitAnd: 1. 12 | s := highBit - 1. 13 | [ v := v bitShift: -1. 14 | v = 0 ] 15 | whileFalse: [ r := r bitShift: 1. 16 | r := r bitOr: (v bitAnd: 1). 17 | s := s - 1 ]. 18 | ^ r bitShift: s 19 | ] 20 | -------------------------------------------------------------------------------- /src/Math-FastFourierTransform/LargePositiveInteger.extension.st: -------------------------------------------------------------------------------- 1 | Extension { #name : #LargePositiveInteger } 2 | 3 | { #category : #'*Math-FastFourierTransform' } 4 | LargePositiveInteger >> bitReverse: highBit [ 5 | "This implementation is faster than super." 6 | 7 | | digitSize reversed | 8 | highBit < self highBit 9 | ifTrue: [ self error: 'Not enough bits.' ]. 10 | digitSize := (highBit + 7) // 8. 11 | reversed := self class new: digitSize. 12 | 1 to: self digitLength do: [ :i | reversed digitAt: digitSize + 1 - i put: (self digitAt: i) byteReversed ]. 13 | ^ reversed bitShift: highBit - (digitSize * 8) 14 | ] 15 | -------------------------------------------------------------------------------- /src/Math-Helpers/SequenceableCollection.extension.st: -------------------------------------------------------------------------------- 1 | Extension { #name : #SequenceableCollection } 2 | 3 | { #category : #'*Math-Helpers' } 4 | SequenceableCollection >> closeTo: aSequenceableCollection [ 5 | 6 | self 7 | with: aSequenceableCollection 8 | do: [ :a :b | (a closeTo: b) ifFalse: [ ^ false ] ]. 9 | ^ true 10 | ] 11 | 12 | { #category : #'*Math-Helpers' } 13 | SequenceableCollection >> equalsTo: aSequenceableCollection [ 14 | 15 | self 16 | deprecated: 'Use closeTo: instead' 17 | transformWith: '`@rec equalsTo: `@arg' -> '`@rec closeTo: `@arg'. 18 | 19 | ^ self closeTo: aSequenceableCollection 20 | ] 21 | -------------------------------------------------------------------------------- /src/Math-Tests-PrincipalComponentAnalysis/PMPCAJacobiTransformationTest.class.st: -------------------------------------------------------------------------------- 1 | " 2 | This test checks that the Jacobi transform based PCA meets the acceptance requirements defined in PMPrincipalComponentAnalysisTest 3 | " 4 | Class { 5 | #name : 'PMPCAJacobiTransformationTest', 6 | #superclass : 'PMPrincipalComponentAnalyserTest', 7 | #category : 'Math-Tests-PrincipalComponentAnalysis', 8 | #package : 'Math-Tests-PrincipalComponentAnalysis' 9 | } 10 | 11 | { #category : 'running' } 12 | PMPCAJacobiTransformationTest >> setUp [ 13 | 14 | super setUp. 15 | pca := PMPrincipalComponentAnalyserJacobiTransformation new componentsNumber: 2 16 | ] 17 | -------------------------------------------------------------------------------- /src/Math-ODE/PMImplicitSystem.class.st: -------------------------------------------------------------------------------- 1 | " 2 | This concept describes how to define a ODE that can be solved by an implicit routine. 3 | 4 | Implicit routines need not only the function f(x,t) but also the Jacobian df/dx = A(x,t). 5 | 6 | A is a matrix and implicit routines need to solve the linear problem Ax = b 7 | " 8 | Class { 9 | #name : #PMImplicitSystem, 10 | #superclass : #PMODESystem, 11 | #instVars : [ 12 | 'jacobian', 13 | 'dxdt' 14 | ], 15 | #category : #'Math-ODE' 16 | } 17 | 18 | { #category : #hooks } 19 | PMImplicitSystem >> jacobianAtX: aState t: aTime [ 20 | "calculate and store jacobian at this point df/dx" 21 | self shouldBeImplemented 22 | ] 23 | -------------------------------------------------------------------------------- /src/Math-Tests-Number-Extensions/PMNumberExtensionsTest.class.st: -------------------------------------------------------------------------------- 1 | Class { 2 | #name : #PMNumberExtensionsTest, 3 | #superclass : #TestCase, 4 | #category : #'Math-Tests-Number-Extensions' 5 | } 6 | 7 | { #category : #tests } 8 | PMNumberExtensionsTest >> testArTanh [ 9 | 10 | self assert: 0 arTanh equals: 0.0 arTanh. 11 | self assert: 1 arTanh isFloat. 12 | self assert: 1 arTanh equals: 1.0 arTanh 13 | ] 14 | 15 | { #category : #tests } 16 | PMNumberExtensionsTest >> testSinc [ 17 | "test cardinal sine" 18 | 19 | self assert: 0 sinc equals: 1. 20 | self assert: 0 sinc isInteger. 21 | self assert: 0.0 sinc isFloat. 22 | self assert: Float pi sinc closeTo: 0.0 23 | ] 24 | -------------------------------------------------------------------------------- /src/Math-ODE/PMSimpleSymplecticSystem.class.st: -------------------------------------------------------------------------------- 1 | " 2 | In most Hamiltonian systems the kinetic term is a quadratic term in the momentum Hkin = p^2 / 2m and in many cases it is possible to rescale coordinates and set m=1 which leads to a trivial equation of motion: 3 | 4 | q'(t) = f(p) = p. 5 | 6 | while for p' we still have the general form 7 | 8 | p'(t) = g(q) 9 | 10 | As this case is very frequent we introduced a concept where only the nontrivial equation for p' has to be provided to the symplectic stepper. We call this concept Simple_Symplectic_System 11 | 12 | " 13 | Class { 14 | #name : #PMSimpleSymplecticSystem, 15 | #superclass : #PMSymplecticSystem, 16 | #category : #'Math-ODE' 17 | } 18 | -------------------------------------------------------------------------------- /.github/workflows/smalltalk-ci.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | 3 | env: 4 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 5 | 6 | on: [ push, pull_request ] 7 | 8 | jobs: 9 | build: 10 | runs-on: ubuntu-latest 11 | env: 12 | PROJECT_NAME: PolyMath-${{ matrix.smalltalk }} 13 | strategy: 14 | matrix: 15 | smalltalk: [ Pharo64-11, Pharo64-12, Pharo64-13, Pharo64-14 ] 16 | name: ${{ matrix.smalltalk }} 17 | steps: 18 | - uses: actions/checkout@v3 19 | - uses: hpi-swa/setup-smalltalkCI@v1 20 | with: 21 | smalltalk-image: ${{ matrix.smalltalk }} 22 | - run: smalltalkci -s ${{ matrix.smalltalk }} 23 | shell: bash 24 | timeout-minutes: 15 25 | -------------------------------------------------------------------------------- /src/Math-CompatibilityUpToPharo11/Float.extension.st: -------------------------------------------------------------------------------- 1 | Extension { #name : #Float } 2 | 3 | { #category : #'*Math-CompatibilityUpToPharo11' } 4 | Float class >> defaultComparisonPrecision [ 5 | 6 | ^ self machineEpsilon sqrt 7 | ] 8 | 9 | { #category : #'*Math-CompatibilityUpToPharo11' } 10 | Float class >> machineEpsilon [ 11 | "Answer the machine epsilon or macheps, defined by wikipedia asCalypsoItemContext 12 | https://en.wikipedia.org/wiki/Machine_epsilon 13 | 14 | *an upper bound on the relative approximation error due to rounding in floating point arithmetic* 15 | 16 | Compute it as the difference between 1.0 and previous representable value" 17 | 18 | ^1.0 timesTwoPower: 1 - self precision 19 | ] 20 | -------------------------------------------------------------------------------- /src/Math-Distributions/PMIncompleteGammaSeriesTermServer.class.st: -------------------------------------------------------------------------------- 1 | Class { 2 | #name : #PMIncompleteGammaSeriesTermServer, 3 | #superclass : #PMSeriesTermServer, 4 | #instVars : [ 5 | 'alpha', 6 | 'sum' 7 | ], 8 | #category : #'Math-Distributions-Gamma' 9 | } 10 | 11 | { #category : #information } 12 | PMIncompleteGammaSeriesTermServer >> initialTerm [ 13 | lastTerm := 1 / alpha. 14 | sum := alpha. 15 | ^lastTerm 16 | ] 17 | 18 | { #category : #initialization } 19 | PMIncompleteGammaSeriesTermServer >> setParameter: aNumber [ 20 | alpha := aNumber asFloat 21 | ] 22 | 23 | { #category : #information } 24 | PMIncompleteGammaSeriesTermServer >> termAt: anInteger [ 25 | sum := sum + 1. 26 | lastTerm := lastTerm * x / sum. 27 | ^lastTerm 28 | ] 29 | -------------------------------------------------------------------------------- /src/Math-Distributions/PMIncompleteGammaFractionTermServer.class.st: -------------------------------------------------------------------------------- 1 | Class { 2 | #name : #PMIncompleteGammaFractionTermServer, 3 | #superclass : #PMSeriesTermServer, 4 | #instVars : [ 5 | 'alpha' 6 | ], 7 | #category : #'Math-Distributions-Gamma' 8 | } 9 | 10 | { #category : #information } 11 | PMIncompleteGammaFractionTermServer >> initialTerm [ 12 | lastTerm := x - alpha + 1. 13 | ^lastTerm 14 | ] 15 | 16 | { #category : #initialization } 17 | PMIncompleteGammaFractionTermServer >> setParameter: aNumber [ 18 | alpha := aNumber asFloat 19 | ] 20 | 21 | { #category : #information } 22 | PMIncompleteGammaFractionTermServer >> termsAt: anInteger [ 23 | lastTerm := lastTerm + 2. 24 | ^Array with: (alpha - anInteger) * anInteger with: lastTerm 25 | ] 26 | -------------------------------------------------------------------------------- /src/Math-Tests-FunctionFit/PMErrorMinimizerTest.class.st: -------------------------------------------------------------------------------- 1 | " 2 | ErrorMinimizerTest tests indirectly also ErrorAsParameterFunction. 3 | " 4 | Class { 5 | #name : #PMErrorMinimizerTest, 6 | #superclass : #TestCase, 7 | #category : #'Math-Tests-FunctionFit' 8 | } 9 | 10 | { #category : #tests } 11 | PMErrorMinimizerTest >> testErrorMinimizer [ 12 | 13 | | f col er fit | 14 | f := [ :x :a :b | a * x / (b + x) ]. 15 | col := (0 to: 20) collect: [ :i | i @ (f cull: i cull: 2 cull: 0.4) ]. 16 | er := PMErrorOfParameterFunction function: f data: col. 17 | er errorType: #insensitive. 18 | er quartile: 0.9. 19 | fit := PMErrorMinimizer function: er. 20 | fit evaluate. 21 | self assert: fit maxFunction > 2. 22 | self assert: fit parameters closeTo: #( 2 0.4 ) 23 | ] 24 | -------------------------------------------------------------------------------- /src/Math-Distributions/PMProbabilityDistributionFunction.class.st: -------------------------------------------------------------------------------- 1 | Class { 2 | #name : #PMProbabilityDistributionFunction, 3 | #superclass : #Object, 4 | #instVars : [ 5 | 'probabilityDensity' 6 | ], 7 | #category : #'Math-Distributions' 8 | } 9 | 10 | { #category : #creation } 11 | PMProbabilityDistributionFunction class >> density: aProbabilityDensity [ 12 | ^self new initialize: aProbabilityDensity 13 | ] 14 | 15 | { #category : #initialization } 16 | PMProbabilityDistributionFunction >> initialize: aProbabilityDensity [ 17 | "Private" 18 | probabilityDensity := aProbabilityDensity. 19 | ^self 20 | ] 21 | 22 | { #category : #information } 23 | PMProbabilityDistributionFunction >> value: aNumber [ 24 | ^probabilityDensity distributionValue: aNumber 25 | ] 26 | -------------------------------------------------------------------------------- /src/Math-PrincipalComponentAnalysis/PMDataTransformer.class.st: -------------------------------------------------------------------------------- 1 | " 2 | PMDataTransformer is the abstract root class of transformers. All data transformers should implemen a fit and a method method. 3 | 4 | " 5 | Class { 6 | #name : 'PMDataTransformer', 7 | #superclass : 'Object', 8 | #category : 'Math-PrincipalComponentAnalysis', 9 | #package : 'Math-PrincipalComponentAnalysis' 10 | } 11 | 12 | { #category : 'accessing' } 13 | PMDataTransformer >> fit: aPMMatrix [ 14 | ^ self subclassResponsibility 15 | ] 16 | 17 | { #category : 'accessing' } 18 | PMDataTransformer >> fitAndTransform: aPMMatrix [ 19 | ^ (self fit: aPMMatrix) transform: aPMMatrix 20 | ] 21 | 22 | { #category : 'transforming' } 23 | PMDataTransformer >> transform: aPMMatrix [ 24 | ^ self subclassResponsibility 25 | ] 26 | -------------------------------------------------------------------------------- /src/Math-Numerical/PMBulirschStoerInterpolator.class.st: -------------------------------------------------------------------------------- 1 | Class { 2 | #name : #PMBulirschStoerInterpolator, 3 | #superclass : #PMNevilleInterpolator, 4 | #category : #'Math-Numerical-Math-Interpolator' 5 | } 6 | 7 | { #category : #private } 8 | PMBulirschStoerInterpolator >> computeDifference: aNumber at: anInteger1 order: anInteger2 [ 9 | 10 | | diff ratio | 11 | ratio := ( ( self xPointAt: anInteger1) - aNumber) * ( rightErrors at: anInteger1) 12 | / ( ( self xPointAt: ( anInteger1 + anInteger2)) - aNumber). 13 | diff := ( ( leftErrors at: ( anInteger1 + 1)) - ( rightErrors at: anInteger1)) 14 | / ( ratio - ( leftErrors at: ( anInteger1 + 1))). 15 | rightErrors at: anInteger1 put: ( leftErrors at: ( anInteger1 + 1)) * diff. 16 | leftErrors at: anInteger1 put: ratio * diff 17 | ] 18 | -------------------------------------------------------------------------------- /src/Math-Tests-Numerical/PMExponentialDistributionTest.class.st: -------------------------------------------------------------------------------- 1 | " 2 | A DhbExponentialDistributionTest is a test class for testing the behavior of DhbExponentialDistribution 3 | " 4 | Class { 5 | #name : 'PMExponentialDistributionTest', 6 | #superclass : 'TestCase', 7 | #category : 'Math-Tests-Numerical', 8 | #package : 'Math-Tests-Numerical' 9 | } 10 | 11 | { #category : 'tests' } 12 | PMExponentialDistributionTest >> testSampleMeanConvergesToDistributionMean [ 13 | "testing a random sample seems suspect. We use a 5% interval here" 14 | 15 | | eg arr | 16 | eg := PMExponentialDistribution scale: 10. 17 | arr := Array new: 10000. 18 | (1 to: 10000) do: [ :index | arr at: index put: eg random ]. 19 | self 20 | assert: (arr average between: eg average * 0.95 and: eg average * 1.05) 21 | ] 22 | -------------------------------------------------------------------------------- /.github/workflows/release.yml: -------------------------------------------------------------------------------- 1 | name: Release 2 | 3 | env: 4 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 5 | 6 | on: 7 | release: 8 | types: [created, edited] 9 | 10 | jobs: 11 | build: 12 | strategy: 13 | matrix: 14 | smalltalk: [ Pharo64-9.0, Pharo64-10, Pharo64-11 ] 15 | os: [ macos-latest, windows-latest, ubuntu-latest] 16 | runs-on: ${{ matrix.os }} 17 | name: ${{ matrix.smalltalk }} on ${{ matrix.os }} 18 | env: 19 | PROJECT_NAME: PolyMath-${{ matrix.smalltalk }} 20 | steps: 21 | - uses: actions/checkout@v3 22 | - uses: hpi-swa/setup-smalltalkCI@v1 23 | with: 24 | smalltalk-image: ${{ matrix.smalltalk }} 25 | - run: smalltalkci -s ${{ matrix.smalltalk }} 26 | shell: bash 27 | timeout-minutes: 15 28 | -------------------------------------------------------------------------------- /src/Math-ODE/PMODESystem.class.st: -------------------------------------------------------------------------------- 1 | " 2 | An ODESystem is a wrapper for a system of or a single ordinary differential equation. 3 | " 4 | Class { 5 | #name : #PMODESystem, 6 | #superclass : #Object, 7 | #instVars : [ 8 | 'block' 9 | ], 10 | #category : #'Math-ODE' 11 | } 12 | 13 | { #category : #accessing } 14 | PMODESystem class >> block: aBlock [ 15 | ^ (self new block: aBlock; yourself) 16 | ] 17 | 18 | { #category : #accessing } 19 | PMODESystem >> block [ 20 | ^ block 21 | ] 22 | 23 | { #category : #accessing } 24 | PMODESystem >> block: aBlock [ 25 | "aBlock should be dyadic, the first parameter is x, the second t" 26 | self assert: aBlock argumentCount = 2. 27 | ^ block := aBlock 28 | ] 29 | 30 | { #category : #evaluation } 31 | PMODESystem >> state: aState time: aTime [ 32 | ^ self block value: aState value: aTime 33 | ] 34 | -------------------------------------------------------------------------------- /src/Math-ODE/PMExplicitSolverAnnouncement.class.st: -------------------------------------------------------------------------------- 1 | " 2 | An ExplicitSolverAnnouncement is a record of a step in an explicit system. It contains a time and a state. 3 | " 4 | Class { 5 | #name : #PMExplicitSolverAnnouncement, 6 | #superclass : #Announcement, 7 | #instVars : [ 8 | 't', 9 | 'state' 10 | ], 11 | #category : #'Math-ODE' 12 | } 13 | 14 | { #category : #accessing } 15 | PMExplicitSolverAnnouncement class >> state: aState time: aTime [ 16 | ^ self new state: aState time: aTime; yourself 17 | ] 18 | 19 | { #category : #accessing } 20 | PMExplicitSolverAnnouncement >> state [ 21 | ^ state 22 | ] 23 | 24 | { #category : #accessing } 25 | PMExplicitSolverAnnouncement >> state: aState time: aTime [ 26 | state:= aState. 27 | t := aTime 28 | ] 29 | 30 | { #category : #accessing } 31 | PMExplicitSolverAnnouncement >> time [ 32 | ^ t 33 | ] 34 | -------------------------------------------------------------------------------- /src/Math-ODE/PMImplicitSolverAnnouncement.class.st: -------------------------------------------------------------------------------- 1 | " 2 | An ImplicitSolverAnnouncement is a record of a step in an implicit system. It contains a time and a state. 3 | 4 | " 5 | Class { 6 | #name : #PMImplicitSolverAnnouncement, 7 | #superclass : #Announcement, 8 | #instVars : [ 9 | 't', 10 | 'state' 11 | ], 12 | #category : #'Math-ODE' 13 | } 14 | 15 | { #category : #accessing } 16 | PMImplicitSolverAnnouncement class >> state: aState time: aTime [ 17 | ^ self new state: aState time: aTime; yourself 18 | ] 19 | 20 | { #category : #accessing } 21 | PMImplicitSolverAnnouncement >> state [ 22 | ^ state 23 | ] 24 | 25 | { #category : #accessing } 26 | PMImplicitSolverAnnouncement >> state: aState time: aTime [ 27 | state:= aState. 28 | t := aTime 29 | ] 30 | 31 | { #category : #accessing } 32 | PMImplicitSolverAnnouncement >> time [ 33 | ^ t 34 | ] 35 | -------------------------------------------------------------------------------- /src/Math-Clustering/PMCovarianceCluster.class.st: -------------------------------------------------------------------------------- 1 | Class { 2 | #name : #PMCovarianceCluster, 3 | #superclass : #PMCluster, 4 | #category : #'Math-Clustering' 5 | } 6 | 7 | { #category : #initialization } 8 | PMCovarianceCluster >> centerOn: aVector [ 9 | 10 | accumulator := aVector ifNotNil: [ PMMahalanobisCenter onVector: aVector ] 11 | ] 12 | 13 | { #category : #transformation } 14 | PMCovarianceCluster >> collectAccumulatorResults [ 15 | 16 | accumulator computeParameters 17 | ] 18 | 19 | { #category : #information } 20 | PMCovarianceCluster >> distanceTo: aVector [ 21 | 22 | ^ accumulator distanceTo: aVector 23 | ] 24 | 25 | { #category : #testing } 26 | PMCovarianceCluster >> isUndefined [ 27 | 28 | ^accumulator isNil 29 | ] 30 | 31 | { #category : #printing } 32 | PMCovarianceCluster >> printOn: aStream [ 33 | 34 | accumulator printOn: aStream 35 | ] 36 | -------------------------------------------------------------------------------- /src/Math-Tests-ODE/PMStepperTest.class.st: -------------------------------------------------------------------------------- 1 | " 2 | A StepperTest is a test class for testing the behavior of Stepper 3 | " 4 | Class { 5 | #name : 'PMStepperTest', 6 | #superclass : 'PMAbstractTest', 7 | #category : 'Math-Tests-ODE', 8 | #package : 'Math-Tests-ODE' 9 | } 10 | 11 | { #category : 'tests' } 12 | PMStepperTest >> testOrderIsNilForBaseClass [ 13 | self assert: PMStepper order isNil 14 | ] 15 | 16 | { #category : 'tests' } 17 | PMStepperTest >> testOrderIsNilForInstanceOfBaseClass [ 18 | self assert: PMStepper new order isNil 19 | ] 20 | 21 | { #category : 'tests' } 22 | PMStepperTest >> testSystem [ 23 | | stepper sys | 24 | sys := PMExplicitSystem new. 25 | sys block: [ :x :t | t ]. 26 | stepper := PMStepper onSystem: sys. 27 | self assert: stepper system equals: sys. 28 | sys := PMExplicitSystem new. 29 | self shouldnt: [ stepper system = sys ] 30 | ] 31 | -------------------------------------------------------------------------------- /src/Math-Series/PMContinuedFraction.class.st: -------------------------------------------------------------------------------- 1 | Class { 2 | #name : #PMContinuedFraction, 3 | #superclass : #PMInfiniteSeries, 4 | #instVars : [ 5 | 'numerator', 6 | 'denominator' 7 | ], 8 | #category : 'Math-Series' 9 | } 10 | 11 | { #category : #operation } 12 | PMContinuedFraction >> evaluateIteration [ 13 | "Perform one iteration." 14 | | terms delta | 15 | terms := termServer termsAt: iterations. 16 | denominator := 1 / ( self limitedSmallValue: ( (terms at: 1) * denominator + (terms at: 2))). 17 | numerator := self limitedSmallValue: ( (terms at: 1) / numerator + (terms at: 2)). 18 | delta := numerator * denominator. 19 | result := result * delta. 20 | ^( delta - 1) abs 21 | ] 22 | 23 | { #category : #operation } 24 | PMContinuedFraction >> initializeIterations [ 25 | "Initialize the series." 26 | 27 | numerator := self limitedSmallValue: termServer initialTerm. 28 | denominator := 0. 29 | result := numerator 30 | ] 31 | -------------------------------------------------------------------------------- /src/Math-KDTree/PMStupidNN.class.st: -------------------------------------------------------------------------------- 1 | " 2 | `PMStupidNN` is a naive nearest neighbour search. `PMKDTree` is much faster though, if you have to do several searches. `PMStupidNN` exists only for the `PMKDTreeTest`s (and as an example, how simple it is to subclass `PMNNStore`). 3 | " 4 | Class { 5 | #name : #PMStupidNN, 6 | #superclass : #PMNNStore, 7 | #type : #variable, 8 | #category : #'Math-KDTree' 9 | } 10 | 11 | { #category : #evaluating } 12 | PMStupidNN >> nnSearch: aCollection i:anInt [ 13 | "search for i nearest neighbours of vector aCollection " 14 | |d| 15 | self sortFor: aCollection asFloatArray. 16 | d :=self data . 17 | anInt =1 ifTrue:[^d first ]. 18 | ^d size >anInt ifTrue: [d copyFrom: 1 to: anInt]ifFalse: [d] 19 | ] 20 | 21 | { #category : #sorting } 22 | PMStupidNN >> sortFor: aCollection [ 23 | "sort nearest neighbours of aCollection " 24 | |i| 25 | self do:[:e| i :=aCollection -(e at:2) . e at: 1 put: (i*i)sum ]; 26 | sort 27 | ] 28 | -------------------------------------------------------------------------------- /src/Math-ODE/PMHeunStepper.class.st: -------------------------------------------------------------------------------- 1 | " 2 | Heun's method may refer to the improved or modified Euler's method (that is, the explicit trapezoidal rule), or a similar two-stage Runge-Kutta method. 3 | 4 | " 5 | Class { 6 | #name : #PMHeunStepper, 7 | #superclass : #PMExplicitStepper, 8 | #category : #'Math-ODE' 9 | } 10 | 11 | { #category : #accessing } 12 | PMHeunStepper class >> order [ 13 | "Heun's method is a second order method." 14 | ^ 2 15 | ] 16 | 17 | { #category : #stepping } 18 | PMHeunStepper >> doStep: aState time: t [ 19 | 20 | "This method should take one step from inState at time t of size dt, and modify the state, then answer it. " 21 | 22 | | xi ti | 23 | self stepSize ifNil: [ self error: 'step size required by stepper' ]. 24 | ti := t + self stepSize. 25 | xi := aState + (self stepSize * (system state: aState time: t)). 26 | ^ aState + (self stepSize / 2 27 | * ((system state: aState time: t) + (system state: xi time: ti))) 28 | ] 29 | -------------------------------------------------------------------------------- /src/Math-ODE/PMImplicitMidpointStepper.class.st: -------------------------------------------------------------------------------- 1 | " 2 | The implicit midpoint method is equavelent to the so-called 2nd order Gauss method. 3 | 4 | " 5 | Class { 6 | #name : #PMImplicitMidpointStepper, 7 | #superclass : #PMImplicitStepper, 8 | #category : #'Math-ODE' 9 | } 10 | 11 | { #category : #accessing } 12 | PMImplicitMidpointStepper class >> order [ 13 | "Implicit Midpoint is a second order method." 14 | ^ 2 15 | ] 16 | 17 | { #category : #stepping } 18 | PMImplicitMidpointStepper >> doStep: aState time: t [ 19 | 20 | "This method should take one step from inState at time t of size dt, and modify the state, then answer it. " 21 | 22 | | xi1 xi2 ti | 23 | self stepSize ifNil: [ self error: 'step size required by stepper' ]. 24 | 25 | ti := t + (1 / 2 * self stepSize). 26 | xi1 := aState + (self stepSize * (system state: aState time: t)). 27 | xi2 := 1 / 2 * (aState + (system state: xi1 time: ti)). 28 | ^ aState + (self stepSize * (system state: xi2 time: ti)) 29 | ] 30 | -------------------------------------------------------------------------------- /src/Math-Distributions/PMIncompleteBetaFractionTermServer.class.st: -------------------------------------------------------------------------------- 1 | Class { 2 | #name : #PMIncompleteBetaFractionTermServer, 3 | #superclass : #PMSeriesTermServer, 4 | #instVars : [ 5 | 'alpha1', 6 | 'alpha2' 7 | ], 8 | #category : #'Math-Distributions-Beta' 9 | } 10 | 11 | { #category : #information } 12 | PMIncompleteBetaFractionTermServer >> initialTerm [ 13 | ^1 14 | ] 15 | 16 | { #category : #initialization } 17 | PMIncompleteBetaFractionTermServer >> setParameter: aNumber1 second: aNumber2 [ 18 | alpha1 := aNumber1. 19 | alpha2 := aNumber2 20 | ] 21 | 22 | { #category : #information } 23 | PMIncompleteBetaFractionTermServer >> termsAt: anInteger [ 24 | | n n2 | 25 | n := anInteger // 2. 26 | n2 := 2 * n. 27 | ^ Array 28 | with: 29 | (n2 < anInteger 30 | ifTrue: 31 | [ x negated * (alpha1 + n) * (alpha1 + alpha2 + n) / ((alpha1 + n2) * (alpha1 + 1 + n2)) ] 32 | ifFalse: [ x * n * (alpha2 - n) / ((alpha1 + n2) * (alpha1 - 1 + n2)) ]) 33 | with: 1 34 | ] 35 | -------------------------------------------------------------------------------- /src/Math-Series/PMInfiniteSeries.class.st: -------------------------------------------------------------------------------- 1 | Class { 2 | #name : #PMInfiniteSeries, 3 | #superclass : #PMIterativeProcess, 4 | #instVars : [ 5 | 'termServer' 6 | ], 7 | #category : 'Math-Series' 8 | } 9 | 10 | { #category : #creation } 11 | PMInfiniteSeries class >> server: aTermServer [ 12 | ^ self new initialize: aTermServer 13 | ] 14 | 15 | { #category : #operation } 16 | PMInfiniteSeries >> evaluateIteration [ 17 | "Perform one iteration." 18 | 19 | | delta | 20 | delta := termServer termAt: iterations. 21 | result := result + delta. 22 | ^ self precisionOf: delta abs relativeTo: result abs 23 | ] 24 | 25 | { #category : #initialization } 26 | PMInfiniteSeries >> initialize: aTermServer [ 27 | "Private - Assigns the object responsible to compute each term." 28 | 29 | termServer := aTermServer. 30 | ^ self 31 | ] 32 | 33 | { #category : #operation } 34 | PMInfiniteSeries >> initializeIterations [ 35 | "Initialize the series." 36 | 37 | result := termServer initialTerm 38 | ] 39 | -------------------------------------------------------------------------------- /src/Math-Clustering/PMEuclideanCluster.class.st: -------------------------------------------------------------------------------- 1 | Class { 2 | #name : #PMEuclideanCluster, 3 | #superclass : #PMCluster, 4 | #instVars : [ 5 | 'center' 6 | ], 7 | #category : #'Math-Clustering' 8 | } 9 | 10 | { #category : #initialization } 11 | PMEuclideanCluster >> centerOn: aVector [ 12 | 13 | center := aVector. 14 | accumulator := PMVectorAccumulator new: (aVector ifNil:[0]ifNotNil:[ aVector size ]) 15 | ] 16 | 17 | { #category : #transformation } 18 | PMEuclideanCluster >> collectAccumulatorResults [ 19 | 20 | center := accumulator average copy 21 | ] 22 | 23 | { #category : #information } 24 | PMEuclideanCluster >> distanceTo: aVector [ 25 | 26 | ^(aVector - center) norm 27 | ] 28 | 29 | { #category : #testing } 30 | PMEuclideanCluster >> isUndefined [ 31 | 32 | ^ center isNil 33 | ] 34 | 35 | { #category : #printing } 36 | PMEuclideanCluster >> printOn: aStream [ 37 | 38 | accumulator count printOn: aStream. 39 | aStream nextPutAll: ': '. 40 | center printOn: aStream 41 | ] 42 | -------------------------------------------------------------------------------- /src/Math-PrincipalComponentAnalysis/PMPrincipalComponentAnalyser.class.st: -------------------------------------------------------------------------------- 1 | Class { 2 | #name : 'PMPrincipalComponentAnalyser', 3 | #superclass : 'Object', 4 | #instVars : [ 5 | 'componentsNumber' 6 | ], 7 | #category : 'Math-PrincipalComponentAnalysis', 8 | #package : 'Math-PrincipalComponentAnalysis' 9 | } 10 | 11 | { #category : 'accessing' } 12 | PMPrincipalComponentAnalyser >> componentsNumber [ 13 | ^ componentsNumber 14 | ] 15 | 16 | { #category : 'accessing' } 17 | PMPrincipalComponentAnalyser >> componentsNumber: anInteger [ 18 | componentsNumber := anInteger 19 | ] 20 | 21 | { #category : 'accessing' } 22 | PMPrincipalComponentAnalyser >> fit: aPMMatrix [ 23 | ^ self subclassResponsibility 24 | ] 25 | 26 | { #category : 'accessing' } 27 | PMPrincipalComponentAnalyser >> fitAndTransform: aPMMatrix [ 28 | ^ (self fit: aPMMatrix) transform: aPMMatrix 29 | ] 30 | 31 | { #category : 'accessing' } 32 | PMPrincipalComponentAnalyser >> transform: aPMMatrix [ 33 | ^ self subclassResponsibility 34 | ] 35 | -------------------------------------------------------------------------------- /src/Math-Numerical/PMVectorProjectedFunction.class.st: -------------------------------------------------------------------------------- 1 | Class { 2 | #name : #PMVectorProjectedFunction, 3 | #superclass : #PMProjectedOneVariableFunction, 4 | #category : #'Math-Numerical' 5 | } 6 | 7 | { #category : #information } 8 | PMVectorProjectedFunction >> argumentWith: aNumber [ 9 | 10 | ^aNumber * self direction + self origin 11 | ] 12 | 13 | { #category : #information } 14 | PMVectorProjectedFunction >> direction [ 15 | ^index 16 | ] 17 | 18 | { #category : #initialization } 19 | PMVectorProjectedFunction >> direction: aVector [ 20 | index := aVector 21 | ] 22 | 23 | { #category : #information } 24 | PMVectorProjectedFunction >> origin [ 25 | ^argument 26 | ] 27 | 28 | { #category : #initialization } 29 | PMVectorProjectedFunction >> origin: aVector [ 30 | argument := aVector 31 | ] 32 | 33 | { #category : #display } 34 | PMVectorProjectedFunction >> printOn: aStream [ 35 | self origin printOn: aStream. 36 | aStream nextPutAll: ' ('. 37 | self direction printOn: aStream. 38 | aStream nextPut: $) 39 | ] 40 | -------------------------------------------------------------------------------- /src/Math-Tests-ODE/PMImplicitMidpointSolverTest.class.st: -------------------------------------------------------------------------------- 1 | Class { 2 | #name : 'PMImplicitMidpointSolverTest', 3 | #superclass : 'PMAbstractTest', 4 | #category : 'Math-Tests-ODE', 5 | #package : 'Math-Tests-ODE' 6 | } 7 | 8 | { #category : 'tests-solving' } 9 | PMImplicitMidpointSolverTest >> testSimpleSystem2 [ 10 | 11 | | solver stepper system dt | 12 | dt := 1.5. 13 | system := PMImplicitSystem block: [ :x :t | 3 * t negated exp - (0.4 * x) ]. 14 | stepper := PMImplicitMidpointStepper onSystem: system. 15 | solver := PMImplicitMidpointSolver new 16 | stepper: stepper; 17 | system: system; 18 | dt: dt. 19 | self should: ((solver solve: system 20 | startState: 5 21 | startTime: 0 22 | endTime: 3) closeTo: 4.9733 precision: 0.0001). 23 | self should: ((solver solve: system 24 | startState: 0 25 | startTime: 1 26 | endTime: 2.5) closeTo: 0.8242 precision: 0.0001) 27 | ] 28 | -------------------------------------------------------------------------------- /src/Math-Quaternion/Number.extension.st: -------------------------------------------------------------------------------- 1 | Extension { #name : #Number } 2 | 3 | { #category : #'*Math-Quaternion' } 4 | Number >> adaptToQuaternion: rcvr andSend: selector [ 5 | "If I am involved in arithmetic with a Quaternion, convert me to a Quaternion." 6 | ^ rcvr perform: selector with: self asQuaternion 7 | ] 8 | 9 | { #category : #'*Math-Quaternion' } 10 | Number >> asQuaternion [ 11 | ^PMQuaternion 12 | qr: self 13 | qi: 0 14 | qj: 0 15 | qk: 0 16 | ] 17 | 18 | { #category : #'*Math-Quaternion' } 19 | Number >> i: qi j: qj k: qk [ 20 | ^PMQuaternion 21 | qr: self 22 | qi: qi 23 | qj: qj 24 | qk: qk 25 | ] 26 | 27 | { #category : #'*Math-Quaternion' } 28 | Number >> j [ 29 | ^PMQuaternion 30 | qr: 0 31 | qi: 0 32 | qj: self 33 | qk: 0 34 | ] 35 | 36 | { #category : #'*Math-Quaternion' } 37 | Number >> k [ 38 | ^PMQuaternion 39 | qr: 0 40 | qi: 0 41 | qj: 0 42 | qk: self 43 | ] 44 | 45 | { #category : #'*Math-Quaternion' } 46 | Number >> multiplyByQuaternion: quaternion [ 47 | ^ quaternion timesNumber: self 48 | ] 49 | -------------------------------------------------------------------------------- /src/Math-Quaternion/PMComplexNumber.extension.st: -------------------------------------------------------------------------------- 1 | Extension { #name : #PMComplexNumber } 2 | 3 | { #category : #'*Math-Quaternion' } 4 | PMComplexNumber >> adaptToQuaternion: rcvr andSend: selector [ 5 | "If I am involved in arithmetic with a Quaternion, convert me to a Quaternion." 6 | ^ rcvr perform: selector with: self asQuaternion 7 | ] 8 | 9 | { #category : #'*Math-Quaternion' } 10 | PMComplexNumber >> asQuaternion [ 11 | ^ PMQuaternion 12 | qr: real 13 | qi: imaginary 14 | qj: 0 15 | qk: 0 16 | ] 17 | 18 | { #category : #'*Math-Quaternion' } 19 | PMComplexNumber >> j [ 20 | "same as self * 1 j" 21 | 22 | ^PMQuaternion 23 | qr: 0 24 | qi: 0 25 | qj: real 26 | qk: imaginary 27 | ] 28 | 29 | { #category : #'*Math-Quaternion' } 30 | PMComplexNumber >> k [ 31 | "same as self * 1 k" 32 | 33 | ^PMQuaternion 34 | qr: 0 35 | qi: 0 36 | qj: imaginary negated 37 | qk: real 38 | ] 39 | 40 | { #category : #'*Math-Quaternion' } 41 | PMComplexNumber >> multiplyByQuaternion: quaternion [ 42 | 43 | ^ quaternion timesComplexNumber: self 44 | ] 45 | -------------------------------------------------------------------------------- /src/Math-Polynomials/PMEstimatedPolynomial.class.st: -------------------------------------------------------------------------------- 1 | Class { 2 | #name : #PMEstimatedPolynomial, 3 | #superclass : #PMPolynomial, 4 | #instVars : [ 5 | 'errorMatrix' 6 | ], 7 | #category : #'Math-Polynomials' 8 | } 9 | 10 | { #category : #information } 11 | PMEstimatedPolynomial >> error: aNumber [ 12 | "Compute the error on the value of the receiver for argument aNumber." 13 | | errorVector term nextTerm | 14 | nextTerm := 1. 15 | errorVector := ( coefficients collect: [ :each | term := nextTerm. nextTerm := aNumber * nextTerm. term]) asPMVector. 16 | ^( errorVector * errorMatrix * errorVector) sqrt 17 | ] 18 | 19 | { #category : #information } 20 | PMEstimatedPolynomial >> errorMatrix [ 21 | ^errorMatrix 22 | ] 23 | 24 | { #category : #initialization } 25 | PMEstimatedPolynomial >> errorMatrix: aMatrix [ 26 | "Defines the error matrix of the receiver." 27 | errorMatrix := aMatrix 28 | ] 29 | 30 | { #category : #information } 31 | PMEstimatedPolynomial >> valueAndError: aNumber [ 32 | ^ Array with: (self value: aNumber) with: (self error: aNumber) 33 | ] 34 | -------------------------------------------------------------------------------- /src/Math-Tests-Numerical/IntegerTest.extension.st: -------------------------------------------------------------------------------- 1 | Extension { #name : 'IntegerTest' } 2 | 3 | { #category : '*Math-Tests-Numerical' } 4 | IntegerTest >> testFindNK [ 5 | 6 | self assert: 6 inverseBinomialCoefficient equals: #(6 1 6 5 4 2). 7 | self assert: 10 inverseBinomialCoefficient equals: #(10 1 10 9 5 2 5 3). 8 | self assert: 20 inverseBinomialCoefficient equals: #(20 1 20 19 6 3). 9 | self assert: 21 inverseBinomialCoefficient equals: #(21 1 21 20 7 2 7 5). 10 | self assert: 55 inverseBinomialCoefficient equals: #(55 1 55 54 11 2 11 9). 11 | self assert: 120 inverseBinomialCoefficient equals: #(120 1 120 119 16 2 16 14 10 3 10 7). 12 | self assert: 3003 inverseBinomialCoefficient equals: #(3003 1 3003 3002 78 2 78 76 15 5 15 10 14 6 14 8). 13 | self assert: 8966473191018617158916954970192684 inverseBinomialCoefficient equals: #(8966473191018617158916954970192684 1 8966473191018617158916954970192684 8966473191018617158916954970192683 123 45 123 78). 14 | 15 | self should: [ 1 inverseBinomialCoefficient ] raise: Error. 16 | self should: [ 0 inverseBinomialCoefficient ] raise: Error 17 | ] 18 | -------------------------------------------------------------------------------- /src/Math-Benchmarks-ODE/PMImplicitBenchmark.class.st: -------------------------------------------------------------------------------- 1 | Class { 2 | #name : #PMImplicitBenchmark, 3 | #superclass : #PMODEBenchmark, 4 | #instVars : [ 5 | 'system' 6 | ], 7 | #category : #'Math-Benchmarks-ODE' 8 | } 9 | 10 | { #category : #benchmarking } 11 | PMImplicitBenchmark >> benchBeckwardEuler [ 12 | | solver stepper | 13 | stepper := PMImplicitStepper onSystem: system. 14 | solver := (PMImplicitSolver new) stepper: stepper; system: system; dt: dt. 15 | 1 to: self problemSize do: [ :i 16 | |solver solve: system startState: startState startTime:startTime endTime: endTime] 17 | ] 18 | 19 | { #category : #benchmarking } 20 | PMImplicitBenchmark >> benchImplicitMidpoint [ 21 | | solver stepper | 22 | stepper := PMImplicitMidpointStepper onSystem: system. 23 | solver := (PMImplicitMidpointSolver new) stepper: stepper; system: system; dt: dt. 24 | 1 to: self problemSize do: [ :i 25 | |solver solve: system startState: startState startTime:startTime endTime: endTime] 26 | ] 27 | 28 | { #category : #running } 29 | PMImplicitBenchmark >> setUp [ 30 | super setUp. 31 | system := PMImplicitSystem block: function 32 | ] 33 | -------------------------------------------------------------------------------- /src/Math-UtilsDataServer/PMAbstractDataServer.class.st: -------------------------------------------------------------------------------- 1 | " 2 | I act as an interface for potential data server. 3 | My subclasses should implement the following methods: atEnd, close, next, open, reset. 4 | " 5 | Class { 6 | #name : #PMAbstractDataServer, 7 | #superclass : #Object, 8 | #category : 'Math-UtilsDataServer' 9 | } 10 | 11 | { #category : #information } 12 | PMAbstractDataServer >> atEnd [ 13 | "Answers true if there is no more data element." 14 | self subclassResponsibility 15 | ] 16 | 17 | { #category : #operation } 18 | PMAbstractDataServer >> close [ 19 | "Close the data stream (must be implemented by subclass)." 20 | ] 21 | 22 | { #category : #operation } 23 | PMAbstractDataServer >> next [ 24 | "Answers the next element on the stream." 25 | self subclassResponsibility 26 | ] 27 | 28 | { #category : #operation } 29 | PMAbstractDataServer >> open [ 30 | "Open the data stream (must be implemented by subclass)." 31 | self subclassResponsibility 32 | ] 33 | 34 | { #category : #operation } 35 | PMAbstractDataServer >> reset [ 36 | "Reset the position of the data stream to the beginning." 37 | self subclassResponsibility 38 | ] 39 | -------------------------------------------------------------------------------- /src/Math-ODE/PMExplicitSystem.class.st: -------------------------------------------------------------------------------- 1 | " 2 | Rather than passing dxdt as a reference to be modified, we will ask the system x:t: and expect reply dxdt. This may be a single value or a vector of values. 3 | 4 | From ODEINT-V2: 5 | System functions 6 | 7 | Up to now, we have nothing said about the system function. This function depends on the stepper. For the explicit Runge-Kutta steppers this function can be a simple callable object hence a simple (global) C-function or a functor. The parameter syntax is sys( x , dxdt , t ) and it is assumed that it calculates dx/dt = f(x,t). 8 | 9 | Other types of system function represent Hamiltonian systems or system which also compute the Jacobian needed in implicit steppers. For informations which stepper uses which system function see the stepper table below. It might be possible, that odeint will introduce new system types in near future. Since the system function is strongly related to the stepper type, such an introduction of a new stepper might result in a new type of system function. 10 | 11 | " 12 | Class { 13 | #name : #PMExplicitSystem, 14 | #superclass : #PMODESystem, 15 | #category : #'Math-ODE' 16 | } 17 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2012-2016 PolyMath community 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /src/Math-ODE/PMBDF2Stepper.class.st: -------------------------------------------------------------------------------- 1 | " 2 | It is stepper for Backward differentiation formulas method of order 2. We can't use BDF2 method until we have old solution value and approximate new one. A BDF2 method is implicit. 3 | 4 | 5 | 6 | " 7 | Class { 8 | #name : #PMBDF2Stepper, 9 | #superclass : #PMImplicitMultiStepper, 10 | #category : #'Math-ODE' 11 | } 12 | 13 | { #category : #accessing } 14 | PMBDF2Stepper class >> order [ 15 | "BDF2 is a second order method." 16 | ^ 2 17 | ] 18 | 19 | { #category : #stepping } 20 | PMBDF2Stepper >> doStep: aState prevState: prevState time: t [ 21 | 22 | | approximateState | 23 | self stepSize ifNil: [ self error: 'step size required by stepper' ]. 24 | approximateState := (system state: aState time: t) * self stepSize 25 | + aState. 26 | ^ 4 / 3 * aState - (1 / 3 * prevState) + (2 / 3 * self stepSize 27 | * (system state: approximateState time: t + self stepSize)) 28 | ] 29 | 30 | { #category : #stepping } 31 | PMBDF2Stepper >> doStep: aState prevState: prevState time: t stepSize: timeStep [ 32 | self stepSize: timeStep. 33 | ^ self doStep: aState prevState: prevState time: t 34 | ] 35 | -------------------------------------------------------------------------------- /src/Math-Tests-PrincipalComponentAnalysis/PMPCASingularValueDecompositionTest.class.st: -------------------------------------------------------------------------------- 1 | " 2 | This test checks that the SVD transform based PCA meets the acceptance requirements defined in PMPrincipalComponentAnalysisTest 3 | " 4 | Class { 5 | #name : 'PMPCASingularValueDecompositionTest', 6 | #superclass : 'PMPrincipalComponentAnalyserTest', 7 | #category : 'Math-Tests-PrincipalComponentAnalysis', 8 | #package : 'Math-Tests-PrincipalComponentAnalysis' 9 | } 10 | 11 | { #category : 'running' } 12 | PMPCASingularValueDecompositionTest >> setUp [ 13 | 14 | super setUp. 15 | pca := PMPrincipalComponentAnalyserSVD new componentsNumber: 2 16 | ] 17 | 18 | { #category : 'scikit-learn-example' } 19 | PMPCASingularValueDecompositionTest >> testPCAwithPCAandJacobiTransformationReturnSame [ 20 | | m pca1 pca2 | 21 | m := PMMatrix rows: #(#(-1 -1) #(-2 -1) #(-3 -2) #(1 1) #(2 1) #(3 2)). 22 | pca1 := PMPrincipalComponentAnalyserSVD new componentsNumber: 2. 23 | pca1 fit: m. 24 | pca2 := PMPrincipalComponentAnalyserJacobiTransformation new componentsNumber: 2. 25 | pca2 fit: m. 26 | 27 | self assert: pca1 transformMatrix abs closeTo: pca2 transformMatrix abs 28 | ] 29 | -------------------------------------------------------------------------------- /src/Math-ArbitraryPrecisionFloat/Number.extension.st: -------------------------------------------------------------------------------- 1 | Extension { #name : #Number } 2 | 3 | { #category : #'*Math-ArbitraryPrecisionFloat' } 4 | Number >> adaptToArbitraryPrecisionFloat: rcvr andCompare: selector [ 5 | "If I am involved in comparison with a Float, convert rcvr to a 6 | Fraction. This way, no bit is lost and comparison is exact." 7 | 8 | ^ rcvr asTrueFraction perform: selector with: self 9 | ] 10 | 11 | { #category : #'*Math-ArbitraryPrecisionFloat' } 12 | Number >> adaptToArbitraryPrecisionFloat: rcvr andSend: selector [ 13 | "If I am involved in arithmetic with a Float, convert me to a Float." 14 | ^ (self isInfinite or: [ self isNaN ]) 15 | ifTrue: [ rcvr asFloat perform: selector with: self ] 16 | ifFalse: [ rcvr perform: selector with: (self asArbitraryPrecisionFloatNumBits: rcvr numBits) ] 17 | ] 18 | 19 | { #category : #'*Math-ArbitraryPrecisionFloat' } 20 | Number >> asArbitraryPrecisionFloatNumBits: n [ 21 | self subclassResponsibility 22 | ] 23 | 24 | { #category : #'*Math-ArbitraryPrecisionFloat' } 25 | Number >> asArbitraryPrecisionFloatNumDecimalDigits: n [ 26 | ^ self asArbitraryPrecisionFloatNumBits: (n / (2 log: 10)) ceiling 27 | ] 28 | -------------------------------------------------------------------------------- /src/Math-ODE/PMImplicitMidpointSolver.class.st: -------------------------------------------------------------------------------- 1 | " 2 | The implicit midpoint method is equavelent to the so-called 2nd order Gauss method. 3 | 4 | " 5 | Class { 6 | #name : #PMImplicitMidpointSolver, 7 | #superclass : #PMImplicitSolver, 8 | #category : #'Math-ODE' 9 | } 10 | 11 | { #category : #private } 12 | PMImplicitMidpointSolver class >> stepperClass [ 13 | ^ PMImplicitMidpointStepper 14 | ] 15 | 16 | { #category : #solving } 17 | PMImplicitMidpointSolver >> solve: aSystem startState: initialState startTime: initialTime endTime: endTime [ 18 | 19 | self system: aSystem. 20 | stepper ifNil: [ 21 | self stepper: ((self stepperClass) onSystem: self system)]. 22 | state := initialState. 23 | 24 | "announce initial conditions" 25 | self announceState: state time: initialTime. 26 | 27 | 28 | "step until the end" 29 | 30 | state := self mainStepsState: state startTime: initialTime endTime: endTime. 31 | 32 | "sanity check" 33 | self assert: [(lastTime between: initialTime and: endTime) 34 | or: [lastTime between: endTime and: initialTime]]. 35 | 36 | "take another step if needed" 37 | state := self lastStepState: state endTime: endTime. 38 | 39 | ^ state 40 | ] 41 | -------------------------------------------------------------------------------- /src/Math-StatisticalMoments/PMFixedStatisticalMoments.class.st: -------------------------------------------------------------------------------- 1 | Class { 2 | #name : #PMFixedStatisticalMoments, 3 | #superclass : #PMStatisticalMoments, 4 | #category : #'Math-StatisticalMoments' 5 | } 6 | 7 | { #category : #creation } 8 | PMFixedStatisticalMoments class >> new [ 9 | ^super new: 4 10 | ] 11 | 12 | { #category : #creation } 13 | PMFixedStatisticalMoments class >> new: anInteger [ 14 | ^self error: 'Illegal creation message for this class' 15 | ] 16 | 17 | { #category : #transformation } 18 | PMFixedStatisticalMoments >> accumulate: aNumber [ 19 | | correction n n1 c2 c3 | 20 | n := moments at: 1. 21 | n1 := n + 1. 22 | correction := ((moments at: 2) - aNumber) / n1. 23 | c2 := correction squared. 24 | c3 := c2 * correction. 25 | moments 26 | at: 5 27 | put: ((moments at: 5) + ((moments at: 4) * correction * 4) 28 | + ((moments at: 3) * c2 * 6) + (c2 squared * (n squared * n + 1))) 29 | * n / n1; 30 | at: 4 31 | put: ((moments at: 4) + ((moments at: 3) * correction * 3) 32 | + (c3 * (1 - n squared))) * n 33 | / n1; 34 | at: 3 put: ((moments at: 3) + (c2 * (1 + n))) * n / n1; 35 | at: 2 put: (moments at: 2) - correction; 36 | at: 1 put: n1 37 | ] 38 | -------------------------------------------------------------------------------- /src/Math-FunctionFit/PMErrorMinimizer.class.st: -------------------------------------------------------------------------------- 1 | " 2 | ErrorMinimizer fits a function to some data using an ErrorOfParameterFunction . It is internally used by GeneralFunctionFit . It is generally better to use GeneralFunctionFit . 3 | f:=[:x :a :b|a*x / (b+x)]. 4 | col:=(1 to: 20)collect: [:i|i@(f cull: i cull: 2 cull: 0.4) ]. 5 | er:=ErrorOfParameterFunction function: f data: col. 6 | er errorType: #median. 7 | fit:= ErrorMinimizer function: er. 8 | fit evaluate . 9 | fit parameters. --> #(2.0 0.39999999999903596) 10 | " 11 | Class { 12 | #name : #PMErrorMinimizer, 13 | #superclass : #PMFunctionFit, 14 | #category : #'Math-FunctionFit' 15 | } 16 | 17 | { #category : #creation } 18 | PMErrorMinimizer class >> function: anErrorOfParameterFunction [ 19 | |f d| 20 | f := PMErrorAsParameterFunction function: anErrorOfParameterFunction. 21 | d :=( 1 to:( f maxFunction ))collect: [:i| i@0]. 22 | ^self new initialize: d data: f 23 | ] 24 | 25 | { #category : #creation } 26 | PMErrorMinimizer class >> function: aBlock data: aCollection [ 27 | ^self shouldNotImplement 28 | ] 29 | 30 | { #category : #accessing } 31 | PMErrorMinimizer >> maxFunction [ 32 | "The number of data partitions used." 33 | 34 | ^ dataHolder size 35 | ] 36 | -------------------------------------------------------------------------------- /src/Math-ODE/PMMidpointStepper.class.st: -------------------------------------------------------------------------------- 1 | " 2 | The midpoint method is also known as the modified Euler method or RK2. It is an explicit method. 3 | 4 | " 5 | Class { 6 | #name : #PMMidpointStepper, 7 | #superclass : #PMExplicitStepper, 8 | #category : #'Math-ODE' 9 | } 10 | 11 | { #category : #accessing } 12 | PMMidpointStepper class >> order [ 13 | "Midpoint is a second order method." 14 | ^ 2 15 | ] 16 | 17 | { #category : #stepping } 18 | PMMidpointStepper >> doStep: aState time: t [ 19 | 20 | "This method should take one step from inState at time t of size dt, and modify the state, then answer it." 21 | 22 | | k1 k2 | 23 | self stepSize ifNil: [ self error: 'step size required by stepper' ]. 24 | k1 := system state: aState time: t. 25 | k2 := system 26 | state: aState + (self stepSize * (1 / 2) * k1) 27 | time: t + (self stepSize * (1 / 2)). 28 | ^ aState + (k2 * self stepSize) 29 | ] 30 | 31 | { #category : #stepping } 32 | PMMidpointStepper >> lastStep: aState time: t stepSize: timeStep deltaT: incrementOfTime [ 33 | "This method should take one step from inState at time t of size dt, and modify the state, then answer it." 34 | 35 | self stepSize: timeStep. 36 | ^ self doStep: aState time: t 37 | ] 38 | -------------------------------------------------------------------------------- /src/Math-ODE/PMStateTime.class.st: -------------------------------------------------------------------------------- 1 | " 2 | A StateTime class is a generalization of point. It holds both a state and a time. 3 | 4 | We don't want to use Point, since state may be a vector quantity, and the behavior of array @ number is a little off (it stores points in an array, what we want is the array itself in state, and the scalar quantity in time). 5 | " 6 | Class { 7 | #name : #PMStateTime, 8 | #superclass : #Object, 9 | #instVars : [ 10 | 'state', 11 | 'time' 12 | ], 13 | #category : #'Math-ODE' 14 | } 15 | 16 | { #category : #'instance creation' } 17 | PMStateTime class >> state: aState time: aTime [ 18 | ^ self new 19 | state: aState; 20 | time: aTime 21 | ] 22 | 23 | { #category : #printing } 24 | PMStateTime >> printOn: aStream [ 25 | "used for inspector. Using the point analogy" 26 | state printOn: aStream. 27 | aStream nextPut: $@. 28 | time printOn: aStream 29 | ] 30 | 31 | { #category : #accessing } 32 | PMStateTime >> state [ 33 | ^ state 34 | ] 35 | 36 | { #category : #accessing } 37 | PMStateTime >> state: anObject [ 38 | state := anObject 39 | ] 40 | 41 | { #category : #accessing } 42 | PMStateTime >> time [ 43 | ^ time 44 | ] 45 | 46 | { #category : #accessing } 47 | PMStateTime >> time: anObject [ 48 | time := anObject 49 | ] 50 | -------------------------------------------------------------------------------- /src/Math-ODE/PMBDF3Stepper.class.st: -------------------------------------------------------------------------------- 1 | " 2 | It is stepper for Backward differentiation formulas method of order 3. 3 | We can't use BDF3 method until we have two old solution values and approximate new one. A BDF3 method is implicit. 4 | 5 | 6 | " 7 | Class { 8 | #name : #PMBDF3Stepper, 9 | #superclass : #PMImplicitMultiStepper, 10 | #category : #'Math-ODE' 11 | } 12 | 13 | { #category : #accessing } 14 | PMBDF3Stepper class >> order [ 15 | "BDF3 is a third order method." 16 | ^ 3 17 | ] 18 | 19 | { #category : #stepping } 20 | PMBDF3Stepper >> doStep: aState prevState: prevState prevPrevState: prevPrevState time: t [ 21 | 22 | | approximateState | 23 | self stepSize ifNil: [ self error: 'step size required by stepper' ]. 24 | approximateState := (system state: aState time: t) * self stepSize 25 | + aState. 26 | ^ 18 / 11 * aState - (9 / 11 * prevState) + (2 / 11 * prevPrevState) 27 | + (6 / 11 * self stepSize 28 | * (system state: approximateState time: t + self stepSize)) 29 | ] 30 | 31 | { #category : #stepping } 32 | PMBDF3Stepper >> doStep: aState prevState: prevState prevPrevState: prevPrevState time: t stepSize: timeStep [ 33 | self stepSize: timeStep. 34 | ^ self doStep: aState prevState: prevState prevPrevState: prevPrevState time: t 35 | ] 36 | -------------------------------------------------------------------------------- /src/Math-AutomaticDifferenciation/PMGradient.class.st: -------------------------------------------------------------------------------- 1 | " 2 | Computes the gradient of a function of a Collection of Numbers. 3 | 4 | Example: f(x,y)=x^2 * y 5 | g := PMGradient of:[:x|x first squared * x second]. 6 | g value:#(3 2). ""-->#(12 9)"" 7 | g value:#(1 1). ""-->#(2 1)"" 8 | 9 | " 10 | Class { 11 | #name : #PMGradient, 12 | #superclass : #Object, 13 | #instVars : [ 14 | 'function' 15 | ], 16 | #category : #'Math-AutomaticDifferenciation' 17 | } 18 | 19 | { #category : #'instance creation' } 20 | PMGradient class >> of: aNumericalBlock [ 21 | ^self new function: aNumericalBlock 22 | ] 23 | 24 | { #category : #accessing } 25 | PMGradient >> function: aBlock [ 26 | "aBlock must accept an Array as argument" 27 | function := aBlock 28 | ] 29 | 30 | { #category : #printing } 31 | PMGradient >> printOn: aStream [ 32 | aStream 33 | nextPutAll: self class name; 34 | nextPutAll: ' of: ' ; 35 | print: function 36 | ] 37 | 38 | { #category : #accessing } 39 | PMGradient >> value: anArray [ 40 | | dualValueTemplate dualValue | 41 | dualValueTemplate := anArray collect: [ :i | PMDualNumber value: i ]. 42 | ^ (1 to: anArray size) 43 | collect: [ :i | 44 | dualValue := dualValueTemplate deepCopy. 45 | (dualValue at: i) eps: 1. 46 | (function value: dualValue) eps ] 47 | as: anArray class 48 | ] 49 | -------------------------------------------------------------------------------- /src/Math-Distributions/PMLaplaceGenerator.class.st: -------------------------------------------------------------------------------- 1 | " 2 | I'm a random number generator whose values are distributed according to Laplace distribution. 3 | 4 | Ideally this class should be a subclass of RandomGenerator 5 | however it is unclear how to implement peek. 6 | " 7 | Class { 8 | #name : #PMLaplaceGenerator, 9 | #superclass : #Object, 10 | #instVars : [ 11 | 'laplaceDistribution', 12 | 'next' 13 | ], 14 | #category : #'Math-Distributions' 15 | } 16 | 17 | { #category : #testing } 18 | PMLaplaceGenerator class >> isDeprecated [ 19 | 20 | "I am just a wrapper for Laplace distribution. The Distribution should be used directly as it is." 21 | 22 | ^ true 23 | ] 24 | 25 | { #category : #'instance creation' } 26 | PMLaplaceGenerator class >> shape: peakValue scale: falloffValue [ 27 | 28 | ^ self new shape: peakValue scale: falloffValue 29 | ] 30 | 31 | { #category : #accessing } 32 | PMLaplaceGenerator >> next [ 33 | 34 | next := laplaceDistribution random. 35 | ^ next 36 | ] 37 | 38 | { #category : #accessing } 39 | PMLaplaceGenerator >> peek [ 40 | 41 | next ifNil: [ self next. ]. 42 | ^ next 43 | ] 44 | 45 | { #category : #creation } 46 | PMLaplaceGenerator >> shape: peakValue scale: falloffValue [ 47 | 48 | laplaceDistribution := PMLaplaceDistribution shape: peakValue scale: falloffValue 49 | ] 50 | -------------------------------------------------------------------------------- /src/Math-Tests-FunctionFit/PMFunctionFitTest.class.st: -------------------------------------------------------------------------------- 1 | Class { 2 | #name : #PMFunctionFitTest, 3 | #superclass : #TestCase, 4 | #instVars : [ 5 | 'f', 6 | 'd' 7 | ], 8 | #category : #'Math-Tests-FunctionFit' 9 | } 10 | 11 | { #category : #running } 12 | PMFunctionFitTest >> setUp [ 13 | 14 | super setUp. 15 | 16 | f := [ :x :a :b | a * x / (b + x) ]. 17 | d := (1 to: 20) collect: [ :i | i @ (f cull: i cull: 2 cull: 0.4) ] 18 | ] 19 | 20 | { #category : #tests } 21 | PMFunctionFitTest >> testFunctionFit [ 22 | 23 | | ff ar p | 24 | ff := PMFunctionFit function: f data: d. 25 | ar := ff parameters. 26 | ar do: [ :i | 27 | self assert: i isNumber. 28 | self assert: i ~= 0 ]. 29 | ff parameters: #( 1 2 ). 30 | self assert: ff parameters equals: #( 1 2 ). 31 | p := PMWeightedPoint point: -2 @ 1. 32 | self shouldnt: [ ff accumulate: p ] raise: Error. "function result will be NaN and should be ignored in the following calculations" 33 | ff parameters: ar. 34 | ff evaluate. 35 | self assert: ff parameters closeTo: #( 2 0.4 ) 36 | ] 37 | 38 | { #category : #tests } 39 | PMFunctionFitTest >> testPrint [ 40 | |aStream ff s| 41 | aStream :=ReadWriteStream with:''. 42 | ff:= PMFunctionFit function: f data: d . 43 | ff printOn: aStream . 44 | s :=aStream contents . 45 | self assert: (s includesSubstring: 'a * x / (b + x)'). 46 | self assert: (s includesSubstring: '20') 47 | ] 48 | -------------------------------------------------------------------------------- /src/Math-Tests-Accuracy/PMAccuracyFindKeyTest.class.st: -------------------------------------------------------------------------------- 1 | " 2 | This test case exercises the findKey: message with some regression tests. 3 | " 4 | Class { 5 | #name : 'PMAccuracyFindKeyTest', 6 | #superclass : 'TestCase', 7 | #instVars : [ 8 | 'accuracy' 9 | ], 10 | #category : 'Math-Tests-Accuracy', 11 | #package : 'Math-Tests-Accuracy' 12 | } 13 | 14 | { #category : 'tests' } 15 | PMAccuracyFindKeyTest >> assertKeyFor: selector equals: expected [ 16 | self assert: (accuracy findKey: selector) equals: expected 17 | ] 18 | 19 | { #category : 'running' } 20 | PMAccuracyFindKeyTest >> setUp [ 21 | super setUp. 22 | accuracy := PMAccuracyTestExample new 23 | ] 24 | 25 | { #category : 'tests' } 26 | PMAccuracyFindKeyTest >> testFindKeyReturnsAllTheRestStringWhenSelectorCorrespondsToNonExistentProperty [ 27 | | selector | 28 | selector := 'NonExistent'. 29 | 30 | self assertKeyFor: selector equals: 'AllTheRest' 31 | ] 32 | 33 | { #category : 'tests' } 34 | PMAccuracyFindKeyTest >> testFindKeyReturnsAllTheRestStringWhenSelectorIsInitialize [ 35 | | selector | 36 | selector := 'initialize'. 37 | 38 | self assertKeyFor: selector equals: 'AllTheRest' 39 | ] 40 | 41 | { #category : 'tests' } 42 | PMAccuracyFindKeyTest >> testFindKeyReturnsPropertyWhenSelectorIsSuffixOfInitializePropertyMessage [ 43 | | selector | 44 | selector := 'Aaa'. 45 | 46 | self assertKeyFor: selector equals: 'Aaa' 47 | ] 48 | -------------------------------------------------------------------------------- /src/Math-ODE/PMMultiStepper.class.st: -------------------------------------------------------------------------------- 1 | " 2 | Another large class of solvers are multi-step method. They save a small part of the history of the solution and compute the next step with the help of this history. Since multistep methods know a part of their history they do not need to compute the system function very often, usually it is only computed once. This makes multistep methods preferable if a call of the system function is expensive. Examples are ODEs defined on networks, where the computation of the interaction is usually where expensive (and might be of order O(N^2)). 3 | 4 | Multistep methods differ from the normal steppers. They safe a part of their history and this part has to be explicitly calculated and initialized. In the following example an Adams-Bashforth-stepper with a history of 5 steps is instantiated and initialized; 5 | 6 | The initialization uses a fourth-order Runge-Kutta stepper and after the call of initialize the state of inout has changed to the current state, such that can be immediately used by passing it to following calls of do_step. Of course, you can also use you own steppers to initialize the internal state of the Adams-Bashforth-Stepper: 7 | 8 | Many multistep methods are also explicit steppers, hence the parameter of do_step method do not differ from the explicit steppers. 9 | " 10 | Class { 11 | #name : #PMMultiStepper, 12 | #superclass : #PMStepper, 13 | #category : #'Math-ODE' 14 | } 15 | -------------------------------------------------------------------------------- /src/Math-Tests-AutomaticDifferenciation/PMGradientAndHessianTest.class.st: -------------------------------------------------------------------------------- 1 | Class { 2 | #name : #PMGradientAndHessianTest, 3 | #superclass : #TestCase, 4 | #category : #'Math-Tests-AutomaticDifferenciation' 5 | } 6 | 7 | { #category : #tests } 8 | PMGradientAndHessianTest >> test [ 9 | | f g h r | 10 | f := [ :i | 11 | | x y | 12 | x := i first. 13 | y := i second. 14 | (x raisedToInteger: 3) + (x squared * y) - y squared - (4 * y) ]. 15 | g := PMGradient of: f. 16 | h := PMHessian of: f. 17 | r := #(0 0). 18 | self assert: (g value: #(0 -2)) equals: r. 19 | self assert: (g value: (Array with: 1 with: (-3 / 2))) equals: r. 20 | self assert: (g value: #(-4 6)) equals: r. 21 | self 22 | assert: (h value: #(0 -2)) 23 | equals: (PMMatrix rows: #(#(-4 0) #(0 -2))). 24 | self assert: h gradient equals: r 25 | ] 26 | 27 | { #category : #tests } 28 | PMGradientAndHessianTest >> test2 [ 29 | 30 | | f g h r | 31 | f := [ :i | 32 | | x y | 33 | x := i first. 34 | y := i second. 35 | (x exp + y exp) ln ]. 36 | g := PMGradient of: f. 37 | (h := PMHessian of: f) value: #( 0 0 ). 38 | r := #( 0.5 0.5 ). 39 | self assert: (g value: #( 0 0 )) closeTo: r. 40 | self assert: h gradient closeTo: r. 41 | r := (-1 exp + 1 exp) reciprocal. 42 | self assert: (g value: #( -1 1 )) closeTo: r * #( -1 1 ) exp. 43 | self assert: h result closeTo: (PMMatrix rows: #( #( 0.25 -0.25 ) #( -0.25 0.25 ) )) 44 | ] 45 | -------------------------------------------------------------------------------- /src/Math-ODE/PMBDF4Stepper.class.st: -------------------------------------------------------------------------------- 1 | " 2 | It is stepper for Backward differentiation formulas method of order 3. 3 | We can't use BDF4 method until we have three old solution values and approximate new one. A BDF4 method is implicit. 4 | 5 | " 6 | Class { 7 | #name : #PMBDF4Stepper, 8 | #superclass : #PMImplicitMultiStepper, 9 | #category : #'Math-ODE' 10 | } 11 | 12 | { #category : #accessing } 13 | PMBDF4Stepper class >> order [ 14 | "BDF4 is a fourth order method." 15 | ^ 4 16 | ] 17 | 18 | { #category : #stepping } 19 | PMBDF4Stepper >> doStep3State: thirdState secondState: secondState firstState: firstState initState: initState thirdTime: t [ 20 | 21 | | approximateState | 22 | self stepSize ifNil: [ self error: 'step size required by stepper' ]. 23 | approximateState := (system state: thirdState time: t) 24 | * self stepSize + thirdState. 25 | ^ 48 / 25 * thirdState - (36 / 25 * secondState) 26 | + (16 / 25 * firstState) - (3 / 25 * initState) 27 | + (12 / 25 * self stepSize 28 | * (system state: approximateState time: t + self stepSize)) 29 | ] 30 | 31 | { #category : #stepping } 32 | PMBDF4Stepper >> doStep3State: thirdState secondState:secondState firstState: firstState initState: initState 33 | thirdTime: t stepSize: timeStep [ 34 | self stepSize: timeStep. 35 | ^ self doStep3State: thirdState secondState:secondState firstState: firstState initState: initState thirdTime: t 36 | ] 37 | -------------------------------------------------------------------------------- /src/Math-Benchmarks-ODE/PMExplicitMultiBenchmark.class.st: -------------------------------------------------------------------------------- 1 | Class { 2 | #name : #PMExplicitMultiBenchmark, 3 | #superclass : #PMODEBenchmark, 4 | #instVars : [ 5 | 'system' 6 | ], 7 | #category : #'Math-Benchmarks-ODE' 8 | } 9 | 10 | { #category : #benchmarking } 11 | PMExplicitMultiBenchmark >> benchAB2 [ 12 | | solver stepper | 13 | stepper := PMAB2Stepper onSystem: system. 14 | solver := (PMAB2Solver new) stepper: stepper; system: system; dt: dt. 15 | 1 to: self problemSize do: [ :i 16 | |solver solve: system startState: startState startTime:startTime endTime: endTime] 17 | ] 18 | 19 | { #category : #benchmarking } 20 | PMExplicitMultiBenchmark >> benchAB3 [ 21 | | solver stepper | 22 | stepper := PMAB3Stepper onSystem: system. 23 | solver := (PMAB3Solver new) stepper: stepper; system: system; dt: dt. 24 | 1 to: self problemSize do: [ :i 25 | |solver solve: system startState: startState startTime:startTime endTime: endTime] 26 | ] 27 | 28 | { #category : #benchmarking } 29 | PMExplicitMultiBenchmark >> benchAB4 [ 30 | | solver stepper | 31 | stepper := PMAB4Stepper onSystem: system. 32 | solver := (PMAB4Solver new) stepper: stepper; system: system; dt: dt. 33 | 1 to: self problemSize do: [ :i 34 | |solver solve: system startState: startState startTime:startTime endTime: endTime] 35 | ] 36 | 37 | { #category : #running } 38 | PMExplicitMultiBenchmark >> setUp [ 39 | super setUp. 40 | system := PMExplicitSystem block: function 41 | ] 42 | -------------------------------------------------------------------------------- /src/Math-UtilsDataServer/PMMemoryBasedDataServer.class.st: -------------------------------------------------------------------------------- 1 | " 2 | I take the data I will serve via the message data: aCollection. 3 | Once done I will be able to serve the data until none is available. 4 | " 5 | Class { 6 | #name : #PMMemoryBasedDataServer, 7 | #superclass : #PMAbstractDataServer, 8 | #instVars : [ 9 | 'data', 10 | 'position' 11 | ], 12 | #category : #'Math-UtilsDataServer' 13 | } 14 | 15 | { #category : #information } 16 | PMMemoryBasedDataServer >> atEnd [ 17 | "Answers true if there is no more data element." 18 | ^data size < position 19 | ] 20 | 21 | { #category : #initialization } 22 | PMMemoryBasedDataServer >> data: anOrderedCollection [ 23 | 24 | data := anOrderedCollection. 25 | self reset 26 | ] 27 | 28 | { #category : #information } 29 | PMMemoryBasedDataServer >> dimension [ 30 | "Answers the dimension of the vectors catered by the receiver." 31 | ^ data first size 32 | ] 33 | 34 | { #category : #operation } 35 | PMMemoryBasedDataServer >> next [ 36 | "Answers the next element on the stream." 37 | | answer | 38 | answer := data at: position. 39 | position := position + 1. 40 | ^answer 41 | ] 42 | 43 | { #category : #operation } 44 | PMMemoryBasedDataServer >> open [ 45 | "Open the data stream (must be implemented by subclass)." 46 | self reset 47 | ] 48 | 49 | { #category : #operation } 50 | PMMemoryBasedDataServer >> reset [ 51 | "Reset the position of the data stream to the beginning." 52 | position := 1 53 | ] 54 | -------------------------------------------------------------------------------- /src/Math-Tests-Numerical/PMCovarianceAccumulatorTest.class.st: -------------------------------------------------------------------------------- 1 | Class { 2 | #name : 'PMCovarianceAccumulatorTest', 3 | #superclass : 'TestCase', 4 | #category : 'Math-Tests-Numerical', 5 | #package : 'Math-Tests-Numerical' 6 | } 7 | 8 | { #category : 'tests' } 9 | PMCovarianceAccumulatorTest >> testCovarianceAccumulation [ 10 | "Code example 12.2" 11 | 12 | | accumulator average covarianceMatrix | 13 | accumulator := PMCovarianceAccumulator new: 3. 14 | #( #( 1 2 3 ) #( 2 3 4 ) #( 1 3 2 ) #( 4 3 1 ) #( 1 3 1 ) #( 1 4 2 ) #( 3 1 2 ) #( 3 4 2 ) ) do: [ :x | accumulator accumulate: x asPMVector ]. 15 | average := accumulator average. 16 | self assert: (average at: 1) closeTo: 2.0. 17 | self assert: (average at: 2) closeTo: 2.875. 18 | self assert: (average at: 3) closeTo: 2.125. 19 | covarianceMatrix := accumulator covarianceMatrix. 20 | self assert: ((covarianceMatrix rowAt: 1) at: 1) closeTo: 1.25. 21 | self assert: ((covarianceMatrix rowAt: 1) at: 2) closeTo: -0.125. 22 | self assert: ((covarianceMatrix rowAt: 2) at: 1) closeTo: -0.125. 23 | self assert: ((covarianceMatrix rowAt: 1) at: 3) closeTo: -0.25. 24 | self assert: ((covarianceMatrix rowAt: 3) at: 1) closeTo: -0.25. 25 | self assert: ((covarianceMatrix rowAt: 2) at: 2) closeTo: 0.859375. 26 | self assert: ((covarianceMatrix rowAt: 2) at: 3) closeTo: -0.109375. 27 | self assert: ((covarianceMatrix rowAt: 3) at: 2) closeTo: -0.109375. 28 | self assert: ((covarianceMatrix rowAt: 3) at: 3) closeTo: 0.859375 29 | ] 30 | -------------------------------------------------------------------------------- /src/Math-Numerical/Integer.extension.st: -------------------------------------------------------------------------------- 1 | Extension { #name : #Integer } 2 | 3 | { #category : #'*Math-Numerical' } 4 | Integer >> gamma [ 5 | self > 0 6 | ifFalse: [^ self error: 'Attempt to compute the Gamma function of a non-positive integer']. 7 | ^ (self - 1) factorial 8 | ] 9 | 10 | { #category : #'*Math-Numerical' } 11 | Integer >> inverseBinomialCoefficient [ 12 | 13 | " Reverse binomial coefficient. Answer a with all n and k such that n take: k = self. Elements in the answered Collection should be read as paired. Each pair represents (n,k) in the binomial coefficient formula. 14 | See https://math.stackexchange.com/a/103385/205 for details. " 15 | 16 | | k | 17 | [ self > 1 ] assert. 18 | k := 0. 19 | ^ Array streamContents: [ :stream | 20 | [ true ] whileTrue: [ 21 | | nmin nmax choose | 22 | k := k + 1. 23 | 2 * k + 1 * self <= (4 ** k) ifTrue: [ ^ stream contents ]. 24 | nmin := ((k factorial * self) nthRoot: k) ceiling. 25 | nmax := nmin + k + 1. 26 | nmin := nmin max: 2 * k. 27 | choose := nmin asInteger numberOfCombinationsTaken: k. 28 | nmin to: nmax do: [ :n | 29 | choose = self ifTrue: [ 30 | stream nextPutAll: { 31 | n asInteger. 32 | k asInteger }. 33 | k < (n - k) ifTrue: [ 34 | stream nextPutAll: { 35 | n asInteger. 36 | (n - k) asInteger } ] ]. 37 | choose := choose * (n + 1). 38 | choose := (choose / (n + 1 - k)) ceiling ] ] ] 39 | ] 40 | -------------------------------------------------------------------------------- /src/Math-Tests-Distributions/PMKolmogorovSmirnovSample.class.st: -------------------------------------------------------------------------------- 1 | " 2 | abstract class, use KolmogorovSmirnov1Sample or KolmogorovSmirnov2Sample 3 | " 4 | Class { 5 | #name : 'PMKolmogorovSmirnovSample', 6 | #superclass : 'Object', 7 | #instVars : [ 8 | 'data', 9 | 'compareWith' 10 | ], 11 | #category : 'Math-Tests-Distributions-KolmogorovSmirnov', 12 | #package : 'Math-Tests-Distributions', 13 | #tag : 'KolmogorovSmirnov' 14 | } 15 | 16 | { #category : 'accessing' } 17 | PMKolmogorovSmirnovSample >> data: aCollection [ 18 | ^self subclassResponsibility 19 | ] 20 | 21 | { #category : 'accessing' } 22 | PMKolmogorovSmirnovSample >> ksStatistic [ 23 | "the kolmogorov-smirnov statistic D" 24 | ^self subclassResponsibility 25 | ] 26 | 27 | { #category : 'accessing' } 28 | PMKolmogorovSmirnovSample >> pValue [ 29 | "the probability of getting a ksStatistic <= the actual one" 30 | ^self subclassResponsibility 31 | ] 32 | 33 | { #category : 'printing' } 34 | PMKolmogorovSmirnovSample >> printOn: aStream [ 35 | super printOn: aStream. 36 | aStream nextPutAll: '(dataSize: '. 37 | data 38 | ifNil: [ aStream nextPut: $- ] 39 | ifNotNil: [ data size printOn: aStream ] 40 | ] 41 | 42 | { #category : 'accessing' } 43 | PMKolmogorovSmirnovSample >> rejectEqualityHypothesisWithAlpha: aFloat [ 44 | ^self pValue > (1-aFloat) 45 | ] 46 | 47 | { #category : 'private' } 48 | PMKolmogorovSmirnovSample >> testDataComplete [ 49 | (data isNil or:[compareWith isNil ]) ifTrue: [ self error:'data not completely set' ] 50 | ] 51 | -------------------------------------------------------------------------------- /src/Math-Number-Extensions/Number.extension.st: -------------------------------------------------------------------------------- 1 | Extension { #name : #Number } 2 | 3 | { #category : #'*Math-Number-Extensions-mathematical functions' } 4 | Number >> arCosh [ 5 | "Answer receiver's area hyperbolic cosine. 6 | That is the inverse function of cosh." 7 | 8 | ^self asFloat arCosh 9 | ] 10 | 11 | { #category : #'*Math-Number-Extensions-mathematical functions' } 12 | Number >> arSinh [ 13 | "Answer receiver's area hyperbolic sine. 14 | That is the inverse function of sinh." 15 | 16 | ^self asFloat arSinh 17 | ] 18 | 19 | { #category : #'*Math-Number-Extensions-mathematical functions' } 20 | Number >> arTanh [ 21 | "Answer receiver's area hyperbolic tangent. 22 | That is the inverse function of tanh." 23 | 24 | ^self asFloat arTanh 25 | ] 26 | 27 | { #category : #'*Math-Number-Extensions-mathematical functions' } 28 | Number >> cosh [ 29 | "Answer receivers hyperbolic cosine." 30 | 31 | ^self asFloat cosh 32 | ] 33 | 34 | { #category : #'*Math-Number-Extensions-mathematical functions' } 35 | Number >> sinc [ 36 | "Answer receivers cardinal sine." 37 | 38 | ^ self isZero 39 | ifTrue: [self class one] 40 | ifFalse: [self sin / self] 41 | ] 42 | 43 | { #category : #'*Math-Number-Extensions-mathematical functions' } 44 | Number >> sinh [ 45 | "Answer receivers hyperbolic sine" 46 | 47 | ^self asFloat sinh 48 | ] 49 | 50 | { #category : #'*Math-Number-Extensions-mathematical functions' } 51 | Number >> tanh [ 52 | "Answer receivers hyperbolic tangent" 53 | 54 | ^self asFloat tanh 55 | ] 56 | -------------------------------------------------------------------------------- /src/Math-Tests-FunctionFit/PMErrorAsParameterFunctionTest.class.st: -------------------------------------------------------------------------------- 1 | Class { 2 | #name : #PMErrorAsParameterFunctionTest, 3 | #superclass : #TestCase, 4 | #instVars : [ 5 | 'f' 6 | ], 7 | #category : #'Math-Tests-FunctionFit' 8 | } 9 | 10 | { #category : #running } 11 | PMErrorAsParameterFunctionTest >> setUp [ 12 | 13 | | col | 14 | super setUp. 15 | f := PMErrorOfParameterFunction new function: [ :x :a :b | a * x / (b + x) ]. 16 | col := (1 to: 3) collect: [ :i | i @ (f function cull: i cull: 1 cull: 1) ]. 17 | f data: col. 18 | f := PMErrorAsParameterFunction new function: f 19 | ] 20 | 21 | { #category : #tests } 22 | PMErrorAsParameterFunctionTest >> testMaxFunction [ 23 | self assert: (f parameters size < f maxFunction ) 24 | ] 25 | 26 | { #category : #tests } 27 | PMErrorAsParameterFunctionTest >> testPrint [ 28 | 29 | | aStream s | 30 | aStream := ReadWriteStream with: ''. 31 | f printOn: aStream. 32 | s := aStream contents. 33 | self assert: (s includesSubstring: 'a * x / (b + x)'). 34 | self assert: (s includesSubstring: '#squared'). 35 | self assert: 36 | (s includesSubstring: 'maxFunction: ' , f maxFunction asString) 37 | ] 38 | 39 | { #category : #tests } 40 | PMErrorAsParameterFunctionTest >> testparameters [ 41 | f parameters: #(1 1). 42 | f changeParametersBy: #(1 1). 43 | self assert: f parameters equals: #(2 2) 44 | ] 45 | 46 | { #category : #tests } 47 | PMErrorAsParameterFunctionTest >> testvalue [ 48 | f parameters: #(2 2). 49 | self assert: (f value: 2) equals: 1 / 9 50 | ] 51 | -------------------------------------------------------------------------------- /src/Math-Tests-Numerical/PMGeneticOptimizerBugsTest.class.st: -------------------------------------------------------------------------------- 1 | Class { 2 | #name : 'PMGeneticOptimizerBugsTest', 3 | #superclass : 'TestCase', 4 | #instVars : [ 5 | 'manager', 6 | 'function', 7 | 'optimizer' 8 | ], 9 | #category : 'Math-Tests-Numerical', 10 | #package : 'Math-Tests-Numerical' 11 | } 12 | 13 | { #category : 'running' } 14 | PMGeneticOptimizerBugsTest >> setUp [ 15 | 16 | super setUp. 17 | manager := PMChromosomeManager new. 18 | manager populationSize: 3. 19 | function := [ :x | (x * x) sum ]. 20 | optimizer := PMGeneticOptimizer minimizingFunction: function. 21 | optimizer chromosomeManager: manager 22 | ] 23 | 24 | { #category : 'tests' } 25 | PMGeneticOptimizerBugsTest >> testBug1 [ 26 | 27 | | r | 28 | optimizer addPointAt: #( 2 3 ). 29 | optimizer addPointAt: #( 2 3 ). 30 | self shouldnt: [ r := optimizer randomScale ] raise: Error. 31 | self assert: r first closeTo: 1 / 2. 32 | self assert: r last closeTo: 1 33 | ] 34 | 35 | { #category : 'tests' } 36 | PMGeneticOptimizerBugsTest >> testBug2 [ 37 | | r | 38 | optimizer addPointAt: #(2 3). 39 | "i add a nan by hand here,which in a way doesnt make sense, just to get a nan result for function, but with a more complicated function it is perfectly possible for the optimizer to produce nan's by itself!" 40 | optimizer addPointAt: (Array with: Float nan with: 3). 41 | "if you inspect r here you will see that a _single nan result can _completely throw off the ga calculations" 42 | r:=optimizer randomScale. 43 | self deny: r first isNaN 44 | ] 45 | -------------------------------------------------------------------------------- /src/Math-PrincipalComponentAnalysis/PMPrincipalComponentAnalyserSVD.class.st: -------------------------------------------------------------------------------- 1 | Class { 2 | #name : 'PMPrincipalComponentAnalyserSVD', 3 | #superclass : 'PMPrincipalComponentAnalyser', 4 | #instVars : [ 5 | 'svd', 6 | 'u', 7 | 'v' 8 | ], 9 | #category : 'Math-PrincipalComponentAnalysis', 10 | #package : 'Math-PrincipalComponentAnalysis' 11 | } 12 | 13 | { #category : 'accessing' } 14 | PMPrincipalComponentAnalyserSVD >> fit: aPMMatrix [ 15 | 16 | svd := aPMMatrix decomposeSV. 17 | u := svd leftSingularMatrix. 18 | v := svd rightSingularMatrix. 19 | self flipEigenvectorsSign 20 | ] 21 | 22 | { #category : 'accessing' } 23 | PMPrincipalComponentAnalyserSVD >> flipEigenvectorsSign [ 24 | "flip eigenvectors sign to enforce deterministic output" 25 | "U-based decision like : https://github.com/scikit-learn/scikit-learn/blob/4c65d8e615c9331d37cbb6225c5b67c445a5c959/sklearn/utils/extmath.py#L609" 26 | 27 | | algo | 28 | algo := PMSciKitLearnSVDFlipAlgorithm flipU: u andV: v. 29 | 30 | u := algo uFlipped . 31 | v := algo vFlipped . 32 | ] 33 | 34 | { #category : 'accessing' } 35 | PMPrincipalComponentAnalyserSVD >> transform: aPMMatrix [ 36 | "Apply dimensionality reduction to aPMMatrix" 37 | 38 | ^ aPMMatrix * self transformMatrix transpose 39 | ] 40 | 41 | { #category : 'accessing' } 42 | PMPrincipalComponentAnalyserSVD >> transformMatrix [ 43 | "Return a matrix that can be applied to any data vector to extract the relevant component of the data vector" 44 | 45 | ^ PMMatrix rows: (v rows copyFrom: 1 to: componentsNumber) 46 | ] 47 | -------------------------------------------------------------------------------- /src/Math-Clustering/PMCluster.class.st: -------------------------------------------------------------------------------- 1 | Class { 2 | #name : #PMCluster, 3 | #superclass : #Object, 4 | #instVars : [ 5 | 'accumulator', 6 | 'previousSampleSize' 7 | ], 8 | #category : #'Math-Clustering' 9 | } 10 | 11 | { #category : #transformation } 12 | PMCluster >> accumulate: aVector [ 13 | 14 | accumulator accumulate: aVector 15 | ] 16 | 17 | { #category : #initialization } 18 | PMCluster >> centerOn: aVector [ 19 | 20 | self subclassResponsibility 21 | ] 22 | 23 | { #category : #information } 24 | PMCluster >> changes [ 25 | 26 | ^ (self sampleSize - previousSampleSize) abs 27 | ] 28 | 29 | { #category : #transformation } 30 | PMCluster >> collectAccumulatorResults [ 31 | self subclassResponsibility 32 | ] 33 | 34 | { #category : #information } 35 | PMCluster >> distanceTo: aVector [ 36 | 37 | ^ self subclassResponsibility 38 | ] 39 | 40 | { #category : #initialization } 41 | PMCluster >> initialize [ 42 | 43 | previousSampleSize := 0. 44 | ^ self 45 | ] 46 | 47 | { #category : #testing } 48 | PMCluster >> isInsignificantIn: aClusterFinder [ 49 | 50 | ^ self sampleSize <= aClusterFinder minimumClusterSize 51 | ] 52 | 53 | { #category : #testing } 54 | PMCluster >> isUndefined [ 55 | 56 | ^ self subclassResponsibility 57 | ] 58 | 59 | { #category : #transformation } 60 | PMCluster >> reset [ 61 | 62 | previousSampleSize := self sampleSize. 63 | self collectAccumulatorResults. 64 | accumulator reset 65 | ] 66 | 67 | { #category : #information } 68 | PMCluster >> sampleSize [ 69 | 70 | ^ accumulator count 71 | ] 72 | -------------------------------------------------------------------------------- /src/Math-Numerical/PMProjectedOneVariableFunction.class.st: -------------------------------------------------------------------------------- 1 | Class { 2 | #name : #PMProjectedOneVariableFunction, 3 | #superclass : #Object, 4 | #instVars : [ 5 | 'index', 6 | 'function', 7 | 'argument' 8 | ], 9 | #category : #'Math-Numerical' 10 | } 11 | 12 | { #category : #creation } 13 | PMProjectedOneVariableFunction class >> function: aVectorFunction [ 14 | ^super new initialize: aVectorFunction 15 | ] 16 | 17 | { #category : #information } 18 | PMProjectedOneVariableFunction >> argumentWith: aNumber [ 19 | 20 | ^argument at: index put: aNumber; yourself 21 | ] 22 | 23 | { #category : #transformation } 24 | PMProjectedOneVariableFunction >> bumpIndex [ 25 | 26 | index 27 | ifNil: [ index := 1 ] 28 | ifNotNil: [ 29 | index := index + 1. 30 | index > argument size ifTrue: [ index := 1 ] ] 31 | ] 32 | 33 | { #category : #information } 34 | PMProjectedOneVariableFunction >> index [ 35 | 36 | index ifNil: [ index := 1 ]. 37 | ^ index 38 | ] 39 | 40 | { #category : #initialization } 41 | PMProjectedOneVariableFunction >> initialize: aFunction [ 42 | function := aFunction. 43 | ^self 44 | ] 45 | 46 | { #category : #initialization } 47 | PMProjectedOneVariableFunction >> setArgument: anArrayOrVector [ 48 | argument := anArrayOrVector copy 49 | ] 50 | 51 | { #category : #initialization } 52 | PMProjectedOneVariableFunction >> setIndex: anInteger [ 53 | index := anInteger 54 | ] 55 | 56 | { #category : #information } 57 | PMProjectedOneVariableFunction >> value: aNumber [ 58 | ^function value: ( self argumentWith: aNumber) 59 | ] 60 | -------------------------------------------------------------------------------- /src/Math-Tests-ODE/PMODESystemTest.class.st: -------------------------------------------------------------------------------- 1 | " 2 | An ODESystemTest is a test class for testing the behavior of ODESystem 3 | " 4 | Class { 5 | #name : 'PMODESystemTest', 6 | #superclass : 'PMAbstractTest', 7 | #instVars : [ 8 | 'sys' 9 | ], 10 | #category : 'Math-Tests-ODE', 11 | #package : 'Math-Tests-ODE' 12 | } 13 | 14 | { #category : 'running' } 15 | PMODESystemTest >> setUp [ 16 | "create a dummy system" 17 | 18 | super setUp. 19 | sys := PMODESystem new 20 | ] 21 | 22 | { #category : 'tests' } 23 | PMODESystemTest >> testBlock [ 24 | | aBlock | 25 | aBlock := [ :x :t | t ]. 26 | sys block: aBlock. 27 | self assert: sys block equals: aBlock 28 | ] 29 | 30 | { #category : 'tests' } 31 | PMODESystemTest >> testVectorBlock [ 32 | "this illustrates using a block that operates on a collection" 33 | 34 | | aBlock | 35 | aBlock := [ :x :t | x collect: [ :ea | t ] ]. 36 | sys block: aBlock. 37 | self assert: sys block equals: aBlock 38 | ] 39 | 40 | { #category : 'tests' } 41 | PMODESystemTest >> testVectorXT [ 42 | "a simple example of using a collection as state" 43 | 44 | | aBlock | 45 | aBlock := [ :x :t | x collect: [ :ea | t ] ]. 46 | sys block: aBlock. 47 | self assert: (sys state: #(1 2 3) time: 2) equals: #(2 2 2). 48 | self assert: (sys state: #(0 0) time: 2) equals: #(2 2) 49 | ] 50 | 51 | { #category : 'tests' } 52 | PMODESystemTest >> testXT [ 53 | | aBlock | 54 | aBlock := [ :x :t | t ]. 55 | sys block: aBlock. 56 | self assert: (sys state: 1 time: 2) equals: 2. 57 | self assert: (sys state: 0 time: 2) equals: 2 58 | ] 59 | -------------------------------------------------------------------------------- /src/Math-FastFourierTransform/SmallInteger.extension.st: -------------------------------------------------------------------------------- 1 | Extension { #name : #SmallInteger } 2 | 3 | { #category : #'*Math-FastFourierTransform' } 4 | SmallInteger >> byteReversed [ 5 | "Answer the receiver with bits reversed in a byte. The receiver must be between 0 and 255. The constant has been obtained by this snippet: 6 | (0 to: 255) collect: [:e | 7 | | r | 8 | r := ((e bitAnd: 2r11110000) bitShift: -4) + ((e bitAnd: 2r00001111) bitShift: 4). 9 | r := ((r bitAnd: 2r11001100) bitShift: -2) + ((r bitAnd: 2r00110011) bitShift: 2). 10 | ((r bitAnd: 2r10101010) bitShift: -1) + ((r bitAnd: 2r01010101) bitShift: 1).] as: ByteArray" 11 | 12 | ^ #[0 128 64 192 32 160 96 224 16 144 80 208 48 176 112 240 8 136 72 200 40 168 104 232 24 152 88 216 56 184 120 248 4 132 68 196 36 164 100 228 20 148 84 212 52 180 116 244 12 140 76 204 44 172 108 236 28 156 92 220 60 188 124 252 2 130 66 194 34 162 98 226 18 146 82 210 50 178 114 242 10 138 74 202 42 170 106 234 26 154 90 218 58 186 122 250 6 134 70 198 38 166 102 230 22 150 86 214 54 182 118 246 14 142 78 206 46 174 110 238 30 158 94 222 62 190 126 254 1 129 65 193 33 161 97 225 17 145 81 209 49 177 113 241 9 137 73 201 41 169 105 233 25 153 89 217 57 185 121 249 5 133 69 197 37 165 101 229 21 149 85 213 53 181 117 245 13 141 77 205 45 173 109 237 29 157 93 221 61 189 125 253 3 131 67 195 35 163 99 227 19 147 83 211 51 179 115 243 11 139 75 203 43 171 107 235 27 155 91 219 59 187 123 251 7 135 71 199 39 167 103 231 23 151 87 215 55 183 119 247 15 143 79 207 47 175 111 239 31 159 95 223 63 191 127 255] 13 | at: 1 + self 14 | ] 15 | -------------------------------------------------------------------------------- /src/Math-Complex/Number.extension.st: -------------------------------------------------------------------------------- 1 | Extension { #name : #Number } 2 | 3 | { #category : #'*Math-Complex' } 4 | Number >> adaptToComplex: rcvr andSend: selector [ 5 | "If I am involved in arithmetic with a Complex number, convert me to a Complex number." 6 | ^ rcvr perform: selector with: self asComplex 7 | ] 8 | 9 | { #category : #'*Math-Complex' } 10 | Number >> asComplex [ 11 | "Answer a Complex number that represents value of the receiver." 12 | 13 | ^ PMComplexNumber real: self imaginary: 0 14 | ] 15 | 16 | { #category : #'*Math-Complex' } 17 | Number >> i [ 18 | ^ PMComplexNumber real: 0 imaginary: self 19 | ] 20 | 21 | { #category : #'*Math-Complex' } 22 | Number >> i: aNumber [ 23 | "Form a complex number with 24 | receiver as realPart 25 | aNumber as imaginaryPart 26 | this is the same as (self + aNumber i) but a little bit more efficient." 27 | 28 | aNumber isNumber ifFalse: [self error: 'Badly formed complex number']. 29 | ^PMComplexNumber real: self imaginary: aNumber 30 | ] 31 | 32 | { #category : #'*Math-Complex' } 33 | Number >> isComplexConjugateOf: aNumber [ 34 | "A complex conjugate of a real number is same real number" 35 | self 36 | deprecated: 'This method is redundant. Just check the equality with complexConjugate' 37 | transformWith: '`@rec isComplexConjugate: `@arg' -> '`@rec complexConjugate = `@arg'. 38 | 39 | ^ self complexConjugate = aNumber 40 | ] 41 | 42 | { #category : #'*Math-Complex' } 43 | Number >> isRealNumber [ 44 | "Answer true if receiver is a real number. All instances of Number are real numbers." 45 | ^ true 46 | ] 47 | -------------------------------------------------------------------------------- /src/Math-Numerical/PMCDFNewtonZeroFinder.class.st: -------------------------------------------------------------------------------- 1 | " 2 | A CdfNewtonZeroFinder is a specialised version of PMNewtonZeroFinder for DhbProbabilityDensity >>privateInverseDistributionValue:, that can deal with problems that can arise there (it doesnt diverge on a cdf) 3 | " 4 | Class { 5 | #name : #PMCDFNewtonZeroFinder, 6 | #superclass : #PMNewtonZeroFinder, 7 | #instVars : [ 8 | 'old' 9 | ], 10 | #category : #'Math-Numerical-Math-FunctionIterator' 11 | } 12 | 13 | { #category : #operation } 14 | PMCDFNewtonZeroFinder >> computeInitialValues [ 15 | 16 | "super computeInitialValues." "the original testing there is skipped for speed reasons, not because of the error,that is produced here, which is completely eliminated" 17 | old:=result-1.0 18 | ] 19 | 20 | { #category : #operation } 21 | PMCDFNewtonZeroFinder >> evaluateIteration [ 22 | "fallback on bisection if necessary (no divideByZero anymore)" 23 | | delta new | 24 | delta :=( functionBlock value: result) / (derivativeBlock value: result). 25 | new := result - delta. 26 | old=old ifTrue: [new:=old+result /2.0]]. 29 | [0.0 = (derivativeBlock value: new )] whileTrue: [new:=new +result /2.0]. 30 | old:=result. 31 | result:=new. 32 | ^self relativePrecision: delta abs 33 | ] 34 | 35 | { #category : #initialization } 36 | PMCDFNewtonZeroFinder >> setDerivative: aBlock [ 37 | "the original testing is skipped for speed reasons, not because of the error (which is largely eliminated anyway)." 38 | 39 | derivativeBlock := aBlock 40 | ] 41 | -------------------------------------------------------------------------------- /src/Math-PrincipalComponentAnalysis/PMStandardizationScaler.class.st: -------------------------------------------------------------------------------- 1 | Class { 2 | #name : 'PMStandardizationScaler', 3 | #superclass : 'PMDataTransformer', 4 | #instVars : [ 5 | 'accumulator' 6 | ], 7 | #category : 'Math-PrincipalComponentAnalysis', 8 | #package : 'Math-PrincipalComponentAnalysis' 9 | } 10 | 11 | { #category : 'accessing' } 12 | PMStandardizationScaler >> fit: aPMMatrix [ 13 | "Compute the mean and the scale of a PMMatrix (in order to have a std of 1)" 14 | 15 | accumulator := PMCovarianceAccumulator new: aPMMatrix numberOfColumns. 16 | aPMMatrix rowsDo: [ :each | accumulator accumulate: each ] 17 | ] 18 | 19 | { #category : 'accessing' } 20 | PMStandardizationScaler >> mean [ 21 | ^ accumulator average 22 | ] 23 | 24 | { #category : 'accessing' } 25 | PMStandardizationScaler >> scale [ 26 | ^ self variance collect: [ :element | 27 | | root | 28 | root := element sqrt. 29 | (root ~= 0) ifTrue: [ root ] ifFalse: [ 1.0 ] 30 | ] 31 | ] 32 | 33 | { #category : 'transforming' } 34 | PMStandardizationScaler >> transform: aPMMatrix [ 35 | "Perform standardization by centering and scaling" 36 | 37 | |mean scale| 38 | mean := self mean. 39 | scale := self scale. 40 | ^ PMMatrix rows: ((PMMatrix rows: (aPMMatrix rowsCollect: [ :each | each - mean ])) rowsCollect: [ :each| each / scale]) 41 | ] 42 | 43 | { #category : 'information' } 44 | PMStandardizationScaler >> variance [ 45 | "Return the diagonal of the covarianceMatrix" 46 | 47 | | c | 48 | c := accumulator covarianceMatrix. 49 | ^ (1 to: c numberOfRows) collect: [ :each | c at: each at: each ] 50 | ] 51 | -------------------------------------------------------------------------------- /src/Math-Numerical/PMMinimizingPoint.class.st: -------------------------------------------------------------------------------- 1 | Class { 2 | #name : #PMMinimizingPoint, 3 | #superclass : #Object, 4 | #instVars : [ 5 | 'value', 6 | 'position' 7 | ], 8 | #category : #'Math-Numerical-Math-FunctionIterator' 9 | } 10 | 11 | { #category : #creation } 12 | PMMinimizingPoint class >> new: aVector value: aNumber [ 13 | ^self new vector: aVector; value: aNumber; yourself 14 | ] 15 | 16 | { #category : #creation } 17 | PMMinimizingPoint class >> vector: aVector function: aFunction [ 18 | ^self new: aVector value: (aFunction value: aVector) 19 | ] 20 | 21 | { #category : #comparing } 22 | PMMinimizingPoint >> = aMPoint [ 23 | "only for testing purposes" 24 | 25 | ^ value = aMPoint value & (position = aMPoint position) 26 | ] 27 | 28 | { #category : #information } 29 | PMMinimizingPoint >> better: anOptimizingPoint [ 30 | ^ value <= anOptimizingPoint value 31 | ] 32 | 33 | { #category : #information } 34 | PMMinimizingPoint >> betterThan: anOptimizingPoint [ 35 | ^value < anOptimizingPoint value 36 | ] 37 | 38 | { #category : #information } 39 | PMMinimizingPoint >> position [ 40 | ^position 41 | ] 42 | 43 | { #category : #display } 44 | PMMinimizingPoint >> printOn: aStream [ 45 | position printOn: aStream. 46 | aStream 47 | nextPut: $:; 48 | space. 49 | value printOn: aStream 50 | ] 51 | 52 | { #category : #information } 53 | PMMinimizingPoint >> value [ 54 | ^value 55 | ] 56 | 57 | { #category : #initialization } 58 | PMMinimizingPoint >> value: aNumber [ 59 | value := aNumber 60 | ] 61 | 62 | { #category : #initialization } 63 | PMMinimizingPoint >> vector: aVector [ 64 | position := aVector 65 | ] 66 | -------------------------------------------------------------------------------- /src/Math-ODE/PMAB2Stepper.class.st: -------------------------------------------------------------------------------- 1 | " 2 | It is stepper for Adams - Bashforth method of order 2. We can't use AB2 method until we have two old solution values. A AB2 method is explicit. We found starting point with Midpoint Method (RK2). 3 | " 4 | Class { 5 | #name : #PMAB2Stepper, 6 | #superclass : #PMExplicitMultiStepper, 7 | #category : #'Math-ODE' 8 | } 9 | 10 | { #category : #accessing } 11 | PMAB2Stepper class >> order [ 12 | "AB2 is a second order method." 13 | ^ 2 14 | ] 15 | 16 | { #category : #stepping } 17 | PMAB2Stepper >> doStep: aState prevState: prevState time: t [ 18 | 19 | self stepSize ifNil: [ self error: 'step size required by stepper' ]. 20 | ^ self stepSize / 2 * (3 * (system state: aState time: t) 21 | - (system state: prevState time: t - self stepSize)) + aState 22 | ] 23 | 24 | { #category : #stepping } 25 | PMAB2Stepper >> doStep: aState prevState: prevState time: t stepSize: timeStep [ 26 | 27 | self stepSize: timeStep. 28 | ^ self doStep: aState prevState: prevState time: t 29 | ] 30 | 31 | { #category : #stepping } 32 | PMAB2Stepper >> lastStep: aState prevState: prevState time: t deltaT: incrementOfTime [ 33 | 34 | self stepSize ifNil: [ self error: 'step size required by stepper' ]. 35 | 36 | ^ self stepSize / 2 * (3 * (system state: aState time: t) 37 | - (system state: prevState time: t - incrementOfTime)) + aState 38 | ] 39 | 40 | { #category : #stepping } 41 | PMAB2Stepper >> lastStep: aState prevState: prevState time: t stepSize: timeStep deltaT: incrementOfTime [ 42 | 43 | self stepSize: timeStep. 44 | ^ self lastStep: aState prevState: prevState time: t deltaT: incrementOfTime 45 | ] 46 | -------------------------------------------------------------------------------- /src/Math-ODE/PMRungeKuttaStepper.class.st: -------------------------------------------------------------------------------- 1 | " 2 | A RungeKuttaStepper is a specialization on Explicit Stepper that provides a higer order estimate. The Euler method implemented in ExplicitStepper is order 1, and the error obtained is proportional to the step size. 3 | 4 | The RungeKuttaStepper is order 4, the error term is proportional to the step size to the fourth power. 5 | " 6 | Class { 7 | #name : #PMRungeKuttaStepper, 8 | #superclass : #PMExplicitStepper, 9 | #category : #'Math-ODE' 10 | } 11 | 12 | { #category : #accessing } 13 | PMRungeKuttaStepper class >> order [ 14 | "RungeKutta is a fourth order method." 15 | ^ 4 16 | ] 17 | 18 | { #category : #stepping } 19 | PMRungeKuttaStepper >> doStep: aState time: t [ 20 | 21 | "This method should take one step from inState at time t of size dt, and modify the state, then answer it." 22 | 23 | | k1 k2 k3 k4 midPoint endPoint | 24 | self stepSize ifNil: [ self error: 'step size required by stepper' ]. 25 | midPoint := self stepSize / 2 + t. 26 | endPoint := self stepSize + t. 27 | k1 := system state: aState time: t. 28 | k2 := system state: aState + (k1 * self stepSize / 2) time: midPoint. 29 | k3 := system state: aState + (k2 * self stepSize / 2) time: midPoint. 30 | k4 := system state: aState + (k3 * self stepSize) time: endPoint. 31 | ^ aState + (k1 + k2 + k2 + k3 + k3 + k4 / 6 * self stepSize) 32 | ] 33 | 34 | { #category : #stepping } 35 | PMRungeKuttaStepper >> lastStep: aState time: t stepSize: timeStep deltaT: incrementOfTime [ 36 | "This method should take one step from inState at time t of size dt, and modify the state, then answer it." 37 | 38 | self stepSize: timeStep. 39 | ^ self doStep: aState time: t 40 | ] 41 | -------------------------------------------------------------------------------- /src/Math-Numerical/PMOptimizingBracketFinder.class.st: -------------------------------------------------------------------------------- 1 | Class { 2 | #name : #PMOptimizingBracketFinder, 3 | #superclass : #PMOneVariableFunctionOptimizer, 4 | #category : #'Math-Numerical' 5 | } 6 | 7 | { #category : #creation } 8 | PMOptimizingBracketFinder class >> initialPoints: aSortedCollection function: aFunction [ 9 | "Private" 10 | ^super new setInitialPoints: aSortedCollection; setFunction: aFunction 11 | ] 12 | 13 | { #category : #operation } 14 | PMOptimizingBracketFinder >> computeInitialValues [ 15 | 16 | [ bestPoints size < 2 ] whileTrue: [ self addPointAt: self randomGenerator next ] 17 | ] 18 | 19 | { #category : #operation } 20 | PMOptimizingBracketFinder >> evaluateIteration [ 21 | 22 | | x1 x2 | 23 | x1 := (bestPoints at: 1) position. 24 | x2 := (bestPoints at: 2) position. 25 | self addPointAt: x1 * 3 - (x2 * 2). 26 | precision := x2 - x1 * ((bestPoints at: 3) position - x1). 27 | self hasConverged ifFalse: [ bestPoints removeLast ]. 28 | ^ precision 29 | ] 30 | 31 | { #category : #operation } 32 | PMOptimizingBracketFinder >> finalizeIterations [ 33 | result := bestPoints 34 | ] 35 | 36 | { #category : #initialization } 37 | PMOptimizingBracketFinder >> initialize [ 38 | 39 | super initialize. 40 | self randomGenerator: Random new 41 | ] 42 | 43 | { #category : #initialization } 44 | PMOptimizingBracketFinder >> initializeForOptimizer: aFunctionOptimizer [ 45 | "Private" 46 | 47 | super initializeForOptimizer: aFunctionOptimizer. 48 | bestPoints := aFunctionOptimizer bestPoints 49 | ] 50 | 51 | { #category : #initialization } 52 | PMOptimizingBracketFinder >> setInitialPoints: aSortedCollection [ 53 | "Private" 54 | 55 | bestPoints := aSortedCollection 56 | ] 57 | -------------------------------------------------------------------------------- /src/Math-Chromosome/PMVectorChromosomeManager.class.st: -------------------------------------------------------------------------------- 1 | Class { 2 | #name : #PMVectorChromosomeManager, 3 | #superclass : #PMChromosomeManager, 4 | #instVars : [ 5 | 'origin', 6 | 'range' 7 | ], 8 | #category : #'Math-Chromosome' 9 | } 10 | 11 | { #category : #operation } 12 | PMVectorChromosomeManager >> crossover: aChromosome1 and: aChromosome2 [ 13 | 14 | | index new1 new2 | 15 | 16 | index := randomNumberGenerator nextIntegerBetween: 2 and: aChromosome1 size. 17 | 18 | new1 := self clone: aChromosome1. 19 | new1 replaceFrom: index to: new1 size with: aChromosome2 startingAt: index. 20 | 21 | new2 := self clone: aChromosome2. 22 | new2 replaceFrom: index to: new2 size with: aChromosome1 startingAt: index. 23 | 24 | ^ Array with: new1 with: new2 25 | ] 26 | 27 | { #category : #operation } 28 | PMVectorChromosomeManager >> mutate: aVector [ 29 | 30 | | index | 31 | index := randomNumberGenerator nextIntegerBetween: 1 and: aVector size. 32 | 33 | ^( aVector copy) 34 | at: index put: (self randomComponent: index); 35 | yourself 36 | ] 37 | 38 | { #category : #initialization } 39 | PMVectorChromosomeManager >> origin: aVector [ 40 | 41 | origin := aVector 42 | ] 43 | 44 | { #category : #creation } 45 | PMVectorChromosomeManager >> randomChromosome [ 46 | 47 | ^ ((1 to: origin size) collect: [ :n | self randomComponent: n ]) asPMVector 48 | ] 49 | 50 | { #category : #information } 51 | PMVectorChromosomeManager >> randomComponent: anInteger [ 52 | 53 | ^ (randomNumberGenerator nextBetween: 0 and: (range at: anInteger)) + (origin at: anInteger) 54 | ] 55 | 56 | { #category : #initialization } 57 | PMVectorChromosomeManager >> range: aVector [ 58 | 59 | range := aVector 60 | ] 61 | -------------------------------------------------------------------------------- /src/Math-Numerical/PMVectorAccumulator.class.st: -------------------------------------------------------------------------------- 1 | " 2 | I'm a simple object summing vectors. The idea that I does not keep the data but information that are representative of the accumulated data. 3 | 4 | The class VectorAccumulator has two instance variables: 5 | - count counts the number of vectors accumulated in the object so far; 6 | - average keeps the average of the accumulated vector. 7 | 8 | " 9 | Class { 10 | #name : #PMVectorAccumulator, 11 | #superclass : #Object, 12 | #instVars : [ 13 | 'count', 14 | 'average' 15 | ], 16 | #category : #'Math-Numerical-Math-UtilsAccumulator' 17 | } 18 | 19 | { #category : #'instance creation' } 20 | PMVectorAccumulator class >> new: anInteger [ 21 | ^ self basicNew initialize: anInteger 22 | ] 23 | 24 | { #category : #transformation } 25 | PMVectorAccumulator >> accumulate: aVectorOrArray [ 26 | | delta | 27 | count := count + 1. 28 | delta := average - aVectorOrArray asPMVector scaleBy: 1 / count. 29 | average accumulateNegated: delta. 30 | ^ delta 31 | ] 32 | 33 | { #category : #operations } 34 | PMVectorAccumulator >> average [ 35 | ^ average 36 | ] 37 | 38 | { #category : #operations } 39 | PMVectorAccumulator >> count [ 40 | ^ count 41 | ] 42 | 43 | { #category : #initialization } 44 | PMVectorAccumulator >> initialize: anInteger [ 45 | average := PMVector new: anInteger. 46 | self reset 47 | ] 48 | 49 | { #category : #printing } 50 | PMVectorAccumulator >> printOn: aStream [ 51 | super printOn: aStream. 52 | aStream space. 53 | count printOn: aStream. 54 | aStream space. 55 | average printOn: aStream 56 | ] 57 | 58 | { #category : #initialization } 59 | PMVectorAccumulator >> reset [ 60 | count := 0. 61 | average atAllPut: 0 62 | ] 63 | -------------------------------------------------------------------------------- /src/Math-ODE/PMTranscriptRecorder.class.st: -------------------------------------------------------------------------------- 1 | " 2 | A TranscriptRecorder is a very primitive tool. 3 | It prints a message to transcript when a step is taken by the solver. 4 | 5 | The demo class method shows an example. 6 | " 7 | Class { 8 | #name : #PMTranscriptRecorder, 9 | #superclass : #PMExplicitSolverSubscriber, 10 | #category : #'Math-ODE' 11 | } 12 | 13 | { #category : #example } 14 | PMTranscriptRecorder class >> demo [ 15 | 16 | "self demo" 17 | 18 | | solver system recorder stepper | 19 | "Transcript openAsMorphLabel: 'TransciptRecorderDemo'." 20 | Transcript 21 | cr; 22 | show: 'TranscriptRecorder demo start'; 23 | cr. 24 | 25 | system := PMExplicitSystem block: [ :x :t | x collect: [ :ea | t ] ]. 26 | stepper := PMRungeKuttaStepper onSystem: system. 27 | solver := PMExplicitSolver new 28 | stepper: stepper; 29 | system: system. 30 | 31 | Transcript 32 | show: system block; 33 | cr. 34 | recorder := self forSolver: solver. 35 | "this should be shown, explicit solution is x=1/2*t^2" 36 | solver 37 | solve: system 38 | startState: #( 1 2 3 4 ) 39 | startTime: 0 40 | endTime: 2 41 | stepSize: 0.1. 42 | recorder unsubscribe. 43 | "this should not be shown" 44 | solver 45 | solve: system 46 | startState: #( 0 ) 47 | startTime: 0 48 | endTime: 5 49 | stepSize: 0.1. 50 | Transcript 51 | show: 'TranscriptRecorder demo end'; 52 | cr 53 | ] 54 | 55 | { #category : #accessing } 56 | PMTranscriptRecorder >> defaultBlock [ 57 | "this is the default transcript output" 58 | ^ [:ann | Transcript show: 'state: '; 59 | show: ann state asString ; 60 | tab; tab; 61 | show: ' time: '; 62 | show: ann time asString; 63 | cr ] 64 | ] 65 | -------------------------------------------------------------------------------- /src/Math-Numerical/PMLeastSquares.class.st: -------------------------------------------------------------------------------- 1 | " 2 | SVD-based implementation of a minimum-norm solution to the overdetermined least squares problem. 3 | " 4 | Class { 5 | #name : #PMLeastSquares, 6 | #superclass : #Object, 7 | #category : #'Math-Numerical-Math-LinearAlgebra' 8 | } 9 | 10 | { #category : #accessing } 11 | PMLeastSquares >> pseudoinverseOfDiagonal: aMatrix [ 12 | 13 | "To get pseudoinverse of a diagonal rectangular matrix, we take reciprocal of any no-zero 14 | element of the main diagonal, leaving all zeros in place. Then we transpose the matrix." 15 | 16 | | pseudoinverse diagonalSize | 17 | "Rows become columns and columns become rows because we transpose" 18 | pseudoinverse := PMMatrix zerosRows: aMatrix numberOfColumns cols: aMatrix numberOfRows. 19 | 20 | "The size of the main diagonal of a rectangular matrix is its smallest dimension" 21 | diagonalSize := aMatrix numberOfRows min: aMatrix numberOfColumns. 22 | 23 | "Inverting the elements on the main diaginal" 24 | 1 to: diagonalSize do: [ :i | 25 | pseudoinverse at: i at: i put: ((aMatrix at: i at: i) = 0 ifTrue: [ 0 ] 26 | ifFalse: [ 1 / (aMatrix at: i at: i) ]) ]. 27 | 28 | ^ pseudoinverse 29 | ] 30 | 31 | { #category : #api } 32 | PMLeastSquares >> solveMatrixA: aMatrix matrixB: aMatrixOrVector [ 33 | 34 | "If b is a vector: 35 | x' = minimize || b - Ax || 36 | 37 | If B is a matrix: 38 | X' = minimize || B - AX ||" 39 | 40 | | svd u s v pseudoinverse | 41 | svd := PMSingularValueDecomposition decompose: aMatrix. 42 | 43 | u := svd leftSingularMatrix. 44 | s := svd diagonalSingularValueMatrix. 45 | v := svd rightSingularMatrix. 46 | 47 | pseudoinverse := self pseudoinverseOfDiagonal: s. 48 | ^ v * pseudoinverse * u transpose * aMatrixOrVector 49 | ] 50 | -------------------------------------------------------------------------------- /src/Math-Numerical/PMCovarianceAccumulator.class.st: -------------------------------------------------------------------------------- 1 | " 2 | It has one additional instance variable compared to its superclass: 3 | 4 | - covariance accumulates the components of the covariance matrix; for efficiency reason, only the lower half of the matrix is computed since it is symmetric. 5 | " 6 | Class { 7 | #name : #PMCovarianceAccumulator, 8 | #superclass : #PMVectorAccumulator, 9 | #instVars : [ 10 | 'covariance' 11 | ], 12 | #category : #'Math-Numerical-Math-UtilsAccumulator' 13 | } 14 | 15 | { #category : #transformation } 16 | PMCovarianceAccumulator >> accumulate: anArray [ 17 | "Accumulate anArray into the receiver." 18 | | delta count1 r| 19 | count1 := count. 20 | delta := super accumulate: anArray. 21 | r := count1 / count. 22 | 1 to: delta size 23 | do: [ :n | 24 | 1 to: n do: 25 | [ :m | 26 | ( covariance at: n) at: m put: ( count1 * ( delta at: n) * ( delta at: m) + ( r * ( ( covariance at: n) at: m))). 27 | ]. 28 | ] 29 | ] 30 | 31 | { #category : #information } 32 | PMCovarianceAccumulator >> covarianceMatrix [ 33 | "Answer a matrix containing the covariance of the accumulated data." 34 | | rows n | 35 | n := 0. 36 | rows := covariance collect: 37 | [ :row | n := n + 1. row, ( ( ( n + 1) to: covariance size) collect: [ :m | ( covariance at: m) at: n ])]. 38 | ^PMSymmetricMatrix rows: rows 39 | ] 40 | 41 | { #category : #initialization } 42 | PMCovarianceAccumulator >> initialize: anInteger [ 43 | 44 | covariance := ( ( 1 to: anInteger) collect: [ :n | PMVector new: n]) asPMVector. 45 | ^super initialize: anInteger 46 | ] 47 | 48 | { #category : #initialization } 49 | PMCovarianceAccumulator >> reset [ 50 | "Set all accumulators to zero." 51 | 52 | super reset. 53 | covariance do: [ :each | each atAllPut: 0 ] 54 | ] 55 | -------------------------------------------------------------------------------- /src/Math-ODE/PMStepper.class.st: -------------------------------------------------------------------------------- 1 | " 2 | Basic steppers execute one timestep of a specific order with a given stepsize. 3 | 4 | From odeint-v2 documentation: 5 | 6 | Solving ordinary differential equation numerically is usually done iteratively, that is a given state of an ordinary differential equation is iterated forward x(t) -> x(t+dt) -> x(t+2dt). Steppers perform one single step. The most general stepper type is described by the Stepper concept. 7 | 8 | Before calling doStep, it is important to associate the stepper with a system. The class method onSystem will assign the system to the Stepper. 9 | 10 | 11 | 12 | " 13 | Class { 14 | #name : #PMStepper, 15 | #superclass : #Object, 16 | #instVars : [ 17 | 'system', 18 | 'dt' 19 | ], 20 | #category : #'Math-ODE' 21 | } 22 | 23 | { #category : #'instance creation' } 24 | PMStepper class >> onSystem: aSystem [ 25 | ^ self new system: aSystem. 26 | ] 27 | 28 | { #category : #accessing } 29 | PMStepper class >> order [ 30 | "Answer the order of this stepper, which is undefined by default. Subclasses should override with their details." 31 | ^ nil 32 | ] 33 | 34 | { #category : #stepping } 35 | PMStepper >> doStep: aState time: t stepSize: stepSize [ 36 | "This method should take one step from inState at time t of size dt, then answer it" 37 | self subclassResponsibility 38 | ] 39 | 40 | { #category : #accessing } 41 | PMStepper >> order [ 42 | ^ self class order 43 | ] 44 | 45 | { #category : #accessing } 46 | PMStepper >> stepSize [ 47 | ^ dt 48 | ] 49 | 50 | { #category : #accessing } 51 | PMStepper >> stepSize: timeStep [ 52 | dt := timeStep 53 | ] 54 | 55 | { #category : #accessing } 56 | PMStepper >> system [ 57 | ^ system 58 | ] 59 | 60 | { #category : #accessing } 61 | PMStepper >> system: aSystem [ 62 | system := aSystem 63 | ] 64 | -------------------------------------------------------------------------------- /src/Math-Tests-FunctionFit/PMSimpleParameterFunctionTest.class.st: -------------------------------------------------------------------------------- 1 | Class { 2 | #name : #PMSimpleParameterFunctionTest, 3 | #superclass : #TestCase, 4 | #instVars : [ 5 | 'f' 6 | ], 7 | #category : #'Math-Tests-FunctionFit' 8 | } 9 | 10 | { #category : #running } 11 | PMSimpleParameterFunctionTest >> setUp [ 12 | f:=[:a :x :cc :b|x] 13 | ] 14 | 15 | { #category : #tests } 16 | PMSimpleParameterFunctionTest >> testChangeParametersBy [ 17 | | s | 18 | s := PMSimpleParameterFunction function: f parameters: #(1 1 2). 19 | s changeParametersBy: #(3 2 1). 20 | self assert: s parameters equals: #(4 3 3) 21 | ] 22 | 23 | { #category : #tests } 24 | PMSimpleParameterFunctionTest >> testParameterNames [ 25 | | s | 26 | s := PMSimpleParameterFunction function: f. 27 | self assert: s parameterNames asArray equals: #('x' 'cc' 'b') 28 | ] 29 | 30 | { #category : #tests } 31 | PMSimpleParameterFunctionTest >> testPrint [ 32 | |aStream s| 33 | aStream :=ReadWriteStream with:''. 34 | s:=PMSimpleParameterFunction function:f parameters: #(1 2 3). 35 | s printOn: aStream . 36 | s :=aStream contents . 37 | self assert: ((s beginsWith: 'a PMSimpleParameterFunction')). 38 | self assert: (s includesSubstring: '1'). 39 | self assert: (s includesSubstring: ' 2'). 40 | self assert: (s includesSubstring: ' 3') 41 | ] 42 | 43 | { #category : #tests } 44 | PMSimpleParameterFunctionTest >> testRest [ 45 | 46 | | s vg | 47 | s := PMSimpleParameterFunction function: f. 48 | self assert: s parameters size equals: 3. 49 | vg := s valueAndGradient: 2. 50 | self assert: s parameters first equals: vg first. 51 | self assert: vg second closeTo: #( 1 0 0 ). 52 | s parameters: #( 2 0 0 ). 53 | self assert: (s value: 0) equals: 2. 54 | self assert: s parameters equals: #( 2 0 0 ). 55 | self assert: s parameterSize equals: 3 56 | ] 57 | -------------------------------------------------------------------------------- /src/Math-Distributions/PMRombergIntegrator.class.st: -------------------------------------------------------------------------------- 1 | " 2 | A PMRombergIntegrator implements the Romberg Method. This uses Richardson extrapolation on the Trapezoid method to generate a higher order estimate. 3 | 4 | Evenly spaced samples are used. 5 | 6 | See also: 7 | http://en.wikipedia.org/wiki/Romberg%27s_method 8 | 9 | methods: 10 | 11 | order: anInteger 12 | number of rows in the tableau. 13 | 14 | 15 | " 16 | Class { 17 | #name : #PMRombergIntegrator, 18 | #superclass : #PMTrapezeIntegrator, 19 | #instVars : [ 20 | 'order', 21 | 'points', 22 | 'interpolator' 23 | ], 24 | #category : #'Math-Distributions-Core' 25 | } 26 | 27 | { #category : #information } 28 | PMRombergIntegrator class >> defaultOrder [ 29 | "Private" 30 | ^5 31 | ] 32 | 33 | { #category : #operation } 34 | PMRombergIntegrator >> computeInitialValues [ 35 | "Private" 36 | super computeInitialValues. 37 | points := OrderedCollection new: order. 38 | interpolator := PMNevilleInterpolator points: points. 39 | points add: 1 @ sum 40 | ] 41 | 42 | { #category : #operation } 43 | PMRombergIntegrator >> evaluateIteration [ 44 | | interpolation | 45 | points addLast: (points last x * 0.25) @ self higherOrderSum. 46 | points size < order 47 | ifTrue: [ ^1]. 48 | interpolation := interpolator valueAndError: 0. 49 | points removeFirst. 50 | result := interpolation at: 1. 51 | ^self relativePrecision: ( interpolation at: 2) abs 52 | ] 53 | 54 | { #category : #initialization } 55 | PMRombergIntegrator >> initialize [ 56 | "Private" 57 | order := self class defaultOrder. 58 | ^super initialize 59 | ] 60 | 61 | { #category : #initialization } 62 | PMRombergIntegrator >> order: anInteger [ 63 | anInteger < 2 64 | ifTrue: [ self error: 'Order for Romberg integration must be larger than 1']. 65 | order := anInteger 66 | ] 67 | -------------------------------------------------------------------------------- /src/Math-KDTree/PMIndexedKDTree.class.st: -------------------------------------------------------------------------------- 1 | " 2 | `PMIndexedKDTree` returns the indices of the nearest neighbours instead of the nearest neighbours itself. additionally it returns the squared distances between the vector and its neighbours, if more than one neighbour is searched. 3 | " 4 | Class { 5 | #name : #PMIndexedKDTree, 6 | #superclass : #PMKDTree, 7 | #instVars : [ 8 | 'index' 9 | ], 10 | #category : #'Math-KDTree' 11 | } 12 | 13 | { #category : #'instance creation' } 14 | PMIndexedKDTree class >> withAll: aCollectionOfCollections [ 15 | "make a KDTree from a SequenceableCollection of SequenceableCollections ,which is an ordered Collection of points in a n-dimensional space. the nearest neighbour search then returns the indices of the neighbours" 16 | ^super withAll: (aCollectionOfCollections withIndexCollect: [:v :i| Array with: i with: v ]). 17 | ] 18 | 19 | { #category : #private } 20 | PMIndexedKDTree >> add: aDistance to: aNNStore [ 21 | aNNStore add: (Array with: aDistance with: (Array with: index with: value)) 22 | ] 23 | 24 | { #category : #private } 25 | PMIndexedKDTree >> adjustValue [ 26 | 27 | index :=value first. 28 | value :=value at: 2 29 | ] 30 | 31 | { #category : #evaluating } 32 | PMIndexedKDTree >> nnSearch: aSequenceableCollection i: anInt [ 33 | "search for i nearest neighbours of vector aSequenceableCollection and return the indices; and squared distances (if i>1, at second position)" 34 | |n| 35 | n :=PMNNStore new: anInt . 36 | self nnSearch: aSequenceableCollection asFloatArray near: n . 37 | ^anInt =1 ifTrue: [n data first first ] ifFalse: [n completeData collect: [:i | Array with: (i at:2) first with: i first] ] 38 | ] 39 | 40 | { #category : #private } 41 | PMIndexedKDTree >> sort: aSequenceableCollection [ 42 | ^ aSequenceableCollection sort: [:a :b| ((a at: 2) at: dim) < ((b at: 2) at: dim)] 43 | ] 44 | -------------------------------------------------------------------------------- /src/Math-Benchmarks-ODE/PMExplicitBenchmark.class.st: -------------------------------------------------------------------------------- 1 | Class { 2 | #name : #PMExplicitBenchmark, 3 | #superclass : #PMODEBenchmark, 4 | #instVars : [ 5 | 'system' 6 | ], 7 | #category : #'Math-Benchmarks-ODE' 8 | } 9 | 10 | { #category : #benchmarking } 11 | PMExplicitBenchmark >> benchEuler [ 12 | | solver stepper | 13 | stepper := PMExplicitStepper onSystem: system. 14 | solver := (PMExplicitSolver new) stepper: stepper; system: system; dt: dt. 15 | 1 to: self problemSize do: [ :i 16 | |solver solve: system startState: startState startTime:startTime endTime: endTime] 17 | ] 18 | 19 | { #category : #benchmarking } 20 | PMExplicitBenchmark >> benchHeun [ 21 | | solver stepper | 22 | stepper := PMHeunStepper onSystem: system. 23 | solver := (PMExplicitSolver new) stepper: stepper; system: system; dt: dt. 24 | 1 to: self problemSize do: [ :i 25 | |solver solve: system startState: startState startTime:startTime endTime: endTime] 26 | ] 27 | 28 | { #category : #benchmarking } 29 | PMExplicitBenchmark >> benchMidpoint [ 30 | | solver stepper | 31 | stepper := PMMidpointStepper onSystem: system. 32 | solver := (PMExplicitSolver new) stepper: stepper; system: system; dt: dt. 33 | 1 to: self problemSize do: [ :i 34 | |solver solve: system startState: startState startTime:startTime endTime: endTime] 35 | ] 36 | 37 | { #category : #benchmarking } 38 | PMExplicitBenchmark >> benchRungeKutta [ 39 | | solver stepper | 40 | stepper := PMRungeKuttaStepper onSystem: system. 41 | solver := (PMExplicitSolver new) stepper: stepper; system: system; dt: dt. 42 | 1 to: self problemSize do: [ :i 43 | |solver solve: system startState: startState startTime:startTime endTime: endTime] 44 | ] 45 | 46 | { #category : #running } 47 | PMExplicitBenchmark >> setUp [ 48 | super setUp. 49 | system := PMExplicitSystem block: function 50 | ] 51 | -------------------------------------------------------------------------------- /src/Math-ODE/PMStateRecorder.class.st: -------------------------------------------------------------------------------- 1 | " 2 | A StateRecorder captures each step in an ODESolvers history. 3 | 4 | It stores these as a sorted collection of StateTime object, in increasing time order. 5 | " 6 | Class { 7 | #name : #PMStateRecorder, 8 | #superclass : #PMExplicitSolverSubscriber, 9 | #instVars : [ 10 | 'states' 11 | ], 12 | #category : #'Math-ODE' 13 | } 14 | 15 | { #category : #example } 16 | PMStateRecorder class >> demo [ 17 | 18 | "self demo" 19 | 20 | | solver system recorder stepper | 21 | system := PMExplicitSystem block: [ :x :t | x collect: [ :i | t ] ]. "exact solution x = 0.5 * t squared + x0" 22 | 23 | stepper := PMRungeKuttaStepper onSystem: system. 24 | solver := PMExplicitSolver new 25 | stepper: stepper; 26 | system: system. 27 | 28 | recorder := self forSolver: solver. 29 | "an example of moving backward in time with fractional dt" 30 | solver 31 | solve: system 32 | startState: #( 3 1 ) 33 | startTime: 2 34 | endTime: 0 35 | stepSize: -1 / 10. 36 | 37 | recorder unsubscribe. 38 | 39 | "these wont be captured. an example of moving forward in time with float dt." 40 | solver 41 | solve: system 42 | startState: #( 0 ) 43 | startTime: 0 44 | endTime: 5 45 | stepSize: 0.1. 46 | 47 | recorder states inspect 48 | ] 49 | 50 | { #category : #accessing } 51 | PMStateRecorder >> add: aState at: aTime [ 52 | states add: (PMStateTime state: aState time: aTime) 53 | ] 54 | 55 | { #category : #accessing } 56 | PMStateRecorder >> defaultBlock [ 57 | ^ [:ann | self add: ann state at: ann time] 58 | ] 59 | 60 | { #category : #initialization } 61 | PMStateRecorder >> initialize [ 62 | super initialize. 63 | states := SortedCollection sortBlock: [:x :y | x time < y time]. 64 | ^ self 65 | ] 66 | 67 | { #category : #accessing } 68 | PMStateRecorder >> states [ 69 | ^ states 70 | ] 71 | -------------------------------------------------------------------------------- /src/Math-Numerical/PMFunctionalIterator.class.st: -------------------------------------------------------------------------------- 1 | " 2 | A PMFunctionalIterator is an abstract base class for IterativeProcesses operating on a function. In the sense of these classes, a function is a block or object responding to the value: selector. 3 | 4 | Subclasses will redefine (in addition to methods prescribed in IterativeProcess) 5 | computeInitialValues 6 | 7 | New instances may be created with the function: class method, or existing instances may be assigned a new function with setFunction: 8 | 9 | All functions are required to be single argument blocks, so multivariable functions must be wrapped in a way that value: takes an array of values as input. 10 | 11 | 12 | " 13 | Class { 14 | #name : #PMFunctionalIterator, 15 | #superclass : #PMIterativeProcess, 16 | #instVars : [ 17 | 'functionBlock' 18 | ], 19 | #category : #'Math-Numerical-Math-FunctionIterator' 20 | } 21 | 22 | { #category : #creation } 23 | PMFunctionalIterator class >> function: aBlock [ 24 | "Convenience method to create a instance with given function block." 25 | 26 | ^ self new 27 | setFunction: aBlock; 28 | yourself 29 | ] 30 | 31 | { #category : #operation } 32 | PMFunctionalIterator >> computeInitialValues [ 33 | self subclassResponsibility 34 | ] 35 | 36 | { #category : #operation } 37 | PMFunctionalIterator >> initializeIterations [ 38 | "If no initial value has been defined, take 0 as the starting point (for lack of anything better)." 39 | super initializeIterations. 40 | functionBlock ifNil: [self error: 'No function supplied']. 41 | self computeInitialValues 42 | ] 43 | 44 | { #category : #information } 45 | PMFunctionalIterator >> relativePrecision: aNumber [ 46 | 47 | ^ self precisionOf: aNumber relativeTo: result abs 48 | ] 49 | 50 | { #category : #initialization } 51 | PMFunctionalIterator >> setFunction: aBlock [ 52 | 53 | functionBlock := aBlock 54 | ] 55 | -------------------------------------------------------------------------------- /src/Math-Clustering/PMMahalanobisCenter.class.st: -------------------------------------------------------------------------------- 1 | Class { 2 | #name : #PMMahalanobisCenter, 3 | #superclass : #Object, 4 | #instVars : [ 5 | 'center', 6 | 'inverseCovariance', 7 | 'accumulator' 8 | ], 9 | #category : #'Math-Clustering' 10 | } 11 | 12 | { #category : #creation } 13 | PMMahalanobisCenter class >> new: anInteger [ 14 | 15 | ^ self new initialize: anInteger ; yourself 16 | ] 17 | 18 | { #category : #creation } 19 | PMMahalanobisCenter class >> onVector: aVector [ 20 | 21 | ^ self new center: aVector 22 | ] 23 | 24 | { #category : #transformation } 25 | PMMahalanobisCenter >> accumulate: aVector [ 26 | 27 | accumulator accumulate: aVector 28 | ] 29 | 30 | { #category : #initialization } 31 | PMMahalanobisCenter >> center: aVector [ 32 | 33 | accumulator := PMCovarianceAccumulator new: aVector size. 34 | center := aVector. 35 | inverseCovariance := PMSymmetricMatrix identity: aVector size 36 | ] 37 | 38 | { #category : #transformation } 39 | PMMahalanobisCenter >> computeParameters [ 40 | 41 | center := accumulator average copy. 42 | inverseCovariance := accumulator covarianceMatrix inverse 43 | ] 44 | 45 | { #category : #information } 46 | PMMahalanobisCenter >> count [ 47 | ^accumulator count 48 | ] 49 | 50 | { #category : #information } 51 | PMMahalanobisCenter >> distanceTo: aVector [ 52 | 53 | | delta | 54 | delta := aVector - center. 55 | ^ delta * inverseCovariance * delta 56 | ] 57 | 58 | { #category : #initialization } 59 | PMMahalanobisCenter >> initialize: anInteger [ 60 | 61 | accumulator := PMCovarianceAccumulator new: anInteger 62 | ] 63 | 64 | { #category : #printing } 65 | PMMahalanobisCenter >> printOn: aStream [ 66 | accumulator count printOn: aStream. 67 | aStream nextPutAll: ': '. 68 | center printOn: aStream 69 | ] 70 | 71 | { #category : #transformation } 72 | PMMahalanobisCenter >> reset [ 73 | 74 | accumulator reset 75 | ] 76 | -------------------------------------------------------------------------------- /src/Math-Distributions/PMIncompleteGammaFunction.class.st: -------------------------------------------------------------------------------- 1 | Class { 2 | #name : #PMIncompleteGammaFunction, 3 | #superclass : #Object, 4 | #instVars : [ 5 | 'alpha', 6 | 'alphaLogGamma', 7 | 'series', 8 | 'fraction' 9 | ], 10 | #category : #'Math-Distributions-Gamma' 11 | } 12 | 13 | { #category : #creation } 14 | PMIncompleteGammaFunction class >> shape: aNumber [ 15 | "Defines a new instance of the receiver with paramater aNumber" 16 | ^self new initialize: aNumber 17 | ] 18 | 19 | { #category : #private } 20 | PMIncompleteGammaFunction >> evaluateFraction: aNumber [ 21 | 22 | fraction ifNil: [ 23 | fraction := PMIncompleteGammaFractionTermServer new. 24 | fraction setParameter: alpha ]. 25 | fraction setArgument: aNumber. 26 | ^ (PMContinuedFraction server: fraction) 27 | desiredPrecision: Float defaultComparisonPrecision; 28 | evaluate 29 | ] 30 | 31 | { #category : #private } 32 | PMIncompleteGammaFunction >> evaluateSeries: aNumber [ 33 | 34 | series ifNil: [ 35 | series := PMIncompleteGammaSeriesTermServer new. 36 | series setParameter: alpha ]. 37 | series setArgument: aNumber. 38 | ^ (PMInfiniteSeries server: series) 39 | desiredPrecision: Float defaultComparisonPrecision; 40 | evaluate 41 | ] 42 | 43 | { #category : #initialization } 44 | PMIncompleteGammaFunction >> initialize: aNumber [ 45 | 46 | alpha := aNumber asFloat. 47 | alphaLogGamma := alpha logGamma. 48 | ^self 49 | ] 50 | 51 | { #category : #information } 52 | PMIncompleteGammaFunction >> value: aNumber [ 53 | 54 | | x norm | 55 | aNumber = 0 ifTrue: [ ^ 0 ]. 56 | x := aNumber asFloat. 57 | norm := [ (x ln * alpha - x - alphaLogGamma) exp ] 58 | on: Error 59 | do: [ :signal | signal return: nil ]. 60 | norm ifNil: [ ^ 1 ]. 61 | ^ x - 1 < alpha 62 | ifTrue: [ (self evaluateSeries: x) * norm ] 63 | ifFalse: [ 1 - (norm / (self evaluateFraction: x)) ] 64 | ] 65 | -------------------------------------------------------------------------------- /src/Math-Tests-Statistics/PMStatisticsTests.class.st: -------------------------------------------------------------------------------- 1 | Class { 2 | #name : #PMStatisticsTests, 3 | #superclass : #TestCase, 4 | #category : #'Math-Tests-Statistics' 5 | } 6 | 7 | { #category : #tests } 8 | PMStatisticsTests >> testGeometricMean [ 9 | self 10 | assert: 11 | ((PMStatisticalSample new data: #(1 1 2 3 4 5 5 6 6 7 8 9)) 12 | geometricMean round: 4) 13 | equals: 3.8584. 14 | self 15 | assert: 16 | ((PMStatisticalSample new 17 | data: 18 | {4. 19 | 1. 20 | (1 / 32)}) geometricMean round: 4) 21 | equals: 0.5. 22 | self 23 | assert: 24 | ((PMStatisticalSample new data: #(3.14 1 4.56 0.333)) geometricMean 25 | round: 4) 26 | equals: 1.4777. 27 | self 28 | should: [ (PMStatisticalSample new data: #(-1 5 6.78)) geometricMean ] 29 | raise: Error. 30 | self 31 | assert: 32 | ((PMStatisticalSample geometricMean: 33 | {4. 34 | 1. 35 | (1 / 32)}) round: 4) 36 | equals: 0.5. 37 | ] 38 | 39 | { #category : #tests } 40 | PMStatisticsTests >> testHarmonicMean [ 41 | self 42 | assert: ((PMStatisticalSample new data: #(2.5 3 10)) harmonicMean round: 1) 43 | equals: 3.6. 44 | self 45 | assert: ((PMStatisticalSample harmonicMean: #(2.5 3 10)) round: 1) 46 | equals: 3.6. 47 | self 48 | should: [ (PMStatisticalSample new data: #(3 1.1 -1)) harmonicMean ] 49 | raise: Error 50 | ] 51 | 52 | { #category : #tests } 53 | PMStatisticsTests >> testMode [ 54 | | c | 55 | c := PMStatisticalSample new data: #(1 2 3 4 5 5). 56 | self assert: c mode equals: 5. 57 | c := PMStatisticalSample mode: #(1 2 3 4 5 5). 58 | self assert: c equals: 5. 59 | c := PMStatisticalSample new data: #(5 6 8 3 3 3 2 2 2). 60 | self assert: c mode equals: 2. 61 | c := PMStatisticalSample new data: #(1.1 1.2 1.1 3.4). 62 | self assert: c mode equals: 1.1. 63 | c := PMStatisticalSample new 64 | data: #(dat1 dat2 dat4 dat1 dat4 dat4 dat2 dat9 dat8). 65 | self assert: c mode equals: 'dat4' 66 | ] 67 | -------------------------------------------------------------------------------- /src/Math-AutomaticDifferenciation/PMHessian.class.st: -------------------------------------------------------------------------------- 1 | " 2 | PMHessian calculates the hessian of a function of a SequentialCollection of Numbers as a PMSymmetricMatrix. As a byproduct it also calculates the gradient. 3 | 4 | Example: f(x,y)=x^2 * y 5 | h := PMHessian of:[:x|x first squared * x second]. 6 | h value:#(3 2). ""--> 7 | a PMVector(4 6) 8 | a PMVector(6 0) "" 9 | h gradient ."" -->#(12 9)"" 10 | " 11 | Class { 12 | #name : #PMHessian, 13 | #superclass : #Object, 14 | #instVars : [ 15 | 'function', 16 | 'result', 17 | 'gradient' 18 | ], 19 | #category : #'Math-AutomaticDifferenciation' 20 | } 21 | 22 | { #category : #'instance creation' } 23 | PMHessian class >> of: aNumericalBlock [ 24 | ^self new function: aNumericalBlock 25 | ] 26 | 27 | { #category : #accessing } 28 | PMHessian >> function: aBlock [ 29 | "aBlock must accept an Array as argument" 30 | function := aBlock 31 | ] 32 | 33 | { #category : #accessing } 34 | PMHessian >> gradient [ 35 | ^gradient 36 | ] 37 | 38 | { #category : #printing } 39 | PMHessian >> printOn: aStream [ 40 | aStream 41 | nextPutAll: self class name; 42 | nextPutAll: ' of: ' ; 43 | print: function 44 | ] 45 | 46 | { #category : #accessing } 47 | PMHessian >> result [ 48 | ^ result 49 | ] 50 | 51 | { #category : #accessing } 52 | PMHessian >> value: anArray [ 53 | | hDualValueTemplate hDualValue | 54 | gradient := Array new: anArray size. 55 | hDualValueTemplate := anArray collect: [ :i | PMHyperDualNumber value: i ]. 56 | ^ result := PMSymmetricMatrix 57 | new: anArray size 58 | function: [ :x :y | 59 | | r | 60 | hDualValue := hDualValueTemplate deepCopy. 61 | x = y 62 | ifTrue: [ 63 | (hDualValue at: x) 64 | eps: 1; 65 | eps2: 1 ] 66 | ifFalse: [ 67 | (hDualValue at: x) eps: 1. 68 | (hDualValue at: y) eps2: 1 ]. 69 | r := function value: hDualValue. 70 | x = y 71 | ifTrue: [ gradient at: x put: r eps ]. 72 | r eps1eps2 ] 73 | ] 74 | -------------------------------------------------------------------------------- /src/Math-Distributions/PMChiSquareDistribution.class.st: -------------------------------------------------------------------------------- 1 | Class { 2 | #name : #PMChiSquareDistribution, 3 | #superclass : #PMGammaDistribution, 4 | #category : #'Math-Distributions-Gamma' 5 | } 6 | 7 | { #category : #creation } 8 | PMChiSquareDistribution class >> degreeOfFreedom: anInteger [ 9 | "Create a new instance of the receiver with given degree of freedom." 10 | 11 | ^ anInteger > 40 12 | ifTrue: [ PMAsymptoticChiSquareDistribution degreeOfFreedom: anInteger ] 13 | ifFalse: [ super shape: anInteger / 2 scale: 2 ] 14 | ] 15 | 16 | { #category : #information } 17 | PMChiSquareDistribution class >> distributionName [ 18 | 19 | ^'Chi square distribution' 20 | ] 21 | 22 | { #category : #creation } 23 | PMChiSquareDistribution class >> fromHistogram: aHistogram [ 24 | "Create an instance of the receiver with parameters estimated from the given histogram using best guesses. This method can be used to find the initial values for a fit. " 25 | | dof | 26 | aHistogram minimum < 0 27 | ifTrue: [ ^nil]. 28 | dof := aHistogram average rounded. 29 | ^dof > 0 ifTrue: [ self degreeOfFreedom: aHistogram average rounded] 30 | ifFalse:[ nil] 31 | ] 32 | 33 | { #category : #creation } 34 | PMChiSquareDistribution class >> shape: aNumber1 scale: aNumber2 [ 35 | 36 | ^self error: 'Illegal creation message for this class' 37 | ] 38 | 39 | { #category : #transformation } 40 | PMChiSquareDistribution >> changeParametersBy: aVector [ 41 | "Modify the parameters of the receiver by aVector." 42 | 43 | super changeParametersBy: (Array with: aVector first / 2 with: 0) 44 | ] 45 | 46 | { #category : #information } 47 | PMChiSquareDistribution >> confidenceLevel: aNumber [ 48 | "Answer the probability in percent of finding a chi square value distributed according to the receiver larger than aNumber." 49 | 50 | ^ (1 - (self distributionValue: aNumber)) * 100 51 | ] 52 | 53 | { #category : #information } 54 | PMChiSquareDistribution >> parameters [ 55 | 56 | ^ Array with: alpha * 2 57 | ] 58 | -------------------------------------------------------------------------------- /src/Math-Benchmarks-ODE/PMODEBenchmark.class.st: -------------------------------------------------------------------------------- 1 | Class { 2 | #name : #PMODEBenchmark, 3 | #superclass : #SMarkSuite, 4 | #instVars : [ 5 | 'dt', 6 | 'function', 7 | 'startState', 8 | 'startTime', 9 | 'endTime' 10 | ], 11 | #category : #'Math-Benchmarks-ODE' 12 | } 13 | 14 | { #category : #defaults } 15 | PMODEBenchmark class >> defaultProblemSize [ 16 | ^200 17 | ] 18 | 19 | { #category : #'as yet unclassified' } 20 | PMODEBenchmark class >> runAll: numOfIterations [ 21 | ^ ((self withAllSubclasses collect: [ :suite | 22 | suite run: numOfIterations]) do: [ :suiteResult | 23 | suiteResult results removeKey: #baseBenchmark ]) reject: [ :runner | 24 | runner results isEmpty ] 25 | ] 26 | 27 | { #category : #'as yet unclassified' } 28 | PMODEBenchmark class >> runAllToString: numOfIterations [ 29 | ^ (self runAll: numOfIterations) joinUsing: Character cr 30 | ] 31 | 32 | { #category : #'as yet unclassified' } 33 | PMODEBenchmark class >> runAllToXML: numOfIterations [ 34 | | writer | 35 | writer := XMLWriter new. 36 | writer 37 | enablePrettyPrinting; 38 | xml. 39 | writer tag: 'smark' with: [ 40 | (self runAll: numOfIterations) do: [ :runner | 41 | writer tag: 'suite' attributes: (Dictionary with: #name -> runner suite class name asString) with: [ 42 | runner results keysAndValuesDo: [ :key :value | 43 | writer tag: key with: ((value inject: 0 into: [ :subTotal :result | 44 | subTotal + result total ]) / value size) asFloat asString] ] ] ]. 45 | 46 | ^ writer 47 | ] 48 | 49 | { #category : #running } 50 | PMODEBenchmark >> baseBenchmark [ 51 | 1 to: self problemSize do: [ :i | 52 | self nil ] 53 | ] 54 | 55 | { #category : #running } 56 | PMODEBenchmark >> nil [ 57 | ] 58 | 59 | { #category : #running } 60 | PMODEBenchmark >> setUp [ 61 | dt := 0.09. 62 | function := [:x :t | (t ** 4) * (t sin ** (2 * t cos)) * ( (2 * t negated) * (2 * t sin) * (t sin log) + 63 | t * (2 * t cos) * (t tan reciprocal) + 5)]. 64 | startState := 0. 65 | startTime := 0.3 . 66 | endTime := Float halfPi 67 | ] 68 | -------------------------------------------------------------------------------- /src/Math-StatisticalMoments/PMFastStatisticalMoments.class.st: -------------------------------------------------------------------------------- 1 | Class { 2 | #name : #PMFastStatisticalMoments, 3 | #superclass : #PMStatisticalMoments, 4 | #category : #'Math-StatisticalMoments' 5 | } 6 | 7 | { #category : #transformation } 8 | PMFastStatisticalMoments >> accumulate: aNumber [ 9 | | var | 10 | var := 1. 11 | 1 to: moments size 12 | do: 13 | [:n | 14 | moments at: n put: (moments at: n) + var. 15 | var := var * aNumber] 16 | ] 17 | 18 | { #category : #information } 19 | PMFastStatisticalMoments >> average [ 20 | self count = 0 ifTrue: [^nil]. 21 | ^(moments at: 2) / self count 22 | ] 23 | 24 | { #category : #information } 25 | PMFastStatisticalMoments >> kurtosis [ 26 | 27 | | var x1 x2 x3 x4 kFact kConst n m4 xSquared | 28 | n := self count. 29 | n < 4 ifTrue: [^nil]. 30 | var := self variance. 31 | var = 0 ifTrue: [^nil]. 32 | x1 := (moments at: 2) / n. 33 | x2 := (moments at: 3) / n. 34 | x3 := (moments at: 4) / n. 35 | x4 := (moments at: 5) / n. 36 | xSquared := x1 squared. 37 | m4 := x4 - (4 * x1 * x3) + (6 * x2 * xSquared) - (xSquared squared * 3). 38 | kFact := n * (n + 1) / (n - 1) / (n - 2) / (n - 3). 39 | kConst := 3 * (n - 1) * (n - 1) / (n - 2) / (n - 3). 40 | ^kFact * (m4 * n / var squared) - kConst 41 | ] 42 | 43 | { #category : #information } 44 | PMFastStatisticalMoments >> skewness [ 45 | 46 | | x1 x2 x3 n stdev | 47 | n := self count. 48 | n < 3 ifTrue: [^nil]. 49 | stdev := self standardDeviation. 50 | stdev = 0 ifTrue: [^nil]. 51 | x1 := (moments at: 2) / n. 52 | x2 := (moments at: 3) / n. 53 | x3 := (moments at: 4) / n. 54 | ^(x3 - (3 * x1 * x2) + (2 * x1 * x1 * x1)) * n * n 55 | / (stdev squared * stdev * (n - 1) * (n - 2)) 56 | ] 57 | 58 | { #category : #information } 59 | PMFastStatisticalMoments >> unnormalizedVariance [ 60 | ^(moments at: 3) - ((moments at: 2) squared * self count) 61 | ] 62 | 63 | { #category : #information } 64 | PMFastStatisticalMoments >> variance [ 65 | 66 | | n | 67 | n := self count. 68 | n < 2 ifTrue: [^nil]. 69 | ^((moments at: 3) - ((moments at: 2) squared / n)) / (n - 1) 70 | ] 71 | -------------------------------------------------------------------------------- /src/Math-Tests-ODE/PMEulerSolverTest.class.st: -------------------------------------------------------------------------------- 1 | " 2 | An ODESolverTest is a test class for testing the behavior of ODESolver 3 | " 4 | Class { 5 | #name : 'PMEulerSolverTest', 6 | #superclass : 'PMAbstractTest', 7 | #category : 'Math-Tests-ODE', 8 | #package : 'Math-Tests-ODE' 9 | } 10 | 11 | { #category : 'tests-solving' } 12 | PMEulerSolverTest >> testSimpleSystem2 [ 13 | 14 | | solver stepper system dt | 15 | dt := 1.5. 16 | system := PMExplicitSystem block: [ :x :t | 3 * t negated exp - (0.4 * x) ]. 17 | stepper := PMHeunStepper onSystem: system. 18 | solver := PMExplicitSolver new 19 | stepper: stepper; 20 | system: system; 21 | dt: dt. 22 | self 23 | assert: (solver 24 | solve: system 25 | startState: 5 26 | startTime: 0 27 | endTime: 1.5) 28 | closeTo: 4.30204. 29 | self should: ((solver 30 | solve: system 31 | startState: 0 32 | startTime: 1 33 | endTime: 2.5) closeTo: 0.5158 precision: 0.0001) 34 | ] 35 | 36 | { #category : 'tests-solving' } 37 | PMEulerSolverTest >> testSolveStartStateStartTimeEndTimeStepSize [ 38 | | solver stepper system dt finalState | 39 | dt := 0.2. 40 | system := PMExplicitSystem block: [ :x :t | (t * x) exp ]. 41 | stepper := PMExplicitStepper onSystem: system. 42 | stepper stepSize: dt. 43 | solver := PMExplicitSolver new 44 | system: system; 45 | stepper: stepper. 46 | finalState := solver 47 | solve: solver system 48 | startState: 0 49 | startTime: 0 50 | endTime: 1 51 | stepSize: dt. 52 | self assert: finalState isFloat 53 | ] 54 | 55 | { #category : 'tests-solving' } 56 | PMEulerSolverTest >> testSolveX0T0T1 [ 57 | | solver stepper system dt finalState | 58 | dt := 0.1. 59 | system := PMExplicitSystem block: [ :x :t | (t * x) exp ]. 60 | stepper := PMExplicitStepper onSystem: system. 61 | stepper stepSize: dt. 62 | solver := PMExplicitSolver new 63 | system: system; 64 | stepper: stepper; 65 | dt: dt. 66 | finalState := solver 67 | solve: solver system 68 | startState: 0 69 | startTime: 0 70 | endTime: 1. 71 | self assert: finalState isFloat 72 | ] 73 | -------------------------------------------------------------------------------- /src/Math-ODE/PMSymplecticSystem.class.st: -------------------------------------------------------------------------------- 1 | " 2 | Description 3 | 4 | This concept describes how to define a symplectic system written with generalized coordinate q and generalized momentum p: 5 | 6 | q'(t) = f(p) 7 | 8 | p'(t) = g(q) 9 | 10 | Such a situation is typically found for Hamiltonian systems with a separable Hamiltonian: 11 | 12 | H(p,q) = Hkin(p) + V(q) 13 | 14 | which gives the equations of motion: 15 | 16 | q'(t) = dHkin / dp = f(p) 17 | 18 | p'(t) = dV / dq = g(q) 19 | 20 | The algorithmic implementation of this situation is described by a pair of callable objects for f and g with a specific parameter signature. Such a system should be implemented as a std::pair of functions or a functors. Symplectic systems are used in symplectic steppers like symplectic_rkn_sb3a_mclachlan. 21 | " 22 | Class { 23 | #name : #PMSymplecticSystem, 24 | #superclass : #PMODESystem, 25 | #instVars : [ 26 | 'dqdt', 27 | 'firstBlock', 28 | 'dpdt', 29 | 'secondBlock' 30 | ], 31 | #category : #'Math-ODE' 32 | } 33 | 34 | { #category : #accessing } 35 | PMSymplecticSystem >> dpdt [ 36 | ^ dpdt 37 | ] 38 | 39 | { #category : #accessing } 40 | PMSymplecticSystem >> dpdt: anObject [ 41 | dpdt := anObject 42 | ] 43 | 44 | { #category : #accessing } 45 | PMSymplecticSystem >> dqdt [ 46 | ^ dqdt 47 | ] 48 | 49 | { #category : #accessing } 50 | PMSymplecticSystem >> dqdt: anObject [ 51 | dqdt := anObject 52 | ] 53 | 54 | { #category : #evaluation } 55 | PMSymplecticSystem >> first: pState [ 56 | dqdt := firstBlock value: pState. 57 | ^ dqdt 58 | ] 59 | 60 | { #category : #accessing } 61 | PMSymplecticSystem >> firstBlock [ 62 | ^ firstBlock 63 | ] 64 | 65 | { #category : #accessing } 66 | PMSymplecticSystem >> firstBlock: anObject [ 67 | firstBlock := anObject 68 | ] 69 | 70 | { #category : #evaluation } 71 | PMSymplecticSystem >> second: qState [ 72 | dpdt := secondBlock value: qState. 73 | ^ dpdt 74 | ] 75 | 76 | { #category : #accessing } 77 | PMSymplecticSystem >> secondBlock [ 78 | ^ secondBlock 79 | ] 80 | 81 | { #category : #accessing } 82 | PMSymplecticSystem >> secondBlock: anObject [ 83 | secondBlock := anObject 84 | ] 85 | -------------------------------------------------------------------------------- /src/Math-ODE/PMExplicitSolverSubscriber.class.st: -------------------------------------------------------------------------------- 1 | " 2 | An ExplicitSolverSubscriber implements the minimal behavior to attach to an ODESolver and receive ExplicitSolverAnnouncements. Subclasses should override block to determine appropriate behavior. 3 | 4 | " 5 | Class { 6 | #name : #PMExplicitSolverSubscriber, 7 | #superclass : #Object, 8 | #instVars : [ 9 | 'announcers', 10 | 'block' 11 | ], 12 | #category : #'Math-ODE' 13 | } 14 | 15 | { #category : #'instance creation' } 16 | PMExplicitSolverSubscriber class >> forAnnouncer: anAnnouncer [ 17 | ^ self new forAnnouncer: anAnnouncer 18 | ] 19 | 20 | { #category : #'instance creation' } 21 | PMExplicitSolverSubscriber class >> forSolver: anODESolver [ 22 | ^ self forAnnouncer: anODESolver announcer 23 | ] 24 | 25 | { #category : #accessing } 26 | PMExplicitSolverSubscriber >> announcers [ 27 | 28 | ^ announcers 29 | ] 30 | 31 | { #category : #accessing } 32 | PMExplicitSolverSubscriber >> block [ 33 | ^ block 34 | ] 35 | 36 | { #category : #accessing } 37 | PMExplicitSolverSubscriber >> block: aFormatBlock [ 38 | block := aFormatBlock 39 | ] 40 | 41 | { #category : #accessing } 42 | PMExplicitSolverSubscriber >> defaultBlock [ 43 | ^ self subclassResponsibility 44 | ] 45 | 46 | { #category : #subscription } 47 | PMExplicitSolverSubscriber >> forAnnouncer: anAnnouncer [ 48 | anAnnouncer when: PMExplicitSolverAnnouncement do: self block. 49 | announcers add: anAnnouncer 50 | ] 51 | 52 | { #category : #initialization } 53 | PMExplicitSolverSubscriber >> initialize [ 54 | super initialize. 55 | announcers := IdentitySet new. 56 | block := self defaultBlock. 57 | ^ self 58 | ] 59 | 60 | { #category : #initialization } 61 | PMExplicitSolverSubscriber >> release [ 62 | "stop announcers from sending messages" 63 | self unsubscribe. 64 | super release 65 | ] 66 | 67 | { #category : #subscription } 68 | PMExplicitSolverSubscriber >> unsubscribe [ 69 | announcers do: [:ea | self unsubscribe: ea] 70 | ] 71 | 72 | { #category : #subscription } 73 | PMExplicitSolverSubscriber >> unsubscribe: anAnnouncer [ 74 | anAnnouncer unsubscribe: self. 75 | announcers remove: anAnnouncer ifAbsent: [] 76 | ] 77 | -------------------------------------------------------------------------------- /src/Math-Number-Extensions/Float.extension.st: -------------------------------------------------------------------------------- 1 | Extension { #name : #Float } 2 | 3 | { #category : #'*Math-Number-Extensions-mathematical functions' } 4 | Float >> arCosh [ 5 | "Answer receiver's area hyperbolic cosine. 6 | That is the inverse function of cosh." 7 | 8 | self < 1 9 | ifTrue: 10 | [^DomainError signal: 'Receiver must be greater or equal to 1']. 11 | ^self + 1 = self 12 | ifTrue: [self abs ln + 2 ln] 13 | ifFalse: [((self squared - 1) sqrt + self) ln] 14 | ] 15 | 16 | { #category : #'*Math-Number-Extensions-mathematical functions' } 17 | Float >> arSinh [ 18 | "Answer receiver's area hyperbolic sine. 19 | That is the inverse function of sinh." 20 | 21 | self = 0.0 ifTrue: [^self]. "Handle negativeZero" 22 | ^self + 1 = self 23 | ifTrue: [(self abs ln + 2 ln) * self sign] 24 | ifFalse: [((self squared + 1) sqrt + self) ln] 25 | ] 26 | 27 | { #category : #'*Math-Number-Extensions-mathematical functions' } 28 | Float >> arTanh [ 29 | "Answer receiver's area hyperbolic tangent. 30 | That is the inverse function of tanh." 31 | 32 | self = 0.0 ifTrue: [^self]. "Handle negativeZero" 33 | self abs = 1 ifTrue: [^self copySignTo: Float infinity]. 34 | self abs > 1 35 | ifTrue: 36 | [^DomainError signal: 'Receiver must be between -1.0 and 1.0']. 37 | ^((1 + self) / (1 - self)) ln / 2 38 | ] 39 | 40 | { #category : #'*Math-Number-Extensions-mathematical functions' } 41 | Float >> cosh [ 42 | "Answer receivers hyperbolic cosine." 43 | 44 | | ex | 45 | ex := self exp. 46 | ^(ex + ex reciprocal) / 2 47 | ] 48 | 49 | { #category : #'*Math-Number-Extensions-mathematical functions' } 50 | Float >> sinh [ 51 | "Answer receivers hyperbolic sine" 52 | 53 | | ex | 54 | ex := self exp. 55 | ^(ex - ex reciprocal) / 2 56 | ] 57 | 58 | { #category : #'*Math-Number-Extensions-mathematical functions' } 59 | Float >> tanh [ 60 | "Answer hyperbolic tangent of receiver. 61 | Trivial implementation is: 62 | ^self sinh/self cosh 63 | This implementation takes care not to overflow." 64 | 65 | | ex emx | 66 | self > 20.0 ifTrue: [^1.0]. 67 | self < -20.0 ifTrue: [^-1.0]. 68 | ex := self exp. 69 | emx := ex reciprocal. 70 | ^(ex - emx) / (ex + emx) 71 | ] 72 | -------------------------------------------------------------------------------- /src/Math-Numerical/PMNewtonInterpolator.class.st: -------------------------------------------------------------------------------- 1 | " 2 | A PMNewtonInterpolator is a specialized Lagrange Interpolator which precomputes the polynomial to be evaluated when interpolating. This is twice as expensive as direct evaluation, but produces a linear time (in the number of points) evaluation method for fixed points. 3 | 4 | add: resets the coefficients 5 | 6 | value: lazily initializes the coefficients and yields an interpolated value. 7 | 8 | computeCoefficients and resetCoefficients should be considered private. 9 | " 10 | Class { 11 | #name : #PMNewtonInterpolator, 12 | #superclass : #PMLagrangeInterpolator, 13 | #instVars : [ 14 | 'coefficients' 15 | ], 16 | #category : #'Math-Numerical-Math-Interpolator' 17 | } 18 | 19 | { #category : #transformation } 20 | PMNewtonInterpolator >> add: aPoint [ 21 | "Add a point to the collection of points." 22 | self resetCoefficients. 23 | ^super add: aPoint 24 | ] 25 | 26 | { #category : #information } 27 | PMNewtonInterpolator >> computeCoefficients [ 28 | "Private - Computes the coefficients for the receiver." 29 | | size k1 kn| 30 | size := pointCollection size. 31 | coefficients := ( 1 to: size) collect: [ :n | self yPointAt: n]. 32 | 1 to: (size - 1) 33 | do: [ :n | 34 | size to: ( n + 1) by: -1 35 | do: [ :k | 36 | k1 := k - 1. 37 | kn := k - n. 38 | coefficients at: k put: ( (( coefficients at: k) - ( coefficients at: k1)) 39 | / ((self xPointAt: k) - (self xPointAt: kn))). 40 | ]. 41 | ] 42 | ] 43 | 44 | { #category : #transformation } 45 | PMNewtonInterpolator >> resetCoefficients [ 46 | "Private - Reset the coefficients of the receiver to force a new computation." 47 | coefficients := nil 48 | ] 49 | 50 | { #category : #information } 51 | PMNewtonInterpolator >> value: aNumber [ 52 | "Compute the value of the Lagrange interpolation polynomial on the receiver's points at aNumber." 53 | 54 | | answer size | 55 | coefficients ifNil: [ self computeCoefficients ]. 56 | size := coefficients size. 57 | answer := coefficients at: size. 58 | size - 1 to: 1 by: -1 do: [ :n | answer := answer * (aNumber - (self xPointAt: n)) + (coefficients at: n) ]. 59 | ^ answer 60 | ] 61 | -------------------------------------------------------------------------------- /src/Math-Tests-Numerical/PMNumberExtensionsTestCase.class.st: -------------------------------------------------------------------------------- 1 | " 2 | A DhbNumberExtensionsTestCase is a suite of tests for extension methods to Number, Integer, and Float defined in Didier Besset's book Object Oriented Numerical Methods 3 | " 4 | Class { 5 | #name : 'PMNumberExtensionsTestCase', 6 | #superclass : 'TestCase', 7 | #category : 'Math-Tests-Numerical', 8 | #package : 'Math-Tests-Numerical' 9 | } 10 | 11 | { #category : 'function evaluation' } 12 | PMNumberExtensionsTestCase >> testBeta [ 13 | "Code example 2.14" 14 | 15 | | value | 16 | value := 2.5 gamma * 5.5 gamma / 8 gamma. 17 | self assert: ((2.5 beta: 5.5) - value) abs < 1.0e-14 18 | ] 19 | 20 | { #category : 'function evaluation' } 21 | PMNumberExtensionsTestCase >> testBetaLog [ 22 | "Code example 2.15" 23 | 24 | | value | 25 | value := (2.5 gamma * 5.5 gamma / 8 gamma) ln. 26 | self assert: ((2.5 logBeta: 5.5) - value) abs < 1.0e-13 27 | ] 28 | 29 | { #category : 'function evaluation' } 30 | PMNumberExtensionsTestCase >> testErrorFunctionCentile [ 31 | "Code example 2.5" 32 | 33 | | weight average stDev centile | 34 | weight := 2.85. 35 | average := 3.39. 36 | stDev := 0.44. 37 | centile := ((weight - average) / stDev) errorFunction * 100. 38 | self assert: (centile - 10.986012) abs < 0.000001 39 | ] 40 | 41 | { #category : 'function evaluation' } 42 | PMNumberExtensionsTestCase >> testGamma [ 43 | "Code example 2.10" 44 | 45 | | value | 46 | value := Float pi sqrt * 3 / 4. 47 | self assert: (2.5 gamma - value) abs < 1.0e-14 48 | ] 49 | 50 | { #category : 'function evaluation' } 51 | PMNumberExtensionsTestCase >> testGammaLog [ 52 | "Code example 2.11" 53 | 54 | | value | 55 | value := 2.5 gamma ln. 56 | self assert: (2.5 logGamma - value) abs < 1.0e-13 57 | ] 58 | 59 | { #category : 'function evaluation' } 60 | PMNumberExtensionsTestCase >> testGammaLow [ 61 | | value | 62 | value := Float pi sqrt / 2. 63 | self assert: ((3 / 2) gamma - value) abs < 1.0e-14 64 | ] 65 | 66 | { #category : 'function evaluation' } 67 | PMNumberExtensionsTestCase >> testGammaNegative [ 68 | | value | 69 | value := Float pi / (1.5 gamma * (Float pi / -2) sin). 70 | self assert: ((-1 / 2) gamma - value) abs < 1.0e-14 71 | ] 72 | -------------------------------------------------------------------------------- /src/Math-ArbitraryPrecisionFloat/Fraction.extension.st: -------------------------------------------------------------------------------- 1 | Extension { #name : #Fraction } 2 | 3 | { #category : #'*Math-ArbitraryPrecisionFloat' } 4 | Fraction >> asArbitraryPrecisionFloatNumBits: n [ 5 | "Answer a Floating point with arbitrary precision 6 | close to the receiver." 7 | 8 | "Note: form below would not be the closest approximation 9 | ^ (numerator asArbitraryPrecisionFloatNumBits: n) 10 | inPlaceDivideBy: (denominator asArbitraryPrecisionFloatNumBits: n)" 11 | 12 | | a b mantissa exponent nBits ha hb hm hasTruncatedBits | 13 | a := numerator abs. 14 | b := denominator abs. 15 | ha := a highBit. 16 | hb := b highBit. 17 | 18 | "If both numerator and denominator are represented exactly in floating point number, 19 | then fastest thing to do is to use hardwired float division" 20 | nBits := n + 1. 21 | (ha < nBits and: [hb < nBits]) 22 | ifTrue: 23 | [^(numerator asArbitraryPrecisionFloatNumBits: n) 24 | inPlaceDivideBy: (denominator asArbitraryPrecisionFloatNumBits: n)]. 25 | 26 | "Shift the fraction by a power of two exponent so as to obtain a mantissa with n+1 bits. 27 | First guess is rough, the mantissa might have n+2 bits." 28 | exponent := ha - hb - nBits. 29 | exponent > 0 30 | ifTrue: [b := b bitShift: exponent] 31 | ifFalse: [a := a bitShift: exponent negated]. 32 | mantissa := a quo: b. 33 | hasTruncatedBits := a > (mantissa * b). 34 | hm := mantissa highBit. 35 | 36 | "Remove excess bits in the mantissa." 37 | hm > nBits 38 | ifTrue: 39 | [exponent := exponent + hm - nBits. 40 | hasTruncatedBits := hasTruncatedBits or: [mantissa anyBitOfMagnitudeFrom: 1 to: hm - nBits]. 41 | mantissa := mantissa bitShift: nBits - hm]. 42 | 43 | "Check if mantissa must be rounded upward. 44 | The case of tie (mantissa odd & hasTruncatedBits not) 45 | will be handled by Integer>>asArbitraryPrecisionFloatNumBits:." 46 | (hasTruncatedBits and: [mantissa odd]) 47 | ifTrue: [mantissa := mantissa + 1]. 48 | 49 | "build the ArbitraryPrecisionFloat from mantissa and exponent" 50 | ^(self positive 51 | ifTrue: [mantissa asArbitraryPrecisionFloatNumBits: n] 52 | ifFalse: [mantissa negated asArbitraryPrecisionFloatNumBits: n]) 53 | inPlaceTimesTwoPower: exponent 54 | ] 55 | -------------------------------------------------------------------------------- /src/Math-Tests-Distributions/PMMultivariateNormalDistributionTest.class.st: -------------------------------------------------------------------------------- 1 | Class { 2 | #name : 'PMMultivariateNormalDistributionTest', 3 | #superclass : 'TestCase', 4 | #instVars : [ 5 | 'meanVector', 6 | 'covarianceMatrix', 7 | 'distribution' 8 | ], 9 | #category : 'Math-Tests-Distributions-Normal', 10 | #package : 'Math-Tests-Distributions', 11 | #tag : 'Normal' 12 | } 13 | 14 | { #category : 'running' } 15 | PMMultivariateNormalDistributionTest >> setUp [ 16 | super setUp. 17 | 18 | meanVector := #(0 1) asPMVector. 19 | 20 | covarianceMatrix := PMMatrix rows: #( 21 | (1 0.8) 22 | (0.8 1)). 23 | 24 | distribution := PMMultivariateNormalDistribution 25 | meanVector: meanVector 26 | covarianceMatrix: covarianceMatrix 27 | ] 28 | 29 | { #category : 'tests' } 30 | PMMultivariateNormalDistributionTest >> testAverage [ 31 | self assert: distribution average equals: meanVector 32 | ] 33 | 34 | { #category : 'tests' } 35 | PMMultivariateNormalDistributionTest >> testPower [ 36 | self assert: distribution power equals: 2 37 | ] 38 | 39 | { #category : 'tests' } 40 | PMMultivariateNormalDistributionTest >> testRandom [ 41 | self assert: distribution random size equals: distribution power 42 | ] 43 | 44 | { #category : 'tests' } 45 | PMMultivariateNormalDistributionTest >> testValue [ 46 | | interval points expectedPDF | 47 | 48 | interval := -1 to: 1 by: 0.5. 49 | 50 | points := interval flatCollect: [ :i | 51 | interval collect: [ :j | { i . j } asPMVector ] ]. 52 | 53 | "Values calculated using Python's scipy" 54 | expectedPDF := #(0.021773722141141552 0.08146294715905042 0.1521928217104107 0.14198250365871087 0.06614272766298167 0.0066868859054493935 0.043603973467720346 0.14198250365871087 0.23086080368580453 0.18744427741405117 0.0010254671663260122 0.011654633617442983 0.06614272766298167 0.18744427741405117 0.26525823848649227 7.852830360809077e-05 0.001555527859401226 0.015386363253590088 0.07599775773306434 0.18744427741405117 3.0028751901985315e-06 0.00010367250011136624 0.0017872959519927589 0.015386363253590088 0.06614272766298167). 55 | 56 | 1 to: points size do: [ :i | 57 | self 58 | assert: (distribution value: (points at: i)) 59 | closeTo: (expectedPDF at: i) ] 60 | ] 61 | -------------------------------------------------------------------------------- /src/Math-Numerical/PMMultiVariableGeneralOptimizer.class.st: -------------------------------------------------------------------------------- 1 | Class { 2 | #name : #PMMultiVariableGeneralOptimizer, 3 | #superclass : #PMFunctionOptimizer, 4 | #category : #'Math-Numerical-Math-FunctionIterator' 5 | } 6 | 7 | { #category : #operation } 8 | PMMultiVariableGeneralOptimizer >> computeInitialValues [ 9 | 10 | self range ifNotNil: [ self performGeneticOptimization ]. 11 | self performSimplexOptimization 12 | ] 13 | 14 | { #category : #operation } 15 | PMMultiVariableGeneralOptimizer >> evaluateIteration [ 16 | | optimizer | 17 | optimizer := PMHillClimbingOptimizer forOptimizer: self. 18 | optimizer desiredPrecision: desiredPrecision; 19 | maximumIterations: maximumIterations; 20 | initialValue: result. 21 | result := optimizer evaluate. 22 | ^optimizer precision 23 | ] 24 | 25 | { #category : #operation } 26 | PMMultiVariableGeneralOptimizer >> finalizeIterations [ 27 | ] 28 | 29 | { #category : #initialization } 30 | PMMultiVariableGeneralOptimizer >> origin [ 31 | ^result 32 | ] 33 | 34 | { #category : #initialization } 35 | PMMultiVariableGeneralOptimizer >> origin: anArrayOrVector [ 36 | result := anArrayOrVector 37 | ] 38 | 39 | { #category : #operation } 40 | PMMultiVariableGeneralOptimizer >> performGeneticOptimization [ 41 | "Private" 42 | | optimizer manager | 43 | optimizer := PMGeneticOptimizer forOptimizer: self. 44 | manager := PMVectorChromosomeManager new: 100 mutation: 0.1 crossover: 0.1. 45 | manager origin: self origin asPMVector; range: self range asPMVector. 46 | optimizer chromosomeManager: manager. 47 | result := optimizer evaluate 48 | ] 49 | 50 | { #category : #operation } 51 | PMMultiVariableGeneralOptimizer >> performSimplexOptimization [ 52 | "Private" 53 | 54 | | optimizer | 55 | optimizer := PMSimplexOptimizer forOptimizer: self. 56 | optimizer desiredPrecision: desiredPrecision sqrt; maximumIterations: maximumIterations; initialValue: result asPMVector. 57 | result := optimizer evaluate 58 | ] 59 | 60 | { #category : #initialization } 61 | PMMultiVariableGeneralOptimizer >> range [ 62 | ^self bestPoints 63 | ] 64 | 65 | { #category : #initialization } 66 | PMMultiVariableGeneralOptimizer >> range: anArrayOrVector [ 67 | bestPoints := anArrayOrVector 68 | ] 69 | -------------------------------------------------------------------------------- /src/Math-ODE/PMAB3Stepper.class.st: -------------------------------------------------------------------------------- 1 | " 2 | It is stepper for Adams - Bashforth method of order 3. We can't use AB3 method until we have three old solution values. A AB3 method is explicit. 3 | 4 | " 5 | Class { 6 | #name : #PMAB3Stepper, 7 | #superclass : #PMExplicitMultiStepper, 8 | #category : #'Math-ODE' 9 | } 10 | 11 | { #category : #accessing } 12 | PMAB3Stepper class >> order [ 13 | "AB3 is a third order method." 14 | ^ 3 15 | ] 16 | 17 | { #category : #stepping } 18 | PMAB3Stepper >> doStep: thisState prevState: prevState prevPrevState: prevPrevState prevPrevTime: prevPrevTime [ 19 | 20 | self stepSize ifNil: [ self error: 'step size required by stepper' ]. 21 | ^ self stepSize * (23 / 12 22 | * 23 | (system state: thisState time: prevPrevTime + (2 * self stepSize)) 24 | - 25 | (4 / 3 26 | * (system state: prevState time: prevPrevTime + self stepSize)) 27 | + (5 / 12 * (system state: prevPrevState time: prevPrevTime))) 28 | + thisState 29 | ] 30 | 31 | { #category : #stepping } 32 | PMAB3Stepper >> doStep: thisState prevState: prevState prevPrevState: prevPrevState prevPrevTime: prevPrevTime stepSize: timeStep [ 33 | self stepSize: timeStep. 34 | ^ self doStep: thisState prevState: prevState prevPrevState: prevPrevState prevPrevTime: prevPrevTime 35 | ] 36 | 37 | { #category : #stepping } 38 | PMAB3Stepper >> lastStep: thisState prevState: prevState prevPrevState: prevPrevState prevPrevTime: prevPrevTime deltaT: incrementOfTime [ 39 | 40 | self stepSize ifNil: [ self error: 'step size required by stepper' ]. 41 | 42 | ^ self stepSize * (23 / 12 43 | * 44 | (system 45 | state: thisState 46 | time: prevPrevTime + (2 * incrementOfTime)) - (4 / 3 47 | * (system state: prevState time: prevPrevTime + incrementOfTime)) 48 | + (5 / 12 * (system state: prevPrevState time: prevPrevTime))) 49 | + thisState 50 | ] 51 | 52 | { #category : #stepping } 53 | PMAB3Stepper >> lastStep: thisState prevState: prevState prevPrevState: prevPrevState prevPrevTime: prevPrevTime stepSize: timeStep deltaT: incrementOfTime [ 54 | self stepSize: timeStep. 55 | ^ self lastStep: thisState prevState: prevState prevPrevState: prevPrevState prevPrevTime: prevPrevTime deltaT: incrementOfTime 56 | ] 57 | -------------------------------------------------------------------------------- /src/Math-Numerical/Number.extension.st: -------------------------------------------------------------------------------- 1 | Extension { #name : #Number } 2 | 3 | { #category : #'*Math-Numerical' } 4 | Number >> addPolynomial: aPolynomial [ 5 | ^aPolynomial addNumber: self 6 | ] 7 | 8 | { #category : #'*Math-Numerical' } 9 | Number >> beta: aNumber [ 10 | "Computes the beta function of the receiver and aNumber" 11 | 12 | ^ (self logBeta: aNumber) exp 13 | ] 14 | 15 | { #category : #'*Math-Numerical' } 16 | Number >> dividingPolynomial: aPolynomial [ 17 | ^aPolynomial timesNumber: (1 / self) 18 | ] 19 | 20 | { #category : #'*Math-Numerical' } 21 | Number >> equalsTo: aNumber [ 22 | 23 | self 24 | deprecated: 'Use closeTo: instead' 25 | transformWith: '`@rec equalsTo: `@arg' -> '`@rec closeTo: `@arg'. 26 | 27 | ^ self closeTo: aNumber 28 | ] 29 | 30 | { #category : #'*Math-Numerical' } 31 | Number >> errorFunction [ 32 | "Answer the error function for the receiver." 33 | ^ PMErfApproximation new value: self 34 | ] 35 | 36 | { #category : #'*Math-Numerical' } 37 | Number >> gamma [ 38 | "Compute the Gamma function for the receiver." 39 | ^ self > 1 40 | ifTrue: [ ^ PMLanczosFormula new gamma: self] 41 | ifFalse:[ self < 0 42 | ifTrue: [ Float pi / ( ( Float pi * self) sin * ( 1 - self) gamma)] 43 | ifFalse:[ ( PMLanczosFormula new gamma: ( self + 1)) / self] 44 | ] 45 | ] 46 | 47 | { #category : #'*Math-Numerical' } 48 | Number >> logBeta: aNumber [ 49 | "Computes the logarithm of the beta function of the receiver and aNumber" 50 | 51 | ^ self logGamma + aNumber logGamma - (self + aNumber) logGamma 52 | ] 53 | 54 | { #category : #'*Math-Numerical' } 55 | Number >> logGamma [ 56 | "Computes the log of the Gamma function (for positive numbers only)" 57 | ^self > 1 58 | ifTrue: [ PMLanczosFormula new logGamma: self] 59 | ifFalse:[ self > 0 60 | ifTrue: [ ( PMLanczosFormula new logGamma: ( self + 1) ) - self ln ] 61 | ifFalse: [ ^self error: 'Argument for the log gamma function must be positive'] 62 | ] 63 | ] 64 | 65 | { #category : #'*Math-Numerical' } 66 | Number >> subtractToPolynomial: aPolynomial [ 67 | ^aPolynomial addNumber: self negated 68 | ] 69 | 70 | { #category : #'*Math-Numerical' } 71 | Number >> timesPolynomial: aPolynomial [ 72 | ^aPolynomial timesNumber: self 73 | ] 74 | -------------------------------------------------------------------------------- /src/Math-Distributions/PMIncompleteBetaFunction.class.st: -------------------------------------------------------------------------------- 1 | Class { 2 | #name : #PMIncompleteBetaFunction, 3 | #superclass : #Object, 4 | #instVars : [ 5 | 'alpha1', 6 | 'alpha2', 7 | 'fraction', 8 | 'inverseFraction', 9 | 'logNorm' 10 | ], 11 | #category : #'Math-Distributions-Beta' 12 | } 13 | 14 | { #category : #creation } 15 | PMIncompleteBetaFunction class >> shape: aNumber1 shape: aNumber2 [ 16 | "Create an instance of the receiver with given shape parameters." 17 | 18 | ^self new initialize: aNumber1 shape: aNumber2 19 | ] 20 | 21 | { #category : #private } 22 | PMIncompleteBetaFunction >> evaluateFraction: aNumber [ 23 | 24 | fraction ifNil: [ 25 | fraction := PMIncompleteBetaFractionTermServer new. 26 | fraction setParameter: alpha1 second: alpha2 ]. 27 | fraction setArgument: aNumber. 28 | ^ (PMContinuedFraction server: fraction) 29 | desiredPrecision: Float defaultComparisonPrecision; 30 | evaluate 31 | ] 32 | 33 | { #category : #private } 34 | PMIncompleteBetaFunction >> evaluateInverseFraction: aNumber [ 35 | 36 | inverseFraction ifNil: [ 37 | inverseFraction := PMIncompleteBetaFractionTermServer new. 38 | inverseFraction setParameter: alpha2 second: alpha1 ]. 39 | inverseFraction setArgument: 1 - aNumber. 40 | ^ (PMContinuedFraction server: inverseFraction) 41 | desiredPrecision: Float defaultComparisonPrecision; 42 | evaluate 43 | ] 44 | 45 | { #category : #initialization } 46 | PMIncompleteBetaFunction >> initialize: aNumber1 shape: aNumber2 [ 47 | 48 | alpha1 := aNumber1. 49 | alpha2 := aNumber2. 50 | logNorm := ( alpha1 + alpha2) logGamma - alpha1 logGamma - alpha2 logGamma. 51 | ^self 52 | ] 53 | 54 | { #category : #information } 55 | PMIncompleteBetaFunction >> value: aNumber [ 56 | "Compute the value of the receiver for argument aNumber. 57 | Note: aNumber must be between 0 and 1 (otherwise an exception will occur)." 58 | | norm | 59 | aNumber = 0 60 | ifTrue: [ ^0]. 61 | aNumber = 1 62 | ifTrue: [ ^1]. 63 | norm := ( aNumber ln * alpha1 + ( ( 1 - aNumber) ln * alpha2) + logNorm) exp. 64 | ^( alpha1 + alpha2 + 2) * aNumber < ( alpha1 + 1) 65 | ifTrue: [ norm / ( ( self evaluateFraction: aNumber) * alpha1)] 66 | ifFalse:[ 1 - ( norm / ( ( self evaluateInverseFraction: aNumber) * alpha2))] 67 | ] 68 | -------------------------------------------------------------------------------- /src/Math-Numerical/PMLanczosFormula.class.st: -------------------------------------------------------------------------------- 1 | " 2 | A PMLanczosFormula is a singleton class which calculates an approximation to Gamma(x). 3 | Gamma function is a continuous extension to Factorial. Gamma(x + 1) = x * Gamma(x). For integers Gamma(n) = (n-1) factorial. 4 | 5 | This is called from Number>>gamma, and Number>>logGamma. 6 | 7 | Instance variable coefficients contains the terms of the approximation. 8 | 9 | 10 | The method is detailed in Numerical Recipes in C. The implementation is detailed in Besset's book Section 2.4 11 | 12 | " 13 | Class { 14 | #name : #PMLanczosFormula, 15 | #superclass : #Object, 16 | #instVars : [ 17 | 'coefficients', 18 | 'sqrt2Pi' 19 | ], 20 | #classVars : [ 21 | 'UniqueInstance' 22 | ], 23 | #category : #'Math-Numerical' 24 | } 25 | 26 | { #category : #creation } 27 | PMLanczosFormula class >> new [ 28 | "Answer a unique instance. Create it if it does not exist." 29 | 30 | UniqueInstance ifNil: [ 31 | UniqueInstance := super new. 32 | UniqueInstance initialize ]. 33 | ^ UniqueInstance 34 | ] 35 | 36 | { #category : #'instance creation' } 37 | PMLanczosFormula class >> reset [ 38 | UniqueInstance := nil 39 | ] 40 | 41 | { #category : #information } 42 | PMLanczosFormula >> gamma: aNumber [ 43 | ^ (self leadingFactor: aNumber) exp * (self series: aNumber) * sqrt2Pi / aNumber 44 | ] 45 | 46 | { #category : #initialization } 47 | PMLanczosFormula >> initialize [ 48 | "Private" 49 | 50 | super initialize. 51 | 52 | sqrt2Pi := (Float pi * 2) sqrt. 53 | coefficients := #( 76.18009172947146 -86.50532032941677 24.01409824083091 -1.231739572450155 0.1208650973866179e-2 -0.5395239384953e-5 ) 54 | ] 55 | 56 | { #category : #information } 57 | PMLanczosFormula >> leadingFactor: aNumber [ 58 | "Private" 59 | 60 | | temp | 61 | temp := aNumber + 5.5. 62 | ^ temp ln * (aNumber + 0.5) - temp 63 | ] 64 | 65 | { #category : #information } 66 | PMLanczosFormula >> logGamma: aNumber [ 67 | ^ (self leadingFactor: aNumber) + ((self series: aNumber) * sqrt2Pi / aNumber) ln 68 | ] 69 | 70 | { #category : #information } 71 | PMLanczosFormula >> series: aNumber [ 72 | "Private" 73 | 74 | | term | 75 | term := aNumber. 76 | ^ coefficients 77 | inject: 1.000000000190015 78 | into: [ :sum :each | 79 | term := term + 1. 80 | each / term + sum ] 81 | ] 82 | -------------------------------------------------------------------------------- /src/Math-Tests-PrincipalComponentAnalysis/PMPrincipalComponentAnalyserTest.class.st: -------------------------------------------------------------------------------- 1 | " 2 | The tests in this class compare the output from PolyMath with: 3 | 1) an example from Scikit-Learn's documentation; and 4 | 2) the PCA Tutorial by Lindsay Smith (see http://www.cs.otago.ac.nz/cosc453/student_tutorials/principal_components.pdf). The input data used 5 | are the mean-centred data in section 3.1, step 2. 6 | " 7 | Class { 8 | #name : 'PMPrincipalComponentAnalyserTest', 9 | #superclass : 'TestCase', 10 | #instVars : [ 11 | 'pca' 12 | ], 13 | #category : 'Math-Tests-PrincipalComponentAnalysis', 14 | #package : 'Math-Tests-PrincipalComponentAnalysis' 15 | } 16 | 17 | { #category : 'testing' } 18 | PMPrincipalComponentAnalyserTest class >> isAbstract [ 19 | ^ self name = #PMPrincipalComponentAnalyserTest 20 | ] 21 | 22 | { #category : 'scikit-learn-example' } 23 | PMPrincipalComponentAnalyserTest >> testTransformMatrix [ 24 | | m expected | 25 | m := PMMatrix 26 | rows: #(#(-1 -1) #(-2 -1) #(-3 -2) #(1 1) #(2 1) #(3 2)). 27 | 28 | pca fit: m. 29 | 30 | expected := (PMMatrix 31 | rows: #(#(-0.83849224 -0.54491354) #(0.54491354 -0.83849224))) abs. 32 | self assert: pca transformMatrix abs closeTo: expected 33 | ] 34 | 35 | { #category : 'lindsay-smith-pca-tutorial' } 36 | PMPrincipalComponentAnalyserTest >> testTransformWithMeanCentredMeasurements [ 37 | | meanCentredData transformedData expected | 38 | meanCentredData := PMMatrix rows: #( 39 | #(0.69 0.49) 40 | #(-1.31 -1.21) 41 | #(0.39 0.99) 42 | #(0.09 0.29) 43 | #(1.29 1.09) 44 | #(0.49 0.79) 45 | #(0.19 -0.31) 46 | #(-0.81 -0.81) 47 | #(-0.31 -0.31) 48 | #(-0.71 -1.01) ). 49 | 50 | transformedData := pca fitAndTransform: meanCentredData. 51 | 52 | expected := PMMatrix rows: #( 53 | #(-0.827970186 -0.175115307) 54 | #(1.77758033 0.142857227) 55 | #(-0.992197494 0.384374989) 56 | #(-0.274210416 0.130417207) 57 | #(-1.67580142 -0.209498461) 58 | #(-0.912949103 0.175282444) 59 | #(0.0991094375 -0.349824698) 60 | #(1.14457216 0.0464172582) 61 | #(0.438046137 0.0177646297) 62 | #(1.22382056 -0.162675287)). 63 | 64 | transformedData abs rows with: expected abs rows do: [ :real : expe | 65 | self assert: real closeTo: expe precision: 0.01 ] 66 | ] 67 | -------------------------------------------------------------------------------- /src/Math-FunctionFit/PMErrorAsParameterFunction.class.st: -------------------------------------------------------------------------------- 1 | " 2 | ErrorAsParameterFunction is used internally by ErrorMinimizer. it is essentially a wrapper around an ErrorOfParameterFunction . 3 | " 4 | Class { 5 | #name : #PMErrorAsParameterFunction, 6 | #superclass : #PMSimpleParameterFunction, 7 | #category : #'Math-FunctionFit' 8 | } 9 | 10 | { #category : #accessing } 11 | PMErrorAsParameterFunction >> changeParametersBy: aVector [ 12 | varArray :=varArray + aVector 13 | ] 14 | 15 | { #category : #initialization } 16 | PMErrorAsParameterFunction >> function: anErrorOfParameterFunction [ 17 | |d f size steps| 18 | d :=anErrorOfParameterFunction data. 19 | usedVars :=anErrorOfParameterFunction parameterSize . 20 | self initializeVarArray: 1. 21 | size:=d size sqrt ceiling . 22 | steps:=d size // (usedVars +1). 23 | size :=size +steps //2. 24 | size := size min: steps. 25 | steps :=d size //size -1. 26 | steps > maxFunction [ 35 | "The number of data partitions used. The highest number that can be send to #value:" 36 | 37 | ^ function ifNil: [ 0 ] ifNotNil: [ :f | f size ] 38 | ] 39 | 40 | { #category : #accessing } 41 | PMErrorAsParameterFunction >> parameters [ 42 | ^varArray 43 | ] 44 | 45 | { #category : #accessing } 46 | PMErrorAsParameterFunction >> parameters: indexableCollection [ 47 | ^varArray :=indexableCollection copy 48 | ] 49 | 50 | { #category : #printing } 51 | PMErrorAsParameterFunction >> printOn: aStream [ 52 | 53 | aStream 54 | nextPutAll: 'an '; 55 | nextPutAll: self class name; 56 | nextPutAll: '( function: '; 57 | print: (function ifNotNil: [ :f | f first ]); 58 | nextPutAll: ' maxFunction: '; 59 | print: self maxFunction. 60 | varArray ifNotNil: [ 61 | aStream 62 | nextPutAll: ' parameters: '; 63 | print: self parameters ]. 64 | aStream nextPut: $) 65 | ] 66 | 67 | { #category : #evaluating } 68 | PMErrorAsParameterFunction >> value: aNumber [ 69 | "aNumber chooses the data partition. it can run from 1 to maxFunction." 70 | ^(function at: aNumber) value: varArray 71 | ] 72 | -------------------------------------------------------------------------------- /src/Math-Tests-Distributions/PMKolmogorovsDistributionTest.class.st: -------------------------------------------------------------------------------- 1 | " 2 | data are taken from: http://www.ism.ac.jp/editsec/aism/pdf/054_3_0577.pdf 3 | " 4 | Class { 5 | #name : 'PMKolmogorovsDistributionTest', 6 | #superclass : 'TestCase', 7 | #category : 'Math-Tests-Distributions-KolmogorovSmirnov', 8 | #package : 'Math-Tests-Distributions', 9 | #tag : 'KolmogorovSmirnov' 10 | } 11 | 12 | { #category : 'tests' } 13 | PMKolmogorovsDistributionTest >> testBig [ 14 | | kd | 15 | kd := PMKolmogorovsDistribution exampleSize: 1000. 16 | self assert: (kd average round: 4) equals: (0.02730 round: 4). 17 | self 18 | assert: (kd standardDeviation round: 5) - (0.008229 round: 5) < 2.01e-5. 19 | self assert: (kd skewness round: 2) equals: (0.8616 round: 2). 20 | self assert: (kd kurtosis round: 2) equals: (3.884 round: 2). 21 | kd := PMKolmogorovsDistribution exampleSize: 500. 22 | self assert: ((kd distributionValue: 0.0229) round: 2) equals: 0.05. 23 | self assert: ((kd distributionValue: 0.0605) round: 3) equals: 0.95 24 | ] 25 | 26 | { #category : 'tests' } 27 | PMKolmogorovsDistributionTest >> testMedium [ 28 | | kd | 29 | kd := PMKolmogorovsDistribution exampleSize: 100. 30 | self assert: (kd average round: 3) equals: (0.08519 round: 3). 31 | self 32 | assert: (kd standardDeviation round: 3) 33 | equals: (0.025916 round: 3). 34 | self assert: (kd skewness round: 2) equals: (0.8561 round: 2). 35 | self 36 | assert: ((kd kurtosis round: 2) - (3.869 round: 2)) abs <= 0.0101. "this result is slightly off" 37 | self assert: ((kd distributionValue: 0.0503) round: 3) equals: 0.05. 38 | self assert: ((kd distributionValue: 0.1339) round: 3) equals: 0.95. 39 | self assert: kd parameters equals: #(100) 40 | ] 41 | 42 | { #category : 'tests' } 43 | PMKolmogorovsDistributionTest >> testSmall [ 44 | | kd | 45 | kd := PMKolmogorovsDistribution exampleSize: 10. 46 | self assert: (kd average round: 2) equals: (0.25916 round: 2). 47 | self 48 | assert: (kd standardDeviation round: 3) 49 | equals: (0.079832 round: 3). 50 | self assert: (kd skewness round: 2) equals: (0.8180 round: 2). 51 | self assert: (kd kurtosis round: 2) equals: (3.697 round: 2). 52 | self assert: ((kd distributionValue: 0.1512) round: 3) equals: 0.05. 53 | self assert: ((kd distributionValue: 0.4103) round: 3) equals: 0.95. 54 | self assert: kd parameters equals: #(10) 55 | ] 56 | -------------------------------------------------------------------------------- /src/Math-PrincipalComponentAnalysis/PMSciKitLearnSVDFlipAlgorithm.class.st: -------------------------------------------------------------------------------- 1 | " 2 | This class uses the SciKit-Learn SVD flip algorithm to ensure the signs of the eigenvectors correlate with the trend of the data. 3 | Instance Variables 4 | u: PMMatrix 5 | v: PMMatrix 6 | 7 | " 8 | Class { 9 | #name : 'PMSciKitLearnSVDFlipAlgorithm', 10 | #superclass : 'Object', 11 | #instVars : [ 12 | 'u', 13 | 'v', 14 | 'signs' 15 | ], 16 | #category : 'Math-PrincipalComponentAnalysis', 17 | #package : 'Math-PrincipalComponentAnalysis' 18 | } 19 | 20 | { #category : 'instance creation' } 21 | PMSciKitLearnSVDFlipAlgorithm class >> flipU: u andV: v [ 22 | ^ self new 23 | initializeWithU: u andV: v; 24 | yourself 25 | ] 26 | 27 | { #category : 'accessing' } 28 | PMSciKitLearnSVDFlipAlgorithm >> computeSignsFromU [ 29 | | maxAbsCols i maxElements | 30 | maxAbsCols := u abs argMaxOnColumns. 31 | i := 0. 32 | maxElements := u transpose 33 | rowsCollect: [ :each | 34 | i := i + 1. 35 | each at: (maxAbsCols at: i) ]. 36 | ^ maxElements sign asPMVector 37 | ] 38 | 39 | { #category : 'initialization' } 40 | PMSciKitLearnSVDFlipAlgorithm >> initializeWithU: uMatrix andV: vMatrix [ 41 | "instantiate the algorithm" 42 | u := uMatrix . 43 | v := vMatrix . 44 | signs := self computeSignsFromU 45 | ] 46 | 47 | { #category : 'accessing' } 48 | PMSciKitLearnSVDFlipAlgorithm >> signMatrixForU [ 49 | ^ PMMatrix 50 | rows: 51 | ((1 to: u numberOfRows) 52 | inject: OrderedCollection new 53 | into: [ :rows :eachRow | 54 | rows 55 | add: signs; 56 | yourself ]) 57 | ] 58 | 59 | { #category : 'accessing' } 60 | PMSciKitLearnSVDFlipAlgorithm >> signMatrixForV [ 61 | | signsForV | 62 | signsForV := self signs copyFrom: 1 to: v numberOfColumns. 63 | ^ (PMMatrix 64 | rows: 65 | ((1 to: v numberOfRows) 66 | inject: OrderedCollection new 67 | into: [ :rows :eachRow | 68 | rows 69 | add: signsForV; 70 | yourself ])) transpose 71 | ] 72 | 73 | { #category : 'accessing' } 74 | PMSciKitLearnSVDFlipAlgorithm >> signs [ 75 | ^ signs 76 | ] 77 | 78 | { #category : 'accessing' } 79 | PMSciKitLearnSVDFlipAlgorithm >> uFlipped [ 80 | ^ u hadamardProduct: (self signMatrixForU) 81 | ] 82 | 83 | { #category : 'accessing' } 84 | PMSciKitLearnSVDFlipAlgorithm >> vFlipped [ 85 | ^ v hadamardProduct: (self signMatrixForV) 86 | ] 87 | -------------------------------------------------------------------------------- /src/Math-Numerical/PMBisectionZeroFinder.class.st: -------------------------------------------------------------------------------- 1 | " 2 | I implement Bisection zero finder for the scalar problem f(x)=0 when it is known the single root exists on the interval (a, b) 3 | " 4 | Class { 5 | #name : #PMBisectionZeroFinder, 6 | #superclass : #PMFunctionalIterator, 7 | #instVars : [ 8 | 'positiveX', 9 | 'negativeX' 10 | ], 11 | #category : #'Math-Numerical-Math-FunctionIterator' 12 | } 13 | 14 | { #category : #operation } 15 | PMBisectionZeroFinder >> computeInitialValues [ 16 | "Private" 17 | 18 | positiveX ifNil: [ self error: 'No positive value supplied' ]. 19 | negativeX ifNil: [ self error: 'No negative value supplied' ] 20 | ] 21 | 22 | { #category : #operation } 23 | PMBisectionZeroFinder >> evaluateIteration [ 24 | "Perform one step of bisection." 25 | result := ( positiveX + negativeX) * 0.5. 26 | ( functionBlock value: result) > 0 27 | ifTrue: [ positiveX := result] 28 | ifFalse:[ negativeX := result]. 29 | ^self relativePrecision: ( positiveX - negativeX) abs 30 | ] 31 | 32 | { #category : #operation } 33 | PMBisectionZeroFinder >> findNegativeXFrom: aNumber1 range: aNumber2 [ 34 | | random n | 35 | n := 0. 36 | random := Random new. 37 | 38 | [ negativeX := random next * aNumber2 + aNumber1. 39 | ( functionBlock value: negativeX) < 0 40 | ] whileFalse: [ n := n + 0.1. 41 | n > maximumIterations 42 | ifTrue: [ self error: 'Unable to find a negative function value']. 43 | ] 44 | ] 45 | 46 | { #category : #operation } 47 | PMBisectionZeroFinder >> findPositiveXFrom: aNumber1 range: aNumber2 [ 48 | | n random | 49 | n := 0. 50 | random := Random new. 51 | 52 | [ positiveX := random next * aNumber2 + aNumber1. 53 | ( functionBlock value: positiveX) > 0 54 | ] whileFalse: [ n := n + 1. 55 | n > maximumIterations 56 | ifTrue: [ self error: 'Unable to find a positive function value']. 57 | ] 58 | ] 59 | 60 | { #category : #initialization } 61 | PMBisectionZeroFinder >> setNegativeX: aNumber [ 62 | ( functionBlock value: aNumber) < 0 63 | ifFalse:[ self error: 'Function is not negative at x = ', aNumber printString]. 64 | negativeX := aNumber 65 | ] 66 | 67 | { #category : #initialization } 68 | PMBisectionZeroFinder >> setPositiveX: aNumber [ 69 | ( functionBlock value: aNumber) > 0 70 | ifFalse:[ self error: 'Function is not positive at x = ', aNumber printString]. 71 | positiveX := aNumber 72 | ] 73 | -------------------------------------------------------------------------------- /src/Math-Distributions/PMMitchellMooreGenerator.class.st: -------------------------------------------------------------------------------- 1 | Class { 2 | #name : #PMMitchellMooreGenerator, 3 | #superclass : #Object, 4 | #instVars : [ 5 | 'randoms', 6 | 'lowIndex', 7 | 'highIndex' 8 | ], 9 | #classVars : [ 10 | 'UniqueInstance' 11 | ], 12 | #category : #'Math-Distributions' 13 | } 14 | 15 | { #category : #creation } 16 | PMMitchellMooreGenerator class >> constants: anArray lowIndex: anInteger [ 17 | 18 | ^super new initialize: anArray lowIndex: anInteger 19 | ] 20 | 21 | { #category : #creation } 22 | PMMitchellMooreGenerator class >> default [ 23 | 24 | | congruentialGenerator | 25 | congruentialGenerator := PMCongruentialRandomNumberGenerator new. 26 | ^self generateSeeds: congruentialGenerator 27 | ] 28 | 29 | { #category : #creation } 30 | PMMitchellMooreGenerator class >> generateSeeds: congruentialGenerator [ 31 | "Private- " 32 | ^self constants: ((1 to: 55) collect: [:n | congruentialGenerator floatValue]) 33 | lowIndex: 24 34 | ] 35 | 36 | { #category : #creation } 37 | PMMitchellMooreGenerator class >> new [ 38 | 39 | UniqueInstance ifNil: [ UniqueInstance := self default ]. 40 | ^ UniqueInstance 41 | ] 42 | 43 | { #category : #creation } 44 | PMMitchellMooreGenerator class >> reset: anInteger [ 45 | "Reset the unique instance used for the default series" 46 | UniqueInstance := self seed: anInteger 47 | ] 48 | 49 | { #category : #creation } 50 | PMMitchellMooreGenerator class >> seed: anInteger [ 51 | 52 | | congruentialGenerator | 53 | congruentialGenerator := PMCongruentialRandomNumberGenerator seed: anInteger. 54 | ^self generateSeeds: congruentialGenerator 55 | ] 56 | 57 | { #category : #information } 58 | PMMitchellMooreGenerator >> floatValue [ 59 | 60 | | x | 61 | x := (randoms at: lowIndex) + (randoms at: highIndex). 62 | x < 1.0 ifFalse: [x := x - 1.0]. 63 | randoms at: highIndex put: x. 64 | highIndex := highIndex + 1. 65 | highIndex > randoms size ifTrue: [highIndex := 1]. 66 | lowIndex := lowIndex + 1. 67 | lowIndex > randoms size ifTrue: [lowIndex := 1]. 68 | ^x 69 | ] 70 | 71 | { #category : #initialization } 72 | PMMitchellMooreGenerator >> initialize: anArray lowIndex: anInteger [ 73 | 74 | randoms := anArray. 75 | lowIndex := anInteger. 76 | highIndex := randoms size 77 | ] 78 | 79 | { #category : #information } 80 | PMMitchellMooreGenerator >> integerValue: anInteger [ 81 | 82 | ^( self floatValue * anInteger) truncated 83 | ] 84 | -------------------------------------------------------------------------------- /src/Math-Helpers/PMWeightedPoint.class.st: -------------------------------------------------------------------------------- 1 | " 2 | I'm a simple point (two values with a weight and an error). 3 | " 4 | Class { 5 | #name : #PMWeightedPoint, 6 | #superclass : #Object, 7 | #instVars : [ 8 | 'xValue', 9 | 'yValue', 10 | 'weight', 11 | 'error' 12 | ], 13 | #category : #'Math-Helpers' 14 | } 15 | 16 | { #category : #creation } 17 | PMWeightedPoint class >> point: aPoint [ 18 | 19 | ^ self new initialize: aPoint weight: 1 20 | ] 21 | 22 | { #category : #creation } 23 | PMWeightedPoint class >> point: aNumber count: anInteger [ 24 | 25 | ^ self 26 | point: aNumber @ anInteger 27 | weight: 28 | (anInteger > 0 29 | ifTrue: [ 1 / anInteger ] 30 | ifFalse: [ 1 ]) 31 | ] 32 | 33 | { #category : #creation } 34 | PMWeightedPoint class >> point: aPoint error: aNumber [ 35 | 36 | ^ self new initialize: aPoint error: aNumber 37 | ] 38 | 39 | { #category : #creation } 40 | PMWeightedPoint class >> point: aPoint weight: aNumber [ 41 | 42 | ^ self basicNew initialize: aPoint weight: aNumber 43 | ] 44 | 45 | { #category : #information } 46 | PMWeightedPoint >> chi2ComparisonContribution: aWeightedPoint [ 47 | 48 | ^ (aWeightedPoint yValue - yValue) squared / (1 / aWeightedPoint weight + (1 / weight)) 49 | ] 50 | 51 | { #category : #information } 52 | PMWeightedPoint >> chi2Contribution: aFunction [ 53 | 54 | ^ (yValue - (aFunction value: xValue)) squared * weight 55 | ] 56 | 57 | { #category : #accessing } 58 | PMWeightedPoint >> error [ 59 | 60 | error ifNil: [ error := 1 / weight sqrt ]. 61 | ^ error 62 | ] 63 | 64 | { #category : #initialization } 65 | PMWeightedPoint >> initialize: aPoint error: aNumber [ 66 | 67 | error := aNumber. 68 | ^ self initialize: aPoint weight: 1 / aNumber squared 69 | ] 70 | 71 | { #category : #initialization } 72 | PMWeightedPoint >> initialize: aPoint weight: aNumber [ 73 | 74 | xValue := aPoint x. 75 | yValue := aPoint y. 76 | weight := aNumber. 77 | ^ self 78 | ] 79 | 80 | { #category : #accessing } 81 | PMWeightedPoint >> point [ 82 | 83 | ^ xValue @ yValue 84 | ] 85 | 86 | { #category : #accessing } 87 | PMWeightedPoint >> weight [ 88 | ^weight 89 | ] 90 | 91 | { #category : #accessing } 92 | PMWeightedPoint >> xValue [ 93 | ^xValue 94 | ] 95 | 96 | { #category : #accessing } 97 | PMWeightedPoint >> yValue [ 98 | ^yValue 99 | ] 100 | -------------------------------------------------------------------------------- /src/Math-Tests-ODE/PMBeckwardEulerSolverTest.class.st: -------------------------------------------------------------------------------- 1 | Class { 2 | #name : 'PMBeckwardEulerSolverTest', 3 | #superclass : 'PMAbstractTest', 4 | #category : 'Math-Tests-ODE', 5 | #package : 'Math-Tests-ODE' 6 | } 7 | 8 | { #category : 'tests-solving' } 9 | PMBeckwardEulerSolverTest >> testSimpleSystem [ 10 | 11 | | solver stepper system dt | 12 | dt := 0.01. 13 | system := PMImplicitSystem block: [ :x :t | t sin ]. 14 | stepper := PMImplicitStepper onSystem: system. 15 | solver := PMImplicitSolver new 16 | stepper: stepper; 17 | system: system; 18 | dt: dt. 19 | self 20 | assert: (solver 21 | solve: system 22 | startState: -1 23 | startTime: 0 24 | endTime: Float pi) 25 | closeTo: 1 26 | ] 27 | 28 | { #category : 'tests-solving' } 29 | PMBeckwardEulerSolverTest >> testSimpleSystem2 [ 30 | 31 | | solver stepper system dt | 32 | dt := 1.5. 33 | system := PMImplicitSystem block: [ :x :t | 3 * t negated exp - (0.4 * x) ]. 34 | stepper := PMImplicitStepper onSystem: system. 35 | solver := PMImplicitSolver new 36 | stepper: stepper; 37 | system: system; 38 | dt: dt. 39 | self should: ((solver solve: system 40 | startState: 5 41 | startTime: 0 42 | endTime: 3) closeTo: 0.16567 precision: 0.00001). 43 | self should: ((solver solve: system 44 | startState: 0 45 | startTime: 1 46 | endTime: 2.5) closeTo: -0.5306 precision: 0.0001) 47 | ] 48 | 49 | { #category : 'tests-solving' } 50 | PMBeckwardEulerSolverTest >> testVectorSystem [ 51 | 52 | | solver stepper system dt | 53 | dt := 0.01. 54 | system := PMImplicitSystem block: [ :x :t | 55 | | c | 56 | c := PMVector new: 2. 57 | c at: 1 put: t sin. 58 | c at: 2 put: t cos. 59 | c ]. 60 | stepper := PMImplicitStepper onSystem: system. 61 | solver := PMImplicitSolver new 62 | stepper: stepper; 63 | system: system; 64 | dt: dt. 65 | self 66 | assert: ((solver 67 | solve: system 68 | startState: #( -1 0 ) 69 | startTime: 0 70 | endTime: Float pi) at: 1) 71 | closeTo: 1. 72 | self 73 | assert: ((solver 74 | solve: system 75 | startState: #( -1 0 ) 76 | startTime: 0 77 | endTime: Float pi / 2) at: 2) 78 | closeTo: 0.995 79 | ] 80 | -------------------------------------------------------------------------------- /src/Math-ArbitraryPrecisionFloat/NumberParser.extension.st: -------------------------------------------------------------------------------- 1 | Extension { #name : #NumberParser } 2 | 3 | { #category : #'*Math-ArbitraryPrecisionFloat' } 4 | NumberParser >> makeArbitraryPrecisionFloatFromMantissa: m exponent: k base: aRadix numBits: nBits [ 5 | "Convert infinite precision arithmetic into Floating point with prescribed precision." 6 | 7 | ^(k positive 8 | ifTrue: [m * (aRadix raisedToInteger: k)] 9 | ifFalse: [Fraction numerator: m denominator: (aRadix raisedToInteger: k negated)]) asArbitraryPrecisionFloatNumBits: nBits 10 | ] 11 | 12 | { #category : #'*Math-ArbitraryPrecisionFloat' } 13 | NumberParser >> nextArbitraryPrecisionFloatNumBits: numBits [ 14 | "Always make an ArbitraryPrecisionFloat whether there is a decimal point or not. 15 | Do not bother with radix scale or other things" 16 | 17 | | numberOfTrailingZeroInIntegerPart mantissa numberOfNonZeroFractionDigits numberOfTrailingZeroInFractionPart | 18 | base := 10. 19 | neg := self peekSignIsMinus. 20 | integerPart := self nextUnsignedIntegerBase: base. 21 | numberOfTrailingZeroInIntegerPart := nDigits - lastNonZero. 22 | numberOfTrailingZeroInFractionPart := ((sourceStream peekFor: $.) and: [ (fractionPart := self nextUnsignedIntegerOrNilBase: base) notNil ]) 23 | ifTrue: [ 24 | numberOfNonZeroFractionDigits := lastNonZero. 25 | nDigits - lastNonZero ] 26 | ifFalse: [ 27 | fractionPart := 0. 28 | numberOfNonZeroFractionDigits := 0. 29 | 0 ]. 30 | self readExponent. 31 | exponent := fractionPart isZero 32 | ifTrue: [ 33 | mantissa := integerPart // (base raisedToInteger: numberOfTrailingZeroInIntegerPart). 34 | exponent + numberOfTrailingZeroInIntegerPart ] 35 | ifFalse: [ 36 | mantissa := integerPart * (base raisedToInteger: numberOfNonZeroFractionDigits) 37 | + (fractionPart // (base raisedToInteger: numberOfTrailingZeroInFractionPart)). 38 | exponent - numberOfNonZeroFractionDigits ]. 39 | neg ifTrue: [ mantissa := mantissa negated ]. 40 | ^ self 41 | makeArbitraryPrecisionFloatFromMantissa: mantissa 42 | exponent: exponent 43 | base: base 44 | numBits: numBits 45 | ] 46 | -------------------------------------------------------------------------------- /src/Math-Distributions/PMCongruentialRandomNumberGenerator.class.st: -------------------------------------------------------------------------------- 1 | Class { 2 | #name : #PMCongruentialRandomNumberGenerator, 3 | #superclass : #Object, 4 | #instVars : [ 5 | 'constant', 6 | 'modulus', 7 | 'multiplicator', 8 | 'seed' 9 | ], 10 | #classVars : [ 11 | 'UniqueInstance' 12 | ], 13 | #category : #'Math-Distributions' 14 | } 15 | 16 | { #category : #creation } 17 | PMCongruentialRandomNumberGenerator class >> constant: aNumber1 multiplicator: aNumber2 modulus: aNumber3 [ 18 | 19 | ^super new 20 | initialize: aNumber1 21 | multiplicator: aNumber2 22 | modulus: aNumber3 23 | ] 24 | 25 | { #category : #creation } 26 | PMCongruentialRandomNumberGenerator class >> new [ 27 | "Create a new instance of the receiver with D. Knuth's constants." 28 | 29 | UniqueInstance ifNil: [ 30 | UniqueInstance := super new initialize. 31 | UniqueInstance setSeed: 1 ]. 32 | ^ UniqueInstance 33 | ] 34 | 35 | { #category : #creation } 36 | PMCongruentialRandomNumberGenerator class >> seed: aNumber [ 37 | "Create a new instance of the receiver with given seed using D. Knuth's constants." 38 | ^ super new initialize; setSeed: aNumber; yourself 39 | ] 40 | 41 | { #category : #information } 42 | PMCongruentialRandomNumberGenerator >> floatValue [ 43 | "Answer the next pseudo-random value between 0 and 1." 44 | ^self value asFloat / modulus 45 | ] 46 | 47 | { #category : #initialization } 48 | PMCongruentialRandomNumberGenerator >> initialize [ 49 | 50 | super initialize. 51 | 52 | self initialize: 2718281829.0 multiplicator: 3141592653.0 modulus: 4294967296.0 53 | ] 54 | 55 | { #category : #initialization } 56 | PMCongruentialRandomNumberGenerator >> initialize: aNumber1 multiplicator: aNumber2 modulus: aNumber3 [ 57 | 58 | constant := aNumber1. 59 | modulus := aNumber2. 60 | multiplicator := aNumber3. 61 | self setSeed: 1 62 | ] 63 | 64 | { #category : #information } 65 | PMCongruentialRandomNumberGenerator >> integerValue: anInteger [ 66 | "Answer a random integer between 0 and the anInteger." 67 | ^( self value \\ ( anInteger * 1000)) // 1000 68 | ] 69 | 70 | { #category : #transformation } 71 | PMCongruentialRandomNumberGenerator >> setSeed: aNumber [ 72 | "Set the seed of the receiver to aNumber." 73 | seed := aNumber 74 | ] 75 | 76 | { #category : #information } 77 | PMCongruentialRandomNumberGenerator >> value [ 78 | "Answer the next pseudo-random value." 79 | seed := ( seed * multiplicator + constant) \\ modulus. 80 | ^seed 81 | ] 82 | -------------------------------------------------------------------------------- /src/Math-Tests-ODE/PMAM4StepperTest.class.st: -------------------------------------------------------------------------------- 1 | Class { 2 | #name : 'PMAM4StepperTest', 3 | #superclass : 'PMAbstractTest', 4 | #category : 'Math-Tests-ODE', 5 | #package : 'Math-Tests-ODE' 6 | } 7 | 8 | { #category : 'tests-stepping' } 9 | PMAM4StepperTest >> testOrderIsFour [ 10 | self assert: PMAM4Stepper order equals: 4 11 | ] 12 | 13 | { #category : 'tests-stepping' } 14 | PMAM4StepperTest >> testSimpleSystem [ 15 | 16 | | solver stepper system dt | 17 | dt := 0.01. 18 | system := PMImplicitSystem block: [ :x :t | t sin ]. 19 | stepper := PMAM4Stepper onSystem: system. 20 | solver := PMAM4Solver new 21 | stepper: stepper; 22 | system: system; 23 | dt: dt. 24 | self 25 | assert: (solver 26 | solve: system 27 | startState: -1 28 | startTime: 0 29 | endTime: Float pi) 30 | closeTo: 1 31 | ] 32 | 33 | { #category : 'tests-stepping' } 34 | PMAM4StepperTest >> testSimpleSystem2 [ 35 | | solver stepper system dt | 36 | dt := 1.5. 37 | system := PMImplicitSystem block: [:x :t | 3 * (t negated exp) - (0.4 * x)]. 38 | stepper := PMAM4Stepper onSystem: system. 39 | solver := (PMAM4Solver new) stepper: stepper; system: system; dt: dt. 40 | self should: ((solver solve: system startState: 5 startTime: 0 endTime: 1.7) closeTo: 4.5912 precision: 0.0001). 41 | self should: ((solver solve: system startState: 5 startTime: 0 endTime: 1.5) closeTo: 4.9614 precision: 0.0001). 42 | self should: ((solver solve: system startState: 5 startTime: 0 endTime: 3.0) closeTo: 2.6231 precision: 0.0001). 43 | self should: ((solver solve: system startState: 0.1 startTime: 2 endTime: 4.1) closeTo: 0.2908 precision: 0.0001) 44 | ] 45 | 46 | { #category : 'tests-stepping' } 47 | PMAM4StepperTest >> testSimpleSystem3 [ 48 | 49 | | solver stepper system dt | 50 | dt := 0.5. 51 | system := PMImplicitSystem block: [ :x :t | 2 * t * x ]. 52 | stepper := PMAM4Stepper onSystem: system. 53 | solver := PMAM4Solver new 54 | stepper: stepper; 55 | system: system; 56 | dt: dt. 57 | self 58 | assert: (solver 59 | solve: system 60 | startState: 1 61 | startTime: 0 62 | endTime: 0.5) 63 | closeTo: 1.3125. 64 | self 65 | assert: (solver 66 | solve: system 67 | startState: 1.25 68 | startTime: 0.5 69 | endTime: 1.3) 70 | closeTo: 6.7429. 71 | self 72 | assert: (solver 73 | solve: system 74 | startState: 1 75 | startTime: 0 76 | endTime: 1.3) 77 | closeTo: 9.6147 78 | ] 79 | -------------------------------------------------------------------------------- /src/Math-ODE/PMAB4Stepper.class.st: -------------------------------------------------------------------------------- 1 | " 2 | It is stepper for Adams - Bashforth method of order 4. We can't use AB4 method until we have three old solution values. A AB4 method is explicit. 3 | 4 | " 5 | Class { 6 | #name : #PMAB4Stepper, 7 | #superclass : #PMExplicitMultiStepper, 8 | #category : #'Math-ODE' 9 | } 10 | 11 | { #category : #accessing } 12 | PMAB4Stepper class >> order [ 13 | "AB4 is a fourth order method." 14 | ^ 4 15 | ] 16 | 17 | { #category : #stepping } 18 | PMAB4Stepper >> doStep3State: thirdState secondState: secondState firstState: firstState initState: initState initTime: initTime [ 19 | 20 | self stepSize ifNil: [ self error: 'step size required by stepper' ]. 21 | ^ self stepSize * (55 / 24 22 | * (system state: thirdState time: initTime + (3 * self stepSize)) 23 | - (59 / 24 24 | * 25 | (system state: secondState time: initTime + (2 * self stepSize))) 26 | + 27 | (37 / 24 28 | * (system state: firstState time: initTime + self stepSize)) 29 | - (3 / 8 * (system state: initState time: initTime))) + thirdState 30 | ] 31 | 32 | { #category : #stepping } 33 | PMAB4Stepper >> doStep3State: thirdState secondState:secondState firstState: firstState initState: initState initTime: initTime stepSize: timeStep [ 34 | self stepSize: timeStep. 35 | ^ self doStep3State: thirdState secondState:secondState firstState: firstState initState: initState initTime: initTime 36 | ] 37 | 38 | { #category : #stepping } 39 | PMAB4Stepper >> lastStep: thirdState secondState: secondState firstState: firstState initState: initState initTime: initTime deltaT: incrementOfTime [ 40 | 41 | self stepSize ifNil: [ self error: 'step size required by stepper' ]. 42 | ^ self stepSize * (55 / 24 43 | * 44 | (system state: thirdState time: initTime + (3 * incrementOfTime)) 45 | - (59 / 24 46 | * 47 | (system 48 | state: secondState 49 | time: initTime + (2 * incrementOfTime))) 50 | + 51 | (37 / 24 52 | * (system state: firstState time: initTime + incrementOfTime)) 53 | - (3 / 8 * (system state: initState time: initTime))) + thirdState 54 | ] 55 | 56 | { #category : #stepping } 57 | PMAB4Stepper >> lastStep: thirdState secondState:secondState firstState: firstState initState: initState initTime: initTime deltaT: incrementOfTime stepSize: timeStep [ 58 | 59 | self stepSize: timeStep. 60 | ^ self lastStep: thirdState secondState:secondState firstState: firstState initState: initState initTime: initTime deltaT: incrementOfTime 61 | ] 62 | -------------------------------------------------------------------------------- /src/Math-Tests-ODE/PMAB3StepperTest.class.st: -------------------------------------------------------------------------------- 1 | Class { 2 | #name : 'PMAB3StepperTest', 3 | #superclass : 'PMAbstractTest', 4 | #category : 'Math-Tests-ODE', 5 | #package : 'Math-Tests-ODE' 6 | } 7 | 8 | { #category : 'tests-stepping' } 9 | PMAB3StepperTest >> testOrderIsThree [ 10 | self assert: PMAB3Stepper order equals: 3 11 | ] 12 | 13 | { #category : 'tests-stepping' } 14 | PMAB3StepperTest >> testSimpleSystem [ 15 | 16 | | solver stepper system dt | 17 | dt := 0.01. 18 | system := PMExplicitSystem block: [ :x :t | t sin ]. 19 | stepper := PMAB3Stepper onSystem: system. 20 | solver := PMAB3Solver new 21 | stepper: stepper; 22 | system: system; 23 | dt: dt. 24 | self 25 | assert: (solver 26 | solve: system 27 | startState: -1 28 | startTime: 0 29 | endTime: Float pi) 30 | closeTo: 1 31 | ] 32 | 33 | { #category : 'tests-stepping' } 34 | PMAB3StepperTest >> testSimpleSystem2 [ 35 | 36 | | solver stepper system dt | 37 | dt := 1.5. 38 | system := PMExplicitSystem block: [ :x :t | 3 * t negated exp - (0.4 * x) ]. 39 | stepper := PMAB3Stepper onSystem: system. 40 | solver := PMAB3Solver new 41 | stepper: stepper; 42 | system: system; 43 | dt: dt. 44 | self 45 | assert: (solver solve: system 46 | startState: 5 47 | startTime: 0 48 | endTime: 1.5) 49 | closeTo: 3.67565 50 | precision: 0.00001. 51 | self should: ((solver solve: system 52 | startState: 0 53 | startTime: 1 54 | endTime: 2.5) closeTo: 0.28534 precision: 0.00001) 55 | ] 56 | 57 | { #category : 'tests-stepping' } 58 | PMAB3StepperTest >> testSimpleSystem3 [ 59 | 60 | | solver stepper system dt | 61 | dt := 0.5. 62 | system := PMExplicitSystem block: [ :x :t | 2 * t * x ]. 63 | stepper := PMAB3Stepper onSystem: system. 64 | solver := PMAB3Solver new 65 | stepper: stepper; 66 | system: system; 67 | dt: dt. 68 | self 69 | assert: (solver 70 | solve: system 71 | startState: 1 72 | startTime: 0 73 | endTime: 0.5) 74 | closeTo: 1.25. 75 | self 76 | assert: (solver 77 | solve: system 78 | startState: 1.25 79 | startTime: 0.5 80 | endTime: 1) 81 | closeTo: 2.4219. 82 | self 83 | assert: (solver 84 | solve: system 85 | startState: 2.4219 86 | startTime: 0.5 87 | endTime: 1) 88 | closeTo: 4.6924 89 | ] 90 | -------------------------------------------------------------------------------- /src/Math-Tests-ODE/PMAB4StepperTest.class.st: -------------------------------------------------------------------------------- 1 | Class { 2 | #name : 'PMAB4StepperTest', 3 | #superclass : 'PMAbstractTest', 4 | #category : 'Math-Tests-ODE', 5 | #package : 'Math-Tests-ODE' 6 | } 7 | 8 | { #category : 'tests-stepping' } 9 | PMAB4StepperTest >> testOrderIsFour [ 10 | self assert: PMAB4Stepper order equals: 4 11 | ] 12 | 13 | { #category : 'tests-stepping' } 14 | PMAB4StepperTest >> testSimpleSystem [ 15 | 16 | | solver stepper system dt | 17 | dt := 0.01. 18 | system := PMExplicitSystem block: [ :x :t | t sin ]. 19 | stepper := PMAB4Stepper onSystem: system. 20 | solver := PMAB4Solver new 21 | stepper: stepper; 22 | system: system; 23 | dt: dt. 24 | self 25 | assert: (solver 26 | solve: system 27 | startState: -1 28 | startTime: 0 29 | endTime: Float pi) 30 | closeTo: 1 31 | ] 32 | 33 | { #category : 'tests-stepping' } 34 | PMAB4StepperTest >> testSimpleSystem2 [ 35 | 36 | | solver stepper system dt | 37 | dt := 1.5. 38 | system := PMExplicitSystem block: [ :x :t | 3 * t negated exp - (0.4 * x) ]. 39 | stepper := PMAB4Stepper onSystem: system. 40 | solver := PMAB4Solver new 41 | stepper: stepper; 42 | system: system; 43 | dt: dt. 44 | self 45 | assert: (solver solve: system 46 | startState: 5 47 | startTime: 0 48 | endTime: 1.5) 49 | closeTo: 3.67565 50 | precision: 0.00001. 51 | self should: ((solver solve: system 52 | startState: 0 53 | startTime: 1 54 | endTime: 2.5) closeTo: 0.28534 precision: 0.00001) 55 | ] 56 | 57 | { #category : 'tests-stepping' } 58 | PMAB4StepperTest >> testSimpleSystem3 [ 59 | 60 | | solver stepper system dt | 61 | dt := 0.5. 62 | system := PMExplicitSystem block: [ :x :t | 2 * t * x ]. 63 | stepper := PMAB4Stepper onSystem: system. 64 | solver := PMAB4Solver new 65 | stepper: stepper; 66 | system: system; 67 | dt: dt. 68 | self 69 | assert: (solver 70 | solve: system 71 | startState: 1 72 | startTime: 0 73 | endTime: 0.5) 74 | closeTo: 1.25. 75 | self 76 | assert: (solver 77 | solve: system 78 | startState: 1.25 79 | startTime: 0.5 80 | endTime: 1) 81 | closeTo: 2.4219. 82 | self 83 | assert: (solver 84 | solve: system 85 | startState: 2.4219 86 | startTime: 0.5 87 | endTime: 1) 88 | closeTo: 4.6924 89 | ] 90 | -------------------------------------------------------------------------------- /src/Math-ODE/PMTrapezoidStepper.class.st: -------------------------------------------------------------------------------- 1 | " 2 | The trapezoidal rule is an implicit second-order method, which can be considered as both a Runge-Kutta method and a linear multistep method. It is Adams - Moulton method of order 2. 3 | 4 | 5 | 6 | " 7 | Class { 8 | #name : #PMTrapezoidStepper, 9 | #superclass : #PMImplicitMultiStepper, 10 | #category : #'Math-ODE' 11 | } 12 | 13 | { #category : #accessing } 14 | PMTrapezoidStepper class >> order [ 15 | "Trapezoid is a second order method." 16 | ^ 2 17 | ] 18 | 19 | { #category : #stepping } 20 | PMTrapezoidStepper >> doStep: aState time: t [ 21 | 22 | "This method should take one step from inState at time t of size dt, and modify the state, then answer it. " 23 | 24 | | xi1 xi2 ti | 25 | self stepSize ifNil: [ self error: 'step size required by stepper' ]. 26 | ti := t + self stepSize. 27 | xi1 := aState + (self stepSize * (system state: aState time: t)). 28 | xi2 := aState + (1 / 2 * self stepSize 29 | * 30 | ((system state: aState time: t) 31 | + (system state: xi1 time: ti))). 32 | ^ aState + (1 / 2 * self stepSize 33 | * ((system state: aState time: t) + (system state: xi2 time: ti))) 34 | ] 35 | 36 | { #category : #stepping } 37 | PMTrapezoidStepper >> doStep: aState time: t stepSize: timeStep [ 38 | "This method should take one step from inState at time t of size dt, and modify the state, then answer it. " 39 | self stepSize: timeStep. 40 | ^ self doStep: aState time: t 41 | ] 42 | 43 | { #category : #stepping } 44 | PMTrapezoidStepper >> lastStep: aState time: t deltaT: incrementOfTime [ 45 | 46 | "This method should take one step from inState at time t of size dt, and modify the state, then answer it. " 47 | 48 | | xi1 xi2 ti | 49 | self stepSize ifNil: [ self error: 'step size required by stepper' ]. 50 | ti := t + incrementOfTime. 51 | xi1 := aState + (self stepSize * (system state: aState time: t)). 52 | xi2 := aState + (1 / 2 * self stepSize 53 | * 54 | ((system state: aState time: t) 55 | + (system state: xi1 time: ti))). 56 | ^ aState + (1 / 2 * self stepSize 57 | * ((system state: aState time: t) + (system state: xi2 time: ti))) 58 | ] 59 | 60 | { #category : #stepping } 61 | PMTrapezoidStepper >> lastStep: aState time: t stepSize: timeStep deltaT: incrementOfTime [ 62 | "This method should take one step from inState at time t of size dt, and modify the state, then answer it. " 63 | self stepSize: timeStep. 64 | ^ self lastStep: aState time: t deltaT: incrementOfTime 65 | ] 66 | --------------------------------------------------------------------------------