Skip to content

Commit af89ae6

Browse files
authored
Add recorded result and distribution (#23)
* Add recorded result * Add documentation * Add results distribution and test * Add missing include and pull out key construction
1 parent a560a47 commit af89ae6

File tree

9 files changed

+422
-30
lines changed

9 files changed

+422
-30
lines changed

src/qiree/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ qiree_add_library(qiree
1818
Assert.cc
1919
Module.cc
2020
Executor.cc
21+
ResultDistribution.cc
2122
QuantumNotImpl.cc
2223
)
2324
target_compile_features(qiree PUBLIC cxx_std_17)

src/qiree/RecordedResult.hh

Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
//----------------------------------*-C++-*----------------------------------//
2+
// Copyright 2025 UT-Battelle, LLC, and other QIR-EE developers.
3+
// See the top-level COPYRIGHT file for details.
4+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
5+
//---------------------------------------------------------------------------//
6+
//! \file qiree/RecordedResult.hh
7+
//---------------------------------------------------------------------------//
8+
#pragma once
9+
10+
#include <string>
11+
#include <vector>
12+
13+
#include "Types.hh"
14+
15+
namespace qiree
16+
{
17+
//---------------------------------------------------------------------------//
18+
/*!
19+
* Accumulate sequential measurement outcomes from a single "shot".
20+
*
21+
* Each call to result_record_output appends a measurement bit (derived from
22+
* the provided Result) and its associated label. The storage is preallocated
23+
* via array_record_output.
24+
*/
25+
//---------------------------------------------------------------------------//
26+
27+
class RecordedResult
28+
{
29+
public:
30+
//!@{
31+
//! \name Type aliases
32+
using VecBits = std::vector<bool>;
33+
using VecString = std::vector<std::string>;
34+
//!@}
35+
36+
public:
37+
RecordedResult() = default;
38+
39+
// Construct with size and optional label
40+
RecordedResult(std::size_t count, OptionalCString label);
41+
42+
explicit RecordedResult(std::size_t count) : RecordedResult{count, nullptr}
43+
{
44+
}
45+
46+
//! Construct with an explicit vector for testing
47+
explicit RecordedResult(VecBits&& bits)
48+
: bits_(std::move(bits)), entry_labels_(bits_.size())
49+
{
50+
}
51+
52+
inline void push_back(QState result, OptionalCString label = nullptr);
53+
54+
//! Accessors
55+
std::string const& container_label() const { return container_label_; }
56+
VecBits const& bits() const { return bits_; }
57+
VecString const& entry_labels() const { return entry_labels_; }
58+
59+
private:
60+
std::string container_label_;
61+
VecBits bits_;
62+
VecString entry_labels_;
63+
};
64+
65+
//---------------------------------------------------------------------------//
66+
// INLINE DEFINITIONS
67+
//---------------------------------------------------------------------------//
68+
/*!
69+
* Preallocate storage for measurement results.
70+
*
71+
* \param count Number of measurement bits expected.
72+
* \param label Label for the container
73+
*/
74+
RecordedResult::RecordedResult(std::size_t count, OptionalCString label)
75+
{
76+
if (label)
77+
{
78+
container_label_ = label;
79+
}
80+
bits_.reserve(count);
81+
entry_labels_.reserve(count);
82+
}
83+
84+
//---------------------------------------------------------------------------//
85+
/*!
86+
* Record a measurement result and its optional label.
87+
*/
88+
void RecordedResult::push_back(QState state, OptionalCString label)
89+
{
90+
bits_.push_back(static_cast<bool>(state));
91+
entry_labels_.push_back(label ? label : std::string{});
92+
}
93+
94+
//---------------------------------------------------------------------------//
95+
} // namespace qiree

src/qiree/ResultDistribution.cc

Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
1+
//----------------------------------*-C++-*----------------------------------//
2+
// Copyright 2025 UT-Battelle, LLC, and other QIR-EE developers.
3+
// See the top-level COPYRIGHT file for details.
4+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
5+
//---------------------------------------------------------------------------//
6+
//! \file qiree/ResultDistribution.cc
7+
//---------------------------------------------------------------------------//
8+
#include "ResultDistribution.hh"
9+
10+
#include <algorithm>
11+
12+
#include "RecordedResult.hh"
13+
#include "qiree/Assert.hh"
14+
15+
namespace qiree
16+
{
17+
namespace
18+
{
19+
//---------------------------------------------------------------------------//
20+
/*!
21+
* Convert bits to a bit string key ("0" for false, "1" for true).
22+
*/
23+
std::string to_key(std::vector<bool> const& bits)
24+
{
25+
std::string key;
26+
key.reserve(bits.size());
27+
for (bool bit : bits)
28+
{
29+
key.push_back(bit ? '1' : '0');
30+
}
31+
return key;
32+
}
33+
34+
} // namespace
35+
36+
//---------------------------------------------------------------------------//
37+
/*!
38+
* Accumulate a single shot.
39+
*/
40+
void ResultDistribution::accumulate(RecordedResult const& result)
41+
{
42+
auto const& bits = result.bits();
43+
44+
if (QIREE_UNLIKELY(key_length_ == 0))
45+
{
46+
// This is the first result: store the key length
47+
key_length_ = bits.size();
48+
}
49+
else
50+
{
51+
QIREE_VALIDATE(bits.size() == key_length_,
52+
<< "RecordedResult bit length " << bits.size()
53+
<< " does not match distribution key length "
54+
<< key_length_);
55+
}
56+
57+
++distribution_[to_key(bits)];
58+
}
59+
60+
//---------------------------------------------------------------------------//
61+
/*!
62+
* Access the number of shots that resulted in this bit string.
63+
*/
64+
std::size_t ResultDistribution::count(std::string const& key) const
65+
{
66+
QIREE_VALIDATE(key.length() == key_length_,
67+
<< "invalid key length: expected " << key_length_
68+
<< " but got " << key.length());
69+
QIREE_VALIDATE(std::all_of(key.begin(),
70+
key.end(),
71+
[](char c) { return c == '0' || c == '1'; }),
72+
<< "invalid key: expected only '0' or '1'");
73+
74+
auto it = distribution_.find(key);
75+
return (it != distribution_.end()) ? it->second : 0;
76+
}
77+
78+
//---------------------------------------------------------------------------//
79+
/*!
80+
* Build a JSON string of the distribution.
81+
*/
82+
std::string ResultDistribution::to_json() const
83+
{
84+
std::ostringstream oss;
85+
oss << "{";
86+
bool first = true;
87+
for (auto const& kv : distribution_)
88+
{
89+
if (!first)
90+
oss << ",";
91+
first = false;
92+
oss << "\"" << kv.first << "\":" << kv.second;
93+
}
94+
oss << "}";
95+
return oss.str();
96+
}
97+
98+
//---------------------------------------------------------------------------//
99+
} // namespace qiree

src/qiree/ResultDistribution.hh

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
//----------------------------------*-C++-*----------------------------------//
2+
// Copyright 2025 UT-Battelle, LLC, and other QIR-EE developers.
3+
// See the top-level COPYRIGHT file for details.
4+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
5+
//---------------------------------------------------------------------------//
6+
//! \file qiree/ResultDistribution.hh
7+
//---------------------------------------------------------------------------//
8+
#pragma once
9+
10+
#include <string>
11+
#include <unordered_map>
12+
13+
namespace qiree
14+
{
15+
class RecordedResult;
16+
//---------------------------------------------------------------------------//
17+
/*!
18+
* Distribution of recorded results from shots.
19+
20+
The distribution is formed by accumulating multiple \c RecordedResult
21+
instances into a sparse map of {bit string -> count}. The length of the bit
22+
string is the same for all keys. We provide accessors by string key such
23+
as \c 01001 which corresponds to a bit string `{false, true, ...}`. It is
24+
serializable to JSON, creating an object that has bit strings as keys and
25+
counts as values.
26+
27+
*/
28+
class ResultDistribution
29+
{
30+
public:
31+
// Accumulate the results from a RecordedResult instance.
32+
// Throws if the bit-length of the new result
33+
// differs from previously accumulated ones.
34+
void accumulate(RecordedResult const& result);
35+
36+
// Access the count for a given bit string key (e.g. "01001").
37+
// Returns 0 if the key is not present.
38+
std::size_t count(std::string const& key) const;
39+
40+
// Serialize the distribution to a JSON string.
41+
// The JSON object has bit string keys and count values.
42+
std::string to_json() const;
43+
44+
// Provide const iterators to iterate over the distribution.
45+
auto begin() const { return distribution_.begin(); }
46+
auto end() const { return distribution_.end(); }
47+
48+
private:
49+
// Sparse map of {bit string -> count}
50+
std::unordered_map<std::string, std::size_t> distribution_;
51+
52+
// key_length_ is 0 until the first RecordedResult is accumulated.
53+
// All subsequent RecordedResult instances must have the same bit length.
54+
std::size_t key_length_ = 0;
55+
};
56+
57+
//---------------------------------------------------------------------------//
58+
} // namespace qiree

src/qirqsim/QsimDefaultRuntime.cc

Lines changed: 0 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -25,30 +25,4 @@ void QsimDefaultRuntime::initialize(OptionalCString env)
2525
}
2626
}
2727

