Skip to content

Commit

Permalink
Merge pull request #185 from QuTech-Delft/update-master-to-release-0.7.4
Browse files Browse the repository at this point in the history
Update `master` to release 0.7.4
  • Loading branch information
rturrado authored Jan 29, 2025
2 parents 3b1843c + 3e23154 commit 50c6090
Show file tree
Hide file tree
Showing 10 changed files with 314 additions and 70 deletions.
13 changes: 11 additions & 2 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,22 @@ This project adheres to [Semantic Versioning](http://semver.org/).
- **Removed** for now removed features.


## [ 0.7.4 ] - [ 2025-01-29 ]

### Added
- Integrate with libqasm 0.6.9 release:
- Add `SWAP` gate instruction.
- Add `barrier`, `wait`, and `ìnit` non-gate instructions.
- The register manager now holds a 'dirty bitset' for virtual qubit and bit indices.


## [ 0.7.3 ] - [ 2025-01-27 ]

### Added
- Documentation: GitHub pages.
- Linters: `.clang-format` and `.clang-tidy`.
- Integrate with libqasm 0.6.8 release:
- Add gate modifiers. Notice though that `pow` only works with integer exponents.
- Documentation: GitHub pages.
- Linters: `.clang-format` and `.clang-tidy`.

### Changed
- Implement instructions as a hierarchy.
Expand Down
4 changes: 2 additions & 2 deletions conanfile.py
Original file line number Diff line number Diff line change
Expand Up @@ -42,14 +42,14 @@ def _should_build_test(self):
return not self.conf.get("tools.build:skip_test", default=True, check_type=bool)

def build_requirements(self):
self.tool_requires("libqasm/0.6.8")
self.tool_requires("libqasm/0.6.9")
if self._should_build_test:
self.test_requires("gtest/1.15.0")

def requirements(self):
self.requires("boost/1.85.0")
self.requires("fmt/11.0.2", transitive_headers=True)
self.requires("libqasm/0.6.8", transitive_headers=True)
self.requires("libqasm/0.6.9", transitive_headers=True)
self.requires("range-v3/0.12.0", transitive_headers=True)

def config_options(self):
Expand Down
15 changes: 15 additions & 0 deletions include/qx/instructions.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,14 @@ namespace qx {

using ControlBits = std::vector<core::BitIndex>;

using qubit_indices_t = std::vector<core::QubitIndex>;
using bit_indices_t = std::vector<core::BitIndex>;

struct Instruction {
virtual ~Instruction() = default;
virtual void execute(SimulationIterationContext& context) = 0;
[[nodiscard]] virtual qubit_indices_t get_qubit_indices() = 0;
[[nodiscard]] virtual bit_indices_t get_bit_indices() = 0;
};

struct BitControlledInstruction : public Instruction {
Expand All @@ -25,6 +30,8 @@ struct BitControlledInstruction : public Instruction {
~BitControlledInstruction() override = default;
BitControlledInstruction(ControlBits control_bits, std::shared_ptr<Instruction> instruction);
void execute(SimulationIterationContext& context) override;
[[nodiscard]] qubit_indices_t get_qubit_indices() override;
[[nodiscard]] bit_indices_t get_bit_indices() override;
};

struct Unitary : public Instruction {
Expand All @@ -37,11 +44,15 @@ struct Unitary : public Instruction {
[[nodiscard]] std::shared_ptr<core::matrix_t> inverse() const;
[[nodiscard]] std::shared_ptr<core::matrix_t> power(double exponent) const;
[[nodiscard]] std::shared_ptr<core::matrix_t> control() const;
[[nodiscard]] qubit_indices_t get_qubit_indices() override;
[[nodiscard]] bit_indices_t get_bit_indices() override;
};

struct NonUnitary : public Instruction {
~NonUnitary() override = default;
void execute(SimulationIterationContext& context) override = 0;
[[nodiscard]] qubit_indices_t get_qubit_indices() override = 0;
[[nodiscard]] bit_indices_t get_bit_indices() override = 0;
};

struct Measure : public NonUnitary {
Expand All @@ -51,6 +62,8 @@ struct Measure : public NonUnitary {
~Measure() override = default;
Measure(const core::QubitIndex& qubit_index, const core::BitIndex& bit_index);
void execute(SimulationIterationContext& context) override;
[[nodiscard]] qubit_indices_t get_qubit_indices() override;
[[nodiscard]] bit_indices_t get_bit_indices() override;
};

struct Reset : public NonUnitary {
Expand All @@ -59,6 +72,8 @@ struct Reset : public NonUnitary {
~Reset() override = default;
explicit Reset(std::optional<core::QubitIndex> qubit_index);
void execute(SimulationIterationContext& context) override;
[[nodiscard]] qubit_indices_t get_qubit_indices() override;
[[nodiscard]] bit_indices_t get_bit_indices() override;
};

} // namespace qx
13 changes: 12 additions & 1 deletion include/qx/register_manager.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

#include <fmt/ostream.h>

#include <boost/dynamic_bitset/dynamic_bitset.hpp>
#include <cstdint> // size_t
#include <memory> // shared_ptr
#include <optional>
Expand Down Expand Up @@ -48,6 +49,7 @@ struct RegisterManagerError : public SimulationError {

using VariableNameToRangeMapT = std::unordered_map<VariableName, Range>;
using IndexToVariableNameMapT = std::vector<VariableName>;
using DirtyBitsetT = boost::dynamic_bitset<uint32_t>;

//----------//
// Register //
Expand All @@ -57,15 +59,18 @@ class Register {
std::size_t register_size_;
VariableNameToRangeMapT variable_name_to_range_;
IndexToVariableNameMapT index_to_variable_name_;
DirtyBitsetT dirty_bitset_;

public:
Register(const TreeOne<CqasmV3xProgram>& program, auto&& is_of_type, std::size_t max_register_size);
virtual ~Register() = 0;
[[nodiscard]] std::size_t size() const;
[[nodiscard]] virtual Range at(const VariableName& name) const;
[[nodiscard]] virtual Index at(const VariableName& name, const std::optional<Index>& sub_index) const;
[[nodiscard]] virtual VariableName at(const std::size_t& index) const;
[[nodiscard]] virtual VariableName at(const Index& index) const;
[[nodiscard]] virtual std::string to_string() const;
[[nodiscard]] virtual bool is_dirty(const Index& index) const;
virtual void set_dirty(const Index& index);
};

//---------------//
Expand Down Expand Up @@ -116,8 +121,14 @@ class RegisterManager {
[[nodiscard]] Index get_bit_index(const VariableName& name, const std::optional<Index>& sub_index) const;
[[nodiscard]] VariableName get_qubit_variable_name(const Index& index) const;
[[nodiscard]] VariableName get_bit_variable_name(const Index& index) const;
[[nodiscard]] Index get_qubit_variable_index(const Index& index) const;
[[nodiscard]] Index get_bit_variable_index(const Index& index) const;
[[nodiscard]] std::shared_ptr<QubitRegister> get_qubit_register() const;
[[nodiscard]] std::shared_ptr<BitRegister> get_bit_register() const;
[[nodiscard]] bool is_dirty_qubit(const Index& index) const;
[[nodiscard]] bool is_dirty_bit(const Index& index) const;
void set_dirty_qubit(const Index& index);
void set_dirty_bit(const Index& index);
};

std::ostream& operator<<(std::ostream& os, const Range& range);
Expand Down
4 changes: 2 additions & 2 deletions include/qx/version.hpp
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
#ifndef QX_VERSION
#define QX_VERSION "0.7.3"
#define QX_RELEASE_YEAR "2024"
#define QX_VERSION "0.7.4"
#define QX_RELEASE_YEAR "2025"
#endif
3 changes: 3 additions & 0 deletions src/qx/circuit.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,9 @@ Circuit::Circuit(const TreeOne<CqasmV3xProgram>& program)
}

void Circuit::add_instruction(std::shared_ptr<Instruction> instruction) {
for (const auto& qubit_index : instruction->get_qubit_indices()) {
RegisterManager::get_instance().set_dirty_qubit(qubit_index.value);
}
instructions_.emplace_back(std::move(instruction));
}

Expand Down
25 changes: 25 additions & 0 deletions src/qx/circuit_builder.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,31 @@ void CircuitBuilder::visit_non_gate_instruction(CqasmV3xNonGateInstruction& non_
circuit_.add_instruction(std::make_shared<Reset>(qubit_index));
}
}
} else if (name == "init") {
for (const auto& instruction_indices : instructions_indices) {
const auto& qubit_index = instruction_indices[0];
const auto& register_manager = RegisterManager::get_instance();
if (register_manager.is_dirty_qubit(qubit_index.value)) {
const auto& variable_name = register_manager.get_qubit_variable_name(qubit_index.value);
const auto& variable_index = register_manager.get_qubit_variable_index(qubit_index.value);
throw CircuitBuilderError{ fmt::format(
"incorrect 'init {}[{}]': the qubit has already been used in a non-control instruction",
variable_name,
variable_index) };
}
}
} else if (name == "barrier") {
} else if (name == "wait") {
auto time = non_gate_instruction.parameter->as_const_int()->value;
if (time < 0) {
const auto& instruction_indices = instructions_indices[0];
const auto& qubit_index = instruction_indices[0];
const auto& register_manager = RegisterManager::get_instance();
const auto& variable_name = register_manager.get_qubit_variable_name(qubit_index.value);
const auto& variable_index = register_manager.get_qubit_variable_index(qubit_index.value);
throw CircuitBuilderError{ fmt::format(
"incorrect 'wait({}) {}[{}]': time cannot be negative", time, variable_name, variable_index) };
}
} else {
throw CircuitBuilderError{ fmt::format("unsupported non-gate instruction: '{}'", name) };
}
Expand Down
32 changes: 32 additions & 0 deletions src/qx/instructions.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,14 @@ void BitControlledInstruction::execute(SimulationIterationContext& context) {
}
}

[[nodiscard]] qubit_indices_t BitControlledInstruction::get_qubit_indices() {
return instruction->get_qubit_indices();
}

[[nodiscard]] bit_indices_t BitControlledInstruction::get_bit_indices() {
return instruction->get_bit_indices();
}

Unitary::Unitary(std::shared_ptr<core::matrix_t> matrix, std::shared_ptr<core::operands_t> operands)
: matrix{ std::move(matrix) }
, operands{ std::move(operands) } {}
Expand All @@ -36,6 +44,18 @@ void Unitary::execute(SimulationIterationContext& context) {
return std::make_shared<core::matrix_t>(matrix->control());
}

[[nodiscard]] qubit_indices_t Unitary::get_qubit_indices() {
return *operands;
}

[[nodiscard]] bit_indices_t Unitary::get_bit_indices() {
return bit_indices_t{};
}

[[nodiscard]] qubit_indices_t Measure::get_qubit_indices() {
return qubit_indices_t{ qubit_index };
}

Measure::Measure(const core::QubitIndex& qubit_index, const core::BitIndex& bit_index)
: qubit_index{ qubit_index }
, bit_index{ bit_index } {}
Expand All @@ -48,6 +68,10 @@ void Measure::execute(SimulationIterationContext& context) {
context.bit_measurement_register);
}

[[nodiscard]] bit_indices_t Measure::get_bit_indices() {
return bit_indices_t{ bit_index };
}

Reset::Reset(std::optional<core::QubitIndex> qubit_index)
: qubit_index{ qubit_index } {}

Expand All @@ -59,4 +83,12 @@ void Reset::execute(SimulationIterationContext& context) {
}
}

