tweak: Tools readme

This commit is contained in:
NunoSempere 2022-06-18 20:14:07 -04:00
parent 4a2fc6fb89
commit 053c316e73
8 changed files with 1878 additions and 27 deletions

File diff suppressed because one or more lines are too long

View File

@ -1,9 +1,60 @@
## Utility function processor # Utility Tools
This package contains a series of utilities to work with the utility functions produced by [this utility function extractor](https://utility-function-extractor.quantifieduncertainty.org/openphil-2018-ai-risk) This package contains a series of utilities to work with the utility functions produced by [this utility function extractor](https://utility-function-extractor.quantifieduncertainty.org/openphil-2018-ai-risk).
### Merge sort ## Built with
Given a list of elements and a list of utilitity comparisons, sort the list. - [Squiggle](https://www.squiggle-language.com/)
- [Nodejs](https://nodejs.org/)
- Plain js
Gotcha: The list of elements has to be the same list, and in the same order, as that produced when initially doing the comparisons. This is because the merge-sort algorithm depends on the initial order of the list. ## Getting started
### Installation
```sh
yarn add utility-tools
```
then in your file:
```js
import { mergeSort, findPaths, aggregatePaths } from "utility-tools";
```
### Usage
You can find an example how to use and concatenate these functions in `/src/example.js`, as well as an example of the input format needed in the `input` folder.
## Interface
### Merge sort (`mergeSort`)
Given a list of elements and a list of utilitity comparisons, sort the list. If there are not enough comparisons to implement the merge sort algorithm, return one of the missing comparisons.
_Gotcha_: The list of elements has to be the same list, and in the same order, as that produced when initially doing the comparisons. This is because the merge-sort algorithm depends on the initial order of the list.
### Find Paths (`findPaths`)
Given an (ordered) list of elements and a list of utility comparisons, find all possible monotonous paths from each element to each other. A monotonous path is a path that is either all increasing or all decreasing, relative to the ordering given.
_Note_: Some elements will have many more paths than others.
### Aggregate paths (`aggregatePaths`)
Given a list of path, aggregate them to finally produce an estimate of the relative utility of each element.
There are two ways of doing this:
- 1. Aggregate the means (expected values) for each path.
- This method is fast
- But has the disadvantage the expected value aggregation is tricky, particularly if one of the elements is positive and the other one negative (because then one can't)
- 2. Aggregate the distributions given for each path.
## Roadmap
I don't have any additions planned for this repository.
## Contact
Feel free to shoot me any questions at `nuno.semperelh@protonmail.com`

View File

@ -1,15 +1,24 @@
// EXPORTS // EXPORTS
import { run } from "@quri/squiggle-lang"; import { run } from "@quri/squiggle-lang";
export function aggregatePaths(pathsArray, nodes) { export async function aggregatePathsThroughMixtureOfDistributions({
pathsArray.map((paths, i) => { pathsArray,
console.log(nodes[i].name); nodes,
VERBOSE,
}) {
let print = (x) => {
if (VERBOSE) {
console.log(x);
}
};
let result = pathsArray.map((paths, i) => {
print(nodes[i].name);
let multipliedDistributions = paths.map( let multipliedDistributions = paths.map(
(path) => path.multipliedDistributionsInPath (path) => path.multipliedDistributionsInPath
); );
console.group(); console.group();
console.log("Number of paths: ", multipliedDistributions.length); print("Number of paths: ", multipliedDistributions.length);
// console.log(multipliedDistributions.slice(0, 10)); // print(multipliedDistributions.slice(0, 10));
let squiggleCode = `aggregatePath = mx(${multipliedDistributions let squiggleCode = `aggregatePath = mx(${multipliedDistributions
.filter((distributions) => distributions != undefined) .filter((distributions) => distributions != undefined)
// .slice(0, 600) // .slice(0, 600)
@ -22,7 +31,7 @@ export function aggregatePaths(pathsArray, nodes) {
let squiggleCodeForMean = squiggleCode + "\n" + "mean(aggregatePath)"; let squiggleCodeForMean = squiggleCode + "\n" + "mean(aggregatePath)";
let meanAnswer = run(squiggleCodeForMean); let meanAnswer = run(squiggleCodeForMean);
let mean = meanAnswer.value.value; let mean = meanAnswer.value.value;
console.log(`Mean: ${mean}`); print(`Mean: ${mean}`);
// Get the 90% CI // Get the 90% CI
let squiggleCodeFor90CI = let squiggleCodeFor90CI =
@ -38,7 +47,7 @@ export function aggregatePaths(pathsArray, nodes) {
let upper = value[1].value; let upper = value[1].value;
return [lower, upper]; return [lower, upper];
}; };
console.log( print(
`90% confidence interval: ${JSON.stringify( `90% confidence interval: ${JSON.stringify(
processCI90(ci90percentAnswer), processCI90(ci90percentAnswer),
null, null,
@ -47,8 +56,98 @@ export function aggregatePaths(pathsArray, nodes) {
); );
// Stop measuring time // Stop measuring time
let end = Date.now(); let end = Date.now();
console.log(`${(end - start) / 1000} seconds needed for processing`); print(`${(end - start) / 1000} seconds needed for processing`);
console.groupEnd(); console.groupEnd();
console.log(""); print("");
return {
name: nodes[i].name,
meanOfAggregatedDistributions: mean,
ninetyPercentileConfidenceIntervalOfAggregatedDistributions:
ci90percentAnswer,
arrayDistributions: squiggleCode,
};
});
return result;
}
const sum = (arr) => arr.reduce((a, b) => a + b, 0);
export const avg = (arr) => sum(arr) / arr.length;
export const geomMean = (arr) => {
let n = arr.length;
let logavg = sum(arr.map((x) => Math.log(x))); // works for low numbers much better
// console.log(logavg);
let result = Math.exp(logavg / n);
return result;
};
export function aggregatePathsThroughMixtureOfMeans({
pathsArray,
nodes,
VERBOSE,
}) {
let print = (x) => {
if (VERBOSE) {
console.log(x);
}
};
let result = pathsArray.map((paths, i) => {
print(nodes[i].name);
let expectedRelativeValues = paths
.map((path) => path.expectedRelativeValue)
.filter((x) => x != undefined);
let hasPositive = expectedRelativeValues.filter((x) => x > 0);
let hasNegative = expectedRelativeValues.filter((x) => x < 0);
let answer;
if (hasPositive.length != 0 && hasNegative.length != 0) {
answer = avg(expectedRelativeValues);
} else {
if (hasNegative.length == 0) {
answer = geomMean(expectedRelativeValues);
} else {
let arrayAsPositive = expectedRelativeValues.map((x) => -x);
answer = -geomMean(arrayAsPositive);
}
}
return {
name: nodes[i].name,
aggregatedMeans: answer,
arrayMeans: expectedRelativeValues,
allPositive: hasNegative.length == 0,
};
});
return result;
}
export async function aggregatePaths({
pathsArray,
nodes,
aggregationType,
VERBOSE,
}) {
if (aggregationType == "distribution") {
if (VERBOSE == undefined) {
VERBOSE = true;
}
console.log("Warning: this may take a long time");
return await aggregatePathsThroughMixtureOfDistributions({
pathsArray,
nodes,
VERBOSE,
});
} else if (aggregationType == "mean") {
return aggregatePathsThroughMixtureOfMeans({
pathsArray,
nodes,
VERBOSE,
});
} else {
return aggregatePathsThroughMixtureOfMeans({
pathsArray,
nodes,
VERBOSE,
}); });
} }
}

View File

@ -24,11 +24,6 @@ async function main() {
const links = JSON.parse(inputLinksAsString); const links = JSON.parse(inputLinksAsString);
const list = JSON.parse(inputListAsString); const list = JSON.parse(inputListAsString);
// process file
// const sources = links.map((link) => link.source);
// const targets = links.map((link) => link.target);
// const list = [...new Set([...sources, ...targets])];
// Merge sort // Merge sort
let mergeSortOutput = mergeSort({ list, links }); let mergeSortOutput = mergeSort({ list, links });
// console.log("Output: "); // console.log("Output: ");
@ -64,7 +59,12 @@ async function main() {
// console.log(JSON.stringify(paths, null, 4)); // console.log(JSON.stringify(paths, null, 4));
// Aggregate paths. // Aggregate paths.
let aggregatedPaths = aggregatePaths(paths, nodes); let aggregatedPaths = await aggregatePaths({
pathsArray: paths,
nodes,
aggregationType: "mean", // alternatively: aggregationType: "distribution"
VERBOSE: false,
});
console.log(aggregatedPaths); console.log(aggregatedPaths);
} }
} }

View File

@ -30,7 +30,7 @@ https://github.com/netlify/netlify-plugin-nextjs/#readme
## To do ## To do
- [x] Extract merge, findPath and aggregatePath functionality into different repos - [x] Extract merge, findPath and aggregatePath functionality into different repos
- [ ] Add functionality like names, etc. - [-] Add functionality like names, etc.
- [x] Send to mongo upon completion - [x] Send to mongo upon completion
- [ ] Add paths table - [ ] Add paths table
- [ ] warn that the paths table is approximate. - [ ] warn that the paths table is approximate.

View File

@ -1,9 +1,11 @@
import React, { useState } from "react"; import React, { useState } from "react";
import { mergeSort } from "utility-tools"; import { mergeSort } from "utility-tools";
import { Title } from "./title.js";
import { ProgressIndicator } from "./progressIndicator.js"; import { ProgressIndicator } from "./progressIndicator.js";
import { DisplayElementForComparison } from "./displayElementForComparison.js"; import { DisplayElementForComparison } from "./displayElementForComparison.js";
import { ComparisonActuator } from "./comparisonActuator.js"; import { ComparisonActuator } from "./comparisonActuator.js";
import { AdvancedOptions } from "./advancedOptions/advancedOptions.js"; import { AdvancedOptions } from "./advancedOptions/advancedOptions.js";
import { Graph } from "./graph/graph.js"; import { Graph } from "./graph/graph.js";
import { pushToMongo } from "../lib/pushToMongo.js"; import { pushToMongo } from "../lib/pushToMongo.js";
@ -110,6 +112,8 @@ export function Homepage({ listOfElementsInit }) {
return ( return (
<div className="block w-full items-center sm:w-full mt-10"> <div className="block w-full items-center sm:w-full mt-10">
<Title />
<ProgressIndicator <ProgressIndicator
numStepsNow={numStepsNow} numStepsNow={numStepsNow}
numElements={listOfElements.length} numElements={listOfElements.length}
@ -143,7 +147,6 @@ export function Homepage({ listOfElementsInit }) {
isListOrdered={isListOrdered} isListOrdered={isListOrdered}
mergeSortOrder={mergeSortOrder} mergeSortOrder={mergeSortOrder}
/> />
{/* Advanced options section */} {/* Advanced options section */}
<div> <div>
<AdvancedOptions <AdvancedOptions

View File

@ -1,7 +1,5 @@
import React, { useState } from "react"; import React, { useState } from "react";
export function Title() { export function Title() {
return ( return <h1 className="text-6xl font-bold ">Utility Function Extractor</h1>;
<h1 className="text-6xl font-bold mt-20">Utility Function Extractor</h1>
);
} }

View File

@ -4,7 +4,6 @@ import "../styles/globals.css";
import "../styles/cytoscape.css"; import "../styles/cytoscape.css";
import Head from "next/head"; import Head from "next/head";
import { Title } from "../components/title.js";
function MyApp({ Component, pageProps }) { function MyApp({ Component, pageProps }) {
return ( return (
@ -17,7 +16,6 @@ function MyApp({ Component, pageProps }) {
</Head> </Head>
{/* Content */} {/* Content */}
<main className="inline flex-col items-center w-full flex-1 px-20 text-center"> <main className="inline flex-col items-center w-full flex-1 px-20 text-center">
<Title />
<Component {...pageProps} /> <Component {...pageProps} />
</main> </main>
</div> </div>