Google ortools CVRP - different distance matrix by vehicle - python

In ortools, I know you can run the CVRP with different capacities per vehicle. However, can you pass a different distance matrix based upon the vehicle? For example, two cities may be 1000 miles apart, but it may be much faster to get there by airplane than by automobile, so in doing CVRP work I may wish to pass a time matrix, not an actual distance matrix. That time matrix would be different based upon the vehicle type.

Should be close to this:
callback_indices = []
for vehicle_idx in range(data['n_vehicles']):
def vehicle_callback(from_index, to_index, i=vehicle_idx):
from_node = manager.IndexToNode(from_index)
to_node = manager.IndexToNode(to_index)
return data['vehicle_costs'][i] * data['time_matrices'][i][from_node][to_node]
callback_index = routing.RegisterTransitCallback(vehicle_callback)
callback_indices.append(callback_index)
routing.AddDimensionWithVehicleTransits(
callback_indices,
0,
max,
False,
'DimensionName')

You can pass a vector/list of evaluators.
Here is the C++ API/

Related

Simple API Rest python script takes a lot of time (or doesn't end)

For a business process I need to calculate driving distance between 1 origin and 30 k destinations.
I get both origins and destinations coordinates from a Google Sheet. Destinations is a matrix (approx 100 x 30).
I'm using HERE api to calculate the distance.
The result should be the same destinations matrix but with the distance (in the same order as the destinations coordinates).
This is the part of the script that calculates the distance and, I think, the one which lasts a lot:
distance= []
distance= pd.DataFrame(distance)
for row in destinations.itertuples():
a= row[1:]
distance1 = []
for column in a:
try:
args = {'waypoint0': 'geo!'+origins, 'waypoint1': 'geo!'+column, 'mode': 'fastest;truck'}
qstr = urlencode(args)
url = "https://route.ls.hereapi.com/routing/7.2/calculateroute.json?apiKey=xxxx" + qstr
response = urllib.request.urlopen(url)
dist = json.loads(response.read())['response']['route'][0]['leg'][0]['length']/1000
except Exception:
dist = 10000
distance1.append(dist)
distance2 = pd.DataFrame(distance1)
distance2 = distance2.T
distance = distance.append(distance2)
Does anyone think of a better way to make the script to actually finish?
Thanks!!
The logic looks pretty much accurate. If you need to limit the loop count, please check Large-Scale Matrix Routing API if it aligns with the use case.
The Large-Scale Matrix Routing service is a HTTP JSON API for calculating routing matrices with a large number of start and destinations points (e.g. 10,000 x 10,000).
For more details, please refer the following doc :
https://developer.here.com/documentation/large-matrix/api-reference-swagger.html
Note : please remove appKey from the shared code snippet

Python GPS Radius / Distance

I have a list in a "file.txt" with GPS coordinates, in it I have the format "latitude, longitude". I will try to explain the example or code I would like in Python, language I try learning.
GPS = current position + RADIUS / MARGIN = 0.9 (900 meters)
The current GPS position would be "collected" from the serial in "/dev/ttyS0", using a GPS module connected to Raspberry Pi3 ( Raspbian ).
I need to know if my current position (using RADIUS / MARGIN of 900 meters) is TRUE or FALSE according to the list of coordinates that i have in the "file.txt".
file.txt
-34.61517, -58.38124
-34.61517, -58.38124
-34.61527, -58.38123
-34.61586, -58.38121
-34.61647, -58.38118
-34.61762, -58.38113
-34.61851, -58.38109
-34.61871, -58.38109
-34.61902, -58.38108
-34.61927, -58.38108
-34.61953, -58.38108
-34.61975, -58.38106
-34.61979, -58.38112
-34.6198, -58.38113
-34.61981, -58.38115
-34.61983, -58.38116
-34.61986, -58.38117
-34.61993, -58.38118
-34.62011, -58.38119
-34.62037, -58.38121
-34.62059, -58.38122
-34.62075, -58.38122
-34.6209, -58.38122
-34.62143, -58.38117
-34.62157, -58.38115
-34.62168, -58.38115
-34.6218, -58.38114
-34.62191, -58.38115
-34.62199, -58.38116
-34.62206, -58.38119
-34.62218, -58.38123
-34.62227, -58.38128
-34.62234, -58.38134
-34.62241, -58.3814
-34.62249, -58.38149
-34.62254, -58.38156
-34.62261, -58.38168
-34.62266, -58.38179
-34.62273, -58.38194
-34.62276, -58.38201
-34.62283, -58.38238
-34.62282, -58.38261
-34.62281, -58.38291
-34.62281, -58.38309
-34.62281, -58.38313
-34.62281, -58.3836
-34.62281, -58.38388
-34.62282, -58.38434
-34.62282, -58.38442
-34.62283, -58.3845
-34.62283, -58.38463
-34.62285, -58.38499
-34.62287, -58.3853
-34.6229, -58.38581
-34.62291, -58.38589
-34.62292, -58.38597
-34.62297, -58.38653
-34,623, -58,3868
-34.62303, -58.3871
-34,623, -58,38713
-34.62299, -58.38714
-34.62298, -58.38715
-34.62298, -58.38716
-34.62297, -58.38717
-34.62297, -58.38728
-34.62297, -58.38735
-34.62298, -58.38755
-34.62299, -58.3877
-34.62305, -58.38829
-34.62308, -58.38848
-34.6231, -58.38865
-34.62311, -58.38874
-34.62316, -58.3892
-34.62318, -58.38933
Sample Image 1
Is this possible in Python?
Thanks in advance (:
I don't understand if it's exactly this that you wanted to know. This solution refers to the problem "Given a point and my current position, is my distance from that point minor than a specific value?".
If that's the case and distances are small enough (less than 1 km), you can use the Pythagorean theorem:
distance = c*6371*180/pi*sqrt((currentPosition.lat - targetLat)**2 +
(currentPosition.long - targetLong)**2)
where c is a coefficient you have to find in your zone (in Italy it's 0.8 for example, just divide the real value - you can obtain it with Google Maps - by the result you get setting c at 1), 6371 is the Earth's radius and pi is 3.14159; then you can just compare the distance with the maximum distance you want with
distance < maxDistance
In this case, maxDistance is 0.9 .
Notice that this formula is approximated, but, given the low distances you're dealing with, it can be accurate enough. You should use trigonometry if distances are higher (for example that doesn't make sense if you want to compare two points in two different continents). In that case, this is the formula you should use - the great circle formula:
distance = 6371*acos(sin(lat1)*sin(lat2)+cos(lat1)cos(lat2)cos(long1-long2))
where (lat1,long1) and (lat2,long2) are the spherical cohordinates of the two points you are measuring. Then compare the distance with the maxDistance like the previous expression, and you're done.
If you want to solve the problem for a set of points in a txt file, just read those values and iterate over them using a for loop or a for-each.
Reference https://en.wikipedia.org/wiki/Great-circle_distance for further details.

Longitude of lunar nodes using Skyfield

I am trying to find out the longitude of ascending/descending moon nodes using Skyfield but unable to find any reference in documentation. Is it possible?
Also do any of the JPL Files provide this data already?
Update:
Skyfield’s almanac module now supports this computation directly! See: Lunar Nodes
Original answer, for those wanting these details:
It is easy to at least find them relative to the J2000 ecliptic — which might be fine for dates far from the year 2000 as well, since I think that only the definition of ecliptic longitude changes with the passing years, but not latitude (which is what the nodes care about)?
In any case, you'd precede like this. Let's say you want the ascending node. It must happen within the next 30 days, because that's more than a full orbit of the Moon, so let's look for the day on which the latitude of the Moon passes from negative to positive:
from skyfield.api import load
ts = load.timescale()
eph = load('de421.bsp')
earth = eph['earth']
moon = eph['moon']
t = ts.utc(2018, 1, range(14, 14 + 30))
lat, lon, distance = earth.at(t).observe(moon).ecliptic_latlon()
angle = lat.radians
for i in range(len(angle)):
if angle[i] < 0 and angle[i+1] > 0:
break
print(t[i].utc_jpl(), angle[i])
print(t[i+1].utc_jpl(), angle[i+1])
The result is the discovery that the ascending node must happen sometime on January 31st:
A.D. 2018-Jan-31 00:00:00.0000 UT -0.0188679292421
A.D. 2018-Feb-01 00:00:00.0000 UT 0.00522392011676
To find the exact time, install the SciPy library, and ask one of its solvers to find the exact time at which the value reaches zero. You just have to create a little function that takes a number and returns a number, by converting the number to a Skyfield time and then the angle back to a plain number:
from scipy.optimize import brentq
def f(jd):
t = ts.tt(jd=jd)
angle, lon, distance = earth.at(t).observe(moon).ecliptic_latlon()
return angle.radians
node_t = brentq(f, t[i].tt, t[i+1].tt)
print(ts.tt(jd=node_t).utc_jpl())
The result should be the exact moment of the node:
A.D. 2018-Jan-31 18:47:54.5856 UT

How to programatically group lap times into teams to minimize difference?

Given the following (arbitrary) lap times:
John: 47.20
Mark: 51.14
Shellie: 49.95
Scott: 48.80
Jack: 46.60
Cheryl: 52.70
Martin: 57.65
Karl: 55.45
Yong: 52.30
Lynetta: 59.90
Sueann: 49.24
Tempie: 47.88
Mack: 51.11
Kecia: 53.20
Jayson: 48.90
Sanjuanita: 45.90
Rosita: 54.43
Lyndia: 52.38
Deloris: 49.90
Sophie: 44.31
Fleta: 58.12
Tai: 61.23
Cassaundra: 49.38 
Oren: 48.39
We're doing a go-kart endurance race, and the idea, rather than allowing team picking is to write a tool to process the initial qualifying times and then spit out the closest-matched groupings.
My initial investigation makes me feel like this is a clique graphing type situation, but having never played with graphing algorithms I feel rather out of my depth.
What would be the fastest/simplest method of generating groups of 3 people with the closest overall average lap time, so as to remove overall advantage/difference between them?
Is this something I can use networkx to achieve, and if so, how would I best define the graph given the dataset above?
When you're faced with a problem like this, one approach is always to leverage randomness.
While other folks say they think X or Y should work, I know my algorithm will converge to at least a local maxima. If you can show that any state space can be reached from any other via pairwise swapping (a property that is true for, say, the Travelling Salesperson Problem), then the algorithm will find the global optimum (given time).
Further, the algorithm attempts to minimize the standard deviation of the average times across the groups, so it provides a natural metric of how good an answer you're getting: Even if the result is non-exact, getting a standard deviation of 0.058 is probably more than close enough for your purposes.
Put another way: there may be an exact solution, but a randomized solution is usually easy to imagine, doesn't take long to code, can converge nicely, and is able to produce acceptable answers.
#!/usr/bin/env python3
import numpy as np
import copy
import random
data = [
(47.20,"John"),
(51.14,"Mark"),
(49.95,"Shellie"),
(48.80,"Scott"),
(46.60,"Jack"),
(52.70,"Cheryl"),
(57.65,"Martin"),
(55.45,"Karl"),
(52.30,"Yong"),
(59.90,"Lynetta"),
(49.24,"Sueann"),
(47.88,"Tempie"),
(51.11,"Mack"),
(53.20,"Kecia"),
(48.90,"Jayson"),
(45.90,"Sanjuanita"),
(54.43,"Rosita"),
(52.38,"Lyndia"),
(49.90,"Deloris"),
(44.31,"Sophie"),
(58.12,"Fleta"),
(61.23,"Tai"),
(49.38 ,"Cassaundra"),
(48.39,"Oren")
]
#Divide into initial groupings
NUM_GROUPS = 8
groups = []
for x in range(NUM_GROUPS): #Number of groups desired
groups.append(data[x*len(data)//NUM_GROUPS:(x+1)*len(data)//NUM_GROUPS])
#Ensure all groups have the same number of members
assert all(len(groups[0])==len(x) for x in groups)
#Get average time of a single group
def FitnessGroup(group):
return np.average([x[0] for x in group])
#Get standard deviation of all groups' average times
def Fitness(groups):
avgtimes = [FitnessGroup(x) for x in groups] #Get all average times
return np.std(avgtimes) #Return standard deviation of average times
#Initially, the best grouping is just the data
bestgroups = copy.deepcopy(groups)
bestfitness = Fitness(groups)
#Generate mutations of the best grouping by swapping two randomly chosen members
#between their groups
for x in range(10000): #Run a large number of times
groups = copy.deepcopy(bestgroups) #Always start from the best grouping
g1 = random.randint(0,len(groups)-1) #Choose a random group A
g2 = random.randint(0,len(groups)-1) #Choose a random group B
m1 = random.randint(0,len(groups[g1])-1) #Choose a random member from group A
m2 = random.randint(0,len(groups[g2])-1) #Choose a random member from group B
groups[g1][m1], groups[g2][m2] = groups[g2][m2], groups[g1][m1] #Swap 'em
fitness = Fitness(groups) #Calculate fitness of new grouping
if fitness<bestfitness: #Is it a better fitness?
bestfitness = fitness #Save fitness
bestgroups = copy.deepcopy(groups) #Save grouping
#Print the results
for g in bestgroups:
for m in g:
print("{0:15}".format(m[1]), end='')
print("{0:15.3f}".format(FitnessGroup(g)), end='')
print("")
print("Standard deviation of teams: {0:.3f}".format(bestfitness))
Running this a couple of times gives a standard deviation of 0.058:
Cheryl Kecia Oren 51.430
Tempie Mark Karl 51.490
Fleta Deloris Jack 51.540
Lynetta Scott Sanjuanita 51.533
Mack Rosita Sueann 51.593
Shellie Lyndia Yong 51.543
Jayson Sophie Tai 51.480
Martin Cassaundra John 51.410
Standard deviation of teams: 0.058
If I understand correctly, just sort the list of times and group the first three, next three, up through the top three.
EDIT: I didn't understand correctly
So, the idea is to take the N people and group them into N/3 teams, making the average times N/3 teams [rather than the 3 people within each team as I mistakenly interpreted] as close as possible. In this case, I think you could still start by sorting the N drivers in decreasing order of times. Then, initialize an empty list of N/3 teams. Then for each driver in decreasing order of lap time, assign them to the team with the smallest current total lap time (or one of these teams, in case of ties). This is a variant of a simple bin packing algorithm.
Here is a simple Python implementation:
times = [47.20, 51.14, 49.95, 48.80, 46.60, 52.70, 57.65, 55.45, 52.30, 59.90, 49.24, 47.88, 51.11, 53.20, 48.90, 45.90, 54.43, 52.38, 49.90, 44.31, 58.12, 61.23, 49.38, 48.39]
Nteams = len(times)/3
team_times = [0] * Nteams
team_members = [[]] * Nteams
times = sorted(times,reverse=True)
for m in range(len(times)):
i = team_times.index(min(team_times))
team_times[i] += times[m]
team_members[i] = team_members[i] + [m]
for i in range(len(team_times)):
print(str(team_members[i]) + ": avg time " + str(round(team_times[i]/3,3)))
whose output is
[0, 15, 23]: avg time 51.593
[1, 14, 22]: avg time 51.727
[2, 13, 21]: avg time 51.54
[3, 12, 20]: avg time 51.6
[4, 11, 19]: avg time 51.48
[5, 10, 18]: avg time 51.32
[6, 9, 17]: avg time 51.433
[7, 8, 16]: avg time 51.327
(Note that the team members numbers refer to them in descending order of lap time, starting from 0, rather than to their original ordering).
One issue with this is that if the times varied dramatically, there is no hard restriction to make the number of players on each team exactly 3. However, for your purposes, maybe that's OK, if it makes the relay close, and its probably a rare occurrence when the spread in times is much less than the average time.
EDIT
If you do just want 3 players on each team, in all cases, then the code can be trivially modified to at each step find the team with the least total lap time that doesn't already have three assigned players. This requires a small modification in the main code block:
times = sorted(times,reverse=True)
for m in range(len(times)):
idx = -1
for i in range(Nteams):
if len(team_members[i]) < 3:
if (idx == -1) or (team_times[i] < team_times[idx]):
idx = i
team_times[idx] += times[m]
team_members[idx] = team_members[idx] + [m]
For the example problem in the question, the above solution is of course identical, because it did not try to fit more or less than 3 players per team.
The following algorithm appears to work pretty well. It takes the fastest and slowest people remaining and then finds the person in the middle so that the group average is closest to the global average. Since the extreme values are being used up first, the averages at the end shouldn't be that far off despite the limited selection pool.
from bisect import bisect
times = sorted([47.20, 51.14, 49.95, 48.80, 46.60, 52.70, 57.65, 55.45, 52.30, 59.90, 49.24, 47.88, 51.11, 53.20, 48.90, 45.90, 54.43, 52.38, 49.90, 44.31, 58.12, 61.23, 49.38, 48.39])
average = lambda c: sum(c)/len(c)
groups = []
average_time = average(times)
while times:
group = [times.pop(0), times.pop()]
# target value for the third person for best average
target = average_time * 3 - sum(group)
index = min(bisect(times, target), len(times) - 1)
# adjust if the left value is better than the right
if index and abs(target - times[index-1]) < abs(target - times[index]):
index -= 1
group.append(times.pop(index))
groups.append(group)
# [44.31, 61.23, 48.9]
# [45.9, 59.9, 48.8]
# [46.6, 58.12, 49.9]
# [47.2, 57.65, 49.38]
# [47.88, 55.45, 51.14]
# [48.39, 54.43, 51.11]
# [49.24, 53.2, 52.3]
# [49.95, 52.7, 52.38]
The sorting and the iterated binary search are both O(n log n), so the total complexity is O(n log n). Unfortunately, expanding this to larger groups might be tough.
The simplest would probably be to just create 3 buckets--a fast bucket, a medium bucket, and a slow bucket--and assign entries to the buckets by their qualifying times.
Then team together the slowest of the slow, the fastest of the fast, and the median or mean of the mediums. (Not sure whether median or mean is the best choice off the top of my head.) Repeat until you're out of entries.

Efficient (spatial) Network Neighbors?

I would like to identify the Kth order neighbors of an edge on a network, specifically the neighbors of a large set of streets. For example, I have a street that I'm interested in looking at, call this the focal street. For each focal street I want to find the streets that share an intersection, these are the first order neighbors. Then for each of those streets that share an intersection with the focal street I would like to find their neighbors (these would be the second order neighbors), and so on...
Calculating the first order neighbors using ArcGIS' geoprocessing library (arcpy) took 6+ hours, second order neighbors are taking 18+ hours. Needless to say I want to find a more efficient solution. I have created a python dictionary which is keyed on each street and contains the connected streets as values. For example:
st2neighs = {street1: [street2, street3, street5], street2: [street1, street4], ...}.
street 1 is connected to street 2, 3, 5; street 2 is connected to street 1 and 4; etc.. There are around 30,000 streets in the study area most have fewer than 7 connected streets. A pickled version of the data used in the code below IS HERE.
I assumed that knowing the first order neighbors would allow me to efficiently trace the higher order neighbors. But the following code is providing incorrect results:
##Select K-order neighbors from a set of sampled streets.
##saves in dictionary format such that
##the key is the sampled street and the neighboring streets are the values
##################
##IMPORT LIBRARIES
##################
import random as random
import pickle
#######################
##LOAD PICKLED DATA
#######################
seg_file = open("seg2st.pkl", "rb")
st_file = open("st2neighs.pkl", "rb")
seg2st = pickle.load(seg_file)
st2neigh = pickle.load(st_file)
##################
##DEF FUNCTIONS
##################
##Takes in a dict of segments (key) and their streets (values).
##returns the desired number of sampled streets per segment
##returns a dict keyed segment containing tlids.
def selectSample(seg2st, nbirths):
randSt = {}
for segK in seg2st.iterkeys():
ranSamp = [int(random.choice(seg2st[segK])) for i in xrange(nbirths)]
randSt[segK] = []
for aSamp in ranSamp:
randSt[segK].append(aSamp)
return randSt
##Takes in a list of all streets (keys) and their first order neighbors (values)
##Takes in a list of sampled streets
##returns a dict of all sampled streets and their neighbors.
##Higher order selections should be possible with findMoreNeighbors
##logic is the same but replacing sample (input) with output from
##findFirstNeighbors
def findFirstNeighbors(st2neigh, sample):
compSts = {}
for samp in sample.iterkeys():
for rSt in sample[samp]:
if rSt not in compSts:
compSts[rSt] = []
for compSt in st2neigh[rSt]:
compSts[rSt].append(compSt)
return compSts
def findMoreNeighbors(st2neigh, compSts):
for aSt in compSts:
for st in compSts[aSt]:
for nSt in st2neigh[st]:
if nSt not in compSts[aSt]:
compSts[aSt].append(nSt)
moreNeighs = compSts
return moreNeighs
#####################
##The nHoods
#####################
samp = selectSample(seg2st, 1)
n1 = findFirstNeighbors(st2neigh, samp)
n2 = findMoreNeighbors(st2neigh, n1)
n3 = findMoreNeighbors(st2neigh, n2)
#####################
##CHECK RESULTS
#####################
def checkResults(neighList):
cntr = {}
for c in neighList.iterkeys():
cntr[c] = 0
for a in neighList[c]:
cntr[c] += 1
return cntr
##There is an error no streets **should** have 2000+ order neighbors
c1 = checkResults(n1)
c2 = checkResults(n2)
c3 = checkResults(n3)
Help!
It seems to me like what you want to implement is the following: http://en.wikipedia.org/wiki/Composition_of_relations
It is actually a straight-forward algorithm. Let R be the relation "is a first order neighbor", therefore if two streets x,y are in R then x is a first order neighbor of y. So, for second order neighbor you want to compute R composed with R. For third order neighbors (R composed R) composed R. And so on.

Categories