Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
52 changes: 52 additions & 0 deletions include/tensorwrapper/generate/add_noise.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
/*
* Copyright 2026 NWChemEx-Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

#pragma once
#include <cstdint>
#include <random>
#include <tensorwrapper/tensor/tensor.hpp>

namespace tensorwrapper::generate {

/** @brief Adds clamped normal noise to each element of @p matrix.
*
* Draws `delta ~ Normal(0, t)` and clamps to `[-t, t]` before adding to each
* element. When @p t is zero the input is copied unchanged.
*
* @param[in] matrix The tensor to perturb.
* @param[in] t Non-negative noise scale (standard deviation and clamp bound).
* @param[in,out] gen Random number generator used for the normal draws.
*
* @return A new tensor with the same shape as @p matrix.
*
* @throw std::invalid_argument if @p t is negative.
*/
Tensor add_noise(const Tensor& matrix, double t, std::mt19937& gen);

/** @brief Overload of add_noise that creates its own RNG from @p seed.
*
* @param[in] matrix The tensor to perturb.
* @param[in] t Non-negative noise scale (standard deviation and clamp bound).
* @param[in] seed Seed for the internal random number generator. A value of
* zero selects a non-deterministic seed.
*
* @return A new tensor with the same shape as @p matrix.
*
* @throw std::invalid_argument if @p t is negative.
*/
Tensor add_noise(const Tensor& matrix, double t, std::uint64_t seed = 42);

} // namespace tensorwrapper::generate
31 changes: 31 additions & 0 deletions include/tensorwrapper/generate/generate.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
/*
* Copyright 2026 NWChemEx-Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

#pragma once
#include <tensorwrapper/generate/add_noise.hpp>
#include <tensorwrapper/generate/generate_eigen_system.hpp>
#include <tensorwrapper/generate/generate_eigenvalues.hpp>
#include <tensorwrapper/generate/generate_utils.hpp>
#include <tensorwrapper/generate/identity_matrix.hpp>
#include <tensorwrapper/generate/random_orthogonal_matrix.hpp>

/** @brief Namespace for utilities that generate synthetic tensors and matrices.
*
* The functions in this namespace create reproducible test matrices,
* eigenvalue
* spectra, and related quantities for unit testing and benchmarking.
*/
namespace tensorwrapper::generate {}
56 changes: 56 additions & 0 deletions include/tensorwrapper/generate/generate_eigen_system.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
/*
* Copyright 2026 NWChemEx-Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

#pragma once
#include <cstddef>
#include <tensorwrapper/generate/generate_utils.hpp>
#include <tensorwrapper/tensor/tensor.hpp>

namespace tensorwrapper::generate {

/** @brief A symmetric matrix together with its eigen-decomposition.
*
* The member @p matrix satisfies @f$M = Q D Q^T@f$ where @p eigenvectors is
* @f$Q@f$ and @p eigenvalues holds the diagonal entries of @f$D@f$.
*/
struct EigenSystem {
/// Dimension of the square matrix.
std::size_t n = 0;
/// Rank-1 tensor of length @p n containing the eigenvalues.
Tensor eigenvalues;
/// Rank-2 tensor of shape `(n, n)` whose columns are the eigenvectors.
Tensor eigenvectors;
/// Rank-2 tensor of shape `(n, n)` representing the symmetric matrix.
Tensor matrix;
};

/** @brief Generates a reproducible symmetric matrix and its
* eigen-decomposition.
*
* Constructs @f$M = Q D Q^T@f$ where @f$D@f$ is built from eigenvalues
* generated according to @p spec and @f$Q@f$ is a random orthogonal matrix.
*
* @param[in] spec Parameters controlling the matrix dimension, spectrum, and
* random seed.
*
* @return An @ref EigenSystem populated with @p spec.n, the eigenvalues,
* eigenvectors, and the assembled matrix.
*
* @throw std::invalid_argument if @p spec.n is outside `[1, kMaxMatrixDim]`.
*/
EigenSystem generate_eigen_system(const SymmetricMatrixSpec& spec);
Comment thread
ryanmrichard marked this conversation as resolved.

} // namespace tensorwrapper::generate
43 changes: 43 additions & 0 deletions include/tensorwrapper/generate/generate_eigenvalues.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
/*
* Copyright 2026 NWChemEx-Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

#pragma once
#include <random>
#include <tensorwrapper/generate/generate_utils.hpp>
#include <tensorwrapper/tensor/tensor.hpp>

namespace tensorwrapper::generate {

/** @brief Generates a sorted rank-1 tensor of eigenvalues from @p spec.
*
* The spectrum spans
* @f$[\texttt{spec.min\_eigenvalue},
* \texttt{spec.min\_eigenvalue} \times \texttt{spec.condition\_number}]@f$
* using the spacing strategy given by @p spec.spacing.
*
* @param[in] spec Parameters controlling the eigenvalue distribution.
* @param[in,out] gen Random number generator used when @p spec.spacing
* requires random draws.
*
* @return A rank-1 tensor of length @p spec.n containing the eigenvalues in
* ascending order.
*
* @throw std::invalid_argument if @p spec.n is outside `[1, kMaxMatrixDim]` or
* if @p spec.spacing is invalid.
*/
Tensor generate_eigenvalues(const SymmetricMatrixSpec& spec, std::mt19937& gen);

} // namespace tensorwrapper::generate
103 changes: 103 additions & 0 deletions include/tensorwrapper/generate/generate_utils.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
/*
* Copyright 2026 NWChemEx-Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

#pragma once
#include <cstdint>
#include <random>
#include <stdexcept>

namespace tensorwrapper::generate {
Comment thread
ryanmrichard marked this conversation as resolved.

/// Maximum supported dimension for generated square matrices.
constexpr std::size_t kMaxMatrixDim = 10;

/** @brief Specifies how eigenvalues are distributed between the endpoints.
*
* Each spacing mode fills the interval
* @f$[\lambda_{\min}, \lambda_{\max}]@f$ with @f$n@f$ eigenvalues, where
* @f$\lambda_{\max} = \lambda_{\min} \times@f$ the condition number.
*/
enum class EigenvalueSpacing {
/// Uniform spacing with @f$\Delta\lambda = (\lambda_{\max} -
/// \lambda_{\min}) / (n - 1)@f$.
Linear,
/// Uniform spacing in log space with
/// @f$\Delta\log\lambda = \log(\lambda_{\max} / \lambda_{\min}) / (n -
/// 1)@f$.
Logarithmic,
/// Eigenvalues are grouped into clusters of width @p cluster_width that are
/// separated by @f$(\lambda_{\max} - \lambda_{\min}) / (n_{\text{clusters}}
/// - 1)@f$.
Clustered,
/// Same cluster centers as @ref Clustered, but all eigenvalues in a cluster
/// are identical.
Degenerate
};

