Skip to content

Commit

Permalink
Initial release
Browse files Browse the repository at this point in the history
Project focuses on developing a spin-glass solver based on a family of simulated annealing algorithms for large system sizes and an exhaustive search for small Ising instances. This solver is implemented using an Intel's oneAPI development stack.
  • Loading branch information
kamilhendzel committed Dec 23, 2020
0 parents commit 1c72590
Show file tree
Hide file tree
Showing 86 changed files with 124,618 additions and 0 deletions.
3 changes: 3 additions & 0 deletions .clang-format
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# Use Clang-Format, docunentation:
# https://clang.llvm.org/docs/ClangFormatStyleOptions.html
BasedOnStyle: LLVM
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
.vscode
build/
doc/
17 changes: 17 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
cmake_minimum_required(VERSION 3.9)

project("one-solver" CXX)

set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)


set(CMAKE_C_COMPILER clang)
set(CMAKE_CXX_COMPILER clang++)

set(CMAKE_CXX_FLAGS "-fsycl")

find_package(Boost 1.71)

add_subdirectory(app)
add_subdirectory(tests)
2,579 changes: 2,579 additions & 0 deletions Doxyfile

Large diffs are not rendered by default.

21 changes: 21 additions & 0 deletions LICENSE
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
MIT License

Copyright (c) 2020 hyperQ – Ewa Hendzel

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
99 changes: 99 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
# oneSolver
Created by hperQ - Ewa Hendzel

## Build instructions

```
mkdir build && cd build
cmake ..
cmake --build .
```

## Running the applications
Binaries are ``one-solver-anneal`` and ``one-solver-exhaustive`` created
in the app folder. Binary for tests is located in ``tests``.

The binary named `one-solver-anneal` allows to solve a QUBO problem using
the simulated annealing algorithm whereas `one-solver-exhaustive` uses
the brute force search method.

In the ``tests`` folder the application called `main-tests` is build.

In order to obtain help about the applications arguments
use the `--help` switch. For example, the command
`./one-solver-anneal --help` prints:

```
Allowed options:
--help produce help message
--input arg input file
--output arg output file
--num-iter arg (=100) number of iterations of the algorithm
--num-tries arg (=100) number of trajectories to try
--schedule-type arg (=geometric) type of beta schedule tu use, either
linear or geometric
--beta-min arg (=0.10000000000000001) minimum value of beta in the annealing
schedule (default 0.1)
--beta-max arg (=1) maximum value of beta in the annealing
schedule (default 1.0)
--device-type arg (=host) device type to use (cpu, gpu or host)
```

The command `./one-solver-exhaustive --help` prints:

```
Allowed options:
--help produce help message
--input arg input file
--output arg output file
--device-type arg (=host) device type to use (cpu, gpu or host)
```

The following applications call

```
./one-solver-exhaustive --input ../../examples/test1.qubo --output result1.qubo
```

executes the program for the model described in ``test1.qubo`` file and
the obtained results are written to ``result1.qubo`` file.

## Original goal of the project

The project\'s goal is to develop a spin-glass (i.e., Ising model)
solver based on a family of simulated annealing algorithms for large
system sizes and an exhaustive search for small Ising instances. This
solver will be implemented using an oneAPI development stack to utilize
massive parallel capabilities of present-day computing architectures.

It is well established that NP-hard problems can be solved by cleverly
mapping them to the classical Ising Hamiltonians (i.e., spin-glasses).
With the rapid development of both quantum and classical annealers, the
scientific and computer engineering communities have intertwined once
again in their efforts to reformulate many real-life combinatorial
optimization problems using the ideal of interacting spin variables
(i.e., spins). Various dedicated devices and computer chips are being
developed currently to tackle the very problem of finding the low-energy
configurations (i.e., spectrum) of the Ising models. For instance,
Fujitsu and Hitachi have produced their digital and analog classical
annealers. In stark contrast, D-Wave Inc. has manufactured its quantum
annealers, which promises to solve the Ising instances using the law of
quantum physics; the famous adiabatic theorem to be more specific.

Although there are many sophisticated methods (e.g., tensor networks,
simulated quantum annealing, path integrals, etc.) to approach the
aforementioned problems, not that many of them can benefit from the
High-Performance Computing (HPC) available today. On the other hand,
fundamental techniques such as the Brute-Force (that simply executes an
exhaustive search) or simulated annealing (that tries to mimic a famous
physical process of annealing known in the material science) can take
advantage of highly parallel architectures (graphics card, many-cores).
This project is devoted precisely for that purpose; i.e., we aim to
provide conceptually simple solvers that can be executed concurrently on
present-day, massively parallel, classical architectures in an agnostic
manner.

