Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add mcmc sampler #384

Merged
merged 58 commits into from
Mar 17, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
58 commits
Select commit Hold shift + click to select a range
daabc67
Add skeleton fo mcmc
trevor-vincent Nov 1, 2022
3789950
Add transition kernel infrastructure
trevor-vincent Nov 3, 2022
c8c1d2e
Make new file for transition kernels
trevor-vincent Nov 3, 2022
0bdabad
Add fixes for compilation errors
trevor-vincent Nov 4, 2022
502451f
Fix all compiler errors
trevor-vincent Nov 8, 2022
95dba4f
Run make format
trevor-vincent Nov 8, 2022
d5d3ec0
Update pennylane_lightning/src/simulator/Measures.hpp
trevor-vincent Nov 8, 2022
778e145
Merge branch 'master' into add-mcmc-sampler
AmintorDusko Nov 9, 2022
b475961
Auto update version
github-actions[bot] Nov 9, 2022
08f14b3
Add comments
trevor-vincent Nov 14, 2022
8736d60
Add initializer list
trevor-vincent Nov 14, 2022
7ce3a7e
Add test for other transition kernel
trevor-vincent Nov 14, 2022
60f6f66
fix instantiation errors
Nov 15, 2022
1fb80ad
fix merge conflicts
Nov 15, 2022
f9eaf24
Auto update version
github-actions[bot] Nov 15, 2022
851c8a8
add python layer
Nov 15, 2022
5c462dc
remove init_state
Nov 16, 2022
f6e0855
clean code
Nov 16, 2022
a7c5cb0
code clean
Nov 16, 2022
b4823a5
add destructor to transitional kernels
Nov 16, 2022
86d80b7
add constructor to TransitionKernel
Nov 16, 2022
aae4fe5
further clean code
Nov 16, 2022
d22b97e
add more tests for codecov
multiphaseCFD Nov 16, 2022
2b64c31
codecov fix test
multiphaseCFD Nov 16, 2022
5eb2353
fix in testhelper
multiphaseCFD Nov 16, 2022
e51c12e
codecov fix try
multiphaseCFD Nov 16, 2022
1deba1c
codecov test
multiphaseCFD Nov 16, 2022
1d663e7
codecov fix test
multiphaseCFD Nov 16, 2022
73de232
codecov fix test
multiphaseCFD Nov 16, 2022
4050e8a
codecov fix
multiphaseCFD Nov 16, 2022
53725cf
codecov fix
multiphaseCFD Nov 16, 2022
d7593c9
codefactor fix try
multiphaseCFD Nov 16, 2022
2979ccf
codefactor fix try
multiphaseCFD Nov 16, 2022
ab62fb1
remove blank lines
Nov 16, 2022
e05b291
add comments
Nov 16, 2022
603c8bc
clean testing
Nov 17, 2022
f40510f
update code based on comments
Nov 21, 2022
bbdb9b6
nomenclature change
Nov 21, 2022
d621227
make format
Nov 21, 2022
a224a81
update code
Nov 22, 2022
854bab6
fix merge conflicts
multiphaseCFD Nov 22, 2022
357bda6
Merge branch 'master' into add-mcmc-sampler
mlxd Feb 8, 2023
2fcb7bb
Auto update version
github-actions[bot] Feb 8, 2023
3794551
Merge branch 'master' into add-mcmc-sampler
multiphaseCFD Mar 10, 2023
b77208b
Auto update version
github-actions[bot] Mar 10, 2023
bc96c28
quick fix
multiphaseCFD Mar 10, 2023
c042a38
make format
multiphaseCFD Mar 10, 2023
a93a6f7
update docstring
multiphaseCFD Mar 14, 2023
789edaf
update docstring
multiphaseCFD Mar 14, 2023
89ae6ef
add mcmc to ctor
multiphaseCFD Mar 14, 2023
5b037dc
add docstrings
multiphaseCFD Mar 15, 2023
1dadfc9
Update tests/test_measures.py
multiphaseCFD Mar 17, 2023
d789e1a
Update doc/devices.rst
multiphaseCFD Mar 17, 2023
1e74845
Update doc/devices.rst
multiphaseCFD Mar 17, 2023
204851e
Update doc/devices.rst
multiphaseCFD Mar 17, 2023
b6850c8
Update pennylane_lightning/lightning_qubit.py
multiphaseCFD Mar 17, 2023
ce3c50d
Update pennylane_lightning/lightning_qubit.py
multiphaseCFD Mar 17, 2023
f51d1d5
Update pennylane_lightning/lightning_qubit.py
multiphaseCFD Mar 17, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion .github/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,9 @@

