Skip to content

Commit f7b54dc

Browse files
authored
Merge pull request #10 from MDPvis/feature-v2
Version 2
2 parents 730b9b1 + 070e5a4 commit f7b54dc

21 files changed

+2124
-1336
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,3 +8,4 @@
88
*.ps
99
static/*
1010
*.pyc
11+
.idea

README.md

Lines changed: 82 additions & 97 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# About
22

3-
MDPvis is a visualization designed to assist in the MDP simulation and optimization process. See the forthcoming research paper for more details, "Facilitating Testing and Debugging of Markov Decision Processes with Interactive Visualization." To play with a live version of the visualization, visit [mdpvis.github.io/](http://mdpvis.github.io/).
3+
MDPvis is a visualization designed to assist in the MDP simulation and optimization process. See "[Facilitating Testing and Debugging of Markov Decision Processes with Interactive Visualization](http://ieeexplore.ieee.org/xpl/login.jsp?tp=&arnumber=7357198&url=http%3A%2F%2Fieeexplore.ieee.org%2Fxpls%2Fabs_all.jsp%3Farnumber%3D7357198)." To play with a live version of the visualization, visit [mdpvis.github.io/](http://mdpvis.github.io/).
44

55
We built MDPvis as a web-based visualization so it would be:
66

@@ -22,142 +22,128 @@ If you don't use our hosted version of the MDPvis web application, you will need
2222
2. Clone MDPvis into your MDP simulator code base, `cd YOUR_SIMULATOR;git clone git@github.com:MDPvis/MDPvis.github.io.git`. You can use a [Git Submodule](https://git-scm.com/book/en/v2/Git-Tools-Submodules) for this if you don't mind figuring out how they work. If you are going to contribute back to MDPvis, you should probably clone your fork of MDPvis.
2323
3. Install [Python 2.7](https://www.python.org/downloads/release/python-279/)
2424
4. Install a few Python libraries with `pip install -U flask-cors`.
25-
5. Navigate into the MDPvis directory and start the server with `python flask_server.py`
26-
27-
At this point the server will start, but it will likely fail because the Flask server expects your MDP simulator to define a file in its code base called `domain_bridge.py`. The next (and final) section helps you define this file.
25+
5. Navigate into the MDPvis directory and start the server with `./serve.sh`
26+
6. Visit http://localhost:8000
27+
7. Select one of the pre-existing simulation and optimization servers, or bridge MDPvis to your domain
2828

2929
# Bridging MDPvis and Your Domain
3030

31-
MDPvis interfaces with any MDP simulator+optimizer that is callable by a web server. If you use the web server packages with MDPvis, you can update an example `domain_bridge.py` file found in `example_domain_bridges`, otherwise we recommend viewing the example domain bridges and writing a version of the file as appropriate for your platform.
31+
MDPvis interfaces with any MDP simulator+optimizer that is callable by a web server.
3232

33-
The bridges take the HTTP requests from the visualization, transforms the query parameters to those expected by the simulator or optimizer, invokes the simulator or optimizer, then returns the results to MDPvis. There are four distinct requests that the bridge should support. We detail these requests below
33+
Your domain web server is responsible for serving four HTTP requests from the visualization, transforms the query parameters to those expected by the simulator or optimizer, invokes the simulator or optimizer, then returns the results to MDPvis. There are four distinct requests that the bridge should support. We detail these requests below.
3434

35-
The visualization expects your code to support the following requests. If your domain is written in Python, we recommend porting one of the example `domain_bridge.py` files to your domain.
3635

3736
## /initialize
3837

39-
The initialize endpoint doesn't query the simulator or optimizer, but it does provide a set of parameters that will influence those systems when requests are made. Here your responsibility is to return a [JSON](http://www.copterlabs.com/blog/json-what-it-is-how-it-works-how-to-use-it/) object listing the:
38+
The `/initialize` endpoint provides a set of parameters that will be sent to the simulator or optimizer on future requests. Here your responsibility is to return a [JSON](http://www.copterlabs.com/blog/json-what-it-is-how-it-works-how-to-use-it/) object listing these properties:
4039

4140
* Name
4241
* Description
4342
* Current Value
4443
* Minimum Value
4544
* Maximum Value
45+
* Step (How fast the value changes when pressing a button)
46+
* Units
4647

47-
of each parameter. An example of this data structure in Python is:
48+
An example of this data structure in Python is:
4849

4950
return {
50-
"reward": [
51-
{"name": "Discount",
52-
"description":"The per-year discount",
53-
"current_value": 1, "max": 1, "min": 0, "units": "~"},
54-
{"name": "Suppression Variable Cost",
55-
"description":"cost per hectare of suppression",
56-
"current_value": 500, "max": 999999, "min": 0, "units": "$"}
57-
],
58-
"transition": [
59-
{"name": "Years to simulate",
60-
"description": "how far to look into the future",
61-
"current_value": 10, "max": 150, "min": 0, "units": "Y"},
62-
{"name": "Harvest Percent",
63-
"description": "timber harvest rate as a percent of annual increment",
64-
"current_value": 0.95, "max": 1, "min": 0, "units": "%"},
65-
{"name": "Minimum Timber Value",
66-
"description":"the minimum timber value required before harvest is allowed",
67-
"current_value": 50, "max":9999, "min": 0, "units": "$"},
68-
{"name": "Growth Model",
69-
"description": "set to 1 to use original model; or 2 for updated model.",
70-
"current_value": 1, "max":2, "min": 1, "units": "~"}
71-
],
72-
"policy": [
73-
{"name": "Constant",
74-
"description":"for the intercept",
75-
"current_value": 0, "max": 10, "min":-10, "units": ""},
76-
{"name": "Fuel Load 8",
77-
"description":"for the average fuel load in the 8 neighboring stands",
78-
"current_value": 0, "max": 10, "min":-10, "units": ""},
79-
{"name": "Fuel Load 24",
80-
"description":"for the average fuel load in the 24 neighboring stands",
81-
"current_value": 0, "max": 10, "min":-10, "units": ""}
82-
]
83-
}
84-
85-
In the MDPvis user interface, each control will be grouped into panels for the reward, model (transition function), and policy.
86-
87-
## /rollouts?QUERY
88-
89-
When requesting Monte Carlo rollouts, MDPvis will send the current set of parameters as defined in the initialization and assigned in the user interface. The job of the domain bridge is to map the parameters of the user interface into parameters to invoke the simulator. After simulations have completed, the data should be JSON serialized. An example of the data in Python is:
9051

91-
return [
52+
# The control panels that appear at the top of the screen
53+
"parameter_collections": [
54+
{
55+
"panel_title": "Sampling Effort",
56+
"panel_icon": "glyphicon-retweet",
57+
"panel_description": "Define how many trajectories you want to generate, and to what time horizon.",
58+
"quantitative": [ # Real valued parameters
59+
{
60+
"name": "Sample Count",
61+
"description": "Specify how many trajectories to generate",
62+
"current_value": 10,
63+
"max": 1000,
64+
"min": 1,
65+
"step": 10,
66+
"units": "#"
67+
},
68+
{
69+
"name": "Horizon",
70+
"description": "The time step at which simulation terminates",
71+
"current_value": 10,
72+
"max": 10000,
73+
"min": 1,
74+
"step": 10,
75+
"units": "Time Steps"
76+
},
77+
{
78+
"name": "Seed",
79+
"description": "The random seed used for simulations",
80+
"current_value": 0,
81+
"max": 100000,
82+
"min": 1,
83+
"step": 1,
84+
"units": "NA"
85+
}
86+
]
87+
}
88+
]
89+
}
90+
91+
In the MDPvis user interface, each control will be grouped into panels under the `panel_title`
92+
and display the [icon](http://getbootstrap.com/components/#glyphicons) specified by `panel_icon`.
93+
94+
## /trajectories?QUERY
95+
96+
When requesting Monte Carlo trajectories, MDPvis will send the current set of parameters as defined in the initialization and assigned in the user interface. The job of the web server is to map the parameters of the user interface into parameters to invoke the simulator. After simulations have completed, the data should be JSON serialized. An example of the data in Python is:
97+
98+
return {"trajectories": [
9299
[
93100
{"Burn Time": 4.261, "Timber Harvested": 251}, {"Burn Time": 40.261, "Timber Harvested": 0}
94101
],
95102
[
96103
{"Burn Time": 0.0, "Timber Harvested": 342}, {"Burn Time": 45.261, "Timber Harvested": 20}
97104
]
98-
]
99-
100-
These data are two rollouts of two states each.
101-
102-
## /optimize?QUERY
103-
104-
MDPvis does not require you to integrate `/optimize` and `/state`, but it is very useful for exploring most problems. Here all the same parameters as are sent to `/rollouts` are sent to `/optimize`, but this query only returns an updated policy. Here is a python example:
105-
106-
return [
107-
{"Constant": 10},
108-
{"Fuel Load 8": 3},
109-
{"Fuel Load 24": -1}
110-
]
111-
112-
## /state?QUERY
105+
]}
113106

114-
This query will be issued when a user clicks an individual rollout in the visualization. All the parameters used to generate the rollout will be sent, with an additional parameter for the rollout number. The expectation is you will use this information to re-generate the rollout and use the simulator to generate descriptive statistics and/or images for the states.
107+
These data are two trajectories of two states each. An additional special state variable, `image row`, gives
108+
an array of images or videos that should be displayed when selecting a trajectory. For example:
115109

116-
An example of the expected return format is:
117-
118-
return {
119-
"images": [
120-
["file_row1_column1.png", "file_row1_column2.png"],
121-
["file_row2_column1.png", "file_row2_column2.png"]]
122-
"statistics": {
123-
"stat 1": 5,
124-
"stat 2": -100
125-
}
126-
}
127-
128-
# Adding Additional Visualizations
129-
130-
There is a default set of visualizations, but if you want to add your own visualizations you should be aware of the three visualization types in MDPvis. These break Monte Carlo rollouts into visualizations for a single time step, variables through time (temporal distributions), and details on a single rollout. Details on these three aspects are below.
131-
132-
**Single Time Step Distributions**
110+
return {"trajectories": [
111+
[
112+
{"Burn Time": 4.261, "Timber Harvested": 251, "image row": ["traj1-1.png"]}, {"Burn Time": 40.261, "Timber Harvested": 0, , "image row": ["traj1-2.png"]}
113+
],
114+
[
115+
{"Burn Time": 0.0, "Timber Harvested": 342, "image row": ["traj2-1.mp4"]}, {"Burn Time": 45.261, "Timber Harvested": 20, "image row": ["traj2-1.mp4"]}
116+
]
117+
]}
133118

134-
Monte Carlo rollouts produce a distribution of states at every time step. This view gives details on the distribution for the currently selected time step. Users may select the current time step for all these visualizations simultaneously from the top of the visualization area, or from the temporal distribution area.
119+
will attempt to display the images `traj1-1.png` and `traj1-1.png` when the user clicks the associated trajectory.
135120

136-
* Histogram
137-
* Bar Chart (comparison mode)
121+
## /optimize?QUERY
138122

139-
**Temporal Distributions**
123+
MDPvis does not require you to integrate `/optimize` and `/state`, but it is very useful for exploring most problems. Here all the same parameters as are sent to `/trajectories` are sent to `/optimize`, but this query only returns an updated policy. Here is a python example for a logistic regression based policy:
140124

141-
In this area we show how the distribution of state variables develops through time.
125+
return {"Constant": 10,
126+
"Fuel Load 8": 3,
127+
"Fuel Load 24": -1}
142128

143-
* Fan Chart
144-
* Fan Chart (comparison mode)
145-
* Time Series
129+
Here is an example where the policy parameters represent versions of a neural network. This would allow for comparing between the performances of different neural networks and
130+
asking for additional training of an existing network.
146131

147-
**Single Rollout**
132+
return [
133+
{"network version": 5}
134+
]
148135

149-
Here a single rollout is shown. This could give a sequence of state snapshots provided as images from the MDP simulator.
136+
## /STATE_DETAIL
150137

151-
* State images (provided by simulator)
152-
* Stats panel
138+
This query will be issued when a user clicks an individual trajectory in the visualization and the state detail area populates with the images and videos specified by the trajectory's "image row" variable. The expectation is you will use the file name to re-generate the trajectory and use the simulator to generate descriptive statistics, videos, and/or images for the states.
153139

154140
## Implementing a New Visualization
155141

156142
If you are interested in implementing a new visualization within MDPvis, we encourage you to make contact by opening an issue in this visualization. The code base is under active development and will be changing substantially to be more easily extensible.
157143

158144
You've been warned. Here are the steps:
159145

160-
1. Select a visualization aspect (Single Time Step Distributions, Temporal Distributions, Single Rollout)
146+
1. Select a visualization aspect (Single Time Step Distributions, Temporal Distributions, Single Trajectory)
161147
1. Copy an existing visualization's script that has the chosen aspect
162148
1. Add the script to index.html
163149
1. Update the index.js script to call your visualization and add it to the DOM.
@@ -172,4 +158,3 @@ Maintainer Mailing Address: PO Box 79, Corvallis, OR 97339, United States of Ame
172158

173159
Implementation by: Sean McGregor
174160
With: Hailey Buckingham, Thomas G. Dietterich, Rachel Houtman, Claire Montgomery, and Ronald Metoyer
175-

css/index.css

Lines changed: 86 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,10 @@ body {
7070
display: none;
7171
}
7272

73+
.x.axis.show path {
74+
display: inherit;
75+
}
76+
7377
.fire_histogram_axis_label {
7478
font-size: large;
7579
}
@@ -218,10 +222,11 @@ body {
218222
.line {
219223
fill: none;
220224
stroke-width: 2px;
225+
opacity: 0.3;
221226
}
222227

223228
.state-detail {
224-
cursor: help;
229+
cursor: pointer;
225230
}
226231

227232
.statistics-area {
@@ -298,3 +303,83 @@ body {
298303
.affix-top {
299304
min-height: 60px;
300305
}
306+
307+
.temporal_zoom {
308+
opacity: 0;
309+
}
310+
311+
.temporal_zoom:hover {
312+
opacity: 0.1;
313+
color: #222222;
314+
cursor: zoom-in;
315+
}
316+
317+
.temporal-brush-boundary {
318+
cursor:pointer;
319+
}
320+
321+
.hover_line:hover {
322+
stroke: yellow;
323+
stroke-width: 8px;
324+
cursor: pointer;
325+
}
326+
327+
.axis text {
328+
text-shadow: 0 1px 0 #fff, 1px 0 0 #fff, 0 -1px 0 #fff, -1px 0 0 #fff;
329+
cursor: move;
330+
}
331+
332+
.color-cycle, .color-cycle.selected_line {
333+
animation-name: selectedCycle;
334+
animation-duration:6s;
335+
animation-iteration-count:infinite;
336+
337+
-webkit-animation-name: selectedCycle;
338+
-webkit-animation-duration:3s;
339+
-webkit-animation-iteration-count:infinite; }
340+
341+
@keyframes selectedCycle
342+
{
343+
0% {stroke:green;}
344+
25% {stroke:red;}
345+
50% {stroke:green;}
346+
75% {stroke:red;}
347+
}
348+
349+
@-webkit-keyframes selectedCycle
350+
{
351+
0% {stroke:green;}
352+
25% {stroke:red;}
353+
50% {stroke:green;}
354+
75% {stroke:red;}
355+
}
356+
357+
.parameter-line {
358+
stroke: grey;
359+
fill: none;
360+
stroke-width: 2px;
361+
}
362+
363+
.selected_line {
364+
stroke-width: 4px;
365+
stroke-dasharray: 5,10,5;
366+
stroke: red;
367+
}
368+
369+
.viewed-parameter-line {
370+
stroke: green;
371+
stroke-width: 4px;
372+
}
373+
374+
.compared-parameter-line {
375+
stroke: red;
376+
stroke-width: 4px;
377+
}
378+
379+
.btn {
380+
min-width: 220px;
381+
}
382+
383+
.modal-title {
384+
text-align: center;
385+
}

flask_server.py

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -83,7 +83,7 @@ def site_root():
8383
integration with your MDP domain and optimizer.</p>
8484
<p style='font-size: 150%;'>To test the other endpoints, visit
8585
<a href="/initialize">/initialize</a>,
86-
<a href="/rollouts">/rollouts</a>,
86+
<a href="/trajectories">/trajectories</a>,
8787
<a href="/optimize">/optimize</a>, or
8888
<a href="/state">/state</a>
8989
'''
@@ -96,16 +96,16 @@ def cross_origin_initialize():
9696
'''
9797
return jsonify(domain_bridge.initialize())
9898

99-
@app.route("/rollouts", methods=['GET'])
99+
@app.route("/trajectories", methods=['GET'])
100100
@cross_origin(allow_headers=['Content-Type'])
101-
def cross_origin_rollouts():
101+
def cross_origin_trajectories():
102102
'''
103-
Asks the domain for the rollouts generated by the
103+
Asks the domain for the trajectories generated by the
104104
requested parameters.
105105
'''
106106
q = parse_query(request.args)
107-
rollouts = domain_bridge.rollouts(q)
108-
return jsonify({"rollouts": rollouts})
107+
trajectories = domain_bridge.trajectories(q)
108+
return jsonify({"trajectories": trajectories})
109109

110110
@app.route("/optimize", methods=['GET'])
111111
@cross_origin(allow_headers=['Content-Type'])

0 commit comments

Comments
 (0)