/// <summary> /// Gets coordinates of vectors with respect to a new basis /// given the coordinates /// with respect to another basis. /// </summary> /// <param name="newBasis"> /// The basis which the new coordinates must be /// referred to. /// </param> /// <param name="currentCoordinates"> /// The current coordinates. /// </param> /// <param name="currentBasis"> /// The current basis. /// </param> /// <remarks> /// <para> /// Each row in <paramref name="currentCoordinates"/> is interpreted as /// the coordinates of a point. /// Hence, a matrix is returned having the same dimensions of /// <paramref name="currentCoordinates"/>, in which the <i>i</i>-th row /// represents the /// coordinates of the <i>i</i>-th point with respect to the new basis. /// </para> /// <para> /// Let <latex>C</latex> and <latex>N</latex> be the /// <see cref="GetBasisMatrix">matrix representations</see> /// of <paramref name="currentBasis"/>, and <paramref name="newBasis"/>, /// respectively, and let <latex>\bc{C}{X}</latex> be /// <paramref name="currentCoordinates"/>, i.e. the /// coordinates matrix w.r.t. basis <latex>\basis{C}</latex> of the points /// <latex>x_1,\dots,x_n</latex> under study: /// <latex mode="display"> /// \bc{C}{X}=\mx{ /// \T{\bc{C}{x_1}} \\ /// \vdots \\ /// \T{\bc{C}{x_n}} }. /// </latex> /// Then /// method <see cref="ChangeCoordinates(Basis, DoubleMatrix, Basis)"/> /// returns the matrix /// <latex mode="display"> /// \bc{N}{X} = \bc{C}{X}\, \T{C}\, \InvT{N}. /// </latex> /// </para> /// </remarks> /// <returns> /// A matrix of coordinates in the new basis. /// </returns> /// <exception cref="ArgumentNullException"> /// <paramref name="currentCoordinates"/> is <b>null</b>.<br/> /// -or-<br/> /// <paramref name="newBasis"/> is <b>null</b>.<br/> /// -or-<br/> /// <paramref name="currentBasis"/> is <b>null</b>. /// </exception> /// <exception cref="ArgumentOutOfRangeException"> /// The <see cref="Dimension"/> of /// <paramref name="currentBasis"/> is /// not equal to the dimension of <paramref name="newBasis"/>.<br/> /// -or-<br/> /// The number of columns of <paramref name="currentCoordinates"/> is /// not equal to the <see cref="Dimension"/> /// of <paramref name="newBasis"/>. /// </exception> public static DoubleMatrix ChangeCoordinates(Basis newBasis, DoubleMatrix currentCoordinates, Basis currentBasis) { #region Input validation if (newBasis is null) { throw new ArgumentNullException(nameof(newBasis)); } if (currentCoordinates is null) { throw new ArgumentNullException(nameof(currentCoordinates)); } if (currentBasis is null) { throw new ArgumentNullException(nameof(currentBasis)); } int k = newBasis.Dimension; if (currentBasis.Dimension != k) { throw new ArgumentOutOfRangeException( nameof(newBasis), ImplementationServices.GetResourceString( "STR_EXCEPT_PAR_BASES_MUST_SHARE_DIMENSION")); } if (currentCoordinates.NumberOfColumns != k) { throw new ArgumentOutOfRangeException( nameof(currentCoordinates), ImplementationServices.GetResourceString( "STR_EXCEPT_PAR_MUST_BE_BASIS_COMPLIANT_MATRIX")); } #endregion return(currentCoordinates * currentBasis.basisMatrixT / newBasis.basisMatrixT); }
/// <summary> /// Initializes a new instance of the <see cref="Cloud"/> class /// that contains points having the specified weights and whose /// coordinates are taken with respect to the given basis, with /// coordinates and weights eventually copied before instantiation. /// </summary> /// <param name="coordinates"> /// The coordinates of the cloud points.</param> /// <param name="weights"> /// The weights of the cloud points.</param> /// <param name="basis"> /// The basis which the point coordinates are /// referred to.</param> /// <param name="copyData"> /// <c>true</c> if <paramref name="coordinates"/> and <paramref name="weights"/> /// must be copied before instantiation; otherwise <c>false</c>. /// </param> /// <remarks> /// <para> /// Matrix <paramref name="coordinates"/> has as many rows as the /// number of points in the cloud. /// The number of columns is the dimension of the space in which /// the points lie. /// </para> /// <para> /// Each row represents the coordinates of a given point in the /// cloud. Points are thus well ordered, and hence thoroughly /// identified, by the index of the row in which its coordinates /// are stored. /// As a consequence, the same order must be followed /// when inserting entries in the vector of weights. /// </para> /// <para> /// The <see cref="Cloud(DoubleMatrix, DoubleMatrix, Basis, bool)"/> /// constructor prevents the copy /// of the elements in <paramref name="coordinates"/> and /// <paramref name="weights"/> before instantiation /// if <paramref name="copyData"/> evaluates to <c>false</c>: the /// returned <see cref="Cloud"/> instance will instead use a direct /// reference to <paramref name="coordinates"/> and <paramref name="weights"/>. /// </para> /// <para> /// <note type="caution"> /// This constructor is intended for advanced users and must always be used /// carefully. /// Do not use this constructor if you do not have complete control of /// the <paramref name="coordinates"/> and <paramref name="weights"/> instances. /// Once such instances are passed to the constructor as arguments, they must be /// treated as read-only objects outside the returned <see cref="Cloud"/> instance: /// you shouldn't manipulate entries via /// a direct reference to <paramref name="coordinates"/> or <paramref name="weights"/>; /// otherwise, the /// behavior of the returned <see cref="Cloud"/> instance /// must be considered as undefined and /// almost surely prone to errors. /// </note> /// </para> /// </remarks> /// <exception cref="ArgumentNullException"> /// <paramref name="coordinates"/> is <b>null</b>.<br/> /// -or-<br/> /// <paramref name="weights"/> is <b>null</b>.<br/> /// -or-<br/> /// <paramref name="basis"/> is <b>null</b>. /// </exception> /// <exception cref="ArgumentOutOfRangeException"> /// <paramref name="weights"/> is not a column vector.<br/> /// -or-<br/> /// <paramref name="coordinates"/> /// and <paramref name="weights"/> have unequal numbers of rows.<br/> /// -or-<br/> /// The <see cref="Basis.Dimension"/> of <paramref name="basis"/> /// is not equal to the number of columns /// of <paramref name="coordinates"/>. /// </exception> public Cloud( DoubleMatrix coordinates, DoubleMatrix weights, Basis basis, bool copyData) { #region Input validation if (coordinates is null) { throw new ArgumentNullException(nameof(coordinates)); } if (weights is null) { throw new ArgumentNullException(nameof(weights)); } if (basis is null) { throw new ArgumentNullException(nameof(basis)); } if (!weights.IsColumnVector) { throw new ArgumentOutOfRangeException( nameof(weights), ImplementationServices.GetResourceString( "STR_EXCEPT_PAR_MUST_BE_COLUMN_VECTOR")); } int n = coordinates.NumberOfRows; if (weights.NumberOfRows != n) { throw new ArgumentOutOfRangeException( nameof(weights), string.Format( CultureInfo.InvariantCulture, ImplementationServices.GetResourceString( "STR_EXCEPT_PAR_MUST_HAVE_SAME_NUM_OF_ROWS"), nameof(coordinates))); } double sum = 0.0, weight; for (int i = 0; i < weights.Count; i++) { weight = weights[i]; if (weight < 0) { throw new ArgumentOutOfRangeException( nameof(weights), ImplementationServices.GetResourceString( "STR_EXCEPT_PAR_ENTRIES_MUST_BE_NON_NEGATIVE")); } sum += weight; } if (Math.Abs(sum - 1.0) > 1.0e-3) { throw new ArgumentOutOfRangeException( nameof(weights), ImplementationServices.GetResourceString( "STR_EXCEPT_PAR_ENTRIES_MUST_SUM_TO_1")); } int k = coordinates.NumberOfColumns; if (basis.Dimension != k) { throw new ArgumentOutOfRangeException( nameof(basis), string.Format( CultureInfo.InvariantCulture, ImplementationServices.GetResourceString( "STR_EXCEPT_PAR_MUST_HAVE_SAME_NUM_OF_COLUMNS"), nameof(coordinates))); } #endregion if (copyData) { this.coordinates = coordinates.Clone(); this.weights = weights.Clone(); } else { this.coordinates = coordinates; this.weights = weights; } this.Basis = basis; #region Statistics // Mean var w_s = this.weights; var x_sa = this.coordinates; var m_sa_t = w_s.Transpose() * x_sa; this.Mean = m_sa_t.AsReadOnly(); // Variance var m_sa = m_sa_t.Transpose(); var q_a = this.Basis.basisScalarProducts; var variance = 0.0; DoubleMatrix coords; for (int i = 0; i < w_s.Count; i++) { coords = x_sa[i, ":"]; variance += w_s[i] * (coords * q_a * coords.Transpose())[0]; } variance -= (m_sa_t * q_a * m_sa)[0]; this.Variance = variance; // Covariance var diag_w_s = DoubleMatrix.Diagonal(w_s); var x_sa_t = x_sa.Transpose(); this.Covariance = ((x_sa_t * diag_w_s * x_sa) - (m_sa * m_sa_t)).AsReadOnly(); #endregion }