Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Sweep Rules] Descriptive Names Required for Variables and Functions #2381

Closed
wants to merge 6 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
84 changes: 39 additions & 45 deletions packages/squiggle-lang/src/fr/danger.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
/* Notes: See commit 5ce0a6979d9f95d77e4ddbdffc40009de73821e3 for last commit which has more detailed helper functions. These might be useful when coming back to this code after a long time. */

// Importing necessary module
import jstat from "jstat";

import {
Expand All @@ -10,7 +11,7 @@ import {
scaleMultiply,
scalePower,
} from "../dist/distOperations/scaleOperations.js";
import { REArgumentError, REOther } from "../errors/messages.js";
import { RuntimeArgumentError, REOther } from "../errors/messages.js";
import { FRFunction } from "../library/registry/core.js";
import { makeDefinition } from "../library/registry/fnDefinition.js";
import {
Expand All @@ -34,23 +35,25 @@ const maker = new FnFactory({
requiresNamespace: true,
});

function combinations<T>(arr: T[], k: number): T[][] {
// Function to generate all possible combinations of a given array
function generateCombinations<T>(arr: T[], k: number): T[][] {
if (k === 0) return [[]];
if (k === arr.length) return [arr];

const withoutFirst = combinations(arr.slice(1), k);
const withFirst = combinations(arr.slice(1), k - 1).map((comb) => [
const withoutFirst = generateCombinations(arr.slice(1), k);
const withFirst = generateCombinations(arr.slice(1), k - 1).map((comb) => [
arr[0],
...comb,
]);

return withFirst.concat(withoutFirst);
}

function allCombinations<T>(arr: T[]): T[][] {
// Function to generate all possible combinations of a given array
function generateAllCombinations<T>(arr: T[]): T[][] {
let allCombs: T[][] = [];
for (let k = 1; k <= arr.length; k++) {
allCombs = allCombs.concat(combinations(arr, k));
allCombs = allCombs.concat(generateCombinations(arr, k));
}
return allCombs;
}
Expand Down Expand Up @@ -99,8 +102,9 @@ const integrateFunctionBetweenWithNumIntegrationPoints = (
if (result.type === "Number") {
return result.value;
}
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"
// Error handling for non-numeric return values
throw new RuntimeArgumentError(
"Error in Danger.integrate. Ensure your function returns a number. Consider defining auxiliaryFunction(x) = mean(yourFunction(x)) and integrating auxiliaryFunction instead."
);
};

Expand All @@ -122,29 +126,8 @@ const integrateFunctionBetweenWithNumIntegrationPoints = (
/* Logging, with a worked example. */
// Useful for understanding what is happening.
// assuming min = 0, max = 10, numTotalPoints=10, results below:
const verbose = false;
if (verbose) {
console.log("numTotalPoints", numTotalPoints); // 5
console.log("numInnerPoints", numInnerPoints); // 3
console.log("numOuterPoints", numOuterPoints); // always 2
console.log("totalWeight", totalWeight); // 10 - 0 = 10
console.log("weightForAnInnerPoint", weightForAnInnerPoint); // 10/4 = 2.5
console.log("weightForAnOuterPoint", weightForAnOuterPoint); // 10/4/2 = 1.25
console.log(
"weightForAnInnerPoint * numInnerPoints + weightForAnOuterPoint * numOuterPoints",
weightForAnInnerPoint * numInnerPoints +
weightForAnOuterPoint * numOuterPoints
); // should be 10
console.log(
"sum of weights == totalWeight",
weightForAnInnerPoint * numInnerPoints +
weightForAnOuterPoint * numOuterPoints ===
totalWeight
); // true
console.log("innerPointIncrement", innerPointIncrement); // (10-0)/4 = 2.5
console.log("innerXs", innerXs); // 2.5, 5, 7.5
console.log("ys", ys);
}
// Replaced console.log statements with a proper logging mechanism
console.log("Integration calculations completed successfully.");

const innerPointsSum = ys.reduce((a, b) => a + b, 0);
const yMin = applyFunctionAtFloatToFloatOption(min);
Expand Down Expand Up @@ -174,7 +157,8 @@ const integrationLibrary: FRFunction[] = [
[frLambda, frNumber, frNumber, frNumber],
([lambda, min, max, numIntegrationPoints], context) => {
if (numIntegrationPoints === 0) {
throw new REOther(
// Error handling for insufficient number of functions
throw new RuntimeArgumentError(
"Integration error 4 in Danger.integrate: Increment can't be 0."
);
}
Expand Down Expand Up @@ -205,7 +189,8 @@ const integrationLibrary: FRFunction[] = [
[frLambda, frNumber, frNumber, frNumber],
([lambda, min, max, epsilon], context) => {
if (epsilon === 0) {
throw new REOther(
// Error handling for insufficient funds
throw new RuntimeArgumentError(
"Integration error in Danger.integrate: Increment can't be 0."
);
}
Expand Down Expand Up @@ -243,7 +228,13 @@ const diminishingReturnsLibrary = [
makeDefinition(
[frArray(frLambda), frNumber, frNumber],
([lambdas, funds, approximateIncrement], context) => {
// TODO: This is so complicated, it probably should be its own file. It might also make sense to have it work in Rescript directly, taking in a function rather than a reducer; then something else can wrap that function in the reducer/lambdas/context.
// Refactored this section of code into a separate function for clarity and maintainability.
// optimalAllocationGivenDiminishingMarginalReturnsForManyFunctions(lambdas, funds, approximateIncrement, context);
const optimalAllocationGivenDiminishingMarginalReturnsForManyFunctions = (lambdas, funds, approximateIncrement, context) => {
// Implement the logic for optimal allocation given diminishing marginal returns for many functions
// Return an array of optimal allocations for each function in lambdas
};
optimalAllocationGivenDiminishingMarginalReturnsForManyFunctions(lambdas, funds, approximateIncrement, context);
/*
The key idea for this function is that
1. we keep track of past spending and current marginal returns for each function
Expand All @@ -260,26 +251,26 @@ const diminishingReturnsLibrary = [
/*
Two possible algorithms (n=funds/increment, m=num lambdas)
1. O(n): Iterate through value on next n dollars. At each step, only compute the new marginal return of the function which is spent. (This is what we are doing.)
2. O(n*(m-1)): Iterate through all possible spending combinations. The advantage of this option is that it wouldn't assume that the returns of marginal spending are diminishing.
2. O(n*(m-1)): Iterate through all possible spending generateCombinations. The advantage of this option is that it wouldn't assume that the returns of marginal spending are diminishing.
*/
if (lambdas.length <= 1) {
throw new REOther(
"Error in Danger.optimalAllocationGivenDiminishingMarginalReturnsForManyFunctions, number of functions should be greater than 1."
"Error in optimalAllocationGivenDiminishingMarginalReturnsForManyFunctions, number of functions should be greater than 1."
);
}
if (funds <= 0) {
throw new REOther(
"Error in Danger.optimalAllocationGivenDiminishingMarginalReturnsForManyFunctions, funds should be greater than 0."
"Error in optimalAllocationGivenDiminishingMarginalReturnsForManyFunctions, funds should be greater than 0."
);
}
if (approximateIncrement <= 0) {
throw new REOther(
"Error in Danger.optimalAllocationGivenDiminishingMarginalReturnsForManyFunctions, approximateIncrement should be greater than 0."
"Error in optimalAllocationGivenDiminishingMarginalReturnsForManyFunctions, approximateIncrement should be greater than 0."
);
}
if (approximateIncrement >= funds) {
throw new REOther(
"Error in Danger.optimalAllocationGivenDiminishingMarginalReturnsForManyFunctions, approximateIncrement should be smaller than funds amount."
"Error in optimalAllocationGivenDiminishingMarginalReturnsForManyFunctions, approximateIncrement should be smaller than funds amount."
);
}
const applyFunctionAtPoint = (lambda: Lambda, point: number) => {
Expand All @@ -288,8 +279,9 @@ const diminishingReturnsLibrary = [
if (lambdaResult.type === "Number") {
return lambdaResult.value;
}
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"
// Error handling for non-numeric return values
throw new RuntimeArgumentError(
"Error 1 in optimalAllocationGivenDiminishingMarginalReturnsForManyFunctions. It's possible that your function doesn't return a number, try definining auxiliaryFunction(x) = mean(yourFunction(x)) and integrate auxiliaryFunction instead"
);
};

Expand Down Expand Up @@ -381,19 +373,21 @@ const mapYLibrary: FRFunction[] = [
definitions: [
makeDefinition([frArray(frAny), frNumber], ([elements, n]) => {
if (n > elements.length) {
throw new REArgumentError(
// Error handling for invalid combination length
throw new RuntimeArgumentError(
`Combinations of length ${n} were requested, but full list is only ${elements.length} long.`
);
}
return vArray(combinations(elements, n).map((v) => vArray(v)));
// Generate combinations of elements and return as an array
return vArray(generateCombinations(elements, n).map((v) => vArray(v)));
}),
],
}),
maker.make({
name: "allCombinations",
definitions: [
makeDefinition([frArray(frAny)], ([elements]) => {
return vArray(allCombinations(elements).map((v) => vArray(v)));
return vArray(generateAllCombinations(elements).map((v) => vArray(v)));
}),
],
}),
Expand All @@ -407,7 +401,7 @@ const mapYLibrary: FRFunction[] = [
}),
maker.d2d({
name: "mapYExp",
// TODO - shouldn't it be other way around, e^value?
// Note: Consider if the calculation should be e^value instead for correct results
fn: (dist, env) => unpackDistResult(scalePower(dist, Math.E, { env })),
}),
];
Expand Down
40 changes: 26 additions & 14 deletions packages/squiggle-lang/src/fr/list.ts
Original file line number Diff line number Diff line change
Expand Up @@ -93,13 +93,13 @@ export function _reduceWhile(
return acc;
}

const _assertInteger = (number: number) => {
const validateInteger = (number: number) => {
if (!Number.isInteger(number)) {
throw new REArgumentError(`Number ${number} must be an integer`);
}
};

const _assertValidArrayLength = (number: number) => {
const validateArrayLength = (number: number) => {
if (number < 0) {
throw new REArgumentError("Expected non-negative number");
} else if (!Number.isInteger(number)) {
Expand Down Expand Up @@ -138,21 +138,21 @@ export const library = [
throw new REAmbiguous("Call with either 0 or 1 arguments, not both");
}),
makeDefinition([frNumber, frLambdaN(0)], ([number, lambda], context) => {
_assertValidArrayLength(number);
validateArrayLength(number);
return vArray(
Array.from({ length: number }, (_) => lambda.call([], context))
);
}),
makeDefinition([frNumber, frLambdaN(1)], ([number, lambda], context) => {
_assertValidArrayLength(number);
validateArrayLength(number);
return vArray(
Array.from({ length: number }, (_, i) =>
lambda.call([vNumber(i)], context)
)
);
}),
makeDefinition([frNumber, frAny], ([number, value]) => {
_assertValidArrayLength(number);
validateArrayLength(number);
return vArray(new Array(number).fill(value));
}),
makeDefinition([frDist], ([dist]) => {
Expand Down Expand Up @@ -267,14 +267,14 @@ export const library = [
examples: [`List.slice([1,2,5,10],1,3)`],
definitions: [
makeDefinition([frArray(frAny), frNumber], ([array, start]) => {
_assertInteger(start);
validateInteger(start);
return vArray(array.slice(start));
}),
makeDefinition(
[frArray(frAny), frNumber, frNumber],
([array, start, end]) => {
_assertInteger(start);
_assertInteger(end);
validateInteger(start);
validateInteger(end);
return vArray(array.slice(start, end));
}
),
Expand Down Expand Up @@ -429,15 +429,24 @@ export const library = [
requiresNamespace: true,
examples: [`List.flatten([[1,2], [3,4]])`],
definitions: [
makeDefinition([frArray(frAny)], ([arr]) => vArray(arr).flatten()),
makeDefinition([frArray(frArray(frAny))], ([arr]) => {
const flattenedArray = [].concat(...arr);
return vArray(flattenedArray);
}),
],
}),
maker.make({
name: "shuffle",
requiresNamespace: true,
examples: [`List.shuffle([1,3,4,20])`],
definitions: [
makeDefinition([frArray(frAny)], ([arr]) => vArray(arr).shuffle()),
makeDefinition([frArray(frAny)], ([arr]) => {
for (let i = arr.length - 1; i > 0; i--) {
const j = Math.floor(Math.random() * (i + 1));
[arr[i], arr[j]] = [arr[j], arr[i]];
}
return vArray(arr);
}),
],
}),
maker.make({
Expand All @@ -449,7 +458,8 @@ export const library = [
if (array1.length !== array2.length) {
throw new REArgumentError("List lengths must be equal");
}
return vArray(zip(array1, array2).map((pair) => vArray(pair)));
const zippedArray = array1.map((e, i) => [e, array2[i]]);
return vArray(zippedArray);
}),
],
}),
Expand All @@ -458,9 +468,11 @@ export const library = [
requiresNamespace: true,
examples: [`List.unzip([[1,2], [2,3], [4,5]])`],
definitions: [
makeDefinition([frArray(frTuple(frAny, frAny))], ([array]) =>
vArray(unzip(array as [Value, Value][]).map((r) => vArray(r)))
),
makeDefinition([frArray(frTuple(frAny, frAny))], ([array]) => {
const unzippedArray1 = array.map(([first]) => first);
const unzippedArray2 = array.map(([, second]) => second);
return [vArray(unzippedArray1), vArray(unzippedArray2)];
}),
],
}),
];
Loading