[[nodiscard]] qubit_indices_t Reset::get_qubit_indices() {
return qubit_index.has_value() ? qubit_indices_t{ *qubit_index } : qubit_indices_t{};
}

[[nodiscard]] bit_indices_t Reset::get_bit_indices() {
return bit_indices_t{};
}

} // namespace qx
42 changes: 41 additions & 1 deletion src/qx/register_manager.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ Register::Register(const TreeOne<CqasmV3xProgram>& program, auto&& is_of_type, s

variable_name_to_range_.reserve(register_size_);
index_to_variable_name_.resize(register_size_);
dirty_bitset_ = DirtyBitsetT{ register_size_ };

auto current_index = size_t{};
for (auto&& variable : variables) {
Expand Down Expand Up @@ -65,7 +66,7 @@ Register::~Register() = default;
return range.first + sub_index.value_or(0);
}

[[nodiscard]] VariableName Register::at(const std::size_t& index) const {
[[nodiscard]] VariableName Register::at(const Index& index) const {
return index_to_variable_name_.at(index);
}

Expand All @@ -80,6 +81,15 @@ Register::~Register() = default;
return fmt::format("{{ {0} }}", entries);
}

[[nodiscard]] bool Register::is_dirty(const Index& index) const {
return dirty_bitset_[index];
}

void Register::set_dirty(const Index& index) {
assert(index < register_size_);
dirty_bitset_[index] = true;
}

//---------------//
// QubitRegister //
//---------------//
Expand Down Expand Up @@ -155,6 +165,20 @@ BitRegister::~BitRegister() = default;
return bit_register_->at(index);
}

