1515#pragma once
1616
1717#include < algorithm> // generate_n
18+ #include < chrono>
1819#include < complex>
20+ #include < cstdio>
21+ #include < fstream>
1922#include < memory>
2023#include < optional>
2124#include < random>
25+ #include < unordered_map>
2226#include < vector>
2327
2428#include " DataView.hpp"
2529#include " QuantumDevice.hpp"
2630#include " QubitManager.hpp"
2731#include " Types.h"
32+ #include " Utils.hpp"
2833
2934namespace Catalyst ::Runtime::Devices {
3035
@@ -40,14 +45,48 @@ namespace Catalyst::Runtime::Devices {
4045 * of the device; these are used to implement Quantum Instruction Set (QIS) instructions.
4146 */
4247struct NullQubit final : public Catalyst::Runtime::QuantumDevice {
43- NullQubit (const std::string &kwargs = " {}" ) {}
44- ~NullQubit () = default ; // LCOV_EXCL_LINE
48+ NullQubit (const std::string &kwargs = " {}" )
49+ {
50+ auto device_kwargs = Catalyst::Runtime::parse_kwargs (kwargs);
51+ if (device_kwargs.find (" track_resources" ) != device_kwargs.end ()) {
52+ track_resources_ = device_kwargs[" track_resources" ] == " True" ;
53+ }
54+ }
55+ ~NullQubit () {} // LCOV_EXCL_LINE
4556
4657 NullQubit &operator =(const NullQubit &) = delete ;
4758 NullQubit (const NullQubit &) = delete ;
4859 NullQubit (NullQubit &&) = delete ;
4960 NullQubit &operator =(NullQubit &&) = delete ;
5061
62+ /* *
63+ * @brief Prints resources that would be used to execute this circuit as a JSON
64+ */
65+ void PrintResourceUsage (FILE *resources_file)
66+ {
67+ // Store the 2 special variables and clear them from the map to make
68+ // pretty-printing easier
69+ const size_t num_qubits = resource_data_[" num_qubits" ];
70+ const size_t num_gates = resource_data_[" num_gates" ];
71+ resource_data_.erase (" num_gates" );
72+ resource_data_.erase (" num_qubits" );
73+
74+ std::stringstream resources;
75+
76+ resources << " {\n " ;
77+ resources << " \" num_qubits\" : " << num_qubits << " ,\n " ;
78+ resources << " \" num_gates\" : " << num_gates << " ,\n " ;
79+ resources << " \" gate_types\" : " ;
80+ pretty_print_dict (resource_data_, 2 , resources);
81+ resources << " \n }" << std::endl;
82+
83+ fwrite (resources.str ().c_str (), 1 , resources.str ().size (), resources_file);
84+
85+ // Restore 2 special variables
86+ resource_data_[" num_qubits" ] = num_qubits;
87+ resource_data_[" num_gates" ] = num_gates;
88+ }
89+
5190 /* *
5291 * @brief Allocate a "null" qubit.
5392 *
@@ -56,6 +95,10 @@ struct NullQubit final : public Catalyst::Runtime::QuantumDevice {
5695 auto AllocateQubit () -> QubitIdType
5796 {
5897 num_qubits_++; // next_id
98+ if (this ->track_resources_ ) {
99+ // Store the highest number of qubits allocated at any time since device creation
100+ resource_data_[" num_qubits" ] = std::max (num_qubits_, resource_data_[" num_qubits" ]);
101+ }
59102 return this ->qubit_manager .Allocate (num_qubits_);
60103 }
61104
@@ -94,6 +137,27 @@ struct NullQubit final : public Catalyst::Runtime::QuantumDevice {
94137 {
95138 num_qubits_ = 0 ;
96139 this ->qubit_manager .ReleaseAll ();
140+ if (this ->track_resources_ ) {
141+ auto time = std::chrono::high_resolution_clock::now ();
142+ auto timestamp =
143+ std::chrono::duration_cast<std::chrono::nanoseconds>(time.time_since_epoch ())
144+ .count ();
145+ std::stringstream resources_fname;
146+ resources_fname << " __pennylane_resources_data_" << timestamp << " .json" ;
147+
148+ // Need to use FILE* instead of ofstream since ofstream has no way to atomically open a
149+ // file only if it does not already exist
150+ FILE *resources_file = fopen (resources_fname.str ().c_str (), " wx" );
151+ if (resources_file == nullptr ) {
152+ std::string err_msg = " Error opening file '" + resources_fname.str () + " '." ;
153+ RT_FAIL (err_msg.c_str ());
154+ }
155+ else {
156+ PrintResourceUsage (resources_file);
157+ fclose (resources_file);
158+ }
159+ this ->resource_data_ .clear ();
160+ }
97161 }
98162
99163 /* *
@@ -170,17 +234,47 @@ struct NullQubit final : public Catalyst::Runtime::QuantumDevice {
170234 const std::vector<QubitIdType> &controlled_wires = {},
171235 const std::vector<bool > &controlled_values = {})
172236 {
237+ if (this ->track_resources_ ) {
238+ std::string prefix = " " ;
239+ std::string suffix = " " ;
240+ if (!controlled_wires.empty ()) {
241+ if (controlled_wires.size () > 1 ) {
242+ prefix += std::to_string (controlled_wires.size ());
243+ }
244+ prefix += " C(" ;
245+ suffix += " )" ;
246+ }
247+ if (inverse) {
248+ prefix += " Adj(" ;
249+ suffix += " )" ;
250+ }
251+ resource_data_[" num_gates" ]++;
252+ resource_data_[prefix + name + suffix]++;
253+ }
173254 }
174255
175256 /* *
176257 * @brief Doesn't apply a given matrix directly to the state vector of a device.
177258 *
178259 */
179260 void MatrixOperation (const std::vector<std::complex <double >> &,
180- const std::vector<QubitIdType> &, bool ,
261+ const std::vector<QubitIdType> &, bool inverse ,
181262 const std::vector<QubitIdType> &controlled_wires = {},
182263 const std::vector<bool > &controlled_values = {})
183264 {
265+ if (this ->track_resources_ ) {
266+ resource_data_[" num_gates" ]++;
267+
268+ std::string op_name = " QubitUnitary" ;
269+
270+ if (!controlled_wires.empty ()) {
271+ op_name = " Controlled" + op_name;
272+ }
273+ if (inverse) {
274+ op_name = " Adj(" + op_name + " )" ;
275+ }
276+ resource_data_[op_name]++;
277+ }
184278 }
185279
186280 /* *
@@ -360,9 +454,28 @@ struct NullQubit final : public Catalyst::Runtime::QuantumDevice {
360454 return {0 , 0 , 0 , {}, {}};
361455 }
362456
457+ /* *
458+ * @brief Returns the number of gates used since the last time all qubits were released. Only
459+ * works if resource tracking is enabled
460+ */
461+ auto ResourcesGetNumGates () -> std::size_t { return resource_data_[" num_gates" ]; }
462+
463+ /* *
464+ * @brief Returns the maximum number of qubits used since the last time all qubits were
465+ * released. Only works if resource tracking is enabled
466+ */
467+ auto ResourcesGetNumQubits () -> std::size_t { return resource_data_[" num_qubits" ]; }
468+
469+ /* *
470+ * @brief Returns whether the device is tracking resources or not.
471+ */
472+ auto IsTrackingResources () const -> bool { return track_resources_; }
473+
363474 private:
475+ bool track_resources_{false };
364476 std::size_t num_qubits_{0 };
365477 std::size_t device_shots_{0 };
478+ std::unordered_map<std::string, std::size_t > resource_data_;
366479 Catalyst::Runtime::QubitManager<QubitIdType, size_t > qubit_manager{};
367480
368481 // static constants for RESULT values
0 commit comments