Skip to content

Commit

Permalink
Noisy and approximating simulation (#19)
Browse files Browse the repository at this point in the history
* Noisy+Approximating Simulation with new Package
* Refactor Noisy Simulation code
* Noise for multi target gates
* Use exceptions instead of std::exit
* Nicer JSON output with nlohmann lib
* More informatative exceptions
* Format fixes
* Ignore parts of file for format
* Added tests
* Updated README
* Updated LICENSE

Co-authored-by: Thomas Grurl <[email protected]>
  • Loading branch information
hillmich and 33Gjl1Xe authored May 6, 2021
1 parent 6dc18e5 commit 6f9585e
Show file tree
Hide file tree
Showing 14 changed files with 1,603 additions and 207 deletions.
3 changes: 1 addition & 2 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,13 @@ cmake_minimum_required(VERSION 3.14...3.19)

project(ddsim
LANGUAGES CXX
VERSION 1.3.0
VERSION 1.4.0
DESCRIPTION "DDSIM - A JKQ quantum simulator based on decision diagrams"
)

set_property(GLOBAL PROPERTY USE_FOLDERS ON)
option(COVERAGE "Configure for coverage report generation")
option(GENERATE_POSITION_INDEPENDENT_CODE "Generate position independent code")
option(TEST_WITH_SANITIZERS "Run test with ASAN and UBSAN")

set(default_build_type "Release")
if(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES)
Expand Down
2 changes: 1 addition & 1 deletion LICENSE
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
MIT License

Copyright (c) 2020 Stefan Hillmich, Lukas Burgholzer, Robert Wille
Copyright (c) 2021 Stefan Hillmich, Lukas Burgholzer, Thomas Grurl, and Robert Wille

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
Expand Down
232 changes: 116 additions & 116 deletions README.md

Large diffs are not rendered by default.

11 changes: 9 additions & 2 deletions apps/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -20,10 +20,17 @@ macro(add_sim_executable APPNAME)
set_target_properties(${PROJECT_NAME}_${APPNAME} PROPERTIES EXPORT_NAME ${PROJECT_NAME}_${APPNAME})
endmacro()

if (Boost_FOUND)
set(THREADS_PREFER_PTHREAD_FLAG ON)
find_package( Threads )
link_libraries( Threads::Threads)

if(Boost_FOUND)
add_sim_executable(simple Boost::program_options)
add_sim_executable(primebases Boost::program_options)

if(Threads_FOUND)
add_sim_executable(noise_aware Boost::program_options )
# target_link_libraries(${PROJECT_NAME}_noise_aware PUBLIC Threads::Threads)
endif()
else ()
message(WARNING "Did not find Boost! Commandline interface will not be an available target!")
endif ()
Expand Down
110 changes: 110 additions & 0 deletions apps/noise_aware.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
#include "StochasticNoiseSimulator.hpp"
#include "nlohmann/json.hpp"

#include <boost/program_options.hpp>
#include <chrono>
#include <iomanip>
#include <iostream>
#include <memory>
#include <string>

namespace nl = nlohmann;

int main(int argc, char** argv) {
namespace po = boost::program_options;
unsigned long long seed;

po::options_description description("JKQ DDSIM by https://iic.jku.at/eda/ -- Allowed options");
// clang-format off
description.add_options()
("help,h", "produce help message")
("seed", po::value<unsigned long long>(&seed)->default_value(0), "seed for random number generator (default zero is possibly directly used as seed!)")
("pm", "print measurements")
("ps", "print simulation stats (applied gates, sim. time, and maximal size of the DD)")
("verbose", "Causes some simulators to print additional information to STDERR")

("simulate_file", po::value<std::string>(), "simulate a quantum circuit given by file (detection by the file extension)")
("step_fidelity", po::value<double>()->default_value(1.0), "target fidelity for each approximation run (>=1 = disable approximation)")
("steps", po::value<unsigned int>()->default_value(1), "number of approximation steps")

("noise_effects", po::value<std::string>()->default_value("APD"), "Noise effects (A (=amplitude damping),D (=depolarization),P (=phase flip)) in the form of a character string describing the noise effects (default=\"APD\")")
("noise_prob", po::value<double>()->default_value(0.001), "Probability for applying noise (default=0.001)")
("confidence", po::value<double>()->default_value(0.05), "Confidence in the error bound of the stochastic simulation (default= 0.05)")
("error_bound", po::value<double>()->default_value(0.1), "Error bound of the stochastic simulation (default=0.1)")
("stoch_runs", po::value<long>()->default_value(0), "Number of stochastic runs. When the value is 0 the value is calculated using the confidence, error_bound and number of tracked properties. (default = 0)")
("properties", po::value<std::string>()->default_value("-3-1000"), R"(Comma separated list of tracked properties. Note that -1 is the fidelity and "-" can be used to specify a range. (default="-3-1000"))")
;
// clang-format on
po::variables_map vm;
try {
po::store(po::parse_command_line(argc, argv, description), vm);

if (vm.count("help")) {
std::cout << description;
return 0;
}
po::notify(vm);
} catch (const po::error& e) {
std::cerr << "[ERROR] " << e.what() << "! Try option '--help' for available commandline options.\n";
std::exit(1);
}

std::unique_ptr<qc::QuantumComputation> quantumComputation;
std::unique_ptr<StochasticNoiseSimulator> ddsim{nullptr};

if (vm.count("simulate_file")) {
const std::string fname = vm["simulate_file"].as<std::string>();
quantumComputation = std::make_unique<qc::QuantumComputation>(fname);
ddsim = std::make_unique<StochasticNoiseSimulator>(quantumComputation,
vm["steps"].as<unsigned int>(),
vm["step_fidelity"].as<double>(),
seed);
} else {
std::cerr << "Did not find anything to simulate. See help below.\n"
<< description << "\n";
std::exit(1);
}

if (quantumComputation && quantumComputation->getNqubits() > 100) {
std::clog << "[WARNING] Quantum computation contains quite many qubits. You're jumping into the deep end.\n";
}

ddsim->setNoiseEffects(vm["noise_effects"].as<std::string>());
ddsim->setAmplitudeDampingProbability(vm["noise_prob"].as<double>());
ddsim->stoch_confidence = vm["confidence"].as<double>();
ddsim->setRecordedProperties(vm["properties"].as<std::string>());
ddsim->stoch_error_margin = vm["error_bound"].as<double>();
ddsim->stochastic_runs = vm["stoch_runs"].as<long>();

auto t1 = std::chrono::steady_clock::now();

const std::map<std::string, double> measurement_results = ddsim->StochSimulate();

auto t2 = std::chrono::steady_clock::now();

std::chrono::duration<float> duration_simulation = t2 - t1;

nl::json output_obj;

if (vm.count("ps")) {
output_obj["statistics"] = {
{"simulation_time", duration_simulation.count()},
{"benchmark", ddsim->getName()},
{"stoch_runs", ddsim->stochastic_runs},
{"threads", ddsim->max_instances},
{"n_qubits", +ddsim->getNumberOfQubits()},
{"applied_gates", ddsim->getNumberOfOps()},
{"max_nodes", ddsim->getMaxNodeCount()},
{"seed", ddsim->getSeed()},
};

for (const auto& item: ddsim->AdditionalStatistics()) {
output_obj["statistics"][item.first] = item.second;
}
}

if (vm.count("pm")) {
output_obj["measurement_results"] = measurement_results;
}
std::cout << std::setw(2) << output_obj << std::endl;
}
135 changes: 51 additions & 84 deletions apps/simple.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3,16 +3,19 @@
#include "ShorFastSimulator.hpp"
#include "ShorSimulator.hpp"
#include "Simulator.hpp"
#include "algorithms/Entanglement.hpp"
#include "algorithms/Grover.hpp"
#include "algorithms/QFT.hpp"
#include "nlohmann/json.hpp"

#include <algorithms/Entanglement.hpp>
#include <algorithms/Grover.hpp>
#include <algorithms/QFT.hpp>
#include <boost/program_options.hpp>
#include <chrono>
#include <iostream>
#include <memory>
#include <string>

namespace nl = nlohmann;

int main(int argc, char** argv) {
namespace po = boost::program_options;
// variables initialized by boost program_options default values
Expand All @@ -24,30 +27,31 @@ int main(int argc, char** argv) {
ApproximationInfo::ApproximationWhen approx_when;

po::options_description description("JKQ DDSIM by https://iic.jku.at/eda/ -- Allowed options");
description.add_options()("help,h", "produce help message")("seed", po::value<>(&seed)->default_value(0),
"seed for random number generator (default zero is possibly directly used as seed!)")("shots", po::value<>(&shots)->default_value(0),
"number of measurements (if the algorithm does not contain non-unitary gates, weak simulation is used)")("display_vector", "display the state vector")("ps", "print simulation stats (applied gates, sim. time, and maximal size of the DD)")("verbose", "Causes some simulators to print additional information to STDERR")("benchmark",
"print simulation stats in a single CSV style line (overrides --ps and suppresses most other output, please don't rely on the format across versions)")

("simulate_file", po::value<std::string>(),
"simulate a quantum circuit given by file (detection by the file extension)")("simulate_qft", po::value<unsigned int>(), "simulate Quantum Fourier Transform for given number of qubits")("simulate_ghz", po::value<unsigned int>(),
"simulate state preparation of GHZ state for given number of qubits")("step_fidelity", po::value<>(&step_fidelity)->default_value(1.0),
"target fidelity for each approximation run (>=1 = disable approximation)")("steps", po::value<>(&approx_steps)->default_value(1), "number of approximation steps")("approx_when", po::value<>(&approx_when)->default_value(ApproximationInfo::FidelityDriven),
"approximation method ('fidelity' (default) or 'memory'")("approx_state",
"do excessive approximation runs at the end of the simulation to see how the quantum state behaves")

("simulate_grover", po::value<unsigned int>(),
"simulate Grover's search for given number of qubits with random oracle")("simulate_grover_emulated", po::value<unsigned int>(),
"simulate Grover's search for given number of qubits with random oracle and emulation")("simulate_grover_oracle_emulated", po::value<std::string>(),
"simulate Grover's search for given number of qubits with given oracle and emulation")

("simulate_shor", po::value<unsigned int>(), "simulate Shor's algorithm factoring this number")("simulate_shor_coprime", po::value<unsigned int>()->default_value(0),
"coprime number to use with Shor's algorithm (zero randomly generates a coprime)")("simulate_shor_no_emulation",
"Force Shor simulator to do modular exponentiation instead of using emulation (you'll usually want emulation)")

("simulate_fast_shor", po::value<unsigned int>(),
"simulate Shor's algorithm factoring this number with intermediate measurements")("simulate_fast_shor_coprime", po::value<unsigned int>()->default_value(0),
"coprime number to use with Shor's algorithm (zero randomly generates a coprime)");
// clang-format off
description.add_options()
("help,h", "produce help message")
("seed", po::value<>(&seed)->default_value(0), "seed for random number generator (default zero is possibly directly used as seed!)")
("shots", po::value<>(&shots)->default_value(0), "number of measurements (if the algorithm does not contain non-unitary gates, weak simulation is used)")
("pv", "display the state vector")
("ps", "print simulation stats (applied gates, sim. time, and maximal size of the DD)")
("pm", "print measurement results")
("verbose", "Causes some simulators to print additional information to STDERR")
("simulate_file", po::value<std::string>(), "simulate a quantum circuit given by file (detection by the file extension)")
("simulate_qft", po::value<unsigned int>(), "simulate Quantum Fourier Transform for given number of qubits")
("simulate_ghz", po::value<unsigned int>(), "simulate state preparation of GHZ state for given number of qubits")
("step_fidelity", po::value<>(&step_fidelity)->default_value(1.0), "target fidelity for each approximation run (>=1 = disable approximation)")
("steps", po::value<>(&approx_steps)->default_value(1), "number of approximation steps")
("approx_when", po::value<>(&approx_when)->default_value(ApproximationInfo::FidelityDriven), "approximation method ('fidelity' (default) or 'memory'")
("approx_state", "do excessive approximation runs at the end of the simulation to see how the quantum state behaves")
("simulate_grover", po::value<unsigned int>(), "simulate Grover's search for given number of qubits with random oracle")
("simulate_grover_emulated", po::value<unsigned int>(), "simulate Grover's search for given number of qubits with random oracle and emulation")
("simulate_grover_oracle_emulated", po::value<std::string>(), "simulate Grover's search for given number of qubits with given oracle and emulation")
("simulate_shor", po::value<unsigned int>(), "simulate Shor's algorithm factoring this number")
("simulate_shor_coprime", po::value<unsigned int>()->default_value(0), "coprime number to use with Shor's algorithm (zero randomly generates a coprime)")
("simulate_shor_no_emulation", "Force Shor simulator to do modular exponentiation instead of using emulation (you'll usually want emulation)")
("simulate_fast_shor", po::value<unsigned int>(), "simulate Shor's algorithm factoring this number with intermediate measurements")
("simulate_fast_shor_coprime", po::value<unsigned int>()->default_value(0),"coprime number to use with Shor's algorithm (zero randomly generates a coprime)");
// clang-format on
po::variables_map vm;
try {
po::store(po::parse_command_line(argc, argv, description), vm);
Expand Down Expand Up @@ -184,69 +188,32 @@ int main(int argc, char** argv) {
}
}

if (vm.count("benchmark")) {
auto more_info = ddsim->AdditionalStatistics();
std::cout << ddsim->getName() << ", "
<< ddsim->getNumberOfQubits() << ", "
<< std::fixed << duration_simulation.count() << std::defaultfloat << ", "
<< more_info["single_shots"] << ","
<< more_info["approximation_runs"] << ","
<< more_info["final_fidelity"] << ", "
<< more_info["coprime_a"] << ", "
<< more_info["sim_result"] << ", "
<< more_info["polr_result"] << ", "
<< ddsim->getSeed() << ", "
<< ddsim->getNumberOfOps() << ", "
<< ddsim->getMaxNodeCount()
<< "\n";
return 0;
}

std::cout << "{\n";
nl::json output_obj;

if (!m.empty()) {
std::cout << " \"measurements\": {";
bool first_element = true;
for (const auto& element: m) {
std::cout << (first_element ? "" : ",") << "\n \"" << element.first << "\": " << element.second;
first_element = false;
}
std::cout << "\n },\n";
if (vm.count("pm")) {
output_obj["measurement_results"] = m;
}
if (vm.count("display_vector")) {
std::cout << " \"state_vector\": [";

bool first_element = true;
unsigned long long non_zero_entries = 0;
for (const auto& element: ddsim->getVector()) {
if (element.r != 0 || element.i != 0) {
non_zero_entries++;
std::cout << (first_element ? "" : ",") << "\n " << std::showpos << element.r << element.i << "i"
<< std::noshowpos;
} else {
std::cout << (first_element ? "" : ",") << "\n 0";
}
first_element = false;
}
std::cout << "\n ],\n";
std::cout << " \"non_zero_entries\": " << non_zero_entries << ",\n";

if (vm.count("pv")) {
output_obj["state_vector"] = ddsim->getVectorPair();
}

if (vm.count("ps")) {
std::cout << " \"statistics\": {\n"
<< " \"simulation_time\": " << std::fixed << duration_simulation.count() << std::defaultfloat
<< ",\n"
<< " \"benchmark\": \"" << ddsim->getName() << "\",\n"
<< " \"shots\": " << shots << ",\n"
<< " \"distinct_results\": " << m.size() << ",\n"
<< " \"n_qubits\": " << ddsim->getNumberOfQubits() << ",\n"
<< " \"applied_gates\": " << ddsim->getNumberOfOps() << ",\n"
<< " \"max_nodes\": " << ddsim->getMaxNodeCount() << ",\n";
output_obj["statistics"] = {
{"simulation_time", duration_simulation.count()},
{"benchmark", ddsim->getName()},
{"n_qubits", +ddsim->getNumberOfQubits()},
{"applied_gates", ddsim->getNumberOfOps()},
{"max_nodes", ddsim->getMaxNodeCount()},
{"shots", shots},
{"distinct_results", m.size()},
{"seed", ddsim->getSeed()},
};

for (const auto& item: ddsim->AdditionalStatistics()) {
std::cout << " \"" << item.first << "\": \"" << item.second << "\",\n";
output_obj["statistics"][item.first] = item.second;
}
std::cout << " \"seed\": " << ddsim->getSeed() << "\n"
<< " },\n";
}
std::cout << " \"dummy\": 0\n}\n"; // trailing element to make json printout easier

std::cout << std::setw(2) << output_obj << std::endl;
}
Loading

0 comments on commit 6f9585e

Please sign in to comment.