/** @brief Parameters controlling the generation of a symmetric test matrix.
*
* The resulting matrix has eigenvalues in
* @f$[\texttt{min\_eigenvalue},
* \texttt{min\_eigenvalue} \times \texttt{condition\_number}]@f$ with the
* spacing determined by @p spacing.
*/
struct SymmetricMatrixSpec {
/// Dimension of the square matrix.
std::size_t n = 2;
/// Ratio of the largest to smallest eigenvalue.
double condition_number = 10.0;
/// Smallest eigenvalue in the spectrum.
double min_eigenvalue = 1.0;
/// Strategy used to distribute eigenvalues between the endpoints.
EigenvalueSpacing spacing = EigenvalueSpacing::Linear;
/// Number of eigenvalue clusters when @p spacing is @ref Clustered or
/// @ref Degenerate.
std::size_t n_clusters = 1;
/// Half-width of each cluster when @p spacing is @ref Clustered.
double cluster_width = 1e-6;
/// Seed for random number generation. A value of zero selects a
/// non-deterministic seed.
std::uint64_t seed = 42;
};

/** @brief Creates a Mersenne Twister RNG from @p seed.
*
* @param[in] seed The seed value. When zero, a seed is drawn from
* `std::random_device`.
*
* @return A `std::mt19937` generator initialized with @p seed.
*/
inline std::mt19937 make_rng(std::uint64_t seed) {
if(seed == 0) {
std::random_device rd;
return std::mt19937(rd());
}
return std::mt19937(static_cast<std::mt19937::result_type>(seed));
}

/** @brief Validates that @p n is an allowed matrix dimension.
*
* @param[in] n The dimension to validate.
*
* @throw std::invalid_argument if @p n is not in `[1, kMaxMatrixDim]`.
*/
inline void require_valid_n(std::size_t n) {
if(n < 1 || n > kMaxMatrixDim) {
throw std::invalid_argument("n must be in [1, kMaxMatrixDim]");
}
}

} // namespace tensorwrapper::generate
41 changes: 41 additions & 0 deletions include/tensorwrapper/generate/identity_matrix.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
/*
* Copyright 2026 NWChemEx-Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

#pragma once
#include <cstddef>
#include <tensorwrapper/generate/generate_utils.hpp>
#include <tensorwrapper/utilities/diagonal_matrix.hpp>
#include <tensorwrapper/utilities/make_tensor.hpp>
#include <vector>

namespace tensorwrapper::generate {

/** @brief Creates an @p n x @p n identity matrix.
*
* @param[in] n Matrix dimension. Must be in `[1, kMaxMatrixDim]`.
*
* @return A rank-2 tensor with ones on the diagonal and zeros elsewhere.
*
* @throw std::invalid_argument if @p n is outside the allowed range.
*/
inline Tensor identity_matrix(std::size_t n) {
require_valid_n(n);
std::vector<double> values(n, 1.0);
auto values_tensor = utilities::make_tensor({n}, values);
return utilities::diagonal_matrix(values_tensor);
}

} // namespace tensorwrapper::generate
37 changes: 37 additions & 0 deletions include/tensorwrapper/generate/random_orthogonal_matrix.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
/*
* Copyright 2026 NWChemEx-Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

#pragma once
#include <random>
#include <tensorwrapper/tensor/tensor.hpp>

namespace tensorwrapper::generate {

/** @brief Creates a random @p n x @p n orthogonal matrix.
*
* Draws entries from a standard normal distribution and applies Householder QR
* factorization to obtain an orthogonal matrix @f$Q@f$.
*
* @param[in] n Matrix dimension. Must be in `[1, kMaxMatrixDim]`.
* @param[in,out] gen Random number generator used for the normal draws.
*
* @return A rank-2 tensor whose columns form an orthonormal basis.
*
* @throw std::invalid_argument if @p n is outside the allowed range.
*/
Tensor random_orthogonal_matrix(std::size_t n, std::mt19937& gen);
Comment thread
ryanmrichard marked this conversation as resolved.

} // namespace tensorwrapper::generate
1 change: 1 addition & 0 deletions include/tensorwrapper/tensorwrapper.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
#include <tensorwrapper/detail_/detail_.hpp>
#include <tensorwrapper/diis/diis.hpp>
#include <tensorwrapper/dsl/dsl.hpp>
#include <tensorwrapper/generate/generate.hpp>
#include <tensorwrapper/layout/layout.hpp>
#include <tensorwrapper/operations/operations.hpp>
#include <tensorwrapper/shape/shape.hpp>
Expand Down
Loading
Loading