Repository: asciineuron/bgls (language: python)

Issue:
Generalize how to compute amplitudes and the signature of `sample`
1. The signature of `sample` is currently

https://github.com/asciineuron/bgls/blob/5566a10b4ab47d1a687f0ca190551f1e8486bb0e/bgls/module/bgls_sampler.py#L6-L10

but should be

```python
def sample(
    circuit: cirq.Circuit,
    initial_state: T = cirq.sim.state_vector_simulation_state.StateVectorSimulationState,
    act_on: Callable[[T, cirq.GateOperation], T] = protcols.act_on,
    compute_amplitude: Callable[[T, Bitstring], complex] = ...,
) -> Bitstring:
```

where the default arguments aren't too important for now (assume state vector and state vector simulator). There eventually will be some other args like `seed` which you already have, but these four args should be there and should come first.

2. The following lines to compute amplitudes from the state

https://github.com/asciineuron/bgls/blob/5566a10b4ab47d1a687f0ca190551f1e8486bb0e/bgls/module/bgls_sampler.py#L46-L53

should just be

```python
candidate_amplitudes = [compute_amplitude(state, bitstring) for bitstring in candidates]
```

This of course assumes the state has been updated by `state = act_on(state, gate)` as you iterate over gates in the circuit.


Discussion / hints:

Fix patch:
diff --git a/.flake8 b/.flake8
index 942e9ee..d3f3d01 100644
--- a/.flake8
+++ b/.flake8
@@ -1,4 +1,3 @@
 [flake8]
-max-line-length = 79
-ignore = E203, E231, W503, F723, F821
-exclude = __init__.py, build
+ignore = E203, E231, E501, W503, F723, F821
+exclude = __init__.py, bgls_sampler_test.py, build
\ No newline at end of file
diff --git a/.gitignore b/.gitignore
index c59a0d5..48e2c5f 100644
--- a/.gitignore
+++ b/.gitignore
@@ -15,4 +15,5 @@ build/
 coverage.xml
 jupyter_execute/
 testenv/
-venv/
\ No newline at end of file
+venv/
+.mypy_cache
\ No newline at end of file
diff --git a/bgls/__init__.py b/bgls/__init__.py
index 1e6e7bd..0a103e7 100644
--- a/bgls/__init__.py
+++ b/bgls/__init__.py
@@ -10,6 +10,11 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
-"""Whatever this package is for."""
+"""The Bgls sampler is a Cirq-based implementation of Bravyi, Gosset, and Liu's
+algorithm for simulating quantum measurement without computing marginals."""
 
