We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
Report "Rays Per Sec" in easier to read format.
It would be great to report the "Rays Per Sec" to fewer sig figs. Ie "1343535.4324" -> "1.34 mil" or "1.34 M". It is becoming trickier to quickly read these numbers now that the model is getting quicker.

Also maybe "Rays Per Sec" -> "Rays / Sec"

--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `scripts/run_eval.py`
Content:
```
1 """
2 run_eval.py
3 """
4 import enum
5 import os
6 from typing import Dict, List, Optional
7 
8 import dcargs
9 import mediapy as media
10 import torch
11 from hydra import compose, initialize
12 from omegaconf import DictConfig
13 from tqdm import tqdm
14 
15 from pyrad.cameras.camera_paths import CameraPath, get_interpolated_camera_path, get_spiral_path
16 from pyrad.data.dataloader import EvalDataloader, setup_dataset_eval, setup_dataset_train
17 from pyrad.graphs.base import Graph, setup_graph
18 from pyrad.utils.writer import TimeWriter
19 
20 
21 def _update_avg(prev_avg: float, new_val: float, step: int) -> float:
22     """helper to calculate the running average
23 
24     Args:
25         prev_avg (float): previous average value
26         new_val (float): new value to update the average with
27         step (int): current step number
28 
29     Returns:
30         float: new updated average
31     """
32     return (step * prev_avg + new_val) / (step + 1)
33 
34 
35 def _load_checkpoint(config: DictConfig, graph: Graph) -> None:
36     """Helper function to load checkpointed graph
37 
38     Args:
39         config (DictConfig): Configuration of graph to load
40         graph (Graph): Graph instance of which to load weights
41     """
42     assert config.load_dir is not None
43     if config.load_step is None:
44         print("Loading latest checkpoint from load_dir")
45         # NOTE: this is specific to the checkpoint name format
46         load_step = sorted([int(x[x.find("-") + 1 : x.find(".")]) for x in os.listdir(config.load_dir)])[-1]
47     else:
48         load_step = config.load_step
49     load_path = os.path.join(config.load_dir, f"step-{load_step:09d}.ckpt")
50     assert os.path.exists(load_path), f"Checkpoint {load_path} does not exist"
51     loaded_state = torch.load(load_path, map_location="cpu")
52     graph.load_graph(loaded_state)
53     print(f"done loading checkpoint from {load_path}")
54 
55 
56 def run_inference_from_config(config: DictConfig) -> Dict[str, float]:
57     """helper function to run inference given config specifications (also used in benchmarking)
58 
59     Args:
60         config (DictConfig): Configuration for loading the evaluation.
61 
62     Returns:
63         Tuple[float, float]: returns both the avg psnr and avg rays per second
64     """
65     print("Running inference.")
66     device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
67     # setup graph and dataset
68     dataset_inputs_train, _ = setup_dataset_train(config.data, device=device)
69     _, dataloader_eval = setup_dataset_eval(config.data, test_mode=True, device=device)
70     graph = setup_graph(config.graph, dataset_inputs_train, device=device)
71     graph.eval()
72 
73     # load checkpointed information
74     _load_checkpoint(config.trainer.resume_train, graph)
75 
76     # calculate average psnr across test dataset
77     return render_stats_dict(graph, dataloader_eval)
78 
79 
80 def render_stats_dict(graph: Graph, dataloader_eval: EvalDataloader) -> Dict[str, float]:
81     """Helper function to evaluate the graph on a dataloader.
82 
83     Args:
84         graph (Graph): Graph to evaluate
85         dataloader_eval (EvalDataloader): Dataloader to evaluate on
86 
87     Returns:
88         dict: returns the average psnr and average rays per second
89     """
90     avg_psnr = 0
91     avg_rays_per_sec = 0
92     avg_fps = 0
93     for step, (camera_ray_bundle, batch) in tqdm(enumerate(dataloader_eval)):
94         with TimeWriter(writer=None, name=None, write=False) as t:
95             with torch.no_grad():
96                 image_idx = int(camera_ray_bundle.camera_indices[0, 0])
97                 outputs = graph.get_outputs_for_camera_ray_bundle(camera_ray_bundle)
98                 psnr = graph.log_test_image_outputs(image_idx, step, batch, outputs)
99         avg_rays_per_sec = _update_avg(avg_rays_per_sec, camera_ray_bundle.origins.shape[0] / t.duration, step)
100         avg_psnr = _update_avg(avg_psnr, psnr, step)
101         avg_fps = _update_avg(avg_fps, 1 / t.duration, step)
102     return {"avg psnr": avg_psnr, "avg rays per sec": avg_rays_per_sec, "avg fps": avg_fps}
103 
104 
105 def render_trajectory_video(
106     graph: Graph,
107     camera_path: CameraPath,
108     output_filename: Optional[str] = None,
109     rendered_output_name: Optional[str] = None,
110     rendered_resolution_scaling_factor: float = 1.0,
111     num_rays_per_chunk: int = 4096,
112 ) -> None:
113     """Helper function to create a video of the spiral trajectory.
114 
115     Args:
116         graph: Graph to evaluate with.
117         camera_path: Index of the image to render.
118         output_filename: Name of the output file.
119         rendered_output_name: Name of the renderer output to use.
120         rendered_resolution_scaling_factor: Scaling factor to apply to the camera image resolution.
121             Defaults to 1.0.
122         num_rays_per_chunk: Number of rays to use per chunk. Defaults to 4096.
123     """
124     print("Creating trajectory video.")
125     images = []
126     for camera in tqdm(camera_path.cameras):
127         camera.rescale_output_resolution(rendered_resolution_scaling_factor)
128         camera_ray_bundle = camera.get_camera_ray_bundle().to(graph.device)
129         camera_ray_bundle.num_rays_per_chunk = num_rays_per_chunk
130         with torch.no_grad():
131             outputs = graph.get_outputs_for_camera_ray_bundle(camera_ray_bundle)
132         # TODO: don't hardcode the key! this will break for some nerf Graphs
133         image = outputs[rendered_output_name].cpu().numpy()
134         images.append(image)
135 
136     seconds = 5.0
137     fps = len(images) / seconds
138     media.write_video(output_filename, images, fps=fps)
139 
140 
141 class MethodType(enum.Enum):
142     """Enum for the method type."""
143 
144     PSNR = enum.auto()
145     TRAJ = enum.auto()
146 
147 
148 class TrajectoryType(enum.Enum):
149     """Enum for the trajectory type."""
150 
151     SPIRAL = enum.auto()
152     INTERP = enum.auto()
153 
154 
155 def main(
156     config_name: str,
157     checkpoint_dir: str,
158     rendered_output_name: str,
159     method: MethodType = MethodType.PSNR,
160     traj: TrajectoryType = TrajectoryType.SPIRAL,
161     output_filename: str = "output.mp4",
162     rendered_resolution_scaling_factor: float = 1.0,
163     config_overrides: Optional[List[str]] = None,
164 ):
165     """Evaluate trained model. This evaluation can either render a trajectory or compute the eval psnr.
166 
167     Args:
168         config_name: Name of the config file to use.
169         checkpoint_dir: Directory to load the checkpoint from.
170         rendered_output_name: Name of the renderer output to use.
171         method: Method to use for evaluation. PSNR computes metrics, TRAJ renders a trajectory.
172         traj: Trajectory to render.
173         output_filename: Name of the output file.
174         rendered_resolution_scaling_factor: Scaling factor to apply to the camera image resolution.
175             Defaults to 1.0.
176         config_overrides: List of strings to override config values.
177     """
178     # parser = my_func_that_returns_a_parser()
179     # args = parser.parse_args()
180 
181     config_path = "../configs"
182     initialize(version_base="1.2", config_path=config_path)
183     config_overrides = config_overrides or []
184     config = compose(config_name, overrides=config_overrides)
185 
186     config.trainer.resume_train.load_dir = checkpoint_dir
187 
188     device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
189     # setup graph and dataset
190     dataset_inputs_train, _ = setup_dataset_train(config.data, device=device)
191     _, dataloader_eval = setup_dataset_eval(config.data, test_mode=True, device=device)
192     graph = setup_graph(config.graph, dataset_inputs_train, device=device)
193     graph.eval()
194     # load checkpointed information
195     _load_checkpoint(config.trainer.resume_train, graph)
196 
197     if method == MethodType.PSNR:
198         stats_dict = render_stats_dict(graph, dataloader_eval)
199         avg_psnr = stats_dict["avg psnr"]
200         avg_rays_per_sec = stats_dict["avg rays per sec"]
201         avg_fps = stats_dict["avg fps"]
202         print(f"Avg. PSNR: {avg_psnr:0.4f}")
203         print(f"Avg. Rays per sec: {avg_rays_per_sec:0.4f}")
204         print(f"Avg. FPS: {avg_fps:0.4f}")
205     elif method == MethodType.TRAJ:
206         # TODO(ethan): pass in camera information into argparse parser
207         if traj == TrajectoryType.SPIRAL:
208             camera_start = dataloader_eval.get_camera(image_idx=0)
209             # TODO(ethan): pass in the up direction of the camera
210             camera_path = get_spiral_path(camera_start, steps=30, radius=0.1)
211         elif traj == TrajectoryType.INTERP:
212             camera_start = dataloader_eval.get_camera(image_idx=0)
213             camera_end = dataloader_eval.get_camera(image_idx=10)
214             camera_path = get_interpolated_camera_path(camera_start, camera_end, steps=30)
215         render_trajectory_video(
216             graph,
217             camera_path,
218             output_filename=output_filename,
219             rendered_output_name=rendered_output_name,
220             rendered_resolution_scaling_factor=rendered_resolution_scaling_factor,
221             num_rays_per_chunk=config.data.dataloader_eval.num_rays_per_chunk,
222         )
223 
224 
225 if __name__ == "__main__":
226     dcargs.cli(main)
227
```

Path: `pyrad/utils/misc.py`
Content:
```
1 # Copyright 2022 The Plenoptix Team. All rights reserved.
2 #
3 # Licensed under the Apache License, Version 2.0 (the "License");
4 # you may not use this file except in compliance with the License.
5 # You may obtain a copy of the License at
6 #
7 #     http://www.apache.org/licenses/LICENSE-2.0
8 #
9 # Unless required by applicable law or agreed to in writing, software
10 # distributed under the License is distributed on an "AS IS" BASIS,
11 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 # See the License for the specific language governing permissions and
13 # limitations under the License.
14 
15 """
16 Miscellaneous helper code.
17 """
18 
19 import hashlib
20 import json
21 from pydoc import locate
22 from typing import Any, Dict
23 
24 import torch
25 from omegaconf import DictConfig
26 
27 
28 class DotDict(dict):
29     """
30     dot.notation access to dictionary attributes
31     """
32 
33     def __getattr__(self, attr):
34         return self[attr]
35 
36     __setattr__ = dict.__setitem__
37     __delattr__ = dict.__delitem__
38 
39 
40 def get_dict_to_torch(stuff, device="cpu"):
41     """Set everything in the dict to the specified torch device."""
42     if isinstance(stuff, dict):
43         for k, v in stuff.items():
44             stuff[k] = get_dict_to_torch(v, device)
45         return stuff
46     if isinstance(stuff, torch.Tensor):
47         return stuff.to(device)
48     return stuff
49 
50 
51 def get_dict_to_cpu(stuff):
52     """Set everything in the dict to CPU."""
53     if isinstance(stuff, dict):
54         for k, v in stuff.items():
55             stuff[k] = get_dict_to_cpu(v)
56         return stuff
57     if isinstance(stuff, torch.Tensor):
58         return stuff.detach().cpu()
59     return stuff
60 
61 
62 def is_not_none(var):
63     """Return True if the variable var is None."""
64     return not isinstance(var, type(None))
65 
66 
67 def get_masked_dict(d, mask):
68     """Return a masked dictionary.
69     TODO(ethan): add more asserts/checks so this doesn't have unpredictable behavior."""
70     masked_dict = {}
71     for key, value in d.items():
72         masked_dict[key] = value[mask]
73     return masked_dict
74 
75 
76 def instantiate_from_dict_config(dict_config: DictConfig, **kwargs):
77     """Our version of hydra's instantiate function."""
78     dict_config_kwargs = {k: v for k, v in dict_config.items() if k != "_target_"}
79     uninstantiated_class = locate(dict_config._target_)  # pylint: disable=protected-access
80     all_kwargs = dict_config_kwargs
81     all_kwargs.update(kwargs)
82     instantiated_class = uninstantiated_class(**all_kwargs)
83     return instantiated_class
84 
85 
86 def get_hash_str_from_dict(dictionary: Dict[str, Any]) -> str:
87     """MD5 hash of a dictionary. Based on
88     https://www.doc.ic.ac.uk/~nuric/coding/how-to-hash-a-dictionary-in-python.html"""
89     dhash = hashlib.md5()
90     encoded = json.dumps(dictionary, sort_keys=True).encode()
91     dhash.update(encoded)
92     return dhash.hexdigest()
93
```
--- END FILES ---

Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.

