2020-09-10 21:44:11 +03:00
/ *
This example runs the same scenario repeatedly , each time cancelling a
different number of trips uniformly at random . The eventual goal is to quantify
how many trips need to be cancelled to substantially speed up remaining ones .
Before running this script , start the API server :
2020-10-14 18:49:24 +03:00
> cargo run -- release -- bin headless -- -- port = 1234
2020-09-10 21:44:11 +03:00
* /
package main
import (
"bytes"
"encoding/json"
2020-09-14 22:37:21 +03:00
"flag"
2020-09-10 21:44:11 +03:00
"fmt"
"io/ioutil"
"net/http"
2020-09-15 04:56:52 +03:00
"os"
2020-09-15 00:46:56 +03:00
"time"
2020-09-10 21:44:11 +03:00
)
const (
2020-09-14 22:37:21 +03:00
api = "http://localhost:1234/"
)
var (
2021-02-14 02:45:59 +03:00
cityName = flag . String ( "city" , "us/seattle" , "city of the map to simulate" )
2020-09-14 22:37:21 +03:00
mapName = flag . String ( "map" , "montlake" , "map name to simulate" )
hoursToSimulate = flag . Int ( "hours" , 24 , "number of hours to simulate" )
2020-09-15 04:56:52 +03:00
comparePct1 = flag . Int64 ( "cmp1" , - 1 , "the baseline percentage for indvidual comparison" )
comparePct2 = flag . Int64 ( "cmp2" , - 1 , "the experimental percentage for indvidual comparison" )
2020-09-10 21:44:11 +03:00
)
func main ( ) {
2020-09-14 22:37:21 +03:00
flag . Parse ( )
2020-09-15 04:56:52 +03:00
if * comparePct1 > * comparePct2 {
fmt . Printf ( "--cmp1=%v --cmp2=%v invalid, --cmp1 is the baseline\n" , * comparePct1 , * comparePct2 )
os . Exit ( 1 )
}
2020-09-14 22:37:21 +03:00
2020-09-15 00:46:56 +03:00
numSucceededLast := 0
2020-09-15 04:56:52 +03:00
var results2 results
2020-09-10 21:44:11 +03:00
for pct := int64 ( 100 ) ; pct >= 0 ; pct -= 10 {
2020-09-15 04:56:52 +03:00
results , err := run ( pct )
2020-09-15 00:46:56 +03:00
if err != nil {
2020-09-10 21:44:11 +03:00
fmt . Println ( "Failure:" , err )
break
}
2020-09-15 04:56:52 +03:00
numSucceeded := len ( results . successTime )
2020-09-15 00:46:56 +03:00
if numSucceeded < numSucceededLast {
fmt . Println ( "--> less trips succeeded this round, so likely hit gridlock" )
break
}
numSucceededLast = numSucceeded
2020-09-15 04:56:52 +03:00
if * comparePct2 == pct {
results2 = * results
}
2020-09-15 23:47:04 +03:00
if * comparePct1 == pct {
results1 := results
fmt . Printf ( "\nBaseline cancelled %v%%, experimental cancelled %v%%\n" , * comparePct1 , * comparePct2 )
var faster [ ] float64
var slower [ ] float64
2020-09-15 04:56:52 +03:00
2020-09-15 23:47:04 +03:00
for id , experimental_dt := range results2 . successTime {
baseline_dt := results1 . successTime [ id ]
if baseline_dt == 0.0 {
// This means the trip didn't finish in hoursToSimulate in the baseline
//fmt.Printf("Trip %v present in experimental, but not baseline\n", id)
continue
}
2020-09-15 04:56:52 +03:00
2020-09-15 23:47:04 +03:00
if false && baseline_dt != experimental_dt {
fmt . Printf ( " Trip %v: %v baseline, %v experimental\n" , id , baseline_dt , experimental_dt )
}
if baseline_dt > experimental_dt {
faster = append ( faster , baseline_dt - experimental_dt )
} else if baseline_dt < experimental_dt {
slower = append ( slower , experimental_dt - baseline_dt )
}
2020-09-15 04:56:52 +03:00
}
2020-09-15 23:47:04 +03:00
fmt . Printf ( "%v trips faster, average %v\n" , len ( faster ) , avg ( faster ) )
fmt . Printf ( "%v trips slower, average %v\n\n" , len ( slower ) , avg ( slower ) )
2020-09-15 04:56:52 +03:00
}
2020-09-10 21:44:11 +03:00
}
}
2020-09-15 04:56:52 +03:00
func run ( pct int64 ) ( * results , error ) {
2020-09-15 00:46:56 +03:00
start := time . Now ( )
2020-09-25 04:08:05 +03:00
_ , err := post ( "sim/load" , LoadSim {
2020-11-06 01:05:48 +03:00
Scenario : fmt . Sprintf ( "data/system/%v/scenarios/%v/weekday.bin" , * cityName , * mapName ) ,
2020-10-20 02:03:17 +03:00
Modifiers : [ ] ScenarioModifier { { ChangeMode : ChangeMode {
PctPpl : uint64 ( pct ) ,
DepartureFilter : [ ] float64 { 0.0 , 86400.0 } ,
FromModes : [ ] string { "Walk" , "Bike" , "Transit" , "Drive" } ,
ToMode : nil ,
} } } ,
2020-09-10 21:44:11 +03:00
} )
if err != nil {
2020-09-15 04:56:52 +03:00
return nil , err
2020-09-10 21:44:11 +03:00
}
2020-09-14 22:37:21 +03:00
_ , err = get ( fmt . Sprintf ( "sim/goto-time?t=%v:00:00" , * hoursToSimulate ) )
2020-09-10 21:44:11 +03:00
if err != nil {
2020-09-15 04:56:52 +03:00
return nil , err
2020-09-10 21:44:11 +03:00
}
resp , err := get ( "data/get-finished-trips" )
if err != nil {
2020-09-15 04:56:52 +03:00
return nil , err
2020-09-10 21:44:11 +03:00
}
2020-09-25 04:08:05 +03:00
var trips [ ] FinishedTrip
2020-09-10 21:44:11 +03:00
if err := json . Unmarshal ( [ ] byte ( resp ) , & trips ) ; err != nil {
2020-09-15 04:56:52 +03:00
return nil , err
2020-09-10 21:44:11 +03:00
}
2020-09-15 04:56:52 +03:00
results := results { }
2020-09-25 04:08:05 +03:00
results . successTime = make ( map [ uint64 ] float64 )
for _ , trip := range trips {
2020-11-22 22:21:44 +03:00
// TODO Null... but Go will just stick in the zero value? :(
if trip . Duration == 0.0 {
2020-10-03 20:41:53 +03:00
results . numCancelled ++
2020-09-10 21:44:11 +03:00
} else {
2020-09-25 04:08:05 +03:00
results . successTime [ trip . ID ] = trip . Duration
2020-09-10 21:44:11 +03:00
}
}
2020-10-03 20:41:53 +03:00
fmt . Printf ( "%v with %v%% of people cancelled: %v trips cancelled, %v trips succeeded. Simulation took %v\n" , * mapName , pct , results . numCancelled , len ( results . successTime ) , time . Since ( start ) )
2020-09-15 04:56:52 +03:00
return & results , nil
}
2020-09-10 21:44:11 +03:00
2020-09-15 04:56:52 +03:00
type results struct {
2020-10-03 20:41:53 +03:00
numCancelled int
2020-09-15 04:56:52 +03:00
// Trip ID to duration
2020-09-25 04:08:05 +03:00
successTime map [ uint64 ] float64
2020-09-10 21:44:11 +03:00
}
func get ( url string ) ( string , error ) {
resp , err := http . Get ( api + url )
if err != nil {
return "" , err
}
body , err := ioutil . ReadAll ( resp . Body )
resp . Body . Close ( )
if err != nil {
return "" , err
}
return string ( body ) , nil
}
func post ( url string , body interface { } ) ( string , error ) {
encoded , err := json . Marshal ( body )
if err != nil {
return "" , err
}
resp , err := http . Post ( api + url , "application/json" , bytes . NewReader ( encoded ) )
if err != nil {
return "" , err
}
respBody , err := ioutil . ReadAll ( resp . Body )
resp . Body . Close ( )
if err != nil {
return "" , err
}
return string ( respBody ) , nil
}
2020-09-15 04:56:52 +03:00
func avg ( list [ ] float64 ) string {
if len ( list ) == 0 {
return "empty"
}
sum := 0.0
for _ , x := range list {
sum += x
}
return fmt . Sprintf ( "%v" , sum / float64 ( len ( list ) ) )
}
2020-09-25 04:08:05 +03:00
type LoadSim struct {
Scenario string ` json:"scenario" `
2020-09-10 21:44:11 +03:00
Modifiers [ ] ScenarioModifier ` json:"modifiers" `
}
type ScenarioModifier struct {
2020-10-20 02:03:17 +03:00
ChangeMode ChangeMode
}
type ChangeMode struct {
PctPpl uint64 ` json:"pct_ppl" `
DepartureFilter [ ] float64 ` json:"departure_filter" `
FromModes [ ] string ` json:"from_modes"' `
// TODO Need to test this
ToMode * string ` json:"to_mode" `
2020-09-10 21:44:11 +03:00
}
2020-09-25 04:08:05 +03:00
type FinishedTrip struct {
ID uint64 ` json:"id" `
Duration float64 ` json:"duration" `
Mode string ` json:"mode" `
Capped bool ` json:"capped" `
2020-09-10 21:44:11 +03:00
}