Source code for qci_client.utilities
- """
 - Utilities for Python client for files REST API for optimization service.
 - Copyright 2023, Quantum Computing Incorporated
 - """
 - import gzip
 - from io import BytesIO
 - import json
 - from math import floor
 - import sys
 - from typing import Generator
 - from qci_client import enum, types
 - MEMORY_MAX: int = 8 * 1000000  
 
- [docs]
 - def get_post_request_body(  
 -     *, file: dict
 - ) -> types.MetadataPostRequestBody:
 -     """
 -     Format metadata body.
 -     """
 -     file_type = enum.get_file_type(file=file)
 -     file_config = file["file_config"][file_type.value]
 -     optional_fields = {}
 -     if "file_name" in file:
 -         optional_fields["file_name"] = file["file_name"]
 -     if file_type == enum.FileType.CONSTRAINTS:
 -         return types.InputMetadataPostRequestBody(
 -             **optional_fields,
 -             file_config=types.ConstraintsMetadataConfig(
 -                 constraints=types.ConstraintsMetadata(
 -                     num_constraints=file_config["num_constraints"],
 -                     num_variables=file_config["num_variables"],
 -                 )
 -             ),
 -         )
 -     if file_type == enum.FileType.GRAPH:
 -         if "directed" in file_config:
 -             optional_fields["directed"] = file_config["directed"]
 -         if "multigraph" in file_config:
 -             optional_fields["multigraph"] = file_config["multigraph"]
 -         if "graph" in file_config:
 -             optional_fields["graph"] = file_config["graph"]
 -         return types.InputMetadataPostRequestBody(
 -             **optional_fields,
 -             file_config=types.GraphMetadataConfig(
 -                 graph=types.GraphMetadata(
 -                     **optional_fields,
 -                     num_edges=file_config["num_edges"],
 -                     num_nodes=file_config["num_nodes"],
 -                 )
 -             ),
 -         )
 -     if file_type == enum.FileType.HAMILTONIAN:
 -         return types.InputMetadataPostRequestBody(
 -             **optional_fields,
 -             file_config=types.HamiltonianMetadataConfig(
 -                 hamiltonian=types.HamiltonianMetadata(
 -                     num_variables=file_config["num_variables"],
 -                 )
 -             ),
 -         )
 -     if file_type == enum.FileType.OBJECTIVE:
 -         return types.InputMetadataPostRequestBody(
 -             **optional_fields,
 -             file_config=types.ObjectiveMetadataConfig(
 -                 objective=types.ObjectiveMetadata(
 -                     num_variables=file_config["num_variables"],
 -                 )
 -             ),
 -         )
 -     if file_type == enum.FileType.POLYNOMIAL:
 -         return types.InputMetadataPostRequestBody(
 -             **optional_fields,
 -             file_config=types.PolynomialMetadataConfig(
 -                 polynomial=types.PolynomialMetadata(
 -                     min_degree=file_config["min_degree"],
 -                     max_degree=file_config["max_degree"],
 -                     num_variables=file_config["num_variables"],
 -                 )
 -             ),
 -         )
 -     if file_type == enum.FileType.QUBO:
 -         return types.InputMetadataPostRequestBody(
 -             **optional_fields,
 -             file_config=types.QuboMetadataConfig(
 -                 qubo=types.QuboMetadata(
 -                     num_variables=file_config["num_variables"],
 -                 )
 -             ),
 -         )
 -     if file_type == enum.FileType.GP_RESULTS:
 -         return types.ResultsMetadataPostRequestBody(
 -             **optional_fields,
 -             user_id=file["user_id"],
 -             organization_id=file["organization_id"],
 -             file_config=types.GpResultsMetadataConfig(
 -                 graph_partitioning_results=types.GpResultsMetadata()
 -             ),
 -         )
 -     if file_type == enum.FileType.IHO_RESULTS:
 -         return types.ResultsMetadataPostRequestBody(
 -             **optional_fields,
 -             user_id=file["user_id"],
 -             organization_id=file["organization_id"],
 -             file_config=types.IhoResultsMetadataConfig(
 -                 ising_hamiltonian_optimization_results=types.IhoResultsMetadata()
 -             ),
 -         )
 -     if file_type == enum.FileType.NQHO_CONTINUOUS_RESULTS:
 -         return types.ResultsMetadataPostRequestBody(
 -             **optional_fields,
 -             user_id=file["user_id"],
 -             organization_id=file["organization_id"],
 -             file_config=types.NqhoContinuousResultsMetadataConfig(
 -                 normalized_qudit_hamiltonian_optimization_continuous_results=types.NqhoContinuousResultsMetadata()  
 -             ),
 -         )
 -     if file_type == enum.FileType.NQHO_INTEGER_RESULTS:
 -         return types.ResultsMetadataPostRequestBody(
 -             **optional_fields,
 -             user_id=file["user_id"],
 -             organization_id=file["organization_id"],
 -             file_config=types.NqhoIntegerResultsMetadataConfig(
 -                 normalized_qudit_hamiltonian_optimization_integer_results=types.NqhoIntegerResultsMetadata()  
 -             ),
 -         )
 -     if file_type == enum.FileType.QLCBO_RESULTS:
 -         return types.ResultsMetadataPostRequestBody(
 -             **optional_fields,
 -             user_id=file["user_id"],
 -             organization_id=file["organization_id"],
 -             file_config=types.QlcboResultsMetadataConfig(
 -                 quadratic_linearly_constrained_binary_optimization_results=types.QlcboResultsMetadata()  
 -             ),
 -         )
 -     if file_type == enum.FileType.QUBO_RESULTS:
 -         return types.ResultsMetadataPostRequestBody(
 -             **optional_fields,
 -             user_id=file["user_id"],
 -             organization_id=file["organization_id"],
 -             file_config=types.QuboResultsMetadataConfig(
 -                 quadratic_unconstrained_binary_optimization_results=types.QuboResultsMetadata()  
 -             ),
 -         )
 -     raise ValueError(f"unsupported file type: '{file_type.value}'")
 