Here is an example:

```diff
diff --git a/examples/server_async.py b/examples/server_async.py
--- a/examples/server_async.py
+++ b/examples/server_async.py
@@ -313,4 +313,4 @@
 
 
 if __name__ == "__main__":
-    asyncio.run(run_async_server("."), debug=True)
+    asyncio.run(run_async_server(), debug=True)
diff --git a/examples/server_sync.py b/examples/server_sync.py
--- a/examples/server_sync.py
+++ b/examples/server_sync.py
@@ -313,5 +313,5 @@
 
 
 if __name__ == "__main__":
-    server = run_sync_server(".")
+    server = run_sync_server()
     server.shutdown()

```

diff --git a/pyrad/utils/misc.py b/pyrad/utils/misc.py
--- a/pyrad/utils/misc.py
+++ b/pyrad/utils/misc.py
@@ -21,6 +21,7 @@
 from pydoc import locate
 from typing import Any, Dict
 
+from math import floor, log
 import torch
 from omegaconf import DictConfig
 
@@ -90,3 +91,11 @@
     encoded = json.dumps(dictionary, sort_keys=True).encode()
     dhash.update(encoded)
     return dhash.hexdigest()
+
+
+def human_format(num):
+    """Format a number in a more human readable way"""
+    units = ["", "K", "M", "B", "T", "P"]
+    k = 1000.0
+    magnitude = int(floor(log(num, k)))
+    return f"{(num / k**magnitude):.2f} {units[magnitude]}"
diff --git a/scripts/run_eval.py b/scripts/run_eval.py
--- a/scripts/run_eval.py
+++ b/scripts/run_eval.py
@@ -15,6 +15,7 @@
 from pyrad.cameras.camera_paths import CameraPath, get_interpolated_camera_path, get_spiral_path
 from pyrad.data.dataloader import EvalDataloader, setup_dataset_eval, setup_dataset_train
 from pyrad.graphs.base import Graph, setup_graph
+from pyrad.utils.misc import human_format
 from pyrad.utils.writer import TimeWriter
 
 
@@ -200,7 +201,7 @@
         avg_rays_per_sec = stats_dict["avg rays per sec"]
         avg_fps = stats_dict["avg fps"]
         print(f"Avg. PSNR: {avg_psnr:0.4f}")
-        print(f"Avg. Rays per sec: {avg_rays_per_sec:0.4f}")
+        print(f"Avg. Rays / Sec: {human_format(avg_rays_per_sec)}")
         print(f"Avg. FPS: {avg_fps:0.4f}")
     elif method == MethodType.TRAJ:
         # TODO(ethan): pass in camera information into argparse parser

