Skip to content
Draft
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
100 changes: 67 additions & 33 deletions cpp/include/qdk/chemistry/data/wavefunction.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -249,31 +249,6 @@ class WavefunctionContainer {
*/
virtual std::unique_ptr<WavefunctionContainer> clone() const = 0;

/**
* @brief Get all coefficients
* @return Vector of all coefficients (real or complex)
*/
virtual const VectorVariant& get_coefficients() const = 0;

/**
* @brief Get coefficient for a specific determinant
* @param det Configuration/determinant to get coefficient for
* @return Scalar coefficient (real or complex)
*/
virtual ScalarVariant get_coefficient(const Configuration& det) const = 0;

/**
* @brief Get all determinants in the wavefunction
* @return Vector of all configurations/determinants
*/
virtual const DeterminantVector& get_active_determinants() const = 0;

/**
* @brief Get number of determinants
* @return Number of determinants in the wavefunction
*/
virtual size_t size() const = 0;

/**
* @brief Calculate overlap with another wavefunction
* @param other Other wavefunction container
Expand Down Expand Up @@ -439,7 +414,7 @@ class WavefunctionContainer {
* @brief Convert container to JSON format
* @return JSON object containing container data
*/
virtual nlohmann::json to_json() const = 0;
nlohmann::json to_json() const;

/**
* @brief Load container from JSON format
Expand All @@ -455,7 +430,7 @@ class WavefunctionContainer {
* @param group HDF5 group to write container data to
* @throws std::runtime_error if HDF5 I/O error occurs
*/
virtual void to_hdf5(H5::Group& group) const;
void to_hdf5(H5::Group& group) const;

/**
* @brief Load container from HDF5 group
Expand All @@ -471,6 +446,11 @@ class WavefunctionContainer {
*/
virtual std::string get_container_type() const = 0;

/**
* @brief Build a human-readable summary string
*/
std::string get_summary() const;

/**
* @brief Get reference to orbital basis set
* @return Shared pointer to orbitals
Expand All @@ -489,12 +469,6 @@ class WavefunctionContainer {
*/
virtual bool is_complex() const = 0;

/**
* @brief Check if this container has coefficients data
* @return True if coefficients are available, false otherwise
*/
virtual bool has_coefficients() const { return false; }

/**
* @brief Check if this container has configuration set data
* @return True if configuration set is available, false otherwise
Expand All @@ -518,6 +492,17 @@ class WavefunctionContainer {
/// Serialization version
static constexpr const char* SERIALIZATION_VERSION = "0.1.0";

/// Subclass hook for ``to_hdf5``: write container-specific fields.
virtual void _to_hdf5_impl(H5::Group& group) const = 0;

/// Subclass hook for ``to_json``: return container-specific fields, merged
/// (top-level keys) into the main JSON object.
virtual nlohmann::json _to_json_impl() const = 0;

/// Subclass hook for ``get_summary``: return container-specific lines
/// (each terminated with ``\n``) appended to the universal summary.
virtual std::string _get_summary_impl() const { return ""; }

/**
* @brief Check if the system uses restricted orbitals with a closed-shell
* (singlet) configuration, i.e. equal alpha and beta active electrons.
Expand Down Expand Up @@ -588,6 +573,55 @@ class WavefunctionContainer {
_deserialize_rdms_from_json(const nlohmann::json& j);
};

/**
* @brief Base class for wavefunctions represented as a linear expansion in
* Slater determinants
*
* Guarantees that determinants and their coefficients are stored and directly
* accessible. Use this type to dispatch on wavefunctions with an explicit
* determinantal representation.
*/
class DeterminantalWavefunctionContainer : public WavefunctionContainer {

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  1. This should live in it's own header
  2. We're going to generalize this concept in a future PR to take non-determinantal statevectors. I'd prefer that this indicates something like a StateVectorContainer or similar.

public:
using MatrixVariant = ContainerTypes::MatrixVariant;
using VectorVariant = ContainerTypes::VectorVariant;
using ScalarVariant = ContainerTypes::ScalarVariant;
using DeterminantVector = ContainerTypes::DeterminantVector;

using WavefunctionContainer::WavefunctionContainer;

~DeterminantalWavefunctionContainer() override = default;

/**
* @brief Get all determinant coefficients
* @return Vector of all coefficients (real or complex)
*/
virtual const VectorVariant& get_coefficients() const = 0;

/**
* @brief Get coefficient for a specific determinant
* @param det Determinant to look up (active space only)
* @return Scalar coefficient (real or complex)
*/
virtual ScalarVariant get_coefficient(const Configuration& det) const = 0;

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Things to do with Configuration should no longer live in the public Wavefunction API as not all instances support it. This is the wrong place to comment on it, but I don't want to page through this file to find the right line.


/**
* @brief Get all determinants in the wavefunction
* @return Vector of determinants, in the same order as ``get_coefficients``
*/
virtual const DeterminantVector& get_active_determinants() const = 0;

/**
* @brief Get number of determinants in the expansion
*/
virtual size_t size() const = 0;

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Size has to be polymorphic from the top-level API - currently size downcasts to the determinant container, which will fail for unnecessary reasons.

@mmoerchen mmoerchen May 12, 2026

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Okay, but what should size() return for a non-determinantal wavefunction?
I move the size method to the StateVectorContainer, which is why I downcast.


protected:
void _to_hdf5_impl(H5::Group& group) const override;
nlohmann::json _to_json_impl() const override;
std::string _get_summary_impl() const override;
};

/**
* @brief Main wavefunction class that wraps container implementations
*
Expand Down
27 changes: 14 additions & 13 deletions cpp/include/qdk/chemistry/data/wavefunction_containers/cas.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@

namespace qdk::chemistry::data {

class CasWavefunctionContainer : public WavefunctionContainer {
class CasWavefunctionContainer : public DeterminantalWavefunctionContainer {
public:
// Use real values for default CAS
using MatrixVariant = ContainerTypes::MatrixVariant;
Expand Down Expand Up @@ -198,29 +198,30 @@ class CasWavefunctionContainer : public WavefunctionContainer {
*/
void clear_caches() const override;

/**
* @brief Convert container to JSON format
* @return JSON object containing container data
*/
nlohmann::json to_json() const override;

Comment on lines -201 to -206

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there a reason to prefer to the _to_json_impl design pattern rather than the previous motif?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This way, each class only has to handle the serialization for its specific data structures instead of implementing the logic of common data (like RDMs, entropies, etc.) as well.

/**
* @brief Get container type identifier for serialization
* @return String "cas"
*/
std::string get_container_type() const override;

/**
* @brief Check if the wavefunction is complex-valued
* @return True if coefficients are complex, false if real
* @brief Deserialize from JSON
* @throws std::runtime_error if JSON does not describe a CAS container
*/
bool is_complex() const override;
static std::unique_ptr<CasWavefunctionContainer> from_json(
const nlohmann::json& j);

/**
* @brief Check if this container has coefficients data
* @return True if coefficients are available, false otherwise
* @brief Deserialize from HDF5
* @throws std::runtime_error if group does not describe a CAS container
*/
bool has_coefficients() const override;
static std::unique_ptr<CasWavefunctionContainer> from_hdf5(H5::Group& group);

/**
* @brief Check if the wavefunction is complex-valued
* @return True if coefficients are complex, false if real
*/
bool is_complex() const override;

/**
* @brief Check if this container has configuration set data
Expand Down
44 changes: 24 additions & 20 deletions cpp/include/qdk/chemistry/data/wavefunction_containers/cc.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -97,20 +97,32 @@ class CoupledClusterContainer : public WavefunctionContainer {
* @return Shared pointer to wavefunction
*/
std::shared_ptr<Wavefunction> get_wavefunction() const;

/**
* @brief Not implemented for CC wavefunctions
* @brief Get CI coefficients generated lazily from CC amplitudes
*
* Truncates the expansion at fourth order. Result is cached after first
* computation.
*
* @return Reference to vector of CI coefficients
*/
const VectorVariant& get_coefficients() const override;
const VectorVariant& get_coefficients() const;

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

One of the main results of this PR should be that amplitudes should be treated differently than state vector coefficients.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, CC and MP2 now only inherit from the base container and aren't required to provide these methods. They do have CI coefficients implemented, so I kept it.

Should we nix these functions to keep the API cleaner, or rename it to make it more explicit that it involves computation (e.g., compute_ci_expansion())?


/**
* @brief Not implemented for CC wavefunctions
* @brief Get coefficient for a specific determinant
*
* Triggers lazy CI expansion on first call.
*
* @param det Configuration to look up
* @return Coefficient value (zero if determinant not in expansion)
*/
ScalarVariant get_coefficient(const Configuration& det) const override;
ScalarVariant get_coefficient(const Configuration& det) const;

/**
* @brief Not implemented for CC wavefunctions
* @brief Get determinants generated lazily from CC amplitudes
* @return Reference to vector of determinant configurations
*/
const DeterminantVector& get_active_determinants() const override;
const DeterminantVector& get_active_determinants() const;

/**
* @brief Get T1 amplitudes
Expand Down Expand Up @@ -151,7 +163,7 @@ class CoupledClusterContainer : public WavefunctionContainer {
* @throws std::runtime_error Always throws as this is not meaningful for CC
* wavefunctions
*/
size_t size() const override;
size_t size() const;

/**
* @brief Not implemented for CC wavefunctions
Expand Down Expand Up @@ -217,12 +229,6 @@ class CoupledClusterContainer : public WavefunctionContainer {
*/
void clear_caches() const override;

/**
* @brief Convert container to JSON format
* @return JSON object containing container data
*/
nlohmann::json to_json() const override;

/**
* @brief Load container from JSON format
* @param j JSON object containing container data
Expand All @@ -232,13 +238,6 @@ class CoupledClusterContainer : public WavefunctionContainer {
static std::unique_ptr<CoupledClusterContainer> from_json(
const nlohmann::json& j);

/**
* @brief Convert container to HDF5 group
* @param group HDF5 group to write container data to
* @throws std::runtime_error if HDF5 I/O error occurs
*/
void to_hdf5(H5::Group& group) const override;

/**
* @brief Load container from HDF5 group
* @param group HDF5 group containing container data
Expand Down Expand Up @@ -353,6 +352,11 @@ class CoupledClusterContainer : public WavefunctionContainer {
*/
const VectorVariant& get_active_two_rdm_spin_traced() const override;

protected:
void _to_hdf5_impl(H5::Group& group) const override;
nlohmann::json _to_json_impl() const override;
std::string _get_summary_impl() const override;

private:
// Orbital information
std::shared_ptr<Orbitals> _orbitals;
Expand Down
25 changes: 9 additions & 16 deletions cpp/include/qdk/chemistry/data/wavefunction_containers/mp2.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -88,15 +88,15 @@ class MP2Container : public WavefunctionContainer {
*
* @return Reference to vector of CI coefficients
*/
const VectorVariant& get_coefficients() const override;
const VectorVariant& get_coefficients() const;

/**
* @brief Get coefficient for a specific determinant
* @param det Configuration to look up
* @return Coefficient value
* @throws std::runtime_error if determinant is not found
*/
ScalarVariant get_coefficient(const Configuration& det) const override;
ScalarVariant get_coefficient(const Configuration& det) const;

/**
* @brief Get active determinants from MP2 wavefunction
Expand All @@ -108,7 +108,7 @@ class MP2Container : public WavefunctionContainer {
*
* @return Reference to vector of determinant configurations
*/
const DeterminantVector& get_active_determinants() const override;
const DeterminantVector& get_active_determinants() const;

/**
* @brief Get T1 amplitudes
Expand Down Expand Up @@ -145,7 +145,7 @@ class MP2Container : public WavefunctionContainer {
*
* @return Number of determinants
*/
size_t size() const override;
size_t size() const;

/**
* @brief Not implemented for MP2 wavefunctions
Expand Down Expand Up @@ -200,25 +200,13 @@ class MP2Container : public WavefunctionContainer {
*/
void clear_caches() const override;

/**
* @brief Serialize to JSON
* @return JSON representation of the container
*/
nlohmann::json to_json() const override;

/**
* @brief Deserialize from JSON
* @param j JSON object
* @return Unique pointer to MP2Container
*/
static std::unique_ptr<MP2Container> from_json(const nlohmann::json& j);

/**
* @brief Serialize to HDF5
* @param group HDF5 group to write to
*/
void to_hdf5(H5::Group& group) const override;

/**
* @brief Deserialize from HDF5
* @param group HDF5 group to read from
Expand Down Expand Up @@ -322,6 +310,11 @@ class MP2Container : public WavefunctionContainer {
*/
const VectorVariant& get_active_two_rdm_spin_traced() const override;

protected:
void _to_hdf5_impl(H5::Group& group) const override;
nlohmann::json _to_json_impl() const override;
std::string _get_summary_impl() const override;

private:
/** @brief Cached coefficients */
VectorVariant _cached_coefficients;
Expand Down
Loading
Loading