-from bgls.module import Object
+from .bgls_sampler import Simulator
+from .bgls_utils import *
+
+__version__ = "0.1.0dev"
+__author__ = "Alex Shapiro"
diff --git a/bgls/bgls_sampler.py b/bgls/bgls_sampler.py
new file mode 100644
index 0000000..0593c2d
--- /dev/null
+++ b/bgls/bgls_sampler.py
@@ -0,0 +1,138 @@
+import itertools
+import cirq
+import numpy as np
+from typing import TypeVar, Callable, Any, Dict, List
+
+
+class Simulator:
+    State = TypeVar("State")
+
+    def __init__(
+        self,
+        initial_state: State,
+        compute_probability: Callable[[State, str], float],
+        apply_gate: Callable[
+            [cirq.Operation, State], Any
+        ] = cirq.protocols.act_on,  # type: ignore[assignment]
+    ):
+        self.initial_state = initial_state
+        self.compute_probability = compute_probability
+        self.apply_gate = apply_gate
+
+    def sample(
+        self,
+        circuit: cirq.Circuit,
+        param_resolver: "cirq.ParamResolverOrSimilarType" = None,
+        repetitions: int = 1,
+        seed: "cirq.RANDOM_STATE_OR_SEED_LIKE" = None,
+    ) -> cirq.Result:
+        rng = cirq.value.parse_random_state(seed)
+
+        resolved_circuit = cirq.resolve_parameters(circuit, param_resolver)
+        cirq.sim.simulator.check_all_resolved(resolved_circuit)
+
+        if not circuit.are_any_measurements_terminal():
+            raise ValueError("No terminal measurements to sample.")
+
+        records: Dict[str, np.ndarray] = {}
+
+        for rep in range(repetitions):
+            keys_to_bitstrs = self.sample_core(
+                resolved_circuit,
+                rng=rng,
+            )
+            for meas_key in keys_to_bitstrs:
+                if rep == 0 and meas_key not in records:
+                    records[meas_key] = np.zeros(
+                        (repetitions, 1, len(keys_to_bitstrs[meas_key][-1]))
+                    )
+                records[meas_key][rep, 0, :] = [
+                    int(bit) for bit in keys_to_bitstrs[meas_key][-1]
+                ]
+
+        return cirq.study.ResultDict(records=records)
+
+    def sample_core(
+        self,
+        circuit: cirq.Circuit,
+        rng: np.random.RandomState = cirq.value.parse_random_state(None),
+        return_history: bool = False,
+    ) -> Dict[str, List[str]]:
+        keys_to_indices: Dict[str, List[int]] = {}
+        keys_to_bitstrings: Dict[str, List[str]] = {}
+
+        qubits = circuit.all_qubits()
+        qubit_index = {q: i for i, q in enumerate(sorted(qubits))}
+        bitstring = "0" * len(qubits)
+        bitstrings = [bitstring]
+
+        state = self.initial_state.copy()
+        for i, op in enumerate(circuit.all_operations()):
+            if cirq.protocols.is_measurement(op):
+                # check if terminal:
+                if circuit.next_moment_operating_on(op.qubits, i + 1) is None:
+                    meas_key = cirq.protocols.measurement_key_name(op.gate)
+                    if meas_key not in keys_to_indices:
+                        meas_indices = [qubit_index[q] for q in op.qubits]
+                        keys_to_indices[meas_key] = meas_indices
+                continue
+
+            self.apply_gate(op, state)
+
+            # Determine the candidate bitstrings to sample:
+            op_support = {qubit_index[q] for q in op.qubits}
+            candidates = list(
+                itertools.product(
+                    *[
+                        ["0", "1"] if i in op_support else [b]
+                        for i, b in enumerate(bitstring)
+                    ]
+                )
+            )
+            joined_cands = ["".join(cand) for cand in candidates]
+
+            # Compute probability of each candidate bitstring:
+            candidate_probs = np.asarray(
+                [
+                    self.compute_probability(state, candidate)
+                    for candidate in joined_cands
+                ]
+            )
+
+            # sample to get bitstring
+            bitstring = "".join(
+                candidates[
+                    rng.choice(
+                        range(len(candidates)),
+                        p=candidate_probs / sum(candidate_probs),
+                    )
+                ]
+            )
+            bitstrings.append(bitstring)
+
+        # return dict of list of bitstrings measured per gate
+        # (optionally over time)
+        for meas in keys_to_indices:
+            if not return_history:
+                keys_to_bitstrings[meas] = [
+                    "".join(
+                        [
+                            bit
+                            for i, bit in enumerate(bitstring)
+                            if i in keys_to_indices[meas]
+                        ]
+                    )
+                ]
+            else:
+                keys_to_bitstrings[meas] = [
+                    "".join(
+                        [
+                            bit
+                            for i, bit in enumerate(bitstr)
+                            if i in keys_to_indices[meas]
+                        ]
+                    )
+                    for bitstr in bitstrings
+                ]
+
+        return keys_to_bitstrings
diff --git a/bgls/bgls_utils.py b/bgls/bgls_utils.py
new file mode 100644
index 0000000..acbb318
--- /dev/null
+++ b/bgls/bgls_utils.py
@@ -0,0 +1,40 @@
+# helper functions to operate on cirq structures
+
+import cirq
+import numpy as np
+from typing import TypeVar
+
+State = TypeVar("State")
+
+
+def state_vector_bitstring_probability(
+    state_vector: cirq.sim.state_vector_simulation_state.StateVectorSimulationState,
+    bitstring: str,
+) -> float:
+    """
+    For a cirq StateVectorSimulationState,
+    returns the probability corresponding to a given bitstring
+    """
+    return (
+        np.abs(
+            cirq.to_valid_state_vector(state_vector.target_tensor)[
+                int(bitstring, 2)
+            ]
+        )
+        ** 2
+    )
+
+
+def density_matrix_bitstring_probability(
+    state: cirq.sim.DensityMatrixSimulationState, bitstring: str
+) -> float:
+    """
+    For a cirq DensityMatrixSimulationState,
+    returns the probability corresponding to a given bitstring
+    """
+    num_qubits = len(bitstring)
+    index = int(bitstring, 2)
+    density_matrix = cirq.to_valid_density_matrix(
+        state.target_tensor, num_qubits
+    )
+    return np.abs(density_matrix[index, index])
diff --git a/bgls/module/__init__.py b/bgls/module/__init__.py
deleted file mode 100644
index b3b3203..0000000
--- a/bgls/module/__init__.py
+++ /dev/null
@@ -1,15 +0,0 @@
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-#     https://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# 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.
-
-"""Whatever this module is for."""
-
-from bgls.module.file import Object
diff --git a/bgls/module/bgls_simulator.py b/bgls/module/bgls_simulator.py
deleted file mode 100644
index 68f9a83..0000000
--- a/bgls/module/bgls_simulator.py
+++ /dev/null
@@ -1,99 +0,0 @@
-import functools
-import itertools
-from builtins import set
-from typing import Dict, FrozenSet
-
-import cirq
-import numpy as np
-
-
-class BglsSimulator(cirq.sim.SimulatesSamples):
-    """General description here.
-
-    Details here.
-    """
-
-    def __init__(self, seed: 'cirq.RANDOM_STATE_OR_SEED_LIKE' = None):
-        self._seed = seed
-
-    def _run(
-            self,
-            circuit: 'cirq.AbstractCircuit',
-            param_resolver: 'cirq.ParamResolver',
-            repetitions: int,
-    ) -> Dict[str, np.ndarray]:
-        """See definition in `cirq.SimulatesSamples`."""
-        param_resolver = param_resolver or cirq.ParamResolver(
-            {})  # in case empty
-        resolved_circuit = cirq.resolve_parameters(circuit, param_resolver)
-        cirq.sim.simulator.check_all_resolved(resolved_circuit)
-
-        return self._ryan_sample_final_results(resolved_circuit, repetitions)
-
-    def _ryan_apply(
-            self,
-            operation: cirq.Operation,
-            state: np.ndarray,
-            qubits: FrozenSet[cirq.Qid],
-    ) -> np.ndarray:
-        unitary = cirq.unitary(
-            cirq.Moment(operation,
-                        [cirq.I.on(q) for q in qubits - set(operation.qubits)])
-        )
-        return unitary @ state
-
-    def _ryan_sample_final_results(
-            self,
-            resolved_circuit: cirq.AbstractCircuit,
-            repetitions: int
-    ) -> Dict[str, np.ndarray]:
-        rng = np.random.RandomState(self._seed)
-        records: Dict[str, np.ndarray] = {}
-
-        qubits = resolved_circuit.all_qubits()
-        qubit_index = {q: i for i, q in enumerate(sorted(qubits))}
-        bitstring = "0" * len(qubits)
-
-        for repetition in range(repetitions):
-            state = functools.reduce(  # TODO: Make this an input.
-                lambda a, b: a * b, [cirq.KET_ZERO(q) for q in sorted(qubits)]
-            ).state_vector()
-
-            for op in resolved_circuit.all_operations():
-                # record and quit when we hit the measurement gate:
-                if isinstance(op.gate, cirq.MeasurementGate):
-                    print(op)
-                    meas_key = op.gate.key
-                    if meas_key not in records:
-                        # TODO assume here we only have 1 appearance of
-                        #  key... so won't work with general circuit
-                        records[meas_key] = np.zeros(
-                            (repetitions, 1, len(qubits)))
-                    # TODO make this fit: np.fromstring(bitstring):
-                    records[meas_key][repetition, 0, :] = 0.1
-                    break
-
-                # Determine the candidate bitstrings to sample.
-                op_support = {qubit_index[q] for q in op.qubits}
-                candidates = list(itertools.product(
-                    *[["0",
-                       "1"] if i in op_support  # Update bits on operation
-                      # support.
-                      else [b]  # Fix bits not on operation support.
-                      for i, b in enumerate(bitstring)]
-                ))
-
-                # Compute probability of each candidate bitstring.
-                state = self._ryan_apply(op, state,
-                                         qubits)  # TODO: Make this an input.
-                probs = [abs(state[int("".join(bits), 2)]) ** 2 for bits in
-                         candidates]
-
-                # Sample from the candidate bitstrings and update the
-                # bitstring.
-                bitstring = "".join(
-                    candidates[rng.choice(range(len(candidates)),
-                                          p=probs / sum(probs))]
-                )
-
-        return records
diff --git a/bgls/module/file.py b/bgls/module/file.py
deleted file mode 100644
index d4319cb..0000000
--- a/bgls/module/file.py
+++ /dev/null
@@ -1,32 +0,0 @@
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-#     https://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# 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.
-
-"""Whatever this file is for."""
-
-from dataclasses import dataclass
-from typing import Any, Sequence
-
-import numpy as np
-from numpy.typing import ArrayLike
-
-
-@dataclass(frozen=True)
-class Object:
-    items: Sequence[Any]
-
-    @property
-    def num_items(self) -> int:
-        return len(self.items)
-
-    @property
-    def asarray(self) -> ArrayLike:
-        return np.array(self.items)
diff --git a/bgls/sample_usage.py b/bgls/sample_usage.py
index 738107a..81f275d 100644
--- a/bgls/sample_usage.py
+++ b/bgls/sample_usage.py
@@ -1,26 +1,43 @@
-# this will give demonstrations on how our sampler is to be used
-# as well as providing clear development targets to satisfy
-
 import cirq
