<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml" lang xml:lang>
<head>
  <meta charset="utf-8" />
  <meta name="generator" content="mpark/wg21" />
  <meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=yes" />
  <meta name="dcterms.date" content="2025-03-20" />
  <title>Fix C++26 by making the rank-1, rank-2, rank-k, and rank-2k updates consistent with the BLAS</title>
  <style>
code{white-space: pre-wrap;}
span.smallcaps{font-variant: small-caps;}
span.underline{text-decoration: underline;}
div.column{display: inline-block; vertical-align: top; width: 50%;}
</style>
  <style>
pre > code.sourceCode { white-space: pre; position: relative; }
pre > code.sourceCode > span { line-height: 1.25; }
pre > code.sourceCode > span:empty { height: 1.2em; }
.sourceCode { overflow: visible; }
code.sourceCode > span { color: inherit; text-decoration: inherit; }
div.sourceCode { margin: 1em 0; }
pre.sourceCode { margin: 0; }
@media screen {
div.sourceCode { overflow: auto; }
}
@media print {
pre > code.sourceCode { white-space: pre-wrap; }
pre > code.sourceCode > span { text-indent: -5em; padding-left: 5em; }
}
pre.numberSource code
{ counter-reset: source-line 0; }
pre.numberSource code > span
{ position: relative; left: -4em; counter-increment: source-line; }
pre.numberSource code > span > a:first-child::before
{ content: counter(source-line);
position: relative; left: -1em; text-align: right; vertical-align: baseline;
border: none; display: inline-block;
-webkit-touch-callout: none; -webkit-user-select: none;
-khtml-user-select: none; -moz-user-select: none;
-ms-user-select: none; user-select: none;
padding: 0 4px; width: 4em;
color: #aaaaaa;
}
pre.numberSource { margin-left: 3em; border-left: 1px solid #aaaaaa; padding-left: 4px; }
div.sourceCode
{ background-color: #f6f8fa; }
@media screen {
pre > code.sourceCode > span > a:first-child::before { text-decoration: underline; }
}
code span { } 
code span.al { color: #ff0000; } 
code span.an { } 
code span.at { } 
code span.bn { color: #9f6807; } 
code span.bu { color: #9f6807; } 
code span.cf { color: #00607c; } 
code span.ch { color: #9f6807; } 
code span.cn { } 
code span.co { color: #008000; font-style: italic; } 
code span.cv { color: #008000; font-style: italic; } 
code span.do { color: #008000; } 
code span.dt { color: #00607c; } 
code span.dv { color: #9f6807; } 
code span.er { color: #ff0000; font-weight: bold; } 
code span.ex { } 
code span.fl { color: #9f6807; } 
code span.fu { } 
code span.im { } 
code span.in { color: #008000; } 
code span.kw { color: #00607c; } 
code span.op { color: #af1915; } 
code span.ot { } 
code span.pp { color: #6f4e37; } 
code span.re { } 
code span.sc { color: #9f6807; } 
code span.ss { color: #9f6807; } 
code span.st { color: #9f6807; } 
code span.va { } 
code span.vs { color: #9f6807; } 
code span.wa { color: #008000; font-weight: bold; } 
code.diff {color: #898887}
code.diff span.va {color: #00AA00}
code.diff span.st {color: #bf0303}
</style>
  <style type="text/css">
body {
margin: 5em;
font-family: serif;

hyphens: auto;
line-height: 1.35;
}
div.wrapper {
max-width: 60em;
margin: auto;
}
ul {
list-style-type: none;
padding-left: 2em;
margin-top: -0.2em;
margin-bottom: -0.2em;
}
a {
text-decoration: none;
color: #4183C4;
}
a.hidden_link {
text-decoration: none;
color: inherit;
}
li {
margin-top: 0.6em;
margin-bottom: 0.6em;
}
h1, h2, h3, h4 {
position: relative;
line-height: 1;
}
a.self-link {
position: absolute;
top: 0;
left: calc(-1 * (3.5rem - 26px));
width: calc(3.5rem - 26px);
height: 2em;
text-align: center;
border: none;
transition: opacity .2s;
opacity: .5;
font-family: sans-serif;
font-weight: normal;
font-size: 83%;
}
a.self-link:hover { opacity: 1; }
a.self-link::before { content: "§"; }
ul > li:before {
content: "\2014";
position: absolute;
margin-left: -1.5em;
}
:target { background-color: #C9FBC9; }
:target .codeblock { background-color: #C9FBC9; }
:target ul { background-color: #C9FBC9; }
.abbr_ref { float: right; }
.folded_abbr_ref { float: right; }
:target .folded_abbr_ref { display: none; }
:target .unfolded_abbr_ref { float: right; display: inherit; }
.unfolded_abbr_ref { display: none; }
.secnum { display: inline-block; min-width: 35pt; }
.header-section-number { display: inline-block; min-width: 35pt; }
.annexnum { display: block; }
div.sourceLinkParent {
float: right;
}
a.sourceLink {
position: absolute;
opacity: 0;
margin-left: 10pt;
}
a.sourceLink:hover {
opacity: 1;
}
a.itemDeclLink {
position: absolute;
font-size: 75%;
text-align: right;
width: 5em;
opacity: 0;
}
a.itemDeclLink:hover { opacity: 1; }
span.marginalizedparent {
position: relative;
left: -5em;
}
li span.marginalizedparent { left: -7em; }
li ul > li span.marginalizedparent { left: -9em; }
li ul > li ul > li span.marginalizedparent { left: -11em; }
li ul > li ul > li ul > li span.marginalizedparent { left: -13em; }
div.footnoteNumberParent {
position: relative;
left: -4.7em;
}
a.marginalized {
position: absolute;
font-size: 75%;
text-align: right;
width: 5em;
}
a.enumerated_item_num {
position: relative;
left: -3.5em;
display: inline-block;
margin-right: -3em;
text-align: right;
width: 3em;
}
div.para { margin-bottom: 0.6em; margin-top: 0.6em; text-align: justify; }
div.section { text-align: justify; }
div.sentence { display: inline; }
span.indexparent {
display: inline;
position: relative;
float: right;
right: -1em;
}
a.index {
position: absolute;
display: none;
}
a.index:before { content: "⟵"; }

a.index:target {
display: inline;
}
.indexitems {
margin-left: 2em;
text-indent: -2em;
}
div.itemdescr {
margin-left: 3em;
}
.bnf {
font-family: serif;
margin-left: 40pt;
margin-top: 0.5em;
margin-bottom: 0.5em;
}
.ncbnf {
font-family: serif;
margin-top: 0.5em;
margin-bottom: 0.5em;
margin-left: 40pt;
}
.ncsimplebnf {
font-family: serif;
font-style: italic;
margin-top: 0.5em;
margin-bottom: 0.5em;
margin-left: 40pt;
background: inherit; 
}
span.textnormal {
font-style: normal;
font-family: serif;
white-space: normal;
display: inline-block;
}
span.rlap {
display: inline-block;
width: 0px;
}
span.descr { font-style: normal; font-family: serif; }
span.grammarterm { font-style: italic; }
span.term { font-style: italic; }
span.terminal { font-family: monospace; font-style: normal; }
span.nonterminal { font-style: italic; }
span.tcode { font-family: monospace; font-style: normal; }
span.textbf { font-weight: bold; }
span.textsc { font-variant: small-caps; }
a.nontermdef { font-style: italic; font-family: serif; }
span.emph { font-style: italic; }
span.techterm { font-style: italic; }
span.mathit { font-style: italic; }
span.mathsf { font-family: sans-serif; }
span.mathrm { font-family: serif; font-style: normal; }
span.textrm { font-family: serif; }
span.textsl { font-style: italic; }
span.mathtt { font-family: monospace; font-style: normal; }
span.mbox { font-family: serif; font-style: normal; }
span.ungap { display: inline-block; width: 2pt; }
span.textit { font-style: italic; }
span.texttt { font-family: monospace; }
span.tcode_in_codeblock { font-family: monospace; font-style: normal; }
span.phantom { color: white; }

span.math { font-style: normal; }
span.mathblock {
display: block;
margin-left: auto;
margin-right: auto;
margin-top: 1.2em;
margin-bottom: 1.2em;
text-align: center;
}
span.mathalpha {
font-style: italic;
}
span.synopsis {
font-weight: bold;
margin-top: 0.5em;
display: block;
}
span.definition {
font-weight: bold;
display: block;
}
.codeblock {
margin-left: 1.2em;
line-height: 127%;
}
.outputblock {
margin-left: 1.2em;
line-height: 127%;
}
div.itemdecl {
margin-top: 2ex;
}
code.itemdeclcode {
white-space: pre;
display: block;
}
span.textsuperscript {
vertical-align: super;
font-size: smaller;
line-height: 0;
}
.footnotenum { vertical-align: super; font-size: smaller; line-height: 0; }
.footnote {
font-size: small;
margin-left: 2em;
margin-right: 2em;
margin-top: 0.6em;
margin-bottom: 0.6em;
}
div.minipage {
display: inline-block;
margin-right: 3em;
}
div.numberedTable {
text-align: center;
margin: 2em;
}
div.figure {
text-align: center;
margin: 2em;
}
table {
border: 1px solid black;
border-collapse: collapse;
margin-left: auto;
margin-right: auto;
margin-top: 0.8em;
text-align: left;
hyphens: none; 
}
td, th {
padding-left: 1em;
padding-right: 1em;
vertical-align: top;
}
td.empty {
padding: 0px;
padding-left: 1px;
}
td.left {
text-align: left;
}
td.right {
text-align: right;
}
td.center {
text-align: center;
}
td.justify {
text-align: justify;
}
td.border {
border-left: 1px solid black;
}
tr.rowsep, td.cline {
border-top: 1px solid black;
}
tr.even, tr.odd {
border-bottom: 1px solid black;
}
tr.capsep {
border-top: 3px solid black;
border-top-style: double;
}
tr.header {
border-bottom: 3px solid black;
border-bottom-style: double;
}
th {
border-bottom: 1px solid black;
}
span.centry {
font-weight: bold;
}
div.table {
display: block;
margin-left: auto;
margin-right: auto;
text-align: center;
width: 90%;
}
span.indented {
display: block;
margin-left: 2em;
margin-bottom: 1em;
margin-top: 1em;
}
ol.enumeratea { list-style-type: none; background: inherit; }
ol.enumerate { list-style-type: none; background: inherit; }

code.sourceCode > span { display: inline; }

div#refs p { padding-left: 32px; text-indent: -32px; }
</style>
  <link href="data:image/vnd.microsoft.icon;base64,AAABAAIAEBAAAAEAIABoBAAAJgAAACAgAAABACAAqBAAAI4EAAAoAAAAEAAAACAAAAABACAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA////AIJEAACCRAAAgkQAAIJEAACCRAAAgkQAVoJEAN6CRADegkQAWIJEAACCRAAAgkQAAIJEAACCRAAA////AP///wCCRAAAgkQAAIJEAACCRAAsgkQAvoJEAP+CRAD/gkQA/4JEAP+CRADAgkQALoJEAACCRAAAgkQAAP///wD///8AgkQAAIJEABSCRACSgkQA/IJEAP99PQD/dzMA/3czAP99PQD/gkQA/4JEAPyCRACUgkQAFIJEAAD///8A////AHw+AFiBQwDqgkQA/4BBAP9/PxP/uZd6/9rJtf/bybX/upd7/39AFP+AQQD/gkQA/4FDAOqAQgBc////AP///wDKklv4jlEa/3o7AP+PWC//8+3o///////////////////////z7un/kFox/35AAP+GRwD/mVYA+v///wD///8A0Zpk+NmibP+0d0T/8evj///////+/fv/1sKz/9bCs//9/fr//////+/m2/+NRwL/nloA/5xYAPj///8A////ANKaZPjRmGH/5cKh////////////k149/3UwAP91MQD/lmQ//86rhv+USg3/m1YA/5hSAP+bVgD4////AP///wDSmmT4zpJY/+/bx///////8+TV/8mLT/+TVx//gkIA/5lVAP+VTAD/x6B//7aEVv/JpH7/s39J+P///wD///8A0ppk+M6SWP/u2sf///////Pj1f/Nj1T/2KFs/8mOUv+eWhD/lEsA/8aee/+0glT/x6F7/7J8Rvj///8A////ANKaZPjRmGH/48Cf///////+/v7/2qt//82PVP/OkFX/37KJ/86siv+USg7/mVQA/5hRAP+bVgD4////AP///wDSmmT40ppk/9CVXP/69O////////7+/v/x4M//8d/P//7+/f//////9u7n/6tnJf+XUgD/nFgA+P///wD///8A0ppk+NKaZP/RmWL/1qNy//r07///////////////////////+vXw/9akdP/Wnmn/y5FY/6JfFvj///8A////ANKaZFTSmmTo0ppk/9GYYv/Ql1//5cWm//Hg0P/x4ND/5cWm/9GXYP/RmGH/0ppk/9KaZOjVnmpY////AP///wDSmmQA0ppkEtKaZI7SmmT60ppk/9CWX//OkVb/zpFW/9CWX//SmmT/0ppk/NKaZJDSmmQS0ppkAP///wD///8A0ppkANKaZADSmmQA0ppkKtKaZLrSmmT/0ppk/9KaZP/SmmT/0ppkvNKaZCrSmmQA0ppkANKaZAD///8A////ANKaZADSmmQA0ppkANKaZADSmmQA0ppkUtKaZNzSmmTc0ppkVNKaZADSmmQA0ppkANKaZADSmmQA////AP5/AAD4HwAA4AcAAMADAACAAQAAgAEAAIABAACAAQAAgAEAAIABAACAAQAAgAEAAMADAADgBwAA+B8AAP5/AAAoAAAAIAAAAEAAAAABACAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA////AP///wCCRAAAgkQAAIJEAACCRAAAgkQAAIJEAACCRAAAgkQAAIJEAACCRAAAgkQAAIJEAAyCRACMgkQA6oJEAOqCRACQgkQAEIJEAACCRAAAgkQAAIJEAACCRAAAgkQAAIJEAACCRAAAgkQAAIJEAACCRAAA////AP///wD///8A////AIJEAACCRAAAgkQAAIJEAACCRAAAgkQAAIJEAACCRAAAgkQAAIJEAACCRABigkQA5oJEAP+CRAD/gkQA/4JEAP+CRADqgkQAZoJEAACCRAAAgkQAAIJEAACCRAAAgkQAAIJEAACCRAAAgkQAAIJEAAD///8A////AP///wD///8AgkQAAIJEAACCRAAAgkQAAIJEAACCRAAAgkQAAIJEAACCRAA4gkQAwoJEAP+CRAD/gkQA/4JEAP+CRAD/gkQA/4JEAP+CRAD/gkQAxIJEADyCRAAAgkQAAIJEAACCRAAAgkQAAIJEAACCRAAAgkQAAP///wD///8A////AP///wCCRAAAgkQAAIJEAACCRAAAgkQAAIJEAACCRAAWgkQAmIJEAP+CRAD/gkQA/4JEAP+CRAD/gkQA/4JEAP+CRAD/gkQA/4JEAP+CRAD/gkQA/4JEAJyCRAAYgkQAAIJEAACCRAAAgkQAAIJEAACCRAAA////AP///wD///8A////AIJEAACCRAAAgkQAAIJEAACCRAAAgkQAdIJEAPCCRAD/gkQA/4JEAP+CRAD/gkQA/4JEAP+CRAD/gkQA/4JEAP+CRAD/gkQA/4JEAP+CRAD/gkQA/4JEAPSCRAB4gkQAAIJEAACCRAAAgkQAAIJEAAD///8A////AP///wD///8AgkQAAIJEAACCRAAAgkQASoJEANKCRAD/gkQA/4JEAP+CRAD/g0YA/39AAP9zLgD/bSQA/2shAP9rIQD/bSQA/3MuAP9/PwD/g0YA/4JEAP+CRAD/gkQA/4JEAP+CRADUgkQAToJEAACCRAAAgkQAAP///wD///8A////AP///wB+PwAAgkUAIoJEAKiCRAD/gkQA/4JEAP+CRAD/hEcA/4BBAP9sIwD/dTAA/5RfKv+viF7/vp56/76ee/+wiF7/lWAr/3YxAP9sIwD/f0AA/4RHAP+CRAD/gkQA/4JEAP+CRAD/gkQArIJEACaBQwAA////AP///wD///8A////AIBCAEBzNAD6f0EA/4NFAP+CRAD/gkQA/4VIAP92MwD/bSUA/6N1Tv/ezsL/////////////////////////////////38/D/6V3Uv9uJgD/dTEA/4VJAP+CRAD/gkQA/4JEAP+BQwD/fUAA/4FDAEj///8A////AP///wD///8AzJRd5qBlKf91NgD/dDUA/4JEAP+FSQD/cy4A/3YyAP/PuKP//////////////////////////////////////////////////////9K7qP94NQD/ciwA/4VJAP+CRAD/fkEA/35BAP+LSwD/mlYA6v///wD///8A////AP///wDdpnL/4qx3/8KJUv+PUhf/cTMA/3AsAP90LgD/4dK+/////////////////////////////////////////////////////////////////+TYxf91MAD/dTIA/31CAP+GRwD/llQA/6FcAP+gWwD8////AP///wD///8A////ANGZY/LSm2X/4ap3/92mcP+wdT3/byQA/8mwj////////////////////////////////////////////////////////////////////////////+LYxv9zLgP/jUoA/59bAP+hXAD/nFgA/5xYAPL///8A////AP///wD///8A0ppk8tKaZP/RmWL/1p9q/9ubXv/XqXj////////////////////////////7+fD/vZyG/6BxS/+gcUr/vJuE//r37f//////////////////////3MOr/5dQBf+dVQD/nVkA/5xYAP+cWAD/nFgA8v///wD///8A////AP///wDSmmTy0ppk/9KaZP/SmWP/yohJ//jo2P//////////////////////4NTG/4JDFf9lGAD/bSQA/20kAP9kGAD/fz8S/+Xb0f//////5NG9/6txN/+LOgD/m1QA/51aAP+cWAD/m1cA/5xYAP+cWADy////AP///wD///8A////ANKaZPLSmmT/0ppk/8+TWf/Unmv//v37//////////////////////+TWRr/VwsA/35AAP+ERgD/g0UA/4JGAP9lHgD/kFga/8KXX/+TRwD/jT4A/49CAP+VTQD/n10A/5xYAP+OQQD/lk4A/55cAPL///8A////AP///wD///8A0ppk8tKaZP/SmmT/y4tO/92yiP//////////////////////8NnE/8eCQP+rcTT/ez0A/3IyAP98PgD/gEMA/5FSAP+USwD/jj8A/5lUAP+JNwD/yqV2/694Mf+HNQD/jkAA/82rf/+laBj/jT4A8v///wD///8A////AP///wDSmmTy0ppk/9KaZP/LiUr/4byY///////////////////////gupX/0I5P/+Wuev/Lklz/l1sj/308AP+QSwD/ol0A/59aAP+aVQD/k0oA/8yoh///////+fXv/6pwO//Lp3v///////Pr4f+oay7y////AP///wD///8A////ANKaZPLSmmT/0ppk/8uJSv/hvJj//////////////////////+G7l//Jhkb/0ppk/96nc//fqXX/x4xO/6dkFP+QSQD/llEA/5xXAP+USgD/yaOA///////38uv/qG05/8ijdv//////8efb/6ZpLPL///8A////AP///wD///8A0ppk8tKaZP/SmmT/zIxO/9yxh///////////////////////7dbA/8iEQf/Sm2X/0Zlj/9ScZv/eqHf/2KJv/7yAQf+XTgD/iToA/5lSAP+JNgD/yKFv/611LP+HNQD/jT8A/8qmeP+kZRT/jT4A8v///wD///8A////AP///wDSmmTy0ppk/9KaZP/Pk1n/1J5q//78+//////////////////+/fv/1aFv/8iEQv/Tm2b/0ppl/9GZY//Wn2z/1pZc/9eldf/Bl2b/kUcA/4w9AP+OQAD/lUwA/59eAP+cWQD/jT8A/5ZOAP+eXADy////AP///wD///8A////ANKaZPLSmmT/0ppk/9KZY//KiEn/8d/P///////////////////////47+f/05tm/8iCP//KiEj/yohJ/8eCP//RmGH//vfy///////n1sP/rXQ7/4k4AP+TTAD/nVoA/5xYAP+cVwD/nFgA/5xYAPL///8A////AP///wD///8A0ppk8tKaZP/SmmT/0ptl/8uLTf/aq37////////////////////////////+/fz/6c2y/961jv/etY7/6Myx//78+v//////////////////////3MWv/5xXD/+ORAD/mFQA/51ZAP+cWAD/nFgA8v///wD///8A////AP///wDSmmTy0ppk/9KaZP/SmmT/0ppk/8mFRP/s1b//////////////////////////////////////////////////////////////////////////////+PD/0JFU/7NzMv+WUQD/kUsA/5tXAP+dWQDy////AP///wD///8A////ANKaZP/SmmT/0ppk/9KaZP/Sm2X/z5NZ/8yMT//z5NX/////////////////////////////////////////////////////////////////9Ofa/8yNUP/UmGH/36p5/8yTWv+qaSD/kksA/5ROAPz///8A////AP///wD///8A0ppk5NKaZP/SmmT/0ppk/9KaZP/TnGf/zY9T/82OUv/t1sD//////////////////////////////////////////////////////+7Yw//OkFX/zI5R/9OcZ//SmmP/26V0/9ymdf/BhUf/ol8R6P///wD///8A////AP///wDSmmQ80ppk9tKaZP/SmmT/0ppk/9KaZP/TnGj/zpFW/8qJSv/dson/8uHS//////////////////////////////////Lj0//etIv/y4lL/86QVf/TnGj/0ppk/9KaZP/RmWP/05xn/9ymdfjUnWdC////AP///wD///8A////ANKaZADSmmQc0ppkotKaZP/SmmT/0ppk/9KaZP/Tm2b/0Zli/8qJSf/NjlH/16Z3/+G8mP/myKr/5siq/+G8mP/Xp3f/zY5S/8qISf/RmGH/05tm/9KaZP/SmmT/0ppk/9KaZP/SmmSm0pljINWdaQD///8A////AP///wD///8A0ppkANKaZADSmmQA0ppkQtKaZMrSmmT/0ppk/9KaZP/SmmT/0ptl/9GYYf/Nj1P/y4lL/8qISP/KiEj/y4lK/82PU//RmGH/0ptl/9KaZP/SmmT/0ppk/9KaZP/SmmTO0ppkRtKaZADSmmQA0ppkAP///wD///8A////AP///wDSmmQA0ppkANKaZADSmmQA0ppkANKaZGzSmmTu0ppk/9KaZP/SmmT/0ppk/9KaZP/SmmT/0ppk/9KaZP/SmmT/0ppk/9KaZP/SmmT/0ppk/9KaZP/SmmTw0ppkcNKaZADSmmQA0ppkANKaZADSmmQA////AP///wD///8A////ANKaZADSmmQA0ppkANKaZADSmmQA0ppkANKaZBLSmmSQ0ppk/9KaZP/SmmT/0ppk/9KaZP/SmmT/0ppk/9KaZP/SmmT/0ppk/9KaZP/SmmT/0ppklNKaZBTSmmQA0ppkANKaZADSmmQA0ppkANKaZAD///8A////AP///wD///8A0ppkANKaZADSmmQA0ppkANKaZADSmmQA0ppkANKaZADSmmQy0ppkutKaZP/SmmT/0ppk/9KaZP/SmmT/0ppk/9KaZP/SmmT/0ppkvtKaZDbSmmQA0ppkANKaZADSmmQA0ppkANKaZADSmmQA0ppkAP///wD///8A////AP///wDSmmQA0ppkANKaZADSmmQA0ppkANKaZADSmmQA0ppkANKaZADSmmQA0ppkXNKaZODSmmT/0ppk/9KaZP/SmmT/0ppk5NKaZGDSmmQA0ppkANKaZADSmmQA0ppkANKaZADSmmQA0ppkANKaZADSmmQA////AP///wD///8A////ANKaZADSmmQA0ppkANKaZADSmmQA0ppkANKaZADSmmQA0ppkANKaZADSmmQA0ppkBtKaZIbSmmTo0ppk6tKaZIrSmmQK0ppkANKaZADSmmQA0ppkANKaZADSmmQA0ppkANKaZADSmmQA0ppkANKaZAD///8A////AP/8P///+B///+AH//+AAf//AAD//AAAP/AAAA/gAAAHwAAAA8AAAAPAAAADwAAAA8AAAAPAAAADwAAAA8AAAAPAAAADwAAAA8AAAAPAAAADwAAAA8AAAAPAAAADwAAAA+AAAAfwAAAP/AAAP/8AAP//gAH//+AH///4H////D//" rel="icon" />
  <!--[if lt IE 9]>
    <script src="//cdnjs.cloudflare.com/ajax/libs/html5shiv/3.7.3/html5shiv-printshiv.min.js"></script>
  <![endif]-->
  
</head>
<body>
<div class="wrapper">
<header id="title-block-header">
<h1 class="title" style="text-align:center">Fix C++26 by making the
rank-1, rank-2, rank-k, and rank-2k updates consistent with the
BLAS</h1>

<table style="border:none;float:right">
  <tr>
    <td>Document #: </td>
    <td>P3371R4</td>
  </tr>
  <tr>
    <td>Date: </td>
    <td>2025-03-20</td>
  </tr>
  <tr>
    <td style="vertical-align:top">Project: </td>
    <td>Programming Language C++<br>
      LEWG<br>
    </td>
  </tr>
  <tr>
    <td style="vertical-align:top">Reply-to: </td>
    <td>
      Mark Hoemmen<br>&lt;<a href="mailto:mhoemmen@nvidia.com" class="email">mhoemmen@nvidia.com</a>&gt;<br>
      Ilya Burylov<br>&lt;<a href="mailto:burylov@gmail.com" class="email">burylov@gmail.com</a>&gt;<br>
    </td>
  </tr>
</table>

</header>
<div style="clear:both">
<div id="TOC" role="doc-toc">
<h1 id="toctitle">Contents</h1>
<ul>
<li><a href="#authors" id="toc-authors"><span class="toc-section-number">1</span> Authors</a></li>
<li><a href="#revision-history" id="toc-revision-history"><span class="toc-section-number">2</span> Revision history</a></li>
<li><a href="#abstract" id="toc-abstract"><span class="toc-section-number">3</span> Abstract</a></li>
<li><a href="#discussion-of-proposed-changes" id="toc-discussion-of-proposed-changes"><span class="toc-section-number">4</span> Discussion of proposed changes</a>
<ul>
<li><a href="#support-both-overwriting-and-updating-rank-k-and-rank-2k-updates" id="toc-support-both-overwriting-and-updating-rank-k-and-rank-2k-updates"><span class="toc-section-number">4.1</span> Support both overwriting and
updating rank-k and rank-2k updates</a>
<ul>
<li><a href="#for-rank-k-and-rank-2k-updates-blas-supports-scaling-factor-beta-while-stdlinalg-currently-does-not" id="toc-for-rank-k-and-rank-2k-updates-blas-supports-scaling-factor-beta-while-stdlinalg-currently-does-not"><span class="toc-section-number">4.1.1</span> For rank-k and rank-2k updates,
BLAS supports scaling factor <code>beta</code>, while std::linalg
currently does not</a></li>
<li><a href="#make-these-functions-consistent-with-general-matrix-product" id="toc-make-these-functions-consistent-with-general-matrix-product"><span class="toc-section-number">4.1.2</span> Make these functions consistent
with general matrix product</a></li>
<li><a href="#fix-requires-changing-behavior-of-existing-overloads" id="toc-fix-requires-changing-behavior-of-existing-overloads"><span class="toc-section-number">4.1.3</span> Fix requires changing behavior
of existing overloads</a></li>
<li><a href="#add-new-updating-overloads-make-existing-ones-overwriting" id="toc-add-new-updating-overloads-make-existing-ones-overwriting"><span class="toc-section-number">4.1.4</span> Add new updating overloads; make
existing ones overwriting</a></li>
<li><a href="#summary-of-proposed-changes" id="toc-summary-of-proposed-changes"><span class="toc-section-number">4.1.5</span> Summary of proposed
changes</a></li>
</ul></li>
<li><a href="#change-rank-1-and-rank-2-updates-to-be-consistent-with-rank-k-and-rank-2k" id="toc-change-rank-1-and-rank-2-updates-to-be-consistent-with-rank-k-and-rank-2k"><span class="toc-section-number">4.2</span> Change rank-1 and rank-2 updates
to be consistent with rank-k and rank-2k</a>
<ul>
<li><a href="#current-stdlinalg-behavior" id="toc-current-stdlinalg-behavior"><span class="toc-section-number">4.2.1</span> Current std::linalg
behavior</a></li>
<li><a href="#current-behavior-is-inconsistent-with-blas-standard-and-rank-k-and-rank-2k-updates" id="toc-current-behavior-is-inconsistent-with-blas-standard-and-rank-k-and-rank-2k-updates"><span class="toc-section-number">4.2.2</span> Current behavior is inconsistent
with BLAS Standard and rank-k and rank-2k updates</a></li>
<li><a href="#this-change-would-remove-a-special-case-in-stdlinalgs-design" id="toc-this-change-would-remove-a-special-case-in-stdlinalgs-design"><span class="toc-section-number">4.2.3</span> This change would remove a
special case in std::linalg’s design</a></li>
<li><a href="#exposition-only-concept-no-longer-needed" id="toc-exposition-only-concept-no-longer-needed"><span class="toc-section-number">4.2.4</span> Exposition-only concept no
longer needed</a></li>
<li><a href="#summary-of-proposed-changes-1" id="toc-summary-of-proposed-changes-1"><span class="toc-section-number">4.2.5</span> Summary of proposed
changes</a></li>
</ul></li>
<li><a href="#use-only-the-real-part-of-scaling-factor-alpha-for-hermitian-matrix-rank-1-and-rank-k-updates" id="toc-use-only-the-real-part-of-scaling-factor-alpha-for-hermitian-matrix-rank-1-and-rank-k-updates"><span class="toc-section-number">4.3</span> Use only the real part of scaling
factor <code>alpha</code> for Hermitian matrix rank-1 and rank-k
updates</a>
<ul>
<li><a href="#survey-of-scaling-factors-in-hermitian-matrix-blas-routines" id="toc-survey-of-scaling-factors-in-hermitian-matrix-blas-routines"><span class="toc-section-number">4.3.1</span> Survey of scaling factors in
Hermitian matrix BLAS routines</a></li>
<li><a href="#translation-of-hermitian-blas-concerns-to-stdlinalg" id="toc-translation-of-hermitian-blas-concerns-to-stdlinalg"><span class="toc-section-number">4.3.2</span> Translation of Hermitian BLAS
concerns to std::linalg</a></li>
<li><a href="#alternative-solutions" id="toc-alternative-solutions"><span class="toc-section-number">4.3.3</span> Alternative solutions</a></li>
<li><a href="#summary-of-proposed-changes-2" id="toc-summary-of-proposed-changes-2"><span class="toc-section-number">4.3.4</span> Summary of proposed
changes</a></li>
</ul></li>
<li><a href="#remove-rank-1-and-rank-k-symmetric-and-hermitian-update-overloads-without-alpha-and-constrain-scalar-alpha" id="toc-remove-rank-1-and-rank-k-symmetric-and-hermitian-update-overloads-without-alpha-and-constrain-scalar-alpha"><span class="toc-section-number">4.4</span> Remove rank-1 and rank-k symmetric
and Hermitian update overloads without <code>alpha</code>, and constrain
<code>Scalar alpha</code></a>
<ul>
<li><a href="#summary" id="toc-summary"><span class="toc-section-number">4.4.1</span> Summary</a></li>
<li><a href="#motivation" id="toc-motivation"><span class="toc-section-number">4.4.2</span> Motivation</a></li>
</ul></li>
<li><a href="#things-relating-to-scaling-factors-that-we-do-not-propose-changing" id="toc-things-relating-to-scaling-factors-that-we-do-not-propose-changing"><span class="toc-section-number">4.5</span> Things relating to scaling factors
that we do not propose changing</a>
<ul>
<li><a href="#hermitian-matrix-vector-and-matrix-matrix-products" id="toc-hermitian-matrix-vector-and-matrix-matrix-products"><span class="toc-section-number">4.5.1</span> Hermitian matrix-vector and
matrix-matrix products</a></li>
<li><a href="#triangular-matrix-products-with-implicit-unit-diagonals" id="toc-triangular-matrix-products-with-implicit-unit-diagonals"><span class="toc-section-number">4.5.2</span> Triangular matrix products with
implicit unit diagonals</a></li>
<li><a href="#triangular-solves-with-implicit-unit-diagonals" id="toc-triangular-solves-with-implicit-unit-diagonals"><span class="toc-section-number">4.5.3</span> Triangular solves with implicit
unit diagonals</a></li>
</ul></li>
</ul></li>
<li><a href="#ordering-with-respect-to-other-proposals-and-lwg-issues" id="toc-ordering-with-respect-to-other-proposals-and-lwg-issues"><span class="toc-section-number">5</span> Ordering with respect to other
proposals and LWG issues</a></li>
<li><a href="#implementation-status" id="toc-implementation-status"><span class="toc-section-number">6</span>
Implementation status</a></li>
<li><a href="#acknowledgments" id="toc-acknowledgments"><span class="toc-section-number">7</span> Acknowledgments</a></li>
<li><a href="#wording" id="toc-wording"><span class="toc-section-number">8</span> Wording</a>
<ul>
<li><a href="#new-exposition-only-concepts-for-possibly-packed-input-and-output-matrices" id="toc-new-exposition-only-concepts-for-possibly-packed-input-and-output-matrices"><span class="toc-section-number">8.1</span> New exposition-only concepts for
possibly-packed input and output matrices</a></li>
<li><a href="#rank-1-update-functions-in-synopsis" id="toc-rank-1-update-functions-in-synopsis"><span class="toc-section-number">8.2</span> Rank-1 update functions in
synopsis</a></li>
<li><a href="#rank-2-update-functions-in-synopsis" id="toc-rank-2-update-functions-in-synopsis"><span class="toc-section-number">8.3</span> Rank-2 update functions in
synopsis</a></li>
<li><a href="#rank-k-update-functions-in-synopsis" id="toc-rank-k-update-functions-in-synopsis"><span class="toc-section-number">8.4</span> Rank-k update functions in
synopsis</a></li>
<li><a href="#rank-2k-update-functions-in-synopsis" id="toc-rank-2k-update-functions-in-synopsis"><span class="toc-section-number">8.5</span> Rank-2k update functions in
synopsis</a></li>
<li><a href="#constrain-linear-algebra-value-type" id="toc-constrain-linear-algebra-value-type"><span class="toc-section-number">8.6</span> Constrain linear algebra value
type</a></li>
<li><a href="#specification-of-nonsymmetric-rank-1-update-functions" id="toc-specification-of-nonsymmetric-rank-1-update-functions"><span class="toc-section-number">8.7</span> Specification of nonsymmetric
rank-1 update functions</a></li>
<li><a href="#specification-of-symmetric-and-hermitian-rank-1-update-functions" id="toc-specification-of-symmetric-and-hermitian-rank-1-update-functions"><span class="toc-section-number">8.8</span> Specification of symmetric and
Hermitian rank-1 update functions</a></li>
<li><a href="#specification-of-symmetric-and-hermitian-rank-2-update-functions" id="toc-specification-of-symmetric-and-hermitian-rank-2-update-functions"><span class="toc-section-number">8.9</span> Specification of symmetric and
Hermitian rank-2 update functions</a></li>
<li><a href="#specification-of-rank-k-update-functions" id="toc-specification-of-rank-k-update-functions"><span class="toc-section-number">8.10</span> Specification of rank-k update
functions</a></li>
<li><a href="#specification-of-rank-2k-update-functions" id="toc-specification-of-rank-2k-update-functions"><span class="toc-section-number">8.11</span> Specification of rank-2k update
functions</a></li>
</ul></li>
<li><a href="#appendix-a-example-of-a-generic-numerical-algorithm" id="toc-appendix-a-example-of-a-generic-numerical-algorithm"><span class="toc-section-number">9</span> Appendix A: Example of a generic
numerical algorithm</a></li>
</ul>
</div>
<h1 data-number="1" id="authors"><span class="header-section-number">1</span> Authors<a href="#authors" class="self-link"></a></h1>
<ul>
<li>Mark Hoemmen (mhoemmen@nvidia.com) (NVIDIA)</li>
<li>Ilya Burylov (burylov@gmail.com) (NVIDIA)</li>
</ul>
<h1 data-number="2" id="revision-history"><span class="header-section-number">2</span> Revision history<a href="#revision-history" class="self-link"></a></h1>
<ul>
<li><p>Revision 0 to be submitted 2024-08-15</p></li>
<li><p>Revision 1 to be submitted 2024-09-15</p>
<ul>
<li><p>Main wording changes from R0:</p>
<ul>
<li><p>Instead of just changing the symmetric and Hermitian rank-k and
rank-2k updates to have both overwriting and updating overloads, change
<em>all</em> the update functions in this way: rank-1 (general,
symmetric, and Hermitian), rank-2, rank-k, and rank-2k</p></li>
<li><p>For all the new updating overloads, specify that <code>C</code>
(or <code>A</code>) may alias <code>E</code></p></li>
<li><p>For the new symmetric and Hermitian updating overloads, specify
that the functions access the new <code>E</code> parameter in the same
way (e.g., with respect to the lower or upper triangle) as the
<code>C</code> (or <code>A</code>) parameter</p></li>
<li><p>Add exposition-only concept <em><code>noncomplex</code></em> to
constrain a scaling factor to be noncomplex, as needed for Hermitian
rank-1 and rank-k functions</p></li>
</ul></li>
<li><p>Add Ilya Burylov as coauthor</p></li>
<li><p>Change title and abstract to express the wording changes</p></li>
<li><p>Add nonwording section explaining why we change rank-1 and rank-2
updates to be consistent with rank-k and rank-2k updates</p></li>
<li><p>Add nonwording sections explaining why we don’t change
<code>hermitian_matrix_vector_product</code>,
<code>hermitian_matrix_product</code>,
<code>triangular_matrix_product</code>, or the triangular
solves</p></li>
<li><p>Add nonwording section explaining why we constrain some scaling
factors to be noncomplex at compile time, instead of taking a run-time
approach</p></li>
<li><p>Reorganize and expand nonwording sections</p></li>
</ul></li>
<li><p>Revision 2 to be submitted 2024-10-15</p>
<ul>
<li><p>For Hermitian matrix rank-1 and rank-k updates, do not constrain
the type of the scaling factor <code>alpha</code>, as R1 did. Instead,
define the algorithms to use
<em><code>real-if-needed</code></em><code>(alpha)</code>. Remove
exposition-only concept <em><code>noncomplex</code></em>.</p></li>
<li><p>Remove the exposition-only concept
<em><code>possibly-packed-in-matrix</code></em> that was introduced in
R1, as it is overly restrictive. (Input matrices never need to have
unique layout, even if they are not packed.)</p></li>
</ul></li>
<li><p>Revision 3 to be submitted 2024-11-15</p>
<ul>
<li><p>Remove rank-1 and rank-k update overloads without a
<code>Scalar alpha</code> parameter. Retain rank-1 and rank-k update
overloads with this parameter.</p></li>
<li><p>Constrain “linear algebra value type” to be neither
<code>mdspan</code> nor an execution policy
(<code>is_execution_policy_v&lt;Scalar&gt;</code> is
<code>false</code>). This will prevent ambiguous overloads, even if we
retain overloads without <code>Scalar alpha</code> or add them
later.</p></li>
<li><p>Add nonwording sections motivating this change.</p></li>
</ul></li>
<li><p>Revision 4 to be submitted 2025-04-??</p>
<ul>
<li><p>LEWG voted to forward R3 on 2025-03-18.</p></li>
<li><p>Make wording diff more compact by improving formatting.</p></li>
<li><p>Update (nonwording) “Implementation status” section.</p></li>
<li><p>Harmonise wording diff with proposed fix for <a href="https://cplusplus.github.io/LWG/lwg-active.html#4137">LWG4137</a>
(atop which this paper was and is rebased) and add Editorial Notes
explaining where the rebase affects this proposal’s wording
changes.</p></li>
</ul></li>
</ul>
<h1 data-number="3" id="abstract"><span class="header-section-number">3</span> Abstract<a href="#abstract" class="self-link"></a></h1>
<p>We propose the following changes to [linalg] that improve consistency
of the rank-1, rank-2, rank-k, and rank-2k update functions with the
BLAS.</p>
<ol type="1">
<li><p>Add “updating” overloads to all the rank-1, rank-2, rank-k, and
rank-2k update functions: general, symmetric, and Hermitian. The new
overloads are analogous to the updating overloads of
<code>matrix_product</code>. For example,
<code>symmetric_matrix_rank_k_update(alpha, A, scaled(beta, C), C, upper_triangle)</code>
will perform <span class="math inline"><em>C</em> := <em>β</em><em>C</em> + <em>α</em><em>A</em><em>A</em><sup><em>T</em></sup></span>.
This makes the functions consistent with the BLAS’s behavior for nonzero
<code>beta</code>, and also more consistent with the behavior of
<code>matrix_product</code> (of which they are mathematically a special
case).</p></li>
<li><p>Change the behavior of all the existing rank-1, rank-2, rank-k,
and rank-2k update functions (general, symmetric, and Hermitian) to be
“overwriting” instead of “unconditionally updating.” For example,
<code>symmetric_matrix_rank_k_update(alpha, A, C, upper_triangle)</code>
will perform <span class="math inline"><em>C</em> = <em>α</em><em>A</em><em>A</em><sup><em>T</em></sup></span>
instead of <span class="math inline"><em>C</em> := <em>C</em> + <em>α</em><em>A</em><em>A</em><sup><em>T</em></sup></span>.
This makes them consistent with the BLAS’s behavior when
<code>beta</code> is zero.</p></li>
<li><p>For the overloads of <code>hermitian_rank_1_update</code> and
<code>hermitian_rank_k_update</code> that have an <code>alpha</code>
scaling factor parameter, only use
<em><code>real-if-needed</code></em><code>(alpha)</code> in the update.
This ensures that the update will be mathematically Hermitian, and makes
the behavior well defined if <code>alpha</code> has nonzero imaginary
part. The change is also consistent with our proposed resolution for LWG
4136 (“Specify behavior of [linalg] Hermitian algorithms on diagonal
with nonzero imaginary part”).</p></li>
<li><p>Remove overloads of rank-1 and rank-k symmetric and Hermitian
update functions without a <code>Scalar alpha</code> parameter. Retain
only those overloads that have a <code>Scalar alpha</code> parameter. In
case WG21 wants to add those overloads or similar ones later, constrain
linear algebra value types (and therefore <code>Scalar</code>) to be
neither <code>mdspan</code> nor an execution policy. This avoids
potentially ambiguous overloads.</p></li>
</ol>
<p>Items (2), (3), and (4) are breaking changes to the current Working
Draft. Thus, we must finish this before finalization of C++26.</p>
<h1 data-number="4" id="discussion-of-proposed-changes"><span class="header-section-number">4</span> Discussion of proposed changes<a href="#discussion-of-proposed-changes" class="self-link"></a></h1>
<h2 data-number="4.1" id="support-both-overwriting-and-updating-rank-k-and-rank-2k-updates"><span class="header-section-number">4.1</span> Support both overwriting and
updating rank-k and rank-2k updates<a href="#support-both-overwriting-and-updating-rank-k-and-rank-2k-updates" class="self-link"></a></h2>
<ol type="1">
<li><p>For rank-k and rank-2k updates (general, symmetric, and
Hermitian), BLAS routines support both overwriting and updating behavior
by exposing a scaling factor <code>beta</code>. The corresponding
[linalg] algorithms currently do not expose the equivalent
functionality. Instead, they are unconditionally updating, as if
<code>beta</code> is one.</p></li>
<li><p>The rank-k and rank-2k updates are special cases of
<code>matrix_product</code>, but as a result of (1), their behavior is
not consistent with <code>matrix_product</code>.</p></li>
<li><p>Therefore, we need to add updating overloads of the rank-k and
rank-2k updates, and change the existing overloads to be
overwriting.</p></li>
<li><p>The change to existing overloads is a breaking change and thus
must be finished before C++26.</p></li>
<li><p>To simplify wording, we add the new exposition-only concept
<em><code>possibly-packed-out-matrix</code></em> for symmetric and
Hermitian matrix update algorithms.</p></li>
</ol>
<h3 data-number="4.1.1" id="for-rank-k-and-rank-2k-updates-blas-supports-scaling-factor-beta-while-stdlinalg-currently-does-not"><span class="header-section-number">4.1.1</span> For rank-k and rank-2k
updates, BLAS supports scaling factor <code>beta</code>, while
std::linalg currently does not<a href="#for-rank-k-and-rank-2k-updates-blas-supports-scaling-factor-beta-while-stdlinalg-currently-does-not" class="self-link"></a></h3>
<p>Each function in any section whose label begins with “linalg.algs”
generally corresponds to one or more routines or functions in the
original BLAS (Basic Linear Algebra Subroutines). Every computation that
the BLAS can do, a function in the C++ Standard Library should be able
to do.</p>
<p>One <code>std::linalg</code> user
<a href="https://github.com/kokkos/stdBLAS/issues/272#issuecomment-2248273146">reported</a>
an exception to this rule. The BLAS routines <code>xSYRK</code>
(SYmmetric Rank-K update) computes <span class="math inline"><em>C</em> := <em>β</em><em>C</em> + <em>α</em><em>A</em><em>A</em><sup><em>T</em></sup></span>,
but the corresponding <code>std::linalg</code> function
<code>symmetric_matrix_rank_k_update</code> only computes <span class="math inline"><em>C</em> := <em>C</em> + <em>α</em><em>A</em><em>A</em><sup><em>T</em></sup></span>.
That is, <code>std::linalg</code> currently has no way to express this
BLAS operation with a general <span class="math inline"><em>β</em></span> scaling factor.</p>
<p>This issue applies to all of the symmetric and Hermitian rank-k and
rank-2k update functions. The following table lists these functions and
what they compute now. <span class="math inline"><em>A</em></span> and
<span class="math inline"><em>B</em></span> denote general matrices,
<span class="math inline"><em>C</em></span> denotes a symmetric or
Hermitian matrix (depending on the algorithm’s name), the superscript
<code>T</code> denotes the transpose, the superscript <code>H</code>
denotes the Hermitian transpose, <span class="math inline"><em>α</em></span> denotes a scaling factor, and
<span class="math inline"><em>ᾱ</em></span> denotes the complex
conjugate of <span class="math inline"><em>α</em></span>. Making the
functions have “updating” overloads that take an input matrix <span class="math inline"><em>E</em></span> would permit <span class="math inline"><em>E</em> = <em>β</em><em>C</em></span>, and thus
make [linalg] able to compute what the BLAS can compute.</p>
<table>
<tr>
<th>
[linalg] algorithm
</th>
<th>
What it computes now
</th>
</tr>
<tr>
<td>
<code>symmetric_matrix_rank_k_update</code>
</td>
<td>
<span class="math inline"><em>C</em> := <em>C</em> + <em>α</em><em>A</em><em>A</em><sup><em>T</em></sup></span>
</td>
</tr>
<tr>
<td>
<code>hermitian_matrix_rank_k_update</code>
</td>
<td>
<span class="math inline"><em>C</em> := <em>C</em> + <em>α</em><em>A</em><em>A</em><sup><em>H</em></sup></span>
</td>
</tr>
<tr>
<td>
<code>symmetric_matrix_rank_2k_update</code>
</td>
<td>
<span class="math inline"><em>C</em> := <em>C</em> + <em>α</em><em>A</em><em>B</em><sup><em>T</em></sup> + <em>α</em><em>B</em><em>A</em><sup><em>T</em></sup></span>
</td>
</tr>
<tr>
<td>
<code>hermitian_matrix_rank_2k_update</code>
</td>
<td>
<span class="math inline"><em>C</em> := <em>C</em> + <em>α</em><em>A</em><em>B</em><sup><em>H</em></sup> + <em>ᾱ</em><em>B</em><em>A</em><sup><em>H</em></sup></span>
</td>
</tr>
</table>
<h3 data-number="4.1.2" id="make-these-functions-consistent-with-general-matrix-product"><span class="header-section-number">4.1.2</span> Make these functions
consistent with general matrix product<a href="#make-these-functions-consistent-with-general-matrix-product" class="self-link"></a></h3>
<p>These functions implement special cases of matrix-matrix products.
The <code>matrix_product</code> function in <code>std::linalg</code>
implements the general case of matrix-matrix products. This function
corresponds to the BLAS’s <code>SGEMM</code>, <code>DGEMM</code>,
<code>CGEMM</code>, and <code>ZGEMM</code>, which compute <span class="math inline"><em>C</em> := <em>β</em><em>C</em> + <em>α</em><em>A</em><em>B</em></span>,
where <span class="math inline"><em>α</em></span> and <span class="math inline"><em>β</em></span> are scaling factors. The
<code>matrix_product</code> function has two kinds of overloads:</p>
<ol type="1">
<li><p><em>overwriting</em> (<span class="math inline"><em>C</em> = <em>A</em><em>B</em></span>)
and</p></li>
<li><p><em>updating</em> (<span class="math inline"><em>C</em> = <em>E</em> + <em>A</em><em>B</em></span>).</p></li>
</ol>
<p>The updating overloads handle the general <span class="math inline"><em>α</em></span> and <span class="math inline"><em>β</em></span> case by
<code>matrix_product(scaled(alpha, A), B, scaled(beta, C), C)</code>.
The specification explicitly permits the input
<code>scaled(beta, C)</code> to alias the output <code>C</code>
(<strong>[linalg.algs.blas3.gemm]</strong> 10: “<em>Remarks</em>:
<code>C</code> may alias <code>E</code>”). The <code>std::linalg</code>
library provides overwriting and updating overloads so that it can do
everything that the BLAS does, just in a more idiomatically C++ way.
Please see
<a href="https://isocpp.org/files/papers/P1673R13.html#function-argument-aliasing-and-zero-scalar-multipliers">P1673R13
Section 10.3</a> (“Function argument aliasing and zero scalar
multipliers”) for a more detailed explanation.</p>
<h3 data-number="4.1.3" id="fix-requires-changing-behavior-of-existing-overloads"><span class="header-section-number">4.1.3</span> Fix requires changing
behavior of existing overloads<a href="#fix-requires-changing-behavior-of-existing-overloads" class="self-link"></a></h3>
<p>The problem with the current symmetric and Hermitian rank-k and
rank-2k functions is that they have the same <em>calling syntax</em> as
the overwriting version of <code>matrix_product</code>, but
<em>semantics</em> that differ from both the overwriting and the
updating versions of <code>matrix_product</code>. For example,</p>
<div class="sourceCode" id="cb1"><pre class="sourceCode cpp"><code class="sourceCode cpp"><span id="cb1-1"><a href="#cb1-1" aria-hidden="true" tabindex="-1"></a>hermitian_matrix_rank_k_update<span class="op">(</span>alpha, A, C<span class="op">)</span>;</span></code></pre></div>
<p>updates <span class="math inline"><em>C</em></span> with <span class="math inline"><em>C</em> + <em>α</em><em>A</em><em>A</em><sup><em>H</em></sup></span>,
but</p>
<div class="sourceCode" id="cb2"><pre class="sourceCode cpp"><code class="sourceCode cpp"><span id="cb2-1"><a href="#cb2-1" aria-hidden="true" tabindex="-1"></a>matrix_product<span class="op">(</span>scaled<span class="op">(</span>alpha, A<span class="op">)</span>, conjugate_transposed<span class="op">(</span>A<span class="op">)</span>, C<span class="op">)</span>;</span></code></pre></div>
<p>overwrites <span class="math inline"><em>C</em></span> with <span class="math inline"><em>α</em><em>A</em><em>A</em><sup><em>H</em></sup></span>.
The current rank-k and rank-2k overloads are not overwriting, so we
can’t just fix this problem by introducing an “updating” overload for
each function.</p>
<p>Incidentally, the fact that these functions have “update” in their
name is not relevant, because that naming choice is original to the
BLAS. The BLAS calls its corresponding <code>xSYRK</code>,
<code>xHERK</code>, <code>xSYR2K</code>, and <code>xHER2K</code>
routines “{Symmetric, Hermitian} rank {one, two} update,” even though
setting <span class="math inline"><em>β</em> = 0</span> makes these
routines “overwriting” in the sense of <code>std::linalg</code>.</p>
<h3 data-number="4.1.4" id="add-new-updating-overloads-make-existing-ones-overwriting"><span class="header-section-number">4.1.4</span> Add new updating overloads;
make existing ones overwriting<a href="#add-new-updating-overloads-make-existing-ones-overwriting" class="self-link"></a></h3>
<p>We propose to fix this by making the functions work just like
<code>matrix_vector_product</code> or <code>matrix_product</code>. This
entails three changes.</p>
<ol type="1">
<li><p>Add two new exposition-only concept
<em><code>possibly-packed-out-matrix</code></em> for constraining output
parameters of the changed or new symmetric and Hermitian update
functions.</p></li>
<li><p>Add “updating” overloads of the symmetric and Hermitian rank-k
and rank-2k update functions.</p>
<ol type="a">
<li><p>The updating overloads take a new input matrix parameter
<code>E</code>, analogous to the updating overloads of
<code>matrix_product</code>, and make <code>C</code> an output parameter
instead of an in/out parameter. For example,
<code>symmetric_matrix_rank_k_update(alpha, A, E, C, upper_triangle)</code>
computes <span class="math inline"><em>C</em> = <em>E</em> + <em>α</em><em>A</em><em>A</em><sup><em>T</em></sup></span>.</p></li>
<li><p>Explicitly permit <code>C</code> and <code>E</code> to alias,
thus permitting the desired case where <code>E</code> is
<code>scaled(beta, C)</code>.</p></li>
<li><p>The updating overloads take <code>E</code> as an
<em><code>in-matrix</code></em>, and take <code>C</code> as a
<em><code>possibly-packed-out-matrix</code></em> (instead of a
<em><code>possibly-packed-inout-matrix</code></em>).</p></li>
<li><p><code>E</code> must be accessed as a symmetric or Hermitian
matrix (depending on the function name) and such accesses must use the
same triangle as <code>C</code>. (The existing [linalg.general] 4
wording for symmetric and Hermitian behavior does not cover
<code>E</code>.)</p></li>
</ol></li>
<li><p>Change the behavior of the existing symmetric and Hermitian
rank-k and rank-2k overloads to be overwriting instead of updating.</p>
<ol type="a">
<li><p>For example,
<code>symmetric_matrix_rank_k_update(alpha, A, C, upper_triangle)</code>
will compute <span class="math inline"><em>C</em> = <em>α</em><em>A</em><em>A</em><sup><em>T</em></sup></span>
instead of <span class="math inline"><em>C</em> := <em>C</em> + <em>α</em><em>A</em><em>A</em><sup><em>T</em></sup></span>.</p></li>
<li><p>Change <code>C</code> from a
<em><code>possibly-packed-inout-matrix</code></em> to a
<em><code>possibly-packed-out-matrix</code></em>.</p></li>
</ol></li>
</ol>
<p>Items (2) and (3) are breaking changes to the current Working Draft.
This needs to be so that we can provide the overwriting behavior <span class="math inline"><em>C</em> := <em>α</em><em>A</em><em>A</em><sup><em>T</em></sup></span>
or <span class="math inline"><em>C</em> := <em>α</em><em>A</em><em>A</em><sup><em>H</em></sup></span>
that the corresponding BLAS routines already provide. Thus, we must
finish this before finalization of C++26.</p>
<p>Both sets of overloads still only write to the specified triangle
(lower or upper) of the output matrix <code>C</code>. As a result, the
new updating overloads only read from that triangle of the input matrix
<code>E</code>. Therefore, even though <code>E</code> may be a different
matrix than <code>C</code>, the updating overloads do not need an
additional <code>Triangle t_E</code> parameter for <code>E</code>. The
<code>symmetric_*</code> functions interpret <code>E</code> as symmetric
in the same way that they interpret <code>C</code> as symmetric, and the
<code>hermitian_*</code> functions interpret <code>E</code> as Hermitian
in the same way that they interpret <code>C</code> as Hermitian.
Nevertheless, we do need new wording to explain how the functions may
interpret and access <code>E</code>.</p>
<h3 data-number="4.1.5" id="summary-of-proposed-changes"><span class="header-section-number">4.1.5</span> Summary of proposed changes<a href="#summary-of-proposed-changes" class="self-link"></a></h3>
<table>
<tr>
<th>
[linalg] algorithm
</th>
<th>
What it computes now
</th>
<th>
Change (overwriting)
</th>
<th>
Add (updating)
</th>
</tr>
<tr>
<td>
<code>symmetric_matrix_rank_k_update</code>
</td>
<td>
<span class="math inline"><em>C</em> := <em>C</em> + <em>α</em><em>A</em><em>A</em><sup><em>T</em></sup></span>
</td>
<td>
<span class="math inline"><em>C</em> = <em>α</em><em>A</em><em>A</em><sup><em>T</em></sup></span>
</td>
<td>
<span class="math inline"><em>C</em> = <em>E</em> + <em>α</em><em>A</em><em>A</em><sup><em>T</em></sup></span>
</td>
</tr>
<tr>
<td>
<code>hermitian_matrix_rank_k_update</code>
</td>
<td>
<span class="math inline"><em>C</em> := <em>C</em> + <em>α</em><em>A</em><em>A</em><sup><em>H</em></sup></span>
</td>
<td>
<span class="math inline"><em>C</em> = <em>α</em><em>A</em><em>A</em><sup><em>H</em></sup></span>
</td>
<td>
<span class="math inline"><em>C</em> = <em>E</em> + <em>α</em><em>A</em><em>A</em><sup><em>H</em></sup></span>
</td>
</tr>
<tr>
<td>
<code>symmetric_matrix_rank_2k_update</code>
</td>
<td>
<span class="math inline"><em>C</em> := <em>C</em> + <em>α</em><em>A</em><em>B</em><sup><em>T</em></sup> + <em>α</em><em>B</em><em>A</em><sup><em>T</em></sup></span>
</td>
<td>
<span class="math inline"><em>C</em> = <em>α</em><em>A</em><em>B</em><sup><em>T</em></sup> + <em>α</em><em>B</em><em>A</em><sup><em>T</em></sup></span>
</td>
<td>
<span class="math inline"><em>C</em> = <em>E</em> + <em>α</em><em>A</em><em>B</em><sup><em>T</em></sup> + <em>α</em><em>B</em><em>A</em><sup><em>T</em></sup></span>
</td>
</tr>
<tr>
<td>
<code>hermitian_matrix_rank_2k_update</code>
</td>
<td>
<span class="math inline"><em>C</em> := <em>C</em> + <em>α</em><em>A</em><em>B</em><sup><em>H</em></sup> + <em>ᾱ</em><em>B</em><em>A</em><sup><em>H</em></sup></span>
</td>
<td>
<span class="math inline"><em>C</em> = <em>α</em><em>A</em><em>B</em><sup><em>H</em></sup> + <em>ᾱ</em><em>B</em><em>A</em><sup><em>H</em></sup></span>
</td>
<td>
<span class="math inline"><em>C</em> = <em>E</em> + <em>α</em><em>A</em><em>B</em><sup><em>H</em></sup> + <em>ᾱ</em><em>B</em><em>A</em><sup><em>H</em></sup></span>
</td>
</tr>
</table>
<h2 data-number="4.2" id="change-rank-1-and-rank-2-updates-to-be-consistent-with-rank-k-and-rank-2k"><span class="header-section-number">4.2</span> Change rank-1 and rank-2
updates to be consistent with rank-k and rank-2k<a href="#change-rank-1-and-rank-2-updates-to-be-consistent-with-rank-k-and-rank-2k" class="self-link"></a></h2>
<ol type="1">
<li><p>Currently, the rank-1 and rank-2 update functions unconditionally
update and do not give users a way to provide a <code>beta</code>
scaling factor.</p></li>
<li><p>This behavior deviates from the BLAS Standard, is inconsistent
with the rank-k and rank-2k update functions, and introduces a special
case in [linalg]’s design.</p></li>
<li><p>We propose making all the rank-1 and rank-2 update functions
consistent with the proposed change to the rank-k and rank-2k update
functions. This means both changing the meaning of the current overloads
to be overwriting, and adding new overloads that are updating. This
includes general (nonsymmetric), symmetric, and Hermitian rank-1 update
functions, as well as symmetric and Hermitian rank-2 update
functions.</p></li>
<li><p>The exposition-only concept
<em><code>possibly-packed-inout-matrix</code></em> is no longer needed.
We propose removing it.</p></li>
</ol>
<h3 data-number="4.2.1" id="current-stdlinalg-behavior"><span class="header-section-number">4.2.1</span> Current std::linalg
behavior<a href="#current-stdlinalg-behavior" class="self-link"></a></h3>
<p>The symmetric and Hermitian rank-k and rank-2k update functions have
the following rank-1 and rank-2 analogs, <span class="math inline"><em>A</em></span> denotes a symmetric or Hermitian
matrix (depending on the function’s name), <span class="math inline"><em>x</em></span> and <span class="math inline"><em>y</em></span> denote vectors, and <span class="math inline"><em>α</em></span> denotes a scaling factor.</p>
<table>
<tr>
<th>
[linalg] algorithm
</th>
<th>
What it computes now
</th>
</tr>
<tr>
<td>
<code>symmetric_matrix_rank_1_update</code>
</td>
<td>
<span class="math inline"><em>A</em> := <em>A</em> + <em>α</em><em>x</em><em>x</em><sup><em>T</em></sup></span>
</td>
</tr>
<tr>
<td>
<code>hermitian_matrix_rank_1_update</code>
</td>
<td>
<span class="math inline"><em>A</em> := <em>A</em> + <em>α</em><em>x</em><em>x</em><sup><em>H</em></sup></span>
</td>
</tr>
<tr>
<td>
<code>symmetric_matrix_rank_2_update</code>
</td>
<td>
<span class="math inline"><em>A</em> := <em>A</em> + <em>α</em><em>x</em><em>y</em><sup><em>T</em></sup> + <em>α</em><em>y</em><em>x</em><sup><em>T</em></sup></span>
</td>
</tr>
<tr>
<td>
<code>hermitian_matrix_rank_2_update</code>
</td>
<td>
<span class="math inline"><em>A</em> := <em>A</em> + <em>α</em><em>x</em><em>y</em><sup><em>H</em></sup> + <em>ᾱ</em><em>x</em><em>y</em><sup><em>H</em></sup></span>
</td>
</tr>
</table>
<p>These functions <em>unconditionally</em> update the matrix <span class="math inline"><em>A</em></span>. They do not have an overwriting
option. In this, they follow the “general” (not necessarily symmetric or
Hermitian) rank-1 update functions.</p>
<table>
<tr>
<th>
[linalg] algorithm
</th>
<th>
What it computes now
</th>
</tr>
<tr>
<td>
<code>matrix_rank_1_update</code>
</td>
<td>
<span class="math inline"><em>A</em> := <em>A</em> + <em>x</em><em>y</em><sup><em>T</em></sup></span>
</td>
</tr>
<tr>
<td>
<code>matrix_rank_1_update_c</code>
</td>
<td>
<span class="math inline"><em>A</em> := <em>A</em> + <em>x</em><em>y</em><sup><em>H</em></sup></span>
</td>
</tr>
</table>
<h3 data-number="4.2.2" id="current-behavior-is-inconsistent-with-blas-standard-and-rank-k-and-rank-2k-updates"><span class="header-section-number">4.2.2</span> Current behavior is
inconsistent with BLAS Standard and rank-k and rank-2k updates<a href="#current-behavior-is-inconsistent-with-blas-standard-and-rank-k-and-rank-2k-updates" class="self-link"></a></h3>
<p>These six rank-1 and rank-2 update functions map to BLAS routines as
follows.</p>
<table>
<tr>
<th>
[linalg] algorithm
</th>
<th>
Corresponding BLAS routine(s)
</th>
</tr>
<tr>
<td>
<code>matrix_rank_1_update</code>
</td>
<td>
<code>xGER</code>
</td>
</tr>
<tr>
<td>
<code>matrix_rank_1_update_c</code>
</td>
<td>
<code>xGERC</code>
</td>
</tr>
<tr>
<td>
<code>symmetric_matrix_rank_1_update</code>
</td>
<td>
<code>xSYR</code>, <code>xSPR</code>
</td>
</tr>
<tr>
<td>
<code>hermitian_matrix_rank_1_update</code>
</td>
<td>
<code>xHER</code>, <code>xHPR</code>
</td>
</tr>
<tr>
<td>
<code>symmetric_matrix_rank_2_update</code>
</td>
<td>
<code>xSYR2</code>, <code>xSPR2</code>
</td>
</tr>
<tr>
<td>
<code>hermitian_matrix_rank_2_update</code>
</td>
<td>
<code>xHER2</code>, <code>xHPR2</code>
</td>
</tr>
</table>
<p>The Reference BLAS and the BLAS Standard (see Chapter 2, pp. 64 - 68)
differ here. The Reference BLAS and the original 1988 BLAS 2 paper
specify all of the rank-1 and rank-2 update routines listed above as
unconditionally updating, and not taking a <span class="math inline"><em>β</em></span> scaling factor. However, the
(2002) BLAS Standard specifies all of these rank-1 and rank-2 update
functions as taking a <span class="math inline"><em>β</em></span>
scaling factor. We consider the latter to express our design intent. It
is also consistent with the corresponding rank-k and rank-2k update
functions in the BLAS, which all take a <span class="math inline"><em>β</em></span> scaling factor and thus can do
either overwriting (with zero <span class="math inline"><em>β</em></span>) or updating (with nonzero <span class="math inline"><em>β</em></span>). These routines include
<code>xSYRK</code>, <code>xHERK</code>, <code>xSYR2K</code>, and
<code>xHER2K</code>. One could also include the general matrix-matrix
product <code>xGEMM</code> among these, as <code>xGEMM</code> also takes
a <span class="math inline"><em>β</em></span> scaling factor.</p>
<h3 data-number="4.2.3" id="this-change-would-remove-a-special-case-in-stdlinalgs-design"><span class="header-section-number">4.2.3</span> This change would remove a
special case in std::linalg’s design<a href="#this-change-would-remove-a-special-case-in-stdlinalgs-design" class="self-link"></a></h3>
<p><a href="https://isocpp.org/files/papers/P1673R13.html#function-argument-aliasing-and-zero-scalar-multipliers">Section
10.3 of P1673R13</a> explains the three ways that the std::linalg design
translates Fortran <code>INTENT(INOUT)</code> arguments into a C++
idiom.</p>
<ol type="1">
<li><p>Provide both in-place and not-in-place overloads for triangular
solve and triangular multiply.</p></li>
<li><p>“Else, if the BLAS function unconditionally updates (like
<code>xGER</code>), we retain read-and-write behavior for that
argument.”</p></li>
<li><p>“Else, if the BLAS function uses a scalar <code>beta</code>
argument to decide whether to read the output argument as well as write
to it (like <code>xGEMM</code>), we provide two versions: a write-only
version (as if <code>beta</code> is zero), and a read-and-write version
(as if <code>beta</code> is nonzero).”</p></li>
</ol>
<p>Our design goal was for functions with vector or matrix output to
imitate <code>std::transform</code> as much as possible. This favors Way
(3) as the default approach, which turns <code>INTENT(INOUT)</code>
arguments on the Fortran side into separate input and output parameters
on the C++ side. Way (2) is really an awkward special case. The BLAS
Standard effectively eliminates this special case by making the rank-1
and rank-2 updates work just like the rank-k and rank-2k updates, with a
<span class="math inline"><em>β</em></span> scaling factor. This makes
it natural to eliminate the Way (2) special case in [linalg] as
well.</p>
<h3 data-number="4.2.4" id="exposition-only-concept-no-longer-needed"><span class="header-section-number">4.2.4</span> Exposition-only concept no
longer needed<a href="#exposition-only-concept-no-longer-needed" class="self-link"></a></h3>
<p>These changes make the exposition-only concept
<em><code>possibly-packed-inout-matrix</code></em> superfluous. We
propose removing it.</p>
<p>Note that this would not eliminate all uses of the exposition-only
concept <em><code>inout-matrix</code></em>. The in-place triangular
matrix product functions <code>triangular_matrix_left_product</code> and
<code>triangular_matrix_right_product</code>, and the in-place
triangular linear system solve functions
<code>triangular_matrix_matrix_left_solve</code> and
<code>triangular_matrix_matrix_right_solve</code> would still use
<em><code>inout-matrix</code></em>.</p>
<h3 data-number="4.2.5" id="summary-of-proposed-changes-1"><span class="header-section-number">4.2.5</span> Summary of proposed changes<a href="#summary-of-proposed-changes-1" class="self-link"></a></h3>
<table>
<tr>
<th>
[linalg] algorithm
</th>
<th>
What it computes now
</th>
<th>
Change (overwriting)
</th>
<th>
Add (updating)
</th>
</tr>
<tr>
<td>
<code>matrix_rank_1_update</code>
</td>
<td>
<span class="math inline"><em>A</em> := <em>A</em> + <em>x</em><em>y</em><sup><em>T</em></sup></span>
</td>
<td>
<span class="math inline"><em>A</em> = <em>x</em><em>y</em><sup><em>T</em></sup></span>
</td>
<td>
<span class="math inline"><em>A</em> = <em>E</em> + <em>x</em><em>y</em><sup><em>T</em></sup></span>
</td>
</tr>
<tr>
<td>
<code>matrix_rank_1_update_c</code>
</td>
<td>
<span class="math inline"><em>A</em> := <em>A</em> + <em>x</em><em>y</em><sup><em>H</em></sup></span>
</td>
<td>
<span class="math inline"><em>A</em> = <em>x</em><em>y</em><sup><em>H</em></sup></span>
</td>
<td>
<span class="math inline"><em>A</em> = <em>E</em> + <em>x</em><em>y</em><sup><em>H</em></sup></span>
</td>
</tr>
<tr>
<td>
<code>symmetric_matrix_rank_1_update</code>
</td>
<td>
<span class="math inline"><em>A</em> := <em>A</em> + <em>α</em><em>x</em><em>x</em><sup><em>T</em></sup></span>
</td>
<td>
<span class="math inline"><em>A</em> = <em>α</em><em>x</em><em>x</em><sup><em>T</em></sup></span>
</td>
<td>
<span class="math inline"><em>A</em> = <em>E</em> + <em>α</em><em>x</em><em>x</em><sup><em>T</em></sup></span>
</td>
</tr>
<tr>
<td>
<code>hermitian_matrix_rank_1_update</code>
</td>
<td>
<span class="math inline"><em>A</em> := <em>A</em> + <em>α</em><em>x</em><em>x</em><sup><em>H</em></sup></span>
</td>
<td>
<span class="math inline"><em>A</em> = <em>α</em><em>x</em><em>x</em><sup><em>H</em></sup></span>
</td>
<td>
<span class="math inline"><em>A</em> = <em>E</em> + <em>α</em><em>x</em><em>x</em><sup><em>H</em></sup></span>
</td>
</tr>
<tr>
<td>
<code>symmetric_matrix_rank_2_update</code>
</td>
<td>
<span class="math inline"><em>A</em> := <em>A</em> + <em>α</em><em>x</em><em>y</em><sup><em>T</em></sup> + <em>α</em><em>y</em><em>x</em><sup><em>T</em></sup></span>
</td>
<td>
<span class="math inline"><em>A</em> = <em>α</em><em>x</em><em>y</em><sup><em>T</em></sup> + <em>α</em><em>y</em><em>x</em><sup><em>T</em></sup></span>
</td>
<td>
<span class="math inline"><em>A</em> = <em>E</em> + <em>α</em><em>x</em><em>y</em><sup><em>T</em></sup> + <em>α</em><em>y</em><em>x</em><sup><em>T</em></sup></span>
</td>
</tr>
<tr>
<td>
<code>hermitian_matrix_rank_2_update</code>
</td>
<td>
<span class="math inline"><em>A</em> := <em>A</em> + <em>α</em><em>x</em><em>y</em><sup><em>H</em></sup> + <em>ᾱ</em><em>x</em><em>y</em><sup><em>H</em></sup></span>
</td>
<td>
<span class="math inline"><em>A</em> = <em>α</em><em>x</em><em>y</em><sup><em>H</em></sup> + <em>ᾱ</em><em>x</em><em>y</em><sup><em>H</em></sup></span>
</td>
<td>
<span class="math inline"><em>A</em> = <em>E</em> + <em>α</em><em>x</em><em>y</em><sup><em>H</em></sup> + <em>ᾱ</em><em>x</em><em>y</em><sup><em>H</em></sup></span>
</td>
</tr>
</table>
<h2 data-number="4.3" id="use-only-the-real-part-of-scaling-factor-alpha-for-hermitian-matrix-rank-1-and-rank-k-updates"><span class="header-section-number">4.3</span> Use only the real part of
scaling factor <code>alpha</code> for Hermitian matrix rank-1 and rank-k
updates<a href="#use-only-the-real-part-of-scaling-factor-alpha-for-hermitian-matrix-rank-1-and-rank-k-updates" class="self-link"></a></h2>
<p>For Hermitian rank-1 and rank-k matrix updates, if users provide a
scaling factor <code>alpha</code>, it must have zero imaginary part.
Otherwise, the matrix update will not be Hermitian, because all elements
on the diagonal of a Hermitian matrix must have zero imaginary part.
Even though <span class="math inline"><em>A</em><em>A</em><sup><em>H</em></sup></span> is
mathematically always Hermitian, if <span class="math inline"><em>α</em></span> has nonzero imaginary part, then
<span class="math inline"><em>α</em><em>A</em><em>A</em><sup><em>H</em></sup></span>
may no longer be a Hermitian matrix. For example, if <span class="math inline"><em>A</em></span> is the identity matrix (with ones
on the diagonal and zeros elsewhere) and <span class="math inline"><em>α</em> = <em>i</em></span> (the imaginary unit,
which is the square root of negative one), then <span class="math inline"><em>α</em><em>A</em><em>A</em><sup><em>H</em></sup></span>
is the diagonal matrix whose diagonal elements are all <span class="math inline"><em>i</em></span>, and thus has nonzero imaginary
part.</p>
<p>The specification of <code>hermitian_matrix_rank_1_update</code> and
<code>hermitian_matrix_rank_k_update</code> does not currently require
that <code>alpha</code> have zero imaginary part. We propose fixing this
by making these update algorithms only use the real part of
<code>alpha</code>, as in
<em><code>real-if-needed</code></em><code>(alpha)</code>. This solution
is consistent with our proposed resolution of
<a href="https://cplusplus.github.io/LWG/issue4136">LWG Issue 4136</a>,
“Specify behavior of [linalg] Hermitian algorithms on diagonal with
nonzero imaginary part,” where we make Hermitian rank-1 and rank-k
matrix updates use only the real part of matrices’ diagonals.</p>
<p>We begin with a summary of all the Hermitian matrix BLAS routines,
how scaling factors influence their mathematical correctness. Then, we
explain how these scaling factor concerns translate into [linalg]
function concerns. Finally, we discuss alternative solutions.</p>
<h3 data-number="4.3.1" id="survey-of-scaling-factors-in-hermitian-matrix-blas-routines"><span class="header-section-number">4.3.1</span> Survey of scaling factors in
Hermitian matrix BLAS routines<a href="#survey-of-scaling-factors-in-hermitian-matrix-blas-routines" class="self-link"></a></h3>
<p>The BLAS’s Hermitian matrix routines take <code>alpha</code> and
<code>beta</code> scaling factors. The BLAS addresses the resulting
correctness concerns in different ways, depending on what each routine
computes. For routines where a nonzero imaginary part could make the
result incorrect, the routine restricts the scaling factor to have a
noncomplex number type. Otherwise, the routine takes the scaling factor
as a complex number type. We discuss all the Hermitian routines
here.</p>
<h4 data-number="4.3.1.1" id="xhemm-hermitian-matrix-matrix-multiply"><span class="header-section-number">4.3.1.1</span> <code>xHEMM</code>:
Hermitian matrix-matrix multiply<a href="#xhemm-hermitian-matrix-matrix-multiply" class="self-link"></a></h4>
<p><code>xHEMM</code> (HErmitian Matrix-matrix Multiply) computes either
<span class="math inline"><em>C</em> := <em>α</em><em>A</em><em>B</em> + <em>β</em><em>C</em></span>
or <span class="math inline"><em>C</em> := <em>α</em><em>B</em><em>A</em> + <em>β</em><em>C</em></span>,
where <span class="math inline"><em>A</em></span> is a Hermitian matrix,
and neither <span class="math inline"><em>B</em></span> nor <span class="math inline"><em>C</em></span> need to be Hermitian. The products
<span class="math inline"><em>A</em><em>B</em></span> and <span class="math inline"><em>B</em><em>A</em></span> thus need not be
Hermitian, so the scaling factors <span class="math inline"><em>α</em></span> and <span class="math inline"><em>β</em></span> can have nonzero imaginary parts.
The BLAS takes them both as complex numbers.</p>
<h4 data-number="4.3.1.2" id="xhemv-hermitian-matrix-vector-multiply"><span class="header-section-number">4.3.1.2</span> <code>xHEMV</code>:
HErmitian Matrix-Vector multiply<a href="#xhemv-hermitian-matrix-vector-multiply" class="self-link"></a></h4>
<p><code>xHEMV</code> (HErmitian Matrix-Vector multiply) computes <span class="math inline"><em>y</em> := <em>α</em><em>A</em><em>x</em> + <em>β</em><em>y</em></span>,
where <span class="math inline"><em>A</em></span> is a Hermitian matrix
and <span class="math inline"><em>x</em></span> and <span class="math inline"><em>y</em></span> are vectors. The scaled matrix
<span class="math inline"><em>α</em><em>A</em></span> does not need to
be Hermitian. Thus, <span class="math inline"><em>α</em></span> and
<span class="math inline"><em>β</em></span> can have nonzero imaginary
parts. The BLAS takes them both as complex numbers.</p>
<h4 data-number="4.3.1.3" id="xher-hermitian-rank-1-update"><span class="header-section-number">4.3.1.3</span> <code>xHER</code>:
HErmitian Rank-1 update<a href="#xher-hermitian-rank-1-update" class="self-link"></a></h4>
<p><code>xHER</code> (HErmitian Rank-1 update) differs between the
Reference BLAS (which computes <span class="math inline"><em>A</em> := <em>α</em><em>x</em><em>x</em><sup><em>H</em></sup> + <em>A</em></span>)
and the BLAS Standard (which computes <span class="math inline"><em>A</em> := <em>α</em><em>x</em><em>x</em><sup><em>H</em></sup> + <em>β</em><em>A</em></span>).
The matrix <span class="math inline"><em>A</em></span> must be
Hermitian, and the rank-1 matrix <span class="math inline"><em>x</em><em>x</em><sup><em>H</em></sup></span> is
always mathematically Hermitian, so both <span class="math inline"><em>α</em></span> and <span class="math inline"><em>β</em></span> need to have zero imaginary part
in order for the update to preserve <span class="math inline"><em>A</em></span>’s Hermitian property. The BLAS
takes them both as real (noncomplex) numbers.</p>
<h4 data-number="4.3.1.4" id="xher2-hermitian-rank-2-update"><span class="header-section-number">4.3.1.4</span> <code>xHER2</code>:
HErmitian Rank-2 update<a href="#xher2-hermitian-rank-2-update" class="self-link"></a></h4>
<p><code>xHER2</code> (HErmitian Rank-2 update) differs between the
Reference BLAS (which computes <span class="math inline"><em>A</em> := <em>α</em><em>x</em><em>y</em><sup><em>H</em></sup> + <em>ᾱ</em><em>y</em><em>x</em><sup><em>H</em></sup> + <em>A</em></span>,
where <span class="math inline"><em>ᾱ</em></span> denotes the complex
conjugate of <span class="math inline"><em>α</em></span>) and the BLAS
Standard (which computes <span class="math inline"><em>A</em> := <em>α</em><em>x</em><em>y</em><sup><em>H</em></sup> + <em>ᾱ</em><em>y</em><em>x</em><sup><em>H</em></sup> + <em>β</em><em>A</em></span>).
The matrix <span class="math inline"><em>A</em></span> must be
Hermitian, and the rank-2 matrix <span class="math inline"><em>α</em><em>x</em><em>y</em><sup><em>H</em></sup> + <em>ᾱ</em><em>y</em><em>x</em><sup><em>H</em></sup></span>
is always mathematically Hermitian, no matter the value of <span class="math inline"><em>α</em></span>. Thus, <span class="math inline"><em>α</em></span> can have nonzero imaginary part,
but <span class="math inline"><em>β</em></span> cannot. The BLAS thus
takes <code>alpha</code> as a complex number, but <code>beta</code> as a
real (noncomplex) number. (There is likely a typo in the BLAS Standard’s
description of the Fortran 95 binding. It says that both
<code>alpha</code> and <code>beta</code> are complex (have type
<code>COMPLEX(&lt;wp&gt;)</code>), even though in the Fortran 77
binding, <code>beta</code> is real (<code>&lt;rtype&gt;</code>). The
BLAS Standard’s description of <code>xHER2K</code> (see below) says that
<code>alpha</code> is complex but <code>beta</code> is real.
<code>xHER2</code> needs to be consistent with <code>xHER2K</code>.)</p>
<h4 data-number="4.3.1.5" id="xherk-hermitian-rank-k-update"><span class="header-section-number">4.3.1.5</span> <code>xHERK</code>:
HErmitian Rank-K update<a href="#xherk-hermitian-rank-k-update" class="self-link"></a></h4>
<p><code>xHERK</code> (HErmitian Rank-K update) computes either <span class="math inline"><em>C</em> := <em>α</em><em>A</em><em>A</em><sup><em>H</em></sup> + <em>β</em><em>C</em></span>
or <span class="math inline"><em>C</em> := <em>α</em><em>A</em><sup><em>H</em></sup><em>A</em> + <em>β</em><em>C</em></span>,
where <span class="math inline"><em>C</em></span> must be Hermitian.
This is a generalization of <code>xHER</code> and thus both <span class="math inline"><em>α</em></span> and <span class="math inline"><em>β</em></span> need to have zero imaginary part.
The BLAS takes them both as real (noncomplex) numbers.</p>
<h4 data-number="4.3.1.6" id="xher2k-hermitian-rank-2k-update"><span class="header-section-number">4.3.1.6</span> <code>xHER2K</code>:
HErmitian Rank-2k update<a href="#xher2k-hermitian-rank-2k-update" class="self-link"></a></h4>
<p><code>xHER2K</code> (HErmitian Rank-2k update) computes either <span class="math inline"><em>C</em> := <em>α</em><em>A</em><em>B</em><sup><em>H</em></sup> + <em>ᾱ</em><em>B</em><em>A</em><sup><em>H</em></sup> + <em>β</em><em>C</em></span>
or <span class="math inline"><em>C</em> := <em>α</em><em>A</em><sup><em>H</em></sup><em>B</em> + <em>ᾱ</em><em>B</em><sup><em>H</em></sup><em>A</em> + <em>β</em><em>C</em></span>.
This is a generalization of <code>xHER2</code>: <span class="math inline"><em>α</em></span> can have nonzero imaginary part,
but <span class="math inline"><em>β</em></span> cannot. The BLAS thus
takes <code>alpha</code> as a complex number, but <code>beta</code> as a
real (noncomplex) number.</p>
<h4 data-number="4.3.1.7" id="summary-of-blas-routine-restrictions"><span class="header-section-number">4.3.1.7</span> Summary of BLAS routine
restrictions<a href="#summary-of-blas-routine-restrictions" class="self-link"></a></h4>
<p>The following table lists, for all the Hermitian matrix update BLAS
routines, whether the routine restricts <code>alpha</code> and/or
<code>beta</code> to have zero imaginary part, and whether the routine
is a generalization of some other routine in the list (N/A, “not
applicable,” means that it is not).</p>
<table>
<tr>
<th>
BLAS routine
</th>
<th>
Restricts <code>alpha</code>
</th>
<th>
Restricts <code>beta</code>
</th>
<th>
Generalizes
</th>
</tr>
<tr>
<td>
<code>xHEMM</code>
</td>
<td>
No
</td>
<td>
No
</td>
<td>
N/A
</td>
</tr>
<tr>
<td>
<code>xHER</code>
</td>
<td>
Yes
</td>
<td>
Yes
</td>
<td>
N/A
</td>
</tr>
<tr>
<td>
<code>xHER2</code>
</td>
<td>
No
</td>
<td>
Yes
</td>
<td>
N/A
</td>
</tr>
<tr>
<td>
<code>xHERK</code>
</td>
<td>
Yes
</td>
<td>
Yes
</td>
<td>
<code>xHER</code>
</td>
</tr>
<tr>
<td>
<code>xHER2K</code>
</td>
<td>
No
</td>
<td>
Yes
</td>
<td>
<code>xHER2</code>
</td>
</tr>
</table>
<h3 data-number="4.3.2" id="translation-of-hermitian-blas-concerns-to-stdlinalg"><span class="header-section-number">4.3.2</span> Translation of Hermitian BLAS
concerns to std::linalg<a href="#translation-of-hermitian-blas-concerns-to-stdlinalg" class="self-link"></a></h3>
<h4 data-number="4.3.2.1" id="assume-changes-proposed-in-previous-sections"><span class="header-section-number">4.3.2.1</span> Assume changes proposed in
previous sections<a href="#assume-changes-proposed-in-previous-sections" class="self-link"></a></h4>
<p>We assume here the changes proposed in previous sections that remove
inout matrix parameters from the rank-1, rank-2, rank-k, and rank-2k
algorithms, and separate these algorithms into overwriting and updating
overloads. This lets us only consider input matrix and vector
parameters.</p>
<h4 data-number="4.3.2.2" id="stdlinalg-and-the-blas-treat-scaling-factors-differently"><span class="header-section-number">4.3.2.2</span> std::linalg and the BLAS
treat scaling factors differently<a href="#stdlinalg-and-the-blas-treat-scaling-factors-differently" class="self-link"></a></h4>
<p>The [linalg] library and the BLAS treat scaling factors in different
ways. First, [linalg] treats the result of <code>scaled</code> just like
any other matrix or vector parameter. It applies any mathematical
requirements (like being Hermitian) to the parameter, regardless of
whether the corresponding argument results from <code>scaled</code>. It
also does not forbid any input argument from being the result of
<code>scaled</code>. Second, the BLAS always exposes <code>alpha</code>
and <code>beta</code> scaling factor parameters separately from the
matrix or vector parameters to which they are applied. In contrast,
[linalg] only exposes a separate <code>alpha</code> scaling factor
(never <code>beta</code>) if it would otherwise be mathematically
impossible to express an operation that the BLAS can express. For
example, for matrices and scaling factors that are noncomplex,
<code>symmetric_matrix_rank_1_update</code> cannot express <span class="math inline"><em>A</em> := <em>A</em> − <em>x</em><em>x</em><sup><em>T</em></sup></span>
by applying <code>scaled</code> to <code>x</code> with a noncomplex
scaling factor (because the square root of <span class="math inline"> − 1</span> is <span class="math inline"><em>i</em></span>).</p>
<h4 data-number="4.3.2.3" id="defer-fixing-places-where-the-blas-can-do-what-stdlinalg-cannot"><span class="header-section-number">4.3.2.3</span> Defer fixing places where
the BLAS can do what std::linalg cannot<a href="#defer-fixing-places-where-the-blas-can-do-what-stdlinalg-cannot" class="self-link"></a></h4>
<p>In some cases, [linalg] does not expose a separate scaling factor
parameter, even when this prevents [linalg] from doing some things that
the BLAS can do. We give an example below of triangular matrix solves
with multiple right-hand sides and <code>alpha</code> scaling not equal
to one, where the matrix has an implicit unit diagonal.</p>
<p>Even though this means that the BLAS can do some things that [linalg]
cannot do, it does not cause [linalg] to violate mathematical
consistency. More importantly, as we show later, [linalg] can be
extended to do whatever the BLAS can do without breaking backwards
compatibility. Thus, we consider the status quo acceptable for
C++26.</p>
<h4 data-number="4.3.2.4" id="scaling-factor-beta-is-not-a-concern"><span class="header-section-number">4.3.2.4</span> Scaling factor
<code>beta</code> is not a concern<a href="#scaling-factor-beta-is-not-a-concern" class="self-link"></a></h4>
<p>The [linalg] library never exposes a scaling factor
<code>beta</code>. For BLAS routines that perform an update with
<code>beta</code> times an inout matrix or vector parameter (e.g., <span class="math inline"><em>β</em><em>y</em></span> or <span class="math inline"><em>β</em><em>C</em></span>), [linalg] instead takes
an input matrix or vector parameter (e.g., <code>E</code>) that can be
separate from the output matrix or vector (e.g., <code>C</code>). For
Hermitian BLAS routines where <code>beta</code> needs to have zero
imaginary part, [linalg] simply requires that <code>E</code> be
Hermitian – a strictly more general requirement. For example, for the
new updating overloads of <code>hermitian_rank_1_update</code> and
<code>hermitian_rank_k_update</code> proposed above, [linalg] expresses
a <code>beta</code> scaling factor by letting users supply
<code>scaled(beta, C)</code> as the argument for <code>E</code>. The
wording only requires that <code>E</code> be Hermitian. If
<code>E</code> is <code>scaled(beta, C)</code>, this concerns only the
product of <code>beta</code> and <code>C</code>. It would be incorrect
to constrain <code>beta</code> or <code>C</code> separately. For
example, if <span class="math inline"><em>β</em> =  − <em>i</em></span>
and <span class="math inline"><em>C</em></span> is the matrix whose
elements are all <span class="math inline"><em>i</em></span>, then <span class="math inline"><em>C</em></span> is not Hermitian but <span class="math inline"><em>β</em><em>C</em></span> (and therefore
<code>scaled(beta, C)</code>) is Hermitian. The same reasoning applies
for the rank-2 and rank-2k updates.</p>
<h4 data-number="4.3.2.5" id="what-this-section-proposes-to-fix-and-the-proposed-solution"><span class="header-section-number">4.3.2.5</span> What this section proposes
to fix, and the proposed solution<a href="#what-this-section-proposes-to-fix-and-the-proposed-solution" class="self-link"></a></h4>
<p>The above arguments help us restrict our concerns. This section of
our proposal concerns itself with Hermitian matrix update algorithms
where</p>
<ul>
<li><p>the algorithm exposes a separate scaling factor parameter
<code>alpha</code>, and</p></li>
<li><p><code>alpha</code> needs to have zero imaginary part,
but</p></li>
<li><p>nothing in the wording currently prevents <code>alpha</code> from
having nonzero imaginary part.</p></li>
</ul>
<p>These correspond exactly to the BLAS’s Hermitian matrix update
routines where the type of <code>alpha</code> is real: <code>xHER</code>
and <code>xHERK</code>. This strongly suggests solving the problem in
[linalg] by constraining the type of <code>alpha</code> to be
noncomplex. However, as we explain in “Alternative solutions” below, it
is hard to define a “noncomplex number” constraint that works well for
user-defined number types. Instead, we propose fixing this in a way that
is consistent with our proposed resolution of
<a href="https://cplusplus.github.io/LWG/issue4136">LWG Issue 4136</a>,
“Specify behavior of [linalg] Hermitian algorithms on diagonal with
nonzero imaginary part.” That is, the Hermitian rank-1 and rank-k update
algorithms will simply use
<em><code>real-if-needed</code></em><code>(alpha)</code> and ignore any
nonzero imaginary part of <code>alpha</code>.</p>
<h3 data-number="4.3.3" id="alternative-solutions"><span class="header-section-number">4.3.3</span> Alternative solutions<a href="#alternative-solutions" class="self-link"></a></h3>
<p>We can think of at least four different ways to solve this problem,
and will explain why we did not choose those solutions.</p>
<ol type="1">
<li><p>Constrain <code>alpha</code> by defining a generic “noncomplex
number type” constraint.</p></li>
<li><p>Only constrain <code>alpha</code> not to be
<code>std::complex&lt;T&gt;</code>; do not try to define a generic
“noncomplex number” constraint.</p></li>
<li><p>Constrain <code>alpha</code> by default using a generic
“noncomplex number type” constraint, but let users specialize an
“opt-out trait” to tell the library that their number type is
noncomplex.</p></li>
<li><p>Impose a precondition that the imaginary part of
<code>alpha</code> is zero.</p></li>
</ol>
<p>If we had to pick one of these solutions, we would favor first (4),
then (3), and then (1). We would object most to (2).</p>
<h4 data-number="4.3.3.1" id="alternative-constrain-alpha-via-generic-noncomplex-constraint"><span class="header-section-number">4.3.3.1</span> Alternative: Constrain
<code>alpha</code> via generic “noncomplex” constraint<a href="#alternative-constrain-alpha-via-generic-noncomplex-constraint" class="self-link"></a></h4>
<p>The BLAS solves this problem by having the Hermitian rank-1 update
routines <code>xHER</code> and rank-k update routines <code>xHERK</code>
take the scaling factor <span class="math inline"><em>α</em></span> as a
noncomplex number. This suggests constraining <code>alpha</code>’s type
to be noncomplex. However, [linalg] accepts user-defined number types,
including user-defined complex number types. How do we define a
“noncomplex number type”? If we get it wrong and say that a number type
is complex when its “imaginary part” is always zero, we end up excluding
a perfectly valid custom number type.</p>
<p>For number types that have an additive identity (like zero for the
integers and reals), it’s mathematically correct to treat those as
“complex numbers” whose imaginary parts are always zero. This is what
<code>conjugated_accessor</code> does if you give it a number type for
which ADL cannot find <code>conj</code>. P3050 optimizes
<code>conjugated</code> for such number types by bypassing
<code>conjugated_accessor</code> and using <code>default_accessor</code>
instead, but this is just a code optimization. Therefore, it can afford
to be conservative: if a number type <em>might</em> be complex, then
<code>conjugated</code> needs to use <code>conjugated_accessor</code>.
This is why P3050’s approach doesn’t apply here. If a number type has
ADL-findable <code>conj</code>, <code>real</code>, and
<code>imag</code>, then it <em>might</em> be complex. However, if we
define the opposite of that as “noncomplex,” then we might be preventing
users from using otherwise reasonable number types.</p>
<p>The following <code>MyRealNumber</code> example always has zero
imaginary part, but nevertheless has ADL-findable <code>conj</code>,
<code>real</code>, and <code>imag</code>. Furthermore, it has a
constructor for which <code>MyRealNumber(1.2, 3.4)</code> is
well-formed. (This is an unfortunate design choice; making
<code>precision</code> have class type that is not implicitly
convertible from <code>double</code> would be wiser, so that users would
have to type something like
<code>MyRealNumber(1.2, Precision(42))</code>.) As a result, there is no
reasonable way to tell at compile time if <code>MyRealNumber</code>
might represent a complex number.</p>
<div class="sourceCode" id="cb3"><pre class="sourceCode cpp"><code class="sourceCode cpp"><span id="cb3-1"><a href="#cb3-1" aria-hidden="true" tabindex="-1"></a><span class="kw">class</span> MyRealNumber <span class="op">{</span></span>
<span id="cb3-2"><a href="#cb3-2" aria-hidden="true" tabindex="-1"></a><span class="kw">public</span><span class="op">:</span></span>
<span id="cb3-3"><a href="#cb3-3" aria-hidden="true" tabindex="-1"></a>  <span class="kw">explicit</span> MyRealNumber<span class="op">(</span><span class="dt">double</span> initial_value<span class="op">)</span>;</span>
<span id="cb3-4"><a href="#cb3-4" aria-hidden="true" tabindex="-1"></a>  <span class="co">// precision represents the amount of storage</span></span>
<span id="cb3-5"><a href="#cb3-5" aria-hidden="true" tabindex="-1"></a>  <span class="co">// used by an arbitrary-precision real number object</span></span>
<span id="cb3-6"><a href="#cb3-6" aria-hidden="true" tabindex="-1"></a>  <span class="kw">explicit</span> MyRealNumber<span class="op">(</span><span class="dt">double</span> initial_value, <span class="dt">int</span> precision<span class="op">)</span>;</span>
<span id="cb3-7"><a href="#cb3-7" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb3-8"><a href="#cb3-8" aria-hidden="true" tabindex="-1"></a>  MyRealNumber<span class="op">()</span>; <span class="co">// result is the additive identity &quot;zero&quot;</span></span>
<span id="cb3-9"><a href="#cb3-9" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb3-10"><a href="#cb3-10" aria-hidden="true" tabindex="-1"></a>  <span class="co">// ... other members to make MyRealNumber regular ...</span></span>
<span id="cb3-11"><a href="#cb3-11" aria-hidden="true" tabindex="-1"></a>  <span class="co">// ... hidden friend overloaded arithmetic operators ...</span></span>
<span id="cb3-12"><a href="#cb3-12" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb3-13"><a href="#cb3-13" aria-hidden="true" tabindex="-1"></a>  <span class="kw">friend</span> MyRealNumber conj<span class="op">(</span>MyRealNumber x<span class="op">)</span> <span class="op">{</span> <span class="cf">return</span>  x; <span class="op">}</span></span>
<span id="cb3-14"><a href="#cb3-14" aria-hidden="true" tabindex="-1"></a>  <span class="kw">friend</span> MyRealNumber real<span class="op">(</span>MyRealNumber x<span class="op">)</span> <span class="op">{</span> <span class="cf">return</span>  x; <span class="op">}</span></span>
<span id="cb3-15"><a href="#cb3-15" aria-hidden="true" tabindex="-1"></a>  <span class="kw">friend</span> MyRealNumber imag<span class="op">(</span>MyRealNumber<span class="op">)</span>   <span class="op">{</span> <span class="cf">return</span> <span class="op">{}</span>; <span class="op">}</span></span>
<span id="cb3-16"><a href="#cb3-16" aria-hidden="true" tabindex="-1"></a><span class="op">}</span>;</span></code></pre></div>
<p>It’s reasonable to write custom noncomplex number types that define
ADL-findable <code>conj</code>, <code>real</code>, and
<code>imag</code>. First, users may want to write or use libraries of
generic numerical algorithms that work for both complex and noncomplex
number types. P1673 argues that defining <code>conj</code> to be
type-preserving (unlike <code>std::conj</code> in the C++ Standard
Library) makes this possible. For example, Appendix A below shows how to
implement a generic two-norm absolute value (or magnitude, for complex
numbers) function, using this interface. Second, the Standard Library
might lead users to write a type like this. <code>std::conj</code>
accepts arguments of any integer or floating-point type, none of which
represent complex numbers. The Standard makes the unfortunate choice for
<code>std::conj</code> of an integer type to return
<code>std::complex&lt;double&gt;</code>. However, users who try to make
<code>conj(MyRealNumber)</code> return
<code>std::complex&lt;MyRealNumber&gt;</code> would find out that
<code>std::complex&lt;MyRealNumber&gt;</code> does not compile, because
<code>std::complex&lt;T&gt;</code> requires that <code>T</code> be a
floating-point type. The least-effort next step would be to make
<code>conj(MyRealNumber)</code> return <code>MyRealNumber</code>.</p>
<p>We want rank-1 and rank-k Hermitian matrix updates to work with types
like <code>MyRealNumber</code>, but any reasonable way to constrain the
type of <code>alpha</code> would exclude <code>MyRealNumber</code>.</p>
<h4 data-number="4.3.3.2" id="alternative-only-constrain-alpha-not-to-be-stdcomplex"><span class="header-section-number">4.3.3.2</span> Alternative: Only constrain
<code>alpha</code> not to be <code>std::complex</code><a href="#alternative-only-constrain-alpha-not-to-be-stdcomplex" class="self-link"></a></h4>
<p>A variant of this suggestion would be only to constrain
<code>alpha</code> not to be <code>std::complex&lt;T&gt;</code>, and not
try to define a generic “noncomplex number” constraint. However, this
would break generic numerical algorithms by making valid code for the
non-Standard complex number case invalid code for
<code>std::complex&lt;T&gt;</code>. We do not want [linalg] to treat
custom complex number types differently than
<code>std::complex</code>.</p>
<h4 data-number="4.3.3.3" id="alternative-constrain-alpha-by-default-but-let-users-opt-out"><span class="header-section-number">4.3.3.3</span> Alternative: Constrain
<code>alpha</code> by default, but let users “opt out”<a href="#alternative-constrain-alpha-by-default-but-let-users-opt-out" class="self-link"></a></h4>
<p>Another option would be to constrain <code>alpha</code> by default
using the same generic “noncomplex number type” constraint as in Option
(1), but let users specialize an “opt-out trait” to tell the library
that their number type is noncomplex. We would add a new “tag” type
trait <code>is_noncomplex</code> that users can specialize. By default,
<code>is_noncomplex&lt;T&gt;::value</code> is <code>false</code> for any
type <code>T</code>. This does <em>not</em> mean that the type is
complex, just that the user declares their type to be noncomplex. The
distinction matters, because a noncomplex number type might still
provide ADL-findable <code>conj</code>, <code>real</code>, and
<code>imag</code>, as we showed above. Users must take positive action
to declare their type <code>U</code> as “not a complex number type,” by
specializing <code>is_noncomplex&lt;U&gt;</code> so that
<code>is_noncomplex&lt;U&gt;::value</code> is <code>true</code>. If
users do that, then the library will ignore any ADL-findable functions
<code>conj</code>, <code>real</code>, and <code>imag</code> (whether or
not they exist), and will assume that the number type is noncomplex.</p>
<p>Standard Library precedent for this approach is in heterogeneous
lookup for associative containers (see N3657 for ordered associative
containers, and P0919 and P1690 for unordered containers). User-defined
hash functions and key equality comparison functions can tell the
container to provide heterogeneous comparisons by exposing a
<code>static constexpr bool is_transparent</code> whose value is
<code>true</code>. Default behavior does not expose heterogeneous
comparisons. Thus, users must opt in at compile time to assert something
about their user-defined types. Another example is
<code>uses_allocator&lt;T, alloc&gt;</code>, whose <code>value</code>
member defaults to <code>false</code> unless <code>T</code> has a nested
type <code>allocator_type</code> that is convertible from
<code>Alloc</code>. Standard Library types like <code>tuple</code> use
<code>uses_allocator</code> to determine if a user-defined type
<code>T</code> is allocator-aware.</p>
<p>Of the three constraint-based approaches discussed in this proposal,
we favor this one the most. It still treats types “as they are” and does
not permit users to claim that a type is complex when it lacks the
needed operations, but it lets users optimize by giving the Standard
Library a single bit of compile-time information. By default, any linear
algebra value type (see [linalg.reqs.val]) that meets the
<code>maybe_complex</code> concept below would be considered “possibly
complex.” Types that do not meet this concept would result in
compilation errors; users would then be able to search documentation or
web pages to find out that they need to specialize
<code>is_noncomplex</code>.</p>
<div class="sourceCode" id="cb4"><pre class="sourceCode cpp"><code class="sourceCode cpp"><span id="cb4-1"><a href="#cb4-1" aria-hidden="true" tabindex="-1"></a><span class="kw">template</span><span class="op">&lt;</span><span class="kw">class</span> T<span class="op">&gt;</span></span>
<span id="cb4-2"><a href="#cb4-2" aria-hidden="true" tabindex="-1"></a><span class="kw">concept</span> maybe_complex <span class="op">=</span></span>
<span id="cb4-3"><a href="#cb4-3" aria-hidden="true" tabindex="-1"></a>  std<span class="op">::</span>semiregular<span class="op">&lt;</span>T<span class="op">&gt;</span> <span class="op">&amp;&amp;</span></span>
<span id="cb4-4"><a href="#cb4-4" aria-hidden="true" tabindex="-1"></a>  <span class="kw">requires</span><span class="op">(</span>T t<span class="op">)</span> <span class="op">{</span></span>
<span id="cb4-5"><a href="#cb4-5" aria-hidden="true" tabindex="-1"></a>    <span class="op">{</span>conj<span class="op">(</span>t<span class="op">)}</span> <span class="op">-&gt;</span> T;</span>
<span id="cb4-6"><a href="#cb4-6" aria-hidden="true" tabindex="-1"></a>    <span class="op">{</span>real<span class="op">(</span>t<span class="op">)}</span> <span class="op">-&gt;</span> std<span class="op">::</span>convertible_to<span class="op">&lt;</span>T<span class="op">&gt;</span>;</span>
<span id="cb4-7"><a href="#cb4-7" aria-hidden="true" tabindex="-1"></a>    <span class="op">{</span>imag<span class="op">(</span>t<span class="op">)}</span> <span class="op">-&gt;</span> std<span class="op">::</span>convertible_to<span class="op">&lt;</span>T<span class="op">&gt;</span>;</span>
<span id="cb4-8"><a href="#cb4-8" aria-hidden="true" tabindex="-1"></a>  <span class="op">}</span>;</span></code></pre></div>
<p>P1673 generally avoids approaches based on specializing traits. Its
design philosophy favors treating types as they are. Users should not
need to do something “extra” with their custom number types to get
correct behavior, beyond what they would reasonably need to define to
make a custom number type behave like a number.</p>
<p>We base this principle on our past experiences in generic numerical
algorithms development. In the 2010’s, one of the authors maintained a
generic mathematical algorithms library called Trilinos. The Teuchos
(pronounced “TEFF-os”) package of Trilinos provides a monolithic
<code>ScalarTraits</code> class template that defines different
properties of a number type. It combines the features of
<code>std::numeric_limits</code> with generic complex arithmetic
operations like <code>conjugate</code>, <code>real</code>, and
<code>imag</code>. Trilinos’ generic algorithms assume that number types
are regular and define overloaded <code>+</code>, <code>-</code>,
<code>*</code>, and <code>/</code>, but use
<code>ScalarTraits&lt;T&gt;::conjugate</code>,
<code>ScalarTraits&lt;T&gt;::real</code>, and
<code>ScalarTraits&lt;T&gt;::imag</code>. As a result, users with a
custom complex number type had to specialize <code>ScalarTraits</code>
and provide all these operations. Even if users had imitated
<code>std::complex</code>’s interface perfectly and provided
ADL-findable <code>conj</code>, <code>real</code>, and
<code>imag</code>, users had to do extra work to make Trilinos compile
and run correctly for their numbers. With P1673, we decided instead that
users who define a custom complex number type with an interface
sufficiently like <code>std::complex</code> should get reasonable
behavior without needing to do anything else.</p>
<p>As a tangent, we would like to comment on the monolithic design of
<code>Teuchos::ScalarTraits</code>. The monolithic design was partly an
imitation of <code>std::numeric_limits</code>, and partly a side effect
of a requirement to support pre-C++11 compilers that did not permit
partial specialization of function templates. (The typical pre-C++11
work-around is to define an unspecialized function template that
dispatches to a possibly specialized class template.) Partial
specialization of function templates and C++14’s variable templates both
encourage “breaking up” monolithic traits classes into separate traits.
Our paper P1370R1 (“Generic numerical algorithm development with(out)
<code>numeric_limits</code>”) aligns with this trend.</p>
<h4 data-number="4.3.3.4" id="alternative-impose-precondition-on-alpha"><span class="header-section-number">4.3.3.4</span> Alternative: Impose
precondition on <code>alpha</code><a href="#alternative-impose-precondition-on-alpha" class="self-link"></a></h4>
<p>Another option would be to impose a precondition that
<em><code>imag-if-needed</code></em><code>(alpha)</code> is zero.
However, this would be inconsistent with our proposed resolution of
<a href="https://cplusplus.github.io/LWG/issue4136">LWG Issue 4136</a>,
“Specify behavior of [linalg] Hermitian algorithms on diagonal with
nonzero imaginary part”. WG21 members have expressed wanting
<em>fewer</em> preconditions and <em>less</em> undefined behavior in the
Standard Library.</p>
<p>If users call Hermitian matrix rank-1 or rank-k updates with
<code>alpha</code> being <code>std::complex&lt;float&gt;</code> or
<code>std::complex&lt;double&gt;</code>, implementations of [linalg]
that call an underlying C or Fortran BLAS would have to get the real
part of <code>alpha</code> anyway, because these BLAS routines only take
<code>alpha</code> as a real type. Thus, our proposed solution – to
<em>define</em> the behavior of the update algorithms as using
<em><code>real-if-needed</code></em><code>(alpha)</code> – would not add
overhead.</p>
<h3 data-number="4.3.4" id="summary-of-proposed-changes-2"><span class="header-section-number">4.3.4</span> Summary of proposed changes<a href="#summary-of-proposed-changes-2" class="self-link"></a></h3>
<p>For Hermitian matrix update algorithms where</p>
<ul>
<li><p>the algorithm exposes a separate scaling factor parameter
<code>alpha</code>, and</p></li>
<li><p><code>alpha</code> needs to have zero imaginary part,
but</p></li>
<li><p>nothing in the wording currently prevents <code>alpha</code> from
having nonzero imaginary part,</p></li>
</ul>
<p>specify that these algorithms use
<em><code>real-if-needed</code></em><code>(alpha)</code> and ignore any
nonzero imaginary part of <code>alpha</code>.</p>
<h2 data-number="4.4" id="remove-rank-1-and-rank-k-symmetric-and-hermitian-update-overloads-without-alpha-and-constrain-scalar-alpha"><span class="header-section-number">4.4</span> Remove rank-1 and rank-k
symmetric and Hermitian update overloads without <code>alpha</code>, and
constrain <code>Scalar alpha</code><a href="#remove-rank-1-and-rank-k-symmetric-and-hermitian-update-overloads-without-alpha-and-constrain-scalar-alpha" class="self-link"></a></h2>
<h3 data-number="4.4.1" id="summary"><span class="header-section-number">4.4.1</span> Summary<a href="#summary" class="self-link"></a></h3>
<p>For the rank-1 and rank-k symmetric and Hermitian update functions,
Revision 3 of this paper adds two changes.</p>
<ol type="1">
<li><p>Constrain linear algebra value types (and thus
<code>Scalar alpha</code>) to be neither <code>mdspan</code> nor an
execution policy.</p></li>
<li><p>Remove overloads that do not have a <code>Scalar alpha</code>
parameter. Keep the overloads that have a <code>Scalar alpha</code>
parameter.</p></li>
</ol>
<h3 data-number="4.4.2" id="motivation"><span class="header-section-number">4.4.2</span> Motivation<a href="#motivation" class="self-link"></a></h3>
<h4 data-number="4.4.2.1" id="constraining-scalar-prevents-ambiguous-overloads"><span class="header-section-number">4.4.2.1</span> Constraining
<code>Scalar</code> prevents ambiguous overloads<a href="#constraining-scalar-prevents-ambiguous-overloads" class="self-link"></a></h4>
<p>As motivation for constraining <code>Scalar</code>, consider
<code>symmetric_matrix_rank_k_update</code>. Previous revisions of this
paper added the first overload (updating, taking <code>E</code> as well
as <code>C</code>).</p>
<div class="sourceCode" id="cb5"><pre class="sourceCode cpp"><code class="sourceCode cpp"><span id="cb5-1"><a href="#cb5-1" aria-hidden="true" tabindex="-1"></a><span class="kw">template</span><span class="op">&lt;</span>in<span class="op">-</span>matrix InMat1,</span>
<span id="cb5-2"><a href="#cb5-2" aria-hidden="true" tabindex="-1"></a>         in<span class="op">-</span>matrix InMat2,</span>
<span id="cb5-3"><a href="#cb5-3" aria-hidden="true" tabindex="-1"></a>         possibly<span class="op">-</span>packed<span class="op">-</span>out<span class="op">-</span>matrix OutMat,</span>
<span id="cb5-4"><a href="#cb5-4" aria-hidden="true" tabindex="-1"></a>         <span class="kw">class</span> Triangle<span class="op">&gt;</span></span>
<span id="cb5-5"><a href="#cb5-5" aria-hidden="true" tabindex="-1"></a><span class="dt">void</span> symmetric_matrix_rank_k_update<span class="op">(</span></span>
<span id="cb5-6"><a href="#cb5-6" aria-hidden="true" tabindex="-1"></a>  InMat1 A,</span>
<span id="cb5-7"><a href="#cb5-7" aria-hidden="true" tabindex="-1"></a>  InMat2 E,</span>
<span id="cb5-8"><a href="#cb5-8" aria-hidden="true" tabindex="-1"></a>  OutMat C,</span>
<span id="cb5-9"><a href="#cb5-9" aria-hidden="true" tabindex="-1"></a>  Triangle t<span class="op">)</span>;</span>
<span id="cb5-10"><a href="#cb5-10" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb5-11"><a href="#cb5-11" aria-hidden="true" tabindex="-1"></a><span class="kw">template</span><span class="op">&lt;</span><span class="kw">class</span> Scalar,</span>
<span id="cb5-12"><a href="#cb5-12" aria-hidden="true" tabindex="-1"></a>         in<span class="op">-</span>matrix InMat,</span>
<span id="cb5-13"><a href="#cb5-13" aria-hidden="true" tabindex="-1"></a>         possibly<span class="op">-</span>packed<span class="op">-</span>out<span class="op">-</span>matrix OutMat,</span>
<span id="cb5-14"><a href="#cb5-14" aria-hidden="true" tabindex="-1"></a>         <span class="kw">class</span> Triangle<span class="op">&gt;</span></span>
<span id="cb5-15"><a href="#cb5-15" aria-hidden="true" tabindex="-1"></a><span class="dt">void</span> symmetric_matrix_rank_k_update<span class="op">(</span></span>
<span id="cb5-16"><a href="#cb5-16" aria-hidden="true" tabindex="-1"></a>  Scalar alpha,</span>
<span id="cb5-17"><a href="#cb5-17" aria-hidden="true" tabindex="-1"></a>  InMat A,</span>
<span id="cb5-18"><a href="#cb5-18" aria-hidden="true" tabindex="-1"></a>  OutMat C,</span>
<span id="cb5-19"><a href="#cb5-19" aria-hidden="true" tabindex="-1"></a>  Triangle t<span class="op">)</span>;</span></code></pre></div>
<p>Our implementation experiments showed that for <code>mdspan</code>
<code>A</code> and <code>C</code>, the call
<code>symmetric_matrix_rank_k_update(A, C, C, Triangle{})</code> is
ambiguous. This is because the <code>Scalar</code> template parameter is
not sufficiently constrained, so it could match either an
<code>mdspan</code> (as in the first overload above) or
<code>Scalar alpha</code> (as in the second overload above).</p>
<p>Throughout <strong>[linalg]</strong>, any template parameter named
<code>Scalar</code> is constrained to be a “linear algebra value type.”
The definition in <strong>[linalg.reqs.val]</strong> 1 – 3 currently
only constrains linear algebra value types to be
<code>semiregular</code>. Constraining it further to be neither
<code>mdspan</code> nor an execution policy resolves this ambiguity.</p>
<p>Another option would be to constrain <code>Scalar</code> to be
multipliable by something. However, this would go against the wording
style expressed in <strong>[linalg.reqs.alg]</strong>. Instead of
constraining types, that section merely says, “It is a precondition of
the algorithm that [any mathematical expression that the algorithm might
reasonably use] is a well-formed expression.” That is, the algorithms
generally don’t constrain linear algebra value types to meet their
expression requirements. This imitates the wording of Standard
Algorithms like <code>accumulate</code> and <code>reduce</code>.</p>
<h4 data-number="4.4.2.2" id="removing-non-scalar-overloads-simplifies-wording-and-implementations"><span class="header-section-number">4.4.2.2</span> Removing
non-<code>Scalar</code> overloads simplifies wording and
implementations<a href="#removing-non-scalar-overloads-simplifies-wording-and-implementations" class="self-link"></a></h4>
<p>We propose to go further. For any algorithm that needs an
<code>alpha</code> parameter overload in order to make sense, we propose
discarding the overloads that do <em>not</em> have an <code>alpha</code>
parameter, and keeping only the overloads that do. The only algorithms
that would be affected are the symmetric and Hermitian rank-1 and rank-k
updates.</p>
<p>This change halves the number of overloads of the symmetric and
Hermitian rank-1 and rank-k functions. This mitigates the addition of
updating overloads. Retaining only the <code>alpha</code> overloads also
makes correct use of this interface more obvious. For example, if users
want to perform a symmetric rank-k update <span class="math inline"><em>C</em> = <em>C</em> + <em>α</em><em>A</em><em>A</em><sup><em>T</em></sup></span>,
they would have to write it like this.</p>
<div class="sourceCode" id="cb6"><pre class="sourceCode cpp"><code class="sourceCode cpp"><span id="cb6-1"><a href="#cb6-1" aria-hidden="true" tabindex="-1"></a>symmetric_matrix_rank_k_update<span class="op">(</span>alpha, A, C, C, Triangle<span class="op">{})</span>;</span></code></pre></div>
<p>Users no longer would be able to write code like the following, which
would scale by <span class="math inline"><em>α</em><sup>2</sup></span>
instead of just <span class="math inline"><em>α</em></span> and thus
would not express what the user meant.</p>
<div class="sourceCode" id="cb7"><pre class="sourceCode cpp"><code class="sourceCode cpp"><span id="cb7-1"><a href="#cb7-1" aria-hidden="true" tabindex="-1"></a>symmetric_matrix_rank_k_update<span class="op">(</span>scaled<span class="op">(</span>alpha, A<span class="op">)</span>, C, C, Triangle<span class="op">{})</span>;</span></code></pre></div>
<p>In terms of functionality, the only reason to retain
non-<code>alpha</code> overloads would be to support matrix and vector
element types that lack a multiplicative identity. However, the C or
Fortran BLAS does not support such types now, and personal experience
with generic C++ linear algebra libraries is that users have never asked
for such types. Removing support for such types from the symmetric and
Hermitian rank-1 and rank-k updates would reduce the testing burden.
Furthermore, we could always add the overloads back later, and we
propose constraining the type of <code>alpha</code> in a way that makes
such overloads not ambiguous.</p>
<p>In terms of performance, one argument for retaining
<code>alpha</code> overloads is to speed up the special case of
<code>alpha = 1</code>, by avoiding unnecessary multiplies with
<code>alpha</code>. Performance of arithmetic operations is more
important for “BLAS 3” operations like rank-k updates than for “BLAS 2”
operations like rank-1 updates, so we will only consider rank-k updates
here. The high-performance case of rank-k updates would likely dispatch
to a BLAS or other optimized library that dispatches at run time based
on special cases of <code>alpha</code>. Thus, there’s no need to expose
<code>alpha = 1</code> as a special case in the interface. Furthermore,
the case <code>alpha = -1</code> is also an important special case where
implementations could avoid multiplications, yet
<strong>[linalg]</strong> does not have special interfaces for
<code>alpha = -1</code>. Thus, we see no pressing motivation to provide
special interfaces for the case <code>alpha = 1</code>, either.</p>
<h2 data-number="4.5" id="things-relating-to-scaling-factors-that-we-do-not-propose-changing"><span class="header-section-number">4.5</span> Things relating to scaling
factors that we do not propose changing<a href="#things-relating-to-scaling-factors-that-we-do-not-propose-changing" class="self-link"></a></h2>
<h3 data-number="4.5.1" id="hermitian-matrix-vector-and-matrix-matrix-products"><span class="header-section-number">4.5.1</span> Hermitian matrix-vector and
matrix-matrix products<a href="#hermitian-matrix-vector-and-matrix-matrix-products" class="self-link"></a></h3>
<p>Both <code>hermitian_matrix_vector_product</code> and
<code>hermitian_matrix_product</code> expect that the (possibly scaled)
input matrix is Hermitian, while the corresponding BLAS routines
<code>xHEMV</code> and <code>xHEMM</code> expect that the unscaled input
matrix is Hermitian and permit the scaling factor <code>alpha</code> to
have nonzero imaginary part. However, this does not affect the ability
of these [linalg] algorithms to compute what the BLAS can compute. Users
who want to supply <code>alpha</code> with nonzero imaginary part should
<em>not</em> scale the matrix <code>A</code> (as in
<code>scaled(alpha, A)</code>). Instead, they should scale the input
vector <code>x</code>, as in the following.</p>
<div class="sourceCode" id="cb8"><pre class="sourceCode cpp"><code class="sourceCode cpp"><span id="cb8-1"><a href="#cb8-1" aria-hidden="true" tabindex="-1"></a><span class="kw">auto</span> alpha <span class="op">=</span> std<span class="op">::</span>complex<span class="op">{</span><span class="fl">0.0</span>, <span class="fl">1.0</span><span class="op">}</span>;</span>
<span id="cb8-2"><a href="#cb8-2" aria-hidden="true" tabindex="-1"></a>hermitian_matrix_vector_product<span class="op">(</span>A, upper_triangle, scaled<span class="op">(</span>alpha, x<span class="op">)</span>, y<span class="op">)</span>;</span></code></pre></div>
<p>Therefore, <code>hermitian_matrix_vector_product</code> and
<code>hermitian_matrix_product</code> do <em>not</em> need extra
overloads with a scaling factor <code>alpha</code> parameter.</p>
<h4 data-number="4.5.1.1" id="in-blas-matrix-is-hermitian-but-scaled-matrix-need-not-be"><span class="header-section-number">4.5.1.1</span> In BLAS, matrix is
Hermitian, but scaled matrix need not be<a href="#in-blas-matrix-is-hermitian-but-scaled-matrix-need-not-be" class="self-link"></a></h4>
<p>In Chapter 2 of the BLAS Standard, both <code>xHEMV</code> and
<code>xHEMM</code> take the scaling factors <span class="math inline"><em>α</em></span> and <span class="math inline"><em>β</em></span> as complex numbers
(<code>COMPLEX&lt;wp&gt;</code>, where <code>&lt;wp&gt;</code>
represents the current working precision). The BLAS permits
<code>xHEMV</code> or <code>xHEMM</code> to be called with
<code>alpha</code> whose imaginary part is nonzero. The matrix that the
BLAS assumes to be Hermitian is <span class="math inline"><em>A</em></span>, not <span class="math inline"><em>α</em><em>A</em></span>. Even if <span class="math inline"><em>A</em></span> is Hermitian, <span class="math inline"><em>α</em><em>A</em></span> might not necessarily be
Hermitian. For example, if <span class="math inline"><em>A</em></span>
is the identity matrix (diagonal all ones) and <span class="math inline"><em>α</em></span> is <span class="math inline"><em>i</em></span>, then <span class="math inline"><em>α</em><em>A</em></span> is not Hermitian but
skew-Hermitian.</p>
<p>The current [linalg] wording requires that the input matrix be
Hermitian. This excludes replicating BLAS behavior by using
<code>scaled(alpha, A)</code> (where <code>alpha</code> has nonzero
imaginary part, and <code>A</code> is any Hermitian matrix) as the input
matrix. Note that the behavior of this is still otherwise well defined,
at least after applying the fix proposed in LWG4136 for diagonal
elements with nonzero imaginary part. It does not violate a
precondition. Therefore, the Standard has no way to tell the user that
they did something wrong.</p>
<h4 data-number="4.5.1.2" id="status-quo-permits-scaling-via-the-input-vector"><span class="header-section-number">4.5.1.2</span> Status quo permits scaling
via the input vector<a href="#status-quo-permits-scaling-via-the-input-vector" class="self-link"></a></h4>
<p>The current wording permits introducing the scaling factor
<code>alpha</code> through the input vector, even if <code>alpha</code>
has nonzero imaginary part.</p>
<div class="sourceCode" id="cb9"><pre class="sourceCode cpp"><code class="sourceCode cpp"><span id="cb9-1"><a href="#cb9-1" aria-hidden="true" tabindex="-1"></a><span class="kw">auto</span> alpha <span class="op">=</span> std<span class="op">::</span>complex<span class="op">{</span><span class="fl">0.0</span>, <span class="fl">1.0</span><span class="op">}</span>;</span>
<span id="cb9-2"><a href="#cb9-2" aria-hidden="true" tabindex="-1"></a>hermitian_matrix_vector_product<span class="op">(</span>A, upper_triangle, scaled<span class="op">(</span>alpha, x<span class="op">)</span>, y<span class="op">)</span>;</span></code></pre></div>
<p>This is mathematically correct as long as <span class="math inline"><em>α</em><em>A</em><em>x</em></span> equals <span class="math inline"><em>A</em><em>α</em><em>x</em></span>, that is, as
long as <span class="math inline"><em>α</em></span> commutes with the
elements of A. This issue would only be of concern if those
multiplications might be noncommutative. Multiplication with
floating-point numbers, integers, and anything that behaves reasonably
like a real or complex number is commutative. However, practical number
types exist that have noncommutative multiplication. Quaternions are one
example. Another is the ring of square <span class="math inline"><em>N</em></span> by <span class="math inline"><em>N</em></span> matrices (for some fixed dimension
<span class="math inline"><em>N</em></span>), with matrix multiplication
using the same definition that <code>linalg::matrix_product</code> uses.
One way for a user-defined complex number type to have noncommutative
multiplication would be if its real and imaginary parts each have a
user-defined number type with noncommutative multiplication, as in the
<code>user_complex&lt;noncommutative&gt;</code> example below.</p>
<div class="sourceCode" id="cb10"><pre class="sourceCode cpp"><code class="sourceCode cpp"><span id="cb10-1"><a href="#cb10-1" aria-hidden="true" tabindex="-1"></a><span class="kw">template</span><span class="op">&lt;</span><span class="kw">class</span> T<span class="op">&gt;</span></span>
<span id="cb10-2"><a href="#cb10-2" aria-hidden="true" tabindex="-1"></a><span class="kw">class</span> user_complex <span class="op">{</span></span>
<span id="cb10-3"><a href="#cb10-3" aria-hidden="true" tabindex="-1"></a><span class="kw">public</span><span class="op">:</span></span>
<span id="cb10-4"><a href="#cb10-4" aria-hidden="true" tabindex="-1"></a>  user_complex<span class="op">(</span>T re, T im<span class="op">)</span> <span class="op">:</span> re_<span class="op">(</span>re<span class="op">)</span>, im_<span class="op">(</span>im<span class="op">)</span> <span class="op">{}</span></span>
<span id="cb10-5"><a href="#cb10-5" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb10-6"><a href="#cb10-6" aria-hidden="true" tabindex="-1"></a>  <span class="kw">friend</span> T real<span class="op">(</span>user_complex<span class="op">&lt;</span>T<span class="op">&gt;</span> z<span class="op">)</span> <span class="op">{</span> <span class="cf">return</span> z<span class="op">.</span>re_; <span class="op">}</span></span>
<span id="cb10-7"><a href="#cb10-7" aria-hidden="true" tabindex="-1"></a>  <span class="kw">friend</span> T imag<span class="op">(</span>user_complex<span class="op">&lt;</span>T<span class="op">&gt;</span> z<span class="op">)</span> <span class="op">{</span> <span class="cf">return</span> z<span class="op">.</span>im_; <span class="op">}</span> </span>
<span id="cb10-8"><a href="#cb10-8" aria-hidden="true" tabindex="-1"></a>  <span class="kw">friend</span> user_complex<span class="op">&lt;</span>T<span class="op">&gt;</span> conj<span class="op">(</span>user_complex<span class="op">&lt;</span>T<span class="op">&gt;</span> z<span class="op">)</span> <span class="op">{</span></span>
<span id="cb10-9"><a href="#cb10-9" aria-hidden="true" tabindex="-1"></a>    <span class="cf">return</span> <span class="op">{</span>real<span class="op">(</span>z<span class="op">)</span>, <span class="op">-</span>imag<span class="op">(</span>z<span class="op">)}</span>;</span>
<span id="cb10-10"><a href="#cb10-10" aria-hidden="true" tabindex="-1"></a>  <span class="op">}</span></span>
<span id="cb10-11"><a href="#cb10-11" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb10-12"><a href="#cb10-12" aria-hidden="true" tabindex="-1"></a>  <span class="co">// ... other overloaded arithmetic operators ...</span></span>
<span id="cb10-13"><a href="#cb10-13" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb10-14"><a href="#cb10-14" aria-hidden="true" tabindex="-1"></a>  <span class="co">// the usual complex arithmetic definition</span></span>
<span id="cb10-15"><a href="#cb10-15" aria-hidden="true" tabindex="-1"></a>  <span class="kw">friend</span> user_complex<span class="op">&lt;</span>T<span class="op">&gt;</span></span>
<span id="cb10-16"><a href="#cb10-16" aria-hidden="true" tabindex="-1"></a>  <span class="kw">operator</span><span class="op">*(</span>user_complex<span class="op">&lt;</span>T<span class="op">&gt;</span> z, user_complex<span class="op">&lt;</span>T<span class="op">&gt;</span> w<span class="op">)</span> <span class="op">{</span></span>
<span id="cb10-17"><a href="#cb10-17" aria-hidden="true" tabindex="-1"></a>    <span class="cf">return</span> <span class="op">{</span></span>
<span id="cb10-18"><a href="#cb10-18" aria-hidden="true" tabindex="-1"></a>      real<span class="op">(</span>z<span class="op">)</span> <span class="op">*</span> real<span class="op">(</span>w<span class="op">)</span> <span class="op">-</span> imag<span class="op">(</span>z<span class="op">)</span> <span class="op">*</span> imag<span class="op">(</span>w<span class="op">)</span>,</span>
<span id="cb10-19"><a href="#cb10-19" aria-hidden="true" tabindex="-1"></a>      real<span class="op">(</span>z<span class="op">)</span> <span class="op">*</span> imag<span class="op">(</span>w<span class="op">)</span> <span class="op">+</span> imag<span class="op">(</span>z<span class="op">)</span> <span class="op">*</span> real<span class="op">(</span>w<span class="op">)</span></span>
<span id="cb10-20"><a href="#cb10-20" aria-hidden="true" tabindex="-1"></a>    <span class="op">}</span>;</span>
<span id="cb10-21"><a href="#cb10-21" aria-hidden="true" tabindex="-1"></a>  <span class="op">}</span></span>
<span id="cb10-22"><a href="#cb10-22" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb10-23"><a href="#cb10-23" aria-hidden="true" tabindex="-1"></a><span class="kw">private</span><span class="op">:</span></span>
<span id="cb10-24"><a href="#cb10-24" aria-hidden="true" tabindex="-1"></a>  T re_, im_;</span>
<span id="cb10-25"><a href="#cb10-25" aria-hidden="true" tabindex="-1"></a><span class="op">}</span>;</span>
<span id="cb10-26"><a href="#cb10-26" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb10-27"><a href="#cb10-27" aria-hidden="true" tabindex="-1"></a><span class="kw">class</span> noncommutative <span class="op">{</span></span>
<span id="cb10-28"><a href="#cb10-28" aria-hidden="true" tabindex="-1"></a><span class="kw">public</span><span class="op">:</span></span>
<span id="cb10-29"><a href="#cb10-29" aria-hidden="true" tabindex="-1"></a>  <span class="kw">explicit</span> noncommutative<span class="op">(</span><span class="dt">double</span> value<span class="op">)</span> <span class="op">:</span> value_<span class="op">(</span>value<span class="op">)</span> <span class="op">{}</span></span>
<span id="cb10-30"><a href="#cb10-30" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb10-31"><a href="#cb10-31" aria-hidden="true" tabindex="-1"></a>  <span class="co">// ... overloaded arithmetic operators ...</span></span>
<span id="cb10-32"><a href="#cb10-32" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb10-33"><a href="#cb10-33" aria-hidden="true" tabindex="-1"></a>  <span class="co">// x * y != y * x here, for example with x=3 and y=5</span></span>
<span id="cb10-34"><a href="#cb10-34" aria-hidden="true" tabindex="-1"></a>  <span class="kw">friend</span> <span class="kw">auto</span> <span class="kw">operator</span><span class="op">*(</span>noncommutative x, noncommutative y<span class="op">)</span> <span class="op">{</span></span>
<span id="cb10-35"><a href="#cb10-35" aria-hidden="true" tabindex="-1"></a>    <span class="cf">return</span> x <span class="op">+</span> <span class="fl">2.0</span> <span class="op">*</span> y<span class="op">.</span>value_;</span>
<span id="cb10-36"><a href="#cb10-36" aria-hidden="true" tabindex="-1"></a>  <span class="op">}</span></span>
<span id="cb10-37"><a href="#cb10-37" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb10-38"><a href="#cb10-38" aria-hidden="true" tabindex="-1"></a><span class="kw">private</span><span class="op">:</span></span>
<span id="cb10-39"><a href="#cb10-39" aria-hidden="true" tabindex="-1"></a>  <span class="dt">double</span> value_;</span>
<span id="cb10-40"><a href="#cb10-40" aria-hidden="true" tabindex="-1"></a><span class="op">}</span>;</span>
<span id="cb10-41"><a href="#cb10-41" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb10-42"><a href="#cb10-42" aria-hidden="true" tabindex="-1"></a><span class="kw">auto</span> alpha <span class="op">=</span> user_complex<span class="op">&lt;</span>noncommutative<span class="op">&gt;{</span></span>
<span id="cb10-43"><a href="#cb10-43" aria-hidden="true" tabindex="-1"></a>  noncommutative<span class="op">{</span><span class="fl">3.0</span><span class="op">}</span>, noncommutative<span class="op">{</span><span class="fl">4.0</span><span class="op">}</span></span>
<span id="cb10-44"><a href="#cb10-44" aria-hidden="true" tabindex="-1"></a><span class="op">}</span>;</span>
<span id="cb10-45"><a href="#cb10-45" aria-hidden="true" tabindex="-1"></a>hermitian_matrix_vector_product<span class="op">(</span>N, upper_triangle, scaled<span class="op">(</span>alpha, x<span class="op">)</span>, y<span class="op">)</span>;</span></code></pre></div>
<p>The [linalg] library was designed to support element types with
noncommutative multiplication. On the other hand, generally, if we speak
of Hermitian matrices or even of inner products (which are used to
define Hermitian matrices), we’re working in a vector space. This means
that multiplication of the matrix’s elements is commutative. Thus, we
think it is not so onerous to restrict use of <code>alpha</code> with
nonzero imaginary part in this case.</p>
<h4 data-number="4.5.1.3" id="scaling-via-the-input-vector-is-the-least-bad-choice"><span class="header-section-number">4.5.1.3</span> Scaling via the input
vector is the least bad choice<a href="#scaling-via-the-input-vector-is-the-least-bad-choice" class="self-link"></a></h4>
<p>Many users may not like the status quo of needing to scale
<code>x</code> instead of <code>A</code>. First, it differs from the
BLAS, which puts <code>alpha</code> before <code>A</code> in its
<code>xHEMV</code> and <code>xHEMM</code> function arguments. Second,
users would get no compile-time feedback and likely no run-time feedback
if they scale <code>A</code> instead of <code>x</code>. Their code would
compile and produce correct results for almost all matrix-vector or
matrix-matrix products, <em>except</em> for the Hermitian case, and
<em>only</em> when the scaling factor has a nonzero imaginary part.
However, we still think the status quo is the least bad choice. We
explain why by discussing the following alternatives.</p>
<ol type="1">
<li><p>Treat <code>scaled(alpha, A)</code> as a special case: expect
<code>A</code> to be Hermitian and permit <code>alpha</code> to have
nonzero imaginary part</p></li>
<li><p>Forbid <code>scaled(alpha, A)</code> at compile time, so that
users must scale <code>x</code> instead</p></li>
<li><p>Add overloads that take <code>alpha</code>, analogous to the
rank-1 and rank-k update functions</p></li>
</ol>
<p>The first choice is mathematically incorrect, as we will explain
below. The second is not incorrect, but could only catch some errors.
The third is likewise not incorrect, but would add a lot of overloads
for an uncommon use case, and would still not prevent users from scaling
the matrix in mathematically incorrect ways.</p>
<h5 data-number="4.5.1.3.1" id="treating-a-scaled-matrix-as-a-special-case-would-be-incorrect"><span class="header-section-number">4.5.1.3.1</span> Treating a scaled matrix
as a special case would be incorrect<a href="#treating-a-scaled-matrix-as-a-special-case-would-be-incorrect" class="self-link"></a></h5>
<p>“Treat <code>scaled(alpha, A)</code> as a special case” actually
means three special cases, given some nested accessor type
<code>Acc</code> and a scaling factor <code>alpha</code> of type
<code>Scalar</code>.</p>
<ol type="a">
<li><p><code>scaled(alpha, A)</code>, whose accessor type is
<code>scaled_accessor&lt;Scalar, Acc&gt;</code></p></li>
<li><p><code>conjugated(scaled(alpha, A))</code>, whose accessor type is
<code>conjugated_accessor&lt;scaled_accessor&lt;Scalar, Acc&gt;&gt;</code></p></li>
<li><p><code>scaled(alpha, conjugated(A))</code>, whose accessor type is
<code>scaled_accessor&lt;Scalar, conjugated_accessor&lt;Acc&gt;&gt;</code></p></li>
</ol>
<p>One could replace <code>conjugated</code> with
<code>conjugate_transposed</code> (which we expect to be more common in
practice) without changing the accessor type.</p>
<p>This approach violates the fundamental [linalg] principle that “…
each <code>mdspan</code> parameter of a function behaves as itself and
is not otherwise ‘modified’ by other parameters” (Section 10.2.5,
P1673R13). The behavior of [linalg] is agnostic of specific accessor
types, even though implementations likely have optimizations for
specific accessor types. [linalg] takes this approach for consistency,
even where it results in different behavior from the BLAS (see Section
10.5.2 of P1673R13). The application of this principle here is “the
input parameter <code>A</code> is always Hermitian.” In this case, the
consistency matters for mathematical correctness. What if
<code>scaled(alpha, A)</code> is Hermitian, but <code>A</code> itself is
not? An example is <span class="math inline"><em>α</em> =  − <em>i</em></span> and <span class="math inline"><em>A</em></span> is the 2 x 2 matrix whose elements
are all <span class="math inline"><em>i</em></span>. If we treat
<code>scaled_accessor</code> as a special case, then
<code>hermitian_matrix_vector_product</code> will compute different
results.</p>
<p>Another problem with this approach is that users might define their
own accessor types with the effect of <code>scaled_accessor</code>, or
combine existing nested accessor types in hard-to-detect ways (like a
long nesting of <code>conjugated_accessor</code> with a
<code>scaled_accessor</code> inside). The [linalg] library has no way to
detect all possible ways that the matrix might be scaled.</p>
<h5 data-number="4.5.1.3.2" id="forbidding-scaled_accessor-would-not-solve-the-problem"><span class="header-section-number">4.5.1.3.2</span> Forbidding
<code>scaled_accessor</code> would not solve the problem<a href="#forbidding-scaled_accessor-would-not-solve-the-problem" class="self-link"></a></h5>
<p>Hermitian matrix-matrix and matrix-vector product functions could
instead <em>forbid</em> scaling the matrix at compile time. This means
excluding, at compile time, the three accessor type cases listed in the
previous option.</p>
<ol type="a">
<li><p><code>scaled_accessor&lt;Scalar, Acc&gt;</code></p></li>
<li><p><code>conjugated_accessor&lt;scaled_accessor&lt;Scalar, Acc&gt;&gt;</code></p></li>
<li><p><code>scaled_accessor&lt;Scalar, conjugated_accessor&lt;Acc&gt;&gt;</code></p></li>
</ol>
<p>Doing this would certainly encourage correct behavior for the most
common cases. However, as mentioned above, users are permitted to define
their own accessor types, and to combine existing nested accessors in
arbitrary ways. The [linalg] library cannot detect all possible ways
that an arbitrary, possibly user-defined accessor might scale the
matrix. Furthermore, scaling the matrix might still be mathematically
correct. A scaling factor with nonzero imaginary part might even
<em>make</em> the matrix Hermitian. Applying <span class="math inline"><em>i</em></span> as a scaling factor twice would
give a perfectly valid scaling factor of <span class="math inline"> − 1</span>.</p>
<h5 data-number="4.5.1.3.3" id="adding-alpha-overloads-would-make-the-problem-worse"><span class="header-section-number">4.5.1.3.3</span> Adding <code>alpha</code>
overloads would make the problem worse<a href="#adding-alpha-overloads-would-make-the-problem-worse" class="self-link"></a></h5>
<p>One could imagine adding overloads that take <code>alpha</code>,
analogous to the rank-1 and rank-k update overloads that take
<code>alpha</code>. Users could then write</p>
<div class="sourceCode" id="cb11"><pre class="sourceCode cpp"><code class="sourceCode cpp"><span id="cb11-1"><a href="#cb11-1" aria-hidden="true" tabindex="-1"></a>hermitian_matrix_vector_product<span class="op">(</span>alpha, A, upper_triangle, x, y<span class="op">)</span>;</span></code></pre></div>
<p>instead of</p>
<div class="sourceCode" id="cb12"><pre class="sourceCode cpp"><code class="sourceCode cpp"><span id="cb12-1"><a href="#cb12-1" aria-hidden="true" tabindex="-1"></a>hermitian_matrix_vector_product<span class="op">(</span>A, upper_triangle, scaled<span class="op">(</span>alpha, x<span class="op">)</span>, y<span class="op">)</span>;</span></code></pre></div>
<p>We do not support this approach. First, users can already introduce a
scaling factor through the input vector parameter, so adding
<code>alpha</code> overloads would not add much to what the existing
overloads can accomplish. Contrast this with the rank-1 and rank-k
Hermitian update functions, where not having <code>alpha</code>
overloads would prevent simple cases, like <span class="math inline"><em>C</em> := <em>C</em> − 2<em>x</em><em>x</em><sup><em>H</em></sup></span>
with a user-defined complex number type whose real and imaginary parts
are both integers. Second, <code>alpha</code> overloads would not
prevent users from <em>also</em> supplying <code>scaled(gamma, A)</code>
as the matrix for some other scaling factor <code>gamma</code>. This
would make the problem worse, because users would need to reason about
the combination of two ways that the matrix could be scaled.</p>
<h3 data-number="4.5.2" id="triangular-matrix-products-with-implicit-unit-diagonals"><span class="header-section-number">4.5.2</span> Triangular matrix products
with implicit unit diagonals<a href="#triangular-matrix-products-with-implicit-unit-diagonals" class="self-link"></a></h3>
<ol type="1">
<li><p>In BLAS, triangular matrix-vector and matrix-matrix products
apply <code>alpha</code> scaling to the implicit unit diagonal. In
[linalg], the scaling factor <code>alpha</code> is not applied to the
implicit unit diagonal. This is because the library does not interpret
<code>scaled(alpha, A)</code> differently than any other
<code>mdspan</code>.</p></li>
<li><p>Users of triangular matrix-vector products can recover BLAS
functionality by scaling the input vector instead of the input matrix,
so this only matters for triangular matrix-matrix products.</p></li>
<li><p>All calls of the BLAS’s triangular matrix-matrix product routine
<code>xTRMM</code> in LAPACK (other than in testing routines) use
<code>alpha</code> equal to one.</p></li>
<li><p>Straightforward approaches for fixing this issue would not break
backwards compatibility.</p></li>
<li><p>Therefore, we do not consider fixing this a high-priority issue,
and we do not propose a fix for it in this paper.</p></li>
</ol>
<h4 data-number="4.5.2.1" id="blas-applies-alpha-after-unit-diagonal-linalg-applies-it-before"><span class="header-section-number">4.5.2.1</span> BLAS applies alpha after
unit diagonal; linalg applies it before<a href="#blas-applies-alpha-after-unit-diagonal-linalg-applies-it-before" class="self-link"></a></h4>
<p>The <code>triangular_matrix_vector_product</code> and
<code>triangular_matrix_product</code> algorithms have an
<code>implicit_unit_diagonal</code> option. This makes the algorithm not
access the diagonal of the matrix, and compute as if the diagonal were
all ones. The option corresponds to the BLAS’s “Unit” flag. BLAS
routines that take both a “Unit” flag and an <code>alpha</code> scaling
factor apply “Unit” <em>before</em> scaling by <code>alpha</code>, so
that the matrix is treated as if it has a diagonal of all
<code>alpha</code> values. In contrast, [linalg] follows the general
principle that <code>scaled(alpha, A)</code> should be treated like any
other kind of <code>mdspan</code>. As a result, algorithms interpret
<code>implicit_unit_diagonal</code> as applied to the matrix
<em>after</em> scaling by <code>alpha</code>, so that the matrix still
has a diagonal of all ones.</p>
<h4 data-number="4.5.2.2" id="triangular-solve-algorithms-not-affected"><span class="header-section-number">4.5.2.2</span> Triangular solve algorithms
not affected<a href="#triangular-solve-algorithms-not-affected" class="self-link"></a></h4>
<p>The triangular solve algorithms in std::linalg are not affected,
because their BLAS analogs either do not take an <code>alpha</code>
argument (as with <code>xTRSV</code>), or the <code>alpha</code>
argument does not affect the triangular matrix (with <code>xTRSM</code>,
<code>alpha</code> affects the right-hand sides <code>B</code>, not the
triangular matrix <code>A</code>).</p>
<h4 data-number="4.5.2.3" id="triangular-matrix-vector-product-work-around"><span class="header-section-number">4.5.2.3</span> Triangular matrix-vector
product work-around<a href="#triangular-matrix-vector-product-work-around" class="self-link"></a></h4>
<p>This issue only reduces functionality of
<code>triangular_matrix_product</code>. Users of
<code>triangular_matrix_vector_product</code> who wish to replicate the
original BLAS functionality can scale the input matrix (by supplying
<code>scaled(alpha, x)</code> instead of <code>x</code> as the input
argument) instead of the triangular matrix.</p>
<h4 data-number="4.5.2.4" id="triangular-matrix-matrix-product-example"><span class="header-section-number">4.5.2.4</span> Triangular matrix-matrix
product example<a href="#triangular-matrix-matrix-product-example" class="self-link"></a></h4>
<p>The following example computes <span class="math inline"><em>A</em> := 2<em>A</em><em>B</em></span> where
<span class="math inline"><em>A</em></span> is a lower triangular
matrix, but it makes the diagonal of <span class="math inline"><em>A</em></span> all ones on the input (right-hand)
side.</p>
<div class="sourceCode" id="cb13"><pre class="sourceCode cpp"><code class="sourceCode cpp"><span id="cb13-1"><a href="#cb13-1" aria-hidden="true" tabindex="-1"></a>triangular_matrix_product<span class="op">(</span>scaled<span class="op">(</span><span class="fl">2.0</span>, A<span class="op">)</span>, lower_triangle, implicit_unit_diagonal, B, A<span class="op">)</span>;</span></code></pre></div>
<p>Contrast with the analogous BLAS routine <code>DTRMM</code>, which
has the effect of making the diagonal elements all <code>2.0</code>.</p>
<div class="sourceCode" id="cb1"><pre class="sourceCode fortran"><code class="sourceCode fortranfixed"><span id="cb1-1"><a href="#cb1-1" aria-hidden="true" tabindex="-1"></a>dtrmm(<span class="st">&#39;Left side&#39;</span>, <span class="st">&#39;Lower triangular&#39;</span>, <span class="st">&#39;No transpose&#39;</span>, <span class="st">&#39;Unit diagonal&#39;</span>, <span class="co">m, n, 2.0, A, lda, B, ldb)</span></span></code></pre></div>
<p>If we want to use [linalg] to express what the BLAS call expresses,
we need to perform a separate scaling.</p>
<div class="sourceCode" id="cb15"><pre class="sourceCode cpp"><code class="sourceCode cpp"><span id="cb15-1"><a href="#cb15-1" aria-hidden="true" tabindex="-1"></a>triangular_matrix_product<span class="op">(</span>A, lower_triangle, implicit_unit_diagonal, B, A<span class="op">)</span>;</span>
<span id="cb15-2"><a href="#cb15-2" aria-hidden="true" tabindex="-1"></a>scale<span class="op">(</span><span class="fl">2.0</span>, A<span class="op">)</span>;</span></code></pre></div>
<p>This is counterintuitive, and may also affect performance.
Performance of <code>scale</code> is typically bound by memory bandwidth
and/or latency, but if the work done by <code>scale</code> could be
fused with the work done by the <code>triangular_matrix_product</code>,
then <code>scale</code>’s memory operations could be “hidden” in the
cost of the matrix product.</p>
<h4 data-number="4.5.2.5" id="lapack-never-calls-xtrmm-with-the-implicit-unit-diagonal-option-and-alpha-not-equal-to-one"><span class="header-section-number">4.5.2.5</span> LAPACK never calls
<code>xTRMM</code> with the implicit unit diagonal option and
<code>alpha</code> not equal to one<a href="#lapack-never-calls-xtrmm-with-the-implicit-unit-diagonal-option-and-alpha-not-equal-to-one" class="self-link"></a></h4>
<p>How much might users care about this missing [linalg] feature?
P1673R13 explains that the BLAS was codesigned with LAPACK and that
every reference BLAS routine is used by some LAPACK routine. “The BLAS
does not aim to provide a complete set of mathematical operations. Every
function in the BLAS exists because some LINPACK or LAPACK algorithm
needs it” (Section 10.6.1). Therefore, to judge the urgency of adding
new functionality to [linalg], we can ask whether the functionality
would be needed by a C++ re-implementation of LAPACK. We think not much,
because the highest-priority target audience of the BLAS is LAPACK
developers, and LAPACK routines (other than testing routines) never use
a scaling factor alpha other than one.</p>
<p>We survey calls to <code>xTRMM</code> in the latest version of LAPACK
as of the publication date of R1 of this proposal, LAPACK 3.12.0. It
suffices to survey <code>DTRMM</code>, the double-precision real case,
since for all the routines of interest, the complex case follows the
same pattern. (We did survey <code>ZTRMM</code>, the double-precision
complex case, just in case.) LAPACK has 24 routines that call
<code>DTRMM</code> directly. They fall into five categories.</p>
<ol type="1">
<li><p>Test routines: <code>DCHK3</code>, <code>DCHKE</code>,
<code>DLARHS</code></p></li>
<li><p>Routines relating to QR factorization or using the result of a QR
factorization (especially with block Householder reflectors):
<code>DGELQT3</code>, <code>DLARFB</code>, <code>DGEQRT3</code>,
<code>DLARFB_GETT</code>, <code>DLARZB</code>,
<code>DORM22</code></p></li>
<li><p>Routines relating to computing an inverse of a triangular matrix
or of a matrix that has been factored into triangular matrices:
<code>DLAUUM</code>, <code>DTRITRI</code>, <code>DTFTRI</code>,
<code>DPFTRI</code></p></li>
<li><p>Routines relating to solving eigenvalue (or generalized
eigenvalue) problems: <code>DLAHR2</code>, <code>DSYGST</code>,
<code>DGEHRD</code>, <code>DSYGV</code>, <code>DSYGV_2STAGE</code>,
<code>DSYGVD</code>, <code>DSYGVX</code> (note that <code>DLAQR5</code>
depends on <code>DTRMM</code> via <code>EXTERNAL</code> declaration, but
doesn’t actually call it)</p></li>
<li><p>Routines relating to symmetric indefinite factorizations:
<code>DSYT01_AA</code>, <code>DSYTRI2X</code>,
<code>DSYTRI_3X</code></p></li>
</ol>
<p>The only routines that call <code>DTRMM</code> with
<code>alpha</code> equal to anything other than one or negative one are
the testing routines. Some calls in <code>DGELQT3</code> and
<code>DLARFB_GETT</code> use negative one, but these calls never specify
an implicit unit diagonal (they use the explicit diagonal option). The
only routine that might possibly call <code>DTRMM</code> with both
negative one as alpha and the implicit unit diagonal is
<code>DTFTRI</code>. (This routine “computes the inverse of a triangular
matrix A stored in RFP [Rectangular Full Packed] format.” RFP format was
introduced to LAPACK in the late 2000’s, well after the BLAS Standard
was published. See
<a href="http://www.netlib.org/lapack/lawnspdf/lawn199.pdf">LAPACK
Working Note 199</a>, which was published in 2008.) <code>DTFTRI</code>
passes its caller’s <code>diag</code> argument (which specifies either
implicit unit diagonal or explicit diagonal) to <code>DTRMM</code>. The
only two LAPACK routines that call <code>DTFTRI</code> are
<code>DERRRFP</code> (a testing routine) and <code>DPFTRI</code>.
<code>DPFTRI</code> only ever calls <code>DTFTRI</code> with
<code>diag</code> <em>not</em> specifying the implicit unit diagonal
option. Therefore, LAPACK never needs both <code>alpha</code> not equal
to one and the implicit unit diagonal option, so adding the ability to
“scale the implicit diagonal” in [linalg] is a low-priority feature.</p>
<h4 data-number="4.5.2.6" id="fixes-would-not-break-backwards-compatibility"><span class="header-section-number">4.5.2.6</span> Fixes would not break
backwards compatibility<a href="#fixes-would-not-break-backwards-compatibility" class="self-link"></a></h4>
<p>We can think of two ways to fix this issue. First, we could add an
<code>alpha</code> scaling parameter, analogous to the symmetric and
Hermitian rank-1 and rank-k update functions. Second, we could add a new
kind of <code>Diagonal</code> template parameter type that expresses a
“diagonal value.” For example, <code>implicit_diagonal_t{alpha}</code>
(or a function form, <code>implicit_diagonal(alpha)</code>) would tell
the algorithm not to access the diagonal elements, but instead to assume
that their value is <code>alpha</code>. Both of these solutions would
let users specify the diagonal’s scaling factor separately from the
scaling factor for the rest of the matrix. Those two scaling factors
could differ, which is new functionality not offered by the BLAS. More
importantly, both of these solutions could be added later, after C++26,
without breaking backwards compatibility.</p>
<h3 data-number="4.5.3" id="triangular-solves-with-implicit-unit-diagonals"><span class="header-section-number">4.5.3</span> Triangular solves with
implicit unit diagonals<a href="#triangular-solves-with-implicit-unit-diagonals" class="self-link"></a></h3>
<ol type="1">
<li><p>In BLAS, triangular solves with possibly multiple right-hand
sides (<code>xTRSM</code>) apply <code>alpha</code> scaling to the
implicit unit diagonal. In [linalg], the scaling factor
<code>alpha</code> is not applied to the implicit unit diagonal. This is
because the library does not interpret <code>scaled(alpha, A)</code>
differently than any other <code>mdspan</code>.</p></li>
<li><p>Users of triangular solves would need a separate
<code>scale</code> call to recover BLAS functionality.</p></li>
<li><p>LAPACK sometimes calls <code>xTRSM</code> with <code>alpha</code>
not equal to one.</p></li>
<li><p>Straightforward approaches for fixing this issue would not break
backwards compatibility.</p></li>
<li><p>Therefore, we do not consider fixing this a high-priority issue,
and we do not propose a fix for it in this paper.</p></li>
</ol>
<h4 data-number="4.5.3.1" id="blas-applies-alpha-after-unit-diagonal-linalg-applies-it-before-1"><span class="header-section-number">4.5.3.1</span> BLAS applies alpha after
unit diagonal; linalg applies it before<a href="#blas-applies-alpha-after-unit-diagonal-linalg-applies-it-before-1" class="self-link"></a></h4>
<p>Triangular solves have a similar issue to the one explained in the
previous section. The BLAS routine <code>xTRSM</code> applies
<code>alpha</code> “after” the implicit unit diagonal, while std::linalg
applies <code>alpha</code> “before.” (<code>xTRSV</code> does not take
an <code>alpha</code> scaling factor.) As a result, the BLAS solves with
a different matrix than std::linalg.</p>
<p>In mathematical terms, <code>xTRSM</code> solves the equation <span class="math inline"><em>α</em>(<em>A</em>+<em>I</em>)<em>X</em> = <em>B</em></span>
for <span class="math inline"><em>X</em></span>, where <span class="math inline"><em>A</em></span> is the user’s input matrix
(without implicit unit diagonal) and <span class="math inline"><em>I</em></span> is the identity matrix (with ones
on the diagonal and zeros everywhere else).
<code>triangular_matrix_matrix_left_solve</code> solves the equation
<span class="math inline">(<em>α</em><em>A</em>+<em>I</em>)<em>Y</em> = <em>B</em></span>
for <span class="math inline"><em>Y</em></span>. The two results <span class="math inline"><em>X</em></span> and <span class="math inline"><em>Y</em></span> are not equal in general.</p>
<h4 data-number="4.5.3.2" id="work-around-requires-changing-all-elements-of-the-matrix"><span class="header-section-number">4.5.3.2</span> Work-around requires
changing all elements of the matrix<a href="#work-around-requires-changing-all-elements-of-the-matrix" class="self-link"></a></h4>
<p>Users could work around this problem by first scaling the matrix
<span class="math inline"><em>A</em></span> by <span class="math inline"><em>α</em></span>, and then solving for <span class="math inline"><em>Y</em></span>. In the common case where the
“other triangle” of <span class="math inline"><em>A</em></span> holds
another triangular matrix, users could not call
<code>scale(alpha, A)</code>. They would instead need to iterate over
the elements of <span class="math inline"><em>A</em></span> manually.
Users might also need to “unscale” the matrix after the solve. Another
option would be to copy the matrix <span class="math inline"><em>A</em></span> before scaling.</p>
<div class="sourceCode" id="cb16"><pre class="sourceCode cpp"><code class="sourceCode cpp"><span id="cb16-1"><a href="#cb16-1" aria-hidden="true" tabindex="-1"></a><span class="cf">for</span> <span class="op">(</span><span class="dt">size_t</span> i <span class="op">=</span> <span class="dv">0</span>; i <span class="op">&lt;</span> A<span class="op">.</span>extent<span class="op">(</span><span class="dv">0</span><span class="op">)</span>; <span class="op">++</span>i<span class="op">)</span> <span class="op">{</span></span>
<span id="cb16-2"><a href="#cb16-2" aria-hidden="true" tabindex="-1"></a>  <span class="cf">for</span> <span class="op">(</span><span class="dt">size_t</span> j <span class="op">=</span> i <span class="op">+</span> <span class="dv">1</span>; j <span class="op">&lt;</span> A<span class="op">.</span>extent<span class="op">(</span><span class="dv">1</span><span class="op">)</span>; <span class="op">++</span>j<span class="op">)</span> <span class="op">{</span></span>
<span id="cb16-3"><a href="#cb16-3" aria-hidden="true" tabindex="-1"></a>    A<span class="op">[</span>i, j<span class="op">]</span> <span class="op">*=</span> alpha;</span>
<span id="cb16-4"><a href="#cb16-4" aria-hidden="true" tabindex="-1"></a>  <span class="op">}</span></span>
<span id="cb16-5"><a href="#cb16-5" aria-hidden="true" tabindex="-1"></a><span class="op">}</span></span>
<span id="cb16-6"><a href="#cb16-6" aria-hidden="true" tabindex="-1"></a>triangular_matrix_matrix_left_solve<span class="op">(</span>A, lower_triangle, implicit_unit_diagonal, B, Y<span class="op">)</span>;</span>
<span id="cb16-7"><a href="#cb16-7" aria-hidden="true" tabindex="-1"></a><span class="cf">for</span> <span class="op">(</span><span class="dt">size_t</span> i <span class="op">=</span> <span class="dv">0</span>; i <span class="op">&lt;</span> A<span class="op">.</span>extent<span class="op">(</span><span class="dv">0</span><span class="op">)</span>; <span class="op">++</span>i<span class="op">)</span> <span class="op">{</span></span>
<span id="cb16-8"><a href="#cb16-8" aria-hidden="true" tabindex="-1"></a>  <span class="cf">for</span> <span class="op">(</span><span class="dt">size_t</span> j <span class="op">=</span> i <span class="op">+</span> <span class="dv">1</span>; j <span class="op">&lt;</span> A<span class="op">.</span>extent<span class="op">(</span><span class="dv">1</span><span class="op">)</span>; <span class="op">++</span>j<span class="op">)</span> <span class="op">{</span></span>
<span id="cb16-9"><a href="#cb16-9" aria-hidden="true" tabindex="-1"></a>    A<span class="op">[</span>i, j<span class="op">]</span> <span class="op">/=</span> alpha;</span>
<span id="cb16-10"><a href="#cb16-10" aria-hidden="true" tabindex="-1"></a>  <span class="op">}</span></span>
<span id="cb16-11"><a href="#cb16-11" aria-hidden="true" tabindex="-1"></a><span class="op">}</span></span></code></pre></div>
<p>Users cannot solve this problem by scaling <span class="math inline"><em>B</em></span> (either with
<code>scaled(1.0 / alpha, B)</code> or with
<code>scale(1.0 / alpha, B)</code>). Transforming <span class="math inline"><em>X</em></span> into <span class="math inline"><em>Y</em></span> or vice versa is mathematically
nontrivial in general, and may introduce new failure conditions. This
issue occurs with both the in-place and out-of-place triangular
solves.</p>
<h4 data-number="4.5.3.3" id="unsupported-case-occurs-in-lapack"><span class="header-section-number">4.5.3.3</span> Unsupported case occurs in
LAPACK<a href="#unsupported-case-occurs-in-lapack" class="self-link"></a></h4>
<p>The common case in LAPACK is calling <code>xTRSM</code> with
<code>alpha</code> equal to one, but other values of <code>alpha</code>
occur. For example, <code>xTRTRI</code> calls <code>xTRSM</code> with
<code>alpha</code> equal to <span class="math inline"> − 1</span>. Thus,
we cannot dismiss this issue, as we could with <code>xTRMM</code>.</p>
<h4 data-number="4.5.3.4" id="fixes-would-not-break-backwards-compatibility-1"><span class="header-section-number">4.5.3.4</span> Fixes would not break
backwards compatibility<a href="#fixes-would-not-break-backwards-compatibility-1" class="self-link"></a></h4>
<p>As with triangular matrix products above, we can think of two ways to
fix this issue. First, we could add an <code>alpha</code> scaling
parameter, analogous to the symmetric and Hermitian rank-1 and rank-k
update functions. Second, we could add a new kind of
<code>Diagonal</code> template parameter type that expresses a “diagonal
value.” For example, <code>implicit_diagonal_t{alpha}</code> (or a
function form, <code>implicit_diagonal(alpha)</code>) would tell the
algorithm not to access the diagonal elements, but instead to assume
that their value is <code>alpha</code>. Both of these solutions would
let users specify the diagonal’s scaling factor separately from the
scaling factor for the rest of the matrix. Those two scaling factors
could differ, which is new functionality not offered by the BLAS. More
importantly, both of these solutions could be added later, after C++26,
without breaking backwards compatibility.</p>
<h1 data-number="5" id="ordering-with-respect-to-other-proposals-and-lwg-issues"><span class="header-section-number">5</span> Ordering with respect to other
proposals and LWG issues<a href="#ordering-with-respect-to-other-proposals-and-lwg-issues" class="self-link"></a></h1>
<p>Note that two other <code>std::linalg</code> fix papers were voted
into the Working Draft for C++26 at the Wrocław, Poland meeting in
November 2024.</p>
<ul>
<li><p><a href="https://wg21.link/p3222">P3222R1</a>: “Fix C++26 by
adding <code>transposed</code> special cases for P2642 layouts”</p></li>
<li><p><a href="https://wg21.link/p3050">P3050R3</a>: “Fix C++26 by
optimizing <code>linalg::conjugated</code> for noncomplex value
types”</p></li>
</ul>
<p>LEWG was aware of these two papers and this proposal P3371 in its
2024-09-03 review of P3050R2. All three of these papers increment the
value of the <code>__cpp_lib_linalg</code> macro. While this technically
causes a conflict between the papers, advice from LEWG on 2024-09-03 was
not to introduce special wording changes to avoid this conflict.</p>
<p>Two outstanding LWG issues for std::linalg remain.</p>
<ul>
<li><p><a href="https://cplusplus.github.io/LWG/lwg-active.html#4136">LWG4136</a>
specifies the behavior of Hermitian algorithms on diagonal matrix
elements with nonzero imaginary part. LWG4136’s proposed fix adds a
sentence to [linalg.general]. P3371 does not change [linalg.general].
However, P3371 does add updating overloads of Hermitian algorithms that
take another Hermitian matrix parameter <code>E</code>. The proposed fix
in LWG4136 does not cover parameters named <code>E</code>, so we apply
the analog of that fix to the wording here in P3371.</p></li>
<li><p><a href="https://cplusplus.github.io/LWG/lwg-active.html#4137">LWG4137</a>,
“Fix Mandates, Preconditions, and Complexity elements of [linalg]
algorithms,” affects [linalg.algs.blas3.rankk] and
[linalg.algs.blas3.rank2k], which this proposal also changes. We rebase
this proposal’s changes atop the wording changes in LWG4137’s proposed
fix. It is our view that the two sets of changes do not conflict in a
mathematical or design specification sense. Note that this proposal only
applies the proposed fixes in LWG4137 to the relevant clauses
[linalg.algs.blas3.rankk] and [linalg.algs.blas3.rank2k]. Thus, P3371
does not (completely) resolve LWG4137.</p></li>
</ul>
<h1 data-number="6" id="implementation-status"><span class="header-section-number">6</span> Implementation status<a href="#implementation-status" class="self-link"></a></h1>
<p><a href="https://github.com/kokkos/stdBLAS/pull/293">Pull Request
293</a> in the reference std::linalg implementation implements the
proposed changes. It also adds tests to ensure test coverage of the new
overloads.</p>
<h1 data-number="7" id="acknowledgments"><span class="header-section-number">7</span> Acknowledgments<a href="#acknowledgments" class="self-link"></a></h1>
<p>Many thanks (with permission) to Raffaele Solcà (CSCS Swiss National
Supercomputing Centre, <code>raffaele.solca@cscs.ch</code>) for pointing
out some of the issues fixed by this paper, as well as the issues
leading to LWG4137.</p>
<h1 data-number="8" id="wording"><span class="header-section-number">8</span> Wording<a href="#wording" class="self-link"></a></h1>
<blockquote>
<p>Text in blockquotes is not proposed wording, but rather instructions
for generating proposed wording. The � character is used to denote a
placeholder section number which the editor shall determine. Additions
are shown in green, and removals in red.</p>
<p>In <strong>[version.syn]</strong>, for the following definition,</p>
</blockquote>
<div class="sourceCode" id="cb17"><pre class="sourceCode cpp"><code class="sourceCode cpp"><span id="cb17-1"><a href="#cb17-1" aria-hidden="true" tabindex="-1"></a><span class="pp">#define __cpp_lib_linalg </span>YYYYMML<span class="pp"> </span><span class="co">// also in &lt;linalg&gt;</span></span></code></pre></div>
<blockquote>
<p>adjust the placeholder value <code>YYYYMML</code> as needed so as to
denote this proposal’s date of adoption.</p>
</blockquote>
<h2 data-number="8.1" id="new-exposition-only-concepts-for-possibly-packed-input-and-output-matrices"><span class="header-section-number">8.1</span> New exposition-only concepts
for possibly-packed input and output matrices<a href="#new-exposition-only-concepts-for-possibly-packed-input-and-output-matrices" class="self-link"></a></h2>
<blockquote>
<p>To the header <code>&lt;linalg&gt;</code> synopsis
<strong>[linalg.syn]</strong>, just after the declaration of the
exposition-only concept <em><code>inout-matrix</code></em>, replace the
exposition-only concept
<em><code>possibly-packed-inout-matrix</code></em> with the new
exposition-only concept
<em><code>possibly-packed-out-matrix</code></em>.</p>
</blockquote>
<div class="sourceCode" id="cb18"><pre class="sourceCode default"><code class="sourceCode default"><span id="cb18-1"><a href="#cb18-1" aria-hidden="true" tabindex="-1"></a>  template&lt;class T&gt;</span>
<span id="cb18-2"><a href="#cb18-2" aria-hidden="true" tabindex="-1"></a>    concept <em>inout-matrix</em> = <em>see below</em>;                // <em>exposition only</em></span>
<span id="cb18-3"><a href="#cb18-3" aria-hidden="true" tabindex="-1"></a>  template&lt;class T&gt;</span>
<span id="cb18-4"><a href="#cb18-4" aria-hidden="true" tabindex="-1"></a>    concept <em>possibly-packed</em>-<span class="rm" style="color: #bf0303"><del><em><span><code>in</code></span></em></del></span><em>out-matrix</em> = <em>see below</em>;  // <em>exposition only</em></span></code></pre></div>
<blockquote>
<p>Then, in <strong>[linalg.helpers.concepts]</strong>, just after the
definition of the exposition-only variable template
<em><code>is-layout-blas-packed</code></em>, replace the exposition-only
concept <em><code>possibly-packed-inout-matrix</code></em> with the new
exposition-only concept
<em><code>possibly-packed-out-matrix</code></em>. The two concepts have
the same definitions and thus differ only in name.</p>
</blockquote>
<div class="sourceCode" id="cb19"><pre class="sourceCode default"><code class="sourceCode default"><span id="cb19-1"><a href="#cb19-1" aria-hidden="true" tabindex="-1"></a>  template&lt;class T&gt;</span>
<span id="cb19-2"><a href="#cb19-2" aria-hidden="true" tabindex="-1"></a>    constexpr bool <em>is-layout-blas-packed</em> = false; // <em>exposition only</em></span>
<span id="cb19-3"><a href="#cb19-3" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb19-4"><a href="#cb19-4" aria-hidden="true" tabindex="-1"></a>  template&lt;class Triangle, class StorageOrder&gt;</span>
<span id="cb19-5"><a href="#cb19-5" aria-hidden="true" tabindex="-1"></a>    constexpr bool <em>is-layout-blas-packed</em>&lt;layout_blas_packed&lt;Triangle, StorageOrder&gt;&gt; = true;</span>
<span id="cb19-6"><a href="#cb19-6" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb19-7"><a href="#cb19-7" aria-hidden="true" tabindex="-1"></a>  template&lt;class T&gt;</span>
<span id="cb19-8"><a href="#cb19-8" aria-hidden="true" tabindex="-1"></a>    concept <em>possibly-packed</em>-<span class="rm" style="color: #bf0303"><del><em><span><code>in</code></span></em></del></span><em>out-matrix</em> =</span>
<span id="cb19-9"><a href="#cb19-9" aria-hidden="true" tabindex="-1"></a>      <em>is-mdspan</em>&lt;T&gt; &amp;&amp; T::rank() == 2 &amp;&amp;</span>
<span id="cb19-10"><a href="#cb19-10" aria-hidden="true" tabindex="-1"></a>      is_assignable_v&lt;typename T::reference, typename T::element_type&gt; &amp;&amp;</span>
<span id="cb19-11"><a href="#cb19-11" aria-hidden="true" tabindex="-1"></a>      (T::is_always_unique() || <em>is-layout-blas-packed</em>&lt;typename T::layout_type&gt;);</span></code></pre></div>
<blockquote>
<p>Then, in [linalg.helpers.concepts], change paragraph 3 to rename
<em><code>possibly-packed-inout-matrix</code></em> to
<em><code>possibly-packed-out-matrix</code></em>.</p>
</blockquote>
<p>Unless explicitly permitted, any <em><code>inout-vector</code></em>,
<em><code>inout-matrix</code></em>, <em><code>inout-object</code></em>,
<em><code>out-vector</code></em>, <em><code>out-matrix</code></em>,
<em><code>out-object</code></em>, or
<em><code>possibly-packed-</code></em><span class="rm" style="color: #bf0303"><del><em><span><code>in</code></span></em></del></span><em><code>out-matrix</code></em>
parameter of a function in [linalg] shall not overlap any other
<code>mdspan</code> parameter of the function.</p>
<h2 data-number="8.2" id="rank-1-update-functions-in-synopsis"><span class="header-section-number">8.2</span> Rank-1 update functions in
synopsis<a href="#rank-1-update-functions-in-synopsis" class="self-link"></a></h2>
<blockquote>
<p>In the header <code>&lt;linalg&gt;</code> synopsis
<strong>[linalg.syn]</strong>, change the declarations of the
<code>matrix_rank_1_update</code>, <code>matrix_rank_1_update_c</code>,
<code>symmetric_matrix_rank_1_update</code>, and
<code>hermitian_matrix_rank_1_update</code> overloads as follows.
<i>[Editorial Note:</i> There are three sets of changes here.</p>
<ol type="1">
<li>The existing overloads become “overwriting” overloads.</li>
<li>New “updating” overloads are added.</li>
<li>Any overloads of the symmetric and Hermitian rank-1 update functions
that do <em>not</em> have a <code>Scalar alpha</code> parameter are
removed. (“General” rank-1 update functions
<code>matrix_rank_1_update</code> and
<code>matrix_rank_1_update_c</code> have never had overloads that take
<code>Scalar alpha</code>.) <i>– end note]</i></li>
</ol>
</blockquote>
<div class="sourceCode" id="cb20"><pre class="sourceCode default"><code class="sourceCode default"><span id="cb20-1"><a href="#cb20-1" aria-hidden="true" tabindex="-1"></a>  // [linalg.algs.blas2.rank1], nonsymmetric rank-1 matrix update</span>
<span id="cb20-2"><a href="#cb20-2" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb20-3"><a href="#cb20-3" aria-hidden="true" tabindex="-1"></a>  <span class="add" style="color: #00AA00"><ins><span><code>// overwriting nonsymmetric rank-1 matrix update</code></span></ins></span></span>
<span id="cb20-4"><a href="#cb20-4" aria-hidden="true" tabindex="-1"></a>  template&lt;<em>in-vector</em> InVec1, <em>in-vector</em> InVec2, <span class="rm" style="color: #bf0303"><del><em><span><code>in</code></span></em></del></span><em>out-matrix</em> <span class="rm" style="color: #bf0303"><del><span><code>In</code></span></del></span>OutMat&gt;</span>
<span id="cb20-5"><a href="#cb20-5" aria-hidden="true" tabindex="-1"></a>    void matrix_rank_1_update(InVec1 x, InVec2 y, <span class="rm" style="color: #bf0303"><del><span><code>In</code></span></del></span>OutMat A);</span>
<span id="cb20-6"><a href="#cb20-6" aria-hidden="true" tabindex="-1"></a>  template&lt;class ExecutionPolicy, <em>in-vector</em> InVec1, <em>in-vector</em> InVec2, <span class="rm" style="color: #bf0303"><del><em><span><code>in</code></span></em></del></span><em>out-matrix</em> <span class="rm" style="color: #bf0303"><del><span><code>In</code></span></del></span>OutMat&gt;</span>
<span id="cb20-7"><a href="#cb20-7" aria-hidden="true" tabindex="-1"></a>    void matrix_rank_1_update(ExecutionPolicy&amp;&amp; exec,</span>
<span id="cb20-8"><a href="#cb20-8" aria-hidden="true" tabindex="-1"></a>                              InVec1 x, InVec2 y, <span class="rm" style="color: #bf0303"><del><span><code>In</code></span></del></span>OutMat A);</span>
<span id="cb20-9"><a href="#cb20-9" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb20-10"><a href="#cb20-10" aria-hidden="true" tabindex="-1"></a>  template&lt;<em>in-vector</em> InVec1, <em>in-vector</em> InVec2, <span class="rm" style="color: #bf0303"><del><em><span><code>in</code></span></em></del></span><em>out-matrix</em> <span class="rm" style="color: #bf0303"><del><span><code>In</code></span></del></span>OutMat&gt;</span>
<span id="cb20-11"><a href="#cb20-11" aria-hidden="true" tabindex="-1"></a>    void matrix_rank_1_update_c(InVec1 x, InVec2 y, <span class="rm" style="color: #bf0303"><del><span><code>In</code></span></del></span>OutMat A);</span>
<span id="cb20-12"><a href="#cb20-12" aria-hidden="true" tabindex="-1"></a>  template&lt;class ExecutionPolicy, <em>in-vector</em> InVec1, <em>in-vector</em> InVec2, <span class="rm" style="color: #bf0303"><del><em><span><code>in</code></span></em></del></span><em>out-matrix</em> <span class="rm" style="color: #bf0303"><del><span><code>In</code></span></del></span>OutMat&gt;</span>
<span id="cb20-13"><a href="#cb20-13" aria-hidden="true" tabindex="-1"></a>    void matrix_rank_1_update_c(ExecutionPolicy&amp;&amp; exec,</span>
<span id="cb20-14"><a href="#cb20-14" aria-hidden="true" tabindex="-1"></a>                                InVec1 x, InVec2 y, <span class="rm" style="color: #bf0303"><del><span><code>In</code></span></del></span>OutMat A);</span></code></pre></div>
<div class="add" style="color: #00AA00">

<div class="sourceCode" id="cb21"><pre class="sourceCode default"><code class="sourceCode default"><span id="cb21-1"><a href="#cb21-1" aria-hidden="true" tabindex="-1"></a>  // updating nonsymmetric rank-1 matrix update</span>
<span id="cb21-2"><a href="#cb21-2" aria-hidden="true" tabindex="-1"></a>  template&lt;<em>in-vector</em> InVec1, <em>in-vector</em> InVec2, <em>in-matrix</em> InMat, <em>out-matrix</em> OutMat&gt;</span>
<span id="cb21-3"><a href="#cb21-3" aria-hidden="true" tabindex="-1"></a>    void matrix_rank_1_update(InVec1 x, InVec2 y, InMat E, OutMat A);</span>
<span id="cb21-4"><a href="#cb21-4" aria-hidden="true" tabindex="-1"></a>  template&lt;class ExecutionPolicy, <em>in-vector</em> InVec1, <em>in-matrix</em> InMat, <em>in-vector</em> InVec2, <em>out-matrix</em> OutMat&gt;</span>
<span id="cb21-5"><a href="#cb21-5" aria-hidden="true" tabindex="-1"></a>    void matrix_rank_1_update(ExecutionPolicy&amp;&amp; exec,</span>
<span id="cb21-6"><a href="#cb21-6" aria-hidden="true" tabindex="-1"></a>                              InVec1 x, InVec2 y, InMat E, OutMat A);</span>
<span id="cb21-7"><a href="#cb21-7" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb21-8"><a href="#cb21-8" aria-hidden="true" tabindex="-1"></a>  template&lt;<em>in-vector</em> InVec1, <em>in-vector</em> InVec2, <em>in-matrix</em> InMat, <em>out-matrix</em> OutMat&gt;</span>
<span id="cb21-9"><a href="#cb21-9" aria-hidden="true" tabindex="-1"></a>    void matrix_rank_1_update_c(InVec1 x, InVec2 y, InMat E, OutMat A);</span>
<span id="cb21-10"><a href="#cb21-10" aria-hidden="true" tabindex="-1"></a>  template&lt;class ExecutionPolicy, <em>in-vector</em> InVec1, <em>in-vector</em> InVec2, <em>in-matrix</em> InMat, <em>out-matrix</em> OutMat&gt;</span>
<span id="cb21-11"><a href="#cb21-11" aria-hidden="true" tabindex="-1"></a>    void matrix_rank_1_update_c(ExecutionPolicy&amp;&amp; exec,</span>
<span id="cb21-12"><a href="#cb21-12" aria-hidden="true" tabindex="-1"></a>                                InVec1 x, InVec2 y, InMat E, OutMat A);</span></code></pre></div>

</div>
<div class="sourceCode" id="cb22"><pre class="sourceCode default"><code class="sourceCode default"><span id="cb22-1"><a href="#cb22-1" aria-hidden="true" tabindex="-1"></a>  // [linalg.algs.blas2.symherrank1], symmetric or Hermitian rank-1 matrix update</span>
<span id="cb22-2"><a href="#cb22-2" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb22-3"><a href="#cb22-3" aria-hidden="true" tabindex="-1"></a>  <span class="add" style="color: #00AA00"><ins><span><code>// overwriting symmetric rank-1 matrix update</code></span></ins></span> </span>
<span id="cb22-4"><a href="#cb22-4" aria-hidden="true" tabindex="-1"></a>  template&lt;class Scalar, <em>in-vector</em> InVec, <em><code>possibly-packed-</code></em><span class="rm" style="color: #bf0303"><del><em><span><code>in</code></span></em></del></span><em>out-matrix</em> <span class="rm" style="color: #bf0303"><del><span><code>In</code></span></del></span>OutMat, class Triangle&gt;</span>
<span id="cb22-5"><a href="#cb22-5" aria-hidden="true" tabindex="-1"></a>    void symmetric_matrix_rank_1_update(Scalar alpha, InVec x, <span class="rm" style="color: #bf0303"><del><span><code>In</code></span></del></span>OutMat A, Triangle t);</span>
<span id="cb22-6"><a href="#cb22-6" aria-hidden="true" tabindex="-1"></a>  template&lt;class ExecutionPolicy,</span>
<span id="cb22-7"><a href="#cb22-7" aria-hidden="true" tabindex="-1"></a>           class Scalar, <em>in-vector</em> InVec, <em><code>possibly-packed-</code></em><span class="rm" style="color: #bf0303"><del><em><span><code>in</code></span></em></del></span><em>out-matrix</em> <span class="rm" style="color: #bf0303"><del><span><code>In</code></span></del></span>OutMat, class Triangle&gt;</span>
<span id="cb22-8"><a href="#cb22-8" aria-hidden="true" tabindex="-1"></a>    void symmetric_matrix_rank_1_update(ExecutionPolicy&amp;&amp; exec,</span>
<span id="cb22-9"><a href="#cb22-9" aria-hidden="true" tabindex="-1"></a>                                        Scalar alpha, InVec x, <span class="rm" style="color: #bf0303"><del><span><code>In</code></span></del></span>OutMat A, Triangle t);</span></code></pre></div>
<div class="rm" style="color: #bf0303">

<div class="sourceCode" id="cb23"><pre class="sourceCode default"><code class="sourceCode default"><span id="cb23-1"><a href="#cb23-1" aria-hidden="true" tabindex="-1"></a>  template&lt;<em>in-vector</em> InVec, <em>possibly-packed-inout-matrix</em> InOutMat, class Triangle&gt;</span>
<span id="cb23-2"><a href="#cb23-2" aria-hidden="true" tabindex="-1"></a>    void symmetric_matrix_rank_1_update(InVec x, InOutMat A, Triangle t);</span>
<span id="cb23-3"><a href="#cb23-3" aria-hidden="true" tabindex="-1"></a>  template&lt;class ExecutionPolicy,</span>
<span id="cb23-4"><a href="#cb23-4" aria-hidden="true" tabindex="-1"></a>           <em>in-vector</em> InVec, <em>possibly-packed-inout-matrix</em> InOutMat, class Triangle&gt;</span>
<span id="cb23-5"><a href="#cb23-5" aria-hidden="true" tabindex="-1"></a>    void symmetric_matrix_rank_1_update(ExecutionPolicy&amp;&amp; exec,</span>
<span id="cb23-6"><a href="#cb23-6" aria-hidden="true" tabindex="-1"></a>                                        InVec x, InOutMat A, Triangle t);</span></code></pre></div>

</div>
<div class="sourceCode" id="cb24"><pre class="sourceCode default"><code class="sourceCode default"><span id="cb24-1"><a href="#cb24-1" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb24-2"><a href="#cb24-2" aria-hidden="true" tabindex="-1"></a>  <span class="add" style="color: #00AA00"><ins><span><code>// overwriting Hermitian rank-1 matrix update</code></span></ins></span> </span>
<span id="cb24-3"><a href="#cb24-3" aria-hidden="true" tabindex="-1"></a>  template&lt;class Scalar, <em>in-vector</em> InVec, <em><code>possibly-packed-</code></em><span class="rm" style="color: #bf0303"><del><em><span><code>in</code></span></em></del></span><em>out-matrix</em> <span class="rm" style="color: #bf0303"><del><span><code>In</code></span></del></span>OutMat, class Triangle&gt;</span>
<span id="cb24-4"><a href="#cb24-4" aria-hidden="true" tabindex="-1"></a>    void hermitian_matrix_rank_1_update(Scalar alpha, InVec x, <span class="rm" style="color: #bf0303"><del><span><code>In</code></span></del></span>OutMat A, Triangle t);</span>
<span id="cb24-5"><a href="#cb24-5" aria-hidden="true" tabindex="-1"></a>  template&lt;class ExecutionPolicy,</span>
<span id="cb24-6"><a href="#cb24-6" aria-hidden="true" tabindex="-1"></a>           class Scalar, <em>in-vector</em> InVec, <em><code>possibly-packed-</code></em><span class="rm" style="color: #bf0303"><del><em><span><code>in</code></span></em></del></span><em>out-matrix</em> <span class="rm" style="color: #bf0303"><del><span><code>In</code></span></del></span>OutMat, class Triangle&gt;</span>
<span id="cb24-7"><a href="#cb24-7" aria-hidden="true" tabindex="-1"></a>    void hermitian_matrix_rank_1_update(ExecutionPolicy&amp;&amp; exec,</span>
<span id="cb24-8"><a href="#cb24-8" aria-hidden="true" tabindex="-1"></a>                                        Scalar alpha, InVec x, <span class="rm" style="color: #bf0303"><del><span><code>In</code></span></del></span>OutMat A, Triangle t);</span></code></pre></div>
<div class="rm" style="color: #bf0303">

<div class="sourceCode" id="cb25"><pre class="sourceCode default"><code class="sourceCode default"><span id="cb25-1"><a href="#cb25-1" aria-hidden="true" tabindex="-1"></a>  template&lt;<em>in-vector</em> InVec, <em>possibly-packed-inout-matrix</em> InOutMat, class Triangle&gt;</span>
<span id="cb25-2"><a href="#cb25-2" aria-hidden="true" tabindex="-1"></a>    void hermitian_matrix_rank_1_update(InVec x, InOutMat A, Triangle t);</span>
<span id="cb25-3"><a href="#cb25-3" aria-hidden="true" tabindex="-1"></a>  template&lt;class ExecutionPolicy,</span>
<span id="cb25-4"><a href="#cb25-4" aria-hidden="true" tabindex="-1"></a>           <em>in-vector</em> InVec, <em>possibly-packed-inout-matrix</em> InOutMat, class Triangle&gt;</span>
<span id="cb25-5"><a href="#cb25-5" aria-hidden="true" tabindex="-1"></a>    void hermitian_matrix_rank_1_update(ExecutionPolicy&amp;&amp; exec,</span>
<span id="cb25-6"><a href="#cb25-6" aria-hidden="true" tabindex="-1"></a>                                        InVec x, InOutMat A, Triangle t);</span></code></pre></div>

</div>
<div class="add" style="color: #00AA00">

<div class="sourceCode" id="cb26"><pre class="sourceCode default"><code class="sourceCode default"><span id="cb26-1"><a href="#cb26-1" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb26-2"><a href="#cb26-2" aria-hidden="true" tabindex="-1"></a>  // updating symmetric rank-1 matrix update </span>
<span id="cb26-3"><a href="#cb26-3" aria-hidden="true" tabindex="-1"></a>  template&lt;class Scalar, <em>in-vector</em> InVec, <em>in-matrix</em> InMat, <em>possibly-packed-out-matrix</em> OutMat, class Triangle&gt;</span>
<span id="cb26-4"><a href="#cb26-4" aria-hidden="true" tabindex="-1"></a>    void symmetric_matrix_rank_1_update(Scalar alpha, InVec x, InMat E, OutMat A, Triangle t);</span>
<span id="cb26-5"><a href="#cb26-5" aria-hidden="true" tabindex="-1"></a>  template&lt;class ExecutionPolicy,</span>
<span id="cb26-6"><a href="#cb26-6" aria-hidden="true" tabindex="-1"></a>           class Scalar, <em>in-vector</em> InVec, <em>in-matrix</em> InMat, <em>possibly-packed-out-matrix</em> OutMat, class Triangle&gt;</span>
<span id="cb26-7"><a href="#cb26-7" aria-hidden="true" tabindex="-1"></a>    void symmetric_matrix_rank_1_update(ExecutionPolicy&amp;&amp; exec,</span>
<span id="cb26-8"><a href="#cb26-8" aria-hidden="true" tabindex="-1"></a>                                        Scalar alpha, InVec x, InMat E, OutMat A, Triangle t);</span>
<span id="cb26-9"><a href="#cb26-9" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb26-10"><a href="#cb26-10" aria-hidden="true" tabindex="-1"></a>  // updating Hermitian rank-1 matrix update </span>
<span id="cb26-11"><a href="#cb26-11" aria-hidden="true" tabindex="-1"></a>  template&lt;class Scalar, <em>in-vector</em> InVec, <em>in-matrix</em> InMat, <em>possibly-packed-out-matrix</em> OutMat, class Triangle&gt;</span>
<span id="cb26-12"><a href="#cb26-12" aria-hidden="true" tabindex="-1"></a>    void hermitian_matrix_rank_1_update(Scalar alpha, InVec x, InMat E, OutMat A, Triangle t);</span>
<span id="cb26-13"><a href="#cb26-13" aria-hidden="true" tabindex="-1"></a>  template&lt;class ExecutionPolicy,</span>
<span id="cb26-14"><a href="#cb26-14" aria-hidden="true" tabindex="-1"></a>           class Scalar, <em>in-vector</em> InVec, <em>in-matrix</em> InMat, <em>possibly-packed-out-matrix</em> OutMat, class Triangle&gt;</span>
<span id="cb26-15"><a href="#cb26-15" aria-hidden="true" tabindex="-1"></a>    void hermitian_matrix_rank_1_update(ExecutionPolicy&amp;&amp; exec,</span>
<span id="cb26-16"><a href="#cb26-16" aria-hidden="true" tabindex="-1"></a>                                        Scalar alpha, InVec x, InMat E, OutMat A, Triangle t);</span></code></pre></div>

</div>
<h2 data-number="8.3" id="rank-2-update-functions-in-synopsis"><span class="header-section-number">8.3</span> Rank-2 update functions in
synopsis<a href="#rank-2-update-functions-in-synopsis" class="self-link"></a></h2>
<blockquote>
<p>In the header <code>&lt;linalg&gt;</code> synopsis
<strong>[linalg.syn]</strong>, change the declarations of the
<code>symmetric_matrix_rank_2_update</code> and
<code>hermitian_matrix_rank_2_update</code> overloads as follows.
<i>[Editorial Note:</i> There are two sets of changes here.</p>
<ol type="1">
<li>The existing overloads become “overwriting” overloads.</li>
<li>New “updating” overloads are added. <i>– end note]</i></li>
</ol>
</blockquote>
<div class="sourceCode" id="cb27"><pre class="sourceCode default"><code class="sourceCode default"><span id="cb27-1"><a href="#cb27-1" aria-hidden="true" tabindex="-1"></a>  // [linalg.algs.blas2.rank2], symmetric and Hermitian rank-2 matrix updates</span>
<span id="cb27-2"><a href="#cb27-2" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb27-3"><a href="#cb27-3" aria-hidden="true" tabindex="-1"></a>  // <span class="add" style="color: #00AA00"><ins><span><code>overwriting</code></span></ins></span> symmetric rank-2 matrix update</span>
<span id="cb27-4"><a href="#cb27-4" aria-hidden="true" tabindex="-1"></a>  template&lt;<em>in-vector</em> InVec1, <em>in-vector</em> InVec2,</span>
<span id="cb27-5"><a href="#cb27-5" aria-hidden="true" tabindex="-1"></a>           <em><code>possibly-packed-</code></em><span class="rm" style="color: #bf0303"><del><em><span><code>in</code></span></em></del></span><em>out-matrix</em> <span class="rm" style="color: #bf0303"><del><span><code>In</code></span></del></span>OutMat, class Triangle&gt;</span>
<span id="cb27-6"><a href="#cb27-6" aria-hidden="true" tabindex="-1"></a>    void symmetric_matrix_rank_2_update(InVec1 x, InVec2 y, <span class="rm" style="color: #bf0303"><del><span><code>In</code></span></del></span>OutMat A, Triangle t);</span>
<span id="cb27-7"><a href="#cb27-7" aria-hidden="true" tabindex="-1"></a>  template&lt;class ExecutionPolicy, <em>in-vector</em> InVec1, <em>in-vector</em> InVec2,</span>
<span id="cb27-8"><a href="#cb27-8" aria-hidden="true" tabindex="-1"></a>           <em><code>possibly-packed-</code></em><span class="rm" style="color: #bf0303"><del><em><span><code>in</code></span></em></del></span><em>out-matrix</em> InOutMat, class Triangle&gt;</span>
<span id="cb27-9"><a href="#cb27-9" aria-hidden="true" tabindex="-1"></a>    void symmetric_matrix_rank_2_update(ExecutionPolicy&amp;&amp; exec,</span>
<span id="cb27-10"><a href="#cb27-10" aria-hidden="true" tabindex="-1"></a>                                        InVec1 x, InVec2 y, <span class="rm" style="color: #bf0303"><del><span><code>In</code></span></del></span>OutMat A, Triangle t);</span>
<span id="cb27-11"><a href="#cb27-11" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb27-12"><a href="#cb27-12" aria-hidden="true" tabindex="-1"></a>  // <span class="add" style="color: #00AA00"><ins><span><code>overwriting</code></span></ins></span> Hermitian rank-2 matrix update</span>
<span id="cb27-13"><a href="#cb27-13" aria-hidden="true" tabindex="-1"></a>  template&lt;<em>in-vector</em> InVec1, <em>in-vector</em> InVec2,</span>
<span id="cb27-14"><a href="#cb27-14" aria-hidden="true" tabindex="-1"></a>           <em><code>possibly-packed-</code></em><span class="rm" style="color: #bf0303"><del><em><span><code>in</code></span></em></del></span><em>out-matrix</em> <span class="rm" style="color: #bf0303"><del><span><code>In</code></span></del></span>OutMat, class Triangle&gt;</span>
<span id="cb27-15"><a href="#cb27-15" aria-hidden="true" tabindex="-1"></a>    void hermitian_matrix_rank_2_update(InVec1 x, InVec2 y, <span class="rm" style="color: #bf0303"><del><span><code>In</code></span></del></span>OutMat A, Triangle t);</span>
<span id="cb27-16"><a href="#cb27-16" aria-hidden="true" tabindex="-1"></a>  template&lt;class ExecutionPolicy, <em>in-vector</em> InVec1, <em>in-vector</em> InVec2,</span>
<span id="cb27-17"><a href="#cb27-17" aria-hidden="true" tabindex="-1"></a>           <em><code>possibly-packed-</code></em><span class="rm" style="color: #bf0303"><del><em><span><code>in</code></span></em></del></span><em>out-matrix</em> InOutMat, class Triangle&gt;</span>
<span id="cb27-18"><a href="#cb27-18" aria-hidden="true" tabindex="-1"></a>    void hermitian_matrix_rank_2_update(ExecutionPolicy&amp;&amp; exec,</span>
<span id="cb27-19"><a href="#cb27-19" aria-hidden="true" tabindex="-1"></a>                                        InVec1 x, InVec2 y, <span class="rm" style="color: #bf0303"><del><span><code>In</code></span></del></span>OutMat A, Triangle t);</span></code></pre></div>
<div class="add" style="color: #00AA00">

<div class="sourceCode" id="cb28"><pre class="sourceCode default"><code class="sourceCode default"><span id="cb28-1"><a href="#cb28-1" aria-hidden="true" tabindex="-1"></a>  // updating symmetric rank-2 matrix update</span>
<span id="cb28-2"><a href="#cb28-2" aria-hidden="true" tabindex="-1"></a>  template&lt;<em>in-vector</em> InVec1, <em>in-vector</em> InVec2,</span>
<span id="cb28-3"><a href="#cb28-3" aria-hidden="true" tabindex="-1"></a>           <em>in-matrix</em> InMat,</span>
<span id="cb28-4"><a href="#cb28-4" aria-hidden="true" tabindex="-1"></a>           <em>possibly-packed-out-matrix</em> OutMat, class Triangle&gt;</span>
<span id="cb28-5"><a href="#cb28-5" aria-hidden="true" tabindex="-1"></a>    void symmetric_matrix_rank_2_update(InVec1 x, InVec2 y, InMat E, OutMat A, Triangle t);</span>
<span id="cb28-6"><a href="#cb28-6" aria-hidden="true" tabindex="-1"></a>  template&lt;class ExecutionPolicy, <em>in-vector</em> InVec1, <em>in-vector</em> InVec2,</span>
<span id="cb28-7"><a href="#cb28-7" aria-hidden="true" tabindex="-1"></a>           <em>in-matrix</em> InMat,</span>
<span id="cb28-8"><a href="#cb28-8" aria-hidden="true" tabindex="-1"></a>           <em>possibly-packed-out-matrix</em> OutMat, class Triangle&gt;</span>
<span id="cb28-9"><a href="#cb28-9" aria-hidden="true" tabindex="-1"></a>    void symmetric_matrix_rank_2_update(ExecutionPolicy&amp;&amp; exec,</span>
<span id="cb28-10"><a href="#cb28-10" aria-hidden="true" tabindex="-1"></a>                                        InVec1 x, InVec2 y, InMat E, OutMat A, Triangle t);</span>
<span id="cb28-11"><a href="#cb28-11" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb28-12"><a href="#cb28-12" aria-hidden="true" tabindex="-1"></a>  // updating Hermitian rank-2 matrix update</span>
<span id="cb28-13"><a href="#cb28-13" aria-hidden="true" tabindex="-1"></a>  template&lt;<em>in-vector</em> InVec1, <em>in-vector</em> InVec2,</span>
<span id="cb28-14"><a href="#cb28-14" aria-hidden="true" tabindex="-1"></a>           <em>in-matrix</em> InMat,</span>
<span id="cb28-15"><a href="#cb28-15" aria-hidden="true" tabindex="-1"></a>           <em>possibly-packed-out-matrix</em> OutMat, class Triangle&gt;</span>
<span id="cb28-16"><a href="#cb28-16" aria-hidden="true" tabindex="-1"></a>    void hermitian_matrix_rank_2_update(InVec1 x, InVec2 y, InMat E, OutMat A, Triangle t);</span>
<span id="cb28-17"><a href="#cb28-17" aria-hidden="true" tabindex="-1"></a>  template&lt;class ExecutionPolicy, <em>in-vector</em> InVec1, <em>in-vector</em> InVec2,</span>
<span id="cb28-18"><a href="#cb28-18" aria-hidden="true" tabindex="-1"></a>           <em>in-matrix</em> InMat,</span>
<span id="cb28-19"><a href="#cb28-19" aria-hidden="true" tabindex="-1"></a>           <em>possibly-packed-out-matrix</em> OutMat, class Triangle&gt;</span>
<span id="cb28-20"><a href="#cb28-20" aria-hidden="true" tabindex="-1"></a>    void hermitian_matrix_rank_2_update(ExecutionPolicy&amp;&amp; exec,</span>
<span id="cb28-21"><a href="#cb28-21" aria-hidden="true" tabindex="-1"></a>                                        InVec1 x, InVec2 y, InMat E, OutMat A, Triangle t);</span></code></pre></div>

</div>
<h2 data-number="8.4" id="rank-k-update-functions-in-synopsis"><span class="header-section-number">8.4</span> Rank-k update functions in
synopsis<a href="#rank-k-update-functions-in-synopsis" class="self-link"></a></h2>
<blockquote>
<p>In the header <code>&lt;linalg&gt;</code> synopsis
<strong>[linalg.syn]</strong>, update the declarations of the
<code>symmetric_matrix_rank_k_update</code> and
<code>hermitian_matrix_rank_k_update</code> overloads as follows.
<i>[Editorial Note:</i> There are three sets of changes here.</p>
<ol type="1">
<li>The existing overloads become “overwriting” overloads.</li>
<li>New “updating” overloads are added.</li>
<li>Any overloads of the symmetric and Hermitian rank-k update functions
that do <em>not</em> have a <code>Scalar alpha</code> parameter are
removed. <i>– end note]</i></li>
</ol>
</blockquote>
<div class="sourceCode" id="cb29"><pre class="sourceCode default"><code class="sourceCode default"><span id="cb29-1"><a href="#cb29-1" aria-hidden="true" tabindex="-1"></a>  // [linalg.algs.blas3.rankk], rank-k update of a symmetric or Hermitian matrix</span>
<span id="cb29-2"><a href="#cb29-2" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb29-3"><a href="#cb29-3" aria-hidden="true" tabindex="-1"></a>  // <span class="add" style="color: #00AA00"><ins><span><code>overwriting</code></span></ins></span> rank-k symmetric matrix update</span>
<span id="cb29-4"><a href="#cb29-4" aria-hidden="true" tabindex="-1"></a>  template&lt;class Scalar,</span>
<span id="cb29-5"><a href="#cb29-5" aria-hidden="true" tabindex="-1"></a>           <em>in-matrix</em> InMat,</span>
<span id="cb29-6"><a href="#cb29-6" aria-hidden="true" tabindex="-1"></a>           <em><code>possibly-packed-</code></em><span class="rm" style="color: #bf0303"><del><em><span><code>in</code></span></em></del></span><em>out-matrix</em> <span class="rm" style="color: #bf0303"><del><span><code>In</code></span></del></span>OutMat,</span>
<span id="cb29-7"><a href="#cb29-7" aria-hidden="true" tabindex="-1"></a>           class Triangle&gt;</span>
<span id="cb29-8"><a href="#cb29-8" aria-hidden="true" tabindex="-1"></a>    void symmetric_matrix_rank_k_update(Scalar alpha, InMat A, <span class="rm" style="color: #bf0303"><del><span><code>In</code></span></del></span>OutMat C, Triangle t);</span>
<span id="cb29-9"><a href="#cb29-9" aria-hidden="true" tabindex="-1"></a>  template&lt;class ExecutionPolicy,</span>
<span id="cb29-10"><a href="#cb29-10" aria-hidden="true" tabindex="-1"></a>           class Scalar,</span>
<span id="cb29-11"><a href="#cb29-11" aria-hidden="true" tabindex="-1"></a>           <em>in-matrix</em> InMat,</span>
<span id="cb29-12"><a href="#cb29-12" aria-hidden="true" tabindex="-1"></a>           <em><code>possibly-packed-</code></em><span class="rm" style="color: #bf0303"><del><em><span><code>in</code></span></em></del></span><em>out-matrix</em> <span class="rm" style="color: #bf0303"><del><span><code>In</code></span></del></span>OutMat,</span>
<span id="cb29-13"><a href="#cb29-13" aria-hidden="true" tabindex="-1"></a>           class Triangle&gt;</span>
<span id="cb29-14"><a href="#cb29-14" aria-hidden="true" tabindex="-1"></a>    void symmetric_matrix_rank_k_update(ExecutionPolicy&amp;&amp; exec,</span>
<span id="cb29-15"><a href="#cb29-15" aria-hidden="true" tabindex="-1"></a>                                        Scalar alpha, InMat A, <span class="rm" style="color: #bf0303"><del><span><code>In</code></span></del></span>OutMat C, Triangle t);</span></code></pre></div>
<div class="rm" style="color: #bf0303">

<div class="sourceCode" id="cb30"><pre class="sourceCode default"><code class="sourceCode default"><span id="cb30-1"><a href="#cb30-1" aria-hidden="true" tabindex="-1"></a>  template&lt;<em>in-matrix</em> InMat,</span>
<span id="cb30-2"><a href="#cb30-2" aria-hidden="true" tabindex="-1"></a>           <em>possibly-packed-inout-matrix</em> InOutMat,</span>
<span id="cb30-3"><a href="#cb30-3" aria-hidden="true" tabindex="-1"></a>           class Triangle&gt;</span>
<span id="cb30-4"><a href="#cb30-4" aria-hidden="true" tabindex="-1"></a>    void symmetric_matrix_rank_k_update(InMat A, InOutMat C, Triangle t);</span>
<span id="cb30-5"><a href="#cb30-5" aria-hidden="true" tabindex="-1"></a>  template&lt;class ExecutionPolicy,</span>
<span id="cb30-6"><a href="#cb30-6" aria-hidden="true" tabindex="-1"></a>           <em>in-matrix</em> InMat,</span>
<span id="cb30-7"><a href="#cb30-7" aria-hidden="true" tabindex="-1"></a>           <em>possibly-packed-inout-matrix</em> InOutMat,</span>
<span id="cb30-8"><a href="#cb30-8" aria-hidden="true" tabindex="-1"></a>           class Triangle&gt;</span>
<span id="cb30-9"><a href="#cb30-9" aria-hidden="true" tabindex="-1"></a>    void symmetric_matrix_rank_k_update(ExecutionPolicy&amp;&amp; exec,</span>
<span id="cb30-10"><a href="#cb30-10" aria-hidden="true" tabindex="-1"></a>                                        InMat A, InOutMat C, Triangle t);</span></code></pre></div>

</div>
<div class="sourceCode" id="cb31"><pre class="sourceCode default"><code class="sourceCode default"><span id="cb31-1"><a href="#cb31-1" aria-hidden="true" tabindex="-1"></a>  // <span class="add" style="color: #00AA00"><ins><span><code>overwriting</code></span></ins></span> rank-k Hermitian matrix update</span>
<span id="cb31-2"><a href="#cb31-2" aria-hidden="true" tabindex="-1"></a>  template&lt;class Scalar,</span>
<span id="cb31-3"><a href="#cb31-3" aria-hidden="true" tabindex="-1"></a>           <em>in-matrix</em> InMat,</span>
<span id="cb31-4"><a href="#cb31-4" aria-hidden="true" tabindex="-1"></a>           <em><code>possibly-packed-</code></em><span class="rm" style="color: #bf0303"><del><em><span><code>in</code></span></em></del></span><em>out-matrix</em> <span class="rm" style="color: #bf0303"><del><span><code>In</code></span></del></span>OutMat,</span>
<span id="cb31-5"><a href="#cb31-5" aria-hidden="true" tabindex="-1"></a>           class Triangle&gt;</span>
<span id="cb31-6"><a href="#cb31-6" aria-hidden="true" tabindex="-1"></a>    void hermitian_matrix_rank_k_update(Scalar alpha, InMat A, <span class="rm" style="color: #bf0303"><del><span><code>In</code></span></del></span>OutMat C, Triangle t);</span>
<span id="cb31-7"><a href="#cb31-7" aria-hidden="true" tabindex="-1"></a>  template&lt;class ExecutionPolicy,</span>
<span id="cb31-8"><a href="#cb31-8" aria-hidden="true" tabindex="-1"></a>           class Scalar,</span>
<span id="cb31-9"><a href="#cb31-9" aria-hidden="true" tabindex="-1"></a>           <em>in-matrix</em> InMat,</span>
<span id="cb31-10"><a href="#cb31-10" aria-hidden="true" tabindex="-1"></a>           <em><code>possibly-packed-</code></em><span class="rm" style="color: #bf0303"><del><em><span><code>in</code></span></em></del></span><em>out-matrix</em> <span class="rm" style="color: #bf0303"><del><span><code>In</code></span></del></span>OutMat,</span>
<span id="cb31-11"><a href="#cb31-11" aria-hidden="true" tabindex="-1"></a>           class Triangle&gt;</span>
<span id="cb31-12"><a href="#cb31-12" aria-hidden="true" tabindex="-1"></a>    void hermitian_matrix_rank_k_update(ExecutionPolicy&amp;&amp; exec,</span>
<span id="cb31-13"><a href="#cb31-13" aria-hidden="true" tabindex="-1"></a>                                        Scalar alpha, InMat A, <span class="rm" style="color: #bf0303"><del><span><code>In</code></span></del></span>OutMat C, Triangle t);</span></code></pre></div>
<div class="rm" style="color: #bf0303">

<div class="sourceCode" id="cb32"><pre class="sourceCode default"><code class="sourceCode default"><span id="cb32-1"><a href="#cb32-1" aria-hidden="true" tabindex="-1"></a>  template&lt;<em>in-matrix</em> InMat,</span>
<span id="cb32-2"><a href="#cb32-2" aria-hidden="true" tabindex="-1"></a>           <em>possibly-packed-inout-matrix</em> InOutMat,</span>
<span id="cb32-3"><a href="#cb32-3" aria-hidden="true" tabindex="-1"></a>           class Triangle&gt;</span>
<span id="cb32-4"><a href="#cb32-4" aria-hidden="true" tabindex="-1"></a>    void hermitian_matrix_rank_k_update(InMat A, InOutMat C, Triangle t);</span>
<span id="cb32-5"><a href="#cb32-5" aria-hidden="true" tabindex="-1"></a>  template&lt;class ExecutionPolicy,</span>
<span id="cb32-6"><a href="#cb32-6" aria-hidden="true" tabindex="-1"></a>           <em>in-matrix</em> InMat,</span>
<span id="cb32-7"><a href="#cb32-7" aria-hidden="true" tabindex="-1"></a>           <em>possibly-packed-inout-matrix</em> InOutMat,</span>
<span id="cb32-8"><a href="#cb32-8" aria-hidden="true" tabindex="-1"></a>           class Triangle&gt;</span>
<span id="cb32-9"><a href="#cb32-9" aria-hidden="true" tabindex="-1"></a>    void hermitian_matrix_rank_k_update(ExecutionPolicy&amp;&amp; exec,</span>
<span id="cb32-10"><a href="#cb32-10" aria-hidden="true" tabindex="-1"></a>                                        InMat A, InOutMat C, Triangle t);</span></code></pre></div>

</div>
<div class="add" style="color: #00AA00">

<div class="sourceCode" id="cb33"><pre class="sourceCode default"><code class="sourceCode default"><span id="cb33-1"><a href="#cb33-1" aria-hidden="true" tabindex="-1"></a>  // updating rank-k symmetric matrix update</span>
<span id="cb33-2"><a href="#cb33-2" aria-hidden="true" tabindex="-1"></a>  template&lt;class Scalar,</span>
<span id="cb33-3"><a href="#cb33-3" aria-hidden="true" tabindex="-1"></a>           <em>in-matrix</em> InMat1,</span>
<span id="cb33-4"><a href="#cb33-4" aria-hidden="true" tabindex="-1"></a>           <em>in-matrix</em> InMat2,</span>
<span id="cb33-5"><a href="#cb33-5" aria-hidden="true" tabindex="-1"></a>           <em>possibly-packed-out-matrix</em> OutMat,</span>
<span id="cb33-6"><a href="#cb33-6" aria-hidden="true" tabindex="-1"></a>           class Triangle&gt;</span>
<span id="cb33-7"><a href="#cb33-7" aria-hidden="true" tabindex="-1"></a>    void symmetric_matrix_rank_k_update(</span>
<span id="cb33-8"><a href="#cb33-8" aria-hidden="true" tabindex="-1"></a>      Scalar alpha,</span>
<span id="cb33-9"><a href="#cb33-9" aria-hidden="true" tabindex="-1"></a>      InMat1 A, InMat2 E, OutMat C, Triangle t);</span>
<span id="cb33-10"><a href="#cb33-10" aria-hidden="true" tabindex="-1"></a>  template&lt;class ExecutionPolicy, class Scalar,</span>
<span id="cb33-11"><a href="#cb33-11" aria-hidden="true" tabindex="-1"></a>           <em>in-matrix</em> InMat1,</span>
<span id="cb33-12"><a href="#cb33-12" aria-hidden="true" tabindex="-1"></a>           <em>in-matrix</em> InMat2,</span>
<span id="cb33-13"><a href="#cb33-13" aria-hidden="true" tabindex="-1"></a>           <em>possibly-packed-out-matrix</em> OutMat,</span>
<span id="cb33-14"><a href="#cb33-14" aria-hidden="true" tabindex="-1"></a>           class Triangle&gt;</span>
<span id="cb33-15"><a href="#cb33-15" aria-hidden="true" tabindex="-1"></a>    void symmetric_matrix_rank_k_update(</span>
<span id="cb33-16"><a href="#cb33-16" aria-hidden="true" tabindex="-1"></a>      ExecutionPolicy&amp;&amp; exec, Scalar alpha,</span>
<span id="cb33-17"><a href="#cb33-17" aria-hidden="true" tabindex="-1"></a>      InMat1 A, InMat2 E, OutMat C, Triangle t);</span>
<span id="cb33-18"><a href="#cb33-18" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb33-19"><a href="#cb33-19" aria-hidden="true" tabindex="-1"></a>  // updating rank-k Hermitian matrix update</span>
<span id="cb33-20"><a href="#cb33-20" aria-hidden="true" tabindex="-1"></a>  template&lt;class Scalar,</span>
<span id="cb33-21"><a href="#cb33-21" aria-hidden="true" tabindex="-1"></a>           <em>in-matrix</em> InMat1,</span>
<span id="cb33-22"><a href="#cb33-22" aria-hidden="true" tabindex="-1"></a>           <em>in-matrix</em> InMat2,</span>
<span id="cb33-23"><a href="#cb33-23" aria-hidden="true" tabindex="-1"></a>           <em>possibly-packed-out-matrix</em> OutMat,</span>
<span id="cb33-24"><a href="#cb33-24" aria-hidden="true" tabindex="-1"></a>           class Triangle&gt;</span>
<span id="cb33-25"><a href="#cb33-25" aria-hidden="true" tabindex="-1"></a>    void hermitian_matrix_rank_k_update(</span>
<span id="cb33-26"><a href="#cb33-26" aria-hidden="true" tabindex="-1"></a>      Scalar alpha,</span>
<span id="cb33-27"><a href="#cb33-27" aria-hidden="true" tabindex="-1"></a>      InMat1 A, InMat2 E, OutMat C, Triangle t);</span>
<span id="cb33-28"><a href="#cb33-28" aria-hidden="true" tabindex="-1"></a>  template&lt;class ExecutionPolicy, class Scalar,</span>
<span id="cb33-29"><a href="#cb33-29" aria-hidden="true" tabindex="-1"></a>           <em>in-matrix</em> InMat1,</span>
<span id="cb33-30"><a href="#cb33-30" aria-hidden="true" tabindex="-1"></a>           <em>in-matrix</em> InMat2,</span>
<span id="cb33-31"><a href="#cb33-31" aria-hidden="true" tabindex="-1"></a>           <em>possibly-packed-out-matrix</em> OutMat,</span>
<span id="cb33-32"><a href="#cb33-32" aria-hidden="true" tabindex="-1"></a>           class Triangle&gt;</span>
<span id="cb33-33"><a href="#cb33-33" aria-hidden="true" tabindex="-1"></a>    void hermitian_matrix_rank_k_update(</span>
<span id="cb33-34"><a href="#cb33-34" aria-hidden="true" tabindex="-1"></a>      ExecutionPolicy&amp;&amp; exec, Scalar alpha,</span>
<span id="cb33-35"><a href="#cb33-35" aria-hidden="true" tabindex="-1"></a>      InMat1 A, InMat2 E, OutMat C, Triangle t);</span></code></pre></div>

</div>
<h2 data-number="8.5" id="rank-2k-update-functions-in-synopsis"><span class="header-section-number">8.5</span> Rank-2k update functions in
synopsis<a href="#rank-2k-update-functions-in-synopsis" class="self-link"></a></h2>
<blockquote>
<p>In the header <code>&lt;linalg&gt;</code> synopsis
<strong>[linalg.syn]</strong>, update the declarations of the
<code>symmetric_matrix_rank_2k_update</code> and
<code>hermitian_matrix_rank_2k_update</code> overloads as follows.
<i>[Editorial Note:</i> There are two sets of changes here.</p>
<ol type="1">
<li>The existing overloads become “overwriting” overloads.</li>
<li>New “updating” overloads are added. <i>– end note]</i></li>
</ol>
</blockquote>
<div class="sourceCode" id="cb34"><pre class="sourceCode default"><code class="sourceCode default"><span id="cb34-1"><a href="#cb34-1" aria-hidden="true" tabindex="-1"></a>  // [linalg.algs.blas3.rank2k], rank-2k update of a symmetric or Hermitian matrix</span>
<span id="cb34-2"><a href="#cb34-2" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb34-3"><a href="#cb34-3" aria-hidden="true" tabindex="-1"></a>  // <span class="add" style="color: #00AA00"><ins><span><code>overwriting</code></span></ins></span> rank-2k symmetric matrix update</span>
<span id="cb34-4"><a href="#cb34-4" aria-hidden="true" tabindex="-1"></a>  template&lt;<em>in-matrix</em> InMat1, <em>in-matrix</em> InMat2,</span>
<span id="cb34-5"><a href="#cb34-5" aria-hidden="true" tabindex="-1"></a>           <em><code>possibly-packed-</code></em><span class="rm" style="color: #bf0303"><del><em><span><code>in</code></span></em></del></span><em><code>out-matrix</code></em> <span class="rm" style="color: #bf0303"><del><span><code>In</code></span></del></span>OutMat, class Triangle&gt;</span>
<span id="cb34-6"><a href="#cb34-6" aria-hidden="true" tabindex="-1"></a>    void symmetric_matrix_rank_2k_update(InMat1 A, InMat2 B, <span class="rm" style="color: #bf0303"><del><span><code>In</code></span></del></span>OutMat C, Triangle t);</span>
<span id="cb34-7"><a href="#cb34-7" aria-hidden="true" tabindex="-1"></a>  template&lt;class ExecutionPolicy,</span>
<span id="cb34-8"><a href="#cb34-8" aria-hidden="true" tabindex="-1"></a>           <em>in-matrix</em> InMat1, <em>in-matrix</em> InMat2,</span>
<span id="cb34-9"><a href="#cb34-9" aria-hidden="true" tabindex="-1"></a>           <em><code>possibly-packed-</code></em><span class="rm" style="color: #bf0303"><del><em><span><code>in</code></span></em></del></span><em><code>out-matrix</code></em> <span class="rm" style="color: #bf0303"><del><span><code>In</code></span></del></span>OutMat, class Triangle&gt;</span>
<span id="cb34-10"><a href="#cb34-10" aria-hidden="true" tabindex="-1"></a>    void symmetric_matrix_rank_2k_update(ExecutionPolicy&amp;&amp; exec,</span>
<span id="cb34-11"><a href="#cb34-11" aria-hidden="true" tabindex="-1"></a>                                         InMat1 A, InMat2 B, <span class="rm" style="color: #bf0303"><del><span><code>In</code></span></del></span>OutMat C, Triangle t);</span>
<span id="cb34-12"><a href="#cb34-12" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb34-13"><a href="#cb34-13" aria-hidden="true" tabindex="-1"></a>  // <span class="add" style="color: #00AA00"><ins><span><code>overwriting</code></span></ins></span> rank-2k Hermitian matrix update</span>
<span id="cb34-14"><a href="#cb34-14" aria-hidden="true" tabindex="-1"></a>  template&lt;<em>in-matrix</em> InMat1, <em>in-matrix</em> InMat2,</span>
<span id="cb34-15"><a href="#cb34-15" aria-hidden="true" tabindex="-1"></a>           <em><code>possibly-packed-</code></em><span class="rm" style="color: #bf0303"><del><em><span><code>in</code></span></em></del></span><em><code>out-matrix</code></em> <span class="rm" style="color: #bf0303"><del><span><code>In</code></span></del></span>OutMat, class Triangle&gt;</span>
<span id="cb34-16"><a href="#cb34-16" aria-hidden="true" tabindex="-1"></a>    void hermitian_matrix_rank_2k_update(InMat1 A, InMat2 B, <span class="rm" style="color: #bf0303"><del><span><code>In</code></span></del></span>OutMat C, Triangle t);</span>
<span id="cb34-17"><a href="#cb34-17" aria-hidden="true" tabindex="-1"></a>  template&lt;class ExecutionPolicy,</span>
<span id="cb34-18"><a href="#cb34-18" aria-hidden="true" tabindex="-1"></a>           <em>in-matrix</em> InMat1, <em>in-matrix</em> InMat2,</span>
<span id="cb34-19"><a href="#cb34-19" aria-hidden="true" tabindex="-1"></a>           <em><code>possibly-packed-</code></em><span class="rm" style="color: #bf0303"><del><em><span><code>in</code></span></em></del></span><em><code>out-matrix</code></em> <span class="rm" style="color: #bf0303"><del><span><code>In</code></span></del></span>OutMat, class Triangle&gt;</span>
<span id="cb34-20"><a href="#cb34-20" aria-hidden="true" tabindex="-1"></a>    void hermitian_matrix_rank_2k_update(ExecutionPolicy&amp;&amp; exec,</span>
<span id="cb34-21"><a href="#cb34-21" aria-hidden="true" tabindex="-1"></a>                                         InMat1 A, InMat2 B, <span class="rm" style="color: #bf0303"><del><span><code>In</code></span></del></span>OutMat C, Triangle t);</span></code></pre></div>
<div class="add" style="color: #00AA00">

<div class="sourceCode" id="cb35"><pre class="sourceCode default"><code class="sourceCode default"><span id="cb35-1"><a href="#cb35-1" aria-hidden="true" tabindex="-1"></a>  // updating symmetric rank-2k matrix update</span>
<span id="cb35-2"><a href="#cb35-2" aria-hidden="true" tabindex="-1"></a>  template&lt;<em>in-matrix</em> InMat1, <em>in-matrix</em> InMat2,</span>
<span id="cb35-3"><a href="#cb35-3" aria-hidden="true" tabindex="-1"></a>           <em>in-matrix</em> InMat3,</span>
<span id="cb35-4"><a href="#cb35-4" aria-hidden="true" tabindex="-1"></a>           <em>possibly-packed-out-matrix</em> OutMat, class Triangle&gt;</span>
<span id="cb35-5"><a href="#cb35-5" aria-hidden="true" tabindex="-1"></a>    void symmetric_matrix_rank_2k_update(InMat1 A, InMat2 B, InMat3 E, OutMat C, Triangle t);</span>
<span id="cb35-6"><a href="#cb35-6" aria-hidden="true" tabindex="-1"></a>  template&lt;class ExecutionPolicy,</span>
<span id="cb35-7"><a href="#cb35-7" aria-hidden="true" tabindex="-1"></a>           <em>in-matrix</em> InMat1, <em>in-matrix</em> InMat2,</span>
<span id="cb35-8"><a href="#cb35-8" aria-hidden="true" tabindex="-1"></a>           <em>in-matrix</em> InMat3,</span>
<span id="cb35-9"><a href="#cb35-9" aria-hidden="true" tabindex="-1"></a>           <em>possibly-packed-out-matrix</em> OutMat, class Triangle&gt;</span>
<span id="cb35-10"><a href="#cb35-10" aria-hidden="true" tabindex="-1"></a>    void symmetric_matrix_rank_2k_update(ExecutionPolicy&amp;&amp; exec,</span>
<span id="cb35-11"><a href="#cb35-11" aria-hidden="true" tabindex="-1"></a>                                         InMat1 A, InMat2 B, InMat3 E, OutMat C, Triangle t);</span>
<span id="cb35-12"><a href="#cb35-12" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb35-13"><a href="#cb35-13" aria-hidden="true" tabindex="-1"></a>  // updating Hermitian rank-2k matrix update</span>
<span id="cb35-14"><a href="#cb35-14" aria-hidden="true" tabindex="-1"></a>  template&lt;<em>in-matrix</em> InMat1, <em>in-matrix</em> InMat2,</span>
<span id="cb35-15"><a href="#cb35-15" aria-hidden="true" tabindex="-1"></a>           <em>in-matrix</em> InMat3,</span>
<span id="cb35-16"><a href="#cb35-16" aria-hidden="true" tabindex="-1"></a>           <em>possibly-packed-out-matrix</em> OutMat, class Triangle&gt;</span>
<span id="cb35-17"><a href="#cb35-17" aria-hidden="true" tabindex="-1"></a>    void hermitian_matrix_rank_2k_update(InMat1 A, InMat2 B, InMat3 E, OutMat C, Triangle t);</span>
<span id="cb35-18"><a href="#cb35-18" aria-hidden="true" tabindex="-1"></a>  template&lt;class ExecutionPolicy,</span>
<span id="cb35-19"><a href="#cb35-19" aria-hidden="true" tabindex="-1"></a>           <em>in-matrix</em> InMat1, <em>in-matrix</em> InMat2,</span>
<span id="cb35-20"><a href="#cb35-20" aria-hidden="true" tabindex="-1"></a>           <em>in-matrix</em> InMat3,</span>
<span id="cb35-21"><a href="#cb35-21" aria-hidden="true" tabindex="-1"></a>           <em>possibly-packed-out-matrix</em> OutMat, class Triangle&gt;</span>
<span id="cb35-22"><a href="#cb35-22" aria-hidden="true" tabindex="-1"></a>    void hermitian_matrix_rank_2k_update(ExecutionPolicy&amp;&amp; exec,</span>
<span id="cb35-23"><a href="#cb35-23" aria-hidden="true" tabindex="-1"></a>                                         InMat1 A, InMat2 B, InMat3 E, OutMat C, Triangle t);</span></code></pre></div>

</div>
<h2 data-number="8.6" id="constrain-linear-algebra-value-type"><span class="header-section-number">8.6</span> Constrain linear algebra value
type<a href="#constrain-linear-algebra-value-type" class="self-link"></a></h2>
<blockquote>
<p>Change [linalg.reqs.val] as follows.</p>
</blockquote>
<p><span class="marginalizedparent"><a class="marginalized">1</a></span>
Throughout [linalg], the following types are <em>linear algebra value
type<span class="add" style="color: #00AA00"><ins>s</ins></span></em>:</p>
<ul>
<li><p><span class="marginalizedparent"><a class="marginalized">(1.1)</a></span> the
<code>value_type</code> type alias of any input or output
<code>mdspan</code> parameter(s) of any function in
<strong>[linalg]</strong>; and</p></li>
<li><p><span class="marginalizedparent"><a class="marginalized">(1.2)</a></span> the
<code>Scalar</code> template parameter (if any) of any function or class
in <strong>[linalg]</strong>.</p></li>
</ul>
<p><span class="marginalizedparent"><a class="marginalized">2</a></span>
Linear algebra value types shall model <code>semiregular</code>.</p>
<div class="add" style="color: #00AA00">
<p><span class="marginalizedparent"><a class="marginalized">3</a></span>
Linear algebra value types shall not be specializations of
<code>mdspan</code>.</p>
<p><span class="marginalizedparent"><a class="marginalized">4</a></span>
If <code>T</code> is a linear algebra value type, then
<code>is_execution_policy_v&lt;T&gt;</code> shall be
<code>false</code>.</p>
</div>
<p><span class="marginalizedparent"><a class="marginalized">5</a></span>
A value-initialized object of linear algebra value type shall act as the
additive identity.</p>
<h2 data-number="8.7" id="specification-of-nonsymmetric-rank-1-update-functions"><span class="header-section-number">8.7</span> Specification of nonsymmetric
rank-1 update functions<a href="#specification-of-nonsymmetric-rank-1-update-functions" class="self-link"></a></h2>
<blockquote>
<p>Change [linalg.algs.blas2.rank1] as follows.</p>
</blockquote>
<div class="add" style="color: #00AA00">
<p><span class="marginalizedparent"><a class="marginalized">1</a></span>
The following elements apply to all functions in
[linalg.algs.blas2.rank1].</p>
<p><span class="marginalizedparent"><a class="marginalized">2</a></span>
<em>Mandates</em>:</p>
<p><span class="marginalizedparent"><a class="marginalized">(2.1)</a></span>
<em><code>possibly-multipliable</code></em><code>&lt;OutMat, InVec2, InVec1&gt;()</code>
is <code>true</code>, and</p>
<p><span class="marginalizedparent"><a class="marginalized">(2.2)</a></span>
<em><code>possibly-addable</code></em><code>(A, E, A)</code> is
<code>true</code> for those overloads that take an <code>E</code>
parameter.</p>
<p><span class="marginalizedparent"><a class="marginalized">3</a></span>
<em>Preconditions</em>:</p>
<p><span class="marginalizedparent"><a class="marginalized">(3.1)</a></span>
<code>multipliable(A, y, x)</code> is <code>true</code>, and</p>
<p><span class="marginalizedparent"><a class="marginalized">(3.2)</a></span>
<em><code>addable</code></em><code>(A, E, A)</code> is <code>true</code>
for those overloads that take an <code>E</code> parameter.</p>
<p><span class="marginalizedparent"><a class="marginalized">4</a></span>
<em>Complexity</em>: <span class="math inline"><em>O</em>(</span>
<code>x.extent(0)</code> × <code>y.extent(0)</code> <span class="math inline">)</span>.</p>
</div>
<div class="sourceCode" id="cb36"><pre class="sourceCode default"><code class="sourceCode default"><span id="cb36-1"><a href="#cb36-1" aria-hidden="true" tabindex="-1"></a>template&lt;<em>in-vector</em> InVec1, <em>in-vector</em> InVec2, <span class="rm" style="color: #bf0303"><del><em><span><code>in</code></span></em></del></span><em><code>out-matrix</code></em> <span class="rm" style="color: #bf0303"><del><span><code>In</code></span></del></span>OutMat&gt;</span>
<span id="cb36-2"><a href="#cb36-2" aria-hidden="true" tabindex="-1"></a>  void matrix_rank_1_update(InVec1 x, InVec2 y, <span class="rm" style="color: #bf0303"><del><span><code>In</code></span></del></span>OutMat A);</span>
<span id="cb36-3"><a href="#cb36-3" aria-hidden="true" tabindex="-1"></a>template&lt;class ExecutionPolicy, <em>in-vector</em> InVec1, <em>in-vector</em> InVec2, <span class="rm" style="color: #bf0303"><del><em><span><code>in</code></span></em></del></span><em><code>out-matrix</code></em> <span class="rm" style="color: #bf0303"><del><span><code>In</code></span></del></span>OutMat&gt;</span>
<span id="cb36-4"><a href="#cb36-4" aria-hidden="true" tabindex="-1"></a>  void matrix_rank_1_update(ExecutionPolicy&amp;&amp; exec, InVec1 x, InVec2 y, <span class="rm" style="color: #bf0303"><del><span><code>In</code></span></del></span>OutMat A);</span></code></pre></div>
<p><span class="marginalizedparent"><a class="marginalized">5</a></span>
These functions perform a<span class="add" style="color: #00AA00"><ins>n
overwriting</ins></span> nonsymmetric nonconjugated rank-1 update.</p>
<p><i>[Note:</i> These functions correspond to the BLAS functions
<code>xGER</code> (for real element types) and <code>xGERU</code> (for
complex element types)[bib]. <i>– end note]</i></p>
<div class="rm" style="color: #bf0303">
<p><span class="marginalizedparent"><a class="marginalized">2</a></span>
<em>Mandates</em>:
<em><code>possibly-multipliable</code></em><code>&lt;InOutMat, InVec2, InVec1&gt;()</code>
is <code>true</code>.</p>
<p><span class="marginalizedparent"><a class="marginalized">3</a></span>
<em>Preconditions</em>:
<em><code>multipliable</code></em><code>(A, y, x)</code> is
<code>true</code>.</p>
<p><span class="marginalizedparent"><a class="marginalized">4</a></span>
<em>Effects</em>: Computes a matrix <span class="math inline"><em>A</em>′</span> such that <span class="math inline"><em>A</em>′ = <em>A</em> + <em>x</em><em>y</em><sup><em>T</em></sup></span>,
and assigns each element of <span class="math inline"><em>A</em>′</span>
to the corresponding element of <span class="math inline"><em>A</em></span>.</p>
</div>
<div class="add" style="color: #00AA00">
<p><span class="marginalizedparent"><a class="marginalized">6</a></span>
<em>Effects</em>: Computes <span class="math inline"><em>A</em> = <em>x</em><em>y</em><sup><em>T</em></sup></span>.</p>
</div>
<div class="rm" style="color: #bf0303">
<p><span class="marginalizedparent"><a class="marginalized">5</a></span>
<em>Complexity</em>: <span class="math inline"><em>O</em>(</span>
<code>x.extent(0)</code> × <code>y.extent(0)</code> <span class="math inline">)</span>.</p>
</div>
<div class="add" style="color: #00AA00">

<div class="sourceCode" id="cb37"><pre class="sourceCode default"><code class="sourceCode default"><span id="cb37-1"><a href="#cb37-1" aria-hidden="true" tabindex="-1"></a>template&lt;<em>in-vector</em> InVec1, <em>in-vector</em> InVec2, <em>in-matrix</em> InMat, <em>out-matrix</em> OutMat&gt;</span>
<span id="cb37-2"><a href="#cb37-2" aria-hidden="true" tabindex="-1"></a>  void matrix_rank_1_update(InVec1 x, InVec2 y, InMat E, OutMat A);</span>
<span id="cb37-3"><a href="#cb37-3" aria-hidden="true" tabindex="-1"></a>template&lt;class ExecutionPolicy, <em>in-vector</em> InVec1, <em>in-vector</em> InVec2, <em>in-matrix</em> InMat, <em>out-matrix</em> OutMat&gt;</span>
<span id="cb37-4"><a href="#cb37-4" aria-hidden="true" tabindex="-1"></a>  void matrix_rank_1_update(ExecutionPolicy&amp;&amp; exec, InVec1 x, InVec2 y, InMat E, OutMat A);</span></code></pre></div>
<p><span class="marginalizedparent"><a class="marginalized">7</a></span>
These functions perform an updating nonsymmetric nonconjugated rank-1
update.</p>
<p><i>[Note:</i> These functions correspond to the BLAS functions
<code>xGER</code> (for real element types) and <code>xGERU</code> (for
complex element types)[bib]. <i>– end note]</i></p>
<p><span class="marginalizedparent"><a class="marginalized">8</a></span>
<em>Effects</em>: Computes <span class="math inline"><em>A</em> = <em>E</em> + <em>x</em><em>y</em><sup><em>T</em></sup></span>.</p>
<p><span class="marginalizedparent"><a class="marginalized">9</a></span>
<em>Remarks</em>: <code>A</code> may alias <code>E</code>.</p>
</div>
<div class="sourceCode" id="cb38"><pre class="sourceCode default"><code class="sourceCode default"><span id="cb38-1"><a href="#cb38-1" aria-hidden="true" tabindex="-1"></a>template&lt;<em>in-vector</em> InVec1, <em>in-vector</em> InVec2, <span class="rm" style="color: #bf0303"><del><em><span><code>in</code></span></em></del></span><em><code>out-matrix</code></em> <span class="rm" style="color: #bf0303"><del><span><code>In</code></span></del></span>OutMat&gt;</span>
<span id="cb38-2"><a href="#cb38-2" aria-hidden="true" tabindex="-1"></a>  void matrix_rank_1_update_c(InVec1 x, InVec2 y, <span class="rm" style="color: #bf0303"><del><span><code>In</code></span></del></span>OutMat A);</span>
<span id="cb38-3"><a href="#cb38-3" aria-hidden="true" tabindex="-1"></a>template&lt;class ExecutionPolicy, <em>in-vector</em> InVec1, <em>in-vector</em> InVec2, <span class="rm" style="color: #bf0303"><del><em><span><code>in</code></span></em></del></span><em><code>out-matrix</code></em> <span class="rm" style="color: #bf0303"><del><span><code>In</code></span></del></span>OutMat&gt;</span>
<span id="cb38-4"><a href="#cb38-4" aria-hidden="true" tabindex="-1"></a>  void matrix_rank_1_update_c(ExecutionPolicy&amp;&amp; exec, InVec1 x, InVec2 y, <span class="rm" style="color: #bf0303"><del><span><code>In</code></span></del></span>OutMat A);</span></code></pre></div>
<p><span class="marginalizedparent"><a class="marginalized">10</a></span> These
functions perform a<span class="add" style="color: #00AA00"><ins>n
overwriting</ins></span> nonsymmetric conjugated rank-1 update.</p>
<p><i>[Note:</i> These functions correspond to the BLAS functions
<code>xGER</code> (for real element types) and <code>xGERC</code> (for
complex element types)[bib]. <i>– end note]</i></p>
<p><span class="marginalizedparent"><a class="marginalized">11</a></span>
<em>Effects</em>:</p>
<p><span class="marginalizedparent"><a class="marginalized">(11.1)</a></span> For
the overloads without an <code>ExecutionPolicy</code> argument,
equivalent to:</p>
<div class="sourceCode" id="cb39"><pre class="sourceCode default"><code class="sourceCode default"><span id="cb39-1"><a href="#cb39-1" aria-hidden="true" tabindex="-1"></a>matrix_rank_1_update(x, conjugated(y), A);</span></code></pre></div>
<p><span class="marginalizedparent"><a class="marginalized">(11.2)</a></span>
otherwise, equivalent to:</p>
<div class="sourceCode" id="cb40"><pre class="sourceCode default"><code class="sourceCode default"><span id="cb40-1"><a href="#cb40-1" aria-hidden="true" tabindex="-1"></a>matrix_rank_1_update(std::forward&lt;ExecutionPolicy&gt;(exec), x, conjugated(y), A);</span></code></pre></div>
<div class="add" style="color: #00AA00">

<div class="sourceCode" id="cb41"><pre class="sourceCode default"><code class="sourceCode default"><span id="cb41-1"><a href="#cb41-1" aria-hidden="true" tabindex="-1"></a>template&lt;<em>in-vector</em> InVec1, <em>in-vector</em> InVec2, <em>in-matrix</em> InMat, <em>out-matrix</em> OutMat&gt;</span>
<span id="cb41-2"><a href="#cb41-2" aria-hidden="true" tabindex="-1"></a>  void matrix_rank_1_update_c(InVec1 x, InVec2 y, InMat E, OutMat A);</span>
<span id="cb41-3"><a href="#cb41-3" aria-hidden="true" tabindex="-1"></a>template&lt;class ExecutionPolicy, <em>in-vector</em> InVec1, <em>in-vector</em> InVec2, <em>in-matrix</em> InMat, <em>out-matrix</em> OutMat&gt;</span>
<span id="cb41-4"><a href="#cb41-4" aria-hidden="true" tabindex="-1"></a>  void matrix_rank_1_update_c(ExecutionPolicy&amp;&amp; exec, InVec1 x, InVec2 y, InMat E, OutMat A);</span></code></pre></div>
<p><span class="marginalizedparent"><a class="marginalized">12</a></span> These
functions perform an updating nonsymmetric conjugated rank-1 update.</p>
<p><i>[Note:</i> These functions correspond to the BLAS functions
<code>xGER</code> (for real element types) and <code>xGERU</code> (for
complex element types)[bib]. <i>– end note]</i></p>
<p><span class="marginalizedparent"><a class="marginalized">13</a></span>
<em>Effects</em>:</p>
<p><span class="marginalizedparent"><a class="marginalized">(13.1)</a></span> For
the overloads without an <code>ExecutionPolicy</code> argument,
equivalent to:</p>
<div class="sourceCode" id="cb42"><pre class="sourceCode default"><code class="sourceCode default"><span id="cb42-1"><a href="#cb42-1" aria-hidden="true" tabindex="-1"></a>matrix_rank_1_update(x, conjugated(y), E, A);</span></code></pre></div>
<p><span class="marginalizedparent"><a class="marginalized">(13.2)</a></span>
otherwise, equivalent to:</p>
<div class="sourceCode" id="cb43"><pre class="sourceCode default"><code class="sourceCode default"><span id="cb43-1"><a href="#cb43-1" aria-hidden="true" tabindex="-1"></a>matrix_rank_1_update(std::forward&lt;ExecutionPolicy&gt;(exec), x, conjugated(y), E, A);</span></code></pre></div>

</div>
<h2 data-number="8.8" id="specification-of-symmetric-and-hermitian-rank-1-update-functions"><span class="header-section-number">8.8</span> Specification of symmetric and
Hermitian rank-1 update functions<a href="#specification-of-symmetric-and-hermitian-rank-1-update-functions" class="self-link"></a></h2>
<blockquote>
<p>Change [linalg.algs.blas2.symherrank1] as follows.</p>
</blockquote>
<p><span class="marginalizedparent"><a class="marginalized">1</a></span>
<i>[Note:</i> These functions correspond to the BLAS functions
<code>xSYR</code>, <code>xSPR</code>, <code>xHER</code>, and
<code>xHPR</code>[bib]. They take a scaling factor <code>alpha</code>,
because it would be impossible to express the update <span class="math inline"><em>A</em> = <em>A</em> − <em>x</em><em>x</em><sup><em>T</em></sup></span>
<span class="add" style="color: #00AA00"><ins>in noncomplex
arithmetic</ins></span> otherwise. <i>– end note]</i></p>
<p><span class="marginalizedparent"><a class="marginalized">2</a></span>
The following elements apply to all functions in
[linalg.algs.blas2.symherrank1].</p>
<div class="add" style="color: #00AA00">
<p><span class="marginalizedparent"><a class="marginalized">3</a></span>
For any function <code>F</code> in this section that takes a parameter
named <code>t</code>, an <code>InMat</code> template parameter, and a
function parameter <code>InMat E</code>, <code>t</code> applies to
accesses done through the parameter <code>E</code>. <code>F</code> will
only access the triangle of <code>E</code> specified by <code>t</code>.
For accesses of diagonal elements <code>E[i, i]</code>, <code>F</code>
will use the value
<em><code>real-if-needed</code></em><code>(E[i, i])</code> if the name
of <code>F</code> starts with <code>hermitian</code>. For accesses
<code>E[i, j]</code> outside the triangle specified by <code>t</code>,
<code>F</code> will use the value</p>
<ul>
<li><p><span class="marginalizedparent"><a class="marginalized">(3.1)</a></span>
<em><code>conj-if-needed</code></em><code>(E[j, i])</code> if the name
of <code>F</code> starts with <code>hermitian</code>, or</p></li>
<li><p><span class="marginalizedparent"><a class="marginalized">(3.2)</a></span>
<code>E[j, i]</code> if the name of <code>F</code> starts with
<code>symmetric</code>.</p></li>
</ul>

</div>
<p><span class="marginalizedparent"><a class="marginalized">4</a></span>
<em>Mandates</em>:</p>
<ul>
<li><p><span class="marginalizedparent"><a class="marginalized">(4.1)</a></span> If
<span class="rm" style="color: #bf0303"><del><span><code>In</code></span></del></span><code>OutMat</code>
has <code>layout_blas_packed</code> layout, then the layout’s
<code>Triangle</code> template argument has the same type as the
function’s <code>Triangle</code> template argument;</p></li>
<li><p><span class="marginalizedparent"><a class="marginalized">(4.2)</a></span> If
the function has an <code>InMat</code> template parameter and
<code>InMat</code> has <code>layout_blas_packed</code> layout, then the
layout’s <code>Triangle</code> template argument has the same type as
the function’s <code>Triangle</code> template argument;</p></li>
<li><p><span class="marginalizedparent"><a class="marginalized">(4.3)</a></span>
<em><code>compatible-static-extents</code></em><code>&lt;decltype(A), decltype(A)&gt;(0, 1)</code>
is <code>true</code>; <span class="rm" style="color: #bf0303"><del>and</del></span></p></li>
<li><p><span class="marginalizedparent"><a class="marginalized">(4.4)</a></span>
<em><code>compatible-static-extents</code></em><code>&lt;decltype(A), decltype(x)&gt;(0, 0)</code>
is <code>true</code><span class="rm" style="color: #bf0303"><del>.</del></span><span class="add" style="color: #00AA00"><ins>; and</ins></span></p></li>
</ul>
<div class="add" style="color: #00AA00">

<ul>
<li><span class="marginalizedparent"><a class="marginalized">(4.5)</a></span>
<em><code>possibly-addable</code></em><code>&lt;decltype(A), decltype(E), decltype(A)&gt;</code>
is <code>true</code> for those overloads that take an <code>E</code>
parameter.</li>
</ul>

</div>
<p><span class="marginalizedparent"><a class="marginalized">5</a></span>
<em>Preconditions</em>:</p>
<ul>
<li><p><span class="marginalizedparent"><a class="marginalized">(5.1)</a></span>
<code>A.extent(0)</code> equals <code>A.extent(1)</code>, <span class="rm" style="color: #bf0303"><del>and</del></span></p></li>
<li><p><span class="marginalizedparent"><a class="marginalized">(5.2)</a></span>
<code>A.extent(0)</code> equals <code>x.extent(0)</code><span class="rm" style="color: #bf0303"><del>.</del></span><span class="add" style="color: #00AA00"><ins>, and</ins></span></p></li>
</ul>
<div class="add" style="color: #00AA00">

<ul>
<li><span class="marginalizedparent"><a class="marginalized">(5.3)</a></span>
<em><code>addable</code></em><code>(A, E, A)</code> is <code>true</code>
for those overloads that take an <code>E</code> parameter.</li>
</ul>

</div>
<p><span class="marginalizedparent"><a class="marginalized">6</a></span>
<em>Complexity</em>: <span class="math inline"><em>O</em>(</span>
<code>x.extent(0)</code> × <code>x.extent(0)</code> <span class="math inline">)</span>.</p>
<div class="sourceCode" id="cb44"><pre class="sourceCode default"><code class="sourceCode default"><span id="cb44-1"><a href="#cb44-1" aria-hidden="true" tabindex="-1"></a>template&lt;class Scalar, <em>in-vector</em> InVec, <em>possibly-packed</em>-<span class="rm" style="color: #bf0303"><del><em><span><code>in</code></span></em></del></span><em>out-matrix</em> <span class="rm" style="color: #bf0303"><del><span><code>In</code></span></del></span>OutMat, class Triangle&gt;</span>
<span id="cb44-2"><a href="#cb44-2" aria-hidden="true" tabindex="-1"></a>  void symmetric_matrix_rank_1_update(Scalar alpha, InVec x, <span class="rm" style="color: #bf0303"><del><span><code>In</code></span></del></span>OutMat A, Triangle t);</span>
<span id="cb44-3"><a href="#cb44-3" aria-hidden="true" tabindex="-1"></a>template&lt;class ExecutionPolicy,</span>
<span id="cb44-4"><a href="#cb44-4" aria-hidden="true" tabindex="-1"></a>         class Scalar, <em>in-vector</em> InVec, <em>possibly-packed</em>-<span class="rm" style="color: #bf0303"><del><em><span><code>in</code></span></em></del></span><em>out-matrix</em> <span class="rm" style="color: #bf0303"><del><span><code>In</code></span></del></span>OutMat, class Triangle&gt;</span>
<span id="cb44-5"><a href="#cb44-5" aria-hidden="true" tabindex="-1"></a>  void symmetric_matrix_rank_1_update(ExecutionPolicy&amp;&amp; exec,</span>
<span id="cb44-6"><a href="#cb44-6" aria-hidden="true" tabindex="-1"></a>                                      Scalar alpha, InVec x, <span class="rm" style="color: #bf0303"><del><span><code>In</code></span></del></span>OutMat A, Triangle t);</span></code></pre></div>
<p><span class="marginalizedparent"><a class="marginalized">7</a></span>
These functions perform a<span class="add" style="color: #00AA00"><ins>n
overwriting</ins></span> symmetric rank-1 update of the symmetric matrix
<code>A</code>, taking into account the <code>Triangle</code> parameter
that applies to <code>A</code> ([linalg.general]).</p>
<p><span class="marginalizedparent"><a class="marginalized">8</a></span>
<em>Effects</em>: Computes <span class="rm" style="color: #bf0303"><del>a matrix <span class="math inline"><em>A</em>′</span> such that <span class="math inline"><em>A</em>′ = <em>A</em> + <em>α</em><em>x</em><em>x</em><sup><em>T</em></sup></span></del></span><span class="add" style="color: #00AA00"><ins><span class="math inline"><em>A</em> = <em>α</em><em>x</em><em>x</em><sup><em>T</em></sup></span></ins></span>,
where the scalar <span class="math inline"><em>α</em></span> is
<code>alpha</code><span class="rm" style="color: #bf0303"><del>, and
assigns each element of <span class="math inline"><em>A</em>′</span> to
the corresponding element of <span class="math inline"><em>A</em></span></del></span>.</p>
<div class="rm" style="color: #bf0303">

<div class="sourceCode" id="cb45"><pre class="sourceCode default"><code class="sourceCode default"><span id="cb45-1"><a href="#cb45-1" aria-hidden="true" tabindex="-1"></a>template&lt;<em>in-vector</em> InVec, <em>possibly-packed-inout-matrix</em> InOutMat, class Triangle&gt;</span>
<span id="cb45-2"><a href="#cb45-2" aria-hidden="true" tabindex="-1"></a>  void symmetric_matrix_rank_1_update(InVec x, InOutMat A, Triangle t);</span>
<span id="cb45-3"><a href="#cb45-3" aria-hidden="true" tabindex="-1"></a>template&lt;class ExecutionPolicy,</span>
<span id="cb45-4"><a href="#cb45-4" aria-hidden="true" tabindex="-1"></a>         <em>in-vector</em> InVec, <em>possibly-packed-inout-matrix</em> InOutMat, class Triangle&gt;</span>
<span id="cb45-5"><a href="#cb45-5" aria-hidden="true" tabindex="-1"></a>  void symmetric_matrix_rank_1_update(ExecutionPolicy&amp;&amp; exec,</span>
<span id="cb45-6"><a href="#cb45-6" aria-hidden="true" tabindex="-1"></a>                                      InVec x, InOutMat A, Triangle t);</span></code></pre></div>
<p><span class="marginalizedparent"><a class="marginalized">9</a></span>
These functions perform a symmetric rank-1 update of the symmetric
matrix <code>A</code>, taking into account the <code>Triangle</code>
parameter that applies to <code>A</code> ([linalg.general]).</p>
<p><span class="marginalizedparent"><a class="marginalized">10</a></span>
<em>Effects</em>: Computes a matrix <span class="math inline"><em>A</em>′</span> such that <span class="math inline"><em>A</em>′ = <em>A</em> + <em>x</em><em>x</em><sup><em>T</em></sup></span>
and assigns each element of <span class="math inline"><em>A</em>′</span>
to the corresponding element of <span class="math inline"><em>A</em></span>.</p>
</div>
<div class="add" style="color: #00AA00">

<div class="sourceCode" id="cb46"><pre class="sourceCode default"><code class="sourceCode default"><span id="cb46-1"><a href="#cb46-1" aria-hidden="true" tabindex="-1"></a>template&lt;class Scalar, <em>in-vector</em> InVec, <em>in-matrix</em> InMat, <em>possibly-packed-out-matrix</em> OutMat, class Triangle&gt;</span>
<span id="cb46-2"><a href="#cb46-2" aria-hidden="true" tabindex="-1"></a>  void symmetric_matrix_rank_1_update(Scalar alpha, InVec x, InMat E, OutMat A, Triangle t);</span>
<span id="cb46-3"><a href="#cb46-3" aria-hidden="true" tabindex="-1"></a>template&lt;class ExecutionPolicy,</span>
<span id="cb46-4"><a href="#cb46-4" aria-hidden="true" tabindex="-1"></a>         class Scalar, <em>in-vector</em> InVec, <em>in-matrix</em> InMat, <em>possibly-packed-out-matrix</em> OutMat, class Triangle&gt;</span>
<span id="cb46-5"><a href="#cb46-5" aria-hidden="true" tabindex="-1"></a>  void symmetric_matrix_rank_1_update(ExecutionPolicy&amp;&amp; exec,</span>
<span id="cb46-6"><a href="#cb46-6" aria-hidden="true" tabindex="-1"></a>                                      Scalar alpha, InVec x, InMat E, OutMat A, Triangle t);</span></code></pre></div>
<p><span class="marginalizedparent"><a class="marginalized">9</a></span>
These functions perform an updating symmetric rank-1 update of the
symmetric matrix <code>A</code> using the symmetric matrix
<code>E</code>, taking into account the <code>Triangle</code> parameter
that applies to <code>A</code> and <code>E</code>
([linalg.general]).</p>
<p><span class="marginalizedparent"><a class="marginalized">10</a></span>
<em>Effects</em>: Computes <span class="math inline"><em>A</em> = <em>E</em> + <em>α</em><em>x</em><em>x</em><sup><em>T</em></sup></span>,
where the scalar <span class="math inline"><em>α</em></span> is
<code>alpha</code>.</p>
</div>
<div class="sourceCode" id="cb47"><pre class="sourceCode default"><code class="sourceCode default"><span id="cb47-1"><a href="#cb47-1" aria-hidden="true" tabindex="-1"></a>template&lt;class Scalar, <em>in-vector</em> InVec, <em>possibly-packed</em>-<span class="rm" style="color: #bf0303"><del><em><span><code>in</code></span></em></del></span><em>out-matrix</em> <span class="rm" style="color: #bf0303"><del><span><code>In</code></span></del></span>OutMat, class Triangle&gt;</span>
<span id="cb47-2"><a href="#cb47-2" aria-hidden="true" tabindex="-1"></a>  void hermitian_matrix_rank_1_update(Scalar alpha, InVec x, <span class="rm" style="color: #bf0303"><del><span><code>In</code></span></del></span>OutMat A, Triangle t);</span>
<span id="cb47-3"><a href="#cb47-3" aria-hidden="true" tabindex="-1"></a>template&lt;class ExecutionPolicy,</span>
<span id="cb47-4"><a href="#cb47-4" aria-hidden="true" tabindex="-1"></a>         class Scalar, <em>in-vector</em> InVec, <em>possibly-packed</em>-<span class="rm" style="color: #bf0303"><del><em><span><code>in</code></span></em></del></span><em>out-matrix</em> <span class="rm" style="color: #bf0303"><del><span><code>In</code></span></del></span>OutMat, class Triangle&gt;</span>
<span id="cb47-5"><a href="#cb47-5" aria-hidden="true" tabindex="-1"></a>  void hermitian_matrix_rank_1_update(ExecutionPolicy&amp;&amp; exec,</span>
<span id="cb47-6"><a href="#cb47-6" aria-hidden="true" tabindex="-1"></a>                                      Scalar alpha, InVec x, <span class="rm" style="color: #bf0303"><del><span><code>In</code></span></del></span>OutMat A, Triangle t);</span></code></pre></div>
<p><span class="marginalizedparent"><a class="marginalized">11</a></span> These
functions perform an overwriting Hermitian rank-1 update of the
Hermitian matrix <code>A</code>, taking into account the
<code>Triangle</code> parameter that applies to <code>A</code>
([linalg.general]).</p>
<p><span class="marginalizedparent"><a class="marginalized">12</a></span>
<em>Effects</em>: Computes <span class="rm" style="color: #bf0303"><del>a matrix <span class="math inline"><em>A</em>′</span> such that <span class="math inline"><em>A</em>′ = <em>A</em> + <em>α</em><em>x</em><em>x</em><sup><em>H</em></sup></span></del></span><span class="add" style="color: #00AA00"><ins><span class="math inline"><em>A</em> = <em>α</em><em>x</em><em>x</em><sup><em>H</em></sup></span></ins></span>,
where the scalar <span class="math inline"><em>α</em></span> is <span class="rm" style="color: #bf0303"><del><span><code>alpha</code></span></del></span><span class="add" style="color: #00AA00"><ins><em><span><code>real-if-needed</code></span></em>(alpha)</ins></span><span class="rm" style="color: #bf0303"><del>, and assigns each element of
<span class="math inline"><em>A</em>′</span> to the corresponding
element of <span class="math inline"><em>A</em></span></del></span>.</p>
<div class="rm" style="color: #bf0303">

<div class="sourceCode" id="cb48"><pre class="sourceCode default"><code class="sourceCode default"><span id="cb48-1"><a href="#cb48-1" aria-hidden="true" tabindex="-1"></a>template&lt;<em>in-vector</em> InVec, <em>possibly-packed-inout-matrix</em> InOutMat, class Triangle&gt;</span>
<span id="cb48-2"><a href="#cb48-2" aria-hidden="true" tabindex="-1"></a>  void hermitian_matrix_rank_1_update(InVec x, InOutMat A, Triangle t);</span>
<span id="cb48-3"><a href="#cb48-3" aria-hidden="true" tabindex="-1"></a>template&lt;class ExecutionPolicy,</span>
<span id="cb48-4"><a href="#cb48-4" aria-hidden="true" tabindex="-1"></a>         <em>in-vector</em> InVec, <em>possibly-packed-inout-matrix</em> InOutMat, class Triangle&gt;</span>
<span id="cb48-5"><a href="#cb48-5" aria-hidden="true" tabindex="-1"></a>  void hermitian_matrix_rank_1_update(ExecutionPolicy&amp;&amp; exec,</span>
<span id="cb48-6"><a href="#cb48-6" aria-hidden="true" tabindex="-1"></a>                                      InVec x, InOutMat A, Triangle t);</span></code></pre></div>
<p><span class="marginalizedparent"><a class="marginalized">13</a></span> These
functions perform a Hermitian rank-1 update of the Hermitian matrix
<code>A</code>, taking into account the <code>Triangle</code> parameter
that applies to <code>A</code> ([linalg.general]).</p>
<p><span class="marginalizedparent"><a class="marginalized">14</a></span>
<em>Effects</em>: Computes a matrix <span class="math inline"><em>A</em>′</span> such that <span class="math inline"><em>A</em>′ = <em>A</em> + <em>x</em><em>x</em><sup><em>H</em></sup></span>
and assigns each element of <span class="math inline"><em>A</em>′</span>
to the corresponding element of <span class="math inline"><em>A</em></span>.</p>
</div>
<div class="add" style="color: #00AA00">

<div class="sourceCode" id="cb49"><pre class="sourceCode default"><code class="sourceCode default"><span id="cb49-1"><a href="#cb49-1" aria-hidden="true" tabindex="-1"></a>template&lt;class Scalar, <em>in-vector</em> InVec, <em>in-matrix</em> InMat, <em>possibly-packed-out-matrix</em> OutMat, class Triangle&gt;</span>
<span id="cb49-2"><a href="#cb49-2" aria-hidden="true" tabindex="-1"></a>  void hermitian_matrix_rank_1_update(Scalar alpha, InVec x, InMat E, OutMat A, Triangle t);</span>
<span id="cb49-3"><a href="#cb49-3" aria-hidden="true" tabindex="-1"></a>template&lt;class ExecutionPolicy,</span>
<span id="cb49-4"><a href="#cb49-4" aria-hidden="true" tabindex="-1"></a>         class Scalar, <em>in-vector</em> InVec, <em>in-matrix</em> InMat, <em>possibly-packed-out-matrix</em> OutMat, class Triangle&gt;</span>
<span id="cb49-5"><a href="#cb49-5" aria-hidden="true" tabindex="-1"></a>  void hermitian_matrix_rank_1_update(ExecutionPolicy&amp;&amp; exec,</span>
<span id="cb49-6"><a href="#cb49-6" aria-hidden="true" tabindex="-1"></a>                                      Scalar alpha, InVec x, InMat E, OutMat A, Triangle t);</span></code></pre></div>
<p><span class="marginalizedparent"><a class="marginalized">15</a></span> These
functions perform an updating Hermitian rank-1 update of the Hermitian
matrix <code>A</code> using the Hermitian matrix <code>E</code>, taking
into account the <code>Triangle</code> parameter that applies to
<code>A</code> and <code>E</code> ([linalg.general]).</p>
<p><span class="marginalizedparent"><a class="marginalized">16</a></span>
<em>Effects</em>: Computes <span class="math inline"><em>A</em> = <em>E</em> + <em>α</em><em>x</em><em>x</em><sup><em>H</em></sup></span>,
where the scalar <span class="math inline"><em>α</em></span> is
<em><code>real-if-needed</code></em><code>(alpha)</code>.</p>
</div>
<h2 data-number="8.9" id="specification-of-symmetric-and-hermitian-rank-2-update-functions"><span class="header-section-number">8.9</span> Specification of symmetric and
Hermitian rank-2 update functions<a href="#specification-of-symmetric-and-hermitian-rank-2-update-functions" class="self-link"></a></h2>
<blockquote>
<p>Change [linalg.algs.blas2.rank2] as follows.</p>
</blockquote>
<p><span class="marginalizedparent"><a class="marginalized">1</a></span>
<i>[Note:</i> These functions correspond to the BLAS functions
<code>xSYR2</code>, <code>xSPR2</code>, <code>xHER2</code>, and
<code>xHPR2</code> [bib]. <i>– end note]</i></p>
<p><span class="marginalizedparent"><a class="marginalized">2</a></span>
The following elements apply to all functions in
[linalg.algs.blas2.rank2].</p>
<div class="add" style="color: #00AA00">
<p><span class="marginalizedparent"><a class="marginalized">3</a></span>
For any function <code>F</code> in this section that takes a parameter
named <code>t</code>, an <code>InMat</code> template parameter, and a
function parameter <code>InMat E</code>, <code>t</code> applies to
accesses done through the parameter <code>E</code>. <code>F</code> will
only access the triangle of <code>E</code> specified by <code>t</code>.
For accesses of diagonal elements <code>E[i, i]</code>, <code>F</code>
will use the value
<em><code>real-if-needed</code></em><code>(E[i, i])</code> if the name
of <code>F</code> starts with <code>hermitian</code>. For accesses
<code>E[i, j]</code> outside the triangle specified by <code>t</code>,
<code>F</code> will use the value</p>
<ul>
<li><p><span class="marginalizedparent"><a class="marginalized">(3.1)</a></span>
<em><code>conj-if-needed</code></em><code>(E[j, i])</code> if the name
of <code>F</code> starts with <code>hermitian</code>, or</p></li>
<li><p><span class="marginalizedparent"><a class="marginalized">(3.2)</a></span>
<code>E[j, i]</code> if the name of <code>F</code> starts with
<code>symmetric</code>.</p></li>
</ul>

</div>
<p><span class="marginalizedparent"><a class="marginalized">4</a></span>
<em>Mandates</em>:</p>
<ul>
<li><span class="marginalizedparent"><a class="marginalized">(4.1)</a></span> If
<span class="rm" style="color: #bf0303"><del><span><code>In</code></span></del></span><code>OutMat</code>
has <code>layout_blas_packed</code> layout, then the layout’s
<code>Triangle</code> template argument has the same type as the
function’s <code>Triangle</code> template argument;</li>
</ul>
<div class="add" style="color: #00AA00">

<ul>
<li><span class="marginalizedparent"><a class="marginalized">(4.2)</a></span> If
the function has an <code>InMat</code> template parameter and
<code>InMat</code> has <code>layout_blas_packed</code> layout, then the
layout’s <code>Triangle</code> template argument has the same type as
the function’s <code>Triangle</code> template argument;</li>
</ul>

</div>
<ul>
<li><p><span class="marginalizedparent"><a class="marginalized">(4.3)</a></span>
<em><code>compatible-static-extents</code></em><code>&lt;decltype(A), decltype(A)&gt;(0, 1)</code>
is <code>true</code>;</p></li>
<li><p><span class="marginalizedparent"><a class="marginalized">(4.4)</a></span>
<em><code>possibly-multipliable</code></em><code>&lt;decltype(A), decltype(x), decltype(y)&gt;()</code>
is <code>true</code><span class="rm" style="color: #bf0303"><del>.</del></span><span class="add" style="color: #00AA00"><ins>; and</ins></span></p></li>
</ul>
<div class="add" style="color: #00AA00">

<ul>
<li><span class="marginalizedparent"><a class="marginalized">(4.5)</a></span>
<em><code>possibly-addable</code></em><code>&lt;decltype(A), decltype(E), decltype(A)&gt;</code>
is <code>true</code> for those overloads that take an <code>E</code>
parameter.</li>
</ul>

</div>
<p><span class="marginalizedparent"><a class="marginalized">5</a></span>
<em>Preconditions</em>:</p>
<ul>
<li><p><span class="marginalizedparent"><a class="marginalized">(5.1)</a></span>
<code>A.extent(0)</code> equals <code>A.extent(1)</code>, <span class="rm" style="color: #bf0303"><del>and</del></span></p></li>
<li><p><span class="marginalizedparent"><a class="marginalized">(5.2)</a></span>
<em><code>multipliable</code></em><code>(A, x, y)</code> is
<code>true</code><span class="rm" style="color: #bf0303"><del>.</del></span><span class="add" style="color: #00AA00"><ins>, and</ins></span></p></li>
</ul>
<div class="add" style="color: #00AA00">

<ul>
<li><span class="marginalizedparent"><a class="marginalized">(5.3)</a></span>
<em><code>addable</code></em><code>(A, E, A)</code> is <code>true</code>
for those overloads that take an <code>E</code> parameter.</li>
</ul>

</div>
<p><span class="marginalizedparent"><a class="marginalized">6</a></span>
<em>Complexity</em>: <span class="math inline"><em>O</em>(</span>
<code>x.extent(0)</code> × <code>y.extent(0)</code> <span class="math inline">)</span>.</p>
<div class="sourceCode" id="cb50"><pre class="sourceCode default"><code class="sourceCode default"><span id="cb50-1"><a href="#cb50-1" aria-hidden="true" tabindex="-1"></a>template&lt;<em>in-vector</em> InVec1, <em>in-vector</em> InVec2,</span>
<span id="cb50-2"><a href="#cb50-2" aria-hidden="true" tabindex="-1"></a>         <em>possibly-packed</em>-<span class="rm" style="color: #bf0303"><del><em><span><code>in</code></span></em></del></span><em>out-matrix</em> <span class="rm" style="color: #bf0303"><del><span><code>In</code></span></del></span>OutMat, class Triangle&gt;</span>
<span id="cb50-3"><a href="#cb50-3" aria-hidden="true" tabindex="-1"></a>  void symmetric_matrix_rank_2_update(InVec1 x, InVec2 y, <span class="rm" style="color: #bf0303"><del><span><code>In</code></span></del></span>OutMat A, Triangle t);</span>
<span id="cb50-4"><a href="#cb50-4" aria-hidden="true" tabindex="-1"></a>template&lt;class ExecutionPolicy, <em>in-vector</em> InVec1, <em>in-vector</em> InVec2,</span>
<span id="cb50-5"><a href="#cb50-5" aria-hidden="true" tabindex="-1"></a>         <em>possibly-packed</em>-<span class="rm" style="color: #bf0303"><del><em><span><code>in</code></span></em></del></span><em>out-matrix</em> <span class="rm" style="color: #bf0303"><del><span><code>In</code></span></del></span>OutMat, class Triangle&gt;</span>
<span id="cb50-6"><a href="#cb50-6" aria-hidden="true" tabindex="-1"></a>  void symmetric_matrix_rank_2_update(ExecutionPolicy&amp;&amp; exec,</span>
<span id="cb50-7"><a href="#cb50-7" aria-hidden="true" tabindex="-1"></a>                                      InVec1 x, InVec2 y, <span class="rm" style="color: #bf0303"><del><span><code>In</code></span></del></span>OutMat A, Triangle t);</span></code></pre></div>
<p><span class="marginalizedparent"><a class="marginalized">7</a></span>
These functions perform a<span class="add" style="color: #00AA00"><ins>n
overwriting</ins></span> symmetric rank-2 update of the symmetric matrix
<code>A</code>, taking into account the <code>Triangle</code> parameter
that applies to <code>A</code> ([linalg.general]).</p>
<p><span class="marginalizedparent"><a class="marginalized">8</a></span>
Effects: Computes <span class="rm" style="color: #bf0303"><del><span class="math inline"><em>A</em>′</span> such that <span class="math inline"><em>A</em>′ = <em>A</em> + <em>x</em><em>y</em><sup><em>T</em></sup> + <em>y</em><em>x</em><sup><em>T</em></sup></span>
and assigns each element of <span class="math inline"><em>A</em>′</span>
to the corresponding element of <span class="math inline"><em>A</em></span></del></span><span class="add" style="color: #00AA00"><ins><span class="math inline"><em>A</em> = <em>x</em><em>y</em><sup><em>T</em></sup> + <em>y</em><em>x</em><sup><em>T</em></sup></span></ins></span>.</p>
<div class="add" style="color: #00AA00">

<div class="sourceCode" id="cb51"><pre class="sourceCode default"><code class="sourceCode default"><span id="cb51-1"><a href="#cb51-1" aria-hidden="true" tabindex="-1"></a>template&lt;<em>in-vector</em> InVec1, <em>in-vector</em> InVec2,</span>
<span id="cb51-2"><a href="#cb51-2" aria-hidden="true" tabindex="-1"></a>         <em>in-matrix</em> InMat,</span>
<span id="cb51-3"><a href="#cb51-3" aria-hidden="true" tabindex="-1"></a>         <em>possibly-packed-out-matrix</em> OutMat, class Triangle&gt;</span>
<span id="cb51-4"><a href="#cb51-4" aria-hidden="true" tabindex="-1"></a>  void symmetric_matrix_rank_2_update(InVec1 x, InVec2 y, InMat E, OutMat A, Triangle t);</span>
<span id="cb51-5"><a href="#cb51-5" aria-hidden="true" tabindex="-1"></a>template&lt;class ExecutionPolicy, <em>in-vector</em> InVec1, <em>in-vector</em> InVec2,</span>
<span id="cb51-6"><a href="#cb51-6" aria-hidden="true" tabindex="-1"></a>         <em>in-matrix</em> InMat,</span>
<span id="cb51-7"><a href="#cb51-7" aria-hidden="true" tabindex="-1"></a>         <em>possibly-packed-out-matrix</em> OutMat, class Triangle&gt;</span>
<span id="cb51-8"><a href="#cb51-8" aria-hidden="true" tabindex="-1"></a>  void symmetric_matrix_rank_2_update(ExecutionPolicy&amp;&amp; exec,</span>
<span id="cb51-9"><a href="#cb51-9" aria-hidden="true" tabindex="-1"></a>                                      InVec1 x, InVec2 y, InMat E, OutMat A, Triangle t);</span></code></pre></div>
<p><span class="marginalizedparent"><a class="marginalized">9</a></span>
These functions perform an updating symmetric rank-2 update of the
symmetric matrix <code>A</code> using the symmetric matrix
<code>E</code>, taking into account the <code>Triangle</code> parameter
that applies to <code>A</code> and <code>E</code>
([linalg.general]).</p>
<p><span class="marginalizedparent"><a class="marginalized">10</a></span>
Effects: Computes <span class="math inline"><em>A</em> = <em>E</em> + <em>x</em><em>y</em><sup><em>T</em></sup> + <em>y</em><em>x</em><sup><em>T</em></sup></span>.</p>
</div>
<div class="sourceCode" id="cb52"><pre class="sourceCode default"><code class="sourceCode default"><span id="cb52-1"><a href="#cb52-1" aria-hidden="true" tabindex="-1"></a>template&lt;<em>in-vector</em> InVec1, <em>in-vector</em> InVec2,</span>
<span id="cb52-2"><a href="#cb52-2" aria-hidden="true" tabindex="-1"></a>         <em>possibly-packed</em>-<span class="rm" style="color: #bf0303"><del><em><span><code>in</code></span></em></del></span><em>out-matrix</em> <span class="rm" style="color: #bf0303"><del><span><code>In</code></span></del></span>OutMat, class Triangle&gt;</span>
<span id="cb52-3"><a href="#cb52-3" aria-hidden="true" tabindex="-1"></a>  void hermitian_matrix_rank_2_update(InVec1 x, InVec2 y, <span class="rm" style="color: #bf0303"><del><span><code>In</code></span></del></span>OutMat A, Triangle t);</span>
<span id="cb52-4"><a href="#cb52-4" aria-hidden="true" tabindex="-1"></a>template&lt;class ExecutionPolicy, <em>in-vector</em> InVec1, <em>in-vector</em> InVec2,</span>
<span id="cb52-5"><a href="#cb52-5" aria-hidden="true" tabindex="-1"></a>         <em>possibly-packed</em>-<span class="rm" style="color: #bf0303"><del><em><span><code>in</code></span></em></del></span><em>out-matrix</em> <span class="rm" style="color: #bf0303"><del><span><code>In</code></span></del></span>OutMat, class Triangle&gt;</span>
<span id="cb52-6"><a href="#cb52-6" aria-hidden="true" tabindex="-1"></a>  void hermitian_matrix_rank_2_update(ExecutionPolicy&amp;&amp; exec,</span>
<span id="cb52-7"><a href="#cb52-7" aria-hidden="true" tabindex="-1"></a>                                      InVec1 x, InVec2 y, <span class="rm" style="color: #bf0303"><del><span><code>In</code></span></del></span>OutMat A, Triangle t);</span></code></pre></div>
<p><span class="marginalizedparent"><a class="marginalized">11</a></span> These
functions perform a<span class="add" style="color: #00AA00"><ins>n
overwriting</ins></span> Hermitian rank-2 update of the Hermitian matrix
<code>A</code>, taking into account the <code>Triangle</code> parameter
that applies to <code>A</code> ([linalg.general]).</p>
<p><span class="marginalizedparent"><a class="marginalized">12</a></span>
Effects: Computes <span class="rm" style="color: #bf0303"><del><span class="math inline"><em>A</em>′</span> such that <span class="math inline"><em>A</em>′ = <em>A</em> + <em>x</em><em>y</em><sup><em>H</em></sup> + <em>y</em><em>x</em><sup><em>H</em></sup></span>
and assigns each element of <span class="math inline"><em>A</em>′</span>
to the corresponding element of <span class="math inline"><em>A</em></span></del></span><span class="add" style="color: #00AA00"><ins><span class="math inline"><em>A</em> = <em>x</em><em>y</em><sup><em>H</em></sup> + <em>y</em><em>x</em><sup><em>H</em></sup></span></ins></span>.</p>
<div class="add" style="color: #00AA00">

<div class="sourceCode" id="cb53"><pre class="sourceCode default"><code class="sourceCode default"><span id="cb53-1"><a href="#cb53-1" aria-hidden="true" tabindex="-1"></a>template&lt;<em>in-vector</em> InVec1, <em>in-vector</em> InVec2,</span>
<span id="cb53-2"><a href="#cb53-2" aria-hidden="true" tabindex="-1"></a>         <em>in-matrix</em> InMat,</span>
<span id="cb53-3"><a href="#cb53-3" aria-hidden="true" tabindex="-1"></a>         <em>possibly-packed-out-matrix</em> OutMat, class Triangle&gt;</span>
<span id="cb53-4"><a href="#cb53-4" aria-hidden="true" tabindex="-1"></a>  void hermitian_matrix_rank_2_update(InVec1 x, InVec2 y, InMat E, OutMat A, Triangle t);</span>
<span id="cb53-5"><a href="#cb53-5" aria-hidden="true" tabindex="-1"></a>template&lt;class ExecutionPolicy, <em>in-vector</em> InVec1, <em>in-vector</em> InVec2,</span>
<span id="cb53-6"><a href="#cb53-6" aria-hidden="true" tabindex="-1"></a>         <em>in-matrix</em> InMat,</span>
<span id="cb53-7"><a href="#cb53-7" aria-hidden="true" tabindex="-1"></a>         <em>possibly-packed-out-matrix</em> OutMat, class Triangle&gt;</span>
<span id="cb53-8"><a href="#cb53-8" aria-hidden="true" tabindex="-1"></a>  void hermitian_matrix_rank_2_update(ExecutionPolicy&amp;&amp; exec,</span>
<span id="cb53-9"><a href="#cb53-9" aria-hidden="true" tabindex="-1"></a>                                      InVec1 x, InVec2 y, InMat E, OutMat A, Triangle t);</span></code></pre></div>
<p><span class="marginalizedparent"><a class="marginalized">13</a></span> These
functions perform an updating Hermitian rank-2 update of the Hermitian
matrix <code>A</code> using the Hermitian matrix <code>E</code>, taking
into account the <code>Triangle</code> parameter that applies to
<code>A</code> and <code>E</code> ([linalg.general]).</p>
<p><span class="marginalizedparent"><a class="marginalized">14</a></span>
Effects: Computes <span class="math inline"><em>A</em> = <em>E</em> + <em>x</em><em>y</em><sup><em>H</em></sup> + <em>y</em><em>x</em><sup><em>H</em></sup></span>.</p>
</div>
<h2 data-number="8.10" id="specification-of-rank-k-update-functions"><span class="header-section-number">8.10</span> Specification of rank-k update
functions<a href="#specification-of-rank-k-update-functions" class="self-link"></a></h2>
<blockquote>
<p>Change [linalg.algs.blas3.rankk] as follows.</p>
<p><i>[Editorial Note:</i> The changes proposed here are rebased atop
the changes proposed in <a href="https://cplusplus.github.io/LWG/lwg-active.html#4137">LWG4137</a>,
“Fix Mandates, Preconditions, and Complexity elements of [linalg]
algorithms.” <i>– end note]</i></p>
</blockquote>
<p><i>[Note:</i> These functions correspond to the BLAS functions
<code>xSYRK</code> and <code>xHERK</code>. <i>– end note]</i></p>
<p><span class="marginalizedparent"><a class="marginalized">1</a></span>
The following elements apply to all functions in
[linalg.algs.blas3.rankk].</p>
<div class="add" style="color: #00AA00">
<p><span class="marginalizedparent"><a class="marginalized">2</a></span>
For any function <code>F</code> in this section that takes a parameter
named <code>t</code>, an <code>InMat2</code> template parameter, and a
function parameter <code>InMat2 E</code>, <code>t</code> applies to
accesses done through the parameter <code>E</code>. <code>F</code> will
only access the triangle of <code>E</code> specified by <code>t</code>.
For accesses of diagonal elements <code>E[i, i]</code>, <code>F</code>
will use the value
<em><code>real-if-needed</code></em><code>(E[i, i])</code> if the name
of <code>F</code> starts with <code>hermitian</code>. For accesses
<code>E[i, j]</code> outside the triangle specified by <code>t</code>,
<code>F</code> will use the value</p>
<ul>
<li><p><span class="marginalizedparent"><a class="marginalized">(2.1)</a></span>
<em><code>conj-if-needed</code></em><code>(E[j, i])</code> if the name
of <code>F</code> starts with <code>hermitian</code>, or</p></li>
<li><p><span class="marginalizedparent"><a class="marginalized">(2.2)</a></span>
<code>E[j, i]</code> if the name of <code>F</code> starts with
<code>symmetric</code>.</p></li>
</ul>

</div>
<p><span class="marginalizedparent"><a class="marginalized">3</a></span>
<em>Mandates:</em></p>
<ul>
<li><span class="marginalizedparent"><a class="marginalized">(3.1)</a></span> If
<span class="rm" style="color: #bf0303"><del><span><code>In</code></span></del></span><code>OutMat</code>
has <code>layout_blas_packed</code> layout, then the layout’s
<code>Triangle</code> template argument has the same type as the
function’s <code>Triangle</code> template argument;</li>
</ul>
<div class="add" style="color: #00AA00">

<ul>
<li><span class="marginalizedparent"><a class="marginalized">(3.2)</a></span> If
the function takes an <code>InMat2</code> template parameter and if
<code>InMat2</code> has <code>layout_blas_packed</code> layout, then the
layout’s <code>Triangle</code> template argument has the same type as
the function’s <code>Triangle</code> template argument.</li>
</ul>

</div>
<ul>
<li><span class="marginalizedparent"><a class="marginalized">(3.3)</a></span>
<span class="add" style="color: #00AA00"><ins><em><span><code>possibly-multipliable</code></span></em><span><code>&lt;decltype(A), decltype(transposed(A)), decltype(C)&gt;</code></span></ins></span>
<span class="rm" style="color: #bf0303"><del><em><span><code>compatible-static-extents</code></span></em><span><code>&lt;decltype(A), decltype(A)&gt;(0, 1)</code></span></del></span>
is <code>true</code>; <span class="add" style="color: #00AA00"><ins>and</ins></span></li>
</ul>
<div class="rm" style="color: #bf0303">

<ul>
<li><p><span class="marginalizedparent"><a class="marginalized">(3.3)</a></span>
<em><code>compatible-static-extents</code></em><code>&lt;decltype(C), decltype(C)&gt;(0, 1)</code>
is <code>true</code>; and</p></li>
<li><p><span class="marginalizedparent"><a class="marginalized">(3.4)</a></span>
<em><code>compatible-static-extents</code></em><code>&lt;decltype(A), decltype(C)&gt;(0, 0)</code>
is <code>true</code>.</p></li>
</ul>

</div>
<div class="add" style="color: #00AA00">

<ul>
<li><span class="marginalizedparent"><a class="marginalized">(3.4)</a></span>
<em><code>possibly-addable</code></em><code>&lt;decltype(C), decltype(E), decltype(C)&gt;</code>
is <code>true</code> for those overloads that take an <code>E</code>
parameter.</li>
</ul>

</div>
<p><span class="marginalizedparent"><a class="marginalized">4</a></span>
<em>Preconditions:</em></p>
<div class="rm" style="color: #bf0303">

<ul>
<li><p><span class="marginalizedparent"><a class="marginalized">(4.1)</a></span>
<code>A.extent(0)</code> equals <code>A.extent(1)</code>,</p></li>
<li><p><span class="marginalizedparent"><a class="marginalized">(4.2)</a></span>
<code>C.extent(0)</code> equals <code>C.extent(1)</code>, and</p></li>
<li><p><span class="marginalizedparent"><a class="marginalized">(4.3)</a></span>
<code>A.extent(0)</code> equals <code>C.extent(0)</code>.</p></li>
</ul>

</div>
<div class="add" style="color: #00AA00">

<ul>
<li><p><span class="marginalizedparent"><a class="marginalized">(4.1)</a></span>
<em><code>multipliable</code></em><code>(A, transposed(A), C)</code> is
<code>true</code>; and <i>[Note:</i> This implies that <code>C</code> is
square <i>– end note]</i></p></li>
<li><p><span class="marginalizedparent"><a class="marginalized">(4.2)</a></span>
<em><code>addable</code></em><code>(C, E, C)</code> is <code>true</code>
for those overloads that take an <code>E</code> parameter.</p></li>
</ul>

</div>
<p><span class="marginalizedparent"><a class="marginalized">5</a></span>
<em>Complexity:</em> <span class="math inline"><em>O</em>(</span>
<code>A.extent(0)</code> <span class="math inline">⋅</span>
<code>A.extent(1)</code> <span class="math inline">⋅</span> <span class="add" style="color: #00AA00"><ins><span><code>A</code></span></ins></span><span class="rm" style="color: #bf0303"><del><span><code>C</code></span></del></span><code>.extent(0)</code>
<span class="math inline">)</span>.</p>
<div class="add" style="color: #00AA00">
<p><span class="marginalizedparent"><a class="marginalized">6</a></span>
<em>Remarks:</em> <code>C</code> may alias <code>E</code> for those
overloads that take an <code>E</code> parameter.</p>
</div>
<div class="sourceCode" id="cb54"><pre class="sourceCode default"><code class="sourceCode default"><span id="cb54-1"><a href="#cb54-1" aria-hidden="true" tabindex="-1"></a>template&lt;class Scalar,</span>
<span id="cb54-2"><a href="#cb54-2" aria-hidden="true" tabindex="-1"></a>         <em>in-matrix</em> InMat,</span>
<span id="cb54-3"><a href="#cb54-3" aria-hidden="true" tabindex="-1"></a>         <em>possibly-packed</em>-<span class="rm" style="color: #bf0303"><del><em><span><code>in</code></span></em></del></span><em>out-matrix</em> <span class="rm" style="color: #bf0303"><del><span><code>In</code></span></del></span>InOutMat,</span>
<span id="cb54-4"><a href="#cb54-4" aria-hidden="true" tabindex="-1"></a>         class Triangle&gt;</span>
<span id="cb54-5"><a href="#cb54-5" aria-hidden="true" tabindex="-1"></a>void symmetric_matrix_rank_k_update(</span>
<span id="cb54-6"><a href="#cb54-6" aria-hidden="true" tabindex="-1"></a>  Scalar alpha,</span>
<span id="cb54-7"><a href="#cb54-7" aria-hidden="true" tabindex="-1"></a>  InMat A,</span>
<span id="cb54-8"><a href="#cb54-8" aria-hidden="true" tabindex="-1"></a>  <span class="rm" style="color: #bf0303"><del><span><code>In</code></span></del></span>OutMat C,</span>
<span id="cb54-9"><a href="#cb54-9" aria-hidden="true" tabindex="-1"></a>  Triangle t);</span>
<span id="cb54-10"><a href="#cb54-10" aria-hidden="true" tabindex="-1"></a>template&lt;class ExecutionPolicy,</span>
<span id="cb54-11"><a href="#cb54-11" aria-hidden="true" tabindex="-1"></a>         class Scalar,</span>
<span id="cb54-12"><a href="#cb54-12" aria-hidden="true" tabindex="-1"></a>         <em>in-matrix</em> InMat,</span>
<span id="cb54-13"><a href="#cb54-13" aria-hidden="true" tabindex="-1"></a>         <em>possibly-packed</em>-<span class="rm" style="color: #bf0303"><del><em><span><code>in</code></span></em></del></span><em>out-matrix</em> <span class="rm" style="color: #bf0303"><del><span><code>In</code></span></del></span>OutMat,</span>
<span id="cb54-14"><a href="#cb54-14" aria-hidden="true" tabindex="-1"></a>         class Triangle&gt;</span>
<span id="cb54-15"><a href="#cb54-15" aria-hidden="true" tabindex="-1"></a>void symmetric_matrix_rank_k_update(</span>
<span id="cb54-16"><a href="#cb54-16" aria-hidden="true" tabindex="-1"></a>  ExecutionPolicy&amp;&amp; exec,</span>
<span id="cb54-17"><a href="#cb54-17" aria-hidden="true" tabindex="-1"></a>  Scalar alpha,</span>
<span id="cb54-18"><a href="#cb54-18" aria-hidden="true" tabindex="-1"></a>  InMat A,</span>
<span id="cb54-19"><a href="#cb54-19" aria-hidden="true" tabindex="-1"></a>  <span class="rm" style="color: #bf0303"><del><span><code>In</code></span></del></span>OutMat C,</span>
<span id="cb54-20"><a href="#cb54-20" aria-hidden="true" tabindex="-1"></a>  Triangle t);</span></code></pre></div>
<p><span class="marginalizedparent"><a class="marginalized">7</a></span>
<em>Effects:</em> Computes <span class="rm" style="color: #bf0303"><del>a matrix <span class="math inline"><em>C</em>′</span> such that <span class="math inline"><em>C</em>′ = <em>C</em> + <em>α</em><em>A</em><em>A</em><sup><em>T</em></sup></span></del></span><span class="add" style="color: #00AA00"><ins><span class="math inline"><em>C</em> = <em>α</em><em>A</em><em>A</em><sup><em>T</em></sup></span></ins></span>,
where the scalar <span class="math inline"><em>α</em></span> is
<code>alpha</code><span class="rm" style="color: #bf0303"><del>, and
assigns each element of <span class="math inline"><em>C</em>′</span> to
the corresponding element of <span class="math inline"><em>C</em></span></del></span><span class="add" style="color: #00AA00"><ins>.</ins></span>.</p>
<div class="rm" style="color: #bf0303">

<div class="sourceCode" id="cb55"><pre class="sourceCode default"><code class="sourceCode default"><span id="cb55-1"><a href="#cb55-1" aria-hidden="true" tabindex="-1"></a>template&lt;<em>in-matrix</em> InMat,</span>
<span id="cb55-2"><a href="#cb55-2" aria-hidden="true" tabindex="-1"></a>         <em>possibly-packed-inout-matrix</em> InOutMat,</span>
<span id="cb55-3"><a href="#cb55-3" aria-hidden="true" tabindex="-1"></a>         class Triangle&gt;</span>
<span id="cb55-4"><a href="#cb55-4" aria-hidden="true" tabindex="-1"></a>void symmetric_matrix_rank_k_update(</span>
<span id="cb55-5"><a href="#cb55-5" aria-hidden="true" tabindex="-1"></a>  InMat A,</span>
<span id="cb55-6"><a href="#cb55-6" aria-hidden="true" tabindex="-1"></a>  InOutMat C,</span>
<span id="cb55-7"><a href="#cb55-7" aria-hidden="true" tabindex="-1"></a>  Triangle t);</span>
<span id="cb55-8"><a href="#cb55-8" aria-hidden="true" tabindex="-1"></a>template&lt;class ExecutionPolicy,</span>
<span id="cb55-9"><a href="#cb55-9" aria-hidden="true" tabindex="-1"></a>         <em>in-matrix</em> InMat,</span>
<span id="cb55-10"><a href="#cb55-10" aria-hidden="true" tabindex="-1"></a>         <em>possibly-packed-inout-matrix</em> InOutMat,</span>
<span id="cb55-11"><a href="#cb55-11" aria-hidden="true" tabindex="-1"></a>         class Triangle&gt;</span>
<span id="cb55-12"><a href="#cb55-12" aria-hidden="true" tabindex="-1"></a>void symmetric_matrix_rank_k_update(</span>
<span id="cb55-13"><a href="#cb55-13" aria-hidden="true" tabindex="-1"></a>  ExecutionPolicy&amp;&amp; exec,</span>
<span id="cb55-14"><a href="#cb55-14" aria-hidden="true" tabindex="-1"></a>  InMat A,</span>
<span id="cb55-15"><a href="#cb55-15" aria-hidden="true" tabindex="-1"></a>  InOutMat C,</span>
<span id="cb55-16"><a href="#cb55-16" aria-hidden="true" tabindex="-1"></a>  Triangle t);</span></code></pre></div>
<p><span class="marginalizedparent"><a class="marginalized">7</a></span>
<em>Effects:</em> Computes a matrix <span class="math inline"><em>C</em>′</span> such that <span class="math inline"><em>C</em>′ = <em>C</em> + <em>A</em><em>A</em><sup><em>T</em></sup></span>,
and assigns each element of <span class="math inline"><em>C</em>′</span>
to the corresponding element of <span class="math inline"><em>C</em></span>.</p>
</div>
<div class="sourceCode" id="cb56"><pre class="sourceCode default"><code class="sourceCode default"><span id="cb56-1"><a href="#cb56-1" aria-hidden="true" tabindex="-1"></a>template&lt;class Scalar,</span>
<span id="cb56-2"><a href="#cb56-2" aria-hidden="true" tabindex="-1"></a>         <em>in-matrix</em> InMat,</span>
<span id="cb56-3"><a href="#cb56-3" aria-hidden="true" tabindex="-1"></a>         <em>possibly-packed</em>-<span class="rm" style="color: #bf0303"><del><em><span><code>in</code></span></em></del></span><em>out-matrix</em> <span class="rm" style="color: #bf0303"><del><span><code>In</code></span></del></span>InOutMat,</span>
<span id="cb56-4"><a href="#cb56-4" aria-hidden="true" tabindex="-1"></a>         class Triangle&gt;</span>
<span id="cb56-5"><a href="#cb56-5" aria-hidden="true" tabindex="-1"></a>void hermitian_matrix_rank_k_update(</span>
<span id="cb56-6"><a href="#cb56-6" aria-hidden="true" tabindex="-1"></a>  Scalar alpha,</span>
<span id="cb56-7"><a href="#cb56-7" aria-hidden="true" tabindex="-1"></a>  InMat A,</span>
<span id="cb56-8"><a href="#cb56-8" aria-hidden="true" tabindex="-1"></a>  <span class="rm" style="color: #bf0303"><del><span><code>In</code></span></del></span>OutMat C,</span>
<span id="cb56-9"><a href="#cb56-9" aria-hidden="true" tabindex="-1"></a>  Triangle t);</span>
<span id="cb56-10"><a href="#cb56-10" aria-hidden="true" tabindex="-1"></a>template&lt;class ExecutionPolicy,</span>
<span id="cb56-11"><a href="#cb56-11" aria-hidden="true" tabindex="-1"></a>         class Scalar,</span>
<span id="cb56-12"><a href="#cb56-12" aria-hidden="true" tabindex="-1"></a>         <em>in-matrix</em> InMat,</span>
<span id="cb56-13"><a href="#cb56-13" aria-hidden="true" tabindex="-1"></a>         <em>possibly-packed</em>-<span class="rm" style="color: #bf0303"><del><em><span><code>in</code></span></em></del></span><em>out-matrix</em> <span class="rm" style="color: #bf0303"><del><span><code>In</code></span></del></span>OutMat,</span>
<span id="cb56-14"><a href="#cb56-14" aria-hidden="true" tabindex="-1"></a>         class Triangle&gt;</span>
<span id="cb56-15"><a href="#cb56-15" aria-hidden="true" tabindex="-1"></a>void hermitian_matrix_rank_k_update(</span>
<span id="cb56-16"><a href="#cb56-16" aria-hidden="true" tabindex="-1"></a>  ExecutionPolicy&amp;&amp; exec,</span>
<span id="cb56-17"><a href="#cb56-17" aria-hidden="true" tabindex="-1"></a>  Scalar alpha,</span>
<span id="cb56-18"><a href="#cb56-18" aria-hidden="true" tabindex="-1"></a>  InMat A,</span>
<span id="cb56-19"><a href="#cb56-19" aria-hidden="true" tabindex="-1"></a>  <span class="rm" style="color: #bf0303"><del><span><code>In</code></span></del></span>OutMat C,</span>
<span id="cb56-20"><a href="#cb56-20" aria-hidden="true" tabindex="-1"></a>  Triangle t);</span></code></pre></div>
<p><span class="marginalizedparent"><a class="marginalized">8</a></span>
<em>Effects:</em> Computes <span class="rm" style="color: #bf0303"><del>a matrix <span class="math inline"><em>C</em>′</span> such that <span class="math inline"><em>C</em>′ = <em>C</em> + <em>α</em><em>A</em><em>A</em><sup><em>H</em></sup></span></del></span><span class="add" style="color: #00AA00"><ins><span class="math inline"><em>C</em> = <em>α</em><em>A</em><em>A</em><sup><em>H</em></sup></span></ins></span>,
where the scalar <span class="math inline"><em>α</em></span> is <span class="rm" style="color: #bf0303"><del><span><code>alpha</code></span></del></span><span class="add" style="color: #00AA00"><ins><em><span><code>real-if-needed</code></span></em><span><code>(alpha)</code></span></ins></span><span class="rm" style="color: #bf0303"><del>, and assigns each element of
<span class="math inline"><em>C</em>′</span> to the corresponding
element of <span class="math inline"><em>C</em></span></del></span><span class="add" style="color: #00AA00"><ins>.</ins></span>.</p>
<div class="rm" style="color: #bf0303">

<div class="sourceCode" id="cb57"><pre class="sourceCode default"><code class="sourceCode default"><span id="cb57-1"><a href="#cb57-1" aria-hidden="true" tabindex="-1"></a>template&lt;<em>in-matrix</em> InMat,</span>
<span id="cb57-2"><a href="#cb57-2" aria-hidden="true" tabindex="-1"></a>         <em>possibly-packed-inout-matrix</em> InOutMat,</span>
<span id="cb57-3"><a href="#cb57-3" aria-hidden="true" tabindex="-1"></a>         class Triangle&gt;</span>
<span id="cb57-4"><a href="#cb57-4" aria-hidden="true" tabindex="-1"></a>void hermitian_matrix_rank_k_update(</span>
<span id="cb57-5"><a href="#cb57-5" aria-hidden="true" tabindex="-1"></a>  InMat A,</span>
<span id="cb57-6"><a href="#cb57-6" aria-hidden="true" tabindex="-1"></a>  InOutMat C,</span>
<span id="cb57-7"><a href="#cb57-7" aria-hidden="true" tabindex="-1"></a>  Triangle t);</span>
<span id="cb57-8"><a href="#cb57-8" aria-hidden="true" tabindex="-1"></a>template&lt;class ExecutionPolicy,</span>
<span id="cb57-9"><a href="#cb57-9" aria-hidden="true" tabindex="-1"></a>         <em>in-matrix</em> InMat,</span>
<span id="cb57-10"><a href="#cb57-10" aria-hidden="true" tabindex="-1"></a>         <em>possibly-packed-inout-matrix</em> InOutMat,</span>
<span id="cb57-11"><a href="#cb57-11" aria-hidden="true" tabindex="-1"></a>         class Triangle&gt;</span>
<span id="cb57-12"><a href="#cb57-12" aria-hidden="true" tabindex="-1"></a>void hermitian_matrix_rank_k_update(</span>
<span id="cb57-13"><a href="#cb57-13" aria-hidden="true" tabindex="-1"></a>  ExecutionPolicy&amp;&amp; exec,</span>
<span id="cb57-14"><a href="#cb57-14" aria-hidden="true" tabindex="-1"></a>  InMat A,</span>
<span id="cb57-15"><a href="#cb57-15" aria-hidden="true" tabindex="-1"></a>  InOutMat C,</span>
<span id="cb57-16"><a href="#cb57-16" aria-hidden="true" tabindex="-1"></a>  Triangle t);</span></code></pre></div>
<p><span class="marginalizedparent"><a class="marginalized">9</a></span>
<em>Effects:</em> Computes a matrix <span class="math inline"><em>C</em>′</span> such that <span class="math inline"><em>C</em>′ = <em>C</em> + <em>A</em><em>A</em><sup><em>H</em></sup></span>,
and assigns each element of <span class="math inline"><em>C</em>′</span>
to the corresponding element of <span class="math inline"><em>C</em></span>.</p>
</div>
<div class="add" style="color: #00AA00">

<div class="sourceCode" id="cb58"><pre class="sourceCode default"><code class="sourceCode default"><span id="cb58-1"><a href="#cb58-1" aria-hidden="true" tabindex="-1"></a>template&lt;class Scalar,</span>
<span id="cb58-2"><a href="#cb58-2" aria-hidden="true" tabindex="-1"></a>         <em>in-matrix</em> InMat1,</span>
<span id="cb58-3"><a href="#cb58-3" aria-hidden="true" tabindex="-1"></a>         <em>in-matrix</em> InMat2,</span>
<span id="cb58-4"><a href="#cb58-4" aria-hidden="true" tabindex="-1"></a>         <em>possibly-packed-out-matrix</em> OutMat,</span>
<span id="cb58-5"><a href="#cb58-5" aria-hidden="true" tabindex="-1"></a>         class Triangle&gt;</span>
<span id="cb58-6"><a href="#cb58-6" aria-hidden="true" tabindex="-1"></a>void symmetric_matrix_rank_k_update(</span>
<span id="cb58-7"><a href="#cb58-7" aria-hidden="true" tabindex="-1"></a>  Scalar alpha,</span>
<span id="cb58-8"><a href="#cb58-8" aria-hidden="true" tabindex="-1"></a>  InMat1 A,</span>
<span id="cb58-9"><a href="#cb58-9" aria-hidden="true" tabindex="-1"></a>  InMat2 E,</span>
<span id="cb58-10"><a href="#cb58-10" aria-hidden="true" tabindex="-1"></a>  OutMat C,</span>
<span id="cb58-11"><a href="#cb58-11" aria-hidden="true" tabindex="-1"></a>  Triangle t);</span>
<span id="cb58-12"><a href="#cb58-12" aria-hidden="true" tabindex="-1"></a>template&lt;class ExecutionPolicy,</span>
<span id="cb58-13"><a href="#cb58-13" aria-hidden="true" tabindex="-1"></a>         class Scalar,</span>
<span id="cb58-14"><a href="#cb58-14" aria-hidden="true" tabindex="-1"></a>         <em>in-matrix</em> InMat1,</span>
<span id="cb58-15"><a href="#cb58-15" aria-hidden="true" tabindex="-1"></a>         <em>in-matrix</em> InMat2,</span>
<span id="cb58-16"><a href="#cb58-16" aria-hidden="true" tabindex="-1"></a>         <em>possibly-packed-out-matrix</em> OutMat,</span>
<span id="cb58-17"><a href="#cb58-17" aria-hidden="true" tabindex="-1"></a>         class Triangle&gt;</span>
<span id="cb58-18"><a href="#cb58-18" aria-hidden="true" tabindex="-1"></a>void symmetric_matrix_rank_k_update(</span>
<span id="cb58-19"><a href="#cb58-19" aria-hidden="true" tabindex="-1"></a>  ExecutionPolicy&amp;&amp; exec,</span>
<span id="cb58-20"><a href="#cb58-20" aria-hidden="true" tabindex="-1"></a>  Scalar alpha,</span>
<span id="cb58-21"><a href="#cb58-21" aria-hidden="true" tabindex="-1"></a>  InMat1 A,</span>
<span id="cb58-22"><a href="#cb58-22" aria-hidden="true" tabindex="-1"></a>  InMat2 E,</span>
<span id="cb58-23"><a href="#cb58-23" aria-hidden="true" tabindex="-1"></a>  OutMat C,</span>
<span id="cb58-24"><a href="#cb58-24" aria-hidden="true" tabindex="-1"></a>  Triangle t);</span></code></pre></div>
<p><span class="marginalizedparent"><a class="marginalized">9</a></span>
<em>Effects:</em> Computes <span class="math inline"><em>C</em> = <em>E</em> + <em>α</em><em>A</em><em>A</em><sup><em>T</em></sup></span>,
where the scalar <span class="math inline"><em>α</em></span> is
<code>alpha</code>.</p>
<div class="sourceCode" id="cb59"><pre class="sourceCode default"><code class="sourceCode default"><span id="cb59-1"><a href="#cb59-1" aria-hidden="true" tabindex="-1"></a>template&lt;class Scalar,</span>
<span id="cb59-2"><a href="#cb59-2" aria-hidden="true" tabindex="-1"></a>         <em>in-matrix</em> InMat1,</span>
<span id="cb59-3"><a href="#cb59-3" aria-hidden="true" tabindex="-1"></a>         <em>in-matrix</em> InMat2,</span>
<span id="cb59-4"><a href="#cb59-4" aria-hidden="true" tabindex="-1"></a>         <em>possibly-packed-out-matrix</em> OutMat,</span>
<span id="cb59-5"><a href="#cb59-5" aria-hidden="true" tabindex="-1"></a>         class Triangle&gt;</span>
<span id="cb59-6"><a href="#cb59-6" aria-hidden="true" tabindex="-1"></a>void hermitian_matrix_rank_k_update(</span>
<span id="cb59-7"><a href="#cb59-7" aria-hidden="true" tabindex="-1"></a>  Scalar alpha,</span>
<span id="cb59-8"><a href="#cb59-8" aria-hidden="true" tabindex="-1"></a>  InMat1 A,</span>
<span id="cb59-9"><a href="#cb59-9" aria-hidden="true" tabindex="-1"></a>  InMat2 E,</span>
<span id="cb59-10"><a href="#cb59-10" aria-hidden="true" tabindex="-1"></a>  OutMat C,</span>
<span id="cb59-11"><a href="#cb59-11" aria-hidden="true" tabindex="-1"></a>  Triangle t);</span>
<span id="cb59-12"><a href="#cb59-12" aria-hidden="true" tabindex="-1"></a>template&lt;class ExecutionPolicy,</span>
<span id="cb59-13"><a href="#cb59-13" aria-hidden="true" tabindex="-1"></a>         class Scalar,</span>
<span id="cb59-14"><a href="#cb59-14" aria-hidden="true" tabindex="-1"></a>         <em>in-matrix</em> InMat1,</span>
<span id="cb59-15"><a href="#cb59-15" aria-hidden="true" tabindex="-1"></a>         <em>in-matrix</em> InMat2,</span>
<span id="cb59-16"><a href="#cb59-16" aria-hidden="true" tabindex="-1"></a>         <em>possibly-packed-out-matrix</em> OutMat,</span>
<span id="cb59-17"><a href="#cb59-17" aria-hidden="true" tabindex="-1"></a>         class Triangle&gt;</span>
<span id="cb59-18"><a href="#cb59-18" aria-hidden="true" tabindex="-1"></a>void hermitian_matrix_rank_k_update(</span>
<span id="cb59-19"><a href="#cb59-19" aria-hidden="true" tabindex="-1"></a>  ExecutionPolicy&amp;&amp; exec,</span>
<span id="cb59-20"><a href="#cb59-20" aria-hidden="true" tabindex="-1"></a>  Scalar alpha,</span>
<span id="cb59-21"><a href="#cb59-21" aria-hidden="true" tabindex="-1"></a>  InMat1 A,</span>
<span id="cb59-22"><a href="#cb59-22" aria-hidden="true" tabindex="-1"></a>  InMat2 E,</span>
<span id="cb59-23"><a href="#cb59-23" aria-hidden="true" tabindex="-1"></a>  OutMat C,</span>
<span id="cb59-24"><a href="#cb59-24" aria-hidden="true" tabindex="-1"></a>  Triangle t);</span></code></pre></div>
<p><span class="marginalizedparent"><a class="marginalized">10</a></span>
<em>Effects:</em> Computes <span class="math inline"><em>C</em> = <em>E</em> + <em>α</em><em>A</em><em>A</em><sup><em>H</em></sup></span>,
where the scalar <span class="math inline"><em>α</em></span> is
<em><code>real-if-needed</code></em><code>(alpha)</code>.</p>
</div>
<h2 data-number="8.11" id="specification-of-rank-2k-update-functions"><span class="header-section-number">8.11</span> Specification of rank-2k
update functions<a href="#specification-of-rank-2k-update-functions" class="self-link"></a></h2>
<blockquote>
<p>Change [linalg.algs.blas3.rank2k] as follows.</p>
<p><i>[Editorial Note:</i> The changes proposed here are rebased atop
the changes proposed in <a href="https://cplusplus.github.io/LWG/lwg-active.html#4137">LWG4137</a>,
“Fix Mandates, Preconditions, and Complexity elements of [linalg]
algorithms.” <i>– end note]</i></p>
</blockquote>
<p><span class="marginalizedparent"><a class="marginalized">1</a></span>
<i>[Note:</i> These functions correspond to the BLAS functions
<code>xSYR2K</code> and <code>xHER2K</code>[bib]. <i>– end note]</i></p>
<p><span class="marginalizedparent"><a class="marginalized">2</a></span>
The following elements apply to all functions in
[linalg.algs.blas3.rank2k].</p>
<div class="add" style="color: #00AA00">
<p><span class="marginalizedparent"><a class="marginalized">3</a></span>
For any function <code>F</code> in this section that takes a parameter
named <code>t</code>, an <code>InMat3</code> template parameter, and a
function parameter <code>InMat3 E</code>, <code>t</code> applies to
accesses done through the parameter <code>E</code>. <code>F</code> will
only access the triangle of <code>E</code> specified by <code>t</code>.
For accesses of diagonal elements <code>E[i, i]</code>, <code>F</code>
will use the value
<em><code>real-if-needed</code></em><code>(E[i, i])</code> if the name
of <code>F</code> starts with <code>hermitian</code>. For accesses
<code>E[i, j]</code> outside the triangle specified by <code>t</code>,
<code>F</code> will use the value</p>
<ul>
<li><p><span class="marginalizedparent"><a class="marginalized">(3.1)</a></span>
<em><code>conj-if-needed</code></em><code>(E[j, i])</code> if the name
of <code>F</code> starts with <code>hermitian</code>, or</p></li>
<li><p><span class="marginalizedparent"><a class="marginalized">(3.2)</a></span>
<code>E[j, i]</code> if the name of <code>F</code> starts with
<code>symmetric</code>.</p></li>
</ul>

</div>
<p><span class="marginalizedparent"><a class="marginalized">4</a></span>
<em>Mandates:</em></p>
<ul>
<li><span class="marginalizedparent"><a class="marginalized">(4.1)</a></span> If
<span class="rm" style="color: #bf0303"><del><span><code>In</code></span></del></span><code>OutMat</code>
has <code>layout_blas_packed</code> layout, then the layout’s
<code>Triangle</code> template argument has the same type as the
function’s <code>Triangle</code> template argument;</li>
</ul>
<div class="add" style="color: #00AA00">

<ul>
<li><span class="marginalizedparent"><a class="marginalized">(4.2)</a></span> If
the function takes an <code>InMat3</code> template parameter and if
<code>InMat3</code> has <code>layout_blas_packed</code> layout, then the
layout’s <code>Triangle</code> template argument has the same type as
the function’s <code>Triangle</code> template argument;</li>
</ul>

</div>
<ul>
<li><p><span class="marginalizedparent"><a class="marginalized">(4.2)</a></span>
<span class="add" style="color: #00AA00"><ins><em><span><code>possibly-multipliable</code></span></em><span><code>&lt;decltype(A), decltype(transposed(B)), decltype(C)&gt;</code></span></ins></span>
<span class="rm" style="color: #bf0303"><del><em><span><code>possibly-addable</code></span></em><span><code>&lt;decltype(A), decltype(B), decltype(C)&gt;()</code></span></del></span>
is <code>true</code>; <span class="rm" style="color: #bf0303"><del>and</del></span></p></li>
<li><p><span class="marginalizedparent"><a class="marginalized">(4.3)</a></span>
<span class="add" style="color: #00AA00"><ins><em><span><code>possibly-multipliable</code></span></em><span><code>&lt;decltype(B), decltype(transposed(A)), decltype(C)&gt;</code></span></ins></span><span class="rm" style="color: #bf0303"><del><em><span><code>compatible-static-extents</code></span></em><span><code>&lt;decltype(A), decltype(A)&gt;(0, 1)</code></span></del></span>
is <code>true</code><span class="rm" style="color: #bf0303"><del>.</del></span><span class="add" style="color: #00AA00"><ins>; and</ins></span></p></li>
</ul>
<div class="add" style="color: #00AA00">

<ul>
<li><span class="marginalizedparent"><a class="marginalized">(4.5)</a></span>
<em><code>possibly-addable</code></em><code>&lt;decltype(C), decltype(E), decltype(C)&gt;</code>
is <code>true</code> for those overloads that take an <code>E</code>
parameter.</li>
</ul>

</div>
<blockquote>
<p><i>[Editorial Note:</i> The proposed fix for <a href="https://cplusplus.github.io/LWG/lwg-active.html#4137">LWG4137</a>,
“Fix Mandates, Preconditions, and Complexity elements of [linalg]
algorithms,” incorrectly adds <code>(0, 1)</code> after
<em><code>possibly-multipliable</code></em><code>&lt;decltype(B), decltype(transposed(A)), decltype(C)&gt;</code>
in paragraph 4.3 above (3.3 in the issue). <i>– end note]</i></p>
</blockquote>
<p><span class="marginalizedparent"><a class="marginalized">5</a></span>
<em>Preconditions:</em></p>
<ul>
<li><p><span class="marginalizedparent"><a class="marginalized">(5.1)</a></span>
<span class="add" style="color: #00AA00"><ins><em><span><code>multipliable</code></span></em><span><code>(A, transposed(B), C)</code></span></ins></span>
<span class="rm" style="color: #bf0303"><del><em><span><code>addable</code></span></em><span><code>(A, B, C)</code></span></del></span>
is <code>true</code>, and</p></li>
<li><p><span class="marginalizedparent"><a class="marginalized">(5.2)</a></span>
<span class="add" style="color: #00AA00"><ins><em><span><code>multipliable</code></span></em><span><code>(B, transposed(A), C)</code></span>
is <span><code>true</code></span></ins></span> <span class="rm" style="color: #bf0303"><del><span><code>A.extent(0)</code></span> equals
<span><code>A.extent(1)</code></span></del></span>. <span class="add" style="color: #00AA00"><ins><i>[Note:</i> This and the previous imply
that <span><code>C</code></span> is square. <i>– end
note]</i></ins></span></p></li>
</ul>
<div class="add" style="color: #00AA00">

<ul>
<li><span class="marginalizedparent"><a class="marginalized">(5.3)</a></span>
<em><code>addable</code></em><code>(C, E, C)</code> is <code>true</code>
for those overloads that take an <code>E</code> parameter.</li>
</ul>

</div>
<p><span class="marginalizedparent"><a class="marginalized">6</a></span>
<em>Complexity:</em> <span class="math inline"><em>O</em>(</span>
<code>A.extent(0)</code> <span class="math inline">⋅</span>
<code>A.extent(1)</code> <span class="math inline">⋅</span> <span class="add" style="color: #00AA00"><ins><span><code>B</code></span></ins></span><span class="rm" style="color: #bf0303"><del><span><code>C</code></span></del></span><code>.extent(0)</code>
<span class="math inline">)</span></p>
<div class="add" style="color: #00AA00">
<p><span class="marginalizedparent"><a class="marginalized">7</a></span>
<em>Remarks:</em> <code>C</code> may alias <code>E</code> for those
overloads that take an <code>E</code> parameter.</p>
</div>
<div class="sourceCode" id="cb60"><pre class="sourceCode default"><code class="sourceCode default"><span id="cb60-1"><a href="#cb60-1" aria-hidden="true" tabindex="-1"></a>template&lt;<em>in-matrix</em> InMat1,</span>
<span id="cb60-2"><a href="#cb60-2" aria-hidden="true" tabindex="-1"></a>         <em>in-matrix</em> InMat2,</span>
<span id="cb60-3"><a href="#cb60-3" aria-hidden="true" tabindex="-1"></a>         <em>possibly-packed</em>-<span class="rm" style="color: #bf0303"><del><em><span><code>in</code></span></em></del></span><em>out-matrix</em> <span class="rm" style="color: #bf0303"><del><span><code>In</code></span></del></span>OutMat,</span>
<span id="cb60-4"><a href="#cb60-4" aria-hidden="true" tabindex="-1"></a>         class Triangle&gt;</span>
<span id="cb60-5"><a href="#cb60-5" aria-hidden="true" tabindex="-1"></a>void symmetric_matrix_rank_2k_update(</span>
<span id="cb60-6"><a href="#cb60-6" aria-hidden="true" tabindex="-1"></a>  InMat1 A,</span>
<span id="cb60-7"><a href="#cb60-7" aria-hidden="true" tabindex="-1"></a>  InMat2 B,</span>
<span id="cb60-8"><a href="#cb60-8" aria-hidden="true" tabindex="-1"></a>  <span class="rm" style="color: #bf0303"><del><span><code>In</code></span></del></span>OutMat C,</span>
<span id="cb60-9"><a href="#cb60-9" aria-hidden="true" tabindex="-1"></a>  Triangle t);</span>
<span id="cb60-10"><a href="#cb60-10" aria-hidden="true" tabindex="-1"></a>template&lt;class ExecutionPolicy,</span>
<span id="cb60-11"><a href="#cb60-11" aria-hidden="true" tabindex="-1"></a>         <em>in-matrix</em> InMat1,</span>
<span id="cb60-12"><a href="#cb60-12" aria-hidden="true" tabindex="-1"></a>         <em>in-matrix</em> InMat2,</span>
<span id="cb60-13"><a href="#cb60-13" aria-hidden="true" tabindex="-1"></a>         <em>possibly-packed</em>-<span class="rm" style="color: #bf0303"><del><em><span><code>in</code></span></em></del></span><em>out-matrix</em> <span class="rm" style="color: #bf0303"><del><span><code>In</code></span></del></span>OutMat,</span>
<span id="cb60-14"><a href="#cb60-14" aria-hidden="true" tabindex="-1"></a>         class Triangle&gt;</span>
<span id="cb60-15"><a href="#cb60-15" aria-hidden="true" tabindex="-1"></a>void symmetric_matrix_rank_2k_update(</span>
<span id="cb60-16"><a href="#cb60-16" aria-hidden="true" tabindex="-1"></a>  ExecutionPolicy&amp;&amp; exec,</span>
<span id="cb60-17"><a href="#cb60-17" aria-hidden="true" tabindex="-1"></a>  InMat1 A,</span>
<span id="cb60-18"><a href="#cb60-18" aria-hidden="true" tabindex="-1"></a>  InMat2 B,</span>
<span id="cb60-19"><a href="#cb60-19" aria-hidden="true" tabindex="-1"></a>  <span class="rm" style="color: #bf0303"><del><span><code>In</code></span></del></span>OutMat C,</span>
<span id="cb60-20"><a href="#cb60-20" aria-hidden="true" tabindex="-1"></a>  Triangle t);</span></code></pre></div>
<p><span class="marginalizedparent"><a class="marginalized">8</a></span>
<em>Effects:</em> Computes <span class="rm" style="color: #bf0303"><del>a matrix <span class="math inline"><em>C</em>′</span> such that <span class="math inline"><em>C</em>′ = <em>C</em> + <em>A</em><em>B</em><sup><em>T</em></sup> + <em>B</em><em>A</em><sup><em>T</em></sup></span>,
and assigns each element of <span class="math inline"><em>C</em>′</span>
to the corresponding element of <span class="math inline"><em>C</em></span></del></span><span class="add" style="color: #00AA00"><ins><span class="math inline"><em>C</em> = <em>A</em><em>B</em><sup><em>T</em></sup> + <em>B</em><em>A</em><sup><em>T</em></sup></span></ins></span>.</p>
<div class="sourceCode" id="cb61"><pre class="sourceCode default"><code class="sourceCode default"><span id="cb61-1"><a href="#cb61-1" aria-hidden="true" tabindex="-1"></a>template&lt;<em>in-matrix</em> InMat1,</span>
<span id="cb61-2"><a href="#cb61-2" aria-hidden="true" tabindex="-1"></a>         <em>in-matrix</em> InMat2,</span>
<span id="cb61-3"><a href="#cb61-3" aria-hidden="true" tabindex="-1"></a>         <em>possibly-packed</em>-<span class="rm" style="color: #bf0303"><del><em><span><code>in</code></span></em></del></span><em>out-matrix</em> <span class="rm" style="color: #bf0303"><del><span><code>In</code></span></del></span>OutMat,</span>
<span id="cb61-4"><a href="#cb61-4" aria-hidden="true" tabindex="-1"></a>         class Triangle&gt;</span>
<span id="cb61-5"><a href="#cb61-5" aria-hidden="true" tabindex="-1"></a>void hermitian_matrix_rank_2k_update(</span>
<span id="cb61-6"><a href="#cb61-6" aria-hidden="true" tabindex="-1"></a>  InMat1 A,</span>
<span id="cb61-7"><a href="#cb61-7" aria-hidden="true" tabindex="-1"></a>  InMat2 B,</span>
<span id="cb61-8"><a href="#cb61-8" aria-hidden="true" tabindex="-1"></a>  <span class="rm" style="color: #bf0303"><del><span><code>In</code></span></del></span>OutMat C,</span>
<span id="cb61-9"><a href="#cb61-9" aria-hidden="true" tabindex="-1"></a>  Triangle t);</span>
<span id="cb61-10"><a href="#cb61-10" aria-hidden="true" tabindex="-1"></a>template&lt;class ExecutionPolicy,</span>
<span id="cb61-11"><a href="#cb61-11" aria-hidden="true" tabindex="-1"></a>         <em>in-matrix</em> InMat1,</span>
<span id="cb61-12"><a href="#cb61-12" aria-hidden="true" tabindex="-1"></a>         <em>in-matrix</em> InMat2,</span>
<span id="cb61-13"><a href="#cb61-13" aria-hidden="true" tabindex="-1"></a>         <em>possibly-packed</em>-<span class="rm" style="color: #bf0303"><del><em><span><code>in</code></span></em></del></span><em>out-matrix</em> <span class="rm" style="color: #bf0303"><del><span><code>In</code></span></del></span>OutMat,</span>
<span id="cb61-14"><a href="#cb61-14" aria-hidden="true" tabindex="-1"></a>         class Triangle&gt;</span>
<span id="cb61-15"><a href="#cb61-15" aria-hidden="true" tabindex="-1"></a>void hermitian_matrix_rank_2k_update(</span>
<span id="cb61-16"><a href="#cb61-16" aria-hidden="true" tabindex="-1"></a>  ExecutionPolicy&amp;&amp; exec,</span>
<span id="cb61-17"><a href="#cb61-17" aria-hidden="true" tabindex="-1"></a>  InMat1 A,</span>
<span id="cb61-18"><a href="#cb61-18" aria-hidden="true" tabindex="-1"></a>  InMat2 B,</span>
<span id="cb61-19"><a href="#cb61-19" aria-hidden="true" tabindex="-1"></a>  <span class="rm" style="color: #bf0303"><del><span><code>In</code></span></del></span>OutMat C,</span>
<span id="cb61-20"><a href="#cb61-20" aria-hidden="true" tabindex="-1"></a>  Triangle t);</span></code></pre></div>
<p><span class="marginalizedparent"><a class="marginalized">9</a></span>
<em>Effects:</em> Computes <span class="rm" style="color: #bf0303"><del>a matrix <span class="math inline"><em>C</em>′</span> such that <span class="math inline"><em>C</em>′ = <em>C</em> + <em>A</em><em>B</em><sup><em>H</em></sup> + <em>B</em><em>A</em><sup><em>H</em></sup></span>,
and assigns each element of <span class="math inline"><em>C</em>′</span>
to the corresponding element of <span class="math inline"><em>C</em></span></del></span><span class="add" style="color: #00AA00"><ins><span class="math inline"><em>C</em> = <em>A</em><em>B</em><sup><em>H</em></sup> + <em>B</em><em>A</em><sup><em>H</em></sup></span></ins></span>.</p>
<div class="add" style="color: #00AA00">

<div class="sourceCode" id="cb62"><pre class="sourceCode default"><code class="sourceCode default"><span id="cb62-1"><a href="#cb62-1" aria-hidden="true" tabindex="-1"></a>template&lt;<em>in-matrix</em> InMat1,</span>
<span id="cb62-2"><a href="#cb62-2" aria-hidden="true" tabindex="-1"></a>         <em>in-matrix</em> InMat2,</span>
<span id="cb62-3"><a href="#cb62-3" aria-hidden="true" tabindex="-1"></a>         <em>in-matrix</em> InMat3,</span>
<span id="cb62-4"><a href="#cb62-4" aria-hidden="true" tabindex="-1"></a>         <em>possibly-packed-out-matrix</em> OutMat,</span>
<span id="cb62-5"><a href="#cb62-5" aria-hidden="true" tabindex="-1"></a>         class Triangle&gt;</span>
<span id="cb62-6"><a href="#cb62-6" aria-hidden="true" tabindex="-1"></a>void symmetric_matrix_rank_2k_update(</span>
<span id="cb62-7"><a href="#cb62-7" aria-hidden="true" tabindex="-1"></a>  InMat1 A,</span>
<span id="cb62-8"><a href="#cb62-8" aria-hidden="true" tabindex="-1"></a>  InMat2 B,</span>
<span id="cb62-9"><a href="#cb62-9" aria-hidden="true" tabindex="-1"></a>  InMat3 E,</span>
<span id="cb62-10"><a href="#cb62-10" aria-hidden="true" tabindex="-1"></a>  OutMat C,</span>
<span id="cb62-11"><a href="#cb62-11" aria-hidden="true" tabindex="-1"></a>  Triangle t);</span>
<span id="cb62-12"><a href="#cb62-12" aria-hidden="true" tabindex="-1"></a>template&lt;class ExecutionPolicy,</span>
<span id="cb62-13"><a href="#cb62-13" aria-hidden="true" tabindex="-1"></a>         <em>in-matrix</em> InMat1,</span>
<span id="cb62-14"><a href="#cb62-14" aria-hidden="true" tabindex="-1"></a>         <em>in-matrix</em> InMat2,</span>
<span id="cb62-15"><a href="#cb62-15" aria-hidden="true" tabindex="-1"></a>         <em>in-matrix</em> InMat3,</span>
<span id="cb62-16"><a href="#cb62-16" aria-hidden="true" tabindex="-1"></a>         <em>possibly-packed-out-matrix</em> OutMat,</span>
<span id="cb62-17"><a href="#cb62-17" aria-hidden="true" tabindex="-1"></a>         class Triangle&gt;</span>
<span id="cb62-18"><a href="#cb62-18" aria-hidden="true" tabindex="-1"></a>void symmetric_matrix_rank_2k_update(</span>
<span id="cb62-19"><a href="#cb62-19" aria-hidden="true" tabindex="-1"></a>  ExecutionPolicy&amp;&amp; exec,</span>
<span id="cb62-20"><a href="#cb62-20" aria-hidden="true" tabindex="-1"></a>  InMat1 A,</span>
<span id="cb62-21"><a href="#cb62-21" aria-hidden="true" tabindex="-1"></a>  InMat2 B,</span>
<span id="cb62-22"><a href="#cb62-22" aria-hidden="true" tabindex="-1"></a>  InMat3 E,</span>
<span id="cb62-23"><a href="#cb62-23" aria-hidden="true" tabindex="-1"></a>  OutMat C,</span>
<span id="cb62-24"><a href="#cb62-24" aria-hidden="true" tabindex="-1"></a>  Triangle t);</span></code></pre></div>
<p><span class="marginalizedparent"><a class="marginalized">10</a></span>
<em>Effects:</em> Computes <span class="math inline"><em>C</em> = <em>E</em> + <em>A</em><em>B</em><sup><em>T</em></sup> + <em>B</em><em>A</em><sup><em>T</em></sup></span>.</p>
<div class="sourceCode" id="cb63"><pre class="sourceCode default"><code class="sourceCode default"><span id="cb63-1"><a href="#cb63-1" aria-hidden="true" tabindex="-1"></a>template&lt;<em>in-matrix</em> InMat1,</span>
<span id="cb63-2"><a href="#cb63-2" aria-hidden="true" tabindex="-1"></a>         <em>in-matrix</em> InMat2,</span>
<span id="cb63-3"><a href="#cb63-3" aria-hidden="true" tabindex="-1"></a>         <em>in-matrix</em> InMat3,</span>
<span id="cb63-4"><a href="#cb63-4" aria-hidden="true" tabindex="-1"></a>         <em>possibly-packed-out-matrix</em> OutMat,</span>
<span id="cb63-5"><a href="#cb63-5" aria-hidden="true" tabindex="-1"></a>         class Triangle&gt;</span>
<span id="cb63-6"><a href="#cb63-6" aria-hidden="true" tabindex="-1"></a>void hermitian_matrix_rank_2k_update(</span>
<span id="cb63-7"><a href="#cb63-7" aria-hidden="true" tabindex="-1"></a>  InMat1 A,</span>
<span id="cb63-8"><a href="#cb63-8" aria-hidden="true" tabindex="-1"></a>  InMat2 B,</span>
<span id="cb63-9"><a href="#cb63-9" aria-hidden="true" tabindex="-1"></a>  InMat3 E,</span>
<span id="cb63-10"><a href="#cb63-10" aria-hidden="true" tabindex="-1"></a>  OutMat C,</span>
<span id="cb63-11"><a href="#cb63-11" aria-hidden="true" tabindex="-1"></a>  Triangle t);</span>
<span id="cb63-12"><a href="#cb63-12" aria-hidden="true" tabindex="-1"></a>template&lt;class ExecutionPolicy,</span>
<span id="cb63-13"><a href="#cb63-13" aria-hidden="true" tabindex="-1"></a>         <em>in-matrix</em> InMat1,</span>
<span id="cb63-14"><a href="#cb63-14" aria-hidden="true" tabindex="-1"></a>         <em>in-matrix</em> InMat2,</span>
<span id="cb63-15"><a href="#cb63-15" aria-hidden="true" tabindex="-1"></a>         <em>in-matrix</em> InMat3,</span>
<span id="cb63-16"><a href="#cb63-16" aria-hidden="true" tabindex="-1"></a>         <em>possibly-packed-out-matrix</em> OutMat,</span>
<span id="cb63-17"><a href="#cb63-17" aria-hidden="true" tabindex="-1"></a>         class Triangle&gt;</span>
<span id="cb63-18"><a href="#cb63-18" aria-hidden="true" tabindex="-1"></a>void hermitian_matrix_rank_2k_update(</span>
<span id="cb63-19"><a href="#cb63-19" aria-hidden="true" tabindex="-1"></a>  ExecutionPolicy&amp;&amp; exec,</span>
<span id="cb63-20"><a href="#cb63-20" aria-hidden="true" tabindex="-1"></a>  InMat1 A,</span>
<span id="cb63-21"><a href="#cb63-21" aria-hidden="true" tabindex="-1"></a>  InMat2 B,</span>
<span id="cb63-22"><a href="#cb63-22" aria-hidden="true" tabindex="-1"></a>  InMat3 E,</span>
<span id="cb63-23"><a href="#cb63-23" aria-hidden="true" tabindex="-1"></a>  OutMat C,</span>
<span id="cb63-24"><a href="#cb63-24" aria-hidden="true" tabindex="-1"></a>  Triangle t);</span></code></pre></div>
<p><span class="marginalizedparent"><a class="marginalized">11</a></span>
<em>Effects:</em> Computes <span class="math inline"><em>C</em> = <em>E</em> + <em>A</em><em>B</em><sup><em>H</em></sup> + <em>B</em><em>A</em><sup><em>H</em></sup></span>.</p>
</div>
<h1 data-number="9" id="appendix-a-example-of-a-generic-numerical-algorithm"><span class="header-section-number">9</span> Appendix A: Example of a generic
numerical algorithm<a href="#appendix-a-example-of-a-generic-numerical-algorithm" class="self-link"></a></h1>
<p>The following example shows how to implement a generic numerical
algorithm, <code>two_norm_abs</code>. This algorithm computes the
absolute value of a variety of number types. For complex numbers, it
returns the magnitude, which is the same as the two-norm of the
two-element vector composed of the real and imaginary parts of the
complex number. <a href="https://godbolt.org/z/KcGPdjPvP">This Compiler
Explorer link</a> demonstrates the implementation. This is not meant to
show an ideal implementation. (A better one would use rescaling, in the
manner of <code>std::hypot</code>, to avoid undue overflow or
underflow.) Instead, it illustrates generic numerical algorithm
development. Commenting out the
<code>#define DEFINE_CONJ_REAL_IMAG_FOR_REAL 1</code> line shows that
without ADL-findable <code>conj</code>, <code>real</code>, and
<code>imag</code>, users’ generic numerical algorithms would need more
special cases and more assumptions on number types.</p>
<div class="sourceCode" id="cb64"><pre class="sourceCode cpp"><code class="sourceCode cpp"><span id="cb64-1"><a href="#cb64-1" aria-hidden="true" tabindex="-1"></a><span class="pp">#include </span><span class="im">&lt;cassert&gt;</span></span>
<span id="cb64-2"><a href="#cb64-2" aria-hidden="true" tabindex="-1"></a><span class="pp">#include </span><span class="im">&lt;cmath&gt;</span></span>
<span id="cb64-3"><a href="#cb64-3" aria-hidden="true" tabindex="-1"></a><span class="pp">#include </span><span class="im">&lt;complex&gt;</span></span>
<span id="cb64-4"><a href="#cb64-4" aria-hidden="true" tabindex="-1"></a><span class="pp">#include </span><span class="im">&lt;concepts&gt;</span></span>
<span id="cb64-5"><a href="#cb64-5" aria-hidden="true" tabindex="-1"></a><span class="pp">#include </span><span class="im">&lt;type_traits&gt;</span></span>
<span id="cb64-6"><a href="#cb64-6" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb64-7"><a href="#cb64-7" aria-hidden="true" tabindex="-1"></a><span class="pp">#define DEFINE_CONJ_REAL_IMAG_FOR_REAL </span><span class="dv">1</span></span>
<span id="cb64-8"><a href="#cb64-8" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb64-9"><a href="#cb64-9" aria-hidden="true" tabindex="-1"></a><span class="kw">template</span><span class="op">&lt;</span><span class="kw">class</span> T<span class="op">&gt;</span></span>
<span id="cb64-10"><a href="#cb64-10" aria-hidden="true" tabindex="-1"></a><span class="kw">constexpr</span> <span class="dt">bool</span> is_std_complex <span class="op">=</span> <span class="kw">false</span>;</span>
<span id="cb64-11"><a href="#cb64-11" aria-hidden="true" tabindex="-1"></a><span class="kw">template</span><span class="op">&lt;</span><span class="kw">class</span> R<span class="op">&gt;</span></span>
<span id="cb64-12"><a href="#cb64-12" aria-hidden="true" tabindex="-1"></a><span class="kw">constexpr</span> <span class="dt">bool</span> is_std_complex<span class="op">&lt;</span>std<span class="op">::</span>complex<span class="op">&lt;</span>R<span class="op">&gt;&gt;</span> <span class="op">=</span> <span class="kw">true</span>;</span>
<span id="cb64-13"><a href="#cb64-13" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb64-14"><a href="#cb64-14" aria-hidden="true" tabindex="-1"></a><span class="kw">template</span><span class="op">&lt;</span><span class="kw">class</span> T<span class="op">&gt;</span></span>
<span id="cb64-15"><a href="#cb64-15" aria-hidden="true" tabindex="-1"></a><span class="kw">auto</span> two_norm_abs<span class="op">(</span>T t<span class="op">)</span> <span class="op">{</span></span>
<span id="cb64-16"><a href="#cb64-16" aria-hidden="true" tabindex="-1"></a>  <span class="cf">if</span> <span class="kw">constexpr</span> <span class="op">(</span>std<span class="op">::</span>is_unsigned_v<span class="op">&lt;</span>T<span class="op">&gt;)</span> <span class="op">{</span></span>
<span id="cb64-17"><a href="#cb64-17" aria-hidden="true" tabindex="-1"></a>    <span class="cf">return</span> t;</span>
<span id="cb64-18"><a href="#cb64-18" aria-hidden="true" tabindex="-1"></a>  <span class="op">}</span></span>
<span id="cb64-19"><a href="#cb64-19" aria-hidden="true" tabindex="-1"></a>  <span class="cf">else</span> <span class="cf">if</span> <span class="kw">constexpr</span> <span class="op">(</span>std<span class="op">::</span>is_arithmetic_v<span class="op">&lt;</span>T<span class="op">&gt;</span> <span class="op">||</span> is_std_complex<span class="op">&lt;</span>T<span class="op">&gt;)</span> <span class="op">{</span></span>
<span id="cb64-20"><a href="#cb64-20" aria-hidden="true" tabindex="-1"></a>    <span class="cf">return</span> std<span class="op">::</span>abs<span class="op">(</span>t<span class="op">)</span>;</span>
<span id="cb64-21"><a href="#cb64-21" aria-hidden="true" tabindex="-1"></a>  <span class="op">}</span></span>
<span id="cb64-22"><a href="#cb64-22" aria-hidden="true" tabindex="-1"></a><span class="pp">#if ! defined(DEFINE_CONJ_REAL_IMAG_FOR_REAL)</span></span>
<span id="cb64-23"><a href="#cb64-23" aria-hidden="true" tabindex="-1"></a>  <span class="cf">else</span> <span class="cf">if</span> <span class="kw">constexpr</span> <span class="op">(</span><span class="kw">requires</span><span class="op">(</span>T x<span class="op">)</span> <span class="op">{</span></span>
<span id="cb64-24"><a href="#cb64-24" aria-hidden="true" tabindex="-1"></a>      <span class="op">{</span>abs<span class="op">(</span>x<span class="op">)}</span> <span class="op">-&gt;</span> std<span class="op">::</span>convertible_to<span class="op">&lt;</span>T<span class="op">&gt;</span>;</span>
<span id="cb64-25"><a href="#cb64-25" aria-hidden="true" tabindex="-1"></a>    <span class="op">})</span> <span class="op">{</span></span>
<span id="cb64-26"><a href="#cb64-26" aria-hidden="true" tabindex="-1"></a>    <span class="cf">return</span> T<span class="op">{</span>abs<span class="op">(</span>t<span class="op">)}</span>;</span>
<span id="cb64-27"><a href="#cb64-27" aria-hidden="true" tabindex="-1"></a>  <span class="op">}</span></span>
<span id="cb64-28"><a href="#cb64-28" aria-hidden="true" tabindex="-1"></a><span class="pp">#endif</span></span>
<span id="cb64-29"><a href="#cb64-29" aria-hidden="true" tabindex="-1"></a>  <span class="cf">else</span> <span class="cf">if</span> <span class="kw">constexpr</span> <span class="op">(</span><span class="kw">requires</span><span class="op">(</span>T x<span class="op">)</span> <span class="op">{</span></span>
<span id="cb64-30"><a href="#cb64-30" aria-hidden="true" tabindex="-1"></a>      <span class="op">{</span>sqrt<span class="op">(</span>real<span class="op">(</span>x <span class="op">*</span> conj<span class="op">(</span>x<span class="op">)))}</span> <span class="op">-&gt;</span> std<span class="op">::</span>same_as<span class="op">&lt;</span><span class="kw">decltype</span><span class="op">(</span>real<span class="op">(</span>x<span class="op">))&gt;</span>;</span>
<span id="cb64-31"><a href="#cb64-31" aria-hidden="true" tabindex="-1"></a>    <span class="op">})</span> <span class="op">{</span></span>
<span id="cb64-32"><a href="#cb64-32" aria-hidden="true" tabindex="-1"></a>    <span class="cf">return</span> sqrt<span class="op">(</span>real<span class="op">(</span>t <span class="op">*</span> conj<span class="op">(</span>t<span class="op">)))</span>;</span>
<span id="cb64-33"><a href="#cb64-33" aria-hidden="true" tabindex="-1"></a>  <span class="op">}</span></span>
<span id="cb64-34"><a href="#cb64-34" aria-hidden="true" tabindex="-1"></a>  <span class="cf">else</span> <span class="op">{</span></span>
<span id="cb64-35"><a href="#cb64-35" aria-hidden="true" tabindex="-1"></a>    <span class="kw">static_assert</span><span class="op">(</span><span class="kw">false</span>, <span class="st">&quot;No reasonable way to implement abs(t)&quot;</span><span class="op">)</span>;</span>
<span id="cb64-36"><a href="#cb64-36" aria-hidden="true" tabindex="-1"></a>  <span class="op">}</span></span>
<span id="cb64-37"><a href="#cb64-37" aria-hidden="true" tabindex="-1"></a><span class="op">}</span></span>
<span id="cb64-38"><a href="#cb64-38" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb64-39"><a href="#cb64-39" aria-hidden="true" tabindex="-1"></a><span class="kw">struct</span> MyRealNumber <span class="op">{</span></span>
<span id="cb64-40"><a href="#cb64-40" aria-hidden="true" tabindex="-1"></a>  MyRealNumber<span class="op">()</span> <span class="op">=</span> <span class="cf">default</span>;</span>
<span id="cb64-41"><a href="#cb64-41" aria-hidden="true" tabindex="-1"></a>  MyRealNumber<span class="op">(</span><span class="dt">double</span> value<span class="op">)</span> <span class="op">:</span> value_<span class="op">(</span>value<span class="op">)</span> <span class="op">{}</span></span>
<span id="cb64-42"><a href="#cb64-42" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb64-43"><a href="#cb64-43" aria-hidden="true" tabindex="-1"></a>  <span class="dt">double</span> value<span class="op">()</span> <span class="kw">const</span> <span class="op">{</span></span>
<span id="cb64-44"><a href="#cb64-44" aria-hidden="true" tabindex="-1"></a>    <span class="cf">return</span> value_;</span>
<span id="cb64-45"><a href="#cb64-45" aria-hidden="true" tabindex="-1"></a>  <span class="op">}</span></span>
<span id="cb64-46"><a href="#cb64-46" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb64-47"><a href="#cb64-47" aria-hidden="true" tabindex="-1"></a>  <span class="kw">friend</span> <span class="dt">bool</span> <span class="kw">operator</span><span class="op">==(</span>MyRealNumber, MyRealNumber<span class="op">)</span> <span class="op">=</span> <span class="cf">default</span>;</span>
<span id="cb64-48"><a href="#cb64-48" aria-hidden="true" tabindex="-1"></a>  <span class="kw">friend</span> MyRealNumber <span class="kw">operator</span><span class="op">-(</span>MyRealNumber x<span class="op">)</span> <span class="op">{</span></span>
<span id="cb64-49"><a href="#cb64-49" aria-hidden="true" tabindex="-1"></a>    <span class="cf">return</span> <span class="op">{-</span>x<span class="op">.</span>value_<span class="op">}</span>;</span>
<span id="cb64-50"><a href="#cb64-50" aria-hidden="true" tabindex="-1"></a>  <span class="op">}</span></span>
<span id="cb64-51"><a href="#cb64-51" aria-hidden="true" tabindex="-1"></a>  <span class="kw">friend</span> MyRealNumber <span class="kw">operator</span><span class="op">+(</span>MyRealNumber x, MyRealNumber y<span class="op">)</span> <span class="op">{</span></span>
<span id="cb64-52"><a href="#cb64-52" aria-hidden="true" tabindex="-1"></a>    <span class="cf">return</span> <span class="op">{</span>x<span class="op">.</span>value_ <span class="op">+</span> y<span class="op">.</span>value_<span class="op">}</span>;</span>
<span id="cb64-53"><a href="#cb64-53" aria-hidden="true" tabindex="-1"></a>  <span class="op">}</span></span>
<span id="cb64-54"><a href="#cb64-54" aria-hidden="true" tabindex="-1"></a>  <span class="kw">friend</span> MyRealNumber <span class="kw">operator</span><span class="op">-(</span>MyRealNumber x, MyRealNumber y<span class="op">)</span> <span class="op">{</span></span>
<span id="cb64-55"><a href="#cb64-55" aria-hidden="true" tabindex="-1"></a>    <span class="cf">return</span> <span class="op">{</span>x<span class="op">.</span>value_ <span class="op">-</span> y<span class="op">.</span>value_<span class="op">}</span>;</span>
<span id="cb64-56"><a href="#cb64-56" aria-hidden="true" tabindex="-1"></a>  <span class="op">}</span></span>
<span id="cb64-57"><a href="#cb64-57" aria-hidden="true" tabindex="-1"></a>  <span class="kw">friend</span> MyRealNumber <span class="kw">operator</span><span class="op">*(</span>MyRealNumber x, MyRealNumber y<span class="op">)</span> <span class="op">{</span></span>
<span id="cb64-58"><a href="#cb64-58" aria-hidden="true" tabindex="-1"></a>    <span class="cf">return</span> x<span class="op">.</span>value_ <span class="op">*</span> y<span class="op">.</span>value_;</span>
<span id="cb64-59"><a href="#cb64-59" aria-hidden="true" tabindex="-1"></a>  <span class="op">}</span></span>
<span id="cb64-60"><a href="#cb64-60" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb64-61"><a href="#cb64-61" aria-hidden="true" tabindex="-1"></a><span class="pp">#if defined(DEFINE_CONJ_REAL_IMAG_FOR_REAL)</span></span>
<span id="cb64-62"><a href="#cb64-62" aria-hidden="true" tabindex="-1"></a>  <span class="kw">friend</span> MyRealNumber conj<span class="op">(</span>MyRealNumber x<span class="op">)</span> <span class="op">{</span> <span class="cf">return</span> x; <span class="op">}</span></span>
<span id="cb64-63"><a href="#cb64-63" aria-hidden="true" tabindex="-1"></a>  <span class="kw">friend</span> MyRealNumber real<span class="op">(</span>MyRealNumber x<span class="op">)</span> <span class="op">{</span> <span class="cf">return</span> x; <span class="op">}</span></span>
<span id="cb64-64"><a href="#cb64-64" aria-hidden="true" tabindex="-1"></a>  <span class="kw">friend</span> MyRealNumber imag<span class="op">(</span>MyRealNumber x<span class="op">)</span> <span class="op">{</span> <span class="cf">return</span> <span class="op">{}</span>; <span class="op">}</span></span>
<span id="cb64-65"><a href="#cb64-65" aria-hidden="true" tabindex="-1"></a><span class="pp">#else</span></span>
<span id="cb64-66"><a href="#cb64-66" aria-hidden="true" tabindex="-1"></a>  <span class="kw">friend</span> MyRealNumber abs<span class="op">(</span>MyRealNumber x<span class="op">)</span> <span class="op">{</span> <span class="cf">return</span> std<span class="op">::</span>abs<span class="op">(</span>x<span class="op">.</span>value_<span class="op">)</span>; <span class="op">}</span></span>
<span id="cb64-67"><a href="#cb64-67" aria-hidden="true" tabindex="-1"></a><span class="pp">#endif</span></span>
<span id="cb64-68"><a href="#cb64-68" aria-hidden="true" tabindex="-1"></a>  <span class="kw">friend</span> MyRealNumber sqrt<span class="op">(</span>MyRealNumber x<span class="op">)</span> <span class="op">{</span> <span class="cf">return</span> std<span class="op">::</span>sqrt<span class="op">(</span>x<span class="op">.</span>value_<span class="op">)</span>; <span class="op">}</span></span>
<span id="cb64-69"><a href="#cb64-69" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb64-70"><a href="#cb64-70" aria-hidden="true" tabindex="-1"></a><span class="kw">private</span><span class="op">:</span></span>
<span id="cb64-71"><a href="#cb64-71" aria-hidden="true" tabindex="-1"></a>  <span class="dt">double</span> value_<span class="op">{}</span>;</span>
<span id="cb64-72"><a href="#cb64-72" aria-hidden="true" tabindex="-1"></a><span class="op">}</span>;</span>
<span id="cb64-73"><a href="#cb64-73" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb64-74"><a href="#cb64-74" aria-hidden="true" tabindex="-1"></a><span class="kw">class</span> MyComplexNumber <span class="op">{</span></span>
<span id="cb64-75"><a href="#cb64-75" aria-hidden="true" tabindex="-1"></a><span class="kw">public</span><span class="op">:</span></span>
<span id="cb64-76"><a href="#cb64-76" aria-hidden="true" tabindex="-1"></a>  MyComplexNumber<span class="op">(</span>MyRealNumber re, MyRealNumber im <span class="op">=</span> <span class="op">{})</span> <span class="op">:</span> re_<span class="op">(</span>re<span class="op">)</span>, im_<span class="op">(</span>im<span class="op">)</span> <span class="op">{}</span></span>
<span id="cb64-77"><a href="#cb64-77" aria-hidden="true" tabindex="-1"></a>  MyComplexNumber<span class="op">()</span> <span class="op">=</span> <span class="cf">default</span>;</span>
<span id="cb64-78"><a href="#cb64-78" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb64-79"><a href="#cb64-79" aria-hidden="true" tabindex="-1"></a>  std<span class="op">::</span>complex<span class="op">&lt;</span><span class="dt">double</span><span class="op">&gt;</span> value<span class="op">()</span> <span class="kw">const</span> <span class="op">{</span></span>
<span id="cb64-80"><a href="#cb64-80" aria-hidden="true" tabindex="-1"></a>    <span class="cf">return</span> <span class="op">{</span>re_<span class="op">.</span>value<span class="op">()</span>, im_<span class="op">.</span>value<span class="op">()}</span>;</span>
<span id="cb64-81"><a href="#cb64-81" aria-hidden="true" tabindex="-1"></a>  <span class="op">}</span></span>
<span id="cb64-82"><a href="#cb64-82" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb64-83"><a href="#cb64-83" aria-hidden="true" tabindex="-1"></a>  <span class="kw">friend</span> <span class="dt">bool</span> <span class="kw">operator</span><span class="op">==(</span>MyComplexNumber, MyComplexNumber<span class="op">)</span> <span class="op">=</span> <span class="cf">default</span>;</span>
<span id="cb64-84"><a href="#cb64-84" aria-hidden="true" tabindex="-1"></a>  <span class="kw">friend</span> MyComplexNumber <span class="kw">operator</span><span class="op">*(</span>MyComplexNumber z, MyComplexNumber w<span class="op">)</span> <span class="op">{</span></span>
<span id="cb64-85"><a href="#cb64-85" aria-hidden="true" tabindex="-1"></a>    <span class="cf">return</span> <span class="op">{</span>real<span class="op">(</span>z<span class="op">)</span> <span class="op">*</span> real<span class="op">(</span>w<span class="op">)</span> <span class="op">-</span> imag<span class="op">(</span>z<span class="op">)</span> <span class="op">*</span> imag<span class="op">(</span>w<span class="op">)</span>,</span>
<span id="cb64-86"><a href="#cb64-86" aria-hidden="true" tabindex="-1"></a>      real<span class="op">(</span>z<span class="op">)</span> <span class="op">*</span> imag<span class="op">(</span>w<span class="op">)</span> <span class="op">+</span> imag<span class="op">(</span>z<span class="op">)</span> <span class="op">*</span> real<span class="op">(</span>w<span class="op">)}</span>;</span>
<span id="cb64-87"><a href="#cb64-87" aria-hidden="true" tabindex="-1"></a>  <span class="op">}</span></span>
<span id="cb64-88"><a href="#cb64-88" aria-hidden="true" tabindex="-1"></a>  <span class="kw">friend</span> MyComplexNumber conj<span class="op">(</span>MyComplexNumber z<span class="op">)</span> <span class="op">{</span> <span class="cf">return</span> <span class="op">{</span>real<span class="op">(</span>z<span class="op">)</span>, <span class="op">-</span>imag<span class="op">(</span>z<span class="op">)}</span>; <span class="op">}</span></span>
<span id="cb64-89"><a href="#cb64-89" aria-hidden="true" tabindex="-1"></a>  <span class="kw">friend</span> MyRealNumber real<span class="op">(</span>MyComplexNumber z<span class="op">)</span> <span class="op">{</span> <span class="cf">return</span> z<span class="op">.</span>re_; <span class="op">}</span></span>
<span id="cb64-90"><a href="#cb64-90" aria-hidden="true" tabindex="-1"></a>  <span class="kw">friend</span> MyRealNumber imag<span class="op">(</span>MyComplexNumber z<span class="op">)</span> <span class="op">{</span> <span class="cf">return</span> z<span class="op">.</span>im_; <span class="op">}</span></span>
<span id="cb64-91"><a href="#cb64-91" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb64-92"><a href="#cb64-92" aria-hidden="true" tabindex="-1"></a><span class="kw">private</span><span class="op">:</span></span>
<span id="cb64-93"><a href="#cb64-93" aria-hidden="true" tabindex="-1"></a>  MyRealNumber re_<span class="op">{}</span>;</span>
<span id="cb64-94"><a href="#cb64-94" aria-hidden="true" tabindex="-1"></a>  MyRealNumber im_<span class="op">{}</span>;</span>
<span id="cb64-95"><a href="#cb64-95" aria-hidden="true" tabindex="-1"></a><span class="op">}</span>;</span>
<span id="cb64-96"><a href="#cb64-96" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb64-97"><a href="#cb64-97" aria-hidden="true" tabindex="-1"></a><span class="dt">int</span> main<span class="op">()</span> <span class="op">{</span></span>
<span id="cb64-98"><a href="#cb64-98" aria-hidden="true" tabindex="-1"></a>  <span class="op">[[</span><span class="at">maybe_unused</span><span class="op">]]</span> <span class="dt">double</span> x1 <span class="op">=</span> two_norm_abs<span class="op">(-</span><span class="fl">4.2</span><span class="op">)</span>;</span>
<span id="cb64-99"><a href="#cb64-99" aria-hidden="true" tabindex="-1"></a>  <span class="ot">assert</span><span class="op">(</span>x1 <span class="op">==</span> <span class="fl">4.2</span><span class="op">)</span>;</span>
<span id="cb64-100"><a href="#cb64-100" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb64-101"><a href="#cb64-101" aria-hidden="true" tabindex="-1"></a>  <span class="op">[[</span><span class="at">maybe_unused</span><span class="op">]]</span> <span class="dt">float</span> y0 <span class="op">=</span> two_norm_abs<span class="op">(</span>std<span class="op">::</span>complex<span class="op">&lt;</span><span class="dt">float</span><span class="op">&gt;{-</span><span class="fl">3.0</span><span class="bu">f</span>, <span class="fl">4.0</span><span class="bu">f</span><span class="op">})</span>;</span>
<span id="cb64-102"><a href="#cb64-102" aria-hidden="true" tabindex="-1"></a>  <span class="ot">assert</span><span class="op">(</span>y0 <span class="op">==</span> <span class="fl">5.0</span><span class="bu">f</span><span class="op">)</span>;</span>
<span id="cb64-103"><a href="#cb64-103" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb64-104"><a href="#cb64-104" aria-hidden="true" tabindex="-1"></a>  <span class="op">[[</span><span class="at">maybe_unused</span><span class="op">]]</span> MyRealNumber r <span class="op">=</span> two_norm_abs<span class="op">(</span>MyRealNumber<span class="op">{-</span><span class="fl">6.7</span><span class="op">})</span>;</span>
<span id="cb64-105"><a href="#cb64-105" aria-hidden="true" tabindex="-1"></a>  <span class="ot">assert</span><span class="op">(</span>r <span class="op">==</span> MyRealNumber<span class="op">{</span><span class="fl">6.7</span><span class="op">})</span>;</span>
<span id="cb64-106"><a href="#cb64-106" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb64-107"><a href="#cb64-107" aria-hidden="true" tabindex="-1"></a>  <span class="op">[[</span><span class="at">maybe_unused</span><span class="op">]]</span> MyRealNumber z <span class="op">=</span> two_norm_abs<span class="op">(</span>MyComplexNumber<span class="op">{-</span><span class="dv">3</span>, <span class="dv">4</span><span class="op">})</span>;</span>
<span id="cb64-108"><a href="#cb64-108" aria-hidden="true" tabindex="-1"></a>  <span class="ot">assert</span><span class="op">(</span>z<span class="op">.</span>value<span class="op">()</span> <span class="op">==</span> <span class="fl">5.0</span><span class="op">)</span>;</span>
<span id="cb64-109"><a href="#cb64-109" aria-hidden="true" tabindex="-1"></a> </span>
<span id="cb64-110"><a href="#cb64-110" aria-hidden="true" tabindex="-1"></a>  <span class="cf">return</span> <span class="dv">0</span>;</span>
<span id="cb64-111"><a href="#cb64-111" aria-hidden="true" tabindex="-1"></a><span class="op">}</span></span></code></pre></div>
</div>
</div>
</body>
</html>
