diff --git a/.bumpversion.cfg b/.bumpversion.cfg index 2cb216aa..4593489d 100644 --- a/.bumpversion.cfg +++ b/.bumpversion.cfg @@ -1,5 +1,5 @@ [bumpversion] -current_version = 3.0.12 +current_version = 3.0.13 commit = True tag = False diff --git a/CHANGELOG.md b/CHANGELOG.md index e0c2aca8..e444fcdd 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,10 @@ For more details refer to the [documentation](https://softwarequtech.github.io/S Upcoming -------- +2020-02-23 (v3.0.13) +------------------- +- Fixed bug in the Boolean Gaussian elimination in stabilizer formalism. + 2020-01-27 (v3.0.12) ------------------- - Boolean Gaussian elimination in stabilizer formalism is now even faster. diff --git a/README.md b/README.md index 505ecd4c..a99ea719 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ [![Build Status](https://travis-ci.com/SoftwareQuTech/SimulaQron.svg?branch=Develop)](https://travis-ci.com/SoftwareQuTech/SimulaQron) -SimulaQron - simple quantum network simulator (3.0.12) +SimulaQron - simple quantum network simulator (3.0.13) ===================================================== The purpose of this simulator of quantum network nodes is to allow you to develop new applications for diff --git a/docs/Makefile b/docs/Makefile index af74befc..72d7e259 100644 --- a/docs/Makefile +++ b/docs/Makefile @@ -251,7 +251,10 @@ dummy: python-deps: @cat requirements.txt | xargs -n 1 -L 1 pip3 install -build: python-deps html +_add_jekyll: + touch build/.nojekyll + +build: python-deps html _add_jekyll open: @echo "test" diff --git a/simulaqron/__init__.py b/simulaqron/__init__.py index a7171129..1e4a2e41 100644 --- a/simulaqron/__init__.py +++ b/simulaqron/__init__.py @@ -1,4 +1,4 @@ from simulaqron.tests_run import main as tests __all__ = ['tests'] -__version__ = '3.0.12' +__version__ = '3.0.13' diff --git a/simulaqron/tests/unittests/quick/stabilizerStates/test_stabilizerStates.py b/simulaqron/tests/unittests/quick/stabilizerStates/test_stabilizerStates.py index 673c4200..bb7839c9 100644 --- a/simulaqron/tests/unittests/quick/stabilizerStates/test_stabilizerStates.py +++ b/simulaqron/tests/unittests/quick/stabilizerStates/test_stabilizerStates.py @@ -402,6 +402,81 @@ def test_measure_eigenstate(self): output = s.measure(qubit) self.assertEqual(output, expected) + def test_correlations(self): + tests = [ # stabilizers + ["ZI", "IZ"], + ["ZZ", "XX"], + ["ZX", "XZ"], + ["XXX", "ZZI", "IZZ"], + ["XIII", "IXII", "IIXI", "IIIX"], + ["XZII", "ZXZI", "IZXZ", "IIZX"], # line graph + ["XZIZ", "ZXZI", "IZXZ", "ZIZX"], # cycle graph + ["XZZZ", "ZXZZ", "ZZXZ", "ZZZX"], # complete graph + ["XZZZ", "ZXII", "ZIXI", "ZIIX"], # star graph + ] + + for stabilizers in tests: + for stabilizer in stabilizers: + for _ in range(10): + with self.subTest(stabilizers=stabilizers, stabilizer=stabilizer): + s = StabilizerState(stabilizers) + outcomes = [] + qubit = 0 + for pauli in stabilizer: + if pauli == 'X': + s.apply_H(qubit) + elif pauli == 'Y': + s.apply_K(qubit) + elif pauli == 'Z': + pass + else: + qubit += 1 + continue + outcomes.append(s.measure(qubit)) + self.assertEqual(sum(outcomes) % 2, 0) + + def test_standard_form(self): + tests = [ # stabilizers + ["ZI", "IZ"], + ["ZZ", "XX"], + ["ZX", "XZ"], + ["XXX", "ZZI", "IZZ"], + ["XIII", "IXII", "IIXI", "IIIX"], + ["XZII", "ZXZI", "IZXZ", "IIZX"], # line graph + ["XZIZ", "ZXZI", "IZXZ", "ZIZX"], # cycle graph + ["XZZZ", "ZXZZ", "ZZXZ", "ZZZX"], # complete graph + ["XZZZ", "ZXII", "ZIXI", "ZIIX"], # star graph + ] + + for stabilizers in tests: + with self.subTest(stabilizers=stabilizers): + state = StabilizerState(stabilizers) + state.put_in_standard_form() + # Check that there are no X or Y in the first column except the first row + for row in state._group[1:, :]: + self.assertFalse(row[0]) + + def test_reduce_when_measuring(self): + tests = [ # stabilizers + ["ZI", "IZ"], + ["ZZ", "XX"], + ["ZX", "XZ"], + ["XXX", "ZZI", "IZZ"], + ["XIII", "IXII", "IIXI", "IIIX"], + ["XZII", "ZXZI", "IZXZ", "IIZX"], # line graph + ["XZIZ", "ZXZI", "IZXZ", "ZIZX"], # cycle graph + ["XZZZ", "ZXZZ", "ZZXZ", "ZZZX"], # complete graph + ["XZZZ", "ZXII", "ZIXI", "ZIIX"], # star graph + ] + + for stabilizers in tests: + with self.subTest(stabilizers=stabilizers): + state = StabilizerState(stabilizers) + n = len(state) + for i in range(n): + state.measure(0) + self.assertEqual(len(state), n - i - 1) + if __name__ == "__main__": unittest.main() diff --git a/simulaqron/toolbox/stabilizerStates.py b/simulaqron/toolbox/stabilizerStates.py index 46de4170..83e3233d 100644 --- a/simulaqron/toolbox/stabilizerStates.py +++ b/simulaqron/toolbox/stabilizerStates.py @@ -225,13 +225,19 @@ def __str__(self): def __len__(self): return self.num_qubits + @staticmethod + def _row_to_string(row): + assert (len(row) - 1) % 2 == 0 + n = int((len(row) - 1) / 2) + to_return = "{} ".format(StabilizerState.bool2phase[row[-1]]) + for i in range(n): + to_return += StabilizerState.bool2Pauli[(row[i], row[i + n])] + return to_return + def to_string(self): to_return = "" for row in self._group: - to_return += "{} ".format(self.bool2phase[row[-1]]) - n = self.num_qubits - for i in range(n): - to_return += self.bool2Pauli[(row[i], row[i + n])] + to_return += self._row_to_string(row) to_return += "\n" return to_return[:-1] @@ -285,6 +291,7 @@ def boolean_gaussian_elimination(matrix, return_pivot_columns=False): else: i_max = non_zero_ind_under_h[0] if i_max != h: + # Move the row i_max to the h row new_matrix[[h, i_max]] = new_matrix[[i_max, h]] # Add pivot row to the rest pivot_columns.append(k) @@ -292,7 +299,7 @@ def boolean_gaussian_elimination(matrix, return_pivot_columns=False): if len(non_zero_except_i_max) > 0: new_matrix[non_zero_except_i_max, :] = np.apply_along_axis( - lambda row: StabilizerState._multiply_stabilizers(row, new_matrix[i_max]), + lambda row: StabilizerState._multiply_stabilizers(row, new_matrix[h]), 1, new_matrix[non_zero_except_i_max, :], ) @@ -707,7 +714,7 @@ def measure(self, position, inplace=False): """ n = self.num_qubits if not (position >= 0 and position < n): - raise ValueError("position= {} if not a valid qubit position (i.e. in [0, {}]".format(position, n)) + raise ValueError("position = {} if not a valid qubit position (not in [0, {}))".format(position, n)) tmp_matrix = self._group # Create a new matrix where the X and Z columns of the corresponding qubit are the first.