-
Notifications
You must be signed in to change notification settings - Fork 356
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add bike team pursuit benchmark (rewriting 1153) (#1387)
* New problem function: Team Pursuit Track Cycling * submitting changes for team pursuit cycling problem * adding cycling experiment * fixing linting issues * headers * fix * fix * fix * fix * Update nevergrad/benchmark/experiments.py Co-authored-by: Jérémy Rapin <jrapin.github@gmail.com> * testcycling * Bike2 (#1390) * testcycling * fix * fix * Update nevergrad/benchmark/experiments.py Co-authored-by: Jérémy Rapin <jrapin.github@gmail.com> * Update nevergrad/benchmark/experiments.py Co-authored-by: Jérémy Rapin <jrapin.github@gmail.com> * Update nevergrad/functions/cycling/cycling.py Co-authored-by: Jérémy Rapin <jrapin.github@gmail.com> * Update nevergrad/functions/cycling/__init__.py Co-authored-by: Jérémy Rapin <jrapin.github@gmail.com> * fix Co-authored-by: Ryan Kroon <rkroon19@gmail.com> Co-authored-by: Jérémy Rapin <jrapin.github@gmail.com>
1 parent
cede21a
commit 07f6ec0
Showing
11 changed files
with
681 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Binary file added
BIN
+714 KB
nevergrad/functions/cycling/Extending Nevergrad, an Optimisation Platform.pdf
Binary file not shown.
Binary file added
BIN
+11 KB
nevergrad/functions/cycling/Extending Nevergrad, an Optimisation Platform.xlsx
Binary file not shown.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
# Copyright (c) Meta Platforms, Inc. and affiliates. All Rights Reserved. | ||
# | ||
# This source code is licensed under the MIT license found in the | ||
# LICENSE file in the root directory of this source tree. | ||
|
||
from .cycling import Cycling as Cycling |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,109 @@ | ||
# Copyright (c) Meta Platforms, Inc. and affiliates. All Rights Reserved. | ||
# | ||
# This source code is licensed under the MIT license found in the | ||
# LICENSE file in the root directory of this source tree. | ||
|
||
# Creator: Ryan Kroon, Email: rkroon19@gmail.com | ||
# Accompanying paper: Extending Nevergrad, an Optimisation Platform (in directory) | ||
|
||
# Evolving Pacing Strategies for Team Pursuit Track Cycling | ||
# Markus Wagner, Jareth Day, Diora Jordan, Trent Kroeger, Frank Neumann | ||
# Advances in Metaheuristics, Vol. 53, Springer, 2013. | ||
# https://link.springer.com/chapter/10.1007/978-1-4614-6322-1_4 | ||
# Java code: https://cs.adelaide.edu.au/~markus/pub/TeamPursuit.zip | ||
|
||
import numpy as np | ||
import typing as tp | ||
from .. import base | ||
from nevergrad.parametrization import parameter as p | ||
from .womensteampursuit import womensteampursuit | ||
from .mensteampursuit import mensteampursuit | ||
|
||
|
||
class Cycling(base.ExperimentFunction): | ||
|
||
""" | ||
Team Pursuit Track Cycling Simulator. | ||
Parameters | ||
---------- | ||
strategy: int | ||
Refers to Transition strategy or Pacing strategy (or both) of the cyclists; this depends on the strategy length. | ||
Strategy length can only be 30, 31, 61, 22, 23, 45. | ||
30: mens transition strategy. | ||
31: mens pacing strategy. | ||
61: mens transition and pacing strategy combined. | ||
22: womens transition strategy. | ||
23: womens pacing strategy. | ||
45: womens transition and pacing strategy combined. | ||
""" | ||
|
||
def __init__(self, strategy_index: int = 30) -> None: | ||
|
||
# Preliminary stuff. | ||
women = strategy_index in [22, 23, 45] | ||
param_transition = p.TransitionChoice([False, True], repetitions=22 if women else 30) | ||
init = (400 if women else 550) * np.ones(23 if women else 31) | ||
gender = "Women" if women else "Men" | ||
param_pacing = p.Array(init=init, lower=200, upper=1200) | ||
target_function = team_pursuit_simulation | ||
|
||
# optimising transition strategy | ||
if strategy_index in (22, 30): | ||
parameter: tp.Any = param_transition | ||
parameter.set_name(f"{gender} Transition strategy") | ||
parameter.set_name("") | ||
|
||
# optimising pacing strategy | ||
elif strategy_index in (23, 31): | ||
init = 550 * np.ones(strategy_index) | ||
parameter = param_pacing | ||
parameter.set_name(f"{gender} Pacing strategy") | ||
parameter.set_name("") | ||
|
||
# optimising pacing and transition strategies | ||
elif strategy_index in (45, 61): | ||
init = 0.5 * np.ones(strategy_index) # type: ignore | ||
parameter = p.Instrumentation(transition=param_transition, pacing=param_pacing) | ||
parameter.set_name(f"{gender} Pacing and Transition") | ||
# For some reason the name above does not work... | ||
# It generates a very long name like | ||
# "(Wom|M)ens Pacing and Transition:[0.5,... | ||
parameter.set_name("") | ||
|
||
# error raised if invalid strategy length given | ||
else: | ||
raise ValueError("Strategy length must be any of 22, 23, 30, 31, 45, 61") | ||
super().__init__(target_function, parameter) | ||
# assert len(self.parametrization.sample().value) == strategy_index, f"{len(self.parametrization.sample().value)} != {strategy_index} (init={init} with len={len(init)})." | ||
|
||
|
||
def team_pursuit_simulation(x) -> float: | ||
|
||
if len(x) == 2: # Let us concatenate the instrumentation. | ||
pacing = x[1]["pacing"] | ||
transition = x[1]["transition"] | ||
elif len(x) in (30, 22): | ||
transition = x | ||
pacing = [550 if len(x) == 30 else 400] * (len(x) + 1) | ||
elif len(x) in (31, 23): | ||
pacing = x | ||
transition = [True, False] * ((len(x) - 1) // 2) | ||
else: | ||
raise ValueError(f"len(x) == {len(x)}") | ||
|
||
# What is this ? | ||
# for i in range(0, len(pacing_strategy)): | ||
# pacing_strategy[i] = 100 * pacing_strategy[i] + 200 | ||
|
||
# Create a mensteampursuit oor womensteampursuit object. | ||
team_pursuit: tp.Any = womensteampursuit() if len(pacing) == 23 else mensteampursuit() | ||
assert len(pacing) in (23, 31) | ||
|
||
# Simulate event with the default strategies | ||
result = team_pursuit.simulate(transition, pacing) | ||
|
||
if result.get_finish_time() > 10000: # in case of inf | ||
return 10000 | ||
else: | ||
return float(result.get_finish_time()) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,153 @@ | ||
# Copyright (c) Meta Platforms, Inc. and affiliates. All Rights Reserved. | ||
# | ||
# This source code is licensed under the MIT license found in the | ||
# LICENSE file in the root directory of this source tree. | ||
|
||
from .teampursuit import teampursuit | ||
import math | ||
|
||
|
||
class Cyclist: | ||
|
||
# static class variables | ||
max_power = 1200 | ||
min_power = 200 | ||
drag_coeffiecient = 0.65 | ||
mechanical_efficiency = 0.977 | ||
bike_mass = 7.7 | ||
fatigue_level = 0 | ||
|
||
def __init__(self, height, weight, mean_maximum_power, event, start_position, gender): | ||
|
||
self.height = height | ||
self.weight = weight | ||
self.mean_maximum_power = mean_maximum_power | ||
self.event = event | ||
self.start_position = start_position | ||
self.position = start_position | ||
self.gender = gender | ||
self.current_velocity = 0.0 | ||
self.fatigue_level = 0 | ||
|
||
self.update_cda() | ||
self.update_total_energy() | ||
self.remaining_energy = self.total_energy | ||
|
||
def set_pace(self, power): | ||
fatigue_factor = 1 - (0.01 * self.fatigue_level) | ||
|
||
delta_ke = ( | ||
(power * self.mechanical_efficiency * fatigue_factor) | ||
- (self.coefficient_drag_area * 0.5 * self.event.air_density * math.pow(self.current_velocity, 3)) | ||
- ( | ||
teampursuit.friction_coefficient | ||
* (self.weight + self.bike_mass) | ||
* teampursuit.gravitational_acceleration | ||
* self.current_velocity | ||
) | ||
) * teampursuit.time_step | ||
|
||
new_velocity = math.pow( | ||
((2 * delta_ke / (self.weight + self.bike_mass)) + math.pow(self.current_velocity, 2)), 0.5 | ||
) | ||
acceleration = new_velocity - self.current_velocity | ||
distance = (self.current_velocity * teampursuit.time_step) + ( | ||
0.5 * acceleration * math.pow(teampursuit.time_step, 2) | ||
) | ||
|
||
self.current_velocity = new_velocity | ||
|
||
if self.remaining_energy > power * teampursuit.time_step: | ||
self.remaining_energy -= power * teampursuit.time_step | ||
else: | ||
self.remaining_energy = 0.0 | ||
|
||
return distance | ||
|
||
def follow(self, distance): | ||
fatigue_factor = 1 - (0.01 * self.fatigue_level) | ||
|
||
acceleration = ( | ||
2 | ||
* (distance - (self.current_velocity * teampursuit.time_step)) | ||
/ math.pow(teampursuit.time_step, 2) | ||
) | ||
new_velocity = self.current_velocity + (acceleration * teampursuit.time_step) | ||
delta_ke = 0.5 * (self.weight + self.bike_mass) * (new_velocity - self.current_velocity) | ||
power = ( | ||
( | ||
self.coefficient_drag_area | ||
* teampursuit.drafting_coefficients[self.position - 2] | ||
* 0.5 | ||
* self.event.air_density | ||
* math.pow(self.current_velocity, 3) | ||
) | ||
+ ( | ||
teampursuit.friction_coefficient | ||
* (self.weight + self.bike_mass) | ||
* teampursuit.gravitational_acceleration | ||
* self.current_velocity | ||
) | ||
+ (delta_ke / teampursuit.time_step) | ||
) / (self.mechanical_efficiency * fatigue_factor) | ||
|
||
self.current_velocity = new_velocity | ||
|
||
if self.remaining_energy > power * teampursuit.time_step: | ||
self.remaining_energy -= power * teampursuit.time_step | ||
else: | ||
self.remaining_energy = 0.0 | ||
|
||
def get_height(self): | ||
return self.height | ||
|
||
def get_weight(self): | ||
return self.weight | ||
|
||
def get_mean_maximum_power(self): | ||
return self.mean_maximum_power | ||
|
||
def get_remaining_energy(self): | ||
return self.remaining_energy | ||
|
||
def get_position(self): | ||
return self.position | ||
|
||
def set_weight(self, weight): | ||
self.weight = weight | ||
self.update_cda() | ||
self.update_total_energy() | ||
|
||
def set_height(self, height): | ||
self.height = height | ||
self.update_cda() | ||
|
||
def set_mean_maximum_power(self, mean_maximum_power): | ||
self.mean_maximum_power = mean_maximum_power | ||
self.update_total_energy() | ||
|
||
def set_position(self, position): | ||
self.position = position | ||
|
||
def increase_fatigue(self): | ||
self.fatigue_level += 2 | ||
|
||
def recover(self): | ||
if self.fatigue_level > 0: | ||
self.fatigue_level -= 1 | ||
|
||
def reset(self): | ||
self.remaining_energy = self.total_energy | ||
self.position = self.start_position | ||
self.fatigue_level = 0 | ||
self.current_velocity = 0 | ||
|
||
def update_cda(self): | ||
self.coefficient_drag_area = self.drag_coeffiecient * ( | ||
(0.0293 * math.pow(self.height, 0.725)) * (math.pow(self.weight, 0.425)) + 0.0604 | ||
) | ||
|
||
def update_total_energy(self): | ||
coeff = 240 if self.gender == "male" else 210 | ||
|
||
self.total_energy = self.mean_maximum_power * self.weight * coeff |
Oops, something went wrong.