Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Adding fares functionality #51

Merged
merged 76 commits into from
Feb 9, 2017
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
76 commits
Select commit Hold shift + click to select a range
6021058
Merge pull request #31 from MetropolitanTransportationCommission/develop
lmz Sep 8, 2016
1cb8f4d
Initial fare implementation
Sep 10, 2016
1c82f3e
Fare lookup bug fix
Sep 12, 2016
4249653
Output fare and include it in python calculate_cost
Sep 12, 2016
083758e
Start implementing stop zones
Sep 13, 2016
fcb4dcd
Addendum to last commit
Sep 13, 2016
7e8ecd2
Zone-based fares in c++ extension
Sep 14, 2016
416bfdd
Add zone-based fares to python
Sep 20, 2016
24073fb
Add fare_class to pathset links as well
Sep 20, 2016
3e5e9d0
Doc fix
Sep 20, 2016
74a42e7
Bug fix: fare_rules suffix so we don't lose route_id
Sep 20, 2016
101aed5
Split transit links: fare/fare period only on first
Sep 20, 2016
3fed8d7
Merge branch 'develop' into fares
Oct 7, 2016
ff7ec9d
Merge branch 'develop' into fares
Oct 7, 2016
943032b
Fix fare bug -- set stop zone ids once
Oct 7, 2016
bd1e1bb
Apply fare transfers in python
Oct 13, 2016
b7af997
Update fare transfer col names, values
Oct 13, 2016
11d2743
Add fare to StopState and return to python
Oct 13, 2016
bf92f04
Add fare transfer disounts to c++ extension
Oct 14, 2016
5f007dc
Update example: add a couple more local bus stops
Oct 15, 2016
38d9b21
Fares (python): handle transfers, transfer_duration
Oct 18, 2016
3ffe289
apply_free_transfers() missed with last commit
Oct 18, 2016
14751ae
Handle free transfer/transfer_duration in c++ ext
Oct 18, 2016
f4a080f
Fix walk_access error and make fasttrips complain
Oct 18, 2016
c333a24
Merged develop and addressed conflicts
Oct 19, 2016
b36e382
Finish merging develop into fares
Oct 19, 2016
f92d7ac
Bug fix: somehow this got dropped
Oct 19, 2016
22089b4
Bump wait needs trip id num
Oct 19, 2016
aee87c8
Keep fare transfer rules columns in output
Oct 19, 2016
03e9d8f
Update doc, comment
Oct 19, 2016
cdcb005
Bug fix: shape_dist_traveled is cumulative
Oct 20, 2016
75852ab
Add probability to StopState
Oct 20, 2016
028f3fd
Warn and enforce a minimum value of time
Oct 20, 2016
aac6ae6
Links with cost>max_cost are invalid for choosing
Oct 20, 2016
3a78924
In StopState, add a pointer to the FarePeriod
Oct 20, 2016
9e02040
Don't send lot/stop transfers to c++ extension
Oct 20, 2016
2702faa
Shorten some fare period names
Oct 21, 2016
7f77cbb
Add fare transfer logic to pathfinding
Oct 21, 2016
c260af0
Implement option to transfer_fare_ignore
Oct 22, 2016
b07131f
Pass initial cost/fare back to python
Oct 22, 2016
cc92111
Updated doc notes
Oct 22, 2016
9119a53
One more doc update
Oct 22, 2016
8ea4cc6
I can count
Oct 22, 2016
22a61f7
Merge develop into fares
Nov 9, 2016
9b26033
Fix merge error
Nov 9, 2016
2b0bc35
Assess fare attribute-based free transfers in pathfinding
Nov 10, 2016
933d964
Hyperlink::setupProbabilities() gets path so far
Nov 10, 2016
77eca46
Implemented fare transfers for path enumeration
Nov 14, 2016
5aded05
Fix crashing bugs
Nov 15, 2016
1fa664e
Rename column according to spec (transfer_fare)
Nov 15, 2016
451abd8
Add options to ignore transfer logic for path enum
Nov 15, 2016
465016c
Merge pull request #37 from MetropolitanTransportationCommission/develop
lmz Nov 15, 2016
6a7040e
Add transfer fare ignore options to runTest.py
Nov 16, 2016
3cbe04f
Merge branch 'fares' of https://github.com/MetropolitanTransportation…
Nov 16, 2016
ef3c67e
Add option to output debug columns
Nov 21, 2016
5ace663
Fare test script
Nov 21, 2016
8db74e9
Fix develop test in testFares.bat
Nov 22, 2016
d8c947b
testFares.bat bug fix
Nov 22, 2016
39ea190
Option to assume zone-to-zone fare symmetry
Nov 29, 2016
459515e
Merge branch 'fares' of https://github.com/MetropolitanTransportation…
Nov 29, 2016
30b2e8e
Handle null dist in transfers_ft.txt (assume zero)
Dec 3, 2016
8d3fac4
Direction is now a column in walk_access_ft.txt
Dec 3, 2016
1cb9b05
Merge develop into fares
Dec 3, 2016
0079106
Add direction column to test_network's walk_access_ft.txt
Dec 3, 2016
08f0f72
Merge develop into fares
Dec 9, 2016
7131be1
Merge branch 'develop' into fares
Dec 10, 2016
214064d
Bug fix: handle single large cost valid link
Dec 13, 2016
0174f18
Debug: widen mode field in debug trace log
Dec 13, 2016
967b698
Merge pull request #41 from MetropolitanTransportationCommission/develop
lmz Dec 15, 2016
5da4048
Create overlap example test
Jan 5, 2017
b4242be
Fix trace so it triggers
Jan 5, 2017
1118dd9
Handle allowed missing network fields
Jan 5, 2017
d953b13
Bug fix: split links have fare on first only
Jan 5, 2017
fd058d2
Bug fix: overlap penalty
Jan 6, 2017
7461fa1
Update fare test for network 1.10
Jan 13, 2017
457a7cd
create_tableau_path_map.py: Handle no trip_ids
Feb 7, 2017
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -143,7 +143,7 @@ There are three places where fares factor into the fast-trips.