- [docs]
 - def get_patch_request_body(  
 -     *, file: dict
 - ) -> types.PartPatchRequestBody:
 -     """Format part body."""
 -     file_type = enum.get_file_type(file=file)
 -     file_config = file["file_config"][file_type.value]
 -     if file_type == enum.FileType.CONSTRAINTS:
 -         return types.PartPatchRequestBody(
 -             file_config=types.ConstraintsPartConfig(
 -                 constraints=types.ConstraintsPart(data=file_config["data"])
 -             ),
 -         )
 -     if file_type == enum.FileType.GRAPH:
 -         return types.PartPatchRequestBody(
 -             file_config=types.GraphPartConfig(
 -                 graph=types.GraphPart(
 -                     links=file_config["links"],
 -                     nodes=file_config["nodes"],
 -                 )
 -             ),
 -         )
 -     if file_type == enum.FileType.HAMILTONIAN:
 -         return types.PartPatchRequestBody(
 -             file_config=types.HamiltonianPartConfig(
 -                 hamiltonian=types.HamiltonianPart(data=file_config["data"])
 -             ),
 -         )
 -     if file_type == enum.FileType.OBJECTIVE:
 -         return types.PartPatchRequestBody(
 -             file_config=types.ObjectivePartConfig(
 -                 objective=types.ObjectivePart(data=file_config["data"])
 -             ),
 -         )
 -     if file_type == enum.FileType.POLYNOMIAL:
 -         return types.PartPatchRequestBody(
 -             file_config=types.PolynomialPartConfig(
 -                 polynomial=types.PolynomialPart(data=file_config["data"])
 -             ),
 -         )
 -     if file_type == enum.FileType.QUBO:
 -         return types.PartPatchRequestBody(
 -             file_config=types.QuboPartConfig(
 -                 qubo=types.QuboPart(data=file_config["data"])
 -             ),
 -         )
 -     if file_type == enum.FileType.GP_RESULTS:
 -         return types.PartPatchRequestBody(
 -             file_config=types.GpResultsPartConfig(
 -                 graph_partitioning_results=types.GpResultsPart(
 -                     balances=file_config["balances"],
 -                     counts=file_config["counts"],
 -                     cut_sizes=file_config["cut_sizes"],
 -                     energies=file_config["energies"],
 -                     feasibilities=file_config["feasibilities"],
 -                     partitions=file_config["partitions"],
 -                     solutions=file_config["solutions"],
 -                 )
 -             ),
 -         )
 -     if file_type == enum.FileType.IHO_RESULTS:
 -         return types.PartPatchRequestBody(
 -             file_config=types.IhoResultsPartConfig(
 -                 ising_hamiltonian_optimization_results=types.IhoResultsPart(
 -                     counts=file_config["counts"],
 -                     energies=file_config["energies"],
 -                     solutions=file_config["solutions"],
 -                 )
 -             ),
 -         )
 -     if file_type == enum.FileType.NQHO_CONTINUOUS_RESULTS:
 -         return types.PartPatchRequestBody(
 -             file_config=types.NqhoContinuousResultsPartConfig(
 -                 normalized_qudit_hamiltonian_optimization_continuous_results=types.NqhoContinuousResultsPart(  
 -                     counts=file_config["counts"],
 -                     energies=file_config["energies"],
 -                     solutions=file_config["solutions"],
 -                 )
 -             ),
 -         )
 -     if file_type == enum.FileType.NQHO_INTEGER_RESULTS:
 -         return types.PartPatchRequestBody(
 -             file_config=types.NqhoIntegerResultsPartConfig(
 -                 normalized_qudit_hamiltonian_optimization_integer_results=types.NqhoIntegerResultsPart(  
 -                     counts=file_config["counts"],
 -                     energies=file_config["energies"],
 -                     solutions=file_config["solutions"],
 -                 )
 -             ),
 -         )
 -     if file_type == enum.FileType.QLCBO_RESULTS:
 -         return types.PartPatchRequestBody(
 -             file_config=types.QlcboResultsPartConfig(
 -                 quadratic_linearly_constrained_binary_optimization_results=types.QlcboResultsPart(  
 -                     counts=file_config["counts"],
 -                     energies=file_config["energies"],
 -                     feasibilities=file_config["feasibilities"],
 -                     objective_values=file_config["objective_values"],
 -                     solutions=file_config["solutions"],
 -                 )
 -             ),
 -         )
 -     if file_type == enum.FileType.QUBO_RESULTS:
 -         return types.PartPatchRequestBody(
 -             file_config=types.QuboResultsPartConfig(
 -                 quadratic_unconstrained_binary_optimization_results=types.QuboResultsPart(  
 -                     counts=file_config["counts"],
 -                     energies=file_config["energies"],
 -                     solutions=file_config["solutions"],
 -                 )
 -             ),
 -         )
 -     raise ValueError(f"unsupported file type: '{file_type.value}'")
 