[[nodiscard]] Index RegisterManager::get_qubit_variable_index(const Index& index) const {
const auto& variable_name = qubit_register_->at(index);
const auto& first_index = qubit_register_->at(variable_name).first;
assert(index >= first_index);
return index - first_index;
}

[[nodiscard]] Index RegisterManager::get_bit_variable_index(const Index& index) const {
const auto& variable_name = bit_register_->at(index);
const auto& first_index = bit_register_->at(variable_name).first;
assert(index >= first_index);
return index - first_index;
}

[[nodiscard]] std::shared_ptr<QubitRegister> RegisterManager::get_qubit_register() const {
return qubit_register_;
}
Expand All @@ -163,6 +187,22 @@ BitRegister::~BitRegister() = default;
return bit_register_;
}

[[nodiscard]] bool RegisterManager::is_dirty_qubit(const Index& index) const {
return qubit_register_->is_dirty(index);
}

[[nodiscard]] bool RegisterManager::is_dirty_bit(const Index& index) const {
return bit_register_->is_dirty(index);
}

void RegisterManager::set_dirty_qubit(const Index& index) {
qubit_register_->set_dirty(index);
}

void RegisterManager::set_dirty_bit(const Index& index) {
qubit_register_->set_dirty(index);
}

std::ostream& operator<<(std::ostream& os, const Range& range) {
return os << fmt::format(
"[{}{}]", range.first, range.size == 1 ? "" : fmt::format("..{}", range.first + range.size - 1));
Expand Down
Loading

0 comments on commit 50c6090

Please sign in to comment.