def from_file(cls, file_like): """Construct a DQM from a file-like object. The inverse of :meth:`~DiscreteQuadraticModel.to_file`. """ if isinstance(file_like, (bytes, bytearray, memoryview)): file_like = _BytesIO(file_like) header_info = read_header(file_like, DQM_MAGIC_PREFIX) version = header_info.version header_data = header_info.data if version >= (2, 0): raise ValueError("cannot load a DQM serialized with version " f"{version!r}, try upgrading your dimod version") obj = cls._from_file_numpy(file_like) if header_data['variables']: obj.variables = Variables() for v in VariablesSection.load(file_like): obj.variables._append(v) if len(obj.variables) != obj.num_variables(): raise ValueError("mismatched labels to BQM in given file") return obj
def from_file(cls, file_like): """Construct a DQM from a file-like object. The inverse of :meth:`~DiscreteQuadraticModel.to_file`. """ if isinstance(file_like, (bytes, bytearray, memoryview)): file_like = _BytesIO(file_like) magic = file_like.read(len(DQM_MAGIC_PREFIX)) if magic != DQM_MAGIC_PREFIX: raise ValueError("unknown file type, expected magic string {} but " "got {}".format(DQM_MAGIC_PREFIX, magic)) version = tuple(file_like.read(2)) if version[0] != 1: raise ValueError("cannot load a DQM serialized with version {!r}, " "try upgrading your dimod version" "".format(version)) header_len = int(np.frombuffer(file_like.read(4), '<u4')[0]) header_data = json.loads(file_like.read(header_len).decode('ascii')) obj = cls._from_file_numpy(file_like) if header_data['variables']: obj.variables = Variables() for v in VariablesSection.load(file_like): obj.variables._append(v) if len(obj.variables) != obj.num_variables(): raise ValueError("mismatched labels to BQM in given file") return obj
def from_file(cls, fp: Union[BinaryIO, ByteString]): """Construct a QM from a file-like object. The inverse of :meth:`~QuadraticModel.to_file`. """ if isinstance(fp, ByteString): file_like: BinaryIO = _BytesIO(fp) # type: ignore[assignment] else: file_like = fp header_info = read_header(file_like, QM_MAGIC_PREFIX) num_variables, num_interactions = header_info.data['shape'] dtype = np.dtype(header_info.data['dtype']) itype = np.dtype(header_info.data['itype']) if header_info.version > (2, 0): raise ValueError("cannot load a QM serialized with version " f"{header_info.version!r}, " "try upgrading your dimod version") obj = cls(dtype=dtype) # the vartypes obj.data._ivartypes_load(VartypesSection.load(file_like), num_variables) # offset obj.offset += OffsetSection.load(file_like, dtype=dtype) # linear obj.data.add_linear_from_array( LinearSection.load(file_like, dtype=dtype, num_variables=num_variables)) # quadratic for vi in range(num_variables): obj.data._ilower_triangle_load( vi, *NeighborhoodSection.load(file_like)) # labels (if applicable) if header_info.data['variables']: obj.relabel_variables( dict(enumerate(VariablesSection.load(file_like)))) return obj
def to_file(self, *, spool_size: int = int(1e9), ) -> tempfile.SpooledTemporaryFile: """Serialize the QM to a file-like object. Args: spool_size: Defines the `max_size` passed to the constructor of :class:`tempfile.SpooledTemporaryFile`. Determines whether the returned file-like's contents will be kept on disk or in memory. Format Specification (Version 1.0): This format is inspired by the `NPY format`_ The first 7 bytes are a magic string: exactly "DIMODQM". The next 1 byte is an unsigned byte: the major version of the file format. The next 1 byte is an unsigned byte: the minor version of the file format. The next 4 bytes form a little-endian unsigned int, the length of the header data HEADER_LEN. The next HEADER_LEN bytes form the header data. This is a json-serialized dictionary. The dictionary is exactly: .. code-block:: python data = dict(shape=qm.shape, dtype=qm.dtype.name, itype=qm.data.index_dtype.name, type=type(qm).__name__, variables=not qm.variables._is_range(), ) it is terminated by a newline character and padded with spaces to make the entire length of the entire header divisible by 64. The binary quadratic model data comes after the header. .. _NPY format: https://numpy.org/doc/stable/reference/generated/numpy.lib.format.html """ # todo: document the serialization format sections file = SpooledTemporaryFile(max_size=spool_size) data = dict(shape=self.shape, dtype=self.dtype.name, itype=self.data.index_dtype.name, type=type(self).__name__, variables=not self.variables._is_range(), ) write_header(file, QM_MAGIC_PREFIX, data, version=(1, 0)) # the vartypes file.write(VartypesSection(self).dumps()) # offset file.write(OffsetSection(self).dumps()) # linear file.write(LinearSection(self).dumps()) # quadraic neighborhood_section = NeighborhoodSection(self) for vi in range(self.num_variables): file.write(neighborhood_section.dumps(vi=vi)) # the labels (if needed) if data['variables']: file.write(VariablesSection(self.variables).dumps()) file.seek(0) return file
def to_file(self, compress=False, compressed=None, ignore_labels=False, spool_size=int(1e9)): """Convert the DQM to a file-like object. Args: compress (bool, optional default=False): If True, most of the data will be compressed. compressed (bool, optional default=None): Deprecated; please use ``compress`` instead. ignore_labels (bool, optional, default=False): Treat the DQM as unlabeled. This is useful for large DQMs to save on space. spool_size (int, optional, default=int(1e9)): Defines the `max_size` passed to the constructor of :class:`tempfile.SpooledTemporaryFile`. Determines whether the returned file-like's contents will be kept on disk or in memory. Returns: A file-like object that can be used to construct a copy of the DQM. The class is a thin wrapper of :class:`tempfile.SpooledTemporaryFile` that includes some methods from :class:`io.IOBase` Format Specification (Version 1.0): This format is inspired by the `NPY format`_ **Header** The first 8 bytes are a magic string: exactly `"DIMODDQM"`. The next 1 byte is an unsigned byte: the major version of the file format. The next 1 byte is an unsigned byte: the minor version of the file format. The next 4 bytes form a little-endian unsigned int, the length of the header data `HEADER_LEN`. The next `HEADER_LEN` bytes form the header data. This is a json-serialized dictionary. The dictionary is exactly: .. code-block:: python dict(num_variables=dqm.num_variables(), num_cases=dqm.num_cases(), num_case_interactions=dqm.num_case_interactions(), num_variable_interactions=dqm.num_variable_interactions(), variables=not (ignore_labels or dqm.variables.is_range), ) it is padded with spaces to make the entire length of the header divisible by 64. **DQM Data** The first 4 bytes are exactly `"BIAS"` The next 4 bytes form a little-endian unsigned int, the length of the DQM data `DATA_LEN`. The next `DATA_LEN` bytes are the vectors as returned by :meth:`DiscreteQuadraticModel.to_numpy_vectors` saved using :func:`numpy.save`. **Variable Data** The first 4 bytes are exactly "VARS". The next 4 bytes form a little-endian unsigned int, the length of the variables array `VARIABLES_LENGTH`. The next VARIABLES_LENGTH bytes are a json-serialized array. As constructed by `json.dumps(list(bqm.variables)). .. _NPY format: https://numpy.org/doc/stable/reference/generated/numpy.lib.format.html See Also: :meth:`DiscreteQuadraticModel.from_file` """ file = SpooledTemporaryFile(max_size=spool_size) index_labeled = ignore_labels or self.variables.is_range data = dict(num_variables=self.num_variables(), num_cases=self.num_cases(), num_case_interactions=self.num_case_interactions(), num_variable_interactions=self.num_variable_interactions(), variables=not index_labeled, ) write_header(file, DQM_MAGIC_PREFIX, data, version=(1, 1)) # the section containing most of the data, encoded with numpy if compressed is not None: warnings.warn( "Argument 'compressed' is deprecated and in future will raise " "an exception; please use 'compress' instead.", DeprecationWarning, stacklevel=2 ) compress = compressed or compress self._to_file_numpy(file, compress) if not index_labeled: file.write(VariablesSection(self.variables).dumps()) file.seek(0) return file
def to_file(self, compressed=False, ignore_labels=False, spool_size=int(1e9)): """Convert the DQM to a file-like object. Args: compressed (bool, optional default=False): If True, most of the data will be compressed. ignore_labels (bool, optional, default=False): Treat the DQM as unlabeled. This is useful for large DQMs to save on space. spool_size (int, optional, default=int(1e9)): Defines the `max_size` passed to the constructor of :class:`tempfile.SpooledTemporaryFile`. Determines whether the returned file-like's contents will be kept on disk or in memory. Returns: :class:`tempfile.SpooledTemporaryFile`: A file-like object that can be used to construct a copy of the DQM. Format Specification (Version 1.0): This format is inspired by the `NPY format`_ **Header** The first 8 bytes are a magic string: exactly `"DIMODDQM"`. The next 1 byte is an unsigned byte: the major version of the file format. The next 1 byte is an unsigned byte: the minor version of the file format. The next 4 bytes form a little-endian unsigned int, the length of the header data `HEADER_LEN`. The next `HEADER_LEN` bytes form the header data. This is a json-serialized dictionary. The dictionary is exactly: .. code-block:: python dict(num_variables=dqm.num_variables(), num_cases=dqm.num_cases(), num_case_interactions=dqm.num_case_interactions(), num_variable_interactions=dqm.num_variable_interactions(), variables=not (ignore_labels or dqm.variables.is_range), ) it is padded with spaces to make the entire length of the header divisible by 64. **DQM Data** The first 4 bytes are exactly `"BIAS"` The next 4 bytes form a little-endian unsigned int, the length of the DQM data `DATA_LEN`. The next `DATA_LEN` bytes are the vectors as returned by :meth:`DiscreteQuadraticModel.to_numpy_vectors` saved using :func:`numpy.save`. **Variable Data** The first 4 bytes are exactly "VARS". The next 4 bytes form a little-endian unsigned int, the length of the variables array `VARIABLES_LENGTH`. The next VARIABLES_LENGTH bytes are a json-serialized array. As constructed by `json.dumps(list(bqm.variables)). .. _NPY format: https://docs.scipy.org/doc/numpy/reference/generated/numpy.lib.format.html See Also: :meth:`DiscreteQuadraticModel.from_file` """ file = tempfile.SpooledTemporaryFile(max_size=spool_size) # attach the header header_parts = [ DQM_MAGIC_PREFIX, VERSION, bytes(4), # placeholder for HEADER_LEN ] index_labeled = ignore_labels or self.variables.is_range header_data = json.dumps(dict( num_variables=self.num_variables(), num_cases=self.num_cases(), num_case_interactions=self.num_case_interactions(), num_variable_interactions=self.num_variable_interactions(), variables=not index_labeled, ), sort_keys=True).encode('ascii') header_parts.append(header_data) # make the entire header length divisible by 64 length = sum(len(part) for part in header_parts) if length % 64: padding = b' ' * (64 - length % 64) else: padding = b'' header_parts.append(padding) HEADER_LEN = len(padding) + len(header_data) header_parts[2] = np.dtype('<u4').type(HEADER_LEN).tobytes() for part in header_parts: file.write(part) # the section containing most of the data, encoded with numpy self._to_file_numpy(file, compressed) if not index_labeled: file.write(VariablesSection(self.variables).dumps()) file.seek(0) return file