- [docs]
 - def zip_payload(*, payload: types.PartPatchRequestBody) -> bytes:
 -     """
 -     :param payload: str - json contents of file to be zipped
 -     :return: zipped request_body
 -     """
 -     with BytesIO() as fileobj:
 -         with gzip.GzipFile(fileobj=fileobj, mode="w", compresslevel=6) as file:
 -             file.write(json.dumps(payload).encode("utf-8"))
 -         return fileobj.getvalue()
 
- [docs]
 - def file_part_generator(*, file: dict, compress: bool) -> Generator:
 -     """
 -     Break file-to-upload's data dictionary into chunks, formatting correctly with each
 -     returned chunk.
 -     :param file: file to break up into file parts
 -     :param compress: whether or not file parts are to be compressed
 -     :return: generator of (part_body, part_number) tuples
 -     """
 -     if compress:
 -         
 -         
 -         
 -         data_chunk_size_max = 20000
 -     else:
 -         
 -         
 -         
 -         
 -         data_chunk_size_max = 10000
 -     file_type = enum.get_file_type(file=file)
 -     file_config = file["file_config"][file_type.value]
 -     if file_type in enum.JOB_INPUTS_NON_GRAPH_FILE_TYPES:
 -         return _data_generator(
 -             file_type=file_type,
 -             file_config=file_config,
 -             step_length=data_chunk_size_max,
 -         )
 -     if file_type == enum.FileType.GRAPH:
 -         return _graph_generator(
 -             file_type=file_type,
 -             file_config=file_config,
 -             step_length=data_chunk_size_max,
 -         )
 -     
 -     if file_type in enum.JOB_RESULTS_FILE_TYPES:
 -         return _results_generator(
 -             file_type=file_type,
 -             file_config=file_config,
 -             step_length=_compute_results_step_len(file_config["solutions"][0]),
 -         )
 -     raise ValueError(f"unhandled file_type: {file_type.value}")
 
