abstreet/docs/design/notes/sim.md

12 KiB

General simulation-related design notes

Spawning agents

Ideally, each method would return a future that would do stuff, right? But the mutations need to be done serially. So I think each sim's method should take the path outright, not even start/end. Stick the rng work in sim for the moment. This should let the start/goal selection and the parallelization of paths happen at a more outer layer, in the sim aggregator.

... and now for scenarios / spawners. these get to run every step, trying to introduce new things in the different simulations. if a parked car can't currently begin departing, it'll keep trying every tick.

... and now we've hit the point where Command enum has tons of boilerplate and also doing the pathfinding every re-attempt is very slow, especially when spawning agents from a border. I would just go do the work immediately, store a CreateCar or equivalent walking sim spec, and execute those instead... except that then we won't parallelize pathfinding!

It'd actually be possibly desirable to do all the expensive pathfinding up-front at the beginning of the sim (during scenario instantiation) to have more consistent performance during the running of the sim. (Though I can also imagine wanting to naturally spread it out too...)

Ahh wait, definitely cannot do all of the pathfinding work up-front. As trip legs are finished, we still need some of those commands available to start new trip legs.

Ideas:

  • make Commands have a cached path. first time hitting a particular command, batch all the requests, calculate paths, strip out commands with impossible paths, and populate the rest.
    • probably the easiest change to make right now
    • keeps boilerplatey and complicated code around
    • flexible... could precompute as many paths at a time as we want
  • shift responsibilities...
    • car/ped ID counter moves to scenario instantiation (possibly a new thing associated with it)
    • the spawn methods that exist today to start new trips actually move to trips, partly? the calculation of legs given a high-level spec from scenario.

Argh, spawn is too convoluted; it does too much stuff right now. Split it out more slowly.

  • first, just a scheduler that does CreateCar or the pedestrian thing at some time, and can retry. it's handed ready-to-go commands.

Notes on determinism

Modeling choices

Don't model sidewalk crowdedness or bike rack availability, because in practice, these things are never scarce resources or problematic. Finding a parking spot is difficult and impacts the quality of one trip and causes externality, so we should model that.

Trips

Time to get even more multi-modal / multi-phase!

  • all trips begin and end at a building

  • spawn peds at a building, make them first traverse the front path.

    • could model another type of On
    • or, just have a special state in the walking sim, just like the driving sim has a temporary parking/unparking state
  • the walking layer shouldnt care about the next layer of the trip. just tell master sim when a ped has reached a bldg or a parking spot, as desired.

  • need to draw the FSM for all of this!

maybe need to organize structs/enums a little...

ParkingSpot - change this to just lane and spot idx, move other stuff to queries for ParkingSim, make it copyable CarParking - rename to ParkedCar SidewalkSpot - this should cache lane and distance. :)

Scenarios

Awkward to turn neighborhoods into buildings/streets; we kind of need the quadtree and stuff for that, which is the Renderable layer right now. Originally there was a separate geometry layer, probably for stuff like this.

Scores

Alright, getting much closer to this being a game! Let's return to the idea of utility functions for agents.

  • everyone cares about total trip time
  • everyone kind of cares about time spent waiting at intersections
  • drivers (anybody using a car for part of their trip)
    • easiness of parking... partly this is time spent walking (from start bldg or to goal bldg), and partly time spent driving after initially reaching destination lane
  • bikes (woops, not implemented yet :P)
    • climbing up hills
    • amount of time on busy roads
      • dedicated lanes are fine
      • even dedicated lanes too close to parking are bad -- stress from possibiliy of being doored
      • driving lanes with few actual cars passing are bad
  • peds
    • hills up OR down
    • amount of greenery along route
    • amount of amenities like cafes along route
    • Seattle greenways had more factors that make a road pleasant or not

Per agent, this score is some kind of a linear combination of factors. Coefficients vary per agent -- some people like hills, don't care about busy roads, etc.

But let's start super simple: just track total trip time for all agents. What's the live UI view we want?

  • per population type (peds, drivers), number of pending and completed trips. sum score so far (can use time so far for pending trips)
    • note that sum score alone is a bit meaningless, even between population types. need to A/B test to meaningfully compare.
  • In headless mode, print scores at the end
  • in UI, have an optional OSD to pop up on the right with scores so far

Edit-Invariant

