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 :
> cargo run -- release -- bin headless -- -- port = 1234 data / system / scenarios / montlake / weekday . bin
* /
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 (
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-10 21:44:11 +03:00
_ , err := post ( "sim/load" , SimFlags {
2020-09-14 22:37:21 +03:00
Load : fmt . Sprintf ( "data/system/scenarios/%v/weekday.bin" , * mapName ) ,
2020-09-10 21:44:11 +03:00
Modifiers : [ ] ScenarioModifier { { CancelPeople : pct } } ,
} )
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
}
var trips FinishedTrips
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 { }
results . successTime = make ( map [ int ] float64 )
2020-09-10 21:44:11 +03:00
for _ , trip := range trips . Trips {
if trip [ 2 ] == nil {
2020-09-15 04:56:52 +03:00
results . numAborted ++
2020-09-10 21:44:11 +03:00
} else {
2020-09-15 04:56:52 +03:00
results . successTime [ int ( trip [ 1 ] . ( float64 ) ) ] = trip [ 3 ] . ( float64 )
2020-09-10 21:44:11 +03:00
}
}
2020-09-15 04:56:52 +03:00
fmt . Printf ( "%v with %v%% of people cancelled: %v trips aborted, %v trips succeeded. Simulation took %v\n" , * mapName , pct , results . numAborted , len ( results . successTime ) , time . Since ( start ) )
return & results , nil
}
2020-09-10 21:44:11 +03:00
2020-09-15 04:56:52 +03:00
type results struct {
numAborted int
// Trip ID to duration
successTime map [ int ] 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-10 21:44:11 +03:00
type SimFlags struct {
Load string ` json:"load" `
Modifiers [ ] ScenarioModifier ` json:"modifiers" `
}
type ScenarioModifier struct {
CancelPeople int64
}
type FinishedTrips struct {
// Vec<(Time, TripID, Option<TripMode>, Duration)>
Trips [ ] [ ] interface { } ` json:"trips" `
}