-
Notifications
You must be signed in to change notification settings - Fork 349
Dynamics cpp integration #2683
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Dynamics cpp integration #2683
Changes from all commits
Commits
Show all changes
77 commits
Select commit
Hold shift + click to select a range
59b584f
Adding cpp integration for dynamics
sacpis b026c91
Adding missing file dynamics_async_tester
sacpis b7e7c3d
Checking if vector has any value in methods with optional return type
sacpis bef5286
Removing optional, fixing unittests
sacpis b90619b
Adding internal check change
sacpis 88b3f48
Fixing spell check
sacpis 24d867a
Adding examples
sacpis a672149
Fixing spelling
sacpis b083add
Adding a descriptive message
sacpis 3a33622
Update runtime/common/EvolveResult.h
sacpis ad9d6f7
Formatting
sacpis 82e693f
removing rydberg test
sacpis 61f3a64
Removing unwanted check for --target for Python version
sacpis 24308de
Removing std::move from the return statement to allow the compiler to…
sacpis cba0881
Excluding *.cpp files from dynamics
sacpis da4fe7d
Passing the states as const &
sacpis fb71004
Including cudensitymat dir as public
sacpis 8c4ad6f
Fix DynamicsAsyncTester
1tnguyen 6113d40
Inclduing public CUDA toolkit directory
sacpis 145c055
Adding cublas for test_dynamics
sacpis 5044fb9
Adding cutensorlib
sacpis 30e2914
Adding a default constructor for the Schedule class which takes in ve…
sacpis 1d0e714
Updating comments
sacpis a616205
Adding cublas and cutensor
sacpis 50abd23
changing cudart_static to cudart to avoid linking libnvqir-dynamics t…
sacpis bdf8d44
Adding cpp integration for dynamics
sacpis b28fffb
Adding missing file dynamics_async_tester
sacpis df06f9f
Checking if vector has any value in methods with optional return type
sacpis 770e35c
Removing optional, fixing unittests
sacpis b09ffe7
Adding internal check change
sacpis 28c0e87
Fixing spell check
sacpis cd84ebd
Adding examples
sacpis 1b83722
Fixing spelling
sacpis 9ab74b8
Adding a descriptive message
sacpis 7eb8a03
Update runtime/common/EvolveResult.h
sacpis 14b12a4
Formatting
sacpis e086582
removing rydberg test
sacpis f5ca416
Removing unwanted check for --target for Python version
sacpis 1ed0765
Removing std::move from the return statement to allow the compiler to…
sacpis 3ccc8ca
Excluding *.cpp files from dynamics
sacpis c699d8b
Passing the states as const &
sacpis 6137ff6
Including cudensitymat dir as public
sacpis 260acc5
Fix DynamicsAsyncTester
1tnguyen 0bf04c5
Inclduing public CUDA toolkit directory
sacpis 3a401bd
Adding cublas for test_dynamics
sacpis cfd627f
Adding cutensorlib
sacpis 3a596c2
Adding a default constructor for the Schedule class which takes in ve…
sacpis c05e272
Updating comments
sacpis 7580bad
Adding cublas and cutensor
sacpis 58f29c0
changing cudart_static to cudart to avoid linking libnvqir-dynamics t…
sacpis 4759ed7
Skipping libnvqir-dynamics validation as one of its dependencies cont…
sacpis de10c30
Merge branch 'dynamics_cpp_integration' of github.com:sacpis/cuda-qua…
1tnguyen bf34576
Code refactor according to spec
1tnguyen 4c8fdde
Merge branch 'main' into dynamics_cpp_integration
sacpis 6f7a413
Accepting cudaq::dimensions_map instead of std::map
sacpis 9e32b5f
Merge remote-tracking branch 'origin/dynamics_cpp_integration' into d…
sacpis b5b91ba
Fix integrator class according to spec review: snake case for public …
1tnguyen 5c6e2b6
* Updating the evolve API signature using concepts (requires clause) to
sacpis 94bb9e4
Formatting
sacpis 3c299fe
Merge branch 'main' into dynamics_cpp_integration
sacpis 6015f4c
Updating type of additional value in the comment
sacpis 37749ca
Adding type constraint for remaining evolve APIs
sacpis 7b0acff
Refactor runge_kutta
1tnguyen 8367d9f
Add gpu_required label to dynamics C++ tests
1tnguyen 0b8c5ee
* Making Schedule class name snake case
sacpis be2d763
Fixing parameter type
sacpis 04b32e2
Merge remote-tracking branch 'origin/dynamics_cpp_integration' into d…
sacpis a6fa8e3
Fixing path to exclude
sacpis 894f363
Merge branch 'main' into dynamics_cpp_integration
sacpis 1ddee3c
Adding SFINAE for evolve API
sacpis 90faa72
Merge branch 'main' into dynamics_cpp_integration
sacpis ed58c02
Keeping the return value as optional for evolve_result
sacpis 63d0f4a
Merge remote-tracking branch 'origin/dynamics_cpp_integration' into d…
sacpis 9bbb3bb
Checking for C++ version to use requires or std::enable_if_t
sacpis 2452c51
Implementing feedback
sacpis 2b793ab
* Updating template constarint for C++20
sacpis bcdcf52
Merge branch 'main' into dynamics_cpp_integration
sacpis File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,140 @@ | ||
| /******************************************************************************* | ||
| * Copyright (c) 2022 - 2025 NVIDIA Corporation & Affiliates. * | ||
| * All rights reserved. * | ||
| * * | ||
| * This source code and the accompanying materials are made available under * | ||
| * the terms of the Apache License 2.0 which accompanies this distribution. * | ||
| ******************************************************************************/ | ||
|
|
||
| // Compile and run with: | ||
| // ``` | ||
| // nvq++ --target dynamics cavity_qed.cpp -o a.out && ./a.out | ||
| // ``` | ||
|
|
||
| #include "cudaq/algorithms/evolve.h" | ||
| #include "cudaq/dynamics_integrators.h" | ||
| #include "cudaq/operators.h" | ||
| #include "export_csv_helper.h" | ||
| #include <cudaq.h> | ||
|
|
||
| int main() { | ||
|
|
||
| // Dimension of our composite quantum system: | ||
| // subsystem 0 (atom) has 2 levels (ground and excited states). | ||
| // subsystem 1 (cavity) has 10 levels (Fock states, representing photon number | ||
| // states). | ||
| cudaq::dimension_map dimensions{{0, 2}, {1, 10}}; | ||
|
|
||
| // For the cavity subsystem 1 | ||
| // We create the annihilation (a) and creation (a+) operators. | ||
| // These operators lower and raise the photon number, respectively. | ||
| auto a = cudaq::boson_operator::annihilate(1); | ||
| auto a_dag = cudaq::boson_operator::create(1); | ||
|
|
||
| // For the atom subsystem 0 | ||
| // We create the annihilation (`sm`) and creation (`sm_dag`) operators. | ||
| // These operators lower and raise the excitation number, respectively. | ||
| auto sm = cudaq::boson_operator::annihilate(0); | ||
| auto sm_dag = cudaq::boson_operator::create(0); | ||
|
|
||
| // Number operators | ||
| // These operators count the number of excitations. | ||
| // For the atom (`subsytem` 0) and the cavity (`subsystem` 1) they give the | ||
| // population in each subsystem. | ||
| auto atom_occ_op = cudaq::matrix_operator::number(0); | ||
| auto cavity_occ_op = cudaq::matrix_operator::number(1); | ||
|
|
||
| // Hamiltonian | ||
| // The `hamiltonian` models the dynamics of the atom-cavity (cavity QED) | ||
| // system It has 3 parts: | ||
| // 1. Cavity energy: 2 * pi * photon_number_operator -> energy proportional to | ||
| // the number of photons. | ||
| // 2. Atomic energy: 2 * pi * atom_number_operator -> energy proportional to | ||
| // the atomic excitation. | ||
| // 3. Atomic-cavity interaction: 2 * pi * 0.25 * (`sm` * a_dag + `sm_dag` * a) | ||
| // -> represents the exchange of energy between the atom and the cavity. It is | ||
| // analogous to the Jaynes-Cummings model in cavity QED. | ||
| auto hamiltonian = (2 * M_PI * cavity_occ_op) + (2 * M_PI * atom_occ_op) + | ||
| (2 * M_PI * 0.25 * (sm * a_dag + sm_dag * a)); | ||
|
|
||
| // Build the initial state | ||
| // Atom (sub-system 0) in ground state. | ||
| // Cavity (sub-system 1) has 5 photons (Fock space). | ||
| // The overall Hilbert space is 2 * 10 = 20. | ||
| const int num_photons = 5; | ||
| std::vector<std::complex<double>> initial_state_vec(20, 0.0); | ||
| // The index is chosen such that the atom is in the ground state while the | ||
| // cavity is in the Fock state with 5 photons. | ||
| initial_state_vec[dimensions[0] * num_photons] = 1; | ||
|
|
||
| // Define a time evolution schedule | ||
| // We define a time grid from 0 to 10 (in arbitrary time units) with 201 time | ||
| // steps. This schedule is used by the integrator to simulate the dynamics. | ||
| const int num_steps = 201; | ||
| std::vector<double> steps = cudaq::linspace(0.0, 10.0, num_steps); | ||
| cudaq::schedule schedule(steps); | ||
|
|
||
| // Create a CUDA quantum state | ||
| // The initial state is converted into a quantum state object for evolution. | ||
| auto rho0 = cudaq::state::from_data(initial_state_vec); | ||
|
|
||
| // Numerical integrator | ||
| // Here we choose a Runge-`Kutta` method for time evolution. | ||
| // `dt` defines the time step for the numerical integration, and order 4 | ||
| // indicates a 4`th` order method. | ||
| cudaq::integrators::runge_kutta integrator(4, 0.01); | ||
|
|
||
| // Evolve without collapse operators | ||
| // This evolution is ideal (closed system) dynamics governed solely by the | ||
| // Hamiltonian. The expectation values of the observables (cavity photon | ||
| // number and atom excitation probability) are recorded. | ||
| cudaq::evolve_result evolve_result = | ||
| cudaq::evolve(hamiltonian, dimensions, schedule, rho0, integrator, {}, | ||
| {cavity_occ_op, atom_occ_op}, true); | ||
|
|
||
| // Adding dissipation | ||
| // To simulate a realistic scenario, we introduce decay (dissipation). | ||
| // Here, the collapse operator represents photon loss from the cavity. | ||
| constexpr double decay_rate = 0.1; | ||
| auto collapse_operator = std::sqrt(decay_rate) * a; | ||
| // Evolve with the collapse operator to incorporate the effect of decay. | ||
| cudaq::evolve_result evolve_result_decay = | ||
| cudaq::evolve(hamiltonian, dimensions, schedule, rho0, integrator, | ||
| {collapse_operator}, {cavity_occ_op, atom_occ_op}, true); | ||
|
|
||
| // Lambda to extract expectation values for a given observable index | ||
| // Here, index 0 corresponds to the cavity photon number and index 1 | ||
| // corresponds to the atom excitation probability. | ||
| auto get_expectation = [](int idx, auto &result) -> std::vector<double> { | ||
| std::vector<double> expectations; | ||
|
|
||
| auto all_exps = result.get_expectation_values().value(); | ||
| for (auto exp_vals : all_exps) { | ||
| expectations.push_back((double)exp_vals[idx]); | ||
| } | ||
| return expectations; | ||
| }; | ||
|
|
||
| // Retrieve expectation values from both the ideal and decaying `evolutions`. | ||
| auto ideal_result0 = get_expectation(0, evolve_result); | ||
| auto ideal_result1 = get_expectation(1, evolve_result); | ||
| auto decay_result0 = get_expectation(0, evolve_result_decay); | ||
| auto decay_result1 = get_expectation(1, evolve_result_decay); | ||
|
|
||
| // Export the results to `CSV` files | ||
| // "cavity_`qed`_ideal_result.`csv`" contains the ideal evolution results. | ||
| // "cavity_`qed`_decay_result.`csv`" contains the evolution results with | ||
| // cavity decay. | ||
| export_csv("cavity_qed_ideal_result.csv", "time", steps, | ||
| "cavity_photon_number", ideal_result0, | ||
| "atom_excitation_probability", ideal_result1); | ||
| export_csv("cavity_qed_decay_result.csv", "time", steps, | ||
| "cavity_photon_number", decay_result0, | ||
| "atom_excitation_probability", decay_result1); | ||
|
|
||
| std::cout << "Simulation complete. The results are saved in " | ||
| "cavity_qed_ideal_result.csv " | ||
| "and cavity_qed_decay_result.csv files." | ||
| << std::endl; | ||
| return 0; | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,164 @@ | ||
| /******************************************************************************* | ||
| * Copyright (c) 2022 - 2025 NVIDIA Corporation & Affiliates. * | ||
| * All rights reserved. * | ||
| * * | ||
| * This source code and the accompanying materials are made available under * | ||
| * the terms of the Apache License 2.0 which accompanies this distribution. * | ||
| ******************************************************************************/ | ||
|
|
||
| // Compile and run with: | ||
| // ``` | ||
| // nvq++ --target dynamics cavity_qed.cpp -o a.out && ./a.out | ||
| // ``` | ||
|
|
||
| #include "cudaq/algorithms/evolve.h" | ||
| #include "cudaq/dynamics_integrators.h" | ||
| #include "cudaq/operators.h" | ||
| #include "export_csv_helper.h" | ||
| #include <cudaq.h> | ||
|
|
||
| int main() { | ||
|
|
||
| // `delta` represents the detuning between the two qubits. | ||
| // In physical terms, detuning is the energy difference (or frequency offset) | ||
| // between qubit levels. Detuning term (in angular frequency units). | ||
| double delta = 100 * 2 * M_PI; | ||
| // `J` is the static coupling strength between the two qubits. | ||
| // This terms facilitates energy exchange between the qubits, effectively | ||
| // coupling their dynamics. | ||
| double J = 7 * 2 * M_PI; | ||
| // `m_12` models spurious electromagnetic `crosstalk`. | ||
| // `Crosstalk` is an unwanted interaction , here represented as a fraction of | ||
| // the drive strength applied to the second qubit. | ||
| double m_12 = 0.2; | ||
| // `Omega` is the drive strength applied to the qubits. | ||
| // A driving field can induce transitions between qubit states. | ||
| double Omega = 20 * 2 * M_PI; | ||
|
|
||
| // For a spin-1/2 system, the raising operator S^+ and lowering operator S^- | ||
| // are defined as: S^+ = 0.5 * (X + `iY`) and S^- = 0.5 * (X - `iY`) These | ||
| // operators allow transitions between the spin states (|0> and |1>). | ||
| auto spin_plus = [](int degree) { | ||
| return 0.5 * | ||
| (cudaq::spin_operator::x(degree) + | ||
| std::complex<double>(0.0, 1.0) * cudaq::spin_operator::y(degree)); | ||
| }; | ||
|
|
||
| auto spin_minus = [](int degree) { | ||
| return 0.5 * | ||
| (cudaq::spin_operator::x(degree) - | ||
| std::complex<double>(0.0, 1.0) * cudaq::spin_operator::y(degree)); | ||
| }; | ||
|
|
||
| // The Hamiltonian describes the energy and dynamics of our 2-qubit system. | ||
| // It consist of several parts: | ||
| // 1. Detuning term for qubit 0: (delta / 2) * Z. This sets the energy | ||
| // splitting for qubit 0. | ||
| // 2. Exchange interaction: J * (S^-_1 * S^+_0 + S^+_1 * S^-_0). This couples | ||
| // the two qubits, enabling excitation transfer. | ||
| // 3. Drive on qubit 0: Omega * X. A control field that drives transition in | ||
| // qubit 0. | ||
| // 4. `Crosstalk` drive on qubit 1: m_12 * Omega * X. A reduces drive on qubit | ||
| // 1 due to electromagnetic `crosstalk`. | ||
| auto hamiltonian = | ||
| (delta / 2.0) * cudaq::spin_operator::z(0) + | ||
| J * (spin_minus(1) * spin_plus(0) + spin_plus(1) * spin_minus(0)) + | ||
| Omega * cudaq::spin_operator::x(0) + | ||
| m_12 * Omega * cudaq::spin_operator::x(1); | ||
|
|
||
| // Each qubit is a 2-level system (dimension 2). | ||
| // The composite system (two qubits) has a total Hilbert space dimension of 2 | ||
| // * 2 = 4. | ||
| cudaq::dimension_map dimensions{{0, 2}, {1, 2}}; | ||
|
|
||
| // Build the initial state | ||
| // psi_00 represents the state |00> (both qubits in the ground state). | ||
| // psi_10 represents the state |10> (first qubit excited, second qubit in the | ||
| // ground state). | ||
| std::vector<std::complex<double>> psi00_data = { | ||
| {1.0, 0.0}, {0.0, 0.0}, {0.0, 0.0}, {0.0, 0.0}}; | ||
| std::vector<std::complex<double>> psi10_data = { | ||
| {0.0, 0.0}, {1.0, 0.0}, {0.0, 0.0}, {0.0, 0.0}}; | ||
|
|
||
| // Two initial state vectors for the 2-qubit system (dimension 4) | ||
| auto psi_00 = cudaq::state::from_data(psi00_data); | ||
| auto psi_10 = cudaq::state::from_data(psi10_data); | ||
|
|
||
| // Create a list of time steps for the simulation. | ||
| // Here we use 1001 points linearly spaced between time 0 and 1. | ||
| // This schedule will be used to integrate the time evolution of the system. | ||
| const int num_steps = 1001; | ||
| std::vector<double> steps = cudaq::linspace(0.0, 1.0, num_steps); | ||
| cudaq::schedule schedule(steps); | ||
|
|
||
| // Use Runge-`Kutta` integrator (4`th` order) to solve the time-dependent | ||
| // evolution. `dt` is the integration time step, and `order` sets the accuracy | ||
| // of the integrator method. | ||
| cudaq::integrators::runge_kutta integrator(4, 0.0001); | ||
|
|
||
| // The observables are the spin components along the x, y, and z directions | ||
| // for both qubits. These observables will be measured during the evolution. | ||
| auto observables = {cudaq::spin_operator::x(0), cudaq::spin_operator::y(0), | ||
| cudaq::spin_operator::z(0), cudaq::spin_operator::x(1), | ||
| cudaq::spin_operator::y(1), cudaq::spin_operator::z(1)}; | ||
|
|
||
| // Evolution with 2 initial states | ||
| // We evolve the system under the defined Hamiltonian for both initial states | ||
| // simultaneously. No collapsed operators are provided (closed system | ||
| // evolution). The evolution returns expectation values for all defined | ||
| // observables at each time step. | ||
| auto evolution_results = | ||
| cudaq::evolve(hamiltonian, dimensions, schedule, {psi_00, psi_10}, | ||
| integrator, {}, observables, true); | ||
|
|
||
| // Retrieve the evolution result corresponding to each initial state. | ||
| auto &evolution_result_00 = evolution_results[0]; | ||
| auto &evolution_result_10 = evolution_results[1]; | ||
|
|
||
| // Lambda to extract expectation values for a given observable index | ||
| auto get_expectation = [](int idx, auto &result) -> std::vector<double> { | ||
| std::vector<double> expectations; | ||
|
|
||
| auto all_exps = result.get_expectation_values().value(); | ||
| for (auto exp_vals : all_exps) { | ||
| expectations.push_back((double)exp_vals[idx]); | ||
| } | ||
| return expectations; | ||
| }; | ||
|
|
||
| // For the two `evolutions`, extract the six observable trajectories. | ||
| // For the |00> initial state, we extract the expectation trajectories for | ||
| // each observable. | ||
| auto result_00_0 = get_expectation(0, evolution_result_00); | ||
| auto result_00_1 = get_expectation(1, evolution_result_00); | ||
| auto result_00_2 = get_expectation(2, evolution_result_00); | ||
| auto result_00_3 = get_expectation(3, evolution_result_00); | ||
| auto result_00_4 = get_expectation(4, evolution_result_00); | ||
| auto result_00_5 = get_expectation(5, evolution_result_00); | ||
|
|
||
| // Similarly, for the |10> initial state: | ||
| auto result_10_0 = get_expectation(0, evolution_result_10); | ||
| auto result_10_1 = get_expectation(1, evolution_result_10); | ||
| auto result_10_2 = get_expectation(2, evolution_result_10); | ||
| auto result_10_3 = get_expectation(3, evolution_result_10); | ||
| auto result_10_4 = get_expectation(4, evolution_result_10); | ||
| auto result_10_5 = get_expectation(5, evolution_result_10); | ||
|
|
||
| // Export the results to a `CSV` file | ||
| // Export the Z-component of qubit 1's expectation values for both initial | ||
| // states. The `CSV` file "cross_resonance_z.`csv`" will have time versus (Z1) | ||
| // data for both |00> and |10> initial conditions. | ||
| export_csv("cross_resonance_z.csv", "time", steps, "<Z1>_00", result_00_5, | ||
| "<Z1>_10", result_10_5); | ||
| // Export the Y-component of qubit 1's expectation values for both initial | ||
| // states. The `CSV` file "cross_resonance_y.`csv`" will have time versus (Y1) | ||
| // data. | ||
| export_csv("cross_resonance_y.csv", "time", steps, "<Y1>_00", result_00_4, | ||
| "<Y1>_10", result_10_4); | ||
|
|
||
| std::cout | ||
| << "Simulation complete. The results are saved in cross_resonance_z.csv " | ||
| "and cross_resonance_y.csv files." | ||
| << std::endl; | ||
| return 0; | ||
| } | ||
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.