{"golden_diff": "diff --git a/pyrad/utils/misc.py b/pyrad/utils/misc.py\n--- a/pyrad/utils/misc.py\n+++ b/pyrad/utils/misc.py\n@@ -21,6 +21,7 @@\n from pydoc import locate\n from typing import Any, Dict\n \n+from math import floor, log\n import torch\n from omegaconf import DictConfig\n \n@@ -90,3 +91,11 @@\n     encoded = json.dumps(dictionary, sort_keys=True).encode()\n     dhash.update(encoded)\n     return dhash.hexdigest()\n+\n+\n+def human_format(num):\n+    \"\"\"Format a number in a more human readable way\"\"\"\n+    units = [\"\", \"K\", \"M\", \"B\", \"T\", \"P\"]\n+    k = 1000.0\n+    magnitude = int(floor(log(num, k)))\n+    return f\"{(num / k**magnitude):.2f} {units[magnitude]}\"\ndiff --git a/scripts/run_eval.py b/scripts/run_eval.py\n--- a/scripts/run_eval.py\n+++ b/scripts/run_eval.py\n@@ -15,6 +15,7 @@\n from pyrad.cameras.camera_paths import CameraPath, get_interpolated_camera_path, get_spiral_path\n from pyrad.data.dataloader import EvalDataloader, setup_dataset_eval, setup_dataset_train\n from pyrad.graphs.base import Graph, setup_graph\n+from pyrad.utils.misc import human_format\n from pyrad.utils.writer import TimeWriter\n \n \n@@ -200,7 +201,7 @@\n         avg_rays_per_sec = stats_dict[\"avg rays per sec\"]\n         avg_fps = stats_dict[\"avg fps\"]\n         print(f\"Avg. PSNR: {avg_psnr:0.4f}\")\n-        print(f\"Avg. Rays per sec: {avg_rays_per_sec:0.4f}\")\n+        print(f\"Avg. Rays / Sec: {human_format(avg_rays_per_sec)}\")\n         print(f\"Avg. FPS: {avg_fps:0.4f}\")\n     elif method == MethodType.TRAJ:\n         # TODO(ethan): pass in camera information into argparse parser\n", "issue": "Report \"Rays Per Sec\" in easier to read format.\nIt would be great to report the \"Rays Per Sec\" to fewer sig figs. Ie \"1343535.4324\" -> \"1.34 mil\" or \"1.34 M\". It is becoming trickier to quickly read these numbers now that the model is getting quicker.\r\n\r\nAlso maybe \"Rays Per Sec\" -> \"Rays / Sec\"\n", "before_files": [{"content": "\"\"\"\nrun_eval.py\n\"\"\"\nimport enum\nimport os\nfrom typing import Dict, List, Optional\n\nimport dcargs\nimport mediapy as media\nimport torch\nfrom hydra import compose, initialize\nfrom omegaconf import DictConfig\nfrom tqdm import tqdm\n\nfrom pyrad.cameras.camera_paths import CameraPath, get_interpolated_camera_path, get_spiral_path\nfrom pyrad.data.dataloader import EvalDataloader, setup_dataset_eval, setup_dataset_train\nfrom pyrad.graphs.base import Graph, setup_graph\nfrom pyrad.utils.writer import TimeWriter\n\n\ndef _update_avg(prev_avg: float, new_val: float, step: int) -> float:\n    \"\"\"helper to calculate the running average\n\n    Args:\n        prev_avg (float): previous average value\n        new_val (float): new value to update the average with\n        step (int): current step number\n\n    Returns:\n        float: new updated average\n    \"\"\"\n    return (step * prev_avg + new_val) / (step + 1)\n\n\ndef _load_checkpoint(config: DictConfig, graph: Graph) -> None:\n    \"\"\"Helper function to load checkpointed graph\n\n    Args:\n        config (DictConfig): Configuration of graph to load\n        graph (Graph): Graph instance of which to load weights\n    \"\"\"\n    assert config.load_dir is not None\n    if config.load_step is None:\n        print(\"Loading latest checkpoint from load_dir\")\n        # NOTE: this is specific to the checkpoint name format\n        load_step = sorted([int(x[x.find(\"-\") + 1 : x.find(\".\")]) for x in os.listdir(config.load_dir)])[-1]\n    else:\n        load_step = config.load_step\n    load_path = os.path.join(config.load_dir, f\"step-{load_step:09d}.ckpt\")\n    assert os.path.exists(load_path), f\"Checkpoint {load_path} does not exist\"\n    loaded_state = torch.load(load_path, map_location=\"cpu\")\n    graph.load_graph(loaded_state)\n    print(f\"done loading checkpoint from {load_path}\")\n\n\ndef run_inference_from_config(config: DictConfig) -> Dict[str, float]:\n    \"\"\"helper function to run inference given config specifications (also used in benchmarking)\n\n    Args:\n        config (DictConfig): Configuration for loading the evaluation.\n\n    Returns:\n        Tuple[float, float]: returns both the avg psnr and avg rays per second\n    \"\"\"\n    print(\"Running inference.\")\n    device = torch.device(\"cuda\" if torch.cuda.is_available() else \"cpu\")\n    # setup graph and dataset\n    dataset_inputs_train, _ = setup_dataset_train(config.data, device=device)\n    _, dataloader_eval = setup_dataset_eval(config.data, test_mode=True, device=device)\n    graph = setup_graph(config.graph, dataset_inputs_train, device=device)\n    graph.eval()\n\n    # load checkpointed information\n    _load_checkpoint(config.trainer.resume_train, graph)\n\n    # calculate average psnr across test dataset\n    return render_stats_dict(graph, dataloader_eval)\n\n\ndef render_stats_dict(graph: Graph, dataloader_eval: EvalDataloader) -> Dict[str, float]:\n    \"\"\"Helper function to evaluate the graph on a dataloader.\n\n    Args:\n        graph (Graph): Graph to evaluate\n        dataloader_eval (EvalDataloader): Dataloader to evaluate on\n\n    Returns:\n        dict: returns the average psnr and average rays per second\n    \"\"\"\n    avg_psnr = 0\n    avg_rays_per_sec = 0\n    avg_fps = 0\n    for step, (camera_ray_bundle, batch) in tqdm(enumerate(dataloader_eval)):\n        with TimeWriter(writer=None, name=None, write=False) as t:\n            with torch.no_grad():\n                image_idx = int(camera_ray_bundle.camera_indices[0, 0])\n                outputs = graph.get_outputs_for_camera_ray_bundle(camera_ray_bundle)\n                psnr = graph.log_test_image_outputs(image_idx, step, batch, outputs)\n        avg_rays_per_sec = _update_avg(avg_rays_per_sec, camera_ray_bundle.origins.shape[0] / t.duration, step)\n        avg_psnr = _update_avg(avg_psnr, psnr, step)\n        avg_fps = _update_avg(avg_fps, 1 / t.duration, step)\n    return {\"avg psnr\": avg_psnr, \"avg rays per sec\": avg_rays_per_sec, \"avg fps\": avg_fps}\n\n\ndef render_trajectory_video(\n    graph: Graph,\n    camera_path: CameraPath,\n    output_filename: Optional[str] = None,\n    rendered_output_name: Optional[str] = None,\n    rendered_resolution_scaling_factor: float = 1.0,\n    num_rays_per_chunk: int = 4096,\n) -> None:\n    \"\"\"Helper function to create a video of the spiral trajectory.\n\n    Args:\n        graph: Graph to evaluate with.\n        camera_path: Index of the image to render.\n        output_filename: Name of the output file.\n        rendered_output_name: Name of the renderer output to use.\n        rendered_resolution_scaling_factor: Scaling factor to apply to the camera image resolution.\n            Defaults to 1.0.\n        num_rays_per_chunk: Number of rays to use per chunk. Defaults to 4096.\n    \"\"\"\n    print(\"Creating trajectory video.\")\n    images = []\n    for camera in tqdm(camera_path.cameras):\n        camera.rescale_output_resolution(rendered_resolution_scaling_factor)\n        camera_ray_bundle = camera.get_camera_ray_bundle().to(graph.device)\n        camera_ray_bundle.num_rays_per_chunk = num_rays_per_chunk\n        with torch.no_grad():\n            outputs = graph.get_outputs_for_camera_ray_bundle(camera_ray_bundle)\n        # TODO: don't hardcode the key! this will break for some nerf Graphs\n        image = outputs[rendered_output_name].cpu().numpy()\n        images.append(image)\n\n    seconds = 5.0\n    fps = len(images) / seconds\n    media.write_video(output_filename, images, fps=fps)\n\n\nclass MethodType(enum.Enum):\n    \"\"\"Enum for the method type.\"\"\"\n\n    PSNR = enum.auto()\n    TRAJ = enum.auto()\n\n\nclass TrajectoryType(enum.Enum):\n    \"\"\"Enum for the trajectory type.\"\"\"\n\n    SPIRAL = enum.auto()\n    INTERP = enum.auto()\n\n\ndef main(\n    config_name: str,\n    checkpoint_dir: str,\n    rendered_output_name: str,\n    method: MethodType = MethodType.PSNR,\n    traj: TrajectoryType = TrajectoryType.SPIRAL,\n    output_filename: str = \"output.mp4\",\n    rendered_resolution_scaling_factor: float = 1.0,\n    config_overrides: Optional[List[str]] = None,\n):\n    \"\"\"Evaluate trained model. This evaluation can either render a trajectory or compute the eval psnr.\n\n    Args:\n        config_name: Name of the config file to use.\n        checkpoint_dir: Directory to load the checkpoint from.\n        rendered_output_name: Name of the renderer output to use.\n        method: Method to use for evaluation. PSNR computes metrics, TRAJ renders a trajectory.\n        traj: Trajectory to render.\n        output_filename: Name of the output file.\n        rendered_resolution_scaling_factor: Scaling factor to apply to the camera image resolution.\n            Defaults to 1.0.\n        config_overrides: List of strings to override config values.\n    \"\"\"\n    # parser = my_func_that_returns_a_parser()\n    # args = parser.parse_args()\n\n    config_path = \"../configs\"\n    initialize(version_base=\"1.2\", config_path=config_path)\n    config_overrides = config_overrides or []\n    config = compose(config_name, overrides=config_overrides)\n\n    config.trainer.resume_train.load_dir = checkpoint_dir\n\n    device = torch.device(\"cuda\" if torch.cuda.is_available() else \"cpu\")\n    # setup graph and dataset\n    dataset_inputs_train, _ = setup_dataset_train(config.data, device=device)\n    _, dataloader_eval = setup_dataset_eval(config.data, test_mode=True, device=device)\n    graph = setup_graph(config.graph, dataset_inputs_train, device=device)\n    graph.eval()\n    # load checkpointed information\n    _load_checkpoint(config.trainer.resume_train, graph)\n\n    if method == MethodType.PSNR:\n        stats_dict = render_stats_dict(graph, dataloader_eval)\n        avg_psnr = stats_dict[\"avg psnr\"]\n        avg_rays_per_sec = stats_dict[\"avg rays per sec\"]\n        avg_fps = stats_dict[\"avg fps\"]\n        print(f\"Avg. PSNR: {avg_psnr:0.4f}\")\n        print(f\"Avg. Rays per sec: {avg_rays_per_sec:0.4f}\")\n        print(f\"Avg. FPS: {avg_fps:0.4f}\")\n    elif method == MethodType.TRAJ:\n        # TODO(ethan): pass in camera information into argparse parser\n        if traj == TrajectoryType.SPIRAL:\n            camera_start = dataloader_eval.get_camera(image_idx=0)\n            # TODO(ethan): pass in the up direction of the camera\n            camera_path = get_spiral_path(camera_start, steps=30, radius=0.1)\n        elif traj == TrajectoryType.INTERP:\n            camera_start = dataloader_eval.get_camera(image_idx=0)\n            camera_end = dataloader_eval.get_camera(image_idx=10)\n            camera_path = get_interpolated_camera_path(camera_start, camera_end, steps=30)\n        render_trajectory_video(\n            graph,\n            camera_path,\n            output_filename=output_filename,\n            rendered_output_name=rendered_output_name,\n            rendered_resolution_scaling_factor=rendered_resolution_scaling_factor,\n            num_rays_per_chunk=config.data.dataloader_eval.num_rays_per_chunk,\n        )\n\n\nif __name__ == \"__main__\":\n    dcargs.cli(main)\n", "path": "scripts/run_eval.py"}, {"content": "# Copyright 2022 The Plenoptix Team. All rights reserved.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\n\"\"\"\nMiscellaneous helper code.\n\"\"\"\n\nimport hashlib\nimport json\nfrom pydoc import locate\nfrom typing import Any, Dict\n\nimport torch\nfrom omegaconf import DictConfig\n\n\nclass DotDict(dict):\n    \"\"\"\n    dot.notation access to dictionary attributes\n    \"\"\"\n\n    def __getattr__(self, attr):\n        return self[attr]\n\n    __setattr__ = dict.__setitem__\n    __delattr__ = dict.__delitem__\n\n\ndef get_dict_to_torch(stuff, device=\"cpu\"):\n    \"\"\"Set everything in the dict to the specified torch device.\"\"\"\n    if isinstance(stuff, dict):\n        for k, v in stuff.items():\n            stuff[k] = get_dict_to_torch(v, device)\n        return stuff\n    if isinstance(stuff, torch.Tensor):\n        return stuff.to(device)\n    return stuff\n\n\ndef get_dict_to_cpu(stuff):\n    \"\"\"Set everything in the dict to CPU.\"\"\"\n    if isinstance(stuff, dict):\n        for k, v in stuff.items():\n            stuff[k] = get_dict_to_cpu(v)\n        return stuff\n    if isinstance(stuff, torch.Tensor):\n        return stuff.detach().cpu()\n    return stuff\n\n\ndef is_not_none(var):\n    \"\"\"Return True if the variable var is None.\"\"\"\n    return not isinstance(var, type(None))\n\n\ndef get_masked_dict(d, mask):\n    \"\"\"Return a masked dictionary.\n    TODO(ethan): add more asserts/checks so this doesn't have unpredictable behavior.\"\"\"\n    masked_dict = {}\n    for key, value in d.items():\n        masked_dict[key] = value[mask]\n    return masked_dict\n\n\ndef instantiate_from_dict_config(dict_config: DictConfig, **kwargs):\n    \"\"\"Our version of hydra's instantiate function.\"\"\"\n    dict_config_kwargs = {k: v for k, v in dict_config.items() if k != \"_target_\"}\n    uninstantiated_class = locate(dict_config._target_)  # pylint: disable=protected-access\n    all_kwargs = dict_config_kwargs\n    all_kwargs.update(kwargs)\n    instantiated_class = uninstantiated_class(**all_kwargs)\n    return instantiated_class\n\n\ndef get_hash_str_from_dict(dictionary: Dict[str, Any]) -> str:\n    \"\"\"MD5 hash of a dictionary. Based on\n    https://www.doc.ic.ac.uk/~nuric/coding/how-to-hash-a-dictionary-in-python.html\"\"\"\n    dhash = hashlib.md5()\n    encoded = json.dumps(dictionary, sort_keys=True).encode()\n    dhash.update(encoded)\n    return dhash.hexdigest()\n", "path": "pyrad/utils/misc.py"}], "after_files": [{"content": "\"\"\"\nrun_eval.py\n\"\"\"\nimport enum\nimport os\nfrom typing import Dict, List, Optional\n\nimport dcargs\nimport mediapy as media\nimport torch\nfrom hydra import compose, initialize\nfrom omegaconf import DictConfig\nfrom tqdm import tqdm\n\nfrom pyrad.cameras.camera_paths import CameraPath, get_interpolated_camera_path, get_spiral_path\nfrom pyrad.data.dataloader import EvalDataloader, setup_dataset_eval, setup_dataset_train\nfrom pyrad.graphs.base import Graph, setup_graph\nfrom pyrad.utils.misc import human_format\nfrom pyrad.utils.writer import TimeWriter\n\n\ndef _update_avg(prev_avg: float, new_val: float, step: int) -> float:\n    \"\"\"helper to calculate the running average\n\n    Args:\n        prev_avg (float): previous average value\n        new_val (float): new value to update the average with\n        step (int): current step number\n\n    Returns:\n        float: new updated average\n    \"\"\"\n    return (step * prev_avg + new_val) / (step + 1)\n\n\ndef _load_checkpoint(config: DictConfig, graph: Graph) -> None:\n    \"\"\"Helper function to load checkpointed graph\n\n    Args:\n        config (DictConfig): Configuration of graph to load\n        graph (Graph): Graph instance of which to load weights\n    \"\"\"\n    assert config.load_dir is not None\n    if config.load_step is None:\n        print(\"Loading latest checkpoint from load_dir\")\n        # NOTE: this is specific to the checkpoint name format\n        load_step = sorted([int(x[x.find(\"-\") + 1 : x.find(\".\")]) for x in os.listdir(config.load_dir)])[-1]\n    else:\n        load_step = config.load_step\n    load_path = os.path.join(config.load_dir, f\"step-{load_step:09d}.ckpt\")\n    assert os.path.exists(load_path), f\"Checkpoint {load_path} does not exist\"\n    loaded_state = torch.load(load_path, map_location=\"cpu\")\n    graph.load_graph(loaded_state)\n    print(f\"done loading checkpoint from {load_path}\")\n\n\ndef run_inference_from_config(config: DictConfig) -> Dict[str, float]:\n    \"\"\"helper function to run inference given config specifications (also used in benchmarking)\n\n    Args:\n        config (DictConfig): Configuration for loading the evaluation.\n\n    Returns:\n        Tuple[float, float]: returns both the avg psnr and avg rays per second\n    \"\"\"\n    print(\"Running inference.\")\n    device = torch.device(\"cuda\" if torch.cuda.is_available() else \"cpu\")\n    # setup graph and dataset\n    dataset_inputs_train, _ = setup_dataset_train(config.data, device=device)\n    _, dataloader_eval = setup_dataset_eval(config.data, test_mode=True, device=device)\n    graph = setup_graph(config.graph, dataset_inputs_train, device=device)\n    graph.eval()\n\n    # load checkpointed information\n    _load_checkpoint(config.trainer.resume_train, graph)\n\n    # calculate average psnr across test dataset\n    return render_stats_dict(graph, dataloader_eval)\n\n\ndef render_stats_dict(graph: Graph, dataloader_eval: EvalDataloader) -> Dict[str, float]:\n    \"\"\"Helper function to evaluate the graph on a dataloader.\n\n    Args:\n        graph (Graph): Graph to evaluate\n        dataloader_eval (EvalDataloader): Dataloader to evaluate on\n\n    Returns:\n        dict: returns the average psnr and average rays per second\n    \"\"\"\n    avg_psnr = 0\n    avg_rays_per_sec = 0\n    avg_fps = 0\n    for step, (camera_ray_bundle, batch) in tqdm(enumerate(dataloader_eval)):\n        with TimeWriter(writer=None, name=None, write=False) as t:\n            with torch.no_grad():\n                image_idx = int(camera_ray_bundle.camera_indices[0, 0])\n                outputs = graph.get_outputs_for_camera_ray_bundle(camera_ray_bundle)\n                psnr = graph.log_test_image_outputs(image_idx, step, batch, outputs)\n        avg_rays_per_sec = _update_avg(avg_rays_per_sec, camera_ray_bundle.origins.shape[0] / t.duration, step)\n        avg_psnr = _update_avg(avg_psnr, psnr, step)\n        avg_fps = _update_avg(avg_fps, 1 / t.duration, step)\n    return {\"avg psnr\": avg_psnr, \"avg rays per sec\": avg_rays_per_sec, \"avg fps\": avg_fps}\n\n\ndef render_trajectory_video(\n    graph: Graph,\n    camera_path: CameraPath,\n    output_filename: Optional[str] = None,\n    rendered_output_name: Optional[str] = None,\n    rendered_resolution_scaling_factor: float = 1.0,\n    num_rays_per_chunk: int = 4096,\n) -> None:\n    \"\"\"Helper function to create a video of the spiral trajectory.\n\n    Args:\n        graph: Graph to evaluate with.\n        camera_path: Index of the image to render.\n        output_filename: Name of the output file.\n        rendered_output_name: Name of the renderer output to use.\n        rendered_resolution_scaling_factor: Scaling factor to apply to the camera image resolution.\n            Defaults to 1.0.\n        num_rays_per_chunk: Number of rays to use per chunk. Defaults to 4096.\n    \"\"\"\n    print(\"Creating trajectory video.\")\n    images = []\n    for camera in tqdm(camera_path.cameras):\n        camera.rescale_output_resolution(rendered_resolution_scaling_factor)\n        camera_ray_bundle = camera.get_camera_ray_bundle().to(graph.device)\n        camera_ray_bundle.num_rays_per_chunk = num_rays_per_chunk\n        with torch.no_grad():\n            outputs = graph.get_outputs_for_camera_ray_bundle(camera_ray_bundle)\n        # TODO: don't hardcode the key! this will break for some nerf Graphs\n        image = outputs[rendered_output_name].cpu().numpy()\n        images.append(image)\n\n    seconds = 5.0\n    fps = len(images) / seconds\n    media.write_video(output_filename, images, fps=fps)\n\n\nclass MethodType(enum.Enum):\n    \"\"\"Enum for the method type.\"\"\"\n\n    PSNR = enum.auto()\n    TRAJ = enum.auto()\n\n\nclass TrajectoryType(enum.Enum):\n    \"\"\"Enum for the trajectory type.\"\"\"\n\n    SPIRAL = enum.auto()\n    INTERP = enum.auto()\n\n\ndef main(\n    config_name: str,\n    checkpoint_dir: str,\n    rendered_output_name: str,\n    method: MethodType = MethodType.PSNR,\n    traj: TrajectoryType = TrajectoryType.SPIRAL,\n    output_filename: str = \"output.mp4\",\n    rendered_resolution_scaling_factor: float = 1.0,\n    config_overrides: Optional[List[str]] = None,\n):\n    \"\"\"Evaluate trained model. This evaluation can either render a trajectory or compute the eval psnr.\n\n    Args:\n        config_name: Name of the config file to use.\n        checkpoint_dir: Directory to load the checkpoint from.\n        rendered_output_name: Name of the renderer output to use.\n        method: Method to use for evaluation. PSNR computes metrics, TRAJ renders a trajectory.\n        traj: Trajectory to render.\n        output_filename: Name of the output file.\n        rendered_resolution_scaling_factor: Scaling factor to apply to the camera image resolution.\n            Defaults to 1.0.\n        config_overrides: List of strings to override config values.\n    \"\"\"\n    # parser = my_func_that_returns_a_parser()\n    # args = parser.parse_args()\n\n    config_path = \"../configs\"\n    initialize(version_base=\"1.2\", config_path=config_path)\n    config_overrides = config_overrides or []\n    config = compose(config_name, overrides=config_overrides)\n\n    config.trainer.resume_train.load_dir = checkpoint_dir\n\n    device = torch.device(\"cuda\" if torch.cuda.is_available() else \"cpu\")\n    # setup graph and dataset\n    dataset_inputs_train, _ = setup_dataset_train(config.data, device=device)\n    _, dataloader_eval = setup_dataset_eval(config.data, test_mode=True, device=device)\n    graph = setup_graph(config.graph, dataset_inputs_train, device=device)\n    graph.eval()\n    # load checkpointed information\n    _load_checkpoint(config.trainer.resume_train, graph)\n\n    if method == MethodType.PSNR:\n        stats_dict = render_stats_dict(graph, dataloader_eval)\n        avg_psnr = stats_dict[\"avg psnr\"]\n        avg_rays_per_sec = stats_dict[\"avg rays per sec\"]\n        avg_fps = stats_dict[\"avg fps\"]\n        print(f\"Avg. PSNR: {avg_psnr:0.4f}\")\n        print(f\"Avg. Rays / Sec: {human_format(avg_rays_per_sec)}\")\n        print(f\"Avg. FPS: {avg_fps:0.4f}\")\n    elif method == MethodType.TRAJ:\n        # TODO(ethan): pass in camera information into argparse parser\n        if traj == TrajectoryType.SPIRAL:\n            camera_start = dataloader_eval.get_camera(image_idx=0)\n            # TODO(ethan): pass in the up direction of the camera\n            camera_path = get_spiral_path(camera_start, steps=30, radius=0.1)\n        elif traj == TrajectoryType.INTERP:\n            camera_start = dataloader_eval.get_camera(image_idx=0)\n            camera_end = dataloader_eval.get_camera(image_idx=10)\n            camera_path = get_interpolated_camera_path(camera_start, camera_end, steps=30)\n        render_trajectory_video(\n            graph,\n            camera_path,\n            output_filename=output_filename,\n            rendered_output_name=rendered_output_name,\n            rendered_resolution_scaling_factor=rendered_resolution_scaling_factor,\n            num_rays_per_chunk=config.data.dataloader_eval.num_rays_per_chunk,\n        )\n\n\nif __name__ == \"__main__\":\n    dcargs.cli(main)\n", "path": "scripts/run_eval.py"}, {"content": "# Copyright 2022 The Plenoptix Team. All rights reserved.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\n\"\"\"\nMiscellaneous helper code.\n\"\"\"\n\nimport hashlib\nimport json\nfrom pydoc import locate\nfrom typing import Any, Dict\n\nfrom math import floor, log\nimport torch\nfrom omegaconf import DictConfig\n\n\nclass DotDict(dict):\n    \"\"\"\n    dot.notation access to dictionary attributes\n    \"\"\"\n\n    def __getattr__(self, attr):\n        return self[attr]\n\n    __setattr__ = dict.__setitem__\n    __delattr__ = dict.__delitem__\n\n\ndef get_dict_to_torch(stuff, device=\"cpu\"):\n    \"\"\"Set everything in the dict to the specified torch device.\"\"\"\n    if isinstance(stuff, dict):\n        for k, v in stuff.items():\n            stuff[k] = get_dict_to_torch(v, device)\n        return stuff\n    if isinstance(stuff, torch.Tensor):\n        return stuff.to(device)\n    return stuff\n\n\ndef get_dict_to_cpu(stuff):\n    \"\"\"Set everything in the dict to CPU.\"\"\"\n    if isinstance(stuff, dict):\n        for k, v in stuff.items():\n            stuff[k] = get_dict_to_cpu(v)\n        return stuff\n    if isinstance(stuff, torch.Tensor):\n        return stuff.detach().cpu()\n    return stuff\n\n\ndef is_not_none(var):\n    \"\"\"Return True if the variable var is None.\"\"\"\n    return not isinstance(var, type(None))\n\n\ndef get_masked_dict(d, mask):\n    \"\"\"Return a masked dictionary.\n    TODO(ethan): add more asserts/checks so this doesn't have unpredictable behavior.\"\"\"\n    masked_dict = {}\n    for key, value in d.items():\n        masked_dict[key] = value[mask]\n    return masked_dict\n\n\ndef instantiate_from_dict_config(dict_config: DictConfig, **kwargs):\n    \"\"\"Our version of hydra's instantiate function.\"\"\"\n    dict_config_kwargs = {k: v for k, v in dict_config.items() if k != \"_target_\"}\n    uninstantiated_class = locate(dict_config._target_)  # pylint: disable=protected-access\n    all_kwargs = dict_config_kwargs\n    all_kwargs.update(kwargs)\n    instantiated_class = uninstantiated_class(**all_kwargs)\n    return instantiated_class\n\n\ndef get_hash_str_from_dict(dictionary: Dict[str, Any]) -> str:\n    \"\"\"MD5 hash of a dictionary. Based on\n    https://www.doc.ic.ac.uk/~nuric/coding/how-to-hash-a-dictionary-in-python.html\"\"\"\n    dhash = hashlib.md5()\n    encoded = json.dumps(dictionary, sort_keys=True).encode()\n    dhash.update(encoded)\n    return dhash.hexdigest()\n\n\ndef human_format(num):\n    \"\"\"Format a number in a more human readable way\"\"\"\n    units = [\"\", \"K\", \"M\", \"B\", \"T\", \"P\"]\n    k = 1000.0\n    magnitude = int(floor(log(num, k)))\n    return f\"{(num / k**magnitude):.2f} {units[magnitude]}\"\n", "path": "pyrad/utils/misc.py"}]}