Back in the day, TurnID went from a number to (Lane, Lane, Intersection), so that a single edit wouldn't totally throw off all the turn IDs. Now seeing a similar problem when removing a parking lane -- the same 'seed parked cars' command has vastly different effects between the same sim, and this tiny change throws off the RNG and percolates everywhere. Let's think about how to make more things invariant.

  • should cars be the same?
    • maybe peds have to travel farther from home to start driving.
    • does the car on the far road belong to anyone in the other sim?
  • forget debugability for a moment, what do we actually need to compare?
    • entire trips! maybe some use a bus or not btwn two worlds
    • warp to trip (using the active mode), compare trips
      • problem: ped IDs right now differ wildly because the rng gets offset. maybe those should always be chosen first? maybe they should be calculated independently and stuck in the Scenario? that part of a trip is a sim-invariant ID, a spawn time, and a start/goal building. the details (legs of trip) are calculated per sim.
  • the RNG is also used after spawning to later roam around for parking
    • small road edits shouldnt affect this. per-car rng in an extreme, or maybe just an RNG for sim and for spawning?
      • but as soon as two cars have to wander for parking instead of one, everything gets offset completely.
  • this will be harder later with spawners on map edges...

Alright, the deviations are still starting too early!

  • Swapping shouldn't show different parked cars when the parking lane is present in both sims... or should it?
    • If 50% of spots in a neighborhood need to be initially populated, then more or less options for those DOES affect it.
      • the problem seems like "seed 50% of parked car spots in this area" is too vague. but theres also a desire that scenarios remain human-readable, high-level.
  • Peds are picking very different parked cars in the neighborhood to go drive, causing big deviations early on.

Can we avoid storing trip/parked car mappings in the scenario?

  • if peds pick the parked car closest to their start building... maybe

Idea: forking RNGs

50% of spots filled isn't really accurate -- we're saying for every spot, flip a coin with some weight. The flips are independent. We can regain an amount of determinism by forking RNGs -- for every single lane in the map, use the main RNG to generate a new RNG. Use that new RNG only if it really is a parking lane.

Cool, much closer to working! Spots are consistently filled out or not. Car colors are different, because car IDs are different. Making CarIDs consistent would require changing them to have a stable form -- namely, their original parking spot (lane ID and an offset). We could do that...

But also wait, ped IDs are a bit different in some cases, and a trip is missing entirely... huh?

Ah, we call gen_range on different inputs. Not sure why that throws off IDs though... Can we fork RNG for that too?

Problems:

  • CarIDs are different, could make them be original parking spot = gen_range on different inputs

Parked cars and ownership

Most cars are associated with a household. They usually park close to that building (during overnight times). Some cars have no building -- buses, or cars that might be visiting from outside the simulated area. A building might have several cars.

Given:

  • number of cars that should be around each house
  • number of visiting cars Can then seed parked cars kinda greedily.

But back up, other section.

Alright, now how do peds picking a car work?

  • easy case: if the same car is present in both worlds, should use the same
  • if cars are associated with buildings in a stable way, this should probably help or at least push the problem to a slightly more explicit place
  • if a parking lane is gone and an agent wouldve used something there, of course they have to walk farther to get to their car. but we want to somehow localize these effects. same number of parked cars in the neighborhood and same assignment to buildings, but maybe some other lane gets more crowded?

Next thoughts:

  • scrap the concept of SeedParkedCars. instead say "x% of buildings in this neighborhood have 1 or 2 cars." peds from these buildings will use those specific cars, others wont.
    • probability of 0 cars = 40, 1 car = 40, 2 cars = 20 <--- thats much nicer
  • go through each building, find a spot to seed that parked car.
    • try to be close to building, random jitter to be a little far away
    • if this process is somewhat stable, then should be fine. doesnt need to be perfectly stable -- removing lanes somewhere might put more pressure on another lane.

steps for this change: = give cars an optional building as an owner, make a query for parking sim to find available cars, hook up UI debug = initially randomly assign a building in the neighborhood = start seeding parked cars per building instead of per spot = make sure stability is vaguely preserved = make peds that'll use a car pick from a house with an available car = should we even specify this, or should they pick mode at the beginning based on availability? leaning towards this option since it's easier to implement right now and seems more natural

Traces between worlds

Alright, now how do we even compare trip progress to show it visually? This is kind of a UI-only problem; total score at the end can be compared way more easily.

  • easy case: if both worlds are using the same mode AND route, trace_route the slower agent with the dist_ahead of the faster agent (difference between the two)
    • alright, have to track current distance traveled to do this. can keep state per trip leg or something.
  • if it's unclear who's closer to the goal, could just pathfind to exactly where the other agent is, display in a neutral way
    • mode changes are potentially weird... but just slide over between sidewalks and driving lanes? mmm...
  • strawman: dont use the trace route thing, just have a straight arrow to the other agent and green/red based on straight-line distance to goal bldg
  • progress is non-monotonic -- might walk away from goal to get to car, then get there faster. or maybe get stuck in traffic. so straightline distance to goal is EXPECTED to fluctuate. thats kind of exciting to watch anyway.

Ah, upon seeing just the line between, I know what would make more sense -- show the divergence. The point in the route where one version goes one way, and the second goes another. Two routes shown, symmetric.

Alright, let's see how performant it is to compare all agents!

Interactive spawning commands

I no longer imagine a use case for these controls other than debugging / playing around. It's very unclear how it interacts with the new scenario stuff. Going to remove these for now.