Je suis paresseux. Pas assez paresseux pour ne pas faire d’exercice, mais assez paresseux pour vouloir que mes programmes de sport se fassent tout seuls.
Par coïncidence, j’ai commencé à lire « Linear Algebra and its applications », et une section a attiré mon attention : comment appliquer l’algèbre linéaire à des problèmes du monde réel.
Il s’avère qu’il existe de nombreuses façons d’optimiser les processus qui vous entourent, si vous savez convertir votre problème de la vie réelle en un modèle mathématique.
S’entraîner à la salle est une question d’équilibre. Trop peu d’intensité, et vous ne progressez pas autant que vous le souhaitez. Trop d’intensité, et vous vous fatiguez (et ne progressez pas autant que vous le souhaitez).
Quand vous créez vos programmes, vous devez avant tout prendre en compte vos objectifs. Cependant, la façon dont vous atteignez vos objectifs est un tout autre sujet :
Comment répartissez-vous vos entraînements : Push/Pull/Legs ; Lower/Upper ; Mixed
Combien de temps voulez-vous passer quotidiennement / hebdomadairement à la salle
Quelles priorités, pour quel groupe musculaire
Mon épaule gauche est-elle intacte cette semaine ?
Alors, et s’il existait un moyen de générer un programme efficacement, en prenant en compte tous ces facteurs ?
Avant de commencer, il y a quelques termes que je dois expliquer
J’appelle (et la plupart des gens aussi) un programme un ensemble d’entraînements : des séries d’exercices, regroupées par jour
Recommencez jusqu’à satisfaction (vous ne l’êtes jamais.)
Vous voyez l’idée. Push/Pull/Leg sont des termes qui décrivent des entraînements plutôt « orientés push », « orientés pull », ou « legs ». Par exemple, une traction est un exercice pull (bien sûr), et une pompe est un exercice push (bien sûr).
Ces programmes sont généralement faits à la main, achetés ou téléchargés auprès d’un influenceur fitness.
Mais ils sont rarement adaptés à vos besoins.
Je ne suis ni mathématicien, ni chercheur en recherche opérationnelle. Je vais faire des erreurs qui vous feront saigner des yeux. C’est un petit projet personnel conçu pour explorer un nouveau concept que j’apprenais
La programmation linéaire (LP) est un processus d’optimisation mathématique qui permet d’optimiser une fonction objectif linéaire (la maximiser/minimiser).
$$
f(x_1,x_2,x_3) = a_1x_1 + a_2x_2 + a_3x_3
$$
En « traduisant » votre problème en langage naturel « Je veux générer un programme d’entraînement qui correspond à mes contraintes » en un problème d’équations linéaires, vous pouvez utiliser un solveur pour approcher la meilleure solution possible.
En fin de compte, vous devrez vraiment trouver :
Une fonction objectif : la cible que vous voulez atteindre, par ex. maximiser le volume.
Des contraintes : par ex. je ne veux pas que le même exercice se répète deux fois sur \(x\) jours
Dans un cadre plus mathématique, voici comment elles sont exprimées.
« Pour chaque exercice, de chaque jour, je veux que le nombre de séries soit inférieur ou égal à 5 si l’exercice est sélectionné, ou 0 si ce n’est pas le cas »
« Pour chaque exercice, de chaque jour, je veux que le nombre de séries soit supérieur ou égal à 3 si l’exercice est sélectionné, ou 0 si ce n’est pas le cas »
Maintenant, étant donné mes objectifs, je ne veux pas entraîner les poignets autant que les abdos, par exemple, ou je veux garder les legdays à un confortable 1 fois par an semaine.
Rappel du jeu de données : chaque exercice a des groupes musculaires primaires et secondaires.
J’ai défini un poids (\(p_{e,m}\)) de \(1\), si l’exercice \(e\) est dans le groupe \(m\) en tant que primaire, ou \(0.25\) si secondaire. \(0\), bien sûr, si non inclus
Disons qu’il y a \(M \in \mathbb{N}\) groupes musculaires
Et ce faisant, nous créons une contrainte qui définit une nouvelle variable. Et c’est la chose la plus importante à retenir de cet article. Vous pouvez construire des systèmes élaborés, en utilisant des contraintes pour définir des variables à partir de la valeur d’autres variables.
Maintenant que nous avons nos variables de contribution musculaire (\(c_m\) avec \(m\) le groupe musculaire), nous pouvons définir des « cibles » quant à la quantité d’utilisation de chaque groupe musculaire. Je veux des abdos, pas des mollets énormes.
J’ai abordé ce problème à travers 2 autres vecteurs de variables qui représenteront à quel point une solution est au-dessus ou en-dessous de la cible.
Définissons deux variables supplémentaires : \(a_m\) représentant de combien un groupe musculaire a été programmé au-dessus de la cible, et \(b_m\) de combien un groupe musculaire est en-dessous de la cible.
$$
c_m + a_m - b_m = \text{target}
$$
[!TIP]
Cela ne peut fonctionner que si vous déclarez les bornes de vos variables comme des entiers positifs. Si vous autorisez \(a_m\) ou \(b_m\) à devenir négatifs, ils perdront tout le sens créé par la forme \(c_m + a_m - b_m \).
Nous avons maintenant deux ensembles de contraintes. Je n’ai pas trouvé de terme direct pour les nommer, j’utiliserai mes propres mots :
Contraintes de solution : elles coupent les régions indésirables de solutions
Contraintes auxiliaires : elles me permettent de créer des variables basées sur les valeurs d’autres variables
Les contraintes de solution sont nécessaires car certaines solutions sont inacceptables. Des jours vides, des jours avec 20+ entraînements
Mais nous devons aussi définir ce que le système doit trouver. Quoi optimiser.
Rappelez-vous à quoi ressemblent les fonctions linéaires. Nous devons fournir à notre solveur une seule fonction qui ressemble à ceci. Avec \(x\) un vecteur de variables, ici, ils pourraient être un mélange de \(o_d\), \(y_{e_d}\), etc.)
$$
f(x_1,x_2,x_3) = a_1x_1 + a_2x_2 + a_3x_3
$$
Bien sûr, vous pouvez vous attendre à ce qu’une véritable fonction objectif soit beaucoup plus grande. Dans un cadre professionnel, j’ai vu des systèmes avec des centaines de milliers de variables.
Je vais construire ma fonction objectif partie par partie. J’ai choisi de minimiser la fonction, mais vous pouvez faire l’inverse. Tout dépend de votre problème spécifique à résoudre.
[!note]
J’ai des sentiments mitigés à propos de cette partie de la fonction objectif, car elle tend à prioriser les exercices qui touchent plusieurs groupes musculaires.
Et enfin, nous pouvons créer un objectif de lissage, en utilisant les variables définies avec les contraintes
$$
0.01\sum^{6}_{d=0} o_d + n_d
$$
Chaque jour qui est au-dessus/en-dessous des autres s’ajoutera à la fonction objectif (qui doit être minimisée), la rendant ainsi légèrement moins susceptible d’être la solution optimale
LP n’est pas flexible. Rappelez-vous que les équations doivent être linéaires :
$$
f(x_1,x_2,x_3) = a_1x_1 + a_2x_2 + a_3x_3
$$
Comment ajouteriez-vous des contraintes telles que : deux jours Push, deux jours Pull, deux jours Leg, par semaine.
Vous ne le pourriez pas, en utilisant seulement des contraintes, ou des fonctions objectif.
Ce problème est résoluble en deux étapes :
Créer 3 variables, pour chaque jour : est-ce push, est-ce pull, est-ce leg. Ces variables sont des sommes de \(y_{d_e}\) pour le jour, et pour les valeurs de \(e\) liées aux exercices qui sont push/pull/legs. Nous les appellerons \(s_d\), \(e_d\) et \(l_d\)
Créer 3 nouvelles contraintes :
\(\sum^{6}_{d=0}\ s_d \geq 2\) : nous voulons au moins deux jours de push
\(\sum^{6}_{d=0}\ v_d \geq 2\) : nous voulons au moins deux jours de pull
\(\sum^{6}_{d=0}\ l_d \geq 2\) : nous voulons au moins deux jours de legs :(
Il nous faut aussi une nouvelle contrainte par jour : chaque jour peut être soit Push/Pull/Leg, pas 2 ou 3 à la fois : \(s_d + v_d + l_d = 1\)
C’est une utilisation directe des variables auxiliaires.
Au fil de mes lectures en ligne, j’ai vu que LP pouvait être utilisé pour modéliser BEAUCOUP de problèmes du monde réel :
Optimisation de la production
Planification du trafic et des routes
Planification d’itinéraires
…
C’est un sujet impressionnant, et il y a des ressources partout.
J’ai vu des produits « IA » que je soupçonne d’être un problème LP déguisé, ou du moins, un LLM qui pourrait être remplacé par un problème LP approprié.
Par commodité, j’ai utilisé un Jupyter Notebook dans un conteneur Docker. J’ai adopté les conteneurs docker jetables dans mon flux de travail depuis quelques semaines maintenant, et l’idée derrière est d’éviter d’avoir à réinstaller Linux tous les mois (j’utilise Arch btw.)
L’implémentation est faite avec Python 3.13, et PuLP
Quant à la méthodologie, c’est principalement de l’essai-erreur. Pas de grand plan, pas de connaissances complexes, juste essayer des choses. J’ai aussi demandé à ChatGPT de m’expliquer certains concepts, et de suggérer des contraintes.
[!quote]
Tu peux juste faire des choses™
— Quelqu’un au hasard sur , probablement
Ce qui est drôle avec LP, c’est qu’une matrice est capable de trouver des failles dans votre raisonnement. Et de vous les faire payer (en temps CPU)
Vous vouliez 30 exercices. Tenez, voici un lundi avec 24 exercices et un mardi avec 6.
Oui, vous vouliez cibler les legs. Saviez-vous que vous pouviez tout simplement faire 300 séries de leg press le lundi ?
Un guide pas à pas testé pour activer le Profile Guided Optimization et le Link Time Optimization dans un projet Meson compilé avec Clang, y compris l'étape de fusion llvm-profdata que la plupart des guides oublient.
Avant de toucher la moindre ligne de ma simulation de fluide, j'ai laissé le compilateur faire le travail. Changer simplement les flags m'a fait passer de 1x à environ 40x.