-from module import bgls_simulator
+import bgls
+import matplotlib.pyplot as plt
 
-# create GHZ circuit
+# create GHZ circuit with measurement
 q0, q1, q2 = cirq.LineQubit.range(3)
 circuit = cirq.Circuit(
     cirq.H(q0),
     cirq.CNOT(q0, q1),
-    cirq.CNOT(q1, q2),
-    # works equally well since all we need is a single measurement of all
-    # qubits:
-    cirq.measure([q0, q1, q2], key='result')
+    cirq.CNOT(q0, q2),
+    cirq.measure([q0, q1, q2], key="result"),
 )
 
-# how to sample measurements with cirq  default:
+# how to sample measurements with cirq default:
 cirq_simulator = cirq.Simulator()
-cirq_results = cirq_simulator.run(circuit, repetitions=3)
+cirq_results = cirq_simulator.run(circuit, repetitions=100)
 print(cirq_results)
+_ = cirq.plot_state_histogram(cirq_results, plt.subplot())
+plt.show()
+
+# initialize state for bgls:
+init_state = cirq.StateVectorSimulationState(
+    qubits=(q0, q1, q2), initial_state=0
+)
 
-# how to sample with our sampler:
-bgls_simulator = bgls_simulator.BglsSimulator()
-bgls_results = bgls_simulator.run(circuit, repetitions=2)
+# how to sample measurements with bgls
+bgls_simulator = bgls.Simulator(
+    init_state,
+    bgls.state_vector_bitstring_probability,
+    cirq.protocols.act_on,
+)
+
+bgls_results = bgls_simulator.sample(circuit, repetitions=100)
 print(bgls_results)
