113113< meta name ="twitter:card " content ="summary ">
114114</ head >
115115
116- < body class ="nav-fixed quarto-light ">
116+ < body class ="nav-fixed fullcontent quarto-light ">
117117
118118< div id ="quarto-search-results "> </ div >
119119 < header id ="quarto-header " class ="headroom fixed-top quarto-banner "> < div id ="quarto-draft-alert " class ="alert alert-warning "> < i class ="bi bi-pencil-square "> </ i > Draft</ div >
@@ -188,41 +188,14 @@ <h1 class="title">MVP: Technical Progress Dashboard for AI Impact Tracking</h1>
188188 </ header > < div id ="quarto-content " class ="quarto-container page-columns page-rows-contents page-layout-article page-navbar ">
189189<!-- sidebar -->
190190<!-- margin-sidebar -->
191- < div id ="quarto-margin-sidebar " class ="sidebar margin-sidebar ">
192- < nav id ="TOC " role ="doc-toc " class ="toc-active ">
193- < h2 id ="toc-title "> On this page</ h2 >
194-
195- < ul class ="collapse ">
196- < li > < a href ="#scope-choices-for-mvp " id ="toc-scope-choices-for-mvp " class ="nav-link active " data-scroll-target ="#scope-choices-for-mvp "> Scope choices for MVP</ a > </ li >
197- < li > < a href ="#dashboard " id ="toc-dashboard " class ="nav-link " data-scroll-target ="#dashboard "> Dashboard</ a > </ li >
198- < li > < a href ="#provenance-visible-series " id ="toc-provenance-visible-series " class ="nav-link " data-scroll-target ="#provenance-visible-series "> Provenance (visible series)</ a > </ li >
199- < li > < a href ="#notes-on-data-quality " id ="toc-notes-on-data-quality " class ="nav-link " data-scroll-target ="#notes-on-data-quality "> Notes on data quality</ a > </ li >
200- </ ul >
201- </ nav >
202- </ div >
191+
203192<!-- main -->
204193< main class ="content quarto-banner-title-block " id ="quarto-document-content ">
205194
206195
207196
208197
209198
210- < p > This MVP is built to support < strong > forecasting and measuring AI’s impact on technological progress</ strong > using a frontier-first lens.</ p >
211- < section id ="scope-choices-for-mvp " class ="level2 ">
212- < h2 class ="anchored " data-anchor-id ="scope-choices-for-mvp "> Scope choices for MVP</ h2 >
213- < ol type ="1 ">
214- < li > Goal: support forecasting and monitoring of AI-driven acceleration in technical efficiency trends.</ li >
215- < li > Included domains: compute hardware, compute storage, energy, energy storage, biotech, agriculture.</ li >
216- < li > Included metric classes: cost efficiency, physical productivity, and energy efficiency.</ li >
217- < li > Frontier rule: include frontier series by default; include average series when they are informative or frontier data is sparse.</ li >
218- < li > Data inclusion threshold: at least 4 time points, direction-of-improvement defined, source URL recorded.</ li >
219- < li > Fitting method: per-series log-linear fit on historical data (< code > log(value) = a + b * year</ code > ).</ li >
220- < li > v1 excludes causal attribution and forecasting beyond observed history.</ li >
221- < li > v1 data are seed-series and should be upgraded to automated ingestion in a follow-up.</ li >
222- </ ol >
223- </ section >
224- < section id ="dashboard " class ="level2 ">
225- < h2 class ="anchored " data-anchor-id ="dashboard "> Dashboard</ h2 >
226199< script src ="https://cdn.plot.ly/plotly-2.35.2.min.js "> </ script >
227200< style >
228201# tpd-controls {
@@ -272,6 +245,12 @@ <h2 class="anchored" data-anchor-id="dashboard">Dashboard</h2>
272245 background : # f3f3f3 ;
273246}
274247</ style >
248+ < div id ="tpd-plot ">
249+
250+ </ div >
251+ < div id ="tpd-notes ">
252+
253+ </ div >
275254< div id ="tpd-controls ">
276255< div class ="tpd-control ">
277256< p > < label for ="tpd-domain "> Domain</ label > < select id ="tpd-domain " multiple ="" size ="6 "> </ select > </ p >
@@ -294,13 +273,6 @@ <h2 class="anchored" data-anchor-id="dashboard">Dashboard</h2>
294273< p > < label style ="margin-top:10px; "> < input id ="tpd-show-fit " type ="checkbox " checked =""> Show log-linear trend fit </ label > </ p >
295274</ div >
296275</ div >
297- < div id ="tpd-notes ">
298-
299- </ div >
300- < div id ="tpd-plot ">
301-
302- </ div >
303- </ section >
304276< section id ="provenance-visible-series " class ="level2 ">
305277< h2 class ="anchored " data-anchor-id ="provenance-visible-series "> Provenance (visible series)</ h2 >
306278< table id ="tpd-provenance ">
@@ -319,7 +291,7 @@ <h2 class="anchored" data-anchor-id="provenance-visible-series">Provenance (visi
319291Frontier class
320292</ th >
321293< th >
322- Direction
294+ Direction (displayed)
323295</ th >
324296< th >
325297Points shown
@@ -483,12 +455,17 @@ <h2 class="anchored" data-anchor-id="provenance-visible-series">Provenance (visi
483455 return grouped ;
484456}
485457
458+ function toLowerBetterValue ( row ) {
459+ if ( row . value <= 0 ) return null ;
460+ return row . direction === "higher_better" ? 1 / row . value : row . value ;
461+ }
462+
486463function fitLogLinear ( points ) {
487- const filtered = points . filter ( ( p ) => p . value > 0 ) ;
464+ const filtered = points . filter ( ( p ) => p . value_lb > 0 ) ;
488465 if ( filtered . length < 2 ) return null ;
489466
490467 const xs = filtered . map ( ( p ) => p . year ) ;
491- const ys = filtered . map ( ( p ) => Math . log ( p . value ) ) ;
468+ const ys = filtered . map ( ( p ) => Math . log ( p . value_lb ) ) ;
492469
493470 const n = xs . length ;
494471 const xMean = xs . reduce ( ( a , b ) => a + b , 0 ) / n ;
@@ -507,18 +484,15 @@ <h2 class="anchored" data-anchor-id="provenance-visible-series">Provenance (visi
507484 return { a, b } ;
508485}
509486
510- function annualImprovementPercent ( slope , direction ) {
511- if ( direction === "higher_better" ) {
512- return ( Math . exp ( slope ) - 1 ) * 100 ;
513- }
487+ function annualImprovementPercent ( slope ) {
514488 return ( 1 - Math . exp ( slope ) ) * 100 ;
515489}
516490
517491function makeDisplayPoints ( points , yMode ) {
518- if ( yMode === "raw" ) return points . map ( ( p ) => ( { year : p . year , y : p . value } ) ) ;
519- const base = points [ 0 ] ?. value ;
492+ if ( yMode === "raw" ) return points . map ( ( p ) => ( { year : p . year , y : p . value_lb } ) ) ;
493+ const base = points [ 0 ] ?. value_lb ;
520494 if ( ! base || base <= 0 ) return [ ] ;
521- return points . map ( ( p ) => ( { year : p . year , y : p . value / base } ) ) ;
495+ return points . map ( ( p ) => ( { year : p . year , y : p . value_lb / base } ) ) ;
522496}
523497
524498function render ( ) {
@@ -535,17 +509,25 @@ <h2 class="anchored" data-anchor-id="provenance-visible-series">Provenance (visi
535509 const search = controls . search . value . trim ( ) . toLowerCase ( ) ;
536510 const showFit = controls . showFit . checked ;
537511
538- const filteredRows = TPD_DATA . filter ( ( row ) => {
539- if ( ! domainSel . includes ( row . domain ) ) return false ;
540- if ( ! typeSel . includes ( row . metric_type ) ) return false ;
541- if ( row . year < yearMin || row . year > yearMax ) return false ;
542- if ( search && ! row . name . toLowerCase ( ) . includes ( search ) ) return false ;
543-
544- if ( frontierSel === "frontier_only" && row . frontier_class !== "frontier" ) return false ;
545- if ( frontierSel === "average_only" && row . frontier_class !== "average" ) return false ;
546-
547- return true ;
548- } ) ;
512+ const filteredRows = TPD_DATA
513+ . filter ( ( row ) => {
514+ if ( ! domainSel . includes ( row . domain ) ) return false ;
515+ if ( ! typeSel . includes ( row . metric_type ) ) return false ;
516+ if ( row . year < yearMin || row . year > yearMax ) return false ;
517+ if ( search && ! row . name . toLowerCase ( ) . includes ( search ) ) return false ;
518+
519+ if ( frontierSel === "frontier_only" && row . frontier_class !== "frontier" ) return false ;
520+ if ( frontierSel === "average_only" && row . frontier_class !== "average" ) return false ;
521+
522+ return true ;
523+ } )
524+ . map ( ( row ) => ( {
525+ ...row ,
526+ value_lb : toLowerBetterValue ( row ) ,
527+ unit_lb : row . direction === "higher_better" ? `1 / (${ row . unit } )` : row . unit ,
528+ direction_lb : "lower_better"
529+ } ) )
530+ . filter ( ( row ) => row . value_lb !== null ) ;
549531
550532 const bySeries = groupBySeries ( filteredRows ) ;
551533
@@ -557,7 +539,12 @@ <h2 class="anchored" data-anchor-id="provenance-visible-series">Provenance (visi
557539
558540 seriesToPlot . forEach ( ( rows ) => {
559541 const first = rows [ 0 ] ;
560- rawUnitSet . add ( first . unit ) ;
542+ rawUnitSet . add ( first . unit_lb ) ;
543+ const fit = fitLogLinear ( rows ) ;
544+ const annualPct = fit ? annualImprovementPercent ( fit . b ) : null ;
545+ const seriesLabel = annualPct === null
546+ ? first . name
547+ : `${ first . name } (${ annualPct . toFixed ( 1 ) } %/yr)` ;
561548
562549 const displayPoints = makeDisplayPoints ( rows , yMode ) ;
563550 if ( ! displayPoints . length ) return ;
@@ -567,27 +554,26 @@ <h2 class="anchored" data-anchor-id="provenance-visible-series">Provenance (visi
567554 mode : "lines+markers" ,
568555 x : displayPoints . map ( ( d ) => d . year ) ,
569556 y : displayPoints . map ( ( d ) => d . y ) ,
570- name : first . name ,
557+ name : seriesLabel ,
571558 hovertemplate :
572559 "<b>%{fullData.name}</b><br>Year: %{x}<br>Value: %{y:.4g}<extra></extra>"
573560 } ) ;
574561
575562 if ( showFit ) {
576- const fit = fitLogLinear ( rows ) ;
577563 if ( fit ) {
578564 const xFit = rows . map ( ( p ) => p . year ) ;
579565 const yFitRaw = xFit . map ( ( x ) => Math . exp ( fit . a + fit . b * x ) ) ;
580566 const yFit = yMode === "raw"
581567 ? yFitRaw
582568 : yFitRaw . map ( ( v ) => v / yFitRaw [ 0 ] ) ;
583569
584- const annualPct = annualImprovementPercent ( fit . b , first . direction ) ;
585570 traces . push ( {
586571 type : "scatter" ,
587572 mode : "lines" ,
588573 x : xFit ,
589574 y : yFit ,
590- name : `${ first . name } trend (${ annualPct . toFixed ( 1 ) } %/yr)` ,
575+ name : `${ seriesLabel } trend` ,
576+ showlegend : false ,
591577 line : { dash : "dot" , width : 2 } ,
592578 hovertemplate :
593579 "<b>%{fullData.name}</b><br>Year: %{x}<br>Fitted: %{y:.4g}<extra></extra>"
@@ -601,18 +587,23 @@ <h2 class="anchored" data-anchor-id="provenance-visible-series">Provenance (visi
601587 metricType : first . metric_type ,
602588 frontierClass : first . frontier_class ,
603589 direction : first . direction ,
590+ transformed : first . direction === "higher_better" ,
604591 points : rows . length ,
605592 sourceName : first . source_name ,
606593 sourceUrl : first . source_url ,
607- note : first . provenance_note
594+ note : first . direction === "higher_better"
595+ ? `${ first . provenance_note } Display transformed as 1/value so lower is better.`
596+ : first . provenance_note
608597 } ) ;
609598 } ) ;
610599
611600 const layout = {
612601 title : "Technical Progress Over Time" ,
613602 xaxis : { title : "Year" } ,
614603 yaxis : {
615- title : yMode === "indexed" ? "Indexed value (first visible year = 1)" : "Raw value" ,
604+ title : yMode === "indexed"
605+ ? "Indexed value (first visible year = 1, lower is better)"
606+ : "Raw value (lower-is-better transformed)" ,
616607 type : yScale
617608 } ,
618609 legend : { orientation : "h" , y : - 0.25 } ,
@@ -642,7 +633,7 @@ <h2 class="anchored" data-anchor-id="provenance-visible-series">Provenance (visi
642633 <td>${ row . domain } </td>
643634 <td>${ row . metricType } </td>
644635 <td>${ row . frontierClass } </td>
645- <td>${ row . direction } </td>
636+ <td>lower_better </td>
646637 <td>${ row . points } </td>
647638 <td><a href="${ row . sourceUrl } " target="_blank" rel="noopener noreferrer">${ row . sourceName } </a></td>
648639 <td>${ row . note } </td>
@@ -684,8 +675,23 @@ <h2 class="anchored" data-anchor-id="notes-on-data-quality">Notes on data qualit
684675< ul >
685676< li > This is a < strong > seed MVP dataset</ strong > intended to validate taxonomy + interaction design.</ li >
686677< li > Several series above are marked as seed points from published charts; next step is scripted ingestion from source APIs/files.</ li >
678+ < li > All displayed series are transformed to < strong > lower-is-better</ strong > (< code > 1/value</ code > for originally higher-is-better metrics).</ li >
687679< li > Because units differ across technologies, indexed mode is the main comparison mode.</ li >
688680</ ul >
681+ < p > This MVP is built to support < strong > forecasting and measuring AI’s impact on technological progress</ strong > using a frontier-first lens.</ p >
682+ </ section >
683+ < section id ="scope-choices-for-mvp " class ="level2 ">
684+ < h2 class ="anchored " data-anchor-id ="scope-choices-for-mvp "> Scope choices for MVP</ h2 >
685+ < ol type ="1 ">
686+ < li > Goal: support forecasting and monitoring of AI-driven acceleration in technical efficiency trends.</ li >
687+ < li > Included domains: compute hardware, compute storage, energy, energy storage, biotech, agriculture.</ li >
688+ < li > Included metric classes: cost efficiency, physical productivity, and energy efficiency.</ li >
689+ < li > Frontier rule: include frontier series by default; include average series when they are informative or frontier data is sparse.</ li >
690+ < li > Data inclusion threshold: at least 4 time points, direction-of-improvement defined, source URL recorded.</ li >
691+ < li > Fitting method: per-series log-linear fit on historical data (< code > log(value) = a + b * year</ code > ).</ li >
692+ < li > v1 excludes causal attribution and forecasting beyond observed history.</ li >
693+ < li > v1 data are seed-series and should be upgraded to automated ingestion in a follow-up.</ li >
694+ </ ol >
689695
690696
691697</ section >
0 commit comments