VRP Implementing cabotage constraints

51 views
Skip to first unread message

Tue Boesen

unread,
Aug 2, 2024, 9:08:37 AM8/2/24
to or-tools-discuss
## Cabotage rules summarized
In EU (not sure about the rest of the world), we have something called [cabotage rules](https://transport.ec.europa.eu/transport-modes/road/mobility-package-i/market-rules/rules-cabotage-applicable-21-february-2022_en) which allows trucks from foreign countries to do some limited amount of national pickup and deliveries. The rules are rather convoluted, but somewhat simplified the rules are:

- 3 deliveries in a country within 7 days, provided it entered the country with a load.
- 1 delivery in a country within 3 days, provided it entered the country without a load.
- In either case, any kind of cabotage driving triggers a cooldown period of 4 days after exiting the country, where the driver cannot do any cabotage driving in that country.
- Before cabotage driving can commence the driver must have completely emptied the truck in the country. (meaning they cannot pickup cabotage load while still carrying the stuff they entered the country with)
 

I am not sure how to implement something like this, so I would be curious about any kind of feedback on how this might be achieved, or even just partially achieved.
## My own idea towards a solution

Start by making each pickup and delivery have an associated country. Say for instance we have 4 deliveries:
```
data['n_deliveries'] = 4
data['pickups_deliveries'] = [[0, 1],[2,3],[4,5],[6,7]]
data['pd_denmark'] = [[0, 1],[1,1],[1,1],[1,0]] #So the first pickup and last delivery and not in denmark, while the rest are.
```
In this case we only track whether vehicles are allowed to do cabotage in Denmark.

Based on this I can create a cabotage matrix:
```
M_c =[
[ 0 1 1 1 1 1 1 0 0 0]
[-1 0 0 0 0 0 0 -1 0 0]
[-1 0 0 0 0 0 0 -1 0 0]
[-1 0 0 0 0 0 0 -1 0 0]
[-1 0 0 0 0 0 0 -1 0 0]
[-1 0 0 0 0 0 0 -1 0 0]
[-1 0 0 0 0 0 0 -1 0 0]
[ 0 1 1 1 1 1 1 0 0 0]
[ 0 0 0 0 0 0 0 0 0 0]
[ 0 0 0 0 0 0 0 0 0 0]]
```
which tells whether moving from index i to j, enables or disables cabotage.
`is_cabotage_delivery = [0, 1, 1, 0]
`Finally I need to know whether a truck is carrying a load, This is relatively simple since I only do pickup and deliveries, hence I can just create the following vector:
`load_carrying = [1, -1, 1, -1, 1, -1, 1, -1]
`
To use these variables in the actual VRP problem, my thinking is to define a bunch of extra dimensions:

- cooldown_dim (which tracks whether a vehicle is now on cooldown) (possible values true/false, might later extend this to a time dimension that actually ticks down again, but for now we keep it simple)
- cabotage_allowed_dim (which tracks whether a vehicle is currently allowed to do cabotage) (so possible values should be true/false)
- cabotage_limit_dim (which tracks how many cabotage drives a vehicle could do (this does not track whether cabotage is allowed), but merely whether the vehicle is currently in Denmark, and whether it entered with a load) (so possible values should be (0, 1 or 3))
- cabotage_drives_done_dim (tracks the amount of cabotage drives this driver has done) (possible values should be (0,1,2,3))
- load_dim (tracks how many delivery jobs the vehicle currently have, aka how many pickups it has picked up, and not yet delivered.) possible values (0...10000)


Then based on these I can add constraints:

`AddConstraint(cabotage_drives_done_dim.CumulVar(pickup_index) <= cabotage_limit_dim.CumulVar(pickup_index)) # So enforce that you cannot do more cabotage drives than allowed.`

```
def cooldown_dim:
Starts as False (maybe go to a special location to set it to false if that is needed)
True if vehicle leaves the country AND cabotage_drives_done > 0
Unchanged otherwise

def cabotage_allowed_dim:
True if load == 0 and cabotage_limit_dim > 0 and cooldown_dim == False
False if vehicle leaves the country
Unchanged for anything else.

def cabotage_limit_callback(from_index, to_index):
from_node = manager.IndexToNode(from_index)
to_node = manager.IndexToNode(to_index)
loads = load_dim.CumulVar(to_index)
if loads > 0:
x = 3
else:
x = 1
return x*M_cabotage_limit[from_node][to_node]
```

## Questions

- Is it possible to get the value of another dimension in a callback of a dimension? (As I try to do in the above example where I try to get the value of load_dim inside cabotage_limit_callback)
- Is it possible to get its own value during a callback?

I crossposted this question here: https://github.com/google/or-tools/discussions/4338

Reply all
Reply to author
Forward
0 new messages