feat: cleanup
8
packages/README.md
Normal file
|
@ -0,0 +1,8 @@
|
|||
[](./webpage-refactor/public/example-graph.png)
|
||||
|
||||
This repository contains the code for the website [utility-function-extractor.quantifieduncertainty.org/](https://utility-function-extractor.quantifieduncertainty.org/) and tools to analyze utility comparisons. For details, see the relevant folders.
|
||||
|
||||
- [Webpage repository](to do: add links)
|
||||
- [Tools repository](to do: add link)
|
||||
|
||||
[](./webpage-refactor/public/example-table.png)
|
1
packages/webpage-legacy/README.md
Normal file
|
@ -0,0 +1 @@
|
|||
Deprecated; see webpage-refactor instead.
|
Before Width: | Height: | Size: 289 KiB After Width: | Height: | Size: 289 KiB |
Before Width: | Height: | Size: 294 KiB After Width: | Height: | Size: 294 KiB |
Before Width: | Height: | Size: 336 KiB After Width: | Height: | Size: 336 KiB |
Before Width: | Height: | Size: 282 KiB After Width: | Height: | Size: 282 KiB |
Before Width: | Height: | Size: 266 KiB After Width: | Height: | Size: 266 KiB |
Before Width: | Height: | Size: 294 KiB After Width: | Height: | Size: 294 KiB |
|
@ -354,7 +354,7 @@ export function CreateTable({ tableRows }) {
|
|||
}
|
||||
return (
|
||||
<div className="w-full">
|
||||
<table className="w-full">
|
||||
<table className="table-auto">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Id</th>
|
337
packages/webpage-legacy/lib/pushToMongoManually.js
Normal file
|
@ -0,0 +1,337 @@
|
|||
import axios from "axios";
|
||||
|
||||
let data = [
|
||||
{
|
||||
source: "Thinking Fast and Slow",
|
||||
|
||||
target: "The Mathematical Theory of Communication",
|
||||
|
||||
distance: 99999.99999999999,
|
||||
|
||||
reasoning:
|
||||
"TFS: Not original work. 1 million buyers, 0.3m readers, 0.03m did anything with it long-term. Debiasing doesn't work that much. But maybe some general sanity effects.\nMTC: foundational to half of good fields. Direct applications key to telecoms and internet, AI. Is very natural and probably invented anyway, but let's ignore replaceability. ",
|
||||
},
|
||||
|
||||
{
|
||||
source: "The Global Priorities Institute's Research Agenda",
|
||||
|
||||
target: "Superintelligence",
|
||||
|
||||
distance: 100,
|
||||
|
||||
reasoning:
|
||||
"GPI: good stuff but v v hard questions read by ~200.\nSuperintelligence: Maybe 50,000 copies? associated interview tour transformed the field.",
|
||||
},
|
||||
|
||||
{
|
||||
source: "Thinking Fast and Slow",
|
||||
|
||||
target: "The Global Priorities Institute's Research Agenda",
|
||||
|
||||
distance: "100",
|
||||
|
||||
reasoning: "",
|
||||
},
|
||||
|
||||
{
|
||||
source: "The Global Priorities Institute's Research Agenda",
|
||||
|
||||
target: "The Mathematical Theory of Communication",
|
||||
|
||||
distance: 1000,
|
||||
|
||||
reasoning: "",
|
||||
},
|
||||
|
||||
{
|
||||
source: "Superintelligence",
|
||||
|
||||
target: "The Mathematical Theory of Communication",
|
||||
|
||||
distance: 10,
|
||||
|
||||
reasoning: "",
|
||||
},
|
||||
|
||||
{
|
||||
source: "Categorizing Variants of Goodhart's Law",
|
||||
|
||||
target: "The Vulnerable World Hypothesis",
|
||||
|
||||
distance: 10,
|
||||
|
||||
reasoning: "",
|
||||
},
|
||||
|
||||
{
|
||||
source: "Shallow evaluations of longtermist organizations",
|
||||
|
||||
target: "The motivated reasoning critique of effective altruism",
|
||||
|
||||
distance: 10,
|
||||
|
||||
reasoning: "",
|
||||
},
|
||||
|
||||
{
|
||||
source: "Shallow evaluations of longtermist organizations",
|
||||
|
||||
target: "Categorizing Variants of Goodhart's Law",
|
||||
|
||||
distance: 100,
|
||||
|
||||
reasoning: "",
|
||||
},
|
||||
|
||||
{
|
||||
source: "The motivated reasoning critique of effective altruism",
|
||||
|
||||
target: "Categorizing Variants of Goodhart's Law",
|
||||
|
||||
distance: 10,
|
||||
|
||||
reasoning: "",
|
||||
},
|
||||
|
||||
{
|
||||
source: "Shallow evaluations of longtermist organizations",
|
||||
|
||||
target: "Thinking Fast and Slow",
|
||||
|
||||
distance: 10,
|
||||
|
||||
reasoning: "",
|
||||
},
|
||||
|
||||
{
|
||||
source: "Thinking Fast and Slow",
|
||||
|
||||
target: "The motivated reasoning critique of effective altruism",
|
||||
|
||||
distance: 1,
|
||||
|
||||
reasoning: "",
|
||||
},
|
||||
|
||||
{
|
||||
source: "The motivated reasoning critique of effective altruism",
|
||||
|
||||
target: "The Global Priorities Institute's Research Agenda",
|
||||
|
||||
distance: 1000,
|
||||
|
||||
reasoning: "",
|
||||
},
|
||||
|
||||
{
|
||||
source: "Categorizing Variants of Goodhart's Law",
|
||||
|
||||
target: "The Global Priorities Institute's Research Agenda",
|
||||
|
||||
distance: 100,
|
||||
|
||||
reasoning: "",
|
||||
},
|
||||
|
||||
{
|
||||
source: "The Vulnerable World Hypothesis",
|
||||
|
||||
target: "The Global Priorities Institute's Research Agenda",
|
||||
|
||||
distance: 10,
|
||||
|
||||
reasoning: "",
|
||||
},
|
||||
|
||||
{
|
||||
source: "Reversals in Psychology",
|
||||
|
||||
target: "A Model of Patient Spending and Movement Building",
|
||||
|
||||
distance: 10,
|
||||
|
||||
reasoning:
|
||||
"RiP: Cleaning up 10,000 people's brains a bit.\nMPS: far more important topic",
|
||||
},
|
||||
|
||||
{
|
||||
source: "Database of orgs relevant to longtermist/x-risk work",
|
||||
|
||||
target:
|
||||
"What are some low-information priors that you find practically useful for thinking about the world?",
|
||||
|
||||
distance: "5",
|
||||
|
||||
reasoning:
|
||||
"\nDatabase just saves a bit of time, maybe 100 counterfactual applications",
|
||||
},
|
||||
|
||||
{
|
||||
source: "Reversals in Psychology",
|
||||
|
||||
target: "Database of orgs relevant to longtermist/x-risk work",
|
||||
|
||||
distance: 1,
|
||||
|
||||
reasoning: "",
|
||||
},
|
||||
|
||||
{
|
||||
source: "Database of orgs relevant to longtermist/x-risk work",
|
||||
|
||||
target: "A Model of Patient Spending and Movement Building",
|
||||
|
||||
distance: 10,
|
||||
|
||||
reasoning: "",
|
||||
},
|
||||
|
||||
{
|
||||
source:
|
||||
"What are some low-information priors that you find practically useful for thinking about the world?",
|
||||
|
||||
target: "A Model of Patient Spending and Movement Building",
|
||||
|
||||
distance: 2,
|
||||
|
||||
reasoning: "",
|
||||
},
|
||||
|
||||
{
|
||||
source: "Center for Election Science EA Wiki stub",
|
||||
|
||||
target:
|
||||
"Extinguishing or preventing coal seam fires is a potential cause area",
|
||||
|
||||
distance: 1000,
|
||||
|
||||
reasoning: "",
|
||||
},
|
||||
|
||||
{
|
||||
source: "A comment on setting up a charity",
|
||||
|
||||
target: "Center for Election Science EA Wiki stub",
|
||||
|
||||
distance: 10,
|
||||
|
||||
reasoning: "",
|
||||
},
|
||||
|
||||
{
|
||||
source: "A comment on setting up a charity",
|
||||
|
||||
target: "Reversals in Psychology",
|
||||
|
||||
distance: 200,
|
||||
|
||||
reasoning: "",
|
||||
},
|
||||
|
||||
{
|
||||
source: "Center for Election Science EA Wiki stub",
|
||||
|
||||
target: "Reversals in Psychology",
|
||||
|
||||
distance: 20,
|
||||
|
||||
reasoning: "",
|
||||
},
|
||||
|
||||
{
|
||||
source: "Reversals in Psychology",
|
||||
|
||||
target:
|
||||
"Extinguishing or preventing coal seam fires is a potential cause area",
|
||||
|
||||
distance: "50",
|
||||
|
||||
reasoning: "",
|
||||
},
|
||||
|
||||
{
|
||||
source: "Database of orgs relevant to longtermist/x-risk work",
|
||||
|
||||
target:
|
||||
"Extinguishing or preventing coal seam fires is a potential cause area",
|
||||
|
||||
distance: "50",
|
||||
|
||||
reasoning: "",
|
||||
},
|
||||
|
||||
{
|
||||
source:
|
||||
"What are some low-information priors that you find practically useful for thinking about the world?",
|
||||
|
||||
target:
|
||||
"Extinguishing or preventing coal seam fires is a potential cause area",
|
||||
|
||||
distance: "10",
|
||||
|
||||
reasoning: "",
|
||||
},
|
||||
|
||||
{
|
||||
source: "A Model of Patient Spending and Movement Building",
|
||||
|
||||
target:
|
||||
"Extinguishing or preventing coal seam fires is a potential cause area",
|
||||
|
||||
distance: 1,
|
||||
|
||||
reasoning: "",
|
||||
},
|
||||
|
||||
{
|
||||
source: "A comment on setting up a charity",
|
||||
|
||||
target: "Shallow evaluations of longtermist organizations",
|
||||
|
||||
distance: 1000,
|
||||
|
||||
reasoning: "",
|
||||
},
|
||||
|
||||
{
|
||||
source: "Center for Election Science EA Wiki stub",
|
||||
|
||||
target: "Shallow evaluations of longtermist organizations",
|
||||
|
||||
distance: 100,
|
||||
|
||||
reasoning: "",
|
||||
},
|
||||
|
||||
{
|
||||
source: "Reversals in Psychology",
|
||||
|
||||
target: "Shallow evaluations of longtermist organizations",
|
||||
|
||||
distance: 100,
|
||||
|
||||
reasoning: "",
|
||||
},
|
||||
|
||||
{
|
||||
source: "Database of orgs relevant to longtermist/x-risk work",
|
||||
|
||||
target: "Shallow evaluations of longtermist organizations",
|
||||
|
||||
distance: 100,
|
||||
|
||||
reasoning: "",
|
||||
},
|
||||
];
|
||||
|
||||
async function pushToMongoManually() {
|
||||
let response = await axios.post(
|
||||
"http://metaforecast-twitter-bot.herokuapp.com/utility-function-extractor",
|
||||
{
|
||||
data: data,
|
||||
}
|
||||
);
|
||||
console.log(response);
|
||||
}
|
||||
pushToMongoManually();
|
||||
|
Before Width: | Height: | Size: 15 KiB After Width: | Height: | Size: 15 KiB |
Before Width: | Height: | Size: 1.1 KiB After Width: | Height: | Size: 1.1 KiB |
1702
packages/webpage-refactor/LICENSE.txt
Normal file
|
@ -4,13 +4,37 @@ 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.
|
||||
|
||||
Then, it cleverly aggregates them, by taking 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.
|
||||
![](./public/example-prompt.png)
|
||||
|
||||
Then, it cleverly aggregates them, on the one hand by producing a graphical representation:
|
||||
|
||||
![](./public/example-graph.png)
|
||||
|
||||
and on the other hand doing some fast and clever mean aggregation [^1]:
|
||||
|
||||
![](./public/example-table.png)
|
||||
|
||||
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".
|
||||
|
||||
##
|
||||
**If you want to use the utility function extractor for a project, we are happy to add a page for your project, like `utility-function-extractor.quantifieduncertainty.org/your-project`**.
|
||||
|
||||
## Object structure
|
||||
## Built with
|
||||
|
||||
- [Nextjs](https://nextjs.org/)
|
||||
- [Netlify](https://github.com/netlify/netlify-plugin-nextjs/#readme)
|
||||
- [React](https://reactjs.org/)
|
||||
- [Squiggle](https://www.squiggle-language.com/)
|
||||
- [Utility tools](to do: add links)
|
||||
|
||||
## Usage
|
||||
|
||||
Navigate to [utility-function-extractor.quantifieduncertainty.org/](https://utility-function-extractor.quantifieduncertainty.org/), and start comparing objects.
|
||||
|
||||
You can change the list of objects to be compared by clicking on "advanced options".
|
||||
|
||||
After comparing objects for a while, you will get a table and a graph with results. You can also use the [utility tools](to do: add link) package to process these results, for which you will need the json of comparisons, which can be found in "Advanced options" -> "Load comparisons"
|
||||
|
||||
## Notes
|
||||
|
||||
The core structure is json array of objects. Only the "name" attribute is required. If there is a "url", it is displayed nicely.
|
||||
|
||||
|
@ -29,9 +53,35 @@ The core structure is json array of objects. Only the "name" attribute is requir
|
|||
]
|
||||
```
|
||||
|
||||
## Netlify
|
||||
The core structure for links is as follows:
|
||||
|
||||
https://github.com/netlify/netlify-plugin-nextjs/#readme
|
||||
```
|
||||
[
|
||||
{
|
||||
"source": "Peter Parker",
|
||||
"target": "Spiderman",
|
||||
"squiggleString": "1 to 100",
|
||||
"distance": 26.639800977355474
|
||||
},
|
||||
{
|
||||
"source": "Spiderman",
|
||||
"target": "Doctor Octopus",
|
||||
"squiggleString": "20 to 2000",
|
||||
"distance": 6.76997149080232
|
||||
},
|
||||
|
||||
]
|
||||
```
|
||||
|
||||
A previous version of this webpage had a more complicated structure, but it has since been simplified.
|
||||
|
||||
## Contributions and help
|
||||
|
||||
We welcome PR requests.
|
||||
|
||||
## License
|
||||
|
||||
Distributed under the MIT License. See LICENSE.txt for more information.
|
||||
|
||||
## To do
|
||||
|
||||
|
@ -40,12 +90,16 @@ https://github.com/netlify/netlify-plugin-nextjs/#readme
|
|||
- [x] Push to github
|
||||
- [x] Push to netlify
|
||||
- [x] Don't allow further comparisons after completion
|
||||
- [ ] Paths table
|
||||
- [ ] Add paths table
|
||||
- [ ] warn that the paths table is approximate.
|
||||
- [x] Paths table
|
||||
- [x] Add paths table
|
||||
- [x] warn that the paths table is approximate.
|
||||
- However, I really don't feel like re-adding this after having worked out the distribution rather than the mean aggregation
|
||||
- However, I think it does make it more user to other users.
|
||||
- [ ] Add functionality like names, etc.
|
||||
- I also don't feel like doing this
|
||||
- [ ] Look back at Amazon thing which has been running
|
||||
- [ ] Change README.
|
||||
- [x] Change README.
|
||||
|
||||
## 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.
|
||||
|
|
|
@ -39,10 +39,11 @@ export function AdvancedOptions({
|
|||
];
|
||||
|
||||
return (
|
||||
<div>
|
||||
<div className="">
|
||||
<br />
|
||||
{/* Show advanced options*/}
|
||||
<button
|
||||
key={"advancedOptionsButton-top"}
|
||||
className="text-gray-500 text-sm "
|
||||
onClick={() => changeShowAdvanceOptions(!showAdvancedOptions)}
|
||||
>
|
||||
|
@ -56,7 +57,7 @@ export function AdvancedOptions({
|
|||
<button
|
||||
className={effectButtonStyle}
|
||||
onClick={() => buttonToggles[i]()}
|
||||
id={`advancedOptionsButton-${i}`}
|
||||
key={`advancedOptionsButton-${i}`}
|
||||
>
|
||||
{buttonName}
|
||||
</button>
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
import React, { useState, useEffect } from "react";
|
||||
import { Separator } from "../separator.js";
|
||||
|
||||
// import JSONInput from "react-json-editor-ajrm/index";
|
||||
// import locale from "react-json-editor-ajrm/locale/en";
|
||||
|
@ -75,6 +76,7 @@ export function ComparisonsChanger({
|
|||
onSubmit={handleSubmitInner}
|
||||
className={`inline ${show ? "" : "hidden"}`}
|
||||
>
|
||||
<Separator />
|
||||
<h3 className="text-lg mt-8">Load comparisons</h3>
|
||||
<p>These can be edited, which will override your current comparisons.</p>
|
||||
<br />
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
import React, { useState } from "react";
|
||||
import { Separator } from "../separator.js";
|
||||
|
||||
export function DataSetChanger({ onChangeOfDataset, show, listOfElements }) {
|
||||
/*let [value, setValue] = useState(`[
|
||||
|
@ -54,6 +55,7 @@ export function DataSetChanger({ onChangeOfDataset, show, listOfElements }) {
|
|||
};
|
||||
return (
|
||||
<div className={`${show ? "" : "hidden"} `}>
|
||||
<Separator />
|
||||
<form onSubmit={handleSubmitInner} className="inline mt-0">
|
||||
<h3 className="text-lg mt-8 mb-4">Change dataset</h3>
|
||||
<textarea
|
||||
|
|
|
@ -1,10 +1,12 @@
|
|||
import React, { state } from "react";
|
||||
import { CopyBlock, googlecode } from "react-code-blocks";
|
||||
import { Separator } from "../separator.js";
|
||||
// googlecode
|
||||
|
||||
export function ShowComparisons({ links, show }) {
|
||||
return (
|
||||
<div className={`text-left ${show ? "" : "hidden"}`}>
|
||||
<Separator />
|
||||
<h3 className="text-lg mt-8">Comparisons</h3>
|
||||
<CopyBlock
|
||||
text={JSON.stringify(links, null, 4)}
|
||||
|
|
|
@ -2,11 +2,13 @@ import React, { useEffect, useState, useRef } from "react";
|
|||
import colormap from "colormap";
|
||||
import cytoscape from "cytoscape";
|
||||
import spread from "cytoscape-spread";
|
||||
|
||||
import {
|
||||
resolveToNumIfPossible,
|
||||
getSquiggleSparkline,
|
||||
} from "../../lib/squiggle.js";
|
||||
import { truncateValueForDisplay } from "../../lib/truncateNums.js";
|
||||
import { cutOffLongNames } from "../../lib/stringManipulations.js";
|
||||
|
||||
// import dagre from "cytoscape-dagre";
|
||||
// import cola from "cytoscape-cola";
|
||||
|
@ -38,32 +40,30 @@ const getEdgeLabel = async (squiggleString) => {
|
|||
};
|
||||
|
||||
const getColors = (n) => {
|
||||
let colors = colormap({
|
||||
let colors;
|
||||
if (n >= 9) {
|
||||
colors = colormap({
|
||||
colormap: "viridis",
|
||||
nshades: n,
|
||||
format: "hex",
|
||||
alpha: 1,
|
||||
});
|
||||
return colors;
|
||||
};
|
||||
|
||||
const cutOffLongNames = (string) => {
|
||||
let maxLength = 40;
|
||||
let result;
|
||||
if (string.length < maxLength) {
|
||||
result = string;
|
||||
} else {
|
||||
result = string.slice(0, maxLength - 4);
|
||||
result = result + "...";
|
||||
colors = colormap({
|
||||
colormap: "greys", // hot,
|
||||
nshades: n,
|
||||
format: "hex",
|
||||
alpha: 1,
|
||||
});
|
||||
}
|
||||
return result;
|
||||
return colors;
|
||||
};
|
||||
|
||||
export function Graph({
|
||||
listOfElements,
|
||||
links,
|
||||
isListOrdered,
|
||||
mergeSortOrder,
|
||||
listAfterMergeSort,
|
||||
}) {
|
||||
const containerRef = useRef("hello-world");
|
||||
const [visibility, setVisibility] = useState(""); /// useState("invisible");
|
||||
|
@ -72,14 +72,14 @@ export function Graph({
|
|||
listOfElements,
|
||||
links,
|
||||
isListOrdered,
|
||||
mergeSortOrder,
|
||||
listAfterMergeSort,
|
||||
}) => {
|
||||
//setVisibility("invisible");
|
||||
let layoutName = "circle"; //
|
||||
|
||||
// cytoscape.use(circle); // spread, circle,
|
||||
let listOfElementsForGraph = isListOrdered
|
||||
? mergeSortOrder
|
||||
? listAfterMergeSort
|
||||
: listOfElements;
|
||||
|
||||
let colors = new Array(listOfElements.length);
|
||||
|
@ -92,9 +92,10 @@ export function Graph({
|
|||
data: {
|
||||
id: cutOffLongNames(element.name),
|
||||
color: colors[i] || "darkgreen",
|
||||
labelColor:
|
||||
isListOrdered && i >= listOfElementsForGraph.length - 2
|
||||
labelColor: isListOrdered
|
||||
? i >= listOfElementsForGraph.length - 2
|
||||
? "black"
|
||||
: "white"
|
||||
: "white",
|
||||
},
|
||||
};
|
||||
|
@ -198,19 +199,35 @@ export function Graph({
|
|||
//setTimeout(() => setVisibility(""), 700);
|
||||
};
|
||||
useEffect(async () => {
|
||||
await callEffect({ listOfElements, links, isListOrdered, mergeSortOrder });
|
||||
await callEffect({
|
||||
listOfElements,
|
||||
links,
|
||||
isListOrdered,
|
||||
listAfterMergeSort,
|
||||
});
|
||||
// console.log(JSON.stringify(config, null, 10));
|
||||
}, [listOfElements, links, isListOrdered]);
|
||||
}, [listOfElements, links, isListOrdered, listAfterMergeSort]);
|
||||
|
||||
return (
|
||||
<div>
|
||||
<div className={visibility}>
|
||||
<div ref={containerRef} style={{ height: "900px", width: "1000px" }} />
|
||||
<div className="">
|
||||
<div className={visibility + "grid grid-cols-1 place-items-center "}>
|
||||
<div
|
||||
ref={containerRef}
|
||||
style={{
|
||||
height: "900px", // isListOrdered ? "900px" : "500px",
|
||||
width: "900px", // isListOrdered ? "900px" : "500px",
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
<button
|
||||
className={effectButtonStyle}
|
||||
onClick={() =>
|
||||
callEffect({ listOfElements, links, isListOrdered, mergeSortOrder })
|
||||
callEffect({
|
||||
listOfElements,
|
||||
links,
|
||||
isListOrdered,
|
||||
listAfterMergeSort,
|
||||
})
|
||||
}
|
||||
>
|
||||
{"Redraw graph"}
|
||||
|
|
|
@ -1,89 +0,0 @@
|
|||
import { SimpleReactCytoscape } from "simple-react-cytoscape";
|
||||
import { Core } from "cytoscape";
|
||||
import { useCallback, useState } from "react";
|
||||
|
||||
/*
|
||||
const elements = [
|
||||
// list of graph elements to start with
|
||||
{
|
||||
// node a
|
||||
data: { id: "a" },
|
||||
},
|
||||
{
|
||||
// node b
|
||||
data: { id: "b" },
|
||||
},
|
||||
{
|
||||
// node c
|
||||
data: { id: "c" },
|
||||
},
|
||||
{
|
||||
// edge ab
|
||||
data: { id: "ab", source: "a", target: "b" },
|
||||
},
|
||||
{
|
||||
// edge ab
|
||||
data: { id: "ac", source: "a", target: "c" },
|
||||
},
|
||||
];
|
||||
*/
|
||||
|
||||
const style = [
|
||||
// the stylesheet for the graph
|
||||
{
|
||||
selector: "node",
|
||||
style: {
|
||||
"background-color": "#666",
|
||||
label: "data(id)",
|
||||
},
|
||||
},
|
||||
|
||||
{
|
||||
selector: "edge",
|
||||
style: {
|
||||
width: 3,
|
||||
"line-color": "#ccc",
|
||||
"target-arrow-color": "#ccc",
|
||||
"target-arrow-shape": "triangle",
|
||||
"curve-style": "bezier",
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
export function Graph({ listOfElements, links }) {
|
||||
const [myCy, setMyCy] = useState();
|
||||
|
||||
const cyCallback = useCallback(
|
||||
(cy) => {
|
||||
setMyCy(cy);
|
||||
},
|
||||
[listOfElements, links]
|
||||
);
|
||||
|
||||
let nodeElements = listOfElements.map((element) => {
|
||||
return { data: { id: element.name } };
|
||||
});
|
||||
let linkElements = links.map((link, i) => {
|
||||
return {
|
||||
data: {
|
||||
id: `link-i`,
|
||||
source: link.source,
|
||||
target: link.target,
|
||||
label: link.distance,
|
||||
},
|
||||
};
|
||||
});
|
||||
let elements = [...nodeElements, ...linkElements];
|
||||
let options = {
|
||||
elements,
|
||||
style,
|
||||
};
|
||||
return (
|
||||
<div className="App">
|
||||
<p>"a"</p>
|
||||
<SimpleReactCytoscape options={options} cyCallback={cyCallback} />
|
||||
<h3>JSON representation</h3>
|
||||
{/*<p>{myCy && JSON.stringify(myCy.json())}</p>*/}
|
||||
</div>
|
||||
);
|
||||
}
|
|
@ -1,30 +0,0 @@
|
|||
import React, { useEffect, useMemo, useState } from "react";
|
||||
import cytoscape from "cytoscape";
|
||||
|
||||
export function Graph({ listOfElements, links, options, getCy }) {
|
||||
const [cy, setCy] = useState();
|
||||
const id = useRef("cytoscape-id");
|
||||
|
||||
useEffect(() => {
|
||||
if (!cy) {
|
||||
let container = null;
|
||||
try {
|
||||
container = document.getElementById(id) || undefined;
|
||||
} catch (e) {
|
||||
// Might be running Headless (the unit test are headless)
|
||||
container = undefined;
|
||||
}
|
||||
const newCy = cytoscape({
|
||||
...options,
|
||||
container,
|
||||
});
|
||||
setCy(newCy);
|
||||
// If a callback was supplied we can now return the value
|
||||
if (getCy) {
|
||||
getCy(newCy);
|
||||
}
|
||||
}
|
||||
}, [cy, getCy, id, options]);
|
||||
|
||||
return <div id={id} className="simple-react-cytoscape" />;
|
||||
}
|
|
@ -5,6 +5,8 @@ import { Title } from "./title.js";
|
|||
import { ProgressIndicator } from "./progressIndicator.js";
|
||||
import { DisplayElementForComparison } from "./displayElementForComparison.js";
|
||||
import { ComparisonActuator } from "./comparisonActuator.js";
|
||||
import { Separator } from "./separator.js";
|
||||
import { ResultsTable } from "./resultsTable.js";
|
||||
|
||||
import { AdvancedOptions } from "./advancedOptions/advancedOptions.js";
|
||||
import { Graph } from "./graph/graph.js";
|
||||
|
@ -26,7 +28,7 @@ export function Homepage({ listOfElementsInit }) {
|
|||
|
||||
// is list ordered?
|
||||
const [isListOrdered, setIsListOrdered] = useState(false);
|
||||
const [mergeSortOrder, setMergeSortOrder] = useState([]);
|
||||
const [listAfterMergeSort, setListAfterMergeSort] = useState([]);
|
||||
|
||||
// list of comparisons
|
||||
const [links, setLinks] = useState([]);
|
||||
|
@ -48,6 +50,7 @@ export function Homepage({ listOfElementsInit }) {
|
|||
setPairCurrentlyBeingCompared([newListOfElements[0], newListOfElements[1]]);
|
||||
setNumStepsNow(0);
|
||||
setIsListOrdered(false);
|
||||
setListAfterMergeSort([]);
|
||||
};
|
||||
|
||||
// process next step
|
||||
|
@ -62,9 +65,9 @@ export function Homepage({ listOfElementsInit }) {
|
|||
setIsListOrdered(false);
|
||||
setPairCurrentlyBeingCompared(newPairToCompare);
|
||||
} else {
|
||||
setIsListOrdered(true);
|
||||
setMergeSortOrder(mergeSortOutput.orderedList);
|
||||
setListAfterMergeSort(mergeSortOutput.orderedList);
|
||||
pushToMongo({ mergeSortOutput, links });
|
||||
setIsListOrdered(true); // good if it's at the end.
|
||||
// alert(JSON.stringify(mergeSortOutput, null, 4));
|
||||
// chooseNextPairToCompareRandomly({ listOfElements });
|
||||
// return 1;
|
||||
|
@ -118,6 +121,7 @@ export function Homepage({ listOfElementsInit }) {
|
|||
numStepsNow={numStepsNow}
|
||||
numElements={listOfElements.length}
|
||||
/>
|
||||
|
||||
{/* Comparisons section */}
|
||||
<div className={"" /*isListOrdered ? "hidden" : ""*/}>
|
||||
<div className="flex justify-evenly mt-10">
|
||||
|
@ -137,18 +141,25 @@ export function Homepage({ listOfElementsInit }) {
|
|||
></DisplayElementForComparison>
|
||||
</div>
|
||||
</div>
|
||||
{/* <Graph />
|
||||
|
||||
{/* <Results table /> */}
|
||||
<ResultsTable
|
||||
isListOrdered={isListOrdered}
|
||||
listAfterMergeSort={listAfterMergeSort}
|
||||
links={links}
|
||||
/>
|
||||
|
||||
*/}
|
||||
{/* <Graph /> */}
|
||||
<Separator />
|
||||
<Graph
|
||||
listOfElements={listOfElements}
|
||||
links={links}
|
||||
isListOrdered={isListOrdered}
|
||||
mergeSortOrder={mergeSortOrder}
|
||||
listAfterMergeSort={listAfterMergeSort}
|
||||
/>
|
||||
|
||||
{/* Advanced options section */}
|
||||
<div>
|
||||
<Separator />
|
||||
<AdvancedOptions
|
||||
links={links}
|
||||
setLinks={setLinks}
|
||||
|
@ -157,6 +168,5 @@ export function Homepage({ listOfElementsInit }) {
|
|||
onChangeOfDataset={onChangeOfDataset}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
|
166
packages/webpage-refactor/components/resultsTable.js
Normal file
|
@ -0,0 +1,166 @@
|
|||
import React, { useState, useEffect } from "react";
|
||||
import { findDistances, aggregatePaths } from "utility-tools";
|
||||
|
||||
import { Separator } from "./separator.js";
|
||||
|
||||
import { truncateValueForDisplay } from "../lib/truncateNums.js";
|
||||
import { cutOffLongNames } from "../lib/stringManipulations.js";
|
||||
|
||||
async function fullResultsTable({ listAfterMergeSort, links }) {
|
||||
console.log("listAfterMergeSort", listAfterMergeSort);
|
||||
console.log(links);
|
||||
let pathsArray = await findDistances({
|
||||
orderedList: listAfterMergeSort,
|
||||
links: links,
|
||||
});
|
||||
// console.log(pathsArray);
|
||||
let aggregatedPaths = await aggregatePaths({
|
||||
pathsArray: pathsArray,
|
||||
orderedList: listAfterMergeSort,
|
||||
aggregationType: "mean",
|
||||
// VERBOSE: false,
|
||||
});
|
||||
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) {
|
||||
let newArray;
|
||||
let formatForDisplay;
|
||||
if (array.length > 10) {
|
||||
newArray = array.slice(0, 9);
|
||||
formatForDisplay = newArray.map((d) => truncateValueForDisplay(d));
|
||||
formatForDisplay[9] = "...";
|
||||
} else {
|
||||
newArray = array;
|
||||
formatForDisplay = newArray.map((d) => truncateValueForDisplay(d));
|
||||
}
|
||||
let result = JSON.stringify(formatForDisplay, null, 2).replaceAll(`"`, "");
|
||||
return result;
|
||||
}
|
||||
|
||||
function getRow(row, i) {
|
||||
return (
|
||||
<tr
|
||||
className="border-b dark:bg-gray-800 dark:border-gray-700 odd:bg-white even:bg-gray-50 odd:dark:bg-gray-800 even:dark:bg-gray-700"
|
||||
key={`row-display-${i}`}
|
||||
>
|
||||
<td className="px-6 py-4 pt-7">{i}</td>
|
||||
<td className="px-6 py-4">{cutOffLongNames(row.name)}</td>
|
||||
<td className="text-center px-6 py-4">
|
||||
{abridgeArrayAndDisplay(row.arrayMeans)}
|
||||
</td>
|
||||
<td className="text-center px-6 py-4">
|
||||
{truncateValueForDisplay(row.aggregatedMeans)}
|
||||
</td>
|
||||
<td className="text-center px-6 py-4">
|
||||
{truncateValueForDisplay(getCoefficientOfVariation(row.arrayMeans))}
|
||||
</td>
|
||||
</tr>
|
||||
);
|
||||
}
|
||||
|
||||
function reactTableContents(tableContents) {
|
||||
// alert(JSON.stringify(tableContents));
|
||||
// return "Hello";
|
||||
return tableContents.map((row, i) => getRow(row, i));
|
||||
}
|
||||
|
||||
export function ResultsTable({ isListOrdered, listAfterMergeSort, links }) {
|
||||
const [isTableComputed, setIsTableComputed] = useState(false);
|
||||
const [tableContents, setTableContents] = useState([]);
|
||||
|
||||
useEffect(async () => {
|
||||
// console.log(JSON.stringify(config, null, 10));
|
||||
if (isListOrdered && listAfterMergeSort.length > 0) {
|
||||
// both necessary because there is a small moment when list is ordered
|
||||
// but listAfterMergeSort wasn't ready yet
|
||||
let tableContentsResult = await fullResultsTable({
|
||||
listAfterMergeSort,
|
||||
links,
|
||||
});
|
||||
console.log(tableContentsResult);
|
||||
// alert(JSON.stringify(tableContentsResult));
|
||||
setTableContents(tableContentsResult);
|
||||
setIsTableComputed(true);
|
||||
}
|
||||
}, [isListOrdered, listAfterMergeSort, links]);
|
||||
|
||||
return (
|
||||
<div>
|
||||
{!(isListOrdered && isTableComputed) ? (
|
||||
""
|
||||
) : (
|
||||
<div>
|
||||
<Separator />
|
||||
<div className="relative overflow-x-auto shadow-md sm:rounded-lg mt-10">
|
||||
<table className="w-full text-sm text-left text-gray-800 dark:text-gray-400">
|
||||
<thead className=" text-xs text-gray-700 bg-gray-50 dark:bg-gray-700 dark:text-gray-400">
|
||||
<tr>
|
||||
<th scope="col" className="px-6 py-3">
|
||||
Position
|
||||
</th>
|
||||
|
||||
<th scope="col" className="px-6 py-3">
|
||||
Element
|
||||
</th>
|
||||
|
||||
<th scope="col" className="text-center px-6 py-3">
|
||||
Possible relative values
|
||||
</th>
|
||||
|
||||
<th scope="col" className="text-center px-6 py-3">
|
||||
Aggregated Means*
|
||||
</th>
|
||||
<th scope="col" className="text-center px-6 py-3">
|
||||
Coefficient of variation
|
||||
</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>{reactTableContents(tableContents)}</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
<div className="grid w-full place-items-center text-center ">
|
||||
<p className="mt-8 max-w-5xl">
|
||||
*This is the geometric mean if all elements are either all positive or
|
||||
all negative, and the arithmetic mean otherwise. For a principled
|
||||
aggregation which is able to produce meaningfull 90% confidence
|
||||
intervals, see the utility-tools package in npm or github
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
9
packages/webpage-refactor/components/separator.js
Normal file
|
@ -0,0 +1,9 @@
|
|||
import React from "react";
|
||||
|
||||
export function Separator() {
|
||||
return (
|
||||
<div class="py-4">
|
||||
<div class="w-full border-t border-4 border-gray-300 mt-10"></div>
|
||||
</div>
|
||||
);
|
||||
}
|
11
packages/webpage-refactor/lib/stringManipulations.js
Normal file
|
@ -0,0 +1,11 @@
|
|||
export const cutOffLongNames = (string) => {
|
||||
let maxLength = 40;
|
||||
let result;
|
||||
if (string.length < maxLength) {
|
||||
result = string;
|
||||
} else {
|
||||
result = string.slice(0, maxLength - 4);
|
||||
result = result + "...";
|
||||
}
|
||||
return result;
|
||||
};
|
|
@ -27,7 +27,7 @@
|
|||
"react-markdown": "^6.0.2",
|
||||
"remark-gfm": "^1.0.0",
|
||||
"simple-react-cytoscape": "^1.0.4",
|
||||
"utility-tools": "^0.2.2"
|
||||
"utility-tools": "^0.2.5"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@netlify/plugin-nextjs": "^4.2.1",
|
||||
|
|
BIN
packages/webpage-refactor/public/example-graph.png
Normal file
After Width: | Height: | Size: 182 KiB |
BIN
packages/webpage-refactor/public/example-graph2.png
Normal file
After Width: | Height: | Size: 243 KiB |
BIN
packages/webpage-refactor/public/example-prompt.png
Normal file
After Width: | Height: | Size: 58 KiB |
BIN
packages/webpage-refactor/public/example-table.png
Normal file
After Width: | Height: | Size: 145 KiB |
|
@ -4048,6 +4048,14 @@ utility-tools@^0.2.2:
|
|||
dependencies:
|
||||
"@quri/squiggle-lang" "^0.2.11"
|
||||
|
||||
utility-tools@^0.2.5:
|
||||
version "0.2.5"
|
||||
resolved "https://registry.yarnpkg.com/utility-tools/-/utility-tools-0.2.5.tgz#73ee06c88d74fc5a7bf87882caf93abed28600c2"
|
||||
integrity sha512-sXT3RiOdjgO0DeYr5G3CPYrrXR2+8+d+GBGN6VELYXSFWfzBNUN1DA/alyYyuDaXRI+iBQequpuPy9rsHvsXGA==
|
||||
dependencies:
|
||||
"@quri/squiggle-lang" "^0.2.11"
|
||||
utility-tools "^0.2.2"
|
||||
|
||||
vfile-message@^2.0.0:
|
||||
version "2.0.4"
|
||||
resolved "https://registry.yarnpkg.com/vfile-message/-/vfile-message-2.0.4.tgz#5b43b88171d409eae58477d13f23dd41d52c371a"
|
||||
|
|
|
@ -1,33 +0,0 @@
|
|||
## About
|
||||
|
||||
This repository creates a react webpage that allows to extract a utility function from possibly inconsistent binary comparisons. 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. Then, it cleverly aggregates them, by taking 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!
|
||||
|
||||
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".
|
||||
|
||||
## Object structure
|
||||
|
||||
The core structure is json array of objects. Only the "name" attribute is required. If there is a "url", it is displayed nicely.
|
||||
|
||||
```
|
||||
[
|
||||
{
|
||||
"name": "Peter Parker",
|
||||
"someOptionalKey": "...",
|
||||
"anotherMoreOptionalKey": "...",
|
||||
},
|
||||
{
|
||||
"name": "Spiderman",
|
||||
"someOptionalKey": "...",
|
||||
"anotherMoreOptionalKey": "..."
|
||||
}
|
||||
]
|
||||
```
|
||||
|
||||
## Netlify
|
||||
|
||||
https://github.com/netlify/netlify-plugin-nextjs/#readme
|
||||
|
||||
## To do
|
||||
|
||||
- [ ] Extract merge, findPath and aggregatePath functionality into different repos
|
||||
- [ ] Add functionality like names, etc.
|