+_ = cirq.plot_state_histogram(bgls_results, plt.subplot())
+plt.show()
+
+# for a single sampling, can get raw bitstring:
+bitstring = bgls_simulator.sample_core(
+    circuit,
+    rng=cirq.value.parse_random_state(None),
+)
+print(bitstring)
diff --git a/mypy.ini b/mypy.ini
index 84a06f1..4629653 100644
--- a/mypy.ini
+++ b/mypy.ini
@@ -11,3 +11,4 @@ disallow_untyped_defs = True
 no_implicit_optional = True
 warn_redundant_casts = True
 warn_unused_configs = True
+disable_error_code = attr-defined
diff --git a/requirements.txt b/requirements.txt
index 2840d77..37d25fb 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -1,4 +1,2 @@
 cirq~=1.0.0
 numpy~=1.20
-
-cirq~=1.0.0
\ No newline at end of file
diff --git a/setup.py b/setup.py
index ab9281e..0450f1c 100644
--- a/setup.py
+++ b/setup.py
@@ -13,7 +13,6 @@
 
 from setuptools import setup, find_packages
 
-
 with open("VERSION.txt", "r") as f:
     __version__ = f.read().strip()
 
@@ -36,7 +35,7 @@
     version=__version__,
     install_requires=requirements,
     extras_require={"development": set(dev_requirements)},