1. During path-finding (C++ extension), fares get assessed as a cost onto links, which translate to generalized cost (minutes) via the traveler's value of time. Transfer fare rules here are complicated, because we don't know which is the next/previous fare, and we can only guess based on probabilities. The fare is estimated using [`Hyperlink::getFareWithTransfer()`](src/hyperlink.cpp). Turn this off using configuration option `transfer_fare_ignore`.
2. During path-enumeration (C++ extension), when the paths are being constructed by choosing links from the hyperpath graph, at the point where each link is added to the path, the fare *could be* adjusted with more certainty of the the path so far. This is currently not being done but it would be in [`Path::addLink()`](src/path.cpp) and/or [`Hyperlink::setupProbabilities()`](src/hyperlink.cpp). It may be valuable since it may affect the probability of the next link being chosen since the fare could be set with more certainty and then the potential link costs could be updated along with their pboabilities.
3. During path-enumeration (C++ extension), after the path is constructed, the trip cost is re-calculated at the end using [`Path::calculateCost()`](src/path.cpp). At this moment in the process, the path is complete and final, so the fare transfer rules are certain.
3. During path-enumeration (C++ extension), after the path is constructed, the trip cost is re-calculated at the end using [`Path::calculateCost()`](src/path.cpp). At this moment in the process, the path is complete and final, so the fare transfer rules are certain. The initial fare and cost are saved and passed back to python to show the effect of step 1.
4. During simulation (python), while the path is being adjusted due to vehicle times, the fares are calculated via [`Route.add_fares()`](fasttrips/Route.py). This is unlikely to change anything unless the fare periods changed due to the slow-down of vehicles -- so consider deprecating this in favor of using the pathfinding results? For now, it's a good test that the C++ code is working as expected; running with simulation off should result in identical fare and cost results from pathfinding and the (non-vehicle-updating) python simulation.

