clean dependencies
This commit is contained in:
parent
470dd45321
commit
ef1a49e32c
33506
package-lock.json
generated
Normal file
33506
package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load Diff
@ -7,6 +7,7 @@
|
||||
"@testing-library/react": "^11.1.0",
|
||||
"@testing-library/user-event": "^12.1.10",
|
||||
"react": "^17.0.2",
|
||||
"react-cytoscapejs": "^1.2.1",
|
||||
"react-dom": "^17.0.2",
|
||||
"react-scripts": "4.0.3",
|
||||
"web-vitals": "^1.0.1"
|
||||
|
||||
38
src/App.css
38
src/App.css
@ -1,38 +0,0 @@
|
||||
.App {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.App-logo {
|
||||
height: 40vmin;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
@media (prefers-reduced-motion: no-preference) {
|
||||
.App-logo {
|
||||
animation: App-logo-spin infinite 20s linear;
|
||||
}
|
||||
}
|
||||
|
||||
.App-header {
|
||||
background-color: #282c34;
|
||||
min-height: 100vh;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-size: calc(10px + 2vmin);
|
||||
color: white;
|
||||
}
|
||||
|
||||
.App-link {
|
||||
color: #61dafb;
|
||||
}
|
||||
|
||||
@keyframes App-logo-spin {
|
||||
from {
|
||||
transform: rotate(0deg);
|
||||
}
|
||||
to {
|
||||
transform: rotate(360deg);
|
||||
}
|
||||
}
|
||||
24
src/App.js
24
src/App.js
@ -1,23 +1,15 @@
|
||||
import logo from './logo.svg';
|
||||
import './App.css';
|
||||
//import logo from './logo.svg';
|
||||
import './css/App.css';
|
||||
import React from 'react';
|
||||
import Simulation from './Simulation/Simulation.jsx';
|
||||
import HeaderData from './Header/Headerdata';
|
||||
|
||||
|
||||
function App() {
|
||||
return (
|
||||
<div className="App">
|
||||
<header className="App-header">
|
||||
<img src={logo} className="App-logo" alt="logo" />
|
||||
<p>
|
||||
Edit <code>src/App.js</code> and save to reload.
|
||||
</p>
|
||||
<a
|
||||
className="App-link"
|
||||
href="https://reactjs.org"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
>
|
||||
Learn React
|
||||
</a>
|
||||
</header>
|
||||
<HeaderData/>
|
||||
<Simulation/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
120
src/Graph.jsx
Normal file
120
src/Graph.jsx
Normal file
@ -0,0 +1,120 @@
|
||||
import { ForceGraph2D } from "react-force-graph";
|
||||
import React from 'react';
|
||||
import './css/Graph.css'
|
||||
|
||||
class DynamicGraph extends React.Component{
|
||||
constructor(props){
|
||||
super(props);
|
||||
this.fgRef = React.createRef();
|
||||
this.ran = false;
|
||||
}
|
||||
|
||||
|
||||
componentDidMount(){
|
||||
this.timerID = setInterval(()=>{
|
||||
//this.addNode();
|
||||
//this.fgRef.current.zoomToFit(400);
|
||||
//this.fgRef.current.resumeAnimation();
|
||||
//this.fgRef.current.pauseAnimation();
|
||||
//this.fgRef.current.cooldownTicks(10);
|
||||
}, 10000);
|
||||
}
|
||||
|
||||
componentWillUnmount(){
|
||||
clearInterval(this.timerID);
|
||||
}
|
||||
|
||||
addNode(){
|
||||
if(this.state.nodes.length < 10){
|
||||
const id = this.state.nodes.length;
|
||||
const newNodes = this.state.nodes;
|
||||
const newLinks = this.state.links;
|
||||
newNodes.push({id:id});
|
||||
newLinks.push({source: id, target: Math.round(Math.random() * (id-1))});
|
||||
if(id > 0){
|
||||
newNodes[Math.round(Math.random() * (id-1))].color = "red";
|
||||
}
|
||||
//newNodes[0].color = "red";
|
||||
this.setState((nodes, links)=>({
|
||||
nodes: newNodes,
|
||||
links: newLinks
|
||||
}));
|
||||
}else{
|
||||
clearInterval(this.timerID);
|
||||
}
|
||||
}
|
||||
|
||||
setCooldownTime(){
|
||||
if(!this.ran){
|
||||
this.ran = true;
|
||||
return 5000;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
onEngineStop(){
|
||||
console.log(this.fgRef.current)
|
||||
if(this.fgRef.current != null){
|
||||
this.fgRef.current.zoomToFit(4);
|
||||
}
|
||||
}
|
||||
|
||||
render(){
|
||||
const data = {nodes: this.props.nodes, links: this.props.links};
|
||||
return <ForceGraph2D
|
||||
enableNodeDrag={false}
|
||||
graphData={data}
|
||||
ref={this.fgRef}
|
||||
cooldownTime={this.setCooldownTime()}
|
||||
onEngineStop={this.onEngineStop()}
|
||||
backgroundColor="black"
|
||||
/>;
|
||||
}
|
||||
}
|
||||
|
||||
//const { useState, useEffect, useCallback } = React;
|
||||
//
|
||||
//const DynamicGraph = () => {
|
||||
//const [data, setData] = useState({ nodes: [{ id: 0 }], links: [] });
|
||||
//
|
||||
//useEffect(() => {
|
||||
//console.log(state);
|
||||
////if(this.state.nodes.length > 10){
|
||||
////return;
|
||||
////}
|
||||
//setInterval(() => {
|
||||
//// Add a new connected node every second
|
||||
//setData(({ nodes, links }) => {
|
||||
//const id = nodes.length;
|
||||
//nodes[0].color = "red";
|
||||
//console.log(nodes[0].color);
|
||||
//return {
|
||||
//nodes: [...nodes, { id }],
|
||||
//links: [...links, { source: id, target: Math.round(Math.random() * (id-1)) }]
|
||||
//};
|
||||
//});
|
||||
//}, 1000);
|
||||
//}, []);
|
||||
//
|
||||
//const handleClick = useCallback(node => {
|
||||
//const { nodes, links } = data;
|
||||
//
|
||||
//// Remove node on click
|
||||
//const newLinks = links.filter(l => l.source !== node && l.target !== node); // Remove links attached to node
|
||||
//const newNodes = nodes.slice();
|
||||
//newNodes.splice(node.id, 1); // Remove node
|
||||
//newNodes.forEach((n, idx) => { n.id = idx; }); // Reset node ids to array index
|
||||
//
|
||||
//setData({ nodes: newNodes, links: newLinks });
|
||||
//}, [data, setData]);
|
||||
//
|
||||
//return <ForceGraph2D
|
||||
//enableNodeDrag={false}
|
||||
//onNodeClick={handleClick}
|
||||
//graphData={data}
|
||||
//ndoeAutoColorBy={d => d.id%12}
|
||||
///>;
|
||||
//};
|
||||
//
|
||||
//
|
||||
export default DynamicGraph;
|
||||
11
src/Header/Headerdata.jsx
Normal file
11
src/Header/Headerdata.jsx
Normal file
@ -0,0 +1,11 @@
|
||||
import React from 'react';
|
||||
|
||||
class HeaderData extends React.Component{
|
||||
render(){
|
||||
return (
|
||||
<h1>Here is the headerdata.</h1>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default HeaderData;
|
||||
57
src/Simulation.jsx
Normal file
57
src/Simulation.jsx
Normal file
@ -0,0 +1,57 @@
|
||||
import React from 'react';
|
||||
import DynamicGraph from './Graph.jsx';
|
||||
import simulate from './SimulationScripts/simulation.js'
|
||||
import edgeListToGraph from './SimulationScripts/graphUtils.js';
|
||||
|
||||
class Simulation extends React.Component{
|
||||
constructor(props){
|
||||
super(props);
|
||||
//this.graph = React.createRef();
|
||||
|
||||
//Example data
|
||||
let rules = [[ "I", "R", 1 ], // spontaneous rule I -> R with rate 1.0
|
||||
[ "R", "S", 0.7 ], // spontaneous rule R -> S with rate 0.7
|
||||
[ [ "I","S","I" ],[ "I","I","I" ], 0.8 ]]; // contact rule I+S -> I+I with rate 0.8
|
||||
let states = ["S", "I", "R"];
|
||||
let initial_distribution = [0.5, 0.5, 0.0]; // gleiche Reihenfolge wie states, musss zu rules passen und normalisiert werden
|
||||
//end of example data
|
||||
|
||||
|
||||
let graph_as_edgelist = [[ 0, 4 ], [ 0, 1 ], [ 1, 5 ], [ 1, 2 ], [ 2, 6 ], [ 2, 3 ], [ 3, 7 ], [ 4, 8 ], [ 4, 5 ], [ 5, 9 ], [ 5, 6 ], [ 6, 10 ], [ 6, 7 ], [ 7, 11 ], [ 8, 12 ], [ 8, 9 ], [ 9, 13 ], [ 9, 10 ], [ 10, 14 ], [ 10, 11 ], [ 11, 15 ], [ 12, 13 ], [ 13, 14 ], [ 14, 15 ]];
|
||||
let horizon = 20.0;
|
||||
let simulationData = simulate(rules, states, initial_distribution, graph_as_edgelist, horizon);
|
||||
|
||||
|
||||
let graphData = edgeListToGraph(graph_as_edgelist);
|
||||
this.state = {rules: rules, states:states, initial_distribution: initial_distribution, simulationData: simulationData, graphData: graphData};
|
||||
//this.graph.current.test("alksdj");
|
||||
}
|
||||
|
||||
componentDidMount(){
|
||||
//this.id = setInterval(() => {
|
||||
//this.setState((prev)=>{
|
||||
//prev.graphData.nodes[Math.round(Math.random() * (prev.graphData.nodes.length - 1))].color = "red";
|
||||
//return prev;
|
||||
//})
|
||||
//},1000)
|
||||
}
|
||||
|
||||
componentWillUnmount(){
|
||||
clearTimeout(this.id);
|
||||
}
|
||||
|
||||
test(e){
|
||||
console.log(e.target.value);
|
||||
}
|
||||
|
||||
render(){
|
||||
return (
|
||||
<div id="Simulation">
|
||||
<DynamicGraph
|
||||
nodes={this.state.graphData.nodes}
|
||||
links={this.state.graphData.links}/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
export default Simulation;
|
||||
62
src/Simulation/ContactSelector.jsx
Normal file
62
src/Simulation/ContactSelector.jsx
Normal file
@ -0,0 +1,62 @@
|
||||
import React from 'react';
|
||||
|
||||
import Dropdown from './Dropdown.jsx'
|
||||
import KarateClass from './exampleNetworks/Karate.jsx';
|
||||
import Grid from './exampleNetworks/Grid.jsx';
|
||||
import Custom from './exampleNetworks/Custom.jsx';
|
||||
|
||||
class ContactSelector extends React.Component{
|
||||
constructor(props){
|
||||
super(props);
|
||||
//predefined networks go here
|
||||
this.Karate = new KarateClass();
|
||||
this.Grid = new Grid();
|
||||
|
||||
//obligatory custom network
|
||||
this.custom = new Custom();
|
||||
|
||||
this.state = {predefinedNetworks: [this.Karate, this.Grid, this.custom]
|
||||
, currentValue: "Karate"};
|
||||
this.dropdownChanged = this.dropdownChanged.bind(this);
|
||||
this.updateGraphObject = this.updateGraphObject.bind(this);
|
||||
}
|
||||
|
||||
dropdownChanged(e){
|
||||
this.setState({currentValue: e.name});
|
||||
this.props.handleChange(e);
|
||||
}
|
||||
|
||||
//when the object changed we need to update it again
|
||||
updateGraphObject(e) {
|
||||
this.props.handleChange(e);
|
||||
}
|
||||
|
||||
renderCustomHtml(){
|
||||
switch(this.state.currentValue){
|
||||
case "Karate":
|
||||
return <KarateClass updateGraphObject={this.updateGraphObject}/>
|
||||
case "2D-Grid":
|
||||
return <Grid updateGraphObject={this.updateGraphObject}/>
|
||||
case "Custom":
|
||||
return <Custom updateGraphObject={this.updateGraphObject}/>
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
render(){
|
||||
return(
|
||||
<div id='ContactSelector'>
|
||||
<h2 id='ContactSelectorName' className='selectorName'>
|
||||
Contact Network</h2>
|
||||
<Dropdown name='Select Network'
|
||||
description='Select a network'
|
||||
options={this.state.predefinedNetworks}
|
||||
handleChange={this.dropdownChanged}/>
|
||||
{this.renderCustomHtml()}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default ContactSelector;
|
||||
34
src/Simulation/Dropdown.jsx
Normal file
34
src/Simulation/Dropdown.jsx
Normal file
@ -0,0 +1,34 @@
|
||||
import React from 'react';
|
||||
|
||||
class Dropdown extends React.Component{
|
||||
constructor(props){
|
||||
super(props);
|
||||
this.changeVal = this.changeVal.bind(this);
|
||||
}
|
||||
|
||||
createOption(data, index){
|
||||
return (
|
||||
<option value={data.name} key={index}>{data.name}</option>
|
||||
);
|
||||
}
|
||||
|
||||
changeVal(e){
|
||||
let newVal = this.props.options[e.target.selectedIndex];
|
||||
this.props.handleChange(newVal);
|
||||
}
|
||||
|
||||
render(){
|
||||
return(
|
||||
<div className='DropdownDiv'>
|
||||
<label htmlFor='Dropdown' className='dropdownDescription'>{this.props.description}</label>
|
||||
<select id='Dropdown'
|
||||
name={this.props.name}
|
||||
onChange={this.changeVal}>
|
||||
{this.props.options.map(this.createOption)}
|
||||
</select>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
}
|
||||
|
||||
export default Dropdown;
|
||||
147
src/Simulation/GraphCytoscape.jsx
Normal file
147
src/Simulation/GraphCytoscape.jsx
Normal file
@ -0,0 +1,147 @@
|
||||
import React from 'react';
|
||||
import CytoscapeComponent from 'react-cytoscapejs';
|
||||
import '../css/Graph.css'
|
||||
|
||||
class GraphCytoscape extends React.Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.cy = React.createRef();
|
||||
this.iteration = 0;
|
||||
this.state ={animationDuration: 4};
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
//initial layout
|
||||
this.layoutGraph();
|
||||
this.iteration = 0;
|
||||
this.visualizeOneStep();
|
||||
}
|
||||
|
||||
componentDidUpdate(prevProps, prevState) {
|
||||
//only recalculate the layout if graph has changed
|
||||
if (prevProps.graphData !== this.props.graphData) {
|
||||
this.layoutGraph();
|
||||
this.iteration = 0;
|
||||
clearInterval(this.animationId);
|
||||
this.visualizeOneStep();
|
||||
}
|
||||
|
||||
//we could check here if the simulation should start displaying
|
||||
}
|
||||
|
||||
//layouting algorithm
|
||||
layoutGraph() {
|
||||
let options = {
|
||||
name: 'cose',
|
||||
// Called on `layoutready`
|
||||
ready: function(){},
|
||||
// Called on `layoutstop`
|
||||
stop: function(){},
|
||||
// Whether to animate while running the layout
|
||||
// true : Animate continuously as the layout is running
|
||||
// false : Just show the end result
|
||||
// 'end' : Animate with the end result, from the initial positions to the end positions
|
||||
animate: 'end',
|
||||
// Easing of the animation for animate:'end'
|
||||
animationEasing: undefined,
|
||||
// The duration of the animation for animate:'end'
|
||||
animationDuration: undefined,
|
||||
// A function that determines whether the node should be animated
|
||||
// All nodes animated by default on animate enabled
|
||||
// Non-animated nodes are positioned immediately when the layout starts
|
||||
animateFilter: function ( node, i ){ return true; },
|
||||
// The layout animates only after this many milliseconds for animate:true
|
||||
// (prevents flashing on fast runs)
|
||||
animationThreshold: 250,
|
||||
// Number of iterations between consecutive screen positions update
|
||||
refresh: 20,
|
||||
// Whether to fit the network view after when done
|
||||
fit: true,
|
||||
// Padding on fit
|
||||
padding: 30,
|
||||
// Constrain layout bounds; { x1, y1, x2, y2 } or { x1, y1, w, h }
|
||||
boundingBox: undefined,
|
||||
// Excludes the label when calculating node bounding boxes for the layout algorithm
|
||||
nodeDimensionsIncludeLabels: false,
|
||||
// Randomize the initial positions of the nodes (true) or use existing positions (false)
|
||||
randomize: false,
|
||||
// Extra spacing between components in non-compound graphs
|
||||
componentSpacing: 40,
|
||||
// Node repulsion (non overlapping) multiplier
|
||||
nodeRepulsion: function( node ){ return 2048; },
|
||||
// Node repulsion (overlapping) multiplier
|
||||
nodeOverlap: 4,
|
||||
// Ideal edge (non nested) length
|
||||
idealEdgeLength: function( edge ){ return 32; },
|
||||
// Divisor to compute edge forces
|
||||
edgeElasticity: function( edge ){ return 32; },
|
||||
// Nesting factor (multiplier) to compute ideal edge length for nested edges
|
||||
nestingFactor: 1.2,
|
||||
// Gravity force (constant)
|
||||
gravity: 1,
|
||||
// Maximum number of iterations to perform
|
||||
numIter: 1000,
|
||||
// Initial temperature (maximum node displacement)
|
||||
initialTemp: 1000,
|
||||
// Cooling factor (how the temperature is reduced between consecutive iterations
|
||||
coolingFactor: 0.99,
|
||||
// Lower temperature threshold (below this point the layout will end)
|
||||
minTemp: 1.0
|
||||
};
|
||||
|
||||
var layout = this.cy.layout( options );
|
||||
layout.run();
|
||||
}
|
||||
|
||||
changeAnimationDuration = (e) => {
|
||||
this.setState({animationDuration: e.target.value});
|
||||
}
|
||||
|
||||
visualizeSimulation = () => {
|
||||
console.log("new animation started");
|
||||
clearInterval(this.animationId);
|
||||
this.iteration = 0;
|
||||
if (this.state.animationDuration <= 0) {
|
||||
this.setState({animationDuration: 4});
|
||||
}
|
||||
this.visualizeOneStep();
|
||||
var stepTime = this.state.animationDuration * 1000 / this.props.simulationData.length;
|
||||
this.animationId = setInterval(this.visualizeOneStep, stepTime);
|
||||
}
|
||||
|
||||
//this is the method to visualize the simulation
|
||||
visualizeOneStep = () => {
|
||||
//the data of the simulation is stored in: this.props.simulationData
|
||||
var data = this.props.simulationData;
|
||||
if (data == null) {
|
||||
return;
|
||||
}
|
||||
var nodeCount = data[0].length
|
||||
var simulationLength = data.length;
|
||||
if (this.iteration >= simulationLength) {
|
||||
console.log("finished animation");
|
||||
clearInterval(this.animationId);
|
||||
return;
|
||||
}
|
||||
//do one step
|
||||
for (var i = 0; i < nodeCount; i++) {
|
||||
//the current state of the current node
|
||||
let state = data[this.iteration][i];
|
||||
var color = this.props.colors.find(element => element[0] === state)[1];
|
||||
|
||||
this.cy.getElementById(i).style('background-color', color);
|
||||
}
|
||||
|
||||
this.iteration++;
|
||||
}
|
||||
|
||||
render() {
|
||||
return (<div id="graphDiv">
|
||||
<button id="runSimulationButton" onClick={this.visualizeSimulation}>Play Simulation</button>
|
||||
<input type="number" onChange={this.changeAnimationDuration} value={this.state.animationDuration}/>
|
||||
<CytoscapeComponent id="cy" cy={(cy) => { this.cy = cy }} elements={this.props.graphData}/>
|
||||
</div>);
|
||||
}
|
||||
}
|
||||
|
||||
export default GraphCytoscape;
|
||||
19
src/Simulation/HorizonSelector.jsx
Normal file
19
src/Simulation/HorizonSelector.jsx
Normal file
@ -0,0 +1,19 @@
|
||||
import React from 'react';
|
||||
import Slider from './Slider';
|
||||
|
||||
class HorizonSelector extends React.Component{
|
||||
render(){
|
||||
return (
|
||||
<div id='HorizonSelector'>
|
||||
<h2 id='HorizonSelectorName' className='selectorName'>
|
||||
Select the animation horizon</h2>
|
||||
<Slider description='Select Horizon'
|
||||
handleChange={this.props.handleChange}
|
||||
currentValue={this.props.currentValue}
|
||||
min='1' max='200'/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default HorizonSelector;
|
||||
64
src/Simulation/ModelSelector.jsx
Normal file
64
src/Simulation/ModelSelector.jsx
Normal file
@ -0,0 +1,64 @@
|
||||
import React from 'react';
|
||||
|
||||
import Dropdown from './Dropdown.jsx'
|
||||
import SIModel from './exampleModels/SIModel.jsx';
|
||||
import SEIRSModel from './exampleModels/SEIRSModel.jsx';
|
||||
import CoronaModel from './exampleModels/CoronaModel.jsx';
|
||||
import Custom from './exampleModels/Custom.jsx';
|
||||
|
||||
class ModelSelector extends React.Component{
|
||||
constructor(props){
|
||||
super(props);
|
||||
//predefined networks go here
|
||||
this.SIModel = new SIModel();
|
||||
this.SEIRSModel = new SEIRSModel();
|
||||
this.CoronaModel = new CoronaModel();
|
||||
//this.custom = new Custom();
|
||||
|
||||
this.state = {predefinedModels: [this.SIModel, this.SEIRSModel, this.CoronaModel]
|
||||
, currentValue: "SIModel"};
|
||||
this.dropdownChanged = this.dropdownChanged.bind(this);
|
||||
this.updateSelectedModel = this.updateSelectedModel.bind(this);
|
||||
}
|
||||
|
||||
dropdownChanged(e){
|
||||
this.setState({currentValue: e.name});
|
||||
this.props.handleChange(e);
|
||||
}
|
||||
|
||||
//when the object changed we need to update it again
|
||||
updateSelectedModel(e) {
|
||||
this.props.handleChange(e);
|
||||
}
|
||||
|
||||
renderCustomHtml(){
|
||||
switch(this.state.currentValue){
|
||||
case "SIModel":
|
||||
return <SIModel updateSelectedModel={this.updateSelectedModel}/>
|
||||
case "SEIRSModel":
|
||||
return <SEIRSModel updateSelectedModel={this.updateSelectedModel}/>
|
||||
case "CoronaModel":
|
||||
return <CoronaModel updateSelectedModel={this.updateSelectedModel}/>
|
||||
case "Custom":
|
||||
return <Custom updateSelectedModel={this.updateSelectedModel}/>
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
render(){
|
||||
return(
|
||||
<div id='ModelSelector'>
|
||||
<h2 id='ModelSelectorName' className='selectorName'>
|
||||
Contact Model</h2>
|
||||
<Dropdown name='Select Model'
|
||||
description='Select a network'
|
||||
options={this.state.predefinedModels}
|
||||
handleChange={this.dropdownChanged}/>
|
||||
{this.renderCustomHtml()}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default ModelSelector;
|
||||
106
src/Simulation/Simulation.jsx
Normal file
106
src/Simulation/Simulation.jsx
Normal file
@ -0,0 +1,106 @@
|
||||
import React from 'react';
|
||||
import simulate from '../SimulationScripts/simulation.js'
|
||||
import edgeListToGraph from '../SimulationScripts/graphUtils.js';
|
||||
|
||||
import '../css/Simulation.css'
|
||||
|
||||
import HorizonSelector from './HorizonSelector.jsx';
|
||||
import ContactSelector from './ContactSelector.jsx';
|
||||
import KarateClass from './exampleNetworks/Karate.jsx';
|
||||
import ModelSelector from './ModelSelector.jsx';
|
||||
import GraphCytoscape from './GraphCytoscape.jsx';
|
||||
import SIModel from './exampleModels/SIModel.jsx';
|
||||
|
||||
class Simulation extends React.Component{
|
||||
constructor(props){
|
||||
super(props);
|
||||
// bind this to the function as it is calles from elsewhere
|
||||
this.horizonChange = this.horizonChange.bind(this);
|
||||
this.networkChange = this.networkChange.bind(this);
|
||||
this.modelChanged = this.modelChanged.bind(this);
|
||||
this.recalculate = this.recalculate.bind(this);
|
||||
|
||||
//HERE WE SET THE DEFAULT VALUES, THIS MUST BE CONSISTEN!!!!!
|
||||
this.networkObject = new KarateClass();
|
||||
this.modelObject = new SIModel();
|
||||
|
||||
//initialize the state correctly
|
||||
var selectedModel = this.modelObject;
|
||||
var graphData = edgeListToGraph(this.networkObject.getGraph());
|
||||
var rules = selectedModel.getRules();
|
||||
var states = selectedModel.getStates();
|
||||
var initial_distribution = selectedModel.getDistribution();
|
||||
|
||||
this.state = {rules: rules, states: states, initial_distribution: initial_distribution, graphData: graphData, horizon: 20.0, selectedNetwork: this.networkObject, selectedModel: this.modelObject, simulationData: undefined,};
|
||||
}
|
||||
|
||||
componentDidMount(){
|
||||
this.recalculate();
|
||||
}
|
||||
|
||||
horizonChange(e){
|
||||
if(e.target.value > 200){
|
||||
e.target.value = 200
|
||||
}
|
||||
this.setState({
|
||||
horizon: e.target.value
|
||||
});
|
||||
}
|
||||
|
||||
networkChange(newNetwork){
|
||||
this.setState({selectedNetwork: newNetwork});
|
||||
}
|
||||
|
||||
modelChanged(newModel) {
|
||||
this.setState({selectedModel: newModel});
|
||||
}
|
||||
|
||||
recalculate(){
|
||||
var selectedModel = this.state.selectedModel;
|
||||
var graphData = edgeListToGraph(this.state.selectedNetwork.getGraph());
|
||||
var rules = selectedModel.getRules();
|
||||
var states = selectedModel.getStates();
|
||||
var initial_distribution = selectedModel.getDistribution();
|
||||
//update the values based on selected Models/Networks
|
||||
//Simulate with given data
|
||||
var newSimulationData = simulate(rules, states, initial_distribution, this.state.selectedNetwork.getGraph(), this.state.horizon);
|
||||
|
||||
this.setState({graphData: graphData, rules: rules, states: states, initial_distribution: initial_distribution, simulationData: newSimulationData});
|
||||
}
|
||||
|
||||
render(){
|
||||
//return (
|
||||
//<div id="Simulation">
|
||||
//<div id="SimulationSettings">
|
||||
//<HorizonSelector
|
||||
//handleChange={this.horizonChange}
|
||||
//currentValue={this.state.horizon}/>
|
||||
//<ContactSelector handleChange={this.networkChange}/>
|
||||
//<ModelSelector handleChange={this.modelChanged}/>
|
||||
//<button id="recalculate" onClick={this.recalculate}>Recalculate</button>
|
||||
//</div>
|
||||
//<div id="SimulationGraph">
|
||||
//<DynamicGraph
|
||||
//nodes={this.state.graphData.nodes}
|
||||
//links={this.state.graphData.links}/>
|
||||
//</div>
|
||||
//</div>
|
||||
//);
|
||||
return (
|
||||
<div id="Simulation">
|
||||
<div id="SimulationSettings">
|
||||
<HorizonSelector
|
||||
handleChange={this.horizonChange}
|
||||
currentValue={this.state.horizon}/>
|
||||
<ContactSelector handleChange={this.networkChange}/>
|
||||
<ModelSelector handleChange={this.modelChanged}/>
|
||||
<button id="recalculate" onClick={this.recalculate}>Recalculate</button>
|
||||
</div>
|
||||
<div id="SimulationGraph">
|
||||
<GraphCytoscape graphData={this.state.graphData} simulationData={this.state.simulationData} colors={this.state.selectedModel.getColors()}/>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
export default Simulation;
|
||||
21
src/Simulation/Slider.jsx
Normal file
21
src/Simulation/Slider.jsx
Normal file
@ -0,0 +1,21 @@
|
||||
import React from "react";
|
||||
import '../css/Slider.css'
|
||||
|
||||
class Slider extends React.Component{
|
||||
componentDidMount() {
|
||||
|
||||
}
|
||||
render(){
|
||||
return (
|
||||
<div className='SliderDiv'>
|
||||
<label htmlFor='Slider' className='SliderDescription'>{this.props.description}</label>
|
||||
<input className='Slider' type='range' min={this.props.min} max={this.props.max} value={this.props.currentValue}
|
||||
onChange={this.props.handleChange} step={this.props.step}
|
||||
/>
|
||||
<input type="number" htmlFor='Slider' className='SliderValue' value={this.props.currentValue} onChange={this.props.handleChange}/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default Slider;
|
||||
21
src/Simulation/exampleModels/CoronaModel.jsx
Normal file
21
src/Simulation/exampleModels/CoronaModel.jsx
Normal file
@ -0,0 +1,21 @@
|
||||
import Model from "./Model";
|
||||
|
||||
class CoronaModel extends Model {
|
||||
constructor() {
|
||||
super("CoronaModel",
|
||||
[["S", 0.965, "#1F85DE"], ["I1", 0.005, "#de1f50"], ["I2", 0.005, "#b91fde"], ["I3", 0.005, "#1fdea7"], ["R", 0.005, "#8fde1f"], ["V", 0.005, "#de801f"], ["D", 0.005, "#3b0b0b"]],
|
||||
[[["I1", "S"], ["I2", "E"], 0.15],
|
||||
[["I2", "S"], ["I2", "E"], 0.05],
|
||||
[["I3", "S"], ["I3", "E"], 0.05],
|
||||
["E", "I1", 0.2],
|
||||
["I1", "I2", 0.03],
|
||||
["I2", "I3", 0.04],
|
||||
["I3", "D", 0.25],
|
||||
["I1", "R", 0.13],
|
||||
["I2", "R", 0.13],
|
||||
["I3", "R", 0.12]],
|
||||
1, 0.01);
|
||||
}
|
||||
}
|
||||
|
||||
export default CoronaModel;
|
||||
0
src/Simulation/exampleModels/Custom.jsx
Normal file
0
src/Simulation/exampleModels/Custom.jsx
Normal file
124
src/Simulation/exampleModels/Model.jsx
Normal file
124
src/Simulation/exampleModels/Model.jsx
Normal file
@ -0,0 +1,124 @@
|
||||
import React from "react";
|
||||
import Slider from "../Slider";
|
||||
|
||||
class Model extends React.Component {
|
||||
constructor(optionName, states, rules, ruleMax, ruleStep) {
|
||||
super();
|
||||
this.optionName = optionName;
|
||||
this.states = states;
|
||||
this.rules = rules;
|
||||
this.ruleMax = ruleMax;
|
||||
this.ruleStep = ruleStep;
|
||||
}
|
||||
|
||||
//override default get name
|
||||
get name() {
|
||||
return this.optionName;
|
||||
}
|
||||
|
||||
getRules() {
|
||||
return this.rules;
|
||||
}
|
||||
|
||||
|
||||
getStates() {
|
||||
var output = [];
|
||||
this.states.forEach((s) => {
|
||||
output.push(s[0]);
|
||||
});
|
||||
return output;
|
||||
}
|
||||
|
||||
getColors() {
|
||||
var output = [];
|
||||
this.states.forEach((s) => {
|
||||
output.push([s[0], s[2]]);
|
||||
});
|
||||
return output;
|
||||
}
|
||||
|
||||
getDistribution() {
|
||||
var output = [];
|
||||
this.states.forEach((s) => {
|
||||
output.push(s[1]);
|
||||
});
|
||||
return output;
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
//update
|
||||
this.props.updateSelectedModel(this);
|
||||
}
|
||||
|
||||
changeProbability = (spot, e) => {
|
||||
//update state
|
||||
this.setState({});
|
||||
this.rules[spot][2] = Number(e.target.value);
|
||||
//update
|
||||
this.props.updateSelectedModel(this);
|
||||
}
|
||||
|
||||
changeDistribution = (spot, e) => {
|
||||
//update state
|
||||
this.setState({});
|
||||
this.states[spot][1] = Number(e.target.value);
|
||||
//update
|
||||
this.props.updateSelectedModel(this);
|
||||
}
|
||||
|
||||
changeColor = (spot, e) => {
|
||||
this.setState({});
|
||||
this.states[spot][2] = e.target.value;
|
||||
//update
|
||||
this.props.updateSelectedModel(this);
|
||||
}
|
||||
|
||||
//build the description of the slider
|
||||
buildDesc(r) {
|
||||
var output = r[0] + "->" + r[1];
|
||||
return output.replaceAll(",", "+");
|
||||
}
|
||||
|
||||
//build all sliders
|
||||
buildSliders(max, step) {
|
||||
var output = [];
|
||||
var count = 0;
|
||||
this.getRules().forEach((r) => {
|
||||
var tempCount = count;
|
||||
output.push(<Slider key={tempCount} description={this.buildDesc(r)} handleChange={(e) => this.changeProbability(tempCount, e)} min="0" max={max} currentValue={r[2]} step={step}/>)
|
||||
count++;
|
||||
});
|
||||
|
||||
return output;
|
||||
}
|
||||
|
||||
//build the sliders for the initial distribution
|
||||
buildSlidersDistribution() {
|
||||
var output = [];
|
||||
var count = 0;
|
||||
this.states.forEach((s) => {
|
||||
var tempCount = count;
|
||||
output.push(<div key={-tempCount * -tempCount - 100}>
|
||||
<input key={-tempCount - 1} type="color" className="ColorPicker" value={this.states[tempCount][2]} onChange={(e) => this.changeColor(tempCount, e)}/>
|
||||
<Slider key={tempCount} description={s[0]} handleChange={(e) => this.changeDistribution(tempCount, e)} min="0" max="1" currentValue={s[1]} step="0.001"/>
|
||||
</div>
|
||||
);
|
||||
count ++;
|
||||
});
|
||||
|
||||
return output;
|
||||
}
|
||||
|
||||
render() {
|
||||
return (
|
||||
<div className="modelSelector">
|
||||
<h3 id="selectRulesHeader">Change state transition probabilities</h3>
|
||||
{this.buildSliders(this.ruleMax, this.ruleStep)}
|
||||
<h3 id="selectDistributionHeader">Change initial distribution of states</h3>
|
||||
{this.buildSlidersDistribution()}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default Model;
|
||||
15
src/Simulation/exampleModels/SEIRSModel.jsx
Normal file
15
src/Simulation/exampleModels/SEIRSModel.jsx
Normal file
@ -0,0 +1,15 @@
|
||||
import Model from "./Model";
|
||||
|
||||
class SEIRSModel extends Model {
|
||||
constructor() {
|
||||
super("SEIRSModel",
|
||||
[["E", 0.03, "#1F85DE"], ["I", 0.03, "#de1f50"], ["R", 0.03, "#b91fde"], ["S", 0.91, "#1fdea7"]],
|
||||
[[["S", "I"], ["I", "E"], 0.5],
|
||||
["E", "I", 0.5],
|
||||
["I", "R", 0.5],
|
||||
["R", "S", 0.5]],
|
||||
20, 0.05);
|
||||
}
|
||||
}
|
||||
|
||||
export default SEIRSModel;
|
||||
12
src/Simulation/exampleModels/SIModel.jsx
Normal file
12
src/Simulation/exampleModels/SIModel.jsx
Normal file
@ -0,0 +1,12 @@
|
||||
import Model from "./Model";
|
||||
|
||||
class SIModel extends Model {
|
||||
constructor() {
|
||||
super("SIModel",
|
||||
[["S", 0.97, "#1F85DE"], ["I", 0.03, "#de1f50"]],
|
||||
[["S", "I", 0.5]],
|
||||
20, 0.05);
|
||||
}
|
||||
}
|
||||
|
||||
export default SIModel;
|
||||
16
src/Simulation/exampleNetworks/Custom.jsx
Normal file
16
src/Simulation/exampleNetworks/Custom.jsx
Normal file
@ -0,0 +1,16 @@
|
||||
import Network from "./Network";
|
||||
import React from "react";
|
||||
|
||||
class Custom extends Network {
|
||||
constructor(){
|
||||
super("Custom",
|
||||
[[1,1], [2,1], [3,1], [4,1]]);
|
||||
}
|
||||
|
||||
render(){
|
||||
return (<h1>Custom</h1>)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export default Custom;
|
||||
47
src/Simulation/exampleNetworks/Grid.jsx
Normal file
47
src/Simulation/exampleNetworks/Grid.jsx
Normal file
@ -0,0 +1,47 @@
|
||||
import Network from "./Network";
|
||||
import Slider from "../Slider";
|
||||
|
||||
class Grid extends Network {
|
||||
constructor(){
|
||||
super("2D-Grid", []);
|
||||
this.state = {dimension: 3};
|
||||
}
|
||||
|
||||
changeDimension = (e) => {
|
||||
let newD = e.target.value;
|
||||
if(newD < 1 || newD > 100){
|
||||
return;
|
||||
}
|
||||
this.setState({dimension: Number(newD)});
|
||||
|
||||
//Update state
|
||||
this.props.updateGraphObject(this);
|
||||
}
|
||||
|
||||
calculateGraph(){
|
||||
//We calculate our edgelist here
|
||||
this.graph = [];
|
||||
for(let i = 0; i < this.state.dimension * this.state.dimension; i++){
|
||||
if((i + 1) % this.state.dimension !== 0) {
|
||||
this.graph.push([i, i + 1]);
|
||||
}
|
||||
if(i < (this.state.dimension * this.state.dimension) - this.state.dimension) {
|
||||
this.graph.push([i, i + this.state.dimension]);
|
||||
}
|
||||
}
|
||||
return this.graph;
|
||||
//return edgeListToGraph(this.graph);
|
||||
}
|
||||
|
||||
getGraph() {
|
||||
return this.calculateGraph();
|
||||
}
|
||||
|
||||
render(){
|
||||
return (<div id='dimensionSlider'> <Slider description='Select a dimension' handleChange={this.changeDimension}
|
||||
min='2' max='12' currentValue={this.state.dimension}/>
|
||||
</div>);
|
||||
}
|
||||
}
|
||||
|
||||
export default Grid;
|
||||
16
src/Simulation/exampleNetworks/Karate.jsx
Normal file
16
src/Simulation/exampleNetworks/Karate.jsx
Normal file
@ -0,0 +1,16 @@
|
||||
import Network from "./Network";
|
||||
|
||||
class KarateClass extends Network {
|
||||
constructor(){
|
||||
//super("Karate",
|
||||
//[[2,1], [3,1], [4,1], [5,1]]);
|
||||
super("Karate",
|
||||
[[0,1], [1,2], [3,4], [4,5], [6,7], [7,8], [0,3], [3,6], [1,4], [4,7], [2,5], [5,8]]);
|
||||
}
|
||||
|
||||
render(){
|
||||
return (<h1>placeholder</h1>);
|
||||
}
|
||||
}
|
||||
|
||||
export default KarateClass;
|
||||
25
src/Simulation/exampleNetworks/Network.jsx
Normal file
25
src/Simulation/exampleNetworks/Network.jsx
Normal file
@ -0,0 +1,25 @@
|
||||
import React from "react";
|
||||
|
||||
class Network extends React.Component{
|
||||
constructor(optionName, graph){
|
||||
super()
|
||||
this.optionName = optionName;
|
||||
this.graph = graph;
|
||||
}
|
||||
|
||||
//override default name getter
|
||||
get name(){
|
||||
return this.optionName;
|
||||
}
|
||||
|
||||
getGraph() {
|
||||
return this.graph;
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
//update state on select
|
||||
this.props.updateGraphObject(this);
|
||||
}
|
||||
}
|
||||
|
||||
export default Network;
|
||||
55
src/SimulationScripts/graphUtils.js
Normal file
55
src/SimulationScripts/graphUtils.js
Normal file
@ -0,0 +1,55 @@
|
||||
function edgelistToDot(name, inp){
|
||||
var output = "strict graph \"" + name + "\" {\n";
|
||||
for(var e in inp){
|
||||
e = inp[e];
|
||||
output += e[0] + " -- " + e[1] + ";\n";
|
||||
}
|
||||
output += "}";
|
||||
return output;
|
||||
}
|
||||
|
||||
function dotToEdgelist(graph){
|
||||
var outList = [];
|
||||
graph = graph.split("\n");
|
||||
//var name = graph[0].split(" ")[2];
|
||||
//name = name.slice(1, name.length - 1);
|
||||
for (var i = 0; i < graph.length - 1; i++){
|
||||
if(i === 0){
|
||||
continue;
|
||||
}
|
||||
var nodes = graph[i].split("--");
|
||||
var node1 = Number(nodes[0]);
|
||||
var node2 = Number(nodes[1].slice(0, nodes[1].length - 1));
|
||||
outList.push([node1, node2]);
|
||||
}
|
||||
return outList;
|
||||
}
|
||||
|
||||
function edgeListToGraph(list){
|
||||
let outNodes = [];
|
||||
let outLinks = [];
|
||||
for (var entry in list){
|
||||
entry = list[entry];
|
||||
outNodes.push(entry[0]);
|
||||
outNodes.push(entry[1]);
|
||||
outLinks.push({data: {source: entry[0], target:entry[1]}});
|
||||
}
|
||||
//sort
|
||||
outNodes = outNodes.sort((a, b) => (a > b));
|
||||
//remove duplicates
|
||||
let finalArray = [];
|
||||
var last = -1;
|
||||
for(var i = 0; i < outNodes.length; i ++){
|
||||
if(last === outNodes[i]){
|
||||
continue;
|
||||
}
|
||||
else{
|
||||
finalArray.push({data: {id: outNodes[i], label: ""}})
|
||||
last = outNodes[i];
|
||||
}
|
||||
}
|
||||
outNodes = finalArray;
|
||||
return [...outNodes, ...outLinks]
|
||||
}
|
||||
|
||||
export default edgeListToGraph;
|
||||
208
src/SimulationScripts/simulation.js
Normal file
208
src/SimulationScripts/simulation.js
Normal file
@ -0,0 +1,208 @@
|
||||
//let states = ["S", "I", "R"];
|
||||
|
||||
//let rules = [[ "I", "R", 1 ], // spontaneous rule I -> R with rate 1.0
|
||||
//[ "R", "S", 0.7 ], // spontaneous rule R -> S with rate 0.7
|
||||
//[ [ "I","S","I" ],[ "I","I","I" ], 0.8 ]]; // contact rule I+S -> I+I with rate 0.8
|
||||
|
||||
|
||||
//output
|
||||
let simulation = []
|
||||
|
||||
|
||||
//let graph_as_edgelist = [[ 0, 4 ], [ 0, 1 ], [ 1, 5 ], [ 1, 2 ], [ 2, 6 ], [ 2, 3 ], [ 3, 7 ], [ 4, 8 ], [ 4, 5 ], [ 5, 9 ], [ 5, 6 ], [ 6, 10 ], [ 6, 7 ], [ 7, 11 ], [ 8, 12 ], [ 8, 9 ], [ 9, 13 ], [ 9, 10 ], [ 10, 14 ], [ 10, 11 ], [ 11, 15 ], [ 12, 13 ], [ 13, 14 ], [ 14, 15 ]];
|
||||
|
||||
//let horizon = 20.0; // wie lange wird simuliert
|
||||
//let initial_distribution = [0.5, 0.5, 0.0]; // gleiche Reihenfolge wie states, musss zu rules passen und normalisiert werden
|
||||
let timepoint_num = 101;
|
||||
|
||||
function get_next_state(current_labels){
|
||||
let fastes_firing_time = 10000000.0; //dummy
|
||||
let firing_rule = null;
|
||||
let firing_node = null;
|
||||
let firing_edge = null;
|
||||
|
||||
//if(current_labels == null)
|
||||
//iterate over nodes
|
||||
for(var currentNode in nodes){
|
||||
currentNode = nodes[currentNode];
|
||||
let current_state = current_labels[currentNode];
|
||||
for(var rule in rules){
|
||||
rule = rules[rule];
|
||||
if(rule[0] instanceof Array){
|
||||
//is contact rule
|
||||
continue;
|
||||
}
|
||||
if(current_state === rule[0]){
|
||||
let current_fireing_time = randomExponential(rule[2]);
|
||||
//addFiringTime(current_fireing_time);
|
||||
if(current_fireing_time < fastes_firing_time){
|
||||
fastes_firing_time = current_fireing_time;
|
||||
firing_rule = rule;
|
||||
firing_node = currentNode;
|
||||
firing_edge = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
//iterate over edges
|
||||
for(var edge in graph_as_edgelist){
|
||||
edge = graph_as_edgelist[edge];
|
||||
let node1 = edge;
|
||||
let node2 = edge;
|
||||
let current_state1 = current_labels[node1];
|
||||
let current_state2 = current_labels[node2];
|
||||
for(var currentRule in rules){
|
||||
currentRule = rules[currentRule];
|
||||
if(typeof currentRule[0] == typeof ""){
|
||||
//is spont. rule
|
||||
continue
|
||||
}
|
||||
if((current_state1 === currentRule[0][0] && current_state2 === currentRule[0][1]) || (current_state2 === currentRule[0][0] && current_state1 === currentRule[0][1])){
|
||||
let current_fireing_time = randomExponential(currentRule[2]);
|
||||
if(current_fireing_time < fastes_firing_time){
|
||||
fastes_firing_time = current_fireing_time;
|
||||
firing_rule = currentRule;
|
||||
firing_node = null;
|
||||
firing_edge = edge;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(firing_rule == null){
|
||||
//no rule could fire
|
||||
return [null, fastes_firing_time]; //would happen anyway but still
|
||||
}
|
||||
//apply rule
|
||||
let new_labels = Array.from(current_labels);
|
||||
|
||||
if(firing_node != null){
|
||||
new_labels[firing_node] = firing_rule[1];
|
||||
return [new_labels, fastes_firing_time];
|
||||
}
|
||||
console.assert(firing_edge != null);
|
||||
let change_node1 = firing_edge[0];
|
||||
let change_node2 = firing_edge[1];
|
||||
//we have to check which node changes in which direction
|
||||
if(new_labels[change_node1] === firing_rule[0][0] && new_labels[change_node2] === firing_rule[0][1]){
|
||||
new_labels[change_node1] = firing_rule[1][0];
|
||||
new_labels[change_node2] = firing_rule[1][1];
|
||||
}else{
|
||||
new_labels[change_node1] = firing_rule[1][1];
|
||||
new_labels[change_node2] = firing_rule[1][0];
|
||||
}
|
||||
|
||||
return [new_labels, fastes_firing_time];
|
||||
}
|
||||
|
||||
|
||||
function count_states(current_labels){
|
||||
let counter = [states.length];
|
||||
|
||||
simulation.push(current_labels);
|
||||
for(var label in current_labels){
|
||||
label = current_labels[label];
|
||||
let index = states[ label ];
|
||||
counter[index] += 1;
|
||||
}
|
||||
}
|
||||
function generateNodes(edgelist){
|
||||
let allNodes = [];
|
||||
for(var e in edgelist){
|
||||
e = edgelist[e];
|
||||
allNodes.push(e[0]);
|
||||
allNodes.push(e[1]);
|
||||
}
|
||||
//sort
|
||||
allNodes = allNodes.sort((a, b) => (a > b));
|
||||
//remove duplicates
|
||||
let finalArray = [];
|
||||
var last = -1;
|
||||
for(var i = 0; i < allNodes.length; i ++){
|
||||
if(last === allNodes[i]){
|
||||
continue;
|
||||
}
|
||||
else{
|
||||
finalArray.push(allNodes[i])
|
||||
last = allNodes[i];
|
||||
}
|
||||
}
|
||||
return finalArray;
|
||||
}
|
||||
|
||||
|
||||
|
||||
// np helper functions
|
||||
function randomExponential(rate){
|
||||
if(rate === 0){ return 10000000 }
|
||||
return -Math.log(Math.random()) / rate;
|
||||
}
|
||||
|
||||
function linspace(start, end, num){
|
||||
console.assert(start < end);
|
||||
console.assert(num > 0);
|
||||
let distance = (end - start) / (num - 1);
|
||||
let current = start;
|
||||
let out = [];
|
||||
while(current <= end){
|
||||
out.push(Math.round(current * 100) / 100);
|
||||
current += distance;
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
function randomChoice(states, num, distr){
|
||||
let out = [];
|
||||
let s = distr.reduce((a,e) => a + e);
|
||||
for(var i = 0; i < num; i++){
|
||||
let r = Math.random() * s;
|
||||
out.push(states.find((_,i) => (r -= distr[i]) < 0));
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
|
||||
//This is to be set by parent
|
||||
let rules;
|
||||
let states;
|
||||
let initial_distribution;
|
||||
let graph_as_edgelist;
|
||||
let horizon;
|
||||
|
||||
let nodes;
|
||||
|
||||
//setup
|
||||
let timepoints_samples;
|
||||
let current_labels;
|
||||
let global_clock;
|
||||
let labels = [];
|
||||
let state_counts = [];
|
||||
|
||||
function simulate(newRules, newStates, newDistr, newGraph, newHorizon){
|
||||
simulation = [];
|
||||
rules = newRules;
|
||||
states = newStates;
|
||||
initial_distribution = newDistr;
|
||||
graph_as_edgelist = newGraph;
|
||||
horizon = newHorizon;
|
||||
//setup
|
||||
nodes = generateNodes(graph_as_edgelist);
|
||||
timepoints_samples = linspace(0, horizon, timepoint_num);
|
||||
current_labels = randomChoice(states, nodes.length, initial_distribution);
|
||||
global_clock = 0;
|
||||
labels = [];
|
||||
state_counts = [];
|
||||
while(timepoints_samples.length > 0){
|
||||
let [new_labels, time_passed] = get_next_state(current_labels);
|
||||
global_clock += time_passed;
|
||||
while(timepoints_samples.length > 0 && global_clock > timepoints_samples[0]){
|
||||
labels.push(Array.from(current_labels));
|
||||
state_counts.push(count_states(current_labels));
|
||||
timepoints_samples = timepoints_samples.slice(1, timepoints_samples.length);
|
||||
}
|
||||
current_labels = new_labels;
|
||||
}
|
||||
return simulation;
|
||||
}
|
||||
|
||||
export default simulate;
|
||||
64
src/VisNetwork.js
Normal file
64
src/VisNetwork.js
Normal file
@ -0,0 +1,64 @@
|
||||
import React, { useEffect, useRef } from "react";
|
||||
import { Network } from "vis-network";
|
||||
|
||||
class Graph extends React.Component{
|
||||
constructor(props){
|
||||
super(props);
|
||||
this.state = {
|
||||
nodes:[
|
||||
{ id: 1, label: "Node 1" },
|
||||
{ id: 2, label: "Node 2" },
|
||||
{ id: 3, label: "Node 3" },
|
||||
{ id: 4, label: "Node 4" },
|
||||
{ id: 5, label: "Node 5" },
|
||||
],
|
||||
edges:[
|
||||
{ from: 1, to: 3 },
|
||||
{ from: 1, to: 2 },
|
||||
{ from: 2, to: 4 },
|
||||
{ from: 2, to: 5 },
|
||||
{ from: 3, to: 3 },
|
||||
]
|
||||
}
|
||||
}
|
||||
componentDidMount(){
|
||||
this.id = setInterval(()=>this.tick(), 1000);
|
||||
}
|
||||
componentWillUnmount(){
|
||||
clearInterval(this.id);
|
||||
}
|
||||
tick(){
|
||||
if(this.state.nodes[0].label === "Node 1"){
|
||||
var copy = this.state;
|
||||
copy.nodes[0].label = "TEUZET";
|
||||
this.setState(copy)
|
||||
}else{
|
||||
var copy = this.state;
|
||||
copy.nodes[0].label = "Node 1";
|
||||
this.setState(copy)
|
||||
}
|
||||
}
|
||||
render(){
|
||||
console.log(this.state);
|
||||
return (<VisNetwork inp={this.state}/>)
|
||||
}
|
||||
}
|
||||
|
||||
function VisNetwork(inp) {
|
||||
const visJsRef = useRef(false);
|
||||
var nodes = inp.inp.nodes;
|
||||
var edges = inp.inp.edges;
|
||||
useEffect(() => {
|
||||
visJsRef.current.graph = new Network(visJsRef.current, { nodes, edges }, {});
|
||||
// Use `network` here to configure events, etc
|
||||
}, [visJsRef, nodes, edges]);
|
||||
|
||||
//redraw the graph
|
||||
if(visJsRef.current.graph != null){
|
||||
console.log("red")
|
||||
visJsRef.current.graph.redraw();
|
||||
}
|
||||
return <div ref={visJsRef} />;
|
||||
};
|
||||
|
||||
export default Graph;
|
||||
2
src/css/App.css
Normal file
2
src/css/App.css
Normal file
@ -0,0 +1,2 @@
|
||||
.App {
|
||||
}
|
||||
6
src/css/Graph.css
Normal file
6
src/css/Graph.css
Normal file
@ -0,0 +1,6 @@
|
||||
/* Generated graph container */
|
||||
#cy {
|
||||
width: 100%;
|
||||
height: 600px;
|
||||
background-color: blue;
|
||||
}
|
||||
27
src/css/Simulation.css
Normal file
27
src/css/Simulation.css
Normal file
@ -0,0 +1,27 @@
|
||||
#Simulation {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
flex-direction: row;
|
||||
}
|
||||
|
||||
#SimulationSettings {
|
||||
width: 60%;
|
||||
height: 100%;
|
||||
flex: 50%;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
#SimulationGraph {
|
||||
width: 40%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
/* Responsive layout - makes a one column layout instead of a two-column layout */
|
||||
@media screen and (max-width: 800px) {
|
||||
#SimulationSettings, #SimulationGraph {
|
||||
width: 100%;
|
||||
}
|
||||
#Simulation {
|
||||
flex-direction: column;
|
||||
}
|
||||
}
|
||||
27
src/css/Slider.css
Normal file
27
src/css/Slider.css
Normal file
@ -0,0 +1,27 @@
|
||||
.Slider {
|
||||
width: 70%;
|
||||
}
|
||||
|
||||
.SliderDescription {
|
||||
display: block;
|
||||
text-align: left;
|
||||
padding-left: 13%;
|
||||
}
|
||||
|
||||
.SliderValue {
|
||||
display: inline-block;
|
||||
width: 3em;
|
||||
}
|
||||
|
||||
/* Remove arrows from number input */
|
||||
/* Chrome, Safari, Edge, Opera */
|
||||
input::-webkit-outer-spin-button,
|
||||
input::-webkit-inner-spin-button {
|
||||
-webkit-appearance: none;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
/* Firefox */
|
||||
input[type=number] {
|
||||
-moz-appearance: textfield;
|
||||
}
|
||||
13
src/css/index.css
Normal file
13
src/css/index.css
Normal file
@ -0,0 +1,13 @@
|
||||
/*body {*/
|
||||
/*margin: 0;*/
|
||||
/*font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen',*/
|
||||
/*'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue',*/
|
||||
/*sans-serif;*/
|
||||
/*-webkit-font-smoothing: antialiased;*/
|
||||
/*-moz-osx-font-smoothing: grayscale;*/
|
||||
/*}*/
|
||||
/**/
|
||||
/*code {*/
|
||||
/*font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New',*/
|
||||
/*monospace;*/
|
||||
/*}*/
|
||||
@ -1,13 +0,0 @@
|
||||
body {
|
||||
margin: 0;
|
||||
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen',
|
||||
'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue',
|
||||
sans-serif;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
}
|
||||
|
||||
code {
|
||||
font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New',
|
||||
monospace;
|
||||
}
|
||||
@ -1,13 +1,12 @@
|
||||
import React from 'react';
|
||||
import ReactDOM from 'react-dom';
|
||||
import './index.css';
|
||||
import './css/index.css';
|
||||
import App from './App';
|
||||
import reportWebVitals from './reportWebVitals';
|
||||
|
||||
//Strict mode was removed because of cytoscape
|
||||
ReactDOM.render(
|
||||
<React.StrictMode>
|
||||
<App />
|
||||
</React.StrictMode>,
|
||||
<App />,
|
||||
document.getElementById('root')
|
||||
);
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user