-    packages=find_packages(),
+    packages=['bgls'],
     include_package_data=True,
     description="Implementation of the gate-by-gate sampling algorithm.",
     long_description=open("README.md", encoding="utf-8").read(),


Test patch:
diff --git a/bgls/bgls_sampler_test.py b/bgls/bgls_sampler_test.py
new file mode 100644
index 0000000..ddba900
--- /dev/null
+++ b/bgls/bgls_sampler_test.py
@@ -0,0 +1,188 @@
+import pytest
+import numpy as np
+import cirq
+import bgls
+
+"""
+Test suite for proper behavior with seeds, measurements, and state types.
+"""
+
+
+def test_seed_continuity():
+    # Running our sample function with the same seed should produce
+    # identical measurements
+    q0, q1, q2 = cirq.LineQubit.range(3)
+    ghz = cirq.Circuit(
+        cirq.H(q0),
+        cirq.CNOT(q0, q1),
+        cirq.CNOT(q0, q2),
+        cirq.measure([q0, q2], key="result1st"),
+    )
+    init_state = cirq.StateVectorSimulationState(
+        qubits=(q0, q1, q2), initial_state=0
+    )
+
+    bgls_simulator = bgls.Simulator(
+        init_state,
+        bgls.state_vector_bitstring_probability,
+        cirq.protocols.act_on,
+    )
+    bgls_result_1 = bgls_simulator.sample(
+        ghz,
+        repetitions=100,
+        seed=1,
+    )
+    bgls_result_2 = bgls_simulator.sample(
+        ghz,
+        repetitions=100,
+        seed=1,
+    )
+    assert bgls_result_1 == bgls_result_2
+
+
+def test_intermediate_measurements():
+    # Intermediate measurement gates do not affect simulation
+    q0, q1, q2 = cirq.LineQubit.range(3)
+    ghz = cirq.Circuit(
+        cirq.H(q0),
+        cirq.CNOT(q0, q1),
+        cirq.CNOT(q1, q2),
+        cirq.measure([q0, q1, q2], key="result"),
+    )
+    ghz_intermediate = cirq.Circuit(
+        cirq.H(q0),
+        cirq.measure([q0, q2], key="result"),
+        cirq.CNOT(q0, q1),
+        cirq.measure([q0, q1, q2], key="result"),
+        cirq.CNOT(q1, q2),
+        cirq.measure([q0, q1, q2], key="result"),
+    )
+    init_state = cirq.StateVectorSimulationState(
+        qubits=(q0, q1, q2), initial_state=0
+    )
+    bgls_simulator = bgls.Simulator(
+        init_state,
+        bgls.state_vector_bitstring_probability,
+        cirq.protocols.act_on,
+    )
+    bgls_result = bgls_simulator.sample(ghz, repetitions=100, seed=1)
+    bgls_result_intermediate = bgls_simulator.sample(
+        ghz_intermediate, repetitions=100, seed=1
+    )
+    assert bgls_result == bgls_result_intermediate
+
+
+def test_multiple_measurements():
+    # Distributing final measurement across multiple gates has same behavior
+    # as a single measurement of all qubits
+    q0, q1, q2 = cirq.LineQubit.range(3)
+    ghz = cirq.Circuit(
+        cirq.H(q0),
+        cirq.CNOT(q0, q1),
+        cirq.CNOT(q0, q2),
+        cirq.measure([q0, q1, q2], key="result"),
+    )
+    ghz_multiple = cirq.Circuit(
+        cirq.H(q0),
+        cirq.CNOT(q0, q1),
+        cirq.CNOT(q0, q2),
+        cirq.measure([q0, q2], key="result1st"),
+        cirq.measure([q1], key="result2nd"),
+    )
+    init_state = cirq.StateVectorSimulationState(
+        qubits=(q0, q1, q2), initial_state=0
+    )
+    bgls_simulator = bgls.Simulator(
+        init_state,
+        bgls.state_vector_bitstring_probability,
+        cirq.protocols.act_on,
+    )
+    bgls_result = bgls_simulator.sample(ghz, repetitions=100, seed=12)
+    bgls_result_multiple = bgls_simulator.sample(
+        ghz_multiple, repetitions=100, seed=12
+    )
+    # converting to histogram for checking recovers original values since
+    # spread across multiple measurement keys
+    np.testing.assert_array_equal(
+        cirq.vis.get_state_histogram(bgls_result),
+        cirq.vis.get_state_histogram(bgls_result_multiple),
+    )
+
+
+def test_no_measurements():
+    # Sampling a circuit without final measurement raises a ValueError
+    q0, q1, q2 = cirq.LineQubit.range(3)
+    ghz = cirq.Circuit(cirq.H(q0), cirq.CNOT(q0, q1), cirq.CNOT(q0, q2))
+    init_state = cirq.StateVectorSimulationState(
+        qubits=(q0, q1, q2), initial_state=0
+    )
+    bgls_simulator = bgls.Simulator(
+        init_state,
+        bgls.state_vector_bitstring_probability,
+        cirq.protocols.act_on,
+    )
+    with pytest.raises(ValueError) as error_info:
+        bgls_result = bgls_simulator.sample(
+            ghz,
+            repetitions=100,
+        )
+
+
+def test_partial_measurements():
+    # Measuring only some final qubits yields the same distribution,
+    # but with bitstring int rep matching the subset of measured bits
+    q0, q1, q2 = cirq.LineQubit.range(3)
+    ghz = cirq.Circuit(
+        cirq.H(q0),
+        cirq.CNOT(q0, q1),
+        cirq.CNOT(q0, q2),
+        cirq.measure([q0, q2], key="result"),
+    )
+    init_state = cirq.StateVectorSimulationState(
+        qubits=(q0, q1, q2), initial_state=0
+    )
+    bgls_simulator = bgls.Simulator(
+        init_state,
+        bgls.state_vector_bitstring_probability,
+        cirq.protocols.act_on,
+    )
+    bgls_result = bgls_simulator.sample(
+        ghz,
+        repetitions=100,
+    )
+    # here because ghz, still only get low and high-end values with equal
+    # prob, but now 0 and 3 (ie 00 11) rather than 0 and 7 (000 111)
+    # can assert these are the only measured values
+    assert set(bgls_result.histogram(key="result")).issubset({0, 3})
+
+
+def test_density_state():
+    # Density matrix simulation yields the same result as state vector
+    q0, q1, q2 = cirq.LineQubit.range(3)
+    ghz = cirq.Circuit(
+        cirq.H(q0),
+        cirq.CNOT(q0, q1),
+        cirq.CNOT(q0, q2),
+        cirq.measure([q0, q1, q2], key="result"),
+    )
+    statevector_state = cirq.StateVectorSimulationState(
+        qubits=(q0, q1, q2), initial_state=0
+    )
+    statevector_simulator = bgls.Simulator(
+        statevector_state,
+        bgls.state_vector_bitstring_probability,
+        cirq.protocols.act_on,
+    )
+    statevector_result = statevector_simulator.sample(
+        ghz, repetitions=100, seed=3
+    )
+    density_state = cirq.DensityMatrixSimulationState(
+        qubits=(q0, q1, q2), initial_state=0
+    )
+    density_simulator = bgls.Simulator(
+        density_state,
+        bgls.state_vector_bitstring_probability,
+        cirq.protocols.act_on,
+    )
+    density_result = density_simulator.sample(ghz, repetitions=100, seed=3)
+    assert statevector_result == density_result
diff --git a/bgls/module/file_test.py b/bgls/module/file_test.py
deleted file mode 100644
index 7421ac6..0000000
--- a/bgls/module/file_test.py
+++ /dev/null
@@ -1,25 +0,0 @@
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-#     https://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# 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.
-
-"""Unit tests for file.py."""
-
-import pytest
-
-import numpy as np
-from bgls.module.file import Object
-
-
-@pytest.mark.parametrize("items", [[1, 2, 10], [np.pi, np.e]])
-def test_object(items):
-    obj = Object(items)
-    assert obj.num_items == len(items)
-    assert np.allclose(obj.asarray, np.array(items))