## Test Sample Input
Expand Down
5 changes: 4 additions & 1 deletion fasttrips/Assignment.py
Original file line number Diff line number Diff line change
Expand Up @@ -922,7 +922,10 @@ def find_trip_based_pathset(iteration, pathset, hyperpath, trace):

pathdict[path_num] = {}
pathdict[path_num][PathSet.PATH_KEY_COST ] = path_costs[path_num, 0]
pathdict[path_num][PathSet.PATH_KEY_PROBABILITY] = path_costs[path_num, 1]
pathdict[path_num][PathSet.PATH_KEY_FARE ] = path_costs[path_num, 1]
pathdict[path_num][PathSet.PATH_KEY_PROBABILITY] = path_costs[path_num, 2]
pathdict[path_num][PathSet.PATH_KEY_INIT_COST ] = path_costs[path_num, 3]
pathdict[path_num][PathSet.PATH_KEY_INIT_FARE ] = path_costs[path_num, 4]
# List of (stop_id, stop_state)
pathdict[path_num][PathSet.PATH_KEY_STATES ] = []

Expand Down
13 changes: 11 additions & 2 deletions fasttrips/Passenger.py
Original file line number Diff line number Diff line change
Expand Up @@ -509,7 +509,10 @@ def setup_passenger_pathsets(self, iteration, stops, trip_id_df, trips_df, modes
`pf_iteration` int64 iteration in which these paths were found
`pathnum` int64 the path number for the path within the pathset
`pf_cost` float64 the cost of the entire path
`pf_fare` float64 the fare of the entire path
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The difference of "cost" and "fare" is not initially obvious.
Perhaps we should use Util instead of cost? Other thoughts?

`pf_probability` float64 the probability of the path
`pf_initcost` float64 the initial cost of the entire path
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"INIT" something in this case is sort of like "FINAL" before "FINAL FINAL". It might be good at some point to change this { and its siblings } to be named after their stage. I.e. "Cost for Pathfinding" rather than "initial cost".

`pf_initfare` float64 the initial fare of the entire path
`description` object string representation of the path
================== =============== =====================================================================================================

Expand Down Expand Up @@ -608,7 +611,10 @@ def setup_passenger_pathsets(self, iteration, stops, trip_id_df, trips_df, modes
iteration,
pathnum,
pathset.pathdict[pathnum][PathSet.PATH_KEY_COST],
pathset.pathdict[pathnum][PathSet.PATH_KEY_PROBABILITY]
pathset.pathdict[pathnum][PathSet.PATH_KEY_FARE],
pathset.pathdict[pathnum][PathSet.PATH_KEY_PROBABILITY],
pathset.pathdict[pathnum][PathSet.PATH_KEY_INIT_COST],
pathset.pathdict[pathnum][PathSet.PATH_KEY_INIT_FARE]
])

link_num = 0
Expand Down Expand Up @@ -689,7 +695,10 @@ def setup_passenger_pathsets(self, iteration, stops, trip_id_df, trips_df, modes
Passenger.PF_COL_PF_ITERATION,
Passenger.PF_COL_PATH_NUM,
PathSet.PATH_KEY_COST,
PathSet.PATH_KEY_PROBABILITY ])
PathSet.PATH_KEY_FARE,
PathSet.PATH_KEY_PROBABILITY,
PathSet.PATH_KEY_INIT_COST,
PathSet.PATH_KEY_INIT_FARE])