28-
//---------------------------------------------------------------------------//
29-
/*!
30-
* Execute circuit and mark the following N results as being part of an array
31-
* named tag
32-
*/
33-
void QsimDefaultRuntime::array_record_output(size_type, OptionalCString) {}
34-
35-
//---------------------------------------------------------------------------//
36-
/*!
37-
* Execute circuit and mark the following N results as being part of a tuple
38-
* named tag.
39-
*/
40-
void QsimDefaultRuntime::tuple_record_output(size_type, OptionalCString) {}
41-
42-
//---------------------------------------------------------------------------//
43-
/*!
44-
* Execute circuit and report a single measurement result.
45-
*/
46-
void QsimDefaultRuntime::result_record_output(Result, OptionalCString)
47-
{
48-
// Access values through the getter
49-
// This prints results every time result_record_output is called
50-
// Can comment out if only want to see final results
51-
(void)sizeof(sim_);
52-
}
53-
5428
} // namespace qiree

src/qirqsim/QsimDefaultRuntime.hh

Lines changed: 18 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
#pragma once
99

1010
#include "QsimQuantum.hh"
11+
#include "qiree/RecordedResult.hh"
1112

1213
namespace qiree
1314
{
@@ -41,22 +42,35 @@ class QsimDefaultRuntime final : virtual public RuntimeInterface
4142

4243
//!@{
4344
//! \name Runtime interface
45+
4446
// Initialize the execution environment, resetting qubits
4547
void initialize(OptionalCString env) override;
4648

4749
//! Mark the following N results as being part of an array named tag
48-
void array_record_output(size_type, OptionalCString tag) final;
50+
void array_record_output(size_type size, OptionalCString tag) final
51+
{
52+
result_ = RecordedResult(size, tag);
53+
}
4954

5055
//! Mark the following N results as being part of a tuple named tag
51-
void tuple_record_output(size_type, OptionalCString) final;
56+
void tuple_record_output(size_type size, OptionalCString tag) final
57+
{
58+
result_ = RecordedResult(size, tag);
59+
}
5260

53-
// Save one result
54-
void result_record_output(Result result, OptionalCString tag) final;
61+
//! Save one result
62+
void result_record_output(Result result, OptionalCString tag) final
63+
{
64+
result_.push_back(sim_.get_result(result), tag);
65+
}
5566
//!@}
5667

68+
RecordedResult const& result() const { return result_; }
69+
5770
private:
5871
std::ostream& output_;
5972
QsimQuantum& sim_;
73+
RecordedResult result_;
6074
};
6175

6276
} // namespace qiree

test/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@ endfunction()
4545

4646
qiree_add_test(qiree Executor)
4747
qiree_add_test(qiree Module)
48+
qiree_add_test(qiree ResultDistribution)
4849

4950
#---------------------------------------------------------------------------##
5051
# QIRXACC TESTS

0 commit comments

Comments
 (0)