- def _get_size(obj, seen=None) -> int:
 -     """
 -     Recursively finds size of objects
 -     :param obj: data object to recursively compute size of
 -     :param seen: takes a set and is used in the recursive step only to record whether an
 -         object has been counted yet.
 -     :return int:
 -     """
 -     size = sys.getsizeof(obj)
 -     if seen is None:
 -         seen = set()
 -     obj_id = id(obj)
 -     if obj_id in seen:
 -         return 0
 -     
 -     
 -     seen.add(obj_id)
 -     if isinstance(obj, dict):
 -         size += sum(_get_size(v, seen) for v in obj.values())
 -         size += sum(_get_size(k, seen) for k in obj.keys())
 -     elif hasattr(obj, "__dict__"):
 -         size += _get_size(obj.__dict__, seen)
 -     elif hasattr(obj, "__iter__") and not isinstance(obj, (str, bytes, bytearray)):
 -         size += sum(_get_size(i, seen) for i in obj)
 -     return size
 - def _get_soln_size(soln):
 -     
 -     
 -     if isinstance(soln[0], dict):
 -         return _get_size(soln)
 -     return sys.getsizeof(soln[0]) * len(soln)
 - def _compute_results_step_len(data: list) -> int:
 -     """
 -     Compute the step length for "chunking" the provided data.
 -     Args:
 -         data: A list of data
 -     Returns:
 -         The step length for "chunking" the data
 -     """
 -     
 -     soln_mem = _get_soln_size(data)
 -     
 -     chunk_ratio = MEMORY_MAX / soln_mem
 -     step_len = max(floor(chunk_ratio), 1)
 -     return step_len
 - def _data_generator(
 -     *, file_type: enum.FileType, file_config: dict, step_length: int
 - ) -> Generator:
 -     
 -     for part_number, i in enumerate(
 -         range(0, max(1, len(file_config["data"])), step_length)
 -     ):
 -         chunk = {
 -             "file_config": {
 -                 file_type.value: {
 -                     "data": file_config["data"][i : i + step_length],
 -                 }
 -             }
 -         }
 -         yield chunk, part_number + 1  
 - def _graph_generator(
 -     *, file_type: enum.FileType, file_config: dict, step_length: int
 - ) -> Generator:
 -     
 -     for part_number, i in enumerate(
 -         range(
 -             0,
 -             max(1, len(file_config["links"]), len(file_config["nodes"])),
 -             step_length,
 -         )
 -     ):
 -         chunk = {
 -             "file_config": {
 -                 file_type.value: {
 -                     "links": file_config["links"][i : i + step_length],
 -                     "nodes": file_config["nodes"][i : i + step_length],
 -                 }
 -             }
 -         }
 -         yield chunk, part_number + 1  
 - def _results_generator(
 -     *, file_type: enum.FileType, file_config: dict, step_length: int
 - ) -> Generator:
 -     for part_number, i in enumerate(
 -         range(0, max(1, len(file_config["solutions"])), step_length)
 -     ):
 -         chunk = {"file_config": {file_type.value: {}}}
 -         for key, value in file_config.items():
 -             chunk["file_config"][file_type.value][key] = value[i : i + step_length]
 -         yield chunk, part_number + 1