time-to-botec

Benchmark sampling in different programming languages
Log | Files | Refs | README

danger.js (12314B)


      1 import jstat from "jstat";
      2 import { scaleLog, scaleLogWithThreshold, } from "../dist/distOperations/index.js";
      3 import { scaleMultiply, scalePower, } from "../dist/distOperations/scaleOperations.js";
      4 import { REArgumentError, REOther } from "../errors/messages.js";
      5 import { makeDefinition } from "../library/registry/fnDefinition.js";
      6 import { frAny, frArray, frDist, frLambda, frNumber, } from "../library/registry/frTypes.js";
      7 import { FnFactory, unpackDistResult, distResultToValue, makeTwoArgsDist, makeOneArgDist, } from "../library/registry/helpers.js";
      8 import * as E_A from "../utility/E_A.js";
      9 import { vArray, vNumber } from "../value/index.js";
     10 import * as SymbolicDist from "../dist/SymbolicDist.js";
     11 const { factorial } = jstat;
     12 const maker = new FnFactory({
     13     nameSpace: "Danger",
     14     requiresNamespace: true,
     15 });
     16 function combinations(arr, k) {
     17     if (k === 0)
     18         return [[]];
     19     if (k === arr.length)
     20         return [arr];
     21     const withoutFirst = combinations(arr.slice(1), k);
     22     const withFirst = combinations(arr.slice(1), k - 1).map((comb) => [
     23         arr[0],
     24         ...comb,
     25     ]);
     26     return withFirst.concat(withoutFirst);
     27 }
     28 function allCombinations(arr) {
     29     let allCombs = [];
     30     for (let k = 1; k <= arr.length; k++) {
     31         allCombs = allCombs.concat(combinations(arr, k));
     32     }
     33     return allCombs;
     34 }
     35 const choose = (n, k) => factorial(n) / (factorial(n - k) * factorial(k));
     36 const combinatoricsLibrary = [
     37     maker.nn2n({
     38         name: "laplace",
     39         examples: [`Danger.laplace(1, 20)`],
     40         fn: (successes, trials) => (successes + 1) / (trials + 2),
     41     }),
     42     maker.n2n({
     43         name: "factorial",
     44         examples: [`Danger.factorial(20)`],
     45         fn: factorial,
     46     }),
     47     maker.nn2n({
     48         name: "choose",
     49         examples: [`Danger.choose(1, 20)`],
     50         fn: choose,
     51     }),
     52     maker.make({
     53         name: "binomial",
     54         output: "Number",
     55         examples: [`Danger.binomial(1, 20, 0.5)`],
     56         definitions: [
     57             makeDefinition([frNumber, frNumber, frNumber], ([n, k, p]) => vNumber(choose(n, k) * Math.pow(p, k) * Math.pow(1 - p, n - k))),
     58         ],
     59     }),
     60 ];
     61 const integrateFunctionBetweenWithNumIntegrationPoints = (lambda, min, max, numIntegrationPoints, context) => {
     62     const applyFunctionAtFloatToFloatOption = (point) => {
     63         const result = lambda.call([vNumber(point)], context);
     64         if (result.type === "Number") {
     65             return result.value;
     66         }
     67         throw new REOther("Error 1 in Danger.integrate. It's possible that your function doesn't return a number, try definining auxiliaryFunction(x) = mean(yourFunction(x)) and integrate auxiliaryFunction instead");
     68     };
     69     const numTotalPoints = numIntegrationPoints | 0;
     70     const numInnerPoints = numTotalPoints - 2;
     71     const numOuterPoints = 2;
     72     const totalWeight = max - min;
     73     const weightForAnInnerPoint = totalWeight / (numTotalPoints - 1);
     74     const weightForAnOuterPoint = totalWeight / (numTotalPoints - 1) / 2;
     75     const innerPointIncrement = (max - min) / (numTotalPoints - 1);
     76     const innerXs = E_A.makeBy(numInnerPoints, (i) => min + (i + 1) * innerPointIncrement);
     77     const ys = innerXs.map((x) => applyFunctionAtFloatToFloatOption(x));
     78     const verbose = false;
     79     if (verbose) {
     80         console.log("numTotalPoints", numTotalPoints);
     81         console.log("numInnerPoints", numInnerPoints);
     82         console.log("numOuterPoints", numOuterPoints);
     83         console.log("totalWeight", totalWeight);
     84         console.log("weightForAnInnerPoint", weightForAnInnerPoint);
     85         console.log("weightForAnOuterPoint", weightForAnOuterPoint);
     86         console.log("weightForAnInnerPoint * numInnerPoints + weightForAnOuterPoint * numOuterPoints", weightForAnInnerPoint * numInnerPoints +
     87             weightForAnOuterPoint * numOuterPoints);
     88         console.log("sum of weights == totalWeight", weightForAnInnerPoint * numInnerPoints +
     89             weightForAnOuterPoint * numOuterPoints ===
     90             totalWeight);
     91         console.log("innerPointIncrement", innerPointIncrement);
     92         console.log("innerXs", innerXs);
     93         console.log("ys", ys);
     94     }
     95     const innerPointsSum = ys.reduce((a, b) => a + b, 0);
     96     const yMin = applyFunctionAtFloatToFloatOption(min);
     97     const yMax = applyFunctionAtFloatToFloatOption(max);
     98     const result = (yMin + yMax) * weightForAnOuterPoint +
     99         innerPointsSum * weightForAnInnerPoint;
    100     return vNumber(result);
    101 };
    102 const integrationLibrary = [
    103     maker.make({
    104         name: "integrateFunctionBetweenWithNumIntegrationPoints",
    105         requiresNamespace: false,
    106         output: "Number",
    107         examples: [
    108             `Danger.integrateFunctionBetweenWithNumIntegrationPoints({|x| x+1}, 1, 10, 10)`,
    109         ],
    110         definitions: [
    111             makeDefinition([frLambda, frNumber, frNumber, frNumber], ([lambda, min, max, numIntegrationPoints], context) => {
    112                 if (numIntegrationPoints === 0) {
    113                     throw new REOther("Integration error 4 in Danger.integrate: Increment can't be 0.");
    114                 }
    115                 return integrateFunctionBetweenWithNumIntegrationPoints(lambda, min, max, numIntegrationPoints, context);
    116             }),
    117         ],
    118     }),
    119     maker.make({
    120         name: "integrateFunctionBetweenWithEpsilon",
    121         requiresNamespace: false,
    122         output: "Number",
    123         examples: [
    124             `Danger.integrateFunctionBetweenWithEpsilon({|x| x+1}, 1, 10, 0.1)`,
    125         ],
    126         definitions: [
    127             makeDefinition([frLambda, frNumber, frNumber, frNumber], ([lambda, min, max, epsilon], context) => {
    128                 if (epsilon === 0) {
    129                     throw new REOther("Integration error in Danger.integrate: Increment can't be 0.");
    130                 }
    131                 return integrateFunctionBetweenWithNumIntegrationPoints(lambda, min, max, (max - min) / epsilon, context);
    132             }),
    133         ],
    134     }),
    135 ];
    136 const findBiggestElementIndex = (xs) => xs.reduce((acc, newElement, index) => {
    137     if (newElement > xs[acc]) {
    138         return index;
    139     }
    140     else {
    141         return acc;
    142     }
    143 }, 0);
    144 const diminishingReturnsLibrary = [
    145     maker.make({
    146         name: "optimalAllocationGivenDiminishingMarginalReturnsForManyFunctions",
    147         output: "Array",
    148         requiresNamespace: false,
    149         examples: [
    150             `Danger.optimalAllocationGivenDiminishingMarginalReturnsForManyFunctions([{|x| x+1}, {|y| 10}], 100, 0.01)`,
    151         ],
    152         definitions: [
    153             makeDefinition([frArray(frLambda), frNumber, frNumber], ([lambdas, funds, approximateIncrement], context) => {
    154                 if (lambdas.length <= 1) {
    155                     throw new REOther("Error in Danger.optimalAllocationGivenDiminishingMarginalReturnsForManyFunctions, number of functions should be greater than 1.");
    156                 }
    157                 if (funds <= 0) {
    158                     throw new REOther("Error in Danger.optimalAllocationGivenDiminishingMarginalReturnsForManyFunctions, funds should be greater than 0.");
    159                 }
    160                 if (approximateIncrement <= 0) {
    161                     throw new REOther("Error in Danger.optimalAllocationGivenDiminishingMarginalReturnsForManyFunctions, approximateIncrement should be greater than 0.");
    162                 }
    163                 if (approximateIncrement >= funds) {
    164                     throw new REOther("Error in Danger.optimalAllocationGivenDiminishingMarginalReturnsForManyFunctions, approximateIncrement should be smaller than funds amount.");
    165                 }
    166                 const applyFunctionAtPoint = (lambda, point) => {
    167                     const lambdaResult = lambda.call([vNumber(point)], context);
    168                     if (lambdaResult.type === "Number") {
    169                         return lambdaResult.value;
    170                     }
    171                     throw new REOther("Error 1 in Danger.optimalAllocationGivenDiminishingMarginalReturnsForManyFunctions. It's possible that your function doesn't return a number, try definining auxiliaryFunction(x) = mean(yourFunction(x)) and integrate auxiliaryFunction instead");
    172                 };
    173                 const numDivisions = Math.round(funds / approximateIncrement);
    174                 const increment = funds / numDivisions;
    175                 const arrayOfIncrements = new Array(numDivisions).fill(increment);
    176                 const initAccumulator = {
    177                     optimalAllocations: new Array(lambdas.length).fill(0),
    178                     currentMarginalReturns: lambdas.map((lambda) => applyFunctionAtPoint(lambda, 0)),
    179                 };
    180                 const optimalAllocationEndAccumulator = arrayOfIncrements.reduce((acc, newIncrement) => {
    181                     const oldMarginalReturns = acc.currentMarginalReturns;
    182                     const indexOfBiggestDMR = findBiggestElementIndex(oldMarginalReturns);
    183                     const newOptimalAllocations = [...acc.optimalAllocations];
    184                     const newOptimalAllocationsi = newOptimalAllocations[indexOfBiggestDMR] + newIncrement;
    185                     newOptimalAllocations[indexOfBiggestDMR] = newOptimalAllocationsi;
    186                     const lambdai = lambdas[indexOfBiggestDMR];
    187                     const newMarginalResultsLambdai = applyFunctionAtPoint(lambdai, newOptimalAllocationsi);
    188                     const newCurrentMarginalReturns = [...oldMarginalReturns];
    189                     newCurrentMarginalReturns[indexOfBiggestDMR] =
    190                         newMarginalResultsLambdai;
    191                     const newAcc = {
    192                         optimalAllocations: newOptimalAllocations,
    193                         currentMarginalReturns: newCurrentMarginalReturns,
    194                     };
    195                     return newAcc;
    196                 }, initAccumulator);
    197                 return vArray(optimalAllocationEndAccumulator.optimalAllocations.map(vNumber));
    198             }),
    199         ],
    200     }),
    201 ];
    202 const mapYLibrary = [
    203     maker.d2d({
    204         name: "mapYLog",
    205         fn: (dist, env) => unpackDistResult(scaleLog(dist, Math.E, { env })),
    206     }),
    207     maker.d2d({
    208         name: "mapYLog10",
    209         fn: (dist, env) => unpackDistResult(scaleLog(dist, 10, { env })),
    210     }),
    211     maker.dn2d({
    212         name: "mapYLog",
    213         fn: (dist, x, env) => unpackDistResult(scaleLog(dist, x, { env })),
    214     }),
    215     maker.make({
    216         name: "mapYLogWithThreshold",
    217         definitions: [
    218             makeDefinition([frDist, frNumber, frNumber], ([dist, base, eps], { environment }) => distResultToValue(scaleLogWithThreshold(dist, {
    219                 env: environment,
    220                 eps,
    221                 base,
    222             }))),
    223         ],
    224     }),
    225     maker.make({
    226         name: "combinations",
    227         definitions: [
    228             makeDefinition([frArray(frAny), frNumber], ([elements, n]) => {
    229                 if (n > elements.length) {
    230                     throw new REArgumentError(`Combinations of length ${n} were requested, but full list is only ${elements.length} long.`);
    231                 }
    232                 return vArray(combinations(elements, n).map((v) => vArray(v)));
    233             }),
    234         ],
    235     }),
    236     maker.make({
    237         name: "allCombinations",
    238         definitions: [
    239             makeDefinition([frArray(frAny)], ([elements]) => {
    240                 return vArray(allCombinations(elements).map((v) => vArray(v)));
    241             }),
    242         ],
    243     }),
    244     maker.dn2d({
    245         name: "mapYMultiply",
    246         fn: (dist, f, env) => unpackDistResult(scaleMultiply(dist, f, { env })),
    247     }),
    248     maker.dn2d({
    249         name: "mapYPow",
    250         fn: (dist, f, env) => unpackDistResult(scalePower(dist, f, { env })),
    251     }),
    252     maker.d2d({
    253         name: "mapYExp",
    254         fn: (dist, env) => unpackDistResult(scalePower(dist, Math.E, { env })),
    255     }),
    256     maker.make({
    257         name: "binomialDist",
    258         examples: ["Danger.binomialDist(8, 0.5)"],
    259         definitions: [makeTwoArgsDist((n, p) => SymbolicDist.Binomial.make(n, p))],
    260     }),
    261     maker.make({
    262         name: "poissonDist",
    263         examples: ["Danger.poissonDist(10)"],
    264         definitions: [
    265             makeOneArgDist((lambda) => SymbolicDist.Poisson.make(lambda)),
    266         ],
    267     }),
    268 ];
    269 export const library = [
    270     ...combinatoricsLibrary,
    271     ...integrationLibrary,
    272     ...diminishingReturnsLibrary,
    273     ...mapYLibrary,
    274 ];
    275 //# sourceMappingURL=danger.js.map