From 6922db891a97ac6cc24a52826c6ae25b22106b65 Mon Sep 17 00:00:00 2001 From: Phillip Weinberg Date: Thu, 27 Feb 2025 23:04:07 -0500 Subject: [PATCH 1/7] adding json object migration script --- docs/home/migration.md | 25 +++++++++++ src/bloqade/analog/migrate.py | 79 +++++++++++++++++++++++++++++++++++ tests/test_migrate.py | 23 ++++++++++ 3 files changed, 127 insertions(+) create mode 100644 src/bloqade/analog/migrate.py create mode 100644 tests/test_migrate.py diff --git a/docs/home/migration.md b/docs/home/migration.md index 9fafa6f9a..71ba8fe16 100644 --- a/docs/home/migration.md +++ b/docs/home/migration.md @@ -33,6 +33,31 @@ from bloqade.analog.atom_arrangement import Square ... ``` +## Migrating old bloqade json files + +If you have old bloqade json files, you will not be able to directly deserialize them anymore because of the package restructuring. Howver we have provided some tools to migrate those JSON files to be compatible with `bloqade-analog`. You can do this by running the following command in the command line for a single file: + +```sh +python -m bloqade.analog.migrate +``` +This will create a new file with the same name as the old file, but with `_analog` appended to the end of the filename. For example, if you have a file called `my_bloqade.json`, the new file will be called `my_bloqade_analog.json`. You can then use `load` to deserialize this file with the `bloqade-analog` package. + +Another option is to use the migration tool in a python script: + +```python +from bloqade.analog.migrate import migrate + + # set the indent level for the output file +indent: int = ... +# set to True if you want to overwrite the old file, otherwise the new file will be created with -analog appended to the end of the filename +overwrite: bool = ... +f +or filename in ["file1.json", "file2.json", ...]: + migrate(filename, indent=indent, overwrite=overwrite) +``` +This will migrate all the files in the list to the new format. + + ## Having trouble, comments, or concerns? Please open an issue on our [GitHub](https://github.com/QuEraComputing/bloqade-analog/issues) diff --git a/src/bloqade/analog/migrate.py b/src/bloqade/analog/migrate.py new file mode 100644 index 000000000..0d605b4af --- /dev/null +++ b/src/bloqade/analog/migrate.py @@ -0,0 +1,79 @@ +from typing import Any, Dict, List +from dataclasses import field, dataclass + +import simplejson as json + + +@dataclass +class JSONWalker: + has_done_something: bool = field(init=False, default=False) + + def walk_dict(self, obj: Dict[str, Any]) -> Dict[str, Any]: + new_obj = {} + for key, value in obj.items(): + + if key.startswith("bloqade.analog."): + new_obj[key] = self.walk(value) + elif key.startswith("bloqade."): + new_obj[key.replace("bloqade.", "bloqade.analog.")] = self.walk(value) + self.has_done_something = True + else: + new_obj[key] = self.walk(value) + + return new_obj + + def walk(self, obj: Dict[str, Any] | List[Any]) -> Dict[str, Any] | List[Any]: + if isinstance(obj, dict): + return self.walk_dict(obj) + elif isinstance(obj, list): + return [self.walk(item) for item in obj] + else: + return obj + + def convert(self, obj: Dict[str, Any] | List[Any]): + self.has_done_something = False + new_obj = self.walk(obj) + return new_obj, self.has_done_something + + +def migrate( + filename: str, + indent: int | None = None, + overwrite: bool = False, +): + + with open(filename, "r") as io: + obj = json.load(io) + walker = JSONWalker() + new_obj, has_done_something = walker.convert(obj) + + if has_done_something: + new_filename = ( + filename if overwrite else filename.replace(".json", "-analog.json") + ) + with open(new_filename, "w") as io: + json.dump(new_obj, io, indent=indent) + + +def _entry(): + import argparse + + parser = argparse.ArgumentParser() + + parser.add_argument("filename", type=str) + parser.add_argument("--indent", type=int, default=None) + parser.add_argument( + "--overwrite", + action="store_true", + help="Overwrite the original file", + default=False, + ) + + args = parser.parse_args() + print(args.overwrite) + migrate(args.filename, args.indent, args.overwrite) + print(f"Converted {args.filename}") + + +if __name__ == "__main__": + _entry() diff --git a/tests/test_migrate.py b/tests/test_migrate.py new file mode 100644 index 000000000..4de6e1a9a --- /dev/null +++ b/tests/test_migrate.py @@ -0,0 +1,23 @@ +from bloqade.analog.migrate import JSONWalker + + +def test_walk_dict(): + walker = JSONWalker() + obj = { + "key1": "value1", + "bloqade.key2": "value2", + "bloqade.analog.key3": "value3", + "nested": {"key4": "bloqade.value4", "bloqade.key5": "value5"}, + "list": [{"key6": "value6"}, {"bloqade.key7": "value7"}], + } + + expected = { + "key1": "value1", + "bloqade.analog.key2": "value2", + "bloqade.analog.key3": "value3", + "nested": {"key4": "bloqade.value4", "bloqade.analog.key5": "value5"}, + "list": [{"key6": "value6"}, {"bloqade.analog.key7": "value7"}], + } + + result = walker.walk_dict(obj) + assert result == expected From 91d992c1ab6a40e4ca54f52310b607415b883085 Mon Sep 17 00:00:00 2001 From: Phillip Weinberg Date: Thu, 27 Feb 2025 23:15:56 -0500 Subject: [PATCH 2/7] support multiple files in migration script --- docs/home/migration.md | 13 +++++++++++-- src/bloqade/analog/migrate.py | 9 +++++---- 2 files changed, 16 insertions(+), 6 deletions(-) diff --git a/docs/home/migration.md b/docs/home/migration.md index 71ba8fe16..de4b85e6c 100644 --- a/docs/home/migration.md +++ b/docs/home/migration.md @@ -35,12 +35,21 @@ from bloqade.analog.atom_arrangement import Square ## Migrating old bloqade json files -If you have old bloqade json files, you will not be able to directly deserialize them anymore because of the package restructuring. Howver we have provided some tools to migrate those JSON files to be compatible with `bloqade-analog`. You can do this by running the following command in the command line for a single file: +If you have old bloqade JSON files, you will not be able to directly deserialize them anymore because of the package restructuring. Howver we have provided some tools to migrate those JSON files to be compatible with `bloqade-analog`. You can do this by running the following command in the command line for a single file: ```sh python -m bloqade.analog.migrate ``` -This will create a new file with the same name as the old file, but with `_analog` appended to the end of the filename. For example, if you have a file called `my_bloqade.json`, the new file will be called `my_bloqade_analog.json`. You can then use `load` to deserialize this file with the `bloqade-analog` package. +This will create a new file with the same name as the old file, but with `_analog` appended to the end of the filename. For example, if you have a file called `my_bloqade.json`, the new file will be called `my_bloqade_analog.json`. You can then use `load` to deserialize this file with the `bloqade-analog` package. There are other options for converting the file, such as setting the indent level for the output file or overwriting the old file. You can see all the options by running: + +```sh +python -m bloqade.analog.migrate --help +``` +You can also migrate multiple files at once by running: + +```sh +python -m bloqade.analog.migrate ... +``` Another option is to use the migration tool in a python script: diff --git a/src/bloqade/analog/migrate.py b/src/bloqade/analog/migrate.py index 0d605b4af..727f195a0 100644 --- a/src/bloqade/analog/migrate.py +++ b/src/bloqade/analog/migrate.py @@ -58,9 +58,11 @@ def migrate( def _entry(): import argparse + import tqdm + parser = argparse.ArgumentParser() - parser.add_argument("filename", type=str) + parser.add_argument("filenames", type=str, nargs="*") parser.add_argument("--indent", type=int, default=None) parser.add_argument( "--overwrite", @@ -70,9 +72,8 @@ def _entry(): ) args = parser.parse_args() - print(args.overwrite) - migrate(args.filename, args.indent, args.overwrite) - print(f"Converted {args.filename}") + for filename in tqdm.tqdm(args.filenames): + migrate(filename, args.indent, args.overwrite) if __name__ == "__main__": From f0881156f4bb424cc9c60e79d89672720de31046 Mon Sep 17 00:00:00 2001 From: Phillip Weinberg Date: Thu, 27 Feb 2025 23:16:58 -0500 Subject: [PATCH 3/7] fixing typo --- docs/home/migration.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/home/migration.md b/docs/home/migration.md index de4b85e6c..53ebd90fe 100644 --- a/docs/home/migration.md +++ b/docs/home/migration.md @@ -40,7 +40,7 @@ If you have old bloqade JSON files, you will not be able to directly deserialize ```sh python -m bloqade.analog.migrate ``` -This will create a new file with the same name as the old file, but with `_analog` appended to the end of the filename. For example, if you have a file called `my_bloqade.json`, the new file will be called `my_bloqade_analog.json`. You can then use `load` to deserialize this file with the `bloqade-analog` package. There are other options for converting the file, such as setting the indent level for the output file or overwriting the old file. You can see all the options by running: +This will create a new file with the same name as the old file, but with `_analog` appended to the end of the filename. For example, if you have a file called `my_bloqade.json`, the new file will be called `my_bloqade-analog.json`. You can then use `load` to deserialize this file with the `bloqade-analog` package. There are other options for converting the file, such as setting the indent level for the output file or overwriting the old file. You can see all the options by running: ```sh python -m bloqade.analog.migrate --help From 500899060f5469200d4ec87e2778332a7e682f85 Mon Sep 17 00:00:00 2001 From: Phillip Weinberg Date: Thu, 27 Feb 2025 23:17:21 -0500 Subject: [PATCH 4/7] fixing typo --- docs/home/migration.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/home/migration.md b/docs/home/migration.md index 53ebd90fe..67b2b556d 100644 --- a/docs/home/migration.md +++ b/docs/home/migration.md @@ -33,7 +33,7 @@ from bloqade.analog.atom_arrangement import Square ... ``` -## Migrating old bloqade json files +## Migrating old bloqade JSON files If you have old bloqade JSON files, you will not be able to directly deserialize them anymore because of the package restructuring. Howver we have provided some tools to migrate those JSON files to be compatible with `bloqade-analog`. You can do this by running the following command in the command line for a single file: From d25ab944868ebeb145a9238c77aecd649ddb97ad Mon Sep 17 00:00:00 2001 From: Phillip Weinberg Date: Thu, 27 Feb 2025 23:18:10 -0500 Subject: [PATCH 5/7] fixing typo --- docs/home/migration.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/home/migration.md b/docs/home/migration.md index 67b2b556d..f62cc7b75 100644 --- a/docs/home/migration.md +++ b/docs/home/migration.md @@ -40,7 +40,7 @@ If you have old bloqade JSON files, you will not be able to directly deserialize ```sh python -m bloqade.analog.migrate ``` -This will create a new file with the same name as the old file, but with `_analog` appended to the end of the filename. For example, if you have a file called `my_bloqade.json`, the new file will be called `my_bloqade-analog.json`. You can then use `load` to deserialize this file with the `bloqade-analog` package. There are other options for converting the file, such as setting the indent level for the output file or overwriting the old file. You can see all the options by running: +This will create a new file with the same name as the old file, but with `-analog` appended to the end of the filename. For example, if you have a file called `my_bloqade.json`, the new file will be called `my_bloqade-analog.json`. You can then use `load` to deserialize this file with the `bloqade-analog` package. There are other options for converting the file, such as setting the indent level for the output file or overwriting the old file. You can see all the options by running: ```sh python -m bloqade.analog.migrate --help From b01b51e9c4dac0c6f35397e0dc2c739d0cb2a91d Mon Sep 17 00:00:00 2001 From: Phillip Weinberg Date: Fri, 28 Feb 2025 12:48:43 -0500 Subject: [PATCH 6/7] refactor migration --- src/bloqade/analog/migrate.py | 27 +++++++++++++-------------- tests/test_migrate.py | 19 +++++++++++++++---- 2 files changed, 28 insertions(+), 18 deletions(-) diff --git a/src/bloqade/analog/migrate.py b/src/bloqade/analog/migrate.py index 727f195a0..beaf88919 100644 --- a/src/bloqade/analog/migrate.py +++ b/src/bloqade/analog/migrate.py @@ -1,4 +1,4 @@ -from typing import Any, Dict, List +from typing import IO, Any, Dict, List from dataclasses import field, dataclass import simplejson as json @@ -26,7 +26,7 @@ def walk(self, obj: Dict[str, Any] | List[Any]) -> Dict[str, Any] | List[Any]: if isinstance(obj, dict): return self.walk_dict(obj) elif isinstance(obj, list): - return [self.walk(item) for item in obj] + return list(map(self.walk, obj)) else: return obj @@ -36,23 +36,22 @@ def convert(self, obj: Dict[str, Any] | List[Any]): return new_obj, self.has_done_something +def _migrate(input_oi: IO[str], output_oi: IO[str], indent: int | None = None): + obj = json.load(input_oi) + new_obj, has_done_something = JSONWalker().convert(obj) + + if has_done_something: + json.dump(new_obj, output_oi, indent=indent) + + def migrate( filename: str, indent: int | None = None, overwrite: bool = False, ): - - with open(filename, "r") as io: - obj = json.load(io) - walker = JSONWalker() - new_obj, has_done_something = walker.convert(obj) - - if has_done_something: - new_filename = ( - filename if overwrite else filename.replace(".json", "-analog.json") - ) - with open(new_filename, "w") as io: - json.dump(new_obj, io, indent=indent) + new_filename = filename if overwrite else filename.replace(".json", "-analog.json") + with open(filename, "r") as in_io, open(new_filename, "w") as out_io: + _migrate(in_io, out_io, indent) def _entry(): diff --git a/tests/test_migrate.py b/tests/test_migrate.py index 4de6e1a9a..d710aabc1 100644 --- a/tests/test_migrate.py +++ b/tests/test_migrate.py @@ -1,8 +1,12 @@ -from bloqade.analog.migrate import JSONWalker +import io + +import simplejson as json + +from bloqade.analog.migrate import _migrate def test_walk_dict(): - walker = JSONWalker() + obj = { "key1": "value1", "bloqade.key2": "value2", @@ -19,5 +23,12 @@ def test_walk_dict(): "list": [{"key6": "value6"}, {"bloqade.analog.key7": "value7"}], } - result = walker.walk_dict(obj) - assert result == expected + obj_str = json.dumps(obj) + expected_str = json.dumps(expected) + + in_io = io.StringIO(obj_str) + out_io = io.StringIO() + + _migrate(in_io, out_io) + + assert out_io.getvalue() == expected_str From f8df09fda80a0ba0bd685e521ccf5001bdbe9f7c Mon Sep 17 00:00:00 2001 From: Phillip Weinberg Date: Fri, 28 Feb 2025 12:59:15 -0500 Subject: [PATCH 7/7] simplify migration page --- docs/home/migration.md | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/docs/home/migration.md b/docs/home/migration.md index f62cc7b75..8c76cfd91 100644 --- a/docs/home/migration.md +++ b/docs/home/migration.md @@ -35,21 +35,16 @@ from bloqade.analog.atom_arrangement import Square ## Migrating old bloqade JSON files -If you have old bloqade JSON files, you will not be able to directly deserialize them anymore because of the package restructuring. Howver we have provided some tools to migrate those JSON files to be compatible with `bloqade-analog`. You can do this by running the following command in the command line for a single file: +If you have old bloqade JSON files, you will not be able to directly deserialize them anymore because of the package restructuring. However, we have provided some tools to migrate those JSON files to be compatible with `bloqade-analog`. You can do this by running the following command in the command line for a one or more files: ```sh -python -m bloqade.analog.migrate +python -m bloqade.analog.migrate ... ``` -This will create a new file with the same name as the old file, but with `-analog` appended to the end of the filename. For example, if you have a file called `my_bloqade.json`, the new file will be called `my_bloqade-analog.json`. You can then use `load` to deserialize this file with the `bloqade-analog` package. There are other options for converting the file, such as setting the indent level for the output file or overwriting the old file. You can see all the options by running: +With default arguments this will create a new file with the same name as the old file, but with `-analog` appended to the end of the filename. For example, if you have a file called `my_bloqade.json`, the new file will be called `my_bloqade-analog.json`. You can then use `load` to deserialize this file with the `bloqade-analog` package. There are other options for converting the file, such as setting the indent level for the output file or overwriting the old file. You can see all the options by running: ```sh python -m bloqade.analog.migrate --help ``` -You can also migrate multiple files at once by running: - -```sh -python -m bloqade.analog.migrate ... -``` Another option is to use the migration tool in a python script: