import _ from 'lodash' ; import { coordsToIndex , checkCoordsSizesCompability , indicesComplement , isPermutation , coordsFromIndex , } from './helpers' ; import Complex , { Cx , ComplexFormat } from './Complex' ; import VectorEntry from './VectorEntry' ; import Dimension from './Dimension' ; import Basis from './Basis' ; import { IColumnOrRow , IEntryIndexValue , IKetComponent } from './interfaces' ; see Complex and  Dimension export default class Vector { readonly entries : VectorEntry [ ] ; readonly dimensions : Dimension [ ] ; /**
     * Creates a vector entry.
     * This is a low-level method (due to the explicit use of  VectorEntry ).
     * You may need  Vector . fromArray or  Vector . fromSparseCoordNames instead.
     *  param entries param dimensions constructor ( entries , dimensions ) { this . entries = entries ; this . dimensions = dimensions ; const size = this . size ; for ( const entry of entries ) { checkCoordsSizesCompability ( entry . coord , size ) ; } } returns see Dimension get size ( ) : number [ ] { return this . dimensions . map ( ( dimension ) => dimension . size ) ; } returns get totalSize ( ) : number { return this . dimensions . reduce ( ( a , dim ) => a * dim . size , 1 ) ; } returns see Dimension get names ( ) : string [ ] { return this . dimensions . map ( ( dimension ) => dimension . name ) ; } returns Coordinate names for each  Dimension .
      get coordNames ( ) : string [ ] [ ] { return this . dimensions . map ( ( dimension ) => dimension . coordNames ) ; } note returns normSquared ( ) { return this . entries . map ( ( entry ) => entry . value . abs2 ( ) ) . reduce ( ( a , b ) => a + b , 0 ) ; } note returns get norm ( ) : number { return Math . sqrt ( this . normSquared ( ) ) ; } todo copy ( ) { return _ . cloneDeep ( this ) ; } /**
     * Sort entires in-place (internal, important for diplay formats)
     */ sortedEntries ( ) { return this . entries . sort ( ( a , b ) => coordsToIndex ( a . coord , this . size ) - coordsToIndex ( b . coord , this . size ) ) ; } returns conj ( ) { const entries = this . entries . map ( ( entry ) => new VectorEntry ( [ ... entry . coord ] , entry . value . conj ( ) ) ) ; return new Vector ( entries , this . dimensions ) ; } returns normalize ( ) { const norm = Math . sqrt ( this . normSquared ( ) ) ; if ( norm === 0 ) { throw new Error ( 'Cannot normalize a zero-length vector!' ) ; } return this . mulConstant ( Cx ( 1 / norm ) ) ; } param c returns mulConstant ( c ) { const entries = this . entries . map ( ( entry ) => new VectorEntry ( entry . coord , entry . value . mul ( c ) ) ) ; return new Vector ( entries , this . dimensions ) ; } param x returns mulByReal ( x ) { return this . mulConstant ( Cx ( x ) ) ; } param v2 returns add ( v2 ) { const v1 = this ; Dimension . checkDimensions ( v1 . dimensions , v2 . dimensions ) ; const entriesByCoord = { } ; for ( const entry of v1 . entries ) { const key = entry . coord . toString ( ) ; entriesByCoord [ key ] = entry ; } for ( const entry of v2 . entries ) { const key = entry . coord . toString ( ) ; if ( entriesByCoord . hasOwnProperty ( key ) ) { const value = entriesByCoord [ key ] . value . add ( entry . value ) ; if ( value . isAlmostZero ( ) ) { delete entriesByCoord [ key ] ; } else { entriesByCoord [ key ] = new VectorEntry ( entry . coord , value ) ; } } else { entriesByCoord [ key ] = entry ; } } return new Vector ( Object . values ( entriesByCoord ) , v1 . dimensions ) ; } param v2 returns sub ( v2 ) { return this . add ( v2 . mulConstant ( Cx ( - 1 ) ) ) ; } see https param v2 returns dot ( v2 ) { const v1 = this ; Dimension . checkDimensions ( v1 . dimensions , v2 . dimensions ) ; let dotSum = Cx ( 0 , 0 ) ; const entriesByCoord = { } ; for ( const entry of v1 . entries ) { const key = entry . coord . toString ( ) ; entriesByCoord [ key ] = entry ; } for ( const entry of v2 . entries ) { const key = entry . coord . toString ( ) ; if ( entriesByCoord . hasOwnProperty ( key ) ) { dotSum = dotSum . add ( entriesByCoord [ key ] . value . mul ( entry . value ) ) ; } } return dotSum ; } param v2 returns inner ( v2 ) { return this . conj ( ) . dot ( v2 ) ; } /**
     * Map values
     */ mapValues ( func ) { const entries = this . entries . map ( ( entry ) => new VectorEntry ( entry . coord , func ( entry . value ) ) ) ; return new Vector ( entries , this . dimensions ) ; } param coordIndices toGroupedByCoords ( coordIndices ) { const complementIndices = indicesComplement ( coordIndices , this . dimensions . length ) ; const contractionDimensions = _ . at ( this . dimensions , coordIndices ) ; return _ ( this . entries ) . groupBy ( ( entry ) => _ . at ( entry . coord , complementIndices ) ) . values ( ) . map ( ( entries ) => { const coord = _ . at ( entries [ 0 ] . coord , complementIndices ) ; const vecEntries = entries . map ( ( entry ) => new VectorEntry ( _ . at ( entry . coord , coordIndices ) , entry . value ) ) ; const vector = new Vector ( vecEntries , contractionDimensions ) ; return { coord , vector } ; } ) . value ( ) ; } param coordIndices param v returns dotPartial ( coordIndices , v ) { const groupedByCoords = v . toGroupedByCoords ( coordIndices ) ; const contractionDimensions = groupedByCoords [ 0 ] . vector . dimensions ; const complementDimensions = v . dimensions . filter ( ( _dim , i ) => ! _ . includes ( coordIndices , i ) ) ; Dimension . checkDimensions ( this . dimensions , contractionDimensions ) ; const vecEntries = groupedByCoords . map ( ( row ) => new VectorEntry ( row . coord , this . dot ( row . vector ) ) ) . filter ( ( entry ) => ! entry . value . isAlmostZero ( ) ) ; return new Vector ( vecEntries , complementDimensions ) ; } param v2 returns innerPartial ( coordIndices , v2 ) { return this . conj ( ) . dotPartial ( coordIndices , v2 ) ; } param v2 returns todo outer ( v2 ) { const v1 = this ; const dimensions = v1 . dimensions . concat ( v2 . dimensions ) ; const entries = [ ] ; v1 . entries . forEach ( ( entry1 ) => v2 . entries . forEach ( ( entry2 ) => entries . push ( entry1 . outer ( entry2 ) ) ) ) ; return new Vector ( entries , dimensions ) ; } param order permute ( order ) { if ( ! isPermutation ( order , this . dimensions . length ) ) { throw new Error ( `${ order } is not a valid permutation for ${ this . dimensions . length } dimensions.` ) ; } const dimensions = _ . at ( this . dimensions , order ) ; const entries = this . entries . map ( ( entry ) => new VectorEntry ( _ . at ( entry . coord , order ) , entry . value ) ) ; return new Vector ( entries , dimensions ) ; } param eps return isCloseToZero ( eps = 1e-6 ) { return Math . sqrt ( this . normSquared ( ) ) < eps ; } see Basis . fromString and  changeAllDimsOfVector param dimName param basisStr toBasisAll ( dimName , basisStr ) { const basis = Basis . fromString ( dimName , basisStr ) ; return basis . changeAllDimsOfVector ( this ) ; } see Complex . toString for formating options.
     *  param complexFormat param precision param separator param intro returns toString ( complexFormat = 'cartesian' , precision = 2 , separator = ' + ' , intro = true ) { const valueStr = this . sortedEntries ( ) . map ( ( entry ) => { const coordStr = entry . coord . map ( ( i , dim ) => this . coordNames [ dim ] [ i ] ) ; return `${ entry . value . toString ( complexFormat , precision ) } |${ coordStr }⟩` ; } ) . join ( separator ) ; if ( intro ) { const introStr = `Vector with ${ this . entries . length } entries ` + `of max size [${ this . size }] with dimensions [${ this . names }]` ; return `${ introStr }\n${ valueStr }\n` ; } else { return valueStr ; } } /**
     * Generates a string for kets.
     * See  Vector . toString for formatting options.
     *  param complexFormat param precision returns toKetString ( complexFormat = 'polarTau' , precision = 2 ) { return this . toString ( complexFormat , precision , ' + ' , false ) ; } returns toDense ( ) { const denseVector = Array ( this . totalSize ) . fill ( Cx ( 0 , 0 ) ) ; this . entries . forEach ( ( entry ) => { denseVector [ coordsToIndex ( entry . coord , this . size ) ] = entry . value ; } ) ; return denseVector ; } returns toIndexValues ( ) { return this . sortedEntries ( ) . map ( ( entry ) => ( { i : coordsToIndex ( entry . coord , this . size ) , v : entry . value , } ) ) ; } param probThreshold returns toKetComponents ( probThreshold = 1e-4 ) { return this . sortedEntries ( ) . map ( ( entry ) : IKetComponent => ( { amplitude : entry . value , coordStrs : entry . coord . map ( ( c , dim ) => this . coordNames [ dim ] [ c ] ) , } ) ) . filter ( ( d ) => d . amplitude . abs2 ( ) > probThreshold ) ; } example param denseArray param dimensions param removeZeros static fromArray ( denseArray , dimensions , removeZeros = true ) { const sizes = dimensions . map ( ( dimension ) => dimension . size ) ; const totalSize = dimensions . reduce ( ( a , dim ) => a * dim . size , 1 ) ; if ( denseArray . length !== totalSize ) { throw new Error ( `Dimension inconsistency: entry count ${ denseArray . length } != total: ${ totalSize }` ) ; } const entries = denseArray . map ( ( value , index ) : [ number , Complex ] => [ index , value ] ) . filter ( ( [ _index , value ] ) : boolean => ! removeZeros || ! value . isZero ( ) ) . map ( ( [ index , value ] ) : VectorEntry => VectorEntry . fromIndexValue ( index , sizes , value ) ) ; return new Vector ( entries , dimensions ) ; } param dimensions static zeros ( dimensions ) { return new Vector ( [ ] , dimensions ) ; } param dimensions static ones ( dimensions ) { const sizes = dimensions . map ( ( dim ) => dim . size ) ; const size = sizes . reduce ( ( a , b ) => a * b , 1 ) ; const values = Array ( size ) . fill ( Cx ( 1 , 0 ) ) ; const entries = values . map ( ( v , i ) => new VectorEntry ( coordsFromIndex ( i , sizes ) , v ) ) ; return new Vector ( entries , dimensions ) ; } param dimensions static random ( dimensions ) { const sizes = dimensions . map ( ( dim ) => dim . size ) ; const size = sizes . reduce ( ( a , b ) => a * b , 1 ) ; const entries = _ . range ( size ) . map ( ( i ) => new VectorEntry ( coordsFromIndex ( i , sizes ) , Complex . randomGaussian ( ) ) ) ; return new Vector ( entries , dimensions ) . normalize ( ) ; } /**
     * A normalized vector, from uniform distribution of U(n),
     * restricted only to indices of vector.
     * Think of it as  Vector . random optimized for inner products with the vector.
      randomOnSubspace ( ) { const entries = this . entries . map ( ( entry ) => new VectorEntry ( entry . coord , Complex . randomGaussian ( ) ) ) ; return new Vector ( entries , this . dimensions ) . normalize ( ) ; } /**
     * A normalized vector, from uniform distribution of U(n),
     * restricted only to indices of vector,
     * on a tensor component.
     * Think of it as  Vector . random optimized for partial inner products with the vector, on a selected subspace.
     *  param coordIndices randomOnPartialSubspace ( coordIndices ) { const contractionDimensions = _ . at ( this . dimensions , coordIndices ) ; const partialCoords = this . entries . map ( ( entry ) => _ . at ( entry . coord , coordIndices ) ) ; const partialCoordsUniq = _ . uniqBy ( partialCoords , ( coord ) => coord . join ( ',' ) ) ; const entries = partialCoordsUniq . map ( ( coord ) => new VectorEntry ( coord , Complex . randomGaussian ( ) ) ) ; return new Vector ( entries , contractionDimensions ) . normalize ( ) ; } z static scalar ( z = Cx ( 1 ) ) { return new Vector ( [ new VectorEntry ( [ ] , z ) ] , [ ] ) ; } example param dimensions param coordNames note static indicator ( dimensions , coordNames ) { const coords = Dimension . stringToCoordIndices ( coordNames , dimensions ) ; const entries = [ new VectorEntry ( coords , Cx ( 1 ) ) ] ; return new Vector ( entries , dimensions ) ; } example param stringedEntries param dimensions returns static fromSparseCoordNames ( stringedEntries , dimensions ) { const entries = stringedEntries . map ( ( [ coordNameStr , value ] ) => new VectorEntry ( Dimension . stringToCoordIndices ( coordNameStr , dimensions ) , value ) ) ; return new Vector ( entries , dimensions ) ; } see Vector . outer for the actual implementation.
     *
     *  param ops returns todo static outer ( vectors ) { return vectors . reduce ( ( acc , x ) => acc . outer ( x ) ) ; } see Vector . add for the actual implementation.
     *
     *  param ops returns todo static add ( vectors ) { return vectors . reduce ( ( acc , x ) => acc . add ( x ) ) ; } }