private static DiscreteDistribution Bivariate(BivariateContinuousDistribution distribution, DistributionsOperation operation) { if (operation == DistributionsOperation.PowerInv) { distribution = distribution.Rotate(); } int samples = distribution.Samples; double[] range = CommonRandomMath.GetRange(distribution.SupportMinLeft, distribution.SupportMaxLeft, distribution.SupportMinRight, distribution.SupportMaxRight, operation); double[] rightAxis = CommonRandomMath.GenerateXAxis(distribution.SupportMinRight, distribution.SupportMaxRight, samples, out double rightStep); double[] xAxis = CommonRandomMath.GenerateXAxis(range[0], range[1], samples, out double step); double[] result = new double[samples]; switch (operation) { case DistributionsOperation.Add: { Parallel.For(0, xAxis.Length, i => { double x = xAxis[i]; double sum = 0; // Trap rule is useless because both normal and t-distributions are smoooth. for (int j = 1; j < samples; j++) { double m = rightAxis[j]; sum += distribution.ProbabilityDensityFunction(x - m, m); } result[i] = sum * rightStep; }); break; } case DistributionsOperation.Sub: { Parallel.For(0, xAxis.Length, i => { double x = xAxis[i]; double sum = 0; for (int j = 1; j < samples; j++) { double m = rightAxis[j]; sum += distribution.ProbabilityDensityFunction(x + m, m); } result[i] = sum * rightStep; }); break; } case DistributionsOperation.Muliply: { Parallel.For(0, xAxis.Length, i => { double x = xAxis[i]; double sum = 0; for (int j = 1; j < samples; j++) { double m = rightAxis[j]; if (m != 0) { sum += distribution.ProbabilityDensityFunction(x / m, m) / Math.Abs(m); } } result[i] = sum * rightStep; }); break; } case DistributionsOperation.Divide: { Parallel.For(0, xAxis.Length, i => { double x = xAxis[i]; double sum = 0; for (int j = 1; j < samples; j++) { double m = rightAxis[j]; if (m != 0) { sum += distribution.ProbabilityDensityFunction(x * m, m) * Math.Abs(m); } } result[i] = sum * rightStep; }); break; } case DistributionsOperation.PowerInv: { Parallel.For(0, xAxis.Length, i => { double m = 0; double sum = 0; double x = xAxis[i]; double d = 0; double k = 0; for (int j = 1; j < samples; j++) { m = rightAxis[j]; d = Math.Log(x, m); k = Math.Abs(Math.Log(m) * x); sum += distribution.ProbabilityDensityFunction(d, m) / k; } result[i] = sum * rightStep; }); break; } case DistributionsOperation.Log: { Parallel.For(0, xAxis.Length, i => { double m = 0; double sum = 0; double x = xAxis[i]; double d = 0; double k = 0; for (int j = 1; j < samples; j++) { m = rightAxis[j]; d = Math.Pow(m, x); k = Math.Abs(Math.Log(m) * d); sum += distribution.ProbabilityDensityFunction(d, m) * k; } result[i] = sum * rightStep; }); break; } default: { throw new DistributionsInvalidOperationException(); } } return(new DiscreteDistribution(xAxis, result)); }
private static DiscreteDistribution DiscreteDistributionAndValue(DiscreteDistribution dpdf, double value, DistributionsOperation action) { int length = dpdf.InnerSamples; double[] leftX = dpdf.XCoordinatesInternal; double[] leftY = dpdf.YCoordinatesInternal; double[] yCoordinates = new double[length]; double[] xCoordinates = new double[length]; switch (action) { case DistributionsOperation.Add: { for (int i = 0; i < length; i++) { yCoordinates[i] = leftY[i]; xCoordinates[i] = leftX[i] + value; } break; } case DistributionsOperation.Sub: { for (int i = 0; i < length; i++) { yCoordinates[i] = leftY[i]; xCoordinates[i] = leftX[i] - value; } break; } case DistributionsOperation.SubInv: { for (int i = 0; i < length; i++) { yCoordinates[length - i - 1] = leftY[i]; xCoordinates[length - i - 1] = value - leftX[i]; } break; } case DistributionsOperation.Muliply: { if (value == 0) { throw new DistributionsInvalidOperationException(DistributionsInvalidOperationExceptionType.MultiplyRandomByZero); } if (value < 0) { for (int i = 0; i < length; i++) { yCoordinates[length - i - 1] = leftY[i] / Math.Abs(value); xCoordinates[length - i - 1] = leftX[i] * value; } } else { for (int i = 0; i < length; i++) { yCoordinates[i] = leftY[i] / Math.Abs(value); xCoordinates[i] = leftX[i] * value; } } break; } case DistributionsOperation.Divide: { if (value == 0) { throw new DistributionsInvalidOperationException(DistributionsInvalidOperationExceptionType.DivisionByZero); } if (value < 0) { for (int i = 0; i < length; i++) { yCoordinates[length - i - 1] = leftY[i] * Math.Abs(value); xCoordinates[length - i - 1] = leftX[i] / value; } } else { for (int i = 0; i < length; i++) { yCoordinates[i] = leftY[i] * Math.Abs(value); xCoordinates[i] = leftX[i] / value; } } break; } default: { throw new NotImplementedException(); } } return(new DiscreteDistribution(xCoordinates, yCoordinates)); }
private static DiscreteDistribution[] Swap(DiscreteDistribution dpdfLeft, DiscreteDistribution dpdfRight, DistributionsOperation action, out DistributionsOperation newAction) { double stepX = dpdfLeft.Step; double stepY = dpdfRight.Step; switch (action) { case DistributionsOperation.Add: { newAction = action; if (stepX < stepY) { return(new DiscreteDistribution[] { dpdfRight, dpdfLeft }); } else { return(new DiscreteDistribution[] { dpdfLeft, dpdfRight }); } } case DistributionsOperation.Sub: { if (stepX < stepY) { newAction = DistributionsOperation.Add; return(new DiscreteDistribution[] { Multiply(dpdfRight, -1), dpdfLeft }); } else { newAction = action; return(new DiscreteDistribution[] { dpdfLeft, dpdfRight }); } } case DistributionsOperation.Muliply: { newAction = action; // When one of distributions crossing Oy we need to exact locate them to eliminate 1/0 error on numerical integration if (dpdfRight.MinX <= 0 && dpdfRight.MaxX >= 0 && !(dpdfLeft.MinX <= 0 && dpdfLeft.MaxX >= 0)) { return(new DiscreteDistribution[] { dpdfRight, dpdfLeft }); } else if (dpdfLeft.MinX <= 0 && dpdfLeft.MaxX >= 0 && !(dpdfRight.MinX <= 0 && dpdfRight.MaxX >= 0)) { return(new DiscreteDistribution[] { dpdfLeft, dpdfRight }); } else { if (stepX < stepY) { return(new DiscreteDistribution[] { dpdfRight, dpdfLeft }); } else { return(new DiscreteDistribution[] { dpdfLeft, dpdfRight }); } } } default: { newAction = action; return(new DiscreteDistribution[] { dpdfLeft, dpdfRight }); } } }
private static DiscreteDistribution TwoDiscreteDistributions(DiscreteDistribution dpdfLeft, DiscreteDistribution dpdfRight, DistributionsOperation action) { var exchange = Swap(dpdfLeft, dpdfRight, action, out DistributionsOperation newAction); action = newAction; dpdfLeft = exchange[0]; dpdfRight = exchange[1]; int lengthLeft = dpdfLeft.InnerSamples; int lengthRight = dpdfRight.InnerSamples; double stepRight = dpdfRight.Step; double stepLeft = dpdfLeft.Step; double leftMinX = dpdfLeft.MinX; double leftMaxX = dpdfLeft.MaxX; double[] leftX = dpdfLeft.XCoordinatesInternal; double[] leftY = dpdfLeft.YCoordinatesInternal; double[] rightX = dpdfRight.XCoordinatesInternal; double[] rightY = dpdfRight.YCoordinatesInternal; double[] yCoordinates = new double[lengthRight]; double[] range = CommonRandomMath.GetRange(dpdfLeft.InnerMinX, dpdfLeft.InnerMaxX, dpdfRight.InnerMinX, dpdfRight.InnerMaxX, action); double[] xCoordinates = CommonRandomMath.GenerateXAxis(range[0], range[1], lengthRight, out double stepX0); switch (action) { case DistributionsOperation.Add: { List <int> operations = new List <int>(); Parallel.For(0, lengthRight, i => { double m = 0; double x = xCoordinates[i]; double sum = 0; double y = 0; double r = 0; for (int j = 0; j < lengthRight; j++) { m = rightX[j]; y = rightY[j]; r = y * GetYByX(x - m, leftY, leftMinX, leftMaxX, stepLeft, lengthLeft); if (j == 0 || j == lengthRight - 1) { r /= 2; } sum += r; } yCoordinates[i] = sum * stepRight; }); break; } case DistributionsOperation.Sub: { Parallel.For(0, lengthRight, i => { double m = 0; double x = xCoordinates[i]; double sum = 0; double r = 0; double y = 0; for (int j = 0; j < lengthRight; j++) { m = rightX[j]; y = rightY[j]; r = y * GetYByX(x + m, leftY, leftMinX, leftMaxX, stepLeft, lengthLeft); if (j == 0 || j == lengthRight - 1) { r /= 2; } sum += r; } yCoordinates[i] = sum * stepRight; }); break; } case DistributionsOperation.Muliply: { Parallel.For(0, lengthRight, i => { double m = 0; double sum = 0; double x = xCoordinates[i]; double k = 0; double r = 0; double y = 0; for (int j = 0; j < lengthRight; j++) { m = rightX[j]; y = rightY[j]; k = Math.Abs(m); if (k != 0) { r = y * GetYByX(x / m, leftY, leftMinX, leftMaxX, stepLeft, lengthLeft) / k; if (j == 0 || j == lengthRight - 1) { r /= 2; } sum += r; } } yCoordinates[i] = sum * stepRight; }); // in case when both of distributions cross Oy it is inf in zero if (dpdfLeft.MinX <= 0 && dpdfLeft.MaxX >= 0 && dpdfRight.MinX <= 0 && dpdfRight.MaxX >= 0) { for (int i = 0; i < lengthRight - 1; i++) { if (xCoordinates[i] <= 0 && xCoordinates[i + 1] > 0) { yCoordinates[i] = double.PositiveInfinity; break; } } } break; } case DistributionsOperation.Divide: { Parallel.For(0, lengthRight, i => { double m = 0; double sum = 0; double x = xCoordinates[i]; double k = 0; double r = 0; double y = 0; for (int j = 0; j < lengthRight; j++) { m = rightX[j]; y = rightY[j]; k = Math.Abs(m); if (k != 0) { r = y * GetYByX(x * m, leftY, leftMinX, leftMaxX, stepLeft, lengthLeft) * k; if (j == 0 || j == lengthRight - 1) { r /= 2; } sum += r; } } yCoordinates[i] = sum * stepRight; }); break; } case DistributionsOperation.PowerInv: { Parallel.For(0, lengthRight, i => { double m = 0; double sum = 0; double x = xCoordinates[i]; double k = 0; double r = 0; double y = 0; for (int j = 0; j < lengthRight; j++) { m = rightX[j]; y = rightY[j]; k = Math.Abs(Math.Log(m) * x); if (k != 0) { r = y * GetYByX(Math.Log(x, m), leftY, leftMinX, leftMaxX, stepLeft, lengthLeft) / k; if (j == 0 || j == lengthRight - 1) { r /= 2; } sum += r; } } yCoordinates[i] = sum * stepRight; }); break; } case DistributionsOperation.Log: { Parallel.For(0, lengthRight, i => { double m = 0; double sum = 0; double x = xCoordinates[i]; double d = 0; double k = 0; double r = 0; double y = 0; for (int j = 0; j < lengthRight; j++) { m = rightX[j]; y = rightY[j]; d = Math.Pow(m, x); k = Math.Abs(Math.Log(m) * d); if (k != 0) { r = y * GetYByX(d, leftY, leftMinX, leftMaxX, stepLeft, lengthLeft) * k; if (j == 0 || j == lengthRight - 1) { r /= 2; } sum += r; } } yCoordinates[i] = sum * stepRight; }); break; } default: { throw new NotImplementedException(); } } var result = new DiscreteDistribution(xCoordinates, yCoordinates); return(result); }
private static DiscreteDistribution Operation(BaseDistribution dpdf, double value, DistributionsOperation action) { int length = dpdf.InnerSamples; double[] yCoordinates = new double[length]; double[] xCoordinates; switch (action) { case DistributionsOperation.DivideInv: { if (value == 0) { throw new DistributionsInvalidOperationException(DistributionsInvalidOperationExceptionType.DivisionOfZero); } if ((dpdf.InnerMinX < 0 && dpdf.InnerMaxX > 0) || dpdf.InnerMinX == 0 || dpdf.InnerMaxX == 0) { throw new DistributionsInvalidOperationException(DistributionsInvalidOperationExceptionType.DivisionByZeroCrossingRandom); } double r1 = value / dpdf.InnerMinX; double r2 = value / dpdf.InnerMaxX; xCoordinates = GenerateXAxis(r1, r2, length, out _); for (int i = 0; i < length; i++) { double z = xCoordinates[i]; double d = value / z; double k = Math.Abs(value) / Math.Pow(z, 2); yCoordinates[i] = dpdf.InnerGetPDFYbyX(d) * k; } break; } case DistributionsOperation.Power: { if (value == 0) { throw new DistributionsInvalidOperationException(DistributionsInvalidOperationExceptionType.ExponentialOfRandomInZeroPower); } if (value < 0 && ((dpdf.InnerMinX < 0 && dpdf.InnerMaxX > 0) || dpdf.InnerMinX == 0 || dpdf.InnerMaxX == 0)) { throw new DistributionsInvalidOperationException(DistributionsInvalidOperationExceptionType.ExponentialOfZeroCrossingRandomInNegativePower); } bool evenPower = Math.Abs(value % 2) == 0; bool naturalPower = value - (int)value == 0; if (dpdf.InnerMinX < 0 && !naturalPower) { throw new DistributionsInvalidOperationException(DistributionsInvalidOperationExceptionType.ExponentialOfNotPositiveRandomInIrrationalPower); } double r1 = Math.Pow(dpdf.InnerMinX, value); double r2 = Math.Pow(dpdf.InnerMaxX, value); if (dpdf.InnerMinX < 0 && dpdf.InnerMaxX > 0 && evenPower) { r2 = Math.Max(r1, r2); r1 = 0; } xCoordinates = GenerateXAxis(r1, r2, length, out _); for (int i = 0; i < length; i++) { double d = Math.Sign(xCoordinates[i]) * Math.Pow(Math.Abs(xCoordinates[i]), 1d / value); double k = Math.Abs(Math.Pow(Math.Abs(xCoordinates[i]), (1d - value) / value) / value); yCoordinates[i] = dpdf.InnerGetPDFYbyX(d) * k; if (evenPower) { yCoordinates[i] += dpdf.InnerGetPDFYbyX(-d) * k; } } break; } case DistributionsOperation.PowerInv: { if (value == 0) { throw new DistributionsInvalidOperationException(DistributionsInvalidOperationExceptionType.ExponentialOfZeroInRandomPower); } else if (value < 0) { throw new DistributionsInvalidOperationException(DistributionsInvalidOperationExceptionType.ExponentialOfNegativeInRandomPower); } else if (value == 1) { throw new DistributionsInvalidOperationException(DistributionsInvalidOperationExceptionType.ExponentialOfOneInRandomPower); } double r1 = Math.Pow(value, dpdf.InnerMinX); double r2 = Math.Pow(value, dpdf.InnerMaxX); xCoordinates = GenerateXAxis(r1, r2, length, out _); for (int i = 0; i < length; i++) { double d = Math.Log(xCoordinates[i], value); double k = Math.Abs(Math.Log(value) * xCoordinates[i]); yCoordinates[i] = dpdf.InnerGetPDFYbyX(d) / k; } break; } case DistributionsOperation.Log: { if (value == 0) { throw new DistributionsInvalidOperationException(DistributionsInvalidOperationExceptionType.LogarithmWithZeroBase); } else if (value < 0) { throw new DistributionsInvalidOperationException(DistributionsInvalidOperationExceptionType.LogarithmWithNegativeBase); } else if (value == 1) { throw new DistributionsInvalidOperationException(DistributionsInvalidOperationExceptionType.LogarithmWithOneBase); } if (dpdf.InnerMinX <= 0) { throw new DistributionsInvalidOperationException(DistributionsInvalidOperationExceptionType.LogarithmOfNotPositiveRandom); } double r1 = Math.Log(dpdf.InnerMinX, value); double r2 = Math.Log(dpdf.InnerMaxX, value); xCoordinates = GenerateXAxis(r1, r2, length, out _); for (int i = 0; i < length; i++) { double d = Math.Pow(value, xCoordinates[i]); double k = Math.Abs(Math.Log(value) * d); yCoordinates[i] = dpdf.InnerGetPDFYbyX(d) * k; } break; } case DistributionsOperation.LogInv: { if (dpdf.InnerMinX <= 0) { throw new DistributionsInvalidOperationException(DistributionsInvalidOperationExceptionType.LogarithmWithNotPositiveRandomBase); } if ((dpdf.InnerMinX < 1 && dpdf.InnerMaxX > 1) || dpdf.InnerMinX == 1 || dpdf.InnerMaxX == 1) { throw new DistributionsInvalidOperationException(DistributionsInvalidOperationExceptionType.LogarithmWithOneCrossingRandomBase); } if (value == 0) { throw new DistributionsInvalidOperationException(DistributionsInvalidOperationExceptionType.LogarithmOfZeroValue); } if (value < 0) { throw new DistributionsInvalidOperationException(DistributionsInvalidOperationExceptionType.LogarithmOfNegativeValue); } double r1 = Math.Log(value, dpdf.InnerMaxX); double r2 = Math.Log(value, dpdf.InnerMinX); xCoordinates = GenerateXAxis(r1, r2, length, out _); for (int i = 0; i < length; i++) { double d = Math.Pow(value, 1d / xCoordinates[i]); double k = Math.Abs((d * Math.Log(value)) / Math.Pow(xCoordinates[i], 2)); yCoordinates[i] = dpdf.InnerGetPDFYbyX(d) * k; } break; } case DistributionsOperation.Abs: { double r1 = Math.Abs(dpdf.InnerMinX); double r2 = Math.Abs(dpdf.InnerMaxX); if (dpdf.InnerMinX <= 0 && dpdf.InnerMaxX >= 0) { r2 = Math.Max(r1, r2); r1 = 0; } xCoordinates = GenerateXAxis(r1, r2, length, out _); for (int i = 0; i < length; i++) { double zPow = xCoordinates[i]; yCoordinates[i] = dpdf.InnerGetPDFYbyX(zPow) + dpdf.InnerGetPDFYbyX(-zPow); } break; } case DistributionsOperation.Sin: { double r1 = -1; double r2 = 1; xCoordinates = GenerateXAxis(r1, r2, length, out _); // Max asin [-PI/2, PI/2] int minJ = (int)(dpdf.MinX / (2 * Math.PI)) - 1; int maxJ = (int)(dpdf.MaxX / (2 * Math.PI)) + 1; for (int i = 0; i < length; i++) { double z = xCoordinates[i]; double arcsin = Math.Asin(z); double k = 1d / Math.Sqrt(1 - Math.Pow(z, 2)); for (double j = minJ; j <= maxJ; j++) { double v = dpdf.InnerGetPDFYbyX((Math.PI * 2 * j) - (Math.PI + arcsin)) + dpdf.InnerGetPDFYbyX((Math.PI * 2 * j) + arcsin); if (v != 0) { yCoordinates[i] += k * v; } } } break; } case DistributionsOperation.Cos: { // https://mathoverflow.net/questions/35260/resultant-probability-distribution-when-taking-the-cosine-of-gaussian-distribute // TODO: Verify for trigonometric functions work properly. And remove range? _ = GetTrigonometricRange(-1, 1, dpdf); double r1 = -1; double r2 = 1; xCoordinates = GenerateXAxis(r1, r2, length, out _); // Max acos [0, PI] int minJ = (int)(dpdf.MinX / (2 * Math.PI)) - 1; int maxJ = (int)(dpdf.MaxX / (2 * Math.PI)) + 1; for (int i = 0; i < length; i++) { double z = xCoordinates[i]; double acos = Math.Acos(z); double k = 1d / Math.Sqrt(1 - Math.Pow(z, 2)); for (double j = minJ; j <= maxJ; j++) { double v = dpdf.InnerGetPDFYbyX((2 * (j + 1) * Math.PI) - acos) + dpdf.InnerGetPDFYbyX((2 * j * Math.PI) + acos); if (v != 0) { yCoordinates[i] += k * v; } } } break; } case DistributionsOperation.Tan: { if (dpdf.MaxX - dpdf.MinX >= Math.PI || dpdf.MinX % Math.PI <= -Math.PI / 2 || dpdf.MaxX % Math.PI >= Math.PI / 2) { throw new DistributionsInvalidOperationException(DistributionsInvalidOperationExceptionType.TangentOfValueCrossingAsymptote); } double r1 = Math.Tan(dpdf.MinX); double r2 = Math.Tan(dpdf.MaxX); xCoordinates = GenerateXAxis(r1, r2, length, out _); int j = dpdf.Mean < 0 ? (int)(dpdf.MinX / Math.PI) : (int)(dpdf.MaxX / Math.PI); for (int i = 0; i < length; i++) { double z = xCoordinates[i]; double atan = Math.Atan(z); double k = 1d / (Math.Pow(z, 2) + 1); double v = dpdf.InnerGetPDFYbyX((-Math.PI / 2d) + (Math.PI * j)) + dpdf.InnerGetPDFYbyX(atan + (j * Math.PI)); if (v != 0) { yCoordinates[i] = k * v; } } break; } default: { throw new NotImplementedException(); } } return(new DiscreteDistribution(xCoordinates, yCoordinates)); }
public static double[] GetRange(double min1, double max1, double min2, double max2, DistributionsOperation action) { double[] allBounds = new double[4]; allBounds[0] = min1; allBounds[1] = max1; allBounds[2] = min2; allBounds[3] = max2; double[] result = new double[2]; switch (action) { case DistributionsOperation.Add: { result[0] = min1 + min2; result[1] = max1 + max2; break; } case DistributionsOperation.Sub: { result[0] = min1 - max2; result[1] = max1 - min2; break; } case DistributionsOperation.Muliply: { double[] variants = new double[4]; variants[0] = min1 * min2; variants[1] = min1 * max2; variants[2] = max1 * min2; variants[3] = max1 * max2; result[0] = variants.Min(); result[1] = variants.Max(); break; } case DistributionsOperation.Divide: { if (min2 == 0 || max2 == 0) { throw new DistributionsInvalidOperationException(DistributionsInvalidOperationExceptionType.DivisionByZeroCrossingRandom); } if (min2 < 0 && max2 > 0) { throw new DistributionsInvalidOperationException(DistributionsInvalidOperationExceptionType.DivisionByZeroCrossingRandom); } double[] variants = new double[4]; variants[0] = min1 / min2; variants[1] = min1 / max2; variants[2] = max1 / min2; variants[3] = max1 / max2; result[0] = variants.Min(); result[1] = variants.Max(); break; } case DistributionsOperation.PowerInv: { if (min2 < 0) { throw new DistributionsInvalidOperationException(DistributionsInvalidOperationExceptionType.ExponentialOfNotPositiveRandomInIrrationalPower); } double[] variants = new double[4]; variants[0] = Math.Pow(min2, min1); variants[1] = Math.Pow(max2, min1); variants[2] = Math.Pow(min2, max1); variants[3] = Math.Pow(max2, max1); result[0] = variants.Min(); result[1] = variants.Max(); break; } case DistributionsOperation.Log: { if (min1 <= 0) { throw new DistributionsInvalidOperationException(DistributionsInvalidOperationExceptionType.LogarithmOfNotPositiveRandom); } if (min2 <= 0) { throw new DistributionsInvalidOperationException(DistributionsInvalidOperationExceptionType.LogarithmWithNotPositiveRandomBase); } if ((min2 < 1 && max2 > 1) || min2 == 1 || max2 == 1) { throw new DistributionsInvalidOperationException(DistributionsInvalidOperationExceptionType.LogarithmWithOneCrossingRandomBase); } double[] variants = new double[4]; variants[0] = Math.Log(min1, min2); variants[1] = Math.Log(max1, min2); variants[2] = Math.Log(min1, max2); variants[3] = Math.Log(max1, max2); result[0] = variants.Min(); result[1] = variants.Max(); break; } default: { throw new NotImplementedException(); } } return(result); }