pathset_links_df = pandas.DataFrame(linklist, columns=[\
Passenger.TRIP_LIST_COLUMN_PERSON_ID,
Expand Down
7 changes: 5 additions & 2 deletions fasttrips/PathSet.py
Original file line number Diff line number Diff line change
Expand Up @@ -100,8 +100,11 @@ class PathSet:
DIR_OUTBOUND = 1 #: Trips outbound from home have preferred arrival times
DIR_INBOUND = 2 #: Trips inbound to home have preferred departure times

PATH_KEY_COST = "pf_cost"
PATH_KEY_PROBABILITY = "pf_probability"
PATH_KEY_COST = "pf_cost" #: path cost according to pathfinder
PATH_KEY_FARE = "pf_fare" #: path fare according to pathfinder
PATH_KEY_PROBABILITY = "pf_probability" #: path probability according to pathfinder
PATH_KEY_INIT_COST = "pf_initcost" #: initial cost (in pathfinding, before path was finalized)
PATH_KEY_INIT_FARE = "pf_initfare" #: initial fare (in pathfinding, before path was finalized)
PATH_KEY_STATES = "states"

STATE_IDX_LABEL = 0 #: :py:class:`datetime.timedelta` instance
Expand Down
2 changes: 1 addition & 1 deletion fasttrips/Util.py
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ class Util:
]
DROP_PATHFINDING_COLUMNS = [
# pathfinding debugging
"pf_iteration","pf_A_time","pf_B_time","pf_linktime","pf_linkcost","pf_linkdist","pf_waittime","pf_linkfare"
"pf_iteration","pf_A_time","pf_B_time","pf_linktime","pf_linkcost","pf_linkdist","pf_waittime","pf_linkfare","pf_cost","pf_fare","pf_initcost","pf_initfare"
]

@staticmethod
Expand Down
9 changes: 6 additions & 3 deletions src/fasttrips.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -142,10 +142,10 @@ _fasttrips_find_pathset(PyObject *self, PyObject *args)
dims_double[1] = 8; // label_, deparr_time_, link_time_, link_cost_, link_dist_, cost_, arrdep_time_
PyArrayObject *ret_double = (PyArrayObject *)PyArray_SimpleNew(2, dims_double, NPY_DOUBLE);

// costs and probability
// cost, fare, probability, initial cost, initial fare
npy_intp dims_paths[2];
dims_paths[0] = pathset.size();
dims_paths[1] = 2;
dims_paths[1] = 5;
PyArrayObject *ret_paths = (PyArrayObject*)PyArray_SimpleNew(2, dims_paths, NPY_DOUBLE);

int ind = 0;
Expand All @@ -154,7 +154,10 @@ _fasttrips_find_pathset(PyObject *self, PyObject *args)
const fasttrips::Path& path = psi->first;

*(npy_double*)PyArray_GETPTR2(ret_paths, path_num, 0) = path.cost();
*(npy_double*)PyArray_GETPTR2(ret_paths, path_num, 1) = psi->second.probability_;
*(npy_double*)PyArray_GETPTR2(ret_paths, path_num, 1) = path.fare();
*(npy_double*)PyArray_GETPTR2(ret_paths, path_num, 2) = psi->second.probability_;
*(npy_double*)PyArray_GETPTR2(ret_paths, path_num, 3) = path.initialCost();
*(npy_double*)PyArray_GETPTR2(ret_paths, path_num, 4) = path.initialFare();

for (int link_num = 0; link_num < path.size(); ++link_num) {
*(npy_int32*)PyArray_GETPTR2(ret_int, ind, 0) = path_num;
Expand Down
42 changes: 35 additions & 7 deletions src/path.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -13,15 +13,21 @@ namespace fasttrips {
Path::Path() :
outbound_(false),
enumerating_(false),
fare_(0),
cost_(0),
capacity_problem_(false)
capacity_problem_(false),
initial_fare_(0),
initial_cost_(0)
{}

Path::Path(bool outbound, bool enumerating) :
outbound_(outbound),
enumerating_(enumerating),
fare_(0),
cost_(0),
capacity_problem_(false)
capacity_problem_(false),
initial_fare_(0),
initial_cost_(0)
{}

Path::~Path()
Expand All @@ -33,12 +39,26 @@ namespace fasttrips {
return links_.size();
}

// What's the cost of this path?
double Path::cost() const
{
return cost_;
}

double Path::fare() const
{
return fare_;
}

double Path::initialCost() const
{
return initial_cost_;
}

double Path::initialFare() const
{
return initial_fare_;
}

// Clear
void Path::clear()
{
Expand Down Expand Up @@ -120,7 +140,7 @@ namespace fasttrips {
trace_file << std::endl;

// delete this later
trace_file << "--------------- path_before ---- (cost " << cost_ << ")" << std::endl;
trace_file << "--------------- path_before ---- (cost " << cost_ << ", fare " << fare_ << ")" << std::endl;
print(trace_file, path_spec, pf);
trace_file << "--------------------------------" << std::endl;
}
Expand Down Expand Up @@ -220,6 +240,7 @@ namespace fasttrips {
}
}
cost_ += new_link.link_cost_;
fare_ += new_link.link_fare_;
new_link.cost_ = cost_;
links_.push_back( std::make_pair(stop_id, new_link) );

Expand All @@ -231,7 +252,7 @@ namespace fasttrips {

// this is excessive but oh well
if (links_.size() > 1) {
trace_file << "--------------- path so far ----" << (feasible ? " (feasible)" : " (infeasible)") << " (cost " << cost_ << ")" << std::endl;
trace_file << "--------------- path so far ----" << (feasible ? " (feasible)" : " (infeasible)") << " (cost " << cost_ << ", fare " << fare_ << ")" << std::endl;
print(trace_file, path_spec, pf);
trace_file << "--------------------------------" << std::endl;
}
Expand Down Expand Up @@ -289,9 +310,14 @@ namespace fasttrips {
// no stops - nothing to do
if (links_.size()==0) { return; }

// save aside the fare and cost
initial_fare_ = fare_;
initial_cost_ = cost_;

bool chrono_order = (!outbound_ && !enumerating_) || (outbound_ && enumerating_);
if (path_spec.trace_ && !hush) {
trace_file << "calculatePathCost: (chrono? " << (chrono_order ? "yes)" : "no)") << std::endl;
trace_file << "calculatePathCost: (chrono? " << (chrono_order ? "yes, " : "no,");
trace_file << " cost: " << initial_cost_ << ", fare: " << initial_fare_ << ")" << std::endl;
print(trace_file, path_spec, pf);
trace_file << std::endl;
}
Expand All @@ -305,6 +331,7 @@ namespace fasttrips {
int inc = chrono_order ? 1 : -1;

cost_ = 0;
fare_ = 0;
std::string last_fare_period;

// for free transfer calculations -- fare_period -> (first board time, board count)
Expand Down Expand Up @@ -412,11 +439,12 @@ namespace fasttrips {
first_trip = false;
}
cost_ += stop_state.link_cost_;
fare_ += stop_state.link_fare_;
stop_state.cost_ = cost_;
}

if (path_spec.trace_ && !hush) {
trace_file << " ==================================================> cost: " << cost_ << std::endl;
trace_file << " ==================================================> cost: " << cost_ << ", fare: " << fare_ << std::endl;
print(trace_file, path_spec, pf);
trace_file << std::endl;
}
Expand Down
15 changes: 13 additions & 2 deletions src/path.h
Original file line number Diff line number Diff line change
Expand Up @@ -34,9 +34,18 @@ namespace fasttrips {
private:
bool outbound_; ///< is this path outbound (preferred arrival) or inbound (preferred departure)?
bool enumerating_; ///< are we enumarating paths? or labeling?
double fare_; ///< Total fare of this path.
double cost_; ///< Cost of this path.
bool capacity_problem_; ///< Does this path have a capacity problem?

/// For debugging/investigation. When we first enumerate this path by adding links
/// via Path::addLink(), we're guessing at fares and costs. When we finally complete the
/// path and call Path::calculateCost(), we will know cost and fare with full information.
/// These variables are for saving our initial understanding to see how far off we were.
/// These are set in calculateCost().
double initial_fare_; ///< Initial total fare
double initial_cost_; ///< Initial cost

/// The links that make up this path (stop id, stop states)
/// They are in origin to destination order for outbound trips,
/// and destination to origin order for inbound trips.
Expand All @@ -52,9 +61,11 @@ namespace fasttrips {

/// How many links are in this path?
size_t size() const;
/// What's the cost of this path?
double cost() const;
/// Clear
double fare() const;
double initialCost() const; ///< initial understanding of the cost before path was finalized
double initialFare() const; ///< initial understanding of the fare before path was finalized

void clear();

/// Accessors
Expand Down