Skip to content

Commit

Permalink
Remove the constraint that stim.Tableau.from_state_vector inputs must…
Browse files Browse the repository at this point in the history
… be normalized (#840)

Just taking a stab at fixing
#638. Note, I don't fully
understand how Tableau should work but I wrote the tests based on the
examples, so I think this should be correct.

- Adds normalization code by scaling each amplitude by the square root
of the inverse of the state vector norm
- Adds tests to match the examples from `from_state_vector` including an
unnormalized version
  • Loading branch information
drqec authored Oct 11, 2024
1 parent 49531a4 commit 34b4168
Show file tree
Hide file tree
Showing 8 changed files with 48 additions and 16 deletions.
2 changes: 1 addition & 1 deletion doc/python_api_reference_vDev.md
Original file line number Diff line number Diff line change
Expand Up @@ -11665,7 +11665,7 @@ def from_state_vector(
Args:
state_vector: A list of complex amplitudes specifying a superposition. The
vector must correspond to a state that is reachable using Clifford
operations, and must be normalized (i.e. it must be a unit vector).
operations, and can be unnormalized.
endian:
"little": state vector is in little endian order, where higher index
qubits correspond to larger changes in the state index.
Expand Down
2 changes: 1 addition & 1 deletion doc/stim.pyi
Original file line number Diff line number Diff line change
Expand Up @@ -9169,7 +9169,7 @@ class Tableau:
Args:
state_vector: A list of complex amplitudes specifying a superposition. The
vector must correspond to a state that is reachable using Clifford
operations, and must be normalized (i.e. it must be a unit vector).
operations, and can be unnormalized.
endian:
"little": state vector is in little endian order, where higher index
qubits correspond to larger changes in the state index.
Expand Down
2 changes: 1 addition & 1 deletion glue/python/src/stim/__init__.pyi
Original file line number Diff line number Diff line change
Expand Up @@ -9169,7 +9169,7 @@ class Tableau:
Args:
state_vector: A list of complex amplitudes specifying a superposition. The
vector must correspond to a state that is reachable using Clifford
operations, and must be normalized (i.e. it must be a unit vector).
operations, and can be unnormalized.
endian:
"little": state vector is in little endian order, where higher index
qubits correspond to larger changes in the state index.
Expand Down
2 changes: 1 addition & 1 deletion src/stim/stabilizers/tableau.pybind.cc
Original file line number Diff line number Diff line change
Expand Up @@ -2147,7 +2147,7 @@ void stim_pybind::pybind_tableau_methods(pybind11::module &m, pybind11::class_<T
Args:
state_vector: A list of complex amplitudes specifying a superposition. The
vector must correspond to a state that is reachable using Clifford
operations, and must be normalized (i.e. it must be a unit vector).
operations, and can be unnormalized.
endian:
"little": state vector is in little endian order, where higher index
qubits correspond to larger changes in the state index.
Expand Down
9 changes: 9 additions & 0 deletions src/stim/stabilizers/tableau_pybind_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
import random
import re

import numpy as np
Expand Down Expand Up @@ -57,6 +58,14 @@ def test_from_named_gate():
stim.Tableau.from_named_gate("X_ERROR")


def test_from_state_vector_fuzz():
for n in range(1, 7):
t = stim.Tableau.random(n)
v = t.to_state_vector() * (random.random() + 1j*random.random())
t2 = stim.Tableau.from_state_vector(v, endian='little')
np.testing.assert_array_equal(t.to_stabilizers(canonicalize=True), t2.to_stabilizers(canonicalize=True))


def test_identity():
t = stim.Tableau(3)
assert len(t) == 3
Expand Down
11 changes: 1 addition & 10 deletions src/stim/util_top/circuit_vs_amplitudes.cc
Original file line number Diff line number Diff line change
Expand Up @@ -40,15 +40,6 @@ Circuit stim::stabilizer_state_vector_to_circuit(
}

uint8_t num_qubits = floor_lg2(state_vector.size());
double weight = 0;
for (const auto &c : state_vector) {
weight += std::norm(c);
}
if (abs(weight - 1) > 0.125) {
throw std::invalid_argument(
"The given state vector wasn't a unit vector. It had a length of " + std::to_string(weight) + ".");
}

VectorSimulator sim(num_qubits);
sim.state = state_vector;

Expand All @@ -73,7 +64,7 @@ Circuit stim::stabilizer_state_vector_to_circuit(
{});
};

// Move biggest amplitude to start of state vector..
// Move biggest amplitude to start of state vector.
size_t pivot = biggest_index(state_vector);
for (size_t q = 0; q < num_qubits; q++) {
if ((pivot >> q) & 1) {
Expand Down
3 changes: 2 additions & 1 deletion src/stim/util_top/circuit_vs_amplitudes.h
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,8 @@ namespace stim {
/// Synthesizes a circuit to generate the given state vector.
///
/// Args:
/// stabilizer_state_vector: The vector of amplitudes to produce using a circuit.
/// stabilizer_state_vector: The vector of amplitudes to produce using a circuit. Does not need to be a unit vector,
/// but must be non-zero.
/// little_endian: Whether the vector is using little endian or big endian ordering.
/// inverted_circuit: If false, returns a circuit that sends |000...0> to the state vector.
/// If true, returns a circuit that sends the state vector to |000...0> instead of a cir.
Expand Down
33 changes: 32 additions & 1 deletion src/stim/util_top/circuit_vs_amplitudes.test.cc
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,12 @@
using namespace stim;

TEST(conversions, stabilizer_state_vector_to_circuit_basic) {
ASSERT_THROW(stabilizer_state_vector_to_circuit({}, false), std::invalid_argument);

ASSERT_THROW(
stabilizer_state_vector_to_circuit(
{
{0.5},
{0},
},
false),
std::invalid_argument);
Expand Down Expand Up @@ -175,6 +177,35 @@ TEST(conversions, stabilizer_state_vector_to_circuit_fuzz_round_trip) {
}
}

TEST(conversions, stabilizer_state_vector_to_circuit_unnormalized_fuzz_round_trip) {
auto rng = INDEPENDENT_TEST_RNG();
auto little_endian = true;

for (size_t i = 0; i < 100; i++) {
// Pick a random stabilizer state.
size_t n = i % 5;
TableauSimulator<64> sim(INDEPENDENT_TEST_RNG(), n);
sim.inv_state = Tableau<64>::random(n, rng);
auto desired_vec = sim.to_state_vector(little_endian);

// Unnormalize by multiplying by a random non-zero factor.
auto scaled_vec = desired_vec;
std::uniform_real_distribution<float> dist(-1000.0, +1000.0);
std::complex<float> scale = {dist(rng), dist(rng)};
while (std::norm(scale) < 0.01) {
scale = {dist(rng), dist(rng)};
}
for (auto &c : scaled_vec) {
c *= scale;
}

// Round trip through a circuit.
auto circuit = stabilizer_state_vector_to_circuit(scaled_vec, little_endian);
auto actual_vec = circuit_to_output_state_vector(circuit, little_endian);
ASSERT_EQ(actual_vec, desired_vec) << " scale=" << scale;
}
}

TEST(conversions, circuit_to_output_state_vector) {
ASSERT_EQ(circuit_to_output_state_vector(Circuit(""), false), (std::vector<std::complex<float>>{{1}}));
ASSERT_EQ(
Expand Down

0 comments on commit 34b4168

Please sign in to comment.