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, *, 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, *, spool_size: int = int(1e9)) -> tempfile.SpooledTemporaryFile: """Serialize 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.1): This format is inspired by the `NPY format`_ The first 8 bytes are a magic string: exactly "DIMODCQM". 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=len(cqm.variables), num_constraints=len(cqm.constraints), num_biases=cqm.num_biases(), num_quadratic_variables=cqm.num_quadratic_variables(), ) it is terminated by a newline character and padded with spaces to make the entire length of the entire header divisible by 64. The constraint quadratic model data comes after the header. It is encoded as a zip file. The zip file will contain one file named `objective`, containing the objective as encoded as a file view. It will also contain a directory called `constraints`. The `constraints` directory will contain one subdirectory for each constraint, each containing `lhs`, `rhs` and `sense` encoding the `lhs` as a fileview, the `rhs` as a float and the sense as a string. Each directory will also contain a `discrete` file, encoding whether the constraint represents a discrete variable. Format Specification (Version 1.0): This format is the same as Version 1.1, except that the data dict does not have `num_quadratic_variables`. .. _NPY format: https://numpy.org/doc/stable/reference/generated/numpy.lib.format.html """ file = SpooledTemporaryFile(max_size=spool_size) data = dict( num_variables=len(self.variables), num_constraints=len(self.constraints), num_biases=self.num_biases(), num_quadratic_variables=self.num_quadratic_variables(), ) write_header(file, CQM_MAGIC_PREFIX, data, version=(1, 1)) # write the values with zipfile.ZipFile(file, mode='a') as zf: try: zf.writestr( 'objective', self.objective.to_file( spool_size=int(1e12))._file.getbuffer()) except AttributeError: # no objective to write pass for label, constraint in self.constraints.items(): # put everything in a constraints/label/ directory lstr = json.dumps(serialize_variable(label)) lhs = constraint.lhs.to_file( spool_size=int(1e12))._file.getbuffer() zf.writestr(f'constraints/{lstr}/lhs', lhs) rhs = np.float64(constraint.rhs).tobytes() zf.writestr(f'constraints/{lstr}/rhs', rhs) sense = bytes(constraint.sense.value, 'ascii') zf.writestr(f'constraints/{lstr}/sense', sense) discrete = bytes((label in self.discrete, )) zf.writestr(f'constraints/{lstr}/discrete', discrete) file.seek(0) return file