Mixed.js (10120B)
1 import * as XYShape from "../XYShape.js"; 2 import * as Continuous from "./Continuous.js"; 3 import * as Discrete from "./Discrete.js"; 4 import * as MixedPoint from "./MixedPoint.js"; 5 import * as Result from "../utility/result.js"; 6 import * as Common from "./Common.js"; 7 import { ContinuousShape } from "./Continuous.js"; 8 import { DiscreteShape } from "./Discrete.js"; 9 export class MixedShape { 10 constructor(args) { 11 this.continuous = args.continuous; 12 this.discrete = args.discrete; 13 this._integralSumCache = args.integralSumCache; 14 this._integralCache = args.integralCache; 15 } 16 get integralCache() { 17 return this._integralCache; 18 } 19 get integralSumCache() { 20 return this._integralSumCache; 21 } 22 withAdjustedIntegralSum(integralSumCache) { 23 return new MixedShape({ 24 continuous: this.continuous, 25 discrete: this.discrete, 26 integralSumCache, 27 integralCache: this.integralCache, 28 }); 29 } 30 minX() { 31 return Math.min(this.continuous.minX(), this.discrete.minX()); 32 } 33 maxX() { 34 return Math.max(this.continuous.maxX(), this.discrete.maxX()); 35 } 36 isEmpty() { 37 return this.continuous.isEmpty() && this.discrete.isEmpty(); 38 } 39 toContinuous() { 40 return this.continuous; 41 } 42 toDiscrete() { 43 return this.discrete; 44 } 45 toMixed() { 46 return this; 47 } 48 isEqual(other) { 49 return (this.continuous.isEqual(other.continuous) && 50 this.discrete.isEqual(other.discrete)); 51 } 52 truncate(leftCutoff, rightCutoff) { 53 return new MixedShape({ 54 continuous: this.continuous.truncate(leftCutoff, rightCutoff), 55 discrete: this.discrete.truncate(leftCutoff, rightCutoff), 56 }); 57 } 58 normalize() { 59 if (this.isEmpty()) { 60 return this; 61 } 62 if (this.continuous.isEmpty()) { 63 return this.discrete.normalize().toMixed(); 64 } 65 if (this.discrete.isEmpty()) { 66 return this.continuous.normalize().toMixed(); 67 } 68 const continuousIntegralSum = this.continuous.integralSum(); 69 const discreteIntegralSum = this.discrete.integralSum(); 70 const totalIntegralSum = continuousIntegralSum + discreteIntegralSum; 71 const newContinuousSum = continuousIntegralSum / totalIntegralSum; 72 const newDiscreteSum = discreteIntegralSum / totalIntegralSum; 73 const normalizedContinuous = this.continuous 74 .scaleBy(newContinuousSum / continuousIntegralSum) 75 .withAdjustedIntegralSum(newContinuousSum); 76 const normalizedDiscrete = this.discrete 77 .scaleBy(newDiscreteSum / discreteIntegralSum) 78 .withAdjustedIntegralSum(newDiscreteSum); 79 return new MixedShape({ 80 continuous: normalizedContinuous, 81 discrete: normalizedDiscrete, 82 integralSumCache: 1, 83 }); 84 } 85 xToY(x) { 86 const { continuous, discrete } = this.normalize(); 87 const c = continuous.xToY(x); 88 const d = discrete.xToY(x); 89 return MixedPoint.add(c, d); 90 } 91 toDiscreteProbabilityMassFraction() { 92 const discreteIntegralSum = this.discrete.integralSum(); 93 const continuousIntegralSum = this.continuous.integralSum(); 94 const totalIntegralSum = discreteIntegralSum + continuousIntegralSum; 95 return discreteIntegralSum / totalIntegralSum; 96 } 97 downsample(count) { 98 const discreteIntegralSum = this.discrete.integralSum(); 99 const continuousIntegralSum = this.continuous.integralSum(); 100 const totalIntegralSum = discreteIntegralSum + continuousIntegralSum; 101 const downsampledDiscrete = this.discrete.downsample(Math.floor((count * discreteIntegralSum) / totalIntegralSum)); 102 const downsampledContinuous = this.continuous.downsample(Math.floor((count * continuousIntegralSum) / totalIntegralSum)); 103 return new MixedShape({ 104 continuous: downsampledContinuous, 105 discrete: downsampledDiscrete, 106 integralSumCache: this.integralSumCache, 107 integralCache: this.integralCache, 108 }); 109 } 110 integral() { 111 if (!this._integralCache) { 112 const continuousIntegral = this.continuous.integral(); 113 const discreteIntegral = Continuous.stepwiseToLinear(this.discrete.integral()); 114 this._integralCache = new ContinuousShape({ 115 xyShape: XYShape.PointwiseCombination.addCombine(XYShape.XtoY.continuousInterpolator("Linear", "UseOutermostPoints"), continuousIntegral.xyShape, discreteIntegral.xyShape), 116 }); 117 } 118 return this._integralCache; 119 } 120 integralSum() { 121 return (this._integralSumCache ??= this.integral().lastY()); 122 } 123 integralXtoY(f) { 124 return XYShape.XtoY.linear(this.integral().xyShape, f); 125 } 126 integralYtoX(f) { 127 return XYShape.YtoX.linear(this.integral().xyShape, f); 128 } 129 mapY(fn, integralSumCacheFn, integralCacheFn) { 130 const discrete = this.discrete.mapY(fn, integralSumCacheFn, integralCacheFn); 131 const continuous = this.continuous.mapY(fn, integralSumCacheFn, integralCacheFn); 132 return new MixedShape({ 133 discrete, 134 continuous, 135 integralSumCache: this.integralSumCache === undefined 136 ? undefined 137 : integralSumCacheFn?.(this.integralSumCache), 138 integralCache: this.integralCache === undefined 139 ? undefined 140 : integralCacheFn?.(this.integralCache), 141 }); 142 } 143 mapYResult(fn, integralSumCacheFn, integralCacheFn) { 144 const discreteResult = this.discrete.mapYResult(fn, integralSumCacheFn, integralCacheFn); 145 const continuousResult = this.continuous.mapYResult(fn, integralSumCacheFn, integralCacheFn); 146 if (!continuousResult.ok) { 147 return continuousResult; 148 } 149 if (!discreteResult.ok) { 150 return discreteResult; 151 } 152 const continuous = continuousResult.value; 153 const discrete = discreteResult.value; 154 return Result.Ok(new MixedShape({ 155 discrete, 156 continuous, 157 integralSumCache: this.integralSumCache === undefined 158 ? undefined 159 : integralSumCacheFn?.(this.integralSumCache), 160 integralCache: this.integralCache === undefined 161 ? undefined 162 : integralCacheFn?.(this.integralCache), 163 })); 164 } 165 mean() { 166 const discreteMean = this.discrete.mean(); 167 const continuousMean = this.continuous.mean(); 168 return ((discreteMean * this.discrete.integralSum() + 169 continuousMean * this.continuous.integralSum()) / 170 this.integralSum()); 171 } 172 variance() { 173 const discreteIntegralSum = this.discrete.integralSum(); 174 const continuousIntegralSum = this.continuous.integralSum(); 175 const totalIntegralSum = discreteIntegralSum + continuousIntegralSum; 176 const getMeanOfSquares = ({ discrete, continuous }) => { 177 const discreteMean = discrete.shapeMap(XYShape.T.square).mean(); 178 const continuousMean = continuous.getMeanOfSquares(); 179 return ((discreteMean * discreteIntegralSum + 180 continuousMean * continuousIntegralSum) / 181 totalIntegralSum); 182 }; 183 switch (discreteIntegralSum / totalIntegralSum) { 184 case 1: 185 return this.discrete.variance(); 186 case 0: 187 return this.continuous.variance(); 188 default: 189 return XYShape.Analysis.getVarianceDangerously(this, (t) => t.mean(), (t) => getMeanOfSquares(t)); 190 } 191 } 192 } 193 export const combineAlgebraically = (op, t1, t2) => { 194 const ccConvResult = Continuous.combineAlgebraically(op, t1.continuous, t2.continuous); 195 const dcConvResult = Continuous.combineAlgebraicallyWithDiscrete(op, t2.continuous, t1.discrete, "First"); 196 const cdConvResult = Continuous.combineAlgebraicallyWithDiscrete(op, t1.continuous, t2.discrete, "Second"); 197 const continuousConvResult = Continuous.sum([ 198 ccConvResult, 199 dcConvResult, 200 cdConvResult, 201 ]); 202 const discreteConvResult = Discrete.combineAlgebraically(op, t1.discrete, t2.discrete); 203 const combinedIntegralSum = Common.combineIntegralSums((a, b) => a * b, t1.integralSumCache, t2.integralSumCache); 204 return new MixedShape({ 205 discrete: discreteConvResult, 206 continuous: continuousConvResult, 207 integralSumCache: combinedIntegralSum, 208 integralCache: undefined, 209 }); 210 }; 211 export const combinePointwise = (t1, t2, fn, integralSumCachesFn = () => undefined, integralCachesFn = () => undefined) => { 212 const reducedDiscrete = Discrete.combinePointwise(t1.toDiscrete(), t2.toDiscrete(), fn, integralSumCachesFn); 213 const reducedContinuous = Continuous.combinePointwise(t1.toContinuous(), t2.toContinuous(), fn, undefined, integralSumCachesFn); 214 const combinedIntegralSum = Common.combineIntegralSums(integralSumCachesFn, t1.integralSumCache, t2.integralSumCache); 215 const combinedIntegral = Common.combineIntegrals(integralCachesFn, t1.integralCache, t2.integralCache); 216 return Result.fmap(Result.merge(reducedContinuous, reducedDiscrete), ([continuous, discrete]) => new MixedShape({ 217 continuous, 218 discrete, 219 integralSumCache: combinedIntegralSum, 220 integralCache: combinedIntegral, 221 })); 222 }; 223 export function buildMixedShape({ continuous, discrete, }) { 224 continuous ??= new ContinuousShape({ 225 integralSumCache: 0, 226 xyShape: { xs: [], ys: [] }, 227 }); 228 discrete ??= new DiscreteShape({ 229 integralSumCache: 0, 230 xyShape: { xs: [], ys: [] }, 231 }); 232 const cLength = continuous.xyShape.xs.length; 233 const dLength = discrete.xyShape.xs.length; 234 if (cLength < 2 && dLength == 0) { 235 return undefined; 236 } 237 else { 238 return new MixedShape({ continuous, discrete }); 239 } 240 } 241 //# sourceMappingURL=Mixed.js.map