### New features since last release

* Add MCMC sampler.
[(#384)] (https://github.com/PennyLaneAI/pennylane-lightning/pull/384)

### Breaking changes

### Improvements
Expand Down Expand Up @@ -68,7 +71,7 @@ Amintor Dusko, Vincent Michaud-Rioux, Lee James O'Riordan, Chae-Yeun Park

This release contains contributions from (in alphabetical order):

Amintor Dusko
Amintor Dusko, Shuli Shu, Trevor Vincent

---

Expand Down
23 changes: 23 additions & 0 deletions doc/devices.rst
Original file line number Diff line number Diff line change
Expand Up @@ -98,3 +98,26 @@ If you are computing a large number of expectation values, or if you are using a
os.environ["OMP_NUM_THREADS"] = 4
import pennylane as qml
dev = qml.device("lightning.qubit", wires=2, batch_obs=True)

.. raw:: html

</div>

**Markov Chain Monte Carlo sampling support:**

The ``lightning.qubit`` device allows users to use the Markov Chain Monte Carlo (MCMC) sampling method to generate approximate samples. To enable the MCMC sampling method for sample generation, initialize a ``lightning.qubit`` device with the ``mcmc=True`` keyword argument, as:

.. code-block:: python

import pennylane as qml
dev = qml.device("lightning.qubit", wires=2, shots=1000, mcmc=True)

By default, the ``kernel_name`` is ``"Local"`` and ``num_burnin`` is ``100``. The local kernel conducts a bit-flip local transition between states. The local kernel generates a random qubit site and then generates a random number to determine the new bit at that qubit site.

The ``lightning.qubit`` device also supports a ``"NonZeroRandom"`` kernel. This kernel randomly transits between states that have nonzero probability. It can be enabled by initializing the device as:

.. code-block:: python

import pennylane as qml
dev = qml.device("lightning.qubit", wires=2, shots=1000, mcmc=True, kernel_name="NonZeroRandom", num_burnin=200)

2 changes: 1 addition & 1 deletion pennylane_lightning/_version.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,4 +16,4 @@
Version number (major.minor.patch[-label])
"""

__version__ = "0.30.0-dev"
__version__ = "0.30.0-dev1"
43 changes: 39 additions & 4 deletions pennylane_lightning/lightning_qubit.py
Original file line number Diff line number Diff line change
Expand Up @@ -160,6 +160,13 @@ class LightningQubit(QubitDevice):
the expectation values. Defaults to ``None`` if not specified. Setting
to ``None`` results in computing statistics like expectation values and
variances analytically.
mcmc (bool): Determine whether to use the approximate Markov Chain Monte Carlo sampling method when generating samples.
kernel_name (str): name of transition kernel. The current version supports two kernels: ``"Local"`` and ``"NonZeroRandom"``.
The local kernel conducts a bit-flip local transition between states. The local kernel generates a
random qubit site and then generates a random number to determine the new bit at that qubit site. The ``"NonZeroRandom"`` kernel
randomly transits between states that have nonzero probability.
num_burnin (int): number of steps that will be dropped. Increasing this value will
result in a closer approximation but increased runtime.
batch_obs (bool): Determine whether we process observables parallelly when computing the
jacobian. This value is only relevant when the lightning qubit is built with OpenMP.
"""
Expand All @@ -173,7 +180,18 @@ class LightningQubit(QubitDevice):
operations = allowed_operations
observables = allowed_observables

def __init__(self, wires, *, c_dtype=np.complex128, shots=None, batch_obs=False, analytic=None):
def __init__(
self,
wires,
*,
c_dtype=np.complex128,
shots=None,
mcmc=False,
kernel_name="Local",
num_burnin=100,
mlxd marked this conversation as resolved.
Show resolved Hide resolved
batch_obs=False,
analytic=None,
):
if c_dtype is np.complex64:
r_dtype = np.float32
self.use_csingle = True
Expand All @@ -190,6 +208,20 @@ def __init__(self, wires, *, c_dtype=np.complex128, shots=None, batch_obs=False,
self._state = self._create_basis_state(0)
self._pre_rotated_state = self._state

self._mcmc = mcmc
if self._mcmc:
if kernel_name not in [
"Local",
"NonZeroRandom",
]:
raise NotImplementedError(
f"The {kernel_name} is not supported and currently only 'Local' and 'NonZeroRandom' kernels are supported."
)
if num_burnin >= shots:
raise ValueError("Shots should be greater than num_burnin.")
self._kernel_name = kernel_name
self._num_burnin = num_burnin

@property
def stopping_condition(self):
""".BooleanFn: Returns the stopping condition for the device. The returned
Expand Down Expand Up @@ -776,14 +808,17 @@ def generate_samples(self):
Returns:
array[int]: array of samples in binary representation with shape ``(dev.shots, dev.num_wires)``
"""

# Initialization of state
ket = np.ravel(self._state)

state_vector = StateVectorC64(ket) if self.use_csingle else StateVectorC128(ket)
M = MeasuresC64(state_vector) if self.use_csingle else MeasuresC128(state_vector)

return M.generate_samples(len(self.wires), self.shots).astype(int, copy=False)
if self._mcmc:
return M.generate_mcmc_samples(
len(self.wires), self._kernel_name, self._num_burnin, self.shots
).astype(int, copy=False)
else:
return M.generate_samples(len(self.wires), self.shots).astype(int, copy=False)

def expval(self, observable, shot_range=None, bin_size=None):
"""Expectation value of the supplied observable.
Expand Down
21 changes: 21 additions & 0 deletions pennylane_lightning/src/bindings/Bindings.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -135,6 +135,27 @@ void lightning_class_bindings(py::module_ &m) {
strides /* strides for each axis */
));
})
.def("generate_mcmc_samples",
[](Measures<PrecisionT> &M, size_t num_wires,
const std::string &kernelname, size_t num_burnin,
size_t num_shots) {
std::vector<size_t> &&result = M.generate_samples_metropolis(
kernelname, num_burnin, num_shots);

const size_t ndim = 2;
const std::vector<size_t> shape{num_shots, num_wires};
constexpr auto sz = sizeof(size_t);
const std::vector<size_t> strides{sz * num_wires, sz};
// return 2-D NumPy array
return py::array(py::buffer_info(
result.data(), /* data as contiguous array */
sz, /* size of one scalar */
py::format_descriptor<size_t>::format(), /* data type */
ndim, /* number of dimensions */
shape, /* shape of the matrix */
strides /* strides for each axis */
));
})
.def("var",
[](Measures<PrecisionT> &M, const std::string &operation,
const std::vector<size_t> &wires) {
Expand Down
2 changes: 1 addition & 1 deletion pennylane_lightning/src/simulator/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
project(lightning_simulator)

set(SIMULATOR_FILES StateVectorRawCPU.cpp Observables.cpp StateVectorManagedCPU.cpp Measures.cpp CACHE INTERNAL "" FORCE)
set(SIMULATOR_FILES StateVectorRawCPU.cpp Observables.cpp StateVectorManagedCPU.cpp TransitionKernels.cpp Measures.cpp CACHE INTERNAL "" FORCE)

add_library(lightning_simulator STATIC ${SIMULATOR_FILES})

Expand Down
101 changes: 101 additions & 0 deletions pennylane_lightning/src/simulator/Measures.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@
#include "Observables.hpp"
#include "StateVectorManagedCPU.hpp"
#include "StateVectorRawCPU.hpp"
#include "TransitionKernels.hpp"

namespace Pennylane::Simulators {

Expand Down Expand Up @@ -331,6 +332,106 @@ class Measures {
return expected_value_list;
};

/**
* @brief Complete a single Metropolis-Hastings step.
*
* @param sv state vector
* @param tk User-defined functor for producing transitions
* between metropolis states.
* @param gen Random number generator.
* @param distrib Random number distribution.
* @param init_idx Init index of basis state.
*/
size_t metropolis_step(const SVType &sv,
const std::unique_ptr<TransitionKernel<fp_t>> &tk,
std::mt19937 &gen,
std::uniform_real_distribution<fp_t> &distrib,
multiphaseCFD marked this conversation as resolved.
Show resolved Hide resolved
size_t init_idx) {
auto init_plog = std::log(
(sv.getData()[init_idx] * std::conj(sv.getData()[init_idx]))
.real());

auto init_qratio = tk->operator()(init_idx);

// transition kernel outputs these two
auto &trans_idx = init_qratio.first;
auto &trans_qratio = init_qratio.second;

auto trans_plog = std::log(
(sv.getData()[trans_idx] * std::conj(sv.getData()[trans_idx]))
.real());

auto alph =
std::min<fp_t>(1., trans_qratio * std::exp(trans_plog - init_plog));
auto ran = distrib(gen);

if (ran < alph) {
return trans_idx;
}
return init_idx;
}

/**
* @brief Generate samples using the Metropolis-Hastings method.
* Reference: Numerical Recipes, NetKet paper
*
* @param transition_kernel User-defined functor for producing transitions
* between metropolis states.
* @param num_burnin Number of Metropolis burn-in steps.
* @param num_samples The number of samples to generate.
* @return 1-D vector of samples in binary, each sample is
* separated by a stride equal to the number of qubits.
*/
std::vector<size_t>
generate_samples_metropolis(const std::string &kernelname,
size_t num_burnin, size_t num_samples) {
size_t num_qubits = original_statevector.getNumQubits();
std::random_device rd;
std::mt19937 gen(rd());
std::uniform_real_distribution<fp_t> distrib(0.0, 1.0);
std::vector<size_t> samples(num_samples * num_qubits, 0);
std::unordered_map<size_t, size_t> cache;

TransitionKernelType transition_kernel =
Pennylane::TransitionKernelType::Local;
if (kernelname == "NonZeroRandom") {
transition_kernel = Pennylane::TransitionKernelType::NonZeroRandom;
}

auto tk =
kernel_factory(transition_kernel, original_statevector.getData(),
original_statevector.getNumQubits());
size_t idx = 0;

// Burn In
for (size_t i = 0; i < num_burnin; i++) {
idx = metropolis_step(original_statevector, tk, gen, distrib,
idx); // Burn-in.
}

// Sample
for (size_t i = 0; i < num_samples; i++) {
idx = metropolis_step(original_statevector, tk, gen, distrib, idx);

if (cache.contains(idx)) {
size_t cache_id = cache[idx];
auto it_temp = samples.begin() + cache_id * num_qubits;
std::copy(it_temp, it_temp + num_qubits,
samples.begin() + i * num_qubits);
}

// If not cached, compute
else {
for (size_t j = 0; j < num_qubits; j++) {
samples[i * num_qubits + (num_qubits - 1 - j)] =
(idx >> j) & 1U;
}
cache[idx] = i;
}
}
return samples;
}

/**
* @brief Variance of a Sparse Hamiltonian.
*
Expand Down
9 changes: 9 additions & 0 deletions pennylane_lightning/src/simulator/TransitionKernels.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
#include "TransitionKernels.hpp"

// explicit instantiation
template class Pennylane::TransitionKernel<float>;
template class Pennylane::TransitionKernel<double>;
template class Pennylane::LocalTransitionKernel<float>;
template class Pennylane::LocalTransitionKernel<double>;
template class Pennylane::NonZeroRandomTransitionKernel<float>;
template class Pennylane::NonZeroRandomTransitionKernel<double>;
Loading