Literature:
1. [Brute-forcing spin-glass problems with CUDA](https://arxiv.org/abs/1904.03621)
2. [Demonstration of a scaling advantage for a quantum annealer over simulated annealing](https://arxiv.org/abs/1705.07452)
3. [Parallel in time dynamics with quantum annealers](https://www.nature.com/articles/s41598-020-70017-x)
13 changes: 13 additions & 0 deletions app/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
find_package(Boost 1.71 COMPONENTS program_options REQUIRED)

set(APP_MODULE_DIRECTORY "${PROJECT_SOURCE_DIR}/app")
set(APP_SOURCE_DIRECTORY "${APP_MODULE_DIRECTORY}")

file(GLOB APP_SOURCES "${APP_SOURCE_DIRECTORY}/*.cpp")
foreach(sourcefile ${APP_SOURCES})
get_filename_component(filename ${sourcefile} NAME_WE)
add_executable(${filename} ${sourcefile})
target_include_directories(${filename} PRIVATE "${PROJECT_SOURCE_DIR}/include")
target_link_libraries(${filename} ${LIBRARY_NAME})
target_link_libraries(${filename} Boost::program_options)
endforeach(sourcefile ${APP_SOURCES})
179 changes: 179 additions & 0 deletions app/one-solver-anneal.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,179 @@
/*! file one-solver-anneal.cpp
*
* @copyright Licensed under MIT license by hyperQ – Ewa Hendzel
*
*/

#include <boost/program_options.hpp>
#include <fstream>
#include <iostream>

#include "CL/sycl.hpp"
#include "helpers/devices.hpp"
#include "helpers/qubo_helpers.hpp"
#include "model/qubo.hpp"
#include "model/solution.hpp"
#include "simulated_annealing/annealing.hpp"

namespace po = boost::program_options;
namespace sycl = cl::sycl;

using queue_ptr = std::unique_ptr<sycl::queue>;

void construct_linear_beta_schedule(std::vector<double> &schedule,
double beta_min, double beta_max,
uint num_iter) {
for (auto i = 0; i < num_iter; i++) {
schedule[i] = beta_min + beta_max * i / static_cast<double>(num_iter - 1);
}
}

void construct_geometric_beta_schedule(std::vector<double> &schedule,
double beta_min, double beta_max,
uint num_iter) {
schedule[0] = beta_min;
auto alpha = pow(beta_max / beta_min, 1.0 / (num_iter - 1));
for (auto i = 1; i < num_iter; i++) {
schedule[i] = (schedule[i - 1]) * alpha;
}
}

int main(int argc, char *argv[]) {

std::string input_file;
std::string output_file;
std::string schedule_type;
std::string device_type;

uint num_iter;
uint num_tries;

double beta_min, beta_max;

try {

po::options_description options("Allowed options");
options.add_options()("help", "produce help message")(
"input", po::value<std::string>(&input_file), "input file")(
"output", po::value<std::string>(&output_file), "output file")(
"num-iter", po::value<uint>(&num_iter)->default_value(100),
"number of iterations of the algorithm")(
"num-tries", po::value<uint>(&num_tries)->default_value(100),
"number of trajectories to try")(
"schedule-type",
po::value<std::string>(&schedule_type)->default_value("geometric"),
"type of beta schedule tu use, either linear or geometric")(
"beta-min", po::value<double>(&beta_min)->default_value(0.1),
"minimum value of beta in the annealing schedule (default 0.1)")(
"beta-max", po::value<double>(&beta_max)->default_value(1.0),
"maximum value of beta in the annealing schedule (default 1.0)")(
"device-type",
po::value<std::string>(&device_type)->default_value("host"),
"device type to use (cpu, gpu or host)");

po::variables_map vm;
po::store(po::parse_command_line(argc, argv, options), vm);
po::notify(vm);

if (vm.count("help")) {
std::cout << options << std::endl;
return 0;
}

if (!vm.count("input")) {
std::cerr << "No input file provided." << std::endl;
return -1;
}

if (!vm.count("output")) {
std::cerr << "No output file provided." << std::endl;
return -1;
}

if (device_type != "cpu" && device_type != "gpu" && device_type != "host") {
std::cerr << "Unknown device type: " << device_type << std::endl;
return -1;
}

if (schedule_type != "linear" && schedule_type != "geometric") {
std::cerr << "Unknown beta schedule: " << schedule_type << std::endl;
return -1;
}

if (beta_max < 0 || beta_min < 0) {
std::cerr
<< "Invalid schedule, both ends of beta range need to be positive"
<< std::endl;
return -1;
}

if (beta_min >= beta_max) {
std::cerr
<< "Invalid schedule, initial beta is not lesser than final beta"
<< std::endl;
return -1;
}

std::cout << "Reading input from: " << input_file << std::endl;

std::cout << "Output will be saved to: " << output_file << std::endl;

std::cout << "Schedule type: " << schedule_type << std::endl;

std::cout << "Beta range: [" << beta_min << ", " << beta_max << "]"
<< std::endl;

std::cout << "Number of iterations: " << num_iter << std::endl;
std::cout << "Number of tries: " << num_tries << std::endl;

std::ifstream qubo_file(input_file);

if (!qubo_file) {
std::cerr << "can not open input file: " << input_file << std::endl;
return -1;
}

qubo_file.unsetf(std::ios::skipws);
auto instance = qubo::QUBOModel<int, double>::load(qubo_file);

queue_ptr q_ptr;

try {
q_ptr = queue_ptr(
new sycl::queue(*devices::construct_device_selector(device_type)));
} catch (std::runtime_error) {
std::cerr << "No devices of given type could be initialized."
<< std::endl;
}

std::cout << "Using device: "
<< q_ptr->get_device().get_info<sycl::info::device::name>()
<< std::endl;

std::vector<double> beta_schedule(num_iter);

if (schedule_type == "linear") {
construct_linear_beta_schedule(beta_schedule, beta_min, beta_max,
num_iter);
} else {
construct_geometric_beta_schedule(beta_schedule, beta_min, beta_max,
num_iter);
}

auto solution =
sa::anneal(instance, *q_ptr, beta_schedule, num_iter, num_tries);

std::ofstream results_file(output_file);

solution.save(results_file);

results_file.close();
} catch (std::exception &e) {
std::cerr << "error: " << e.what() << "\n";
return 1;
} catch (...) {
std::cerr << "Exception of unknown type!\n";
}

return 0;
}
Loading

0 comments on commit 1c72590

Please sign in to comment.