From 676beebb14ddc80c8e766fa81993bc1748989ac6 Mon Sep 17 00:00:00 2001 From: Ethan Weber Date: Mon, 16 May 2022 14:03:19 -0700 Subject: [PATCH] Setting near and far with scene bounds collider. Also LR schedules. (#74) * some initial cuda code as I learn. using git to save progress. * first cuda kernel for ray sampling that mostly works * cleaning up code. working on lr. structs for scene bounds aabb * added scene bounds collider code * updated setup * setup * setup * black * including some files * exporting env variable * new file * git workflows * github actions * github actions * github actions * removing env var --- .gitignore | 6 +- .vscode/launch.json | 9 - .vscode/settings.json | 3 +- configs/data/dataset/blender_lego.yaml | 6 +- .../dataset/friends_TBBT-big_living_room.yaml | 4 +- configs/data/default_data.yaml | 3 +- configs/graph/default_graph.yaml | 22 +- configs/graph/instant_ngp_graph.yaml | 1 - configs/tests/test_dryrun.yaml | 8 +- environment/run_jupyter.sh | 2 +- environment/run_setup_environment.sh | 1 + environment/run_tensorboard.sh | 1 + mattport/cuda/include/helpers.cuh | 5 + mattport/cuda/mattport_cuda.cpp | 30 + mattport/cuda/mattport_cuda_kernel.cu | 133 + mattport/cuda/sampler.py | 45 + mattport/nerf/dataset/format/__init__.py | 0 mattport/nerf/dataset/{ => format}/blender.py | 5 +- mattport/nerf/dataset/{ => format}/friends.py | 0 mattport/nerf/dataset/format/instant_ngp.py | 4 + mattport/nerf/dataset/{ => format}/llff.py | 0 mattport/nerf/dataset/structs.py | 13 +- mattport/nerf/dataset/utils.py | 49 +- mattport/nerf/field_modules/embedding.py | 4 +- .../field_modules/scene_bounds_collider.py | 90 + mattport/nerf/graph/base.py | 50 +- mattport/nerf/graph/vanilla_nerf.py | 12 +- mattport/nerf/optimizers.py | 59 +- mattport/nerf/sampler.py | 25 +- mattport/nerf/trainer.py | 93 +- mattport/structures/rays.py | 16 + mattport/utils/io.py | 9 + mattport/utils/misc.py | 14 + mattport/utils/profiler.py | 7 +- notebooks/visualize_dataset.ipynb | 8240 +---------------- pyproject.toml | 6 +- scripts/benchmarking/run_benchmark.py | 2 +- scripts/debugging/run_actions.py | 4 +- scripts/run_train.py | 5 +- setup.cfg | 5 +- setup.py | 39 + tests/cuda/test_mattport_cuda.py | 102 + .../nerf/dataloader/configs/blender_lego.yaml | 6 +- tests/nerf/dataloader/test_ray_generator.py | 10 +- tests/nerf/test_sampler.py | 9 +- 45 files changed, 803 insertions(+), 8354 deletions(-) create mode 100644 environment/run_setup_environment.sh create mode 100644 environment/run_tensorboard.sh create mode 100644 mattport/cuda/include/helpers.cuh create mode 100644 mattport/cuda/mattport_cuda.cpp create mode 100644 mattport/cuda/mattport_cuda_kernel.cu create mode 100644 mattport/cuda/sampler.py create mode 100644 mattport/nerf/dataset/format/__init__.py rename mattport/nerf/dataset/{ => format}/blender.py (91%) rename mattport/nerf/dataset/{ => format}/friends.py (100%) create mode 100644 mattport/nerf/dataset/format/instant_ngp.py rename mattport/nerf/dataset/{ => format}/llff.py (100%) create mode 100644 mattport/nerf/field_modules/scene_bounds_collider.py create mode 100644 setup.py create mode 100644 tests/cuda/test_mattport_cuda.py diff --git a/.gitignore b/.gitignore index 9a0a3fd90..51ac32dce 100644 --- a/.gitignore +++ b/.gitignore @@ -165,11 +165,13 @@ outputs/ # Data data/ +data_*/ !configs/data -data_shared/ # Misc old/ temp* .nfs* -external/ \ No newline at end of file +external/ +__MACOSX/ +outputs*/ \ No newline at end of file diff --git a/.vscode/launch.json b/.vscode/launch.json index 4fc7507dd..feda559d3 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -17,9 +17,6 @@ "type": "python", "request": "launch", "program": "scripts/run_train.py", - "env": { - "CUDA_VISIBLE_DEVICES": "8" - }, "console": "integratedTerminal" }, { @@ -47,9 +44,6 @@ "type": "python", "request": "launch", "program": "scripts/run_train.py", - "env": { - "CUDA_VISIBLE_DEVICES": "8" - }, "console": "integratedTerminal", "args": [ "--config-name=nerfw.yaml" @@ -60,9 +54,6 @@ "type": "python", "request": "launch", "program": "scripts/run_train.py", - "env": { - "CUDA_VISIBLE_DEVICES": "4" - }, "console": "integratedTerminal", "args": [ "--config-name=semantic_nerf.yaml" diff --git a/.vscode/settings.json b/.vscode/settings.json index 7262775be..3a9b1b0cf 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -9,6 +9,7 @@ "**/temp*": true, }, "editor.formatOnSave": true, + "python.envFile": "${workspaceFolder}/.env", "python.formatting.provider": "black", "python.formatting.blackArgs": [ "--line-length=120" @@ -25,5 +26,5 @@ "**/site-packages/**/*.py", ".vscode/*.py", "external/**/*.py", - ], + ] } \ No newline at end of file diff --git a/configs/data/dataset/blender_lego.yaml b/configs/data/dataset/blender_lego.yaml index 6b80a1cff..3e495e1fd 100644 --- a/configs/data/dataset/blender_lego.yaml +++ b/configs/data/dataset/blender_lego.yaml @@ -1,4 +1,4 @@ data_directory: data/blender/lego -dataset_type: blender -downscale_factor: 4 -alpha_color: white \ No newline at end of file +dataset_format: blender +downscale_factor: 1 +alpha_color: white diff --git a/configs/data/dataset/friends_TBBT-big_living_room.yaml b/configs/data/dataset/friends_TBBT-big_living_room.yaml index 152d90958..539beca58 100644 --- a/configs/data/dataset/friends_TBBT-big_living_room.yaml +++ b/configs/data/dataset/friends_TBBT-big_living_room.yaml @@ -1,3 +1,3 @@ data_directory: data/friends/TBBT-big_living_room -dataset_type: friends -downscale_factor: 8 \ No newline at end of file +dataset_format: friends +downscale_factor: 8 diff --git a/configs/data/default_data.yaml b/configs/data/default_data.yaml index e67475ec6..eba1a6036 100644 --- a/configs/data/default_data.yaml +++ b/configs/data/default_data.yaml @@ -3,7 +3,8 @@ defaults: - dataset: blender_lego # validation options -validation_image_indices: [0, 10] # Which images to evaluate on. +val_image_indices: [0, 10, 20] # Which images to evaluate on. +val_downscale_factor: 8 # dataloader options dataloader: diff --git a/configs/graph/default_graph.yaml b/configs/graph/default_graph.yaml index eb29d08c6..327346cb6 100644 --- a/configs/graph/default_graph.yaml +++ b/configs/graph/default_graph.yaml @@ -1,15 +1,14 @@ # saving model specifications -model_dir: /tmp/mattport_models/ +model_dir: /tmp/mattport_models/ steps_per_save: null steps_per_test: 100 -max_num_iterations: 100000 +max_num_iterations: 1000000 # pretrained loading options resume_train: load_dir: # pretrained model directory load_step: # pretrained model's step number -# NOTE(ethan): currently setting to nothing b/c we're still designing the config format... network: _target_: mattport.nerf.graph.vanilla_nerf.NeRFGraph near_plane: 2.0 @@ -25,16 +24,17 @@ network: param_groups: fields: optimizer: - type: Adam - lr: 5e-4 + _target_: torch.optim.Adam + lr: 0.0005 scheduler: - type: StepLR - step_size: 30 # Period of learning rate decay. - gamma: 0.1 # Multiplicative factor of learning rate decay. Default: 0.1. + _target_: mattport.nerf.optimizers.ExponentialDecaySchedule + lr_final: 0.000005 + max_steps: 1000000 + cameras: optimizer: - type: SGD + _target_: torch.optim.SGD lr: 1e-4 scheduler: - type: ExponentialLR - gamma: 0.1 \ No newline at end of file + _target_: torch.optim.lr_scheduler.ExponentialLR + gamma: 0.1 diff --git a/configs/graph/instant_ngp_graph.yaml b/configs/graph/instant_ngp_graph.yaml index a2434147b..cda7fa4a9 100644 --- a/configs/graph/instant_ngp_graph.yaml +++ b/configs/graph/instant_ngp_graph.yaml @@ -9,5 +9,4 @@ network: param_groups: fields: optimizer: - type: Adam lr: 1e-2 diff --git a/configs/tests/test_dryrun.yaml b/configs/tests/test_dryrun.yaml index 92266ec31..8d5f53271 100644 --- a/configs/tests/test_dryrun.yaml +++ b/configs/tests/test_dryrun.yaml @@ -8,10 +8,10 @@ machine_config: data: dataset: - dataset_type: blender + dataset_format: blender data_directory: tests/data/lego_test downscale_factor: 8 - validation_image_indices: [0] + val_image_indices: [0] dataloader: num_images_to_sample_from: 1 num_rays_per_batch: 8 @@ -20,9 +20,9 @@ graph: max_num_iterations: 5 logging: - writer: + writer: TensorboardWriter: - save_dir: '/tmp/' + save_dir: "/tmp/" hydra: searchpath: diff --git a/environment/run_jupyter.sh b/environment/run_jupyter.sh index 0164bc6fd..dc42e53a6 100644 --- a/environment/run_jupyter.sh +++ b/environment/run_jupyter.sh @@ -1 +1 @@ -python -m jupyter lab --ip 0.0.0.0 --port 9999 --no-browser --config environments/jupyter.py \ No newline at end of file +python -m jupyter lab --ip 0.0.0.0 --port ${JUPYTER_PORT} --no-browser --config environments/jupyter.py diff --git a/environment/run_setup_environment.sh b/environment/run_setup_environment.sh new file mode 100644 index 000000000..29851782a --- /dev/null +++ b/environment/run_setup_environment.sh @@ -0,0 +1 @@ +PROJECT_ROOT=`pwd` diff --git a/environment/run_tensorboard.sh b/environment/run_tensorboard.sh new file mode 100644 index 000000000..29ac6584e --- /dev/null +++ b/environment/run_tensorboard.sh @@ -0,0 +1 @@ +tensorboard --samples_per_plugin scalars=100,images=10 --port ${TENSORBOARD_PORT} --logdir outputs diff --git a/mattport/cuda/include/helpers.cuh b/mattport/cuda/include/helpers.cuh new file mode 100644 index 000000000..040f61c9a --- /dev/null +++ b/mattport/cuda/include/helpers.cuh @@ -0,0 +1,5 @@ +/* + * Helper code. + */ + +#define CUDA_NUM_BLOCKS_NEEDED(Q, CUDA_N_THREADS) ((Q - 1) / CUDA_N_THREADS + 1) \ No newline at end of file diff --git a/mattport/cuda/mattport_cuda.cpp b/mattport/cuda/mattport_cuda.cpp new file mode 100644 index 000000000..50e9eb219 --- /dev/null +++ b/mattport/cuda/mattport_cuda.cpp @@ -0,0 +1,30 @@ +/* +This file uses pybind to make CUDA calls. +*/ + +#include + +#include + +// CUDA forward declarations + +std::vector sample_uniformly_along_ray_bundle( + torch::Tensor origins, + torch::Tensor directions, + torch::Tensor nears, + torch::Tensor fars, + torch::Tensor offsets, + int max_num_samples); + +// C++ interface + +#define CHECK_CUDA(x) TORCH_CHECK(x.is_cuda(), #x " must be a CUDA tensor") +#define CHECK_CONTIGUOUS(x) TORCH_CHECK(x.is_contiguous(), #x " must be contiguous") +#define CHECK_INPUT(x) \ + CHECK_CUDA(x); \ + CHECK_CONTIGUOUS(x) + +PYBIND11_MODULE(TORCH_EXTENSION_NAME, m) +{ + m.def("sample_uniformly_along_ray_bundle", &sample_uniformly_along_ray_bundle); +} \ No newline at end of file diff --git a/mattport/cuda/mattport_cuda_kernel.cu b/mattport/cuda/mattport_cuda_kernel.cu new file mode 100644 index 000000000..fc0cb4009 --- /dev/null +++ b/mattport/cuda/mattport_cuda_kernel.cu @@ -0,0 +1,133 @@ +/* +Note that this file has the _kernel.cu extension because +setuptools cannot handle files with the same name but different extensions. +*/ + +#include + +#include +#include + +#include + +#define CUDA_N_BLOCKS_NEEDED(Q, CUDA_N_THREADS) ((Q - 1) / CUDA_N_THREADS + 1) + +namespace +{ + template + __device__ __forceinline__ scalar_t sigmoid(scalar_t z) + { + return 1.0 / (1.0 + exp(-z)); + } + + template + __device__ __forceinline__ scalar_t d_sigmoid(scalar_t z) + { + const auto s = sigmoid(z); + return (1.0 - s) * s; + } + + template + __device__ __forceinline__ scalar_t d_tanh(scalar_t z) + { + const auto t = tanh(z); + return 1 - (t * t); + } + + template + __device__ __forceinline__ scalar_t elu(scalar_t z, scalar_t alpha = 1.0) + { + return fmaxf(0.0, z) + fminf(0.0, alpha * (exp(z) - 1.0)); + } + + template + __device__ __forceinline__ scalar_t d_elu(scalar_t z, scalar_t alpha = 1.0) + { + const auto e = exp(z); + const auto d_relu = z < 0.0 ? 0.0 : 1.0; + return d_relu + (((alpha * (e - 1.0)) < 0.0) ? (alpha * e) : 0.0); + } + + template + __global__ void sample_uniformly_along_ray_bundle_kernel( + const torch::PackedTensorAccessor32 origins, + const torch::PackedTensorAccessor32 directions, + const torch::PackedTensorAccessor32 nears, + const torch::PackedTensorAccessor32 fars, + const torch::PackedTensorAccessor32 offsets, + torch::PackedTensorAccessor32 output_time_steps, + torch::PackedTensorAccessor32 output_samples, + torch::PackedTensorAccessor32 output_time_steps_mask) + { + + const int thread_id = blockIdx.x * blockDim.x + threadIdx.x; + const int max_num_samples = output_samples.size(1); + + scalar_t t_min = nears[thread_id]; + scalar_t t_max = fars[thread_id]; + scalar_t offset = offsets[thread_id]; + int sample_index = 0; + + // #pragma unroll + for (scalar_t t = t_min; t <= t_max; t += offset) + { + // #pragma unroll + for (int i = 0; i < 3; ++i) + { + if (sample_index < max_num_samples) + { + scalar_t pos_i = origins[thread_id][i] + t * directions[thread_id][i]; + output_samples[thread_id][sample_index][i] = pos_i; + } + } + if (sample_index < max_num_samples) + { + output_time_steps[thread_id][sample_index] = t; + output_time_steps_mask[thread_id][sample_index] = 1.0; + } + sample_index += 1; + } + } +} // namespace + +std::vector sample_uniformly_along_ray_bundle( + torch::Tensor origins, + torch::Tensor directions, + torch::Tensor nears, + torch::Tensor fars, + torch::Tensor offsets, + int max_num_samples) +{ + std::cout << "sample_uniformly_along_ray_bundle -- start" << std::endl; + + TORCH_CHECK(origins.is_floating_point()); // should be float32 + TORCH_CHECK(origins.ndimension() == 2); // and check that shape is (num_rays, 3) + TORCH_CHECK(origins.size(1) == 3); + const auto num_rays = origins.size(0); + + // TODO: change the number of threads based on GPU + const int threads = 1024; // number of threads in a thread block + // const int blocks = num_rays / threads; + const int blocks = CUDA_N_BLOCKS_NEEDED(num_rays, threads); + + // std::cout << origins.options() << std::endl; + torch::Tensor output_time_steps = torch::zeros({num_rays, max_num_samples}, origins.options()); + torch::Tensor output_samples = torch::zeros({num_rays, max_num_samples, 3}, origins.options()); + torch::Tensor output_time_steps_mask = torch::zeros({num_rays, max_num_samples}, origins.options()); + + // The second argument is used for error messages. + // __FUNCTION__ macro == "sample_uniformly_along_ray_bundle" in this case. + AT_DISPATCH_FLOATING_TYPES(origins.type(), __FUNCTION__, [&] + { sample_uniformly_along_ray_bundle_kernel<<>>( + origins.packed_accessor32(), + directions.packed_accessor32(), + nears.packed_accessor32(), + fars.packed_accessor32(), + offsets.packed_accessor32(), + output_time_steps.packed_accessor32(), + output_samples.packed_accessor32(), + output_time_steps_mask.packed_accessor32()); }); + + std::cout << "sample_uniformly_along_ray_bundle -- end" << std::endl; + return {output_time_steps, output_samples, output_time_steps_mask}; +} diff --git a/mattport/cuda/sampler.py b/mattport/cuda/sampler.py new file mode 100644 index 000000000..7188f8136 --- /dev/null +++ b/mattport/cuda/sampler.py @@ -0,0 +1,45 @@ +""" +Sampler. +""" + +import math + +import torch +from torch import nn + +import mattport_cuda + + +class LLTMFunction(torch.autograd.Function): + @staticmethod + def forward(ctx, input, weights, bias, old_h, old_cell): + outputs = mattport_cuda.forward(input, weights, bias, old_h, old_cell) + new_h, new_cell = outputs[:2] + variables = outputs[1:] + [weights] + ctx.save_for_backward(*variables) + + return new_h, new_cell + + @staticmethod + def backward(ctx, grad_h, grad_cell): + outputs = mattport_cuda.backward(grad_h.contiguous(), grad_cell.contiguous(), *ctx.saved_variables) + d_old_h, d_input, d_weights, d_bias, d_old_cell, d_gates = outputs + return d_input, d_weights, d_bias, d_old_h, d_old_cell + + +class Sampler(nn.Module): + def __init__(self, input_features, state_size): + super().__init__() + self.input_features = input_features + self.state_size = state_size + self.weights = nn.Parameter(torch.Tensor(3 * state_size, input_features + state_size)) + self.bias = nn.Parameter(torch.Tensor(1, 3 * state_size)) + self.reset_parameters() + + def reset_parameters(self): + stdv = 1.0 / math.sqrt(self.state_size) + for weight in self.parameters(): + weight.data.uniform_(-stdv, +stdv) + + def forward(self, input, state): + return LLTMFunction.apply(input, self.weights, self.bias, *state) diff --git a/mattport/nerf/dataset/format/__init__.py b/mattport/nerf/dataset/format/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/mattport/nerf/dataset/blender.py b/mattport/nerf/dataset/format/blender.py similarity index 91% rename from mattport/nerf/dataset/blender.py rename to mattport/nerf/dataset/format/blender.py index c8e0e9d12..d66ac29f0 100644 --- a/mattport/nerf/dataset/blender.py +++ b/mattport/nerf/dataset/format/blender.py @@ -10,7 +10,7 @@ import numpy as np import torch from torchtyping import TensorType -from mattport.nerf.dataset.structs import DatasetInputs +from mattport.nerf.dataset.structs import DatasetInputs, SceneBounds from mattport.utils.io import load_from_json @@ -53,12 +53,15 @@ def load_blender_data( intrinsics = torch.ones((num_cameras, num_intrinsics_params), dtype=torch.float32) intrinsics *= torch.tensor([cx, cy, focal_length]) + scene_bounds = SceneBounds(aabb=torch.tensor([[-1, -1, -1], [1, 1, 1]], dtype=torch.float32)) + dataset_inputs = DatasetInputs( image_filenames=image_filenames, downscale_factor=downscale_factor, alpha_color=alpha_color, intrinsics=intrinsics * 1.0 / downscale_factor, # downscaling the intrinsics here camera_to_world=camera_to_world, + scene_bounds=scene_bounds, ) return dataset_inputs diff --git a/mattport/nerf/dataset/friends.py b/mattport/nerf/dataset/format/friends.py similarity index 100% rename from mattport/nerf/dataset/friends.py rename to mattport/nerf/dataset/format/friends.py diff --git a/mattport/nerf/dataset/format/instant_ngp.py b/mattport/nerf/dataset/format/instant_ngp.py new file mode 100644 index 000000000..2440ed7ae --- /dev/null +++ b/mattport/nerf/dataset/format/instant_ngp.py @@ -0,0 +1,4 @@ +""" +Code for loading instant-ngp dataset formats. +# TODO +""" diff --git a/mattport/nerf/dataset/llff.py b/mattport/nerf/dataset/format/llff.py similarity index 100% rename from mattport/nerf/dataset/llff.py rename to mattport/nerf/dataset/format/llff.py diff --git a/mattport/nerf/dataset/structs.py b/mattport/nerf/dataset/structs.py index d7c689169..a3f3d3427 100644 --- a/mattport/nerf/dataset/structs.py +++ b/mattport/nerf/dataset/structs.py @@ -27,6 +27,17 @@ class Semantics: thing_filenames: List[str] = None +@dataclass +class SceneBounds: + """Data to represent the scene bounds. + + aabb: axis-aligned bounding box + aabb[0] is the minimum (x,y,z) point. aabb[1] is the maximum (x,y,z) point. + """ + + aabb: TensorType[2, 3] = None + + @dataclass class DatasetInputs: """Dataset inputs for the image dataset and the ray generator. @@ -40,9 +51,9 @@ class DatasetInputs: downscale_factor: int = 1 intrinsics: torch.tensor = None camera_to_world: torch.tensor = None - bounding_box: torch.tensor = None mask_filenames: List[str] = None depth_filenames: List[str] = None + scene_bounds: SceneBounds = SceneBounds() semantics: Semantics = Semantics() point_cloud: PointCloud = PointCloud() alpha_color: Optional[TensorType[3]] = None diff --git a/mattport/nerf/dataset/utils.py b/mattport/nerf/dataset/utils.py index 97b0d8754..1888626a4 100644 --- a/mattport/nerf/dataset/utils.py +++ b/mattport/nerf/dataset/utils.py @@ -2,38 +2,37 @@ For loading the blender dataset format. """ -from typing import Dict, Optional, Tuple, Union +from typing import Optional, Union from omegaconf import ListConfig -from mattport.nerf.dataset.blender import load_blender_data -from mattport.nerf.dataset.friends import load_friends_data +from mattport.nerf.dataset.format.blender import load_blender_data +from mattport.nerf.dataset.format.friends import load_friends_data from mattport.nerf.dataset.structs import DatasetInputs from mattport.structures.colors import get_color from mattport.utils.io import get_absolute_path -def get_dataset_inputs_dict( +def get_dataset_inputs( data_directory: str, - dataset_type: str, + dataset_format: str, + split: str, downscale_factor: int = 1, alpha_color: Optional[Union[str, list, ListConfig]] = None, - splits: Tuple[str] = ("train", "val"), load_dataset_inputs_from_cache: bool = False, -) -> Dict[str, DatasetInputs]: +) -> DatasetInputs: """Returns the dataset inputs, which will be used with an ImageDataset and RayGenerator. # TODO: implement the `test` split, which will have depths and normals, etc. Args: data_directory (str): Location of data - dataset_type (str): Name of dataset type + dataset_format (str): Name of dataset type downscale_factor (int, optional): How much to downscale images. Defaults to 1.0. alpha_color (str, list, optional): Sets transparent regions to specified color, otherwise black. load_from_cache (bool) Returns: - Dict[str, DatasetInputs]: The inputs needed for generating rays. + DatasetInputs: The inputs needed for generating rays. """ - dataset_inputs_dict = {} if load_dataset_inputs_from_cache: print("Loading from cache.") @@ -42,16 +41,14 @@ def get_dataset_inputs_dict( if alpha_color is not None: alpha_color = get_color(alpha_color) - if dataset_type == "blender": - for split in splits: - dataset_inputs = load_blender_data( - get_absolute_path(data_directory), - downscale_factor=downscale_factor, - split=split, - alpha_color=alpha_color, - ) - dataset_inputs_dict[split] = dataset_inputs - elif dataset_type == "friends": + if dataset_format == "blender": + dataset_inputs = load_blender_data( + get_absolute_path(data_directory), + downscale_factor=downscale_factor, + split=split, + alpha_color=alpha_color, + ) + elif dataset_format == "friends": # TODO(ethan): change this hack, and do proper splits for the friends dataset # currently we assume that there is only the training set of images dataset_inputs = load_friends_data( @@ -60,15 +57,7 @@ def get_dataset_inputs_dict( split="train", include_point_cloud=False, ) - for split in splits: - dataset_inputs_dict[split] = dataset_inputs else: - raise NotImplementedError(f"{dataset_type} is not a valid dataset type") + raise NotImplementedError(f"{dataset_format} is not a valid dataset type") - for split in splits: - # save the dataset inputs to cache - # TODO: - # dataset_inputs_dict[splits].save_to_folder_name() - pass - - return dataset_inputs_dict + return dataset_inputs diff --git a/mattport/nerf/field_modules/embedding.py b/mattport/nerf/field_modules/embedding.py index ae22d1fc4..9a7770451 100644 --- a/mattport/nerf/field_modules/embedding.py +++ b/mattport/nerf/field_modules/embedding.py @@ -9,7 +9,9 @@ from mattport.nerf.field_modules.base import FieldModule class Embedding(FieldModule): - """Index into embeddings.""" + """Index into embeddings. + # TODO: add different types of initializations + """ def __init__(self, in_dim: int, out_dim: int) -> None: """ diff --git a/mattport/nerf/field_modules/scene_bounds_collider.py b/mattport/nerf/field_modules/scene_bounds_collider.py new file mode 100644 index 000000000..538ec8e44 --- /dev/null +++ b/mattport/nerf/field_modules/scene_bounds_collider.py @@ -0,0 +1,90 @@ +""" +Ray generator. +""" +import torch +from torch import nn +from torchtyping import TensorType +from mattport.nerf.dataset.structs import SceneBounds +from mattport.structures.rays import RayBundle + + +class SceneBoundsCollider(nn.Module): + """Module for setting near and far values for rays.""" + + def forward(self, ray_bundle: RayBundle) -> RayBundle: + """To be implemented.""" + raise NotImplementedError + + +class AABBBoxCollider(SceneBoundsCollider): + """Module for colliding rays with the scene bounds to compute near and far values.""" + + def __init__(self, scene_bounds: SceneBounds) -> None: + super().__init__() + self.scene_bounds = scene_bounds + + @classmethod + def intersect_with_aabb( + cls, rays_o: TensorType["num_rays", 3], rays_d: TensorType["num_rays", 3], aabb: TensorType[2, 3] + ): + """_summary_ + + Args: + rays_o (torch.tensor): (num_rays, 3) + rays_d (torch.tensor): (num_rays, 3) + aabb (torch.tensor): (2, 3) This is [min point (x,y,z), max point (x,y,z)] + """ + # avoid divide by zero + dir_fraction = 1.0 / (rays_d + 1e-6) + + # x + t1 = (aabb[0, 0] - rays_o[:, 0:1]) * dir_fraction[:, 0:1] + t2 = (aabb[1, 0] - rays_o[:, 0:1]) * dir_fraction[:, 0:1] + # y + t3 = (aabb[0, 1] - rays_o[:, 1:2]) * dir_fraction[:, 1:2] + t4 = (aabb[1, 1] - rays_o[:, 1:2]) * dir_fraction[:, 1:2] + # z + t5 = (aabb[0, 2] - rays_o[:, 2:3]) * dir_fraction[:, 2:3] + t6 = (aabb[1, 2] - rays_o[:, 2:3]) * dir_fraction[:, 2:3] + + nears = torch.max( + torch.cat([torch.minimum(t1, t2), torch.minimum(t3, t4), torch.minimum(t5, t6)], dim=1), dim=1 + ).values + fars = torch.min( + torch.cat([torch.maximum(t1, t2), torch.maximum(t3, t4), torch.maximum(t5, t6)], dim=1), dim=1 + ).values + + # fars < 0: means the ray is behind the camera + # nears > fars: means no intersection + valid_mask = ((fars > nears).float() * (fars > 0).float()).bool() + nears[nears < 0.0] = 0.0 + nears[~valid_mask] = 0.0 + fars[~valid_mask] = 0.0 + return nears, fars, valid_mask + + def forward(self, ray_bundle: RayBundle) -> RayBundle: + """Intersects the rays with the scene bounds and updates the near and far values. + Populates nears and fars fields and returns the ray_bundle. + """ + aabb = self.scene_bounds.aabb + nears, fars, valid_mask = self.intersect_with_aabb(ray_bundle.origins, ray_bundle.directions, aabb) + ray_bundle.nears = nears + ray_bundle.fars = fars + ray_bundle.valid_mask = valid_mask + return ray_bundle + + +class NearFarCollider(SceneBoundsCollider): + """Sets the nears and fars with fixed values.""" + + def __init__(self, near_plane, far_plane) -> None: + self.near_plane = near_plane + self.far_plane = far_plane + super().__init__() + + def forward(self, ray_bundle: RayBundle) -> RayBundle: + ones = torch.ones_like(ray_bundle.origins[:, 0]) + ray_bundle.nears = ones * self.near_plane + ray_bundle.fars = ones * self.far_plane + ray_bundle.valid_mask = ones.bool() + return ray_bundle diff --git a/mattport/nerf/graph/base.py b/mattport/nerf/graph/base.py index 045fae1ee..8f52b133e 100644 --- a/mattport/nerf/graph/base.py +++ b/mattport/nerf/graph/base.py @@ -12,9 +12,12 @@ from torch import nn from torch.nn import Parameter from torchtyping import TensorType +from mattport.nerf.dataset.structs import SceneBounds from mattport.nerf.field_modules.ray_generator import RayGenerator +from mattport.nerf.field_modules.scene_bounds_collider import AABBBoxCollider from mattport.structures.cameras import get_camera_model from mattport.structures.rays import RayBundle +from mattport.utils.misc import get_masked_dict @dataclass @@ -41,13 +44,24 @@ class Node: class Graph(nn.Module): """_summary_""" - def __init__(self, intrinsics=None, camera_to_world=None, loss_coefficients: DictConfig = None, **kwargs) -> None: + def __init__( + self, + intrinsics=None, + camera_to_world=None, + loss_coefficients: DictConfig = None, + scene_bounds: SceneBounds = None, + **kwargs, + ) -> None: super().__init__() self.intrinsics = intrinsics self.camera_to_world = camera_to_world + self.scene_bounds = scene_bounds self.loss_coefficients = loss_coefficients + # assert self.scene_bounds is not None self.kwargs = kwargs + self.scene_bounds_collider = None self.ray_generator = RayGenerator(self.intrinsics, self.camera_to_world) + self.populate_collider() self.populate_modules() # populate the modules # to keep track of which device the nn.Module is on @@ -57,6 +71,10 @@ class Graph(nn.Module): """Returns the device that the torch parameters are on.""" return self.device_indicator_param.device + def populate_collider(self): + """Set the scene bounds collider to use.""" + self.scene_bounds_collider = AABBBoxCollider(self.scene_bounds) + @abstractmethod def populate_modules(self): """Initializes the modules that are part of the network.""" @@ -89,13 +107,22 @@ class Graph(nn.Module): def forward(self, ray_indices: TensorType["num_rays", 3], batch: Union[str, Dict[str, torch.tensor]] = None): """Forward function that needs to be overridden.""" - # get the rays: - ray_bundle = self.ray_generator.forward(ray_indices) # RayBundle - outputs = self.get_outputs(ray_bundle) - if not isinstance(batch, type(None)): - loss_dict = self.get_loss_dict(outputs=outputs, batch=batch) - return outputs, loss_dict - return outputs + # get the rays + original_ray_bundle = self.ray_generator.forward(ray_indices) # RayBundle + intersected_ray_bundle = self.scene_bounds_collider(original_ray_bundle) + + if isinstance(batch, type(None)): + # during inference, keep all rays + outputs = self.get_outputs(intersected_ray_bundle) + return outputs + + # during training, keep only the rays that intersect the scene. discard the rest + valid_mask = intersected_ray_bundle.valid_mask + masked_intersected_ray_bundle = intersected_ray_bundle.get_masked_ray_bundle(valid_mask) + masked_batch = get_masked_dict(batch, valid_mask) + outputs = self.get_outputs(masked_intersected_ray_bundle) + loss_dict = self.get_loss_dict(outputs=outputs, batch=masked_batch) + return outputs, loss_dict @abstractmethod def get_loss_dict(self, outputs, batch) -> Dict[str, torch.tensor]: @@ -130,9 +157,10 @@ class Graph(nn.Module): for i in range(0, num_rays, chunk_size): start_idx = i end_idx = i + chunk_size - ray_bundle = camera_ray_bundle.get_row_major_sliced_ray_bundle(start_idx, end_idx) - ray_bundle.move_to_device(device) - outputs = self.get_outputs(ray_bundle) + original_ray_bundle = camera_ray_bundle.get_row_major_sliced_ray_bundle(start_idx, end_idx) + original_ray_bundle.move_to_device(device) + intersected_ray_bundle = self.scene_bounds_collider(original_ray_bundle) + outputs = self.get_outputs(intersected_ray_bundle) for output_name, output in outputs.items(): outputs_lists[output_name].append(output) for output_name, outputs_list in outputs_lists.items(): diff --git a/mattport/nerf/graph/vanilla_nerf.py b/mattport/nerf/graph/vanilla_nerf.py index 9e841dcdc..3332bfc43 100644 --- a/mattport/nerf/graph/vanilla_nerf.py +++ b/mattport/nerf/graph/vanilla_nerf.py @@ -20,6 +20,7 @@ from mattport.nerf.renderers import AccumulationRenderer, DepthRenderer, RGBRend from mattport.nerf.sampler import PDFSampler, UniformSampler from mattport.structures import colors from mattport.structures.rays import RayBundle, RaySamples +from mattport.nerf.field_modules.scene_bounds_collider import NearFarCollider from mattport.utils import stats_tracker, visualization, writer @@ -116,12 +117,13 @@ class NeRFGraph(Graph): self.field_coarse = NeRFField() self.field_fine = NeRFField() + def populate_collider(self): + self.scene_bounds_collider = NearFarCollider(self.near_plane, self.far_plane) + def populate_modules(self): # samplers - self.sampler_uniform = UniformSampler( - near_plane=self.near_plane, far_plane=self.far_plane, num_samples=self.num_coarse_samples - ) + self.sampler_uniform = UniformSampler(num_samples=self.num_coarse_samples) self.sampler_pdf = PDFSampler(num_samples=self.num_importance_samples) # field @@ -147,11 +149,13 @@ class NeRFGraph(Graph): Dict[str, List[Parameter]]: Mapping of different parameter groups """ param_groups = {} - # param_groups["cameras"] = list(self.ray_generator.parameters()) param_groups["fields"] = list(self.field_coarse.parameters()) + list(self.field_fine.parameters()) return param_groups def get_outputs(self, ray_bundle: RayBundle): + + # TODO: set the new rays... + # coarse network: uniform_ray_samples = self.sampler_uniform(ray_bundle) # RaySamples diff --git a/mattport/nerf/optimizers.py b/mattport/nerf/optimizers.py index 3acb05a91..480358f64 100644 --- a/mattport/nerf/optimizers.py +++ b/mattport/nerf/optimizers.py @@ -3,9 +3,29 @@ Optimizers class. """ from typing import Dict, List + +import numpy as np +from hydra.utils import instantiate from omegaconf import DictConfig -import torch from torch.nn import Parameter +from torch.optim.lr_scheduler import LambdaLR + +from mattport.utils import writer + + +class ExponentialDecaySchedule(LambdaLR): + """Exponential learning rate decay function.""" + + def __init__(self, optimizer, lr_init, lr_final, max_steps) -> None: + def func(step): + t = np.clip(step / max_steps, 0, 1) + log_lerp = np.exp(np.log(lr_init) * (1 - t) + np.log(lr_final) * t) + multiplier = ( + log_lerp / lr_init + ) # divided by lr_init because the multiplier is with the initial learning rate + return multiplier + + super().__init__(optimizer, lr_lambda=func) class Optimizers: @@ -21,33 +41,30 @@ class Optimizers: self.config = config self.optimizers = {} self.schedulers = {} - for param_name, params in param_groups.items(): - optimizer_config = config[param_name].optimizer - optimizer = getattr(torch.optim, optimizer_config.type) - kwargs = {k: v for k, v in optimizer_config.items() if k != "type"} - self.optimizers[param_name] = optimizer(params, **kwargs) - if config[param_name].scheduler: - scheduler_config = config[param_name].scheduler - scheduler = getattr(torch.optim.lr_scheduler, scheduler_config.type) - kwargs = {k: v for k, v in scheduler_config.items() if k != "type"} - self.schedulers[param_name] = scheduler(self.optimizers[param_name], **kwargs) + for param_group_name, params in param_groups.items(): + lr_init = config[param_group_name].optimizer.lr + self.optimizers[param_group_name] = instantiate(config[param_group_name].optimizer, params=params) + if config[param_group_name].scheduler: + self.schedulers[param_group_name] = instantiate( + config[param_group_name].scheduler, optimizer=self.optimizers[param_group_name], lr_init=lr_init + ) - def optimizer_step(self, param_name: str) -> None: + def optimizer_step(self, param_group_name: str) -> None: """fetch and step corresponding optimizer Args: - param_name (str): name of optimizer to step forward + param_group_name (str): name of optimizer to step forward """ - self.optimizers[param_name].step() + self.optimizers[param_group_name].step() - def scheduler_step(self, param_name: str) -> None: + def scheduler_step(self, param_group_name: str) -> None: """fetch and step corresponding scheduler Args: - param_name (str): name of scheduler to step forward + param_group_name (str): name of scheduler to step forward """ - if self.config.param_name.scheduler: - self.schedulers[param_name].step() + if self.config.param_group_name.scheduler: + self.schedulers[param_group_name].step() def zero_grad_all(self): """zero the gradients for all optimizer parameters""" @@ -62,4 +79,8 @@ class Optimizers: def scheduler_step_all(self, step): """Run step for all schedulers.""" - # TODO: this needs to be implemented + for param_group_name, scheduler in self.schedulers.items(): + scheduler.step() + # TODO(ethan): clean this up. why is there indexing into a list? + lr = scheduler.get_last_lr()[0] + writer.write_event({"name": f"{param_group_name}", "scalar": lr, "step": step, "group": "learning_rate"}) diff --git a/mattport/nerf/sampler.py b/mattport/nerf/sampler.py index 0b84ac2f6..70f1b94c0 100644 --- a/mattport/nerf/sampler.py +++ b/mattport/nerf/sampler.py @@ -13,7 +13,7 @@ from mattport.structures.rays import RayBundle, RaySamples class UniformSampler(nn.Module): """Sample uniformly along a ray""" - def __init__(self, near_plane: float, far_plane: float, num_samples: int, train_stratified=True) -> None: + def __init__(self, num_samples: int, train_stratified=True) -> None: """ Args: near_plane (float): Minimum distance along ray to sample @@ -23,8 +23,6 @@ class UniformSampler(nn.Module): """ super().__init__() - self.near_plane = near_plane - self.far_plane = far_plane self.num_samples = num_samples self.train_stratified = train_stratified @@ -32,36 +30,33 @@ class UniformSampler(nn.Module): def forward( self, ray_bundle: RayBundle, - near_plane: Optional[float] = None, - far_plane: Optional[float] = None, num_samples: Optional[int] = None, ) -> RaySamples: """Generates position samples uniformly. Args: ray_bundle (RayBundle): Rays to generate samples for - TensorType[..., "output_dim"]: A encoded input tensor - near_plane (Optional[float]): Minimum distance along ray to sample - far_plane (Optional[float]): Maximum distance along ray to sample num_samples (Optional[int]): Number of samples per ray Returns: RaySamples: Positions and deltas for samples along a ray """ - near_plane = near_plane or self.near_plane - far_plane = far_plane or self.far_plane - num_samples = num_samples or self.num_samples + assert ray_bundle.nears is not None + assert ray_bundle.fars is not None + num_samples = num_samples or self.num_samples num_rays = ray_bundle.origins.shape[0] - bins = torch.linspace(near_plane, far_plane, num_samples + 1).to(ray_bundle.origins.device) # (num_samples+1) + bins = torch.linspace(0.0, 1.0, num_samples + 1).to(ray_bundle.origins.device) # shape (num_samples+1,) + bins = ray_bundle.nears[:, None] + bins[None, :] * ( + ray_bundle.fars[:, None] - ray_bundle.nears[:, None] + ) # shape (num_rays, num_samples+1) if self.train_stratified and self.training: t_rand = torch.rand((num_rays, num_samples), dtype=bins.dtype, device=bins.device) - ts = bins[None, :-1] + t_rand * (bins[None, 1:] - bins[None, :-1]) # (num_rays, num_samples) + ts = bins[:, :-1] + t_rand * (bins[:, 1:] - bins[:, :-1]) # shape (num_rays, num_samples) else: - ts = (bins[1:] + bins[:-1]) / 2 - ts = ts.unsqueeze(0).repeat(num_rays, 1) # (num_rays, num_samples) + ts = (bins[:, 1:] + bins[:, :-1]) / 2 ray_samples = RaySamples( ts=ts, diff --git a/mattport/nerf/trainer.py b/mattport/nerf/trainer.py index 0d9bd9c76..6a04b91fa 100644 --- a/mattport/nerf/trainer.py +++ b/mattport/nerf/trainer.py @@ -1,21 +1,22 @@ """ Code to train model. """ +import copy import logging import os +from pydoc import locate from time import time from typing import Dict import torch import torch.distributed as dist -from hydra.utils import instantiate from omegaconf import DictConfig from torch.nn.parallel import DistributedDataParallel as DDP from torch.utils.data import DataLoader from mattport.nerf.dataset.collate import CollateIterDataset, collate_batch_size_one from mattport.nerf.dataset.image_dataset import ImageDataset, collate_batch -from mattport.nerf.dataset.utils import DatasetInputs, get_dataset_inputs_dict +from mattport.nerf.dataset.utils import DatasetInputs, get_dataset_inputs from mattport.nerf.optimizers import Optimizers from mattport.utils import profiler, stats_tracker, writer from mattport.utils.decorators import check_main_thread @@ -55,12 +56,18 @@ class Trainer: profiler.setup_profiler(config.logging) self.device = "cpu" if self.world_size == 0 else f"cuda:{self.local_rank}" - @profiler.time_function def setup(self, test_mode=False): """Setup the Trainer by calling other setup functions.""" - dataset_inputs_dict = get_dataset_inputs_dict(**self.config.data.dataset) - self.setup_datasets(dataset_inputs_dict, test_mode=test_mode) - self.setup_graph(dataset_inputs_dict["train"]) + dataset_inputs_train = get_dataset_inputs(**self.config.data.dataset, split="train") + if test_mode: + dataset_inputs_eval = get_dataset_inputs(**self.config.data.dataset, split="test") + else: + config_data_dataset_val = copy.deepcopy(self.config.data.dataset) + config_data_dataset_val.downscale_factor = self.config.data.val_downscale_factor + dataset_inputs_eval = get_dataset_inputs(**config_data_dataset_val, split="val") + self.setup_dataset_train(dataset_inputs_train) + self.setup_dataset_eval(dataset_inputs_eval) + self.setup_graph(dataset_inputs_train) def collate_fn(self, batch_list): """TODO(ethan): I need to replace this. @@ -68,43 +75,39 @@ class Trainer: """ return collate_batch(batch_list, self.config.data.dataloader.num_rays_per_batch, keep_full_image=False) - def load_test_dataset(self, dataset_inputs_dict, mode): + @profiler.time_function + def setup_dataset_eval(self, dataset_inputs: DatasetInputs): """Helper method to load test or val dataset based on test/train mode""" self.val_image_dataset = ImageDataset( - image_filenames=dataset_inputs_dict[mode].image_filenames, - downscale_factor=dataset_inputs_dict[mode].downscale_factor, - alpha_color=dataset_inputs_dict[mode].alpha_color, + image_filenames=dataset_inputs.image_filenames, + downscale_factor=dataset_inputs.downscale_factor, + alpha_color=dataset_inputs.alpha_color, ) - self.val_image_intrinsics = dataset_inputs_dict[mode].intrinsics - self.val_image_camera_to_world = dataset_inputs_dict[mode].camera_to_world + self.val_image_intrinsics = dataset_inputs.intrinsics + self.val_image_camera_to_world = dataset_inputs.camera_to_world @profiler.time_function - def setup_datasets(self, dataset_inputs_dict: Dict[str, DatasetInputs], test_mode: bool): + def setup_dataset_train(self, dataset_inputs: DatasetInputs): """_summary_""" - if test_mode: - # TODO(change "val" to "test") - self.load_test_dataset(dataset_inputs_dict, "val") - else: - self.train_image_dataset = ImageDataset( - image_filenames=dataset_inputs_dict["train"].image_filenames, - downscale_factor=dataset_inputs_dict["train"].downscale_factor, - semantics=dataset_inputs_dict["train"].semantics, - alpha_color=dataset_inputs_dict["train"].alpha_color, - ) - self.train_dataset = CollateIterDataset( - self.train_image_dataset, - collate_fn=self.collate_fn, - num_samples_to_collate=self.config.data.dataloader.num_images_to_sample_from, - num_times_to_repeat=self.config.data.dataloader.num_times_to_repeat_images, - ) - self.train_dataloader = DataLoader( - self.train_dataset, - batch_size=1, - num_workers=self.config.data.dataloader.num_workers, - collate_fn=collate_batch_size_one, - pin_memory=True, - ) - self.load_test_dataset(dataset_inputs_dict, "val") + self.train_image_dataset = ImageDataset( + image_filenames=dataset_inputs.image_filenames, + downscale_factor=dataset_inputs.downscale_factor, + semantics=dataset_inputs.semantics, + alpha_color=dataset_inputs.alpha_color, + ) + self.train_dataset = CollateIterDataset( + self.train_image_dataset, + collate_fn=self.collate_fn, + num_samples_to_collate=self.config.data.dataloader.num_images_to_sample_from, + num_times_to_repeat=self.config.data.dataloader.num_times_to_repeat_images, + ) + self.train_dataloader = DataLoader( + self.train_dataset, + batch_size=1, + num_workers=self.config.data.dataloader.num_workers, + collate_fn=collate_batch_size_one, + pin_memory=True, + ) @profiler.time_function def setup_graph(self, dataset_inputs: DatasetInputs): @@ -113,12 +116,18 @@ class Trainer: Args: dataset_inputs (DatasetInputs): The inputs which will be used to define the camera parameters. """ - self.graph = instantiate( - self.config.graph.network, + # hydra instantiate fails because of scene_bounds, so we are doing the following instead + kwargs = {k: v for k, v in self.config.graph.network.items() if k != "_target_"} + graph_class = locate(self.config.graph.network._target_) # pylint: disable=protected-access + self.graph = graph_class( + **kwargs, intrinsics=dataset_inputs.intrinsics, camera_to_world=dataset_inputs.camera_to_world, + scene_bounds=dataset_inputs.scene_bounds, stuff_classes=dataset_inputs.semantics.stuff_classes, - ).to(self.device) + ) + self.graph.to(self.device) + self.setup_optimizers() # NOTE(ethan): can this be before DDP? if self.config.graph.resume_train.load_dir: @@ -223,7 +232,7 @@ class Trainer: if step != 0 and self.config.graph.steps_per_save and step % self.config.graph.steps_per_save == 0: self.save_checkpoint(self.config.graph.model_dir, step) if step % self.config.graph.steps_per_test == 0: # NOTE(ethan): we should still run this in dry-run mode! - for image_idx in self.config.data.validation_image_indices: + for image_idx in self.config.data.val_image_indices: _ = self.test_image(image_idx=image_idx, step=step) stats_tracker.print_stats(i / num_iterations) @@ -241,8 +250,8 @@ class Trainer: loss = loss_dict["aggregated_loss"] self.optimizers.zero_grad_all() loss.backward() - self.optimizers.scheduler_step_all(step) # NOTE(ethan): I think the scheduler needs to know what step we are on self.optimizers.optimizer_step_all() + self.optimizers.scheduler_step_all(step) return loss_dict @profiler.time_function diff --git a/mattport/structures/rays.py b/mattport/structures/rays.py index d567cc2f2..8f5697a6d 100644 --- a/mattport/structures/rays.py +++ b/mattport/structures/rays.py @@ -8,6 +8,8 @@ import torch from torchtyping import TensorType +from mattport.utils.misc import is_not_none + @dataclass class RayBundle: @@ -20,6 +22,9 @@ class RayBundle: origins: TensorType["num_rays", 3] directions: TensorType["num_rays", 3] camera_indices: Optional[TensorType["num_rays"]] = None + nears: Optional[TensorType["num_rays"]] = None + fars: Optional[TensorType["num_rays"]] = None + valid_mask: Optional[TensorType["num_rays"]] = None def to_camera_ray_bundle(self, image_height, image_width) -> "CameraRayBundle": """Returns a CameraRayBundle from this object.""" @@ -60,6 +65,17 @@ class RayBundle: camera_indices=self.camera_indices[indices], ) + def get_masked_ray_bundle(self, valid_mask): + """Return a masked instance of the ray bundle.""" + return RayBundle( + origins=self.origins[valid_mask], + directions=self.directions[valid_mask], + camera_indices=self.camera_indices[valid_mask] if is_not_none(self.camera_indices) else None, + nears=self.nears[valid_mask] if is_not_none(self.nears) else None, + fars=self.fars[valid_mask] if is_not_none(self.fars) else None, + valid_mask=self.valid_mask[valid_mask] if is_not_none(self.valid_mask) else None, + ) + @dataclass class CameraRayBundle: diff --git a/mattport/utils/io.py b/mattport/utils/io.py index cc4f576ef..445a0c3fe 100644 --- a/mattport/utils/io.py +++ b/mattport/utils/io.py @@ -51,6 +51,15 @@ def get_git_root(path, dirs=(".git",), default=None): return default +def get_project_root(): + """Return the project root directory from an environment variable. + # TODO: handle this better and report user error + """ + project_root = os.getenv("PROJECT_ROOT") + assert project_root is not None, "Please set PROJECT_ROOT to the root directory of this repo." + return project_root + + def get_absolute_path(path, proj_root_func=get_git_root): """ Returns the full, absolute path. diff --git a/mattport/utils/misc.py b/mattport/utils/misc.py index 25bdefe22..e1f90aa5d 100644 --- a/mattport/utils/misc.py +++ b/mattport/utils/misc.py @@ -37,3 +37,17 @@ def get_dict_to_cpu(stuff): if isinstance(stuff, torch.Tensor): return stuff.detach().cpu() return stuff + + +def is_not_none(var): + """Return True if the variable var is None.""" + return not isinstance(var, type(None)) + + +def get_masked_dict(d, mask): + """Return a masked dictionary. + TODO(ethan): add more asserts/checks so this doesn't have unpredictable behavior.""" + masked_dict = {} + for key, value in d.items(): + masked_dict[key] = value[mask] + return masked_dict diff --git a/mattport/utils/profiler.py b/mattport/utils/profiler.py index 02ecded9a..a2b4987dd 100644 --- a/mattport/utils/profiler.py +++ b/mattport/utils/profiler.py @@ -2,7 +2,6 @@ Profiler base class and functionality """ import logging -import sys import time from typing import Callable @@ -21,11 +20,7 @@ def time_function(func: Callable) -> Callable: start = time.time() ret = func(*args, **kwargs) if PROFILER: - vals = vars(sys.modules[func.__module__]) - class_str = "" - for attr in func.__qualname__.split(".")[:-1]: - class_str += f"{vals[attr].__qualname__}_" - class_str += func.__name__ + class_str = func.__qualname__ PROFILER[0].update_time(class_str, start, time.time()) return ret diff --git a/notebooks/visualize_dataset.ipynb b/notebooks/visualize_dataset.ipynb index 0ed5072d5..2623caee3 100644 --- a/notebooks/visualize_dataset.ipynb +++ b/notebooks/visualize_dataset.ipynb @@ -10,7 +10,7 @@ }, { "cell_type": "code", - "execution_count": 1, + "execution_count": null, "id": "b15dfdc1-5054-40bc-9631-4c7426eecfd9", "metadata": {}, "outputs": [], @@ -22,8 +22,9 @@ "\n", "from mattport.nerf.dataset.image_dataset import ImageDataset, collate_batch\n", "from mattport.nerf.dataset.collate import CollateIterDataset, collate_batch_size_one\n", - "from mattport.nerf.dataset.utils import get_dataset_inputs_dict\n", + "from mattport.nerf.dataset.utils import get_dataset_inputs\n", "from mattport.nerf.field_modules.ray_generator import RayGenerator\n", + "from mattport.nerf.field_modules.scene_bounds_collider import SceneBoundsCollider\n", "from mattport.structures.rays import RayBundle\n", "from mattport.utils.io import get_absolute_path\n", "from mattport.viewer.plotly import get_line_segments_from_lines\n", @@ -39,19 +40,10 @@ }, { "cell_type": "code", - "execution_count": 2, + "execution_count": null, "id": "012bf657", "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "The autoreload extension is already loaded. To reload it, use:\n", - " %reload_ext autoreload\n" - ] - } - ], + "outputs": [], "source": [ "import goat\n", "vis = goat.vis_utils.get_vis()" @@ -59,7 +51,7 @@ }, { "cell_type": "code", - "execution_count": 3, + "execution_count": null, "id": "71f46c79", "metadata": {}, "outputs": [], @@ -69,91 +61,14 @@ }, { "cell_type": "code", - "execution_count": 4, + "execution_count": null, "id": "7fe7478d", "metadata": {}, "outputs": [], "source": [ "with initialize(config_path=\"../configs\"):\n", - " # cfg = compose(config_name=\"default.yaml\")\n", - " cfg = compose(config_name=\"default.yaml\", overrides=[\"data/dataset=friends_TBBT-big_living_room\"])" - ] - }, - { - "cell_type": "code", - "execution_count": 5, - "id": "ad63e53a", - "metadata": {}, - "outputs": [], - "source": [ - "import os\n", - "from mattport.nerf.dataset.colmap_utils import read_cameras_binary, read_images_binary, read_pointsTD_binary" - ] - }, - { - "cell_type": "code", - "execution_count": 13, - "id": "01c42863", - "metadata": {}, - "outputs": [], - "source": [ - "from mattport.nerf.dataset.friends import load_friends_data" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "b097e5eb", - "metadata": {}, - "outputs": [], - "source": [] - }, - { - "cell_type": "code", - "execution_count": 40, - "id": "c5516443", - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "[2930.31056439 960. 544. ]\n", - "[4466.94830951 1024. 574. ]\n" - ] - } - ], - "source": [ - "dataset_inputs = load_friends_data(\"/shared/ethanweber/cartoons/data/colmap/smith_house_pair_manual_after\", include_point_cloud=True)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "eba07414", - "metadata": {}, - "outputs": [], - "source": [] - }, - { - "cell_type": "code", - "execution_count": 24, - "id": "8ff323ed", - "metadata": {}, - "outputs": [], - "source": [ - "imdata = read_images_binary(os.path.join(\"/shared/ethanweber/mattport/data/friends/TBBT-big_living_room\", \"colmap\", \"images.bin\"))" - ] - }, - { - "cell_type": "code", - "execution_count": 41, - "id": "10539dc6", - "metadata": {}, - "outputs": [], - "source": [ - "# cfg.data.dataset.data_directory = get_absolute_path(cfg.data.dataset.data_directory)\n", - "# dataset_inputs = get_dataset_inputs_dict(**cfg.data.dataset, splits=[\"train\"])[\"train\"]" + " cfg = compose(config_name=\"default.yaml\")\n", + " # cfg = compose(config_name=\"default.yaml\", overrides=[\"data/dataset=friends_TBBT-big_living_room\"])" ] }, { @@ -162,39 +77,33 @@ "id": "0b010534", "metadata": {}, "outputs": [], - "source": [] - }, - { - "cell_type": "code", - "execution_count": 46, - "id": "e95073a9", - "metadata": {}, - "outputs": [], "source": [ - "import goat.threedvis.meshcat.geometry as g\n", - "import numpy as np\n", - "point_cloud = dataset_inputs.point_cloud\n", - "size = 0.1\n", - "vis[\"point_cloud\"].set_object(\n", - " g.PointCloud(position=np.transpose(np.array(point_cloud.xyz)),\n", - " color=np.transpose(np.array(point_cloud.rgb)) / 255.0, size=size)\n", - ")" + "dataset_inputs = get_dataset_inputs(**cfg.data.dataset, split=\"train\")" ] }, { "cell_type": "code", - "execution_count": 47, + "execution_count": null, + "id": "e95073a9", + "metadata": {}, + "outputs": [], + "source": [ + "# import goat.threedvis.meshcat.geometry as g\n", + "# import numpy as np\n", + "# point_cloud = dataset_inputs.point_cloud\n", + "# size = 0.1\n", + "# vis[\"point_cloud\"].set_object(\n", + "# g.PointCloud(position=np.transpose(np.array(point_cloud.xyz)),\n", + "# color=np.transpose(np.array(point_cloud.rgb)) / 255.0, size=size)\n", + "# )" + ] + }, + { + "cell_type": "code", + "execution_count": null, "id": "aac97d4a", "metadata": {}, - "outputs": [ - { - "name": "stderr", - "output_type": "stream", - "text": [ - "100%|██████████| 2/2 [00:00<00:00, 4.15it/s]\n" - ] - } - ], + "outputs": [], "source": [ "# show dataset inputs with three.js\n", "# goat.vis_utils.show_box_test(vis)\n", @@ -225,18 +134,10 @@ }, { "cell_type": "code", - "execution_count": 8, + "execution_count": null, "id": "001bdf3b-60a0-4bb2-9f88-ae36f2036c80", "metadata": {}, - "outputs": [ - { - "name": "stderr", - "output_type": "stream", - "text": [ - " 0%| | 0/10 [00:03" - ], - "text/plain": [ - "" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], + "outputs": [], "source": [ "media.show_image(image_dataset.get_image(10))" ] }, { "cell_type": "code", - "execution_count": 10, + "execution_count": null, "id": "b90f4a78-4b8b-4615-a127-7d7579874765", "metadata": {}, "outputs": [], @@ -318,30 +206,17 @@ }, { "cell_type": "code", - "execution_count": 11, + "execution_count": null, "id": "09e625f7-fa1f-4b3f-acd4-bddfcdf7cd9f", "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "
" - ], - "text/plain": [ - "" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], + "outputs": [], "source": [ "batch = sample_and_show_batch()" ] }, { "cell_type": "code", - "execution_count": 12, + "execution_count": null, "id": "fd8cf48a-823e-4395-99ca-eda4c2026440", "metadata": {}, "outputs": [], @@ -352,7 +227,7 @@ }, { "cell_type": "code", - "execution_count": 23, + "execution_count": null, "id": "646107c0-f979-408a-941f-98b2ad3e07f7", "metadata": {}, "outputs": [], @@ -372,22 +247,43 @@ }, { "cell_type": "code", - "execution_count": 24, + "execution_count": null, + "id": "fc627f05", + "metadata": {}, + "outputs": [], + "source": [ + "scene_bounds_collider = SceneBoundsCollider(dataset_inputs.scene_bounds)\n", + "intersected_ray_bundle = scene_bounds_collider(ray_bundle)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "9ca1b3c3", + "metadata": {}, + "outputs": [], + "source": [ + "# intersected_ray_bundle.nears" + ] + }, + { + "cell_type": "code", + "execution_count": null, "id": "c73ba9bd-2902-4c46-b592-bc87ac8c71f6", "metadata": {}, "outputs": [], "source": [ - "near_plane = cfg.graph.network.near_plane\n", - "far_plane = cfg.graph.network.far_plane\n", - "lines = torch.stack([ray_bundle.origins + ray_bundle.directions * near_plane,\n", - " ray_bundle.origins + ray_bundle.directions * far_plane\n", + "# near_plane = cfg.graph.network.near_plane\n", + "# far_plane = cfg.graph.network.far_plane\n", + "lines = torch.stack([intersected_ray_bundle.origins + intersected_ray_bundle.directions * intersected_ray_bundle.nears,\n", + " intersected_ray_bundle.origins + intersected_ray_bundle.directions * intersected_ray_bundle.fars\n", " ], dim=1).tolist() # (num_rays, 2, 3)\n", "lines = torch.tensor(random.sample(lines, k=100))" ] }, { "cell_type": "code", - "execution_count": 25, + "execution_count": null, "id": "991df6a2-efe3-4412-ba41-9a244e2e90cc", "metadata": {}, "outputs": [], @@ -397,8009 +293,10 @@ }, { "cell_type": "code", - "execution_count": 26, + "execution_count": null, "id": "0b2404d8-ca9e-4630-9f67-7c68d19458cb", "metadata": {}, - "outputs": [ - { - "data": { - "application/vnd.plotly.v1+json": { - "config": { - "plotlyServerURL": "https://plot.ly" - }, - "data": [ - { - "marker": { - "color": "rgba(0, 0, 0, 1)", - "size": 8 - }, - "mode": "markers", - "name": "origins", - "type": "scatter3d", - "x": [ - -2.620957851409912, - -2.3065438270568848, - -1.3201767206192017, - 3.2671120166778564, - 0.3710319399833679, - 0.4049864411354065, - 4.826180934906006, - -0.9473855495452881, - -3.0205116271972656, - 3.922758102416992, - 1.361860990524292, - 4.85964822769165, - 1.5065038204193115, - -3.674560070037842, - 1.7150750160217285, - -3.4039230346679688, - -0.67200767993927, - -2.094449043273926, - -1.6862897872924805, - 3.1038875579833984, - -1.6938903331756592, - -1.8510231971740723, - 1.3389005661010742, - 2.562008857727051, - -2.0709762573242188, - 1.103877067565918, - -2.8057069778442383, - -1.4376013278961182, - -2.484976291656494, - -2.4179115295410156, - -1.969143271446228, - 3.150782346725464, - -1.0036602020263672, - -3.0639021396636963, - 0.7507435083389282, - -0.8690291047096252, - 1.3284640312194824, - 1.3980400562286377, - -3.7091758251190186, - 3.407508373260498, - -2.9073688983917236, - 2.8712522983551025, - 3.3745851516723633, - 3.4474329948425293, - 0.1343022882938385, - -1.0858500003814697, - 1.9163532257080078, - 3.006258249282837, - -2.099956512451172, - -1.5952181816101074, - -2.9141554832458496, - -1.5751419067382812, - -1.5284204483032227, - 0.7405332922935486, - -3.091270923614502, - -3.121455430984497, - -1.3108881711959839, - 1.566190242767334, - -1.9271529912948608, - 1.976480484008789, - -1.101997971534729, - -1.650305151939392, - 1.4763436317443848, - -4.018808364868164, - -0.5259644389152527, - -0.4001197814941406, - 1.0715441703796387, - -0.41969823837280273, - -1.703543782234192, - -3.217604637145996, - 1.6377414464950562, - 0.9316978454589844, - 2.1592748165130615, - -2.612527370452881, - 1.679912805557251, - 2.7434144020080566, - 2.713665008544922, - 0.5630910992622375, - -0.6957547664642334, - 0.4105933904647827, - 3.7846343517303467, - 3.800171136856079, - 1.6267359256744385, - 5.1108856201171875, - -0.8661962151527405, - -2.8871655464172363, - 0.5945320725440979, - -0.4169619679450989, - -1.2841501235961914, - 1.9518399238586426, - -0.10009393095970154, - -0.2565694749355316, - 0.12403451651334763, - 2.9587600231170654, - 1.7632112503051758, - 4.5645856857299805, - 4.909059524536133, - 0.41644370555877686, - 3.5382237434387207, - 3.8257718086242676, - 1.3245147466659546, - 3.758837938308716, - -1.2901984453201294, - 0.7849958539009094, - -2.9857308864593506, - 0.936840832233429, - 0.7352339029312134, - 0.7705292701721191, - 0.9413787126541138, - -0.31442952156066895, - -0.9062077403068542, - -1.9406589269638062, - 2.916642665863037, - 2.0731399059295654, - -1.2346454858779907, - 4.311000347137451, - 4.2350969314575195, - 4.695927619934082, - 0.6286726593971252, - 3.818126916885376, - 4.161957263946533, - -3.6973514556884766, - 0.35286945104599, - 0.32111644744873047, - 2.9532952308654785, - 3.3116161823272705, - 4.7901105880737305, - 4.9534807205200195, - -2.7650246620178223, - 0.023515939712524414, - -0.08924168348312378, - 4.132203102111816, - -3.3169941902160645, - -1.430415153503418, - -1.2428169250488281, - -0.9602839350700378, - 0.8066017031669617, - 5.096758842468262, - 3.4243295192718506, - 0.8318295478820801, - -3.4428513050079346, - -3.0043654441833496, - -2.150261878967285, - -1.014682650566101, - -0.9344648718833923, - -0.9566317200660706, - 1.1658564805984497, - -0.8845604062080383, - 0.9025858640670776, - -0.8809413909912109, - 0.13712775707244873, - 0.7652997970581055, - -2.1050829887390137, - -1.3460723161697388, - 3.1971683502197266, - 3.891072988510132, - -0.358493447303772, - -2.3962655067443848, - -0.13333958387374878, - 2.477949857711792, - 2.0839552879333496, - 1.4337520599365234, - 0.1493113934993744, - -4.199881553649902, - -1.4152389764785767 - ], - "y": [ - -0.02269723080098629, - -0.007217392325401306, - 0.21683987975120544, - -0.06122439727187157, - 0.02551107667386532, - 0.05321834236383438, - -0.22067244350910187, - 0.25079548358917236, - -0.0777716189622879, - -0.13868878781795502, - -0.06678775697946548, - -0.2136428952217102, - -0.2517271041870117, - -0.06253424286842346, - -0.12914849817752838, - 0.08952926099300385, - 0.273040771484375, - 0.35393786430358887, - 0.2592128813266754, - -0.24914368987083435, - 0.21290244162082672, - 0.2601046562194824, - 0.3141436278820038, - -0.020961087197065353, - 0.39360663294792175, - 0.1447618007659912, - 0.28828945755958557, - 0.31241458654403687, - 0.36017167568206787, - 0.26626187562942505, - 0.23621948063373566, - -0.21083968877792358, - 0.16181844472885132, - 0.043499767780303955, - 0.12156285345554352, - 0.34818994998931885, - 0.0889679566025734, - 0.06825277954339981, - -0.038001418113708496, - -0.03809872642159462, - 0.18523260951042175, - -0.08598195016384125, - -0.02564994990825653, - -0.08750346302986145, - -0.06744293123483658, - 0.04242028295993805, - -0.007192827761173248, - -0.08793851733207703, - 0.10602737963199615, - 0.1460573822259903, - 0.22212782502174377, - 0.11642644554376602, - 0.10535178333520889, - 0.0066524893045425415, - 0.25616663694381714, - 0.2738812267780304, - -0.21472252905368805, - 0.1279269903898239, - 0.18506981432437897, - 0.016538791358470917, - 0.1003207117319107, - 0.15850825607776642, - 0.17389647662639618, - 0.14783470332622528, - 0.25039857625961304, - 0.24107114970684052, - 0.0804978758096695, - 0.24512138962745667, - 0.29673323035240173, - 0.2719915509223938, - -0.1328202486038208, - -0.06810294091701508, - -0.051776885986328125, - 0.2780889570713043, - 0.030238378793001175, - 0.00009394437074661255, - 0.0035497136414051056, - 0.00658223545178771, - -0.024308204650878906, - 0.02515861578285694, - -0.07199440896511078, - -0.07394362986087799, - 0.04916439205408096, - -0.15977804362773895, - 0.04948298633098602, - 0.17402295768260956, - 0.03083505854010582, - 0.0303938165307045, - 0.08235487341880798, - -0.09861154109239578, - 0.02883356809616089, - -0.050524357706308365, - 0.16528648138046265, - -0.14075598120689392, - -0.12077564746141434, - -0.3628769516944885, - -0.41275396943092346, - -0.09400872886180878, - -0.17872187495231628, - -0.20870640873908997, - -0.0996347963809967, - -0.21469084918498993, - 0.10958370566368103, - 0.15271781384944916, - -0.03783717751502991, - -0.002108961343765259, - 0.20103159546852112, - 0.09026075899600983, - 0.0506536141037941, - 0.18152305483818054, - 0.13792148232460022, - 0.11439946293830872, - -0.4924408197402954, - -0.054306816309690475, - 0.24151383340358734, - 0.010649655014276505, - -0.03100436180830002, - -0.25425511598587036, - -0.09674733132123947, - -0.12923409044742584, - -0.12281599640846252, - 0.15590913593769073, - -0.11266174167394638, - -0.10231234133243561, - -0.1753341406583786, - -0.2106318324804306, - -0.22799140214920044, - -0.22186097502708435, - -0.008078809827566147, - 0.01734292507171631, - 0.0024195071309804916, - -0.4445480704307556, - 0.04546790570020676, - 0.08874533325433731, - 0.04983121156692505, - 0.0713605135679245, - 0.22153843939304352, - -0.1405496448278427, - -0.06161907687783241, - -0.28465574979782104, - 0.328725129365921, - 0.317128986120224, - 0.25215595960617065, - 0.3592537045478821, - 0.22900663316249847, - 0.2263166904449463, - -0.008280416950583458, - 0.13492918014526367, - -0.4833546578884125, - 0.12935712933540344, - -0.465570330619812, - -0.822230339050293, - 0.15049239993095398, - 0.15180951356887817, - 0.28338778018951416, - -0.32651835680007935, - -0.7699631452560425, - -0.6617442965507507, - -0.038882672786712646, - -0.06576817482709885, - -0.055319830775260925, - 0.029805634170770645, - 0.06506314873695374, - 0.03998251259326935, - 0.02849458158016205 - ], - "z": [ - -0.6259534358978271, - -2.0112080574035645, - -1.6169897317886353, - -0.018804995343089104, - -1.398890495300293, - -1.5020315647125244, - -0.06478672474622726, - -1.2854127883911133, - 0.26107773184776306, - 0.48101526498794556, - -0.6294559240341187, - -0.07973085343837738, - 2.2341413497924805, - 2.959912061691284, - 0.09198955446481705, - 0.6389332413673401, - 1.7146213054656982, - -1.5720854997634888, - -1.1691135168075562, - 1.4464598894119263, - -1.0655180215835571, - -1.4064373970031738, - -0.5899629592895508, - -0.7430453300476074, - -1.448909044265747, - -0.3947642147541046, - -1.395993947982788, - -1.5644348859786987, - -1.4415491819381714, - -1.4967446327209473, - -1.4168566465377808, - 0.11062050610780716, - -1.5025733709335327, - 2.180180788040161, - -1.2096606492996216, - -1.2222822904586792, - -0.6224474310874939, - -0.6229749321937561, - 2.6515817642211914, - 0.22910255193710327, - -1.9885410070419312, - 0.4994273781776428, - 0.29146718978881836, - 0.15315395593643188, - -0.6983036398887634, - -1.2735430002212524, - -1.182043433189392, - -0.3822988271713257, - -1.5829613208770752, - -1.7921134233474731, - -1.9087226390838623, - -1.5780413150787354, - -1.7105379104614258, - -1.8278754949569702, - -0.8935827016830444, - -0.9839081168174744, - 2.2705588340759277, - -0.8218976259231567, - -1.415997862815857, - 0.11395318806171417, - -1.368291974067688, - -1.434346079826355, - -0.965854287147522, - 0.0329008549451828, - -1.0497677326202393, - -1.146047592163086, - -0.668433666229248, - -1.1488443613052368, - -1.3651847839355469, - -1.6926286220550537, - -0.3662630021572113, - -0.5354233384132385, - -0.7429128885269165, - -1.333686351776123, - -0.4357435405254364, - -0.26990756392478943, - -0.22622956335544586, - -0.7744665741920471, - 2.3329436779022217, - -0.8853849768638611, - -0.17051385343074799, - -0.18387442827224731, - -0.722726047039032, - -0.22386765480041504, - -1.0865975618362427, - -1.1870149374008179, - -0.6184709668159485, - -1.3269069194793701, - -1.7547537088394165, - -0.4397783875465393, - -1.301986575126648, - -0.323600709438324, - -0.965795636177063, - 0.11810342967510223, - -0.2080032229423523, - 1.70197594165802, - 1.5080652236938477, - -0.321573406457901, - 0.5963672995567322, - 0.15875357389450073, - -0.42475417256355286, - 0.23752571642398834, - -1.376319408416748, - -1.7711848020553589, - -0.5854673981666565, - -0.5635768175125122, - -0.6070570349693298, - -0.9656419157981873, - -0.8695501089096069, - -1.8131901025772095, - -1.413840889930725, - -1.1244473457336426, - 2.686849594116211, - -0.2676960527896881, - -1.2912307977676392, - -1.3466274738311768, - -1.1066513061523438, - 0.9939570426940918, - -0.07081498950719833, - 0.005207570735365152, - -0.04425918683409691, - 1.2914749383926392, - 1.838129997253418, - 1.8501781225204468, - 0.8168551325798035, - 0.6904395818710327, - 0.017272477969527245, - -0.135447159409523, - -1.1490519046783447, - -1.148095965385437, - -1.0472012758255005, - 1.8627870082855225, - -1.1610256433486938, - -1.4728754758834839, - -1.2383031845092773, - -1.6193757057189941, - -0.3687993288040161, - -0.08464117348194122, - -0.498749315738678, - 1.8428467512130737, - -1.3862802982330322, - -1.5068855285644531, - -1.3503378629684448, - -1.4887340068817139, - -0.6515887379646301, - -0.6726614832878113, - 0.049496639519929886, - -0.7778870463371277, - 3.4374642372131348, - -0.7840431928634644, - 3.31127667427063, - 4.327725887298584, - -0.8048294186592102, - -0.824744701385498, - 1.743438959121704, - 1.137464165687561, - 3.8119280338287354, - 6.3555145263671875, - -0.7761297225952148, - -0.4843476712703705, - -0.1734239161014557, - -0.48877382278442383, - -0.7759600281715393, - -0.8362661600112915, - -1.2391544580459595 - ] - }, - { - "line": { - "color": "rgb(255, 0, 0)", - "width": 10 - }, - "marker": { - "color": "rgb(255, 0, 0)", - "colorscale": [ - [ - 0, - "#440154" - ], - [ - 0.1111111111111111, - "#482878" - ], - [ - 0.2222222222222222, - "#3e4989" - ], - [ - 0.3333333333333333, - "#31688e" - ], - [ - 0.4444444444444444, - "#26828e" - ], - [ - 0.5555555555555556, - "#1f9e89" - ], - [ - 0.6666666666666666, - "#35b779" - ], - [ - 0.7777777777777778, - "#6ece58" - ], - [ - 0.8888888888888888, - "#b5de2b" - ], - [ - 1, - "#fde725" - ] - ], - "size": 4 - }, - "showlegend": false, - "type": "scatter3d", - "x": [ - 3.6154143810272217, - -1.130441665649414 - ], - "y": [ - -0.18036767840385437, - 0.7321786284446716 - ], - "z": [ - 1.9584215879440308, - 3.240650177001953 - ] - }, - { - "line": { - "color": "rgb(255, 0, 0)", - "width": 10 - }, - "marker": { - "color": "rgb(255, 0, 0)", - "colorscale": [ - [ - 0, - "#440154" - ], - [ - 0.1111111111111111, - "#482878" - ], - [ - 0.2222222222222222, - "#3e4989" - ], - [ - 0.3333333333333333, - "#31688e" - ], - [ - 0.4444444444444444, - "#26828e" - ], - [ - 0.5555555555555556, - "#1f9e89" - ], - [ - 0.6666666666666666, - "#35b779" - ], - [ - 0.7777777777777778, - "#6ece58" - ], - [ - 0.8888888888888888, - "#b5de2b" - ], - [ - 1, - "#fde725" - ] - ], - "size": 4 - }, - "showlegend": false, - "type": "scatter3d", - "x": [ - 3.5910754203796387, - -1.2764759063720703 - ], - "y": [ - -0.14492997527122498, - 0.944804847240448 - ], - "z": [ - 1.771091103553772, - 2.116666793823242 - ] - }, - { - "line": { - "color": "rgb(255, 0, 0)", - "width": 10 - }, - "marker": { - "color": "rgb(255, 0, 0)", - "colorscale": [ - [ - 0, - "#440154" - ], - [ - 0.1111111111111111, - "#482878" - ], - [ - 0.2222222222222222, - "#3e4989" - ], - [ - 0.3333333333333333, - "#31688e" - ], - [ - 0.4444444444444444, - "#26828e" - ], - [ - 0.5555555555555556, - "#1f9e89" - ], - [ - 0.6666666666666666, - "#35b779" - ], - [ - 0.7777777777777778, - "#6ece58" - ], - [ - 0.8888888888888888, - "#b5de2b" - ], - [ - 1, - "#fde725" - ] - ], - "size": 4 - }, - "showlegend": false, - "type": "scatter3d", - "x": [ - 3.7927937507629395, - 3.755906105041504 - ], - "y": [ - 0.14729002118110657, - 1.2534582614898682 - ], - "z": [ - 0.7913184762001038, - 5.667282581329346 - ] - }, - { - "line": { - "color": "rgb(255, 0, 0)", - "width": 10 - }, - "marker": { - "color": "rgb(255, 0, 0)", - "colorscale": [ - [ - 0, - "#440154" - ], - [ - 0.1111111111111111, - "#482878" - ], - [ - 0.2222222222222222, - "#3e4989" - ], - [ - 0.3333333333333333, - "#31688e" - ], - [ - 0.4444444444444444, - "#26828e" - ], - [ - 0.5555555555555556, - "#1f9e89" - ], - [ - 0.6666666666666666, - "#35b779" - ], - [ - 0.7777777777777778, - "#6ece58" - ], - [ - 0.8888888888888888, - "#b5de2b" - ], - [ - 1, - "#fde725" - ] - ], - "size": 4 - }, - "showlegend": false, - "type": "scatter3d", - "x": [ - -1.6169376373291016, - -1.2701771259307861 - ], - "y": [ - 0.3978783190250397, - 1.0912054777145386 - ], - "z": [ - -0.18120545148849487, - 4.758334636688232 - ] - }, - { - "line": { - "color": "rgb(255, 0, 0)", - "width": 10 - }, - "marker": { - "color": "rgb(255, 0, 0)", - "colorscale": [ - [ - 0, - "#440154" - ], - [ - 0.1111111111111111, - "#482878" - ], - [ - 0.2222222222222222, - "#3e4989" - ], - [ - 0.3333333333333333, - "#31688e" - ], - [ - 0.4444444444444444, - "#26828e" - ], - [ - 0.5555555555555556, - "#1f9e89" - ], - [ - 0.6666666666666666, - "#35b779" - ], - [ - 0.7777777777777778, - "#6ece58" - ], - [ - 0.8888888888888888, - "#b5de2b" - ], - [ - 1, - "#fde725" - ] - ], - "size": 4 - }, - "showlegend": false, - "type": "scatter3d", - "x": [ - -2.318216562271118, - -1.4844176769256592 - ], - "y": [ - 0.2501121163368225, - -0.30018579959869385 - ], - "z": [ - -0.46171337366104126, - 4.437465667724609 - ] - }, - { - "line": { - "color": "rgb(255, 0, 0)", - "width": 10 - }, - "marker": { - "color": "rgb(255, 0, 0)", - "colorscale": [ - [ - 0, - "#440154" - ], - [ - 0.1111111111111111, - "#482878" - ], - [ - 0.2222222222222222, - "#3e4989" - ], - [ - 0.3333333333333333, - "#31688e" - ], - [ - 0.4444444444444444, - "#26828e" - ], - [ - 0.5555555555555556, - "#1f9e89" - ], - [ - 0.6666666666666666, - "#35b779" - ], - [ - 0.7777777777777778, - "#6ece58" - ], - [ - 0.8888888888888888, - "#b5de2b" - ], - [ - 1, - "#fde725" - ] - ], - "size": 4 - }, - "showlegend": false, - "type": "scatter3d", - "x": [ - -2.3784866333007812, - -1.8460384607315063 - ], - "y": [ - 0.07468342781066895, - -1.3527578115463257 - ], - "z": [ - -0.4891015887260437, - 4.273136138916016 - ] - }, - { - "line": { - "color": "rgb(255, 0, 0)", - "width": 10 - }, - "marker": { - "color": "rgb(255, 0, 0)", - "colorscale": [ - [ - 0, - "#440154" - ], - [ - 0.1111111111111111, - "#482878" - ], - [ - 0.2222222222222222, - "#3e4989" - ], - [ - 0.3333333333333333, - "#31688e" - ], - [ - 0.4444444444444444, - "#26828e" - ], - [ - 0.5555555555555556, - "#1f9e89" - ], - [ - 0.6666666666666666, - "#35b779" - ], - [ - 0.7777777777777778, - "#6ece58" - ], - [ - 0.8888888888888888, - "#b5de2b" - ], - [ - 1, - "#fde725" - ] - ], - "size": 4 - }, - "showlegend": false, - "type": "scatter3d", - "x": [ - 4.121050834655762, - 5.725449562072754 - ], - "y": [ - 0.08558230102062225, - 0.8832119703292847 - ], - "z": [ - 0.7497142553329468, - 5.417657375335693 - ] - }, - { - "line": { - "color": "rgb(255, 0, 0)", - "width": 10 - }, - "marker": { - "color": "rgb(255, 0, 0)", - "colorscale": [ - [ - 0, - "#440154" - ], - [ - 0.1111111111111111, - "#482878" - ], - [ - 0.2222222222222222, - "#3e4989" - ], - [ - 0.3333333333333333, - "#31688e" - ], - [ - 0.4444444444444444, - "#26828e" - ], - [ - 0.5555555555555556, - "#1f9e89" - ], - [ - 0.6666666666666666, - "#35b779" - ], - [ - 0.7777777777777778, - "#6ece58" - ], - [ - 0.8888888888888888, - "#b5de2b" - ], - [ - 1, - "#fde725" - ] - ], - "size": 4 - }, - "showlegend": false, - "type": "scatter3d", - "x": [ - 3.455401659011841, - 1.7315537929534912 - ], - "y": [ - -0.277576208114624, - -1.295738935470581 - ], - "z": [ - 0.7324594855308533, - 5.314128875732422 - ] - }, - { - "line": { - "color": "rgb(255, 0, 0)", - "width": 10 - }, - "marker": { - "color": "rgb(255, 0, 0)", - "colorscale": [ - [ - 0, - "#440154" - ], - [ - 0.1111111111111111, - "#482878" - ], - [ - 0.2222222222222222, - "#3e4989" - ], - [ - 0.3333333333333333, - "#31688e" - ], - [ - 0.4444444444444444, - "#26828e" - ], - [ - 0.5555555555555556, - "#1f9e89" - ], - [ - 0.6666666666666666, - "#35b779" - ], - [ - 0.7777777777777778, - "#6ece58" - ], - [ - 0.8888888888888888, - "#b5de2b" - ], - [ - 1, - "#fde725" - ] - ], - "size": 4 - }, - "showlegend": false, - "type": "scatter3d", - "x": [ - -0.9037959575653076, - -2.7929537296295166 - ], - "y": [ - 0.24904261529445648, - 0.24226278066635132 - ], - "z": [ - -0.12389439344406128, - 4.505472183227539 - ] - }, - { - "line": { - "color": "rgb(255, 0, 0)", - "width": 10 - }, - "marker": { - "color": "rgb(255, 0, 0)", - "colorscale": [ - [ - 0, - "#440154" - ], - [ - 0.1111111111111111, - "#482878" - ], - [ - 0.2222222222222222, - "#3e4989" - ], - [ - 0.3333333333333333, - "#31688e" - ], - [ - 0.4444444444444444, - "#26828e" - ], - [ - 0.5555555555555556, - "#1f9e89" - ], - [ - 0.6666666666666666, - "#35b779" - ], - [ - 0.7777777777777778, - "#6ece58" - ], - [ - 0.8888888888888888, - "#b5de2b" - ], - [ - 1, - "#fde725" - ] - ], - "size": 4 - }, - "showlegend": false, - "type": "scatter3d", - "x": [ - -1.100243091583252, - -3.9716360569000244 - ], - "y": [ - 0.3515505790710449, - 0.8573105335235596 - ], - "z": [ - -0.23738086223602295, - 3.8245532512664795 - ] - }, - { - "line": { - "color": "rgb(255, 0, 0)", - "width": 10 - }, - "marker": { - "color": "rgb(255, 0, 0)", - "colorscale": [ - [ - 0, - "#440154" - ], - [ - 0.1111111111111111, - "#482878" - ], - [ - 0.2222222222222222, - "#3e4989" - ], - [ - 0.3333333333333333, - "#31688e" - ], - [ - 0.4444444444444444, - "#26828e" - ], - [ - 0.5555555555555556, - "#1f9e89" - ], - [ - 0.6666666666666666, - "#35b779" - ], - [ - 0.7777777777777778, - "#6ece58" - ], - [ - 0.8888888888888888, - "#b5de2b" - ], - [ - 1, - "#fde725" - ] - ], - "size": 4 - }, - "showlegend": false, - "type": "scatter3d", - "x": [ - 3.7725210189819336, - 3.6342709064483643 - ], - "y": [ - -0.16448014974594116, - -0.6171627044677734 - ], - "z": [ - 0.8116347789764404, - 5.789180755615234 - ] - }, - { - "line": { - "color": "rgb(255, 0, 0)", - "width": 10 - }, - "marker": { - "color": "rgb(255, 0, 0)", - "colorscale": [ - [ - 0, - "#440154" - ], - [ - 0.1111111111111111, - "#482878" - ], - [ - 0.2222222222222222, - "#3e4989" - ], - [ - 0.3333333333333333, - "#31688e" - ], - [ - 0.4444444444444444, - "#26828e" - ], - [ - 0.5555555555555556, - "#1f9e89" - ], - [ - 0.6666666666666666, - "#35b779" - ], - [ - 0.7777777777777778, - "#6ece58" - ], - [ - 0.8888888888888888, - "#b5de2b" - ], - [ - 1, - "#fde725" - ] - ], - "size": 4 - }, - "showlegend": false, - "type": "scatter3d", - "x": [ - -1.020065426826477, - -3.490570306777954 - ], - "y": [ - 0.4307911992073059, - 1.332754135131836 - ], - "z": [ - -0.19928401708602905, - 4.053134918212891 - ] - }, - { - "line": { - "color": "rgb(255, 0, 0)", - "width": 10 - }, - "marker": { - "color": "rgb(255, 0, 0)", - "colorscale": [ - [ - 0, - "#440154" - ], - [ - 0.1111111111111111, - "#482878" - ], - [ - 0.2222222222222222, - "#3e4989" - ], - [ - 0.3333333333333333, - "#31688e" - ], - [ - 0.4444444444444444, - "#26828e" - ], - [ - 0.5555555555555556, - "#1f9e89" - ], - [ - 0.6666666666666666, - "#35b779" - ], - [ - 0.7777777777777778, - "#6ece58" - ], - [ - 0.8888888888888888, - "#b5de2b" - ], - [ - 1, - "#fde725" - ] - ], - "size": 4 - }, - "showlegend": false, - "type": "scatter3d", - "x": [ - 3.4056546688079834, - 1.3432929515838623 - ], - "y": [ - -0.3115953207015991, - -1.223401427268982 - ], - "z": [ - 0.8977382779121399, - 5.360391616821289 - ] - }, - { - "line": { - "color": "rgb(255, 0, 0)", - "width": 10 - }, - "marker": { - "color": "rgb(255, 0, 0)", - "colorscale": [ - [ - 0, - "#440154" - ], - [ - 0.1111111111111111, - "#482878" - ], - [ - 0.2222222222222222, - "#3e4989" - ], - [ - 0.3333333333333333, - "#31688e" - ], - [ - 0.4444444444444444, - "#26828e" - ], - [ - 0.5555555555555556, - "#1f9e89" - ], - [ - 0.6666666666666666, - "#35b779" - ], - [ - 0.7777777777777778, - "#6ece58" - ], - [ - 0.8888888888888888, - "#b5de2b" - ], - [ - 1, - "#fde725" - ] - ], - "size": 4 - }, - "showlegend": false, - "type": "scatter3d", - "x": [ - -0.8130481243133545, - -2.2484664916992188 - ], - "y": [ - 0.3897104263305664, - 1.0862696170806885 - ], - "z": [ - -0.10204672813415527, - 4.636558532714844 - ] - }, - { - "line": { - "color": "rgb(255, 0, 0)", - "width": 10 - }, - "marker": { - "color": "rgb(255, 0, 0)", - "colorscale": [ - [ - 0, - "#440154" - ], - [ - 0.1111111111111111, - "#482878" - ], - [ - 0.2222222222222222, - "#3e4989" - ], - [ - 0.3333333333333333, - "#31688e" - ], - [ - 0.4444444444444444, - "#26828e" - ], - [ - 0.5555555555555556, - "#1f9e89" - ], - [ - 0.6666666666666666, - "#35b779" - ], - [ - 0.7777777777777778, - "#6ece58" - ], - [ - 0.8888888888888888, - "#b5de2b" - ], - [ - 1, - "#fde725" - ] - ], - "size": 4 - }, - "showlegend": false, - "type": "scatter3d", - "x": [ - -2.079252243041992, - -0.05063199996948242 - ], - "y": [ - 0.14982977509498596, - -0.9018796682357788 - ], - "z": [ - -0.5520862340927124, - 3.895228385925293 - ] - }, - { - "line": { - "color": "rgb(255, 0, 0)", - "width": 10 - }, - "marker": { - "color": "rgb(255, 0, 0)", - "colorscale": [ - [ - 0, - "#440154" - ], - [ - 0.1111111111111111, - "#482878" - ], - [ - 0.2222222222222222, - "#3e4989" - ], - [ - 0.3333333333333333, - "#31688e" - ], - [ - 0.4444444444444444, - "#26828e" - ], - [ - 0.5555555555555556, - "#1f9e89" - ], - [ - 0.6666666666666666, - "#35b779" - ], - [ - 0.7777777777777778, - "#6ece58" - ], - [ - 0.8888888888888888, - "#b5de2b" - ], - [ - 1, - "#fde725" - ] - ], - "size": 4 - }, - "showlegend": false, - "type": "scatter3d", - "x": [ - -2.373893976211548, - -1.8184822797775269 - ], - "y": [ - 0.20148207247257233, - -0.591965913772583 - ], - "z": [ - -0.4604893922805786, - 4.444809436798096 - ] - }, - { - "line": { - "color": "rgb(255, 0, 0)", - "width": 10 - }, - "marker": { - "color": "rgb(255, 0, 0)", - "colorscale": [ - [ - 0, - "#440154" - ], - [ - 0.1111111111111111, - "#482878" - ], - [ - 0.2222222222222222, - "#3e4989" - ], - [ - 0.3333333333333333, - "#31688e" - ], - [ - 0.4444444444444444, - "#26828e" - ], - [ - 0.5555555555555556, - "#1f9e89" - ], - [ - 0.6666666666666666, - "#35b779" - ], - [ - 0.7777777777777778, - "#6ece58" - ], - [ - 0.8888888888888888, - "#b5de2b" - ], - [ - 1, - "#fde725" - ] - ], - "size": 4 - }, - "showlegend": false, - "type": "scatter3d", - "x": [ - -1.0388758182525635, - -3.6034328937530518 - ], - "y": [ - 0.14581626653671265, - -0.3770952820777893 - ], - "z": [ - -0.19772052764892578, - 4.0625152587890625 - ] - }, - { - "line": { - "color": "rgb(255, 0, 0)", - "width": 10 - }, - "marker": { - "color": "rgb(255, 0, 0)", - "colorscale": [ - [ - 0, - "#440154" - ], - [ - 0.1111111111111111, - "#482878" - ], - [ - 0.2222222222222222, - "#3e4989" - ], - [ - 0.3333333333333333, - "#31688e" - ], - [ - 0.4444444444444444, - "#26828e" - ], - [ - 0.5555555555555556, - "#1f9e89" - ], - [ - 0.6666666666666666, - "#35b779" - ], - [ - 0.7777777777777778, - "#6ece58" - ], - [ - 0.8888888888888888, - "#b5de2b" - ], - [ - 1, - "#fde725" - ] - ], - "size": 4 - }, - "showlegend": false, - "type": "scatter3d", - "x": [ - -1.6311581134796143, - -1.3554996252059937 - ], - "y": [ - 0.16671282052993774, - -0.2957874834537506 - ], - "z": [ - -0.17492836713790894, - 4.795997142791748 - ] - }, - { - "line": { - "color": "rgb(255, 0, 0)", - "width": 10 - }, - "marker": { - "color": "rgb(255, 0, 0)", - "colorscale": [ - [ - 0, - "#440154" - ], - [ - 0.1111111111111111, - "#482878" - ], - [ - 0.2222222222222222, - "#3e4989" - ], - [ - 0.3333333333333333, - "#31688e" - ], - [ - 0.4444444444444444, - "#26828e" - ], - [ - 0.5555555555555556, - "#1f9e89" - ], - [ - 0.6666666666666666, - "#35b779" - ], - [ - 0.7777777777777778, - "#6ece58" - ], - [ - 0.8888888888888888, - "#b5de2b" - ], - [ - 1, - "#fde725" - ] - ], - "size": 4 - }, - "showlegend": false, - "type": "scatter3d", - "x": [ - 3.9389541149139404, - 4.632868766784668 - ], - "y": [ - 0.09440241754055023, - 0.9361326694488525 - ], - "z": [ - 0.7920348644256592, - 5.671581268310547 - ] - }, - { - "line": { - "color": "rgb(255, 0, 0)", - "width": 10 - }, - "marker": { - "color": "rgb(255, 0, 0)", - "colorscale": [ - [ - 0, - "#440154" - ], - [ - 0.1111111111111111, - "#482878" - ], - [ - 0.2222222222222222, - "#3e4989" - ], - [ - 0.3333333333333333, - "#31688e" - ], - [ - 0.4444444444444444, - "#26828e" - ], - [ - 0.5555555555555556, - "#1f9e89" - ], - [ - 0.6666666666666666, - "#35b779" - ], - [ - 0.7777777777777778, - "#6ece58" - ], - [ - 0.8888888888888888, - "#b5de2b" - ], - [ - 1, - "#fde725" - ] - ], - "size": 4 - }, - "showlegend": false, - "type": "scatter3d", - "x": [ - 3.593783140182495, - -1.2602300643920898 - ], - "y": [ - -0.15975284576416016, - 0.8558676838874817 - ], - "z": [ - 1.8295799493789673, - 2.467599868774414 - ] - }, - { - "line": { - "color": "rgb(255, 0, 0)", - "width": 10 - }, - "marker": { - "color": "rgb(255, 0, 0)", - "colorscale": [ - [ - 0, - "#440154" - ], - [ - 0.1111111111111111, - "#482878" - ], - [ - 0.2222222222222222, - "#3e4989" - ], - [ - 0.3333333333333333, - "#31688e" - ], - [ - 0.4444444444444444, - "#26828e" - ], - [ - 0.5555555555555556, - "#1f9e89" - ], - [ - 0.6666666666666666, - "#35b779" - ], - [ - 0.7777777777777778, - "#6ece58" - ], - [ - 0.8888888888888888, - "#b5de2b" - ], - [ - 1, - "#fde725" - ] - ], - "size": 4 - }, - "showlegend": false, - "type": "scatter3d", - "x": [ - -1.446848750114441, - -0.24964332580566406 - ], - "y": [ - 0.14292669296264648, - -0.43850430846214294 - ], - "z": [ - -0.2051914930343628, - 4.6144185066223145 - ] - }, - { - "line": { - "color": "rgb(255, 0, 0)", - "width": 10 - }, - "marker": { - "color": "rgb(255, 0, 0)", - "colorscale": [ - [ - 0, - "#440154" - ], - [ - 0.1111111111111111, - "#482878" - ], - [ - 0.2222222222222222, - "#3e4989" - ], - [ - 0.3333333333333333, - "#31688e" - ], - [ - 0.4444444444444444, - "#26828e" - ], - [ - 0.5555555555555556, - "#1f9e89" - ], - [ - 0.6666666666666666, - "#35b779" - ], - [ - 0.7777777777777778, - "#6ece58" - ], - [ - 0.8888888888888888, - "#b5de2b" - ], - [ - 1, - "#fde725" - ] - ], - "size": 4 - }, - "showlegend": false, - "type": "scatter3d", - "x": [ - 3.4888501167297363, - 1.8424657583236694 - ], - "y": [ - -0.32929059863090515, - -1.329573154449463 - ], - "z": [ - 0.9280045628547668, - 5.541989326477051 - ] - }, - { - "line": { - "color": "rgb(255, 0, 0)", - "width": 10 - }, - "marker": { - "color": "rgb(255, 0, 0)", - "colorscale": [ - [ - 0, - "#440154" - ], - [ - 0.1111111111111111, - "#482878" - ], - [ - 0.2222222222222222, - "#3e4989" - ], - [ - 0.3333333333333333, - "#31688e" - ], - [ - 0.4444444444444444, - "#26828e" - ], - [ - 0.5555555555555556, - "#1f9e89" - ], - [ - 0.6666666666666666, - "#35b779" - ], - [ - 0.7777777777777778, - "#6ece58" - ], - [ - 0.8888888888888888, - "#b5de2b" - ], - [ - 1, - "#fde725" - ] - ], - "size": 4 - }, - "showlegend": false, - "type": "scatter3d", - "x": [ - 3.9883968830108643, - 4.929524898529053 - ], - "y": [ - -0.2211797833442688, - -0.9573605060577393 - ], - "z": [ - 0.7871521711349487, - 5.642284870147705 - ] - }, - { - "line": { - "color": "rgb(255, 0, 0)", - "width": 10 - }, - "marker": { - "color": "rgb(255, 0, 0)", - "colorscale": [ - [ - 0, - "#440154" - ], - [ - 0.1111111111111111, - "#482878" - ], - [ - 0.2222222222222222, - "#3e4989" - ], - [ - 0.3333333333333333, - "#31688e" - ], - [ - 0.4444444444444444, - "#26828e" - ], - [ - 0.5555555555555556, - "#1f9e89" - ], - [ - 0.6666666666666666, - "#35b779" - ], - [ - 0.7777777777777778, - "#6ece58" - ], - [ - 0.8888888888888888, - "#b5de2b" - ], - [ - 1, - "#fde725" - ] - ], - "size": 4 - }, - "showlegend": false, - "type": "scatter3d", - "x": [ - -1.4305938482284546, - -0.1521141529083252 - ], - "y": [ - 0.3497944474220276, - 0.8027023077011108 - ], - "z": [ - -0.20660918951034546, - 4.605912208557129 - ] - }, - { - "line": { - "color": "rgb(255, 0, 0)", - "width": 10 - }, - "marker": { - "color": "rgb(255, 0, 0)", - "colorscale": [ - [ - 0, - "#440154" - ], - [ - 0.1111111111111111, - "#482878" - ], - [ - 0.2222222222222222, - "#3e4989" - ], - [ - 0.3333333333333333, - "#31688e" - ], - [ - 0.4444444444444444, - "#26828e" - ], - [ - 0.5555555555555556, - "#1f9e89" - ], - [ - 0.6666666666666666, - "#35b779" - ], - [ - 0.7777777777777778, - "#6ece58" - ], - [ - 0.8888888888888888, - "#b5de2b" - ], - [ - 1, - "#fde725" - ] - ], - "size": 4 - }, - "showlegend": false, - "type": "scatter3d", - "x": [ - -2.351006507873535, - -1.6811578273773193 - ], - "y": [ - 0.08429938554763794, - -1.2950620651245117 - ], - "z": [ - -0.48973697423934937, - 4.269323825836182 - ] - }, - { - "line": { - "color": "rgb(255, 0, 0)", - "width": 10 - }, - "marker": { - "color": "rgb(255, 0, 0)", - "colorscale": [ - [ - 0, - "#440154" - ], - [ - 0.1111111111111111, - "#482878" - ], - [ - 0.2222222222222222, - "#3e4989" - ], - [ - 0.3333333333333333, - "#31688e" - ], - [ - 0.4444444444444444, - "#26828e" - ], - [ - 0.5555555555555556, - "#1f9e89" - ], - [ - 0.6666666666666666, - "#35b779" - ], - [ - 0.7777777777777778, - "#6ece58" - ], - [ - 0.8888888888888888, - "#b5de2b" - ], - [ - 1, - "#fde725" - ] - ], - "size": 4 - }, - "showlegend": false, - "type": "scatter3d", - "x": [ - 3.4759016036987305, - 1.7647755146026611 - ], - "y": [ - -0.15074323117733002, - -0.2582889199256897 - ], - "z": [ - 0.9445793628692627, - 5.641438007354736 - ] - }, - { - "line": { - "color": "rgb(255, 0, 0)", - "width": 10 - }, - "marker": { - "color": "rgb(255, 0, 0)", - "colorscale": [ - [ - 0, - "#440154" - ], - [ - 0.1111111111111111, - "#482878" - ], - [ - 0.2222222222222222, - "#3e4989" - ], - [ - 0.3333333333333333, - "#31688e" - ], - [ - 0.4444444444444444, - "#26828e" - ], - [ - 0.5555555555555556, - "#1f9e89" - ], - [ - 0.6666666666666666, - "#35b779" - ], - [ - 0.7777777777777778, - "#6ece58" - ], - [ - 0.8888888888888888, - "#b5de2b" - ], - [ - 1, - "#fde725" - ] - ], - "size": 4 - }, - "showlegend": false, - "type": "scatter3d", - "x": [ - 3.6637046337127686, - -0.8407011032104492 - ], - "y": [ - -0.193550705909729, - 0.6530805230140686 - ], - "z": [ - 2.1016530990600586, - 4.100038528442383 - ] - }, - { - "line": { - "color": "rgb(255, 0, 0)", - "width": 10 - }, - "marker": { - "color": "rgb(255, 0, 0)", - "colorscale": [ - [ - 0, - "#440154" - ], - [ - 0.1111111111111111, - "#482878" - ], - [ - 0.2222222222222222, - "#3e4989" - ], - [ - 0.3333333333333333, - "#31688e" - ], - [ - 0.4444444444444444, - "#26828e" - ], - [ - 0.5555555555555556, - "#1f9e89" - ], - [ - 0.6666666666666666, - "#35b779" - ], - [ - 0.7777777777777778, - "#6ece58" - ], - [ - 0.8888888888888888, - "#b5de2b" - ], - [ - 1, - "#fde725" - ] - ], - "size": 4 - }, - "showlegend": false, - "type": "scatter3d", - "x": [ - 3.702563762664795, - 3.1247477531433105 - ], - "y": [ - -0.13121438026428223, - -0.14111584424972534 - ], - "z": [ - 0.9985057711601257, - 5.964996814727783 - ] - }, - { - "line": { - "color": "rgb(255, 0, 0)", - "width": 10 - }, - "marker": { - "color": "rgb(255, 0, 0)", - "colorscale": [ - [ - 0, - "#440154" - ], - [ - 0.1111111111111111, - "#482878" - ], - [ - 0.2222222222222222, - "#3e4989" - ], - [ - 0.3333333333333333, - "#31688e" - ], - [ - 0.4444444444444444, - "#26828e" - ], - [ - 0.5555555555555556, - "#1f9e89" - ], - [ - 0.6666666666666666, - "#35b779" - ], - [ - 0.7777777777777778, - "#6ece58" - ], - [ - 0.8888888888888888, - "#b5de2b" - ], - [ - 1, - "#fde725" - ] - ], - "size": 4 - }, - "showlegend": false, - "type": "scatter3d", - "x": [ - 0.4080146551132202, - -1.476891279220581 - ], - "y": [ - 0.12125931680202484, - -0.03603313863277435 - ], - "z": [ - -0.8454982042312622, - 3.7829346656799316 - ] - }, - { - "line": { - "color": "rgb(255, 0, 0)", - "width": 10 - }, - "marker": { - "color": "rgb(255, 0, 0)", - "colorscale": [ - [ - 0, - "#440154" - ], - [ - 0.1111111111111111, - "#482878" - ], - [ - 0.2222222222222222, - "#3e4989" - ], - [ - 0.3333333333333333, - "#31688e" - ], - [ - 0.4444444444444444, - "#26828e" - ], - [ - 0.5555555555555556, - "#1f9e89" - ], - [ - 0.6666666666666666, - "#35b779" - ], - [ - 0.7777777777777778, - "#6ece58" - ], - [ - 0.8888888888888888, - "#b5de2b" - ], - [ - 1, - "#fde725" - ] - ], - "size": 4 - }, - "showlegend": false, - "type": "scatter3d", - "x": [ - -2.169994592666626, - -0.595085620880127 - ], - "y": [ - 0.207540363073349, - -0.5556161999702454 - ], - "z": [ - -0.504804790019989, - 4.178916931152344 - ] - }, - { - "line": { - "color": "rgb(255, 0, 0)", - "width": 10 - }, - "marker": { - "color": "rgb(255, 0, 0)", - "colorscale": [ - [ - 0, - "#440154" - ], - [ - 0.1111111111111111, - "#482878" - ], - [ - 0.2222222222222222, - "#3e4989" - ], - [ - 0.3333333333333333, - "#31688e" - ], - [ - 0.4444444444444444, - "#26828e" - ], - [ - 0.5555555555555556, - "#1f9e89" - ], - [ - 0.6666666666666666, - "#35b779" - ], - [ - 0.7777777777777778, - "#6ece58" - ], - [ - 0.8888888888888888, - "#b5de2b" - ], - [ - 1, - "#fde725" - ] - ], - "size": 4 - }, - "showlegend": false, - "type": "scatter3d", - "x": [ - -1.3905425071716309, - -5.713432788848877 - ], - "y": [ - 0.46384674310684204, - 1.5310873985290527 - ], - "z": [ - -0.5948560237884521, - 1.6797022819519043 - ] - }, - { - "line": { - "color": "rgb(255, 0, 0)", - "width": 10 - }, - "marker": { - "color": "rgb(255, 0, 0)", - "colorscale": [ - [ - 0, - "#440154" - ], - [ - 0.1111111111111111, - "#482878" - ], - [ - 0.2222222222222222, - "#3e4989" - ], - [ - 0.3333333333333333, - "#31688e" - ], - [ - 0.4444444444444444, - "#26828e" - ], - [ - 0.5555555555555556, - "#1f9e89" - ], - [ - 0.6666666666666666, - "#35b779" - ], - [ - 0.7777777777777778, - "#6ece58" - ], - [ - 0.8888888888888888, - "#b5de2b" - ], - [ - 1, - "#fde725" - ] - ], - "size": 4 - }, - "showlegend": false, - "type": "scatter3d", - "x": [ - 3.6305694580078125, - -1.0395116806030273 - ], - "y": [ - -0.25658655166625977, - 0.27486538887023926 - ], - "z": [ - 2.043027400970459, - 3.748284339904785 - ] - }, - { - "line": { - "color": "rgb(255, 0, 0)", - "width": 10 - }, - "marker": { - "color": "rgb(255, 0, 0)", - "colorscale": [ - [ - 0, - "#440154" - ], - [ - 0.1111111111111111, - "#482878" - ], - [ - 0.2222222222222222, - "#3e4989" - ], - [ - 0.3333333333333333, - "#31688e" - ], - [ - 0.4444444444444444, - "#26828e" - ], - [ - 0.5555555555555556, - "#1f9e89" - ], - [ - 0.6666666666666666, - "#35b779" - ], - [ - 0.7777777777777778, - "#6ece58" - ], - [ - 0.8888888888888888, - "#b5de2b" - ], - [ - 1, - "#fde725" - ] - ], - "size": 4 - }, - "showlegend": false, - "type": "scatter3d", - "x": [ - 0.11006754636764526, - -3.2645742893218994 - ], - "y": [ - 0.4392663836479187, - 1.8720091581344604 - ], - "z": [ - -1.0912129878997803, - 2.3086462020874023 - ] - }, - { - "line": { - "color": "rgb(255, 0, 0)", - "width": 10 - }, - "marker": { - "color": "rgb(255, 0, 0)", - "colorscale": [ - [ - 0, - "#440154" - ], - [ - 0.1111111111111111, - "#482878" - ], - [ - 0.2222222222222222, - "#3e4989" - ], - [ - 0.3333333333333333, - "#31688e" - ], - [ - 0.4444444444444444, - "#26828e" - ], - [ - 0.5555555555555556, - "#1f9e89" - ], - [ - 0.6666666666666666, - "#35b779" - ], - [ - 0.7777777777777778, - "#6ece58" - ], - [ - 0.8888888888888888, - "#b5de2b" - ], - [ - 1, - "#fde725" - ] - ], - "size": 4 - }, - "showlegend": false, - "type": "scatter3d", - "x": [ - 3.754448413848877, - 3.525834083557129 - ], - "y": [ - -0.1677413433790207, - -0.636729896068573 - ], - "z": [ - 0.8106664419174194, - 5.783370494842529 - ] - }, - { - "line": { - "color": "rgb(255, 0, 0)", - "width": 10 - }, - "marker": { - "color": "rgb(255, 0, 0)", - "colorscale": [ - [ - 0, - "#440154" - ], - [ - 0.1111111111111111, - "#482878" - ], - [ - 0.2222222222222222, - "#3e4989" - ], - [ - 0.3333333333333333, - "#31688e" - ], - [ - 0.4444444444444444, - "#26828e" - ], - [ - 0.5555555555555556, - "#1f9e89" - ], - [ - 0.6666666666666666, - "#35b779" - ], - [ - 0.7777777777777778, - "#6ece58" - ], - [ - 0.8888888888888888, - "#b5de2b" - ], - [ - 1, - "#fde725" - ] - ], - "size": 4 - }, - "showlegend": false, - "type": "scatter3d", - "x": [ - -1.4045445919036865, - 0.0041817426681518555 - ], - "y": [ - 0.3239462375640869, - 0.6476130485534668 - ], - "z": [ - -0.2118104100227356, - 4.574705123901367 - ] - }, - { - "line": { - "color": "rgb(255, 0, 0)", - "width": 10 - }, - "marker": { - "color": "rgb(255, 0, 0)", - "colorscale": [ - [ - 0, - "#440154" - ], - [ - 0.1111111111111111, - "#482878" - ], - [ - 0.2222222222222222, - "#3e4989" - ], - [ - 0.3333333333333333, - "#31688e" - ], - [ - 0.4444444444444444, - "#26828e" - ], - [ - 0.5555555555555556, - "#1f9e89" - ], - [ - 0.6666666666666666, - "#35b779" - ], - [ - 0.7777777777777778, - "#6ece58" - ], - [ - 0.8888888888888888, - "#b5de2b" - ], - [ - 1, - "#fde725" - ] - ], - "size": 4 - }, - "showlegend": false, - "type": "scatter3d", - "x": [ - 3.2817296981811523, - 0.5997436046600342 - ], - "y": [ - -0.0893968790769577, - 0.10978914797306061 - ], - "z": [ - 0.8482325673103333, - 5.063357353210449 - ] - }, - { - "line": { - "color": "rgb(255, 0, 0)", - "width": 10 - }, - "marker": { - "color": "rgb(255, 0, 0)", - "colorscale": [ - [ - 0, - "#440154" - ], - [ - 0.1111111111111111, - "#482878" - ], - [ - 0.2222222222222222, - "#3e4989" - ], - [ - 0.3333333333333333, - "#31688e" - ], - [ - 0.4444444444444444, - "#26828e" - ], - [ - 0.5555555555555556, - "#1f9e89" - ], - [ - 0.6666666666666666, - "#35b779" - ], - [ - 0.7777777777777778, - "#6ece58" - ], - [ - 0.8888888888888888, - "#b5de2b" - ], - [ - 1, - "#fde725" - ] - ], - "size": 4 - }, - "showlegend": false, - "type": "scatter3d", - "x": [ - -0.022557556629180908, - -4.060324668884277 - ], - "y": [ - 0.1366623491048813, - 0.056385040283203125 - ], - "z": [ - -1.1816089153289795, - 1.7662702798843384 - ] - }, - { - "line": { - "color": "rgb(255, 0, 0)", - "width": 10 - }, - "marker": { - "color": "rgb(255, 0, 0)", - "colorscale": [ - [ - 0, - "#440154" - ], - [ - 0.1111111111111111, - "#482878" - ], - [ - 0.2222222222222222, - "#3e4989" - ], - [ - 0.3333333333333333, - "#31688e" - ], - [ - 0.4444444444444444, - "#26828e" - ], - [ - 0.5555555555555556, - "#1f9e89" - ], - [ - 0.6666666666666666, - "#35b779" - ], - [ - 0.7777777777777778, - "#6ece58" - ], - [ - 0.8888888888888888, - "#b5de2b" - ], - [ - 1, - "#fde725" - ] - ], - "size": 4 - }, - "showlegend": false, - "type": "scatter3d", - "x": [ - 3.468932628631592, - 1.722961187362671 - ], - "y": [ - -0.27083683013916016, - -0.9788504242897034 - ], - "z": [ - 0.9314969182014465, - 5.562943458557129 - ] - }, - { - "line": { - "color": "rgb(255, 0, 0)", - "width": 10 - }, - "marker": { - "color": "rgb(255, 0, 0)", - "colorscale": [ - [ - 0, - "#440154" - ], - [ - 0.1111111111111111, - "#482878" - ], - [ - 0.2222222222222222, - "#3e4989" - ], - [ - 0.3333333333333333, - "#31688e" - ], - [ - 0.4444444444444444, - "#26828e" - ], - [ - 0.5555555555555556, - "#1f9e89" - ], - [ - 0.6666666666666666, - "#35b779" - ], - [ - 0.7777777777777778, - "#6ece58" - ], - [ - 0.8888888888888888, - "#b5de2b" - ], - [ - 1, - "#fde725" - ] - ], - "size": 4 - }, - "showlegend": false, - "type": "scatter3d", - "x": [ - -2.337939739227295, - -1.6027568578720093 - ], - "y": [ - 0.19729743897914886, - -0.6170737743377686 - ], - "z": [ - -0.46591997146606445, - 4.412225723266602 - ] - }, - { - "line": { - "color": "rgb(255, 0, 0)", - "width": 10 - }, - "marker": { - "color": "rgb(255, 0, 0)", - "colorscale": [ - [ - 0, - "#440154" - ], - [ - 0.1111111111111111, - "#482878" - ], - [ - 0.2222222222222222, - "#3e4989" - ], - [ - 0.3333333333333333, - "#31688e" - ], - [ - 0.4444444444444444, - "#26828e" - ], - [ - 0.5555555555555556, - "#1f9e89" - ], - [ - 0.6666666666666666, - "#35b779" - ], - [ - 0.7777777777777778, - "#6ece58" - ], - [ - 0.8888888888888888, - "#b5de2b" - ], - [ - 1, - "#fde725" - ] - ], - "size": 4 - }, - "showlegend": false, - "type": "scatter3d", - "x": [ - 2.0077943801879883, - -1.5215578079223633 - ], - "y": [ - 0.2605786919593811, - 1.545723557472229 - ], - "z": [ - 0.4338332414627075, - 3.734147071838379 - ] - }, - { - "line": { - "color": "rgb(255, 0, 0)", - "width": 10 - }, - "marker": { - "color": "rgb(255, 0, 0)", - "colorscale": [ - [ - 0, - "#440154" - ], - [ - 0.1111111111111111, - "#482878" - ], - [ - 0.2222222222222222, - "#3e4989" - ], - [ - 0.3333333333333333, - "#31688e" - ], - [ - 0.4444444444444444, - "#26828e" - ], - [ - 0.5555555555555556, - "#1f9e89" - ], - [ - 0.6666666666666666, - "#35b779" - ], - [ - 0.7777777777777778, - "#6ece58" - ], - [ - 0.8888888888888888, - "#b5de2b" - ], - [ - 1, - "#fde725" - ] - ], - "size": 4 - }, - "showlegend": false, - "type": "scatter3d", - "x": [ - -1.438315987586975, - -6.000073432922363 - ], - "y": [ - 0.22751906514167786, - 0.11312155425548553 - ], - "z": [ - -0.640999972820282, - 1.402838945388794 - ] - }, - { - "line": { - "color": "rgb(255, 0, 0)", - "width": 10 - }, - "marker": { - "color": "rgb(255, 0, 0)", - "colorscale": [ - [ - 0, - "#440154" - ], - [ - 0.1111111111111111, - "#482878" - ], - [ - 0.2222222222222222, - "#3e4989" - ], - [ - 0.3333333333333333, - "#31688e" - ], - [ - 0.4444444444444444, - "#26828e" - ], - [ - 0.5555555555555556, - "#1f9e89" - ], - [ - 0.6666666666666666, - "#35b779" - ], - [ - 0.7777777777777778, - "#6ece58" - ], - [ - 0.8888888888888888, - "#b5de2b" - ], - [ - 1, - "#fde725" - ] - ], - "size": 4 - }, - "showlegend": false, - "type": "scatter3d", - "x": [ - 3.6671810150146484, - -0.8198413848876953 - ], - "y": [ - -0.33984634280204773, - -0.22469331324100494 - ], - "z": [ - 2.1425833702087402, - 4.345620155334473 - ] - }, - { - "line": { - "color": "rgb(255, 0, 0)", - "width": 10 - }, - "marker": { - "color": "rgb(255, 0, 0)", - "colorscale": [ - [ - 0, - "#440154" - ], - [ - 0.1111111111111111, - "#482878" - ], - [ - 0.2222222222222222, - "#3e4989" - ], - [ - 0.3333333333333333, - "#31688e" - ], - [ - 0.4444444444444444, - "#26828e" - ], - [ - 0.5555555555555556, - "#1f9e89" - ], - [ - 0.6666666666666666, - "#35b779" - ], - [ - 0.7777777777777778, - "#6ece58" - ], - [ - 0.8888888888888888, - "#b5de2b" - ], - [ - 1, - "#fde725" - ] - ], - "size": 4 - }, - "showlegend": false, - "type": "scatter3d", - "x": [ - 3.5932860374450684, - -1.263211727142334 - ], - "y": [ - -0.23982523381710052, - 0.37543338537216187 - ], - "z": [ - 1.905532717704773, - 2.923316478729248 - ] - }, - { - "line": { - "color": "rgb(255, 0, 0)", - "width": 10 - }, - "marker": { - "color": "rgb(255, 0, 0)", - "colorscale": [ - [ - 0, - "#440154" - ], - [ - 0.1111111111111111, - "#482878" - ], - [ - 0.2222222222222222, - "#3e4989" - ], - [ - 0.3333333333333333, - "#31688e" - ], - [ - 0.4444444444444444, - "#26828e" - ], - [ - 0.5555555555555556, - "#1f9e89" - ], - [ - 0.6666666666666666, - "#35b779" - ], - [ - 0.7777777777777778, - "#6ece58" - ], - [ - 0.8888888888888888, - "#b5de2b" - ], - [ - 1, - "#fde725" - ] - ], - "size": 4 - }, - "showlegend": false, - "type": "scatter3d", - "x": [ - -0.8573349714279175, - -2.5141875743865967 - ], - "y": [ - 0.24129177629947662, - 0.19575780630111694 - ], - "z": [ - -0.10631096363067627, - 4.610973358154297 - ] - }, - { - "line": { - "color": "rgb(255, 0, 0)", - "width": 10 - }, - "marker": { - "color": "rgb(255, 0, 0)", - "colorscale": [ - [ - 0, - "#440154" - ], - [ - 0.1111111111111111, - "#482878" - ], - [ - 0.2222222222222222, - "#3e4989" - ], - [ - 0.3333333333333333, - "#31688e" - ], - [ - 0.4444444444444444, - "#26828e" - ], - [ - 0.5555555555555556, - "#1f9e89" - ], - [ - 0.6666666666666666, - "#35b779" - ], - [ - 0.7777777777777778, - "#6ece58" - ], - [ - 0.8888888888888888, - "#b5de2b" - ], - [ - 1, - "#fde725" - ] - ], - "size": 4 - }, - "showlegend": false, - "type": "scatter3d", - "x": [ - 0.19062435626983643, - -2.781233072280884 - ], - "y": [ - 0.44794464111328125, - 1.9240787029266357 - ], - "z": [ - -1.0231449604034424, - 2.7170543670654297 - ] - }, - { - "line": { - "color": "rgb(255, 0, 0)", - "width": 10 - }, - "marker": { - "color": "rgb(255, 0, 0)", - "colorscale": [ - [ - 0, - "#440154" - ], - [ - 0.1111111111111111, - "#482878" - ], - [ - 0.2222222222222222, - "#3e4989" - ], - [ - 0.3333333333333333, - "#31688e" - ], - [ - 0.4444444444444444, - "#26828e" - ], - [ - 0.5555555555555556, - "#1f9e89" - ], - [ - 0.6666666666666666, - "#35b779" - ], - [ - 0.7777777777777778, - "#6ece58" - ], - [ - 0.8888888888888888, - "#b5de2b" - ], - [ - 1, - "#fde725" - ] - ], - "size": 4 - }, - "showlegend": false, - "type": "scatter3d", - "x": [ - 1.9099631309509277, - -2.108546257019043 - ], - "y": [ - 0.0628596842288971, - 0.3594095706939697 - ], - "z": [ - 0.36583930253982544, - 3.326183319091797 - ] - }, - { - "line": { - "color": "rgb(255, 0, 0)", - "width": 10 - }, - "marker": { - "color": "rgb(255, 0, 0)", - "colorscale": [ - [ - 0, - "#440154" - ], - [ - 0.1111111111111111, - "#482878" - ], - [ - 0.2222222222222222, - "#3e4989" - ], - [ - 0.3333333333333333, - "#31688e" - ], - [ - 0.4444444444444444, - "#26828e" - ], - [ - 0.5555555555555556, - "#1f9e89" - ], - [ - 0.6666666666666666, - "#35b779" - ], - [ - 0.7777777777777778, - "#6ece58" - ], - [ - 0.8888888888888888, - "#b5de2b" - ], - [ - 1, - "#fde725" - ] - ], - "size": 4 - }, - "showlegend": false, - "type": "scatter3d", - "x": [ - -1.9905409812927246, - 0.48163509368896484 - ], - "y": [ - 0.1887534111738205, - -0.6683379411697388 - ], - "z": [ - -0.5894051194190979, - 3.6713151931762695 - ] - }, - { - "line": { - "color": "rgb(255, 0, 0)", - "width": 10 - }, - "marker": { - "color": "rgb(255, 0, 0)", - "colorscale": [ - [ - 0, - "#440154" - ], - [ - 0.1111111111111111, - "#482878" - ], - [ - 0.2222222222222222, - "#3e4989" - ], - [ - 0.3333333333333333, - "#31688e" - ], - [ - 0.4444444444444444, - "#26828e" - ], - [ - 0.5555555555555556, - "#1f9e89" - ], - [ - 0.6666666666666666, - "#35b779" - ], - [ - 0.7777777777777778, - "#6ece58" - ], - [ - 0.8888888888888888, - "#b5de2b" - ], - [ - 1, - "#fde725" - ] - ], - "size": 4 - }, - "showlegend": false, - "type": "scatter3d", - "x": [ - 3.873030662536621, - 4.237328052520752 - ], - "y": [ - -0.2838485836982727, - -1.3333733081817627 - ], - "z": [ - 0.7911288738250732, - 5.666145324707031 - ] - }, - { - "line": { - "color": "rgb(255, 0, 0)", - "width": 10 - }, - "marker": { - "color": "rgb(255, 0, 0)", - "colorscale": [ - [ - 0, - "#440154" - ], - [ - 0.1111111111111111, - "#482878" - ], - [ - 0.2222222222222222, - "#3e4989" - ], - [ - 0.3333333333333333, - "#31688e" - ], - [ - 0.4444444444444444, - "#26828e" - ], - [ - 0.5555555555555556, - "#1f9e89" - ], - [ - 0.6666666666666666, - "#35b779" - ], - [ - 0.7777777777777778, - "#6ece58" - ], - [ - 0.8888888888888888, - "#b5de2b" - ], - [ - 1, - "#fde725" - ] - ], - "size": 4 - }, - "showlegend": false, - "type": "scatter3d", - "x": [ - 3.4635465145111084, - 1.6906442642211914 - ], - "y": [ - -0.3338582515716553, - -1.3569790124893188 - ], - "z": [ - 0.9175681471824646, - 5.479370594024658 - ] - }, - { - "line": { - "color": "rgb(255, 0, 0)", - "width": 10 - }, - "marker": { - "color": "rgb(255, 0, 0)", - "colorscale": [ - [ - 0, - "#440154" - ], - [ - 0.1111111111111111, - "#482878" - ], - [ - 0.2222222222222222, - "#3e4989" - ], - [ - 0.3333333333333333, - "#31688e" - ], - [ - 0.4444444444444444, - "#26828e" - ], - [ - 0.5555555555555556, - "#1f9e89" - ], - [ - 0.6666666666666666, - "#35b779" - ], - [ - 0.7777777777777778, - "#6ece58" - ], - [ - 0.8888888888888888, - "#b5de2b" - ], - [ - 1, - "#fde725" - ] - ], - "size": 4 - }, - "showlegend": false, - "type": "scatter3d", - "x": [ - 2.3071346282958984, - 0.27448320388793945 - ], - "y": [ - 0.05379800125956535, - 0.3050394356250763 - ], - "z": [ - 0.6860249638557434, - 5.247297286987305 - ] - }, - { - "line": { - "color": "rgb(255, 0, 0)", - "width": 10 - }, - "marker": { - "color": "rgb(255, 0, 0)", - "colorscale": [ - [ - 0, - "#440154" - ], - [ - 0.1111111111111111, - "#482878" - ], - [ - 0.2222222222222222, - "#3e4989" - ], - [ - 0.3333333333333333, - "#31688e" - ], - [ - 0.4444444444444444, - "#26828e" - ], - [ - 0.5555555555555556, - "#1f9e89" - ], - [ - 0.6666666666666666, - "#35b779" - ], - [ - 0.7777777777777778, - "#6ece58" - ], - [ - 0.8888888888888888, - "#b5de2b" - ], - [ - 1, - "#fde725" - ] - ], - "size": 4 - }, - "showlegend": false, - "type": "scatter3d", - "x": [ - 3.718177080154419, - -0.5138654708862305 - ], - "y": [ - -0.2566904127597809, - 0.2742422819137573 - ], - "z": [ - 2.22381591796875, - 4.8330159187316895 - ] - }, - { - "line": { - "color": "rgb(255, 0, 0)", - "width": 10 - }, - "marker": { - "color": "rgb(255, 0, 0)", - "colorscale": [ - [ - 0, - "#440154" - ], - [ - 0.1111111111111111, - "#482878" - ], - [ - 0.2222222222222222, - "#3e4989" - ], - [ - 0.3333333333333333, - "#31688e" - ], - [ - 0.4444444444444444, - "#26828e" - ], - [ - 0.5555555555555556, - "#1f9e89" - ], - [ - 0.6666666666666666, - "#35b779" - ], - [ - 0.7777777777777778, - "#6ece58" - ], - [ - 0.8888888888888888, - "#b5de2b" - ], - [ - 1, - "#fde725" - ] - ], - "size": 4 - }, - "showlegend": false, - "type": "scatter3d", - "x": [ - 3.96914005279541, - 4.813983917236328 - ], - "y": [ - 0.11297598481178284, - 1.0475740432739258 - ], - "z": [ - 0.7838603854179382, - 5.622534275054932 - ] - }, - { - "line": { - "color": "rgb(255, 0, 0)", - "width": 10 - }, - "marker": { - "color": "rgb(255, 0, 0)", - "colorscale": [ - [ - 0, - "#440154" - ], - [ - 0.1111111111111111, - "#482878" - ], - [ - 0.2222222222222222, - "#3e4989" - ], - [ - 0.3333333333333333, - "#31688e" - ], - [ - 0.4444444444444444, - "#26828e" - ], - [ - 0.5555555555555556, - "#1f9e89" - ], - [ - 0.6666666666666666, - "#35b779" - ], - [ - 0.7777777777777778, - "#6ece58" - ], - [ - 0.8888888888888888, - "#b5de2b" - ], - [ - 1, - "#fde725" - ] - ], - "size": 4 - }, - "showlegend": false, - "type": "scatter3d", - "x": [ - 3.655548095703125, - 2.9324331283569336 - ], - "y": [ - -0.068553626537323, - -0.04160361364483833 - ], - "z": [ - 0.805597722530365, - 5.752958297729492 - ] - }, - { - "line": { - "color": "rgb(255, 0, 0)", - "width": 10 - }, - "marker": { - "color": "rgb(255, 0, 0)", - "colorscale": [ - [ - 0, - "#440154" - ], - [ - 0.1111111111111111, - "#482878" - ], - [ - 0.2222222222222222, - "#3e4989" - ], - [ - 0.3333333333333333, - "#31688e" - ], - [ - 0.4444444444444444, - "#26828e" - ], - [ - 0.5555555555555556, - "#1f9e89" - ], - [ - 0.6666666666666666, - "#35b779" - ], - [ - 0.7777777777777778, - "#6ece58" - ], - [ - 0.8888888888888888, - "#b5de2b" - ], - [ - 1, - "#fde725" - ] - ], - "size": 4 - }, - "showlegend": false, - "type": "scatter3d", - "x": [ - 2.079613447189331, - -1.0906438827514648 - ], - "y": [ - 0.1339007318019867, - 0.7856558561325073 - ], - "z": [ - 0.5359956622123718, - 4.347121715545654 - ] - }, - { - "line": { - "color": "rgb(255, 0, 0)", - "width": 10 - }, - "marker": { - "color": "rgb(255, 0, 0)", - "colorscale": [ - [ - 0, - "#440154" - ], - [ - 0.1111111111111111, - "#482878" - ], - [ - 0.2222222222222222, - "#3e4989" - ], - [ - 0.3333333333333333, - "#31688e" - ], - [ - 0.4444444444444444, - "#26828e" - ], - [ - 0.5555555555555556, - "#1f9e89" - ], - [ - 0.6666666666666666, - "#35b779" - ], - [ - 0.7777777777777778, - "#6ece58" - ], - [ - 0.8888888888888888, - "#b5de2b" - ], - [ - 1, - "#fde725" - ] - ], - "size": 4 - }, - "showlegend": false, - "type": "scatter3d", - "x": [ - -1.4136720895767212, - -5.85221004486084 - ], - "y": [ - 0.06954985857009888, - -0.8346937298774719 - ], - "z": [ - -0.6263661980628967, - 1.4906413555145264 - ] - }, - { - "line": { - "color": "rgb(255, 0, 0)", - "width": 10 - }, - "marker": { - "color": "rgb(255, 0, 0)", - "colorscale": [ - [ - 0, - "#440154" - ], - [ - 0.1111111111111111, - "#482878" - ], - [ - 0.2222222222222222, - "#3e4989" - ], - [ - 0.3333333333333333, - "#31688e" - ], - [ - 0.4444444444444444, - "#26828e" - ], - [ - 0.5555555555555556, - "#1f9e89" - ], - [ - 0.6666666666666666, - "#35b779" - ], - [ - 0.7777777777777778, - "#6ece58" - ], - [ - 0.8888888888888888, - "#b5de2b" - ], - [ - 1, - "#fde725" - ] - ], - "size": 4 - }, - "showlegend": false, - "type": "scatter3d", - "x": [ - 3.4549055099487305, - 1.6387989521026611 - ], - "y": [ - -0.1411500871181488, - -0.20073005557060242 - ], - "z": [ - 0.9368342161178589, - 5.594967365264893 - ] - }, - { - "line": { - "color": "rgb(255, 0, 0)", - "width": 10 - }, - "marker": { - "color": "rgb(255, 0, 0)", - "colorscale": [ - [ - 0, - "#440154" - ], - [ - 0.1111111111111111, - "#482878" - ], - [ - 0.2222222222222222, - "#3e4989" - ], - [ - 0.3333333333333333, - "#31688e" - ], - [ - 0.4444444444444444, - "#26828e" - ], - [ - 0.5555555555555556, - "#1f9e89" - ], - [ - 0.6666666666666666, - "#35b779" - ], - [ - 0.7777777777777778, - "#6ece58" - ], - [ - 0.8888888888888888, - "#b5de2b" - ], - [ - 1, - "#fde725" - ] - ], - "size": 4 - }, - "showlegend": false, - "type": "scatter3d", - "x": [ - 3.3416547775268555, - 0.9592933654785156 - ], - "y": [ - -0.15238726139068604, - -0.2681530714035034 - ], - "z": [ - 0.8840922117233276, - 5.278515338897705 - ] - }, - { - "line": { - "color": "rgb(255, 0, 0)", - "width": 10 - }, - "marker": { - "color": "rgb(255, 0, 0)", - "colorscale": [ - [ - 0, - "#440154" - ], - [ - 0.1111111111111111, - "#482878" - ], - [ - 0.2222222222222222, - "#3e4989" - ], - [ - 0.3333333333333333, - "#31688e" - ], - [ - 0.4444444444444444, - "#26828e" - ], - [ - 0.5555555555555556, - "#1f9e89" - ], - [ - 0.6666666666666666, - "#35b779" - ], - [ - 0.7777777777777778, - "#6ece58" - ], - [ - 0.8888888888888888, - "#b5de2b" - ], - [ - 1, - "#fde725" - ] - ], - "size": 4 - }, - "showlegend": false, - "type": "scatter3d", - "x": [ - 0.15483307838439941, - -2.995980978012085 - ], - "y": [ - 0.35909318923950195, - 1.39096999168396 - ], - "z": [ - -1.0226502418518066, - 2.720022201538086 - ] - }, - { - "line": { - "color": "rgb(255, 0, 0)", - "width": 10 - }, - "marker": { - "color": "rgb(255, 0, 0)", - "colorscale": [ - [ - 0, - "#440154" - ], - [ - 0.1111111111111111, - "#482878" - ], - [ - 0.2222222222222222, - "#3e4989" - ], - [ - 0.3333333333333333, - "#31688e" - ], - [ - 0.4444444444444444, - "#26828e" - ], - [ - 0.5555555555555556, - "#1f9e89" - ], - [ - 0.6666666666666666, - "#35b779" - ], - [ - 0.7777777777777778, - "#6ece58" - ], - [ - 0.8888888888888888, - "#b5de2b" - ], - [ - 1, - "#fde725" - ] - ], - "size": 4 - }, - "showlegend": false, - "type": "scatter3d", - "x": [ - -1.6473461389541626, - -1.4526278972625732 - ], - "y": [ - 0.2984682321548462, - 0.49474504590034485 - ], - "z": [ - -0.17064350843429565, - 4.821706295013428 - ] - }, - { - "line": { - "color": "rgb(255, 0, 0)", - "width": 10 - }, - "marker": { - "color": "rgb(255, 0, 0)", - "colorscale": [ - [ - 0, - "#440154" - ], - [ - 0.1111111111111111, - "#482878" - ], - [ - 0.2222222222222222, - "#3e4989" - ], - [ - 0.3333333333333333, - "#31688e" - ], - [ - 0.4444444444444444, - "#26828e" - ], - [ - 0.5555555555555556, - "#1f9e89" - ], - [ - 0.6666666666666666, - "#35b779" - ], - [ - 0.7777777777777778, - "#6ece58" - ], - [ - 0.8888888888888888, - "#b5de2b" - ], - [ - 1, - "#fde725" - ] - ], - "size": 4 - }, - "showlegend": false, - "type": "scatter3d", - "x": [ - 3.279736042022705, - 0.5877823829650879 - ], - "y": [ - -0.2737683057785034, - -0.9964392781257629 - ], - "z": [ - 0.8354156017303467, - 4.98645544052124 - ] - }, - { - "line": { - "color": "rgb(255, 0, 0)", - "width": 10 - }, - "marker": { - "color": "rgb(255, 0, 0)", - "colorscale": [ - [ - 0, - "#440154" - ], - [ - 0.1111111111111111, - "#482878" - ], - [ - 0.2222222222222222, - "#3e4989" - ], - [ - 0.3333333333333333, - "#31688e" - ], - [ - 0.4444444444444444, - "#26828e" - ], - [ - 0.5555555555555556, - "#1f9e89" - ], - [ - 0.6666666666666666, - "#35b779" - ], - [ - 0.7777777777777778, - "#6ece58" - ], - [ - 0.8888888888888888, - "#b5de2b" - ], - [ - 1, - "#fde725" - ] - ], - "size": 4 - }, - "showlegend": false, - "type": "scatter3d", - "x": [ - -2.381643056869507, - -1.8649775981903076 - ], - "y": [ - 0.28804510831832886, - -0.07258772850036621 - ], - "z": [ - -0.44952094554901123, - 4.5106201171875 - ] - }, - { - "line": { - "color": "rgb(255, 0, 0)", - "width": 10 - }, - "marker": { - "color": "rgb(255, 0, 0)", - "colorscale": [ - [ - 0, - "#440154" - ], - [ - 0.1111111111111111, - "#482878" - ], - [ - 0.2222222222222222, - "#3e4989" - ], - [ - 0.3333333333333333, - "#31688e" - ], - [ - 0.4444444444444444, - "#26828e" - ], - [ - 0.5555555555555556, - "#1f9e89" - ], - [ - 0.6666666666666666, - "#35b779" - ], - [ - 0.7777777777777778, - "#6ece58" - ], - [ - 0.8888888888888888, - "#b5de2b" - ], - [ - 1, - "#fde725" - ] - ], - "size": 4 - }, - "showlegend": false, - "type": "scatter3d", - "x": [ - -1.6428682804107666, - -1.4257608652114868 - ], - "y": [ - 0.308014839887619, - 0.5520246028900146 - ], - "z": [ - -0.17124927043914795, - 4.8180718421936035 - ] - }, - { - "line": { - "color": "rgb(255, 0, 0)", - "width": 10 - }, - "marker": { - "color": "rgb(255, 0, 0)", - "colorscale": [ - [ - 0, - "#440154" - ], - [ - 0.1111111111111111, - "#482878" - ], - [ - 0.2222222222222222, - "#3e4989" - ], - [ - 0.3333333333333333, - "#31688e" - ], - [ - 0.4444444444444444, - "#26828e" - ], - [ - 0.5555555555555556, - "#1f9e89" - ], - [ - 0.6666666666666666, - "#35b779" - ], - [ - 0.7777777777777778, - "#6ece58" - ], - [ - 0.8888888888888888, - "#b5de2b" - ], - [ - 1, - "#fde725" - ] - ], - "size": 4 - }, - "showlegend": false, - "type": "scatter3d", - "x": [ - 4.090513229370117, - 5.542224884033203 - ], - "y": [ - -0.15836885571479797, - -0.5804949998855591 - ], - "z": [ - 0.7693169713020325, - 5.535274028778076 - ] - }, - { - "line": { - "color": "rgb(255, 0, 0)", - "width": 10 - }, - "marker": { - "color": "rgb(255, 0, 0)", - "colorscale": [ - [ - 0, - "#440154" - ], - [ - 0.1111111111111111, - "#482878" - ], - [ - 0.2222222222222222, - "#3e4989" - ], - [ - 0.3333333333333333, - "#31688e" - ], - [ - 0.4444444444444444, - "#26828e" - ], - [ - 0.5555555555555556, - "#1f9e89" - ], - [ - 0.6666666666666666, - "#35b779" - ], - [ - 0.7777777777777778, - "#6ece58" - ], - [ - 0.8888888888888888, - "#b5de2b" - ], - [ - 1, - "#fde725" - ] - ], - "size": 4 - }, - "showlegend": false, - "type": "scatter3d", - "x": [ - 2.0521111488342285, - -1.255657434463501 - ], - "y": [ - -0.14290766417980194, - -0.8751945495605469 - ], - "z": [ - 0.5092273950576782, - 4.186511993408203 - ] - }, - { - "line": { - "color": "rgb(255, 0, 0)", - "width": 10 - }, - "marker": { - "color": "rgb(255, 0, 0)", - "colorscale": [ - [ - 0, - "#440154" - ], - [ - 0.1111111111111111, - "#482878" - ], - [ - 0.2222222222222222, - "#3e4989" - ], - [ - 0.3333333333333333, - "#31688e" - ], - [ - 0.4444444444444444, - "#26828e" - ], - [ - 0.5555555555555556, - "#1f9e89" - ], - [ - 0.6666666666666666, - "#35b779" - ], - [ - 0.7777777777777778, - "#6ece58" - ], - [ - 0.8888888888888888, - "#b5de2b" - ], - [ - 1, - "#fde725" - ] - ], - "size": 4 - }, - "showlegend": false, - "type": "scatter3d", - "x": [ - 3.5785117149353027, - 2.3804357051849365 - ], - "y": [ - -0.1176733449101448, - -0.05986960232257843 - ], - "z": [ - 0.9760066866874695, - 5.830002307891846 - ] - }, - { - "line": { - "color": "rgb(255, 0, 0)", - "width": 10 - }, - "marker": { - "color": "rgb(255, 0, 0)", - "colorscale": [ - [ - 0, - "#440154" - ], - [ - 0.1111111111111111, - "#482878" - ], - [ - 0.2222222222222222, - "#3e4989" - ], - [ - 0.3333333333333333, - "#31688e" - ], - [ - 0.4444444444444444, - "#26828e" - ], - [ - 0.5555555555555556, - "#1f9e89" - ], - [ - 0.6666666666666666, - "#35b779" - ], - [ - 0.7777777777777778, - "#6ece58" - ], - [ - 0.8888888888888888, - "#b5de2b" - ], - [ - 1, - "#fde725" - ] - ], - "size": 4 - }, - "showlegend": false, - "type": "scatter3d", - "x": [ - 3.39186429977417, - 1.3503296375274658 - ], - "y": [ - 0.08177639544010162, - 0.8603765368461609 - ], - "z": [ - 0.7155902981758118, - 5.212913990020752 - ] - }, - { - "line": { - "color": "rgb(255, 0, 0)", - "width": 10 - }, - "marker": { - "color": "rgb(255, 0, 0)", - "colorscale": [ - [ - 0, - "#440154" - ], - [ - 0.1111111111111111, - "#482878" - ], - [ - 0.2222222222222222, - "#3e4989" - ], - [ - 0.3333333333333333, - "#31688e" - ], - [ - 0.4444444444444444, - "#26828e" - ], - [ - 0.5555555555555556, - "#1f9e89" - ], - [ - 0.6666666666666666, - "#35b779" - ], - [ - 0.7777777777777778, - "#6ece58" - ], - [ - 0.8888888888888888, - "#b5de2b" - ], - [ - 1, - "#fde725" - ] - ], - "size": 4 - }, - "showlegend": false, - "type": "scatter3d", - "x": [ - 0.01409226655960083, - -3.840425729751587 - ], - "y": [ - 0.3158668577671051, - 1.1316120624542236 - ], - "z": [ - -1.1554818153381348, - 1.9230328798294067 - ] - }, - { - "line": { - "color": "rgb(255, 0, 0)", - "width": 10 - }, - "marker": { - "color": "rgb(255, 0, 0)", - "colorscale": [ - [ - 0, - "#440154" - ], - [ - 0.1111111111111111, - "#482878" - ], - [ - 0.2222222222222222, - "#3e4989" - ], - [ - 0.3333333333333333, - "#31688e" - ], - [ - 0.4444444444444444, - "#26828e" - ], - [ - 0.5555555555555556, - "#1f9e89" - ], - [ - 0.6666666666666666, - "#35b779" - ], - [ - 0.7777777777777778, - "#6ece58" - ], - [ - 0.8888888888888888, - "#b5de2b" - ], - [ - 1, - "#fde725" - ] - ], - "size": 4 - }, - "showlegend": false, - "type": "scatter3d", - "x": [ - 0.5420961976051331, - -0.6724022030830383 - ], - "y": [ - 0.3546110689640045, - 1.3640773296356201 - ], - "z": [ - -0.8223756551742554, - 3.9216699600219727 - ] - }, - { - "line": { - "color": "rgb(255, 0, 0)", - "width": 10 - }, - "marker": { - "color": "rgb(255, 0, 0)", - "colorscale": [ - [ - 0, - "#440154" - ], - [ - 0.1111111111111111, - "#482878" - ], - [ - 0.2222222222222222, - "#3e4989" - ], - [ - 0.3333333333333333, - "#31688e" - ], - [ - 0.4444444444444444, - "#26828e" - ], - [ - 0.5555555555555556, - "#1f9e89" - ], - [ - 0.6666666666666666, - "#35b779" - ], - [ - 0.7777777777777778, - "#6ece58" - ], - [ - 0.8888888888888888, - "#b5de2b" - ], - [ - 1, - "#fde725" - ] - ], - "size": 4 - }, - "showlegend": false, - "type": "scatter3d", - "x": [ - 3.580146551132202, - -1.3420486450195312 - ], - "y": [ - -0.19936804473400116, - 0.6181765198707581 - ], - "z": [ - 1.7663536071777344, - 2.0882420539855957 - ] - }, - { - "line": { - "color": "rgb(255, 0, 0)", - "width": 10 - }, - "marker": { - "color": "rgb(255, 0, 0)", - "colorscale": [ - [ - 0, - "#440154" - ], - [ - 0.1111111111111111, - "#482878" - ], - [ - 0.2222222222222222, - "#3e4989" - ], - [ - 0.3333333333333333, - "#31688e" - ], - [ - 0.4444444444444444, - "#26828e" - ], - [ - 0.5555555555555556, - "#1f9e89" - ], - [ - 0.6666666666666666, - "#35b779" - ], - [ - 0.7777777777777778, - "#6ece58" - ], - [ - 0.8888888888888888, - "#b5de2b" - ], - [ - 1, - "#fde725" - ] - ], - "size": 4 - }, - "showlegend": false, - "type": "scatter3d", - "x": [ - -0.8728460073471069, - -2.6072537899017334 - ], - "y": [ - 0.4024415612220764, - 1.162656307220459 - ], - "z": [ - -0.12426447868347168, - 4.503252029418945 - ] - }, - { - "line": { - "color": "rgb(255, 0, 0)", - "width": 10 - }, - "marker": { - "color": "rgb(255, 0, 0)", - "colorscale": [ - [ - 0, - "#440154" - ], - [ - 0.1111111111111111, - "#482878" - ], - [ - 0.2222222222222222, - "#3e4989" - ], - [ - 0.3333333333333333, - "#31688e" - ], - [ - 0.4444444444444444, - "#26828e" - ], - [ - 0.5555555555555556, - "#1f9e89" - ], - [ - 0.6666666666666666, - "#35b779" - ], - [ - 0.7777777777777778, - "#6ece58" - ], - [ - 0.8888888888888888, - "#b5de2b" - ], - [ - 1, - "#fde725" - ] - ], - "size": 4 - }, - "showlegend": false, - "type": "scatter3d", - "x": [ - 3.720463752746582, - -0.5001468658447266 - ], - "y": [ - -0.4605043828487396, - -0.9486414790153503 - ], - "z": [ - 2.229163646697998, - 4.865101337432861 - ] - }, - { - "line": { - "color": "rgb(255, 0, 0)", - "width": 10 - }, - "marker": { - "color": "rgb(255, 0, 0)", - "colorscale": [ - [ - 0, - "#440154" - ], - [ - 0.1111111111111111, - "#482878" - ], - [ - 0.2222222222222222, - "#3e4989" - ], - [ - 0.3333333333333333, - "#31688e" - ], - [ - 0.4444444444444444, - "#26828e" - ], - [ - 0.5555555555555556, - "#1f9e89" - ], - [ - 0.6666666666666666, - "#35b779" - ], - [ - 0.7777777777777778, - "#6ece58" - ], - [ - 0.8888888888888888, - "#b5de2b" - ], - [ - 1, - "#fde725" - ] - ], - "size": 4 - }, - "showlegend": false, - "type": "scatter3d", - "x": [ - -1.4584628343582153, - -0.31932783126831055 - ], - "y": [ - 0.4082842171192169, - 1.153640866279602 - ], - "z": [ - -0.2068907618522644, - 4.604222774505615 - ] - }, - { - "line": { - "color": "rgb(255, 0, 0)", - "width": 10 - }, - "marker": { - "color": "rgb(255, 0, 0)", - "colorscale": [ - [ - 0, - "#440154" - ], - [ - 0.1111111111111111, - "#482878" - ], - [ - 0.2222222222222222, - "#3e4989" - ], - [ - 0.3333333333333333, - "#31688e" - ], - [ - 0.4444444444444444, - "#26828e" - ], - [ - 0.5555555555555556, - "#1f9e89" - ], - [ - 0.6666666666666666, - "#35b779" - ], - [ - 0.7777777777777778, - "#6ece58" - ], - [ - 0.8888888888888888, - "#b5de2b" - ], - [ - 1, - "#fde725" - ] - ], - "size": 4 - }, - "showlegend": false, - "type": "scatter3d", - "x": [ - 3.734048366546631, - -0.4186387062072754 - ], - "y": [ - -0.14398780465126038, - 0.9504579901695251 - ], - "z": [ - 2.214123487472534, - 4.7748613357543945 - ] - }, - { - "line": { - "color": "rgb(255, 0, 0)", - "width": 10 - }, - "marker": { - "color": "rgb(255, 0, 0)", - "colorscale": [ - [ - 0, - "#440154" - ], - [ - 0.1111111111111111, - "#482878" - ], - [ - 0.2222222222222222, - "#3e4989" - ], - [ - 0.3333333333333333, - "#31688e" - ], - [ - 0.4444444444444444, - "#26828e" - ], - [ - 0.5555555555555556, - "#1f9e89" - ], - [ - 0.6666666666666666, - "#35b779" - ], - [ - 0.7777777777777778, - "#6ece58" - ], - [ - 0.8888888888888888, - "#b5de2b" - ], - [ - 1, - "#fde725" - ] - ], - "size": 4 - }, - "showlegend": false, - "type": "scatter3d", - "x": [ - 4.116491317749023, - 5.698093414306641 - ], - "y": [ - -0.1041860431432724, - -0.25539809465408325 - ], - "z": [ - 0.764295756816864, - 5.505146503448486 - ] - }, - { - "line": { - "color": "rgb(255, 0, 0)", - "width": 10 - }, - "marker": { - "color": "rgb(255, 0, 0)", - "colorscale": [ - [ - 0, - "#440154" - ], - [ - 0.1111111111111111, - "#482878" - ], - [ - 0.2222222222222222, - "#3e4989" - ], - [ - 0.3333333333333333, - "#31688e" - ], - [ - 0.4444444444444444, - "#26828e" - ], - [ - 0.5555555555555556, - "#1f9e89" - ], - [ - 0.6666666666666666, - "#35b779" - ], - [ - 0.7777777777777778, - "#6ece58" - ], - [ - 0.8888888888888888, - "#b5de2b" - ], - [ - 1, - "#fde725" - ] - ], - "size": 4 - }, - "showlegend": false, - "type": "scatter3d", - "x": [ - 3.7565393447875977, - -0.2836923599243164 - ], - "y": [ - -0.43869465589523315, - -0.8177831768989563 - ], - "z": [ - 2.286195755004883, - 5.207294940948486 - ] - }, - { - "line": { - "color": "rgb(255, 0, 0)", - "width": 10 - }, - "marker": { - "color": "rgb(255, 0, 0)", - "colorscale": [ - [ - 0, - "#440154" - ], - [ - 0.1111111111111111, - "#482878" - ], - [ - 0.2222222222222222, - "#3e4989" - ], - [ - 0.3333333333333333, - "#31688e" - ], - [ - 0.4444444444444444, - "#26828e" - ], - [ - 0.5555555555555556, - "#1f9e89" - ], - [ - 0.6666666666666666, - "#35b779" - ], - [ - 0.7777777777777778, - "#6ece58" - ], - [ - 0.8888888888888888, - "#b5de2b" - ], - [ - 1, - "#fde725" - ] - ], - "size": 4 - }, - "showlegend": false, - "type": "scatter3d", - "x": [ - 3.373577117919922, - 1.1508288383483887 - ], - "y": [ - -0.13598282635211945, - -0.16972650587558746 - ], - "z": [ - 0.9009363651275635, - 5.379580020904541 - ] - }, - { - "line": { - "color": "rgb(255, 0, 0)", - "width": 10 - }, - "marker": { - "color": "rgb(255, 0, 0)", - "colorscale": [ - [ - 0, - "#440154" - ], - [ - 0.1111111111111111, - "#482878" - ], - [ - 0.2222222222222222, - "#3e4989" - ], - [ - 0.3333333333333333, - "#31688e" - ], - [ - 0.4444444444444444, - "#26828e" - ], - [ - 0.5555555555555556, - "#1f9e89" - ], - [ - 0.6666666666666666, - "#35b779" - ], - [ - 0.7777777777777778, - "#6ece58" - ], - [ - 0.8888888888888888, - "#b5de2b" - ], - [ - 1, - "#fde725" - ] - ], - "size": 4 - }, - "showlegend": false, - "type": "scatter3d", - "x": [ - 0.29927679896354675, - -2.1293184757232666 - ], - "y": [ - 0.20799458026885986, - 0.4843783974647522 - ], - "z": [ - -0.8988193273544312, - 3.463007926940918 - ] - }, - { - "line": { - "color": "rgb(255, 0, 0)", - "width": 10 - }, - "marker": { - "color": "rgb(255, 0, 0)", - "colorscale": [ - [ - 0, - "#440154" - ], - [ - 0.1111111111111111, - "#482878" - ], - [ - 0.2222222222222222, - "#3e4989" - ], - [ - 0.3333333333333333, - "#31688e" - ], - [ - 0.4444444444444444, - "#26828e" - ], - [ - 0.5555555555555556, - "#1f9e89" - ], - [ - 0.6666666666666666, - "#35b779" - ], - [ - 0.7777777777777778, - "#6ece58" - ], - [ - 0.8888888888888888, - "#b5de2b" - ], - [ - 1, - "#fde725" - ] - ], - "size": 4 - }, - "showlegend": false, - "type": "scatter3d", - "x": [ - 3.4729321002960205, - 1.746957778930664 - ], - "y": [ - -0.25868064165115356, - -0.9059132933616638 - ], - "z": [ - 0.9347688555717468, - 5.58257532119751 - ] - }, - { - "line": { - "color": "rgb(255, 0, 0)", - "width": 10 - }, - "marker": { - "color": "rgb(255, 0, 0)", - "colorscale": [ - [ - 0, - "#440154" - ], - [ - 0.1111111111111111, - "#482878" - ], - [ - 0.2222222222222222, - "#3e4989" - ], - [ - 0.3333333333333333, - "#31688e" - ], - [ - 0.4444444444444444, - "#26828e" - ], - [ - 0.5555555555555556, - "#1f9e89" - ], - [ - 0.6666666666666666, - "#35b779" - ], - [ - 0.7777777777777778, - "#6ece58" - ], - [ - 0.8888888888888888, - "#b5de2b" - ], - [ - 1, - "#fde725" - ] - ], - "size": 4 - }, - "showlegend": false, - "type": "scatter3d", - "x": [ - 3.5796475410461426, - -1.3450427055358887 - ], - "y": [ - -0.2209884524345398, - 0.48845404386520386 - ], - "z": [ - 1.8007924556732178, - 2.294875383377075 - ] - }, - { - "line": { - "color": "rgb(255, 0, 0)", - "width": 10 - }, - "marker": { - "color": "rgb(255, 0, 0)", - "colorscale": [ - [ - 0, - "#440154" - ], - [ - 0.1111111111111111, - "#482878" - ], - [ - 0.2222222222222222, - "#3e4989" - ], - [ - 0.3333333333333333, - "#31688e" - ], - [ - 0.4444444444444444, - "#26828e" - ], - [ - 0.5555555555555556, - "#1f9e89" - ], - [ - 0.6666666666666666, - "#35b779" - ], - [ - 0.7777777777777778, - "#6ece58" - ], - [ - 0.8888888888888888, - "#b5de2b" - ], - [ - 1, - "#fde725" - ] - ], - "size": 4 - }, - "showlegend": false, - "type": "scatter3d", - "x": [ - 4.022977828979492, - 5.13701057434082 - ], - "y": [ - -0.18300923705101013, - -0.728337287902832 - ], - "z": [ - 0.784868061542511, - 5.628580570220947 - ] - }, - { - "line": { - "color": "rgb(255, 0, 0)", - "width": 10 - }, - "marker": { - "color": "rgb(255, 0, 0)", - "colorscale": [ - [ - 0, - "#440154" - ], - [ - 0.1111111111111111, - "#482878" - ], - [ - 0.2222222222222222, - "#3e4989" - ], - [ - 0.3333333333333333, - "#31688e" - ], - [ - 0.4444444444444444, - "#26828e" - ], - [ - 0.5555555555555556, - "#1f9e89" - ], - [ - 0.6666666666666666, - "#35b779" - ], - [ - 0.7777777777777778, - "#6ece58" - ], - [ - 0.8888888888888888, - "#b5de2b" - ], - [ - 1, - "#fde725" - ] - ], - "size": 4 - }, - "showlegend": false, - "type": "scatter3d", - "x": [ - 0.6423107385635376, - -0.07111471891403198 - ], - "y": [ - 0.4374389052391052, - 1.8610444068908691 - ], - "z": [ - -0.8232529163360596, - 3.9164066314697266 - ] - }, - { - "line": { - "color": "rgb(255, 0, 0)", - "width": 10 - }, - "marker": { - "color": "rgb(255, 0, 0)", - "colorscale": [ - [ - 0, - "#440154" - ], - [ - 0.1111111111111111, - "#482878" - ], - [ - 0.2222222222222222, - "#3e4989" - ], - [ - 0.3333333333333333, - "#31688e" - ], - [ - 0.4444444444444444, - "#26828e" - ], - [ - 0.5555555555555556, - "#1f9e89" - ], - [ - 0.6666666666666666, - "#35b779" - ], - [ - 0.7777777777777778, - "#6ece58" - ], - [ - 0.8888888888888888, - "#b5de2b" - ], - [ - 1, - "#fde725" - ] - ], - "size": 4 - }, - "showlegend": false, - "type": "scatter3d", - "x": [ - 3.39778733253479, - 1.2960889339447021 - ], - "y": [ - -0.28243768215179443, - -1.0484555959701538 - ], - "z": [ - 0.8995471000671387, - 5.371244430541992 - ] - }, - { - "line": { - "color": "rgb(255, 0, 0)", - "width": 10 - }, - "marker": { - "color": "rgb(255, 0, 0)", - "colorscale": [ - [ - 0, - "#440154" - ], - [ - 0.1111111111111111, - "#482878" - ], - [ - 0.2222222222222222, - "#3e4989" - ], - [ - 0.3333333333333333, - "#31688e" - ], - [ - 0.4444444444444444, - "#26828e" - ], - [ - 0.5555555555555556, - "#1f9e89" - ], - [ - 0.6666666666666666, - "#35b779" - ], - [ - 0.7777777777777778, - "#6ece58" - ], - [ - 0.8888888888888888, - "#b5de2b" - ], - [ - 1, - "#fde725" - ] - ], - "size": 4 - }, - "showlegend": false, - "type": "scatter3d", - "x": [ - -1.7690134048461914, - -2.182631254196167 - ], - "y": [ - 0.37100571393966675, - 0.9299699068069458 - ], - "z": [ - -0.17883116006851196, - 4.772580623626709 - ] - }, - { - "line": { - "color": "rgb(255, 0, 0)", - "width": 10 - }, - "marker": { - "color": "rgb(255, 0, 0)", - "colorscale": [ - [ - 0, - "#440154" - ], - [ - 0.1111111111111111, - "#482878" - ], - [ - 0.2222222222222222, - "#3e4989" - ], - [ - 0.3333333333333333, - "#31688e" - ], - [ - 0.4444444444444444, - "#26828e" - ], - [ - 0.5555555555555556, - "#1f9e89" - ], - [ - 0.6666666666666666, - "#35b779" - ], - [ - 0.7777777777777778, - "#6ece58" - ], - [ - 0.8888888888888888, - "#b5de2b" - ], - [ - 1, - "#fde725" - ] - ], - "size": 4 - }, - "showlegend": false, - "type": "scatter3d", - "x": [ - 2.1498942375183105, - -0.6689589023590088 - ], - "y": [ - -0.01825335994362831, - -0.1272687315940857 - ], - "z": [ - 0.5994139909744263, - 4.727631568908691 - ] - }, - { - "line": { - "color": "rgb(255, 0, 0)", - "width": 10 - }, - "marker": { - "color": "rgb(255, 0, 0)", - "colorscale": [ - [ - 0, - "#440154" - ], - [ - 0.1111111111111111, - "#482878" - ], - [ - 0.2222222222222222, - "#3e4989" - ], - [ - 0.3333333333333333, - "#31688e" - ], - [ - 0.4444444444444444, - "#26828e" - ], - [ - 0.5555555555555556, - "#1f9e89" - ], - [ - 0.6666666666666666, - "#35b779" - ], - [ - 0.7777777777777778, - "#6ece58" - ], - [ - 0.8888888888888888, - "#b5de2b" - ], - [ - 1, - "#fde725" - ] - ], - "size": 4 - }, - "showlegend": false, - "type": "scatter3d", - "x": [ - 3.631443738937378, - -1.034266471862793 - ], - "y": [ - -0.3283533453941345, - -0.15573537349700928 - ], - "z": [ - 2.0598225593566895, - 3.849055767059326 - ] - }, - { - "line": { - "color": "rgb(255, 0, 0)", - "width": 10 - }, - "marker": { - "color": "rgb(255, 0, 0)", - "colorscale": [ - [ - 0, - "#440154" - ], - [ - 0.1111111111111111, - "#482878" - ], - [ - 0.2222222222222222, - "#3e4989" - ], - [ - 0.3333333333333333, - "#31688e" - ], - [ - 0.4444444444444444, - "#26828e" - ], - [ - 0.5555555555555556, - "#1f9e89" - ], - [ - 0.6666666666666666, - "#35b779" - ], - [ - 0.7777777777777778, - "#6ece58" - ], - [ - 0.8888888888888888, - "#b5de2b" - ], - [ - 1, - "#fde725" - ] - ], - "size": 4 - }, - "showlegend": false, - "type": "scatter3d", - "x": [ - 3.3978354930877686, - 1.2963781356811523 - ], - "y": [ - -0.13548552989959717, - -0.1667427122592926 - ], - "z": [ - 0.9125753045082092, - 5.449413776397705 - ] - }, - { - "line": { - "color": "rgb(255, 0, 0)", - "width": 10 - }, - "marker": { - "color": "rgb(255, 0, 0)", - "colorscale": [ - [ - 0, - "#440154" - ], - [ - 0.1111111111111111, - "#482878" - ], - [ - 0.2222222222222222, - "#3e4989" - ], - [ - 0.3333333333333333, - "#31688e" - ], - [ - 0.4444444444444444, - "#26828e" - ], - [ - 0.5555555555555556, - "#1f9e89" - ], - [ - 0.6666666666666666, - "#35b779" - ], - [ - 0.7777777777777778, - "#6ece58" - ], - [ - 0.8888888888888888, - "#b5de2b" - ], - [ - 1, - "#fde725" - ] - ], - "size": 4 - }, - "showlegend": false, - "type": "scatter3d", - "x": [ - 3.622642755508423, - -1.0870723724365234 - ], - "y": [ - -0.3615984618663788, - -0.3552059531211853 - ], - "z": [ - 2.0377461910247803, - 3.716597080230713 - ] - }, - { - "line": { - "color": "rgb(255, 0, 0)", - "width": 10 - }, - "marker": { - "color": "rgb(255, 0, 0)", - "colorscale": [ - [ - 0, - "#440154" - ], - [ - 0.1111111111111111, - "#482878" - ], - [ - 0.2222222222222222, - "#3e4989" - ], - [ - 0.3333333333333333, - "#31688e" - ], - [ - 0.4444444444444444, - "#26828e" - ], - [ - 0.5555555555555556, - "#1f9e89" - ], - [ - 0.6666666666666666, - "#35b779" - ], - [ - 0.7777777777777778, - "#6ece58" - ], - [ - 0.8888888888888888, - "#b5de2b" - ], - [ - 1, - "#fde725" - ] - ], - "size": 4 - }, - "showlegend": false, - "type": "scatter3d", - "x": [ - 3.404407024383545, - 1.425586462020874 - ], - "y": [ - -0.25361400842666626, - -1.151965856552124 - ], - "z": [ - 0.7167305946350098, - 5.21975564956665 - ] - }, - { - "line": { - "color": "rgb(255, 0, 0)", - "width": 10 - }, - "marker": { - "color": "rgb(255, 0, 0)", - "colorscale": [ - [ - 0, - "#440154" - ], - [ - 0.1111111111111111, - "#482878" - ], - [ - 0.2222222222222222, - "#3e4989" - ], - [ - 0.3333333333333333, - "#31688e" - ], - [ - 0.4444444444444444, - "#26828e" - ], - [ - 0.5555555555555556, - "#1f9e89" - ], - [ - 0.6666666666666666, - "#35b779" - ], - [ - 0.7777777777777778, - "#6ece58" - ], - [ - 0.8888888888888888, - "#b5de2b" - ], - [ - 1, - "#fde725" - ] - ], - "size": 4 - }, - "showlegend": false, - "type": "scatter3d", - "x": [ - -1.3603322505950928, - 0.26945579051971436 - ], - "y": [ - 0.19182372093200684, - -0.14512202143669128 - ], - "z": [ - -0.22613400220870972, - 4.488763332366943 - ] - }, - { - "line": { - "color": "rgb(255, 0, 0)", - "width": 10 - }, - "marker": { - "color": "rgb(255, 0, 0)", - "colorscale": [ - [ - 0, - "#440154" - ], - [ - 0.1111111111111111, - "#482878" - ], - [ - 0.2222222222222222, - "#3e4989" - ], - [ - 0.3333333333333333, - "#31688e" - ], - [ - 0.4444444444444444, - "#26828e" - ], - [ - 0.5555555555555556, - "#1f9e89" - ], - [ - 0.6666666666666666, - "#35b779" - ], - [ - 0.7777777777777778, - "#6ece58" - ], - [ - 0.8888888888888888, - "#b5de2b" - ], - [ - 1, - "#fde725" - ] - ], - "size": 4 - }, - "showlegend": false, - "type": "scatter3d", - "x": [ - -1.33823561668396, - 0.4020349979400635 - ], - "y": [ - 0.2512294352054596, - 0.2113121896982193 - ], - "z": [ - -0.231673002243042, - 4.455529689788818 - ] - }, - { - "line": { - "color": "rgb(255, 0, 0)", - "width": 10 - }, - "marker": { - "color": "rgb(255, 0, 0)", - "colorscale": [ - [ - 0, - "#440154" - ], - [ - 0.1111111111111111, - "#482878" - ], - [ - 0.2222222222222222, - "#3e4989" - ], - [ - 0.3333333333333333, - "#31688e" - ], - [ - 0.4444444444444444, - "#26828e" - ], - [ - 0.5555555555555556, - "#1f9e89" - ], - [ - 0.6666666666666666, - "#35b779" - ], - [ - 0.7777777777777778, - "#6ece58" - ], - [ - 0.8888888888888888, - "#b5de2b" - ], - [ - 1, - "#fde725" - ] - ], - "size": 4 - }, - "showlegend": false, - "type": "scatter3d", - "x": [ - -2.2796695232391357, - -1.2531354427337646 - ], - "y": [ - 0.30856838822364807, - 0.05055195093154907 - ], - "z": [ - -0.4642128348350525, - 4.422468662261963 - ] - }, - { - "line": { - "color": "rgb(255, 0, 0)", - "width": 10 - }, - "marker": { - "color": "rgb(255, 0, 0)", - "colorscale": [ - [ - 0, - "#440154" - ], - [ - 0.1111111111111111, - "#482878" - ], - [ - 0.2222222222222222, - "#3e4989" - ], - [ - 0.3333333333333333, - "#31688e" - ], - [ - 0.4444444444444444, - "#26828e" - ], - [ - 0.5555555555555556, - "#1f9e89" - ], - [ - 0.6666666666666666, - "#35b779" - ], - [ - 0.7777777777777778, - "#6ece58" - ], - [ - 0.8888888888888888, - "#b5de2b" - ], - [ - 1, - "#fde725" - ] - ], - "size": 4 - }, - "showlegend": false, - "type": "scatter3d", - "x": [ - 3.567387342453003, - -1.4186043739318848 - ], - "y": [ - -0.3361452519893646, - -0.2024868130683899 - ], - "z": [ - 1.771838665008545, - 2.121152400970459 - ] - }, - { - "line": { - "color": "rgb(255, 0, 0)", - "width": 10 - }, - "marker": { - "color": "rgb(255, 0, 0)", - "colorscale": [ - [ - 0, - "#440154" - ], - [ - 0.1111111111111111, - "#482878" - ], - [ - 0.2222222222222222, - "#3e4989" - ], - [ - 0.3333333333333333, - "#31688e" - ], - [ - 0.4444444444444444, - "#26828e" - ], - [ - 0.5555555555555556, - "#1f9e89" - ], - [ - 0.6666666666666666, - "#35b779" - ], - [ - 0.7777777777777778, - "#6ece58" - ], - [ - 0.8888888888888888, - "#b5de2b" - ], - [ - 1, - "#fde725" - ] - ], - "size": 4 - }, - "showlegend": false, - "type": "scatter3d", - "x": [ - 3.567720651626587, - 2.3156890869140625 - ], - "y": [ - -0.19131886959075928, - -0.5017427206039429 - ], - "z": [ - 0.971355676651001, - 5.802095890045166 - ] - }, - { - "line": { - "color": "rgb(255, 0, 0)", - "width": 10 - }, - "marker": { - "color": "rgb(255, 0, 0)", - "colorscale": [ - [ - 0, - "#440154" - ], - [ - 0.1111111111111111, - "#482878" - ], - [ - 0.2222222222222222, - "#3e4989" - ], - [ - 0.3333333333333333, - "#31688e" - ], - [ - 0.4444444444444444, - "#26828e" - ], - [ - 0.5555555555555556, - "#1f9e89" - ], - [ - 0.6666666666666666, - "#35b779" - ], - [ - 0.7777777777777778, - "#6ece58" - ], - [ - 0.8888888888888888, - "#b5de2b" - ], - [ - 1, - "#fde725" - ] - ], - "size": 4 - }, - "showlegend": false, - "type": "scatter3d", - "x": [ - -1.975256085395813, - 0.5733449459075928 - ], - "y": [ - 0.14889287948608398, - -0.9075011014938354 - ], - "z": [ - -0.6075548529624939, - 3.5624165534973145 - ] - }, - { - "line": { - "color": "rgb(255, 0, 0)", - "width": 10 - }, - "marker": { - "color": "rgb(255, 0, 0)", - "colorscale": [ - [ - 0, - "#440154" - ], - [ - 0.1111111111111111, - "#482878" - ], - [ - 0.2222222222222222, - "#3e4989" - ], - [ - 0.3333333333333333, - "#31688e" - ], - [ - 0.4444444444444444, - "#26828e" - ], - [ - 0.5555555555555556, - "#1f9e89" - ], - [ - 0.6666666666666666, - "#35b779" - ], - [ - 0.7777777777777778, - "#6ece58" - ], - [ - 0.8888888888888888, - "#b5de2b" - ], - [ - 1, - "#fde725" - ] - ], - "size": 4 - }, - "showlegend": false, - "type": "scatter3d", - "x": [ - 2.0141355991363525, - -1.483510971069336 - ], - "y": [ - 0.0267411507666111, - 0.14269833266735077 - ], - "z": [ - 0.4879978895187378, - 4.0591349601745605 - ] - }, - { - "line": { - "color": "rgb(255, 0, 0)", - "width": 10 - }, - "marker": { - "color": "rgb(255, 0, 0)", - "colorscale": [ - [ - 0, - "#440154" - ], - [ - 0.1111111111111111, - "#482878" - ], - [ - 0.2222222222222222, - "#3e4989" - ], - [ - 0.3333333333333333, - "#31688e" - ], - [ - 0.4444444444444444, - "#26828e" - ], - [ - 0.5555555555555556, - "#1f9e89" - ], - [ - 0.6666666666666666, - "#35b779" - ], - [ - 0.7777777777777778, - "#6ece58" - ], - [ - 0.8888888888888888, - "#b5de2b" - ], - [ - 1, - "#fde725" - ] - ], - "size": 4 - }, - "showlegend": false, - "type": "scatter3d", - "x": [ - -1.5353657007217407, - -0.7807453870773315 - ], - "y": [ - 0.2122775912284851, - -0.022398918867111206 - ], - "z": [ - -0.18168306350708008, - 4.755468845367432 - ] - }, - { - "line": { - "color": "rgb(255, 0, 0)", - "width": 10 - }, - "marker": { - "color": "rgb(255, 0, 0)", - "colorscale": [ - [ - 0, - "#440154" - ], - [ - 0.1111111111111111, - "#482878" - ], - [ - 0.2222222222222222, - "#3e4989" - ], - [ - 0.3333333333333333, - "#31688e" - ], - [ - 0.4444444444444444, - "#26828e" - ], - [ - 0.5555555555555556, - "#1f9e89" - ], - [ - 0.6666666666666666, - "#35b779" - ], - [ - 0.7777777777777778, - "#6ece58" - ], - [ - 0.8888888888888888, - "#b5de2b" - ], - [ - 1, - "#fde725" - ] - ], - "size": 4 - }, - "showlegend": false, - "type": "scatter3d", - "x": [ - 3.9038517475128174, - 4.42225456237793 - ], - "y": [ - -0.026610352098941803, - 0.21005605161190033 - ], - "z": [ - 0.8096093535423279, - 5.7770280838012695 - ] - }, - { - "line": { - "color": "rgb(255, 0, 0)", - "width": 10 - }, - "marker": { - "color": "rgb(255, 0, 0)", - "colorscale": [ - [ - 0, - "#440154" - ], - [ - 0.1111111111111111, - "#482878" - ], - [ - 0.2222222222222222, - "#3e4989" - ], - [ - 0.3333333333333333, - "#31688e" - ], - [ - 0.4444444444444444, - "#26828e" - ], - [ - 0.5555555555555556, - "#1f9e89" - ], - [ - 0.6666666666666666, - "#35b779" - ], - [ - 0.7777777777777778, - "#6ece58" - ], - [ - 0.8888888888888888, - "#b5de2b" - ], - [ - 1, - "#fde725" - ] - ], - "size": 4 - }, - "showlegend": false, - "type": "scatter3d", - "x": [ - 2.0290279388427734, - -1.3941569328308105 - ], - "y": [ - -0.07616037130355835, - -0.47471076250076294 - ], - "z": [ - 0.4982830286026001, - 4.120845794677734 - ] - }, - { - "line": { - "color": "rgb(255, 0, 0)", - "width": 10 - }, - "marker": { - "color": "rgb(255, 0, 0)", - "colorscale": [ - [ - 0, - "#440154" - ], - [ - 0.1111111111111111, - "#482878" - ], - [ - 0.2222222222222222, - "#3e4989" - ], - [ - 0.3333333333333333, - "#31688e" - ], - [ - 0.4444444444444444, - "#26828e" - ], - [ - 0.5555555555555556, - "#1f9e89" - ], - [ - 0.6666666666666666, - "#35b779" - ], - [ - 0.7777777777777778, - "#6ece58" - ], - [ - 0.8888888888888888, - "#b5de2b" - ], - [ - 1, - "#fde725" - ] - ], - "size": 4 - }, - "showlegend": false, - "type": "scatter3d", - "x": [ - 3.2695255279541016, - 0.5265178680419922 - ], - "y": [ - -0.11681312322616577, - -0.05470828711986542 - ], - "z": [ - 0.8411992788314819, - 5.021157741546631 - ] - }, - { - "line": { - "color": "rgb(255, 0, 0)", - "width": 10 - }, - "marker": { - "color": "rgb(255, 0, 0)", - "colorscale": [ - [ - 0, - "#440154" - ], - [ - 0.1111111111111111, - "#482878" - ], - [ - 0.2222222222222222, - "#3e4989" - ], - [ - 0.3333333333333333, - "#31688e" - ], - [ - 0.4444444444444444, - "#26828e" - ], - [ - 0.5555555555555556, - "#1f9e89" - ], - [ - 0.6666666666666666, - "#35b779" - ], - [ - 0.7777777777777778, - "#6ece58" - ], - [ - 0.8888888888888888, - "#b5de2b" - ], - [ - 1, - "#fde725" - ] - ], - "size": 4 - }, - "showlegend": false, - "type": "scatter3d", - "x": [ - 3.8046815395355225, - 3.82723331451416 - ], - "y": [ - -0.17924976348876953, - -0.7057803869247437 - ], - "z": [ - 0.8105551600456238, - 5.782702922821045 - ] - } - ], - "layout": { - "autosize": false, - "height": 1000, - "margin": { - "b": 100, - "l": 50, - "pad": 4, - "r": 50, - "t": 100 - }, - "scene": { - "aspectmode": "data", - "camera": { - "center": { - "x": 0, - "y": 0, - "z": 0 - }, - "eye": { - "x": 1.25, - "y": 1.25, - "z": 1.25 - }, - "up": { - "x": 0, - "y": 0, - "z": 1 - } - } - }, - "template": { - "data": { - "bar": [ - { - "error_x": { - "color": "#2a3f5f" - }, - "error_y": { - "color": "#2a3f5f" - }, - "marker": { - "line": { - "color": "#E5ECF6", - "width": 0.5 - }, - "pattern": { - "fillmode": "overlay", - "size": 10, - "solidity": 0.2 - } - }, - "type": "bar" - } - ], - "barpolar": [ - { - "marker": { - "line": { - "color": "#E5ECF6", - "width": 0.5 - }, - "pattern": { - "fillmode": "overlay", - "size": 10, - "solidity": 0.2 - } - }, - "type": "barpolar" - } - ], - "carpet": [ - { - "aaxis": { - "endlinecolor": "#2a3f5f", - "gridcolor": "white", - "linecolor": "white", - "minorgridcolor": "white", - "startlinecolor": "#2a3f5f" - }, - "baxis": { - "endlinecolor": "#2a3f5f", - "gridcolor": "white", - "linecolor": "white", - "minorgridcolor": "white", - "startlinecolor": "#2a3f5f" - }, - "type": "carpet" - } - ], - "choropleth": [ - { - "colorbar": { - "outlinewidth": 0, - "ticks": "" - }, - "type": "choropleth" - } - ], - "contour": [ - { - "colorbar": { - "outlinewidth": 0, - "ticks": "" - }, - "colorscale": [ - [ - 0, - "#0d0887" - ], - [ - 0.1111111111111111, - "#46039f" - ], - [ - 0.2222222222222222, - "#7201a8" - ], - [ - 0.3333333333333333, - "#9c179e" - ], - [ - 0.4444444444444444, - "#bd3786" - ], - [ - 0.5555555555555556, - "#d8576b" - ], - [ - 0.6666666666666666, - "#ed7953" - ], - [ - 0.7777777777777778, - "#fb9f3a" - ], - [ - 0.8888888888888888, - "#fdca26" - ], - [ - 1, - "#f0f921" - ] - ], - "type": "contour" - } - ], - "contourcarpet": [ - { - "colorbar": { - "outlinewidth": 0, - "ticks": "" - }, - "type": "contourcarpet" - } - ], - "heatmap": [ - { - "colorbar": { - "outlinewidth": 0, - "ticks": "" - }, - "colorscale": [ - [ - 0, - "#0d0887" - ], - [ - 0.1111111111111111, - "#46039f" - ], - [ - 0.2222222222222222, - "#7201a8" - ], - [ - 0.3333333333333333, - "#9c179e" - ], - [ - 0.4444444444444444, - "#bd3786" - ], - [ - 0.5555555555555556, - "#d8576b" - ], - [ - 0.6666666666666666, - "#ed7953" - ], - [ - 0.7777777777777778, - "#fb9f3a" - ], - [ - 0.8888888888888888, - "#fdca26" - ], - [ - 1, - "#f0f921" - ] - ], - "type": "heatmap" - } - ], - "heatmapgl": [ - { - "colorbar": { - "outlinewidth": 0, - "ticks": "" - }, - "colorscale": [ - [ - 0, - "#0d0887" - ], - [ - 0.1111111111111111, - "#46039f" - ], - [ - 0.2222222222222222, - "#7201a8" - ], - [ - 0.3333333333333333, - "#9c179e" - ], - [ - 0.4444444444444444, - "#bd3786" - ], - [ - 0.5555555555555556, - "#d8576b" - ], - [ - 0.6666666666666666, - "#ed7953" - ], - [ - 0.7777777777777778, - "#fb9f3a" - ], - [ - 0.8888888888888888, - "#fdca26" - ], - [ - 1, - "#f0f921" - ] - ], - "type": "heatmapgl" - } - ], - "histogram": [ - { - "marker": { - "pattern": { - "fillmode": "overlay", - "size": 10, - "solidity": 0.2 - } - }, - "type": "histogram" - } - ], - "histogram2d": [ - { - "colorbar": { - "outlinewidth": 0, - "ticks": "" - }, - "colorscale": [ - [ - 0, - "#0d0887" - ], - [ - 0.1111111111111111, - "#46039f" - ], - [ - 0.2222222222222222, - "#7201a8" - ], - [ - 0.3333333333333333, - "#9c179e" - ], - [ - 0.4444444444444444, - "#bd3786" - ], - [ - 0.5555555555555556, - "#d8576b" - ], - [ - 0.6666666666666666, - "#ed7953" - ], - [ - 0.7777777777777778, - "#fb9f3a" - ], - [ - 0.8888888888888888, - "#fdca26" - ], - [ - 1, - "#f0f921" - ] - ], - "type": "histogram2d" - } - ], - "histogram2dcontour": [ - { - "colorbar": { - "outlinewidth": 0, - "ticks": "" - }, - "colorscale": [ - [ - 0, - "#0d0887" - ], - [ - 0.1111111111111111, - "#46039f" - ], - [ - 0.2222222222222222, - "#7201a8" - ], - [ - 0.3333333333333333, - "#9c179e" - ], - [ - 0.4444444444444444, - "#bd3786" - ], - [ - 0.5555555555555556, - "#d8576b" - ], - [ - 0.6666666666666666, - "#ed7953" - ], - [ - 0.7777777777777778, - "#fb9f3a" - ], - [ - 0.8888888888888888, - "#fdca26" - ], - [ - 1, - "#f0f921" - ] - ], - "type": "histogram2dcontour" - } - ], - "mesh3d": [ - { - "colorbar": { - "outlinewidth": 0, - "ticks": "" - }, - "type": "mesh3d" - } - ], - "parcoords": [ - { - "line": { - "colorbar": { - "outlinewidth": 0, - "ticks": "" - } - }, - "type": "parcoords" - } - ], - "pie": [ - { - "automargin": true, - "type": "pie" - } - ], - "scatter": [ - { - "fillpattern": { - "fillmode": "overlay", - "size": 10, - "solidity": 0.2 - }, - "type": "scatter" - } - ], - "scatter3d": [ - { - "line": { - "colorbar": { - "outlinewidth": 0, - "ticks": "" - } - }, - "marker": { - "colorbar": { - "outlinewidth": 0, - "ticks": "" - } - }, - "type": "scatter3d" - } - ], - "scattercarpet": [ - { - "marker": { - "colorbar": { - "outlinewidth": 0, - "ticks": "" - } - }, - "type": "scattercarpet" - } - ], - "scattergeo": [ - { - "marker": { - "colorbar": { - "outlinewidth": 0, - "ticks": "" - } - }, - "type": "scattergeo" - } - ], - "scattergl": [ - { - "marker": { - "colorbar": { - "outlinewidth": 0, - "ticks": "" - } - }, - "type": "scattergl" - } - ], - "scattermapbox": [ - { - "marker": { - "colorbar": { - "outlinewidth": 0, - "ticks": "" - } - }, - "type": "scattermapbox" - } - ], - "scatterpolar": [ - { - "marker": { - "colorbar": { - "outlinewidth": 0, - "ticks": "" - } - }, - "type": "scatterpolar" - } - ], - "scatterpolargl": [ - { - "marker": { - "colorbar": { - "outlinewidth": 0, - "ticks": "" - } - }, - "type": "scatterpolargl" - } - ], - "scatterternary": [ - { - "marker": { - "colorbar": { - "outlinewidth": 0, - "ticks": "" - } - }, - "type": "scatterternary" - } - ], - "surface": [ - { - "colorbar": { - "outlinewidth": 0, - "ticks": "" - }, - "colorscale": [ - [ - 0, - "#0d0887" - ], - [ - 0.1111111111111111, - "#46039f" - ], - [ - 0.2222222222222222, - "#7201a8" - ], - [ - 0.3333333333333333, - "#9c179e" - ], - [ - 0.4444444444444444, - "#bd3786" - ], - [ - 0.5555555555555556, - "#d8576b" - ], - [ - 0.6666666666666666, - "#ed7953" - ], - [ - 0.7777777777777778, - "#fb9f3a" - ], - [ - 0.8888888888888888, - "#fdca26" - ], - [ - 1, - "#f0f921" - ] - ], - "type": "surface" - } - ], - "table": [ - { - "cells": { - "fill": { - "color": "#EBF0F8" - }, - "line": { - "color": "white" - } - }, - "header": { - "fill": { - "color": "#C8D4E3" - }, - "line": { - "color": "white" - } - }, - "type": "table" - } - ] - }, - "layout": { - "annotationdefaults": { - "arrowcolor": "#2a3f5f", - "arrowhead": 0, - "arrowwidth": 1 - }, - "autotypenumbers": "strict", - "coloraxis": { - "colorbar": { - "outlinewidth": 0, - "ticks": "" - } - }, - "colorscale": { - "diverging": [ - [ - 0, - "#8e0152" - ], - [ - 0.1, - "#c51b7d" - ], - [ - 0.2, - "#de77ae" - ], - [ - 0.3, - "#f1b6da" - ], - [ - 0.4, - "#fde0ef" - ], - [ - 0.5, - "#f7f7f7" - ], - [ - 0.6, - "#e6f5d0" - ], - [ - 0.7, - "#b8e186" - ], - [ - 0.8, - "#7fbc41" - ], - [ - 0.9, - "#4d9221" - ], - [ - 1, - "#276419" - ] - ], - "sequential": [ - [ - 0, - "#0d0887" - ], - [ - 0.1111111111111111, - "#46039f" - ], - [ - 0.2222222222222222, - "#7201a8" - ], - [ - 0.3333333333333333, - "#9c179e" - ], - [ - 0.4444444444444444, - "#bd3786" - ], - [ - 0.5555555555555556, - "#d8576b" - ], - [ - 0.6666666666666666, - "#ed7953" - ], - [ - 0.7777777777777778, - "#fb9f3a" - ], - [ - 0.8888888888888888, - "#fdca26" - ], - [ - 1, - "#f0f921" - ] - ], - "sequentialminus": [ - [ - 0, - "#0d0887" - ], - [ - 0.1111111111111111, - "#46039f" - ], - [ - 0.2222222222222222, - "#7201a8" - ], - [ - 0.3333333333333333, - "#9c179e" - ], - [ - 0.4444444444444444, - "#bd3786" - ], - [ - 0.5555555555555556, - "#d8576b" - ], - [ - 0.6666666666666666, - "#ed7953" - ], - [ - 0.7777777777777778, - "#fb9f3a" - ], - [ - 0.8888888888888888, - "#fdca26" - ], - [ - 1, - "#f0f921" - ] - ] - }, - "colorway": [ - "#636efa", - "#EF553B", - "#00cc96", - "#ab63fa", - "#FFA15A", - "#19d3f3", - "#FF6692", - "#B6E880", - "#FF97FF", - "#FECB52" - ], - "font": { - "color": "#2a3f5f" - }, - "geo": { - "bgcolor": "white", - "lakecolor": "white", - "landcolor": "#E5ECF6", - "showlakes": true, - "showland": true, - "subunitcolor": "white" - }, - "hoverlabel": { - "align": "left" - }, - "hovermode": "closest", - "mapbox": { - "style": "light" - }, - "paper_bgcolor": "white", - "plot_bgcolor": "#E5ECF6", - "polar": { - "angularaxis": { - "gridcolor": "white", - "linecolor": "white", - "ticks": "" - }, - "bgcolor": "#E5ECF6", - "radialaxis": { - "gridcolor": "white", - "linecolor": "white", - "ticks": "" - } - }, - "scene": { - "xaxis": { - "backgroundcolor": "#E5ECF6", - "gridcolor": "white", - "gridwidth": 2, - "linecolor": "white", - "showbackground": true, - "ticks": "", - "zerolinecolor": "white" - }, - "yaxis": { - "backgroundcolor": "#E5ECF6", - "gridcolor": "white", - "gridwidth": 2, - "linecolor": "white", - "showbackground": true, - "ticks": "", - "zerolinecolor": "white" - }, - "zaxis": { - "backgroundcolor": "#E5ECF6", - "gridcolor": "white", - "gridwidth": 2, - "linecolor": "white", - "showbackground": true, - "ticks": "", - "zerolinecolor": "white" - } - }, - "shapedefaults": { - "line": { - "color": "#2a3f5f" - } - }, - "ternary": { - "aaxis": { - "gridcolor": "white", - "linecolor": "white", - "ticks": "" - }, - "baxis": { - "gridcolor": "white", - "linecolor": "white", - "ticks": "" - }, - "bgcolor": "#E5ECF6", - "caxis": { - "gridcolor": "white", - "linecolor": "white", - "ticks": "" - } - }, - "title": { - "x": 0.05 - }, - "xaxis": { - "automargin": true, - "gridcolor": "white", - "linecolor": "white", - "ticks": "", - "title": { - "standoff": 15 - }, - "zerolinecolor": "white", - "zerolinewidth": 2 - }, - "yaxis": { - "automargin": true, - "gridcolor": "white", - "linecolor": "white", - "ticks": "", - "title": { - "standoff": 15 - }, - "zerolinecolor": "white", - "zerolinewidth": 2 - } - } - }, - "width": 1000 - } - }, - "text/html": [ - "
" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], + "outputs": [], "source": [ "layout = go.Layout(\n", " autosize=False,\n", @@ -8462,6 +359,9 @@ } ], "metadata": { + "interpreter": { + "hash": "4a749acc7d255f078aee52e0584cc77b3cb5aaed1b3a7407ec4262c1bf6cb526" + }, "kernelspec": { "display_name": "mattport", "language": "python", diff --git a/pyproject.toml b/pyproject.toml index 8a009c6e3..5cd8755fb 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -8,10 +8,10 @@ line-length = 120 # pylint [tool.pylint.messages_control] - max-line-length = 120 generated-members = ["numpy.*", "torch.*", "cv2.*", "cv.*"] -good-names-rgxs= "^[_a-zA-Z][_a-z0-9]?$" +good-names-rgxs = "^[_a-zA-Z][_a-z0-9]?$" +ignore = ["mattport/cuda", "tests/cuda"] disable = [ "too-many-arguments", @@ -23,7 +23,7 @@ disable = [ #pytest [tool.pytest.ini_options] -addopts = "--typeguard-packages=mattport --torchtyping-patch-typeguard --disable-warnings" +addopts = "--typeguard-packages=mattport --torchtyping-patch-typeguard --disable-warnings --ignore=tests/cuda" testpaths = [ "tests", ] \ No newline at end of file diff --git a/scripts/benchmarking/run_benchmark.py b/scripts/benchmarking/run_benchmark.py index d41dbc674..789ebddc9 100644 --- a/scripts/benchmarking/run_benchmark.py +++ b/scripts/benchmarking/run_benchmark.py @@ -56,7 +56,7 @@ def main(): trainer = Trainer(config, local_rank=0, world_size=1) trainer.setup(test_mode=True) avg_psnr = 0 - for step, image_idx in enumerate(config.data.validation_image_indices): + for step, image_idx in enumerate(config.data.val_image_indices): with torch.no_grad(): psnr = trainer.test_image(image_idx=image_idx, step=step) avg_psnr = (step * avg_psnr + psnr) / (step + 1) diff --git a/scripts/debugging/run_actions.py b/scripts/debugging/run_actions.py index d55506ca9..fa0e2b933 100644 --- a/scripts/debugging/run_actions.py +++ b/scripts/debugging/run_actions.py @@ -15,8 +15,8 @@ if __name__ == "__main__": if step["name"] == "Run Black": curr_command = f"black mattport/ {step['with']['options']};" else: - compressed = step["run"].replace("\n", "").replace("\\", "") - curr_command = f"{compressed};" + compressed = step["run"].replace("\n", ";").replace("\\", "") + curr_command = f"{compressed}" print("*" * 100) print(f"Running {curr_command}") diff --git a/scripts/run_train.py b/scripts/run_train.py index 5c05e28a8..bdaea1e80 100644 --- a/scripts/run_train.py +++ b/scripts/run_train.py @@ -18,6 +18,7 @@ from omegaconf import DictConfig from mattport.nerf.trainer import Trainer from mattport.utils import comms, profiler +from time import time logging.basicConfig(format="[%(filename)s:%(lineno)d] %(message)s", level=logging.DEBUG) @@ -99,7 +100,7 @@ def _distributed_worker( assert num_gpus_per_machine <= torch.cuda.device_count() torch.cuda.set_device(local_rank) - _set_random_seed(1234 + local_rank) # NOTE(ethan): will this work for multiple machines? should we use global_rank? + _set_random_seed(1234 + global_rank) comms.synchronize(world_size) output = main_func(local_rank, world_size, config) @@ -148,9 +149,11 @@ def launch( world_size = num_machines * num_gpus_per_machine if world_size == 0: # Using only CPU and one process. + _set_random_seed(0) main_func(local_rank=0, world_size=0, config=config) elif world_size == 1: # Using one gpu and one process. + _set_random_seed(0) try: main_func(local_rank=0, world_size=1, config=config) except KeyboardInterrupt: diff --git a/setup.cfg b/setup.cfg index 1d92ebac3..d7994bea2 100644 --- a/setup.cfg +++ b/setup.cfg @@ -1,7 +1,8 @@ [metadata] name = mattport -version = 0.0.1 -description = Volumetric rendering package +version = 0.0.0 +url = https://github.com/ethanweber/mattport.git +description = differentiable volume rendering package long_description = file: README.md [options] diff --git a/setup.py b/setup.py new file mode 100644 index 000000000..78db55ab3 --- /dev/null +++ b/setup.py @@ -0,0 +1,39 @@ +import os +from setuptools import setup + +ROOT_DIR = os.path.dirname(os.path.abspath(__file__)) +USE_CUDA = False + +ext_modules = [] +cmdclass = {} +setup_requires = [] +if USE_CUDA: + try: + from torch.utils.cpp_extension import BuildExtension, CUDAExtension + + ext_modules = [ + CUDAExtension( + name="mattport_cuda", + sources=[ + "mattport/cuda/mattport_cuda.cpp", + "mattport/cuda/mattport_cuda_kernel.cu", + ], + include_dirs=[os.path.join("mattport", "cuda", "include")], + extra_compile_args={ + "cxx": [], + "nvcc": [], + }, + verbose=True, + ), + ] + cmdclass = {"build_ext": BuildExtension} + setup_requires = ["pybind11>=2.5.0"] + except: + print("Failed to build the CUDA extension.") + +if __name__ == "__main__": + setup( + ext_modules=ext_modules, + cmdclass=cmdclass, + setup_requires=setup_requires, + ) diff --git a/tests/cuda/test_mattport_cuda.py b/tests/cuda/test_mattport_cuda.py new file mode 100644 index 000000000..cce9b4130 --- /dev/null +++ b/tests/cuda/test_mattport_cuda.py @@ -0,0 +1,102 @@ +""" +Test the CUDA bindings. +""" +import torch +from mattport.cuda.sampler import Sampler +import time +import mattport_cuda +import os + + +def test_gradients(): + """_summary_""" + pass + + +def test_outputs(): + """_summary_""" + pass + + +def pytorch_sample_uniformly_along_ray_bundle(origins, directions, nears, fars, range_offsets): + """Sample along every ray with a constant step size.""" + time_steps = nears[:, None] + range_offsets[None, :] # (num_rays, max_num_samples) + samples = origins[:, None, :] + directions[:, None, :] * time_steps[:, :, None] + time_steps_mask = (time_steps <= fars[:, None]).float() + return time_steps, samples, time_steps_mask + + +def get_rgb_density(samples, time_steps_mask): + num_rays, max_num_samples = time_steps_mask.shape + total_max_num_samples = num_rays * max_num_samples + + time_steps_mask_flat = time_steps_mask.view(total_max_num_samples) == 1.0 + samples_flat = samples.view(total_max_num_samples, 3) + samples_flat_masked = samples_flat[time_steps_mask_flat] + + # TODO: this goes through the forward pass of the MLP + + rgb = torch.zeros((num_rays, max_num_samples, 3)) + density = torch.zeros((num_rays, max_num_samples)) + rgb.view(total_max_num_samples, 3)[time_steps_mask_flat] = 1.0 + density.view(total_max_num_samples)[time_steps_mask_flat] = 1.0 + return rgb, density + + +def test_benchmark(): + """The test cases assume that rays can be at most of length 1.0.""" + + device = torch.device("cuda") + dtype = torch.float32 + + near_range = [0, 0.2] + far_range = [0.8, 1.0] + max_num_samples = 128 * 8 + offset = 1.0 / max_num_samples + + print("offset is", offset) + # import sys; sys.exit() + + print("Running ray sampling tests.") + # origins = torch.tensor() + # we want these to be dtype=torch.float32 and device="cuda" + num_rays = 1024 * 8 + print("num_rays:", num_rays) + print("max_num_samples:", max_num_samples) + origins = torch.zeros((num_rays, 3)).to(device) + directions = torch.ones((num_rays, 3)).to(device) + nears = torch.rand((num_rays)).to(device) * (near_range[1] - near_range[0]) + near_range[0] + fars = torch.rand((num_rays)).to(device) * (far_range[1] - far_range[0]) + far_range[0] + offsets = torch.ones(max_num_samples).to(device) * offset + range_offsets = torch.arange(0, max_num_samples).to(device) * offset + + start = time.time() + time_steps_pt, samples_pt, time_steps_mask_pt = pytorch_sample_uniformly_along_ray_bundle( + origins, directions, nears, fars, range_offsets + ) + PYTORCH_SAMPLE_TIME = time.time() - start + print("PYTORCH_SAMPLE_TIME:", PYTORCH_SAMPLE_TIME) + + start = time.time() + time_steps_cu, samples_cu, time_steps_mask_cu = mattport_cuda.sample_uniformly_along_ray_bundle( + origins, directions, nears, fars, offsets, max_num_samples + ) + CUDA_SAMPLE_TIME = time.time() - start + print("CUDA_SAMPLE_TIME: ", CUDA_SAMPLE_TIME) + + # assert torch.allclose(time_steps_pt, time_steps_cu) + # assert torch.allclose(samples_pt, samples_cu) + # assert torch.allclose(time_steps_mask_pt, time_steps_mask_cu) + + # rgb_pt, density_pt = get_rgb_density(samples_pt, time_steps_mask_pt) + # rgb_cu, density_cu = get_rgb_density(samples_cu, time_steps_mask_cu) + + perc_samples_used = float(time_steps_mask_pt.sum() / (time_steps_mask_pt.shape[0] * time_steps_mask_pt.shape[1])) + perc_samples_used_str = str(round(perc_samples_used * 100.0, 2)) + print(f"Using field network for %{perc_samples_used_str} of samples in this ray bundle.") + + # TODO: rendering of rgb, density, and deltas with pytorch vs. cuda + + +if __name__ == "__main__": + test_benchmark() diff --git a/tests/nerf/dataloader/configs/blender_lego.yaml b/tests/nerf/dataloader/configs/blender_lego.yaml index a4793a94e..d6dae7b05 100644 --- a/tests/nerf/dataloader/configs/blender_lego.yaml +++ b/tests/nerf/dataloader/configs/blender_lego.yaml @@ -1,7 +1,7 @@ # dataset options dataset: data_directory: data/lego - dataset_type: blender + dataset_format: blender downscale_factor: 1.0 # dataloader options @@ -15,7 +15,7 @@ dataloader: network: ray_generator: class_name: RayGenerator - inputs: ['cxy'] # camera, x, and y indices + inputs: ["cxy"] # camera, x, and y indices meta_data: in_dim: 0 - learning_rate: 0.0 \ No newline at end of file + learning_rate: 0.0 diff --git a/tests/nerf/dataloader/test_ray_generator.py b/tests/nerf/dataloader/test_ray_generator.py index 4a2636157..cd311cc98 100644 --- a/tests/nerf/dataloader/test_ray_generator.py +++ b/tests/nerf/dataloader/test_ray_generator.py @@ -9,7 +9,7 @@ from tqdm import tqdm from mattport.nerf.dataset.collate import CollateIterDataset, collate_batch_size_one from mattport.nerf.dataset.image_dataset import ImageDataset, collate_batch -from mattport.nerf.dataset.utils import get_dataset_inputs_dict +from mattport.nerf.dataset.utils import get_dataset_inputs from mattport.nerf.field_modules.ray_generator import RayGenerator from mattport.utils.io import get_absolute_path from mattport.viewer.plotly import visualize_dataset @@ -34,7 +34,7 @@ def test_dataloader(visualize=False): """Testing for the dataloader from input dataset parameters to rays.""" data_directory = "tests/data/lego_test" - dataset_type = "blender" + dataset_format = "blender" downscale_factor = 1 num_images_to_sample_from = 1 num_times_to_repeat_images = 40 @@ -42,9 +42,9 @@ def test_dataloader(visualize=False): num_workers = 0 data_directory = get_absolute_path(data_directory) - dataset_inputs = get_dataset_inputs_dict( - data_directory=data_directory, dataset_type=dataset_type, downscale_factor=downscale_factor - )["train"] + dataset_inputs = get_dataset_inputs( + data_directory=data_directory, dataset_format=dataset_format, downscale_factor=downscale_factor, split="train" + ) image_dataset = ImageDataset( image_filenames=dataset_inputs.image_filenames, downscale_factor=dataset_inputs.downscale_factor ) diff --git a/tests/nerf/test_sampler.py b/tests/nerf/test_sampler.py index 951b07c98..f69e43b8d 100644 --- a/tests/nerf/test_sampler.py +++ b/tests/nerf/test_sampler.py @@ -5,6 +5,7 @@ import torch from mattport.nerf.sampler import PDFSampler, UniformSampler from mattport.structures.rays import RayBundle +from mattport.nerf.field_modules.scene_bounds_collider import NearFarCollider def test_uniform_sampler(): @@ -12,11 +13,13 @@ def test_uniform_sampler(): near_plane = 2 far_plane = 4 num_samples = 15 - sampler = UniformSampler(near_plane=near_plane, far_plane=far_plane, num_samples=num_samples) + sampler = UniformSampler(num_samples=num_samples) origins = torch.zeros((10, 3)) directions = torch.ones_like(origins) ray_bundle = RayBundle(origins=origins, directions=directions) + collider = NearFarCollider(near_plane, far_plane) + ray_bundle = collider(ray_bundle) ray_samples = sampler(ray_bundle) @@ -34,8 +37,10 @@ def test_pdf_sampler(): origins = torch.zeros((10, 3)) directions = torch.ones_like(origins) ray_bundle = RayBundle(origins=origins, directions=directions) + collider = NearFarCollider(near_plane, far_plane) + ray_bundle = collider(ray_bundle) - uniform_sampler = UniformSampler(near_plane=near_plane, far_plane=far_plane, num_samples=num_samples) + uniform_sampler = UniformSampler(num_samples=num_samples) coarse_ray_samples = uniform_sampler(ray_bundle) weights = torch.ones((10, num_samples))