<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0"><channel><title><![CDATA[Foundations]]></title><description><![CDATA[Foundations est structuré en quatre couches d'expertise. Chacune fonde la suivante.
                                                                                                                                                                                        
  Couche 1 — Les Données. Pipelines de données, Knowledge Graphs, architecture data pour l'IA. Ce qu'il faut construire avant de brancher le moindre modèle.                            

  Couche 2 — L'Optimisation. Intelligence en essaim, contraintes multi-objectifs, optimisation quantique. L'algorithme confronté aux problèmes que la force brute ne résout pas.

  Couche 3 — La Physique. Machine Learning appliqué aux phénomènes physiques. La théorie derrière la pratique : pourquoi un pipeline de nettoyage de capteurs fonctionne, quelles lois le gouvernent.

  Couche 4 — La Lucidité. Analyse critique de la course à l'AGI. Fractures physiques, financières et architecturales. Ce que l'expérience opérationnelle en industrie critique révèle sur les limites réelles de l'IA.

Deux séries de formation complètes accompagnent ces couches : Time Series Forecasting Lab et Quantum Computing with R — les tutoriels techniques qui prouvent la profondeur derrière les essais.

Chaque article part d'un problème concret — jamais d'un buzzword. Ce blog est le carnet de bord d'un ingénieur data qui construit, mesure et conclut.]]></description><link>https://blog.bguarisma.com</link><image><url>https://cloudmate-test.s3.us-east-1.amazonaws.com/uploads/logos/61bc41016916c3593a6b04c3/3169c8e7-8e5f-4dd3-b992-0611557ce52c.png</url><title>Foundations</title><link>https://blog.bguarisma.com</link></image><generator>RSS for Node</generator><lastBuildDate>Tue, 15 Sep 2026 09:12:21 GMT</lastBuildDate><atom:link href="https://blog.bguarisma.com/rss.xml" rel="self" type="application/rss+xml"/><language><![CDATA[en]]></language><ttl>60</ttl><item><title><![CDATA[Mistral AI : Le Mirage du SaaS et le Grand Pivot vers le Réel]]></title><description><![CDATA[Le 16 juin 2026, Arthur Mensch, le patron emblématique de Mistral AI, a publié sur LinkedIn un message d’une rare solennité. Le discours officiel y célèbre, comme toujours, l’indépendance technologiqu]]></description><link>https://blog.bguarisma.com/mistral-ai-le-mirage-du-saas-et-le-grand-pivot-vers-le-reel</link><guid isPermaLink="true">https://blog.bguarisma.com/mistral-ai-le-mirage-du-saas-et-le-grand-pivot-vers-le-reel</guid><category><![CDATA[mistral]]></category><category><![CDATA[Souveraineté numérique]]></category><category><![CDATA[TSMC]]></category><category><![CDATA[GPU]]></category><category><![CDATA[FDE]]></category><category><![CDATA[palantir]]></category><category><![CDATA[Europe]]></category><dc:creator><![CDATA[Boris Guarisma]]></dc:creator><pubDate>Thu, 18 Jun 2026 10:19:17 GMT</pubDate><enclosure url="https://cdn.hashnode.com/uploads/covers/61bc41016916c3593a6b04c3/1db25881-d36a-4a39-b22f-904bffdda680.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Le 16 juin 2026, Arthur Mensch, le patron emblématique de Mistral AI, a publié sur LinkedIn un message d’une rare solennité. Le discours officiel y célèbre, comme toujours, l’indépendance technologique européenne, la « souveraineté » et la mise à disposition imminente de nouveaux poids ouverts pour l’été.</p>
<p>Pourtant, sous le vernis des relations publiques et de l'alignement politique national, se glisse un aveu de taille :</p>
<blockquote>
<p><em>« Aujourd’hui, nous ne possédons pas encore les meilleurs modèles de langage, mais nous avons constamment réduit cet écart. »</em></p>
</blockquote>
<p>Cette phrase est une clé de lecture fondamentale. Elle signe la fin de l'illusion. L’écart technologique sur le raisonnement général pur face aux géants américains ne se comble pas par la simple élégance de l'algorithme ; il se creuse sous le poids des gigawatts de serveurs et des milliards de dollars de capitalisation. Pour survivre face à la Silicon Valley, le champion européen de l'intelligence artificielle vient d'opérer un pivot stratégique discret, mais d'une envergure historique : <strong>la Palantirisation</strong>.</p>
<p>En choisissant de dépêcher des troupes d’ingénieurs d’élite directement chez ses clients pour faire plier ses modèles à leurs processus métiers complexes, Mistral AI abandonne discrètement le rêve de l’éditeur logiciel pur à marges infinies pour embrasser la réalité humaine, physique et réglementaire de l'économie européenne.</p>
<hr />
<h2>1. La capitulation du « Pure Software »</h2>
<p>Pour mesurer l'ampleur de cette mue, il faut se souvenir de la promesse de départ. En juin 2023, la note stratégique d'amorçage de Mistral AI — un document fondateur de sept pages rédigé par des transfuges de Meta et de Google DeepMind — esquissait un modèle d’affaires à faire saliver les plus grands fonds de capital-risque de la planète. La startup promettait une stratégie « Open Core » articulée autour d’un effet de réseau gratuit (des modèles ouverts téléchargeables par tous) redirigeant vers leur plateforme payante d’API logicielles.</p>
<p>C’était la promesse du modèle SaaS classique appliqué à l'ère cognitive : un coût marginal d’inférence décroissant, des équipes ultra-légères, pas d'infrastructure physique à gérer, et des marges brutes de près de 80 %. C’est sur cette thèse de scalabilité pure que les investisseurs ont valorisé Mistral à hauteur de 12 milliards de dollars en moins de trois ans.</p>
<p>Mais l'IA en entreprise a une propriété physique tenace : <strong>elle ne s'auto-installe pas</strong>.</p>
<p>Brancher un modèle de langage générique sur un système informatique d'entreprise hérité des années 1990 (<em>legacy</em>), s'assurer que les données ne fuitent pas chez un sous-traitant cloud, nettoyer les bases de données sémantiques et orchestrer des flottes d'agents fiables demande un effort d'ingénierie humaine titanesque. L'IA n'est pas un logiciel fluide ; c'est une infrastructure lourde.</p>
<p>La réponse d'Arthur Mensch ? L'introduction officielle du modèle <strong>FDE</strong> :</p>
<blockquote>
<p><em>« Nous les aidons avec des pro-services (FDE est le nom chic), car c’est essentiel pour assurer la réussite de nos clients. »</em></p>
</blockquote>
<p>L'acronyme <strong>FDE</strong> — <em>Forward Deployed Engineers</em> — est le secret de fabrication jalousement gardé d'un autre géant de la tech américaine : <strong>Palantir Technologies</strong>. Ce modèle consiste à détacher des ingénieurs d’élite directement dans les bureaux du client (que ce soit une banque d'affaires, un constructeur aéronautique ou un ministère de la Défense) pour câbler le logiciel aux sources de données réelles.</p>
<p>Pour Palantir, ce modèle d’affaires a longtemps été boudé par Wall Street, qui y voyait une vulgaire activité de « société de services » à forte intensité de main-d’œuvre et à faible scalabilité. Mais aujourd'hui, c’est le seul qui fonctionne en Europe. En adoptant les FDE, Mistral admet que pour vendre de l'IA sur le Vieux Continent, il ne suffit pas de mettre à disposition une clé d'API. Il faut envoyer des hommes pour construire l'ouvrage d'art.</p>
<hr />
<h2>2. Le poids de la matière et la revente de kilowattheures</h2>
<p>Le second axe de cette transformation est d'ordre physique et financier. Au cours du premier trimestre 2026, Mistral AI a contracté un emprunt d'un montant inédit de <strong>830 millions de dollars auprès de la BNP Paribas</strong>. Le collatéral de cette dette ? Pas des actions, mais de la matière brute : <strong>13 800 puces de calcul de dernière génération (les GPU Nvidia GB300)</strong>.</p>
<p>Pour une startup logicielle, se charger d'une telle dette d'infrastructure est un pari extrêmement risqué. Si les puces dorment ou si le prix d'inférence mondial s'effondre sous le coup du dumping des tokens des géants de l'open-source (comme le chinois DeepSeek), l'entreprise risque l'asphyxie financière.</p>
<p>Pour honorer ses créances et générer du cash-flow immédiat, Mistral a dû descendre dans la cave. Elle est passée de créatrice de modèles à <strong>fournisseur d'électricité cognitive</strong> :</p>
<blockquote>
<p><em>« Suite à notre investissement dans l’infrastructure de calcul, nous proposons également des services cloud d’IA hébergés. »</em></p>
</blockquote>
<p>L'entreprise utilise désormais ses serveurs excédentaires pour faire de la revente de puissance de calcul pure (<em>GPU-as-a-Service</em>), louant ses puces à de grands groupes industriels européens comme ASML, Ericsson ou l'Agence Spatiale Européenne pour qu'ils y pré-entraînent leurs propres réseaux via sa plateforme de calcul <strong>Forge</strong>.</p>
<p>C'est une intégration verticale inversée remarquable. Pour financer ses modèles de pointe, Mistral doit se comporter comme un gestionnaire de réseau électrique, vendant du kilowattheure de calcul en même temps que du token d'intelligence.</p>
<hr />
<h2>3. La souveraineté d'orchestration : l'interdépendance du silicium</h2>
<p>Le discours d'Arthur Mensch insiste lourdement sur un niveau de service et de sécurité <em>« totalement découplé des fournisseurs américains »</em>. C'est le cœur du positionnement marketing de la startup : offrir aux acteurs de l'industrie, de la santé et de l'État un environnement de confiance face à l'hégémonie de Microsoft et de Google.</p>
<p>Pour certains observateurs, ce grand écart entre un discours d'indépendance et la réalité matérielle (des puces Nvidia GB300 conçues en Californie et gravées à Taïwan par TSMC, une structure de capital à forte composante anglo-saxonne) relève de la contradiction.</p>
<p>C'est oublier une vérité fondamentale que la physique et la géopolitique des semi-conducteurs nous imposent : <strong>dans l'industrie de pointe, l'autarcie à 100 % est une chimère</strong>.</p>
<p>La fabrication d'une seule puce de calcul IA est le produit d'une interdépendance radicale et d'une symbiose de monopoles nationaux. Les États-Unis conçoivent le design logiciel, le Japon détient la chimie de pointe et le silicium ultra-pur, l'Allemagne produit les optiques de Zeiss et les lasers de Trumpf, et les Pays-Bas, via le monopole absolu d'ASML, assemblent les machines de lithographie EUV. Et même ces machines néerlandaises dépendent de brevets et de technologies d'origine américaine (comme les sources de lumière Cymer).</p>
<p>Dans ce réseau à clés d'accès dispersées, exiger de l'Europe ou de Mistral qu'ils possèdent la chaîne de bout en bout est un contresens. La souveraineté réelle ne réside pas dans l'autarcie, mais dans le contrôle de <strong>verrous stratégiques spécifiques</strong>.</p>
<p>C'est ce qu'il convient d'appeler la <strong>souveraineté d'orchestration</strong>.</p>
<p>En se positionnant sur l'hébergement physique sur site (on-premise via Vibe) et l'intégration métier sur mesure par des ingénieurs FDE, Mistral ne prétend pas fabriquer du silicium souverain. Elle sécurise la couche finale et la plus critique de la pile : celle qui touche aux données d'affaires, au savoir tacite et aux secrets industriels de ses clients. Vous louez peut-être des puces soumises aux législations d'exportation américaines, mais vous gardez le contrôle total sur l'intelligence décisionnelle et la compliance de votre organisation. C'est un levier de confiance inestimable, et le seul pragmatique pour les entreprises régulées et les États européens (comme le prouve l'accord de défense signé en janvier 2026 avec le Ministère des Armées).</p>
<hr />
<h2>Conclusion : Un pivot pragmatique et risqué</h2>
<p>Le pivot « Palantir » de Mistral AI n'est pas un aveu de défaite. C'est un éclair de génie tactique et de pragmatisme européen.</p>
<p>En acceptant la réalité des contraintes physiques de la course au calcul brut généraliste, Mistral choisit de s'ancrer là où la valeur réelle et durable se capture sur notre continent : dans la plomberie des processus industriels critiques, l'orchestration locale d'agents et la garantie d'une conformité de terrain. L'IA d'action ne se gagnera pas uniquement dans des laboratoires de recherche simulant une intelligence pure à coups de téraflops, mais sur le terrain, en codant auprès des systèmes legacy de l'économie réelle.</p>
<p>Cependant, ce choix opérationnel a un coût. L'ingénierie de solutions et les ingénieurs déployés (FDE) pèsent lourdement sur la marge brute de l'entreprise et sont complexes à faire monter à l'échelle par rapport à un pur modèle de distribution d'API logicielle. Si la baisse globale des prix de l'inférence se poursuit, Mistral devra rapidement stabiliser ses revenus récurrents issus de sa suite logicielle d'orchestration pour amortir le remboursement de sa dette d'infrastructure de 830 millions de dollars.</p>
<p>L'Europe n'aura peut-être pas son OpenAI scalable aux marges infinies d'un monopole d'accès d'API. Mais si le pari d'Arthur Mensch réussit, elle aura son Palantir de l'IA : un acteur capable de mailler la puissance de calcul physique à la haute précision de l'intégration industrielle. C'est le triomphe de la lucidité d'usage sur le fantasme de l'autarcie.</p>
<hr />
<h2>Sources et Références</h2>
<ul>
<li><p><strong>[1] Strategical Seed Memo of Mistral AI (juin 2023)</strong> : <em>"Mistral AI: generative AI at European scale"</em>, décrivant le business model de plateforme d'API logicielle Open Core à marges brutes scalables de type SaaS.</p>
</li>
<li><p><strong>[2] Benchmarks de Raisonnement d'Entreprise 2025-2026</strong> : Évaluations d'inférence documentant l'écart persistant de 12 à 18 mois entre les modèles open-weights de Mistral AI et les capacités de raisonnement logique de GPT-4o, o1 ou Claude 3.5 Sonnet.</p>
</li>
<li><p><strong>[3] Palantir Technologies S-1 Registration Statement (2020)</strong> : Document d'introduction en bourse décrivant le modèle opérationnel des <em>Forward Deployed Engineers</em> (FDE).</p>
</li>
<li><p><strong>[4] Financement d'infrastructure BNP Paribas - Mistral AI (Q1 2026)</strong> : Structuration du crédit à hauteur de 830 millions de dollars garanti sur collatéral d'actifs physiques (13 800 GPU Nvidia GB300).</p>
</li>
<li><p><strong>[5] Accord-cadre Ministère des Armées - Mistral AI (janvier 2026)</strong> : Protocole de déploiement souverain de modèles de langage sur site pour les forces armées françaises.</p>
</li>
</ul>
]]></content:encoded></item><item><title><![CDATA[23 Millions d'Euros par Emploi — Le Vrai Prix de la Réindustrialisation par l'IA]]></title><description><![CDATA[En janvier 2026, la directrice financière d'OpenAI a confirmé un chiffre que personne n'a relevé. Le triplement des revenus de l'entreprise — de 6,7 à 20 milliards de dollars — suit exactement le trip]]></description><link>https://blog.bguarisma.com/23-millions-euros-emploi-reindustrialisation-ia</link><guid isPermaLink="true">https://blog.bguarisma.com/23-millions-euros-emploi-reindustrialisation-ia</guid><category><![CDATA[AI]]></category><category><![CDATA[Energy]]></category><category><![CDATA[infrastructure]]></category><category><![CDATA[Data Center]]></category><category><![CDATA[FRAnce]]></category><dc:creator><![CDATA[Boris Guarisma]]></dc:creator><pubDate>Sun, 22 Mar 2026 17:45:12 GMT</pubDate><enclosure url="https://cdn.hashnode.com/uploads/covers/61bc41016916c3593a6b04c3/d96f2a4d-7c1a-487a-93e5-b2851a841524.jpg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>En janvier 2026, la directrice financière d'OpenAI a confirmé un chiffre que personne n'a relevé. Le triplement des revenus de l'entreprise — de 6,7 à 20 milliards de dollars — suit <em>exactement</em> le triplement de ses ressources informatiques. Pas approximativement. Exactement. Sa formulation est clinique : <em>"Notre capacité à servir nos clients suit directement celle des ressources informatiques disponibles."</em></p>
<p>Traduisons : OpenAI ne croît pas parce qu'elle trouve de nouveaux clients. Elle croît parce qu'elle branche de nouveaux serveurs. Son chiffre d'affaires n'est pas une métrique commerciale — c'est une fonction dérivée de sa consommation électrique. À 1,9 gigawatt, soit la puissance de deux réacteurs nucléaires, les revenus suivent les watts.</p>
<p>Ce fait change tout. Si le revenu de l'IA est indexé sur l'énergie, alors la question qui structure le marché n'est pas "qui a le meilleur modèle ?" mais "qui a le plus de mégawatts ?" Et cette question a des implications que la plupart des décideurs n'ont pas encore intégrées.</p>
<hr />
<h2>1. La cascade des pénuries</h2>
<p>Le discours dominant traite chaque contrainte physique comme un problème isolé. Les puces manquent — on construit des fonderies. L'énergie manque — on signe des contrats. La mémoire manque — on investit. En réalité, ce que je vois dans les données de mars 2026, c'est une cascade séquentielle : chaque couche de la stack matérielle révèle sa pénurie au moment où la couche précédente est partiellement résolue.</p>
<p><strong>Énergie.</strong> C'est la contrainte fondatrice, documentée depuis 2025. Les data centers consomment déjà 22 % du réseau électrique irlandais — un pays de 5,1 millions d'habitants. Les délais de raccordement au réseau haute tension : 5 à 7 ans. Chaque gigawatt contracté par un data center est physiquement indisponible pour une usine, un hôpital, un immeuble.</p>
<p><strong>Puces.</strong> Nvidia concentre 1 000 milliards de dollars de capitalisation sur un parc de 20 millions de GPU. La monoculture est telle que Meta, OpenAI et Amazon conçoivent désormais leurs propres puces pour s'en affranchir — mais ces alternatives ne seront matures qu'en 2027-2028.</p>
<p><strong>Mémoire.</strong> Le chairman de SK Group prédit des pénuries de mémoire HBM jusqu'en 2030. Ces puces-mémoire, soudées directement sur les GPU, sont le goulet le plus discret et le plus contraignant : sans HBM, le GPU le plus puissant reste un processeur sans données.</p>
<p><strong>Stockage.</strong> En marge de la GTC 2026, Greg Matson, vice-président de Solidigm (filiale SK Hynix), déclare que les systèmes IA de fin 2026 nécessiteront 35 % de stockage SSD en plus. <em>"Je pourrais vendre deux fois plus que ce que je vends aujourd'hui."</em> Jensen Huang confirme : <em>"Le système de stockage va être pilonné."</em> Pénurie prévue : jusqu'en 2030.</p>
<p><strong>Refroidissement.</strong> En mars 2026, Ecolab — une entreprise de traitement des eaux — a racheté CoolIT Systems pour 4,75 milliards de dollars, soit 8,6 fois le chiffre d'affaires. CoolIT conçoit des systèmes de refroidissement liquide pour les racks de GPU. La densité thermique des puces Blackwell dépasse ce que l'air peut dissiper. Le passage au refroidissement liquide n'est pas un choix technologique — c'est une contrainte thermodynamique. Et quand une entreprise de chimie de l'eau paie 8,6 fois le chiffre d'affaires pour entrer dans ce marché, c'est que le refroidissement est devenu une couche d'infrastructure critique, au même titre que l'électricité.</p>
<p>La cascade n'est pas un accident. C'est la conséquence mécanique d'une industrie qui construit plus vite qu'elle ne peut s'approvisionner. Chaque couche est traitée comme un problème séparé. En réalité, elles forment un système : résoudre la pénurie de puces accélère la pénurie de mémoire, qui révèle la pénurie de stockage, qui exacerbe le besoin de refroidissement, qui alourdit la facture électrique.</p>
<hr />
<h2>2. Le matching fantôme</h2>
<p>Microsoft annonce avoir atteint son objectif : "matcher" 100 % de sa consommation électrique avec de l'énergie renouvelable, via 40 gigawatts de contrats d'achat (PPA) répartis dans 26 pays. Le chiffre est impressionnant. La mécanique, moins.</p>
<p>Un PPA est un contrat financier. Microsoft achète de la capacité renouvelable quelque part dans le monde et "matche" cet achat avec sa consommation globale. Mais les électrons ne se téléportent pas. Le data center de Dublin fonctionne sur le réseau irlandais — un réseau dont les data centers consomment 22 % et dont le mix inclut du gaz naturel comme backup. Le matching est annuel et agrégé, pas horaire et local. C'est une comptabilité carbone, pas de la physique.</p>
<p>Le signal le plus révélateur est un glissement sémantique. La responsable développement durable de Microsoft déclare que l'électricité <em>"carbon-free"</em> — comprendre : nucléaire — <em>"jouerait un rôle croissant"</em> d'ici 2030. Le vocabulaire passe de "renewable" à "carbon-free." Ce n'est pas anodin. C'est un aveu : le renouvelable intermittent — éolien, solaire — ne peut pas fournir le baseload qu'un data center exige 24 heures sur 24. Pour le prouver, Microsoft a signé un accord avec Constellation Energy pour redémarrer Three Mile Island, le site du plus grave accident nucléaire de l'histoire américaine. Quand on choisit de relancer TMI malgré la charge symbolique, c'est que la contrainte physique a pris le dessus sur le narratif.</p>
<p>Le certificat vert n'est pas l'électron vert. Et la distance entre les deux croît avec chaque gigawatt contracté.</p>
<hr />
<h2>3. L'effet d'éviction français</h2>
<p>Pour un lecteur français, la cascade a une implication directe. En février 2026, Les Echos rapportaient que les data centers représentent désormais le premier poste d'investissement industriel en France : 67 milliards d'euros. Pour 2 800 emplois.</p>
<p>Faisons le calcul. Un emploi en data center "coûte" 23 millions d'euros d'investissement. Un emploi dans l'industrie manufacturière classique coûte 0,6 million d'euros. Le ratio d'inefficacité sociale est de 38 pour 1. Pour le prix d'un poste de technicien de data center, on pourrait créer 38 emplois en usine.</p>
<p>Les 125 milliards d'euros de "réindustrialisation" affichés par les statistiques officielles sont gonflés par ces investissements massifs en infrastructure numérique. Le problème n'est pas comptable — il est physique. Les délais de raccordement électrique haute tension sont de 5 à 7 ans. Chaque gigawatt verrouillé par un data center est un gigawatt physiquement indisponible pour une usine de batteries, une ligne de production automobile ou une fonderie de semiconducteurs. Ce n'est pas une addition à l'économie productive — c'est une soustraction.</p>
<p>L'image de la "Maladie Hollandaise" est tentante : un pays qui exporte sa ressource brute (l'électricité nucléaire bas-carbone) sous forme d'infrastructure pour l'IA étrangère, sans capturer la valeur ajoutée logicielle. Les puces restent américaines. Les modèles restent américains. La France fournit les watts.</p>
<hr />
<h2>Le contre-argument — et ses limites</h2>
<p>Je dois nuancer. L'investissement en data centers n'est pas sans retombées. Les PPA de Microsoft, aussi comptables soient-ils, financent de la capacité renouvelable réelle : 19 GW déjà injectés dans des réseaux. L'accélération de la transition énergétique par la demande des hyperscalers est un effet systémique positif, même si les électrons physiques ne correspondent pas au marketing.</p>
<p>De même, la cascade des pénuries a un revers optimiste : chaque goulot crée un marché. Le refroidissement liquide, le stockage haute densité, la gestion thermique — ce sont des secteurs industriels où l'Europe a des compétences réelles. Schneider Electric (distribution électrique, 40 milliards d'euros de capitalisation) est déjà positionnée sur la plomberie de l'IA. Ecolab rachète CoolIT. Il y a de la valeur à capturer dans les couches physiques, même quand les couches logicielles sont américaines.</p>
<p>Et le contre-argument le plus solide : le stockage SSD est plus scalable que l'énergie. On construit des usines de NAND plus vite que des centrales. La cascade des pénuries n'est pas forcément permanente — elle pourrait se résoudre couche par couche, comme elle s'est révélée.</p>
<p>Mais ces nuances ne changent pas le diagnostic fondamental. La demande de compute IA croît de manière exponentielle. La capacité physique — réseau électrique, mémoire, stockage, refroidissement — croît de manière linéaire. L'écart se creuse. Et les décideurs qui pensent "abonnement SaaS" raisonnent sur le mauvais objet.</p>
<hr />
<h2>En conclusion</h2>
<p>Dans <a href="https://blog.bguarisma.com/arroseur-arrose-ingenierie-financiere-ia">mon article précédent</a>, j'avais documenté le circuit financier circulaire qui alimente l'IA. Ce texte pose la question en amont : <em>qu'est-ce que cet argent achète ?</em></p>
<p>La réponse est physique. Des watts. Des puces. De la mémoire. Du stockage. Du refroidissement liquide. Chaque couche révèle sa pénurie au moment où la précédente est partiellement levée. Le cloud n'est pas une abstraction — c'est du béton, du cuivre et de l'eau froide.</p>
<p>L'IA n'est pas un logiciel qu'on déploie. C'est une infrastructure qu'on alimente. Celui qui contrôle les watts contrôle l'inférence. Et pour l'instant, les watts sont en France — mais la valeur est en Californie.</p>
<p>La question, pour un décideur européen, n'est pas de savoir si l'IA va transformer son secteur. C'est de savoir s'il sera du côté de ceux qui capturent la valeur — ou de ceux qui fournissent l'électricité.</p>
<hr />
<p><strong>Sources :</strong></p>
<ol>
<li><p>Les Echos, <em>"OpenAI a plus que triplé ses revenus sur un an"</em>, 20 janvier 2026</p>
</li>
<li><p>Stephen Nellis, <em>"AI's demand for data could cause tight storage chip supplies, Solidigm executive says"</em>, Reuters, 20 mars 2026</p>
</li>
<li><p>Reuters, <em>"Ecolab to buy CoolIT for $4.75 billion to tap into AI data center boom"</em>, 20 mars 2026</p>
</li>
<li><p>Padraic Halpin, <em>"Microsoft to keep buying enough renewable energy to match all its electricity needs"</em>, Reuters (Dublin), 18 février 2026</p>
</li>
<li><p>Abu Sultan, <em>"Microsoft says it is on pace to invest $50 billion in 'Global South' AI push"</em>, Reuters (Bengaluru), 18 février 2026</p>
</li>
<li><p>Mehdi Laghrari, <em>"Le boom des data centers sauve l'investissement industriel en France"</em>, Les Echos, 6 février 2026</p>
</li>
</ol>
]]></content:encoded></item><item><title><![CDATA[L'Arroseur Arrosé — Comment l'IA Finance sa Propre Demande]]></title><description><![CDATA[En moins de trois mois, début 2026, Nvidia a injecté plus de 42 milliards de dollars dans l'écosystème IA. 30 milliards dans OpenAI. 10 milliards dans Anthropic. 2 milliards dans Nebius. Le reste disp]]></description><link>https://blog.bguarisma.com/arroseur-arrose-ingenierie-financiere-ia</link><guid isPermaLink="true">https://blog.bguarisma.com/arroseur-arrose-ingenierie-financiere-ia</guid><category><![CDATA[AI]]></category><category><![CDATA[startup]]></category><category><![CDATA[finance]]></category><category><![CDATA[Venture Capital]]></category><category><![CDATA[business]]></category><dc:creator><![CDATA[Boris Guarisma]]></dc:creator><pubDate>Sun, 22 Mar 2026 17:44:23 GMT</pubDate><enclosure url="https://cdn.hashnode.com/uploads/covers/61bc41016916c3593a6b04c3/3aba8502-a266-489c-b9f5-451d511ff1b4.jpg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>En moins de trois mois, début 2026, Nvidia a injecté plus de 42 milliards de dollars dans l'écosystème IA. 30 milliards dans OpenAI. 10 milliards dans Anthropic. 2 milliards dans Nebius. Le reste dispersé entre Cursor, Figure AI, Quantinuum et d'autres. Avec 63 milliards de trésorerie et plus de 200 milliards de chiffre d'affaires annuel, Nvidia pouvait se le permettre. La question n'est pas de savoir s'il pouvait — c'est de savoir <em>pourquoi</em>.</p>
<p>La réponse tient dans une phrase des Echos : <em>"Une large partie de ces nouveaux capitaux injectés dans OpenAI ont vocation à financer l'achat du matériel auprès de Nvidia."</em></p>
<p>Le fournisseur finance son propre client. Le client achète au fournisseur avec l'argent du fournisseur. L'argent ne quitte jamais le circuit. C'est ce que les analystes financiers appellent du round-tripping — et c'est le mécanisme fondateur de l'économie de l'IA en 2025-2026.</p>
<hr />
<h2>1. L'anatomie d'une boucle</h2>
<p>Le round-tripping de Nvidia n'est pas nouveau. En 2024, la même logique opérait déjà à plus petite échelle : environ 5 milliards de dollars investis dans des clients GPU. Ce qui a changé, c'est l'ordre de grandeur. De 5 à 42 milliards en un an. Pour comparaison, Intel Capital — le plus grand fonds de capital-risque corporate de l'histoire des semiconducteurs — a investi environ 20 milliards cumulés en trente-quatre ans d'existence. Nvidia a dépassé ce montant en un trimestre.</p>
<p>Et le mécanisme n'est pas caché. Au moment où Nvidia préparait son entrée au capital d'OpenAI, celle-ci annonçait 100 milliards de dollars de location de processeurs Nvidia. Ce n'est pas une coïncidence. C'est un bon de commande prépayé, déguisé en prise de participation.</p>
<p>Puis, en mars 2026, Jensen Huang a déclaré que ces investissements seraient "probablement les derniers." La justification officielle : OpenAI et Anthropic préparent leur introduction en bourse. La justification structurelle est ailleurs — le round-tripping a rempli sa fonction. Les labs sont désormais dépendants de l'architecture Nvidia (CUDA, NVLink, Blackwell). L'investissement en equity n'est plus nécessaire quand la dépendance technique suffit.</p>
<p>Mais il serait naïf de penser que le mécanisme s'est arrêté. Il a changé de mains.</p>
<hr />
<h2>2. L'investissement-facture</h2>
<p>En février 2026, OpenAI a bouclé la plus grosse levée de fonds de l'histoire du capital-risque : 110 milliards de dollars, valorisation 730 milliards. Les investisseurs : Amazon (jusqu'à 50 milliards, dont 15 fermes et 35 conditionnels), SoftBank (30 milliards), Nvidia (30 milliards).</p>
<p>Chaque investisseur est aussi un fournisseur. Et c'est là que le mécanisme devient limpide.</p>
<p>Amazon investit jusqu'à 50 milliards en equity. En retour, OpenAI signe un contrat cloud de 100 milliards supplémentaires sur huit ans avec AWS — qui s'ajoute aux 38 milliards déjà contractés à l'automne 2025. Total : 138 milliards de factures cloud garanties. Pour chaque dollar d'equity investi, Amazon récupère jusqu'à 2,76 dollars de revenus cloud. Ce n'est pas un investissement — c'est un crédit fournisseur avec de l'equity en collatéral.</p>
<p>Le pattern est identique pour chaque participant :</p>
<ul>
<li><p><strong>Amazon</strong> investit 50 milliards, vend 138 milliards de cloud</p>
</li>
<li><p><strong>Nvidia</strong> investit 30 milliards, vend des GPU</p>
</li>
<li><p><strong>SoftBank</strong> investit 30 milliards, revend le narratif à ses limited partners</p>
</li>
</ul>
<p>Personne n'investit en tant qu'investisseur pur. La "levée de fonds record" est une opération de trésorerie multilatérale entre acteurs de la même chaîne de valeur.</p>
<p>Et le phénomène le plus révélateur est le hedge croisé. Amazon investit dans OpenAI <em>et</em> dans Anthropic (8 milliards). Microsoft investit dans OpenAI (135 milliards historiques) <em>et</em> dans Anthropic (15 milliards). Nvidia investit dans les deux. Chaque hyperscaler est au capital de <em>tous les labs</em>. La "compétition" entre OpenAI et Anthropic est financée par les mêmes fournisseurs d'infrastructure — qui récupèrent leur mise en contrats cloud quel que soit le lab qui "gagne."</p>
<p>Les hyperscalers ne parient pas sur l'IA. Ils vendent des pelles pendant la ruée vers l'or et financent les chercheurs d'or avec le produit des pelles.</p>
<hr />
<h2>3. Le recul silencieux</h2>
<p>Ce qui rend ce mécanisme fascinant, c'est qu'il a laissé des traces visibles de sa propre fragilité.</p>
<p>En septembre 2025, Nvidia annonce un investissement de 100 milliards dans OpenAI. En janvier 2026, Jensen Huang qualifie les doutes de "complètement absurdes" : <em>"Nous allons réaliser un investissement colossal dans OpenAI."</em> En février, le Financial Times révèle que le montant est réduit à 30 milliards — une contraction de 70 %. Trois semaines entre le déni public et la correction.</p>
<p>Pourquoi cette contraction ? Parce que la boucle a atteint sa limite de visibilité. Les analystes financiers ont commencé à nommer ce que je décris ici : un "arrangement circulaire" où le fournisseur préfinance ses propres ventes. Le round-tripping fonctionne tant qu'il reste implicite. Une fois nommé, il devient un risque réputationnel.</p>
<p>Mais le mécanisme ne disparaît pas — il mute. Nvidia se retire du round-tripping direct parce que ses clients (Amazon, SoftBank) le font désormais à sa place. Amazon investit 50 milliards dans OpenAI et récupère 138 milliards en cloud. La boucle s'est déplacée d'un étage dans la chaîne de valeur. Le vendeur de puces laisse la place au vendeur de cloud. Le circuit reste fermé.</p>
<hr />
<h2>4. L'IPO comme socialisation</h2>
<p>La prochaine étape est déjà visible. OpenAI vise une valorisation d'environ 1 000 milliards de dollars en bourse, possiblement dès fin 2026. Anthropic, valorisé 380 milliards après sa Series G de février, réfléchit aussi à une introduction.</p>
<p>L'IPO change le mécanisme sans le supprimer. Aujourd'hui, le round-tripping est financé par des investisseurs stratégiques (Nvidia, Amazon, SoftBank) qui récupèrent leur mise en factures. Demain, il sera financé par les marchés publics — des investisseurs retail qui achèteront des actions OpenAI sans savoir que le chiffre d'affaires repose en partie sur des flux circulaires entre investisseurs-fournisseurs. C'est la socialisation du round-tripping : le risque passe des bilans corporate aux portefeuilles d'épargnants.</p>
<p>Les marchés de dette ont déjà perçu le signal. En mars 2026, Reuters rapporte que les gestionnaires de CLOs (Collateralized Loan Obligations) vendent massivement leurs prêts au secteur logiciel. Le software représente 12 % des portefeuilles CLO américains — le plus grand sous-secteur. JPMorgan estime l'exposition à risque entre 40 et 150 milliards de dollars. Des prêts à Intuit, Dayforce, Citrix se sont échangés entre 89 et 98 centimes sur le dollar — des niveaux de stress inédits. Jim Egan, de Morgan Stanley, résume : <em>"Software is a sector where there is more selling coming from CLO managers than there is buying."</em> JPMorgan qualifie le logiciel de "secteur orphelin."</p>
<p>La dette ne spécule pas. Elle protège le principal. Quand les marchés de dette commencent à fuir un secteur, c'est un signal de conviction profonde — pas une panique passagère.</p>
<hr />
<h2>Le contre-argument — et ses limites</h2>
<p>Je dois être honnête : le round-tripping n'est pas de la fraude. OpenAI a réellement besoin de compute massif. Amazon offre un service réel à un prix de marché. Les contrats cloud ne sont pas fictifs — ils reflètent une transaction économique sous-jacente. Et OpenAI négocie activement ses conditions : l'adoption des puces Trainium d'Amazon montre que le lab diversifie ses fournisseurs de silicium. Ce n'est pas un client captif qui accepte passivement.</p>
<p>De même, les valorisations ne sont pas sans fondement. Anthropic affichait 14 milliards de revenus annualisés en février 2026, déjà proche de 19 milliards en mars. OpenAI est dans des ordres de grandeur comparables. Les multiples sont élevés, mais pas absurdes pour une phase d'hyper-croissance.</p>
<p>Le risque n'est pas que le circuit soit fictif. C'est qu'il soit fragile. Si le marché du compute se détend — nouvelles puces, gains d'efficience, saturation de la demande d'inférence — les contrats cloud à huit ans deviennent des engagements à perte. Et l'equity qui les garantit ne vaut plus rien. L'histoire de la tech est remplie de boucles qui semblaient vertueuses tant que le marché montait — et qui se sont révélées circulaires quand il a tourné.</p>
<hr />
<h2>En conclusion</h2>
<p>Ce que je décris ici n'est pas un complot. C'est un mécanisme. Un mécanisme où le fournisseur de puces finance les labs, les labs achètent des puces au fournisseur, les hyperscalers convertissent l'equity en factures cloud, et l'ensemble s'apprête à être transmis aux marchés publics via des IPO.</p>
<p>Dans <a href="https://blog.bguarisma.com/code-gratuit-gouvernance-chere-vibe-coding">mon article précédent</a>, j'avais documenté le ratio 1:100 entre production de code et gouvernance — le vrai coût du code gratuit. Ce texte pose la question en amont : <em>qui finance l'infrastructure sur laquelle ce code tourne</em> ?</p>
<p>Tout le monde — et personne. Le vendeur de puces finance le lab. Le lab paie le cloud. Le cloud paie les puces. La boucle se referme. Bientôt, l'épargnant financera le + tout — via des IPO dont la valorisation repose sur des contrats entre investisseurs-fournisseurs.</p>
<p>Jensen Huang a eu l'intelligence de quitter la table quand le jeu est devenu visible. La question est de savoir si les prochains joueurs — les marchés publics — comprendront les règles avant de miser.</p>
<hr />
<p><strong>Sources :</strong></p>
<ol>
<li><p>Joséphine Boone, <em>"IA : comment Nvidia assoit sa domination en arrosant les pépites du secteur à coups de milliards"</em>, Les Echos, 16 mars 2026</p>
</li>
<li><p>Juby Babu, <em>"Le directeur général de Nvidia annonce la fin des investissements dans OpenAI et Anthropic"</em>, Reuters, 4 mars 2026</p>
</li>
<li><p>Joséphine Boone, <em>"La fusée OpenAI boucle une levée de fonds record à 110 milliards de dollars avec le soutien d'Amazon"</em>, Les Echos, 27 février 2026</p>
</li>
<li><p>Les Echos / Financial Times, <em>"Nvidia va investir trois fois moins que prévu dans OpenAI"</em>, 20 février 2026</p>
</li>
<li><p>Joséphine Boone, <em>"Les nuages s'amoncellent au-dessus du champion de l'intelligence artificielle OpenAI"</em>, Les Echos, 5 février 2026</p>
</li>
<li><p>Anirban Sen, Paritosh Bansal, <em>"Debt investors offloading exposure to software companies is latest sign of pain"</em>, Reuters, 17 mars 2026</p>
</li>
<li><p>GeekWire, <em>"Filings: How Amazon's $50B OpenAI deal actually works"</em>, février 2026</p>
</li>
<li><p>Anthropic, <em>"Anthropic raises $30 billion Series G"</em>, communiqué officiel, 12 février 2026</p>
</li>
</ol>
]]></content:encoded></item><item><title><![CDATA[Code Gratuit, Gouvernance Chère — Le Vrai Coût du Vibe Coding]]></title><description><![CDATA[C'est l'histoire de Treasure Data, plateforme de données client financée par SoftBank. En février 2026, un seul ingénieur a construit Treasure Code — une interface en langage naturel pour leur CDP — e]]></description><link>https://blog.bguarisma.com/code-gratuit-gouvernance-chere-vibe-coding</link><guid isPermaLink="true">https://blog.bguarisma.com/code-gratuit-gouvernance-chere-vibe-coding</guid><category><![CDATA[AI Governance]]></category><category><![CDATA[Software Engineering]]></category><category><![CDATA[vibe coding]]></category><category><![CDATA[Productivity]]></category><dc:creator><![CDATA[Boris Guarisma]]></dc:creator><pubDate>Sat, 28 Feb 2026 14:54:59 GMT</pubDate><enclosure url="https://cdn.hashnode.com/uploads/covers/61bc41016916c3593a6b04c3/414741a6-8698-4ff2-b0a9-ee273d4a1bac.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>C'est l'histoire de Treasure Data, plateforme de données client financée par SoftBank. En février 2026, un seul ingénieur a construit Treasure Code — une interface en langage naturel pour leur CDP — en utilisant Claude Code. Le temps de développement : une heure. Le temps de gouvernance qui a rendu cette heure possible : plusieurs semaines.</p>
<p>Le ratio est d'environ 1:100. Pour chaque minute de code IA, cent minutes de gouvernance humaine.</p>
<p>C'est ce ratio qui m'intéresse. Pas parce qu'il est choquant — mais parce qu'il est structurel.</p>
<h2><strong>La thèse</strong></h2>
<p>Le coût de production du code tend vers zéro. Le coût de sa gouvernance — sécurité, audit, fiabilité, compliance — reste constant ou augmente. Et plus la production s'accélère, plus l'écart se creuse.</p>
<p>Ce n'est pas un problème technique. C'est un problème d'architecture organisationnelle. Et il concerne tous ceux qui décident d'adopter l'IA en entreprise — pas seulement les développeurs.</p>
<h2><strong>1. Le ratio 1:100 — Quand le code est gratuit mais la confiance est chère</strong></h2>
<p>Chez Treasure Data, le CISO, le CTO, les responsables d'ingénierie et le CPO ont passé des semaines à construire le cadre qui permettrait à un ingénieur de coder en 60 minutes. Le cadre, c'est : contrôle d'accès hérité de la plateforme, données personnelles non exposables, clés API non surfaçables, pipeline de validation à trois niveaux.</p>
<p>Le pipeline lui-même est un hack de gouvernance intéressant :</p>
<ul>
<li><p><strong>Niveau 1</strong> : un reviewer IA (lui-même construit avec Claude Code) évalue chaque pull request contre une checklist structurée — alignement architectural, compliance sécurité, couverture de tests.</p>
</li>
<li><p><strong>Niveau 2</strong> : le pipeline CI/CD classique — tests unitaires, intégration, analyse statique, linting.</p>
</li>
<li><p><strong>Niveau 3</strong> : review humaine, uniquement quand les niveaux 1 et 2 signalent un risque.</p>
</li>
</ul>
<p>Le principe opérationnel est formulé avec une clarté rare : <em>"AI writes code, but AI does not ship code."</em> L'IA produit — le pipeline décide si le produit passe. C'est une séparation des pouvoirs appliquée au code.</p>
<p>Mais ce qui a cassé est aussi instructif que ce qui a marché.</p>
<p>Treasure Data a rendu le produit accessible sans plan de lancement — pensant que personne ne le trouverait. Plus de 1 000 utilisateurs l'ont adopté en deux semaines, par découverte organique. La certification compliance n'était pas terminée. Le produit construit en 60 minutes a atteint les clients avant que la gouvernance ne soit prête.</p>
<p>Deuxième faille : quand les équipes non-techniques (commerciaux, gestionnaires de comptes) ont voulu contribuer, elles ont soumis des "skills" sans comprendre les critères d'approbation — créant un backlog ingérable. La démocratisation de la production de code sans l'expertise de jugement produit de la friction organisationnelle, pas de l'efficacité.</p>
<p>Le code était gratuit. La gouvernance était en retard. Et la production a débordé.</p>
<h2><strong>2. Ce qui marche en démo crashe à l'échelle</strong></h2>
<p>Anni Chen est tech lead chez Amazon, responsable des systèmes GenAI à grande échelle. Elle vibe code "chaque jour." Son témoignage, publié en février 2026, est précieux parce qu'il vient d'une praticienne senior à l'intérieur d'un hyperscaler — ni évangéliste, ni observateur extérieur.</p>
<p>Le gain est réel : un problème qui aurait pris une journée de recherche résolu en 15 minutes. Ratio 30:1 pour le brainstorming de solutions techniques. C'est cohérent avec les données macro : +4 % de productivité du travail dans l'UE selon le CEPR, +73 % dans les tâches cognitives de marketing selon Ju et Aral.</p>
<p>Mais Chen identifie la faille structurelle : <em>"Les LLM font des hypothèses implicites que vous ne réalisez pas qu'ils font."</em> Si vous ne spécifiez pas explicitement le multi-threading, le LLM produira "la version minimale qui marche" — et qui crashera à un million d'utilisateurs. Le prototype fonctionne en conditions contrôlées. La production révèle les contraintes que la démonstration masque.</p>
<p>Le non-technicien ne peut pas anticiper ces contraintes en amont. Le technicien anticipe proactivement. La différence n'est pas de vitesse. C'est de fiabilité à l'échelle.</p>
<p>Et la pression sociale rend la résistance impossible. Chen : <em>"Quand vos pairs l'utilisent et codent plus vite, c'est difficile de résister. Si vous ne suivez pas le rythme, la collaboration devient difficile."</em> Même ceux qui résistent "consomment de l'IA passivement" — les code reviews contiennent déjà du code généré par IA.</p>
<p>Steve Yegge, 40 ans de développement logiciel (Amazon, Google), auteur du livre <em>Vibe Coding</em>, complète le tableau avec un phénomène que personne ne mentionne : le drainage cognitif. <em>"Je me retrouve à faire des siestes en journée, et je parle à des amis en startups qui font pareil."</em> Sa conclusion opérationnelle : un employeur ne devrait pas attendre plus de <strong>3 heures productives par jour</strong> d'un ingénieur qui vibe-code à vitesse maximale. Le cerveau qui orchestre 4 à 10 agents simultanément s'épuise plus vite que celui qui code manuellement.</p>
<p>Les 100x de productivité sont réels. Mais ils tiennent dans 3 heures. Et ils nécessitent un expert pour les valider.</p>
<h2><strong>3. L'escalier qu'on supprime</strong></h2>
<p>En février 2026, le ministère des Finances irlandais a publié la première étude gouvernementale quantifiant l'impact de l'IA sur l'emploi par tranche d'âge. Le résultat est asymétrique.</p>
<p>Dans les secteurs à haut risque IA (tech, finance), l'emploi des 15-29 ans a chuté de <strong>20 %</strong> entre 2023 et 2025 — pendant que celui des 30-59 ans progressait de <strong>12 %</strong>. Dans les secteurs à faible exposition IA, c'est l'inverse : les jeunes progressent plus vite que les seniors.</p>
<p>Ce croisement élimine l'hypothèse d'un "problème jeunes" générique. C'est un effet IA spécifique aux secteurs knowledge-intensive. Le mécanisme : les tâches d'apprentissage — recherche, synthèse, premières analyses, code de base — sont exactement celles que l'IA automatise en premier. Les juniors n'entrent plus parce que les tâches de junior n'existent plus.</p>
<p>Et c'est là que le ratio 1:100 prend une dimension temporelle.</p>
<p>Si le code est gratuit, la valeur humaine migre vers la gouvernance — audit, review, compliance, jugement. Mais cette expertise de gouvernance ne s'apprend pas dans un cours. On l'apprend en faisant le travail de junior pendant des années. En revoyant du code, en comprenant pourquoi un système crashe à l'échelle, en absorbant les contraintes implicites que les LLM ne formalisent pas.</p>
<p>Chen formule une métaphore limpide : <em>"Le LLM présente tous les plats, et vous choisissez. Vous savez ce qui est bon parce que vous avez cuisiné."</em> Mais si personne ne cuisine plus — parce que le vibe coding élimine le besoin de cuisiner — qui formera les futurs goûteurs ?</p>
<p>L'escalier qui permettait de devenir senior est en train d'être supprimé. Et avec lui, le pipeline de formation des personnes capables de faire la gouvernance que le ratio 1:100 exige.</p>
<h2><strong>4. Le gaspillage invisible</strong></h2>
<p>Un dernier angle complète le tableau. Guillaume Besson, CPO, a récemment nommé un problème que les praticiens connaissent mais que personne ne mesure : le gaspillage de raisonnement dans les agents IA.</p>
<p>On empile les tokens. On multiplie les appels API. On se réjouit d'un résultat correct sans regarder le coût du chemin emprunté. Un agent qui résout une tâche en 50 boucles de raisonnement là où 3 suffiraient produit le bon résultat — mais à un coût 15 fois supérieur.</p>
<p>Quand le token est bon marché (Gemini Flash, DeepSeek V3), le gaspillage est invisible. Quand le modèle est premium (Claude Sonnet, GPT-5), il devient un gouffre. Et dans les deux cas, il complique la gouvernance : comment auditer un raisonnement de 50 boucles dont 47 sont du brassage d'air ?</p>
<p>Le gaspillage de raisonnement est la face computationnelle du ratio 1:100. Plus l'agent produit de tokens inutiles, plus la gouvernance humaine doit filtrer, plus le coût de supervision augmente.</p>
<h2><strong>En conclusion — La gouvernance est le nouveau métier</strong></h2>
<p>Je ne crois pas que le vibe coding soit un piège. Les gains de productivité sont réels — PIB américain +3,7 %, productivité UE +4 %, ratio 30:1 chez Amazon. Les rejeter serait absurde.</p>
<p>Mais je crois que l'industrie fait une erreur de cadrage. Elle célèbre la vitesse de production — 60 minutes, 15 minutes, 6 600 commits — et sous-estime le coût de tout ce qui entoure la production : la gouvernance, la compliance, le jugement humain, la formation des personnes capables de juger.</p>
<p>Le ratio 1:100 de Treasure Data n'est pas une anomalie. C'est le vrai prix du code gratuit. Et ce prix ne baisse pas avec le temps — il augmente, parce que :</p>
<p>NaN.  <strong>La production s'accélère</strong> — plus de code signifie plus de surface à auditer</p>
<p>NaN.  <strong>Les non-techniciens entrent dans la boucle</strong> — la démocratisation multiplie les contributions à gouverner</p>
<p>NaN.  <strong>L'escalier d'apprentissage disparaît</strong> — les futurs gouverneurs ne sont plus formés</p>
<p>NaN.  <strong>Le gaspillage de raisonnement alourdit la charge de supervision</strong></p>
<p>Treasure Data a compris quelque chose que beaucoup d'organisations n'ont pas encore vu : <em>"AI writes code, but AI does not ship code."</em> La valeur n'est plus dans l'écriture. Elle est dans le droit de publier.</p>
<p>Pour les organisations, la question n'est plus "faut-il adopter le vibe coding ?" — la pression sociale rend cette question obsolète. La question est : avons-nous investi dans la gouvernance au même rythme que dans la production ?</p>
<p>Pour la plupart, la réponse est non.</p>
<hr />
<p><strong>Sources :</strong></p>
<ul>
<li><p>Sean Michael Kerner, <em>"One engineer made a production SaaS product in an hour: here's the governance system that made it possible"</em>, VentureBeat, 23 février 2026</p>
</li>
<li><p>Anni Chen (recueilli par Lee Chong Ming), <em>"I'm an Amazon tech lead who uses AI to write code daily. There's one situation I hesitate to use it in"</em>, Business Insider, 18 février 2026</p>
</li>
<li><p>Gergely Orosz, <em>"Steve Yegge on AI Agents and the Future of Software Engineering"</em>, The Pragmatic Engineer, 10 février 2026</p>
</li>
<li><p>Padraic Halpin, <em>"AI adoption already hitting Irish graduate jobs, finance department says"</em>, Reuters, 18 février 2026</p>
</li>
<li><p>Guillaume Besson, <em>"L'efficience n'est pas la performance"</em>, LinkedIn, février 2026</p>
</li>
<li><p>Erik Brynjolfsson, <em>"The AI productivity take-off is finally visible"</em>, Financial Times, 15 février 2026</p>
</li>
<li><p>Inaki Aldasoro et al., <em>"How AI is affecting productivity and jobs in Europe"</em>, CEPR, 17 février 2026</p>
</li>
</ul>
]]></content:encoded></item><item><title><![CDATA[Quand l'IA rencontre la physique (et perd)]]></title><description><![CDATA[Un grand modèle de langage ne sait rien de votre métier. Ce qui le rend utile en industrie critique, ce n'est pas sa puissance — c'est l'architecture de données qui le contraint. J'ai construit un Kno]]></description><link>https://blog.bguarisma.com/quand-ia-rencontre-physique-et-perd</link><guid isPermaLink="true">https://blog.bguarisma.com/quand-ia-rencontre-physique-et-perd</guid><category><![CDATA[knowledge graph]]></category><category><![CDATA[data-engineering]]></category><category><![CDATA[AI]]></category><category><![CDATA[Databases]]></category><dc:creator><![CDATA[Boris Guarisma]]></dc:creator><pubDate>Thu, 19 Feb 2026 10:08:07 GMT</pubDate><enclosure url="https://cloudmate-test.s3.us-east-1.amazonaws.com/uploads/covers/61bc41016916c3593a6b04c3/c3e52855-079e-473e-8e23-1c898591abb2.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Un grand modèle de langage ne sait rien de votre métier. Ce qui le rend utile en industrie critique, ce n'est pas sa puissance — c'est l'architecture de données qui le contraint. J'ai construit un Knowledge Graph pour la surveillance de réacteurs nucléaires. Voici ce que cette expérience m'a appris sur l'endroit où se cache réellement l'intelligence d'un système IA.</p>
<hr />
<h2>1. Le constat : l'IA ne sait rien</h2>
<p>Demandez à un LLM généraliste quel capteur surveiller quand la température primaire d'un réacteur nucléaire semble instable. Il vous répondra avec assurance. Il inventera un code capteur plausible. Il citera peut-être une norme ISO qui n'existe pas. Et il aura tort — de manière indétectable par un non-expert.</p>
<p>Ce n'est pas un défaut du modèle. C'est une conséquence structurelle de son architecture. Un LLM prédit le token suivant le plus probable. Il ne <em>sait</em> rien du domaine. Il n'a aucune représentation interne de ce qu'est un capteur, de ce que mesure ce capteur, de la loi physique qui relie cette mesure à une autre. Sans cette structure, il ne lui reste que la vraisemblance statistique — et en industrie critique, la vraisemblance tue.</p>
<p>Le réflexe courant est de demander un modèle plus puissant, plus de paramètres, plus de données d'entraînement. C'est la mauvaise question. Le problème n'est pas la puissance du modèle. C'est l'absence d'une architecture de données qui encode la connaissance de votre domaine — ce que la discipline appelle une <em>ontologie</em>.</p>
<hr />
<h2>2. Ce que les grands groupes ont appris</h2>
<p>Avant de concevoir quoi que ce soit, j'ai étudié ce que faisaient ceux qui ont vingt ans d'avance. L'ingénierie ontologique n'est pas une invention récente. C'est une discipline formelle, avec ses méthodologies, ses standards et ses retours d'expérience industriels. Trois principes m'ont guidé.</p>
<p><strong>Le Middle-Out (Airbus).</strong> Quand Airbus a voulu structurer les exigences de conception de ses ailes d'avion (projet OntoREM), les équipes n'ont ni commencé par une modélisation abstraite du monde (approche descendante, trop lente), ni par un inventaire exhaustif de toutes les données (approche ascendante, trop chaotique). Elles sont parties des concepts que les ingénieurs manipulent au quotidien — "maintenance", "réparabilité", "inspection" — puis ont généralisé vers le haut et spécialisé vers le bas. C'est l'approche Middle-Out : on ancre l'ontologie dans le vocabulaire réel de l'expert métier, pas dans une vision philosophique du réel ni dans les colonnes d'un tableur.</p>
<p><strong>L'Engagement Ontologique Minimal (EDF).</strong> Le principe MOC (<em>Minimal Ontological Commitment</em>) stipule qu'une ontologie ne doit contenir que les axiomes strictement nécessaires au besoin. Chaque concept supplémentaire alourdit le raisonnement et ralentit le système. Dans le nucléaire, un exploitant du parc français utilise des ontologies OWL DL pour l'analyse automatisée des modes de défaillance (FMEA) : le raisonneur déduit les conséquences systémiques d'une panne en parcourant le graphe. Si l'ontologie est "obèse" — trop de concepts, trop de relations — le raisonneur ne termine pas en temps utile. Le MOC est la discipline qui empêche cela.</p>
<p><strong>La réingénierie de l'existant (TotalEnergies).</strong> Le framework NeOn, appliqué par TotalEnergies via sa suite SousLeSens, part d'un constat pragmatique : le développement ontologique <em>ex nihilo</em> est un échec économique. L'industrie possède déjà des standards (ISO 15926), des schémas de bases de données, des taxonomies internes. Le Scénario 2 de NeOn consiste à transformer ces ressources non-ontologiques en modèles formels. On ne repart pas de zéro — on fédère l'existant.</p>
<p>Ces trois principes partagent une conviction : la sobriété est une architecture. Il ne s'agit pas de tout modéliser, mais de modéliser <em>juste assez</em> pour que le raisonnement fonctionne. C'est exactement ce que j'ai tenté d'appliquer.</p>
<hr />
<h2>3. Trois modules pour modéliser la physique d'un réacteur</h2>
<p>Quand j'ai conçu le Knowledge Graph pour la surveillance de réacteurs nucléaires, j'ai appliqué le Middle-Out à la lettre : je suis parti de ce que l'ingénieur de surveillance pense quand il travaille. Il pense "puissance", "température primaire", "bore", "phase opérationnelle". Il ne pense pas en termes de schéma relationnel, ni en termes de colonnes Parquet.</p>
<p>L'ontologie tient en trois modules — conformément au MOC, pas un de plus que nécessaire.</p>
<p><strong>Module A — Les actifs (la structure).</strong> Un réacteur appartient à un palier technologique (900 MWe, 1300 MWe, 1450 MWe). Le palier détermine le nombre de boucles primaires, donc le nombre de capteurs de température. C'est une contrainte physique, pas un paramètre configurable. Le graphe modélise : <code>ReactorUnit → belongsToPalier → ReactorModel</code>. Cinq types de composants, pas deux cents. Le MOC en action.</p>
<p><strong>Module B — La métrologie (la mesure).</strong> Chaque capteur est lié au paramètre physique qu'il mesure, avec ses métadonnées : code technique, colonne dans le Data Lake, phase opérationnelle privilégiée. La relation clé est <code>Sensor → measures → PhysicalParameter</code>. C'est elle qui permet la résolution sémantique — transformer le mot "puissance" prononcé par l'ingénieur en code capteur technique. Les alias ("puissance", "power", "neutronique") sont stockés dans le graphe, pas dans le prompt du LLM. Si demain le vocabulaire change, on modifie le graphe. Ni le code, ni le modèle ne bougent.</p>
<p><strong>Module C — La physique (les lois).</strong> C'est ici que l'ontologie devient "intelligente". Les paramètres physiques sont reliés par des règles causales, issues de la modélisation physique du réacteur : la concentration en bore <em>inhibe</em> la puissance neutronique ; la position des grappes de contrôle <em>détermine</em> le déséquilibre axial ; la phase opérationnelle <em>active</em> ou <em>désactive</em> certains paramètres. Ces relations — <code>inhibits</code>, <code>determines</code>, <code>enables</code>, <code>disables</code> — ne sont pas des corrélations statistiques. Ce sont des lois de la physique, codées comme des arêtes du graphe.</p>
<p>Pourquoi trois modules et pas un seul ? La modularité est un principe de l'IOF (<em>Industrial Ontologies Foundry</em>) : chaque module évolue indépendamment. Quand un nouveau palier de réacteur entre en service, seul le Module A change. Quand une nouvelle règle physique est identifiée, seul le Module C est mis à jour. Le reste du système continue de fonctionner.</p>
<p>Les conventions de nommage suivent le standard IOF : classes en PascalCase (<code>NeutronicPower</code>, <code>ControlRod</code>), propriétés en camelCase (<code>monitorsParam</code>, <code>hasOperationalPhase</code>). Ce n'est pas du formalisme pour le plaisir — c'est ce qui permet à un raisonneur automatique de traiter le graphe sans ambiguïté, et à un ingénieur qui n'a pas conçu l'ontologie de la comprendre en la lisant.</p>
<hr />
<h2>4. "Affiche la puissance" — Anatomie d'une requête en quatre couches</h2>
<p>La meilleure façon de montrer ce que fait le Knowledge Graph, c'est de tracer une requête réelle, couche par couche. Un ingénieur tape en langage naturel : <em>"Affiche la puissance sur [un réacteur du parc]."</em></p>
<p><strong>Couche 1 — Le vocabulaire humain.</strong> Le LLM analyse la phrase. Il identifie un concept : "puissance". Il ne connaît pas encore le code technique du capteur. Mais il sait qu'il doit interroger le graphe.</p>
<p><strong>Couche 2 — Le concept physique.</strong> Le graphe reçoit le mot "puissance" et le cherche dans les alias des paramètres physiques. Grâce à la liste d'alias enrichie en amont (<code>['puissance', 'power', 'neutronique']</code>), le mot humain est résolu vers le concept formel <code>NeutronicPower</code> — puis vers le capteur qui mesure ce paramètre. Ce n'est pas une recherche textuelle dans une base SQL. C'est une navigation sémantique : le graphe <em>comprend</em> que "puissance" et "neutronique" réfèrent au même paramètre physique.</p>
<p><strong>Couche 3 — L'enrichissement contextuel.</strong> Le système ne renvoie pas un code brut. Il interroge le contexte autour du capteur : que mesure-t-il ? Quelles règles physiques le gouvernent ? Quels autres capteurs sont corrélés ? Le graphe renvoie que ce capteur correspond à la "chaîne neutronique large échelle" — pas juste un identifiant, mais une <em>donnée qualifiée</em>.</p>
<p><strong>Couche 4 — La donnée brute.</strong> Le noeud <code>Sensor</code> dans le graphe contient une propriété <code>dataColumnGold</code> : l'adresse exacte de la donnée dans le Data Lake Parquet. L'assistant utilise cette adresse pour extraire les séries temporelles via DuckDB — en sub-seconde, zero-copy, 32 threads.</p>
<p>Le système a traversé : <strong>Vocabulaire humain → Concept physique → Capteur réel → Donnée brute.</strong> Quatre couches, chacune déterministe et traçable. À aucun moment le LLM n'a "deviné" le code capteur. À aucun moment il n'a halluciné. Chaque étape est explicable : on sait <em>pourquoi</em> "puissance" a mené à tel capteur, via quelle relation du graphe.</p>
<p>Et c'est précisément le point. La valeur n'est pas dans le LLM — un des modèles les plus légers du marché suffit. La valeur est dans l'ontologie qui le contraint.</p>
<hr />
<h2>Conclusion : les fondations ne changent jamais</h2>
<p>Ce blog s'appelle <em>Foundations</em>. Quatre couches d'expertise empilées : les données, l'optimisation, la physique, la lucidité. J'ai commencé en 2021 par des tutoriels de séries temporelles en R (Couche 1). Quatre ans plus tard, je rédige des essais sur les limites de l'IA (Couche 4).</p>
<p>Ce que cette expérience m'a appris : même à la Couche 4, c'est la Couche 1 qui fait la différence. Le Knowledge Graph n'est pas une "fonctionnalité IA". C'est une architecture de données — un modèle formel qui encode les lois de votre métier avant que le moindre algorithme n'entre en jeu.</p>
<p>Un petit modèle contraint par une ontologie bien conçue surpasse un modèle puissant sans structure. L'intelligence d'un système IA industriel ne réside pas dans ses paramètres. Elle réside dans ses données — et dans la rigueur avec laquelle on les a structurées.</p>
<p>On ne s'évade jamais des fondations.</p>
<hr />
<h3>Sources</h3>
<ul>
<li><p>Airbus/UWE, <em>OntoREM — Ontology-driven Requirements Engineering Methodology</em>, IEEE, 2009</p>
</li>
<li><p>TotalEnergies, <em>SousLeSens — A Comprehensive Suite for the Industrial Practice of Semantic Knowledge Graphs</em>, ESWC, 2024</p>
</li>
<li><p>EDF/Oxford, <em>The Energy Management Adviser at EDF</em>, Proceedings of the ISWC, 2013</p>
</li>
<li><p>Thales, <em>Towards the Deployment of Knowledge Based Systems in Safety-Critical Systems</em>, ESWC, 2023</p>
</li>
<li><p>Industrial Ontologies Foundry (IOF), <em>Technical Principles</em>, NIST/OAGi</p>
</li>
<li><p><em>AI- and Ontology-Based Enhancements to FMEA for Advanced Systems Engineering</em>, arXiv, 2024</p>
</li>
</ul>
]]></content:encoded></item><item><title><![CDATA[La Fin de l'Illusion Scolaire]]></title><description><![CDATA[Sommaire

Introduction

Partie 1 : Le Syndrome du "Bachotage" (L'Analogie de Sutskever)

Partie 2 : L'Aveu des Créateurs (L'Architecture de la Vitesse)

Partie 3 : Le Mur du Réel (L'Intelligence Spati]]></description><link>https://blog.bguarisma.com/la-fin-de-lillusion-scolaire</link><guid isPermaLink="true">https://blog.bguarisma.com/la-fin-de-lillusion-scolaire</guid><category><![CDATA[worldmodels]]></category><category><![CDATA[agi]]></category><category><![CDATA[AI world models]]></category><category><![CDATA[ai learning]]></category><dc:creator><![CDATA[Boris Guarisma]]></dc:creator><pubDate>Thu, 29 Jan 2026 18:29:30 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1769711149858/cec30cd9-0833-4bfa-95b3-bdd36dc7019a.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<h3>Sommaire</h3>
<ol>
<li><p><strong>Introduction</strong></p>
</li>
<li><p><strong>Partie 1 : Le Syndrome du "Bachotage" (L'Analogie de Sutskever)</strong></p>
</li>
<li><p><strong>Partie 2 : L'Aveu des Créateurs (L'Architecture de la Vitesse)</strong></p>
</li>
<li><p><strong>Partie 3 : Le Mur du Réel (L'Intelligence Spatiale)</strong></p>
</li>
<li><p><strong>Conclusion du Dossier : La Fin de l'Enfance</strong></p>
</li>
<li><p><strong>Le Glossaire Décomplexé</strong></p>
</li>
<li><p><strong>Sources &amp; Lectures Recommandées</strong></p>
</li>
</ol>
<h3><strong>Introduction</strong></h3>
<p>En janvier 2026, nous faisons face à un paradoxe qui hante la Silicon Valley. Sur le papier, les Intelligences Artificielles n'ont jamais été aussi "intelligentes". Elles écrasent les examens (<strong>Evals</strong>), réussissent le concours du Barreau et résolvent des olympiades de mathématiques.</p>
<p>Pourtant, dès qu'il s'agit de s'intégrer dans l'économie réelle, le "premier de la classe" trébuche. Selon les dernières données, 95 % des projets d'IA en entreprise échouent à passer en production. Ilya Sutskever, co-fondateur d'OpenAI, résumait récemment ce malaise : "L'impact économique est dramatiquement en retard sur la performance aux examens".</p>
<p><strong>Comment expliquer ce décalage ?</strong></p>
<p>Nous avons commis une erreur classique de confusion entre la carte et le territoire. Nous avons construit des modèles excellents pour "bachoter" (mémoriser des réponses pour réussir un test), mais fragiles dès qu'il faut improviser face à la complexité du monde réel.</p>
<p>Dans ce dossier, nous allons déconstruire cette "illusion scolaire" en trois temps :</p>
<ol>
<li><p><strong>Le Symptôme :</strong> L'analogie de l'étudiant qui a tout appris par cœur mais ne comprend rien (Ilya Sutskever).</p>
</li>
<li><p><strong>La Cause :</strong> L'aveu des créateurs du "Transformer" : nous avons construit des machines pour la vitesse, pas pour le raisonnement.</p>
</li>
<li><p><strong>La Réalité :</strong> Pourquoi l'avenir de l'IA ne se joue plus dans les mots, mais dans le monde physique.</p>
</li>
</ol>
<p>Bienvenue dans l'ère de la lucidité.</p>
<hr />
<h3>Partie 1 : Le Syndrome du "Bachotage" (L'Analogie de Sutskever)</h3>
<p>Pour comprendre pourquoi l'IA trébuche dans l'économie réelle, il faut écouter Ilya Sutskever. Lors d'une interview rare en novembre 2025, il a utilisé une analogie simple pour expliquer ce paradoxe : la différence entre <strong>réussir un examen</strong> et <strong>maîtriser un métier</strong>.</p>
<p>Imaginez deux étudiants qui se préparent pour un concours de programmation :</p>
<ul>
<li><p><strong>L'Étudiant n°1 (L'IA Actuelle) :</strong> Il s'entraîne pendant 10 000 heures. Il mémorise absolument tous les problèmes passés, toutes les techniques de preuve et toutes les variantes possibles. Il devient une encyclopédie vivante du concours. Le jour de l'examen, il obtient un score parfait.</p>
</li>
<li><p><strong>L'Étudiant n°2 (L'Humain / L'AGI) :</strong> Il s'entraîne beaucoup moins, peut-être 100 heures. Il ne connaît pas tout par cœur, mais il a compris la logique fondamentale ("the it factor"). Il réussit aussi l'examen, peut-être avec un score légèrement inférieur,.</p>
</li>
</ul>
<p><strong>La Question qui tue :</strong> Lequel des deux aura la meilleure carrière professionnelle 10 ans plus tard ?</p>
<p>Sutskever est formel : c'est l'étudiant n°2.</p>
<p><strong>Pourquoi ?</strong> Parce que l'économie réelle n'est pas un examen standardisé. L'étudiant n°1 (nos modèles actuels) a optimisé la <strong>Forme</strong> (le score au test) en "sur-apprenant" des cas spécifiques,. Dès qu'il sort du cadre prévu (le "territoire" réel), il s'effondre car il ne possède pas la compétence fluide nécessaire pour généraliser.</p>
<p>C'est ce qui explique le phénomène de "jaggedness" (performance en dents de scie) : une IA peut écrire un sonnet parfait, mais échouer à corriger un bug simple sans en créer un nouveau, car elle ne <em>comprend</em> pas le code, elle prédit statistiquement la solution la plus probable basée sur son "bachotage" intensif.</p>
<p><strong>En résumé :</strong> Nous avons confondu la capacité de mémorisation (10 000 heures de données) avec l'intelligence (la capacité d'adaptation).</p>
<hr />
<h3>Partie 2 : L'Aveu des Créateurs (L'Architecture de la Vitesse)</h3>
<p>Si l'IA actuelle est cet étudiant qui "bachote" sans comprendre, ce n'est pas un accident. C'est un choix de conception. Pour comprendre pourquoi, il faut écouter Llion Jones, le co-auteur du papier fondateur de Google (<em>Attention Is All You Need</em>) qui a lancé la révolution de l'IA générative.</p>
<p>En janvier 2026, lors de la conférence DLD à Munich, il a fait un aveu surprenant : le "Transformer" (le moteur de ChatGPT) n'était pas une percée cognitive pour simuler l'intelligence. C'était une <strong>percée d'ingénierie</strong> pour satisfaire le matériel.</p>
<p><strong>Le Péché Originel : Optimiser pour le GPU, pas pour le Cerveau</strong></p>
<p>Avant 2017, les IA lisaient le texte comme nous : mot après mot, séquentiellement. C'était logique, mais trop lent pour les processeurs graphiques (GPU) qui aiment traiter des milliards de chiffres simultanément.</p>
<p>L'équipe de Google a donc posé un problème purement logistique : "Comment pousser les données à travers le matériel le plus vite possible ?".</p>
<p>La solution fut le <strong>Transformer</strong>. Au lieu de lire une phrase mot à mot, cette architecture permet d'ingérer tout le texte d'un coup, en parallèle.</p>
<ul>
<li><p><strong>Le résultat :</strong> Une capacité d'ingestion de données phénoménale, permettant d'apprendre par cœur tout Internet.</p>
</li>
<li><p><strong>Le coût caché :</strong> Nous avons construit une machine optimisée pour la <strong>vitesse de traitement</strong>, en supposant que l'intelligence émergerait automatiquement de la quantité de données.</p>
</li>
</ul>
<p><strong>La conséquence : Le Raisonnement par le "Perroquet"</strong></p>
<p>C'est ici que le bât blesse. Comme l'explique Jones, ces modèles sont forcés de "raisonner" en prédisant le mot suivant. Imaginez devoir résoudre un problème de maths complexe, mais avec une contrainte : vous ne pouvez pas réfléchir en silence dans votre tête. Vous devez écrire votre pensée mot après mot, sans pause, et chaque mot écrit détermine le suivant.</p>
<p>C'est un "goulot d'étranglement" artificiel. L'IA ne simule pas le monde ; elle simule la probabilité du langage qui décrit le monde. Elle n'a pas appris la physique ou la logique, elle a appris la grammaire de la physique et la syntaxe de la logique.</p>
<p>Nous avons confondu la <strong>carte</strong> (le texte) avec le <strong>territoire</strong> (la réalité).</p>
<hr />
<h3>Partie 3 : Le Mur du Réel (L'Intelligence Spatiale)</h3>
<p>Si le langage n'est pas le mécanisme fondamental de la pensée, qu'est-ce qui l'est ? Pour répondre à cette question, il faut se tourner vers Fei-Fei Li, la "marraine de l'IA" qui a permis l'essor du Deep Learning avec ImageNet.</p>
<p>Dans son essai de novembre 2025, <em>From Words to Worlds</em>, elle pose un diagnostic sans appel sur les modèles actuels : ce sont des <strong>"forgerons de mots dans le noir"</strong> (<em>wordsmiths in the dark</em>). Ils sont éloquents, mais aveugles. Ils manipulent des concepts sans jamais avoir fait l'expérience de la réalité physique qu'ils décrivent.</p>
<p><strong>La leçon de l'évolution : Voir avant de Parler</strong></p>
<p>Fei-Fei Li nous rappelle un principe premier biologique : l'évolution n'a pas commencé par le langage. Pendant des centaines de millions d'années, l'intelligence s'est construite sur une boucle <strong>Perception-Action</strong>. Voir un obstacle, évaluer une distance, agir pour survivre. Le langage est venu bien plus tard, comme une couche d'abstraction posée sur cet échafaudage spatial.</p>
<p>L'erreur de l'industrie a été de vouloir construire le toit (le langage) sans les fondations (l'intelligence spatiale). C'est pourquoi nos modèles hallucinent : ils n'ont pas de "point d'ancrage" dans le monde réel. Ils connaissent le mot "chute", mais ils ne comprennent pas la gravité.</p>
<p><strong>La Solution : Les "World Models"</strong></p>
<p>La prochaine frontière n'est donc pas un chatbot qui parle mieux, mais une IA qui <strong>voit et simule</strong> le monde. C'est ce que Li appelle les "World Models". Contrairement aux LLM qui prédisent le mot suivant, ces modèles prédisent l'état futur du monde physique : "Si je lâche ce verre, il va tomber et se briser".</p>
<h3>Conclusion du Dossier : La Fin de l'Enfance</h3>
<p>En résumé, le décalage observé par Ilya Sutskever entre les examens (<em>evals</em>) et l'économie réelle s'explique simplement :</p>
<ol>
<li><p><strong>Le Symptôme :</strong> Nous avons créé des étudiants brillants qui bachotent sans comprendre (Sutskever).</p>
</li>
<li><p><strong>La Cause :</strong> Nous les avons forcés à penser uniquement à travers des séquences de mots pour satisfaire nos processeurs (Jones).</p>
</li>
<li><p><strong>L'Avenir :</strong> L'IA ne deviendra économiquement viable que lorsqu'elle sortira du texte pour comprendre la physique du monde (Li).</p>
</li>
</ol>
<p>L'année 2026 ne sera pas celle où l'IA apprendra à mieux écrire, mais celle où elle devra apprendre à <strong>vivre</strong> dans notre réalité.</p>
<hr />
<h3>Le Glossaire Décomplexé</h3>
<p>• <strong>Evals (Évaluations / Benchmarks) :</strong> Ce sont les "examens scolaires" de l'IA. Imaginez le Bac ou le concours du Barreau. Quand on dit qu'un modèle "réussit les evals", cela signifie qu'il a obtenu un score élevé à un test standardisé. Le piège ? Comme un étudiant qui bachote, réussir l'examen ne garantit pas qu'on saura faire le métier dans la vraie vie.</p>
<p>• <strong>Training vs Inférence :</strong></p>
<p>    ◦ <strong>Training (Entraînement) :</strong> C'est la phase d'apprentissage (lire toute la bibliothèque). C'est long et coûteux.</p>
<p>    ◦ <strong>Inférence :</strong> C'est le moment où l'IA répond à votre question. C'est l'usage au quotidien.</p>
<p>• <strong>Transformer :</strong> C'est l'architecture logicielle (le "moteur") inventée par Google en 2017 qui a permis ChatGPT. Important : elle a été conçue pour traiter des données <em>vite</em> (ingénierie), pas forcément pour <em>penser</em> mieux.</p>
<p>• <strong>RL (Reinforcement Learning / Apprentissage par Renforcement) :</strong> La méthode où l'on "récompense" l'IA quand elle donne la bonne réponse. Le risque est le "Reward Hacking" : l'IA apprend à tricher pour avoir la récompense sans résoudre le problème de fond.</p>
<hr />
<h3>Sources &amp; Lectures Recommandées</h3>
<p>Pour aller plus loin dans la compréhension des mécanismes fondamentaux discutés dans cet article, voici les documents originaux sur lesquels s'appuie notre analyse.</p>
<p><strong>1. Sur le "Mur du Scaling" et le paradoxe des Evals (Le Symptôme)</strong></p>
<p>• <strong>Source :</strong> <em>We're moving from the age of scaling to the age of research</em></p>
<p>• <strong>Intervenant :</strong> Ilya Sutskever (Co-fondateur, SSI)</p>
<p>• <strong>Contexte :</strong> Dwarkesh Podcast, 25 Novembre 2025</p>
<p>• <strong>Pourquoi lire/écouter :</strong> Pour comprendre l'analogie fondamentale des "deux étudiants" et pourquoi réussir un test ne signifie pas posséder une compétence économique réelle,.</p>
<p><strong>2. Sur les limites architecturales des LLM (La Cause)</strong></p>
<p>• <strong>Source :</strong> <em>Closing the Intelligence Gap: The Next Wave of AI Breakthroughs</em></p>
<p>• <strong>Intervenant :</strong> Llion Jones (Co-auteur du papier "Attention Is All You Need", Sakana AI)</p>
<p>• <strong>Contexte :</strong> Conférence DLD26 (Munich), 22 Janvier 2026</p>
<p>• <strong>Pourquoi lire/écouter :</strong> Pour découvrir l'aveu technique selon lequel le Transformer a été conçu pour satisfaire le matériel (GPU), et non pour résoudre l'intelligence,.</p>
<p><strong>3. Sur l'Intelligence Spatiale et les "World Models" (La Solution)</strong></p>
<p>• <strong>Source :</strong> <em>From Words to Worlds: Spatial Intelligence is AI’s Next Frontier</em></p>
<p>• <strong>Auteur :</strong> Dr. Fei-Fei Li (World Labs)</p>
<p>• <strong>Contexte :</strong> Essai publié sur Substack, 10 Novembre 2025</p>
<p>• <strong>Pourquoi lire :</strong> Pour saisir pourquoi l'avenir de l'IA passe par la vision et la 3D, et pourquoi les modèles actuels sont des "forgerons de mots dans le noir",.</p>
<p><strong>4. Données économiques et contexte de marché</strong></p>
<p>• <strong>Source :</strong> <em>They Said AI Would Replace You By Now</em></p>
<p>• <strong>Auteur :</strong> Vanessa Wingårdh (TechButMakeItReal)</p>
<p>• <strong>Contexte :</strong> Analyse vidéo, 4 Janvier 2026</p>
<p>• <strong>Pourquoi regarder :</strong> Pour les statistiques sur le taux d'échec des projets IA en entreprise (95% selon le MIT) et le décalage entre les promesses marketing et la réalité de l'emploi,.</p>
]]></content:encoded></item><item><title><![CDATA[L'AGI 2025 : La fracture entre la physique du réel et la magie du bilan]]></title><description><![CDATA[Nous sommes en décembre 2025. Si vous écoutez les bruits de couloir de la Silicon Valley, vous entendez deux mélodies radicalement opposées. D'un côté, les PDG des plus grandes entreprises d'IA — Sam ]]></description><link>https://blog.bguarisma.com/lagi-2025-la-fracture-entre-la-physique-du-reel-et-la-magie-du-bilan</link><guid isPermaLink="true">https://blog.bguarisma.com/lagi-2025-la-fracture-entre-la-physique-du-reel-et-la-magie-du-bilan</guid><category><![CDATA[agi]]></category><dc:creator><![CDATA[Boris Guarisma]]></dc:creator><pubDate>Sat, 27 Dec 2025 23:31:13 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1766928148792/2708d87e-7f60-4c72-8fbe-459c23d5fb14.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Nous sommes en décembre 2025. Si vous écoutez les bruits de couloir de la Silicon Valley, vous entendez deux mélodies radicalement opposées. D'un côté, les PDG des plus grandes entreprises d'IA — Sam Altman, Elon Musk, Dario Amodei — nous assurent que la "Superintelligence" est imminente, probablement pour 2026 ou 2027. De l'autre, les pères fondateurs de la discipline, comme Yann LeCun ou Demis Hassabis, tempèrent : nous sommes encore à 5 ou 10 ans du but, et il manque des pièces fondamentales au puzzle.</p>
<p>Comment expliquer une telle divergence sur une même réalité technologique ? Après avoir analysé les rapports financiers, les interviews techniques et les mouvements de capitaux de cette fin d'année, une conclusion s'impose. Ce n'est pas un simple débat d'experts. C'est une fracture structurelle entre deux mondes : la <strong>Physique</strong> (la difficulté réelle de la recherche) et la <strong>Finance</strong> (la nécessité absolue de justifier des investissements colossaux).</p>
<p>Voici pourquoi nous n'avons peut-être pas acheté une technologie, mais une narrative.</p>
<h3>1. La Magie du Bilan : L'AGI comme "Permission de perdre de l'argent"</h3>
<p>Pour comprendre l'urgence affichée par Sam Altman ou Elon Musk, il ne faut pas regarder leur code, mais leur comptabilité. En cette fin 2025, une anomalie financière est devenue trop grosse pour être ignorée.</p>
<p>Des entreprises comme OpenAI sont souvent comparées à des éditeurs de logiciels (SaaS) classiques. C'est une erreur fondamentale. Un SaaS a des marges infinies : une fois le logiciel créé, le vendre à un client de plus ne coûte presque rien. Or, l'IA est une industrie lourde. Chaque requête coûte de l'énergie et du calcul. Les chiffres sont brutaux : pour 4 milliards de revenus, OpenAI affiche environ 5 milliards de pertes.</p>
<p>Dans n'importe quel autre secteur, un modèle perdant 53% de son revenu serait sanctionné. Mais ici, le terme "AGI" agit comme un bouclier magique. Comme le notent les analystes financiers, la narrative de l'AGI permet d'acheter la <em>"permission de perdre de l'argent à une échelle extraordinaire"</em>.</p>
<p>Ce système est soutenu par une "économie circulaire" fascinante. Nvidia investit dans des néo-clouds (comme CoreWeave ou Nebius), qui utilisent cet argent pour... acheter des puces Nvidia, gonflant ainsi le chiffre d'affaires du fabricant. Tant que la promesse d'une superintelligence imminente tient, cette boucle de financement tourne. Si l'horizon s'éloigne, la bulle de la dette risque d'éclater. C'est pourquoi les "vendeurs" sont condamnés à l'optimisme : leur survie financière en dépend.</p>
<h3>2. La Physique du Réel : La fin de l'illusion du Scaling</h3>
<p>Pendant que la finance exige de la vitesse, la science se heurte aux murs de la réalité. Ilya Sutskever, co-fondateur d'OpenAI et désormais à la tête de SSI, a jeté un pavé dans la mare en novembre : l'ère du simple "Scaling" (ajouter toujours plus de données et de puces) est terminée. Nous entrons dans "l'âge de la Recherche".</p>
<p>Les modèles actuels, aussi impressionnants soient-ils, restent des <em>"forgerons de mots dans le noir"</em>. Ils peuvent écrire un poème sur la gravité, mais peinent à prédire la chute d'un verre d'eau. Fei-Fei Li, la créatrice d'ImageNet, nous rappelle une vérité biologique : l'intelligence ne naît pas du langage, mais de l'interaction spatiale avec le monde physique.</p>
<p>C'est là que réside la fracture. Les modèles actuels excellent à simuler la compétence (réussir un examen), mais échouent souvent dans la robustesse opérationnelle (le modèle corrige un bug pour en créer un autre). Pour Yann LeCun ou Demis Hassabis, atteindre une véritable intelligence générale nécessitera de nouvelles architectures capables de comprendre la cause et l'effet, et pas seulement de prédire le mot suivant. Ces percées ne se décrètent pas sur un calendrier trimestriel.</p>
<h3>3. La Dilution Stratégique : Changer la définition pour gagner la course</h3>
<p>Face à l'impossibilité physique de livrer une "Intelligence de niveau humain" dans les délais promis aux investisseurs (2026-2027), nous assistons à une manœuvre subtile : la redéfinition de l'objectif.</p>
<p>Regardez bien les déclarations récentes. Sam Altman et Dario Amodei (Anthropic) ont "silencieusement dilué" la définition de l'AGI. Ils ne parlent plus d'une conscience ou d'une polyvalence totale, mais d'un "stagiaire intelligent" ou d'un "assistant de recherche" capable de synthétiser de la littérature.</p>
<p>Pourquoi ce glissement sémantique ? Parce que c'est un objectif atteignable à court terme. En réduisant la barre, ils peuvent déclarer victoire, satisfaire les marchés et justifier les milliards engloutis, même si la technologie reste fondamentalement un outil statistique très performant plutôt qu'une nouvelle espèce intelligente.</p>
<h3>Conclusion : N'achetez pas la narrative, achetez la physique</h3>
<p>En 2026, la vraie révolution ne sera peut-être pas celle qu'on nous vend. Pendant que les regards sont fixés sur une hypothétique AGI, une transformation bien plus tangible s'opère : l'émergence de "l'IA Physique" et la souveraineté des infrastructures.</p>
<p>La leçon de cette fin d'année 2025 est claire : l'horizon court (l'imminence) est un impératif financier pour ceux qui vendent des pelles et des rêves. L'horizon long (la patience) est une réalité scientifique pour ceux qui cherchent à résoudre les énigmes de l'intelligence.</p>
<p>Pour vos stratégies d'entreprise, ignorez le bruit des annonces marketing. Ne misez pas sur un "Dieu numérique" qui viendrait tout résoudre l'année prochaine. Misez sur ce qui existe : des outils de productivité puissants mais imparfaits, dont la valeur dépendra de votre capacité à les intégrer dans le monde réel, avec ses contraintes physiques et économiques.</p>
<hr />
<p><strong>Sources et Citations :</strong></p>
<p><a href="https://youtu.be/GhJK46g-G_s?si=mm4j0dZLzvYo4c-d"><em>Inside AI’s Circular Economy</em></a> (Oct 2025).</p>
<p><a href="https://youtu.be/Z6q2iJZmvOM?si=ar0vmGmuCAytTRY_"><em>How close is AGI</em></a> (Dec 2025)</p>
<p><a href="https://youtu.be/UJpcbQIUWUo?si=A8jNqlvNBUqSqsT0"><em>The Business of “Almost” AGI</em></a> (Nov 2025).</p>
<p><em>I</em><a href="https://youtu.be/aR20FWCCjAs?si=9t25GlhQZtDIYpGh"><em>lya Sutskever - Interview</em></a> (Nov 2025).</p>
<p><a href="https://www.lesechos.fr/tech-medias/hightech/cette-superintelligence-artificielle-qui-se-cacherait-derriere-le-divorce-entre-yann-lecun-meta-et-mark-zuckerberg-2198266"><em>Cette « superintelligence artificielle » qui se cacherait derrière le divorce entre Yann LeCun, Meta et Mark Zuckerberg</em></a><em>, Les Echos, 13 novembre 2025.</em></p>
<p><a href="https://open.substack.com/pub/drfeifei/p/from-words-to-worlds-spatial-intelligence?utm_campaign=post-expanded-share&amp;utm_medium=web"><em>From Words to Worlds</em>, Fei-Fei Li</a> (Nov 2025).</p>
]]></content:encoded></item><item><title><![CDATA[IA Générative : Pourquoi vos données ne sont pas prêtes]]></title><description><![CDATA[Nous vivons actuellement la rupture technologique la plus significative depuis l'internet commercial. Pourtant, passée l'euphorie des premières démos de GPT-4 ou Claude, une réalité opérationnelle fro]]></description><link>https://blog.bguarisma.com/ia-generative-pourquoi-vos-donnees-ne-sont-pas-pretes</link><guid isPermaLink="true">https://blog.bguarisma.com/ia-generative-pourquoi-vos-donnees-ne-sont-pas-pretes</guid><category><![CDATA[#semantique]]></category><category><![CDATA[#graphe-connaissance]]></category><category><![CDATA[graphrag]]></category><category><![CDATA[knowledge graph]]></category><category><![CDATA[generative ai]]></category><category><![CDATA[semantic]]></category><dc:creator><![CDATA[Boris Guarisma]]></dc:creator><pubDate>Sun, 14 Dec 2025 17:08:45 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1765731586451/cc99e400-7494-45c1-82c8-51050a3fc0d0.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Nous vivons actuellement la rupture technologique la plus significative depuis l'internet commercial. Pourtant, passée l'euphorie des premières démos de GPT-4 ou Claude, une <strong>réalité opérationnelle froide</strong> rattrape les décideurs : la performance de ces modèles est intrinsèquement bridée par l'architecture des données sur laquelle ils reposent.</p>
<p>Si vous avez l'impression que votre IA "patine", qu'elle hallucine ou qu'elle manque de contexte, ce n'est probablement pas la faute du modèle. C'est la faute à quarante années de modélisation orientée vers les applications (comptabilité, ERP) qui se révèlent incompatibles avec les exigences cognitives de l'IA.</p>
<p>J'ai décortiqué un rapport de recherche exhaustif sur le sujet pour vous livrer une analyse chirurgicale des obstacles qui séparent vos données de l'IA, et les solutions pour les surmonter.</p>
<hr />
<h2>Le conflit : Transaction vs Déduction</h2>
<p>Historiquement, nous avons conçu nos bases de données pour répondre à des questions binaires et connues à l'avance (ex: "Le produit X est-il en stock ?"). L'IA générative inverse cette logique : elle doit synthétiser une réponse complexe à partir de fragments dispersés.</p>
<p>C'est ici que se crée une "friction tectonique". Voici les quatre obstacles majeurs qui transforment cette friction en mur.</p>
<h3>1. La logique est prisonnière du code</h3>
<p>C'est l'obstacle le plus insidieux. Dans une architecture classique, la base de données est "bête" ; l'intelligence est dans le code (Java, C++, etc.). Le Modèle d'Objets Métiers (MOM) agit comme une structure exécutive rigide qui capture l'intelligence métier dans la couche applicative, dissociant ainsi totalement le sens de la donnée brute stockée.</p>
<ul>
<li><p><strong>L'exemple :</strong> Une colonne "Statut" contient la valeur 'S'. Pour la base de données, c'est une lettre. Pour l'application, via une ligne de code, cela signifie "Suspendu mais récupérable".</p>
</li>
<li><p><strong>Le problème :</strong> L'IA connectée aux données ne voit que le 'S'. Elle n'a pas accès au code qui détient la clé de compréhension. Sans ce "principe d'organisation explicite", l'IA ne peut pas raisonner, elle ne peut que faire des corrélations statistiques.</p>
</li>
</ul>
<h3>2. L'échec du modèle relationnel</h3>
<p>Paradoxalement, les bases de données relationnelles gèrent très mal les... relations profondes.</p>
<ul>
<li><p>Pour détecter une fraude ou comprendre un contexte complexe, l'IA doit faire des sauts de connexion (A est lié à B, qui est lié à C...).</p>
</li>
<li><p>Au-delà de trois niveaux de profondeur (three-hop radius), les performances SQL s'effondrent.</p>
</li>
<li><p><strong>Conséquence :</strong> Pour ne pas écrouler le système, on nourrit l'IA avec des données "plates", amputées de leur contexte relationnel, la rendant myope aux signaux faibles.</p>
</li>
</ul>
<h3>3. L'archipel des silos</h3>
<p>L'intelligence, c'est la capacité de lier des concepts disparates. Or, nos entreprises sont des archipels de données isolés (Salesforce, SAP, WMS) sans identifiants communs.</p>
<ul>
<li><strong>L'ambiguïté sémantique :</strong> Sans contexte unifié, l'IA ne peut pas résoudre le sens des mots. Un terme technique dans un document de maintenance peut avoir un sens totalement différent à la finance. Sans savoir "qui parle", l'IA hallucine.</li>
</ul>
<h3>4. Le fossé structuré / non-structuré</h3>
<p>C'est le défi critique de l'ère des LLM. D'un côté, les bases de données (chiffres), de l'autre, les documents (PDF, mails).</p>
<ul>
<li><p>Un rapport de maintenance contient souvent la causalité (ex: "Filtre encrassé -&gt; Surchauffe pompe").</p>
</li>
<li><p>Mais pour les bases traditionnelles, ce document est un "BLOB", une boîte noire illisible. L'IA peine à lier une ligne de l'ERP à la procédure PDF correspondante.</p>
</li>
</ul>
<hr />
<h2>La preuve par l'exemple : Le cas de l'Aéronautique</h2>
<p>Pour comprendre l'impact concret, prenons l'exemple d'un géant de l'aéronautique qui a voulu créer un "Copilote de Maintenance".</p>
<p>L'IA devait répondre à une question simple d'un mécanicien : "Comment réparer la fuite sur la pompe hydraulique ?". Le projet a failli échouer. Pourquoi ?</p>
<ul>
<li><p>La configuration de l'avion (quel numéro de série de pièce est installé ?) était dans l'<strong>ERP</strong>.</p>
</li>
<li><p>La procédure de réparation était dans un <strong>PDF</strong>.</p>
</li>
<li><p>L'IA, incapable de faire le pont rigoureux entre l'ID de la pièce (Structuré) et la mention textuelle dans le manuel (Non-structuré), hallucinait des procédures incompatibles.</p>
</li>
</ul>
<hr />
<h2>La solution : Vers une navigation conceptuelle</h2>
<p>Alors, comment sortir de l'impasse ? La réponse ne réside pas dans un meilleur modèle de langage, mais dans une meilleure <strong>mémoire numérique</strong>.</p>
<h3>Le Graphe de Connaissances (Knowledge Graph)</h3>
<p>C'est la pierre angulaire de la solution. Contrairement aux bases SQL, le graphe stocke la relation comme une donnée de première classe.</p>
<ol>
<li><p><strong>Logique explicite :</strong> La règle métier n'est plus cachée dans le code, elle est lisible dans le graphe.</p>
</li>
<li><p><strong>Performance :</strong> Traverser 5 niveaux de relations se fait en millisecondes.</p>
</li>
<li><p><strong>Pont sémantique :</strong> Le graphe est le seul modèle capable de relier proprement un nœud "Document PDF" à un nœud "Pièce ERP".</p>
</li>
</ol>
<h3>GraphRAG : L'avenir de l'IA en entreprise</h3>
<p>La tendance actuelle est au <strong>GraphRAG</strong> (Graph Retrieval-Augmented Generation). Cette technique combine :</p>
<ul>
<li><p>La recherche vectorielle (pour l'intuition et le flou linguistique).</p>
</li>
<li><p>La structure du graphe (pour la précision factuelle et le respect des liens causaux).</p>
</li>
</ul>
<p>Cela permet à l'IA de "raisonner" en suivant un chemin précis : <em>Machine X -&gt; Ligne Y -&gt; Produit Z -&gt; Impact Financier</em>, chose impossible avec une simple recherche par mots-clés.</p>
<hr />
<h2>Conclusion : Ne mettez pas une Formule 1 sur un chemin de terre</h2>
<p>L'IA Générative agit comme un révélateur impitoyable du désordre de nos données. Tenter de déployer des LLM sophistiqués sur une architecture obsolète revient à construire une Formule 1 pour la faire rouler sur un chemin de terre : c'est possible, mais dangereux et économiquement non viable.</p>
<p><strong>Votre prochain mouvement ?</strong> Cessez de concevoir vos modèles de données uniquement pour vos applications. Commencez à les concevoir pour vos agents cognitifs. Cela passe par l'investissement dans des couches sémantiques (Knowledge Graphs) et la libération des règles métier enfouies dans votre code historique.</p>
<p>C'est à ce prix que l'IA passera du statut de gadget coûteux à celui d'oracle industriel.</p>
]]></content:encoded></item><item><title><![CDATA[Vers l’AGI, à pas lents et lucides]]></title><description><![CDATA[Cher journal,
Aujourd’hui nous sommes le 13 mai 2025, et une question me trotte dans la tête :“Et si l’AGI n’arrivait jamais comme on l’imagine ?”
Je sais, c’est une question un peu provocante, presqu]]></description><link>https://blog.bguarisma.com/vers-lagi-a-pas-lents-et-lucides</link><guid isPermaLink="true">https://blog.bguarisma.com/vers-lagi-a-pas-lents-et-lucides</guid><category><![CDATA[real models]]></category><category><![CDATA[agi]]></category><category><![CDATA[llm]]></category><category><![CDATA[AI trends 2025]]></category><category><![CDATA[Neuro-Symbolic AI]]></category><dc:creator><![CDATA[Boris Guarisma]]></dc:creator><pubDate>Tue, 13 May 2025 19:35:43 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1747165448906/6c6d47e1-1c1f-4e16-8040-de6365f954b8.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Cher journal,</p>
<p>Aujourd’hui nous sommes le 13 mai 2025, et une question me trotte dans la tête :<br /><strong>“Et si l’AGI n’arrivait jamais comme on l’imagine ?”</strong></p>
<p>Je sais, c’est une question un peu provocante, presque sacrilège dans un monde où chaque semaine apporte son lot de promesses, de modèles “révolutionnaires” et d’annonces tambour battant. Mais plus je creuse, plus un malaise s’installe.</p>
<p>Cela fait quelques mois que je veille intensément le sujet de l’AGI — cette fameuse Intelligence Artificielle Générale qui serait censée rivaliser avec l’intelligence humaine dans sa polyvalence et son adaptabilité. Mon point de départ ? Une série de vidéos de <a href="https://www.youtube.com/@SabineHossenfelder">Sabine Hossenfelder</a>, physicienne lucide à l’humour tranchant, et surtout dénuée d’illusions marketing.</p>
<h3>Première claque : le modèle o3</h3>
<p>Début janvier, OpenAI lance o3. Buzz immédiat. Les chiffres sont impressionnants :</p>
<ul>
<li><p>87 % au test ARC-AGI, conçu pour évaluer des capacités abstraites proches de l’humain.</p>
</li>
<li><p>Performances hallucinantes en génération de code.</p>
</li>
<li><p>Raisonnement en chaîne de pensée (“Chain of Thought”).</p>
</li>
</ul>
<p>Sur le papier, tout y est. Pourtant, à y regarder de plus près… tout y est <em>justement</em> trop. Trop beau, trop calibré, trop cher (jusqu’à 3 000 $ pour une tâche). Et surtout : trop semblable aux versions précédentes.</p>
<p>GPT-4.5, Claude 3.7, o3 : tous progressent, mais à la marge. Le consensus émerge doucement : <strong>on atteint les limites du passage à l’échelle</strong>. Les LLMs ne “scalent” plus de manière significative. Ce n’est plus une ligne droite vers l’AGI, c’est un plateau.</p>
<h3>Deuxième claque : la définition d’AGI elle-même</h3>
<p>Sam Altman le reconnaît : “AGI” est devenu un mot-valise. Chacun y met ce qu’il veut. Un assistant très compétent ? Un surhomme numérique ? Une IA qui fait de la physique quantique en écoutant Bach ? La confusion règne, et elle arrange bien les discours marketing.</p>
<p>Sabine pointe un élément crucial : réussir un test (même impressionnant comme ARC-AGI) ne signifie pas comprendre. o3 peut battre des records en reconnaissance de motifs… sans rien saisir du sens profond. C’est l’illusion d’intelligence. Une simulation de réflexion — pas la réflexion elle-même.</p>
<h3>Troisième claque : les limites des LLMs</h3>
<p>Les LLMs ne comprennent pas. Ils imitent. Et leur imitation a des angles morts criants :</p>
<ul>
<li><p>Ils n’apprennent pas après leur entraînement.</p>
</li>
<li><p>Ils échouent sur des tâches logiques élémentaires (essayez de leur faire compter les “r” dans “strawberry”…).</p>
</li>
<li><p>Ils n’ont aucune idée de ce qu’est le monde réel.</p>
</li>
</ul>
<p>Yann LeCun le dit sans détour : “Ils ne savent pas débarrasser une table comme un enfant de 10 ans.” On y est.</p>
<h3>Alors, on fait quoi ?</h3>
<p>C’est là que les choses deviennent fascinantes. Loin des projecteurs, une révolution silencieuse s’opère. Deux pistes émergent avec sérieux :</p>
<ol>
<li><p><strong>Le raisonnement symbolique</strong> : injecter de la logique, des structures formelles, une mémoire organisée. C’est le retour du “noyau pensant”. DeepMind expérimente déjà avec AlphaProof et le “neurosymbolic AI”.</p>
</li>
<li><p><strong>Les world models</strong> : des modèles qui ne prédisent pas juste le prochain mot, mais simulent la dynamique du monde réel. Genie 2 chez DeepMind, Cosmos chez NVIDIA : on parle ici d’intelligence incarnée, d’apprentissage dans des mondes simulés, de cognition située.</p>
</li>
</ol>
<p>Et là, enfin, il y a de la substance. Il ne s’agit plus d’agrandir une boîte noire, mais de la <em>repenser</em>.</p>
<h3>Mon point de vue ? Un tournant épistémologique</h3>
<p>Ce que je sens, c’est qu’on quitte une phase quantitative pour entrer dans une phase qualitative.<br />Fini les records creux, place aux explorations profondes. Les années 2010 ont été celles du passage à l’échelle. Les années 2025–2030 pourraient bien être celles de la redécouverte des fondements de l’intelligence.</p>
<p>L’AGI n’arrivera pas par surprise, dans une update mineure d’un LLM. Elle émergera peut-être, un jour, d’un agencement fin entre perception, action, mémoire, logique… et modestie algorithmique.</p>
<p>Je termine ce billet avec cette intuition :<br /><strong>L’AGI ne sera pas une explosion. Ce sera une cristallisation.</strong><br />Silencieuse, patiente, méthodique. Comme la recherche, en somme.<br />Et c’est peut-être pour le mieux.</p>
]]></content:encoded></item><item><title><![CDATA[Quantum Entanglement]]></title><description><![CDATA[Cover photo by inkoly ref. 1185114379 on iStock
Go to R-bloggers for R news and tutorials contributed by hundreds of R bloggers.
This is the second article of the Quantum Computing simulation with R series.
Introduction
Welcome back to our quantum jo...]]></description><link>https://blog.bguarisma.com/quantum-entanglement</link><guid isPermaLink="true">https://blog.bguarisma.com/quantum-entanglement</guid><category><![CDATA[R Language]]></category><category><![CDATA[quantum computing]]></category><category><![CDATA[Quantum]]></category><category><![CDATA[Tutorial]]></category><dc:creator><![CDATA[Boris Guarisma]]></dc:creator><pubDate>Sun, 30 Jul 2023 15:12:08 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1690643119751/96ae280a-fc27-411d-81b4-7e4540d07ffc.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Cover photo by <a target="_blank" href="https://www.istockphoto.com/fr/portfolio/inkoly?mediatype=illustration">inkoly</a> ref. 1185114379 on <a target="_blank" href="https://www.istockphoto.com/fr">iStock</a></p>
<p>Go to <a target="_blank" href="https://www.r-bloggers.com/">R-bloggers</a> for R news and tutorials contributed by hundreds of R bloggers.</p>
<p>This is the second article of the <a target="_blank" href="https://blog.bguarisma.com/series/qc-r-series">Quantum Computing simulation with R</a> series.</p>
<h2 id="heading-introduction">Introduction</h2>
<p>Welcome back to our quantum journey! Today, we're delving into a phenomenon that lies at the heart of quantum computing's unique power - <strong>entanglement</strong>. At a high level, quantum entanglement is the deep and mysterious link that can exist between two qubits, no matter the distance that separates them. This powerful feature allows quantum computers to process a massive number of possibilities at once and solve certain problems much faster than classical computers.</p>
<p>But how do we get there? We'll start by exploring two-qubit gates, the essential quantum operations that can bring about entanglement. The most common of these gates, such as the CNOT gate, can modify the state of one qubit based on the state of another, creating a correlation between the two.</p>
<p>Next, we'll dive into the concept of the tensor product. When dealing with multiple qubits, we cannot simply consider them separately; we must take into account the joint system, described mathematically by the tensor product of the individual states. In some situations, the state of the whole system cannot be described by individual qubit states, and that's when we encounter true entanglement.</p>
<p>We'll contrast this with the notion of product states, which do not involve entanglement. In these cases, the state of the whole system can be factored into individual qubit states. However, the fascinating aspect of quantum systems is that product states and entangled states are not mutually exclusive categories; a quantum state can evolve from one to the other under the action of certain quantum gates.</p>
<p>Entanglement is a key resource in quantum computing and quantum information theory, as it is a prerequisite for quantum teleportation, quantum cryptography, superdense coding, and several types of quantum computing algorithms. The phenomenon of quantum entanglement allows quantum computers to perform complex calculations in parallel, providing them with immense computational power. However, managing and maintaining entanglement remains one of the main challenges in the practical implementation of quantum computing.</p>
<p>Get ready to delve into these deep waters where classical intuition may fall short, but a new, broader understanding awaits.</p>
<h2 id="heading-what-is-quantum-entanglement">What is quantum entanglement?</h2>
<p>Quantum entanglement is a profound physical phenomenon that occurs when a pair or a group of quantum particles interact in a way that the state of each particle cannot be described independently of the state of the others, regardless of the distance separating them. This implies that <strong>an alteration in the state of one entangled particle instantaneously affects the state of the other particle(s), no matter how far apart they are</strong>. This counterintuitive feature stems from the principles of quantum mechanics, notably the superposition principle.</p>
<p>In more technical terms, <strong>an entangled state</strong> cannot be factorized as a product of the states of its constituents; that is, it <strong>cannot be broken down into separate states for each particle</strong>.</p>
<p><strong>Superposition and entanglement are two distinct concepts in quantum mechanics</strong>.</p>
<ul>
<li><p><strong>Superposition</strong> refers to the fact that a quantum state can exist in multiple states at once. For example, a qubit could be in a superposition of the states |0⟩ and |1⟩, represented as α|0⟩ + β|1⟩, where α and β are complex numbers and |α|^2 + |β|^2 = 1. Superposition <strong>can occur with a single qubit</strong>.</p>
</li>
<li><p><strong>Entanglement</strong> refers to <strong>a</strong> <strong>special correlation</strong> that can exist between quantum states. When two qubits are entangled, the state of one qubit is immediately connected to the state of the other, no matter the distance between them. Entanglement is a property of a <strong>system of</strong> <strong>two or more qubits</strong>.</p>
</li>
</ul>
<h3 id="heading-how-do-we-create-an-entangled-quantum-state">How do we create an entangled quantum state?</h3>
<p>Although I have not introduced the <strong>CNOT gate</strong> yet, it is good to know why we apply it in the following example.</p>
<p>Example:</p>
<ol>
<li><p>Start with a <em>separable</em> state, say |00⟩</p>
</li>
<li><p>Apply Hadamard gate to the first qubit: 1/√2 (|00⟩ + |10⟩)</p>
</li>
<li><p>Apply CNOT gate =&gt; <strong>1/√2 (|00⟩ + |11⟩) is an entangled state</strong></p>
</li>
</ol>
<p>Let us perform these steps with the qsimulatR package</p>
<pre><code class="lang-r"><span class="hljs-comment"># Create qstate object |00&gt;</span>
&gt; x &lt;- qstate(nbits = <span class="hljs-number">2</span>)
&gt; x
   ( <span class="hljs-number">1</span> )    * |<span class="hljs-number">00</span>&gt; 
<span class="hljs-comment"># Display superposition of bit 1 of |00&gt; (the rightmost qubit)</span>
<span class="hljs-comment"># with the Hadamard gate</span>
&gt; H(<span class="hljs-number">1</span>)*x
   ( <span class="hljs-number">0.7071068</span> )    * |<span class="hljs-number">00</span>&gt; 
 + ( <span class="hljs-number">0.7071068</span> )    * |<span class="hljs-number">01</span>&gt;
<span class="hljs-comment"># Apply CNOT gate</span>
&gt; x &lt;- CNOT(c(<span class="hljs-number">1</span>,<span class="hljs-number">2</span>))*(H(<span class="hljs-number">1</span>)*x)
<span class="hljs-comment"># control bit = bit 1, target bit (t) = bit 2</span>
&gt; x
   ( <span class="hljs-number">0.7071068</span> )    * |<span class="hljs-number">00</span>&gt; <span class="hljs-comment"># control = 0 then target = 0</span>
 + ( <span class="hljs-number">0.7071068</span> )    * |<span class="hljs-number">11</span>&gt; <span class="hljs-comment"># control = 1 then target = 1</span>
<span class="hljs-comment"># Verify result with CNOT truth table</span>
&gt; truth.table(CNOT, nbits = <span class="hljs-number">2</span>)
  In2 In1 Out2 Out1
<span class="hljs-number">1</span>   <span class="hljs-number">0</span>   <span class="hljs-number">0</span>    <span class="hljs-number">0</span>    <span class="hljs-number">0</span> <span class="hljs-comment"># CNOT(I00&gt;) = |00&gt;</span>
<span class="hljs-number">2</span>   <span class="hljs-number">0</span>   <span class="hljs-number">1</span>    <span class="hljs-number">1</span>    <span class="hljs-number">1</span> <span class="hljs-comment"># CNOT(|01&gt;) = |11&gt;</span>
<span class="hljs-number">3</span>   <span class="hljs-number">1</span>   <span class="hljs-number">0</span>    <span class="hljs-number">1</span>    <span class="hljs-number">0</span>
<span class="hljs-number">4</span>   <span class="hljs-number">1</span>   <span class="hljs-number">1</span>    <span class="hljs-number">0</span>    <span class="hljs-number">1</span>
</code></pre>
<p>There are two notions here that were not included in my <a target="_blank" href="https://blog.bguarisma.com/quantum-computing-with-qsimulatr">first article</a>: the <strong>CNOT gate and</strong> a <strong>separable state</strong>.</p>
<h3 id="heading-the-cnot-controlled-not-gate">The CNOT (Controlled-NOT) gate</h3>
<p>Let us start by plotting the circuit of the previous result.</p>
<pre><code class="lang-r"><span class="hljs-comment"># qubitnames = c("|0&gt;", "|0&gt;") since we started with |00&gt;</span>
&gt; qsimulatR::plot(x, qubitnames = c(<span class="hljs-string">"|0&gt;"</span>, <span class="hljs-string">"|0&gt;"</span>))
</code></pre>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1690655837189/6ad3230e-fb38-4e5e-afa6-20bc783cdbbb.png" alt class="image--center mx-auto" /></p>
<p>The Controlled-Not gate (CNot, controlled-X, CX, Feynman) is typically represented by the circuit diagrams [1]</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1690713609984/ad0f4c2a-5687-4824-9fde-576da3f6bf23.png" alt class="image--center mx-auto" /></p>
<p>The <strong>control bit</strong> • influences the state of the <strong>target bit</strong> ⊕, and the target bit has no influence on the state of the control bit e.g. it is not symmetric between the two qubits.</p>
<p><strong>The gate flips (or NOTs) the target qubit if the control qubit is in state |1⟩</strong>, as shown in the CNOT truth table above where <code>In1</code> is the control bit and <code>In2</code> is the target bit.</p>
<p>The state of the target qubit becomes entangled with the control qubit, which means that the state of the control qubit is now related to the state of the target qubit (the back reaction).</p>
<p>This concept of "action and back reaction" is a reflection of the principle of entanglement in quantum mechanics, and the foundation of quantum gates being <strong>reversible</strong> e.g. if we know the final state of the two qubits, we can figure out their initial state.</p>
<p>In quantum computing, every operation is required to be reversible, meaning if we know the output state, we can exactly figure out the input state.</p>
<pre><code class="lang-r"><span class="hljs-comment"># the first bit is the control and the second bit the target.</span>
&gt; CNOT(c(<span class="hljs-number">1</span>,<span class="hljs-number">2</span>))
An object of class <span class="hljs-string">"cnotgate"</span>
Slot <span class="hljs-string">"bits"</span>:
[<span class="hljs-number">1</span>] <span class="hljs-number">1</span> <span class="hljs-number">2</span>
<span class="hljs-comment"># you can switch the control and atrget bits</span>
&gt; CNOT(c(<span class="hljs-number">2</span>,<span class="hljs-number">1</span>))
An object of class <span class="hljs-string">"cnotgate"</span>
Slot <span class="hljs-string">"bits"</span>:
[<span class="hljs-number">1</span>] <span class="hljs-number">2</span> <span class="hljs-number">1</span>

&gt; x &lt;- qstate(nbits = <span class="hljs-number">2</span>)
&gt; qsimulatR::plot(CNOT(c(<span class="hljs-number">2</span>,<span class="hljs-number">1</span>))*(H(<span class="hljs-number">2</span>)*x), qubitnames = c(<span class="hljs-string">"|0&gt;"</span>, <span class="hljs-string">"|0&gt;"</span>))
</code></pre>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1690716161346/5ff322ca-2bfd-4b8c-a2d4-39186b6b33a0.png" alt class="image--center mx-auto" /></p>
<p>In quantum logic, there are no pure control operations per se. There is no unambiguous distinction between control and target [1]. This means that <strong>after</strong> a CNOT operation, neither qubit can be said to have a purely "control" or "target" role because their states are intertwined - each qubit's state depends on the other's.</p>
<p>Now, what do we mean by <em>separable</em> qubit?</p>
<h2 id="heading-product-states">Product states</h2>
<p>In quantum mechanics, a state is <strong>separable</strong> (or is a <strong>product state</strong>) if it can be written as a product of states of individual subsystems.</p>
<p>For a two-qubit system, a state is separable if it can be written in the form <strong>|ψ⟩ = |a⟩ ⊗ |b⟩</strong>, where |a⟩ and |b⟩ are states of the individual qubits, and <strong>⊗</strong> is the <strong>tensor product</strong>.</p>
<p>For instance, |00⟩, |01⟩, |10⟩, and |11⟩ are all separable states.</p>
<p>If you can factor the state into a product of two single-qubit states then the state is separable. If you can't, then the state is entangled.</p>
<p>For instance, the state α|00⟩ + β|11⟩ cannot be factored into a product of two single-qubit states unless either α or β is zero. Therefore, this state is entangled.</p>
<h3 id="heading-tensor-product">Tensor product</h3>
<p>Tensor products of quantum state vectors are also quantum state vectors — and they represent <em>independence</em> among systems.</p>
<p>The tensor product <strong>|φ⟩⊗|ψ⟩</strong> may alternatively be written as <strong>|φ⟩|ψ⟩</strong> or as <strong>|φψ⟩</strong>.</p>
<p>$$|0&gt;⊗|1&gt; = \begin{pmatrix} 1 \\ 0 \end{pmatrix} ⊗ \begin{pmatrix} 0 \\ 1 \end{pmatrix} = \begin{pmatrix} 0 \\ 1 \\ 0 \\ 0 \end{pmatrix} = |01&gt;$$</p><p>You can compute the tensor product between two quantum state vectors by applying the <code>kronecker()</code> function in R to qstate <code>coefs</code>.</p>
<pre><code class="lang-r">&gt; a &lt;- qstate(<span class="hljs-number">1</span>) <span class="hljs-comment"># |0&gt;</span>
&gt; b &lt;- X(<span class="hljs-number">1</span>)*a    <span class="hljs-comment"># |1&gt;</span>
&gt; kronecker(a@coefs, b@coefs)
[<span class="hljs-number">1</span>] <span class="hljs-number">0</span>+<span class="hljs-number">0i</span> <span class="hljs-number">1</span>+<span class="hljs-number">0i</span> <span class="hljs-number">0</span>+<span class="hljs-number">0i</span> <span class="hljs-number">0</span>+<span class="hljs-number">0i</span>
&gt; ab &lt;- qstate(<span class="hljs-number">2</span>, coefs = kronecker(a@coefs, b@coefs))
&gt; ab
   ( <span class="hljs-number">1</span> )    * |<span class="hljs-number">01</span>&gt;
</code></pre>
<h2 id="heading-proving-entanglement-with-cnot-gates">Proving entanglement with CNOT gates</h2>
<p>Based on the example provided in Chapter 2.1.10 from the book [2] by Bourreau et al., I am going to implement the detailed computation with the qsimulatR package. I hope you will get another sense of what entanglement means.</p>
<p>We start with two qubits</p>
<p>$$|x1&gt;= \begin{pmatrix} 0.8 \\ 0.6 \end{pmatrix} \hspace{1cm} |x2&gt;= \begin{pmatrix} 0.38 \\ 0.92 \end{pmatrix}$$</p><p>After applying the <strong>first</strong> CNOT gate we obtain</p>
<p>$$|x1&gt;= \begin{pmatrix} 0.8 \\ 0.6 \end{pmatrix} \hspace{1cm} |x2&gt;= \begin{pmatrix} 0.63 \\ 0.77 \end{pmatrix}$$</p><p>After applying the <strong>second</strong> CNOT gate we obtain</p>
<p>$$|x1&gt;= \begin{pmatrix} 0.8 \\ 0.6 \end{pmatrix} \hspace{1cm} |x2&gt;= \begin{pmatrix} 0.683 \\ 0.721 \end{pmatrix}$$</p><pre><code class="lang-r"><span class="hljs-comment"># Helper function: probabilities of a quantum state</span>
qstateProbabilities &lt;- <span class="hljs-keyword">function</span>(x){
  probs &lt;- Mod(z = x@coefs)^<span class="hljs-number">2</span>
  names(probs) &lt;- x@basis
  <span class="hljs-keyword">return</span>(probs)
}

<span class="hljs-comment"># qubit |x1&gt;</span>
&gt; x1 &lt;- qstate(<span class="hljs-number">1</span>, coefs = as.complex(c(<span class="hljs-number">.8</span>, <span class="hljs-number">.6</span>)))
&gt; x1
   ( <span class="hljs-number">0.8</span> )    * |<span class="hljs-number">0</span>&gt; 
 + ( <span class="hljs-number">0.6</span> )    * |<span class="hljs-number">1</span>&gt; 
&gt; qstateProbabilities(x1)
 |<span class="hljs-number">0</span>&gt;  |<span class="hljs-number">1</span>&gt; 
<span class="hljs-number">0.64</span> <span class="hljs-number">0.36</span> 
<span class="hljs-comment"># qubit |x2&gt;</span>
&gt; x2 &lt;- qstate(<span class="hljs-number">1</span>, coefs = as.complex(c(<span class="hljs-number">5</span>/<span class="hljs-number">13</span>, <span class="hljs-number">12</span>/<span class="hljs-number">13</span>)))
&gt; x2
   ( <span class="hljs-number">0.3846154</span> )    * |<span class="hljs-number">0</span>&gt; 
 + ( <span class="hljs-number">0.9230769</span> )    * |<span class="hljs-number">1</span>&gt; 
&gt; 
&gt; qstateProbabilities(x2)
     |<span class="hljs-number">0</span>&gt;      |<span class="hljs-number">1</span>&gt; 
<span class="hljs-number">0.147929</span> <span class="hljs-number">0.852071</span>
</code></pre>
<p>Let us perform the tensor product <strong>|x1&gt;⊗ |x2&gt;</strong> = <strong>|x1x2&gt;</strong> with the <code>kronecker()</code> function.</p>
<pre><code class="lang-r"><span class="hljs-comment"># quantum state |x1x2&gt;</span>
&gt; x1x2 &lt;- qstate(<span class="hljs-number">2</span>, coefs = as.complex(kronecker(x1@coefs, x2@coefs)))
&gt; x1x2
   ( <span class="hljs-number">0.3076923</span> )    * |<span class="hljs-number">00</span>&gt; 
 + ( <span class="hljs-number">0.7384615</span> )    * |<span class="hljs-number">01</span>&gt; 
 + ( <span class="hljs-number">0.2307692</span> )    * |<span class="hljs-number">10</span>&gt; 
 + ( <span class="hljs-number">0.5538462</span> )    * |<span class="hljs-number">11</span>&gt;
</code></pre>
<p>Now we apply the <strong>first</strong> CNOT gate and compute the probabilities for both qubits, |x1&gt; and |x2&gt;, to be in states |0&gt; and |1&gt;, respectively.</p>
<pre><code class="lang-r"><span class="hljs-comment"># First CNOT : CNOT(|x1x2&gt;)</span>
<span class="hljs-comment"># x1 is the control bit thus bit 2</span>
<span class="hljs-comment"># x2 is the target bit thus bit 1</span>
&gt; res &lt;- CNOT(c(<span class="hljs-number">2</span>,<span class="hljs-number">1</span>))*x1x2
&gt; res
   ( <span class="hljs-number">0.3076923</span> )    * |<span class="hljs-number">00</span>&gt; 
 + ( <span class="hljs-number">0.7384615</span> )    * |<span class="hljs-number">01</span>&gt; 
 + ( <span class="hljs-number">0.5538462</span> )    * |<span class="hljs-number">10</span>&gt; 
 + ( <span class="hljs-number">0.2307692</span> )    * |<span class="hljs-number">11</span>&gt; 
&gt; probs &lt;- qstateProbabilities(res)
&gt; probs
      |<span class="hljs-number">00</span>&gt;       |<span class="hljs-number">01</span>&gt;       |<span class="hljs-number">10</span>&gt;       |<span class="hljs-number">11</span>&gt; 
<span class="hljs-number">0.09467456</span> <span class="hljs-number">0.54532544</span> <span class="hljs-number">0.30674556</span> <span class="hljs-number">0.05325444</span> 

<span class="hljs-comment"># Prob(x1=|0&gt;) =&gt; unchanged</span>
&gt; as.numeric(probs[<span class="hljs-number">1</span>]+probs[<span class="hljs-number">2</span>])
[<span class="hljs-number">1</span>] <span class="hljs-number">0.64</span>
<span class="hljs-comment"># Prob(x1=|1&gt;) =&gt; unchanged</span>
&gt; as.numeric(probs[<span class="hljs-number">3</span>]+probs[<span class="hljs-number">4</span>])
[<span class="hljs-number">1</span>] <span class="hljs-number">0.36</span> 
<span class="hljs-comment"># Prob(x2=|0&gt;) =&gt; updated to 0.4014201 &gt; 0.147929</span>
&gt; as.numeric(probs[<span class="hljs-number">1</span>]+probs[<span class="hljs-number">3</span>])
[<span class="hljs-number">1</span>] <span class="hljs-number">0.4014201</span> 
<span class="hljs-comment"># Prob(x2=|1&gt;) =&gt; updated to 0.5985799 &lt; 0.852071</span>
&gt; as.numeric(probs[<span class="hljs-number">2</span>]+probs[<span class="hljs-number">4</span>])
[<span class="hljs-number">1</span>] <span class="hljs-number">0.5985799</span>
</code></pre>
<p>This means that we obtain an <strong>updated |x2&gt;</strong> with updated probabilities, we compute its coefficients knowing that coef = √prob. Consequently, we obtain an <strong>updated</strong> quantum state <strong>|x1&gt;⊗ |x2&gt;</strong> = <strong>|x1x2&gt;</strong></p>
<pre><code class="lang-r"><span class="hljs-comment"># Updated |x2&gt;</span>
&gt; x2 &lt;- qstate(nbits = <span class="hljs-number">1</span>, 
               coefs = as.complex(c(sqrt(probs[<span class="hljs-number">1</span>]+probs[<span class="hljs-number">3</span>]), 
                                    sqrt(probs[<span class="hljs-number">2</span>]+probs[<span class="hljs-number">4</span>]))))
&gt; x2
   ( <span class="hljs-number">0.6335772</span> )    * |<span class="hljs-number">0</span>&gt; 
 + ( <span class="hljs-number">0.7736794</span> )    * |<span class="hljs-number">1</span>&gt; 
<span class="hljs-comment"># Thus, quantum state |x1x2&gt; is now</span>
&gt; x1x2 &lt;- qstate(<span class="hljs-number">2</span>, coefs = as.complex(kronecker(x1@coefs, x2@coefs)))
&gt; x1x2
   ( <span class="hljs-number">0.5068618</span> )    * |<span class="hljs-number">00</span>&gt; 
 + ( <span class="hljs-number">0.6189436</span> )    * |<span class="hljs-number">01</span>&gt; 
 + ( <span class="hljs-number">0.3801463</span> )    * |<span class="hljs-number">10</span>&gt; 
 + ( <span class="hljs-number">0.4642077</span> )    * |<span class="hljs-number">11</span>&gt;
</code></pre>
<p>Now we apply the <strong>second</strong> CNOT gate and compute the probabilities for both qubits, |x1&gt; and |x2&gt;, to be in states |0&gt; and |1&gt;, respectively.</p>
<pre><code class="lang-r"><span class="hljs-comment"># Second CNOT : CNOT(|x1x2&gt;)</span>
<span class="hljs-comment"># x1 is the control bit thus bit 2</span>
<span class="hljs-comment"># x2 is the target bit thus bit 1</span>
&gt; res &lt;- CNOT(c(<span class="hljs-number">2</span>,<span class="hljs-number">1</span>))*x1x2
&gt; res
   ( <span class="hljs-number">0.5068618</span> )    * |<span class="hljs-number">00</span>&gt; 
 + ( <span class="hljs-number">0.6189436</span> )    * |<span class="hljs-number">01</span>&gt; 
 + ( <span class="hljs-number">0.4642077</span> )    * |<span class="hljs-number">10</span>&gt; 
 + ( <span class="hljs-number">0.3801463</span> )    * |<span class="hljs-number">11</span>&gt; 
&gt; probs &lt;- qstateProbabilities(res)
&gt; probs
     |<span class="hljs-number">00</span>&gt;      |<span class="hljs-number">01</span>&gt;      |<span class="hljs-number">10</span>&gt;      |<span class="hljs-number">11</span>&gt; 
<span class="hljs-number">0.2569089</span> <span class="hljs-number">0.3830911</span> <span class="hljs-number">0.2154888</span> <span class="hljs-number">0.1445112</span> 
<span class="hljs-comment"># Prob(x1=|0&gt;) =&gt; unchanged</span>
&gt; as.numeric(probs[<span class="hljs-number">1</span>]+probs[<span class="hljs-number">2</span>])
[<span class="hljs-number">1</span>] <span class="hljs-number">0.64</span>
<span class="hljs-comment"># Prob(x1=|1&gt;) =&gt; unchanged</span>
&gt; as.numeric(probs[<span class="hljs-number">3</span>]+probs[<span class="hljs-number">4</span>])
[<span class="hljs-number">1</span>] <span class="hljs-number">0.36</span>
<span class="hljs-comment"># Prob(x2=|0&gt;) =&gt; updated to 0.4723976 &gt; 0.4014201 &gt; 0.147929</span>
&gt; as.numeric(probs[<span class="hljs-number">1</span>]+probs[<span class="hljs-number">3</span>])
[<span class="hljs-number">1</span>] <span class="hljs-number">0.4723976</span>
<span class="hljs-comment"># Prob(x2=|1&gt;) =&gt; updated to 0.5276024 &lt; 0.5985799 &lt; 0.852071</span>
&gt; as.numeric(probs[<span class="hljs-number">2</span>]+probs[<span class="hljs-number">4</span>])
[<span class="hljs-number">1</span>] <span class="hljs-number">0.5276024</span>
</code></pre>
<p>As before, we obtain an <strong>updated |x2&gt;</strong> with updated probabilities, we compute its coefficients knowing that coef = √prob.</p>
<pre><code class="lang-r"><span class="hljs-comment"># Updated |x2&gt;</span>
&gt; x2
   ( <span class="hljs-number">0.6873119</span> )    * |<span class="hljs-number">0</span>&gt; 
 + ( <span class="hljs-number">0.7263624</span> )    * |<span class="hljs-number">1</span>&gt;
</code></pre>
<p>Thus,</p>
<ul>
<li><p>the probability of (|x2&gt; = |0&gt;) <strong>increased</strong> from 0.15 to 0.47</p>
</li>
<li><p>the probability of (|x2&gt; = |1&gt;) <strong>decreased</strong> from 0.85 to 0.53</p>
</li>
</ul>
<p><strong>And there is a reason for this!</strong> indeed, the (unchanged) probability of (|x1&gt; = |1&gt;) = 0.36 <strong>&lt; 0.5</strong> thus, |x1&gt; is closer to state |0&gt;. This means that applying the CNOT several times, with x1 as the control bit and x2 as the target bit, will make <strong>|x2&gt; to <em>converge</em> to state |0&gt;</strong> as well =&gt; <strong>entanglement!</strong></p>
<h2 id="heading-bell-states">Bell states</h2>
<p>Remember the entangled state <strong>1/√2 (|00⟩ + |11⟩)</strong> from the very first example in this article? it is called a <strong>Bell state</strong>.</p>
<p>All four of the Bell states represent <strong>entanglement</strong> between two qubits [3].</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1690728796067/7b80b836-8c66-42c5-a7eb-2c51d92323ae.png" alt class="image--center mx-auto" /></p>
<p>The collection of all four Bell states is known as the <em>Bell basis.</em> Any quantum state vector of two qubits can be expressed as a linear combination of the four Bell states [3]. For example,</p>
<p>$$|00&gt; =\frac{1}{\sqrt{2}}|\phi^+&gt; + \frac{1}{\sqrt{2}}|\phi^-&gt;$$</p><pre><code class="lang-r">&gt; x &lt;- qstate(<span class="hljs-number">2</span>) <span class="hljs-comment"># |00&gt;</span>
&gt; x &lt;- CNOT(c(<span class="hljs-number">1</span>,<span class="hljs-number">2</span>))*(H(<span class="hljs-number">1</span>)*x)
<span class="hljs-comment"># Bell state |phi+&gt;</span>
&gt; x
   ( <span class="hljs-number">0.7071068</span> )    * |<span class="hljs-number">00</span>&gt; <span class="hljs-comment"># control = 0 then target = 0</span>
 + ( <span class="hljs-number">0.7071068</span> )    * |<span class="hljs-number">11</span>&gt; <span class="hljs-comment"># control = 1 then target = 1</span>
<span class="hljs-comment"># Try it with x = {|01&gt;, |10&gt;, |11&gt;}</span>
<span class="hljs-comment"># I guess you have understood how we obtain the Bell states now</span>
qsimulatR::plot(x)
</code></pre>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1690729570245/e8f2a666-2c19-46fe-ac70-703861ed656d.png" alt class="image--center mx-auto" /></p>
<h2 id="heading-references">References</h2>
<p>[1] Crooks G. E., "Gates, States, and Circuits", Tech. Note 014v0.9.0 beta, 2023-03-01</p>
<p>[2] Bourreau E., Fleury, G., Lacomme P., "<em>Introduction à l'informatique quantique, Apprendre à calculer sur des ordinateurs quantiques avec Python</em>", Collection Blanche, <a target="_blank" href="https://www.eyrolles.com/Informatique/Livre/introduction-a-l-informatique-quantique-9782416006531/"><strong>Eyrolles</strong></a>, 2022</p>
<p>[3] Qiskit, Basics of Quantum Information, <a target="_blank" href="https://learn.qiskit.org/course/basics/single-systems"><strong>https://learn.qiskit.org/course/basics/single-systems</strong></a></p>
]]></content:encoded></item><item><title><![CDATA[Grover's algorithm with qsimulatR]]></title><description><![CDATA[Cover photo by gorodenkoff on iStock
Go to R-bloggers for R news and tutorials contributed by hundreds of R bloggers.
This is the fourth article of the Quantum Computing simulation with R series.
Introduction
Grover's algorithm, named after its inven...]]></description><link>https://blog.bguarisma.com/grovers-algorithm-with-qsimulatr</link><guid isPermaLink="true">https://blog.bguarisma.com/grovers-algorithm-with-qsimulatr</guid><category><![CDATA[R Language]]></category><category><![CDATA[quantum computing]]></category><category><![CDATA[Quantum]]></category><category><![CDATA[Tutorial]]></category><dc:creator><![CDATA[Boris Guarisma]]></dc:creator><pubDate>Fri, 28 Jul 2023 21:36:52 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1690580098191/4053d3ee-f2dc-49a7-a4cb-2d31fcfc88eb.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Cover photo by <a target="_blank" href="https://www.istockphoto.com/fr/portfolio/gorodenkoff?mediatype=photography"><strong>gorodenkoff</strong></a> on <a target="_blank" href="https://www.istockphoto.com/fr">iStock</a></p>
<p>Go to <a target="_blank" href="https://www.r-bloggers.com/">R-bloggers</a> for R news and tutorials contributed by hundreds of R bloggers.</p>
<p>This is the fourth article of the <a target="_blank" href="https://blog.bguarisma.com/series/qc-r-series">Quantum Computing simulation with R</a> series.</p>
<h2 id="heading-introduction">Introduction</h2>
<p>Grover's algorithm, named after its inventor <a target="_blank" href="https://en.wikipedia.org/wiki/Lov_Grover">Lov Grover</a>, is one of the foundational algorithms in quantum computing, and understanding it is crucial for several reasons:</p>
<ol>
<li><p><strong>Quantum Speedup</strong>: Grover's algorithm provides a practical demonstration of "quantum speedup." It's used for unstructured search problems and offers a significant speed advantage over classical algorithms. In a database with N items, Grover's algorithm can find a target item in roughly √N steps, compared to N/2 steps on average for a classical algorithm. This quadratic speedup exemplifies the <em>computational potential</em>* of quantum computing.</p>
</li>
<li><p><strong>Amplitude Amplification</strong>: Grover's algorithm introduces the key concept of amplitude amplification, a technique used to increase the probability amplitude of the desired states. This is central not only to Grover's algorithm but also to other quantum algorithms.</p>
</li>
<li><p><strong>Quantum Gates and Operations</strong>: Implementing Grover's algorithm helps understand the application of various quantum gates and their combined operations. It makes use of the Hadamard gate to create superposition, and phase inversion and amplitude amplification to manipulate these superpositions.</p>
</li>
<li><p><strong>Quantum Advantage in Practice</strong>: While some quantum algorithms require a fully fault-tolerant quantum computer to demonstrate quantum advantage, Grover's algorithm can provide quantum speedup on near-term quantum devices, which makes it particularly important for practical quantum computing.</p>
</li>
<li><p><strong>Basis for Other Algorithms</strong>: Understanding Grover's algorithm lays the groundwork for learning more complex quantum algorithms. Many advanced algorithms use principles demonstrated in Grover's algorithm, such as the amplitude amplification technique.</p>
</li>
</ol>
<p>(*) Although Grover's algorithm requires the entire search space to be in superposition, which may not be feasible for certain large-scale practical applications, it remains an important example of quantum speedup, and understanding it is crucial to understanding many other quantum algorithms.</p>
<h2 id="heading-the-basics-of-grovers-algorithm"><strong>The Basics of Grover's Algorithm</strong></h2>
<p>Grover's algorithm is designed to address the problem of <strong>unstructured search</strong>. By "unstructured" search, we mean that the algorithm is designed to search through an unordered, unsorted, or random list of elements where no particular structure or relationship among the elements can be exploited to simplify the search.</p>
<p>Grover's algorithm is highly versatile and not restricted to problems with unique solutions. <strong>It can efficiently handle problems with multiple solutions</strong>, enhancing the probabilities associated with all solution states, thereby increasing the likelihood of measuring a correct answer upon executing the algorithm.</p>
<p>Grover's algorithm involves two main steps that are repeated iteratively: the application of the <strong>oracle function</strong>, and the application of the <strong>Grover diffusion operator</strong> (amplitude amplification). See the figure below from Figgat et al. [1].</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1690566188628/3d9446f1-a19b-4339-9d77-c2ea18834011.png" alt class="image--center mx-auto" /></p>
<p>Here is a high-level overview of Grover's algorithm and its stages:</p>
<ol>
<li><p><strong>Initialization</strong>: The algorithm begins by initializing a quantum system of n qubits into a superposition of all possible states. This is done by applying a Hadamard gate to each qubit in the initial state |0&gt;. As a result, we obtain an equal superposition of all 2^n possible states.</p>
</li>
<li><p><strong>Oracle Application</strong>: In the second step, an "<strong>oracle</strong>" function is applied to the system. The oracle is a black box function that recognizes the solution(s) we are searching for. When it encounters a solution, it flips the sign of the state, leaving other states untouched. This marks the correct solution(s) with a phase difference.</p>
</li>
<li><p><strong>Amplitude Amplification</strong>: After the oracle application, the amplitudes of the states are manipulated through a process called "amplitude amplification" or <strong>Grover diffusion operator</strong>. This process involves flipping the amplitudes of the states about the average amplitude, thus amplifying the amplitude (probability) of the marked state(s) while diminishing others.</p>
</li>
<li><p><strong>Iteration</strong>: Steps 2 and 3 are iteratively repeated approximately √N times to increase the probability of measuring the marked state(s) (N is the total number of items in the list or the size of the search space).</p>
</li>
<li><p><strong>Measurement</strong>: Finally, a measurement is performed. At this point, the system will most likely collapse into the marked state(s) — the solution(s) to the search problem.</p>
</li>
</ol>
<h2 id="heading-understanding-the-oracle"><strong>Understanding the Oracle</strong></h2>
<p>An oracle, in the context of quantum computing, is a "black box" operation used in quantum algorithms that provides a solution to a specific problem instance. It's a key component in many quantum algorithms such as Grover's and Shor's algorithms. It encodes the problem we're trying to solve into a quantum state, and <strong>it can recognize a solution to the problem without revealing what the solution is</strong>.</p>
<p>In Grover's algorithm, <strong>the oracle is a function that marks the elements we are searching for</strong>. It's capable of recognizing whether a given input is a solution and flips the sign of the state if it is a solution.</p>
<p>The quantum oracle is implemented as a unitary transformation, which when applied to a particular quantum state, alters the state in some way dependent on the problem at hand. <strong>The rest of the quantum algorithm then uses this "marked" state to amplify the probability of measuring the system in the state corresponding to the solution</strong>, using the principles of quantum superposition and interference.</p>
<p>Oracles are hypothetical constructs used in the analysis of quantum algorithms. <strong>In practice, constructing an efficient oracle is often as hard as solving the problem itself classically.</strong> However, for certain problems, we can construct efficient quantum oracles, and this gives us quantum speedup over classical algorithms.</p>
<h3 id="heading-example-1-solution-grover-algorithm">Example: 1-solution Grover algorithm</h3>
<p>As an example, let's assume we have a list of four items, {0, 1, 2, 3}, and <strong>we know that item 2 is the solution to our problem</strong>. In binary representation, these items are {00, 01, 10, 11}. In this scenario, the quantum oracle will flip the sign of the amplitude of the state <strong>|10⟩</strong> (which corresponds to item 2) while leaving all other states unchanged.</p>
<p>In quantum circuit terms, this can be realized by a <strong>controlled-Z gate</strong> acting on the two qubits of the system, with the target being the second qubit.</p>
<p>This process does not disclose the solution (item 2) to us directly, but it prepares the ground for the next stage of the algorithm, the amplitude amplification stage, which will make this solution more likely to be observed upon measurement.</p>
<p>When implementing Grover's algorithm for a problem with a unique solution, an extra qubit, often referred to as an "ancillary" or "auxiliary" qubit, is utilized. Therefore, we implement it on an <strong>(n+1)-qubit system</strong>, where the additional qubit assists in marking the solution state during the oracle operation.</p>
<pre><code class="lang-r"><span class="hljs-comment"># Here's the function that implements an oracle </span>
<span class="hljs-comment"># marking item 2 = |10&gt; as the solution </span>
oracle_item2 &lt;- <span class="hljs-keyword">function</span>(x){
<span class="hljs-comment"># CCNOT = "controlled controlled NOT gate"</span>
    x &lt;- X(<span class="hljs-number">1</span>) * (CCNOT(c(<span class="hljs-number">1</span>,<span class="hljs-number">2</span>,<span class="hljs-number">3</span>))*(X(<span class="hljs-number">1</span>) * x)) 
    <span class="hljs-keyword">return</span>(x)
}

<span class="hljs-comment"># item 1 = |00&gt; + ancillary qubit =&gt; |000&gt;</span>
&gt; item0 &lt;- qstate(nbits = <span class="hljs-number">2</span>+<span class="hljs-number">1</span>, coefs = c(<span class="hljs-number">1</span>,<span class="hljs-number">0</span>,<span class="hljs-number">0</span>,<span class="hljs-number">0</span>,<span class="hljs-number">0</span>,<span class="hljs-number">0</span>,<span class="hljs-number">0</span>,<span class="hljs-number">0</span>))
&gt; item0
   ( <span class="hljs-number">1</span> )    * |<span class="hljs-number">000</span>&gt; 
<span class="hljs-comment"># item 1 = |01&gt; + ancillary qubit =&gt; |001&gt;</span>
&gt; item1 &lt;- qstate(nbits = <span class="hljs-number">2</span>+<span class="hljs-number">1</span>, coefs = c(<span class="hljs-number">0</span>,<span class="hljs-number">1</span>,<span class="hljs-number">0</span>,<span class="hljs-number">0</span>,<span class="hljs-number">0</span>,<span class="hljs-number">0</span>,<span class="hljs-number">0</span>,<span class="hljs-number">0</span>))
&gt; item1
   ( <span class="hljs-number">1</span> )    * |<span class="hljs-number">001</span>&gt; 
<span class="hljs-comment"># item 2 = |10&gt; + ancillary qubit =&gt; |010&gt;</span>
&gt; item2 &lt;- qstate(nbits = <span class="hljs-number">2</span>+<span class="hljs-number">1</span>, coefs = c(<span class="hljs-number">0</span>,<span class="hljs-number">0</span>,<span class="hljs-number">1</span>,<span class="hljs-number">0</span>,<span class="hljs-number">0</span>,<span class="hljs-number">0</span>,<span class="hljs-number">0</span>,<span class="hljs-number">0</span>))
&gt; item2 <span class="hljs-comment"># the solution</span>
   ( <span class="hljs-number">1</span> )    * |<span class="hljs-number">010</span>&gt; 
<span class="hljs-comment"># item 3 = |11&gt; + ancillary qubit =&gt; |011&gt;</span>
&gt; item3 &lt;- qstate(nbits = <span class="hljs-number">2</span>+<span class="hljs-number">1</span>, coefs = c(<span class="hljs-number">0</span>,<span class="hljs-number">0</span>,<span class="hljs-number">0</span>,<span class="hljs-number">1</span>,<span class="hljs-number">0</span>,<span class="hljs-number">0</span>,<span class="hljs-number">0</span>,<span class="hljs-number">0</span>))
&gt; item3
   ( <span class="hljs-number">1</span> )    * |<span class="hljs-number">011</span>&gt;

<span class="hljs-comment"># The oracle function flip the 3rd qubit (or bit 3) to |1&gt;</span>
&gt; oracle_item2(item0)
   ( <span class="hljs-number">1</span> )    * |<span class="hljs-number">000</span>&gt; 
&gt; oracle_item2(item1)
   ( <span class="hljs-number">1</span> )    * |<span class="hljs-number">001</span>&gt; 
&gt; oracle_item2(item2) <span class="hljs-comment"># the oracle marks item 2, bit 3 flipped to |1&gt;</span>
   ( <span class="hljs-number">1</span> )    * |<span class="hljs-number">110</span>&gt;    
&gt; oracle_item2(item3)
   ( <span class="hljs-number">1</span> )    * |<span class="hljs-number">011</span>&gt; 

&gt; measure(oracle_item2(item0), bit = <span class="hljs-number">3</span>)$value
[<span class="hljs-number">1</span>] <span class="hljs-number">0</span>
&gt; measure(oracle_item2(item1), bit = <span class="hljs-number">3</span>)$value
[<span class="hljs-number">1</span>] <span class="hljs-number">0</span>
&gt; measure(oracle_item2(item2), bit = <span class="hljs-number">3</span>)$value <span class="hljs-comment"># solution</span>
[<span class="hljs-number">1</span>] <span class="hljs-number">1</span>
&gt; measure(oracle_item2(item3), bit = <span class="hljs-number">3</span>)$value
[<span class="hljs-number">1</span>] <span class="hljs-number">0</span>
</code></pre>
<p>Now that we have verified that our oracle function <code>oracle_item2</code> works, let us plot its circuit with any (2+1)-qubit system (<code>nbits=3</code>), here I used |000&gt; as input. I also labeled the <strong>ancillary qubit</strong> as "<strong>j</strong>".</p>
<pre><code class="lang-r">&gt; qsimulatR::plot(oracle_item2(qstate(<span class="hljs-number">3</span>)), qubitnames=c(<span class="hljs-string">"q1"</span>,<span class="hljs-string">"q2"</span>,<span class="hljs-string">"j"</span>))
</code></pre>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1690553743080/04615f8c-af2f-4d44-ae4d-d4fe40c72a95.png" alt class="image--center mx-auto" /></p>
<p>See the figure below from Figgat et al. article [1]. It shows <strong>1-solution oracles</strong> for a (3+1)-qubit system. The red square shows the equivalent <em>item2-oracle</em> for 3 qubits e.g. marked solution = <strong>|010&gt;</strong>.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1690554343736/12f31fb5-d68f-48d3-91ef-191588c44007.png" alt class="image--center mx-auto" /></p>
<h3 id="heading-oracle-mathematical-explanation">Oracle - Mathematical explanation</h3>
<p>For our previous (2+1)-qubit system with solution = item 2 = |10&gt;, we usually write the <strong>oracle's output</strong> quantum state as</p>
<p>$$|\psi'&gt; = \frac{1}{2}|00&gt; +\frac{1}{2}|01&gt; \textbf{-}\frac{1}{2}\textbf{|10&gt;} +\frac{1}{2}|11&gt;$$</p><p>Thus, the oracle marks the solution by <strong>flipping its sign</strong>. This corresponds to a <strong>phase flip</strong> that can be achieved with a Z gate on a single qubit, and this is why you see a "<em>Phase Oracle</em>" implementation in the figure above e.g. a <strong>Controlled-Z (CZ)</strong> gate can be used to implement an oracle in Grover's algorithm without the need for an ancillary qubit, assuming your problem is defined in such a way that a single CZ operation can correctly mark your solution state(s).</p>
<p>Note: here we stick to the "<em>Boolean Oracle</em>" implementation because we will also be working with more than two qubits or dealing with situations where the solution state isn't simply marked by a single CZ operation. Thus, a more complex oracle involving multiple gates (like CCNOTs, ancillary qubits, and additional phase gates) may be required.</p>
<p>That said, let's get back to this <strong>phase flip</strong> performed by the oracle from a mathematical perspective: the oracle function is essentially performing a <strong>symmetry operation</strong> e.g. implementing <strong>a reflection about the axis corresponding to the state |0&gt;</strong>.</p>
<p>$$|\psi'&gt; =\textbf{S}_{|0&gt;}.|\psi&gt;$$</p><p>$$|\psi'&gt; =({2|0\gt\lt0| - I})(|\psi&gt;)$$</p><p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1690561603985/6ef09310-387c-49b2-993e-20ae84e1e82f.png" alt class="image--center mx-auto" /></p>
<p>This symmetry is critical for the "amplitude amplification" process that follows the oracle operation in Grover's algorithm, where another reflection is performed about the superposition state, effectively amplifying the amplitudes of the solution state(s).</p>
<h2 id="heading-grover-diffusion-operator">Grover diffusion operator</h2>
<p>The Grover diffusion operator performs <strong>amplitude amplification</strong> is a procedure that manipulates these amplitudes to increase ("amplify") the amplitudes of desired states (i.e., solution states) while decreasing the amplitudes of undesired states. In essence, amplitude amplification increases the probability that a measurement will result in a desired state, making the correct solution more likely to be found when a measurement is made.</p>
<p>Thus, when the oracle is coupled with Grover's diffusion operator, the process iteratively moves the quantum system closer and closer to the desired solution state(s), improving the probability of measuring a solution when the quantum state is finally read out.</p>
<p>In the context of Grover's algorithm, amplitude amplification plays a crucial role in improving the algorithm's efficiency.</p>
<p><strong>The combination of the oracle and the Grover diffusion operator forms the Grover iterator</strong>, and after approximately sqrt(N) iterations (where N is the total number of possible states), the quantum system is likely to be found in a solution state when a measurement is performed. Thus, Grover's algorithm can find a solution with high probability in O(sqrt(N)) steps, which is a significant speedup over classical algorithms that require O(N) steps for similar problems.</p>
<p>Here's a <em>quick &amp; dirty</em> implementation of Grover's algorithm for item 2 = |10&gt; as a solution, using qsimulatR.</p>
<pre><code class="lang-r"><span class="hljs-comment"># Initialization</span>
x &lt;- qstate(<span class="hljs-number">3</span>)     <span class="hljs-comment"># all 2+1 qubits initialized: |000&gt;</span>
x &lt;- H(<span class="hljs-number">2</span>)*(H(<span class="hljs-number">1</span>)*x) <span class="hljs-comment"># uniform superposition for bits 1 and 2</span>
x &lt;- H(<span class="hljs-number">3</span>)*(X(<span class="hljs-number">3</span>)*x) <span class="hljs-comment"># ancillary qubit (bit 3) flipped to |1&gt; before H </span>
<span class="hljs-comment"># Oracle function</span>
x &lt;- oracle_item2(x)
<span class="hljs-comment"># Grover diffusiion</span>
<span class="hljs-keyword">for</span>(i <span class="hljs-keyword">in</span> c(<span class="hljs-number">1</span>:<span class="hljs-number">3</span>)) {
  x &lt;- H(i) * x
}
x &lt;- X(<span class="hljs-number">1</span>) * (X(<span class="hljs-number">2</span>) * x)
x &lt;- cqgate(bits = c(<span class="hljs-number">1</span>,<span class="hljs-number">2</span>), gate = Z(<span class="hljs-number">2L</span>)) * x <span class="hljs-comment"># custom CZ gate</span>
x &lt;- X(<span class="hljs-number">1</span>) * (X(<span class="hljs-number">2</span>) * x)
<span class="hljs-keyword">for</span>(i <span class="hljs-keyword">in</span> c(<span class="hljs-number">1</span>:<span class="hljs-number">2</span>)) {
  x &lt;- H(i) * x
}
&gt; x
   ( -<span class="hljs-number">1</span> )    * |<span class="hljs-number">110</span>&gt; 
<span class="hljs-comment"># N=2^2=4 is the special case where Grover's algorithm returns </span>
<span class="hljs-comment"># the correct result with certainty after only a single iteration </span>
<span class="hljs-comment"># Measure bit 1</span>
&gt; summary(measure(e1 = x, bit = <span class="hljs-number">1</span>)) <span class="hljs-comment"># repetitions = 1 by default</span>
Bit <span class="hljs-number">1</span> has been measured <span class="hljs-number">1</span> times with the outcome:
<span class="hljs-number">0</span>:  <span class="hljs-number">1</span> 
<span class="hljs-number">1</span>:  <span class="hljs-number">0</span> 
<span class="hljs-comment"># Measure bit 2</span>
&gt; summary(measure(e1 = x, bit = <span class="hljs-number">2</span>))
Bit <span class="hljs-number">2</span> has been measured <span class="hljs-number">1</span> times with the outcome:
<span class="hljs-number">0</span>:  <span class="hljs-number">0</span> 
<span class="hljs-number">1</span>:  <span class="hljs-number">1</span> 
&gt; allStatesProbabilities(x) <span class="hljs-comment"># custom function, see previous article</span>
|<span class="hljs-number">000</span>&gt; |<span class="hljs-number">001</span>&gt; |<span class="hljs-number">010</span>&gt; |<span class="hljs-number">011</span>&gt; |<span class="hljs-number">100</span>&gt; |<span class="hljs-number">101</span>&gt; |<span class="hljs-number">110</span>&gt; |<span class="hljs-number">111</span>&gt; 
    <span class="hljs-number">0</span>     <span class="hljs-number">0</span>     <span class="hljs-number">0</span>     <span class="hljs-number">0</span>     <span class="hljs-number">0</span>     <span class="hljs-number">0</span>     <span class="hljs-number">1</span>     <span class="hljs-number">0</span>  <span class="hljs-comment"># Prob('10')=1</span>
</code></pre>
<p>Let us plot Grover's algorithm final circuit for our example. I've added some comments manually (in blue) in the figure.</p>
<pre><code class="lang-r"><span class="hljs-comment"># The second qubit is measured and its value is stored in res$value</span>
&gt; res &lt;- measure(e1 = x, bit = <span class="hljs-number">2</span>)
&gt; res$value
[<span class="hljs-number">1</span>] <span class="hljs-number">1</span>
<span class="hljs-comment"># The wave function of the quantum state collapses, </span>
<span class="hljs-comment"># the result of which is stored again as a qstate object in res$psi.</span>
&gt; qsimulatR::plot(res$psi, qubitnames = c(<span class="hljs-string">"x1"</span>, <span class="hljs-string">"x2"</span>, <span class="hljs-string">"j"</span>))
</code></pre>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1690566500900/0247dc2b-bf67-4701-ac93-ef49c589ef6e.png" alt class="image--center mx-auto" /></p>
<h3 id="heading-grover-diffusion-operator-mathematical-explanation">Grover diffusion operator - Mathematical explanation</h3>
<p>The Grover diffusion operator effectively performs a <strong>reflection of the state vector about the mean amplitude</strong>. Imagine that each state in your superposition is a point in your high-dimensional state space, and the average amplitude of all states forms another point in that space. The diffusion operator "reflects" each point about this average point. In the two-dimensional case, this would be like reflecting a point about a line in a plane.</p>
<p>$$|\phi&gt; =\textbf{S}|\psi&gt;.|\psi'&gt; =\textbf{S}|\psi&gt;.(S_{|0&gt;}.|\psi&gt;)$$</p><p>$$|\phi&gt; =(2|{\psi\gt\lt\psi|} - I)(S_{|0&gt;}.{|\psi&gt;})$$</p><p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1690567457801/d27752a4-6e66-4643-b322-d8a400e300fa.png" alt class="image--center mx-auto" /></p>
<p>In summary [2], for the first iteration:</p>
<ul>
<li><p>Superposition of |ψ⟩ =&gt; angle <strong>θ/2</strong> between |ψ⟩ and |0⟩</p>
</li>
<li><p>Oracle function (symmetry) =&gt; angle <strong>-θ/2</strong> with respect to |0⟩</p>
</li>
<li><p>Grover's diffusion operator (symmetry) =&gt; angle <strong>3θ/2</strong> with respect to |0⟩</p>
</li>
</ul>
<p>Next iteration:</p>
<ul>
<li><p>Oracle function (symmetry) =&gt; angle <strong>-3θ/2</strong> with respect to |0⟩</p>
</li>
<li><p>Grover's diffusion operator (symmetry) =&gt; angle <strong>5θ/2</strong> with respect to |0⟩</p>
</li>
</ul>
<h2 id="heading-convert-to-qiskit">Convert to Qiskit</h2>
<p>Let us convert to Qiskit the result of the quantum state collapse <code>res$psi</code> with the qsimulatR <code>export2qiskit</code> function.</p>
<p>A <code>circuit.py</code> file will be created in the working directory. <strong>Important: the Qiskit Python code will not work!</strong> you need to perform some manual updates:</p>
<pre><code class="lang-r">&gt; res$psi
   ( -<span class="hljs-number">1</span> )    * |<span class="hljs-number">110</span>&gt; 
<span class="hljs-comment"># check @ circuit in str(x)</span>
&gt; export2qiskit(res$psi)
</code></pre>
<pre><code class="lang-python"><span class="hljs-comment"># automatically generated by qsimulatR</span>
qc = QuantumCircuit(<span class="hljs-number">3</span>,<span class="hljs-number">1</span>) <span class="hljs-comment"># we want 2 classical bits for measurement</span>
qc.h(<span class="hljs-number">0</span>)
qc.h(<span class="hljs-number">1</span>)
qc.x(<span class="hljs-number">2</span>)
qc.h(<span class="hljs-number">2</span>)
qc.x(<span class="hljs-number">0</span>)
qc.ccx(<span class="hljs-number">0</span>, <span class="hljs-number">1</span>, <span class="hljs-number">2</span>)
qc.x(<span class="hljs-number">0</span>)
qc.h(<span class="hljs-number">0</span>)
qc.h(<span class="hljs-number">1</span>)
qc.h(<span class="hljs-number">2</span>)
qc.x(<span class="hljs-number">1</span>)
qc.x(<span class="hljs-number">0</span>)
qc.z(<span class="hljs-number">0</span>, <span class="hljs-number">1</span>) <span class="hljs-comment"># error: because custom cqgate() export is not supported</span>
qc.x(<span class="hljs-number">1</span>)
qc.x(<span class="hljs-number">0</span>)
qc.h(<span class="hljs-number">0</span>)
qc.h(<span class="hljs-number">1</span>)
qc.measure(<span class="hljs-number">1</span>, <span class="hljs-number">0</span>) <span class="hljs-comment"># we want to measure index 0 and index 1 resp.</span>
</code></pre>
<p>Below I've provided the <strong>updated Qiskit code</strong> with corrections as comments (<code># new</code>)</p>
<pre><code class="lang-python"><span class="hljs-comment"># new: imports</span>
<span class="hljs-keyword">from</span> qiskit <span class="hljs-keyword">import</span> *
<span class="hljs-keyword">from</span> qiskit.visualization <span class="hljs-keyword">import</span> plot_histogram
<span class="hljs-comment"># automatically generated by qsimulatR</span>
qc = QuantumCircuit(<span class="hljs-number">3</span>,<span class="hljs-number">2</span>) <span class="hljs-comment"># new</span>
qc.h(<span class="hljs-number">0</span>)
qc.h(<span class="hljs-number">1</span>)
qc.x(<span class="hljs-number">2</span>)
qc.h(<span class="hljs-number">2</span>)
qc.x(<span class="hljs-number">0</span>)
qc.ccx(<span class="hljs-number">0</span>, <span class="hljs-number">1</span>, <span class="hljs-number">2</span>)
qc.x(<span class="hljs-number">0</span>)
qc.h(<span class="hljs-number">0</span>)
qc.h(<span class="hljs-number">1</span>)
qc.h(<span class="hljs-number">2</span>)
qc.x(<span class="hljs-number">1</span>)
qc.x(<span class="hljs-number">0</span>)
qc.ccz(<span class="hljs-number">0</span>, <span class="hljs-number">1</span>, <span class="hljs-number">2</span>) <span class="hljs-comment"># new: replaced by ccz gate</span>
qc.x(<span class="hljs-number">1</span>)
qc.x(<span class="hljs-number">0</span>)
qc.h(<span class="hljs-number">0</span>)
qc.h(<span class="hljs-number">1</span>)
<span class="hljs-comment"># new: Measure the first two qubits into the classical bits</span>
qc.measure(<span class="hljs-number">0</span>,<span class="hljs-number">0</span>)
qc.measure(<span class="hljs-number">1</span>,<span class="hljs-number">1</span>)
<span class="hljs-comment"># new: draw the circuit</span>
qc.draw()
</code></pre>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1690577248254/da5903cd-e144-436f-8f6a-80cfbeca7ab2.png" alt class="image--center mx-auto" /></p>
<pre><code class="lang-python"><span class="hljs-comment"># ... Cont'd</span>
<span class="hljs-comment"># new: job execution with simulator backend</span>
backend = BasicAer.get_backend(<span class="hljs-string">'qasm_simulator'</span>)
job = execute(qc, backend)
job.result().get_counts() <span class="hljs-comment"># result: {'10': 1024}</span>
plot_histogram(job.result().get_counts())
</code></pre>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1690570993637/9e7be297-b104-4e54-9a9a-f463a63fc879.png" alt class="image--center mx-auto" /></p>
<h2 id="heading-before-concluding">Before concluding</h2>
<p>As we delve into the fascinating world of quantum computing and begin to grapple with concepts such as Grover's algorithm, it's perfectly natural for certain questions to arise.</p>
<p>You might wonder, for example, <em>why we initialize the ancillary 'j' qubit to |1&gt;?,</em> or <em>why we use a CCZ gate and additional gates for the Grover diffusion process?</em></p>
<p>Remember, at this stage of our exploration, we are focusing on the key principles and operations without getting too lost in the mathematical underpinnings. These aspects are influenced by specific quantum mechanical properties and mathematical considerations that ensure the algorithm's optimal performance.</p>
<p>However, rest assured that as we progress in our quantum computing journey, and as your understanding deepens, these concepts will become more intuitive. The goal right now is to build a solid foundation, encourage curiosity, and nurture a passion for this transformative field.</p>
<h2 id="heading-conclusion">Conclusion</h2>
<p>In conclusion, Grover's algorithm, a cornerstone in quantum computing, provides an extraordinary advantage for unstructured search, making it an essential topic for those venturing into this field. The beauty of this algorithm lies in its simplicity and the fundamental quantum principles it employs, such as superposition, interference, and quantum entanglement.</p>
<p>In this article, we've explored the key elements that make Grover's algorithm tick, including the creation of a specific quantum oracle and the Grover diffusion operator, which is responsible for the algorithm's amplitude amplification process. By doing so, we've dived into the depth of the algorithm, visualizing it not just as a sequence of gates but rather as a dance of symmetries in the quantum space.</p>
<p>Alongside the theoretical understanding, we've delved into practical implementation using the qsimulatR package in R and how to convert our code to Qiskit. This exercise enhances our comprehension of the algorithm and gives us the hands-on experience necessary to embark on more complex quantum computing problems.</p>
<p>As we continue our journey in quantum computing, keep in mind that the principles and techniques employed in Grover's algorithm echo through many other quantum algorithms. Therefore, a solid grasp of this topic forms a vital stepping stone in your quantum computing journey.</p>
<h2 id="heading-references">References</h2>
<p>[1] Figgat C., Maslov D., Landsman K.A., Linke N.M., Debnath S., Monroe C., "<em>Complete 3-Qubit Grover search on a programmable quantum computer</em>", Nature Communications, 8:1918|DOI:10.1038/s41467-017-01904-7| www.nature.com/naturecommunications, 2017</p>
<p>[2] Bourreau E., Fleury, G., Lacomme P., "<em>Introduction à l'informatique quantique, Apprendre à calculer sur des ordinateurs quantiques avec Python</em>", Collection Blanche, <a target="_blank" href="https://www.eyrolles.com/Informatique/Livre/introduction-a-l-informatique-quantique-9782416006531/"><strong>Eyrolles</strong></a>, 2022</p>
]]></content:encoded></item><item><title><![CDATA[Quantum programs]]></title><description><![CDATA[Cover photo by NatalyaBurova ref. 1308269282 on iStock
Go to R-bloggers for R news and tutorials contributed by hundreds of R bloggers.
This is the third article of the Quantum Computing simulation with R series.
Introduction
The figure below is from...]]></description><link>https://blog.bguarisma.com/quantum-programs</link><guid isPermaLink="true">https://blog.bguarisma.com/quantum-programs</guid><category><![CDATA[R Language]]></category><category><![CDATA[quantum computing]]></category><category><![CDATA[Quantum]]></category><category><![CDATA[Tutorial]]></category><dc:creator><![CDATA[Boris Guarisma]]></dc:creator><pubDate>Fri, 28 Jul 2023 10:46:36 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1690540893825/9ea6398e-8b7b-431f-aea1-a1e307b2fcd9.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Cover photo by NatalyaBurova ref. 1308269282 on <a target="_blank" href="https://www.istockphoto.com/fr">iStock</a></p>
<p>Go to <a target="_blank" href="https://www.r-bloggers.com/">R-bloggers</a> for R news and tutorials contributed by hundreds of R bloggers.</p>
<p>This is the third article of the <a target="_blank" href="https://blog.bguarisma.com/series/qc-r-series">Quantum Computing simulation with R</a> series.</p>
<h2 id="heading-introduction">Introduction</h2>
<p>The figure below is from the excellent book [1] by Bourreau et al.. It illustrates the structure of a quantum program as a quantum circuit. Just as classical programs consist of sequences of operations (like additions, multiplications, or logical comparisons) acting on bits, quantum programs consist of sequences of quantum gates acting on qubits.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1690538869303/98645d0d-ef14-40ae-8068-14780356ddd7.png" alt class="image--center mx-auto" /></p>
<p>Creating a quantum circuit is a fundamental process in quantum computing. Following the figure above, this process can be broken down into several steps as follows:</p>
<ol>
<li><p><strong>Qubits Declaration</strong>: This is the first step in setting up a quantum program and it involves specifying the number of qubits to be used. By declaring qubits, we are essentially setting up 'variables' that will be used in the quantum program. <strong>Classical bits</strong> can also be declared, and they often come into play when we need to store and manipulate the results of quantum measurements. In addition, auxiliary qubits (also known as <strong>ancillary qubits</strong>) are often declared to assist in quantum computations. These are used as "scratch space" during computation or for holding temporary values, and they do not typically contain meaningful data at the end of the computation. Just like regular qubits, they are declared at the start of your quantum program.</p>
</li>
<li><p><strong>Qubits Initialization</strong>: After declaring qubits, the next step is to initialize them. All qubits in a quantum computer start from the |0⟩ state, however, we can use quantum gates, such as the Hadamard gate, to initialize our qubits into a superposition state.</p>
</li>
<li><p><strong>Problem Specification with Quantum Gates</strong>: After initializing the qubits, we then specify our problem in the form of a <strong>quantum circuit</strong>. A quantum circuit is a series of quantum gates (operations) that are applied to our qubits in a particular order to perform computations. The gates transform the initial state of the qubits into another state. Different problems will require different quantum circuits, and the design of these circuits is a crucial aspect of quantum algorithm design.</p>
</li>
<li><p><strong>Measurement to Obtain Results</strong>: The final step in a quantum program involves measuring the qubits to obtain results. Measurements in quantum computing are unique because they not only give you the state of the qubits (either |0⟩ or |1⟩ for each qubit) but also collapse the quantum state to the measured state. The results of the measurement are then read out to classical bits, which can be further processed on a classical computer if necessary. This step allows us to extract useful information from the quantum system into the classical world.</p>
</li>
</ol>
<h2 id="heading-quantum-program-compilation-and-execution">Quantum program compilation and execution</h2>
<ol>
<li><p><strong>Create Your Quantum Algorithm</strong>: The first step in quantum programming is very similar to classic programming - you have to come up with an algorithm. Instead of typical if... then... else... type instructions, you'll be working with a quantum circuit filled with quantum gates, which act as the basic instructions for your algorithm.</p>
</li>
<li><p><strong>Choose Your Environment and Language</strong>: Next, you'll need to select an environment and programming language for writing your quantum code. Many beginners start with Python because of its simplicity and the availability of quantum libraries, such as Qiskit from IBM or QLM from ATOS.</p>
</li>
<li><p><strong>Transpile Your Code</strong>: After writing your code, you'll need to convert (or transpile) it into a language that quantum computers understand - this is often QASM (Quantum Assembly Language). The transpiler not only translates your code but also optimizes it, ensuring your quantum circuit runs as efficiently as possible. This optimization takes into account the specific properties of the quantum machine that will run the code.</p>
</li>
<li><p><strong>Execute Your Quantum Circuit</strong>: The final step is to run your quantum circuit. You can do this in two ways - either on a classic computer using a quantum simulator or on a real quantum machine. Running a circuit on a simulator is useful for testing and learning, as it allows you to experiment with "perfect" qubits without the hardware limitations or noise issues of real quantum computers. However, to fully experience the power of quantum computing, you may want to run your code on a real quantum machine. This is typically done by sending your code over the internet to a quantum computer service, like the one provided by IBM through Qiskit. Once received, your code is placed in a queue and executed when the machine is available.</p>
</li>
</ol>
<h2 id="heading-our-scope-for-this-tutorial">Our scope for this tutorial</h2>
<p>The main objective of the <a target="_blank" href="https://blog.bguarisma.com/series/qc-r-series">Quantum Computing simulation with R</a> is to learn how to write quantum programs using the qsmulatR package. Thus we will only focus on point "2. <strong>Choose Your Environment and Language</strong>" above by converting our R code into Qiskit with the <code>qsimulatR::export2qiskit()</code> function.</p>
<p>Here's a quick &amp; dirty implementation with qsimulatR for a very famous quantum algorithm, try to identify the <em>initialization</em>, the <em>problem specification</em> and the <em>measurement</em> parts. The algorithm will be explained in the next article.</p>
<pre><code class="lang-r">x &lt;- qstate(<span class="hljs-number">3</span>)
x &lt;- H(<span class="hljs-number">2</span>)*(H(<span class="hljs-number">1</span>)*x)
x &lt;- H(<span class="hljs-number">3</span>)*(X(<span class="hljs-number">3</span>)*x)
x &lt;- X(<span class="hljs-number">1</span>) * (CCNOT(c(<span class="hljs-number">1</span>,<span class="hljs-number">2</span>,<span class="hljs-number">3</span>)) *(X(<span class="hljs-number">1</span>) * x))
<span class="hljs-keyword">for</span>(i <span class="hljs-keyword">in</span> c(<span class="hljs-number">1</span>:<span class="hljs-number">3</span>)) {
  x &lt;- H(i) * x
}
x &lt;- X(<span class="hljs-number">1</span>) * (X(<span class="hljs-number">2</span>) * x)
x &lt;- cqgate(bits = c(<span class="hljs-number">1</span>,<span class="hljs-number">2</span>), gate = Z(<span class="hljs-number">2L</span>)) * x
x &lt;- X(<span class="hljs-number">1</span>) * (X(<span class="hljs-number">2</span>) * x)
<span class="hljs-keyword">for</span>(i <span class="hljs-keyword">in</span> c(<span class="hljs-number">1</span>:<span class="hljs-number">2</span>)) {
  x &lt;- H(i) * x
}
x
qsimulatR::plot(x, qubitnames = c(<span class="hljs-string">"x1"</span>, <span class="hljs-string">"x2"</span>, <span class="hljs-string">"j"</span>))
hist(measure(e1 = x, bit = <span class="hljs-number">2</span>, repetitions = <span class="hljs-number">1</span>))
</code></pre>
<p>Remember, quantum programming can seem daunting at first, but with practice and patience, you'll start to grasp these new concepts and begin to appreciate the immense potential that quantum computing offers. Happy coding!</p>
<h2 id="heading-references">References</h2>
<p>[1] Bourreau E., Fleury, G., Lacomme P., "<em>Introduction à l'informatique quantique, Apprendre à calculer sur des ordinateurs quantiques avec Python</em>", Collection Blanche, <a target="_blank" href="https://www.eyrolles.com/Informatique/Livre/introduction-a-l-informatique-quantique-9782416006531/">Eyrolles</a>, 2022</p>
]]></content:encoded></item><item><title><![CDATA[Quantum computing with qsimulatR]]></title><description><![CDATA[Cover photo by berya113 ref. 1126414116 on iStock
Go to R-bloggers for R news and tutorials contributed by hundreds of R bloggers.
This is the first article of the Quantum Computing simulation with R series.
Welcome to our series on quantum computing...]]></description><link>https://blog.bguarisma.com/quantum-computing-with-qsimulatr</link><guid isPermaLink="true">https://blog.bguarisma.com/quantum-computing-with-qsimulatr</guid><category><![CDATA[R Language]]></category><category><![CDATA[quantum computing]]></category><category><![CDATA[Quantum]]></category><category><![CDATA[Tutorial]]></category><dc:creator><![CDATA[Boris Guarisma]]></dc:creator><pubDate>Thu, 27 Jul 2023 22:04:02 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1690215227214/0ce83388-8ea1-4f6f-846a-99e41d38095f.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Cover photo by berya113 ref. 1126414116 on <a target="_blank" href="https://www.istockphoto.com/fr">iStock</a></p>
<p>Go to <a target="_blank" href="https://www.r-bloggers.com/">R-bloggers</a> for R news and tutorials contributed by hundreds of R bloggers.</p>
<p>This is the first article of the <a target="_blank" href="https://blog.bguarisma.com/series/qc-r-series">Quantum Computing simulation with R</a> series.</p>
<p>Welcome to our series on quantum computing basics with the <a target="_blank" href="https://cran.r-project.org/web/packages/qsimulatR/">qsimulatR</a> package. This is the first in a series of articles aimed at providing an accessible and practical introduction to the basics of quantum computing using the R language. If you are a beginner in quantum computing but have a working knowledge of R, you're in the right place!</p>
<h2 id="heading-introduction">Introduction</h2>
<p><a target="_blank" href="https://cran.r-project.org/web/packages/qsimulatR/">qsimulatR</a> is a quantum computer simulator developed by Ostmeyer and Urbach [1], it provides an excellent package to learn, explore, and experiment with quantum computing basics in the comfort of the R programming environment. Not only does it simulate the behavior of a quantum computer, but it also allows you to construct quantum circuits, perform computations and convert the code to Qiskit, making it a powerful tool for quantum computing beginners.</p>
<p>In this article, we will explore <strong>single-system</strong> quantum states, delve into the concept of superposition, calculate state probabilities, and understand what happens during a quantum measurement. Along the way, we will also learn about essential quantum gates such as the Hadamard gate, Pauli gates (X, Z, Y), and Phase gates (S, T), as well as their geometric interpretations with the Bloch sphere.</p>
<p>While the <a target="_blank" href="https://cran.r-project.org/web/packages/qsimulatR/">qsimulatR</a> package provides a succinct explanation in its vignette document [2], our goal in this series is to expand on these foundations and present them in a more beginner-friendly manner. We will make use of numerous examples, code snippets, and visual aids, all aimed at making your quantum computing journey more manageable and enjoyable.</p>
<p>So let's set off on this exciting journey of discovery into the world of quantum computing. Prepare to leave your classical computation instincts behind and enter a realm where seemingly impossible tasks become possible!</p>
<h2 id="heading-quantum-states">Quantum States</h2>
<p>The term <strong>qubit</strong> (quantum bit) refers to a quantum system whose classical state set is {0, 1}. It is just a bit, but it can be in a <strong>quantum state</strong> [3].</p>
<p>A quantum state of a system is represented by a <em>column vector</em> characterized by these two properties:</p>
<ol>
<li><p>The entries of a quantum state vector are <strong>complex numbers</strong>.</p>
</li>
<li><p>The <strong>sum of the absolute values squared</strong> of the entries of a quantum state vector must be equal to 1.</p>
</li>
</ol>
<p>For a single qubit, the <strong>computational basis</strong> consists of two states: <strong>|0⟩</strong> and <strong>|1⟩</strong>. These basis states are orthogonal, meaning they are independent of each other and can't be created by combining the other.</p>
<p>Thus, the following are the vectors of states |0⟩ and |1⟩ respectively,</p>
<p>$$\begin{pmatrix} 1 \\ 0 \\ \end{pmatrix} = |0&gt; , \begin{pmatrix} 0 \\ 1 \\ \end{pmatrix} = |1&gt;$$</p><p>Let us define <strong>|0&gt;</strong> with the qsimulatR package:</p>
<pre><code class="lang-r">&gt; ket0 &lt;- qstate(nbits = <span class="hljs-number">1</span>)
&gt; ket0
 ( <span class="hljs-number">1</span> )    * |<span class="hljs-number">0</span>&gt;
</code></pre>
<p>Let us now look at the structure of the |0&gt; <code>qstate</code> object</p>
<pre><code class="lang-r">&gt; str(ket0)
Formal class <span class="hljs-string">'qstate'</span> [package <span class="hljs-string">"qsimulatR"</span>] with <span class="hljs-number">5</span> slots
  ..@ nbits  : int <span class="hljs-number">1</span>
  ..@ coefs  : cplx [<span class="hljs-number">1</span>:<span class="hljs-number">2</span>] <span class="hljs-number">1</span>+<span class="hljs-number">0i</span> <span class="hljs-number">0</span>+<span class="hljs-number">0i</span>
  ..@ basis  : chr [<span class="hljs-number">1</span>:<span class="hljs-number">2</span>] <span class="hljs-string">"|0&gt;"</span> <span class="hljs-string">"|1&gt;"</span>
  ..@ noise  :List of <span class="hljs-number">4</span>
  .. ..$ p    : num <span class="hljs-number">0</span>
  .. ..$ bits : int <span class="hljs-number">1</span>
  .. ..$ error: chr <span class="hljs-string">"any"</span>
  .. ..$ args : list()
  ..@ circuit:List of <span class="hljs-number">2</span>
  .. ..$ ncbits  : num <span class="hljs-number">0</span>
  .. ..$ gatelist: list()
</code></pre>
<p>The values of the <code>coefs</code> slot confirm that |0&gt; is indeed a vector with two complex entries (1, 0) = (1+0i, 0+0i).</p>
<p>Let us do the same thing for <strong>|1&gt;.</strong> We can define <strong>|1&gt;</strong> by explicitly creating the qstate object with coefficient vector (0, 1) or by <strong>applying a bit flip with an X gate to |0&gt; (recommended).</strong> The Pauli X gate will be explained later in this chapter.</p>
<pre><code class="lang-r"><span class="hljs-comment"># create |1&gt; with coefficient vector (0, 1).</span>
&gt; ket1 &lt;- qstate(nbits = <span class="hljs-number">1</span>, coefs = c(<span class="hljs-number">0</span>, <span class="hljs-number">1</span>))
&gt; ket1
   ( <span class="hljs-number">1</span> )    * |<span class="hljs-number">1</span>&gt; 

<span class="hljs-comment"># apply bit flip with an X gate to |0&gt; (recommended)</span>
&gt; ket1 &lt;- X(<span class="hljs-number">1</span>)*ket0
&gt; ket1
   ( <span class="hljs-number">1</span> )    * |<span class="hljs-number">1</span>&gt;
</code></pre>
<p>Let us now compute the <em>sum of the absolute values squared</em> of the entries of a quantum state vector. The square of the absolute value of the complex number a+ib is |a+ib|². In R, you can compute the squared value of the result of the <code>Mod()</code> function as follows:</p>
<pre><code class="lang-r"><span class="hljs-comment"># square of modules of complex entries</span>
&gt; Mod(ket0@coefs)^<span class="hljs-number">2</span>
 <span class="hljs-number">1</span> <span class="hljs-number">0</span>
<span class="hljs-comment"># sum of square of modules of complex entries</span>
&gt; sum(Mod(ket0@coefs)^<span class="hljs-number">2</span>))
 <span class="hljs-number">1</span>
</code></pre>
<h2 id="heading-superposition">Superposition</h2>
<p>The power of quantum computing lies in its use of quantum bits or "qubits," which, unlike classical bits, <strong>can exist in multiple states simultaneously</strong> thanks to a phenomenon known as <strong>superposition</strong>.</p>
<p>Superposition allows us to perform calculations on many states at the same time, and <em>in fine</em> allows exponential speed-up of quantum algorithms compared to classical ones [2].</p>
<p>Thus, in the quantum world, a qubit can exist in a state that is a <strong>linear combination</strong> of basis states |0&gt; and |1&gt;.</p>
<p>$$|\psi&gt; = \begin{pmatrix} \frac{1+2i}{3} \\ -\frac{2}{3} \\ \end{pmatrix} = \frac{1+2i}{3}|0&gt; -\frac{2}{3} |1&gt;$$</p><p>The following quantum state <strong>|ψ⟩</strong> is a superposition of |0&gt; and |1&gt; quantum states</p>
<pre><code class="lang-r">&gt; psi &lt;- qstate(nbits = <span class="hljs-number">1</span>, coefs = c((<span class="hljs-number">1</span>+<span class="hljs-number">2i</span>)/<span class="hljs-number">3</span>, -<span class="hljs-number">2</span>/<span class="hljs-number">3</span>))
&gt; psi
   ( <span class="hljs-number">0.3333333</span>+<span class="hljs-number">0.6666667i</span> )    * |<span class="hljs-number">0</span>&gt; 
 + ( -<span class="hljs-number">0.6666667</span>+<span class="hljs-number">0i</span> )        * |<span class="hljs-number">1</span>&gt;
</code></pre>
<p>We can check that <strong>|phi&gt;</strong> is a valid quantum state vector by verifying that the sum of the absolute values squared of its entries is equal to 1.</p>
<pre><code class="lang-r">&gt; sum(Mod(psi@coefs)^<span class="hljs-number">2</span>)
[<span class="hljs-number">1</span>] <span class="hljs-number">1</span>
</code></pre>
<p>This confirms that the condition that the sum of the absolute values squared of any quantum state vector equals 1 is therefore equivalent to that vector having <strong>Euclidean norm equal to 1</strong>. That is, <strong>quantum state vectors are <em>unit vectors</em></strong> with respect to the Euclidean norm [3].</p>
<h2 id="heading-measuring-quantum-states"><strong>Measuring quantum states</strong></h2>
<p>In quantum mechanics, the "<strong>collapse</strong>" of a quantum state, also known as "wave function collapse" or "projection postulate", refers to the process of going from a superposition of states to a single state. This happens when a <strong>measurement</strong> is made.</p>
<p>Let's now explore the intriguing phenomenon that occurs during the <strong>measurement of a quantum state</strong>, with particular attention to a type of measurement called a standard basis measurement.</p>
<p>The act of measuring a system in a quantum state won't yield a quantum state vector to the observer. Instead, what the observer witnesses is a <strong>classical state</strong>. This essentially portrays measurements as a bridge between the quantum and classical realms, enabling the extraction of classical information from quantum states.</p>
<p>The fundamental rule of quantum measurement, known as the <strong>Born rule</strong>, is quite straightforward: <strong>when you measure a quantum state, each classical state of the system has a probability of being the result</strong>. This probability is equal to the squared absolute value of the component in the quantum state vector that corresponds to that particular classical state.</p>
<p>Interestingly, this rule aligns with the prerequisite that the sum of the squared absolute values of the components in a quantum state vector equals 1. This implies that the probabilities of obtaining various classical states upon measurement collectively add up to 1.</p>
<p>Let us then compute the probabilities to obtain classical states 0 and 1 respectively when measuring the quantum state <strong>|ψ⟩</strong></p>
<p>$$Prob(0) = \Bigl|\frac{1+2i}{3} \Bigr|^2 = \frac{5}{9}\hspace{1cm} Prob(1) = \Bigl|-\frac{2}{3} \Bigr|^2 = \frac{4}{9}$$</p><pre><code class="lang-r">&gt; psi
   ( <span class="hljs-number">0.3333333</span>+<span class="hljs-number">0.6666667i</span> )    * |<span class="hljs-number">0</span>&gt; 
 + ( -<span class="hljs-number">0.6666667</span>+<span class="hljs-number">0i</span> )        * |<span class="hljs-number">1</span>&gt;
&gt; res &lt;- Mod(psi@coefs)^<span class="hljs-number">2</span>
&gt; names(res) &lt;- psi@basis
&gt; res
      |<span class="hljs-number">0</span>&gt;       |<span class="hljs-number">1</span>&gt; 
<span class="hljs-number">0.5555556</span> <span class="hljs-number">0.4444444</span>
</code></pre>
<p>In qsimulatR you do not need to code the probabilities computation, we use the <code>measure</code> function instead. It performs <code>repetitions</code> measurements of the qubit <code>bit</code>.</p>
<pre><code class="lang-r">&gt; (measure(e1 = psi, bit = <span class="hljs-number">1</span>, repetitions = <span class="hljs-number">100</span>))
$bit
[<span class="hljs-number">1</span>] <span class="hljs-number">1</span>

$repetitions
[<span class="hljs-number">1</span>] <span class="hljs-number">100</span>

$prob
[<span class="hljs-number">1</span>] <span class="hljs-number">0.5555556</span> <span class="hljs-number">0.4444444</span>

$value
  [<span class="hljs-number">1</span>] <span class="hljs-number">1</span> <span class="hljs-number">1</span> <span class="hljs-number">0</span> <span class="hljs-number">0</span> <span class="hljs-number">0</span> <span class="hljs-number">0</span> <span class="hljs-number">0</span> <span class="hljs-number">0</span> <span class="hljs-number">1</span> <span class="hljs-number">1</span> <span class="hljs-number">1</span> <span class="hljs-number">0</span> <span class="hljs-number">0</span> <span class="hljs-number">0</span> <span class="hljs-number">0</span> <span class="hljs-number">1</span> <span class="hljs-number">0</span> <span class="hljs-number">0</span> <span class="hljs-number">0</span> <span class="hljs-number">0</span> <span class="hljs-number">0</span> <span class="hljs-number">0</span> <span class="hljs-number">1</span> <span class="hljs-number">1</span> <span class="hljs-number">0</span> <span class="hljs-number">0</span> <span class="hljs-number">0</span> <span class="hljs-number">1</span> <span class="hljs-number">1</span> <span class="hljs-number">0</span> <span class="hljs-number">0</span> <span class="hljs-number">1</span> <span class="hljs-number">0</span> <span class="hljs-number">0</span> <span class="hljs-number">0</span> <span class="hljs-number">1</span> <span class="hljs-number">0</span> <span class="hljs-number">1</span> <span class="hljs-number">0</span> <span class="hljs-number">0</span> <span class="hljs-number">1</span> <span class="hljs-number">0</span> <span class="hljs-number">0</span> <span class="hljs-number">0</span> <span class="hljs-number">0</span> <span class="hljs-number">0</span> <span class="hljs-number">0</span> <span class="hljs-number">0</span> <span class="hljs-number">1</span> <span class="hljs-number">1</span>
 [<span class="hljs-number">51</span>] <span class="hljs-number">1</span> <span class="hljs-number">1</span> <span class="hljs-number">1</span> <span class="hljs-number">1</span> <span class="hljs-number">1</span> <span class="hljs-number">1</span> <span class="hljs-number">0</span> <span class="hljs-number">0</span> <span class="hljs-number">0</span> <span class="hljs-number">1</span> <span class="hljs-number">0</span> <span class="hljs-number">0</span> <span class="hljs-number">1</span> <span class="hljs-number">1</span> <span class="hljs-number">0</span> <span class="hljs-number">0</span> <span class="hljs-number">1</span> <span class="hljs-number">1</span> <span class="hljs-number">0</span> <span class="hljs-number">1</span> <span class="hljs-number">0</span> <span class="hljs-number">1</span> <span class="hljs-number">0</span> <span class="hljs-number">0</span> <span class="hljs-number">0</span> <span class="hljs-number">0</span> <span class="hljs-number">1</span> <span class="hljs-number">0</span> <span class="hljs-number">0</span> <span class="hljs-number">1</span> <span class="hljs-number">0</span> <span class="hljs-number">0</span> <span class="hljs-number">0</span> <span class="hljs-number">0</span> <span class="hljs-number">0</span> <span class="hljs-number">0</span> <span class="hljs-number">0</span> <span class="hljs-number">0</span> <span class="hljs-number">0</span> <span class="hljs-number">0</span> <span class="hljs-number">1</span> <span class="hljs-number">0</span> <span class="hljs-number">1</span> <span class="hljs-number">0</span> <span class="hljs-number">1</span> <span class="hljs-number">0</span> <span class="hljs-number">0</span> <span class="hljs-number">1</span> <span class="hljs-number">0</span> <span class="hljs-number">0</span>

attr(,<span class="hljs-string">"class"</span>)
[<span class="hljs-number">1</span>] <span class="hljs-string">"measurement"</span> <span class="hljs-string">"list"</span>
</code></pre>
<p>qsimulatR displays summary of the measurement and its corresponding histogram.</p>
<pre><code class="lang-r">&gt; meas &lt;- measure(e1 = psi, bit = <span class="hljs-number">1</span>, repetitions = <span class="hljs-number">100</span>)
&gt; summary(meas)
Bit <span class="hljs-number">1</span> has been measured <span class="hljs-number">100</span> times with the outcome:
<span class="hljs-number">0</span>:  <span class="hljs-number">58</span> 
<span class="hljs-number">1</span>:  <span class="hljs-number">42</span>
&gt;hist(meas)
</code></pre>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1690285752279/c3be00e2-8400-439b-af19-f9b0d2c2fc93.png" alt class="image--center mx-auto" /></p>
<h2 id="heading-the-bloch-sphere">The Bloch sphere</h2>
<p>We introduce the Bloc sphere to help you understand the geometric interpretation of the H, X, Y, Z, S and T gates (unitary operations on qubits) explained later in this article.</p>
<p>We can write any normalized (pure) quantum state as</p>
<p>$$|\psi&gt; = \cos(\frac{\theta}{2})|0&gt; + e^{i\phi} \sin(\frac{\theta}{2})|1&gt;$$</p><p>where the angle <strong>θ</strong> ε [0, 2π] determines the <em>probability</em> to measure |0&gt; or |1&gt; states, and the angle <strong>φ</strong> ε [0, π] describes the <em>relative</em> <em>phase</em>.</p>
<p>All normalized (pure) states can be illustrated on the surface of a sphere with radius <strong>r</strong>, where |<strong>r</strong>| = 1. We call this sphere the <strong>Bloch sphere</strong>.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1690287192871/cf554d7f-7f77-4bf9-b865-f8fc4d2e9c85.png" alt class="image--center mx-auto" /></p>
<p><strong><em>IMPORTANT</em></strong>*: Please be aware that the angles used in the Bloch sphere are twice as big as in the Hilbert space. Example: in the Hilbert space,* <strong><em>z-axis basis states |0&gt; and |1&gt; are orthogonal</em></strong> <em>e.g. the angle is π/2=90°,</em> <strong><em>but in the Bloch sphere the angle between states |0&gt; and |1&gt; is</em></strong> <em>π</em> <strong><em>\= 180°</em></strong> <em>(z-axis).</em></p>
<p>The coordinates of such a state <strong>|ψ⟩</strong> are given by the Bloch vector <strong>r</strong></p>
<p>$$\vec{r} = \begin{pmatrix} \sin(\theta)\cos(\phi) \\ \sin(\theta)\sin(\phi) \\ \cos(\phi) \end{pmatrix}$$</p><p>The following are the coordinates of vector r for the following states on their corresponding axis on the Bloch spere:</p>
<p>$$z-axis, |0&gt;:\hspace{0.5cm} \theta=0, \phi=arbitrary \hspace{0.5cm}\Rightarrow \vec{r}=\begin{pmatrix} 0\\ 0\\ 1 \end{pmatrix}$$</p><p>$$z-axis, |1&gt;:\hspace{0.5cm} \theta=\pi, \phi= arbitrary \hspace{0.5cm}\Rightarrow\vec{r}=\begin{pmatrix} 0\\ 0\\ -1 \end{pmatrix}$$</p><p>$$x-axis, |+&gt;:\hspace{0.5cm} \theta=\frac{\pi}{2}, \phi=0 \hspace{0.5cm}\Rightarrow\vec{r}=\begin{pmatrix} 1\\ 0\\ 0 \end{pmatrix}$$</p><p>$$x-axis, |-&gt;:\hspace{0.5cm} \theta=\frac{\pi}{2}, \phi=\pi \hspace{0.5cm}\Rightarrow\vec{r}=\begin{pmatrix} -1\\ 0\\ 0 \end{pmatrix}$$</p><p>$$y-axis, |+i&gt;:\hspace{0.5cm} \theta=\frac{\pi}{2}, \phi=\frac{\pi}{2} \hspace{0.5cm}\Rightarrow\vec{r}=\begin{pmatrix} 0\\ 1\\ 0 \end{pmatrix}$$</p><p>$$y-axis, |-i&gt;:\hspace{0.5cm} \theta=\frac{\pi}{2}, \phi=\frac{3\pi}{2} \hspace{0.5cm}\Rightarrow\vec{r}=\begin{pmatrix} 0\\ -1\\ 0 \end{pmatrix}$$</p><h3 id="heading-projections">Projections</h3>
<p>Remember that for a normalized quantum state <strong>|ψ⟩</strong></p>
<p>$$|\psi&gt; = \cos(\frac{\theta}{2})|0&gt; + e^{i\phi} \sin(\frac{\theta}{2})|1&gt;$$</p><p>the angle <strong>θ</strong> determines the <em>probability</em> to measure |0&gt; or |1&gt; states, and here is how these probabilities are computed:</p>
<p>$$Prob(0) = \cos²\theta, \hspace{.05cm}Prob(1)=\sin^2\theta$$</p><p>We say that the <strong>Z-measurement</strong> corresponds to the <strong>projection of |ψ⟩ onto the z-axis</strong>. A Z-measurement, also referred to as a measurement in the computational basis, is a common operation in quantum computing. It is associated with the Pauli Z gate and essentially measures a quantum state in the basis states |0⟩ and |1⟩.</p>
<p>The projections of |ψ⟩ onto the z-axis are written as</p>
<p>$$ = Prob(0) \hspace{1cm} = Prob(1)$$</p><p>Example: consider the quantum state <strong>|ψ⟩</strong></p>
<p>$$|\psi&gt; = \frac{1+2i}{3}|0&gt; -\frac{2}{3} |1&gt;$$</p><p>$$=Prob(0) = \Bigl|\frac{1+2i}{3} \Bigr|^2 = \frac{5}{9}\hspace{1cm}$$</p><p>$$=Prob(1) = \Bigl|-\frac{2}{3} \Bigr|^2 = \frac{4}{9}$$</p><p><strong>&lt;0|</strong> is pronounced as "<strong>bra</strong> 0". It refers to the row vector obtained by taking the <strong><em>conjugate-transpose</em></strong> of the column vector |0&gt;, where (in addition to transposing the vector from a column vector to a row vector) each entry is replaced by its complex conjugate [1].</p>
<p>$$<pre><code class="lang-r"><span class="hljs-comment"># &lt;0| is a row vector</span>
&gt; bra0 &lt;- Conj(t(ket0@coefs))
bra0
     [,<span class="hljs-number">1</span>] [,<span class="hljs-number">2</span>]
[<span class="hljs-number">1</span>,] <span class="hljs-number">1</span>+<span class="hljs-number">0i</span> <span class="hljs-number">0</span>+<span class="hljs-number">0i</span>
<span class="hljs-comment"># |ψ&gt; is a column vector</span>
&gt; psi &lt;- qstate(nbits = <span class="hljs-number">1</span>, coefs = c((<span class="hljs-number">1</span>+<span class="hljs-number">2i</span>)/<span class="hljs-number">3</span>, -<span class="hljs-number">2</span>/<span class="hljs-number">3</span>))
&gt; psi@coefs
[<span class="hljs-number">1</span>]  <span class="hljs-number">0.3333333</span>+<span class="hljs-number">0.6666667i</span> -<span class="hljs-number">0.6666667</span>+<span class="hljs-number">0.0000000i</span>
<span class="hljs-comment"># &lt;0|ψ&gt; is a matrix product = inner product between the two vectors</span>
&gt; bra0 %*% psi@coefs
                     [,<span class="hljs-number">1</span>]
[<span class="hljs-number">1</span>,] <span class="hljs-number">0.3333333</span>+<span class="hljs-number">0.6666667i</span>
<span class="hljs-comment"># Probabilty to obtain 0 after measurement   </span>
&gt; Mod(bra0 %*% psi@coefs)^<span class="hljs-number">2</span>
          [,<span class="hljs-number">1</span>]
[<span class="hljs-number">1</span>,] <span class="hljs-number">0.5555556</span>

<span class="hljs-comment"># Same thing for |1&gt;</span>
&gt; ket1 &lt;- X(<span class="hljs-number">1</span>)*ket0
&gt; bra1 &lt;- Conj(t(ket1@coefs))
&gt; bra1
     [,<span class="hljs-number">1</span>] [,<span class="hljs-number">2</span>]
[<span class="hljs-number">1</span>,] <span class="hljs-number">0</span>+<span class="hljs-number">0i</span> <span class="hljs-number">1</span>+<span class="hljs-number">0i</span>
&gt; bra1 %*% psi@coefs
              [,<span class="hljs-number">1</span>]
[<span class="hljs-number">1</span>,] -<span class="hljs-number">0.6666667</span>+<span class="hljs-number">0i</span>
&gt; Mod(bra1 %*% psi@coefs)^<span class="hljs-number">2</span>
          [,<span class="hljs-number">1</span>]
[<span class="hljs-number">1</span>,] <span class="hljs-number">0.4444444</span>
</code></pre>
<h2 id="heading-hadamard-gate">Hadamard gate</h2>
<p>When running quantum algorithms we usually start with several quantum states <strong>initialized</strong> as |0&gt;, and the first thing we do is to perform the superposition of each one of the initialized states. This is done with an operation called the <strong>Hadamard</strong> gate.</p>
<p>The <strong>Hadamard</strong> gate creates a <strong>uniform superposition</strong> of the quantum state.</p>
<p>Let us apply the H gate to <strong>|0&gt;</strong></p>
<pre><code class="lang-r">&gt; H(<span class="hljs-number">1</span>)*qstate(<span class="hljs-number">1</span>)
   ( <span class="hljs-number">0.7071068</span> )    * |<span class="hljs-number">0</span>&gt; 
 + ( <span class="hljs-number">0.7071068</span> )    * |<span class="hljs-number">1</span>&gt;
</code></pre>
<p>Notice that coefficients values are equal to <code>1/sqrt(2)</code>, we can generalize this to <code>1/sqrt(N)</code> where <code>N</code> is the number of possible states which is <code>N=2^nbits</code>.</p>
<p>So far, we have only considered <code>nbits=1</code> cases since we're working with single systems taking values from the set {0, 1} of possible classical states, thus the size of the set is N = 2^1 = 2.</p>
<p>Notice that the Hadamard operation is reversible e.g. if we apply H(1) twice we "come back" to <strong>|0&gt;</strong></p>
<pre><code class="lang-r"><span class="hljs-comment"># Parentheses are important !!</span>
&gt; H(<span class="hljs-number">1</span>)*( H(<span class="hljs-number">1</span>)*qstate(<span class="hljs-number">1</span>) )
 ( <span class="hljs-number">1</span> )    * |<span class="hljs-number">0</span>&gt;
</code></pre>
<p>Let us look at the structure of the <strong>H(1)</strong> operation</p>
<pre><code class="lang-r">&gt; str(H(<span class="hljs-number">1</span>))
Formal class <span class="hljs-string">'sqgate'</span> [package <span class="hljs-string">"qsimulatR"</span>] with <span class="hljs-number">3</span> slots
  ..@ bit : int <span class="hljs-number">1</span>
  ..@ M   : cplx [<span class="hljs-number">1</span>:<span class="hljs-number">2</span>, <span class="hljs-number">1</span>:<span class="hljs-number">2</span>] <span class="hljs-number">0.707</span>+<span class="hljs-number">0i</span> <span class="hljs-number">0.707</span>+<span class="hljs-number">0i</span> <span class="hljs-number">0.707</span>+<span class="hljs-number">0i</span> <span class="hljs-keyword">...</span>
  ..@ type: chr <span class="hljs-string">"H"</span>
</code></pre>
<p>Notice that the <code>M</code> slot is a complex matrix</p>
<pre><code class="lang-r">&gt; H(<span class="hljs-number">1</span>)@M
             [,<span class="hljs-number">1</span>]          [,<span class="hljs-number">2</span>]
[<span class="hljs-number">1</span>,] <span class="hljs-number">0.7071068</span>+<span class="hljs-number">0i</span>  <span class="hljs-number">0.7071068</span>+<span class="hljs-number">0i</span>
[<span class="hljs-number">2</span>,] <span class="hljs-number">0.7071068</span>+<span class="hljs-number">0i</span> -<span class="hljs-number">0.7071068</span>+<span class="hljs-number">0i</span>
</code></pre>
<p>We say that the <strong>H(1)</strong> operation is represented by the <strong>unitary matrix</strong> <code>M</code>.</p>
<p>$$H = \frac{1}{\sqrt{2}} \begin{pmatrix} 1 &amp; 1 \\ -1 &amp; 1 \\ \end{pmatrix}$$</p><p>If you multiply <code>M</code> by its conjugate transpose you will obtain the identity matrix.</p>
<pre><code class="lang-r"><span class="hljs-comment"># Conj(t(H(1)@M)): conjugate transpose of M </span>
<span class="hljs-comment"># e.g., transpose M and take the complex conjugate of each entry</span>
&gt; H(<span class="hljs-number">1</span>)@M %*% Conj(t(H(<span class="hljs-number">1</span>)@M))
     [,<span class="hljs-number">1</span>] [,<span class="hljs-number">2</span>]
[<span class="hljs-number">1</span>,] <span class="hljs-number">1</span>+<span class="hljs-number">0i</span> <span class="hljs-number">0</span>+<span class="hljs-number">0i</span>
[<span class="hljs-number">2</span>,] <span class="hljs-number">0</span>+<span class="hljs-number">0i</span> <span class="hljs-number">1</span>+<span class="hljs-number">0i</span>
</code></pre>
<p>Generally speaking, <strong>all allowable operations on quantum state vectors are presented by unitary matrices</strong>. Unitary matrices are a set of linear mappings which transform quantum state vectors to other quantum state vectors.</p>
<p>Here is the action of the Hadamard operation on a few qubit state vectors [1].</p>
<p>$$H |0&gt; = |+&gt;\hspace{0.5cm} H |1&gt; = |-&gt;\hspace{0.5cm} H |+&gt; = |0&gt;\hspace{0.5cm} H |-&gt; = |1&gt;$$</p><pre><code class="lang-r"><span class="hljs-comment"># H|0&gt;</span>
&gt; H(<span class="hljs-number">1</span>)*qstate(<span class="hljs-number">1</span>)
   ( <span class="hljs-number">0.7071068</span> )    * |<span class="hljs-number">0</span>&gt; 
 + ( <span class="hljs-number">0.7071068</span> )    * |<span class="hljs-number">1</span>&gt;
<span class="hljs-comment"># H|1&gt;</span>
&gt; H(<span class="hljs-number">1</span>)*(X(<span class="hljs-number">1</span>)*qstate(<span class="hljs-number">1</span>))
   ( <span class="hljs-number">0.7071068</span> )    * |<span class="hljs-number">0</span>&gt; 
 + ( -<span class="hljs-number">0.7071068</span> )    * |<span class="hljs-number">1</span>&gt; 
<span class="hljs-comment"># H|+&gt; = HH|0&gt; = |0&gt;</span>
&gt; H(<span class="hljs-number">1</span>)*(H(<span class="hljs-number">1</span>)*qstate(<span class="hljs-number">1</span>))
   ( <span class="hljs-number">1</span> )    * |<span class="hljs-number">0</span>&gt; 
<span class="hljs-comment"># H|-&gt; = HH|1&gt; = |1&gt;</span>
</code></pre>
<h3 id="heading-h-gate-representation">H gate representation</h3>
<p>In the quantum circuit model, wires represent qubits and gates represent operations acting on these qubits. We will discuss quantum circuits in this series but for now, let us present the <code>plot</code> function</p>
<pre><code class="lang-r">&gt; x &lt;- qstate(<span class="hljs-number">1</span>)
&gt; x &lt;- H(<span class="hljs-number">1</span>)*x <span class="hljs-comment"># H.|0&gt;</span>
&gt; qsimulatR::plot(x, qubitnames=<span class="hljs-string">"|0&gt;"</span>)
</code></pre>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1690286702093/25722875-d55a-427f-891d-96514efb51ff.png" alt class="image--center mx-auto" /></p>
<h3 id="heading-geometric-interpretation-of-h">Geometric interpretation of H</h3>
<p>In terms of the Bloch sphere, the Hadamard gate interchanges the x- and z-axis, and inverts the y-axis [5].</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1690354052504/ad6ca044-dde0-45b6-b173-d647885d7d38.png" alt class="image--center mx-auto" /></p>
<p>The GIF images included in this article come from the <a target="_blank" href="https://lewisla.gitbook.io/learning-quantum/quantum-circuits/single-qubit-gates">Learning Quantum</a> website [6].</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1690221887693/39725306-eb04-4224-b5dc-cf0a6c1cc548.gif" alt class="image--center mx-auto" /></p>
<p>Let us remind ourselves of the result of applying H(1) to <strong>|0&gt;</strong></p>
<pre><code class="lang-r">&gt; H(<span class="hljs-number">1</span>)*qstate(<span class="hljs-number">1</span>)
   ( <span class="hljs-number">0.7071068</span> )    * |<span class="hljs-number">0</span>&gt; 
 + ( -<span class="hljs-number">0.7071068</span> )    * |<span class="hljs-number">1</span>&gt;
</code></pre>
<p>The result of this uniform superposition of <strong>|0&gt;</strong> with the H gate can be labeled as "<strong>|+&gt;</strong>" (the <em>plus state</em>) quantum state vector.</p>
<p>We say that the <code>H(1)</code> operation is used to exchange between the z-axis basis ({<strong>|0&gt;</strong>, <strong>|1&gt;</strong>}) and the x-axis basis {<strong>|+&gt;</strong>, <strong>|-&gt;</strong>} in the Bloch sphere.</p>
<h2 id="heading-pauli-x-gate-bit-flip">Pauli X gate - Bit Flip</h2>
<p>Concerning the computational basis, the <strong>X gate</strong> is equivalent to a classical NOT operation, or logical negation.</p>
<p>The computation basis states are interchanged, so that |0⟩ becomes |1⟩ and |1⟩ becomes |0⟩.</p>
<p>Note: the X-gate is not a true quantum NOT gate, since it only logically negates the state in the computational basis.</p>
<p>Let us remind ourselves how we obtained <strong>|1&gt;</strong> by performing a <strong>bit flip on</strong> <strong>|0&gt;</strong></p>
<pre><code class="lang-r"><span class="hljs-comment"># X.|0&gt;</span>
&gt; X(<span class="hljs-number">1</span>)*qstate(<span class="hljs-number">1</span>)
 ( <span class="hljs-number">1</span> )    * |<span class="hljs-number">1</span>&gt; 

<span class="hljs-comment"># X is also reversible: we come back to |0&gt;</span>
&gt; X(<span class="hljs-number">1</span>)*(X(<span class="hljs-number">1</span>)*qstate(<span class="hljs-number">1</span>))
 ( <span class="hljs-number">1</span> )    * |<span class="hljs-number">0</span>&gt;

<span class="hljs-comment"># Let us look at the structure of X(1)</span>
&gt; str(X(<span class="hljs-number">1</span>))
Formal class <span class="hljs-string">'sqgate'</span> [package <span class="hljs-string">"qsimulatR"</span>] with <span class="hljs-number">3</span> slots
  ..@ bit : int <span class="hljs-number">1</span>
  ..@ M   : cplx [<span class="hljs-number">1</span>:<span class="hljs-number">2</span>, <span class="hljs-number">1</span>:<span class="hljs-number">2</span>] <span class="hljs-number">0</span>+<span class="hljs-number">0i</span> <span class="hljs-number">1</span>+<span class="hljs-number">0i</span> <span class="hljs-number">1</span>+<span class="hljs-number">0i</span> <span class="hljs-keyword">...</span>
  ..@ type: chr <span class="hljs-string">"X"</span>

<span class="hljs-comment"># We notice the Pauli matrix in the M slot</span>
&gt; X(<span class="hljs-number">1</span>)@M
     [,<span class="hljs-number">1</span>] [,<span class="hljs-number">2</span>]
[<span class="hljs-number">1</span>,] <span class="hljs-number">0</span>+<span class="hljs-number">0i</span> <span class="hljs-number">1</span>+<span class="hljs-number">0i</span>
[<span class="hljs-number">2</span>,] <span class="hljs-number">1</span>+<span class="hljs-number">0i</span> <span class="hljs-number">0</span>+<span class="hljs-number">0i</span>

<span class="hljs-comment"># The matrix above is a unitary matrix</span>
&gt; X(<span class="hljs-number">1</span>)@M %*% Conj(t(X(<span class="hljs-number">1</span>)@M))
     [,<span class="hljs-number">1</span>] [,<span class="hljs-number">2</span>]
[<span class="hljs-number">1</span>,] <span class="hljs-number">1</span>+<span class="hljs-number">0i</span> <span class="hljs-number">0</span>+<span class="hljs-number">0i</span>
[<span class="hljs-number">2</span>,] <span class="hljs-number">0</span>+<span class="hljs-number">0i</span> <span class="hljs-number">1</span>+<span class="hljs-number">0i</span>
</code></pre>
<h3 id="heading-x-gate-representation">X gate representation</h3>
<p>In the quantum circuit model, wires represent qubits and gates represent operations acting on these qubits. We will discuss quantum circuits in this series but for now, let us present the <code>plot</code> function</p>
<pre><code class="lang-r">&gt; qsimulatR::plot(X(<span class="hljs-number">1</span>)*qstate(<span class="hljs-number">1</span>), qubitnames=<span class="hljs-string">"|0&gt;"</span>)
</code></pre>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1690355665303/385989dc-0ef8-486a-8a87-8d9773e19c8b.png" alt class="image--center mx-auto" /></p>
<h3 id="heading-geometric-interpretation-of-x">Geometric interpretation of X</h3>
<p>The X gate generates a half-turn in the Bloch sphere about the x-axis [5]. We say that for the X gate only the <strong>angle</strong> (<strong>θ,</strong> theta) matters, the <strong>phase</strong> (<strong>φ</strong>, phi) is <em>arbitrary</em>.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1690354485494/30384728-19ac-4b57-a88e-7383c608d5fb.png" alt class="image--center mx-auto" /></p>
<p>The GIF images included in this article come from the <a target="_blank" href="https://lewisla.gitbook.io/learning-quantum/quantum-circuits/single-qubit-gates">Learning Quantum</a> website [6].</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1690356746358/9947a944-4b1b-447f-9387-03533c88dbae.gif" alt class="image--center mx-auto" /></p>
<h2 id="heading-pauli-z-gate-phase-flip">Pauli Z gate - Phase Flip</h2>
<p>Let us perform a <strong>phase flip on the |0&gt;</strong> <strong>and</strong> <strong>the |1&gt;</strong> <strong>states respectively.</strong></p>
<pre><code class="lang-r"><span class="hljs-comment"># Z.|0&gt;</span>
&gt; Z(<span class="hljs-number">1</span>)*qstate(<span class="hljs-number">1</span>)
 ( <span class="hljs-number">1</span> )    * |<span class="hljs-number">0</span>&gt; 

<span class="hljs-comment"># Z.|1&gt;</span>
&gt; Z(<span class="hljs-number">1</span>)*(X(<span class="hljs-number">1</span>)*qstate(<span class="hljs-number">1</span>))
 ( -<span class="hljs-number">1</span> )    * |<span class="hljs-number">1</span>&gt;

<span class="hljs-comment"># Let us look at the structure of Z(1)</span>
&gt; str(Z(<span class="hljs-number">1</span>))
Formal class <span class="hljs-string">'sqgate'</span> [package <span class="hljs-string">"qsimulatR"</span>] with <span class="hljs-number">3</span> slots
  ..@ bit : int <span class="hljs-number">1</span>
  ..@ M   : cplx [<span class="hljs-number">1</span>:<span class="hljs-number">2</span>, <span class="hljs-number">1</span>:<span class="hljs-number">2</span>] <span class="hljs-number">1</span>+<span class="hljs-number">0i</span> <span class="hljs-number">0</span>+<span class="hljs-number">0i</span> <span class="hljs-number">0</span>+<span class="hljs-number">0i</span> <span class="hljs-keyword">...</span>
  ..@ type: chr <span class="hljs-string">"Z"</span>

<span class="hljs-comment"># We notice the Pauli matrix in the M slot</span>
&gt; Z(<span class="hljs-number">1</span>)@M
     [,<span class="hljs-number">1</span>]  [,<span class="hljs-number">2</span>]
[<span class="hljs-number">1</span>,] <span class="hljs-number">1</span>+<span class="hljs-number">0i</span>  <span class="hljs-number">0</span>+<span class="hljs-number">0i</span>
[<span class="hljs-number">2</span>,] <span class="hljs-number">0</span>+<span class="hljs-number">0i</span> -<span class="hljs-number">1</span>+<span class="hljs-number">0i</span>

<span class="hljs-comment"># The matrix above is a unitary matrix</span>
&gt; Z(<span class="hljs-number">1</span>)@M %*% Conj(t(Z(<span class="hljs-number">1</span>)@M))
     [,<span class="hljs-number">1</span>] [,<span class="hljs-number">2</span>]
[<span class="hljs-number">1</span>,] <span class="hljs-number">1</span>+<span class="hljs-number">0i</span> <span class="hljs-number">0</span>+<span class="hljs-number">0i</span>
[<span class="hljs-number">2</span>,] <span class="hljs-number">0</span>+<span class="hljs-number">0i</span> <span class="hljs-number">1</span>+<span class="hljs-number">0i</span>
</code></pre>
<p>Thus, applying the Z gate to an <em>initialized</em> qubit (<strong>|0&gt;</strong>) will leave the qubits computational state unchanged when measured. For the <strong>|1&gt;</strong> state, notice that the <em>complex amplitude</em> of the resulting state is displayed as "( -1 )".</p>
<p>Remember, that |0&gt; and |1&gt; are orthogonal in the Hilbert space. Thus, concerning the computational basis, the Z gate flips the phase of the |1⟩ state relative to the |0⟩ state.</p>
<h3 id="heading-z-gate-representation">Z gate representation</h3>
<p>In the quantum circuit model, wires represent qubits and gates represent operations acting on these qubits. We will discuss quantum circuits in this series but for now, let us present the <code>plot</code> function</p>
<h3 id="heading-geometric-interpretation-of-z">Geometric interpretation of Z</h3>
<p>The Pauli <strong>Z gate</strong> generates a half-turn in the Bloch sphere about the z-axis.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1690354518469/91455194-1563-4514-8d48-caa85e8c041f.png" alt class="image--center mx-auto" /></p>
<p>The GIF images included in this article come from the <a target="_blank" href="https://lewisla.gitbook.io/learning-quantum/quantum-circuits/single-qubit-gates">Learning Quantum</a> website [6].</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1690356832641/b9a8b40a-d6d2-4d8e-93eb-ff3c52e4376f.gif" alt class="image--center mx-auto" /></p>
<p>Specifically, it maps 1 to -1 and leaves 0 unchanged. It does this by rotating around the Z axis of the qubit by π radians (180 degrees). By doing this it flips the phase of the qubit.</p>
<h2 id="heading-pauli-y-gate-bit-andamp-phase-flip">Pauli Y gate - Bit &amp; Phase Flip</h2>
<p>Let us perform a <strong>bit and phase flip on the |0&gt;</strong> <strong>state and</strong> <strong>the |1&gt;</strong> <strong>state respectively</strong>.</p>
<pre><code class="lang-r"><span class="hljs-comment"># Y.|0&gt;</span>
&gt; Y(<span class="hljs-number">1</span>)*qstate(<span class="hljs-number">1</span>)
 ( <span class="hljs-number">0</span>-<span class="hljs-number">1i</span> )    * |<span class="hljs-number">1</span>&gt; 

<span class="hljs-comment"># Y.|1&gt;</span>
&gt; Y(<span class="hljs-number">1</span>)*(X(<span class="hljs-number">1</span>)*qstate(<span class="hljs-number">1</span>))
 ( <span class="hljs-number">0</span>+<span class="hljs-number">1i</span> )    * |<span class="hljs-number">0</span>&gt;

<span class="hljs-comment"># Let us look at the structure of Y(1)</span>
&gt; str(Y(<span class="hljs-number">1</span>))
Formal class <span class="hljs-string">'sqgate'</span> [package <span class="hljs-string">"qsimulatR"</span>] with <span class="hljs-number">3</span> slots
  ..@ bit : int <span class="hljs-number">1</span>
  ..@ M   : cplx [<span class="hljs-number">1</span>:<span class="hljs-number">2</span>, <span class="hljs-number">1</span>:<span class="hljs-number">2</span>] <span class="hljs-number">0</span>+<span class="hljs-number">0i</span> <span class="hljs-number">0</span>-<span class="hljs-number">1i</span> <span class="hljs-number">0</span>+<span class="hljs-number">1i</span> <span class="hljs-keyword">...</span>
  ..@ type: chr <span class="hljs-string">"Y"</span>

<span class="hljs-comment"># We notice the Pauli matrix in the M slot</span>
&gt; Y(<span class="hljs-number">1</span>)@M
     [,<span class="hljs-number">1</span>] [,<span class="hljs-number">2</span>]
[<span class="hljs-number">1</span>,] <span class="hljs-number">0</span>+<span class="hljs-number">0i</span> <span class="hljs-number">0</span>+<span class="hljs-number">1i</span>
[<span class="hljs-number">2</span>,] <span class="hljs-number">0</span>-<span class="hljs-number">1i</span> <span class="hljs-number">0</span>+<span class="hljs-number">0i</span>

<span class="hljs-comment"># The matrix above is a unitary matrix</span>
&gt; Y(<span class="hljs-number">1</span>)@M %*% Conj(t(Y(<span class="hljs-number">1</span>)@M))
     [,<span class="hljs-number">1</span>] [,<span class="hljs-number">2</span>]
[<span class="hljs-number">1</span>,] <span class="hljs-number">1</span>+<span class="hljs-number">0i</span> <span class="hljs-number">0</span>+<span class="hljs-number">0i</span>
[<span class="hljs-number">2</span>,] <span class="hljs-number">0</span>+<span class="hljs-number">0i</span> <span class="hljs-number">1</span>+<span class="hljs-number">0i</span>
</code></pre>
<p>Concerning the computational basis, we interchange the |0&gt; and |1&gt; states and apply a relative phase flip.</p>
<p>The Y gate can be thought of as a combination of X and Z gates, <strong>Y = −iZX</strong>.</p>
<h3 id="heading-y-gate-representation">Y gate representation</h3>
<p>In the quantum circuit model, wires represent qubits and gates represent operations acting on these qubits. We will discuss quantum circuits in this series but for now, let us present the <code>plot</code> function</p>
<h3 id="heading-geometric-interpretation-of-y">Geometric interpretation of Y</h3>
<p>The Pauli <strong>Y gate</strong> generates a half-turn in the Bloch sphere about the y-axis [5].</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1690354557983/3f06651e-49bc-4418-b75c-80f86031863a.png" alt class="image--center mx-auto" /></p>
<p>The GIF images included in this article come from the <a target="_blank" href="https://lewisla.gitbook.io/learning-quantum/quantum-circuits/single-qubit-gates">Learning Quantum</a> website [6].</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1690356856147/435f208c-5976-405c-b298-8f1a882e779b.gif" alt class="image--center mx-auto" /></p>
<h2 id="heading-s-gate-phase-gate-z90">S gate - Phase gate Z90</h2>
<p>Historically called the phase gate (and denoted by "P"), since it shifts the phase of the one state relative to the zero state [5].</p>
<p>Let us apply the S gate to the <strong>|0&gt;</strong> and <strong>|1&gt;</strong> states respectively.</p>
<pre><code class="lang-r"><span class="hljs-comment"># S.|0&gt;</span>
&gt; S(<span class="hljs-number">1</span>)*qstate(<span class="hljs-number">1</span>)
 ( <span class="hljs-number">1</span> )    * |<span class="hljs-number">0</span>&gt;

<span class="hljs-comment"># S.|1&gt;</span>
&gt; S(<span class="hljs-number">1</span>)*(X(<span class="hljs-number">1</span>)*qstate(<span class="hljs-number">1</span>))
 ( <span class="hljs-number">0</span>+<span class="hljs-number">1i</span> )    * |<span class="hljs-number">1</span>&gt;

<span class="hljs-comment"># Let us look at the structure of S(1)</span>
&gt; str(S(<span class="hljs-number">1</span>))
Formal class <span class="hljs-string">'sqgate'</span> [package <span class="hljs-string">"qsimulatR"</span>] with <span class="hljs-number">3</span> slots
  ..@ bit : int <span class="hljs-number">1</span>
  ..@ M   : cplx [<span class="hljs-number">1</span>:<span class="hljs-number">2</span>, <span class="hljs-number">1</span>:<span class="hljs-number">2</span>] <span class="hljs-number">1</span>+<span class="hljs-number">0i</span> <span class="hljs-number">0</span>+<span class="hljs-number">0i</span> <span class="hljs-number">0</span>+<span class="hljs-number">0i</span> <span class="hljs-keyword">...</span>
  ..@ type: chr <span class="hljs-string">"S"</span>

<span class="hljs-comment"># We notice the Pauli matrix in the M slot</span>
&gt; S(<span class="hljs-number">1</span>)@M
     [,<span class="hljs-number">1</span>] [,<span class="hljs-number">2</span>]
[<span class="hljs-number">1</span>,] <span class="hljs-number">1</span>+<span class="hljs-number">0i</span> <span class="hljs-number">0</span>+<span class="hljs-number">0i</span>
[<span class="hljs-number">2</span>,] <span class="hljs-number">0</span>+<span class="hljs-number">0i</span> <span class="hljs-number">0</span>+<span class="hljs-number">1i</span>


<span class="hljs-comment"># The matrix above is a unitary matrix</span>
&gt; S(<span class="hljs-number">1</span>)@M %*% Conj(t(S(<span class="hljs-number">1</span>)@M))
     [,<span class="hljs-number">1</span>] [,<span class="hljs-number">2</span>]
[<span class="hljs-number">1</span>,] <span class="hljs-number">1</span>+<span class="hljs-number">0i</span> <span class="hljs-number">0</span>+<span class="hljs-number">0i</span>
[<span class="hljs-number">2</span>,] <span class="hljs-number">0</span>+<span class="hljs-number">0i</span> <span class="hljs-number">1</span>+<span class="hljs-number">0i</span>
</code></pre>
<p>Note: <strong>S.H</strong> is applied to change from Z to Y basis</p>
<h3 id="heading-s-gate-representation">S gate representation</h3>
<p>In the quantum circuit model, wires represent qubits and gates represent operations acting on these qubits. We will discuss quantum circuits in this series but for now, let us present the <code>plot</code> function</p>
<pre><code class="lang-r">&gt; qsimulatR::plot(S(<span class="hljs-number">1</span>)*qstate(<span class="hljs-number">1</span>), qubitnames=<span class="hljs-string">"|0&gt;"</span>)
</code></pre>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1690356373038/f615c100-9eb1-4c46-9095-b94e40d306de.png" alt class="image--center mx-auto" /></p>
<h3 id="heading-geometric-interpretation-of-s">Geometric interpretation of S</h3>
<p>The S gate adds 90° rotation to the phase. It performs a half of the Z gate e.g., it is the square root of the Z-gate, <strong>SS = Z</strong> [5].</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1690354574924/b55ae67d-2234-4796-8e5c-c272a157d8ff.png" alt class="image--center mx-auto" /></p>
<p>The GIF images included in this article come from the <a target="_blank" href="https://lewisla.gitbook.io/learning-quantum/quantum-circuits/single-qubit-gates">Learning Quantum</a> website [6].</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1690356962600/a2a34572-f00e-4d38-85db-e663f10a6e3a.gif" alt class="image--center mx-auto" /></p>
<h2 id="heading-t-gate-phase-gate">T gate - Phase gate</h2>
<p>It performs a <strong>quarter of the Z gate and a half of the S gate</strong>.</p>
<p>Let us apply the S gate to the <strong>|0&gt;</strong> and <strong>|1&gt;</strong> states respectively.</p>
<pre><code class="lang-r"><span class="hljs-comment">#</span>
</code></pre>
<h3 id="heading-t-gate-representation">T gate representation</h3>
<p>In the quantum circuit model, wires represent qubits and gates represent operations acting on these qubits. We will discuss quantum circuits in this series but for now, let us present the <code>plot</code> function</p>
<h3 id="heading-geometric-interpretation-of-t">Geometric interpretation of T</h3>
<p>An eight-turn anti-clockwise about the z-axis [5].</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1690354587643/af049023-1be2-478b-9274-93f6877a01e5.png" alt class="image--center mx-auto" /></p>
<p>The GIF images included in this article come from the <a target="_blank" href="https://lewisla.gitbook.io/learning-quantum/quantum-circuits/single-qubit-gates">Learning Quantum</a> website [6].</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1690356899348/3fef46fc-2b43-4610-8b01-d7472131e1b4.gif" alt class="image--center mx-auto" /></p>
<h2 id="heading-conclusion">Conclusion</h2>
<p>xx</p>
<h2 id="heading-references">References</h2>
<p>[1] Ostmeyer J., Urbach C., qsimulatR: A Quantum Computer Simulator, <a target="_blank" href="https://cran.r-project.org/web/packages/qsimulatR/">https://cran.r-project.org/web/packages/qsimulatR/</a></p>
<p>[2] qsimulatR package vignette, <a target="_blank" href="https://cran.r-project.org/web/packages/qsimulatR/vignettes/qsimulatR.html">https://cran.r-project.org/web/packages/qsimulatR/vignettes/qsimulatR.html</a></p>
<p>[3] Qiskit, Basics of Quantum Information, <a target="_blank" href="https://learn.qiskit.org/course/basics/single-systems">https://learn.qiskit.org/course/basics/single-systems</a></p>
<p>[4] Qiskit, Summer School 2021, <a target="_blank" href="https://learn.qiskit.org/summer-school/2021/lec1-1-vector-spaces-tensor-products-qubits">https://learn.qiskit.org/summer-school/2021/lec1-1-vector-spaces-tensor-products-qubits</a></p>
<p>[5] Crooks G. E., "Gates, States, and Circuits", Tech. Note 014v0.9.0 beta, 2023-03-01</p>
<p>[6] Learning Quantum, Single Qubit Gates, <a target="_blank" href="https://lewisla.gitbook.io/learning-quantum/quantum-circuits/single-qubit-gates">https://lewisla.gitbook.io/learning-quantum/quantum-circuits/single-qubit-gates</a></p>
</p>]]></content:encoded></item><item><title><![CDATA[Part 3 - Yet Another PSO Article with R]]></title><description><![CDATA[Cover photo by Eric Ward on Unsplash
Go to R-bloggers for R news and tutorials contributed by hundreds of R bloggers.
Introduction
This article is the Part 3 of the series (Swarm) Portfolio Optimizati]]></description><link>https://blog.bguarisma.com/part-3-yet-another-pso-article-with-r</link><guid isPermaLink="true">https://blog.bguarisma.com/part-3-yet-another-pso-article-with-r</guid><category><![CDATA[optimization]]></category><category><![CDATA[portfolio]]></category><category><![CDATA[R Language]]></category><dc:creator><![CDATA[Boris Guarisma]]></dc:creator><pubDate>Mon, 16 May 2022 14:01:35 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1652706843061/yaAnGmzud.jpg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Cover photo by <a href="https://unsplash.com/@ericjamesward?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText">Eric Ward</a> on <a href="https://unsplash.com/">Unsplash</a></p>
<p>Go to <a href="https://www.r-bloggers.com/">R-bloggers</a> for R news and tutorials contributed by hundreds of R bloggers.</p>
<h2>Introduction</h2>
<p>This article is the <strong>Part 3</strong> of the series <a href="https://blog.bguarisma.com/series/portfolio-optim">(Swarm) Portfolio Optimization</a>. I assume you have read <a href="https://blog.bguarisma.com/notion-of-non-dominated-solutions">Part 2 - Notion of non-dominated solutions</a>.</p>
<p>Here, I am going to take a step back and resolve a <strong>single-objective optimization</strong> with a Particle Swarm Optimization (PSO) algorithm before moving forward with multi-objective optimization introduced in <a href="https://blog.bguarisma.com/notion-of-non-dominated-solutions">Part 2 - Notion of non-dominated solutions</a>.</p>
<p><strong>The proposed code is not an optimized one</strong>. I wanted to differentiate this article from other R-related ones by implementing the PSO algorithm with tibbles: saving all the population information at each iteration  in this format can be useful if you are willing to e.g., plot dynamic movement of the swarm, and perform further analyses.</p>
<h2>The equations of motion</h2>
<p>This chapter is based on <strong>Maurice Leclerc</strong>'s book "Particle Swarm Optimization" [1]. There are tons of articles and bibliography on PSO and its variants. I will stick to a simple version for PSO, <em>without informants</em>, for the sake of clarity based on the algorithm described in the next chapter.</p>
<p>We are interested only in continuous problems with real variables. A search space is defined, for example, classically, like one (hyper)cube of the form [XMIN, XMAX]^D.</p>
<p>Initialization simply consists of initially randomly placing the particles according to a <strong>uniform distribution</strong> in this search space.</p>
<p>The particles have velocities. By definition, a **velocity ** is a vector or, more precisely, an operator, which, applied to a position, will give another position. It is in fact a <em>displacement</em>, called velocity because the time increment of the iterations is always implicitly regarded as equal to 1.</p>
<p>For the moment, let us be satisfied with deriving at random the values of the components of each velocity, according to a uniform distribution in:</p>
<img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1652697150571/eKA4acpEP.png" alt="image.png" />

<p>The dimension of the search space is D. Therefore, the <strong>current position or location</strong> of a particle in this space at the moment t is given by a <strong>vector x(t)</strong>, with D components. </p>
<p>Its current velocity is v(t). The <strong>best position or location</strong> found up to now by this particle is given by a <strong>vector p(t)</strong>. </p>
<p>Lastly, the <strong>best position or location</strong> found by informants of the particle is indicated by a <strong>vector g(t)</strong>. </p>
<p>In general, we will write simply <strong>x</strong>, <strong>v</strong>, <strong>p</strong>, and <strong>g</strong>. </p>
<p>The <em>dth</em> component of one of these vectors is indicated by the index <em>d</em>, for example <em>xd</em>. </p>
<p>With these notations, the <strong>equations of motion of a particle</strong> are, for each dimension <em>d</em>:</p>
<img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1652696189890/F6Wof-OA3.png" alt="image.png" />

<p>*c1 * is constant (<strong>confidence in its own movement</strong>) and it will be represented by the <code>VELOCITY_FACTOR</code> constant.</p>
<p>To use this model, the two parameters <em>c1</em> and <em>cmax <em>(represented by the <code>CMAX</code> constant) must be defined. <code>CMAX</code> can be regarded as the ** maximum confidence granted by the particle to any performance transmitted by another</em></em>. For each problem, “the right” values can be found only by experiment: both VELOCITY_FACTOR and CMAX must not be chosen independently and the following couple of values are recommended </p>
<ul>
<li><p>(<code>VELOCITY_FACTOR</code> = <strong>0.7</strong>, <code>CMAX</code>  = <strong>1.47</strong>)</p>
</li>
<li><p>(<code>VELOCITY_FACTOR</code> =** 0.8**, <code>CMAX</code>  = <strong>1.62</strong>)</p>
</li>
</ul>
<p>The first product <em>cmax . alea(0,1)</em> in the first equation of motion will be denoted as <code>C1</code> (confidence granted by the particle to its self-performance), and second one as <code>C2</code> (confidence granted by the particle to the best particle performance). Note: <code>C2</code> is supposed to be confidence granted by the particle to its best *informant *performance, but as stated earlier in this article we do not consider Leclerc's implementation with informants.</p>
<p>In practice, it is not desirable that too many particles tend to leave the search space as early as the first increment. We supplement the mechanism of **confinement **(which brings back the particle to the border of the search space) with a <strong>velocity modification</strong>. In this article we choose to perform a velocity component cancellation  thus, the complete mechanism is described by the following operations:</p>
<img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1652697489050/Kgt3-C2Kb.png" alt="image.png" />

<h2>The PSO algorithm</h2>
<p>This chapter is based on a book <em>Meta-heuristic and Evolutionary Algorithms for Engineering Optimization</em> [2]</p>
<p>Generally speaking, the objective of the PSO algorithm is to <strong>minimize a function f(x)</strong> where x denotes a <strong>particle</strong>.  </p>
<ul>
<li><p>We start with an <strong>initial population</strong>. </p>
</li>
<li><p>For any particle, a position x is said to be better than a position y if **f(x)&lt;f(y) **.</p>
</li>
<li><p>Now the population is updated by the movement of particles around the search space. </p>
</li>
<li><p>Each particle i remembers the <strong>best position Pi</strong> attained by it in the past. </p>
</li>
<li><p>Also the swarm remembers the <strong>global best value G</strong> attained by any particle in the past. </p>
</li>
<li><p>Each particle's movement is determined by its <strong>current velocity</strong>, its <strong>own best known position</strong> and the <strong>global best value</strong> of the swarm. </p>
</li>
<li><p>The swarm is expected to move towards the global best solution or at least towards a satisfactory solution for which the function value is close to the global minimum.</p>
</li>
</ul>
<p>The PSO algorithm implemented in this article is the following:</p>
<img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1652606288342/_NRLDN68A.png" alt="image.png" />

<h2>Single-objective optimization problem</h2>
<p>We will use the same 4-asset portfolio (MSFT, AAPL, GOOG, NFLX) as in <a href="https://blog.bguarisma.com/notion-of-non-dominated-solutions">Part 2</a>.</p>
<p>The goal is to find the optimal solution (or portfolio weights) for the single-objective optimization problem below. I have strikethrough the second objective function from <a href="https://blog.bguarisma.com/notion-of-non-dominated-solutions">Part 2</a>, leaving only the minimization of the first objective function: the <strong>Value-at-Risk (VaR), relative to the mean</strong>, of the portfolio.</p>
<img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1652704810559/0OHqqyfir.png" alt="image.png" />

<h2>Packages</h2>
<p>Load the same packages as in <a href="https://blog.bguarisma.com/notion-of-non-dominated-solutions">Part 2</a></p>
<pre><code>library(PerformanceAnalytics)
library(quantmod)
library(tidyquant)
library(tidyverse)
library(timetk)
library(skimr) # not used in this article
library(plotly) # not used in this article
library(caret) # not used in this article
library(tictoc)
</code></pre>
<h2>Parameters</h2>
<p>Algorithm steps 1. (population size as <code>NUMBER_PARTICLES</code>) and 5. (w as <code>VELOCITY_FACTOR</code>) will be defined along with the other PSO parameters.</p>
<pre><code># PSO parameters
NUMBER_OF_ASSETS = 4   # D = 4 dimensions
NUMBER_PARTICLES = 40  # N = population size
VELOCITY_FACTOR = .7   # confidence in its own movement
XMIN = .05  # used for mechanism of confinement, not as constraint
XMAX = .80 # used for mechanism of confinement, not as constraint
CMAX = 1.47 # maximum confidence granted by the particle to any performance transmitted by another
VMAX = (XMAX-XMIN)/2
VMIN = (XMIN-XMAX)/2
MAX_ITERATIONS = 50
</code></pre>
<h2>Data - Daily Returns</h2>
<p>As explained in the code snippet below, if you have already saved as <code>returns_daily_tbl.rds</code> the 4 assets daily returns generated in <a href="https://blog.bguarisma.com/notion-of-non-dominated-solutions">Part 2</a> chapter "<em>Collect prices, compute daily returns and clean data</em>", you can load it directly and apply the <code>formatTibble()</code> function to generate the <code>returns</code> tibble that will be used in this article.</p>
<p>If you haven't saved the daily returns then run the code snippet in Part 2's chapter "<em>Collect prices, compute daily returns and clean data</em>" and don't forget to apply the <code>formatTibble()</code> function.</p>
<pre><code>## Assets &amp; Daily returns data ----

# Microsoft, Apple, Google, Netflix
assets &lt;- c("MSFT", "AAPL", "GOOG", "NFLX")

returns_daily_tbl &lt;- readRDS("&lt;RDS file path&gt;/returns_daily_tbl.rds")

returns &lt;- returns_daily_tbl %&gt;%
  formatTibble()

rm(returns_daily_tbl)
</code></pre>
<h2>Initialization</h2>
<h3>Population tibble definition</h3>
<p>At each iteration the program will generate a tibble with the following format: each line is a particle representing a portfolio of the 4 assets. Since there are 4 assets there will be 4 components for the location, velocity and best location of each particle.</p>
<ul>
<li><p>Columns <code>x_1, x_2, x_3, x_4</code> are the <strong>location or position components</strong> of the particle.</p>
</li>
<li><p>Column <code>pRelVaR </code> is the <strong>"relative to the mean" VaR</strong> (see Part 1) calculated with the previous locations (see function <code>computeRelVaR()</code> below).</p>
</li>
<li><p>Columns <code>v_1, v_2, v_3, v_4</code> are the <strong>velocity components</strong> of the particle.</p>
</li>
<li><p>Columns <code>p_1, p_2, p_3, p_4</code> are the <strong>best location components</strong> of the particle.</p>
</li>
</ul>
<h3>The Initial Population tibble</h3>
<h4>The initializePopulation function</h4>
<p>The <code>initializePopulation()</code> function applies algorithm steps 2., 3., 4., 6., 7., and <strong>it will be run once</strong>. </p>
<p>You can save the resulting tibble (with <code>saveRDS</code> for instance) in order to rerun the program several times using the same initial population.</p>
<p>The <code>initializePopulation()</code> function calls the following three functions:</p>
<ol>
<li><p><code>generateRandomLocations()</code>: for steps 2. and 3.</p>
</li>
<li><p><code>generateRandomVelocities()</code>: for step 4.</p>
</li>
<li><p><code>computeRelVaR()</code>: for step 7., thus it needs the daily returns tibble.</p>
</li>
</ol>
<p>Step 6. does not need a specific function, all best locations<code>p_</code> will be initialized with the initial locations <code>x_</code> values.</p>
<p>Here's the <strong>reference initial population tibble</strong> used in this article and for other optimization scenarios e.g., changing parameter couple (<code>VELOCITY_FACTOR, CMAX</code>) and/or population size <code>NUMBER_PARTICLES</code>.</p>
<pre><code>init_pop &lt;- initializePopulation(returns = returns)

&gt; init_pop
# A tibble: 40 x 13
      x_1    x_2   x_3   x_4 pRelVaR     v_1     v_2     v_3     v_4    p_1    p_2   p_3   p_4
    &lt;dbl&gt;  &lt;dbl&gt; &lt;dbl&gt; &lt;dbl&gt;   &lt;dbl&gt;   &lt;dbl&gt;   &lt;dbl&gt;   &lt;dbl&gt;   &lt;dbl&gt;  &lt;dbl&gt;  &lt;dbl&gt; &lt;dbl&gt; &lt;dbl&gt;
 1 0.137  0.0829 0.392 0.389    1.15 -0.266  -0.0821  0.210   0.245  0.137  0.0829 0.392 0.389
 2 0.604  0.106  0.144 0.146    1.10 -0.227  -0.360  -0.350  -0.0668 0.604  0.106  0.144 0.146
 3 0.341  0.350  0.176 0.133    1.10  0.257   0.0866  0.122  -0.0764 0.341  0.350  0.176 0.133
 4 0.0442 0.449  0.361 0.147    1.13 -0.260  -0.267   0.191  -0.342  0.0442 0.449  0.361 0.147
 5 0.144  0.103  0.399 0.355    1.14 -0.0740 -0.224   0.0213  0.155  0.144  0.103  0.399 0.355
 6 0.409  0.0956 0.296 0.200    1.10  0.0740  0.310  -0.293  -0.255  0.409  0.0956 0.296 0.200
 7 0.0575 0.316  0.198 0.429    1.14 -0.0175 -0.324  -0.106  -0.189  0.0575 0.316  0.198 0.429
 8 0.0716 0.363  0.111 0.454    1.14 -0.301  -0.0417 -0.0640 -0.156  0.0716 0.363  0.111 0.454
 9 0.0367 0.358  0.425 0.180    1.10 -0.128  -0.129   0.104  -0.293  0.0367 0.358  0.425 0.180
10 0.109  0.164  0.246 0.481    1.18  0.271  -0.0885 -0.160   0.121  0.109  0.164  0.246 0.481
# ... with 30 more rows
</code></pre>
<pre><code>initializePopulation &lt;- function(returns){
  
  ## Randomly generate locations of particles ----
  init_locations &lt;- lapply(X = 1:NUMBER_PARTICLES, function(x){
    generateRandomLocations(n = NUMBER_OF_ASSETS, min = XMIN, max = XMAX)
  })
  init_locations &lt;- init_locations %&gt;%
    bind_rows()
  
  ## Randomly generate velocities of particles ----
  init_velocities &lt;- lapply(X = 1:NUMBER_PARTICLES, function(x){
    generateRandomVelocities(n = NUMBER_OF_ASSETS, min = VMIN, max = VMAX)
  })
  init_velocities &lt;- init_velocities %&gt;%
    bind_rows()
  
  ## Initialize best locations as first locations ----
  init_bestlocations &lt;- init_locations
  colnames(init_bestlocations) &lt;- sapply(X = 1:NUMBER_OF_ASSETS, function(x) paste0("p_",x))
  
  ## Compute fitness function value, here pRelVaR, for each particle ----
  init_particles &lt;- bind_cols(computeRelVaR(tbl = init_locations, returns = returns), 
                              init_velocities)
  
  init_particles &lt;- bind_cols(init_particles, init_bestlocations)
  
  return(init_particles)
  
}
</code></pre>
<h4>The generateRandomLocations function</h4>
<p>The function returns a tibble withh n lines, each composed of the <code>x_1, x_2, x_3, x_4</code> location components randomly generated with the <code>runif()</code> function. The <code>min</code> and <code>max</code> arguments default values will be overwritten with <code>XMIN</code> and <code>XMAX</code> values. </p>
<p>The locations must sum up to 1 (100%) since they represent the weigths of the assets.</p>
<pre><code>generateRandomLocations &lt;- function(n, min=.05, max=.75){
  
  tmp &lt;- runif(n = n, min = min, max = max)
  
  # Weights must sum up to 1 (100%)
  nweights &lt;- tmp/sum(tmp)
  
  new_cols &lt;- list()
  for(x in 1:n) {
    new_cols[[paste0("x_", x, sep="")]] = nweights[x]
  }
  
  res &lt;- as_tibble(new_cols)
  
  return(res)
}
</code></pre>
<h4>The generateRandomVelocities function</h4>
<p>The function returns a tibble withh n lines, each composed of the <code>v_1, v_2, v_3, v_4</code> velocity components randomly generated with the <code>runif()</code> function. The <code>min</code> and <code>max</code> arguments default values will be overwritten with <code>VMIN</code> and <code>VMAX</code> values.</p>
<pre><code>generateRandomVelocities &lt;- function(n, min=-1, max=1){
  
  velocities &lt;- runif(n = n, min = min, max = max)
  
  new_cols &lt;- list()
  for(x in 1:n) {
    new_cols[[paste0("v_", x, sep="")]] = velocities[x]
  }
  
  res &lt;- as_tibble(new_cols)
  
  return(res)
}
</code></pre>
<h4>The computeRelVaR function</h4>
<p>The <code>computeRelVaR()</code> function returns a tibble with <code>NUMBER_OF_PARTICLES</code> lines each composed of the <code>x_1, x_2, x_3, x_4</code> locations and its "relative to the mean" VaR result.</p>
<p>The <code>computeRelVaR()</code> function calls the following three functions:</p>
<ol>
<li><p><code>getWeigthedReturns()</code>: as defined in <a href="https://blog.bguarisma.com/notion-of-non-dominated-solutions">Part 2</a></p>
</li>
<li><p><code>getMeanWeigthedReturns()</code>: as defined in <a href="https://blog.bguarisma.com/notion-of-non-dominated-solutions">Part 2</a></p>
</li>
<li><p><code>getVaRValues()</code>: a simplified version of <a href="https://blog.bguarisma.com/notion-of-non-dominated-solutions">Part 2</a>'s <code>getObjectiveFunctionsValues</code> function, it computes and returns VaR-related values by calling only the <code>objectiveFunction1</code> as defined in <a href="https://blog.bguarisma.com/notion-of-non-dominated-solutions">Part 2</a>.</p>
</li>
</ol>
<pre><code>computeRelVaR &lt;- function(tbl, returns){
  
  lrelVaRValues &lt;- lapply(1:nrow(tbl), function(x){
    
    weights &lt;- unlist(tbl[x,c(1:NUMBER_OF_ASSETS)])
    
    returns %&gt;%
      getWeigthedReturns(vecA = assets, vecW = weights) %&gt;%
      getMeanWeigthedReturns() %&gt;%
      getVaRValues(vecW = weights)
    
  })
  
  relVaRValues &lt;- lrelVaRValues %&gt;%
    bind_rows() %&gt;%
    select(starts_with("x_"), pRelVaR)
  
  return(relVaRValues)
}
</code></pre>
<h3>Find global best location</h3>
<p>The following code snippet implements algorithm step 8.</p>
<p>It simply finds the particle within the initial population that has the **minimum **"relative to the mean" VaR.</p>
<pre><code>g_particle &lt;- init_pop[which(init_pop\(pRelVaR == min(init_pop\)pRelVaR)),]
</code></pre>
<h2>Iterations and Updates</h2>
<h3>Main program</h3>
<p>The code snippet below implements all remaining steps, from 9. to 12.</p>
<p>At each iteration we will save the corresponding population tibble  with the iterations_data  list. Its initialization is shown in the code snippet below.</p>
<pre><code>iterations_data &lt;- vector("list", MAX_ITERATIONS)

iterations_data[[1]] &lt;- list(pop = init_pop, gb = g_particle)
</code></pre>
<p>The main program calls the <code>computeNextLocation()</code> function. This function computes the next location components <code>x_1, x_2, x_3, x_4</code> based on the velocities <code>v_1, v_2, v_3, v_4</code> which must be calculated by <code>getVelocityMatrix()</code> based on current best locations<code>p_1, p_2, p_3, p_4</code> and global best location.</p>
<p>The new locations tibble will used to calculate the "relative to the mean" VaR (<code>pRelVaR</code>) with the <code>computeRelVaR()</code> function, already described above.</p>
<p>Next, we must identfy if they are any particles with a smaller <code>pRelVaR</code> than the last iteration <code>pRelVaR</code>. If yes, then we must update their best position components  <code>p_1, p_2, p_3, p_4</code> with their new location components <code>x_1, x_2, x_3, x_4</code>, and "rebuild" the population tibble. Else, no best positions will be updated and the same population tibble will be used it next iteration hoping the the randomness introduced in <code>getVelocityMatrix()</code> will generate new locations with smaller <code>pRelVaR</code>.</p>
<p>In either case, we find global best particle with minimum pRelVaR and save it (g_particle). Then, we add the population tibble and globa best particle yo</p>
<pre><code># Iterations
iter &lt;- 2
while(iter &lt;= MAX_ITERATIONS){  
  
  # Compute next velocities and locations
  next_locvels &lt;- computeNextLocation(tbl = iterations_data[[iter-1]]\(pop, gbest = iterations_data[[iter-1]]\)gb)
 
  # Get velocities tibble
  next_vels &lt;- next_locvels$vels
  
 # Get new locations tibble
  next_fitnessval &lt;- computeRelVaR(tbl = next_locvels$locs, returns = returns)
  
  # Get best locations tibble
  next_bestloc &lt;- iterations_data[[iter-1]]$pop %&gt;%
    select(starts_with("p_"))
  
  # Identify particles with smaller pRelVaR  than last iteration pRelVaR 
  update_idx &lt;- which(next_fitnessval\(pRelVaR &lt; iterations_data[[iter-1]]\)pop$pRelVaR)
  
  # For particles with smaller pRelVaR  than last iteration pRelVaR
  
  if(length(update_idx) &gt; 0){
    
    # Update particles' pbest locations (p) with new locations =&gt; update pbest tibble
    
    next_bestloc[update_idx, ] &lt;- next_fitnessval[update_idx, -which(colnames(next_fitnessval)=="pRelVaR")]
    
    newRelVaRloc &lt;- computeRelVaR(tbl = next_bestloc, returns = returns)
    
    new_pop &lt;-  bind_cols(bind_cols(newRelVaRloc, next_vels), next_bestloc)
    
  }else{
    
    # No updates : did not find particles with smaller pRelVaR  than last iteration pRelVaR
    new_pop &lt;-  iterations_data[[iter-1]]$pop
    
  }
  
  # Find global best particle with minimum pRelVaR
  g_particle &lt;- new_pop[which(new_pop\(pRelVaR == min(new_pop\)pRelVaR)),]
  
  iterations_data[[iter]] &lt;- list(pop = new_pop, gb = g_particle)
  
  # iterations_data &lt;- rlist::list.append(iterations_data, it_1 = list(pop = new_pop, gb = g_particle))
  
  iter &lt;- iter + 1
}

# Find particle with solution x_1, x_2, x_3, x_4 with minimum pRelVaR

iterations_data[[MAX_ITERATIONS]]$gb
</code></pre>
<h3>The <code>computeNextLocation()</code>  function</h3>
<p>It starts by calling the <code>getVelocityMatrix()</code>, decribed later, which will return particles' velocities as a matrix.</p>
<p>As stated by the second equation of motion, we must sum the velocities matrix to the current locations for which the tibble has been coverted to matrix.</p>
<p>Next, we must verify that the newly calculated locations values stay within the XMIN and XMAX bounds. If not, set its corresponding velocity coordinate to zero and bring the location coordinate value back to its limit, XMIN or XMAX according to the case (new location coordinate &lt; XMIN or &gt; XMAX).</p>
<p>Remember that locations represent the weights of the assets, thus they must sum up to 1 (100%). That is why the simply function <code>asDistribution()</code>  is called.  This is where <strong>I will introduce some flexibility to the algorithm since afetr calling this function you may encounter a location coordinate <code>x_</code> which is actually smaller than XMIN</strong>. See "red" weight vaues in the Excel sheet run results shown at the end of the article.</p>
<p>Both updated locations and velocities matrices will be converted back to tibbles prior being returned within a list.</p>
<pre><code>asDistribution &lt;- function(vec){
  vec/sum(vec)
}

computeNextLocation &lt;- function(tbl, gbest){
  
  velmat &lt;- getVelocityMatrix(tbl = tbl, gbest = gbest)
  current_mat &lt;- as.matrix(tbl %&gt;% select(starts_with("x_")))
  res &lt;- current_mat + velmat
  
  # Verify that weights are within min and max bounds
  
  if (length(which(res &lt; XMIN)) &gt; 0) { 
    velmat[which(res &lt; XMIN)] = 0L
    res[which(res &lt; XMIN)] = XMIN
    }
  
  if (length(which(res &gt; XMAX)) &gt; 0) {
    velmat[which(res &gt; XMAX)] = 0L
    res[which(res &gt; XMAX)] = XMAX
    }
  
  # Locations (assets weights) msut sum up to 1
  lnorm &lt;- lapply(X = 1:nrow(tbl), 
                  FUN = function(x) asDistribution(res[x,]))
  
  # Convert to tibble
  locs &lt;- lnorm %&gt;% 
    bind_rows() %&gt;%
    as_tibble()
  
  vels &lt;- as_tibble(velmat)
  colnames(vels) &lt;- sapply(X = 1:NUMBER_OF_ASSETS, FUN = function(x) paste0("v_",x))
  
  # Return tibbles within a list
  return(list(locs=locs, vels=vels))

}
</code></pre>
<h3>The <code>getVelocityMatrix()</code>  function</h3>
<p>Here is where the famous <em>equations of motion</em> (explained above) take place and where randomness is introduced by calculating <code>C1</code> and <code>C2</code> parameters.</p>
<p>The <code>getVelocityMatrix()</code> breaks the population tibble into its main components (locations, best locations, and velocities) also as tibbles, and it will extract the best location of the current global best location particle as a vector.</p>
<p>Then the random parameters <code>C1 </code> and <code>C2</code> will calculated by multiplying fixed parameter <code>CMAX</code> by <code>runif(1, min=0, max=1)</code>.</p>
<p>Each particle velocity vetor will be calculated by applying the first equation of motion as R vector operations. The result will be the vector <code>velocities</code>.</p>
<p>We convert the vector <code>velocities</code> into a matrix <code>velmat</code> with number of lines as the number of particles and the number of columns as the number of assets.</p>
<p>Next, we must verify that the newly calculated velocties values stay within the <code>VMIN</code> and <code>VMAX</code> bounds. If not, bring the velocity coordinate back to its limit, <code>VMIN</code> or <code>VMAX</code>, according to the case.</p>
<p>Finally, return the <code>velocities</code> matrix .</p>
<pre><code>getVelocityMatrix &lt;- function(tbl, gbest){
  
  current_tbl &lt;- tbl %&gt;% select(starts_with("x_"))
  pbest_tbl &lt;- tbl %&gt;% select(starts_with("p_"))
  velocity_tbl &lt;- tbl %&gt;% select(starts_with("v_"))
  g &lt;- as.numeric(gbest %&gt;% select(starts_with("p_")))
  
  C1 &lt;- CMAX*runif(1,0,1)
  C2 &lt;- CMAX*runif(1,0,1)
  
  velocities &lt;- lapply(X = 1:nrow(tbl), FUN = function(x){
                         
                         y &lt;- as.numeric(current_tbl[x,])
                         v &lt;- as.numeric(velocity_tbl[x,])
                         p &lt;- as.numeric(pbest_tbl[x,])
                         
                         VELOCITY_FACTOR*v + C1*(p-y) + C2*(g-y)
                         
                       })
  
  velmat &lt;- matrix(data = as.numeric(unlist(velocities)), 
                   nrow = nrow(tbl), 
                   ncol = NUMBER_OF_ASSETS)
  
  # Setting columns names to "x_" since it will sum with a location matrix
  colnames(velmat) &lt;- sapply(X = 1:NUMBER_OF_ASSETS, FUN = function(x) paste0("x_",x))
  
  if (length(which(velmat &lt; VMIN)) &gt; 0) velmat[which(velmat &lt; VMIN)] = VMIN
  if (length(which(velmat &gt; VMAX)) &gt; 0) velmat[which(velmat &gt; VMAX)] = VMAX
  
  return(velmat)
}
</code></pre>
<h2>Optimization results</h2>
<p>Using the <strong>same initial population tibble</strong> (<code>inti_pop</code>) I have run 19 optimization scenarios with different population sizes (<code>NUMBER_OF_PARTICLES</code>) and 2 couples of (<code>CMAX</code>, <code>VELOCITY_FACTOR</code>) values.</p>
<p>The **minimum **"relative to the mean" VaR was <strong>1.064213</strong> with following solution of portfolio:</p>
<ul>
<li><p>**MSFT **= 0.434 (<strong>43.4%</strong>)</p>
</li>
<li><p>**APPL **= 0.274  (<strong>27.4%</strong>)</p>
</li>
<li><p>**GOOG **= 0.0378 (<strong>3.78%</strong>)</p>
</li>
<li><p>**NFLX **= 0.255 (<strong>25.5%</strong>)</p>
</li>
</ul>
<p>Below, the results and details of each of the 19 optimization scenarios sorted by ascending "relative to the mean" VaR values.</p>
<p>The "red" values in the portfolios (SOLUTIONS column) below correspond to ones being smaller than XMIN.</p>
<img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1652694066420/hd_45oKNw.png" alt="image.png" />

<h2>Conclusion</h2>
<p>This article introduced the basics of the PSO algorithm with tibbles. The goal was to resolve a single-objective optimization problem by minimizing the "relative to the mean" VaR, the objective function. </p>
<p>This version of the algorithm did not use the notion of group of informants per particle (as introduced in [1]) thus, all particles "know" the best global location.</p>
<p>Although location limits XMIN and XMAX were defined we did not use them as optimization constraints. Since the location components (weights of assets) must sum up to 1 (100%) some flexibility was introduced while doing this computation.</p>
<p>In Part 4 we will come back to our multi-objective optimization problem introduced in <a href="https://blog.bguarisma.com/notion-of-non-dominated-solutions">Part 2 - Notion of non-dominated solutions</a></p>
<h2>References</h2>
<p>[1] Leclerc M., "<em>Particle Swarm Optimization</em>", Lavoisier, 2005</p>
<p>[2] Bozorg-Haddad O., Solgi M., Loaiciga H., "<em>Meta-heuristic and Evolutionary Algorithms for Engineering Optimization</em>"</p>
]]></content:encoded></item><item><title><![CDATA[Part 2 - Notion of non-dominated solutions]]></title><description><![CDATA[Cover photo by Mehdi Sepehri on Unsplash
Go to R-bloggers for R news and tutorials contributed by hundreds of R bloggers.
Introduction
This article is part of the series (Swarm) Portfolio Optimization]]></description><link>https://blog.bguarisma.com/part-2-notion-of-non-dominated-solutions</link><guid isPermaLink="true">https://blog.bguarisma.com/part-2-notion-of-non-dominated-solutions</guid><category><![CDATA[R Language]]></category><category><![CDATA[optimization]]></category><category><![CDATA[portfolio]]></category><category><![CDATA[Artificial Intelligence]]></category><dc:creator><![CDATA[Boris Guarisma]]></dc:creator><pubDate>Wed, 09 Feb 2022 09:53:40 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1644331052684/FWjuE6JgX.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Cover photo by <a href="https://unsplash.com/@mehdisepehri?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText">Mehdi Sepehri</a> on <a href="https://unsplash.com/s/photos/flock-of-birds?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText">Unsplash</a></p>
<p>Go to <a href="https://www.r-bloggers.com/">R-bloggers</a> for R news and tutorials contributed by hundreds of R bloggers.</p>
<h2>Introduction</h2>
<p>This article is part of the series <a href="https://blog.bguarisma.com/series/portfolio-optim">(Swarm) Portfolio Optimization</a>. I assume you have read the <a href="https://blog.bguarisma.com/nonparametric-value-at-risk-var">Nonparametric VaR</a> article or have solid notions about its calculation.</p>
<p>We will use the same 4-asset portfolio (MSFT, AAPL, GOOG, NFLX) as introduced in <a href="https://blog.bguarisma.com/nonparametric-value-at-risk-var">Nonparametric VaR</a>.</p>
<p>We will</p>
<ol>
<li><p>collect assets' prices, compute daily returns and clean data</p>
</li>
<li><p>format the daily returns tibble</p>
</li>
<li><p>compute weighted returns per asset</p>
</li>
<li><p>compute the mean of weighted returns per day</p>
</li>
<li><p>generate 1000 <strong>random 4-weight vectors</strong> or solutions or decision vectors</p>
</li>
<li><p>find the objective vectors corresponding to the non-nondominated solutions</p>
</li>
<li><p>visualize and distinguish the non-nondominated solutions from the dominated ones</p>
</li>
</ol>
<p>As stated in the nonparametric VaR article, I like working with tibbles (and not time series R objects), that's why I use and recommend the <a href="https://business-science.github.io/tidyquant/">tidyquant</a> package.</p>
<h2>Multi-objective optimization</h2>
<p>Since the final goal of this series is the implementation of MOPSO, let us start with the mathematical concept behind <strong>multi-objective optimization</strong> as reminded in  Mostaghim et al. paper [1]:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1644392642462/NLzEaTsmZ.png" alt="image.png" /></p>
<h3>Our multi-objective problem</h3>
<p>We want to use a multi-objective PSO approach to find VaR-efficient portfolios by solving the following optimization problem: [2]</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1644393150304/4vDqyrS95.png" alt="image.png" /></p>
<p>where</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1644393258591/26QOOlZYs.png" alt="image.png" /></p>
<p>Thus, we want to find a set of optimal solutions (weights <em><strong>w'</strong></em>) to</p>
<ul>
<li><p>minimize de Value-at-Risk (VaR), relative to the mean, of the portfolio</p>
</li>
<li><p>maximize the expected return of the portfolio</p>
</li>
</ul>
<h3>Our objective functions</h3>
<p>We will define <strong>f1</strong> as being the <strong>Value-at-Risk (VaR), relative to the mean</strong>, of the portfolio, and <strong>f2</strong> as the <strong>expected return</strong> of the portfolio.</p>
<p>Finally, based on the mathematical explanation provided above we want to find the Pareto-optimal <em>decision vectors</em> (or weights) and their corresponding <em>Pareto-optimal objective vector</em> values (<strong>black squares</strong> in the figure below).</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1644394263590/LtEZS0aEv.png" alt="image.png" /></p>
<p>These Pareto-optimal decision vectors (or weights) are also called <strong>non-dominated solutions</strong>. In a MOPSO context they are also called <strong>archive-members</strong>.</p>
<h2>Packages</h2>
<p>Load the following packages:</p>
<pre><code>library(PerformanceAnalytics)
library(quantmod)
library(tidyquant)
library(tidyverse)
library(timetk)
library(skimr)
library(plotly)
library(caret)
library(tictoc)
</code></pre>
<h2>Collect prices, compute daily returns and clean data</h2>
<p>This is the same code used in the <a href="https://blog.bguarisma.com/nonparametric-value-at-risk-var">Nonparametric VaR</a> article.</p>
<pre><code># ASSETS
assets &lt;- c("MSFT", "AAPL", "GOOG", "NFLX")

# TIME INTERVAL

end &lt;- "2021-12-31" %&gt;% ymd()
start &lt;- end - years(5) + days(1)

# GET ASSETS' DAILY RETURNS

returns_daily_tbl &lt;- assests %&gt;%
  tq_get(from = start, to = end) %&gt;%
  group_by(symbol) %&gt;%
  tq_transmute(select = adjusted,
               mutate_fun = periodReturn, 
               period = "daily") %&gt;%
  ungroup()

# PREPROCESSING - CLEAN

## Delete each asset first row (= 0)
rows_to_delete &lt;- which(returns_daily_tbl$date == ymd("2017-01-03"))
returns_daily_tbl &lt;- returns_daily_tbl[-rows_to_delete,] 
</code></pre>
<h2>Daily returns tibble format</h2>
<p>Let us put the tibble in the right format by renaming the columns and converting the <em>symbol</em> column to factor, here below the corresponding custom function:</p>
<pre><code>formatTibble &lt;- function(tbl){
  columnNames &lt;- c("symbol", "date", "returns")
  res &lt;- tbl %&gt;%
    mutate(symbol = as.factor(symbol))
  colnames(res) &lt;- columnNames
  
  return(res)
}
</code></pre>
<h2>Get weighted returns</h2>
<p>The next custom function <code>getWeigthedReturns()</code> multiplies a weight from the weight vector to its corresponding asset daily returns and outputs an updated tibble with new columns:  <em>weight</em> and <em>weighted.returns</em>.</p>
<pre><code>getWeigthedReturns &lt;- function(tbl, vecA, vecW){
  # Generate by-symbol tibble with weighted returns
  # add by-symbol tibble to a list
  lsymboltbl &lt;- lapply(X = 1:length(vecA), function(x){
    tbl %&gt;%
      filter(symbol == vecA[x]) %&gt;%
      mutate(weight = vecW[x]) %&gt;%
      mutate(weighted.returns = returns * weight)
  })
  
  # Bind symbol tibble by rows
  res &lt;- lsymboltbl %&gt;% 
    bind_rows()
  
  return(res)
}
</code></pre>
<h2>Compute mean of weighted returns</h2>
<p>Our goal here is to generate the same tibble (portfolio) from which we calculated the expected return and the "relative to the mean" VaR in the <a href="https://blog.bguarisma.com/nonparametric-value-at-risk-var">Nonparametric VaR</a> article. Thus, we group by <strong>date</strong> and calculate the mean of daily returns of the 4 assets.</p>
<pre><code>getMeanWeigthedReturns &lt;- function(tbl){
  res &lt;- tbl %&gt;%
    group_by(date) %&gt;%
    summarise(mean.weighted.returns = mean(weighted.returns))
  
  return(res)
}
</code></pre>
<h2>Objective function f1: VaR</h2>
<p>Although the custom function calculates both absolute and relative VaR, we consider only the <strong>"relative to the mean" VaR</strong> as the output of objective function f1.</p>
<pre><code>objectiveFunction1 &lt;- function(tbl, p = .99){
  # Absolute VaR
  absoluteVaR &lt;- tbl %&gt;% 
    tq_performance(Ra = mean.weighted.returns,
                   performance_fun = VaR, 
                   p = p, 
                   method = "historical")
  
# Relative VaR: E(Rp) - q(Rp)
  relativeVaR &lt;- mean(tbl\(mean.weighted.returns) - absoluteVaR\)VaR
  
  return(list(VaR = list(abolute = absoluteVaR$VaR,
                         relative = relativeVaR)))
}
</code></pre>
<h2>Objective function f2: Expected return</h2>
<p>Since we apply a minimization for both objective functions, the <code>objectiveFunction2()</code>  custom function outputs a <strong>negative value</strong> for the expected return.</p>
<pre><code>objectiveFunction2 &lt;- function(tbl){
  # w'E(R)
  res &lt;- mean(tbl$mean.weighted.returns)
  return(-res)
}
</code></pre>
<h2>Other custom functions</h2>
<p>We will generate 1000 random 4-weight solutions with the <code>generateRandomWeightsVector()</code> custom function. All four weights must sum to 1. I have also added a constraint to avoid generating 0-valued and 1-valued weights.</p>
<pre><code>generateRandomWeightsVector &lt;- function(n, min=.05, max=.75){
  
  tmp &lt;- runif(n = n, min = min, max = max)
  nweights &lt;- tmp/sum(tmp)
  
  new_cols &lt;- list()
  for(x in 1:n) {
    new_cols[[paste0("w_", x, sep="")]] = nweights[x]
  }
  
  res &lt;- as_tibble(new_cols)
  
  return(res)
}
</code></pre>
<p>We <strong>group the random solutions with their respective objective functions results</strong> in the same tibble with the following custom function:</p>
<pre><code>getObjectiveFunctionsValues &lt;- function(tbl, vecW){
  
  new_cols &lt;- list()
  for(x in 1:length(vecW)) {
    new_cols[[paste0("w_", x, sep="")]] = vecW[x]
  }
  res &lt;- as_tibble(new_cols)
  
  objf1val &lt;- objectiveFunction1(tbl)
  objf2val &lt;- objectiveFunction2(tbl)
  
  res &lt;- res %&gt;%
    mutate(absVaR = objf1val\(VaR\)abolute) %&gt;%
    mutate(relVaR = objf1val\(VaR\)relative) %&gt;%
    mutate(expRet = objf2val) %&gt;%
    mutate(pAbsVaR = absVaR * 100) %&gt;%
    mutate(pRelVaR = relVaR * 100) %&gt;%
    mutate(pExpRet = expRet * 100)
  
  return(res)
}
</code></pre>
<p>The tibble generated by the function above will be used to <strong>find the non-dominated solutions</strong> using the following custom function:</p>
<pre><code>getNonDominated &lt;- function(tbl) {
  
  idxVar &lt;- which(colnames(tbl) == "relVaR")
  idxExpR &lt;- which(colnames(tbl) == "expRet")
  
  i &lt;- order(tbl[, idxVar], tbl[, idxExpR], decreasing=FALSE)
  frontier &lt;- rep(0, length(i))
  k &lt;- 0; 
  y &lt;- Inf
  for (j in i) {
    if (tbl[j, idxExpR] &lt; y) {
      frontier[k &lt;- k+1] &lt;- j
      y &lt;- tbl[j, idxExpR]
    }
  }
  return(frontier[1:k])
}
</code></pre>
<h2>Main R code</h2>
<p>The code within the <em>tictoc</em> ran for 2 minutes, I have a Windows laptop, i5 CPU (8 vCores), 16GB RAM. </p>
<pre><code>## 1. Create random particles tibble ----

NUMBER_OF_ASSETS = 4
NUMBER_PARTICLES = 1000

lrandomparticles &lt;- lapply(X = 1:NUMBER_PARTICLES, function(x){
  generateRandomWeightsVector(n = NUMBER_OF_ASSETS)
})
random_particles &lt;- lrandomparticles %&gt;%
  bind_rows()

# 1000 random solutions
&gt; random_particles
# A tibble: 1,000 x 4
      w_1    w_2    w_3    w_4
    &lt;dbl&gt;  &lt;dbl&gt;  &lt;dbl&gt;  &lt;dbl&gt;
 1 0.424  0.431  0.0569 0.0873
 2 0.283  0.363  0.126  0.227 
 3 0.351  0.258  0.0496 0.341 
 4 0.266  0.218  0.431  0.0854
 5 0.225  0.304  0.329  0.142 
 6 0.298  0.0952 0.409  0.198 
 7 0.190  0.197  0.424  0.189 
 8 0.335  0.310  0.264  0.0912
 9 0.0437 0.430  0.274  0.252 
10 0.0981 0.413  0.0400 0.449 
# ... with 990 more rows

## 2. Generate particle &amp; objective functions' results tibble ----

tic()
lparticlesObjFunResults &lt;- lapply(1:NUMBER_PARTICLES, function(x){
  
  weights &lt;- as.numeric(random_particles[x,])
  
  returns_daily_tbl %&gt;%
    formatTibble() %&gt;%
    getWeigthedReturns(vecA = assests, vecW = weights) %&gt;%
    getMeanWeigthedReturns() %&gt;%
    getObjectiveFunctionsValues(vecW = weights) %&gt;%
    mutate(sharpe = expRet/relVaR)
  
})
toc()

particlesObjFunResults &lt;- lparticlesObjFunResults %&gt;% 
  bind_rows()

&gt; particlesObjFunResults
# A tibble: 1,000 x 11
      w_1    w_2    w_3    w_4  absVaR relVaR    expRet pAbsVaR pRelVaR pExpRet sharpe
    &lt;dbl&gt;  &lt;dbl&gt;  &lt;dbl&gt;  &lt;dbl&gt;   &lt;dbl&gt;  &lt;dbl&gt;     &lt;dbl&gt;   &lt;dbl&gt;   &lt;dbl&gt;   &lt;dbl&gt;  &lt;dbl&gt;
 1 0.424  0.431  0.0569 0.0873 -0.0109 0.0113 -0.000396   -1.09    1.13 -0.0396 0.0352
 2 0.283  0.363  0.126  0.227  -0.0106 0.0110 -0.000387   -1.06    1.10 -0.0387 0.0352
 3 0.351  0.258  0.0496 0.341  -0.0107 0.0111 -0.000390   -1.07    1.11 -0.0390 0.0353
 4 0.266  0.218  0.431  0.0854 -0.0110 0.0113 -0.000355   -1.10    1.13 -0.0355 0.0314
 5 0.225  0.304  0.329  0.142  -0.0108 0.0112 -0.000367   -1.08    1.12 -0.0367 0.0329
 6 0.298  0.0952 0.409  0.198  -0.0106 0.0109 -0.000353   -1.06    1.09 -0.0353 0.0324
 7 0.190  0.197  0.424  0.189  -0.0108 0.0111 -0.000355   -1.08    1.11 -0.0355 0.0319
 8 0.335  0.310  0.264  0.0912 -0.0108 0.0111 -0.000373   -1.08    1.11 -0.0373 0.0336
 9 0.0437 0.430  0.274  0.252  -0.0107 0.0110 -0.000376   -1.07    1.10 -0.0376 0.0340
10 0.0981 0.413  0.0400 0.449  -0.0112 0.0116 -0.000396   -1.12    1.16 -0.0396 0.0340
# ... with 990 more rows

ndidx &lt;- getNonDominated(tbl = particlesObjFunResults)

gp &lt;- ggplot() +
  geom_point(particlesObjFunResults[ndidx,], mapping = aes(x = pRelVaR, y = pExpRet), color = "red") +
  geom_point(particlesObjFunResults[-ndidx,], mapping = aes(x = pRelVaR, y = pExpRet), color = "black")
ggplotly(gp) 
</code></pre>
<p>Be reminded that the objective function f2 outputs <strong>negative values</strong> for the portfolio expected returns (because we consider a minimization problem, see the figure of Pareto-optimal objective vector at the beginning of the article). Thus, we are actually <strong>displaying the frontier upsides down</strong>. </p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1644331149630/KyQQYw9Ea.png" alt="image.png" /></p>
<p>Below you will find the 16 <strong>non-dominated solutions</strong> (<em>red</em> points) along with their corresponding VaR (%) and expected return (%) results (expressed in percentage), keeping the negative sign of the expected return (%).</p>
<p>In the <a href="https://blog.bguarisma.com/nonparametric-value-at-risk-var">Nonparametric VaR</a> article we considered an equally distributed weight set (25% weight for each asset) and obtained an expected return of 0.032% and a relative VaR of 1.08%. We see that the first 3 non-dominated solutions below provide a higher expected return (ignore the negative sign) with less risk.</p>
<pre><code>&gt; particlesObjFunResults[ndidx,] %&gt;% 
    select(w_1, w_2, w_3, w_4, pRelVaR, pExpRet) %&gt;% 
    arrange(pRelVaR)
# A tibble: 16 x 6
     w_1    w_2    w_3   w_4 pRelVaR pExpRet
   &lt;dbl&gt;  &lt;dbl&gt;  &lt;dbl&gt; &lt;dbl&gt;   &lt;dbl&gt;   &lt;dbl&gt;
 1 0.313 0.0748 0.386  0.226    1.07 -0.0355
 2 0.310 0.110  0.354  0.225    1.07 -0.0359
 3 0.283 0.138  0.347  0.232    1.07 -0.0360
 4 0.345 0.0844 0.318  0.252    1.08 -0.0361
 5 0.447 0.193  0.101  0.259    1.08 -0.0384
 6 0.359 0.256  0.0869 0.298    1.08 -0.0387
 7 0.420 0.338  0.0818 0.161    1.08 -0.0391
 8 0.458 0.310  0.0629 0.169    1.09 -0.0391
 9 0.378 0.394  0.0476 0.181    1.09 -0.0395
10 0.302 0.419  0.0466 0.233    1.10 -0.0396
11 0.244 0.481  0.0645 0.211    1.12 -0.0396
12 0.168 0.527  0.0759 0.230    1.12 -0.0397
13 0.217 0.446  0.0313 0.305    1.12 -0.0398
14 0.378 0.453  0.0408 0.128    1.12 -0.0398
15 0.175 0.600  0.0810 0.144    1.16 -0.0399
16 0.105 0.604  0.0752 0.216    1.17 -0.0399
</code></pre>
<h2>Conclusion</h2>
<p>In this article I wanted to <em>illustrate</em> the notion of <strong>non-dominated solutions</strong> applied to a portofolio optimization use case. Here we started by generating a <strong>random set of 1000 weight vectors</strong> in order to find the non-dominated solutions among this set of 1000 weight vectors. Our utimate goal will be to generate these weight vectors and non-dominated solutions with the MOPSO algorithm, but first we will explain other notions such as <em>finding good local guides with the Sigma method [1]</em>.</p>
<h2>References</h2>
<p>[1] Mostaghim S., Teich J., « Strategies for Finding Local Guides in Multi-objective Particle Swarm Optimization (MOPSO) »</p>
<p>[2] Alfaro Cid E. et al., « Minimizing value-at-risk in a portfolio optimization problem using a multiobjective genetic algorithm », International Journal of Risk Assessment and Management, 2011</p>
]]></content:encoded></item><item><title><![CDATA[Part 1 - Nonparametric Value-at-Risk (VaR)]]></title><description><![CDATA[Cover photo by Chris Liverani on Unsplash
Go to R-bloggers for R news and tutorials contributed by hundreds of R bloggers.
Introduction
This is the first of a series of articles explaining how to appl]]></description><link>https://blog.bguarisma.com/part-1-nonparametric-value-at-risk-var</link><guid isPermaLink="true">https://blog.bguarisma.com/part-1-nonparametric-value-at-risk-var</guid><category><![CDATA[portfolio]]></category><category><![CDATA[optimization]]></category><category><![CDATA[R Language]]></category><dc:creator><![CDATA[Boris Guarisma]]></dc:creator><pubDate>Sun, 06 Feb 2022 14:59:40 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1643984320844/ZAzCCd79n.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Cover photo by <a href="https://unsplash.com/@chrisliverani?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText">Chris Liverani</a> on <a href="https://unsplash.com/s/photos/graph?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText">Unsplash</a></p>
<p>Go to <a href="https://www.r-bloggers.com/">R-bloggers</a> for R news and tutorials contributed by hundreds of R bloggers.</p>
<h2>Introduction</h2>
<p>This is the first of a <a href="https://blog.bguarisma.com/series/portfolio-optim">series</a> of articles explaining how to apply <strong>multi-objective particle swarm optimization</strong>* (MOPSO) to portfolio management.</p>
<h2>Notion of Value-at-Risk (VaR)</h2>
<p>As per P. Jorion's book [1], "<em>we can formally define the value at risk (VaR) of a portfolio as <strong>the worst loss over a target horizon such that there is a low, prespecified probability that the actual loss will be larger</strong>.</em>"</p>
<p><em>This definition involves two quantitative factors, the <strong>horizon</strong> and the <strong>confidence level</strong>.</em> </p>
<p><em>Define c as the confidence level and L as the loss, measured as a positive number. VAR is also reported as a positive number.</em> </p>
<p><em>A general definition of VAR is that it is the smallest loss, in absolute value, such that</em> </p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1643984611973/Fpoch0f1j.png" alt="image.png" /></p>
<p><em>Take, for instance, a 99 percent confidence level, or c = 0.99. VAR then is the cutoff loss such that the probability of experiencing a greater loss is less than 1 percent</em>.</p>
<h2>Computing VaR</h2>
<p>The figure below comes from a <a href="https://www.youtube.com/watch?v=L2xzlvhkagk&amp;t=18s">Bionic Turtle video</a> available in YouTube [2], it takes only 6 minutes, have a look!</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1643985630374/6qNMFZDVD.png" alt="image.png" /></p>
<ul>
<li><p>The <strong>nonparametric approach</strong> uses actual historical data, it is simple and easy to use. There is no hypothesis about the distribution of the data. This is the approach used in this article.</p>
</li>
<li><p>The <strong>Monte Carlo simulation</strong> is about imagining hypothetical future data. It generates its own data i.e., given a model specification about the assets of the portfolio we run any number of trials in order to obtain a simulated distribution of data.</p>
</li>
<li><p>The <strong>parametric approach</strong> uses the data (<em>as an excuse</em>) to find a distribution, <em>usually</em> a normal distribution which requires only two parameters: the mean and the standard deviation.</p>
</li>
</ul>
<p>As per [3], "<em>approaches to quantify VaR such as delta-normal, delta-gamma or Monte Carlo simulation method rely on the normality assumption or other prespecified distributions. These approaches have several drawbacks, such as the estimation of parameters and whether the distribution fit properly the data in the tail or not.</em>"</p>
<h2>Computing nonparametric VaR with R</h2>
<h3>Example</h3>
<p>Below is the example taken from Jorion's book [1]. I'm going to copie his explanation about the VaR computation, and then I will base my explanations and code on my interpretation of Jorion's book [1] explanations and figure.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1643987015825/BtPNNu9KO.png" alt="image.png" /></p>
<p><em>Assume that this (the nonparametric VaR) can be used to define a forward-looking distribution, making the hypothesis that daily revenues are identically and independently distributed.</em> </p>
<p><em>We can derive the VAR at the 95 percent confidence level from the 5 percent left-side “losing tail” in the histogram.</em></p>
<p><em>The figure (above), for instance, reports J.P. Morgan’s distribution of daily revenues in 1994. The graph shows how to compute nonparametric VAR.</em></p>
<p><em>Let <strong>W'</strong> be the lowest portfolio value at the given confidence level c.</em></p>
<p><em>From this graph, the <strong>average revenue is about $5.1 million</strong>. There is a total of 254 observations; therefore, we would like to find <strong>W'</strong> such that the number of observations to its left is 254 × 5 percent = 12.7.</em></p>
<p><em>We have 11 observations to the left of −\(10 million and 15 to the left of −\)9 million. Interpolating, we find <strong>W' = −$9.6 million</strong>.</em> </p>
<p><em>The VaR of daily revenues, measured <strong>relative to the mean</strong>, is <strong>VAR = E (W) − W' = \(5.1 − (−\)9.6) = $14.7 million</strong>.</em></p>
<p><em>If one wishes to measure VaR in terms of <strong>absolute</strong> dollar loss, <strong>VaR then is $9.6 million</strong>.</em></p>
<p>Finally, it is useful to describe the average of losses beyond VaR, which is \(20 million here. Adding the mean, we find an <strong>expected tail loss (ETL) of \)25 million</strong>.*</p>
<h3>Basis of my explanation</h3>
<p>Let us consider that the daily returns ofthe figure  correspond to a portfolio composed of four assets: Microsoft ("MSFT"), Apple ("AAPL"), Google ("GOOG"), and Netflix ("NFLX").</p>
<p>In order to display the histogram we should have a (final) tibble like this one:</p>
<pre><code># A tibble: 1,257 x 2
   date       mean.weighted.returns
   &lt;date&gt;                     &lt;dbl&gt;
 1 2017-01-04              0.000652
 2 2017-01-05              0.00204 
 3 2017-01-06              0.00184 
 4 2017-01-09              0.000355
 5 2017-01-10             -0.000607
 6 2017-01-11              0.00144 
 7 2017-01-12             -0.00159 
 8 2017-01-13              0.00228 
 9 2017-01-17             -0.000297
10 2017-01-18              0.000252
# ... with 1,247 more rows
</code></pre>
<p>The tibble above has a column called "mean.weighted.returns" which means that we have taken the <strong>weighted mean of the assets' daily returns</strong>. Here, the weights have been equally distributed e.g., <strong>25% MSFT, 25% AAPL, 25% GOOG, and 25% NFLX</strong>.</p>
<p>OK, let us then generate this tibble.</p>
<h3>R packages</h3>
<pre><code>library(PerformanceAnalytics)
library(quantmod)
library(tidyquant)
library(tidyverse)
library(timetk)
library(skimr)
library(plotly)
</code></pre>
<h3>Get the data</h3>
<p>Let us collect 5 years of data e.g., the prices of the four assets: Microsoft ("MSFT"), Apple ("AAPL"), Google ("GOOG"), and Netflix ("NFLX"). For this, we will use the <code>tq_get()</code> from tidyquant package.</p>
<p>I prefer to use the tidyquant package because I like to work with tibbles, I don't have to worry about the different time series data format (xts, ...).</p>
<p>We will directly compute the <strong>daily returns</strong> of the <strong>adjusted</strong> prices.</p>
<pre><code>assests &lt;- c("MSFT", "AAPL", "GOOG", "NFLX")

end &lt;- "2021-12-31" %&gt;% ymd()
start &lt;- end - years(5) + days(1)

returns_daily_tbl &lt;- assests %&gt;%
  tq_get(from = start, to = end) %&gt;%
  group_by(symbol) %&gt;%
  tq_transmute(select = adjusted,
               mutate_fun = periodReturn, 
               period = "daily") %&gt;%
  ungroup()

&gt; returns_daily_tbl
# A tibble: 5,032 x 3
   symbol date       daily.returns
   &lt;chr&gt;  &lt;date&gt;             &lt;dbl&gt;
 1 MSFT   2017-01-03      0       
 2 MSFT   2017-01-04     -0.00447 
 3 MSFT   2017-01-05      0       
 4 MSFT   2017-01-06      0.00867 
 5 MSFT   2017-01-09     -0.00318 
 6 MSFT   2017-01-10     -0.000319
 7 MSFT   2017-01-11      0.00910 
 8 MSFT   2017-01-12     -0.00918 
 9 MSFT   2017-01-13      0.00144 
10 MSFT   2017-01-17     -0.00271 
# ... with 5,022 more rows
</code></pre>
<h3>Clean the data</h3>
<p>Because of the daily return calculation, the first row ("2017-01-03") of each asset will be zero. Let us delete these four rows, one per asset (or symbol).</p>
<pre><code>rows_to_delete &lt;- which(returns_daily_tbl$date == ymd("2017-01-03"))
returns_daily_tbl &lt;- returns_daily_tbl[-rows_to_delete,]
</code></pre>
<h3>Define the weights vector</h3>
<pre><code>wts_tbl &lt;- returns_daily_tbl %&gt;%
  distinct(symbol) %&gt;%
  mutate(weight = c(.25, .25, .25, .25))
</code></pre>
<h3>Apply the weights</h3>
<p>Let us apply the weights by creating two new columns, <em>weight</em> and <em>weighted.returns</em>. The <em>symbol</em> column will be converted from character to factor.</p>
<pre><code>returns_daily_weighted_tbl &lt;- returns_daily_tbl %&gt;%
  group_by(symbol) %&gt;%
  mutate(weight = case_when(symbol == "MSFT" ~ as.numeric(wts_tbl[1,2]),
                            symbol == "AAPL" ~ as.numeric(wts_tbl[2,2]),
                            symbol == "GOOG" ~ as.numeric(wts_tbl[3,2]),
                            symbol == "NFLX" ~ as.numeric(wts_tbl[4,2]))) %&gt;%
  mutate(weighted.returns = daily.returns * weight) %&gt;%
  mutate(symbol = as.factor(symbol))

&gt; returns_daily_weighted_tbl
# A tibble: 5,028 x 5
# Groups:   symbol [4]
   symbol date       daily.returns weight weighted.returns
   &lt;fct&gt;  &lt;date&gt;             &lt;dbl&gt;  &lt;dbl&gt;            &lt;dbl&gt;
 1 MSFT   2017-01-04     -0.00447    0.25       -0.00112  
 2 MSFT   2017-01-05      0          0.25        0        
 3 MSFT   2017-01-06      0.00867    0.25        0.00217  
 4 MSFT   2017-01-09     -0.00318    0.25       -0.000796 
 5 MSFT   2017-01-10     -0.000319   0.25       -0.0000798
 6 MSFT   2017-01-11      0.00910    0.25        0.00228  
 7 MSFT   2017-01-12     -0.00918    0.25       -0.00229  
 8 MSFT   2017-01-13      0.00144    0.25        0.000359 
 9 MSFT   2017-01-17     -0.00271    0.25       -0.000678 
10 MSFT   2017-01-18     -0.000480   0.25       -0.000120 
# ... with 5,018 more rows
</code></pre>
<h3>Compute the mean</h3>
<p>Group by date and take the mean of the 4 assets' weighted daily returns.</p>
<pre><code>returns_daily_weighted_mean_tbl &lt;- returns_daily_weighted_tbl %&gt;%
  group_by(date) %&gt;%
  summarise(mean.weighted.returns = mean(weighted.returns))

&gt; returns_daily_weighted_mean_tbl
# A tibble: 1,257 x 2
   date       mean.weighted.returns
   &lt;date&gt;                     &lt;dbl&gt;
 1 2017-01-04              0.000652
 2 2017-01-05              0.00204 
 3 2017-01-06              0.00184 
 4 2017-01-09              0.000355
 5 2017-01-10             -0.000607
 6 2017-01-11              0.00144 
 7 2017-01-12             -0.00159 
 8 2017-01-13              0.00228 
 9 2017-01-17             -0.000297
10 2017-01-18              0.000252
# ... with 1,247 more rows
</code></pre>
<p>And <em>that's it</em>! we have finally obtained the tibble from which we will generate the histogram and calculate the VaR.</p>
<h3>Display summary statistics</h3>
<p>Let us define a skimr custom function to add the min and max values to the default summary statistics display of the previous tibble.</p>
<pre><code>myskim &lt;- skim_with(numeric = sfl(max, min), 
                    append = TRUE)

returns_daily_weighted_mean_tbl %&gt;%
  myskim()

&gt; returns_daily_weighted_mean_tbl %&gt;%
     myskim()
-- Data Summary ------------------------
                           Values    
Name                       Piped data
Number of rows             1257      
Number of columns          2         
_______________________              
Column type frequency:               
  Date                     1         
  numeric                  1         
________________________             
Group variables            None      

-- Variable type: Date ------------------------------------------------------------------------------------------------------------------------------------------
# A tibble: 1 x 7
  skim_variable n_missing complete_rate min        max        median     n_unique
* &lt;chr&gt;             &lt;int&gt;         &lt;dbl&gt; &lt;date&gt;     &lt;date&gt;     &lt;date&gt;        &lt;int&gt;
1 date                  0             1 2017-01-04 2021-12-30 2019-07-05     1257

-- Variable type: numeric ---------------------------------------------------------------------------------------------------------------------------------------
# A tibble: 1 x 13
  skim_variable         n_missing complete_rate     mean      sd      p0      p25      p50     p75   p100 hist     max     min
* &lt;chr&gt;                     &lt;int&gt;         &lt;dbl&gt;    &lt;dbl&gt;   &lt;dbl&gt;   &lt;dbl&gt;    &lt;dbl&gt;    &lt;dbl&gt;   &lt;dbl&gt;  &lt;dbl&gt; &lt;chr&gt;  &lt;dbl&gt;   &lt;dbl&gt;
1 mean.weighted.returns         0             1 0.000372 0.00407 -0.0312 -0.00131 0.000547 0.00230 0.0264 ▁▁▇▂▁ 0.0264 -0.0312
</code></pre>
<p>From the skimr resut above we can see that the mean of <em>mean.weighted.returns</em> (or the <strong>portfolio Expected Return</strong>) is 0.000372 or <strong>0.037%</strong>. You can also have a quick look at the displayed (mini) histogram provided.</p>
<h2>Compute nonparametric VaR</h2>
<p>To compute the <strong>absolute</strong> nonparametric VaR at 99% confidence level with use the <code>tidyquant::tq_performance()</code> function. Thus, you must specify:</p>
<ul>
<li><p><code>Ra</code>: the column of asset returs (here, the <em>mean.weighted.returns</em>)</p>
</li>
<li><p><code>performance_fun</code>: the performnace function (here, the <em>VaR</em>)</p>
</li>
<li><p><code>p</code>: here, the confidence level (<em>99%</em>)</p>
</li>
<li><p><code>method</code>: for nonparametric VaR we use the *historical *method as explained at the beginning of the article.</p>
</li>
</ul>
<pre><code>VaR.tq &lt;- returns_daily_weighted_mean_tbl %&gt;% 
  tq_performance(Ra = mean.weighted.returns,
                 performance_fun = VaR, 
                 p = .99, 
                 method = "historical")

&gt; cat("VaR @99% = ", VaR.tq$VaR)
VaR @99% =  -0.01050326
</code></pre>
<p>We obtain the <strong>absolute VaR @99% =  -0.01050326</strong>, this is the cutoff loss such that the probability of experiencing a greater loss is less than 1 percent.</p>
<p>The <strong>relative (to the mean) VaR @99%</strong> should be 0.000372 - (-0.01050326) = <strong>0.01087526</strong>, let us check:</p>
<pre><code>&gt; mean(returns_daily_weighted_mean_tbl\(mean.weighted.returns) - VaR.tq\)VaR
[1] 0.0108755
</code></pre>
<p>Here, for this fictitious example, we consider the <strong>holding period = 1 day</strong>. Since we are computing a nonparametric VaR for which no hypothesis of the distribution e.g., normality, has been made, we <em>cannot</em> apply the "VaR(T days) = VaR*SQRT(T)" rule.</p>
<h2>Visualize nonparametric VaR</h2>
<pre><code>gp &lt;- ggplot(returns_daily_weighted_mean_tbl, aes(x=mean.weighted.returns)) +
  geom_histogram(color="black", fill="white") +
  geom_vline(aes(xintercept=VaR.tq$VaR), color="blue", linetype="dashed", size=1)
ggplotly(gp)
</code></pre>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1644157508733/TodrVESpn.png" alt="image.png" /></p>
<h2>Apply positions amount</h2>
<p>Let us consider an <strong>investment of \(1,000,000</strong> which has been equally distributed across all four assets e.g., 25% per asset thus, \)250,000 per asset.</p>
<p>The portfolio <strong>Expected Return</strong> is 0.000372 of \(1,000,000 = <strong>\)372</strong></p>
<p>The <strong>absolute VaR @99%</strong> is  -0.01050326 x \(1,000,000 = <strong>-\)10,503.26</strong>, this is the cutoff loss such that the probability of experiencing a greater loss is less than 1 percent.</p>
<p>The <strong>relative (to the mean) VaR @99%</strong> is <strong>\(372</strong> - (-\)10,503.26) = <strong>$10,875.26</strong></p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1644157464011/MSf4l2TBy.png" alt="image.png" /></p>
<h2>Change the weights</h2>
<p>Say we apply the following weights to the four assets:</p>
<pre><code>&gt; wts_tbl2
# A tibble: 4 x 2
  symbol weight
  &lt;chr&gt;   &lt;dbl&gt;
1 MSFT     0.2 
2 AAPL     0.15
3 GOOG     0.15
4 NFLX     0.5
</code></pre>
<p>The portfolio <strong>Expected Return</strong> is <strong>$377</strong></p>
<p>The <strong>absolute VaR @99%</strong> is  <strong>-$11,228</strong>, this is the cutoff loss such that the probability of experiencing a greater loss is less than 1 percent.</p>
<p>The <strong>relative (to the mean) VaR @99%</strong> is <strong>$11,606</strong></p>
<p>Consequently, by applying these new weights <strong>we have increased the VaR of the portfolio</strong>. Thus, in terms of <em>risk management</em>, this new set of weights was not a good choice. </p>
<h2>Towards portfolio optimization</h2>
<p>Now, in terms <em>portfolio optimization</em> when considering the following  <strong>objective function</strong> which is the minimization of the <strong>VaR relative to the mean</strong> equation:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1644158895584/LK0fOjZDt.png" alt="image.png" /></p>
<p>the investor preferences are expressed as a function of return and maximum loss, and a <strong>VaR-efficient frontier</strong> (made up of Pareto efficient portfolios) in the mean-VaR space has to be found. For this reason, we will use multiobjective PSO approach to find VaR-efficient portfolios by solving the following optimization problem: [3]</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1644305802814/OsSECjs2S.png" alt="image.png" /></p>
<p>This is the goal of this <a href="https://blog.bguarisma.com/series/portfolio-optim">series</a> of articles covering the application of MOPSO algorithm to portfolio optimization.</p>
<h2>References</h2>
<p>[1] Jorion, Philippe "Value-at-Risk", Third Edition</p>
<p>[2] <a href="https://www.youtube.com/watch?">FRM: Three approaches to value at risk (VaR)</a>, Bionic Turtle, 16 juil. 2008, YouTube.</p>
<p>[3] Alfaro Cid E. et al., « Minimizing value-at-risk in a portfolio optimization problem using a multiobjective genetic algorithm », International Journal of Risk Assessment and Management, 2011</p>
]]></content:encoded></item><item><title><![CDATA[Projet Optimisation multicritères du portefeuille (French)]]></title><description><![CDATA[Cover photo by James Wainscoat on Unsplash
Note de l'auteur: cet article a pour objectif de partager mon travail sur la conception d'un outil d'optimisation de portefeuille destiné aux utilisateurs tr]]></description><link>https://blog.bguarisma.com/projet-optimisation-multicriteres-du-portefeuille-french</link><guid isPermaLink="true">https://blog.bguarisma.com/projet-optimisation-multicriteres-du-portefeuille-french</guid><category><![CDATA[Business and Finance ]]></category><category><![CDATA[optimization]]></category><dc:creator><![CDATA[Boris Guarisma]]></dc:creator><pubDate>Thu, 03 Feb 2022 14:47:14 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1643894217776/9vPzLTTes.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Cover photo by <a href="https://unsplash.com/@tumbao1949?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText">James Wainscoat</a> on <a href="https://unsplash.com/s/photos/swarm?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText">Unsplash</a></p>
<p><strong>Note de l'auteur</strong>: cet article a pour objectif de partager mon travail sur la conception d'un outil d'optimisation de portefeuille destiné aux utilisateurs travaillant dans le domaine de la gestion de portefeuilles. L'outil est resté au stade "prototype" et n'a jamais fait partie d'aucune démarche commerciale. Il n'est jamais devenu un produit ... et avec un peu recul, je peux dire qu'il était plutôt destinés aux data scientists spécialisés dans la finance qu'aux asset managers. Par ailleurs, je ne suis pas un expert de la finance, je suis un passionné de la Swarm Intelligence. Les explications financières et scientifiques proviennent de <strong>sources dont les références bibliographiques sont indiquées à la fin de l'article</strong>.</p>
<h2>1.1. Contexte scientifique</h2>
<p>Le présent projet en Data Science propose une alternative à la méthode appliquée dans l’étude scientifique effectué par Alfaro Cid E. et al., voir référence [ALF], pour la minimisation du risque d’un portefeuille d’actifs financiers. En effet, dans [ALF] on propose une méthode de minimisation de la Value-at-Risk (valeur à risque) avec des algorithmes génétiques (GA). Dans notre étude, nous proposons une version modifiée d’un autre algorithme, également issu du domaine de la Swarm Intelligence (intelligence de l’essaim), appelé <strong>Particle Swarm Optimization (PSO)</strong> ou optimisation par essaims particulaires (OEP). </p>
<p>Nous essayons dans ce projet de démontrer que les portefeuilles (ou solutions) trouvés par PSO sont plus efficaces que ceux trouvés par GA vis-à-vis des solutions trouvées par une méthode classique de minimisation du risque. Pour ce faire, nous avons influencé le comportement standard de l’algorithme.</p>
<p>Par ailleurs, un outil permettant de générer les solutions de chaque algorithme sur une longue période, comprenant plusieurs tendances du marché, a été développé. L’outil permet ensuite d’appliquer les solutions trouvées à des scénarios correspondants à une tendance spécifique (soit une hausse soit une baisse) et ultérieure dans le temps et par conséquent, de valider l’efficacité des portefeuilles trouvés. Finalement, <em>l’unexpected loss</em> (ou perte inattendue) est calculée, c’est un indicateur essentiel qui permet de maîtriser le coût en capital exigé par les régulateurs.</p>
<p>Malgré l’existence d’outils permettant de calculer la VaR, ceux qui permettent de la minimiser sont inexistants sur l’Internet. Nous pensons que ce type de solution devrait peut-être exister dans le cercle très fermé des éditeurs logiciels financiers avec des développements customisés, cependant nous n’avons pas de connaissance à l’heure actuelle sur les méthodes et les types d’optimisation proposés par ces solutions. </p>
<p>La <strong>Value-at-Risk (VaR)</strong> est un indicateur très utilisé en gestion de risques financiers. Elle permet de mesurer un niveau de perte inattendue qui peut être dépassée à un horizon de temps donnée avec une probabilité donnée. Elle est fondamentale pour les investisseurs en bourse, les asset managers et également pour les banques et les assurances qui doivent couvrir leurs pertes inattendues par du capital (réglementation Bale, Solvency).</p>
<p>Minimiser la VaR est un « problème difficile » (terme définie dans [CLE]), en effet, la VaR est une fonction non convexe et non différentiable, par conséquent sa minimisation ne peut s’effectuer avec des méthodes classiques d’optimisation telles que la descente du gradient. Les algorithmes issus de la Swarm Intelligence s’adaptent bien à ce genre de « problèmes difficiles ».</p>
<p>L’optimisation appliquée est en réalité une optimisation multi-critère car non seulement on essaie de minimiser la VaR mais on cherche également à maximiser le rendement ou gain de portefeuilles d’actifs financiers.</p>
<h3>1.1.1.	Enjeux</h3>
<ul>
<li><p>Une minimisation judicieuse de la VaR permet de maitriser le coût en capital exigé par les régulateurs.</p>
</li>
<li><p>Des adaptations innovantes des algorithmes issus de la swarm intelligence permettent de réaliser de telles optimisations de la VaR.</p>
</li>
<li><p>Concevoir une nouvelle démarche pour les acteurs du domaine financier leur permettant d’intégrer simultanément les solutions trouvées par plusieurs méthodes (classiques et issues de la Swarm Intelligence) et d’effectuer un choix de la meilleure solution.</p>
</li>
</ul>
<h2>1.2. Etat de l’art existant et disponible au début des travaux</h2>
<h3>1.2.1.	Notion de « problème difficile »</h3>
<p>Selon [CLE], « la difficulté d’un problème d’optimisation dans un espace de recherche donnée est la probabilité de ne pas trouver une solution en choisissant une position au hasard selon une distribution uniforme. C’est donc la probabilité d’échec au premier essai. » </p>
<p>Ci-dessous trois exemples extraits d’un jeu d’essai classique pour les problèmes d’optimisation. Ce sont des fonctions possédant un espace de recherche à deux dimensions (axes x1 et x2).</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1643895699293/PIbrHA4mXI.png" alt="image.png" />
Figure 1 Deux problèmes difficiles (Tripod et Alpine) et un problème convexe (Parabole)</p>
<p>Le problème « Tripod » déroute de nombreux algorithmes qui se sont facilement piéger dans l’un ou l’autre des deux minimums locaux. Il présente des discontinuités brutales ce qui ne gêne en rien le PSO. La « Parabole » présente un seul minimum, de par son caractère stochastique, le PSO ne pourra pas être aussi efficace qu’un algorithme déterministe, tel que la descente du gradient.</p>
<p>L’objectif de ce sous-chapitre est de rappeler que la minimisation du VaR est un « problème difficile » pour lequel les algorithmes de la Swarm Intelligence sont plus adaptés que les algorithmes classiques tels que la programmation quadratique (QP).</p>
<h3>1.2.2.	Gestion de portefeuille : Le modèle de Markowitz</h3>
<p>Le modèle de Markowitz fait la double hypothèse que :</p>
<ol>
<li>   Les marchés d'actifs financiers sont efficients.</li>
</ol>
<ul>
<li>C'est l'hypothèse d'efficience du marché selon laquelle les prix et rendements des actifs sont censés refléter, de façon objective, toutes les informations disponibles concernant ces actifs.</li>
</ul>
<ol>
<li>   Les investisseurs ont de l'aversion envers le risque.</li>
</ol>
<ul>
<li><p>Ils ne seront prêts à prendre plus de risques qu'en échange d'un rendement plus élevé. </p>
</li>
<li><p>À l'inverse, un investisseur qui souhaite améliorer la rentabilité de son portefeuille doit accepter de prendre plus de risques. </p>
</li>
<li><p>L'équilibre risque/rendement jugé optimal dépend de la tolérance au risque de chaque investisseur.</p>
</li>
</ul>
<h3>1.2.3.	Rendement et Volatilité d’un Portefeuille</h3>
<p>On suppose généralement que la préférence de l'investisseur pour un couple risque / rendement peut être décrite par une fonction d'utilité quadratique. De plus, les évolutions du marché sont supposées suivre une distribution symétrique de Pareto. </p>
<p>Par conséquent, seuls le rendement attendu (l'espérance de gain) et la volatilité (l'écart type) sont les paramètres examinés par l'investisseur. Ce dernier ne tient pas compte des autres caractéristiques de la distribution des gains, comme son asymétrie ou même le niveau de fortune investi.</p>
<p>Le rendement d'un portefeuille est une combinaison linéaire de celui des actifs qui le composent, pondérés par leur poids ωi dans le portefeuille. Placé dans un univers incertain, l’investisseur ne peut pas calculer d’avance la rentabilité, car la valeur du titre en fin de période est aléatoire, ainsi que dans certain cas, la rémunération perçue durant la période. L’investisseur utilise alors, un rendement attendu qui est la moyenne des rendements possibles pondérés par leur possibilité de réalisation.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1643895888792/HiEurdWWb.png" alt="image.png" /></p>
<p>La volatilité du portefeuille (constatez le symbole grec « Sigma », ci-dessus) est une fonction de la corrélation entre les actifs qui le composent. Cette fonction n'est pas linéaire.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1643895909251/2QeRQE6sR.png" alt="image.png" /></p>
<h3>1.2.4.	Les portefeuilles Sigma-efficient</h3>
<p>Le portefeuille de variance minimale (Minimum Variance Portfolio) est le portefeuille efficient avec le risque « Volatilité » minimale, explications [CAP] :</p>
<ul>
<li><p>En supposant un grand nombre d'actifs financiers et toutes les combinaisons possibles, il est donc possible de calculer l'espérance et la variance du rendement prévisionnel d'un très grand nombre de portefeuilles. </p>
</li>
<li><p>Chaque portefeuille aura donc des caractéristiques d'espérance et de variance différentes, en fonction du choix des actifs, des pondérations et des corrélations entre les actifs. </p>
</li>
<li><p>Il est alors possible d'obtenir un graphique représentant le risque et le rendement de chaque portefeuille, et de déterminer une frontière d'efficience à partir des portefeuilles dominants/dominés.</p>
</li>
<li><p>Chaque point sur la courbe bleue à partir du point rouge "Portefeuille à variance minimale" correspond à un portefeuille efficient ; c'est ce que l'on appelle la frontière d'efficience ou frontière de Markowitz.</p>
</li>
</ul>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1643896213049/N9ELd7kw9.png" alt="image.png" /></p>
<p>Figure 3 Graphique illustrant une frontière efficiente et la solution (en rouge) représentant le portefeuille ou solution à variance minimale.</p>
<ul>
<li><p>Si un portefeuille se trouve dans la zone hachurée, il n'est pas efficient car il existe (1) un autre portefeuille apportant ce même niveau de rendement mais avec un risque plus faible ou (2) un autre portefeuille apportant un rendement supérieur pour le niveau de risque considéré. </p>
</li>
<li><p>Chaque investisseur peut ensuite choisir n'importe quel portefeuille sur le demi-courbe bleue, en fonction du niveau de risque qu'il est prêt à supporter ou bien du rendement qu'il espère (maximisation de l'utilité de l'investisseur).</p>
</li>
</ul>
<h3>1.2.5.	Les limites du modèle de Markowitz</h3>
<p>Avec les ajustements récents, ce modèle s’est trouvé plusieurs limites soulevées par plusieurs praticiens de la théorie financière. Parmi ces limites, on note :</p>
<ul>
<li><p>Le modèle suppose la rationalité des investisseurs. Or, la réalité a prouvé qu’une croyance tout à fait irrationnelle peut être vu légitime par le seul fait qu’elle soit collectivement admise par un opérateur crédible ;</p>
</li>
<li><p>Le modèle ne s’est pas intéressé à la décomposition du risque global du marché mais s’est limité à l’analyse et à l’évaluation du risque individuel ou spécifique ; d’où l’apparition d’un nouveau modèle d’évaluation des actifs financiers (MEDAF) ;</p>
</li>
<li><p>Le modèle suppose également la normalité de la distribution des rentabilités, chose qui n’est pas toujours vérifiable dans la réalité. Cette limite a été résolue par l’apparition du modèle de « Dominance stochastique » qui s’applique à tout type de distribution ;</p>
</li>
<li><p>La variance a été considérée comme une mesure simplificatrice de la fonction de la rentabilité, tandis que la « Dominance stochastique » admet une comparaison de la distribution entière ;</p>
</li>
<li><p>La variance étant une mesure non parfaite du risque, une nouvelle technique de mesure a été développée en 1993, appelé Value-At-Risk (VaR). Cette technique permet de déterminer la perte maximale probabilisée sur un portefeuille quelconque.</p>
</li>
</ul>
<h3>1.2.6.	La Value-At-Risk (Valeur à risque)</h3>
<p>La VaR est une notion utilisée généralement pour mesurer le risque de marché d'un portefeuille d'instruments financiers. Elle correspond au montant de pertes qui ne devrait être dépassé qu'avec une probabilité donnée sur un horizon temporel donné.
L'utilisation de la VaR n'est désormais plus limitée aux instruments financiers : on peut en faire un outil de gestion des risques dans tous les domaines.</p>
<p>La VaR est définie par rapport à un horizon de temps T et le seuil de confiance α (on parle par exemple de <strong>VaR 10 jours 95%</strong>). </p>
<p>La VaR T jours à une confiance α peut être définie (de manière équivalente) comme :</p>
<ul>
<li><p>La pire des pertes pouvant être constatée en T jours dans les α = 95% de cas les plus favorables</p>
</li>
<li><p>La moindre perte pouvant être constatée en T jours dans les 1-α = 5% de cas les moins favorables</p>
</li>
<li><p>Le montant au-delà duquel une perte survient en T jours avec une probabilité de 1-α = 5%</p>
</li>
</ul>
<h3>1.2.7.	La VaR et les pertes inattendues</h3>
<p>La VaR est un indicateur essentiel pour les régulateurs, en effet elle permet de maitriser le coût en capital exigé par ces derniers. </p>
<p>Basel II Operational Risk, AMA Quantitative Standards 669(b) : « <em>Les autorités de contrôle demanderont à la banque de calculer ses exigences de fonds propres réglementaires en faisant la somme de la perte attendue (EL) et de la perte inattendue (UL), à moins que la banque ne puisse démontrer qu’elle intègre adéquatement le niveau de solvabilité dans ses pratiques commerciales internes. Autrement dit, pour fonder l'exigence de fonds propres réglementaire minimale sur UL uniquement, la banque doit être en mesure de démontrer, à la satisfaction de son autorité de contrôle nationale, qu'elle a mesuré et comptabilisé son exposition à l’EL</em> ».</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1643896460151/QCqa3kmHZ.png" alt="image.png" /></p>
<h3>1.2.8.	Notre approche par rapport aux études existantes</h3>
<p>La référence principale est l’étude de l’équipe d’Alfaro Cid E. et al., « <em>Minimizing Value-at-Risk in a portfolio optimization problem using a multiobjective genetic algorithm (GA)</em> » [ALF].  Cette étude nous a servie de base pour la définition de la démarche et les objectifs à atteindre dans ce projet. Ce qui nous différencie de cette étude est l’utilisation d’un autre algorithme d’optimisation multi-critère issu également de la Swarm Intelligence, à savoir le multiobjective PSO (MOPSO). </p>
<p>L’étude de Yourdkhani S., « <em>Portfolio Management by using Value-at-Risk (VaR) - A Comparison between Particle Swarm Optimization and Genetic Algorithms</em> » [YOU], montre en effet une comparaison entre PSO et GA. Cependant, l’optimisation appliquée est monocritère (absence de maximisation de la rentabilité) et la comparaison est effectuée en termes de performances (rapidité et robustesse de l’algorithme). Ce qui nous différencie de cette étude est l’utilisation d’un algorithme d’optimisation multicritère et une comparaison en termes de l’efficacité des portefeuilles (solutions) ; nous proposons donc un comparatif lié au cas d’usage métier.</p>
<h2>1.3. Objectifs techniques visés et Performances à atteindre</h2>
<p>Nous souhaitons effectués un comparatif entre nos résultats et ceux de l’étude [ALF]. </p>
<p>Le comparatif sera basé sur l’écart d’efficacité (ou erreur de substitution), définit dans [ALF], entre les portefeuilles VaR-efficient trouvés par les algorithmes de Swarm Intelligence (NSGA II et MOPSO, voir chapitre 3) et les portefeuilles Sigma-efficient trouvés par la méthode classique (programmation quadratique.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1643896518519/SjSLe1L86.png" alt="image.png" /></p>
<p>Il y a plusieurs niveaux d’écart d’efficacité :</p>
<ul>
<li><p>Ecart supérieur à -1% (VaR-efficient moins efficace mais proche de Sigma-efficient)</p>
</li>
<li><p>Ecart supérieur à -0.5% (VaR-efficient moins efficace mais très proche de Sigma-efficient)</p>
</li>
<li><p>Ecart supérieur à 0%</p>
</li>
<li><p>Ecart supérieur à +0.5%</p>
</li>
<li><p>Ecart supérieur à +1%</p>
</li>
</ul>
<p>Nous cherchons à connaître la proportion de portefeuilles VaR-efficient qui se trouvent dans chaque écart défini ci-dessus.</p>
<p>D’autres indicateurs de performances sont utilisés : on calcule les différences (ou erreurs) entre les valeurs VaR des portefeuilles VaR-efficient et des portefeuilles Sigma-efficient :</p>
<ul>
<li><p>MSE (Mean Square Error)</p>
</li>
<li><p>MAE (Mean Absolute Error)</p>
</li>
</ul>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1643896595527/jSQoytDfw.png" alt="image.png" /></p>
<ol>
<li>   <strong>Données In-samples</strong>, voir chapitre  1.5.2.2.3; l’objectif est de montrer qu’il existe un écart d’efficacité important en faveur des solutions trouvées par les algorithmes de la Swarm Intelligence (NSGA II et MOPSO) par rapport à celles de la méthode classique (programmation quadratique, QP). Ci-dessous les résultats obtenus par l’étude [ALF] :</li>
</ol>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1643896644356/xXYcMMpSm.png" alt="image.png" />
Tableau 1 Résultats de l'équipe [ALF], comparatif NSGA-II vs QP pour les périodes à plusieurs tendances (in-samples)</p>
<ol>
<li>   <strong>Données Out-samples</strong>, voir chapitre  1.5.2.2.3 ; l’objectif est de tester les solutions trouvées dans le scénario précédents sur les valeurs d’actifs financiers correspondantes à des périodes spécifiques, de baisse ou de hausse, différentes et ultérieures à celles des données in-samples. On cherche à constater si cet écart d’efficacité en faveur des solutions trouvées par les algorithmes de la Swarm Intelligence (NSGA II et MOPSO) a pu être conservé. Ci-dessous les résultats obtenus par l’étude [ALF] :</li>
</ol>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1643896669717/OfcxcH4S7.png" alt="image.png" />
Tableau 2 Résultats de l'équipe [ALF], comparatif NSGA-II vs QP pour les périodes à tendance unique (out-sample)</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1643896692386/biwZv8aW_.png" alt="image.png" />
Figure 2 Les quatre périodes in-sample avec les période out-sample correspondantes</p>
<p>Finalement, nous comparons les écarts entre NSGA II et MOPSO en espérant montrer que la proportion des portefeuilles VaR-efficient trouvés par MOPSO par rapport à QP dans chaque niveau d’écart d’efficacité est plus importante que celle trouvée pour NSGA II par rapport à QP dans [ALF].</p>
<h3>1.3.1.	Algorithmes génétiques (AG ou GA)</h3>
<p>Les algorithmes génétiques (Genetic Algorithms) tentent d’imiter informatiquement les procédés par lesquels la sélection naturelle opère, et de les appliquer pour résoudre divers problèmes, en entreprise ou dans le cadre de la recherche [LAR].</p>
<p>Ils fournissent un cadre pour étudier les effets de facteurs inspirés de la biologie, comme la sélection d’un compagnon, la reproduction, la mutation et le recouvrement d’information génétique.</p>
<p>Dans le monde des GA, la qualité des différentes solutions est comparée et les meilleures solutions potentielles évoluent pour produire des solutions plus optimales encore.</p>
<h4>1.3.1.1.	Non-dominated Sorting Genetic Algorithm-II (NSGA II)</h4>
<p>De manière générale, les algorithmes évolutionnistes multi-objectif affectent un score à une solution selon qu’elle est dominée ou non par d’autres solutions de la population courante et éventuellement si elle domine d’autres solutions. Un exemple classique est NSGA II. Cet algorithme génétique utilise une méthode de ranking qui attribue aux <strong>solutions non-dominées</strong> (voir Annexe) de la population courante le meilleur score. Puis les solutions, qui ne sont dominées que par les solutions potentiellement efficaces, reçoivent le second meilleur score et ainsi de suite.</p>
<p>De cette manière, la population est organisée en couches où chaque couche contient des solutions non comparables entre elles</p>
<h3>1.3.2.	Algorithmes d’optimisation par essaims particulaires (OEP ou PSO)</h3>
<p>Le modèle de base de l’optimisation par essaims particulaires (Particle Swarm Optimization) est défini de façon informelle en s’inspirant des échanges d’informations entre abeilles d’une ruche. Un fois qu’un bon site a été localisé par une abeille ouvrière, il est rapidement et efficacement exploité par d’autres [CLE].</p>
<p>Chaque particule (donc abeille) combine linéairement 3 éléments pour décider de son prochain déplacement :</p>
<ul>
<li><p>Sa vitesse actuelle</p>
</li>
<li><p>La meilleure position qu’elle a trouvée jusqu’ici</p>
</li>
<li><p>La meilleure position trouvée par ses informatrices</p>
</li>
</ul>
<h4>1.3.2.1.	Multi-objective PSO (MOPSO)</h4>
<p>Le Multi-objective PSO est la version multi-critère de PSO. Comme expliqué dans 3.1.1.1, cet algorithme se base également sur la notion de « domination » entre les solutions. On appelle « archive » (applicable également à NSGA II) l’ensemble de solutions non-dominées (voir Annexe) à un moment ou itération (ou génération) donnée lors de l’exécution de l’algorithme.</p>
<p>Il existe plusieurs méthodes pour la détermination de la non-dominance :</p>
<ul>
<li><p>Le calcul de la sigma-distance, voir [MOS]. C’est la méthode applicable à ce projet.</p>
</li>
<li><p>Le calcul de la Enhanced Є-dominance, voir [HAO] et [MOS2]</p>
</li>
</ul>
<h2>1.4. Incertitudes techniques et scientifiques, verrous technologiques et problèmes à résoudre</h2>
<h3>1.4.1.	Volumétrie</h3>
<p>La volumétrie des données appliquée à ce projet est déterminée par la profondeur temporelle de la période in-sample, ici 10 ans. La volumétrie correspondante à une profondeur de 10 ans pour les 12 indices boursiers utilisés pouvait très bien être gérée sur un seul ordinateur portable avec une configuration de 16Go de mémoire vive et un processeur Intel i5.</p>
<p>Cependant, pour l’industrialisation d’une telle solution, il faut apporter un maximum de flexibilité dans la sélection du nombre d’indices et de profondeur temporelle de données lors de l’exécution des différents algorithmes d’optimisation. Par conséquent, nous avons intégré à notre solution un design de calcul distribué dans un environnement Big Data, illustré ci-dessous.
Le développement et l’implémentation du calcul distribué de l’algorithme MOPSO reste un « next step » dans la roadmap de notre outil.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1643896893644/uvM2y-6tn.png" alt="image.png" />
Figure 10 Design macro pour le calcul distribué dans un environnement Big Data</p>
<h2>1.5. Travaux effectués</h2>
<h3>1.5.1.	Projet Agile</h3>
<p>Nous considérons que la psychologie du data scientist ne doit pas se calquer sur la psychologie type d’un développeur efficace ou d’un créateur de startup. Par conséquent, la méthode Agile, ici Scrum, a été adaptée à une démarche Data Science, donc aux cycles de réflexion R&amp;D des data scientists.
L’équipe est composée d’un data scientist expérimenté, également Scrum Master, et de trois data scientists junior. Chaque data scientist est responsable d’un algorithme (NSGA-II et MOPSO).</p>
<p>L’environnement technique de développement Data Science a été défini selon les compétences de chaque data scientist en langage R (environnement RStudio) ou en langage Python (environnement Jupyter notebook). L’interface graphique permettant de visualiser et de partager les résultats du benchmark a été développée avec le langage R Shiny</p>
<h3>1.5.2.	La démarche projet</h3>
<h4>1.5.2.1.	Equipe et rôles</h4>
<p>L’équipe est composée d’un data scientist expérimenté, également Scrum Master, et de trois data scientists junior. Chaque data scientist est responsable d’un algorithme (NSGA-II et MOPSO). Un responsable Financial Services de l’entreprise a été désigné comme Product Owner du projet.</p>
<p>En plus des objectifs expliqués dans l’introduction du présent document, nous considérons que la montée en compétences des data scientists junior est primordial pour le succès de l’activité. Par conséquent, les data scientists ont été assisté par le data scientist senior afin de comprendre « la philosophie », ainsi que les avantages et les inconvénients de l’algorithme dont il ou elle avait la responsabilité.</p>
<p>Chaque data scientist était responsable du choix et de l’utilisation d’une librairie spécifique pour exécuter l’algorithme ainsi que de la documentation associée à son travail.</p>
<p>A la fin de chaque itération (sprint) le data scientist junior doit fournir au data scientist senior les résultats des différents scénarios d’optimisation effectués avec les librairies. Le data scientist senior était chargé de collecter les résultats et de les intégrer à l’outil destiné aux asset managers (développé également par le data scientist senior).</p>
<p>Après constatation des limites (manque de flexibilité) des librairies utilisées par les data scientists junor, <strong>le data scientist senior décide de prendre en charge le développement de la modification du comportement standard de l’algorithme MOPSO</strong>.</p>
<h4>1.5.2.2.	Démarche Data Science</h4>
<p>La démarche data science comprends plusieurs étapes et elle s’exécute de manière itérative, au sein d’un même sprint on peut effectuer soit une partie soit la totalité des étapes, tout dépend de la complexité du problème.</p>
<p>Les étapes de la démarche data science sont les suivantes et elles sont décrites ci-après :</p>
<ul>
<li><p>Collecte des données</p>
</li>
<li><p>Qualité de la donnée</p>
</li>
<li><p>Modélisation et optimisation</p>
</li>
</ul>
<p>Les trois étapes précédentes ont été implémentées dans un environnement de programmation en langage R (RStudio). </p>
<p>La visualisation n’est pas une étape de la démarche data science, cependant elle est nécessaire pour le partage des résultats intermédiaires obtenus. L’interface graphique permettant de visualiser et de partager les résultats a été développée avec le langage R Shiny</p>
<h5>1.5.2.2.1	Environnent Data Science</h5>
<p>o	Langage : R version 3.4.4
o	IDE : RStudio 1.1.453
o	Visualisation : RStudio Shiny
Principaux packages utilisés 
o	Collecte de données : package quantmod
o	Traitement des séries temporelles : package timeSeries
o	Calcul de la VaR, de la rentabilité et de la Frontière Efficiente avec méthode classique (programmation quadratique) : package fPortfolio</p>
<h5>1.5.2.2.2	Collecte de données</h5>
<p>Valeurs journalières ajustées (<em>adjusted daily returns</em>) de 12 indices boursiers entre 1990 et 2007 :</p>
<ol>
<li>   USA DJ Industrial (DJI)</li>
<li>   USA SP500 (GSPC)	</li>
<li>   USA Nasdaq (IXIC)</li>
<li>   Canada SPTSX (GSPTSE)</li>
<li>   UK Footsie (FTSE)</li>
<li>   France CAC 40 (FCHI)</li>
<li>   Germany DAX (GDAXI)</li>
<li>   Spain IBEX 35 (IBEX)	</li>
<li>   Holland AEX (AEX)</li>
<li>   Sweden OMX (OMX)	</li>
<li>   Japan Nikkei 225 (N225) </li>
<li>   Europe Euro Stoxx 50 (STOXX50E)</li>
</ol>
<p>Source (publique) : <strong>Yahoo ! Finance</strong>.
Collecte des données avec le package R <strong>quantmod</strong>.</p>
<h5>1.5.2.2.3	Qualité de la donnée</h5>
<p>Pour la période 1990-1993, trois indices boursiers présentaient une quantité importante de valeurs manquantes. L’équipe a décidé de supprimer toutes les « lignes » présentant des valeurs manquantes, par conséquent les valeurs des indices possédant des valeurs à ces dates ont été également supprimées afin de garder des données sans valeurs manquantes. Par conséquent les dates des valeurs de référence des 12 indices boursiers commencent à partir du mois de mars 1993 (au lieu de janvier 1990).</p>
<p><strong>Préparation des données</strong></p>
<p>Les packages R <strong>quantmod</strong> et <strong>timeSeries</strong> ont été utilisés afin d’obtenir les huit 8 datasets suivants (voir chapitre 1.3 pour rappel des objectifs des datasets) composés des valeurs hebdomadaires ajustées des 12 indices boursiers :</p>
<p><strong>In-samples</strong> (échantillons d’entrée) : périodes de 10 comprenant plusieurs tendances :</p>
<ol>
<li>   In-sample 1990-1999</li>
<li>   In-sample 1992-2001</li>
<li>   In-sample 1994-2003</li>
<li>   In-sample 1996-2005</li>
</ol>
<p><strong>Out-samples</strong> (échantillons de sortie) : période de 2 ans comprenant une seule tendance, ces données permettent de valider les portefeuilles (solutions) trouvées avec les In-samples :</p>
<ol>
<li>   Out-sample 2000-2001</li>
<li>   Out-sample 2002-2003</li>
<li>   Out-sample 2004-2005</li>
<li>   Out-sample 2006-2007</li>
</ol>
<h5>1.5.2.2.4	Modélisation et optimisation</h5>
<p>Le code écrit en langage R de l’algorithme MOPSO est une adaptation souhaitée et innovante de plusieurs spécifications PSO et MOPSO :</p>
<ul>
<li>La référence [CLE] apporte un paramétrage en termes des valeurs conseillées de la taille de l’essaim, de la confiance en soi, de la confiance en les autres et du confinement de l’espace de recherche.</li>
</ul>
<p>Paramètre MOPSO	    Valeurs conseillées
Confiance en soi (<strong>ω</strong>)	    0.689343
Confiance en les autres (<strong>Cmax</strong>)	    1.42694
Taille de l’essaim (<strong>N</strong>)	    300
Nombre de générations (<strong>EXEC</strong>)	    100</p>
<ul>
<li>La référence [MOS] apporte le pseudo-code général de la MOPSO, du calcul de la sigma-distance et le nombre de générations conseillé (ou nombre d’exécutions). Ci-dessous le pseudo-code, général de la MOPSO.</li>
</ul>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1643897326567/4Kki3F1zu.png" alt="image.png" /></p>
<p>Par conséquent, nous avons influencé le comportement standard de l’algorithme, par rapport à celui disponible dans les packages les plus connus, tels que DEAP (Python), en effectuant un développement qui intègre ce que nous considérons comme les meilleurs atouts du PSO mono-critère et multi-critère. A notre connaissance, cette approche n’a jamais été tentée.</p>
<p><strong>Fonctions à optimiser</strong></p>
<p>On cherche à maximiser la rentabilité et à minimiser la VaR du portfolio. On utilise des fonctions existantes dans le package fPortfolio. En effet, ce dernier sera utilisé pour l’optimisation des portfolios Sigma-effcient utilisant la méthode classique (programmation quadratique), ainsi le comparatif des résultats sera fiable.</p>
<ul>
<li><p>Le calcul de la rentabilité du portfolio est effectué en prenant la moyenne du résultat de la fonction fPortfolio::pfolioReturn(timeSeries, weights). </p>
</li>
<li><p>Le calcul de la VaR du portfolio est effectué avec la fonction fPortfolio::pfolioVaR(timeSeries, weights, alpha).</p>
</li>
</ul>
<p>Avec</p>
<ul>
<li><p>*timeSeries *: les valeurs hebdomadaires des indices (soit in-sample ou out-sample) sous format de series temporelles. </p>
</li>
<li><p>*weights *: c’est le portfolio (ou possible solution en question ou position d’une particule). </p>
</li>
<li><p>*alpha *: c’est la confiance correspondante à la VaR, fixée à 95%</p>
</li>
</ul>
<h3>1.5.3.	Données In-samples : Efficacité des portefeuilles VaR-efficient</h3>
<p>Les résultats expliqués dans ce chapitre permettent de montrer le bien fondé de notre démarche scientifique. En effet, avant d’appliquer nos solutions (portefeuilles trouvés par MOPSO) sur des périodes à tendance unique, on chercher à effectuer le même type de comparatif « MOPSO vs Programmation Quadratique (QP) » que celui effectué par [ALF] dans le cas de NSGA-II.</p>
<h3>1.5.4.	Procédure</h3>
<p>L’écart d’efficacité (ou erreur de substitution) définie au chapitre 1.3 a été calculé de la façon suivante :</p>
<p>A partir des portefeuilles trouvés par MOPSO, on calcule pour chaque valeur Return du couple (Return, VaR) correspondant, la VaR avec la fonction fPortfolio::<strong>efficientPortfolio</strong>(<em>timeSeries, spec, constraints</em>) où le paramètre spec inclut la définition de la valeur Return cible (avec <em>setTargetReturn</em>) égale à celle de MOPSO.</p>
<p>Ainsi on obtient pour la programmation quadratique (QP) le même nombre de portefeuilles que pour MOPSO et par conséquent, on peut effectuer une comparaison fiable des résultats.</p>
<h3>1.5.5.	Les graphes des résultats</h3>
<p>La légende suivante s’applique aux quatre figures ci-dessous :</p>
<p>A gauche : portefeuilles VaR-efficient trouvés par l’équipe [ALF] avec NSGA II vs. portefeuilles Sigma-efficient</p>
<ul>
<li><p>Graphe noire continue = VaR-efficient</p>
</li>
<li><p>Graphe noire en tiret = Sigma-efficient</p>
</li>
</ul>
<p>A droite : portefeuilles VaR-efficient trouvés avec MOPSO vs. portefeuilles Sigma-efficient</p>
<ul>
<li><p>Graphe noire = VaR-efficient</p>
</li>
<li><p>Graphe rouge = Sigma-efficient</p>
</li>
</ul>
<h3>1.5.6.	Données In-samples 1990 – 1999</h3>
<p>MOPSO a généré 12 solutions ou portefeuilles VaR-efficient.</p>
<p>La figure ci-dessous montre que l’efficacité des portefeuilles VaR-efficient trouvés par MOPSO est supérieure à celle de NSGA II, vis-a-vis des portefeuilles Sigma-efficient respectifs. </p>
<p>La proportion des portefeuilles VaR-efficient de MOPSO est de 100% pour tous les niveaux d’écart d’efficacité (ou erreur de substitution) définis dans le chapitre 1.3.</p>
<p>Les erreurs MSE, MAE et de substitution restent assez importantes : à 0.2%, 0.4% et 0.3% respectivement.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1643897669269/jab64yADg.png" alt="image.png" />
Figure 4 Résultats in-sample 1990-1999</p>
<h3>1.5.7.	Données In-samples 1992 – 2001</h3>
<p>MOPSO a généré 20 solutions ou portefeuilles VaR-efficient.
La figure ci-dessous montre que l’efficacité des portefeuilles VaR-efficient trouvés par MOPSO est supérieure à celle de NSGA II, vis-à-vis des portefeuilles Sigma-efficient respectifs. </p>
<p>La proportion des portefeuilles VaR-efficient de MOPSO est de 100% pour les 3 premiers niveaux d’écart d’efficacité (ou erreur de substitution) définis dans le chapitre 1.3 et de 45% et 20% pour les écarts supérieurs à 0.5% et à 1% respectivement.</p>
<p>Les erreurs MAE et de substitution sont assez importantes : 0.3% et 0.7% respectivement. Le MSE est à 0.096%.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1643897708602/RrKYfGObu.png" alt="image.png" />
Figure 5 Résultats in-sample 1992-2001</p>
<h3>1.5.8.	Données In-samples 1994 – 2003</h3>
<p>MOPSO a généré 23 solutions ou portefeuilles VaR-efficient.
La figure ci-dessous montre que l’efficacité des portefeuilles VaR-efficient trouvés par MOPSO est inférieure à celle de NSGA II, vis-à-vis des portefeuilles Sigma-efficient respectifs. C’est le seul scénario montrant ce cas. Une analyse est toujours en cours afin d’expliquer ce phénomène.</p>
<p>La proportion des portefeuilles VaR-efficient de MOPSO est de 100% pour les 2 premiers niveaux d’écart d’efficacité (ou erreur de substitution) définis dans le chapitre 1.3 et de 9%, 0% et 0% pour les écarts supérieurs à 0%, à 0.5% et à 1% respectivement. Le résultat est en effet très décevant.</p>
<p>Les erreurs MAE, MSE et de substitution sont malgré tout proches de 0% : 0.004%, 0.058% et -0.092%% respectivement</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1643897737782/tp9NkT7d_.png" alt="image.png" />
Figure 6 Résultats in-sample 1994-2003</p>
<h3>1.5.9.	Données In-samples 1996 – 2005</h3>
<p>MOPSO a généré 16 solutions ou portefeuilles VaR-efficient.
La figure ci-dessous montre que l’efficacité des portefeuilles VaR-efficient trouvés par MOPSO est supérieure à celle de NSGA II, vis-à-vis des portefeuilles Sigma-efficient respectifs. </p>
<p>La proportion des portefeuilles VaR-efficient de MOPSO est de 100% pour les 2 premiers niveaux d’écart d’efficacité (ou erreur de substitution) définis dans le chapitre 1.3 et de 69%, 13%, 6% pour les écarts supérieurs à 0%, à 0.5% et à 1% respectivement.</p>
<p>Les erreurs MAE, MSE et de substitution sont correctes : 0.06%, 0.17% et 0.23% %% respectivement.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1643897788567/N_QJS04XB.png" alt="image.png" />
Figure 7 Résultats in-sample 1996-2005</p>
<h3>1.5.10.	Résumé des résultats</h3>
<p>Ci-dessous le résumé des résultats obtenus pour les portefeuilles VaR-efficient trouvés avec MOPSO vis-à-vis des portefeuilles Sigma-efficient obtenus avec la programmation quadratique.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1643897817389/StHq_V654.png" alt="image.png" />
Tableau 3 Résultats in-sample "MOPSO vs QP"</p>
<p>On peut constater que la proportion des portefeuilles VaR-efficient dont l’écart est supérieur à 0% par rapport aux portefeuilles Sigma-efficient et de 100%, 100%, 9% et 69% pour chaque ensemble de données in-sample respectivement. </p>
<p>Seule l’application des portefeuilles VaR-efficient sur les données in-sample 1994-2003 ont montré des résultats décevants avec seulement 9% des solutions à &gt; 0%, ce qui est très loin d’une majorité de portefeuilles MOPSO.</p>
<h3>1.5.11.	Données Out-samples : Validation des portefeuilles VaR-efficient</h3>
<p>Ci-dessous le résumé des résultats obtenus en appliquant les portefeuilles VaR-efficient de MOPSO (solutions in-samples) aux données out-samples ou périodes à tendance unique.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1643897868097/b1CPCpvaf.png" alt="image.png" />
Tableau 4 Résultats out-sample "MOPSO vs QP</p>
<h3>1.5.12.	Fonctionnalités</h3>
<p>L’outil, développé avec RStudio Shiny, permet aux asset managers de</p>
<ul>
<li><p>Collecter et stocker avec une fréquence journalière le cours des indices boursiers à partir des sources publiques (actuellement Yahoo ! Finance). Calculer les rendements journaliers, hebdomadaires et annuels</p>
</li>
<li><p>Obtenir les portefeuilles optimisés avec des algorithmes « classiques » et ceux issus de la Swarm Intelligence (actuellement MOPSO).</p>
</li>
<li><p>Appliquer les portefeuilles sur des périodes à tendance unique et effectuer un comparatif « Swarm Intelligence (actuellement MOPSO) vs QP » avec des indicateurs d’efficacité VaR-efficiency.</p>
</li>
<li><p>Sélectionner la meilleure solution, en calculant les VaR et la perte inattendue (UL) selon le rendement désiré et comparer les résultats des différents algorithmes.</p>
</li>
</ul>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1643897973334/FuFWgo9dS.png" alt="image.png" />
Figure 8 Fonctionnalités de l'outil destiné aux asset managers</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1643898013478/LuGnXq77s.png" alt="image.png" />
Figure 9 Prises d'écran correspondantes à chaque fonctionnalité de l’outil</p>
<h2>1.6. Réussites techniques, indicateurs de R&amp;D</h2>
<h3>1.6.1.	Acquisition de connaissances</h3>
<p>Au moment de l’écriture de ce rapport, nous n’avons aucune connaissance d’outils ou de logiciels sur le marché permettant d’automatiser les tâches (1) calcul des portefeuilles avec différents algorithmes, (2) applications des solutions sur des périodes à une tendance, (3) comparatif des portefeuilles et (4) calcul de la VaR minimale et la perte inattendue (<em>unexpected loss</em>, UL).</p>
<h3>1.6.2.	Démarche réussie</h3>
<p>Nous avons réussi à appliquer le même niveau de rigueur de la démarche décrite dans la référence [ALF]. Notre innovation se situe dans l’adaptation ou customisation d’un algorithme différent de celui utilisé dans [ALF], l’algorithme MOPSO (Multi-objective Particle Swarm Intelligence). En effet, nous avons influencé le comportement standard de l’algorithme, par rapport à celui disponible dans les packages les plus connus (tels que la librairie DEAP écrite en Python). Notre adaptation se situe dans le choix des valeurs des coefficients de confiance, dans le confinement de l’espace de recherche et dans la méthode pour la détermination des solutions « non dominées ». </p>
<p>Par ailleurs, nous souhaitons aller plus loin dans cette dernière avec une customisation de la méthode Є-Dominance.</p>
<h2>1.7. Références bibliographiques</h2>
<h3>1.1.1.	Livres blancs (white papers)</h3>
<ul>
<li><p>[ALF] Alfaro Cid E. et al., « Minimizing value-at-risk in a portfolio optimization problem using a multiobjective genetic algorithm », International Journal of Risk Assessment and Management, 2011</p>
</li>
<li><p>[YOU] Yourdkhani S., « Portfolio Management by using Value-at-Risk (VaR) - A Comparison between Particle Swarm Optimization and Genetic Algorithms », Life Science Journal, 2014</p>
</li>
<li><p>[HAO] Hao J. et al., « Multi-objective Particle Swarm Optimization Algorithm based on Enhanced Є-Dominance », IEEE, 2006</p>
</li>
<li><p>[MOS] Mostaghim S., Teich J., « Strategies for Finding Local Guides in Multi-objective Particle Swarm Optimization (MOPSO) »</p>
</li>
<li><p>[MOS2] Mostaghim S., Teich J., « The rôle of Є-Dominance in Multi-objective Particle Swarm Optimization Methods »</p>
</li>
</ul>
<h3>1.1.2.	Livres</h3>
<ul>
<li><p>[CLE] Clerc M., L’optimisation par essaims particulaires, Hermès-Sciences, 2005</p>
</li>
<li><p>[LAR] Larose D., « Exploration de données », Editions Vuibert, 2006</p>
</li>
</ul>
<h3>1.1.3.	Sites web</h3>
<ul>
<li><p>[EPI] Epiquant, <a href="https://www.ephiquant.com/optimisation-de-portefeuille-modele-mean-variance-de-markowitz/">Optimisation de portefeuille : Modèle Mean – Variance de Markowitz (avec R)</a></p>
</li>
<li><p>[CAP] <a href="http://www.captaineconomics.fr/-theorie-moderne-portefeuille-markowitz-portefeuille-efficient-frontiere">Captain Econimics, La théorie moderne du portefeuille : une introduction</a></p>
</li>
</ul>
<h2>1.8. Annexes</h2>
<h3>1.1.4.	Notions de solutions non dominées</h3>
<p>Ci-dessous une explication et une illustration de solutions non dominées (carrés noirs).</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1643898305404/xG1aUw-DJ.png" alt="image.png" /></p>
<h3>1.1.5.	Théorie de rationalité avancée par Markowitz</h3>
<p>Le problème posé par Markowitz est la recherche d’un portefeuille qui minimise la variance du rendement du portefeuille pour un niveau d’espérance de rentabilité donné. </p>
<p>Ce portefeuille est dit efficace, il a l’espérance de rentabilité la plus forte parmi les portefeuilles qui ont la même variance de rentabilité que lui. </p>
<p>Ainsi, le problème de l’investisseur face à plusieurs titre serait de déterminer la proportion de ses fonds à investir dans chaque titre pour former le portefeuille efficient qui correspond au mieux à ses goûts vis-à-vis du risque.</p>
<p>L’ensemble de tous les portefeuilles efficaces constitue la frontière efficace, encore appelée frontière efficiente de Markowitz (Prix Nobel d’économie en 1990), qui la dériva en 1952 et la généralisa en 1959.</p>
<p>Markowitz est classé comme le premier modélisateur de la relation « Risque / Rentabilité ». </p>
<p>Le modèle de Markowitz devient l’outil le plus préconisé par les opérateurs et ce grâce à son opérationnalité et sa technicité.</p>
<h3>1.1.6.	Les différents types de portefeuille</h3>
<p><strong>Portefeuille efficient</strong> (efficient portfolio)</p>
<p>Pour chaque rendement, il existe un portefeuille qui minimise le risque. À l'inverse, pour chaque niveau de risque, on peut trouver un portefeuille maximisant le rendement attendu. L'ensemble de ces portefeuilles est appelé frontière efficiente ou frontière de Markowitz.</p>
<p><strong>Portefeuille à variance minimale</strong> (Global minimum risk or Minimum Variance Portfolio)
C’est le portefeuille efficient avec le risque minimal.</p>
<p><strong>Portefeuille à rentabilité maximale</strong> (Maximum Return Portfolio)</p>
<p>C’est le portefeuille qui pour un risqué donné (cible), possède la rentabilité maximale.</p>
<p><strong>Portefeuille tangent ou de risque minimal</strong> (Minumum Risk or Tangency Portfolio)</p>
<p>Dans un univers comprenant des actifs risqués et un actif sans risque, le portefeuille tangent est le seul portefeuille risqué efficient puisqu'il est celui dont le ratio de Sharpe est le plus élevé. C’est le portefeuille efficient avec le ratio rendement/risque (ou ratio de Sharpe) le plus élevé.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1643898391803/Vf-pS8Bts.png" alt="image.png" /></p>
<p><strong>Autres définitions</strong></p>
<p><strong>L'actif sans risque</strong> est un actif théorique qui rapporte le taux d'intérêt sans risque. Il est en général associé aux emprunts d'État à court terme. Cet actif possède une variance nulle, son rendement est donc connu à l'avance. Il n'est pas corrélé avec les autres actifs. Par conséquent, associé à un autre actif, il modifie linéairement l'espérance de rendement et la variance.</p>
<p>Le portefeuille devient donc:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1643898427588/irZ-ghFx6.png" alt="image.png" /></p>
<p>En conséquence, l'espérance de rentabilité est constituée de l'actif sans risque augmenté d'une prime de risque.</p>
<p><strong>Droite de marché des capitaux (Capital Market Line)</strong></p>
<p>Elle représente la rentabilité attendue en ordonné et le risque en abscisse de l'ensemble des titres présents sur le marché. Si un titre se situe au-dessus de cette droite, il est sous-évalué. En effet, cela signifie qu'il rapporte plus que ce qui est attendu à un risque donné, donc investir !</p>
<p>L'intersection avec la droite des ordonnées représente le taux de rentabilité attendu sur les marchés pour un risque nul.</p>
]]></content:encoded></item><item><title><![CDATA[Time Series Forecasting Lab (Part 6) - Stacked Ensembles]]></title><description><![CDATA[Cover photo by Markus Spiske on Unsplash
Go to R-bloggers for R news and tutorials contributed by hundreds of R bloggers.
Introduction
This is the sixth of a series of 6 articles about time series forecasting with panel data and ensemble stacking wit...]]></description><link>https://blog.bguarisma.com/time-series-forecasting-lab-part-6-stacked-ensembles</link><guid isPermaLink="true">https://blog.bguarisma.com/time-series-forecasting-lab-part-6-stacked-ensembles</guid><category><![CDATA[R Language]]></category><category><![CDATA[Machine Learning]]></category><dc:creator><![CDATA[Boris Guarisma]]></dc:creator><pubDate>Mon, 17 Jan 2022 22:25:46 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1642170345635/lDuYbOgBb.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Cover photo by <a href="https://unsplash.com/@markusspiske?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText">Markus Spiske</a> on <a href="https://unsplash.com/s/photos/stacking?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText">Unsplash</a></p>
<p>Go to <a target="_blank" href="https://www.r-bloggers.com/">R-bloggers</a> for R news and tutorials contributed by hundreds of R bloggers.</p>
<h2 id="heading-introduction">Introduction</h2>
<p>This is the sixth of a series of 6 articles about time series forecasting with panel data and ensemble stacking with R.</p>
<p>Through these articles I will be putting into practice what I have learned from the Business Science University training course  <a target="_blank" href="https://university.business-science.io/courses">1</a> DS4B 203-R: High-Performance Time Series Forecasting", delivered by Matt Dancho. If you are looking to gain a high level of expertise in time series with R I strongly recommend this course.  </p>
<p>The objective of this article is to learn how to build multi-level stacking ensembles with modeltime. In the figure below we start from the bottom by reminding us of each <em>individual model or submodel</em> performance from <a target="_blank" href="https://blog.bguarisma.com/time-series-forecasting-and-ensemble-stacking-part-4">Part 4</a> (non-tuned models) and Part 5 (tuned models). Then we will define <em>meta learners</em> which learn <strong>from k-fold cross-validation predictions</strong> from all submodels. We can compare meta-learners' performance and select best meta-learners to be used for defining a weighted average ensemble to produce a final multi-level stacking model.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1642457993590/DxzoAV9aJ.png" alt="p6_stacked_schema1.png" /></p>
<h3 id="heading-prerequisites">Prerequisites</h3>
<p>I assume you are already familiar with the following topics, packages and terms:</p>
<ul>
<li><p>dplyr or tidyverse R packages</p>
</li>
<li><p>k-fold cross-validation</p>
</li>
<li><p>hyperparameter tuning from  <a target="_blank" href="https://blog.bguarisma.com/time-series-forecasting-and-ensemble-stacking-part-4">Part 4</a> </p>
</li>
<li><p>average and weighted ensembles from  <a target="_blank" href="https://blog.bguarisma.com/time-series-forecasting-and-ensemble-stacking-part-5">Part 5</a></p>
</li>
</ul>
<h2 id="heading-packages">Packages</h2>
<p>The following packages must be loaded:</p>
<pre><code><span class="hljs-comment"># 01 FEATURE ENGINEERING</span>
<span class="hljs-keyword">library</span>(tidyverse)  <span class="hljs-comment"># loading dplyr, tibble, ggplot2, .. dependencies</span>
<span class="hljs-keyword">library</span>(timetk)  <span class="hljs-comment"># using timetk plotting, diagnostics and augment operations</span>
<span class="hljs-keyword">library</span>(tsibble)  <span class="hljs-comment"># for month to Date conversion</span>
<span class="hljs-keyword">library</span>(tsibbledata)  <span class="hljs-comment"># for aus_retail dataset</span>
<span class="hljs-keyword">library</span>(fastDummies)  <span class="hljs-comment"># for dummyfying categorical variables</span>
<span class="hljs-keyword">library</span>(skimr) <span class="hljs-comment"># for quick statistics</span>

<span class="hljs-comment"># 02 FEATURE ENGINEERING WITH RECIPES</span>
<span class="hljs-keyword">library</span>(tidymodels) <span class="hljs-comment"># with workflow dependency</span>

<span class="hljs-comment"># 03 MACHINE LEARNING</span>
<span class="hljs-keyword">library</span>(modeltime) <span class="hljs-comment"># ML models specifications and engines</span>
<span class="hljs-keyword">library</span>(tictoc) <span class="hljs-comment"># measure training elapsed time</span>

<span class="hljs-comment"># 04 HYPERPARAMETER TUNING</span>
<span class="hljs-keyword">library</span>(future)
<span class="hljs-keyword">library</span>(doFuture)
<span class="hljs-keyword">library</span>(plotly)

<span class="hljs-comment"># 05 ENSEMBLES</span>
<span class="hljs-keyword">library</span>(modeltime.ensemble)
</code></pre><h2 id="heading-reminder-of-individual-models">Reminder of individual models</h2>
<p>Leu us remind us of the performance of each individual model or submodel. We do not consider the ensemble models from <a target="_blank" href="https://blog.bguarisma.com/time-series-forecasting-lab-part-5-ensembles">Part 5.</a></p>
<pre><code><span class="hljs-comment"># Load all calibration tables (tuned &amp; non-tuned models)</span>
<span class="hljs-string">&gt;</span> <span class="hljs-string">calibration_tbl</span> <span class="hljs-string">&lt;-</span> <span class="hljs-string">read_rds("workflows_NonandTuned_artifacts_list.rds")</span>
<span class="hljs-string">&gt;</span> <span class="hljs-string">calibration_tbl</span> <span class="hljs-string">&lt;-</span> <span class="hljs-string">calibration_tbl$calibration</span>
<span class="hljs-string">&gt;</span> <span class="hljs-string">calibration_tbl</span> <span class="hljs-string">%&gt;%</span> 
   <span class="hljs-string">modeltime_accuracy()</span> <span class="hljs-string">%&gt;%</span>
   <span class="hljs-string">arrange(rmse)</span>

<span class="hljs-comment"># A tibble: 8 x 9</span>
  <span class="hljs-string">.model_id</span> <span class="hljs-string">.model_desc</span>                       <span class="hljs-string">.type</span>   <span class="hljs-string">mae</span>  <span class="hljs-string">mape</span>  <span class="hljs-string">mase</span> <span class="hljs-string">smape</span>  <span class="hljs-string">rmse</span>   <span class="hljs-string">rsq</span>
      <span class="hljs-string">&lt;int&gt;</span> <span class="hljs-string">&lt;chr&gt;</span>                             <span class="hljs-string">&lt;chr&gt;</span> <span class="hljs-string">&lt;dbl&gt;</span> <span class="hljs-string">&lt;dbl&gt;</span> <span class="hljs-string">&lt;dbl&gt;</span> <span class="hljs-string">&lt;dbl&gt;</span> <span class="hljs-string">&lt;dbl&gt;</span> <span class="hljs-string">&lt;dbl&gt;</span>
<span class="hljs-number">1</span>         <span class="hljs-number">7</span> <span class="hljs-string">PROPHET</span> <span class="hljs-string">W/</span> <span class="hljs-string">REGRESSORS</span>             <span class="hljs-string">Test</span>  <span class="hljs-number">0.189</span>  <span class="hljs-number">21.6</span> <span class="hljs-number">0.417</span>  <span class="hljs-number">21.5</span> <span class="hljs-number">0.240</span> <span class="hljs-number">0.856</span>
<span class="hljs-number">2</span>         <span class="hljs-number">3</span> <span class="hljs-string">PROPHET</span> <span class="hljs-string">W/</span> <span class="hljs-string">REGRESSORS</span> <span class="hljs-bullet">-</span> <span class="hljs-string">Tuned</span>     <span class="hljs-string">Test</span>  <span class="hljs-number">0.189</span>  <span class="hljs-number">21.6</span> <span class="hljs-number">0.418</span>  <span class="hljs-number">21.5</span> <span class="hljs-number">0.241</span> <span class="hljs-number">0.857</span>
<span class="hljs-number">3</span>         <span class="hljs-number">4</span> <span class="hljs-string">PROPHET</span> <span class="hljs-string">W/</span> <span class="hljs-string">XGBOOST</span> <span class="hljs-string">ERRORS</span> <span class="hljs-bullet">-</span> <span class="hljs-string">Tuned</span> <span class="hljs-string">Test</span>  <span class="hljs-number">0.191</span>  <span class="hljs-number">25.7</span> <span class="hljs-number">0.421</span>  <span class="hljs-number">19.9</span> <span class="hljs-number">0.253</span> <span class="hljs-number">0.780</span>
<span class="hljs-number">4</span>         <span class="hljs-number">8</span> <span class="hljs-string">PROPHET</span> <span class="hljs-string">W/</span> <span class="hljs-string">XGBOOST</span> <span class="hljs-string">ERRORS</span>         <span class="hljs-string">Test</span>  <span class="hljs-number">0.204</span>  <span class="hljs-number">24.8</span> <span class="hljs-number">0.451</span>  <span class="hljs-number">20.8</span> <span class="hljs-number">0.287</span> <span class="hljs-number">0.753</span>
<span class="hljs-number">5</span>         <span class="hljs-number">2</span> <span class="hljs-string">XGBOOST</span> <span class="hljs-bullet">-</span> <span class="hljs-string">Tuned</span>                   <span class="hljs-string">Test</span>  <span class="hljs-number">0.219</span>  <span class="hljs-number">23.6</span> <span class="hljs-number">0.482</span>  <span class="hljs-number">22.9</span> <span class="hljs-number">0.296</span> <span class="hljs-number">0.762</span>
<span class="hljs-number">6</span>         <span class="hljs-number">6</span> <span class="hljs-string">XGBOOST</span>                           <span class="hljs-string">Test</span>  <span class="hljs-number">0.216</span>  <span class="hljs-number">25.0</span> <span class="hljs-number">0.476</span>  <span class="hljs-number">22.4</span> <span class="hljs-number">0.299</span> <span class="hljs-number">0.747</span>
<span class="hljs-number">7</span>         <span class="hljs-number">1</span> <span class="hljs-string">RANGER</span> <span class="hljs-bullet">-</span> <span class="hljs-string">Tuned</span>                    <span class="hljs-string">Test</span>  <span class="hljs-number">0.231</span>  <span class="hljs-number">26.1</span> <span class="hljs-number">0.509</span>  <span class="hljs-number">24.6</span> <span class="hljs-number">0.303</span> <span class="hljs-number">0.766</span>
<span class="hljs-number">8</span>         <span class="hljs-number">5</span> <span class="hljs-string">RANGER</span>                            <span class="hljs-string">Test</span>  <span class="hljs-number">0.235</span>  <span class="hljs-number">25.4</span> <span class="hljs-number">0.519</span>  <span class="hljs-number">25.0</span> <span class="hljs-number">0.312</span> <span class="hljs-number">0.765</span>
</code></pre><h2 id="heading-stacking-algorithms">Stacking Algorithms</h2>
<p>A stacking algorithm or <strong>meta-learner</strong> learns from predictions. The predictions come from k-fold cross-validations applied to each submodel. Once fed into the meta-learner tunable specification a hyperpameter tuning will be peformed for the meta-learner.</p>
<h3 id="heading-generate-predictions-for-meta-learners">Generate predictions for meta-learners</h3>
<p>Remember, for non-sequential models you must perform a k-fold cross-validation and not a time series cross-validation. Let us get resampeld predictions with <code>modeltime_fit_resamples()</code>. Here, <strong>k=10 folds</strong>.</p>
<pre><code>set.seed(<span class="hljs-number">123</span>)
resamples_kfold <span class="hljs-operator">&lt;</span><span class="hljs-operator">-</span> training(splits) <span class="hljs-operator">%</span><span class="hljs-operator">&gt;</span><span class="hljs-operator">%</span>
  drop_na() <span class="hljs-operator">%</span><span class="hljs-operator">&gt;</span><span class="hljs-operator">%</span>
  vfold_cv(v <span class="hljs-operator">=</span> <span class="hljs-number">10</span>)

tic()
submodels_resamples_kfold_tbl <span class="hljs-operator">&lt;</span><span class="hljs-operator">-</span> calibration_tbl <span class="hljs-operator">%</span><span class="hljs-operator">&gt;</span><span class="hljs-operator">%</span>
  modeltime_fit_resamples(
    resamples <span class="hljs-operator">=</span> resamples_kfold,
    control   <span class="hljs-operator">=</span> control_resamples(
      verbose    <span class="hljs-operator">=</span> TRUE, 
      allow_par  <span class="hljs-operator">=</span> TRUE,
    )
  )
toc()
</code></pre><h3 id="heading-setup-parallel-processing">Setup parallel processing</h3>
<p>Parallel processing is set and toggled on by running the following commands:</p>
<pre><code><span class="hljs-comment"># Parallel Processing ----</span>
registerDoFuture()
n_cores &lt;- parallel::detectCores()

plan(
  strategy = cluster,
  workers  = parallel::makeCluster(n_cores)
)
</code></pre><h3 id="heading-meta-learners-specification">Meta-learners specification</h3>
<p>In this article we will use 3 meta-learners: <em>Random Forest</em>, <em>XGBoost</em>, and <em>SVM</em>.</p>
<p>Create a stacking algorithm specification with <code>ensemble_model_spec()</code>. Within the meta-learner specification we must provide the <em>tunable parameters</em>, the <em>number of folds</em>, and the <em>size of the grid</em>. We will also set grid search control parameters by <em>activating verbose</em> and <em>allowing parallel processing</em>.</p>
<p>For all meta-learners specifications we will define a <strong>k=10 folds</strong> and a <strong>grid size of 20</strong>. </p>
<h3 id="heading-random-forest-stacking-algorithm">Random Forest stacking algorithm</h3>
<p>We perform tuning on 2 parameters: <code>mtry</code> and <code>min_n</code>.</p>
<p>We notice that the <em>in-sample RMSE</em> for the meta-learner is 0.125. Its performance on test data has <strong>RMSE = 0.230</strong> and <strong>RSQ = 0.817</strong>.</p>
<pre><code><span class="hljs-string">tic()</span>
<span class="hljs-string">set.seed(123)</span>
<span class="hljs-string">ensemble_fit_ranger_kfold</span> <span class="hljs-string">&lt;-</span> <span class="hljs-string">submodels_resamples_kfold_tbl</span> <span class="hljs-string">%&gt;%</span>
  <span class="hljs-string">ensemble_model_spec(</span>
    <span class="hljs-string">model_spec</span> <span class="hljs-string">=</span> <span class="hljs-string">rand_forest(</span>
      <span class="hljs-string">trees</span> <span class="hljs-string">=</span> <span class="hljs-string">tune(),</span>
      <span class="hljs-string">min_n</span> <span class="hljs-string">=</span> <span class="hljs-string">tune()</span>
    <span class="hljs-string">)</span> <span class="hljs-string">%&gt;%</span>
      <span class="hljs-string">set_engine("ranger"),</span>
    <span class="hljs-string">kfolds</span>  <span class="hljs-string">=</span> <span class="hljs-number">10</span><span class="hljs-string">,</span> 
    <span class="hljs-string">grid</span>    <span class="hljs-string">=</span> <span class="hljs-number">20</span><span class="hljs-string">,</span>
    <span class="hljs-string">control</span> <span class="hljs-string">=</span> <span class="hljs-string">control_grid(verbose</span> <span class="hljs-string">=</span> <span class="hljs-literal">TRUE</span><span class="hljs-string">,</span> 
                           <span class="hljs-string">allow_par</span> <span class="hljs-string">=</span> <span class="hljs-literal">TRUE</span><span class="hljs-string">)</span>
  <span class="hljs-string">)</span>
<span class="hljs-string">toc()</span>

<span class="hljs-attr">i Model Parameters:</span>
<span class="hljs-comment"># A tibble: 1 x 8</span>
  <span class="hljs-string">trees</span> <span class="hljs-string">min_n</span> <span class="hljs-string">.metric</span> <span class="hljs-string">.estimator</span>  <span class="hljs-string">mean</span>     <span class="hljs-string">n</span> <span class="hljs-string">std_err</span> <span class="hljs-string">.config</span>              
  <span class="hljs-string">&lt;int&gt;</span> <span class="hljs-string">&lt;int&gt;</span> <span class="hljs-string">&lt;chr&gt;</span>   <span class="hljs-string">&lt;chr&gt;</span>      <span class="hljs-string">&lt;dbl&gt;</span> <span class="hljs-string">&lt;int&gt;</span>   <span class="hljs-string">&lt;dbl&gt;</span> <span class="hljs-string">&lt;chr&gt;</span>                
<span class="hljs-number">1</span>  <span class="hljs-number">1906    </span><span class="hljs-number">32</span> <span class="hljs-string">rmse</span>    <span class="hljs-string">standard</span>   <span class="hljs-number">0.176</span>    <span class="hljs-number">10</span> <span class="hljs-number">0.00225</span> <span class="hljs-string">Preprocessor1_Model02</span>

<span class="hljs-attr">i Prediction Error Comparison:</span>
<span class="hljs-comment"># A tibble: 9 x 3</span>
  <span class="hljs-string">.model_id</span>  <span class="hljs-string">rmse</span> <span class="hljs-string">.model_desc</span>                      
  <span class="hljs-string">&lt;chr&gt;</span>     <span class="hljs-string">&lt;dbl&gt;</span> <span class="hljs-string">&lt;chr&gt;</span>                            
<span class="hljs-number">1</span> <span class="hljs-number">1</span>         <span class="hljs-number">0.196</span> <span class="hljs-string">RANGER</span> <span class="hljs-bullet">-</span> <span class="hljs-string">Tuned</span>                   
<span class="hljs-number">2</span> <span class="hljs-number">2</span>         <span class="hljs-number">0.183</span> <span class="hljs-string">XGBOOST</span> <span class="hljs-bullet">-</span> <span class="hljs-string">Tuned</span>                  
<span class="hljs-number">3</span> <span class="hljs-number">3</span>         <span class="hljs-number">0.207</span> <span class="hljs-string">PROPHET</span> <span class="hljs-string">W/</span> <span class="hljs-string">REGRESSORS</span> <span class="hljs-bullet">-</span> <span class="hljs-string">Tuned</span>    
<span class="hljs-number">4</span> <span class="hljs-number">4</span>         <span class="hljs-number">0.179</span> <span class="hljs-string">PROPHET</span> <span class="hljs-string">W/</span> <span class="hljs-string">XGBOOST</span> <span class="hljs-string">ERRORS</span> <span class="hljs-bullet">-</span> <span class="hljs-string">Tuned</span>
<span class="hljs-number">5</span> <span class="hljs-number">5</span>         <span class="hljs-number">0.203</span> <span class="hljs-string">RANGER</span>                           
<span class="hljs-number">6</span> <span class="hljs-number">6</span>         <span class="hljs-number">0.203</span> <span class="hljs-string">XGBOOST</span>                          
<span class="hljs-number">7</span> <span class="hljs-number">7</span>         <span class="hljs-number">0.208</span> <span class="hljs-string">PROPHET</span> <span class="hljs-string">W/</span> <span class="hljs-string">REGRESSORS</span>            
<span class="hljs-number">8</span> <span class="hljs-number">8</span>         <span class="hljs-number">0.207</span> <span class="hljs-string">PROPHET</span> <span class="hljs-string">W/</span> <span class="hljs-string">XGBOOST</span> <span class="hljs-string">ERRORS</span>        
<span class="hljs-number">9</span> <span class="hljs-string">ensemble</span>  <span class="hljs-number">0.125</span> <span class="hljs-string">ENSEMBLE</span> <span class="hljs-string">(MODEL</span> <span class="hljs-string">SPEC)</span>           

<span class="hljs-string">modeltime_table(</span>
  <span class="hljs-string">ensemble_fit_xgboost_kfold</span>
<span class="hljs-string">)</span> <span class="hljs-string">%&gt;%</span>
  <span class="hljs-string">modeltime_accuracy(testing(splits))</span>

<span class="hljs-comment"># A tibble: 1 x 9</span>
  <span class="hljs-string">.model_id</span> <span class="hljs-string">.model_desc</span>                       <span class="hljs-string">.type</span>   <span class="hljs-string">mae</span>  <span class="hljs-string">mape</span>  <span class="hljs-string">mase</span> <span class="hljs-string">smape</span>  <span class="hljs-string">rmse</span>   <span class="hljs-string">rsq</span>
      <span class="hljs-string">&lt;int&gt;</span> <span class="hljs-string">&lt;chr&gt;</span>                             <span class="hljs-string">&lt;chr&gt;</span> <span class="hljs-string">&lt;dbl&gt;</span> <span class="hljs-string">&lt;dbl&gt;</span> <span class="hljs-string">&lt;dbl&gt;</span> <span class="hljs-string">&lt;dbl&gt;</span> <span class="hljs-string">&lt;dbl&gt;</span> <span class="hljs-string">&lt;dbl&gt;</span>
<span class="hljs-number">1</span>         <span class="hljs-number">1</span> <span class="hljs-string">ENSEMBLE</span> <span class="hljs-string">(RANGER</span> <span class="hljs-string">STACK):</span> <span class="hljs-number">8</span> <span class="hljs-string">MODELS</span> <span class="hljs-string">Test</span>  <span class="hljs-number">0.166</span>  <span class="hljs-number">21.8</span> <span class="hljs-number">0.367</span>  <span class="hljs-number">18.2</span> <span class="hljs-number">0.230</span> <span class="hljs-number">0.817</span>
</code></pre><h3 id="heading-xgboost-stacking-algorithm">XGBoost stacking algorithm</h3>
<p>We perform tuning on 4 parameters: <code>trees</code>, <code>tree_depth</code>, <code>learn_rate</code>, <code>loss_reduction</code>. </p>
<p>We notice that the <em>in-sample RMSE</em> for the meta-learner is 0.0957. Its performance on test data <strong>RMSE = 0.251</strong> and <strong>RSQ = 0.769</strong>.</p>
<pre><code><span class="hljs-string">tic()</span>
<span class="hljs-string">set.seed(123)</span>
<span class="hljs-string">ensemble_fit_xgboost_kfold</span> <span class="hljs-string">&lt;-</span> <span class="hljs-string">submodels_resamples_kfold_tbl</span> <span class="hljs-string">%&gt;%</span>
  <span class="hljs-string">ensemble_model_spec(</span>
    <span class="hljs-string">model_spec</span> <span class="hljs-string">=</span> <span class="hljs-string">boost_tree(</span>
      <span class="hljs-string">trees</span>          <span class="hljs-string">=</span> <span class="hljs-string">tune(),</span>
      <span class="hljs-string">tree_depth</span>     <span class="hljs-string">=</span> <span class="hljs-string">tune(),</span>
      <span class="hljs-string">learn_rate</span>     <span class="hljs-string">=</span> <span class="hljs-string">tune(),</span>
      <span class="hljs-string">loss_reduction</span> <span class="hljs-string">=</span> <span class="hljs-string">tune()</span>
    <span class="hljs-string">)</span> <span class="hljs-string">%&gt;%</span>
      <span class="hljs-string">set_engine("xgboost"),</span>
    <span class="hljs-string">kfolds</span> <span class="hljs-string">=</span> <span class="hljs-number">10</span><span class="hljs-string">,</span> 
    <span class="hljs-string">grid</span>   <span class="hljs-string">=</span> <span class="hljs-number">20</span><span class="hljs-string">,</span> 
    <span class="hljs-string">control</span> <span class="hljs-string">=</span> <span class="hljs-string">control_grid(verbose</span> <span class="hljs-string">=</span> <span class="hljs-literal">TRUE</span><span class="hljs-string">,</span> 
                           <span class="hljs-string">allow_par</span> <span class="hljs-string">=</span> <span class="hljs-literal">TRUE</span><span class="hljs-string">)</span>
  <span class="hljs-string">)</span>
<span class="hljs-string">toc()</span>

<span class="hljs-comment"># A tibble: 1 x 10</span>
  <span class="hljs-string">trees</span> <span class="hljs-string">tree_depth</span> <span class="hljs-string">learn_rate</span> <span class="hljs-string">loss_reduction</span> <span class="hljs-string">.metric</span> <span class="hljs-string">.estimator</span>  <span class="hljs-string">mean</span>     <span class="hljs-string">n</span> <span class="hljs-string">std_err</span> <span class="hljs-string">.config</span>              
  <span class="hljs-string">&lt;int&gt;</span>      <span class="hljs-string">&lt;int&gt;</span>      <span class="hljs-string">&lt;dbl&gt;</span>          <span class="hljs-string">&lt;dbl&gt;</span> <span class="hljs-string">&lt;chr&gt;</span>   <span class="hljs-string">&lt;chr&gt;</span>      <span class="hljs-string">&lt;dbl&gt;</span> <span class="hljs-string">&lt;int&gt;</span>   <span class="hljs-string">&lt;dbl&gt;</span> <span class="hljs-string">&lt;chr&gt;</span>                
<span class="hljs-number">1</span>  <span class="hljs-number">1717          </span><span class="hljs-number">8</span>     <span class="hljs-number">0.0330</span>        <span class="hljs-number">0.00876</span> <span class="hljs-string">rmse</span>    <span class="hljs-string">standard</span>   <span class="hljs-number">0.180</span>    <span class="hljs-number">10</span> <span class="hljs-number">0.00175</span> <span class="hljs-string">Preprocessor1_Model10</span>

<span class="hljs-attr">i Prediction Error Comparison:</span>
<span class="hljs-comment"># A tibble: 9 x 3</span>
  <span class="hljs-string">.model_id</span>   <span class="hljs-string">rmse</span> <span class="hljs-string">.model_desc</span>                      
  <span class="hljs-string">&lt;chr&gt;</span>      <span class="hljs-string">&lt;dbl&gt;</span> <span class="hljs-string">&lt;chr&gt;</span>                            
<span class="hljs-number">1</span> <span class="hljs-number">1</span>         <span class="hljs-number">0.196</span>  <span class="hljs-string">RANGER</span> <span class="hljs-bullet">-</span> <span class="hljs-string">Tuned</span>                   
<span class="hljs-number">2</span> <span class="hljs-number">2</span>         <span class="hljs-number">0.183</span>  <span class="hljs-string">XGBOOST</span> <span class="hljs-bullet">-</span> <span class="hljs-string">Tuned</span>                  
<span class="hljs-number">3</span> <span class="hljs-number">3</span>         <span class="hljs-number">0.207</span>  <span class="hljs-string">PROPHET</span> <span class="hljs-string">W/</span> <span class="hljs-string">REGRESSORS</span> <span class="hljs-bullet">-</span> <span class="hljs-string">Tuned</span>    
<span class="hljs-number">4</span> <span class="hljs-number">4</span>         <span class="hljs-number">0.179</span>  <span class="hljs-string">PROPHET</span> <span class="hljs-string">W/</span> <span class="hljs-string">XGBOOST</span> <span class="hljs-string">ERRORS</span> <span class="hljs-bullet">-</span> <span class="hljs-string">Tuned</span>
<span class="hljs-number">5</span> <span class="hljs-number">5</span>         <span class="hljs-number">0.203</span>  <span class="hljs-string">RANGER</span>                           
<span class="hljs-number">6</span> <span class="hljs-number">6</span>         <span class="hljs-number">0.203</span>  <span class="hljs-string">XGBOOST</span>                          
<span class="hljs-number">7</span> <span class="hljs-number">7</span>         <span class="hljs-number">0.208</span>  <span class="hljs-string">PROPHET</span> <span class="hljs-string">W/</span> <span class="hljs-string">REGRESSORS</span>            
<span class="hljs-number">8</span> <span class="hljs-number">8</span>         <span class="hljs-number">0.207</span>  <span class="hljs-string">PROPHET</span> <span class="hljs-string">W/</span> <span class="hljs-string">XGBOOST</span> <span class="hljs-string">ERRORS</span>        
<span class="hljs-number">9</span> <span class="hljs-string">ensemble</span>  <span class="hljs-number">0.0957</span> <span class="hljs-string">ENSEMBLE</span> <span class="hljs-string">(MODEL</span> <span class="hljs-string">SPEC)</span>  

<span class="hljs-string">&gt;</span> <span class="hljs-string">modeltime_table(</span>
   <span class="hljs-string">ensemble_fit_xgboost_kfold</span>
 <span class="hljs-string">)</span> <span class="hljs-string">%&gt;%</span>
   <span class="hljs-string">modeltime_accuracy(testing(splits))</span>

<span class="hljs-comment"># A tibble: 1 x 9</span>
  <span class="hljs-string">.model_id</span> <span class="hljs-string">.model_desc</span>                        <span class="hljs-string">.type</span>   <span class="hljs-string">mae</span>  <span class="hljs-string">mape</span>  <span class="hljs-string">mase</span> <span class="hljs-string">smape</span>  <span class="hljs-string">rmse</span>   <span class="hljs-string">rsq</span>
      <span class="hljs-string">&lt;int&gt;</span> <span class="hljs-string">&lt;chr&gt;</span>                              <span class="hljs-string">&lt;chr&gt;</span> <span class="hljs-string">&lt;dbl&gt;</span> <span class="hljs-string">&lt;dbl&gt;</span> <span class="hljs-string">&lt;dbl&gt;</span> <span class="hljs-string">&lt;dbl&gt;</span> <span class="hljs-string">&lt;dbl&gt;</span> <span class="hljs-string">&lt;dbl&gt;</span>
<span class="hljs-number">1</span>         <span class="hljs-number">1</span> <span class="hljs-string">ENSEMBLE</span> <span class="hljs-string">(XGBOOST</span> <span class="hljs-string">STACK):</span> <span class="hljs-number">8</span> <span class="hljs-string">MODELS</span> <span class="hljs-string">Test</span>  <span class="hljs-number">0.182</span>  <span class="hljs-number">23.9</span> <span class="hljs-number">0.401</span>  <span class="hljs-number">19.7</span> <span class="hljs-number">0.251</span> <span class="hljs-number">0.769</span>
</code></pre><h3 id="heading-svm-stacking-algorithm">SVM stacking algorithm</h3>
<p>We perform tuning on 3 parameters: <code>cost</code>, <code>rbf_sigma</code>, <code>margin</code>.  Run <code>?svm_rbf</code> for explanations.</p>
<p>We notice that the <em>in-sample RMSE</em> for the meta-learner is 0.172. Its performance on test data <strong>RMSE = 0.219</strong> and <strong>RSQ = 0.823</strong>.</p>
<pre><code><span class="hljs-string">tic()</span>
<span class="hljs-string">set.seed(123)</span>
<span class="hljs-string">ensemble_fit_svm_kfold</span> <span class="hljs-string">&lt;-</span> <span class="hljs-string">submodels_resamples_kfold_tbl</span> <span class="hljs-string">%&gt;%</span>
  <span class="hljs-string">ensemble_model_spec(</span>
    <span class="hljs-string">model_spec</span> <span class="hljs-string">=</span> <span class="hljs-string">svm_rbf(</span>
      <span class="hljs-string">mode</span>      <span class="hljs-string">=</span> <span class="hljs-string">"regression"</span><span class="hljs-string">,</span>
      <span class="hljs-string">cost</span>      <span class="hljs-string">=</span> <span class="hljs-string">tune(),</span>
      <span class="hljs-string">rbf_sigma</span> <span class="hljs-string">=</span> <span class="hljs-string">tune(),</span>  
      <span class="hljs-string">margin</span>    <span class="hljs-string">=</span> <span class="hljs-string">tune()</span>
    <span class="hljs-string">)</span> <span class="hljs-string">%&gt;%</span>
      <span class="hljs-string">set_engine("kernlab"),</span>
    <span class="hljs-string">kfold</span> <span class="hljs-string">=</span> <span class="hljs-number">10</span><span class="hljs-string">,</span> 
    <span class="hljs-string">grid</span>  <span class="hljs-string">=</span> <span class="hljs-number">20</span><span class="hljs-string">,</span> 
    <span class="hljs-string">control</span> <span class="hljs-string">=</span> <span class="hljs-string">control_grid(verbose</span> <span class="hljs-string">=</span> <span class="hljs-literal">TRUE</span><span class="hljs-string">,</span> 
                           <span class="hljs-string">allow_par</span> <span class="hljs-string">=</span> <span class="hljs-literal">TRUE</span><span class="hljs-string">)</span>
  <span class="hljs-string">)</span>
<span class="hljs-string">toc()</span>

<span class="hljs-comment"># A tibble: 1 x 9</span>
   <span class="hljs-string">cost</span> <span class="hljs-string">rbf_sigma</span> <span class="hljs-string">margin</span> <span class="hljs-string">.metric</span> <span class="hljs-string">.estimator</span>  <span class="hljs-string">mean</span>     <span class="hljs-string">n</span> <span class="hljs-string">std_err</span> <span class="hljs-string">.config</span>              
  <span class="hljs-string">&lt;dbl&gt;</span>     <span class="hljs-string">&lt;dbl&gt;</span>  <span class="hljs-string">&lt;dbl&gt;</span> <span class="hljs-string">&lt;chr&gt;</span>   <span class="hljs-string">&lt;chr&gt;</span>      <span class="hljs-string">&lt;dbl&gt;</span> <span class="hljs-string">&lt;int&gt;</span>   <span class="hljs-string">&lt;dbl&gt;</span> <span class="hljs-string">&lt;chr&gt;</span>                
<span class="hljs-number">1</span>  <span class="hljs-number">4.60</span>    <span class="hljs-number">0.0110</span> <span class="hljs-number">0.0595</span> <span class="hljs-string">rmse</span>    <span class="hljs-string">standard</span>   <span class="hljs-number">0.172</span>    <span class="hljs-number">10</span> <span class="hljs-number">0.00222</span> <span class="hljs-string">Preprocessor1_Model07</span>

<span class="hljs-attr">i Prediction Error Comparison:</span>
<span class="hljs-comment"># A tibble: 9 x 3</span>
  <span class="hljs-string">.model_id</span>  <span class="hljs-string">rmse</span> <span class="hljs-string">.model_desc</span>                      
  <span class="hljs-string">&lt;chr&gt;</span>     <span class="hljs-string">&lt;dbl&gt;</span> <span class="hljs-string">&lt;chr&gt;</span>                            
<span class="hljs-number">1</span> <span class="hljs-number">1</span>         <span class="hljs-number">0.196</span> <span class="hljs-string">RANGER</span> <span class="hljs-bullet">-</span> <span class="hljs-string">Tuned</span>                   
<span class="hljs-number">2</span> <span class="hljs-number">2</span>         <span class="hljs-number">0.183</span> <span class="hljs-string">XGBOOST</span> <span class="hljs-bullet">-</span> <span class="hljs-string">Tuned</span>                  
<span class="hljs-number">3</span> <span class="hljs-number">3</span>         <span class="hljs-number">0.207</span> <span class="hljs-string">PROPHET</span> <span class="hljs-string">W/</span> <span class="hljs-string">REGRESSORS</span> <span class="hljs-bullet">-</span> <span class="hljs-string">Tuned</span>    
<span class="hljs-number">4</span> <span class="hljs-number">4</span>         <span class="hljs-number">0.179</span> <span class="hljs-string">PROPHET</span> <span class="hljs-string">W/</span> <span class="hljs-string">XGBOOST</span> <span class="hljs-string">ERRORS</span> <span class="hljs-bullet">-</span> <span class="hljs-string">Tuned</span>
<span class="hljs-number">5</span> <span class="hljs-number">5</span>         <span class="hljs-number">0.203</span> <span class="hljs-string">RANGER</span>                           
<span class="hljs-number">6</span> <span class="hljs-number">6</span>         <span class="hljs-number">0.203</span> <span class="hljs-string">XGBOOST</span>                          
<span class="hljs-number">7</span> <span class="hljs-number">7</span>         <span class="hljs-number">0.208</span> <span class="hljs-string">PROPHET</span> <span class="hljs-string">W/</span> <span class="hljs-string">REGRESSORS</span>            
<span class="hljs-number">8</span> <span class="hljs-number">8</span>         <span class="hljs-number">0.207</span> <span class="hljs-string">PROPHET</span> <span class="hljs-string">W/</span> <span class="hljs-string">XGBOOST</span> <span class="hljs-string">ERRORS</span>        
<span class="hljs-number">9</span> <span class="hljs-string">ensemble</span>  <span class="hljs-number">0.172</span> <span class="hljs-string">ENSEMBLE</span> <span class="hljs-string">(MODEL</span> <span class="hljs-string">SPEC)</span>  

<span class="hljs-string">&gt;</span> <span class="hljs-string">modeltime_table(</span>
   <span class="hljs-string">ensemble_fit_svm_kfold</span>
 <span class="hljs-string">)</span> <span class="hljs-string">%&gt;%</span>
   <span class="hljs-string">modeltime_accuracy(testing(splits))</span>

<span class="hljs-comment"># A tibble: 1 x 9</span>
  <span class="hljs-string">.model_id</span> <span class="hljs-string">.model_desc</span>                        <span class="hljs-string">.type</span>   <span class="hljs-string">mae</span>  <span class="hljs-string">mape</span>  <span class="hljs-string">mase</span> <span class="hljs-string">smape</span>  <span class="hljs-string">rmse</span>   <span class="hljs-string">rsq</span>
      <span class="hljs-string">&lt;int&gt;</span> <span class="hljs-string">&lt;chr&gt;</span>                              <span class="hljs-string">&lt;chr&gt;</span> <span class="hljs-string">&lt;dbl&gt;</span> <span class="hljs-string">&lt;dbl&gt;</span> <span class="hljs-string">&lt;dbl&gt;</span> <span class="hljs-string">&lt;dbl&gt;</span> <span class="hljs-string">&lt;dbl&gt;</span> <span class="hljs-string">&lt;dbl&gt;</span>
<span class="hljs-number">1</span>         <span class="hljs-number">1</span> <span class="hljs-string">ENSEMBLE</span> <span class="hljs-string">(KERNLAB</span> <span class="hljs-string">STACK):</span> <span class="hljs-number">8</span> <span class="hljs-string">MODELS</span> <span class="hljs-string">Test</span>  <span class="hljs-number">0.160</span>  <span class="hljs-number">21.8</span> <span class="hljs-number">0.352</span>  <span class="hljs-number">18.0</span> <span class="hljs-number">0.219</span> <span class="hljs-number">0.823</span>
</code></pre><h2 id="heading-multi-level-stacks">Multi-Level Stacks</h2>
<p>The previous stacking approaches can be mutualized in a <strong>multi-level stack</strong>. We can combine meta-learners into a higher level of the stack which will be a normal (weighted) average ensemble as introduced in <a target="_blank" href="https://blog.bguarisma.com/time-series-forecasting-and-ensemble-stacking-part-5">Part 5</a>.</p>
<p>Below is a reminder of the previous stacking algorithms' performance:</p>
<pre><code><span class="hljs-string">modeltime_table(</span>
  <span class="hljs-string">ensemble_fit_ranger_kfold,</span> 
  <span class="hljs-string">ensemble_fit_xgboost_kfold,</span>
  <span class="hljs-string">ensemble_fit_svm_kfold</span>
<span class="hljs-string">)</span> <span class="hljs-string">%&gt;%</span>
  <span class="hljs-string">modeltime_accuracy(testing(splits))</span> <span class="hljs-string">%&gt;%</span> 
  <span class="hljs-string">arrange(rmse)</span>

<span class="hljs-comment"># A tibble: 3 x 9</span>
  <span class="hljs-string">.model_id</span> <span class="hljs-string">.model_desc</span>                        <span class="hljs-string">.type</span>   <span class="hljs-string">mae</span>  <span class="hljs-string">mape</span>  <span class="hljs-string">mase</span> <span class="hljs-string">smape</span>  <span class="hljs-string">rmse</span>   <span class="hljs-string">rsq</span>
      <span class="hljs-string">&lt;int&gt;</span> <span class="hljs-string">&lt;chr&gt;</span>                              <span class="hljs-string">&lt;chr&gt;</span> <span class="hljs-string">&lt;dbl&gt;</span> <span class="hljs-string">&lt;dbl&gt;</span> <span class="hljs-string">&lt;dbl&gt;</span> <span class="hljs-string">&lt;dbl&gt;</span> <span class="hljs-string">&lt;dbl&gt;</span> <span class="hljs-string">&lt;dbl&gt;</span>
<span class="hljs-number">1</span>         <span class="hljs-number">3</span> <span class="hljs-string">ENSEMBLE</span> <span class="hljs-string">(KERNLAB</span> <span class="hljs-string">STACK):</span> <span class="hljs-number">8</span> <span class="hljs-string">MODELS</span> <span class="hljs-string">Test</span>  <span class="hljs-number">0.160</span>  <span class="hljs-number">21.8</span> <span class="hljs-number">0.352</span>  <span class="hljs-number">18.0</span> <span class="hljs-number">0.219</span> <span class="hljs-number">0.823</span>
<span class="hljs-number">2</span>         <span class="hljs-number">1</span> <span class="hljs-string">ENSEMBLE</span> <span class="hljs-string">(RANGER</span> <span class="hljs-string">STACK):</span> <span class="hljs-number">8</span> <span class="hljs-string">MODELS</span>  <span class="hljs-string">Test</span>  <span class="hljs-number">0.166</span>  <span class="hljs-number">21.8</span> <span class="hljs-number">0.367</span>  <span class="hljs-number">18.2</span> <span class="hljs-number">0.230</span> <span class="hljs-number">0.817</span>
<span class="hljs-number">3</span>         <span class="hljs-number">2</span> <span class="hljs-string">ENSEMBLE</span> <span class="hljs-string">(XGBOOST</span> <span class="hljs-string">STACK):</span> <span class="hljs-number">8</span> <span class="hljs-string">MODELS</span> <span class="hljs-string">Test</span>  <span class="hljs-number">0.182</span>  <span class="hljs-number">23.9</span> <span class="hljs-number">0.401</span>  <span class="hljs-number">19.7</span> <span class="hljs-number">0.251</span> <span class="hljs-number">0.769</span>
</code></pre><p>Let us now define a weighted average ensemble model from the stacking algorithms as we did in <a target="_blank" href="https://blog.bguarisma.com/time-series-forecasting-lab-part-5-ensembles">Part 5</a>. We will apply the ranking technique to calculate the weights.</p>
<p>First we create the accuracy results table on which we apply the ranking technique for weigths calculation, then we create the weighted average ensemble model with <code>ensemble_weighted()</code> by applying the weights, and finally, we evaluate the model.</p>
<pre><code>loadings_tbl <span class="hljs-tag">&lt;<span class="hljs-name">-</span> <span class="hljs-attr">modeltime_table</span>(
  <span class="hljs-attr">ensemble_fit_ranger_kfold</span>, 
  <span class="hljs-attr">ensemble_fit_xgboost_kfold</span>,
  <span class="hljs-attr">ensemble_fit_svm_kfold</span>
) %&gt;</span>% 
  modeltime_calibrate(testing(splits)) %&gt;%
  modeltime_accuracy() %&gt;%
  mutate(rank = min_rank(-rmse)) %&gt;%
  select(.model_id, rank)

stacking_fit_wt <span class="hljs-tag">&lt;<span class="hljs-name">-</span> <span class="hljs-attr">modeltime_table</span>(
  <span class="hljs-attr">ensemble_fit_ranger_kfold</span>, 
  <span class="hljs-attr">ensemble_fit_xgboost_kfold</span>,
  <span class="hljs-attr">ensemble_fit_svm_kfold</span>
) %&gt;</span>%
  ensemble_weighted(loadings = loadings_tbl$rank)

&gt; stacking_fit_wt  %&gt;% 
   modeltime_calibrate(testing(splits)) %&gt;%
   modeltime_accuracy() %&gt;%
   arrange(rmse)
# A tibble: 1 x 9
  .model_id .model_desc                   .type   mae  mape  mase smape  rmse   rsq
      <span class="hljs-tag">&lt;<span class="hljs-name">int</span>&gt;</span> <span class="hljs-tag">&lt;<span class="hljs-name">chr</span>&gt;</span>                         <span class="hljs-tag">&lt;<span class="hljs-name">chr</span>&gt;</span> <span class="hljs-tag">&lt;<span class="hljs-name">dbl</span>&gt;</span> <span class="hljs-tag">&lt;<span class="hljs-name">dbl</span>&gt;</span> <span class="hljs-tag">&lt;<span class="hljs-name">dbl</span>&gt;</span> <span class="hljs-tag">&lt;<span class="hljs-name">dbl</span>&gt;</span> <span class="hljs-tag">&lt;<span class="hljs-name">dbl</span>&gt;</span> <span class="hljs-tag">&lt;<span class="hljs-name">dbl</span>&gt;</span>
1         1 ENSEMBLE (WEIGHTED): 3 MODELS Test  0.161  21.8 0.354  18.1 0.223 0.817
</code></pre><p>With <strong>RMSE = 0.223</strong>, the stacked ensemble algorithm outperformed all algorithms!</p>
<h2 id="heading-forescast-plot">Forescast plot</h2>
<p>Below the stacked ensemble algorithm's forecast plot on the test dataset for all industries, it looks good ! except for one industry the model seems to follow pretty well the trend and spikes of the actual data.</p>
<pre><code>calibration_stacking <span class="hljs-operator">&lt;</span><span class="hljs-operator">-</span> stacking_fit_wt <span class="hljs-operator">%</span><span class="hljs-operator">&gt;</span><span class="hljs-operator">%</span> 
  modeltime_table() <span class="hljs-operator">%</span><span class="hljs-operator">&gt;</span><span class="hljs-operator">%</span>
  modeltime_calibrate(testing(splits))

calibration_stacking <span class="hljs-operator">%</span><span class="hljs-operator">&gt;</span><span class="hljs-operator">%</span>
  modeltime_forecast(
    new_data    <span class="hljs-operator">=</span> testing(splits),
    actual_data <span class="hljs-operator">=</span> artifacts$data$data_prepared_tbl,
    keep_data   <span class="hljs-operator">=</span> TRUE 
  ) <span class="hljs-operator">%</span><span class="hljs-operator">&gt;</span><span class="hljs-operator">%</span>
  group_by(Industry) <span class="hljs-operator">%</span><span class="hljs-operator">&gt;</span><span class="hljs-operator">%</span>
  plot_modeltime_forecast(
    .facet_ncol         <span class="hljs-operator">=</span> <span class="hljs-number">4</span>, 
    .conf_interval_show <span class="hljs-operator">=</span> FALSE,
    .interactive        <span class="hljs-operator">=</span> TRUE,
    .title <span class="hljs-operator">=</span> <span class="hljs-string">"Forecast stacking level model (test data)"</span>
  )
</code></pre><p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1642730024695/6QVGVq30T.png" alt="p6_stacked_fcst_plot.png" /></p>
<h2 id="heading-next-12-months-turnover-forecast">Next 12 months Turnover forecast</h2>
<p>Let us now (finally!) perform the <strong>next 12 months Turnover forecast for all 20 industries</strong> as we promised in <a target="_blank" href="https://blog.bguarisma.com/time-series-forecasting-and-ensemble-stacking-part-1">Part  1</a>.</p>
<p>First, we must refit the stack-level weighted average model with the <strong>prepared dataset</strong> from <a target="_blank" href="https://blog.bguarisma.com/time-series-forecasting-and-ensemble-stacking-part-2">Part 2</a>. We do this with <code>modeltime_refit()</code>.</p>
<p>Then we calculate the Turnover predictions over the next 12 months (values are still transformed). We do this with <code>modeltime_forecast()</code> and by setting <code>new_data</code> to the future dataset.</p>
<pre><code><span class="hljs-comment"># Toggle ON parallel processing</span>
plan(
  strategy = cluster,
  workers  = parallel::makeCluster(n_cores)
)

<span class="hljs-comment"># Refit the model on prepared dataset</span>
tic()
set.seed(<span class="hljs-number">123</span>)
refit_stacking_tbl &lt;- calibration_stacking %&gt;% 
  modeltime_refit(
    data = artifacts$data$data_prepared_tbl,
    resamples = artifacts$data$data_prepared_tbl %&gt;%
      drop_na() %&gt;%
      vfold_cv(v = <span class="hljs-number">10</span>)
  )
toc()

<span class="hljs-comment"># 12-month forecast calculations with future dataset</span>

forecast_stacking_tbl &lt;- refit_stacking_tbl %&gt;%
  modeltime_forecast(
    new_data    = artifacts$data$future_tbl,
    actual_data = artifacts$data$data_prepared_tbl %&gt;%
      drop_na(), 
    keep_data = <span class="hljs-literal">TRUE</span>
  )

<span class="hljs-comment"># Toggle OFF parallel processing</span>
plan(sequential)
</code></pre><p>We invert the Turnover values back to their <strong>original values</strong> with <code>standardize_inv_vec()</code> and <code>expm1()</code>.</p>
<p>Finally, we plot the <strong>forecast for the next 12 months</strong> within the <strong>original scale</strong> of the Turnover measure.</p>
<pre><code>lforecasts &lt;- lapply(X = <span class="hljs-number">1</span>:length(Industries), FUN = <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params">x</span>)</span>{
  forecast_stacking_tbl %&gt;%
    filter(Industry == Industries[x]) %&gt;%
    <span class="hljs-comment">#group_by(Industry) %&gt;%</span>
    mutate(across(.value:.conf_hi,
                  .fns = ~standardize_inv_vec(x = .,
                                              mean = artifacts$standardize$std_mean[x],
                                              sd = artifacts$standardize$std_sd[x]))) %&gt;%
    mutate(across(.value:.conf_hi,
                  .fns = ~expm1(x = .)))
})

forecast_stacking_tbl &lt;- bind_rows(lforecasts)

forecast_stacking_tbl %&gt;%
  group_by(Industry) %&gt;%
  plot_modeltime_forecast(.title = <span class="hljs-string">"Turnover 1-year forecast"</span>,     
                          .facet_ncol         = <span class="hljs-number">4</span>, 
                          .conf_interval_show = <span class="hljs-literal">FALSE</span>,
                          .interactive        = <span class="hljs-literal">TRUE</span>)
</code></pre><p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1642760771587/FbFsJdXxK.png" alt="p6_stacked_fcst_plot_origval.png" /></p>
<p>Below a zoomed view of the same plot</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1642760783961/kU3yrCI98.png" alt="p6_stacked_fcst_plot_origval_ZOOM.png" /></p>
<h2 id="heading-conclusion">Conclusion</h2>
<p>In this article you have learned how to implement stacked ensemble models. A stacking algorithm or <strong>meta-learner</strong> learns from predictions. The predictions come from k-fold cross-validations applied to each submodel. Once fed into the meta-learner tunable specification, a hyperpameter tuning will be peformed for the meta-learner. </p>
<p>We defined three meta-learners: SVM, Random Forest, and XGBoost from which we defined a weighted average ensemble model (stack-level) using the same method as explained  <a target="_blank" href="https://blog.bguarisma.com/time-series-forecasting-and-ensemble-stacking-part-5">Part 5</a>.</p>
<p>The stacked ensemble algorithm outperformed all algorithms.</p>
<h2 id="heading-references">References</h2>
<p><a target="_blank" href="https://university.business-science.io/courses">1</a> Dancho, Matt, "<em>DS4B 203-R: High-Performance Time Series Forecasting</em>", Business Science University</p>
]]></content:encoded></item><item><title><![CDATA[Time Series Forecasting Lab (Part 5) - Ensembles]]></title><description><![CDATA[Cover photo by Hannah Busing on Unsplash
Go to R-bloggers for R news and tutorials contributed by hundreds of R bloggers.
Introduction
This is the fifth of a series of 6 articles about time series forecasting with panel data and ensemble stacking wit...]]></description><link>https://blog.bguarisma.com/time-series-forecasting-lab-part-5-ensembles</link><guid isPermaLink="true">https://blog.bguarisma.com/time-series-forecasting-lab-part-5-ensembles</guid><category><![CDATA[R Language]]></category><category><![CDATA[Machine Learning]]></category><dc:creator><![CDATA[Boris Guarisma]]></dc:creator><pubDate>Sat, 15 Jan 2022 17:36:09 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1642168573463/uG0FuJRSq.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Cover photo by <a href="https://unsplash.com/@hannahbusing?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText">Hannah Busing</a> on <a href="https://unsplash.com/s/photos/team?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText">Unsplash</a></p>
<p>Go to <a target="_blank" href="https://www.r-bloggers.com/">R-bloggers</a> for R news and tutorials contributed by hundreds of R bloggers.</p>
<h2 id="heading-introduction">Introduction</h2>
<p>This is the fifth of a series of 6 articles about time series forecasting with panel data and ensemble stacking with R.</p>
<p>Through these articles I will be putting into practice what I have learned from the Business Science University training course  <a target="_blank" href="https://university.business-science.io/courses">1</a> DS4B 203-R: High-Performance Time Series Forecasting", delivered by Matt Dancho. If you are looking to gain a high level of expertise in time series with R I strongly recommend this course.  </p>
<p>The objective of this article is learn how to create average and weighted ensembles using all (both non-tuned and tuned) model from <a target="_blank" href="https://blog.bguarisma.com/time-series-forecasting-and-ensemble-stacking-part-3">Part 3</a>  and  <a target="_blank" href="https://blog.bguarisma.com/time-series-forecasting-and-ensemble-stacking-part-4">Part 4</a>. We will also check if any of these ensemble models can outperform the tuned Prophet Boost model.</p>
<h3 id="heading-prerequisites">Prerequisites</h3>
<p>I assume you are already familiar with the following topics, packages and terms:</p>
<ul>
<li><p>dplyr or tidyverse R packages</p>
</li>
<li><p>calibration table from  <a target="_blank" href="https://blog.bguarisma.com/time-series-forecasting-and-ensemble-stacking-part-3">Part 3</a>  and  <a target="_blank" href="https://blog.bguarisma.com/time-series-forecasting-and-ensemble-stacking-part-4">Part 4</a> </p>
</li>
<li><p>model evaluation metrics: RMSE, R-squared, ...</p>
</li>
</ul>
<h2 id="heading-packages">Packages</h2>
<p>The following packages must be loaded:</p>
<pre><code><span class="hljs-comment"># 01 FEATURE ENGINEERING</span>
<span class="hljs-keyword">library</span>(tidyverse)  <span class="hljs-comment"># loading dplyr, tibble, ggplot2, .. dependencies</span>
<span class="hljs-keyword">library</span>(timetk)  <span class="hljs-comment"># using timetk plotting, diagnostics and augment operations</span>
<span class="hljs-keyword">library</span>(tsibble)  <span class="hljs-comment"># for month to Date conversion</span>
<span class="hljs-keyword">library</span>(tsibbledata)  <span class="hljs-comment"># for aus_retail dataset</span>
<span class="hljs-keyword">library</span>(fastDummies)  <span class="hljs-comment"># for dummyfying categorical variables</span>
<span class="hljs-keyword">library</span>(skimr) <span class="hljs-comment"># for quick statistics</span>

<span class="hljs-comment"># 02 FEATURE ENGINEERING WITH RECIPES</span>
<span class="hljs-keyword">library</span>(tidymodels) <span class="hljs-comment"># with workflow dependency</span>

<span class="hljs-comment"># 03 MACHINE LEARNING</span>
<span class="hljs-keyword">library</span>(modeltime) <span class="hljs-comment"># ML models specifications and engines</span>
<span class="hljs-keyword">library</span>(tictoc) <span class="hljs-comment"># measure training elapsed time</span>

<span class="hljs-comment"># 04 HYPERPARAMETER TUNING</span>
<span class="hljs-keyword">library</span>(future)
<span class="hljs-keyword">library</span>(doFuture)
<span class="hljs-keyword">library</span>(plotly)

<span class="hljs-comment"># 05 ENSEMBLES</span>
<span class="hljs-keyword">library</span>(modeltime.ensemble)
</code></pre><h2 id="heading-load-calibration-tables">Load calibration tables</h2>
<p>Load all calibration tables for all tuned &amp; non-tuned models we have trained so far, nad remind ourselves of the accuracy results so far:</p>
<pre><code><span class="hljs-string">&gt;</span> <span class="hljs-string">calibration_tbl</span> <span class="hljs-string">&lt;-</span> <span class="hljs-string">read_rds("workflows_NonandTuned_artifacts_list.rds")</span>
<span class="hljs-string">&gt;</span> <span class="hljs-string">calibration_tbl</span> <span class="hljs-string">&lt;-</span> <span class="hljs-string">calibration_tbl$calibration</span>
<span class="hljs-string">&gt;</span> <span class="hljs-string">calibration_tbl</span> <span class="hljs-string">%&gt;%</span> 
   <span class="hljs-string">modeltime_accuracy()</span> <span class="hljs-string">%&gt;%</span>
   <span class="hljs-string">arrange(rmse)</span>
<span class="hljs-comment"># A tibble: 8 x 9</span>
  <span class="hljs-string">.model_id</span> <span class="hljs-string">.model_desc</span>                       <span class="hljs-string">.type</span>   <span class="hljs-string">mae</span>  <span class="hljs-string">mape</span>  <span class="hljs-string">mase</span> <span class="hljs-string">smape</span>  <span class="hljs-string">rmse</span>   <span class="hljs-string">rsq</span>
      <span class="hljs-string">&lt;int&gt;</span> <span class="hljs-string">&lt;chr&gt;</span>                             <span class="hljs-string">&lt;chr&gt;</span> <span class="hljs-string">&lt;dbl&gt;</span> <span class="hljs-string">&lt;dbl&gt;</span> <span class="hljs-string">&lt;dbl&gt;</span> <span class="hljs-string">&lt;dbl&gt;</span> <span class="hljs-string">&lt;dbl&gt;</span> <span class="hljs-string">&lt;dbl&gt;</span>
<span class="hljs-number">1</span>         <span class="hljs-number">7</span> <span class="hljs-string">PROPHET</span> <span class="hljs-string">W/</span> <span class="hljs-string">REGRESSORS</span>             <span class="hljs-string">Test</span>  <span class="hljs-number">0.189</span>  <span class="hljs-number">21.6</span> <span class="hljs-number">0.417</span>  <span class="hljs-number">21.5</span> <span class="hljs-number">0.240</span> <span class="hljs-number">0.856</span>
<span class="hljs-number">2</span>         <span class="hljs-number">3</span> <span class="hljs-string">PROPHET</span> <span class="hljs-string">W/</span> <span class="hljs-string">REGRESSORS</span> <span class="hljs-bullet">-</span> <span class="hljs-string">Tuned</span>     <span class="hljs-string">Test</span>  <span class="hljs-number">0.189</span>  <span class="hljs-number">21.6</span> <span class="hljs-number">0.418</span>  <span class="hljs-number">21.5</span> <span class="hljs-number">0.241</span> <span class="hljs-number">0.857</span>
<span class="hljs-number">3</span>         <span class="hljs-number">4</span> <span class="hljs-string">PROPHET</span> <span class="hljs-string">W/</span> <span class="hljs-string">XGBOOST</span> <span class="hljs-string">ERRORS</span> <span class="hljs-bullet">-</span> <span class="hljs-string">Tuned</span> <span class="hljs-string">Test</span>  <span class="hljs-number">0.191</span>  <span class="hljs-number">25.7</span> <span class="hljs-number">0.421</span>  <span class="hljs-number">19.9</span> <span class="hljs-number">0.253</span> <span class="hljs-number">0.780</span>
<span class="hljs-number">4</span>         <span class="hljs-number">8</span> <span class="hljs-string">PROPHET</span> <span class="hljs-string">W/</span> <span class="hljs-string">XGBOOST</span> <span class="hljs-string">ERRORS</span>         <span class="hljs-string">Test</span>  <span class="hljs-number">0.204</span>  <span class="hljs-number">24.8</span> <span class="hljs-number">0.451</span>  <span class="hljs-number">20.8</span> <span class="hljs-number">0.287</span> <span class="hljs-number">0.753</span>
<span class="hljs-number">5</span>         <span class="hljs-number">2</span> <span class="hljs-string">XGBOOST</span> <span class="hljs-bullet">-</span> <span class="hljs-string">Tuned</span>                   <span class="hljs-string">Test</span>  <span class="hljs-number">0.219</span>  <span class="hljs-number">23.6</span> <span class="hljs-number">0.482</span>  <span class="hljs-number">22.9</span> <span class="hljs-number">0.296</span> <span class="hljs-number">0.762</span>
<span class="hljs-number">6</span>         <span class="hljs-number">6</span> <span class="hljs-string">XGBOOST</span>                           <span class="hljs-string">Test</span>  <span class="hljs-number">0.216</span>  <span class="hljs-number">25.0</span> <span class="hljs-number">0.476</span>  <span class="hljs-number">22.4</span> <span class="hljs-number">0.299</span> <span class="hljs-number">0.747</span>
<span class="hljs-number">7</span>         <span class="hljs-number">1</span> <span class="hljs-string">RANGER</span> <span class="hljs-bullet">-</span> <span class="hljs-string">Tuned</span>                    <span class="hljs-string">Test</span>  <span class="hljs-number">0.231</span>  <span class="hljs-number">26.1</span> <span class="hljs-number">0.509</span>  <span class="hljs-number">24.6</span> <span class="hljs-number">0.303</span> <span class="hljs-number">0.766</span>
<span class="hljs-number">8</span>         <span class="hljs-number">5</span> <span class="hljs-string">RANGER</span>                            <span class="hljs-string">Test</span>  <span class="hljs-number">0.235</span>  <span class="hljs-number">25.4</span> <span class="hljs-number">0.519</span>  <span class="hljs-number">25.0</span> <span class="hljs-number">0.312</span> <span class="hljs-number">0.765</span>
</code></pre><h2 id="heading-average-ensembles">Average ensembles</h2>
<p>Average ensembles is a fairly simple concept, its main advantage is that it does not require retraining. If you use the <strong>mean</strong> be aware of the individual performance of each model since the "average model" performance will be sensitive to "bad models" and outliers. To cope with this situation you can use the <strong>median</strong> (more robust than the mean). Do not use the median when all models are doing pretty well.</p>
<p>To create these two ensembles is pretty simple, all you have to do is to pipe the modeltime table with all workflows into the <code>ensemble_average()</code> and specify the <code>type</code>, either mean or median.</p>
<pre><code>&gt; ensemble_fit_mean <span class="hljs-tag">&lt;<span class="hljs-name">-</span> <span class="hljs-attr">submodels_tbl</span> %&gt;</span>%
  ensemble_average(type = "mean")

&gt; ensemble_fit_mean
-- Modeltime Ensemble -------------------------------------------
Ensemble of 8 Models (MEAN)

# Modeltime Table
# A tibble: 8 x 5
  .model_id .model     .model_desc                       .type .calibration_data   
      <span class="hljs-tag">&lt;<span class="hljs-name">int</span>&gt;</span> <span class="hljs-tag">&lt;<span class="hljs-name">list</span>&gt;</span>     <span class="hljs-tag">&lt;<span class="hljs-name">chr</span>&gt;</span>                             <span class="hljs-tag">&lt;<span class="hljs-name">chr</span>&gt;</span> <span class="hljs-tag">&lt;<span class="hljs-name">list</span>&gt;</span>              
1         1 <span class="hljs-tag">&lt;<span class="hljs-name">workflow</span>&gt;</span> RANGER - Tuned                    Test  <span class="hljs-tag">&lt;<span class="hljs-name">tibble</span> [<span class="hljs-attr">1</span>,<span class="hljs-attr">000</span> <span class="hljs-attr">x</span> <span class="hljs-attr">4</span>]&gt;</span>
2         2 <span class="hljs-tag">&lt;<span class="hljs-name">workflow</span>&gt;</span> XGBOOST - Tuned                   Test  <span class="hljs-tag">&lt;<span class="hljs-name">tibble</span> [<span class="hljs-attr">1</span>,<span class="hljs-attr">000</span> <span class="hljs-attr">x</span> <span class="hljs-attr">4</span>]&gt;</span>
3         3 <span class="hljs-tag">&lt;<span class="hljs-name">workflow</span>&gt;</span> PROPHET W/ REGRESSORS - Tuned     Test  <span class="hljs-tag">&lt;<span class="hljs-name">tibble</span> [<span class="hljs-attr">1</span>,<span class="hljs-attr">000</span> <span class="hljs-attr">x</span> <span class="hljs-attr">4</span>]&gt;</span>
4         4 <span class="hljs-tag">&lt;<span class="hljs-name">workflow</span>&gt;</span> PROPHET W/ XGBOOST ERRORS - Tuned Test  <span class="hljs-tag">&lt;<span class="hljs-name">tibble</span> [<span class="hljs-attr">1</span>,<span class="hljs-attr">000</span> <span class="hljs-attr">x</span> <span class="hljs-attr">4</span>]&gt;</span>
5         5 <span class="hljs-tag">&lt;<span class="hljs-name">workflow</span>&gt;</span> RANGER                            Test  <span class="hljs-tag">&lt;<span class="hljs-name">tibble</span> [<span class="hljs-attr">1</span>,<span class="hljs-attr">000</span> <span class="hljs-attr">x</span> <span class="hljs-attr">4</span>]&gt;</span>
6         6 <span class="hljs-tag">&lt;<span class="hljs-name">workflow</span>&gt;</span> XGBOOST                           Test  <span class="hljs-tag">&lt;<span class="hljs-name">tibble</span> [<span class="hljs-attr">1</span>,<span class="hljs-attr">000</span> <span class="hljs-attr">x</span> <span class="hljs-attr">4</span>]&gt;</span>
7         7 <span class="hljs-tag">&lt;<span class="hljs-name">workflow</span>&gt;</span> PROPHET W/ REGRESSORS             Test  <span class="hljs-tag">&lt;<span class="hljs-name">tibble</span> [<span class="hljs-attr">1</span>,<span class="hljs-attr">000</span> <span class="hljs-attr">x</span> <span class="hljs-attr">4</span>]&gt;</span>
8         8 <span class="hljs-tag">&lt;<span class="hljs-name">workflow</span>&gt;</span> PROPHET W/ XGBOOST ERRORS         Test  <span class="hljs-tag">&lt;<span class="hljs-name">tibble</span> [<span class="hljs-attr">1</span>,<span class="hljs-attr">000</span> <span class="hljs-attr">x</span> <span class="hljs-attr">4</span>]&gt;</span>

&gt; ensemble_fit_mean <span class="hljs-tag">&lt;<span class="hljs-name">-</span> <span class="hljs-attr">submodels_tbl</span> %&gt;</span>%
  ensemble_average(type = "median")

-- Modeltime Ensemble -------------------------------------------
Ensemble of 8 Models (MEDIAN)

# Modeltime Table
# A tibble: 8 x 5
  .model_id .model     .model_desc                       .type .calibration_data   
      <span class="hljs-tag">&lt;<span class="hljs-name">int</span>&gt;</span> <span class="hljs-tag">&lt;<span class="hljs-name">list</span>&gt;</span>     <span class="hljs-tag">&lt;<span class="hljs-name">chr</span>&gt;</span>                             <span class="hljs-tag">&lt;<span class="hljs-name">chr</span>&gt;</span> <span class="hljs-tag">&lt;<span class="hljs-name">list</span>&gt;</span>              
1         1 <span class="hljs-tag">&lt;<span class="hljs-name">workflow</span>&gt;</span> RANGER - Tuned                    Test  <span class="hljs-tag">&lt;<span class="hljs-name">tibble</span> [<span class="hljs-attr">1</span>,<span class="hljs-attr">000</span> <span class="hljs-attr">x</span> <span class="hljs-attr">4</span>]&gt;</span>
2         2 <span class="hljs-tag">&lt;<span class="hljs-name">workflow</span>&gt;</span> XGBOOST - Tuned                   Test  <span class="hljs-tag">&lt;<span class="hljs-name">tibble</span> [<span class="hljs-attr">1</span>,<span class="hljs-attr">000</span> <span class="hljs-attr">x</span> <span class="hljs-attr">4</span>]&gt;</span>
3         3 <span class="hljs-tag">&lt;<span class="hljs-name">workflow</span>&gt;</span> PROPHET W/ REGRESSORS - Tuned     Test  <span class="hljs-tag">&lt;<span class="hljs-name">tibble</span> [<span class="hljs-attr">1</span>,<span class="hljs-attr">000</span> <span class="hljs-attr">x</span> <span class="hljs-attr">4</span>]&gt;</span>
4         4 <span class="hljs-tag">&lt;<span class="hljs-name">workflow</span>&gt;</span> PROPHET W/ XGBOOST ERRORS - Tuned Test  <span class="hljs-tag">&lt;<span class="hljs-name">tibble</span> [<span class="hljs-attr">1</span>,<span class="hljs-attr">000</span> <span class="hljs-attr">x</span> <span class="hljs-attr">4</span>]&gt;</span>
5         5 <span class="hljs-tag">&lt;<span class="hljs-name">workflow</span>&gt;</span> RANGER                            Test  <span class="hljs-tag">&lt;<span class="hljs-name">tibble</span> [<span class="hljs-attr">1</span>,<span class="hljs-attr">000</span> <span class="hljs-attr">x</span> <span class="hljs-attr">4</span>]&gt;</span>
6         6 <span class="hljs-tag">&lt;<span class="hljs-name">workflow</span>&gt;</span> XGBOOST                           Test  <span class="hljs-tag">&lt;<span class="hljs-name">tibble</span> [<span class="hljs-attr">1</span>,<span class="hljs-attr">000</span> <span class="hljs-attr">x</span> <span class="hljs-attr">4</span>]&gt;</span>
7         7 <span class="hljs-tag">&lt;<span class="hljs-name">workflow</span>&gt;</span> PROPHET W/ REGRESSORS             Test  <span class="hljs-tag">&lt;<span class="hljs-name">tibble</span> [<span class="hljs-attr">1</span>,<span class="hljs-attr">000</span> <span class="hljs-attr">x</span> <span class="hljs-attr">4</span>]&gt;</span>
8         8 <span class="hljs-tag">&lt;<span class="hljs-name">workflow</span>&gt;</span> PROPHET W/ XGBOOST ERRORS         Test  <span class="hljs-tag">&lt;<span class="hljs-name">tibble</span> [<span class="hljs-attr">1</span>,<span class="hljs-attr">000</span> <span class="hljs-attr">x</span> <span class="hljs-attr">4</span>]&gt;</span>
</code></pre><h2 id="heading-weigthed-ensembles">Weigthed ensembles</h2>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1642180266391/XFFgWRkMg.png" alt="p5_02_weighted.png" /></p>
<p>Weighted ensembles can improve performance compared to average ensembles but we must decide of the weight values. One solution is to use a simple <em>rank technique</em>: create a rank column for which highest value is for the best model (lowest RMSE).</p>
<p>In the code snippet below we add the <code>rank</code> column and then we select <code>.model_id</code> and <code>rank</code> columns to define a loadings tibble which will be used for defining the weighted ensemble.</p>
<pre><code><span class="hljs-string">&gt;</span> <span class="hljs-string">calibration_tbl</span> <span class="hljs-string">%&gt;%</span>
   <span class="hljs-string">modeltime_accuracy()</span> <span class="hljs-string">%&gt;%</span>
   <span class="hljs-string">mutate(rank</span> <span class="hljs-string">=</span> <span class="hljs-string">min_rank(-rmse))</span> 
<span class="hljs-comment"># A tibble: 8 x 10</span>
  <span class="hljs-string">.model_id</span> <span class="hljs-string">.model_desc</span>                       <span class="hljs-string">.type</span>   <span class="hljs-string">mae</span>  <span class="hljs-string">mape</span>  <span class="hljs-string">mase</span> <span class="hljs-string">smape</span>  <span class="hljs-string">rmse</span>   <span class="hljs-string">rsq</span>  <span class="hljs-string">rank</span>
      <span class="hljs-string">&lt;int&gt;</span> <span class="hljs-string">&lt;chr&gt;</span>                             <span class="hljs-string">&lt;chr&gt;</span> <span class="hljs-string">&lt;dbl&gt;</span> <span class="hljs-string">&lt;dbl&gt;</span> <span class="hljs-string">&lt;dbl&gt;</span> <span class="hljs-string">&lt;dbl&gt;</span> <span class="hljs-string">&lt;dbl&gt;</span> <span class="hljs-string">&lt;dbl&gt;</span> <span class="hljs-string">&lt;int&gt;</span>
<span class="hljs-number">1</span>         <span class="hljs-number">1</span> <span class="hljs-string">RANGER</span> <span class="hljs-bullet">-</span> <span class="hljs-string">Tuned</span>                    <span class="hljs-string">Test</span>  <span class="hljs-number">0.231</span>  <span class="hljs-number">26.1</span> <span class="hljs-number">0.509</span>  <span class="hljs-number">24.6</span> <span class="hljs-number">0.303</span> <span class="hljs-number">0.766</span>     <span class="hljs-number">2</span>
<span class="hljs-number">2</span>         <span class="hljs-number">2</span> <span class="hljs-string">XGBOOST</span> <span class="hljs-bullet">-</span> <span class="hljs-string">Tuned</span>                   <span class="hljs-string">Test</span>  <span class="hljs-number">0.219</span>  <span class="hljs-number">23.6</span> <span class="hljs-number">0.482</span>  <span class="hljs-number">22.9</span> <span class="hljs-number">0.296</span> <span class="hljs-number">0.762</span>     <span class="hljs-number">4</span>
<span class="hljs-number">3</span>         <span class="hljs-number">3</span> <span class="hljs-string">PROPHET</span> <span class="hljs-string">W/</span> <span class="hljs-string">REGRESSORS</span> <span class="hljs-bullet">-</span> <span class="hljs-string">Tuned</span>     <span class="hljs-string">Test</span>  <span class="hljs-number">0.189</span>  <span class="hljs-number">21.6</span> <span class="hljs-number">0.418</span>  <span class="hljs-number">21.5</span> <span class="hljs-number">0.241</span> <span class="hljs-number">0.857</span>     <span class="hljs-number">7</span>
<span class="hljs-number">4</span>         <span class="hljs-number">4</span> <span class="hljs-string">PROPHET</span> <span class="hljs-string">W/</span> <span class="hljs-string">XGBOOST</span> <span class="hljs-string">ERRORS</span> <span class="hljs-bullet">-</span> <span class="hljs-string">Tuned</span> <span class="hljs-string">Test</span>  <span class="hljs-number">0.191</span>  <span class="hljs-number">25.7</span> <span class="hljs-number">0.421</span>  <span class="hljs-number">19.9</span> <span class="hljs-number">0.253</span> <span class="hljs-number">0.780</span>     <span class="hljs-number">6</span>
<span class="hljs-number">5</span>         <span class="hljs-number">5</span> <span class="hljs-string">RANGER</span>                            <span class="hljs-string">Test</span>  <span class="hljs-number">0.235</span>  <span class="hljs-number">25.4</span> <span class="hljs-number">0.519</span>  <span class="hljs-number">25.0</span> <span class="hljs-number">0.312</span> <span class="hljs-number">0.765</span>     <span class="hljs-number">1</span>
<span class="hljs-number">6</span>         <span class="hljs-number">6</span> <span class="hljs-string">XGBOOST</span>                           <span class="hljs-string">Test</span>  <span class="hljs-number">0.216</span>  <span class="hljs-number">25.0</span> <span class="hljs-number">0.476</span>  <span class="hljs-number">22.4</span> <span class="hljs-number">0.299</span> <span class="hljs-number">0.747</span>     <span class="hljs-number">3</span>
<span class="hljs-number">7</span>         <span class="hljs-number">7</span> <span class="hljs-string">PROPHET</span> <span class="hljs-string">W/</span> <span class="hljs-string">REGRESSORS</span>             <span class="hljs-string">Test</span>  <span class="hljs-number">0.189</span>  <span class="hljs-number">21.6</span> <span class="hljs-number">0.417</span>  <span class="hljs-number">21.5</span> <span class="hljs-number">0.240</span> <span class="hljs-number">0.856</span>     <span class="hljs-number">8</span>
<span class="hljs-number">8</span>         <span class="hljs-number">8</span> <span class="hljs-string">PROPHET</span> <span class="hljs-string">W/</span> <span class="hljs-string">XGBOOST</span> <span class="hljs-string">ERRORS</span>         <span class="hljs-string">Test</span>  <span class="hljs-number">0.204</span>  <span class="hljs-number">24.8</span> <span class="hljs-number">0.451</span>  <span class="hljs-number">20.8</span> <span class="hljs-number">0.287</span> <span class="hljs-number">0.753</span>     <span class="hljs-number">5</span>

<span class="hljs-string">&gt;</span> <span class="hljs-string">loadings_tbl</span> <span class="hljs-string">&lt;-</span> <span class="hljs-string">submodels_tbl</span> <span class="hljs-string">%&gt;%</span>
  <span class="hljs-string">modeltime_accuracy()</span> <span class="hljs-string">%&gt;%</span>
  <span class="hljs-string">mutate(rank</span> <span class="hljs-string">=</span> <span class="hljs-string">min_rank(-rmse))</span> <span class="hljs-string">%&gt;%</span>
  <span class="hljs-string">select(.model_id,</span> <span class="hljs-string">rank)</span>
</code></pre><p>Then the weigths are created by passing models' rank to the <code>ensemble_weighted()</code> function. The newly created ".loadings" column are the weights, the sum of all weigths is equal to 1.</p>
<pre><code><span class="hljs-string">&gt;</span> <span class="hljs-string">ensemble_fit_wt</span> <span class="hljs-string">&lt;-</span> <span class="hljs-string">submodels_tbl</span> <span class="hljs-string">%&gt;%</span>
  <span class="hljs-string">ensemble_weighted(loadings</span> <span class="hljs-string">=</span> <span class="hljs-string">loadings_tbl$rank)</span>

<span class="hljs-string">&gt;</span> <span class="hljs-string">ensemble_fit_wt$fit$loadings_tbl</span>
<span class="hljs-comment"># Modeltime Table</span>
<span class="hljs-comment"># A tibble: 8 x 2</span>
  <span class="hljs-string">.model_id</span> <span class="hljs-string">.loadings</span>
      <span class="hljs-string">&lt;int&gt;</span>     <span class="hljs-string">&lt;dbl&gt;</span>
<span class="hljs-number">1</span>         <span class="hljs-number">1</span>    <span class="hljs-number">0.0556</span>
<span class="hljs-number">2</span>         <span class="hljs-number">2</span>    <span class="hljs-number">0.111</span> 
<span class="hljs-number">3</span>         <span class="hljs-number">3</span>    <span class="hljs-number">0.194</span> 
<span class="hljs-number">4</span>         <span class="hljs-number">4</span>    <span class="hljs-number">0.167</span> 
<span class="hljs-number">5</span>         <span class="hljs-number">5</span>    <span class="hljs-number">0.0278</span>
<span class="hljs-number">6</span>         <span class="hljs-number">6</span>    <span class="hljs-number">0.0833</span>
<span class="hljs-number">7</span>         <span class="hljs-number">7</span>    <span class="hljs-number">0.222</span> 
<span class="hljs-number">8</span>         <span class="hljs-number">8</span>    <span class="hljs-number">0.139</span>
</code></pre><h2 id="heading-models-evaluation">Models evaluation</h2>
<p>Finally, let us display the accuracy results by adding previous ensemble models to a modeltime table, calibrating the latter and piping it into the <code>modeltime_accuracy()</code> function. We sort by ascending RMSE.</p>
<pre><code><span class="hljs-string">modeltime_table(</span>
  <span class="hljs-string">ensemble_fit_mean,</span>
  <span class="hljs-string">ensemble_fit_median,</span>
  <span class="hljs-string">ensemble_fit_wt</span>
<span class="hljs-string">)</span> <span class="hljs-string">%&gt;%</span>
  <span class="hljs-string">modeltime_calibrate(testing(splits))</span> <span class="hljs-string">%&gt;%</span>
  <span class="hljs-string">modeltime_accuracy(testing(splits))</span> <span class="hljs-string">%&gt;%</span>
  <span class="hljs-string">arrange(rmse)</span>

<span class="hljs-comment"># A tibble: 3 x 9</span>
  <span class="hljs-string">.model_id</span> <span class="hljs-string">.model_desc</span>                   <span class="hljs-string">.type</span>   <span class="hljs-string">mae</span>  <span class="hljs-string">mape</span>  <span class="hljs-string">mase</span> <span class="hljs-string">smape</span>  <span class="hljs-string">rmse</span>   <span class="hljs-string">rsq</span>
      <span class="hljs-string">&lt;int&gt;</span> <span class="hljs-string">&lt;chr&gt;</span>                         <span class="hljs-string">&lt;chr&gt;</span> <span class="hljs-string">&lt;dbl&gt;</span> <span class="hljs-string">&lt;dbl&gt;</span> <span class="hljs-string">&lt;dbl&gt;</span> <span class="hljs-string">&lt;dbl&gt;</span> <span class="hljs-string">&lt;dbl&gt;</span> <span class="hljs-string">&lt;dbl&gt;</span>
<span class="hljs-number">1</span>         <span class="hljs-number">3</span> <span class="hljs-string">ENSEMBLE</span> <span class="hljs-string">(WEIGHTED):</span> <span class="hljs-number">8</span> <span class="hljs-string">MODELS</span> <span class="hljs-string">Test</span>  <span class="hljs-number">0.176</span>  <span class="hljs-number">20.7</span> <span class="hljs-number">0.388</span>  <span class="hljs-number">19.5</span> <span class="hljs-number">0.237</span> <span class="hljs-number">0.840</span>
<span class="hljs-number">2</span>         <span class="hljs-number">1</span> <span class="hljs-string">ENSEMBLE</span> <span class="hljs-string">(MEAN):</span> <span class="hljs-number">8</span> <span class="hljs-string">MODELS</span>     <span class="hljs-string">Test</span>  <span class="hljs-number">0.189</span>  <span class="hljs-number">21.8</span> <span class="hljs-number">0.417</span>  <span class="hljs-number">20.6</span> <span class="hljs-number">0.255</span> <span class="hljs-number">0.819</span>
<span class="hljs-number">3</span>         <span class="hljs-number">2</span> <span class="hljs-string">ENSEMBLE</span> <span class="hljs-string">(MEDIAN):</span> <span class="hljs-number">8</span> <span class="hljs-string">MODELS</span>   <span class="hljs-string">Test</span>  <span class="hljs-number">0.204</span>  <span class="hljs-number">23.0</span> <span class="hljs-number">0.450</span>  <span class="hljs-number">21.8</span> <span class="hljs-number">0.275</span> <span class="hljs-number">0.797</span>
</code></pre><p>As per the table below the <strong>weighted average ensemble</strong> performs better than other two average ensembles.</p>
<p>Let us now check if it performs better than the individual workflows from <a target="_blank" href="https://blog.bguarisma.com/time-series-forecasting-and-ensemble-stacking-part-4">Part 4</a>.</p>
<p>We add the ensembles' calibration table to the non-tuned and tuned models' calibration table (<code>calibration_tbl</code>).</p>
<pre><code><span class="hljs-string">&gt;</span> <span class="hljs-string">calibration_all_tbl</span> <span class="hljs-string">&lt;-</span> <span class="hljs-string">modeltime_table(</span>
  <span class="hljs-string">ensemble_fit_mean,</span>
  <span class="hljs-string">ensemble_fit_median,</span>
  <span class="hljs-string">ensemble_fit_wt</span>
<span class="hljs-string">)</span> <span class="hljs-string">%&gt;%</span> 
  <span class="hljs-string">modeltime_calibrate(testing(splits))</span> <span class="hljs-string">%&gt;%</span>
  <span class="hljs-string">combine_modeltime_tables(calibration_tbl)</span>

<span class="hljs-string">&gt;</span> <span class="hljs-string">calibration_all_tbl</span> <span class="hljs-string">%&gt;%</span>
  <span class="hljs-string">modeltime_accuracy()</span> <span class="hljs-string">%&gt;%</span>
  <span class="hljs-string">arrange(rmse)</span>

<span class="hljs-comment"># A tibble: 11 x 9</span>
   <span class="hljs-string">.model_id</span> <span class="hljs-string">.model_desc</span>                       <span class="hljs-string">.type</span>   <span class="hljs-string">mae</span>  <span class="hljs-string">mape</span>  <span class="hljs-string">mase</span> <span class="hljs-string">smape</span>  <span class="hljs-string">rmse</span>   <span class="hljs-string">rsq</span>
       <span class="hljs-string">&lt;int&gt;</span> <span class="hljs-string">&lt;chr&gt;</span>                             <span class="hljs-string">&lt;chr&gt;</span> <span class="hljs-string">&lt;dbl&gt;</span> <span class="hljs-string">&lt;dbl&gt;</span> <span class="hljs-string">&lt;dbl&gt;</span> <span class="hljs-string">&lt;dbl&gt;</span> <span class="hljs-string">&lt;dbl&gt;</span> <span class="hljs-string">&lt;dbl&gt;</span>
 <span class="hljs-number">1</span>         <span class="hljs-number">3</span> <span class="hljs-string">ENSEMBLE</span> <span class="hljs-string">(WEIGHTED):</span> <span class="hljs-number">8</span> <span class="hljs-string">MODELS</span>     <span class="hljs-string">Test</span>  <span class="hljs-number">0.176</span>  <span class="hljs-number">20.7</span> <span class="hljs-number">0.388</span>  <span class="hljs-number">19.5</span> <span class="hljs-number">0.237</span> <span class="hljs-number">0.840</span>
 <span class="hljs-number">2</span>        <span class="hljs-number">10</span> <span class="hljs-string">PROPHET</span> <span class="hljs-string">W/</span> <span class="hljs-string">REGRESSORS</span>             <span class="hljs-string">Test</span>  <span class="hljs-number">0.189</span>  <span class="hljs-number">21.6</span> <span class="hljs-number">0.417</span>  <span class="hljs-number">21.5</span> <span class="hljs-number">0.240</span> <span class="hljs-number">0.856</span>
 <span class="hljs-number">3</span>         <span class="hljs-number">6</span> <span class="hljs-string">PROPHET</span> <span class="hljs-string">W/</span> <span class="hljs-string">REGRESSORS</span> <span class="hljs-bullet">-</span> <span class="hljs-string">Tuned</span>     <span class="hljs-string">Test</span>  <span class="hljs-number">0.189</span>  <span class="hljs-number">21.6</span> <span class="hljs-number">0.418</span>  <span class="hljs-number">21.5</span> <span class="hljs-number">0.241</span> <span class="hljs-number">0.857</span>
 <span class="hljs-number">4</span>         <span class="hljs-number">7</span> <span class="hljs-string">PROPHET</span> <span class="hljs-string">W/</span> <span class="hljs-string">XGBOOST</span> <span class="hljs-string">ERRORS</span> <span class="hljs-bullet">-</span> <span class="hljs-string">Tuned</span> <span class="hljs-string">Test</span>  <span class="hljs-number">0.191</span>  <span class="hljs-number">25.7</span> <span class="hljs-number">0.421</span>  <span class="hljs-number">19.9</span> <span class="hljs-number">0.253</span> <span class="hljs-number">0.780</span>
 <span class="hljs-number">5</span>         <span class="hljs-number">1</span> <span class="hljs-string">ENSEMBLE</span> <span class="hljs-string">(MEAN):</span> <span class="hljs-number">8</span> <span class="hljs-string">MODELS</span>         <span class="hljs-string">Test</span>  <span class="hljs-number">0.189</span>  <span class="hljs-number">21.8</span> <span class="hljs-number">0.417</span>  <span class="hljs-number">20.6</span> <span class="hljs-number">0.255</span> <span class="hljs-number">0.819</span>
 <span class="hljs-number">6</span>         <span class="hljs-number">2</span> <span class="hljs-string">ENSEMBLE</span> <span class="hljs-string">(MEDIAN):</span> <span class="hljs-number">8</span> <span class="hljs-string">MODELS</span>       <span class="hljs-string">Test</span>  <span class="hljs-number">0.204</span>  <span class="hljs-number">23.0</span> <span class="hljs-number">0.450</span>  <span class="hljs-number">21.8</span> <span class="hljs-number">0.275</span> <span class="hljs-number">0.797</span>
 <span class="hljs-number">7</span>        <span class="hljs-number">11</span> <span class="hljs-string">PROPHET</span> <span class="hljs-string">W/</span> <span class="hljs-string">XGBOOST</span> <span class="hljs-string">ERRORS</span>         <span class="hljs-string">Test</span>  <span class="hljs-number">0.204</span>  <span class="hljs-number">24.8</span> <span class="hljs-number">0.451</span>  <span class="hljs-number">20.8</span> <span class="hljs-number">0.287</span> <span class="hljs-number">0.753</span>
 <span class="hljs-number">8</span>         <span class="hljs-number">5</span> <span class="hljs-string">XGBOOST</span> <span class="hljs-bullet">-</span> <span class="hljs-string">Tuned</span>                   <span class="hljs-string">Test</span>  <span class="hljs-number">0.219</span>  <span class="hljs-number">23.6</span> <span class="hljs-number">0.482</span>  <span class="hljs-number">22.9</span> <span class="hljs-number">0.296</span> <span class="hljs-number">0.762</span>
 <span class="hljs-number">9</span>         <span class="hljs-number">9</span> <span class="hljs-string">XGBOOST</span>                           <span class="hljs-string">Test</span>  <span class="hljs-number">0.216</span>  <span class="hljs-number">25.0</span> <span class="hljs-number">0.476</span>  <span class="hljs-number">22.4</span> <span class="hljs-number">0.299</span> <span class="hljs-number">0.747</span>
<span class="hljs-number">10</span>         <span class="hljs-number">4</span> <span class="hljs-string">RANGER</span> <span class="hljs-bullet">-</span> <span class="hljs-string">Tuned</span>                    <span class="hljs-string">Test</span>  <span class="hljs-number">0.231</span>  <span class="hljs-number">26.1</span> <span class="hljs-number">0.509</span>  <span class="hljs-number">24.6</span> <span class="hljs-number">0.303</span> <span class="hljs-number">0.766</span>
<span class="hljs-number">11</span>         <span class="hljs-number">8</span> <span class="hljs-string">RANGER</span>                            <span class="hljs-string">Test</span>  <span class="hljs-number">0.235</span>  <span class="hljs-number">25.4</span> <span class="hljs-number">0.519</span>  <span class="hljs-number">25.0</span> <span class="hljs-number">0.312</span> <span class="hljs-number">0.765</span>
</code></pre><h2 id="heading-forescast-plot">Forescast plot</h2>
<p>We have already plotted all tuned and non-tuned models' forecast in <a target="_blank" href="https://blog.bguarisma.com/time-series-forecasting-lab-part-4-hyperparameter-tuning">Part 4</a>, here we plot only the average ensembles' forecast.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1642702803022/jQucOypYT.png" alt="p5_ensemble_fcst_plot.png" /></p>
<h2 id="heading-conclusion">Conclusion</h2>
<p>In this article you've learned how to create <strong>average (mean or median) ensembles</strong> and <strong>weighted average ensembles</strong>. The latter was created using the rank technique.</p>
<p>Since one of the average ensemble was the best model, we did not bother with improving ensemble models' performance with <strong>model selection</strong> e.g., selecting the <strong>top 5 models</strong> and/or <strong>suppressing redundant models</strong> such as the tuned and non-tuned Prophet models which have the same performance. </p>
<p>In Part 5, we will try to outperform the weighted average ensemble .</p>
<h2 id="heading-references">References</h2>
<p><a target="_blank" href="https://university.business-science.io/courses">1</a> Dancho, Matt, "<em>DS4B 203-R: High-Performance Time Series Forecasting</em>", Business Science University</p>
]]></content:encoded></item><item><title><![CDATA[Time Series Forecasting Lab (Part 4) - Hyperparameter Tuning]]></title><description><![CDATA[Cover photo by christian buehner on Unsplash
Go to R-bloggers for R news and tutorials contributed by hundreds of R bloggers.
Introduction
This is the fourth of a  series  of 6 articles about time series forecasting with panel data and ensemble stack...]]></description><link>https://blog.bguarisma.com/time-series-forecasting-lab-part-4-hyperparameter-tuning</link><guid isPermaLink="true">https://blog.bguarisma.com/time-series-forecasting-lab-part-4-hyperparameter-tuning</guid><category><![CDATA[R Language]]></category><category><![CDATA[Machine Learning]]></category><dc:creator><![CDATA[Boris Guarisma]]></dc:creator><pubDate>Sat, 15 Jan 2022 15:43:45 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1641977451024/aEqAPaYpq.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Cover photo by <a href="https://unsplash.com/@christianbuehner?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText">christian buehner</a> on <a href="https://unsplash.com/s/photos/mechanics?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText">Unsplash</a></p>
<p>Go to <a target="_blank" href="https://www.r-bloggers.com/">R-bloggers</a> for R news and tutorials contributed by hundreds of R bloggers.</p>
<h2 id="heading-introduction">Introduction</h2>
<p>This is the fourth of a  <a target="_blank" href="https://blog.bguarisma.com/series/time-series-forecasting">series</a>  of 6 articles about time series forecasting with panel data and ensemble stacking with R.</p>
<p>Through these articles I will be putting into practice what I have learned from the Business Science University training course  <a target="_blank" href="https://university.business-science.io/courses">1</a> "DS4B 203-R: High-Performance Time Series Forecasting", delivered by Matt Dancho. If you are looking to gain a high level of expertise in time series with R I strongly recommend this course.  </p>
<p>The objective of this article is to explain the end-to-end process of time series hyperparamter tuning for <em>non-sequential</em> machine learning models like Random Forest, XGBoost, Prophet, and Prophet Boost. k-fold cross-validation can be applied to non-sequential algorithms.</p>
<p>You will understand how to tune parameters for <strong>Prophet Boost</strong> by performing several tuning rounds to adjust and stabilize the performance of the model. The hyperparameter tuning process is the same for the other algorithms.</p>
<p>The provided code is <strong>highly redundant</strong> for the sake of clarity. There is a lot of room for optimization with custom functions.</p>
<h3 id="heading-prerequisites">Prerequisites</h3>
<p>I assume you are already familiar with the following topics, packages and terms:</p>
<ul>
<li><p>dplyr or tidyverse R packages</p>
</li>
<li><p>preprocessing recipes from  <a target="_blank" href="https://blog.bguarisma.com/time-series-forecasting-and-ensemble-stacking-part-2">Part 2</a> </p>
</li>
<li><p>model specification and workflows from  <a target="_blank" href="https://blog.bguarisma.com/time-series-forecasting-and-ensemble-stacking-part-3">Part 3</a> </p>
</li>
<li><p>k-fold cross-validation</p>
</li>
<li><p>parameters of Random Forest, XGBoost, Prophet, and Prophet Boost algorithms</p>
</li>
<li><p>grid search</p>
</li>
<li><p>model evaluation metrics: RMSE, R-squared, ...</p>
</li>
</ul>
<h2 id="heading-packages">Packages</h2>
<p>The following packages must be loaded:</p>
<pre><code><span class="hljs-comment"># 01 FEATURE ENGINEERING</span>
<span class="hljs-keyword">library</span>(tidyverse)  <span class="hljs-comment"># loading dplyr, tibble, ggplot2, .. dependencies</span>
<span class="hljs-keyword">library</span>(timetk)  <span class="hljs-comment"># using timetk plotting, diagnostics and augment operations</span>
<span class="hljs-keyword">library</span>(tsibble)  <span class="hljs-comment"># for month to Date conversion</span>
<span class="hljs-keyword">library</span>(tsibbledata)  <span class="hljs-comment"># for aus_retail dataset</span>
<span class="hljs-keyword">library</span>(fastDummies)  <span class="hljs-comment"># for dummyfying categorical variables</span>
<span class="hljs-keyword">library</span>(skimr) <span class="hljs-comment"># for quick statistics</span>

<span class="hljs-comment"># 02 FEATURE ENGINEERING WITH RECIPES</span>
<span class="hljs-keyword">library</span>(tidymodels) <span class="hljs-comment"># with workflow dependency</span>

<span class="hljs-comment"># 03 MACHINE LEARNING</span>
<span class="hljs-keyword">library</span>(modeltime) <span class="hljs-comment"># ML models specifications and engines</span>
<span class="hljs-keyword">library</span>(tictoc) <span class="hljs-comment"># measure training elapsed time</span>

<span class="hljs-comment"># 04 HYPERPARAMETER TUNING</span>
<span class="hljs-keyword">library</span>(future)
<span class="hljs-keyword">library</span>(doFuture)
<span class="hljs-keyword">library</span>(plotly)
</code></pre><h2 id="heading-hyperparameter-tuning-end-to-end-process">Hyperparameter Tuning end-to-end process</h2>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1641886231392/NhgKEbaPv.png" alt="p4_01_CV-plan.png" /></p>
<p>The end-to-end process is as follows:</p>
<ol>
<li><p>Get the resamples. Here we will perform a k-fold cross-validation and obtain a cross-validation plan that we can plot to see "inside the folds".</p>
</li>
<li><p>Prepare for parallel process: register to future and get the number of vCores.</p>
</li>
<li><p>Define the tunable model specification: explicitly indicate the tunable parameters.</p>
</li>
<li><p>Define a tunable workflow into which we add the tunable model specification and the existing recipe; update the recipe if necessary.</p>
</li>
<li><p>Verify that the system has all tunable parameters' range, some parameter such as <code>mtry</code> do not have its values range correctly initialized.</p>
</li>
<li><p>Define the search grid specification: you must provide type of grid (random or latin hypercube sampling) and its size.</p>
</li>
<li><p>Toggle on the parallel processing</p>
</li>
<li><p>Run the hyperparameter tuning by provding the tunable workflow, the resamples, and the grid specification.</p>
</li>
<li><p>Run several tuning rounds if you can e.g., if you have the time and resources and/or you are able to fix some parameter ranges.</p>
</li>
<li><p>After several tuning rounds, select the best model: with the lowest RMSE or with the higest R-squared, it may be same model or they can be different models.</p>
</li>
<li><p>(Re)fit the best model(s) with the training data by providing the tuned workflow.</p>
</li>
</ol>
<h2 id="heading-reminder-of-part-3-results">Reminder of Part 3 results</h2>
<p>Let us recall the <strong>non-tuned</strong> model evaluation results for the 4 machine learning algorithms:</p>
<pre><code><span class="hljs-comment"># Load saved work from Part 3</span>
<span class="hljs-string">&gt;</span> <span class="hljs-string">wflw_artifacts</span> <span class="hljs-string">&lt;-</span> <span class="hljs-string">read_rds("workflows_artifacts_list.rds")</span>

<span class="hljs-string">&gt;</span> <span class="hljs-string">wflw_artifacts$calibration$calibration_tbl</span> <span class="hljs-string">%&gt;%</span>
   <span class="hljs-string">modeltime_accuracy(testing(splits))</span> <span class="hljs-string">%&gt;%</span>
   <span class="hljs-string">arrange(rmse)</span>

<span class="hljs-comment"># A tibble: 4 x 9</span>
  <span class="hljs-string">.model_id</span> <span class="hljs-string">.model_desc</span>               <span class="hljs-string">.type</span>   <span class="hljs-string">mae</span>  <span class="hljs-string">mape</span>  <span class="hljs-string">mase</span> <span class="hljs-string">smape</span>  <span class="hljs-string">rmse</span>   <span class="hljs-string">rsq</span>
      <span class="hljs-string">&lt;int&gt;</span> <span class="hljs-string">&lt;chr&gt;</span>                     <span class="hljs-string">&lt;chr&gt;</span> <span class="hljs-string">&lt;dbl&gt;</span> <span class="hljs-string">&lt;dbl&gt;</span> <span class="hljs-string">&lt;dbl&gt;</span> <span class="hljs-string">&lt;dbl&gt;</span> <span class="hljs-string">&lt;dbl&gt;</span> <span class="hljs-string">&lt;dbl&gt;</span>
<span class="hljs-number">1</span>         <span class="hljs-number">3</span> <span class="hljs-string">PROPHET</span> <span class="hljs-string">W/</span> <span class="hljs-string">REGRESSORS</span>     <span class="hljs-string">Test</span>  <span class="hljs-number">0.213</span>  <span class="hljs-number">22.9</span> <span class="hljs-number">0.470</span>  <span class="hljs-number">22.9</span> <span class="hljs-number">0.267</span> <span class="hljs-number">0.846</span>
<span class="hljs-number">2</span>         <span class="hljs-number">4</span> <span class="hljs-string">PROPHET</span> <span class="hljs-string">W/</span> <span class="hljs-string">XGBOOST</span> <span class="hljs-string">ERRORS</span> <span class="hljs-string">Test</span>  <span class="hljs-number">0.203</span>  <span class="hljs-number">27.1</span> <span class="hljs-number">0.448</span>  <span class="hljs-number">20.6</span> <span class="hljs-number">0.283</span> <span class="hljs-number">0.750</span>
<span class="hljs-number">3</span>         <span class="hljs-number">2</span> <span class="hljs-string">XGBOOST</span>                   <span class="hljs-string">Test</span>  <span class="hljs-number">0.228</span>  <span class="hljs-number">26.9</span> <span class="hljs-number">0.503</span>  <span class="hljs-number">22.7</span> <span class="hljs-number">0.306</span> <span class="hljs-number">0.761</span>
<span class="hljs-number">4</span>         <span class="hljs-number">1</span> <span class="hljs-string">RANGER</span>                    <span class="hljs-string">Test</span>  <span class="hljs-number">0.244</span>  <span class="hljs-number">25.6</span> <span class="hljs-number">0.538</span>  <span class="hljs-number">25.3</span> <span class="hljs-number">0.322</span> <span class="hljs-number">0.763</span>
</code></pre><p>Note: "RANGER" is the default <em>engine</em> for Random Forest <code>modeltime::rand_forest()</code>.</p>
<h2 id="heading-cross-validation-plan">Cross-validation plan</h2>
<p>A <strong>k-fold cross-validation</strong> will randomly split the training data  into k groups of roughly equal size (called "folds"). A resample of the analysis data consisted of k-1 of the folds while the assessment set contains the final fold. In basic k-fold cross-validation (i.e. no repeats), the number of resamples is equal to k.</p>
<p>It is important to understand that you may perform a k-fold cross-validation for all four machine learning models since they are <strong>non-sequential models</strong>. This is not the case for other models such as ARIMA, ETS (exponential smoothing) and NNETAR for which you should perform a <code>time_series_cv()</code> cross-validation.</p>
<p>Perform the resample with function <code>rsample::vfold_cv()</code> as shown in the code snippet below. You may also display the cross validation plan and its plot (here, for one Industry) with <code>tk_time_series_cv_plan()</code>  and <code>plot_time_series_cv_plan()</code>, respectively.</p>
<pre><code># k = <span class="hljs-number">10</span> folds
&gt; <span class="hljs-keyword">set</span>.seed(<span class="hljs-number">123</span>)
&gt; resamples_kfold &lt;- training(splits) %&gt;% 
  vfold_cv(v = <span class="hljs-number">10</span>)

resamples_kfold %&gt;%
  tk_time_series_cv_plan() %&gt;%
  <span class="hljs-keyword">filter</span>(Industry == Industries[<span class="hljs-number">1</span>]) %&gt;%
  plot_time_series_cv_plan(.date_var = Month, 
                             .<span class="hljs-keyword">value</span> = Turnover, 
                             .facet_ncol = <span class="hljs-number">2</span>)
</code></pre><p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1642700228626/tLZMyYKcu.png" alt="p4_kfold_cv_plan.png" /></p>
<h2 id="heading-setup-parallel-processing">Setup parallel processing</h2>
<p>As per <a target="_blank" href="https://university.business-science.io/courses">1</a>, "the  <a target="_blank" href="https://future.futureverse.org/">future</a>  library allows for evaluating R expressions asynchronously. The  <a target="_blank" href="https://github.com/HenrikBengtsson/doFuture">doFuture</a>  package is an adapter that provides for running loops in parallel. The  <a target="_blank" href="https://tune.tidymodels.org/">tune</a>  package manages the adapter process internally, so all we need to do is to set up doFuture".</p>
<pre><code><span class="hljs-comment"># Registers the doFuture parallel processing</span>
registerDoFuture()

<span class="hljs-comment"># My laptop i5-8350U CPU has 8 threads (or vCores)</span>
n_cores &lt;- parallel::detectCores()
</code></pre><h2 id="heading-prophet-boost">Prophet Boost</h2>
<h3 id="heading-prophet-boost-identify-tuning-parameters">Prophet Boost - Identify tuning parameters</h3>
<p>I've decided to tune the following parameters, you can run <code>?prophet_boost</code> for a full description:</p>
<ul>
<li><p><code>changepoint_num</code> (prophet): Number of potential changepoints to include for modeling trend.</p>
</li>
<li><p><code>mtry</code> (xgboost): An integer for the number of predictors that will be randomly sampled at each split when creating the tree models. </p>
</li>
<li><p><code>trees</code> (xgboost): An integer for the number of trees contained in the ensemble</p>
</li>
<li><p><code>min_n</code> (xgboost): An integer for the minimum number of data points in a node that are required for the node to be split further.
tree_depth: An integer for the maximum depth of the tree (i.e. number of splits) (specific engines only).</p>
</li>
<li><p><code>learn_rate</code> (xgboost): A number for the rate at which the boosting algorithm adapts from iteration-to-iteration.</p>
</li>
<li><p><code>loss_reduction</code> (xgboost): A number for the reduction in the loss function required to split further.</p>
</li>
<li><p><code>tree_depth</code> (xgboost): An integer for the maximum depth of the tree (i.e. number of splits).</p>
</li>
</ul>
<p>Define the tunable parameters with <code>tune()</code>  within the model specification of  <code>prophet_boost()</code>. Then,  define a new tunable workflow for Prophet Boost (<code>wflw_spec_rf_tune</code>) by adding the tunable specification and the same recipe as in  <a target="_blank" href="https://blog.bguarisma.com/time-series-forecasting-and-ensemble-stacking-part-3">Part 3</a>. Display the summary of the workflow as shown in the code snippet below.</p>
<pre><code><span class="hljs-string">&gt;</span> <span class="hljs-string">model_spec_prophet_boost_tune</span> <span class="hljs-string">&lt;-</span> <span class="hljs-string">prophet_boost(</span>

  <span class="hljs-string">mode</span> <span class="hljs-string">=</span> <span class="hljs-string">"regression"</span><span class="hljs-string">,</span>
  <span class="hljs-comment"># growth = NULL,</span>
  <span class="hljs-string">changepoint_num</span> <span class="hljs-string">=</span> <span class="hljs-string">tune(),</span>
  <span class="hljs-comment">#changepoint_range = NULL,</span>
  <span class="hljs-string">seasonality_yearly</span> <span class="hljs-string">=</span> <span class="hljs-literal">FALSE</span><span class="hljs-string">,</span>
  <span class="hljs-string">seasonality_weekly</span> <span class="hljs-string">=</span> <span class="hljs-literal">FALSE</span><span class="hljs-string">,</span>
  <span class="hljs-string">seasonality_daily</span> <span class="hljs-string">=</span> <span class="hljs-literal">FALSE</span><span class="hljs-string">,</span>
  <span class="hljs-comment"># season = NULL,</span>
  <span class="hljs-comment"># prior_scale_changepoints = NULL,</span>
  <span class="hljs-comment"># prior_scale_seasonality = NULL,</span>
  <span class="hljs-comment"># prior_scale_holidays = NULL,</span>
  <span class="hljs-comment"># logistic_cap = NULL,</span>
  <span class="hljs-comment"># logistic_floor = NULL,</span>
  <span class="hljs-string">mtry</span> <span class="hljs-string">=</span> <span class="hljs-string">tune(),</span>
  <span class="hljs-string">trees</span> <span class="hljs-string">=</span> <span class="hljs-string">tune(),</span>
  <span class="hljs-string">min_n</span> <span class="hljs-string">=</span> <span class="hljs-string">tune(),</span>
  <span class="hljs-string">tree_depth</span> <span class="hljs-string">=</span> <span class="hljs-string">tune(),</span>
  <span class="hljs-string">learn_rate</span> <span class="hljs-string">=</span> <span class="hljs-string">tune(),</span>
  <span class="hljs-string">loss_reduction</span> <span class="hljs-string">=</span> <span class="hljs-string">tune(),</span>
  <span class="hljs-comment"># sample_size = NULL,</span>
  <span class="hljs-comment"># stop_iter = NULL</span>

<span class="hljs-string">)</span> <span class="hljs-string">%&gt;%</span>
  <span class="hljs-string">set_engine("prophet_xgboost")</span>

<span class="hljs-string">&gt;</span> <span class="hljs-string">wflw_spec_prophet_boost_tune</span> <span class="hljs-string">&lt;-</span> <span class="hljs-string">workflow()</span> <span class="hljs-string">%&gt;%</span>
  <span class="hljs-string">add_model(model_spec_prophet_boost_tune)</span> <span class="hljs-string">%&gt;%</span>
  <span class="hljs-string">add_recipe(artifacts$recipes$recipe_spec)</span>

<span class="hljs-string">&gt;</span> <span class="hljs-string">wflw_spec_prophet_boost_tune</span>
<span class="hljs-string">==</span> <span class="hljs-string">Workflow</span> <span class="hljs-string">===================================================================================================================================</span>
<span class="hljs-attr">Preprocessor:</span> <span class="hljs-string">Recipe</span>
<span class="hljs-attr">Model:</span> <span class="hljs-string">prophet_boost()</span>

<span class="hljs-string">--</span> <span class="hljs-string">Preprocessor</span> <span class="hljs-string">-------------------------------------------------------------------------------------------------------------------------------</span>
<span class="hljs-number">5</span> <span class="hljs-string">Recipe</span> <span class="hljs-string">Steps</span>

<span class="hljs-string">*</span> <span class="hljs-string">step_other()</span>
<span class="hljs-string">*</span> <span class="hljs-string">step_timeseries_signature()</span>
<span class="hljs-string">*</span> <span class="hljs-string">step_rm()</span>
<span class="hljs-string">*</span> <span class="hljs-string">step_dummy()</span>
<span class="hljs-string">*</span> <span class="hljs-string">step_normalize()</span>

<span class="hljs-string">--</span> <span class="hljs-string">Model</span> <span class="hljs-string">--------------------------------------------------------------------------------------------------------------------------------------</span>
<span class="hljs-string">PROPHET</span> <span class="hljs-string">Regression</span> <span class="hljs-string">Model</span> <span class="hljs-string">Specification</span> <span class="hljs-string">(regression)</span>

<span class="hljs-attr">Main Arguments:</span>
  <span class="hljs-string">changepoint_num</span> <span class="hljs-string">=</span> <span class="hljs-string">tune()</span>
  <span class="hljs-string">seasonality_yearly</span> <span class="hljs-string">=</span> <span class="hljs-literal">FALSE</span>
  <span class="hljs-string">seasonality_weekly</span> <span class="hljs-string">=</span> <span class="hljs-literal">FALSE</span>
  <span class="hljs-string">seasonality_daily</span> <span class="hljs-string">=</span> <span class="hljs-literal">FALSE</span>
  <span class="hljs-string">mtry</span> <span class="hljs-string">=</span> <span class="hljs-string">tune()</span>
  <span class="hljs-string">trees</span> <span class="hljs-string">=</span> <span class="hljs-string">tune()</span>
  <span class="hljs-string">min_n</span> <span class="hljs-string">=</span> <span class="hljs-string">tune()</span>
  <span class="hljs-string">tree_depth</span> <span class="hljs-string">=</span> <span class="hljs-string">tune()</span>
  <span class="hljs-string">learn_rate</span> <span class="hljs-string">=</span> <span class="hljs-string">tune()</span>
  <span class="hljs-string">loss_reduction</span> <span class="hljs-string">=</span> <span class="hljs-string">tune()</span>

<span class="hljs-attr">Computational engine:</span> <span class="hljs-string">prophet_xgboost</span>
</code></pre><h3 id="heading-prophet-boost-grid-spec">Prophet Boost - Grid spec</h3>
<h4 id="heading-check-parameters-range">Check parameters' range</h4>
<p>The first thing to do is to display the parameters of the model with <code>extract_parameter_set_dials()</code>: you must check if there is any parameter with missing information about its values range.</p>
<p>As shown below, <code>nparam[?]</code> means that values range is missing for <code>mtry</code> parameter. </p>
<pre><code>&gt; <span class="hljs-selector-tag">extract_parameter_set_dials</span>(model_spec_prophet_boost_tune)
<span class="hljs-selector-tag">Collection</span> <span class="hljs-selector-tag">of</span> <span class="hljs-selector-tag">3</span> <span class="hljs-selector-tag">parameters</span> <span class="hljs-selector-tag">for</span> <span class="hljs-selector-tag">tuning</span>

 <span class="hljs-selector-tag">identifier</span>  <span class="hljs-selector-tag">type</span>    <span class="hljs-selector-tag">object</span>
       <span class="hljs-selector-tag">mtry</span>  <span class="hljs-selector-tag">mtry</span> <span class="hljs-selector-tag">nparam</span><span class="hljs-selector-attr">[?]</span>
      <span class="hljs-selector-tag">trees</span> <span class="hljs-selector-tag">trees</span> <span class="hljs-selector-tag">nparam</span><span class="hljs-selector-attr">[+]</span>
      <span class="hljs-selector-tag">min_n</span> <span class="hljs-selector-tag">min_n</span> <span class="hljs-selector-tag">nparam</span><span class="hljs-selector-attr">[+]</span>

<span class="hljs-selector-tag">Model</span> <span class="hljs-selector-tag">parameters</span> <span class="hljs-selector-tag">needing</span> <span class="hljs-selector-tag">finalization</span>:
   # <span class="hljs-selector-tag">Randomly</span> <span class="hljs-selector-tag">Selected</span> <span class="hljs-selector-tag">Predictors</span> (<span class="hljs-string">'mtry'</span>)

<span class="hljs-selector-tag">See</span> `?<span class="hljs-selector-tag">dials</span><span class="hljs-selector-pseudo">::finalize</span>` <span class="hljs-selector-tag">or</span> `?<span class="hljs-selector-tag">dials</span><span class="hljs-selector-pseudo">::update.parameters</span>` <span class="hljs-selector-tag">for</span> <span class="hljs-selector-tag">more</span> <span class="hljs-selector-tag">information</span>.
</code></pre><p>Check the number of predictors looking at the recipe's summary.</p>
<pre><code><span class="hljs-string">&gt;</span> <span class="hljs-string">artifacts$recipes$recipe_spec</span> <span class="hljs-string">%&gt;%</span> 
   <span class="hljs-string">update_role(Month,</span> <span class="hljs-string">new_role</span> <span class="hljs-string">=</span> <span class="hljs-string">"indicator"</span><span class="hljs-string">)</span> <span class="hljs-string">%&gt;%</span>
   <span class="hljs-string">prep()</span> <span class="hljs-string">%&gt;%</span>
   <span class="hljs-string">summary()</span> <span class="hljs-string">%&gt;%</span> 
   <span class="hljs-string">group_by(role)</span> <span class="hljs-string">%&gt;%</span> 
   <span class="hljs-string">summarise(n=n())</span>
<span class="hljs-comment"># A tibble: 3 x 2</span>
  <span class="hljs-string">role</span>          <span class="hljs-string">n</span>
  <span class="hljs-string">&lt;chr&gt;</span>     <span class="hljs-string">&lt;int&gt;</span>
<span class="hljs-number">1</span> <span class="hljs-string">indicator</span>     <span class="hljs-number">1</span>
<span class="hljs-number">2</span> <span class="hljs-string">outcome</span>       <span class="hljs-number">1</span>
<span class="hljs-number">3</span> <span class="hljs-string">predictor</span>    <span class="hljs-number">50</span>
</code></pre><p>The new range update will be applied within the grid specifications in the next code snippet.</p>
<h4 id="heading-grid-search-specification">Grid search specification</h4>
<p>Thanks to the  <a target="_blank" href="https://dials.tidymodels.org/">dials</a>  package it is possible to define specifications for either Random Grid or Latin Hypercube Sampling (LHS).</p>
<p>We define <strong>a grid of size 20</strong> e.g. 20 random values combinations of the 7 tunable parameters. Remember to set the <em>seed</em> to fix the grid since it uses a random process, you may have different values.</p>
<p>Since we have defined a 10-fold cross-validation there will be 20 predictions per fold and the mean of the performance metric (RMSE, R-squared, ...) will be calculated.</p>
<pre><code><span class="hljs-string">set.seed(123)</span>
<span class="hljs-string">grid_spec_1</span> <span class="hljs-string">&lt;-</span> <span class="hljs-string">grid_latin_hypercube(</span>
  <span class="hljs-string">extract_parameter_set_dials(model_spec_prophet_boost_tune)</span> <span class="hljs-string">%&gt;%</span> 
    <span class="hljs-string">update(mtry</span> <span class="hljs-string">=</span> <span class="hljs-string">mtry(range</span> <span class="hljs-string">=</span> <span class="hljs-string">c(1,</span> <span class="hljs-number">50</span><span class="hljs-string">))),</span>
  <span class="hljs-string">size</span> <span class="hljs-string">=</span> <span class="hljs-number">20</span>
<span class="hljs-string">)</span>

<span class="hljs-string">&gt;</span> <span class="hljs-string">grid_spec_1</span>
<span class="hljs-comment"># A tibble: 20 x 7</span>
   <span class="hljs-string">changepoint_num</span>  <span class="hljs-string">mtry</span> <span class="hljs-string">trees</span> <span class="hljs-string">min_n</span> <span class="hljs-string">tree_depth</span> <span class="hljs-string">learn_rate</span> <span class="hljs-string">loss_reduction</span>
             <span class="hljs-string">&lt;int&gt;</span> <span class="hljs-string">&lt;int&gt;</span> <span class="hljs-string">&lt;int&gt;</span> <span class="hljs-string">&lt;int&gt;</span>      <span class="hljs-string">&lt;int&gt;</span>      <span class="hljs-string">&lt;dbl&gt;</span>          <span class="hljs-string">&lt;dbl&gt;</span>
 <span class="hljs-number">1</span>               <span class="hljs-number">6</span>     <span class="hljs-number">9</span>   <span class="hljs-number">145</span>    <span class="hljs-number">26</span>          <span class="hljs-number">5</span>   <span class="hljs-number">2.</span><span class="hljs-string">50e-</span> <span class="hljs-number">7</span>       <span class="hljs-number">1.</span><span class="hljs-string">89e-</span> <span class="hljs-number">6</span>
 <span class="hljs-number">2</span>              <span class="hljs-number">33</span>    <span class="hljs-number">33</span>  <span class="hljs-number">1538    </span><span class="hljs-number">18</span>         <span class="hljs-number">14</span>   <span class="hljs-number">5.</span><span class="hljs-string">55e-</span> <span class="hljs-number">6</span>       <span class="hljs-number">2.</span><span class="hljs-string">17e-</span> <span class="hljs-number">2</span>
 <span class="hljs-number">3</span>               <span class="hljs-number">8</span>    <span class="hljs-number">42</span>    <span class="hljs-number">42</span>    <span class="hljs-number">21</span>          <span class="hljs-number">3</span>   <span class="hljs-number">2.</span><span class="hljs-string">22e-</span> <span class="hljs-number">6</span>       <span class="hljs-number">1.</span><span class="hljs-string">63e-</span> <span class="hljs-number">1</span>
 <span class="hljs-number">4</span>               <span class="hljs-number">0</span>    <span class="hljs-number">23</span>  <span class="hljs-number">1183     </span><span class="hljs-number">4</span>         <span class="hljs-number">12</span>   <span class="hljs-number">2.</span><span class="hljs-string">44e-</span> <span class="hljs-number">3</span>       <span class="hljs-number">1.91e-10</span>
 <span class="hljs-number">5</span>              <span class="hljs-number">30</span>    <span class="hljs-number">30</span>  <span class="hljs-number">1261    </span><span class="hljs-number">25</span>         <span class="hljs-number">11</span>   <span class="hljs-number">1.</span><span class="hljs-string">34e-</span> <span class="hljs-number">9</span>       <span class="hljs-number">1.</span><span class="hljs-string">90e-</span> <span class="hljs-number">8</span>
 <span class="hljs-number">6</span>              <span class="hljs-number">22</span>     <span class="hljs-number">5</span>  <span class="hljs-number">1882     </span><span class="hljs-number">3</span>          <span class="hljs-number">7</span>   <span class="hljs-number">1.</span><span class="hljs-string">45e-</span> <span class="hljs-number">8</span>       <span class="hljs-number">1.</span><span class="hljs-string">01e-</span> <span class="hljs-number">7</span>
 <span class="hljs-number">7</span>              <span class="hljs-number">45</span>    <span class="hljs-number">48</span>   <span class="hljs-number">971</span>    <span class="hljs-number">30</span>          <span class="hljs-number">6</span>   <span class="hljs-number">2.94e-10</span>       <span class="hljs-number">5.</span><span class="hljs-string">34e-</span> <span class="hljs-number">3</span>
 <span class="hljs-number">8</span>              <span class="hljs-number">20</span>     <span class="hljs-number">7</span>   <span class="hljs-number">798</span>    <span class="hljs-number">27</span>         <span class="hljs-number">15</span>   <span class="hljs-number">1.</span><span class="hljs-string">18e-</span> <span class="hljs-number">4</span>       <span class="hljs-number">4.</span><span class="hljs-string">65e-</span> <span class="hljs-number">9</span>
 <span class="hljs-number">9</span>              <span class="hljs-number">14</span>    <span class="hljs-number">22</span>  <span class="hljs-number">1993    </span><span class="hljs-number">12</span>          <span class="hljs-number">3</span>   <span class="hljs-number">6.</span><span class="hljs-string">54e-</span> <span class="hljs-number">7</span>       <span class="hljs-number">1.</span><span class="hljs-string">42e-</span> <span class="hljs-number">4</span>
<span class="hljs-number">10</span>              <span class="hljs-number">27</span>    <span class="hljs-number">37</span>  <span class="hljs-number">1620    </span><span class="hljs-number">35</span>          <span class="hljs-number">8</span>   <span class="hljs-number">3.</span><span class="hljs-string">16e-</span> <span class="hljs-number">2</span>       <span class="hljs-number">7.</span><span class="hljs-string">35e-</span> <span class="hljs-number">8</span>
<span class="hljs-number">11</span>              <span class="hljs-number">23</span>    <span class="hljs-number">45</span>  <span class="hljs-number">1450     </span><span class="hljs-number">8</span>          <span class="hljs-number">4</span>   <span class="hljs-number">6.</span><span class="hljs-string">11e-</span> <span class="hljs-number">4</span>       <span class="hljs-number">7.</span><span class="hljs-string">60e-</span> <span class="hljs-number">2</span>
<span class="hljs-number">12</span>              <span class="hljs-number">31</span>     <span class="hljs-number">1</span>  <span class="hljs-number">1756    </span><span class="hljs-number">14</span>         <span class="hljs-number">13</span>   <span class="hljs-number">8.</span><span class="hljs-string">92e-</span> <span class="hljs-number">8</span>       <span class="hljs-number">2.</span><span class="hljs-string">41e-</span> <span class="hljs-number">3</span>
<span class="hljs-number">13</span>              <span class="hljs-number">37</span>    <span class="hljs-number">19</span>   <span class="hljs-number">402</span>    <span class="hljs-number">39</span>          <span class="hljs-number">9</span>   <span class="hljs-number">2.</span><span class="hljs-string">28e-</span> <span class="hljs-number">9</span>       <span class="hljs-number">4.</span><span class="hljs-string">83e-</span> <span class="hljs-number">4</span>
<span class="hljs-number">14</span>              <span class="hljs-number">39</span>    <span class="hljs-number">46</span>   <span class="hljs-number">245</span>    <span class="hljs-number">31</span>         <span class="hljs-number">11</span>   <span class="hljs-number">2.</span><span class="hljs-string">60e-</span> <span class="hljs-number">8</span>       <span class="hljs-number">9.</span><span class="hljs-string">67e-</span> <span class="hljs-number">7</span>
<span class="hljs-number">15</span>              <span class="hljs-number">42</span>    <span class="hljs-number">13</span>   <span class="hljs-number">336</span>    <span class="hljs-number">37</span>          <span class="hljs-number">2</span>   <span class="hljs-number">6.</span><span class="hljs-string">48e-</span> <span class="hljs-number">3</span>       <span class="hljs-number">5.83e-10</span>
<span class="hljs-number">16</span>              <span class="hljs-number">17</span>    <span class="hljs-number">28</span>  <span class="hljs-number">1311    </span><span class="hljs-number">34</span>          <span class="hljs-number">8</span>   <span class="hljs-number">2.41e-10</span>       <span class="hljs-number">2.</span><span class="hljs-string">92e-</span> <span class="hljs-number">5</span>
<span class="hljs-number">17</span>              <span class="hljs-number">49</span>    <span class="hljs-number">31</span>   <span class="hljs-number">870</span>    <span class="hljs-number">11</span>          <span class="hljs-number">2</span>   <span class="hljs-number">2.</span><span class="hljs-string">32e-</span> <span class="hljs-number">5</span>       <span class="hljs-number">1.</span><span class="hljs-string">70e+</span> <span class="hljs-number">1</span>
<span class="hljs-number">18</span>               <span class="hljs-number">4</span>    <span class="hljs-number">13</span>   <span class="hljs-number">575</span>     <span class="hljs-number">8</span>         <span class="hljs-number">13</span>   <span class="hljs-number">2.</span><span class="hljs-string">68e-</span> <span class="hljs-number">4</span>       <span class="hljs-number">1.</span><span class="hljs-string">09e+</span> <span class="hljs-number">0</span>
<span class="hljs-number">19</span>              <span class="hljs-number">44</span>    <span class="hljs-number">18</span>  <span class="hljs-number">1097    </span><span class="hljs-number">20</span>         <span class="hljs-number">10</span>   <span class="hljs-number">3.</span><span class="hljs-string">33e-</span> <span class="hljs-number">5</span>       <span class="hljs-number">5.</span><span class="hljs-string">98e+</span> <span class="hljs-number">0</span>
<span class="hljs-number">20</span>              <span class="hljs-number">11</span>    <span class="hljs-number">40</span>   <span class="hljs-number">613</span>    <span class="hljs-number">16</span>          <span class="hljs-number">6</span>   <span class="hljs-number">5.</span><span class="hljs-string">62e-</span> <span class="hljs-number">2</span>       <span class="hljs-number">7.</span><span class="hljs-string">32e-</span> <span class="hljs-number">6</span>
</code></pre><h3 id="heading-prophet-boost-tune-grid">Prophet Boost - Tune Grid</h3>
<p>Let us perform 3 tuning rounds with 3 different grid specifications. I will explain at each round why do we perform the corresponding grid spec:</p>
<ul>
<li><p>Round 1 with <code>grid_spec_1</code></p>
</li>
<li><p>Round 2 with <code>grid_spec_2</code></p>
</li>
<li><p>Round 3 with <code>grid_spec_3</code></p>
</li>
</ul>
<p>To tune the workflow with a grid you need to provide the resamples, the grid specification and some controls on the grid search process.</p>
<pre><code><span class="hljs-string">tic()</span>
<span class="hljs-string">tune_results_prophet_boost_1</span> <span class="hljs-string">&lt;-</span> <span class="hljs-string">wflw_spec_prophet_boost_tune</span> <span class="hljs-string">%&gt;%</span>
  <span class="hljs-string">tune_grid(</span>
    <span class="hljs-string">resamples</span>  <span class="hljs-string">=</span> <span class="hljs-string">resamples_kfold,</span>
    <span class="hljs-string">grid</span> <span class="hljs-string">=</span> <span class="hljs-string">grid_spec_1,</span>
    <span class="hljs-string">control</span> <span class="hljs-string">=</span> <span class="hljs-string">control_grid(verbose</span> <span class="hljs-string">=</span> <span class="hljs-literal">TRUE</span><span class="hljs-string">,</span> 
                           <span class="hljs-string">allow_par</span> <span class="hljs-string">=</span> <span class="hljs-literal">TRUE</span><span class="hljs-string">)</span>
  <span class="hljs-string">)</span>
<span class="hljs-string">toc()</span>

<span class="hljs-string">&gt;</span> <span class="hljs-string">plan(strategy</span> <span class="hljs-string">=</span> <span class="hljs-string">sequential)</span>

<span class="hljs-string">&gt;</span> <span class="hljs-string">tune_results_prophet_boost_1</span> <span class="hljs-string">%&gt;%</span> 
<span class="hljs-string">+</span>     <span class="hljs-string">show_best("rmse",</span> <span class="hljs-string">n</span> <span class="hljs-string">=</span> <span class="hljs-string">Inf)</span>
<span class="hljs-comment"># A tibble: 20 x 13</span>
   <span class="hljs-string">changepoint_num</span>  <span class="hljs-string">mtry</span> <span class="hljs-string">trees</span> <span class="hljs-string">min_n</span> <span class="hljs-string">tree_depth</span> <span class="hljs-string">learn_rate</span> <span class="hljs-string">loss_reduction</span> <span class="hljs-string">.metric</span> <span class="hljs-string">.estimator</span>  <span class="hljs-string">mean</span>     <span class="hljs-string">n</span> <span class="hljs-string">std_err</span> <span class="hljs-string">.config</span>      
             <span class="hljs-string">&lt;int&gt;</span> <span class="hljs-string">&lt;int&gt;</span> <span class="hljs-string">&lt;int&gt;</span> <span class="hljs-string">&lt;int&gt;</span>      <span class="hljs-string">&lt;int&gt;</span>      <span class="hljs-string">&lt;dbl&gt;</span>          <span class="hljs-string">&lt;dbl&gt;</span> <span class="hljs-string">&lt;chr&gt;</span>   <span class="hljs-string">&lt;chr&gt;</span>      <span class="hljs-string">&lt;dbl&gt;</span> <span class="hljs-string">&lt;int&gt;</span>   <span class="hljs-string">&lt;dbl&gt;</span> <span class="hljs-string">&lt;chr&gt;</span>        
 <span class="hljs-number">1</span>              <span class="hljs-number">11</span>    <span class="hljs-number">40</span>   <span class="hljs-number">613</span>    <span class="hljs-number">16</span>          <span class="hljs-number">6</span>   <span class="hljs-number">5.</span><span class="hljs-string">62e-</span> <span class="hljs-number">2</span>       <span class="hljs-number">7.</span><span class="hljs-string">32e-</span> <span class="hljs-number">6</span> <span class="hljs-string">rmse</span>    <span class="hljs-string">standard</span>   <span class="hljs-number">0.185</span>    <span class="hljs-number">10</span> <span class="hljs-number">0.00372</span> <span class="hljs-string">Preprocessor~</span>
 <span class="hljs-number">2</span>              <span class="hljs-number">27</span>    <span class="hljs-number">37</span>  <span class="hljs-number">1620    </span><span class="hljs-number">35</span>          <span class="hljs-number">8</span>   <span class="hljs-number">3.</span><span class="hljs-string">16e-</span> <span class="hljs-number">2</span>       <span class="hljs-number">7.</span><span class="hljs-string">35e-</span> <span class="hljs-number">8</span> <span class="hljs-string">rmse</span>    <span class="hljs-string">standard</span>   <span class="hljs-number">0.187</span>    <span class="hljs-number">10</span> <span class="hljs-number">0.00378</span> <span class="hljs-string">Preprocessor~</span>
 <span class="hljs-number">3</span>               <span class="hljs-number">0</span>    <span class="hljs-number">23</span>  <span class="hljs-number">1183     </span><span class="hljs-number">4</span>         <span class="hljs-number">12</span>   <span class="hljs-number">2.</span><span class="hljs-string">44e-</span> <span class="hljs-number">3</span>       <span class="hljs-number">1.91e-10</span> <span class="hljs-string">rmse</span>    <span class="hljs-string">standard</span>   <span class="hljs-number">0.192</span>    <span class="hljs-number">10</span> <span class="hljs-number">0.00422</span> <span class="hljs-string">Preprocessor~</span>
 <span class="hljs-number">4</span>              <span class="hljs-number">42</span>    <span class="hljs-number">13</span>   <span class="hljs-number">336</span>    <span class="hljs-number">37</span>          <span class="hljs-number">2</span>   <span class="hljs-number">6.</span><span class="hljs-string">48e-</span> <span class="hljs-number">3</span>       <span class="hljs-number">5.83e-10</span> <span class="hljs-string">rmse</span>    <span class="hljs-string">standard</span>   <span class="hljs-number">0.308</span>    <span class="hljs-number">10</span> <span class="hljs-number">0.00407</span> <span class="hljs-string">Preprocessor~</span>
 <span class="hljs-number">5</span>              <span class="hljs-number">23</span>    <span class="hljs-number">45</span>  <span class="hljs-number">1450     </span><span class="hljs-number">8</span>          <span class="hljs-number">4</span>   <span class="hljs-number">6.</span><span class="hljs-string">11e-</span> <span class="hljs-number">4</span>       <span class="hljs-number">7.</span><span class="hljs-string">60e-</span> <span class="hljs-number">2</span> <span class="hljs-string">rmse</span>    <span class="hljs-string">standard</span>   <span class="hljs-number">0.360</span>    <span class="hljs-number">10</span> <span class="hljs-number">0.00406</span> <span class="hljs-string">Preprocessor~</span>
 <span class="hljs-number">6</span>               <span class="hljs-number">4</span>    <span class="hljs-number">13</span>   <span class="hljs-number">575</span>     <span class="hljs-number">8</span>         <span class="hljs-number">13</span>   <span class="hljs-number">2.</span><span class="hljs-string">68e-</span> <span class="hljs-number">4</span>       <span class="hljs-number">1.</span><span class="hljs-string">09e+</span> <span class="hljs-number">0</span> <span class="hljs-string">rmse</span>    <span class="hljs-string">standard</span>   <span class="hljs-number">0.550</span>    <span class="hljs-number">10</span> <span class="hljs-number">0.00560</span> <span class="hljs-string">Preprocessor~</span>
 <span class="hljs-number">7</span>              <span class="hljs-number">20</span>     <span class="hljs-number">7</span>   <span class="hljs-number">798</span>    <span class="hljs-number">27</span>         <span class="hljs-number">15</span>   <span class="hljs-number">1.</span><span class="hljs-string">18e-</span> <span class="hljs-number">4</span>       <span class="hljs-number">4.</span><span class="hljs-string">65e-</span> <span class="hljs-number">9</span> <span class="hljs-string">rmse</span>    <span class="hljs-string">standard</span>   <span class="hljs-number">0.579</span>    <span class="hljs-number">10</span> <span class="hljs-number">0.00588</span> <span class="hljs-string">Preprocessor~</span>
 <span class="hljs-number">8</span>              <span class="hljs-number">44</span>    <span class="hljs-number">18</span>  <span class="hljs-number">1097    </span><span class="hljs-number">20</span>         <span class="hljs-number">10</span>   <span class="hljs-number">3.</span><span class="hljs-string">33e-</span> <span class="hljs-number">5</span>       <span class="hljs-number">5.</span><span class="hljs-string">98e+</span> <span class="hljs-number">0</span> <span class="hljs-string">rmse</span>    <span class="hljs-string">standard</span>   <span class="hljs-number">0.606</span>    <span class="hljs-number">10</span> <span class="hljs-number">0.00605</span> <span class="hljs-string">Preprocessor~</span>
 <span class="hljs-number">9</span>              <span class="hljs-number">49</span>    <span class="hljs-number">31</span>   <span class="hljs-number">870</span>    <span class="hljs-number">11</span>          <span class="hljs-number">2</span>   <span class="hljs-number">2.</span><span class="hljs-string">32e-</span> <span class="hljs-number">5</span>       <span class="hljs-number">1.</span><span class="hljs-string">70e+</span> <span class="hljs-number">1</span> <span class="hljs-string">rmse</span>    <span class="hljs-string">standard</span>   <span class="hljs-number">0.614</span>    <span class="hljs-number">10</span> <span class="hljs-number">0.00619</span> <span class="hljs-string">Preprocessor~</span>
<span class="hljs-number">10</span>              <span class="hljs-number">33</span>    <span class="hljs-number">33</span>  <span class="hljs-number">1538    </span><span class="hljs-number">18</span>         <span class="hljs-number">14</span>   <span class="hljs-number">5.</span><span class="hljs-string">55e-</span> <span class="hljs-number">6</span>       <span class="hljs-number">2.</span><span class="hljs-string">17e-</span> <span class="hljs-number">2</span> <span class="hljs-string">rmse</span>    <span class="hljs-string">standard</span>   <span class="hljs-number">0.619</span>    <span class="hljs-number">10</span> <span class="hljs-number">0.00617</span> <span class="hljs-string">Preprocessor~</span>
<span class="hljs-number">11</span>              <span class="hljs-number">14</span>    <span class="hljs-number">22</span>  <span class="hljs-number">1993    </span><span class="hljs-number">12</span>          <span class="hljs-number">3</span>   <span class="hljs-number">6.</span><span class="hljs-string">54e-</span> <span class="hljs-number">7</span>       <span class="hljs-number">1.</span><span class="hljs-string">42e-</span> <span class="hljs-number">4</span> <span class="hljs-string">rmse</span>    <span class="hljs-string">standard</span>   <span class="hljs-number">0.623</span>    <span class="hljs-number">10</span> <span class="hljs-number">0.00619</span> <span class="hljs-string">Preprocessor~</span>
<span class="hljs-number">12</span>              <span class="hljs-number">17</span>    <span class="hljs-number">28</span>  <span class="hljs-number">1311    </span><span class="hljs-number">34</span>          <span class="hljs-number">8</span>   <span class="hljs-number">2.41e-10</span>       <span class="hljs-number">2.</span><span class="hljs-string">92e-</span> <span class="hljs-number">5</span> <span class="hljs-string">rmse</span>    <span class="hljs-string">standard</span>   <span class="hljs-number">0.623</span>    <span class="hljs-number">10</span> <span class="hljs-number">0.00620</span> <span class="hljs-string">Preprocessor~</span>
<span class="hljs-number">13</span>              <span class="hljs-number">39</span>    <span class="hljs-number">46</span>   <span class="hljs-number">245</span>    <span class="hljs-number">31</span>         <span class="hljs-number">11</span>   <span class="hljs-number">2.</span><span class="hljs-string">60e-</span> <span class="hljs-number">8</span>       <span class="hljs-number">9.</span><span class="hljs-string">67e-</span> <span class="hljs-number">7</span> <span class="hljs-string">rmse</span>    <span class="hljs-string">standard</span>   <span class="hljs-number">0.623</span>    <span class="hljs-number">10</span> <span class="hljs-number">0.00620</span> <span class="hljs-string">Preprocessor~</span>
<span class="hljs-number">14</span>              <span class="hljs-number">31</span>     <span class="hljs-number">1</span>  <span class="hljs-number">1756    </span><span class="hljs-number">14</span>         <span class="hljs-number">13</span>   <span class="hljs-number">8.</span><span class="hljs-string">92e-</span> <span class="hljs-number">8</span>       <span class="hljs-number">2.</span><span class="hljs-string">41e-</span> <span class="hljs-number">3</span> <span class="hljs-string">rmse</span>    <span class="hljs-string">standard</span>   <span class="hljs-number">0.623</span>    <span class="hljs-number">10</span> <span class="hljs-number">0.00621</span> <span class="hljs-string">Preprocessor~</span>
<span class="hljs-number">15</span>              <span class="hljs-number">22</span>     <span class="hljs-number">5</span>  <span class="hljs-number">1882     </span><span class="hljs-number">3</span>          <span class="hljs-number">7</span>   <span class="hljs-number">1.</span><span class="hljs-string">45e-</span> <span class="hljs-number">8</span>       <span class="hljs-number">1.</span><span class="hljs-string">01e-</span> <span class="hljs-number">7</span> <span class="hljs-string">rmse</span>    <span class="hljs-string">standard</span>   <span class="hljs-number">0.623</span>    <span class="hljs-number">10</span> <span class="hljs-number">0.00621</span> <span class="hljs-string">Preprocessor~</span>
<span class="hljs-number">16</span>              <span class="hljs-number">37</span>    <span class="hljs-number">19</span>   <span class="hljs-number">402</span>    <span class="hljs-number">39</span>          <span class="hljs-number">9</span>   <span class="hljs-number">2.</span><span class="hljs-string">28e-</span> <span class="hljs-number">9</span>       <span class="hljs-number">4.</span><span class="hljs-string">83e-</span> <span class="hljs-number">4</span> <span class="hljs-string">rmse</span>    <span class="hljs-string">standard</span>   <span class="hljs-number">0.623</span>    <span class="hljs-number">10</span> <span class="hljs-number">0.00620</span> <span class="hljs-string">Preprocessor~</span>
<span class="hljs-number">17</span>               <span class="hljs-number">8</span>    <span class="hljs-number">42</span>    <span class="hljs-number">42</span>    <span class="hljs-number">21</span>          <span class="hljs-number">3</span>   <span class="hljs-number">2.</span><span class="hljs-string">22e-</span> <span class="hljs-number">6</span>       <span class="hljs-number">1.</span><span class="hljs-string">63e-</span> <span class="hljs-number">1</span> <span class="hljs-string">rmse</span>    <span class="hljs-string">standard</span>   <span class="hljs-number">0.623</span>    <span class="hljs-number">10</span> <span class="hljs-number">0.00617</span> <span class="hljs-string">Preprocessor~</span>
<span class="hljs-number">18</span>              <span class="hljs-number">45</span>    <span class="hljs-number">48</span>   <span class="hljs-number">971</span>    <span class="hljs-number">30</span>          <span class="hljs-number">6</span>   <span class="hljs-number">2.94e-10</span>       <span class="hljs-number">5.</span><span class="hljs-string">34e-</span> <span class="hljs-number">3</span> <span class="hljs-string">rmse</span>    <span class="hljs-string">standard</span>   <span class="hljs-number">0.623</span>    <span class="hljs-number">10</span> <span class="hljs-number">0.00619</span> <span class="hljs-string">Preprocessor~</span>
<span class="hljs-number">19</span>              <span class="hljs-number">30</span>    <span class="hljs-number">30</span>  <span class="hljs-number">1261    </span><span class="hljs-number">25</span>         <span class="hljs-number">11</span>   <span class="hljs-number">1.</span><span class="hljs-string">34e-</span> <span class="hljs-number">9</span>       <span class="hljs-number">1.</span><span class="hljs-string">90e-</span> <span class="hljs-number">8</span> <span class="hljs-string">rmse</span>    <span class="hljs-string">standard</span>   <span class="hljs-number">0.623</span>    <span class="hljs-number">10</span> <span class="hljs-number">0.00619</span> <span class="hljs-string">Preprocessor~</span>
<span class="hljs-number">20</span>               <span class="hljs-number">6</span>     <span class="hljs-number">9</span>   <span class="hljs-number">145</span>    <span class="hljs-number">26</span>          <span class="hljs-number">5</span>   <span class="hljs-number">2.</span><span class="hljs-string">50e-</span> <span class="hljs-number">7</span>       <span class="hljs-number">1.</span><span class="hljs-string">89e-</span> <span class="hljs-number">6</span> <span class="hljs-string">rmse</span>    <span class="hljs-string">standard</span>   <span class="hljs-number">0.624</span>    <span class="hljs-number">10</span> <span class="hljs-number">0.00616</span> <span class="hljs-string">Preprocessor~</span>

<span class="hljs-string">&gt;</span> <span class="hljs-string">tune_results_prophet_boost_1</span> <span class="hljs-string">%&gt;%</span> 
<span class="hljs-string">+</span>     <span class="hljs-string">show_best("rsq",</span> <span class="hljs-string">n</span> <span class="hljs-string">=</span> <span class="hljs-string">Inf)</span>
<span class="hljs-comment"># A tibble: 20 x 13</span>
   <span class="hljs-string">changepoint_num</span>  <span class="hljs-string">mtry</span> <span class="hljs-string">trees</span> <span class="hljs-string">min_n</span> <span class="hljs-string">tree_depth</span> <span class="hljs-string">learn_rate</span> <span class="hljs-string">loss_reduction</span> <span class="hljs-string">.metric</span> <span class="hljs-string">.estimator</span>  <span class="hljs-string">mean</span>     <span class="hljs-string">n</span> <span class="hljs-string">std_err</span> <span class="hljs-string">.config</span>      
             <span class="hljs-string">&lt;int&gt;</span> <span class="hljs-string">&lt;int&gt;</span> <span class="hljs-string">&lt;int&gt;</span> <span class="hljs-string">&lt;int&gt;</span>      <span class="hljs-string">&lt;int&gt;</span>      <span class="hljs-string">&lt;dbl&gt;</span>          <span class="hljs-string">&lt;dbl&gt;</span> <span class="hljs-string">&lt;chr&gt;</span>   <span class="hljs-string">&lt;chr&gt;</span>      <span class="hljs-string">&lt;dbl&gt;</span> <span class="hljs-string">&lt;int&gt;</span>   <span class="hljs-string">&lt;dbl&gt;</span> <span class="hljs-string">&lt;chr&gt;</span>        
 <span class="hljs-number">1</span>              <span class="hljs-number">11</span>    <span class="hljs-number">40</span>   <span class="hljs-number">613</span>    <span class="hljs-number">16</span>          <span class="hljs-number">6</span>   <span class="hljs-number">5.</span><span class="hljs-string">62e-</span> <span class="hljs-number">2</span>       <span class="hljs-number">7.</span><span class="hljs-string">32e-</span> <span class="hljs-number">6</span> <span class="hljs-string">rsq</span>     <span class="hljs-string">standard</span>   <span class="hljs-number">0.958</span>    <span class="hljs-number">10</span> <span class="hljs-number">0.00191</span> <span class="hljs-string">Preprocessor~</span>
 <span class="hljs-number">2</span>              <span class="hljs-number">27</span>    <span class="hljs-number">37</span>  <span class="hljs-number">1620    </span><span class="hljs-number">35</span>          <span class="hljs-number">8</span>   <span class="hljs-number">3.</span><span class="hljs-string">16e-</span> <span class="hljs-number">2</span>       <span class="hljs-number">7.</span><span class="hljs-string">35e-</span> <span class="hljs-number">8</span> <span class="hljs-string">rsq</span>     <span class="hljs-string">standard</span>   <span class="hljs-number">0.957</span>    <span class="hljs-number">10</span> <span class="hljs-number">0.00195</span> <span class="hljs-string">Preprocessor~</span>
 <span class="hljs-number">3</span>               <span class="hljs-number">0</span>    <span class="hljs-number">23</span>  <span class="hljs-number">1183     </span><span class="hljs-number">4</span>         <span class="hljs-number">12</span>   <span class="hljs-number">2.</span><span class="hljs-string">44e-</span> <span class="hljs-number">3</span>       <span class="hljs-number">1.91e-10</span> <span class="hljs-string">rsq</span>     <span class="hljs-string">standard</span>   <span class="hljs-number">0.956</span>    <span class="hljs-number">10</span> <span class="hljs-number">0.00218</span> <span class="hljs-string">Preprocessor~</span>
 <span class="hljs-number">4</span>              <span class="hljs-number">23</span>    <span class="hljs-number">45</span>  <span class="hljs-number">1450     </span><span class="hljs-number">8</span>          <span class="hljs-number">4</span>   <span class="hljs-number">6.</span><span class="hljs-string">11e-</span> <span class="hljs-number">4</span>       <span class="hljs-number">7.</span><span class="hljs-string">60e-</span> <span class="hljs-number">2</span> <span class="hljs-string">rsq</span>     <span class="hljs-string">standard</span>   <span class="hljs-number">0.893</span>    <span class="hljs-number">10</span> <span class="hljs-number">0.00325</span> <span class="hljs-string">Preprocessor~</span>
 <span class="hljs-number">5</span>              <span class="hljs-number">42</span>    <span class="hljs-number">13</span>   <span class="hljs-number">336</span>    <span class="hljs-number">37</span>          <span class="hljs-number">2</span>   <span class="hljs-number">6.</span><span class="hljs-string">48e-</span> <span class="hljs-number">3</span>       <span class="hljs-number">5.83e-10</span> <span class="hljs-string">rsq</span>     <span class="hljs-string">standard</span>   <span class="hljs-number">0.888</span>    <span class="hljs-number">10</span> <span class="hljs-number">0.00293</span> <span class="hljs-string">Preprocessor~</span>
 <span class="hljs-number">6</span>               <span class="hljs-number">4</span>    <span class="hljs-number">13</span>   <span class="hljs-number">575</span>     <span class="hljs-number">8</span>         <span class="hljs-number">13</span>   <span class="hljs-number">2.</span><span class="hljs-string">68e-</span> <span class="hljs-number">4</span>       <span class="hljs-number">1.</span><span class="hljs-string">09e+</span> <span class="hljs-number">0</span> <span class="hljs-string">rsq</span>     <span class="hljs-string">standard</span>   <span class="hljs-number">0.855</span>    <span class="hljs-number">10</span> <span class="hljs-number">0.00460</span> <span class="hljs-string">Preprocessor~</span>
 <span class="hljs-number">7</span>              <span class="hljs-number">20</span>     <span class="hljs-number">7</span>   <span class="hljs-number">798</span>    <span class="hljs-number">27</span>         <span class="hljs-number">15</span>   <span class="hljs-number">1.</span><span class="hljs-string">18e-</span> <span class="hljs-number">4</span>       <span class="hljs-number">4.</span><span class="hljs-string">65e-</span> <span class="hljs-number">9</span> <span class="hljs-string">rsq</span>     <span class="hljs-string">standard</span>   <span class="hljs-number">0.845</span>    <span class="hljs-number">10</span> <span class="hljs-number">0.00511</span> <span class="hljs-string">Preprocessor~</span>
 <span class="hljs-number">8</span>              <span class="hljs-number">44</span>    <span class="hljs-number">18</span>  <span class="hljs-number">1097    </span><span class="hljs-number">20</span>         <span class="hljs-number">10</span>   <span class="hljs-number">3.</span><span class="hljs-string">33e-</span> <span class="hljs-number">5</span>       <span class="hljs-number">5.</span><span class="hljs-string">98e+</span> <span class="hljs-number">0</span> <span class="hljs-string">rsq</span>     <span class="hljs-string">standard</span>   <span class="hljs-number">0.835</span>    <span class="hljs-number">10</span> <span class="hljs-number">0.00535</span> <span class="hljs-string">Preprocessor~</span>
 <span class="hljs-number">9</span>              <span class="hljs-number">33</span>    <span class="hljs-number">33</span>  <span class="hljs-number">1538    </span><span class="hljs-number">18</span>         <span class="hljs-number">14</span>   <span class="hljs-number">5.</span><span class="hljs-string">55e-</span> <span class="hljs-number">6</span>       <span class="hljs-number">2.</span><span class="hljs-string">17e-</span> <span class="hljs-number">2</span> <span class="hljs-string">rsq</span>     <span class="hljs-string">standard</span>   <span class="hljs-number">0.832</span>    <span class="hljs-number">10</span> <span class="hljs-number">0.00551</span> <span class="hljs-string">Preprocessor~</span>
<span class="hljs-number">10</span>              <span class="hljs-number">49</span>    <span class="hljs-number">31</span>   <span class="hljs-number">870</span>    <span class="hljs-number">11</span>          <span class="hljs-number">2</span>   <span class="hljs-number">2.</span><span class="hljs-string">32e-</span> <span class="hljs-number">5</span>       <span class="hljs-number">1.</span><span class="hljs-string">70e+</span> <span class="hljs-number">1</span> <span class="hljs-string">rsq</span>     <span class="hljs-string">standard</span>   <span class="hljs-number">0.832</span>    <span class="hljs-number">10</span> <span class="hljs-number">0.00549</span> <span class="hljs-string">Preprocessor~</span>
<span class="hljs-number">11</span>              <span class="hljs-number">14</span>    <span class="hljs-number">22</span>  <span class="hljs-number">1993    </span><span class="hljs-number">12</span>          <span class="hljs-number">3</span>   <span class="hljs-number">6.</span><span class="hljs-string">54e-</span> <span class="hljs-number">7</span>       <span class="hljs-number">1.</span><span class="hljs-string">42e-</span> <span class="hljs-number">4</span> <span class="hljs-string">rsq</span>     <span class="hljs-string">standard</span>   <span class="hljs-number">0.830</span>    <span class="hljs-number">10</span> <span class="hljs-number">0.00555</span> <span class="hljs-string">Preprocessor~</span>
<span class="hljs-number">12</span>              <span class="hljs-number">17</span>    <span class="hljs-number">28</span>  <span class="hljs-number">1311    </span><span class="hljs-number">34</span>          <span class="hljs-number">8</span>   <span class="hljs-number">2.41e-10</span>       <span class="hljs-number">2.</span><span class="hljs-string">92e-</span> <span class="hljs-number">5</span> <span class="hljs-string">rsq</span>     <span class="hljs-string">standard</span>   <span class="hljs-number">0.830</span>    <span class="hljs-number">10</span> <span class="hljs-number">0.00557</span> <span class="hljs-string">Preprocessor~</span>
<span class="hljs-number">13</span>              <span class="hljs-number">39</span>    <span class="hljs-number">46</span>   <span class="hljs-number">245</span>    <span class="hljs-number">31</span>         <span class="hljs-number">11</span>   <span class="hljs-number">2.</span><span class="hljs-string">60e-</span> <span class="hljs-number">8</span>       <span class="hljs-number">9.</span><span class="hljs-string">67e-</span> <span class="hljs-number">7</span> <span class="hljs-string">rsq</span>     <span class="hljs-string">standard</span>   <span class="hljs-number">0.830</span>    <span class="hljs-number">10</span> <span class="hljs-number">0.00557</span> <span class="hljs-string">Preprocessor~</span>
<span class="hljs-number">14</span>              <span class="hljs-number">22</span>     <span class="hljs-number">5</span>  <span class="hljs-number">1882     </span><span class="hljs-number">3</span>          <span class="hljs-number">7</span>   <span class="hljs-number">1.</span><span class="hljs-string">45e-</span> <span class="hljs-number">8</span>       <span class="hljs-number">1.</span><span class="hljs-string">01e-</span> <span class="hljs-number">7</span> <span class="hljs-string">rsq</span>     <span class="hljs-string">standard</span>   <span class="hljs-number">0.830</span>    <span class="hljs-number">10</span> <span class="hljs-number">0.00557</span> <span class="hljs-string">Preprocessor~</span>
<span class="hljs-number">15</span>              <span class="hljs-number">45</span>    <span class="hljs-number">48</span>   <span class="hljs-number">971</span>    <span class="hljs-number">30</span>          <span class="hljs-number">6</span>   <span class="hljs-number">2.94e-10</span>       <span class="hljs-number">5.</span><span class="hljs-string">34e-</span> <span class="hljs-number">3</span> <span class="hljs-string">rsq</span>     <span class="hljs-string">standard</span>   <span class="hljs-number">0.830</span>    <span class="hljs-number">10</span> <span class="hljs-number">0.00556</span> <span class="hljs-string">Preprocessor~</span>
<span class="hljs-number">16</span>              <span class="hljs-number">37</span>    <span class="hljs-number">19</span>   <span class="hljs-number">402</span>    <span class="hljs-number">39</span>          <span class="hljs-number">9</span>   <span class="hljs-number">2.</span><span class="hljs-string">28e-</span> <span class="hljs-number">9</span>       <span class="hljs-number">4.</span><span class="hljs-string">83e-</span> <span class="hljs-number">4</span> <span class="hljs-string">rsq</span>     <span class="hljs-string">standard</span>   <span class="hljs-number">0.830</span>    <span class="hljs-number">10</span> <span class="hljs-number">0.00558</span> <span class="hljs-string">Preprocessor~</span>
<span class="hljs-number">17</span>               <span class="hljs-number">8</span>    <span class="hljs-number">42</span>    <span class="hljs-number">42</span>    <span class="hljs-number">21</span>          <span class="hljs-number">3</span>   <span class="hljs-number">2.</span><span class="hljs-string">22e-</span> <span class="hljs-number">6</span>       <span class="hljs-number">1.</span><span class="hljs-string">63e-</span> <span class="hljs-number">1</span> <span class="hljs-string">rsq</span>     <span class="hljs-string">standard</span>   <span class="hljs-number">0.830</span>    <span class="hljs-number">10</span> <span class="hljs-number">0.00554</span> <span class="hljs-string">Preprocessor~</span>
<span class="hljs-number">18</span>              <span class="hljs-number">30</span>    <span class="hljs-number">30</span>  <span class="hljs-number">1261    </span><span class="hljs-number">25</span>         <span class="hljs-number">11</span>   <span class="hljs-number">1.</span><span class="hljs-string">34e-</span> <span class="hljs-number">9</span>       <span class="hljs-number">1.</span><span class="hljs-string">90e-</span> <span class="hljs-number">8</span> <span class="hljs-string">rsq</span>     <span class="hljs-string">standard</span>   <span class="hljs-number">0.830</span>    <span class="hljs-number">10</span> <span class="hljs-number">0.00557</span> <span class="hljs-string">Preprocessor~</span>
<span class="hljs-number">19</span>              <span class="hljs-number">31</span>     <span class="hljs-number">1</span>  <span class="hljs-number">1756    </span><span class="hljs-number">14</span>         <span class="hljs-number">13</span>   <span class="hljs-number">8.</span><span class="hljs-string">92e-</span> <span class="hljs-number">8</span>       <span class="hljs-number">2.</span><span class="hljs-string">41e-</span> <span class="hljs-number">3</span> <span class="hljs-string">rsq</span>     <span class="hljs-string">standard</span>   <span class="hljs-number">0.830</span>    <span class="hljs-number">10</span> <span class="hljs-number">0.00557</span> <span class="hljs-string">Preprocessor~</span>
<span class="hljs-number">20</span>               <span class="hljs-number">6</span>     <span class="hljs-number">9</span>   <span class="hljs-number">145</span>    <span class="hljs-number">26</span>          <span class="hljs-number">5</span>   <span class="hljs-number">2.</span><span class="hljs-string">50e-</span> <span class="hljs-number">7</span>       <span class="hljs-number">1.</span><span class="hljs-string">89e-</span> <span class="hljs-number">6</span> <span class="hljs-string">rsq</span>     <span class="hljs-string">standard</span>   <span class="hljs-number">0.829</span>    <span class="hljs-number">10</span> <span class="hljs-number">0.00552</span> <span class="hljs-string">Preprocessor~</span>
</code></pre><h4 id="heading-round-1">Round 1</h4>
<p>Plot the tuning results for analysis. You may pipe the tuning results into <code>feasts::autoplot()</code> and generated and plot an interactive ggplot object with ggplotly, as shown in the code snippet below:</p>
<pre><code>gr1<span class="hljs-operator">&lt;</span><span class="hljs-operator">-</span> tune_results_prophet_boost <span class="hljs-operator">%</span><span class="hljs-operator">&gt;</span><span class="hljs-operator">%</span>
  autoplot() <span class="hljs-operator">+</span>
  geom_smooth(se <span class="hljs-operator">=</span> FALSE)

ggplotly(gr1)
</code></pre><p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1642700577538/7RkSlQSbL.png" alt="p4_phtxb_round1.png" /></p>
<p>This is the most important part of the process: as a data scientist, you will decide of the tuning strategy.</p>
<p>Try to spot the plot with a monotonic smooth fonction (blue line), descending for RMSE or ascending for R-squared. Here, it corresponds to "<em>Learning Rate (log-10)</em>"  parameter. Update the grid spec with a new range of values for <em>Learning Rate</em> where the RMSE is minimal.</p>
<p>Generally speaking we will do the following steps for each tuning round</p>
<ol>
<li><p>update or adjust the parameter range within the grid specification.</p>
</li>
<li><p>toggle on parallel processing</p>
</li>
<li><p>perform hyperparameter tuning with new grid specification</p>
</li>
<li><p>toggle off parallel processing</p>
</li>
<li><p>analyze best RMSE and RSQ results</p>
</li>
<li><p>go to 1.</p>
</li>
</ol>
<h4 id="heading-round-2">Round 2</h4>
<p>We fix <code>learn_rate</code> parameter range of values.</p>
<pre><code><span class="hljs-comment"># 1. update or adjust the parameter range within the grid specification.</span>
<span class="hljs-string">set.seed(123)</span>
<span class="hljs-string">grid_spec_2</span> <span class="hljs-string">&lt;-</span> <span class="hljs-string">grid_latin_hypercube(</span>
  <span class="hljs-string">extract_parameter_set_dials(model_spec_prophet_boost_tune)</span> <span class="hljs-string">%&gt;%</span> 
    <span class="hljs-string">update(mtry</span> <span class="hljs-string">=</span> <span class="hljs-string">mtry(range</span> <span class="hljs-string">=</span> <span class="hljs-string">c(1,</span> <span class="hljs-number">50</span><span class="hljs-string">)),</span>
           <span class="hljs-string">learn_rate</span> <span class="hljs-string">=</span> <span class="hljs-string">learn_rate(range</span> <span class="hljs-string">=</span> <span class="hljs-string">c(-2.0,</span> <span class="hljs-number">-1.0</span><span class="hljs-string">))),</span>
  <span class="hljs-string">size</span> <span class="hljs-string">=</span> <span class="hljs-number">20</span>
<span class="hljs-string">)</span>

<span class="hljs-comment"># 2. toggle on parallel processing</span>
<span class="hljs-string">plan(</span>
  <span class="hljs-string">strategy</span> <span class="hljs-string">=</span> <span class="hljs-string">cluster,</span>
  <span class="hljs-string">workers</span>  <span class="hljs-string">=</span> <span class="hljs-string">parallel::makeCluster(n_cores)</span>
<span class="hljs-string">)</span>

<span class="hljs-comment"># 3. perform hyperparameter tuning with new grid specification</span>
<span class="hljs-string">tic()</span>
<span class="hljs-string">tune_results_prophet_boost_2</span> <span class="hljs-string">&lt;-</span> <span class="hljs-string">wflw_spec_prophet_boost_tune</span> <span class="hljs-string">%&gt;%</span>
  <span class="hljs-string">tune_grid(</span>
    <span class="hljs-string">resamples</span>  <span class="hljs-string">=</span> <span class="hljs-string">resamples_kfold,</span>
    <span class="hljs-string">grid</span> <span class="hljs-string">=</span> <span class="hljs-string">grid_spec_2,</span>
    <span class="hljs-string">control</span> <span class="hljs-string">=</span> <span class="hljs-string">control_grid(verbose</span> <span class="hljs-string">=</span> <span class="hljs-literal">TRUE</span><span class="hljs-string">,</span> 
                           <span class="hljs-string">allow_par</span> <span class="hljs-string">=</span> <span class="hljs-literal">TRUE</span><span class="hljs-string">)</span>
  <span class="hljs-string">)</span>
<span class="hljs-string">toc()</span>

<span class="hljs-comment"># 4. toggle off parallel processing</span>
<span class="hljs-string">plan(strategy</span> <span class="hljs-string">=</span> <span class="hljs-string">sequential)</span>

<span class="hljs-comment"># 5. analyze best RMSE and RSQ results (here top 2)</span>
<span class="hljs-string">&gt;</span> <span class="hljs-string">tune_results_prophet_boost_2</span> <span class="hljs-string">%&gt;%</span> 
<span class="hljs-string">+</span>     <span class="hljs-string">show_best("rsq",</span> <span class="hljs-string">n</span> <span class="hljs-string">=</span> <span class="hljs-number">2</span><span class="hljs-string">)</span>
<span class="hljs-comment"># A tibble: 2 x 13</span>
  <span class="hljs-string">changepoint_num</span>  <span class="hljs-string">mtry</span> <span class="hljs-string">trees</span> <span class="hljs-string">min_n</span> <span class="hljs-string">tree_depth</span> <span class="hljs-string">learn_rate</span> <span class="hljs-string">loss_reduction</span> <span class="hljs-string">.metric</span> <span class="hljs-string">.estimator</span>  <span class="hljs-string">mean</span>     <span class="hljs-string">n</span> <span class="hljs-string">std_err</span> <span class="hljs-string">.config</span>       
            <span class="hljs-string">&lt;int&gt;</span> <span class="hljs-string">&lt;int&gt;</span> <span class="hljs-string">&lt;int&gt;</span> <span class="hljs-string">&lt;int&gt;</span>      <span class="hljs-string">&lt;int&gt;</span>      <span class="hljs-string">&lt;dbl&gt;</span>          <span class="hljs-string">&lt;dbl&gt;</span> <span class="hljs-string">&lt;chr&gt;</span>   <span class="hljs-string">&lt;chr&gt;</span>      <span class="hljs-string">&lt;dbl&gt;</span> <span class="hljs-string">&lt;int&gt;</span>   <span class="hljs-string">&lt;dbl&gt;</span> <span class="hljs-string">&lt;chr&gt;</span>         
<span class="hljs-number">1</span>               <span class="hljs-number">0</span>    <span class="hljs-number">23</span>  <span class="hljs-number">1183     </span><span class="hljs-number">4</span>         <span class="hljs-number">12</span>     <span class="hljs-number">0.0662</span>       <span class="hljs-number">1.91e-10</span> <span class="hljs-string">rsq</span>     <span class="hljs-string">standard</span>   <span class="hljs-number">0.961</span>    <span class="hljs-number">10</span> <span class="hljs-number">0.00217</span> <span class="hljs-string">Preprocessor1~</span>
<span class="hljs-number">2</span>              <span class="hljs-number">11</span>    <span class="hljs-number">40</span>   <span class="hljs-number">613</span>    <span class="hljs-number">16</span>          <span class="hljs-number">6</span>     <span class="hljs-number">0.0938</span>       <span class="hljs-number">7.</span><span class="hljs-string">32e-</span> <span class="hljs-number">6</span> <span class="hljs-string">rsq</span>     <span class="hljs-string">standard</span>   <span class="hljs-number">0.960</span>    <span class="hljs-number">10</span> <span class="hljs-number">0.00171</span> <span class="hljs-string">Preprocessor1~</span>

<span class="hljs-string">&gt;</span> <span class="hljs-string">tune_results_prophet_boost_2</span> <span class="hljs-string">%&gt;%</span> 
<span class="hljs-string">+</span>     <span class="hljs-string">show_best("rmse",</span> <span class="hljs-string">n</span> <span class="hljs-string">=</span> <span class="hljs-number">2</span><span class="hljs-string">)</span>
<span class="hljs-comment"># A tibble: 2 x 13</span>
  <span class="hljs-string">changepoint_num</span>  <span class="hljs-string">mtry</span> <span class="hljs-string">trees</span> <span class="hljs-string">min_n</span> <span class="hljs-string">tree_depth</span> <span class="hljs-string">learn_rate</span> <span class="hljs-string">loss_reduction</span> <span class="hljs-string">.metric</span> <span class="hljs-string">.estimator</span>  <span class="hljs-string">mean</span>     <span class="hljs-string">n</span> <span class="hljs-string">std_err</span> <span class="hljs-string">.config</span>       
            <span class="hljs-string">&lt;int&gt;</span> <span class="hljs-string">&lt;int&gt;</span> <span class="hljs-string">&lt;int&gt;</span> <span class="hljs-string">&lt;int&gt;</span>      <span class="hljs-string">&lt;int&gt;</span>      <span class="hljs-string">&lt;dbl&gt;</span>          <span class="hljs-string">&lt;dbl&gt;</span> <span class="hljs-string">&lt;chr&gt;</span>   <span class="hljs-string">&lt;chr&gt;</span>      <span class="hljs-string">&lt;dbl&gt;</span> <span class="hljs-string">&lt;int&gt;</span>   <span class="hljs-string">&lt;dbl&gt;</span> <span class="hljs-string">&lt;chr&gt;</span>         
<span class="hljs-number">1</span>               <span class="hljs-number">0</span>    <span class="hljs-number">23</span>  <span class="hljs-number">1183     </span><span class="hljs-number">4</span>         <span class="hljs-number">12</span>     <span class="hljs-number">0.0662</span>       <span class="hljs-number">1.91e-10</span> <span class="hljs-string">rmse</span>    <span class="hljs-string">standard</span>   <span class="hljs-number">0.179</span>    <span class="hljs-number">10</span> <span class="hljs-number">0.00462</span> <span class="hljs-string">Preprocessor1~</span>
<span class="hljs-number">2</span>              <span class="hljs-number">11</span>    <span class="hljs-number">40</span>   <span class="hljs-number">613</span>    <span class="hljs-number">16</span>          <span class="hljs-number">6</span>     <span class="hljs-number">0.0938</span>       <span class="hljs-number">7.</span><span class="hljs-string">32e-</span> <span class="hljs-number">6</span> <span class="hljs-string">rmse</span>    <span class="hljs-string">standard</span>   <span class="hljs-number">0.181</span>    <span class="hljs-number">10</span> <span class="hljs-number">0.00340</span> <span class="hljs-string">Preprocessor1~</span>
</code></pre><p>Let us analyse the plot</p>
<pre><code>gr2 <span class="hljs-operator">&lt;</span><span class="hljs-operator">-</span> tune_results_prophet_boost_2 <span class="hljs-operator">%</span><span class="hljs-operator">&gt;</span><span class="hljs-operator">%</span>
  autoplot() <span class="hljs-operator">+</span>
  geom_smooth(se <span class="hljs-operator">=</span> FALSE)

ggplotly(gr2)
</code></pre><p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1642700779222/kwcTK0YU3y.png" alt="p4_phtxb_round2.png" /></p>
<h4 id="heading-round-3">Round 3</h4>
<p>We fix <code>trees</code> parameter range of values.</p>
<pre><code><span class="hljs-string">set.seed(123)</span>
<span class="hljs-string">grid_spec_3</span> <span class="hljs-string">&lt;-</span> <span class="hljs-string">grid_latin_hypercube(</span>
  <span class="hljs-string">extract_parameter_set_dials(model_spec_prophet_boost_tune)</span> <span class="hljs-string">%&gt;%</span> 
    <span class="hljs-string">update(mtry</span> <span class="hljs-string">=</span> <span class="hljs-string">mtry(range</span> <span class="hljs-string">=</span> <span class="hljs-string">c(1,</span> <span class="hljs-number">50</span><span class="hljs-string">)),</span>
           <span class="hljs-string">learn_rate</span> <span class="hljs-string">=</span> <span class="hljs-string">learn_rate(range</span> <span class="hljs-string">=</span> <span class="hljs-string">c(-2.0,</span> <span class="hljs-number">-1.0</span><span class="hljs-string">)),</span>
           <span class="hljs-string">trees</span> <span class="hljs-string">=</span> <span class="hljs-string">trees(range</span> <span class="hljs-string">=</span> <span class="hljs-string">c(1183,</span> <span class="hljs-number">1993</span><span class="hljs-string">))),</span>
  <span class="hljs-string">size</span> <span class="hljs-string">=</span> <span class="hljs-number">20</span>
<span class="hljs-string">)</span>

<span class="hljs-string">plan(</span>
  <span class="hljs-string">strategy</span> <span class="hljs-string">=</span> <span class="hljs-string">cluster,</span>
  <span class="hljs-string">workers</span>  <span class="hljs-string">=</span> <span class="hljs-string">parallel::makeCluster(n_cores)</span>
<span class="hljs-string">)</span>

<span class="hljs-string">tic()</span>
<span class="hljs-string">tune_results_prophet_boost_3</span> <span class="hljs-string">&lt;-</span> <span class="hljs-string">wflw_spec_prophet_boost_tune</span> <span class="hljs-string">%&gt;%</span>
  <span class="hljs-string">tune_grid(</span>
    <span class="hljs-string">resamples</span>  <span class="hljs-string">=</span> <span class="hljs-string">resamples_kfold,</span>
    <span class="hljs-string">grid</span> <span class="hljs-string">=</span> <span class="hljs-string">grid_spec_3,</span>
    <span class="hljs-string">control</span> <span class="hljs-string">=</span> <span class="hljs-string">control_grid(verbose</span> <span class="hljs-string">=</span> <span class="hljs-literal">TRUE</span><span class="hljs-string">,</span> 
                           <span class="hljs-string">allow_par</span> <span class="hljs-string">=</span> <span class="hljs-literal">TRUE</span><span class="hljs-string">)</span>
  <span class="hljs-string">)</span>
<span class="hljs-string">toc()</span>

<span class="hljs-string">plan(strategy</span> <span class="hljs-string">=</span> <span class="hljs-string">sequential)</span>

<span class="hljs-string">&gt;</span> <span class="hljs-string">tune_results_prophet_boost_3</span> <span class="hljs-string">%&gt;%</span> 
<span class="hljs-string">+</span>     <span class="hljs-string">show_best("rmse",</span> <span class="hljs-string">n</span> <span class="hljs-string">=</span> <span class="hljs-number">2</span><span class="hljs-string">)</span>
<span class="hljs-comment"># A tibble: 2 x 13</span>
  <span class="hljs-string">changepoint_num</span>  <span class="hljs-string">mtry</span> <span class="hljs-string">trees</span> <span class="hljs-string">min_n</span> <span class="hljs-string">tree_depth</span> <span class="hljs-string">learn_rate</span> <span class="hljs-string">loss_reduction</span> <span class="hljs-string">.metric</span> <span class="hljs-string">.estimator</span>  <span class="hljs-string">mean</span>     <span class="hljs-string">n</span> <span class="hljs-string">std_err</span> <span class="hljs-string">.config</span>       
            <span class="hljs-string">&lt;int&gt;</span> <span class="hljs-string">&lt;int&gt;</span> <span class="hljs-string">&lt;int&gt;</span> <span class="hljs-string">&lt;int&gt;</span>      <span class="hljs-string">&lt;int&gt;</span>      <span class="hljs-string">&lt;dbl&gt;</span>          <span class="hljs-string">&lt;dbl&gt;</span> <span class="hljs-string">&lt;chr&gt;</span>   <span class="hljs-string">&lt;chr&gt;</span>      <span class="hljs-string">&lt;dbl&gt;</span> <span class="hljs-string">&lt;int&gt;</span>   <span class="hljs-string">&lt;dbl&gt;</span> <span class="hljs-string">&lt;chr&gt;</span>         
<span class="hljs-number">1</span>               <span class="hljs-number">0</span>    <span class="hljs-number">23</span>  <span class="hljs-number">1662     </span><span class="hljs-number">4</span>         <span class="hljs-number">12</span>     <span class="hljs-number">0.0662</span>       <span class="hljs-number">1.91e-10</span> <span class="hljs-string">rmse</span>    <span class="hljs-string">standard</span>   <span class="hljs-number">0.179</span>    <span class="hljs-number">10</span> <span class="hljs-number">0.00464</span> <span class="hljs-string">Preprocessor1~</span>
<span class="hljs-number">2</span>              <span class="hljs-number">11</span>    <span class="hljs-number">40</span>  <span class="hljs-number">1431    </span><span class="hljs-number">16</span>          <span class="hljs-number">6</span>     <span class="hljs-number">0.0938</span>       <span class="hljs-number">7.</span><span class="hljs-string">32e-</span> <span class="hljs-number">6</span> <span class="hljs-string">rmse</span>    <span class="hljs-string">standard</span>   <span class="hljs-number">0.179</span>    <span class="hljs-number">10</span> <span class="hljs-number">0.00363</span> <span class="hljs-string">Preprocessor1~</span>

<span class="hljs-string">&gt;</span> <span class="hljs-string">tune_results_prophet_boost_3</span> <span class="hljs-string">%&gt;%</span> 
<span class="hljs-string">+</span>     <span class="hljs-string">show_best("rsq",</span> <span class="hljs-string">n</span> <span class="hljs-string">=</span> <span class="hljs-number">2</span><span class="hljs-string">)</span>
<span class="hljs-comment"># A tibble: 2 x 13</span>
  <span class="hljs-string">changepoint_num</span>  <span class="hljs-string">mtry</span> <span class="hljs-string">trees</span> <span class="hljs-string">min_n</span> <span class="hljs-string">tree_depth</span> <span class="hljs-string">learn_rate</span> <span class="hljs-string">loss_reduction</span> <span class="hljs-string">.metric</span> <span class="hljs-string">.estimator</span>  <span class="hljs-string">mean</span>     <span class="hljs-string">n</span> <span class="hljs-string">std_err</span> <span class="hljs-string">.config</span>       
            <span class="hljs-string">&lt;int&gt;</span> <span class="hljs-string">&lt;int&gt;</span> <span class="hljs-string">&lt;int&gt;</span> <span class="hljs-string">&lt;int&gt;</span>      <span class="hljs-string">&lt;int&gt;</span>      <span class="hljs-string">&lt;dbl&gt;</span>          <span class="hljs-string">&lt;dbl&gt;</span> <span class="hljs-string">&lt;chr&gt;</span>   <span class="hljs-string">&lt;chr&gt;</span>      <span class="hljs-string">&lt;dbl&gt;</span> <span class="hljs-string">&lt;int&gt;</span>   <span class="hljs-string">&lt;dbl&gt;</span> <span class="hljs-string">&lt;chr&gt;</span>         
<span class="hljs-number">1</span>              <span class="hljs-number">11</span>    <span class="hljs-number">40</span>  <span class="hljs-number">1431    </span><span class="hljs-number">16</span>          <span class="hljs-number">6</span>     <span class="hljs-number">0.0938</span>       <span class="hljs-number">7.</span><span class="hljs-string">32e-</span> <span class="hljs-number">6</span> <span class="hljs-string">rsq</span>     <span class="hljs-string">standard</span>   <span class="hljs-number">0.961</span>    <span class="hljs-number">10</span> <span class="hljs-number">0.00174</span> <span class="hljs-string">Preprocessor1~</span>
<span class="hljs-number">2</span>               <span class="hljs-number">0</span>    <span class="hljs-number">23</span>  <span class="hljs-number">1662     </span><span class="hljs-number">4</span>         <span class="hljs-number">12</span>     <span class="hljs-number">0.0662</span>       <span class="hljs-number">1.91e-10</span> <span class="hljs-string">rsq</span>     <span class="hljs-string">standard</span>   <span class="hljs-number">0.961</span>    <span class="hljs-number">10</span> <span class="hljs-number">0.00220</span> <span class="hljs-string">Preprocessor1~</span>
</code></pre><p>We obtain two best models, they show the same RMSE and RSQ values because there are rounded figures.</p>
<h3 id="heading-select-and-fit-the-best-models">Select and fit the best model(s)</h3>
<p>Please recall that we deal with <strong>workflows</strong> (not models directly) which incoporate a a model and a preprocessing recipe.</p>
<p>We <em>finalize</em> (<code>finalize_workflow()</code>) the tuning process by selecting the best model (<code>select_best()</code>) from round 3 tuning results  (<code>tune_results_prophet_boost_3</code>) and finally, we refit the model (<code>fit(training(splits))</code>), and display accuracy results.</p>
<pre><code><span class="hljs-comment"># Fitting round 3 best RMSE model</span>
<span class="hljs-string">set.seed(123)</span>
<span class="hljs-string">wflw_fit_prophet_boost_tuned</span> <span class="hljs-string">&lt;-</span> <span class="hljs-string">wflw_spec_prophet_boost_tune</span> <span class="hljs-string">%&gt;%</span>
  <span class="hljs-string">finalize_workflow(</span>
    <span class="hljs-string">select_best(tune_results_prophet_boost_3,</span> <span class="hljs-string">"rmse"</span><span class="hljs-string">,</span> <span class="hljs-string">n=1))</span> <span class="hljs-string">%&gt;%</span>
  <span class="hljs-string">fit(training(splits))</span>

<span class="hljs-string">modeltime_table(wflw_fit_prophet_boost_tuned)</span> <span class="hljs-string">%&gt;%</span>
  <span class="hljs-string">modeltime_calibrate(testing(splits))</span> <span class="hljs-string">%&gt;%</span>
  <span class="hljs-string">modeltime_accuracy()</span>

<span class="hljs-comment"># A tibble: 1 x 9</span>
  <span class="hljs-string">.model_id</span> <span class="hljs-string">.model_desc</span>               <span class="hljs-string">.type</span>   <span class="hljs-string">mae</span>  <span class="hljs-string">mape</span>  <span class="hljs-string">mase</span> <span class="hljs-string">smape</span>  <span class="hljs-string">rmse</span>   <span class="hljs-string">rsq</span>
      <span class="hljs-string">&lt;int&gt;</span> <span class="hljs-string">&lt;chr&gt;</span>                     <span class="hljs-string">&lt;chr&gt;</span> <span class="hljs-string">&lt;dbl&gt;</span> <span class="hljs-string">&lt;dbl&gt;</span> <span class="hljs-string">&lt;dbl&gt;</span> <span class="hljs-string">&lt;dbl&gt;</span> <span class="hljs-string">&lt;dbl&gt;</span> <span class="hljs-string">&lt;dbl&gt;</span>
<span class="hljs-number">1</span>         <span class="hljs-number">1</span> <span class="hljs-string">PROPHET</span> <span class="hljs-string">W/</span> <span class="hljs-string">XGBOOST</span> <span class="hljs-string">ERRORS</span> <span class="hljs-string">Test</span>  <span class="hljs-number">0.191</span>  <span class="hljs-number">25.7</span> <span class="hljs-number">0.421</span>  <span class="hljs-number">19.9</span> <span class="hljs-number">0.253</span> <span class="hljs-number">0.780</span>

<span class="hljs-comment"># Fitting round 3 best RSQmodel</span>
<span class="hljs-string">set.seed(123)</span>
<span class="hljs-string">wflw_fit_prophet_boost_tuned_rsq</span> <span class="hljs-string">&lt;-</span> <span class="hljs-string">wflw_spec_prophet_boost_tune</span> <span class="hljs-string">%&gt;%</span>
  <span class="hljs-string">finalize_workflow(</span>
    <span class="hljs-string">select_best(tune_results_prophet_boost_3,</span> <span class="hljs-string">"rsq"</span><span class="hljs-string">,</span> <span class="hljs-string">n=1))</span> <span class="hljs-string">%&gt;%</span>
  <span class="hljs-string">fit(training(splits))</span>

<span class="hljs-string">modeltime_table(wflw_fit_prophet_boost_tuned_rsq)</span> <span class="hljs-string">%&gt;%</span>
  <span class="hljs-string">modeltime_calibrate(testing(splits))</span> <span class="hljs-string">%&gt;%</span>
  <span class="hljs-string">modeltime_accuracy()</span>

<span class="hljs-comment"># A tibble: 1 x 9</span>
  <span class="hljs-string">.model_id</span> <span class="hljs-string">.model_desc</span>               <span class="hljs-string">.type</span>   <span class="hljs-string">mae</span>  <span class="hljs-string">mape</span>  <span class="hljs-string">mase</span> <span class="hljs-string">smape</span>  <span class="hljs-string">rmse</span>   <span class="hljs-string">rsq</span>
      <span class="hljs-string">&lt;int&gt;</span> <span class="hljs-string">&lt;chr&gt;</span>                     <span class="hljs-string">&lt;chr&gt;</span> <span class="hljs-string">&lt;dbl&gt;</span> <span class="hljs-string">&lt;dbl&gt;</span> <span class="hljs-string">&lt;dbl&gt;</span> <span class="hljs-string">&lt;dbl&gt;</span> <span class="hljs-string">&lt;dbl&gt;</span> <span class="hljs-string">&lt;dbl&gt;</span>
<span class="hljs-number">1</span>         <span class="hljs-number">1</span> <span class="hljs-string">PROPHET</span> <span class="hljs-string">W/</span> <span class="hljs-string">XGBOOST</span> <span class="hljs-string">ERRORS</span> <span class="hljs-string">Test</span>  <span class="hljs-number">0.223</span>  <span class="hljs-number">26.2</span> <span class="hljs-number">0.492</span>  <span class="hljs-number">22.3</span> <span class="hljs-number">0.308</span> <span class="hljs-number">0.722</span>
</code></pre><p>As shown above, the best <strong>fitted</strong> model is the first one, thus we select and save it.</p>
<h4 id="heading-save-prophet-boot-tuning-artifacts">Save Prophet Boot tuning artifacts</h4>
<p>It is very helpful the save all artifacts of the tuning process. I've created a saved to an RDS file a R list for which the structure should the same for all algorithms.</p>
<pre><code>tuned_prophet_xgb <span class="hljs-tag">&lt;<span class="hljs-name">-</span> <span class="hljs-attr">list</span>(

  # <span class="hljs-attr">Workflow</span> <span class="hljs-attr">spec</span>
  <span class="hljs-attr">tune_wkflw_spec</span> = <span class="hljs-string">wflw_spec_prophet_boost_tune,</span>
  # <span class="hljs-attr">Grid</span> <span class="hljs-attr">spec</span>
  <span class="hljs-attr">tune_grid_spec</span> = <span class="hljs-string">list(</span>
    <span class="hljs-attr">round1</span> = <span class="hljs-string">grid_spec_1,</span>
    <span class="hljs-attr">round2</span> = <span class="hljs-string">grid_spec_2,</span>
    <span class="hljs-attr">round3</span> = <span class="hljs-string">grid_spec_3),</span>
  # <span class="hljs-attr">Tuning</span> <span class="hljs-attr">Results</span>
  <span class="hljs-attr">tune_results</span> = <span class="hljs-string">list(</span>
    <span class="hljs-attr">round1</span> = <span class="hljs-string">tune_results_prophet_boost_1,</span>
    <span class="hljs-attr">round2</span> = <span class="hljs-string">tune_results_prophet_boost_2,</span>
    <span class="hljs-attr">round3</span> = <span class="hljs-string">tune_results_prophet_boost_3),</span>
  # <span class="hljs-attr">Tuned</span> <span class="hljs-attr">Workflow</span> <span class="hljs-attr">Fit</span>
  <span class="hljs-attr">tune_wflw_fit</span> = <span class="hljs-string">wflw_fit_prophet_boost_tuned,</span>
  # <span class="hljs-attr">from</span> <span class="hljs-attr">FE</span>
  <span class="hljs-attr">splits</span>        = <span class="hljs-string">artifacts$splits,</span>
  <span class="hljs-attr">data</span>          = <span class="hljs-string">artifacts$data,</span>
  <span class="hljs-attr">recipes</span>       = <span class="hljs-string">artifacts$recipes,</span>
  <span class="hljs-attr">standardize</span>   = <span class="hljs-string">artifacts$standardize,</span>
  <span class="hljs-attr">normalize</span>     = <span class="hljs-string">artifacts$normalize</span>

)

<span class="hljs-attr">tuned_prophet_xgb</span> %&gt;</span>% 
  write_rds("tuning/tuned_prophet_xgb.rds")
</code></pre><h2 id="heading-prophet-boost-summary-results">Prophet Boost summary results</h2>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1642701719644/KrX3zUkk8.png" alt="p4_results_phtxb.png" /></p>
<h2 id="heading-random-forest-summary-results">Random Forest summary results</h2>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1642701505656/zqdUrZU5_.png" alt="p4_results_rf.png" /></p>
<h2 id="heading-xgboost-summary-results">XGBoost summary results</h2>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1643210709789/tUPAk-Xqd.png" alt="p4_results_xgboost.png" /></p>
<h2 id="heading-prophet-summary-results">Prophet summary results</h2>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1642701516157/XNN4SU6H3-.png" alt="p4_results_prophet.png" /></p>
<h2 id="heading-modeltime-andamp-calibration-tables">Modeltime &amp; Calibration tables</h2>
<p>The objective is to add <strong>all non-tuned and tuned models</strong> into a single modeltime table <code>submodels_all_tbl</code>.</p>
<p>Please recall that we deal with <strong>workflows</strong> (not models directly) which incoporates a model and a preprocessing recipe.</p>
<p>Notice how we combined (<code>combine_modeltime_tables()</code>) the tables and updated the description (<code>update_model_description()</code>) for the tuned workflows.</p>
<pre><code>&gt; submodels_tbl &lt;- modeltime_table(
  wflw_artifacts$workflows$wflw_random_forest,
  wflw_artifacts$workflows$wflw_xgboost,
  wflw_artifacts$workflows$wflw_prophet,
  wflw_artifacts$workflows$wflw_prophet_boost
)

&gt; submodels_all_tbl &lt;- modeltime_table(
  tuned_rf_rds$tune_wflw_fit,
  tuned_xgboost_rds$tune_wflw_fit,
  tuned_prophet_rds$tune_wflw_fit,
  tuned_prophet_fou_boost_rds$tune_wflw_fit
) %&gt;%
  update_model_description(<span class="hljs-number">1</span>, <span class="hljs-string">"RANGER - Tuned"</span>) %&gt;%
  update_model_description(<span class="hljs-number">2</span>, <span class="hljs-string">"XGBOOST - Tuned"</span>) %&gt;%
  update_model_description(<span class="hljs-number">3</span>, <span class="hljs-string">"PROPHET W/ REGRESSORS - Tuned"</span>) %&gt;%
  update_model_description(<span class="hljs-number">4</span>, <span class="hljs-string">"PROPHET W/ XGBOOST ERRORS - Tuned"</span>) %&gt;%
  combine_modeltime_tables(submodels_tbl)

&gt; submodels_all_tbl
<span class="hljs-comment"># Modeltime Table</span>
<span class="hljs-comment"># A tibble: 8 x 3</span>
  .model_id .model     .model_desc                      
      &lt;<span class="hljs-keyword">int</span>&gt; &lt;<span class="hljs-keyword">list</span>&gt;     &lt;chr&gt;                            
<span class="hljs-number">1</span>         <span class="hljs-number">1</span> &lt;workflow&gt; RANGER - Tuned                   
<span class="hljs-number">2</span>         <span class="hljs-number">2</span> &lt;workflow&gt; XGBOOST - Tuned                  
<span class="hljs-number">3</span>         <span class="hljs-number">3</span> &lt;workflow&gt; PROPHET W/ REGRESSORS - Tuned    
<span class="hljs-number">4</span>         <span class="hljs-number">4</span> &lt;workflow&gt; PROPHET W/ XGBOOST ERRORS - Tuned
<span class="hljs-number">5</span>         <span class="hljs-number">5</span> &lt;workflow&gt; RANGER                           
<span class="hljs-number">6</span>         <span class="hljs-number">6</span> &lt;workflow&gt; XGBOOST                          
<span class="hljs-number">7</span>         <span class="hljs-number">7</span> &lt;workflow&gt; PROPHET W/ REGRESSORS            
<span class="hljs-number">8</span>         <span class="hljs-number">8</span> &lt;workflow&gt; PROPHET W/ XGBOOST ERRORS
</code></pre><h2 id="heading-model-evaluation-results">Model evaluation results</h2>
<p>We calibrate all <strong>fitted</strong> workflows with the test dataset and display the accuracy results for all non-tuned and tuned models. You may also create an interactive table which facilitates sorting, as shown in the code snippet below.</p>
<pre><code><span class="hljs-string">&gt;</span> <span class="hljs-string">calibration_all_tbl</span> <span class="hljs-string">&lt;-</span> <span class="hljs-string">submodels_all_tbl</span> <span class="hljs-string">%&gt;%</span>
  <span class="hljs-string">modeltime_calibrate(testing(splits))</span>

<span class="hljs-string">calibration_all_tbl</span> <span class="hljs-string">%&gt;%</span> 
  <span class="hljs-string">modeltime_accuracy()</span> <span class="hljs-string">%&gt;%</span>
  <span class="hljs-string">arrange(rmse)</span>

<span class="hljs-comment"># A tibble: 8 x 9</span>
  <span class="hljs-string">.model_id</span> <span class="hljs-string">.model_desc</span>                       <span class="hljs-string">.type</span>   <span class="hljs-string">mae</span>  <span class="hljs-string">mape</span>  <span class="hljs-string">mase</span> <span class="hljs-string">smape</span>  <span class="hljs-string">rmse</span>   <span class="hljs-string">rsq</span>
      <span class="hljs-string">&lt;int&gt;</span> <span class="hljs-string">&lt;chr&gt;</span>                             <span class="hljs-string">&lt;chr&gt;</span> <span class="hljs-string">&lt;dbl&gt;</span> <span class="hljs-string">&lt;dbl&gt;</span> <span class="hljs-string">&lt;dbl&gt;</span> <span class="hljs-string">&lt;dbl&gt;</span> <span class="hljs-string">&lt;dbl&gt;</span> <span class="hljs-string">&lt;dbl&gt;</span>
<span class="hljs-number">1</span>         <span class="hljs-number">7</span> <span class="hljs-string">PROPHET</span> <span class="hljs-string">W/</span> <span class="hljs-string">REGRESSORS</span>             <span class="hljs-string">Test</span>  <span class="hljs-number">0.189</span>  <span class="hljs-number">21.6</span> <span class="hljs-number">0.417</span>  <span class="hljs-number">21.5</span> <span class="hljs-number">0.240</span> <span class="hljs-number">0.856</span>
<span class="hljs-number">2</span>         <span class="hljs-number">3</span> <span class="hljs-string">PROPHET</span> <span class="hljs-string">W/</span> <span class="hljs-string">REGRESSORS</span> <span class="hljs-bullet">-</span> <span class="hljs-string">Tuned</span>     <span class="hljs-string">Test</span>  <span class="hljs-number">0.189</span>  <span class="hljs-number">21.6</span> <span class="hljs-number">0.418</span>  <span class="hljs-number">21.5</span> <span class="hljs-number">0.241</span> <span class="hljs-number">0.857</span>
<span class="hljs-number">3</span>         <span class="hljs-number">4</span> <span class="hljs-string">PROPHET</span> <span class="hljs-string">W/</span> <span class="hljs-string">XGBOOST</span> <span class="hljs-string">ERRORS</span> <span class="hljs-bullet">-</span> <span class="hljs-string">Tuned</span> <span class="hljs-string">Test</span>  <span class="hljs-number">0.191</span>  <span class="hljs-number">25.7</span> <span class="hljs-number">0.421</span>  <span class="hljs-number">19.9</span> <span class="hljs-number">0.253</span> <span class="hljs-number">0.780</span>
<span class="hljs-number">4</span>         <span class="hljs-number">8</span> <span class="hljs-string">PROPHET</span> <span class="hljs-string">W/</span> <span class="hljs-string">XGBOOST</span> <span class="hljs-string">ERRORS</span>         <span class="hljs-string">Test</span>  <span class="hljs-number">0.204</span>  <span class="hljs-number">24.8</span> <span class="hljs-number">0.451</span>  <span class="hljs-number">20.8</span> <span class="hljs-number">0.287</span> <span class="hljs-number">0.753</span>
<span class="hljs-number">5</span>         <span class="hljs-number">2</span> <span class="hljs-string">XGBOOST</span> <span class="hljs-bullet">-</span> <span class="hljs-string">Tuned</span>                   <span class="hljs-string">Test</span>  <span class="hljs-number">0.219</span>  <span class="hljs-number">23.6</span> <span class="hljs-number">0.482</span>  <span class="hljs-number">22.9</span> <span class="hljs-number">0.296</span> <span class="hljs-number">0.762</span>
<span class="hljs-number">6</span>         <span class="hljs-number">6</span> <span class="hljs-string">XGBOOST</span>                           <span class="hljs-string">Test</span>  <span class="hljs-number">0.216</span>  <span class="hljs-number">25.0</span> <span class="hljs-number">0.476</span>  <span class="hljs-number">22.4</span> <span class="hljs-number">0.299</span> <span class="hljs-number">0.747</span>
<span class="hljs-number">7</span>         <span class="hljs-number">1</span> <span class="hljs-string">RANGER</span> <span class="hljs-bullet">-</span> <span class="hljs-string">Tuned</span>                    <span class="hljs-string">Test</span>  <span class="hljs-number">0.231</span>  <span class="hljs-number">26.1</span> <span class="hljs-number">0.509</span>  <span class="hljs-number">24.6</span> <span class="hljs-number">0.303</span> <span class="hljs-number">0.766</span>
<span class="hljs-number">8</span>         <span class="hljs-number">5</span> <span class="hljs-string">RANGER</span>                            <span class="hljs-string">Test</span>  <span class="hljs-number">0.235</span>  <span class="hljs-number">25.4</span> <span class="hljs-number">0.519</span>  <span class="hljs-number">25.0</span> <span class="hljs-number">0.312</span> <span class="hljs-number">0.765</span>

<span class="hljs-string">calibration_all_tbl</span> <span class="hljs-string">%&gt;%</span> 
  <span class="hljs-string">modeltime_accuracy()</span> <span class="hljs-string">%&gt;%</span>
  <span class="hljs-string">arrange(desc(rsq))</span>

<span class="hljs-comment"># A tibble: 8 x 9</span>
  <span class="hljs-string">.model_id</span> <span class="hljs-string">.model_desc</span>                       <span class="hljs-string">.type</span>   <span class="hljs-string">mae</span>  <span class="hljs-string">mape</span>  <span class="hljs-string">mase</span> <span class="hljs-string">smape</span>  <span class="hljs-string">rmse</span>   <span class="hljs-string">rsq</span>
      <span class="hljs-string">&lt;int&gt;</span> <span class="hljs-string">&lt;chr&gt;</span>                             <span class="hljs-string">&lt;chr&gt;</span> <span class="hljs-string">&lt;dbl&gt;</span> <span class="hljs-string">&lt;dbl&gt;</span> <span class="hljs-string">&lt;dbl&gt;</span> <span class="hljs-string">&lt;dbl&gt;</span> <span class="hljs-string">&lt;dbl&gt;</span> <span class="hljs-string">&lt;dbl&gt;</span>
<span class="hljs-number">1</span>         <span class="hljs-number">3</span> <span class="hljs-string">PROPHET</span> <span class="hljs-string">W/</span> <span class="hljs-string">REGRESSORS</span> <span class="hljs-bullet">-</span> <span class="hljs-string">Tuned</span>     <span class="hljs-string">Test</span>  <span class="hljs-number">0.189</span>  <span class="hljs-number">21.6</span> <span class="hljs-number">0.418</span>  <span class="hljs-number">21.5</span> <span class="hljs-number">0.241</span> <span class="hljs-number">0.857</span>
<span class="hljs-number">2</span>         <span class="hljs-number">7</span> <span class="hljs-string">PROPHET</span> <span class="hljs-string">W/</span> <span class="hljs-string">REGRESSORS</span>             <span class="hljs-string">Test</span>  <span class="hljs-number">0.189</span>  <span class="hljs-number">21.6</span> <span class="hljs-number">0.417</span>  <span class="hljs-number">21.5</span> <span class="hljs-number">0.240</span> <span class="hljs-number">0.856</span>
<span class="hljs-number">3</span>         <span class="hljs-number">4</span> <span class="hljs-string">PROPHET</span> <span class="hljs-string">W/</span> <span class="hljs-string">XGBOOST</span> <span class="hljs-string">ERRORS</span> <span class="hljs-bullet">-</span> <span class="hljs-string">Tuned</span> <span class="hljs-string">Test</span>  <span class="hljs-number">0.191</span>  <span class="hljs-number">25.7</span> <span class="hljs-number">0.421</span>  <span class="hljs-number">19.9</span> <span class="hljs-number">0.253</span> <span class="hljs-number">0.780</span>
<span class="hljs-number">4</span>         <span class="hljs-number">1</span> <span class="hljs-string">RANGER</span> <span class="hljs-bullet">-</span> <span class="hljs-string">Tuned</span>                    <span class="hljs-string">Test</span>  <span class="hljs-number">0.231</span>  <span class="hljs-number">26.1</span> <span class="hljs-number">0.509</span>  <span class="hljs-number">24.6</span> <span class="hljs-number">0.303</span> <span class="hljs-number">0.766</span>
<span class="hljs-number">5</span>         <span class="hljs-number">5</span> <span class="hljs-string">RANGER</span>                            <span class="hljs-string">Test</span>  <span class="hljs-number">0.235</span>  <span class="hljs-number">25.4</span> <span class="hljs-number">0.519</span>  <span class="hljs-number">25.0</span> <span class="hljs-number">0.312</span> <span class="hljs-number">0.765</span>
<span class="hljs-number">6</span>         <span class="hljs-number">2</span> <span class="hljs-string">XGBOOST</span> <span class="hljs-bullet">-</span> <span class="hljs-string">Tuned</span>                   <span class="hljs-string">Test</span>  <span class="hljs-number">0.219</span>  <span class="hljs-number">23.6</span> <span class="hljs-number">0.482</span>  <span class="hljs-number">22.9</span> <span class="hljs-number">0.296</span> <span class="hljs-number">0.762</span>
<span class="hljs-number">7</span>         <span class="hljs-number">8</span> <span class="hljs-string">PROPHET</span> <span class="hljs-string">W/</span> <span class="hljs-string">XGBOOST</span> <span class="hljs-string">ERRORS</span>         <span class="hljs-string">Test</span>  <span class="hljs-number">0.204</span>  <span class="hljs-number">24.8</span> <span class="hljs-number">0.451</span>  <span class="hljs-number">20.8</span> <span class="hljs-number">0.287</span> <span class="hljs-number">0.753</span>
<span class="hljs-number">8</span>         <span class="hljs-number">6</span> <span class="hljs-string">XGBOOST</span>                           <span class="hljs-string">Test</span>  <span class="hljs-number">0.216</span>  <span class="hljs-number">25.0</span> <span class="hljs-number">0.476</span>  <span class="hljs-number">22.4</span> <span class="hljs-number">0.299</span> <span class="hljs-number">0.747</span>

<span class="hljs-comment"># Interactive table (bonus !)</span>
<span class="hljs-string">calibration_all_tbl</span> <span class="hljs-string">%&gt;%</span> 
  <span class="hljs-string">modeltime_accuracy()</span> <span class="hljs-string">%&gt;%</span>
  <span class="hljs-string">table_modeltime_accuracy()</span>
</code></pre><p>Best <strong>fitted</strong> models are Prophet (best RMSE) and tuned Prophet (best RSQ).</p>
<h2 id="heading-forecast-plot">Forecast plot</h2>
<p>We plot a forecast with <strong>test data</strong> using all models and industries, the figure below is a zoom of the test data predictions vs. actual data.</p>
<pre><code><span class="hljs-string">calibration_all_tbl</span> <span class="hljs-string">%&gt;%</span>
  <span class="hljs-string">modeltime_forecast(</span>
    <span class="hljs-string">new_data</span>    <span class="hljs-string">=</span> <span class="hljs-string">testing(splits),</span>
    <span class="hljs-string">actual_data</span> <span class="hljs-string">=</span> <span class="hljs-string">artifacts$data$data_prepared_tbl,</span>
    <span class="hljs-string">keep_data</span>   <span class="hljs-string">=</span> <span class="hljs-literal">TRUE</span> 
  <span class="hljs-string">)</span> <span class="hljs-string">%&gt;%</span>
  <span class="hljs-string">filter(Industry</span> <span class="hljs-string">==</span> <span class="hljs-string">Industries[1])</span> <span class="hljs-string">%&gt;%</span>
  <span class="hljs-string">plot_modeltime_forecast(</span>
    <span class="hljs-comment">#.facet_ncol         = 4, </span>
    <span class="hljs-string">.conf_interval_show</span> <span class="hljs-string">=</span> <span class="hljs-literal">FALSE</span><span class="hljs-string">,</span>
    <span class="hljs-string">.interactive</span>        <span class="hljs-string">=</span> <span class="hljs-literal">TRUE</span><span class="hljs-string">,</span>
    <span class="hljs-string">.title</span> <span class="hljs-string">=</span> <span class="hljs-string">Industries[1]</span>
  <span class="hljs-string">)</span>
</code></pre><p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1642701958899/vBWOH_8E0.png" alt="p4_all_forecast_plot.png" /></p>
<p>You may check and confirm the models that best follow the trend and spikes.</p>
<h2 id="heading-save-your-work">Save your work</h2>
<pre><code>workflow_all_artifacts <span class="hljs-operator">&lt;</span><span class="hljs-operator">-</span> list(

  workflows <span class="hljs-operator">=</span> submodels_all_tbl,

  calibration <span class="hljs-operator">=</span> calibration_all_tbl
)

workflow_all_artifacts <span class="hljs-operator">%</span><span class="hljs-operator">&gt;</span><span class="hljs-operator">%</span>
    write_rds(<span class="hljs-string">"workflows_NonandTuned_artifacts_list.rds"</span>)
</code></pre><h2 id="heading-conclusion">Conclusion</h2>
<p>In this article you have learned how to perform hyperparameter tuning for 4 machine learning (non-sequentual) models: Random Forest, XGBoost, Prophet and Prohet Boost. A step-by-step detailed process was provided for Prophet Boost.</p>
<p>You have learned </p>
<ul>
<li><p>how to define tunable specifications for each machine learning algorithm.</p>
</li>
<li><p>how to set up parallel processing with a cluster of vCores.</p>
</li>
<li><p>how to verify parameter values range prior tuning.</p>
</li>
<li><p>how to define a grid search specification for each algorithm</p>
</li>
<li><p>how to perform hyperparameter tuning and anlyse results with the plot against RMSE.</p>
</li>
<li><p>how to adjust a specific parameter and retune, performing several hyperparameter tuning rounds.</p>
</li>
<li><p>how to select the model and retrain the model.</p>
</li>
<li><p>how to add all tuned and non-tuned models into a modeltime and calibration table.</p>
</li>
<li><p>how to display all models accuracy results and plot forecast against the test dataset.</p>
</li>
</ul>
<h2 id="heading-references">References</h2>
<p><a target="_blank" href="https://university.business-science.io/courses">1</a> Dancho, Matt, "DS4B 203-R: High-Performance Time Series Forecasting", Business Science University</p>
]]></content:encoded></item></channel></rss>