Compare commits
No commits in common. "master" and "develop" have entirely different histories.
File diff suppressed because one or more lines are too long
|
@ -40,8 +40,6 @@ Given an (ordered) list of elements and a list of utility comparisons, find all
|
||||||
|
|
||||||
_Note_: Some elements will have many more paths than others.
|
_Note_: Some elements will have many more paths than others.
|
||||||
|
|
||||||
_Note_: The `findPaths.js` file has a few un-used functions which should make it easier to understand the code.
|
|
||||||
|
|
||||||
### Aggregate paths (`aggregatePaths`)
|
### Aggregate paths (`aggregatePaths`)
|
||||||
|
|
||||||
Given a list of path, aggregate them to finally produce an estimate of the relative utility of each element.
|
Given a list of path, aggregate them to finally produce an estimate of the relative utility of each element.
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"name": "utility-tools",
|
"name": "utility-tools",
|
||||||
"version": "1.0.5",
|
"version": "1.0.0",
|
||||||
"description": "Process the json produced by utility-function-extractor.quantifieduncertainty.org",
|
"description": "Process the json produced by utility-function-extractor.quantifieduncertainty.org",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"start": "node --max-old-space-size=8192 src/index.js",
|
"start": "node --max-old-space-size=8192 src/index.js",
|
||||||
|
@ -11,6 +11,7 @@
|
||||||
"author": "Nuño Sempere",
|
"author": "Nuño Sempere",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@quri/squiggle-lang": "^0.2.11"
|
"@quri/squiggle-lang": "^0.2.11",
|
||||||
|
"utility-tools": "^0.2.2"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,8 +18,10 @@ export async function aggregatePathsThroughMixtureOfDistributions({
|
||||||
);
|
);
|
||||||
console.group();
|
console.group();
|
||||||
print("Number of paths: ", multipliedDistributions.length);
|
print("Number of paths: ", multipliedDistributions.length);
|
||||||
|
// 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)
|
||||||
.join(", ")})`;
|
.join(", ")})`;
|
||||||
|
|
||||||
// Start measuring time
|
// Start measuring time
|
||||||
|
@ -52,7 +54,6 @@ export async function aggregatePathsThroughMixtureOfDistributions({
|
||||||
4
|
4
|
||||||
)}`
|
)}`
|
||||||
);
|
);
|
||||||
|
|
||||||
// Stop measuring time
|
// Stop measuring time
|
||||||
let end = Date.now();
|
let end = Date.now();
|
||||||
print(`${(end - start) / 1000} seconds needed for processing`);
|
print(`${(end - start) / 1000} seconds needed for processing`);
|
||||||
|
@ -76,6 +77,7 @@ export const avg = (arr) => sum(arr) / arr.length;
|
||||||
export const geomMean = (arr) => {
|
export const geomMean = (arr) => {
|
||||||
let n = arr.length;
|
let n = arr.length;
|
||||||
let logavg = sum(arr.map((x) => Math.log(x))); // works for low numbers much better
|
let logavg = sum(arr.map((x) => Math.log(x))); // works for low numbers much better
|
||||||
|
// console.log(logavg);
|
||||||
let result = Math.exp(logavg / n);
|
let result = Math.exp(logavg / n);
|
||||||
return result;
|
return result;
|
||||||
};
|
};
|
||||||
|
@ -84,7 +86,6 @@ export function aggregatePathsThroughMixtureOfMeans({
|
||||||
pathsArray,
|
pathsArray,
|
||||||
orderedList,
|
orderedList,
|
||||||
VERBOSE,
|
VERBOSE,
|
||||||
DONT_EXCLUDE_INFINITIES_AND_NANS,
|
|
||||||
}) {
|
}) {
|
||||||
let print = (x) => {
|
let print = (x) => {
|
||||||
if (VERBOSE) {
|
if (VERBOSE) {
|
||||||
|
@ -94,37 +95,26 @@ export function aggregatePathsThroughMixtureOfMeans({
|
||||||
|
|
||||||
let result = pathsArray.map((paths, i) => {
|
let result = pathsArray.map((paths, i) => {
|
||||||
print(orderedList[i].name);
|
print(orderedList[i].name);
|
||||||
let expectedRelativeValues = paths.map(
|
let expectedRelativeValues = paths
|
||||||
(path) => path.expectedRelativeValue
|
.map((path) => path.expectedRelativeValue)
|
||||||
);
|
.filter((x) => x != undefined);
|
||||||
|
let hasPositive = expectedRelativeValues.filter((x) => x > 0);
|
||||||
let expectedRelativeValuesFiltered = expectedRelativeValues;
|
let hasNegative = expectedRelativeValues.filter((x) => x < 0);
|
||||||
|
|
||||||
if (!DONT_EXCLUDE_INFINITIES_AND_NANS) {
|
|
||||||
expectedRelativeValuesFiltered = expectedRelativeValues
|
|
||||||
.filter((x) => x != undefined)
|
|
||||||
.filter((x) => !isNaN(x))
|
|
||||||
.filter((x) => isFinite(x))
|
|
||||||
.filter((x) => x != null);
|
|
||||||
}
|
|
||||||
|
|
||||||
let hasPositive = expectedRelativeValuesFiltered.filter((x) => x > 0);
|
|
||||||
let hasNegative = expectedRelativeValuesFiltered.filter((x) => x < 0);
|
|
||||||
let answer;
|
let answer;
|
||||||
if (hasPositive.length != 0 && hasNegative.length != 0) {
|
if (hasPositive.length != 0 && hasNegative.length != 0) {
|
||||||
answer = avg(expectedRelativeValuesFiltered);
|
answer = avg(expectedRelativeValues);
|
||||||
} else {
|
} else {
|
||||||
if (hasNegative.length == 0) {
|
if (hasNegative.length == 0) {
|
||||||
answer = geomMean(expectedRelativeValuesFiltered);
|
answer = geomMean(expectedRelativeValues);
|
||||||
} else {
|
} else {
|
||||||
let arrayAsPositive = expectedRelativeValuesFiltered.map((x) => -x);
|
let arrayAsPositive = expectedRelativeValues.map((x) => -x);
|
||||||
answer = -geomMean(arrayAsPositive);
|
answer = -geomMean(arrayAsPositive);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return {
|
return {
|
||||||
name: orderedList[i].name,
|
name: orderedList[i].name,
|
||||||
aggregatedMeans: answer,
|
aggregatedMeans: answer,
|
||||||
arrayMeans: expectedRelativeValuesFiltered,
|
arrayMeans: expectedRelativeValues,
|
||||||
allPositive: hasNegative.length == 0,
|
allPositive: hasNegative.length == 0,
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
|
@ -13,7 +13,7 @@ const outputFilePath = "./output/output.json";
|
||||||
|
|
||||||
// MAIN
|
// MAIN
|
||||||
async function main() {
|
async function main() {
|
||||||
// Read file
|
// read file
|
||||||
const inputLinksAsString = fs.readFileSync(inputLinksFilePath);
|
const inputLinksAsString = fs.readFileSync(inputLinksFilePath);
|
||||||
const inputListAsString = fs.readFileSync(inputListFilePath);
|
const inputListAsString = fs.readFileSync(inputListFilePath);
|
||||||
const links = JSON.parse(inputLinksAsString);
|
const links = JSON.parse(inputLinksAsString);
|
||||||
|
@ -21,6 +21,7 @@ async function main() {
|
||||||
|
|
||||||
// Merge sort
|
// Merge sort
|
||||||
let mergeSortOutput = mergeSort({ list, links });
|
let mergeSortOutput = mergeSort({ list, links });
|
||||||
|
// console.log("Output: ");
|
||||||
if (mergeSortOutput.finishedOrderingList == false) {
|
if (mergeSortOutput.finishedOrderingList == false) {
|
||||||
console.log("Merge could not proceed");
|
console.log("Merge could not proceed");
|
||||||
console.group();
|
console.group();
|
||||||
|
@ -29,6 +30,7 @@ async function main() {
|
||||||
console.groupEnd();
|
console.groupEnd();
|
||||||
} else {
|
} else {
|
||||||
let orderedList = mergeSortOutput.orderedList;
|
let orderedList = mergeSortOutput.orderedList;
|
||||||
|
// console.log(orderedList);
|
||||||
console.log("Sorted output: ");
|
console.log("Sorted output: ");
|
||||||
console.group();
|
console.group();
|
||||||
console.log(orderedList.map((x) => x.name));
|
console.log(orderedList.map((x) => x.name));
|
||||||
|
@ -37,14 +39,14 @@ async function main() {
|
||||||
|
|
||||||
// find Paths
|
// find Paths
|
||||||
let paths = await findDistances({ orderedList, links });
|
let paths = await findDistances({ orderedList, links });
|
||||||
|
// console.log(JSON.stringify(paths, null, 4));
|
||||||
|
|
||||||
// Aggregate paths.
|
// Aggregate paths.
|
||||||
let aggregatedPaths = await aggregatePaths({
|
let aggregatedPaths = await aggregatePaths({
|
||||||
pathsArray: paths,
|
pathsArray: paths,
|
||||||
orderedList,
|
orderedList,
|
||||||
aggregationType: "mean", // alternatively: aggregationType: "distribution"
|
aggregationType: "mean", // alternatively: aggregationType: "distribution"
|
||||||
VERBOSE: false, // optional arg
|
VERBOSE: false,
|
||||||
DONT_EXCLUDE_INFINITIES_AND_NANS: false, // optional arg
|
|
||||||
});
|
});
|
||||||
console.log(aggregatedPaths);
|
console.log(aggregatedPaths);
|
||||||
}
|
}
|
||||||
|
|
|
@ -13,8 +13,6 @@ const findElementPosition = (name, nodes) => {
|
||||||
};
|
};
|
||||||
|
|
||||||
async function findPathsWithoutPrunning({
|
async function findPathsWithoutPrunning({
|
||||||
// DO NOT DELETE THIS UN-USED FUNCTION
|
|
||||||
// USEFUL FOR UNDERSTANDING AGAIN HOW THIS CODE WORKS AFTER A FEW MONTHS
|
|
||||||
sourceElementName,
|
sourceElementName,
|
||||||
targetElementName,
|
targetElementName,
|
||||||
maxLengthOfPath,
|
maxLengthOfPath,
|
||||||
|
@ -112,11 +110,10 @@ async function findPaths({
|
||||||
link.target == targetElementName) ||
|
link.target == targetElementName) ||
|
||||||
(link.source == targetElementName && link.target == sourceElementName)
|
(link.source == targetElementName && link.target == sourceElementName)
|
||||||
) {
|
) {
|
||||||
// We have found a direct path.
|
// direct Path
|
||||||
let newPath = pathPlusLink(pathSoFar, link);
|
let newPath = pathPlusLink(pathSoFar, link);
|
||||||
paths.push(newPath);
|
paths.push(newPath);
|
||||||
} else if (link.source == sourceElementName) {
|
} else if (link.source == sourceElementName) {
|
||||||
// Recursively call find Paths
|
|
||||||
let newPaths = await findPaths({
|
let newPaths = await findPaths({
|
||||||
pathSoFar: pathPlusLink(pathSoFar, link),
|
pathSoFar: pathPlusLink(pathSoFar, link),
|
||||||
maxLengthOfPath: maxLengthOfPath - 1,
|
maxLengthOfPath: maxLengthOfPath - 1,
|
||||||
|
@ -163,7 +160,11 @@ async function findExpectedValuesAndDistributionsForElement({
|
||||||
// then orders them correctly in the for loop
|
// then orders them correctly in the for loop
|
||||||
// (by flipping the distance to 1/distance when necessary)
|
// (by flipping the distance to 1/distance when necessary)
|
||||||
// and then gets the array of weights for the different paths.
|
// and then gets the array of weights for the different paths.
|
||||||
|
/*
|
||||||
|
console.log(
|
||||||
|
`findDistance@findPaths.js from ${sourceElementPosition} to ${targetElementPosition}`
|
||||||
|
);
|
||||||
|
*/
|
||||||
let maxLengthOfPath = Math.abs(sourceElementPosition - targetElementPosition);
|
let maxLengthOfPath = Math.abs(sourceElementPosition - targetElementPosition);
|
||||||
let paths = await findPaths({
|
let paths = await findPaths({
|
||||||
sourceElementName,
|
sourceElementName,
|
||||||
|
@ -206,6 +207,49 @@ async function findExpectedValuesAndDistributionsForElement({
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
return processedPaths;
|
return processedPaths;
|
||||||
|
|
||||||
|
/*
|
||||||
|
let expectedValues = [];
|
||||||
|
for (let path of paths) {
|
||||||
|
let currentSource = sourceElementName;
|
||||||
|
let weight = 1;
|
||||||
|
for (let element of path) {
|
||||||
|
let distance = 0;
|
||||||
|
if (element.source == currentSource) {
|
||||||
|
distance = element.distance;
|
||||||
|
currentSource = element.target;
|
||||||
|
} else if (element.target == currentSource) {
|
||||||
|
distance = 1 / Number(element.distance);
|
||||||
|
currentSource = element.source;
|
||||||
|
}
|
||||||
|
weight = weight * distance;
|
||||||
|
}
|
||||||
|
expectedValues.push(weight);
|
||||||
|
}
|
||||||
|
|
||||||
|
let distributionalForm = [];
|
||||||
|
for (let path of paths) {
|
||||||
|
let currentSource = sourceElementName;
|
||||||
|
let multipliedDistributionsInPath = 1;
|
||||||
|
for (let element of path) {
|
||||||
|
let anotherDistributionInPath;
|
||||||
|
if (element.source == currentSource) {
|
||||||
|
distributionInPath = element.squiggleString;
|
||||||
|
currentSource = element.target;
|
||||||
|
} else if (element.target == currentSource) {
|
||||||
|
distance = `1 / (${element.squiggleString})`;
|
||||||
|
currentSource = element.source;
|
||||||
|
}
|
||||||
|
multipliedDistributionsInPath = `${multipliedDistributionsInPath} * (${anotherDistributionInPath})`;
|
||||||
|
}
|
||||||
|
distributionalForm.push(multipliedDistributionsInPath);
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
expectedValues,
|
||||||
|
distributionalForm,
|
||||||
|
// paths,
|
||||||
|
};
|
||||||
|
*/
|
||||||
}
|
}
|
||||||
|
|
||||||
async function findDistancesFromAllElementsToReferencePoint({
|
async function findDistancesFromAllElementsToReferencePoint({
|
||||||
|
@ -219,12 +263,15 @@ async function findDistancesFromAllElementsToReferencePoint({
|
||||||
/* Get or build reference element */
|
/* Get or build reference element */
|
||||||
let midpoint = Math.round(nodes.length / 2);
|
let midpoint = Math.round(nodes.length / 2);
|
||||||
referenceElement = referenceElement || nodes[midpoint];
|
referenceElement = referenceElement || nodes[midpoint];
|
||||||
|
// console.log(`referenceElement.position: ${referenceElement.position}`);
|
||||||
|
|
||||||
/* Get distances. */
|
/* Get distances. */
|
||||||
let distancesArray = nodes.map((node) => {
|
let distancesArray = nodes.map((node) => {
|
||||||
if (node.name == referenceElement.name) {
|
if (node.name == referenceElement.name) {
|
||||||
return [1];
|
return [1];
|
||||||
} else {
|
} else {
|
||||||
|
// console.log("node");
|
||||||
|
// console.log(node);
|
||||||
let expectedValuesAndDistributionsForElement =
|
let expectedValuesAndDistributionsForElement =
|
||||||
findExpectedValuesAndDistributionsForElement({
|
findExpectedValuesAndDistributionsForElement({
|
||||||
sourceElementName: referenceElement.name,
|
sourceElementName: referenceElement.name,
|
||||||
|
@ -256,6 +303,7 @@ export async function findDistancesFromAllElementsToAllReferencePoints({
|
||||||
links,
|
links,
|
||||||
referenceElement: node,
|
referenceElement: node,
|
||||||
});
|
});
|
||||||
|
// alert(`distancesFromNode.length: ${distancesFromNode.length}`);
|
||||||
distancesForAllElements = distancesForAllElements.map((arr, i) => {
|
distancesForAllElements = distancesForAllElements.map((arr, i) => {
|
||||||
return !!arr && arr.length > 0
|
return !!arr && arr.length > 0
|
||||||
? [...arr, ...distancesFromNode[i]]
|
? [...arr, ...distancesFromNode[i]]
|
||||||
|
|
|
@ -7,6 +7,7 @@ function isFirstElementGreater(links, element1, element2) {
|
||||||
(link.source == element2.name && link.target == element1.name)
|
(link.source == element2.name && link.target == element1.name)
|
||||||
);
|
);
|
||||||
if (relevantComparisons.length == 0) {
|
if (relevantComparisons.length == 0) {
|
||||||
|
// console.log(element1, "vs", element2);
|
||||||
let answer = {
|
let answer = {
|
||||||
foundAnswer: false,
|
foundAnswer: false,
|
||||||
error: errorMsg,
|
error: errorMsg,
|
||||||
|
@ -14,6 +15,7 @@ function isFirstElementGreater(links, element1, element2) {
|
||||||
return answer;
|
return answer;
|
||||||
} else {
|
} else {
|
||||||
const firstLink = relevantComparisons[0];
|
const firstLink = relevantComparisons[0];
|
||||||
|
// console.log(firstLink);
|
||||||
const firstElementFirst =
|
const firstElementFirst =
|
||||||
firstLink.source == element1.name && firstLink.target == element2.name
|
firstLink.source == element1.name && firstLink.target == element2.name
|
||||||
? true
|
? true
|
||||||
|
@ -32,11 +34,16 @@ function isFirstElementGreater(links, element1, element2) {
|
||||||
}
|
}
|
||||||
|
|
||||||
function merge(links, left, right) {
|
function merge(links, left, right) {
|
||||||
let sortedArr = [];
|
let sortedArr = []; // the sorted elements will go here
|
||||||
|
|
||||||
while (left.length && right.length) {
|
while (left.length && right.length) {
|
||||||
// insert the biggest element to the sortedArr
|
// insert the biggest element to the sortedArr
|
||||||
let getComparisonAnswer = isFirstElementGreater(links, left[0], right[0]);
|
let getComparisonAnswer = isFirstElementGreater(links, left[0], right[0]);
|
||||||
if (getComparisonAnswer.foundAnswer == false) {
|
if (getComparisonAnswer.foundAnswer == false) {
|
||||||
|
// console.log("Error@:");
|
||||||
|
// console.group();
|
||||||
|
// console.log({ left, right });
|
||||||
|
// console.groupEnd();
|
||||||
let result = {
|
let result = {
|
||||||
finishedMerge: false,
|
finishedMerge: false,
|
||||||
uncomparedElements: [left[0], right[0]],
|
uncomparedElements: [left[0], right[0]],
|
||||||
|
@ -46,8 +53,6 @@ function merge(links, left, right) {
|
||||||
} else if (getComparisonAnswer.foundAnswer == true) {
|
} else if (getComparisonAnswer.foundAnswer == true) {
|
||||||
if (getComparisonAnswer.isFirstElementFirst == true) {
|
if (getComparisonAnswer.isFirstElementFirst == true) {
|
||||||
// left[0] > right[0]
|
// left[0] > right[0]
|
||||||
// note that we can order from smallest to largest or the reverse
|
|
||||||
// ; I forget which one this is.
|
|
||||||
sortedArr.push(right.shift());
|
sortedArr.push(right.shift());
|
||||||
} else {
|
} else {
|
||||||
sortedArr.push(left.shift());
|
sortedArr.push(left.shift());
|
||||||
|
@ -65,6 +70,7 @@ function merge(links, left, right) {
|
||||||
}
|
}
|
||||||
|
|
||||||
export function mergeSortInner({ recursiveInput, links }) {
|
export function mergeSortInner({ recursiveInput, links }) {
|
||||||
|
// console.log({ l: list.length });
|
||||||
if (recursiveInput.bottleneckedByComparison == true) {
|
if (recursiveInput.bottleneckedByComparison == true) {
|
||||||
let result = {
|
let result = {
|
||||||
recursiveInput: {
|
recursiveInput: {
|
||||||
|
@ -91,7 +97,7 @@ export function mergeSortInner({ recursiveInput, links }) {
|
||||||
}
|
}
|
||||||
|
|
||||||
const left = recursiveInput.list.slice(0, half); // the first half of the list
|
const left = recursiveInput.list.slice(0, half); // the first half of the list
|
||||||
const right = recursiveInput.list.slice(half, recursiveInput.list.length); // Note that splice is destructive, and that slice instead creates a new array.
|
const right = recursiveInput.list.slice(half, recursiveInput.list.length); // Note that splice is destructive.
|
||||||
let orderedFirstHalfAnswer = mergeSortInner({
|
let orderedFirstHalfAnswer = mergeSortInner({
|
||||||
recursiveInput: { list: left, bottleneckedByComparison: false },
|
recursiveInput: { list: left, bottleneckedByComparison: false },
|
||||||
links,
|
links,
|
||||||
|
@ -158,7 +164,7 @@ export function mergeSort({ list, links }) {
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
/*
|
/*
|
||||||
// otherwise, test all other permutations:
|
// otherwise
|
||||||
let permutation = list.slice();
|
let permutation = list.slice();
|
||||||
var length = list.length;
|
var length = list.length;
|
||||||
// let result = [list.slice()];
|
// let result = [list.slice()];
|
||||||
|
|
File diff suppressed because one or more lines are too long
|
@ -4,21 +4,15 @@ This repository creates a react webpage that allows to extract a utility functio
|
||||||
|
|
||||||
It presents the users with a series of elements to compare, using merge-sort in the background to cleverly minimize the number of choices needed.
|
It presents the users with a series of elements to compare, using merge-sort in the background to cleverly minimize the number of choices needed.
|
||||||
|
|
||||||
<p align="center">
|
![](./public/example-prompt.png)
|
||||||
<img width="50%" height="50%" src="./public/example-prompt.png">
|
|
||||||
</p>
|
|
||||||
|
|
||||||
Then, it cleverly aggregates them, on the one hand by producing a graphical representation:
|
Then, it cleverly aggregates them, on the one hand by producing a graphical representation:
|
||||||
|
|
||||||
<p align="center">
|
![](./public/example-graph.png)
|
||||||
<img width="50%" height="50%" src="./public/example-graph.png">
|
|
||||||
</p>
|
|
||||||
|
|
||||||
and on the other hand doing some fast and clever mean aggregation [^1]:
|
and on the other hand doing some fast and clever mean aggregation [^1]:
|
||||||
|
|
||||||
<p align="center">
|
![](./public/example-table.png)
|
||||||
<img width="50%" height="50%" src="./public/example-table.png">
|
|
||||||
</p>
|
|
||||||
|
|
||||||
Initially, users could only input numbers, e.g., "A is `3` times better than B". But now, users can also input distributions, using the [squiggle](https://www.squiggle-language.com/) syntax, e.g., "A is `1 to 10` times better than B", or "A is `mm(normal(1, 10), uniform(0,100))` better than B".
|
Initially, users could only input numbers, e.g., "A is `3` times better than B". But now, users can also input distributions, using the [squiggle](https://www.squiggle-language.com/) syntax, e.g., "A is `1 to 10` times better than B", or "A is `mm(normal(1, 10), uniform(0,100))` better than B".
|
||||||
|
|
||||||
|
@ -71,7 +65,7 @@ The core structure for links is as follows:
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"source": "Spiderman",
|
"source": "Spiderman",
|
||||||
"target": "Jonah Jameson",
|
"target": "Doctor Octopus",
|
||||||
"squiggleString": "20 to 2000",
|
"squiggleString": "20 to 2000",
|
||||||
"distance": 6.76997149080232
|
"distance": 6.76997149080232
|
||||||
},
|
},
|
||||||
|
@ -99,13 +93,13 @@ Distributed under the MIT License. See LICENSE.txt for more information.
|
||||||
- [x] Paths table
|
- [x] Paths table
|
||||||
- [x] Add paths table
|
- [x] Add paths table
|
||||||
- [x] warn that the paths table is approximate.
|
- [x] warn that the paths table is approximate.
|
||||||
- I really don't feel like re-adding this after having worked out the distribution rather than the mean aggregation
|
- However, I really don't feel like re-adding this after having worked out the distribution rather than the mean aggregation
|
||||||
- On the other hand, I think it does make it more user to other users.
|
- However, I think it does make it more user to other users.
|
||||||
- [x] Change README.
|
|
||||||
- [ ] Add functionality like names, etc.
|
- [ ] Add functionality like names, etc.
|
||||||
- I also don't feel like doing this
|
- I also don't feel like doing this
|
||||||
- [ ] Look back at Amazon thing which has been running
|
- [ ] Look back at Amazon thing which has been running
|
||||||
- [ ] Simplify Graph and DynamicSquiggleChart components
|
- [x] Change README.
|
||||||
- [ ] Add squiggle component to initial comparison?
|
|
||||||
|
## Footnotes
|
||||||
|
|
||||||
[^1]: The program takes each element as a reference point in turn, and computing the possible distances from that reference point to all other points, and taking the geometric mean of these distances. This produces a number representing the value of each element, such that the ratios between elements represent the user's preferences: a utility function. However, this isn't perfect; the principled approach woud be to aggregate the distributions rather than their means. But this principled approach is much more slowly. For the principled approach, see the `utility-tools` repository.
|
[^1]: The program takes each element as a reference point in turn, and computing the possible distances from that reference point to all other points, and taking the geometric mean of these distances. This produces a number representing the value of each element, such that the ratios between elements represent the user's preferences: a utility function. However, this isn't perfect; the principled approach woud be to aggregate the distributions rather than their means. But this principled approach is much more slowly. For the principled approach, see the `utility-tools` repository.
|
||||||
|
|
|
@ -40,6 +40,7 @@ export function AdvancedOptions({
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="">
|
<div className="">
|
||||||
|
<br />
|
||||||
{/* Show advanced options*/}
|
{/* Show advanced options*/}
|
||||||
<button
|
<button
|
||||||
key={"advancedOptionsButton-top"}
|
key={"advancedOptionsButton-top"}
|
||||||
|
@ -48,6 +49,7 @@ export function AdvancedOptions({
|
||||||
>
|
>
|
||||||
Advanced options ▼
|
Advanced options ▼
|
||||||
</button>
|
</button>
|
||||||
|
<br />
|
||||||
{/* Toggle buttons */}
|
{/* Toggle buttons */}
|
||||||
<div className={showAdvancedOptions ? "" : "hidden"}>
|
<div className={showAdvancedOptions ? "" : "hidden"}>
|
||||||
{buttonNames.map((buttonName, i) => {
|
{buttonNames.map((buttonName, i) => {
|
||||||
|
@ -62,8 +64,7 @@ export function AdvancedOptions({
|
||||||
);
|
);
|
||||||
})}
|
})}
|
||||||
{/* Element: Show comparisons */}
|
{/* Element: Show comparisons */}
|
||||||
{/* <ShowComparisons links={links} show={showComparisons} /> */}
|
<ShowComparisons links={links} show={showComparisons} />
|
||||||
|
|
||||||
{/* Element: Change comparisons */}
|
{/* Element: Change comparisons */}
|
||||||
<ComparisonsChanger
|
<ComparisonsChanger
|
||||||
setLinks={setLinks}
|
setLinks={setLinks}
|
||||||
|
@ -72,8 +73,6 @@ export function AdvancedOptions({
|
||||||
moveToNextStep={moveToNextStep}
|
moveToNextStep={moveToNextStep}
|
||||||
links={links}
|
links={links}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
{/* Element: Dataset changer */}
|
|
||||||
<DataSetChanger
|
<DataSetChanger
|
||||||
onChangeOfDataset={onChangeOfDataset}
|
onChangeOfDataset={onChangeOfDataset}
|
||||||
show={showChangeDataset}
|
show={showChangeDataset}
|
||||||
|
|
|
@ -1,6 +1,9 @@
|
||||||
import React, { useState, useEffect } from "react";
|
import React, { useState, useEffect } from "react";
|
||||||
import { Separator } from "../separator.js";
|
import { Separator } from "../separator.js";
|
||||||
|
|
||||||
|
// import JSONInput from "react-json-editor-ajrm/index";
|
||||||
|
// import locale from "react-json-editor-ajrm/locale/en";
|
||||||
|
|
||||||
const checkLinksAreOk = (links, listOfElements) => {
|
const checkLinksAreOk = (links, listOfElements) => {
|
||||||
let linkSourceNames = links.map((link) => link.source.name);
|
let linkSourceNames = links.map((link) => link.source.name);
|
||||||
let linkTargetNames = links.map((link) => link.target.name);
|
let linkTargetNames = links.map((link) => link.target.name);
|
||||||
|
@ -63,8 +66,9 @@ export function ComparisonsChanger({
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(async () => {
|
||||||
setValue(JSON.stringify(links, null, 4));
|
setValue(JSON.stringify(links, null, 4));
|
||||||
|
// console.log(JSON.stringify(config, null, 10));
|
||||||
}, [links]);
|
}, [links]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
@ -80,9 +84,30 @@ export function ComparisonsChanger({
|
||||||
value={value}
|
value={value}
|
||||||
onChange={handleTextChange}
|
onChange={handleTextChange}
|
||||||
rows={4 + JSON.stringify(links, null, 4).split("\n").length}
|
rows={4 + JSON.stringify(links, null, 4).split("\n").length}
|
||||||
cols={70}
|
cols={90}
|
||||||
className="text-left text-gray-600 bg-white rounded text-normal p-10 border-0 shadow outline-none focus:outline-none focus:ring "
|
className="text-left text-gray-600 bg-white rounded text-normal p-10 border-0 shadow outline-none focus:outline-none focus:ring "
|
||||||
/>
|
/>
|
||||||
|
{/* */}
|
||||||
|
{/*
|
||||||
|
<div className="flex text-left text-xl justify-around ">
|
||||||
|
<JSONInput
|
||||||
|
placeholder={value} // data to display
|
||||||
|
theme="dark_vscode_tribute" //"light_mitsuketa_tribute" //
|
||||||
|
locale={locale}
|
||||||
|
colors={{
|
||||||
|
string: "#DAA520", // overrides theme colors with whatever color value you want
|
||||||
|
}}
|
||||||
|
height="550px"
|
||||||
|
style={{
|
||||||
|
body: {
|
||||||
|
fontSize: "20px",
|
||||||
|
},
|
||||||
|
}}
|
||||||
|
onChange={onChangeForJsonEditor}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
*/}
|
||||||
<br />
|
<br />
|
||||||
<button
|
<button
|
||||||
className="bg-transparent hover:bg-blue-500 text-blue-700 font-semibold hover:text-white py-2 px-4 border border-blue-500 hover:border-transparent rounded mt-5 p-10"
|
className="bg-transparent hover:bg-blue-500 text-blue-700 font-semibold hover:text-white py-2 px-4 border border-blue-500 hover:border-transparent rounded mt-5 p-10"
|
||||||
|
|
|
@ -33,10 +33,14 @@ export function DataSetChanger({ onChangeOfDataset, show, listOfElements }) {
|
||||||
let handleSubmitInner = (event) => {
|
let handleSubmitInner = (event) => {
|
||||||
clearTimeout(displayingDoneMessageTimer);
|
clearTimeout(displayingDoneMessageTimer);
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
|
//console.log(event)
|
||||||
console.log("value@handleSubmitInner@DataSetChanger");
|
console.log("value@handleSubmitInner@DataSetChanger");
|
||||||
|
//console.log(typeof(value));
|
||||||
console.log(value);
|
console.log(value);
|
||||||
try {
|
try {
|
||||||
let newData = JSON.parse(value);
|
let newData = JSON.parse(value);
|
||||||
|
//console.log(typeof(newData))
|
||||||
|
//console.log(newData)
|
||||||
if (!newData.length || newData.length < 2) {
|
if (!newData.length || newData.length < 2) {
|
||||||
throw Error("Not enough objects");
|
throw Error("Not enough objects");
|
||||||
}
|
}
|
||||||
|
@ -60,7 +64,7 @@ export function DataSetChanger({ onChangeOfDataset, show, listOfElements }) {
|
||||||
rows={
|
rows={
|
||||||
1.2 * JSON.stringify(listOfElements, null, 4).split("\n").length
|
1.2 * JSON.stringify(listOfElements, null, 4).split("\n").length
|
||||||
}
|
}
|
||||||
cols={70}
|
cols={90}
|
||||||
className="text-left text-gray-600 bg-white rounded text-normal p-10 border-0 shadow outline-none focus:outline-none focus:ring "
|
className="text-left text-gray-600 bg-white rounded text-normal p-10 border-0 shadow outline-none focus:outline-none focus:ring "
|
||||||
/>
|
/>
|
||||||
<br />
|
<br />
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
import React, { state } from "react";
|
import React, { state } from "react";
|
||||||
import { CopyBlock, googlecode } from "react-code-blocks";
|
import { CopyBlock, googlecode } from "react-code-blocks";
|
||||||
import { Separator } from "../separator.js";
|
import { Separator } from "../separator.js";
|
||||||
|
// googlecode
|
||||||
|
|
||||||
export function ShowComparisons({ links, show }) {
|
export function ShowComparisons({ links, show }) {
|
||||||
return (
|
return (
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
import React, { useState } from "react";
|
import React, { useState } from "react";
|
||||||
|
// import { SubmitButton } from "./submitButton";
|
||||||
|
|
||||||
export function ComparisonActuator({
|
export function ComparisonActuator({
|
||||||
listOfElements,
|
listOfElements,
|
||||||
|
@ -6,10 +7,8 @@ export function ComparisonActuator({
|
||||||
moveToNextStep,
|
moveToNextStep,
|
||||||
isListOrdered,
|
isListOrdered,
|
||||||
}) {
|
}) {
|
||||||
const initialComparisonString = "";
|
const initialComparisonString = "x to y";
|
||||||
const [comparisonString, setComparisonString] = useState(
|
const [comparisonString, setComparisonString] = useState("x to y");
|
||||||
initialComparisonString
|
|
||||||
);
|
|
||||||
const onChangeComparisonString = async (event) => {
|
const onChangeComparisonString = async (event) => {
|
||||||
if (!isListOrdered) {
|
if (!isListOrdered) {
|
||||||
await setComparisonString(event.target.value);
|
await setComparisonString(event.target.value);
|
||||||
|
@ -17,6 +16,7 @@ export function ComparisonActuator({
|
||||||
};
|
};
|
||||||
|
|
||||||
const onClickSubmitEvent = (event) => {
|
const onClickSubmitEvent = (event) => {
|
||||||
|
// console.log(event.target.value);
|
||||||
if (!isListOrdered) {
|
if (!isListOrdered) {
|
||||||
moveToNextStep({
|
moveToNextStep({
|
||||||
listOfElements,
|
listOfElements,
|
||||||
|
@ -36,7 +36,6 @@ export function ComparisonActuator({
|
||||||
<br />
|
<br />
|
||||||
<input
|
<input
|
||||||
disabled={isListOrdered ? true : false}
|
disabled={isListOrdered ? true : false}
|
||||||
placeholder={"x to y"}
|
|
||||||
type="text"
|
type="text"
|
||||||
className="text-center text-blueGray-600 bg-white rounded text-lg border-0 shadow outline-none focus:outline-none focus:ring w-8/12 h-10 m-2"
|
className="text-center text-blueGray-600 bg-white rounded text-lg border-0 shadow outline-none focus:outline-none focus:ring w-8/12 h-10 m-2"
|
||||||
value={comparisonString}
|
value={comparisonString}
|
||||||
|
|
|
@ -1,74 +0,0 @@
|
||||||
import React from "react";
|
|
||||||
// import { SquiggleChart } from "@quri/squiggle-components";
|
|
||||||
|
|
||||||
import dynamic from "next/dynamic";
|
|
||||||
|
|
||||||
const SquiggleChart = dynamic(
|
|
||||||
() => import("@quri/squiggle-components").then((mod) => mod.SquiggleChart),
|
|
||||||
{
|
|
||||||
loading: () => <p>Loading...</p>,
|
|
||||||
ssr: false,
|
|
||||||
}
|
|
||||||
);
|
|
||||||
/*
|
|
||||||
const SquiggleChart = dynamic(
|
|
||||||
() => import("@quri/squiggle-components").then((mod) => mod.SquiggleChart),
|
|
||||||
{
|
|
||||||
suspense: true,
|
|
||||||
ssr: false,
|
|
||||||
}
|
|
||||||
);
|
|
||||||
*/
|
|
||||||
|
|
||||||
const effectButtonStyle =
|
|
||||||
"bg-transparent m-2 hover:bg-blue-500 text-blue-700 font-semibold hover:text-white py-2 px-4 border border-blue-500 hover:border-transparent rounded mt-5";
|
|
||||||
|
|
||||||
export function DynamicSquiggleChart({ link, stopShowing }) {
|
|
||||||
if (link == null) {
|
|
||||||
return "";
|
|
||||||
} else {
|
|
||||||
let usefulLink = {
|
|
||||||
source: link.source,
|
|
||||||
target: link.target,
|
|
||||||
squiggleString: link.squiggleString,
|
|
||||||
};
|
|
||||||
return (
|
|
||||||
<div className="">
|
|
||||||
<textarea
|
|
||||||
value={JSON.stringify(usefulLink, null, 4)}
|
|
||||||
//onChange={handleChange}
|
|
||||||
disabled={true}
|
|
||||||
rows={JSON.stringify(usefulLink, null, 4).split("\n").length}
|
|
||||||
cols={37}
|
|
||||||
className="text-left text-gray-600 bg-white rounded text-normal p-6 border-0 shadow outline-none focus:outline-none focus:ring mb-4"
|
|
||||||
/>
|
|
||||||
<SquiggleChart
|
|
||||||
squiggleString={link.squiggleString}
|
|
||||||
width={445}
|
|
||||||
height={200}
|
|
||||||
showSummary={true}
|
|
||||||
showTypes={true}
|
|
||||||
/>
|
|
||||||
{/*
|
|
||||||
SquiggleChart props:
|
|
||||||
squiggleString?: string;
|
|
||||||
sampleCount?: number;
|
|
||||||
environment?: environment;
|
|
||||||
chartSettings?: FunctionChartSettings;
|
|
||||||
onChange?(expr: squiggleExpression): void;
|
|
||||||
width?: number;
|
|
||||||
height?: number;
|
|
||||||
bindings?: bindings;
|
|
||||||
jsImports?: jsImports;
|
|
||||||
showSummary?: boolean;
|
|
||||||
showTypes?: boolean;
|
|
||||||
showControls?: boolean;
|
|
||||||
*/}
|
|
||||||
|
|
||||||
<button className={effectButtonStyle} onClick={() => stopShowing()}>
|
|
||||||
Hide chart
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,16 +1,15 @@
|
||||||
import React, { useEffect, useState, useRef } from "react";
|
import React, { useEffect, useState, useRef } from "react";
|
||||||
import colormap from "colormap";
|
import colormap from "colormap";
|
||||||
import cytoscape from "cytoscape";
|
import cytoscape from "cytoscape";
|
||||||
|
import spread from "cytoscape-spread";
|
||||||
|
|
||||||
import { DynamicSquiggleChart } from "../dynamicSquiggleChart.js";
|
|
||||||
import {
|
import {
|
||||||
resolveToNumIfPossible,
|
resolveToNumIfPossible,
|
||||||
getSquiggleSparkline,
|
getSquiggleSparkline,
|
||||||
} from "../../lib/squiggleCalculations.js";
|
} from "../../lib/squiggle.js";
|
||||||
import { truncateValueForDisplay } from "../../lib/truncateNums.js";
|
import { truncateValueForDisplay } from "../../lib/truncateNums.js";
|
||||||
import { cutOffLongNames } from "../../lib/stringManipulations.js";
|
import { cutOffLongNames } from "../../lib/stringManipulations.js";
|
||||||
|
|
||||||
// import spread from "cytoscape-spread";
|
|
||||||
// import dagre from "cytoscape-dagre";
|
// import dagre from "cytoscape-dagre";
|
||||||
// import cola from "cytoscape-cola";
|
// import cola from "cytoscape-cola";
|
||||||
// import fcose from "cytoscape-fcose";
|
// import fcose from "cytoscape-fcose";
|
||||||
|
@ -37,7 +36,7 @@ const getEdgeLabel = async (squiggleString) => {
|
||||||
//alert("▁▁▁▁▁▁▁▁▁▁▁");
|
//alert("▁▁▁▁▁▁▁▁▁▁▁");
|
||||||
}
|
}
|
||||||
|
|
||||||
return squiggleString + sparklineConcat;
|
return squiggleString + sparklineConcat; //sparkline;
|
||||||
};
|
};
|
||||||
|
|
||||||
const getColors = (n) => {
|
const getColors = (n) => {
|
||||||
|
@ -51,7 +50,7 @@ const getColors = (n) => {
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
colors = colormap({
|
colors = colormap({
|
||||||
colormap: "greys", // other themes: hot, winter, etc.
|
colormap: "greys", // hot,
|
||||||
nshades: n,
|
nshades: n,
|
||||||
format: "hex",
|
format: "hex",
|
||||||
alpha: 1,
|
alpha: 1,
|
||||||
|
@ -68,9 +67,6 @@ export function Graph({
|
||||||
}) {
|
}) {
|
||||||
const containerRef = useRef("hello-world");
|
const containerRef = useRef("hello-world");
|
||||||
const [visibility, setVisibility] = useState(""); /// useState("invisible");
|
const [visibility, setVisibility] = useState(""); /// useState("invisible");
|
||||||
const [cs, setCs] = useState(null); /// useState("invisible");
|
|
||||||
const [selectedLink, setSelectedLink] = useState(null);
|
|
||||||
const [selectedLinkTimeout, setSelectedLinkTimeout] = useState(null);
|
|
||||||
|
|
||||||
const callEffect = async ({
|
const callEffect = async ({
|
||||||
listOfElements,
|
listOfElements,
|
||||||
|
@ -81,7 +77,7 @@ export function Graph({
|
||||||
//setVisibility("invisible");
|
//setVisibility("invisible");
|
||||||
let layoutName = "circle"; //
|
let layoutName = "circle"; //
|
||||||
|
|
||||||
// cytoscape.use(spread); // necessary for non-default themes,
|
// cytoscape.use(circle); // spread, circle,
|
||||||
let listOfElementsForGraph = isListOrdered
|
let listOfElementsForGraph = isListOrdered
|
||||||
? listAfterMergeSort
|
? listAfterMergeSort
|
||||||
: listOfElements;
|
: listOfElements;
|
||||||
|
@ -112,7 +108,6 @@ export function Graph({
|
||||||
source: cutOffLongNames(link.source),
|
source: cutOffLongNames(link.source),
|
||||||
target: cutOffLongNames(link.target),
|
target: cutOffLongNames(link.target),
|
||||||
label: await getEdgeLabel(link.squiggleString),
|
label: await getEdgeLabel(link.squiggleString),
|
||||||
squiggleString: link.squiggleString,
|
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
})
|
})
|
||||||
|
@ -127,6 +122,7 @@ export function Graph({
|
||||||
content: "data(id)",
|
content: "data(id)",
|
||||||
"background-color": "data(color)",
|
"background-color": "data(color)",
|
||||||
"text-wrap": "wrap",
|
"text-wrap": "wrap",
|
||||||
|
//"text-overflow-wrap": "anywhere",
|
||||||
"text-max-width": 70,
|
"text-max-width": 70,
|
||||||
"z-index": 1,
|
"z-index": 1,
|
||||||
},
|
},
|
||||||
|
@ -170,6 +166,8 @@ export function Graph({
|
||||||
"text-border-width": 0.5,
|
"text-border-width": 0.5,
|
||||||
"text-border-opacity": 1,
|
"text-border-opacity": 1,
|
||||||
"z-index": 3,
|
"z-index": 3,
|
||||||
|
|
||||||
|
// "text-rotation": "autorotate"
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
@ -177,69 +175,48 @@ export function Graph({
|
||||||
const config = {
|
const config = {
|
||||||
container: containerRef.current,
|
container: containerRef.current,
|
||||||
style: cytoscapeStylesheet,
|
style: cytoscapeStylesheet,
|
||||||
elements: [...nodeElements, ...linkElements],
|
elements: [
|
||||||
|
/* Dummy data:
|
||||||
|
{ data: { id: "n1" } },
|
||||||
|
{ data: { id: "n2" } },
|
||||||
|
{ data: { id: "e1", source: "n1", target: "n2" } },
|
||||||
|
|
||||||
|
Real data:*/
|
||||||
|
...nodeElements,
|
||||||
|
...linkElements,
|
||||||
|
],
|
||||||
layout: {
|
layout: {
|
||||||
name: layoutName, // circle, grid, dagre
|
name: layoutName, // circle, grid, dagre
|
||||||
minDist: 10,
|
minDist: 10,
|
||||||
//prelayout: false,
|
//prelayout: false,
|
||||||
// animate: false, // whether to transition the node positions
|
// animate: false, // whether to transition the node positions
|
||||||
// animationDuration: 250, // duration of animation in ms if enabled
|
// animationDuration: 250, // duration of animation in ms if enabled
|
||||||
// the cytoscape documentation is pretty good here.
|
|
||||||
},
|
},
|
||||||
userZoomingEnabled: false,
|
userZoomingEnabled: false,
|
||||||
userPanningEnabled: false,
|
userPanningEnabled: false,
|
||||||
};
|
};
|
||||||
let newCs = cytoscape(config);
|
cytoscape(config);
|
||||||
setCs(newCs);
|
//setTimeout(() => setVisibility(""), 700);
|
||||||
// setTimeout(() => setVisibility(""), 700);
|
|
||||||
// necessary for themes like spread, which have
|
|
||||||
// a confusing animation at the beginning
|
|
||||||
};
|
};
|
||||||
useEffect(() => {
|
useEffect(async () => {
|
||||||
callEffect({
|
await callEffect({
|
||||||
listOfElements,
|
listOfElements,
|
||||||
links,
|
links,
|
||||||
isListOrdered,
|
isListOrdered,
|
||||||
listAfterMergeSort,
|
listAfterMergeSort,
|
||||||
});
|
});
|
||||||
}, [listOfElements, links, isListOrdered, listAfterMergeSort, selectedLink]);
|
// console.log(JSON.stringify(config, null, 10));
|
||||||
|
}, [listOfElements, links, isListOrdered, listAfterMergeSort]);
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (cs != null) {
|
|
||||||
clearTimeout(selectedLinkTimeout);
|
|
||||||
let newTimeout = setTimeout(() => {
|
|
||||||
cs.edges().on("mouseover", (event) => {
|
|
||||||
// on("click",
|
|
||||||
let edge = event.target;
|
|
||||||
// alert(JSON.stringify(edge.json()));
|
|
||||||
console.log(JSON.stringify(edge.json()));
|
|
||||||
setSelectedLink(JSON.parse(JSON.stringify(edge.json())).data);
|
|
||||||
});
|
|
||||||
}, 100);
|
|
||||||
setSelectedLinkTimeout(newTimeout);
|
|
||||||
}
|
|
||||||
}, [cs]);
|
|
||||||
return (
|
return (
|
||||||
<div className="grid place-items-center">
|
<div className="">
|
||||||
<div
|
<div className={visibility + "grid grid-cols-1 place-items-center "}>
|
||||||
className={
|
|
||||||
visibility +
|
|
||||||
`grid grid-cols-${
|
|
||||||
selectedLink == null ? "1 " : "2"
|
|
||||||
} place-items-center place-self-center space-x-0 w-10/12 `
|
|
||||||
}
|
|
||||||
>
|
|
||||||
<div
|
<div
|
||||||
ref={containerRef}
|
ref={containerRef}
|
||||||
style={{
|
style={{
|
||||||
height: "900px", // isListOrdered ? "900px" : "500px",
|
height: "900px", // isListOrdered ? "900px" : "500px",
|
||||||
width: "900px", // isListOrdered ? "900px" : "500px",
|
width: "900px", // isListOrdered ? "900px" : "500px",
|
||||||
}}
|
}}
|
||||||
className=""
|
|
||||||
/>
|
|
||||||
<DynamicSquiggleChart
|
|
||||||
link={selectedLink}
|
|
||||||
stopShowing={() => setSelectedLink(null)}
|
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<button
|
<button
|
||||||
|
|
|
@ -11,11 +11,10 @@ import { ResultsTable } from "./resultsTable.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";
|
||||||
import { resolveToNumIfPossible } from "../lib/squiggleCalculations.js";
|
import { resolveToNumIfPossible } from "../lib/squiggle.js";
|
||||||
|
|
||||||
export function Homepage({ listOfElementsInit }) {
|
export function Homepage({ listOfElementsInit }) {
|
||||||
const SLICE = false;
|
const SLICE = false;
|
||||||
|
|
||||||
/* Statefull elements */
|
/* Statefull elements */
|
||||||
|
|
||||||
// list of elements
|
// list of elements
|
||||||
|
@ -44,8 +43,6 @@ export function Homepage({ listOfElementsInit }) {
|
||||||
pairCurrentlyBeingComparedInit
|
pairCurrentlyBeingComparedInit
|
||||||
);
|
);
|
||||||
|
|
||||||
/* Effects */
|
|
||||||
|
|
||||||
// dataset changer
|
// dataset changer
|
||||||
const onChangeOfDataset = (newListOfElements) => {
|
const onChangeOfDataset = (newListOfElements) => {
|
||||||
setListOfElements(newListOfElements);
|
setListOfElements(newListOfElements);
|
||||||
|
@ -70,11 +67,14 @@ export function Homepage({ listOfElementsInit }) {
|
||||||
} else {
|
} else {
|
||||||
setListAfterMergeSort(mergeSortOutput.orderedList);
|
setListAfterMergeSort(mergeSortOutput.orderedList);
|
||||||
pushToMongo({ mergeSortOutput, links });
|
pushToMongo({ mergeSortOutput, links });
|
||||||
setIsListOrdered(true); // should be at the end, because some useEffects are triggered by it.
|
setIsListOrdered(true); // good if it's at the end.
|
||||||
|
// alert(JSON.stringify(mergeSortOutput, null, 4));
|
||||||
|
// chooseNextPairToCompareRandomly({ listOfElements });
|
||||||
|
// return 1;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// Main mergesort step
|
// full
|
||||||
const moveToNextStep = async ({
|
const moveToNextStep = async ({
|
||||||
listOfElements,
|
listOfElements,
|
||||||
pairCurrentlyBeingCompared,
|
pairCurrentlyBeingCompared,
|
||||||
|
@ -82,8 +82,8 @@ export function Homepage({ listOfElementsInit }) {
|
||||||
whileChangingStuff,
|
whileChangingStuff,
|
||||||
newLinksFromChangingStuff,
|
newLinksFromChangingStuff,
|
||||||
}) => {
|
}) => {
|
||||||
|
// In the normal course of things:
|
||||||
if (!whileChangingStuff) {
|
if (!whileChangingStuff) {
|
||||||
// In the normal course of things:
|
|
||||||
let newLink = {
|
let newLink = {
|
||||||
source: pairCurrentlyBeingCompared[0].name,
|
source: pairCurrentlyBeingCompared[0].name,
|
||||||
target: pairCurrentlyBeingCompared[1].name,
|
target: pairCurrentlyBeingCompared[1].name,
|
||||||
|
@ -94,6 +94,8 @@ export function Homepage({ listOfElementsInit }) {
|
||||||
if (numOption.asNum == false) {
|
if (numOption.asNum == false) {
|
||||||
alert(JSON.stringify(numOption.errorMsg));
|
alert(JSON.stringify(numOption.errorMsg));
|
||||||
} else if (numOption.asNum == true) {
|
} else if (numOption.asNum == true) {
|
||||||
|
// addLink({ ...newLink, distance: numOption.num }, links);
|
||||||
|
/// let newLinks = [...links, { ...newLink, distance: numOption.num }];
|
||||||
newLink = { ...newLink, distance: numOption.num };
|
newLink = { ...newLink, distance: numOption.num };
|
||||||
addLink(newLink, links);
|
addLink(newLink, links);
|
||||||
let newLinks = [...links, newLink];
|
let newLinks = [...links, newLink];
|
||||||
|
@ -101,13 +103,14 @@ export function Homepage({ listOfElementsInit }) {
|
||||||
mergeSortStep({ list: listOfElements, links: newLinks });
|
mergeSortStep({ list: listOfElements, links: newLinks });
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// When changing comparisons:
|
|
||||||
mergeSortStep({
|
mergeSortStep({
|
||||||
list: listOfElements,
|
list: listOfElements,
|
||||||
links: newLinksFromChangingStuff,
|
links: newLinksFromChangingStuff,
|
||||||
});
|
});
|
||||||
setNumStepsNow(0); // almost no guarantees of how many left.
|
setNumStepsNow(0); // almost no guarantees of how many left.
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// When changing comparisons:
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
@ -120,10 +123,8 @@ export function Homepage({ listOfElementsInit }) {
|
||||||
/>
|
/>
|
||||||
|
|
||||||
{/* Comparisons section */}
|
{/* Comparisons section */}
|
||||||
<div
|
<div className={"" /*isListOrdered ? "hidden" : ""*/}>
|
||||||
className={"grid place-items-center" /*isListOrdered ? "hidden" : ""*/}
|
<div className="flex justify-evenly mt-10">
|
||||||
>
|
|
||||||
<div className="grid grid-rows-1 grid-cols-3 place-items-center w-6/11 mt-10">
|
|
||||||
<DisplayElementForComparison
|
<DisplayElementForComparison
|
||||||
element={pairCurrentlyBeingCompared[0]}
|
element={pairCurrentlyBeingCompared[0]}
|
||||||
></DisplayElementForComparison>
|
></DisplayElementForComparison>
|
||||||
|
|
|
@ -5,7 +5,6 @@ import { Separator } from "./separator.js";
|
||||||
|
|
||||||
import { truncateValueForDisplay } from "../lib/truncateNums.js";
|
import { truncateValueForDisplay } from "../lib/truncateNums.js";
|
||||||
import { cutOffLongNames } from "../lib/stringManipulations.js";
|
import { cutOffLongNames } from "../lib/stringManipulations.js";
|
||||||
import { getCoefficientOfVariation } from "../lib/coefficientOfVariation.js";
|
|
||||||
|
|
||||||
async function fullResultsTable({ listAfterMergeSort, links }) {
|
async function fullResultsTable({ listAfterMergeSort, links }) {
|
||||||
console.log("listAfterMergeSort", listAfterMergeSort);
|
console.log("listAfterMergeSort", listAfterMergeSort);
|
||||||
|
@ -14,6 +13,7 @@ async function fullResultsTable({ listAfterMergeSort, links }) {
|
||||||
orderedList: listAfterMergeSort,
|
orderedList: listAfterMergeSort,
|
||||||
links: links,
|
links: links,
|
||||||
});
|
});
|
||||||
|
// console.log(pathsArray);
|
||||||
let aggregatedPaths = await aggregatePaths({
|
let aggregatedPaths = await aggregatePaths({
|
||||||
pathsArray: pathsArray,
|
pathsArray: pathsArray,
|
||||||
orderedList: listAfterMergeSort,
|
orderedList: listAfterMergeSort,
|
||||||
|
@ -23,6 +23,38 @@ async function fullResultsTable({ listAfterMergeSort, links }) {
|
||||||
return aggregatedPaths;
|
return aggregatedPaths;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const sum = (arr) => arr.reduce((a, b) => a + b, 0);
|
||||||
|
|
||||||
|
function getStdev(arr) {
|
||||||
|
if (Array.isArray(arr) && arr.length > 0) {
|
||||||
|
const n = arr.length;
|
||||||
|
const mean = arr.reduce((a, b) => a + b) / n;
|
||||||
|
return Math.sqrt(
|
||||||
|
arr.map((x) => Math.pow(x - mean, 2)).reduce((a, b) => a + b) / n
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export const geomMean = (arr) => {
|
||||||
|
let n = arr.length;
|
||||||
|
let logavg = sum(arr.map((x) => Math.log(x))); // works for low numbers much better
|
||||||
|
let result = Math.exp(logavg / n);
|
||||||
|
return result;
|
||||||
|
};
|
||||||
|
|
||||||
|
const getCoefficientOfVariation = (arr) => {
|
||||||
|
let nonPositiveNumbers = arr.filter((x) => x <= 0);
|
||||||
|
if (nonPositiveNumbers.length == 0) {
|
||||||
|
let gm = geomMean(arr);
|
||||||
|
let stdev = getStdev(arr);
|
||||||
|
return stdev / gm;
|
||||||
|
} else {
|
||||||
|
return getStdev(arr) / avg(arr);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
function abridgeArrayAndDisplay(array) {
|
function abridgeArrayAndDisplay(array) {
|
||||||
let newArray;
|
let newArray;
|
||||||
let formatForDisplay;
|
let formatForDisplay;
|
||||||
|
@ -60,6 +92,8 @@ function getRow(row, i) {
|
||||||
}
|
}
|
||||||
|
|
||||||
function reactTableContents(tableContents) {
|
function reactTableContents(tableContents) {
|
||||||
|
// alert(JSON.stringify(tableContents));
|
||||||
|
// return "Hello";
|
||||||
return tableContents.map((row, i) => getRow(row, i));
|
return tableContents.map((row, i) => getRow(row, i));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -67,22 +101,20 @@ export function ResultsTable({ isListOrdered, listAfterMergeSort, links }) {
|
||||||
const [isTableComputed, setIsTableComputed] = useState(false);
|
const [isTableComputed, setIsTableComputed] = useState(false);
|
||||||
const [tableContents, setTableContents] = useState([]);
|
const [tableContents, setTableContents] = useState([]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(async () => {
|
||||||
let iAsync = async () => {
|
// console.log(JSON.stringify(config, null, 10));
|
||||||
if (isListOrdered && listAfterMergeSort.length > 0) {
|
if (isListOrdered && listAfterMergeSort.length > 0) {
|
||||||
// both comparisons aren't strictly necessary,
|
// both necessary because there is a small moment when list is ordered
|
||||||
// but it bit me once, so I'm leaving it
|
// but listAfterMergeSort wasn't ready yet
|
||||||
let tableContentsResult = await fullResultsTable({
|
let tableContentsResult = await fullResultsTable({
|
||||||
listAfterMergeSort,
|
listAfterMergeSort,
|
||||||
links,
|
links,
|
||||||
});
|
});
|
||||||
console.log(tableContentsResult);
|
console.log(tableContentsResult);
|
||||||
setTableContents(tableContentsResult);
|
// alert(JSON.stringify(tableContentsResult));
|
||||||
setIsTableComputed(true);
|
setTableContents(tableContentsResult);
|
||||||
}
|
setIsTableComputed(true);
|
||||||
return () => console.log("cleanup");
|
}
|
||||||
};
|
|
||||||
iAsync();
|
|
||||||
}, [isListOrdered, listAfterMergeSort, links]);
|
}, [isListOrdered, listAfterMergeSort, links]);
|
||||||
|
|
||||||
return !(isListOrdered && isTableComputed) ? (
|
return !(isListOrdered && isTableComputed) ? (
|
||||||
|
@ -121,18 +153,10 @@ export function ResultsTable({ isListOrdered, listAfterMergeSort, links }) {
|
||||||
</div>
|
</div>
|
||||||
<div className="grid w-full place-items-center text-center ">
|
<div className="grid w-full place-items-center text-center ">
|
||||||
<p className="mt-8 max-w-5xl">
|
<p className="mt-8 max-w-5xl">
|
||||||
*This is the geometric mean of all means of paths if all elements are
|
*This is the geometric mean if all elements are either all positive or
|
||||||
either all positive or all negative, and the arithmetic mean
|
all negative, and the arithmetic mean otherwise. For a principled
|
||||||
otherwise. Paths with a non-numeric mean (e.g., resulting from
|
aggregation which is able to produce meaningfull 90% confidence
|
||||||
dividing by a mean of 0) are ignored. For a principled aggregation
|
intervals, see the utility-tools package in npm or github
|
||||||
which is able to produce meaningfull 90% confidence intervals, see the{" "}
|
|
||||||
<a
|
|
||||||
href="https://github.com/quantified-uncertainty/utility-function-extractor/tree/master/packages/utility-tools"
|
|
||||||
target="_blank"
|
|
||||||
>
|
|
||||||
utility-tools package
|
|
||||||
</a>{" "}
|
|
||||||
in npm or Github
|
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -2,8 +2,8 @@ import React from "react";
|
||||||
|
|
||||||
export function Separator() {
|
export function Separator() {
|
||||||
return (
|
return (
|
||||||
<div className="py-4">
|
<div class="py-4">
|
||||||
<div className="w-full border-t border-4 border-gray-300 mt-10"></div>
|
<div class="w-full border-t border-4 border-gray-300 mt-10"></div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,33 +0,0 @@
|
||||||
const sum = (arr) => arr.reduce((a, b) => a + b, 0);
|
|
||||||
|
|
||||||
function getStdev(arr) {
|
|
||||||
if (Array.isArray(arr) && arr.length > 0) {
|
|
||||||
const n = arr.length;
|
|
||||||
const mean = arr.reduce((a, b) => a + b) / n;
|
|
||||||
return Math.sqrt(
|
|
||||||
arr.map((x) => Math.pow(x - mean, 2)).reduce((a, b) => a + b) / n
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
return 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, numerically
|
|
||||||
let result = Math.exp(logavg / n);
|
|
||||||
return result;
|
|
||||||
};
|
|
||||||
|
|
||||||
export const getCoefficientOfVariation = (arr) => {
|
|
||||||
let nonPositiveNumbers = arr.filter((x) => x <= 0);
|
|
||||||
if (nonPositiveNumbers.length == 0) {
|
|
||||||
let gm = geomMean(arr);
|
|
||||||
let stdev = getStdev(arr);
|
|
||||||
return stdev / gm;
|
|
||||||
} else {
|
|
||||||
return getStdev(arr) / avg(arr);
|
|
||||||
}
|
|
||||||
};
|
|
|
@ -13,3 +13,4 @@ export async function pushToMongo(data) {
|
||||||
console.log(response);
|
console.log(response);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// pushToMongo()
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
let topOutAt100AndValidate = (x) => {
|
let topOutAt100AndValidate = (x) => {
|
||||||
|
// return 10;
|
||||||
if (x == x) {
|
if (x == x) {
|
||||||
return x > 99 ? 99 : x < 0 ? 2 : x;
|
return x > 99 ? 99 : x < 0 ? 2 : x;
|
||||||
} else {
|
} else {
|
||||||
|
@ -21,8 +22,6 @@ export const truncateValueForDisplay = (value) => {
|
||||||
candidateNumSignificantDigits
|
candidateNumSignificantDigits
|
||||||
);
|
);
|
||||||
result = value.toFixed(numSignificantDigits);
|
result = value.toFixed(numSignificantDigits);
|
||||||
} else if (value == 0) {
|
|
||||||
return 0;
|
|
||||||
} else if (-1 < value) {
|
} else if (-1 < value) {
|
||||||
let candidateNumSignificantDigits =
|
let candidateNumSignificantDigits =
|
||||||
-Math.floor(Math.log(Math.abs(value)) / Math.log(10)) + 1;
|
-Math.floor(Math.log(Math.abs(value)) / Math.log(10)) + 1;
|
||||||
|
|
|
@ -9,7 +9,6 @@
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@nextui-org/react": "^1.0.0-beta.9",
|
"@nextui-org/react": "^1.0.0-beta.9",
|
||||||
"@quri/squiggle-components": "^0.2.22",
|
|
||||||
"@quri/squiggle-lang": "^0.2.11",
|
"@quri/squiggle-lang": "^0.2.11",
|
||||||
"axios": "^0.21.4",
|
"axios": "^0.21.4",
|
||||||
"colormap": "^2.3.2",
|
"colormap": "^2.3.2",
|
||||||
|
@ -21,14 +20,14 @@
|
||||||
"cytoscape-spread": "^3.0.0",
|
"cytoscape-spread": "^3.0.0",
|
||||||
"next": "latest",
|
"next": "latest",
|
||||||
"path": "^0.12.7",
|
"path": "^0.12.7",
|
||||||
"react": "^18.0.1",
|
"react": "^17.0.1",
|
||||||
"react-code-blocks": "^0.0.9-0",
|
"react-code-blocks": "^0.0.9-0",
|
||||||
"react-compound-slider": "^3.3.1",
|
"react-compound-slider": "^3.3.1",
|
||||||
"react-dom": "^18.0.1",
|
"react-dom": "17.0.1",
|
||||||
"react-markdown": "^6.0.2",
|
"react-markdown": "^6.0.2",
|
||||||
"remark-gfm": "^1.0.0",
|
"remark-gfm": "^1.0.0",
|
||||||
"simple-react-cytoscape": "^1.0.4",
|
"simple-react-cytoscape": "^1.0.4",
|
||||||
"utility-tools": "^1.0.4"
|
"utility-tools": "^0.2.5"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@netlify/plugin-nextjs": "^4.2.1",
|
"@netlify/plugin-nextjs": "^4.2.1",
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
/* Notes */
|
/* Notes */
|
||||||
|
|
||||||
// This function is just a simple wrapper around components/homepage.
|
// This function is just a simple wrapper around lib/comparisonView.
|
||||||
// Most of the time, I'll want to edit that instead
|
// Most of the time, I'll want to edit that instead
|
||||||
|
|
||||||
/* Imports */
|
/* Imports */
|
||||||
|
|
Binary file not shown.
Before Width: | Height: | Size: 231 KiB |
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue
Block a user