├── LICENSE ├── README.md ├── VACA_modified ├── .DS_Store ├── .ipynb_checkpoints │ ├── VACA_helper-checkpoint.py │ ├── _create_data_toy-checkpoint.py │ ├── demo-checkpoint.ipynb │ ├── environment-checkpoint.yml │ └── mvp-checkpoint.ipynb ├── LICENSE ├── README.md ├── VACA │ └── 2nodes_linear │ │ ├── test_2500_U.npy │ │ ├── test_2500_X.npy │ │ ├── train_5000_U.npy │ │ ├── train_5000_X.npy │ │ ├── valid_2500_U.npy │ │ └── valid_2500_X.npy ├── VACA_helper.py ├── __pycache__ │ ├── VACA_helper.cpython-37.pyc │ └── VACA_helper.cpython-39.pyc ├── _create_data_toy.py ├── _params │ ├── .ipynb_checkpoints │ │ ├── dataset_adult-checkpoint.yaml │ │ ├── dataset_chain-checkpoint.yaml │ │ ├── dataset_collider-checkpoint.yaml │ │ ├── dataset_german-checkpoint.yaml │ │ ├── dataset_loan-checkpoint.yaml │ │ ├── dataset_mgraph-checkpoint.yaml │ │ ├── dataset_triangle-checkpoint.yaml │ │ ├── model_carefl-checkpoint.yaml │ │ ├── model_mcvae-checkpoint.yaml │ │ ├── model_vaca-checkpoint.yaml │ │ ├── model_vaca_piwae-checkpoint.yaml │ │ └── trainer-checkpoint.yaml │ ├── dataset_adult.yaml │ ├── dataset_chain.yaml │ ├── dataset_collider.yaml │ ├── dataset_german.yaml │ ├── dataset_loan.yaml │ ├── dataset_mgraph.yaml │ ├── dataset_triangle.yaml │ ├── model_carefl.yaml │ ├── model_mcvae.yaml │ ├── model_vaca.yaml │ ├── model_vaca_piwae.yaml │ └── trainer.yaml ├── data_modules │ ├── .DS_Store │ ├── .ipynb_checkpoints │ │ ├── __init__-checkpoint.py │ │ ├── _scalers-checkpoint.py │ │ ├── het_scm-checkpoint.py │ │ └── my_toy_scm-checkpoint.py │ ├── __init__.py │ ├── __pycache__ │ │ ├── __init__.cpython-37.pyc │ │ ├── __init__.cpython-39.pyc │ │ ├── _scalers.cpython-37.pyc │ │ ├── _scalers.cpython-39.pyc │ │ ├── het_scm.cpython-37.pyc │ │ ├── het_scm.cpython-39.pyc │ │ ├── my_toy_scm.cpython-37.pyc │ │ └── my_toy_scm.cpython-39.pyc │ ├── _scalers.py │ ├── het_scm.py │ └── my_toy_scm.py ├── datasets │ ├── .DS_Store │ ├── .ipynb_checkpoints │ │ ├── _heterogeneous-checkpoint.py │ │ ├── chain-checkpoint.py │ │ ├── collider-checkpoint.py │ │ ├── toy-checkpoint.py │ │ └── utils-checkpoint.py │ ├── __init__.py │ ├── __pycache__ │ │ ├── __init__.cpython-37.pyc │ │ ├── __init__.cpython-39.pyc │ │ ├── _adjacency.cpython-37.pyc │ │ ├── _adjacency.cpython-39.pyc │ │ ├── _heterogeneous.cpython-37.pyc │ │ ├── _heterogeneous.cpython-39.pyc │ │ ├── toy.cpython-37.pyc │ │ ├── toy.cpython-39.pyc │ │ ├── transforms.cpython-37.pyc │ │ └── transforms.cpython-39.pyc │ ├── _adjacency.py │ ├── _heterogeneous.py │ ├── adult.py │ ├── chain.py │ ├── collider.py │ ├── german.py │ ├── loan.py │ ├── mgraph.py │ ├── toy.py │ ├── transforms.py │ ├── triangle.py │ └── utils.py ├── demo.ipynb ├── environment.yml ├── main.py ├── models │ ├── .DS_Store │ ├── .ipynb_checkpoints │ │ └── _evaluator-checkpoint.py │ ├── __init__.py │ ├── __pycache__ │ │ ├── __init__.cpython-37.pyc │ │ ├── __init__.cpython-39.pyc │ │ ├── _density_estimators.cpython-37.pyc │ │ ├── _density_estimators.cpython-39.pyc │ │ ├── _evaluator.cpython-37.pyc │ │ └── _evaluator.cpython-39.pyc │ ├── _density_estimators.py │ ├── _evaluator.py │ ├── _utils.py │ ├── carefl │ │ ├── .DS_Store │ │ ├── .ipynb_checkpoints │ │ │ └── carefl-checkpoint.py │ │ ├── __init__.py │ │ ├── __pycache__ │ │ │ ├── __init__.cpython-37.pyc │ │ │ ├── __init__.cpython-39.pyc │ │ │ ├── carefl.cpython-37.pyc │ │ │ └── carefl.cpython-39.pyc │ │ ├── carefl.py │ │ └── nflib │ │ │ ├── .ipynb_checkpoints │ │ │ ├── flows-checkpoint.py │ │ │ └── spline_flows-checkpoint.py │ │ │ ├── __init__.py │ │ │ ├── __pycache__ │ │ │ ├── __init__.cpython-37.pyc │ │ │ ├── __init__.cpython-39.pyc │ │ │ ├── flows.cpython-37.pyc │ │ │ ├── flows.cpython-39.pyc │ │ │ ├── nets.cpython-37.pyc │ │ │ ├── nets.cpython-39.pyc │ │ │ ├── spline_flows.cpython-37.pyc │ │ │ └── spline_flows.cpython-39.pyc │ │ │ ├── flows.py │ │ │ ├── nets.py │ │ │ └── spline_flows.py │ ├── multicvae │ │ ├── __init__.py │ │ ├── cvae_module.py │ │ └── multicvae.py │ └── vaca │ │ ├── .DS_Store │ │ ├── .ipynb_checkpoints │ │ ├── vaca-checkpoint.py │ │ └── vaca_module-checkpoint.py │ │ ├── __init__.py │ │ ├── __pycache__ │ │ ├── __init__.cpython-37.pyc │ │ ├── __init__.cpython-39.pyc │ │ ├── vaca.cpython-37.pyc │ │ ├── vaca.cpython-39.pyc │ │ ├── vaca_module.cpython-37.pyc │ │ └── vaca_module.cpython-39.pyc │ │ ├── hvaca_module.py │ │ ├── vaca.py │ │ ├── vaca_module.py │ │ └── vaca_piwae.py ├── modules │ ├── .DS_Store │ ├── __init__.py │ ├── __pycache__ │ │ ├── __init__.cpython-37.pyc │ │ ├── __init__.cpython-39.pyc │ │ ├── disjoint_gnn.cpython-37.pyc │ │ └── disjoint_gnn.cpython-39.pyc │ ├── blocks │ │ ├── .DS_Store │ │ ├── __init__.py │ │ ├── __pycache__ │ │ │ ├── __init__.cpython-37.pyc │ │ │ ├── __init__.cpython-39.pyc │ │ │ ├── disjoint_dense.cpython-37.pyc │ │ │ ├── disjoint_dense.cpython-39.pyc │ │ │ ├── disjoint_graph_conv.cpython-37.pyc │ │ │ └── disjoint_graph_conv.cpython-39.pyc │ │ ├── dense.py │ │ ├── disjoint_dense.py │ │ ├── disjoint_graph_conv.py │ │ └── pna │ │ │ ├── __init__.py │ │ │ ├── aggregators.py │ │ │ ├── disjoint_pna.py │ │ │ ├── pna.py │ │ │ └── scalers.py │ ├── dense.py │ ├── disjoint_gnn.py │ ├── disjoint_pna.py │ └── pna.py ├── mvp.ipynb ├── tests │ ├── __init__.py │ ├── test_custom_dataset.py │ ├── test_datasets.py │ ├── test_models.py │ └── test_probabilistic_model.py └── utils │ ├── .DS_Store │ ├── .ipynb_checkpoints │ ├── constants-checkpoint.py │ ├── distributions-checkpoint.py │ └── likelihoods-checkpoint.py │ ├── __init__.py │ ├── __pycache__ │ ├── __init__.cpython-37.pyc │ ├── __init__.cpython-39.pyc │ ├── _errors.cpython-37.pyc │ ├── _errors.cpython-39.pyc │ ├── activations.cpython-37.pyc │ ├── activations.cpython-39.pyc │ ├── args_parser.cpython-37.pyc │ ├── args_parser.cpython-39.pyc │ ├── constants.cpython-37.pyc │ ├── constants.cpython-39.pyc │ ├── distributions.cpython-37.pyc │ ├── distributions.cpython-39.pyc │ ├── dropout.cpython-37.pyc │ ├── dropout.cpython-39.pyc │ ├── likelihoods.cpython-37.pyc │ ├── likelihoods.cpython-39.pyc │ ├── optimizers.cpython-37.pyc │ └── optimizers.cpython-39.pyc │ ├── _errors.py │ ├── activations.py │ ├── args_parser.py │ ├── constants.py │ ├── distributions.py │ ├── dropout.py │ ├── likelihoods.py │ ├── metrics │ ├── __init__.py │ ├── __pycache__ │ │ ├── __init__.cpython-37.pyc │ │ ├── __init__.cpython-39.pyc │ │ ├── mmd.cpython-37.pyc │ │ ├── mmd.cpython-39.pyc │ │ ├── time.cpython-37.pyc │ │ └── time.cpython-39.pyc │ ├── metrics.py │ ├── mmd.py │ └── time.py │ ├── optimizers.py │ ├── probabilistic_model.py │ ├── results.py │ └── tools.py ├── all_exp.py ├── environment.yml ├── experiments ├── MMD.py ├── __init__.py ├── data_generation.py ├── exp_helper.py ├── generated_values │ ├── .DS_Store │ ├── counterfactual │ │ └── .DS_Store │ ├── interventional │ │ └── .DS_Store │ └── observational │ │ └── .DS_Store ├── images │ ├── .DS_Store │ ├── Nonadditive_5000_500.pdf │ ├── Nonlinear_5000_500.pdf │ ├── ladder_random_Nonadditive_5000_500.jpg │ ├── ladder_random_Nonadditive_5000_500.pdf │ ├── ladder_random_Nonlinear_5000_500.jpg │ └── ladder_random_Nonlinear_5000_500.pdf ├── metric_summaries │ └── .DS_Store └── structural_equations.py ├── model ├── __init__.py └── diffusion.py └── mvp.ipynb /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2025 Patrick Chao 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | #   **Interventional and Counterfactual Inference with Diffusion Models**   5 | 6 | [![arXiv](https://img.shields.io/badge/stat.ML-arXiv%3A2302.00860-b31b1b)](https://arxiv.org/abs/2302.00860) 7 | 8 | 9 | 10 | 11 | ## Abstract 12 | 13 | We consider the problem of answering observational, interventional, and counterfactual queries in a causally sufficient setting where only observational data and the causal graph are available. Utilizing the recent developments in diffusion models, we introduce diffusion-based causal models (DCM) to learn causal mechanisms, that generate unique latent encodings. These encodings enable us to directly sample under interventions and perform abduction for counterfactuals. Diffusion models are a natural fit here, since they can encode each node to a latent representation that acts as a proxy for exogenous noise. Our empirical evaluations demonstrate significant improvements over existing state-of-the-art methods for answering causal queries. Furthermore, we provide theoretical results that offer a methodology for analyzing counterfactual estimation in general encoder-decoder models, which could be useful in settings beyond our proposed approach. 14 | 15 | 16 | 17 | ## Code 18 | 19 | ### Installation 20 | Create a conda environment with the command: 21 | ```bash 22 | conda env create -f environment.yml 23 | ``` 24 | 25 | 26 | ### Example with Custom Data 27 | Diffusion based Causal Models (DCMs) can answer causal queries using observational data and the causal graph. We may consider an example with a triangle graph, where X1 causes X2, and both X1 and X2 cause X3. We may first generate a dataset. 28 | ```python 29 | import numpy as np 30 | import pandas as pd 31 | import networkx as nx 32 | from model.diffusion import create_model_from_graph 33 | import dowhy.gcm as cy 34 | from dowhy.gcm import draw_samples, interventional_samples, counterfactual_samples 35 | 36 | n = 1000 37 | # Make dataset 38 | x1 = np.random.normal(size=(n)) 39 | x2 = x1 + np.random.normal(size=(n)) 40 | x3 = x1 + x2 + np.random.normal(size=(n)) 41 | factual = pd.DataFrame({"x1" : x1, "x2" : x2, "x3" : x3}) 42 | 43 | # Make Graph 44 | graph = nx.DiGraph([('x1', 'x2'), ('x1', 'x3'), ('x2','x3')]) 45 | ``` 46 | 47 | Next, we specify parameters for our DCMs, create the model, and fit the model on the data. 48 | ```python 49 | params = {'num_epochs' : 200, 50 | 'lr' : 1e-4, 51 | 'batch_size': 64, 52 | 'hidden_dim' : 64} 53 | 54 | diff_model = create_model_from_graph(graph, params) 55 | 56 | cy.fit(diff_model, factual) 57 | ``` 58 | 59 | After we fit our model, we can ask causal queries. For example, we may perform *observational queries*: 60 | ```python 61 | # Observational Query 62 | obs_samples = draw_samples(diff_model, num_samples = 20) 63 | ``` 64 | 65 | We may also perform *interventional queries*: 66 | ```python 67 | # Interventional Query 68 | intervention = {"x1": lambda x: 2, "x2": lambda x: x - 1} 69 | int_samples = interventional_samples(diff_model, intervention, num_samples_to_draw=20) 70 | ``` 71 | 72 | And we may perform *counterfactual queries*: 73 | ```python 74 | # Counterfactual Query 75 | cf_estimates = counterfactual_samples(diff_model, intervention, observed_data = factual) 76 | cf_estimates.head() 77 | ``` 78 | 79 | For more examples, see `mvp.ipynb`. To rerun our experiments in the paper, run the following command: 80 | ```bash 81 | python3 all_exp.py 82 | ``` 83 | 84 | 85 | ## Citation 86 | 87 | If you find this work useful, please cite: 88 | 89 | ```bibtex 90 | @misc{chao2023interventional, 91 | title={Interventional and Counterfactual Inference with Diffusion Models}, 92 | author={Patrick Chao and Patrick Blöbaum and Shiva Prasad Kasiviswanathan}, 93 | year={2023}, 94 | eprint={2302.00860}, 95 | archivePrefix={arXiv}, 96 | primaryClass={stat.ML} 97 | } 98 | ``` -------------------------------------------------------------------------------- /VACA_modified/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/patrickrchao/DiffusionBasedCausalModels/33fe8010a2d7700bd715563e70c5802d748e4a29/VACA_modified/.DS_Store -------------------------------------------------------------------------------- /VACA_modified/.ipynb_checkpoints/_create_data_toy-checkpoint.py: -------------------------------------------------------------------------------- 1 | """ 2 | This file creates all the Toy datasets included in this repository 3 | """ 4 | 5 | from data_modules.het_scm import HeterogeneousSCMDataModule 6 | import os 7 | from utils.constants import Cte 8 | 9 | for data_name in [Cte.COLLIDER, Cte.MGRAPH, Cte.TRIANGLE, Cte.CHAIN]: 10 | for sem in [Cte.LINEAR, Cte.NONLINEAR, Cte.NONADDITIVE]: 11 | print(f"\n\n{data_name} {sem}") 12 | if data_name == Cte.MGRAPH: 13 | likelihood_names = 'd_d_d_d_d' 14 | else: 15 | likelihood_names = 'd_d_d' 16 | 17 | dm = HeterogeneousSCMDataModule(data_dir=os.path.join('..', 'Data'), 18 | dataset_name=data_name, 19 | equations_type=sem, 20 | num_samples_tr=5000, 21 | num_workers=0, 22 | normalize='lik', 23 | likelihood_names=likelihood_names, 24 | lambda_=0.05, 25 | normalize_A=None, 26 | seed=42, 27 | batch_size=4) 28 | 29 | dm.prepare_data() 30 | 31 | dataset = dm.train_dataset 32 | 33 | print(f"X: {dataset.X.shape}") 34 | print(f"U: {dataset.U.shape}") 35 | 36 | 37 | 38 | # %% 39 | 40 | data_name = Cte.LOAN 41 | sem = Cte.LINEAR 42 | print(f"{data_name} {sem}") 43 | dm = HeterogeneousSCMDataModule(data_dir=os.path.join('..', 'Data'), 44 | dataset_name=data_name, 45 | equations_type=sem, 46 | num_samples_tr=5000, 47 | num_workers=0, 48 | normalize='lik', 49 | likelihood_names='d_d_d_d_d_d_d', 50 | lambda_=0.05, 51 | normalize_A=None, 52 | seed=42, 53 | batch_size=4) 54 | 55 | dm.prepare_data() 56 | 57 | print(dm.train_dataset.name) 58 | 59 | dataset = dm.train_dataset 60 | 61 | print(f"X: {dataset.X.shape}") 62 | print(f"U: {dataset.U.shape}") 63 | 64 | 65 | # %% 66 | data_name = Cte.ADULT 67 | sem = Cte.LINEAR 68 | print(f"{data_name} {sem}") 69 | dm = HeterogeneousSCMDataModule(data_dir=os.path.join('..', 'Data'), 70 | dataset_name=data_name, 71 | equations_type=sem, 72 | num_samples_tr=5000, 73 | num_workers=0, 74 | normalize='lik', 75 | likelihood_names='d_d_d_d_d_d_d', 76 | lambda_=0.05, 77 | normalize_A=None, 78 | seed=42, 79 | batch_size=4) 80 | 81 | dm.prepare_data() 82 | 83 | print(dm.train_dataset.name) 84 | 85 | dataset = dm.train_dataset 86 | 87 | print(f"X: {dataset.X.shape}") 88 | print(f"U: {dataset.U.shape}") -------------------------------------------------------------------------------- /VACA_modified/.ipynb_checkpoints/environment-checkpoint.yml: -------------------------------------------------------------------------------- 1 | name: vaca 2 | channels: 3 | - defaults 4 | dependencies: 5 | - pip=21.2.4 6 | - python=3.9.0 7 | - pip: 8 | - matplotlib==3.4.3 9 | - networkx==2.6.3 10 | - numpy==1.21.2 11 | - pandas==1.3.4 12 | - pytorch-lightning==1.4.9 13 | - scikit-learn==1.0 14 | - scipy==1.7.1 15 | - seaborn==0.11.2 16 | - tensorboard==2.7.0 17 | - torch==1.9.1 18 | - torch-cluster==1.5.9 19 | - torch-geometric==2.0.1 20 | - torch-scatter==2.0.8 21 | - torch-sparse==0.6.12 22 | - torch-spline-conv==1.2.1 23 | - torchaudio==0.9.1 24 | - torchmetrics==0.5.1 25 | - torchvision==0.10.1 26 | - tqdm==4.62.3 27 | -------------------------------------------------------------------------------- /VACA_modified/LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /VACA_modified/VACA/2nodes_linear/test_2500_U.npy: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/patrickrchao/DiffusionBasedCausalModels/33fe8010a2d7700bd715563e70c5802d748e4a29/VACA_modified/VACA/2nodes_linear/test_2500_U.npy -------------------------------------------------------------------------------- /VACA_modified/VACA/2nodes_linear/test_2500_X.npy: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/patrickrchao/DiffusionBasedCausalModels/33fe8010a2d7700bd715563e70c5802d748e4a29/VACA_modified/VACA/2nodes_linear/test_2500_X.npy -------------------------------------------------------------------------------- /VACA_modified/VACA/2nodes_linear/train_5000_U.npy: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/patrickrchao/DiffusionBasedCausalModels/33fe8010a2d7700bd715563e70c5802d748e4a29/VACA_modified/VACA/2nodes_linear/train_5000_U.npy -------------------------------------------------------------------------------- /VACA_modified/VACA/2nodes_linear/train_5000_X.npy: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/patrickrchao/DiffusionBasedCausalModels/33fe8010a2d7700bd715563e70c5802d748e4a29/VACA_modified/VACA/2nodes_linear/train_5000_X.npy -------------------------------------------------------------------------------- /VACA_modified/VACA/2nodes_linear/valid_2500_U.npy: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/patrickrchao/DiffusionBasedCausalModels/33fe8010a2d7700bd715563e70c5802d748e4a29/VACA_modified/VACA/2nodes_linear/valid_2500_U.npy -------------------------------------------------------------------------------- /VACA_modified/VACA/2nodes_linear/valid_2500_X.npy: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/patrickrchao/DiffusionBasedCausalModels/33fe8010a2d7700bd715563e70c5802d748e4a29/VACA_modified/VACA/2nodes_linear/valid_2500_X.npy -------------------------------------------------------------------------------- /VACA_modified/__pycache__/VACA_helper.cpython-37.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/patrickrchao/DiffusionBasedCausalModels/33fe8010a2d7700bd715563e70c5802d748e4a29/VACA_modified/__pycache__/VACA_helper.cpython-37.pyc -------------------------------------------------------------------------------- /VACA_modified/__pycache__/VACA_helper.cpython-39.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/patrickrchao/DiffusionBasedCausalModels/33fe8010a2d7700bd715563e70c5802d748e4a29/VACA_modified/__pycache__/VACA_helper.cpython-39.pyc -------------------------------------------------------------------------------- /VACA_modified/_create_data_toy.py: -------------------------------------------------------------------------------- 1 | """ 2 | This file creates all the Toy datasets included in this repository 3 | """ 4 | 5 | from data_modules.het_scm import HeterogeneousSCMDataModule 6 | import os 7 | from utils.constants import Cte 8 | 9 | for data_name in [Cte.COLLIDER, Cte.MGRAPH, Cte.TRIANGLE, Cte.CHAIN]: 10 | for sem in [Cte.LINEAR, Cte.NONLINEAR, Cte.NONADDITIVE]: 11 | print(f"\n\n{data_name} {sem}") 12 | if data_name == Cte.MGRAPH: 13 | likelihood_names = 'd_d_d_d_d' 14 | else: 15 | likelihood_names = 'd_d_d' 16 | 17 | dm = HeterogeneousSCMDataModule(data_dir=os.path.join('..', 'Data'), 18 | dataset_name=data_name, 19 | equations_type=sem, 20 | num_samples_tr=5000, 21 | num_workers=0, 22 | normalize='lik', 23 | likelihood_names=likelihood_names, 24 | lambda_=0.05, 25 | normalize_A=None, 26 | seed=42, 27 | batch_size=4) 28 | 29 | dm.prepare_data() 30 | 31 | dataset = dm.train_dataset 32 | 33 | print(f"X: {dataset.X.shape}") 34 | print(f"U: {dataset.U.shape}") 35 | 36 | 37 | 38 | # %% 39 | 40 | data_name = Cte.LOAN 41 | sem = Cte.LINEAR 42 | print(f"{data_name} {sem}") 43 | dm = HeterogeneousSCMDataModule(data_dir=os.path.join('..', 'Data'), 44 | dataset_name=data_name, 45 | equations_type=sem, 46 | num_samples_tr=5000, 47 | num_workers=0, 48 | normalize='lik', 49 | likelihood_names='d_d_d_d_d_d_d', 50 | lambda_=0.05, 51 | normalize_A=None, 52 | seed=42, 53 | batch_size=4) 54 | 55 | dm.prepare_data() 56 | 57 | print(dm.train_dataset.name) 58 | 59 | dataset = dm.train_dataset 60 | 61 | print(f"X: {dataset.X.shape}") 62 | print(f"U: {dataset.U.shape}") 63 | 64 | 65 | # %% 66 | data_name = Cte.ADULT 67 | sem = Cte.LINEAR 68 | print(f"{data_name} {sem}") 69 | dm = HeterogeneousSCMDataModule(data_dir=os.path.join('..', 'Data'), 70 | dataset_name=data_name, 71 | equations_type=sem, 72 | num_samples_tr=5000, 73 | num_workers=0, 74 | normalize='lik', 75 | likelihood_names='d_d_d_d_d_d_d', 76 | lambda_=0.05, 77 | normalize_A=None, 78 | seed=42, 79 | batch_size=4) 80 | 81 | dm.prepare_data() 82 | 83 | print(dm.train_dataset.name) 84 | 85 | dataset = dm.train_dataset 86 | 87 | print(f"X: {dataset.X.shape}") 88 | print(f"U: {dataset.U.shape}") -------------------------------------------------------------------------------- /VACA_modified/_params/.ipynb_checkpoints/dataset_adult-checkpoint.yaml: -------------------------------------------------------------------------------- 1 | dataset: 2 | name: adult 3 | params1: 4 | data_dir: ../Data 5 | batch_size: 1000 # 1024 6 | num_workers: 0 7 | params2: # Parameters we can cross validate 8 | num_samples_tr: 5000 9 | equations_type: linear # non-linear, #non-additive 10 | normalize: lik # null, power, std 11 | likelihood_names: 'd' # 'c_d_c_b_d_d_c_c_c_c_d' 12 | lambda_: 0.05 13 | normalize_A: null # default: null -------------------------------------------------------------------------------- /VACA_modified/_params/.ipynb_checkpoints/dataset_chain-checkpoint.yaml: -------------------------------------------------------------------------------- 1 | dataset: 2 | name: chain 3 | params1: 4 | data_dir: ../Data 5 | batch_size: 1000 # 1024 6 | num_workers: 0 7 | params2: # Parameters we can cross validate 8 | num_samples_tr: 5000 9 | equations_type: linear # non-linear #non-additive 10 | normalize: lik # null, power, std 11 | likelihood_names: 'd_d_d' 12 | lambda_: 0.05 13 | normalize_A: null # default: null -------------------------------------------------------------------------------- /VACA_modified/_params/.ipynb_checkpoints/dataset_collider-checkpoint.yaml: -------------------------------------------------------------------------------- 1 | dataset: 2 | name: collider 3 | params1: 4 | data_dir: ../Data 5 | batch_size: 1000 # 1024 6 | num_workers: 0 7 | params2: # Parameters we can cross validate 8 | num_samples_tr: 5000 9 | equations_type: linear # non-linear #non-additive 10 | normalize: lik # null, power, std 11 | likelihood_names: 'd_d_d' 12 | lambda_: 0.05 13 | normalize_A: null # default: null -------------------------------------------------------------------------------- /VACA_modified/_params/.ipynb_checkpoints/dataset_german-checkpoint.yaml: -------------------------------------------------------------------------------- 1 | dataset: 2 | name: german 3 | params1: 4 | data_dir: ../Data 5 | batch_size: 100 # 1024 6 | num_workers: 0 7 | params2: # total of 1000 datapoints 8 | num_samples_tr: 800 9 | normalize: lik # null, power, std 10 | normalize_A: null # default: nul 11 | params3: 12 | train_kfold: False 13 | kfold_idx: 5 14 | 15 | -------------------------------------------------------------------------------- /VACA_modified/_params/.ipynb_checkpoints/dataset_loan-checkpoint.yaml: -------------------------------------------------------------------------------- 1 | dataset: 2 | name: loan 3 | params1: 4 | data_dir: ../Data 5 | batch_size: 1000 # 1024 6 | num_workers: 0 7 | params2: # Parameters we can cross validate 8 | num_samples_tr: 5000 9 | equations_type: linear # non-linear, #non-additive 10 | normalize: lik # null, power, std 11 | likelihood_names: 'b_d_d_d_d_d_d' 12 | lambda_: 0.05 13 | normalize_A: null # default: null -------------------------------------------------------------------------------- /VACA_modified/_params/.ipynb_checkpoints/dataset_mgraph-checkpoint.yaml: -------------------------------------------------------------------------------- 1 | dataset: 2 | name: mgraph 3 | params1: 4 | data_dir: ../Data 5 | batch_size: 1000 # 1024 6 | num_workers: 0 7 | params2: # Parameters we can cross validate 8 | num_samples_tr: 5000 9 | equations_type: linear # non-linear, #non-additive 10 | normalize: lik # null, power, std 11 | likelihood_names: 'd_d_d_d_d' 12 | lambda_: 0.05 13 | normalize_A: null # default: null -------------------------------------------------------------------------------- /VACA_modified/_params/.ipynb_checkpoints/dataset_triangle-checkpoint.yaml: -------------------------------------------------------------------------------- 1 | dataset: 2 | name: triangle 3 | params1: 4 | data_dir: ../Data 5 | batch_size: 1000 # 1024 6 | num_workers: 0 7 | params2: # Parameters we can cross validate 8 | num_samples_tr: 5000 9 | equations_type: linear # non-linear, #non-additive 10 | normalize: lik # null, power, std 11 | likelihood_names: 'd' 12 | lambda_: 0.05 13 | normalize_A: null # default: null -------------------------------------------------------------------------------- /VACA_modified/_params/.ipynb_checkpoints/model_carefl-checkpoint.yaml: -------------------------------------------------------------------------------- 1 | optimizer: 2 | name: adam 3 | params: 4 | lr: 0.005 5 | betas: [0.9, 0.999] 6 | weight_decay: 1.2e-6 # L2 regularization 7 | scheduler: 8 | name: exp_lr 9 | params: 10 | gamma: 0.99 11 | model: 12 | name: carefl #other option mfvgae, fvgae 13 | params: 14 | flow_architecture: spline 15 | flow_net_class: mlp 16 | distr_z: normal 17 | n_layers: 4 18 | n_hidden: 10 19 | 20 | -------------------------------------------------------------------------------- /VACA_modified/_params/.ipynb_checkpoints/model_mcvae-checkpoint.yaml: -------------------------------------------------------------------------------- 1 | 2 | optimizer: 3 | name: adam 4 | params: 5 | lr: 0.005 6 | betas: [0.9, 0.999] 7 | weight_decay: 1.2e-6 # L2 regularization 8 | scheduler: 9 | name: exp_lr 10 | params: 11 | gamma: 0.99 12 | model: 13 | name: mcvae #other option mfvgae, fvgae 14 | params: 15 | h_dim_list_dec: [32, 32] 16 | h_dim_list_enc: [32, 32] 17 | z_dim: 1 18 | act_name: relu # Activation name 19 | drop_rate: 0.0 20 | distr_z: normal 21 | 22 | 23 | -------------------------------------------------------------------------------- /VACA_modified/_params/.ipynb_checkpoints/model_vaca-checkpoint.yaml: -------------------------------------------------------------------------------- 1 | 2 | optimizer: 3 | name: adam 4 | params: 5 | lr: 0.005 6 | betas: [0.9, 0.999] 7 | weight_decay: 1.2e-6 # L2 regularization 8 | scheduler: 9 | name: exp_lr 10 | params: 11 | gamma: 0.99 12 | model: 13 | name: vaca 14 | params: 15 | architecture: dgnn 16 | estimator: elbo #multi elbo_iwae, simple iwaedreg, elbo, iwae 17 | h_dim_list_dec: [8, 8] 18 | h_dim_list_enc: [16] 19 | z_dim: 4 20 | distr_z: normal 21 | dropout_adj_rate: 0.0 22 | dropout_adj_pa_rate: 0.2 23 | dropout_adj_pa_prob_keep_self: 0.0 24 | residual: 0.0 25 | norm_categorical: 0 # False, True 26 | 27 | 28 | -------------------------------------------------------------------------------- /VACA_modified/_params/.ipynb_checkpoints/model_vaca_piwae-checkpoint.yaml: -------------------------------------------------------------------------------- 1 | 2 | optimizer: 3 | name: adam 4 | params: 5 | lr: 0.005 6 | betas: [0.9, 0.999] 7 | weight_decay: 1.2e-6 # L2 regularization 8 | scheduler: 9 | name: exp_lr 10 | params: 11 | gamma: 0.99 12 | model: 13 | name: vaca_piwae 14 | params: 15 | architecture: dgnn 16 | estimator: elbo_iwae #multi elbo_iwae, simple iwaedreg, elbo, iwae 17 | h_dim_list_dec: [ 16, 16 ] 18 | h_dim_list_enc: [ 16 ] 19 | z_dim: 2 20 | distr_z: normal 21 | K: 3 22 | dropout_adj_rate: 0.0 23 | dropout_adj_pa_rate: 0.2 24 | norm_categorical: 0 # False, True 25 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /VACA_modified/_params/.ipynb_checkpoints/trainer-checkpoint.yaml: -------------------------------------------------------------------------------- 1 | seed: 10 # Initialization for the random number generator 2 | root_dir: exper_test # Root folder to save the model and TensorBoard data 3 | early_stopping: True 4 | trainer: 5 | max_epochs: 500 #500 # Maximum number of epochs to train 6 | min_epochs: 100 #100 # Min number of epochs to train 7 | limit_train_batches: 1.0 8 | limit_val_batches: 0 9 | limit_test_batches: 0 10 | check_val_every_n_epoch: 0 11 | progress_bar_refresh_rate: 1 12 | flush_logs_every_n_steps: 100 13 | log_every_n_steps: 2 # How often to add logging rows (does not write to disk) 14 | precision: 32 15 | terminate_on_nan: True 16 | auto_select_gpus: True 17 | deterministic: True 18 | weights_summary: null 19 | gpus: null # number of gpus to train on (int) or which GPUs to train on (list or str) applied per node 20 | num_sanity_val_steps: 2 21 | track_grad_norm: -1 # Otherwise tracks that norm (2 for 2-norm) 22 | gradient_clip_val: 0.0 -------------------------------------------------------------------------------- /VACA_modified/_params/dataset_adult.yaml: -------------------------------------------------------------------------------- 1 | dataset: 2 | name: adult 3 | params1: 4 | data_dir: ../Data 5 | batch_size: 1000 # 1024 6 | num_workers: 0 7 | params2: # Parameters we can cross validate 8 | num_samples_tr: 5000 9 | equations_type: linear # non-linear, #non-additive 10 | normalize: lik # null, power, std 11 | likelihood_names: 'd' # 'c_d_c_b_d_d_c_c_c_c_d' 12 | lambda_: 0.05 13 | normalize_A: null # default: null -------------------------------------------------------------------------------- /VACA_modified/_params/dataset_chain.yaml: -------------------------------------------------------------------------------- 1 | dataset: 2 | name: chain 3 | params1: 4 | data_dir: ../Data 5 | batch_size: 1000 # 1024 6 | num_workers: 0 7 | params2: # Parameters we can cross validate 8 | num_samples_tr: 5000 9 | equations_type: linear # non-linear #non-additive 10 | normalize: lik # null, power, std 11 | likelihood_names: 'd_d_d' 12 | lambda_: 0.05 13 | normalize_A: null # default: null -------------------------------------------------------------------------------- /VACA_modified/_params/dataset_collider.yaml: -------------------------------------------------------------------------------- 1 | dataset: 2 | name: collider 3 | params1: 4 | data_dir: ../Data 5 | batch_size: 1000 # 1024 6 | num_workers: 0 7 | params2: # Parameters we can cross validate 8 | num_samples_tr: 5000 9 | equations_type: linear # non-linear #non-additive 10 | normalize: lik # null, power, std 11 | likelihood_names: 'd_d_d' 12 | lambda_: 0.05 13 | normalize_A: null # default: null -------------------------------------------------------------------------------- /VACA_modified/_params/dataset_german.yaml: -------------------------------------------------------------------------------- 1 | dataset: 2 | name: german 3 | params1: 4 | data_dir: ../Data 5 | batch_size: 100 # 1024 6 | num_workers: 0 7 | params2: # total of 1000 datapoints 8 | num_samples_tr: 800 9 | normalize: lik # null, power, std 10 | normalize_A: null # default: nul 11 | params3: 12 | train_kfold: False 13 | kfold_idx: 5 14 | 15 | -------------------------------------------------------------------------------- /VACA_modified/_params/dataset_loan.yaml: -------------------------------------------------------------------------------- 1 | dataset: 2 | name: loan 3 | params1: 4 | data_dir: ../Data 5 | batch_size: 1000 # 1024 6 | num_workers: 0 7 | params2: # Parameters we can cross validate 8 | num_samples_tr: 5000 9 | equations_type: linear # non-linear, #non-additive 10 | normalize: lik # null, power, std 11 | likelihood_names: 'b_d_d_d_d_d_d' 12 | lambda_: 0.05 13 | normalize_A: null # default: null -------------------------------------------------------------------------------- /VACA_modified/_params/dataset_mgraph.yaml: -------------------------------------------------------------------------------- 1 | dataset: 2 | name: mgraph 3 | params1: 4 | data_dir: ../Data 5 | batch_size: 1000 # 1024 6 | num_workers: 0 7 | params2: # Parameters we can cross validate 8 | num_samples_tr: 5000 9 | equations_type: linear # non-linear, #non-additive 10 | normalize: lik # null, power, std 11 | likelihood_names: 'd_d_d_d_d' 12 | lambda_: 0.05 13 | normalize_A: null # default: null -------------------------------------------------------------------------------- /VACA_modified/_params/dataset_triangle.yaml: -------------------------------------------------------------------------------- 1 | dataset: 2 | name: triangle 3 | params1: 4 | data_dir: ../Data 5 | batch_size: 1000 # 1024 6 | num_workers: 0 7 | params2: # Parameters we can cross validate 8 | num_samples_tr: 5000 9 | equations_type: linear # non-linear, #non-additive 10 | normalize: lik # null, power, std 11 | likelihood_names: 'd' 12 | lambda_: 0.05 13 | normalize_A: null # default: null -------------------------------------------------------------------------------- /VACA_modified/_params/model_carefl.yaml: -------------------------------------------------------------------------------- 1 | optimizer: 2 | name: adam 3 | params: 4 | lr: 0.005 5 | betas: [0.9, 0.999] 6 | weight_decay: 1.2e-6 # L2 regularization 7 | scheduler: 8 | name: exp_lr 9 | params: 10 | gamma: 0.99 11 | model: 12 | name: carefl #other option mfvgae, fvgae 13 | params: 14 | flow_architecture: spline 15 | flow_net_class: mlp 16 | distr_z: normal 17 | n_layers: 4 18 | n_hidden: 10 19 | 20 | -------------------------------------------------------------------------------- /VACA_modified/_params/model_mcvae.yaml: -------------------------------------------------------------------------------- 1 | 2 | optimizer: 3 | name: adam 4 | params: 5 | lr: 0.005 6 | betas: [0.9, 0.999] 7 | weight_decay: 1.2e-6 # L2 regularization 8 | scheduler: 9 | name: exp_lr 10 | params: 11 | gamma: 0.99 12 | model: 13 | name: mcvae #other option mfvgae, fvgae 14 | params: 15 | h_dim_list_dec: [32, 32] 16 | h_dim_list_enc: [32, 32] 17 | z_dim: 1 18 | act_name: relu # Activation name 19 | drop_rate: 0.0 20 | distr_z: normal 21 | 22 | 23 | -------------------------------------------------------------------------------- /VACA_modified/_params/model_vaca.yaml: -------------------------------------------------------------------------------- 1 | 2 | optimizer: 3 | name: adam 4 | params: 5 | lr: 0.005 6 | betas: [0.9, 0.999] 7 | weight_decay: 1.2e-6 # L2 regularization 8 | scheduler: 9 | name: exp_lr 10 | params: 11 | gamma: 0.99 12 | model: 13 | name: vaca 14 | params: 15 | architecture: dgnn 16 | estimator: elbo #multi elbo_iwae, simple iwaedreg, elbo, iwae 17 | h_dim_list_dec: [8, 8] 18 | h_dim_list_enc: [16] 19 | z_dim: 4 20 | distr_z: normal 21 | dropout_adj_rate: 0.0 22 | dropout_adj_pa_rate: 0.2 23 | dropout_adj_pa_prob_keep_self: 0.0 24 | residual: 0.0 25 | norm_categorical: 0 # False, True 26 | 27 | 28 | -------------------------------------------------------------------------------- /VACA_modified/_params/model_vaca_piwae.yaml: -------------------------------------------------------------------------------- 1 | 2 | optimizer: 3 | name: adam 4 | params: 5 | lr: 0.005 6 | betas: [0.9, 0.999] 7 | weight_decay: 1.2e-6 # L2 regularization 8 | scheduler: 9 | name: exp_lr 10 | params: 11 | gamma: 0.99 12 | model: 13 | name: vaca_piwae 14 | params: 15 | architecture: dgnn 16 | estimator: elbo_iwae #multi elbo_iwae, simple iwaedreg, elbo, iwae 17 | h_dim_list_dec: [ 16, 16 ] 18 | h_dim_list_enc: [ 16 ] 19 | z_dim: 2 20 | distr_z: normal 21 | K: 3 22 | dropout_adj_rate: 0.0 23 | dropout_adj_pa_rate: 0.2 24 | norm_categorical: 0 # False, True 25 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /VACA_modified/_params/trainer.yaml: -------------------------------------------------------------------------------- 1 | seed: 10 # Initialization for the random number generator 2 | root_dir: exper_test # Root folder to save the model and TensorBoard data 3 | early_stopping: True 4 | trainer: 5 | max_epochs: 200 # Maximum number of epochs to train 6 | min_epochs: 100 # Min number of epochs to train 7 | limit_train_batches: 1.0 8 | limit_val_batches: 0 9 | limit_test_batches: 0 10 | check_val_every_n_epoch: 0 11 | progress_bar_refresh_rate: 1 12 | flush_logs_every_n_steps: 100 13 | log_every_n_steps: 2 # How often to add logging rows (does not write to disk) 14 | precision: 32 15 | terminate_on_nan: True 16 | auto_select_gpus: True 17 | deterministic: True 18 | weights_summary: null 19 | gpus: null # number of gpus to train on (int) or which GPUs to train on (list or str) applied per node 20 | num_sanity_val_steps: 2 21 | track_grad_norm: -1 # Otherwise tracks that norm (2 for 2-norm) 22 | gradient_clip_val: 0.0 -------------------------------------------------------------------------------- /VACA_modified/data_modules/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/patrickrchao/DiffusionBasedCausalModels/33fe8010a2d7700bd715563e70c5802d748e4a29/VACA_modified/data_modules/.DS_Store -------------------------------------------------------------------------------- /VACA_modified/data_modules/.ipynb_checkpoints/__init__-checkpoint.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/patrickrchao/DiffusionBasedCausalModels/33fe8010a2d7700bd715563e70c5802d748e4a29/VACA_modified/data_modules/.ipynb_checkpoints/__init__-checkpoint.py -------------------------------------------------------------------------------- /VACA_modified/data_modules/.ipynb_checkpoints/_scalers-checkpoint.py: -------------------------------------------------------------------------------- 1 | import torch 2 | from sklearn import preprocessing 3 | 4 | flatten = lambda t: [item for sublist in t for item in sublist] 5 | 6 | 7 | class MaskedTensorStandardScaler: 8 | def __init__(self, list_dim_to_scale_x0, list_dim_to_scale, total_num_dimensions): 9 | self.list_dim_to_scale_x0 = list_dim_to_scale_x0 # [0, 1, 4, 5 ,7 ,8] 10 | self.list_dim_to_scale = list_dim_to_scale # [0, 1, 4, 5 ,7 ,8] 11 | self.total_num_dimensions = total_num_dimensions 12 | self.scaler = preprocessing.StandardScaler() 13 | 14 | def fit(self, x): 15 | if x.shape[1] != self.total_num_dimensions: 16 | self.scaler.fit(x[:, self.list_dim_to_scale_x0]) 17 | else: 18 | self.scaler.fit(x[:, self.list_dim_to_scale]) 19 | 20 | def transform(self, x): 21 | if x.shape[1] != self.total_num_dimensions: 22 | x_scaled = self.scaler.transform(x[:, self.list_dim_to_scale_x0]) 23 | x[:, self.list_dim_to_scale_x0] = x_scaled 24 | else: 25 | x_scaled = self.scaler.transform(x[:, self.list_dim_to_scale]) 26 | x[:, self.list_dim_to_scale] = x_scaled 27 | return torch.tensor(x) 28 | 29 | def inverse_transform(self, x): 30 | if x.shape[1] != self.total_num_dimensions: 31 | x_unscaled = self.scaler.inverse_transform(x[:, self.list_dim_to_scale_x0]) 32 | x[:, self.list_dim_to_scale_x0] = torch.tensor(x_unscaled) 33 | else: 34 | x_unscaled = self.scaler.inverse_transform(x[:, self.list_dim_to_scale]) 35 | x[:, self.list_dim_to_scale] = torch.tensor(x_unscaled) 36 | return x 37 | 38 | 39 | class MaskedTensorLikelihoodScaler: 40 | def __init__(self, likelihoods, mask_x0): 41 | self.likelihoods = flatten(likelihoods) 42 | self.mask_x0 = mask_x0 43 | 44 | self.total_num_dim_x0 = len(mask_x0) 45 | 46 | self.dim_list = [] 47 | for lik in self.likelihoods: 48 | self.dim_list.append(lik.domain_size) 49 | 50 | def fit(self, x): 51 | x = torch.tensor(x).type(torch.float32) 52 | x = x[:, self.mask_x0] if self.total_num_dim_x0 == x.shape[1] else x 53 | x_list = torch.split(x, split_size_or_sections=self.dim_list, dim=1) 54 | 55 | for lik_i, x_i in zip(self.likelihoods, x_list): 56 | lik_i.fit(x_i) 57 | 58 | def transform(self, x): 59 | x = torch.tensor(x).type(torch.float32) 60 | 61 | if self.total_num_dim_x0 == x.shape[1]: 62 | x_tmp = x[:, self.mask_x0] 63 | else: 64 | x_tmp = x 65 | x_list = torch.split(x_tmp, split_size_or_sections=self.dim_list, dim=1) 66 | x_norm = [] 67 | for lik_i, x_i in zip(self.likelihoods, x_list): 68 | x_norm.append(lik_i.normalize_data(x_i)) 69 | x_norm = torch.cat(x_norm, dim=1) 70 | 71 | if self.total_num_dim_x0 == x.shape[1]: 72 | x[:, self.mask_x0] = x_norm 73 | return x 74 | else: 75 | return x_norm 76 | 77 | def fit_transform(self, x): 78 | self.fit(x) 79 | return self.transform(x) 80 | 81 | def inverse_transform(self, x_norm): 82 | x_norm = torch.tensor(x_norm) 83 | #Added by Patrick 84 | x_norm = x_norm.cpu() 85 | if self.total_num_dim_x0 == x_norm.shape[1]: 86 | x_tmp = x_norm[:, self.mask_x0] 87 | else: 88 | x_tmp = x_norm 89 | 90 | x_list = torch.split(x_tmp, split_size_or_sections=self.dim_list, dim=1) 91 | x = [] 92 | for lik_i, x_i in zip(self.likelihoods, x_list): 93 | x.append(lik_i.denormalize_data(x_i)) 94 | x = torch.cat(x, dim=1) 95 | if self.total_num_dim_x0 == x.shape[1]: 96 | x_norm[:, self.mask_x0] = x 97 | return x_norm 98 | else: 99 | return x 100 | 101 | 102 | class TensorScaler: 103 | def __init__(self, scaler): 104 | self.scaler = scaler 105 | 106 | def transform(self, x): 107 | return torch.tensor(self.scaler.transform(x)) 108 | 109 | def inverse_transform(self, x): 110 | return torch.tensor(self.scaler.inverse_transform(x)) 111 | 112 | 113 | -------------------------------------------------------------------------------- /VACA_modified/data_modules/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/patrickrchao/DiffusionBasedCausalModels/33fe8010a2d7700bd715563e70c5802d748e4a29/VACA_modified/data_modules/__init__.py -------------------------------------------------------------------------------- /VACA_modified/data_modules/__pycache__/__init__.cpython-37.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/patrickrchao/DiffusionBasedCausalModels/33fe8010a2d7700bd715563e70c5802d748e4a29/VACA_modified/data_modules/__pycache__/__init__.cpython-37.pyc -------------------------------------------------------------------------------- /VACA_modified/data_modules/__pycache__/__init__.cpython-39.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/patrickrchao/DiffusionBasedCausalModels/33fe8010a2d7700bd715563e70c5802d748e4a29/VACA_modified/data_modules/__pycache__/__init__.cpython-39.pyc -------------------------------------------------------------------------------- /VACA_modified/data_modules/__pycache__/_scalers.cpython-37.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/patrickrchao/DiffusionBasedCausalModels/33fe8010a2d7700bd715563e70c5802d748e4a29/VACA_modified/data_modules/__pycache__/_scalers.cpython-37.pyc -------------------------------------------------------------------------------- /VACA_modified/data_modules/__pycache__/_scalers.cpython-39.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/patrickrchao/DiffusionBasedCausalModels/33fe8010a2d7700bd715563e70c5802d748e4a29/VACA_modified/data_modules/__pycache__/_scalers.cpython-39.pyc -------------------------------------------------------------------------------- /VACA_modified/data_modules/__pycache__/het_scm.cpython-37.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/patrickrchao/DiffusionBasedCausalModels/33fe8010a2d7700bd715563e70c5802d748e4a29/VACA_modified/data_modules/__pycache__/het_scm.cpython-37.pyc -------------------------------------------------------------------------------- /VACA_modified/data_modules/__pycache__/het_scm.cpython-39.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/patrickrchao/DiffusionBasedCausalModels/33fe8010a2d7700bd715563e70c5802d748e4a29/VACA_modified/data_modules/__pycache__/het_scm.cpython-39.pyc -------------------------------------------------------------------------------- /VACA_modified/data_modules/__pycache__/my_toy_scm.cpython-37.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/patrickrchao/DiffusionBasedCausalModels/33fe8010a2d7700bd715563e70c5802d748e4a29/VACA_modified/data_modules/__pycache__/my_toy_scm.cpython-37.pyc -------------------------------------------------------------------------------- /VACA_modified/data_modules/__pycache__/my_toy_scm.cpython-39.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/patrickrchao/DiffusionBasedCausalModels/33fe8010a2d7700bd715563e70c5802d748e4a29/VACA_modified/data_modules/__pycache__/my_toy_scm.cpython-39.pyc -------------------------------------------------------------------------------- /VACA_modified/data_modules/_scalers.py: -------------------------------------------------------------------------------- 1 | import torch 2 | from sklearn import preprocessing 3 | 4 | flatten = lambda t: [item for sublist in t for item in sublist] 5 | 6 | 7 | class MaskedTensorStandardScaler: 8 | def __init__(self, list_dim_to_scale_x0, list_dim_to_scale, total_num_dimensions): 9 | self.list_dim_to_scale_x0 = list_dim_to_scale_x0 # [0, 1, 4, 5 ,7 ,8] 10 | self.list_dim_to_scale = list_dim_to_scale # [0, 1, 4, 5 ,7 ,8] 11 | self.total_num_dimensions = total_num_dimensions 12 | self.scaler = preprocessing.StandardScaler() 13 | 14 | def fit(self, x): 15 | if x.shape[1] != self.total_num_dimensions: 16 | self.scaler.fit(x[:, self.list_dim_to_scale_x0]) 17 | else: 18 | self.scaler.fit(x[:, self.list_dim_to_scale]) 19 | 20 | def transform(self, x): 21 | if x.shape[1] != self.total_num_dimensions: 22 | x_scaled = self.scaler.transform(x[:, self.list_dim_to_scale_x0]) 23 | x[:, self.list_dim_to_scale_x0] = x_scaled 24 | else: 25 | x_scaled = self.scaler.transform(x[:, self.list_dim_to_scale]) 26 | x[:, self.list_dim_to_scale] = x_scaled 27 | return torch.tensor(x) 28 | 29 | def inverse_transform(self, x): 30 | if x.shape[1] != self.total_num_dimensions: 31 | x_unscaled = self.scaler.inverse_transform(x[:, self.list_dim_to_scale_x0]) 32 | x[:, self.list_dim_to_scale_x0] = torch.tensor(x_unscaled) 33 | else: 34 | x_unscaled = self.scaler.inverse_transform(x[:, self.list_dim_to_scale]) 35 | x[:, self.list_dim_to_scale] = torch.tensor(x_unscaled) 36 | return x 37 | 38 | 39 | class MaskedTensorLikelihoodScaler: 40 | def __init__(self, likelihoods, mask_x0): 41 | self.likelihoods = flatten(likelihoods) 42 | self.mask_x0 = mask_x0 43 | 44 | self.total_num_dim_x0 = len(mask_x0) 45 | 46 | self.dim_list = [] 47 | for lik in self.likelihoods: 48 | self.dim_list.append(lik.domain_size) 49 | 50 | def fit(self, x): 51 | x = torch.tensor(x).type(torch.float32) 52 | x = x[:, self.mask_x0] if self.total_num_dim_x0 == x.shape[1] else x 53 | x_list = torch.split(x, split_size_or_sections=self.dim_list, dim=1) 54 | 55 | for lik_i, x_i in zip(self.likelihoods, x_list): 56 | lik_i.fit(x_i) 57 | 58 | def transform(self, x): 59 | x = torch.tensor(x).type(torch.float32) 60 | 61 | if self.total_num_dim_x0 == x.shape[1]: 62 | x_tmp = x[:, self.mask_x0] 63 | else: 64 | x_tmp = x 65 | x_list = torch.split(x_tmp, split_size_or_sections=self.dim_list, dim=1) 66 | x_norm = [] 67 | for lik_i, x_i in zip(self.likelihoods, x_list): 68 | x_norm.append(lik_i.normalize_data(x_i)) 69 | x_norm = torch.cat(x_norm, dim=1) 70 | 71 | if self.total_num_dim_x0 == x.shape[1]: 72 | x[:, self.mask_x0] = x_norm 73 | return x 74 | else: 75 | return x_norm 76 | 77 | def fit_transform(self, x): 78 | self.fit(x) 79 | return self.transform(x) 80 | 81 | def inverse_transform(self, x_norm): 82 | x_norm = torch.tensor(x_norm) 83 | #Added by Patrick 84 | x_norm = x_norm.cpu() 85 | if self.total_num_dim_x0 == x_norm.shape[1]: 86 | x_tmp = x_norm[:, self.mask_x0] 87 | else: 88 | x_tmp = x_norm 89 | 90 | x_list = torch.split(x_tmp, split_size_or_sections=self.dim_list, dim=1) 91 | x = [] 92 | for lik_i, x_i in zip(self.likelihoods, x_list): 93 | x.append(lik_i.denormalize_data(x_i)) 94 | x = torch.cat(x, dim=1) 95 | if self.total_num_dim_x0 == x.shape[1]: 96 | x_norm[:, self.mask_x0] = x 97 | return x_norm 98 | else: 99 | return x 100 | 101 | 102 | class TensorScaler: 103 | def __init__(self, scaler): 104 | self.scaler = scaler 105 | 106 | def transform(self, x): 107 | return torch.tensor(self.scaler.transform(x)) 108 | 109 | def inverse_transform(self, x): 110 | return torch.tensor(self.scaler.inverse_transform(x)) 111 | 112 | 113 | -------------------------------------------------------------------------------- /VACA_modified/datasets/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/patrickrchao/DiffusionBasedCausalModels/33fe8010a2d7700bd715563e70c5802d748e4a29/VACA_modified/datasets/.DS_Store -------------------------------------------------------------------------------- /VACA_modified/datasets/.ipynb_checkpoints/chain-checkpoint.py: -------------------------------------------------------------------------------- 1 | from datasets.toy import ToySCM 2 | from utils.constants import Cte 3 | from utils.distributions import * 4 | 5 | 6 | class ChainSCM(ToySCM): 7 | 8 | def __init__(self, root_dir, 9 | split: str = 'train', 10 | num_samples: int = 5000, 11 | equations_type=Cte.LINEAR, 12 | likelihood_names: str = 'd_d_cb', 13 | transform=None, 14 | **kwargs 15 | ): 16 | 17 | if equations_type == Cte.LINEAR: 18 | structural_eq = { 19 | 'x1': lambda u1: u1, 20 | 'x2': lambda u2, x1: -x1 + u2, 21 | 'x3': lambda u3, x2: 0.25 * x2 + u3, 22 | } 23 | noises_distr = { 24 | 'x1': MixtureOfGaussians(probs=[0.5, 0.5], means=[-2, 1.5], vars=[1.5, 1]), 25 | 'x2': Normal(0, 1), 26 | 'x3': Normal(0, 1), 27 | } 28 | elif equations_type == Cte.NONLINEAR: 29 | structural_eq = { 30 | 'x1': lambda u1: u1, 31 | 'x2': lambda u2, x1: -x1 + u2, 32 | 'x3': lambda u3, x2: 0.25 * x2 + u3, 33 | } 34 | noises_distr = { 35 | 'x1': MixtureOfGaussians(probs=[0.5, 0.5], means=[-2, 1.5], vars=[1.5, 1]), 36 | 'x2': Normal(0, 1), 37 | 'x3': Normal(0, 1), 38 | } 39 | elif equations_type == Cte.NONADDITIVE: 40 | structural_eq = { 41 | 'x1': lambda u1: u1, 42 | 'x2': lambda u2, x1: -x1 + u2, 43 | 'x3': lambda u3, x2: 0.25 * x2 + u3, 44 | } 45 | noises_distr = { 46 | 'x1': MixtureOfGaussians(probs=[0.5, 0.5], means=[-2, 1.5], vars=[1.5, 1]), 47 | 'x2': Normal(0, 1), 48 | 'x3': Normal(0, 1), 49 | } 50 | else: 51 | raise NotImplementedError 52 | 53 | super().__init__(root_dir=root_dir, 54 | name=Cte.CHAIN, 55 | eq_type=equations_type, 56 | nodes_to_intervene=['x1', 'x2'], 57 | structural_eq=structural_eq, 58 | noises_distr=noises_distr, 59 | adj_edges={'x1': ['x2'], 60 | 'x2': ['x3'], 61 | 'x3': []}, 62 | split=split, 63 | num_samples=num_samples, 64 | likelihood_names=likelihood_names, 65 | transform=transform, 66 | **kwargs, 67 | ) 68 | -------------------------------------------------------------------------------- /VACA_modified/datasets/.ipynb_checkpoints/collider-checkpoint.py: -------------------------------------------------------------------------------- 1 | from datasets.toy import ToySCM 2 | from utils.constants import Cte 3 | from utils.distributions import * 4 | 5 | 6 | class ColliderSCM(ToySCM): 7 | 8 | def __init__(self, root_dir, 9 | split: str = 'train', 10 | num_samples: int = 5000, 11 | equations_type=Cte.LINEAR, 12 | likelihood_names: str = 'd_d_cb', 13 | transform=None, 14 | **kwargs 15 | ): 16 | 17 | if equations_type == Cte.LINEAR: 18 | structural_eq = { 19 | 'x1': lambda u1: u1, 20 | 'x2': lambda u2: u2, 21 | 'x3': lambda u3, x1, x2: 0.05 * x1 + 0.25 * x2 + u3, 22 | } 23 | 24 | noises_distr = { 25 | 'x1': MixtureOfGaussians(probs=[0.5, 0.5], means=[-2, 1.5], vars=[1.5, 1]), 26 | 'x2': Normal(0, 1), 27 | 'x3': Normal(0, 1), 28 | } 29 | 30 | elif equations_type == Cte.NONLINEAR: 31 | structural_eq = { 32 | 'x1': lambda u1: u1, 33 | 'x2': lambda u2: u2, 34 | 'x3': lambda u3, x1, x2: 0.05 * x1 + 0.25 * x2 ** 2 + u3, 35 | } 36 | 37 | noises_distr = { 38 | 'x1': MixtureOfGaussians(probs=[0.5, 0.5], means=[-2, 1.5], vars=[1.5, 1]), 39 | 'x2': Normal(0, 0.1), 40 | 'x3': Normal(0, 1), 41 | } 42 | 43 | elif equations_type == Cte.NONADDITIVE: 44 | structural_eq = { 45 | 'x1': lambda u1: u1, 46 | 'x2': lambda u2: u2, 47 | 'x3': lambda u3, x1, x2: -1 + 0.1 * np.sign(u3) * (x1 ** 2 + x2 ** 2) * u3, 48 | } 49 | 50 | noises_distr = { 51 | 'x1': MixtureOfGaussians(probs=[0.5, 0.5], means=[-2.5, 2.5], vars=[1, 1]), 52 | 'x2': Normal(0, 0.25), 53 | 'x3': Normal(0, 0.25 ** 2), 54 | } 55 | else: 56 | raise NotImplementedError 57 | 58 | super().__init__(root_dir=root_dir, 59 | name=Cte.COLLIDER, 60 | eq_type=equations_type, 61 | nodes_to_intervene=['x1', 'x2'], 62 | structural_eq=structural_eq, 63 | noises_distr=noises_distr, 64 | adj_edges={'x1': ['x3'], 65 | 'x2': ['x3'], 66 | 'x3': []}, 67 | split=split, 68 | num_samples=num_samples, 69 | likelihood_names=likelihood_names, 70 | transform=transform, 71 | **kwargs 72 | ) 73 | -------------------------------------------------------------------------------- /VACA_modified/datasets/.ipynb_checkpoints/toy-checkpoint.py: -------------------------------------------------------------------------------- 1 | from datasets._heterogeneous import HeterogeneousSCM 2 | 3 | flatten = lambda t: [item for sublist in t for item in sublist] 4 | 5 | from utils.distributions import * 6 | from utils.constants import Cte 7 | import os 8 | from utils.args_parser import mkdir 9 | 10 | 11 | class ToySCM(HeterogeneousSCM): 12 | 13 | def __init__(self, root_dir, 14 | name: str = 'chain', 15 | eq_type: str = Cte.LINEAR, 16 | nodes_to_intervene: list = None, 17 | structural_eq: dict = None, 18 | noises_distr: dict = None, 19 | adj_edges: dict = None, 20 | split: str = 'train', 21 | num_samples: int = 5000, 22 | likelihood_names: str = 'd_d_cb', 23 | lambda_: float = 0.05, 24 | transform=None, 25 | nodes_list=None, 26 | ): 27 | assert split in ['train', 'valid', 'test'] 28 | 29 | self.split = split 30 | self.name = name 31 | self._num_samples = num_samples 32 | self.eq_type = eq_type 33 | num_nodes = len(structural_eq) 34 | 35 | likelihood_names = likelihood_names.split('_') 36 | if len(likelihood_names) == 1: 37 | likelihood_names = [likelihood_names[0], ] * num_nodes 38 | self.likelihood_names = likelihood_names 39 | assert num_nodes == len(noises_distr) 40 | assert num_nodes == len(adj_edges) 41 | assert num_nodes == len(self.likelihood_names) 42 | 43 | if nodes_list is None: 44 | nodes_list = [f'x{i + 1}' for i in range(num_nodes)] 45 | 46 | assert set(structural_eq.keys()) == set(noises_distr.keys()), 'Keys for the SE and Noise distribution should be the same' 47 | #for key_eq, key_n in zip(structural_eq.keys(), noises_distr.keys()): 48 | # assert key_eq == key_n, 'Keys for the SE and Noise distribution should be the same' 49 | super().__init__(root_dir=root_dir, 50 | transform=transform, 51 | nodes_to_intervene=nodes_to_intervene, 52 | nodes_list=nodes_list, 53 | adj_edges=adj_edges, 54 | structural_eq=structural_eq, 55 | noises_distr=noises_distr, 56 | lambda_=lambda_ 57 | ) 58 | 59 | @property 60 | def likelihoods(self): 61 | likelihoods_tmp = [] 62 | 63 | for i, lik_name in enumerate(self.likelihood_names): 64 | likelihoods_tmp.append([self._get_lik(lik_name, 65 | dim=1, 66 | normalize='dim')]) 67 | 68 | return likelihoods_tmp 69 | 70 | @property 71 | def std_list(self): 72 | return [-1, -0.5, 0, 0.5, 1] 73 | 74 | def _create_data(self, new_data): 75 | X = np.zeros([self._num_samples, self.num_dimensions]) 76 | U = np.zeros([self._num_samples, self.num_nodes]) 77 | 78 | folder = mkdir(os.path.join(self.root_dir, f'{self.name}_{self.eq_type}')) 79 | 80 | X_file = os.path.join(folder, f'{self.split}_{self._num_samples}_X.npy') 81 | U_file = os.path.join(folder, f'{self.split}_{self._num_samples}_U.npy') 82 | if (not new_data) and os.path.exists(X_file) and os.path.exists(U_file): 83 | X = np.load(X_file) 84 | U = np.load(U_file) 85 | np.save(X_file, X) 86 | np.save(U_file, U) 87 | else: 88 | for i in range(self._num_samples): 89 | x, u = self.sample() 90 | X[i, :] = x 91 | U[i, :] = u 92 | 93 | 94 | self.X = X.astype(np.float32) 95 | self.U = U.astype(np.float32) 96 | 97 | def node_is_image(self): 98 | return [False for _ in self.num_nodes] 99 | 100 | 101 | def create_toy_dataset(root_dir, 102 | name: str = 'chain', 103 | eq_type: str = Cte.LINEAR, 104 | nodes_to_intervene: list = None, 105 | structural_eq: dict = None, 106 | noises_distr: dict = None, 107 | adj_edges: dict = None, 108 | split: str = 'train', 109 | num_samples: int = 5000, 110 | likelihood_names: str = 'd_d_cb', 111 | lambda_: float = 0.05, 112 | transform=None): 113 | 114 | return ToySCM(root_dir, 115 | name, 116 | eq_type, 117 | nodes_to_intervene, 118 | structural_eq, 119 | noises_distr, 120 | adj_edges, 121 | split, 122 | num_samples, 123 | likelihood_names, 124 | lambda_=lambda_, 125 | transform=transform) 126 | -------------------------------------------------------------------------------- /VACA_modified/datasets/.ipynb_checkpoints/utils-checkpoint.py: -------------------------------------------------------------------------------- 1 | import networkx as nx 2 | import numpy as np 3 | import pandas as pd 4 | import torch 5 | import torch_geometric 6 | 7 | 8 | def normalize_adj(adj, how='sym'): 9 | if how == 'sym': 10 | deg = adj.sum(1) 11 | deg_inv = 1 / np.sqrt(deg) 12 | deg_inv[deg_inv == float('inf')] = 0 13 | D_inv = np.diag(deg_inv) 14 | return D_inv @ adj @ D_inv 15 | elif how == 'row': # Normalize by children 16 | deg = adj.sum(1) 17 | deg_inv = 1 / deg 18 | deg_inv[deg_inv == float('inf')] = 0 19 | D_inv = np.diag(deg_inv) 20 | return D_inv @ adj 21 | elif how == 'col': # Normalize by parents 22 | deg = adj.sum(0) 23 | deg_inv = 1 / deg 24 | deg_inv[deg_inv == float('inf')] = 0 25 | D_inv = np.diag(deg_inv) 26 | return adj @ D_inv 27 | 28 | 29 | def convert_nodes_to_df(G, node_id_col='node_id'): 30 | data = [] 31 | for node_id, attr_dict in G.nodes(True): 32 | my_dict = {node_id_col: node_id} 33 | my_dict.update(attr_dict) 34 | data.append(my_dict) 35 | return pd.DataFrame(data) 36 | 37 | 38 | def convert_edges_to_df(G): 39 | data = [] 40 | for node_src, node_dst in G.edges: 41 | my_dict = {'node_src': node_src, 'node_dst': node_dst} 42 | my_dict.update(G.edges[(node_src, node_dst)]) 43 | data.append(my_dict) 44 | return pd.DataFrame(data) 45 | 46 | 47 | def from_networkx(G): 48 | r"""Converts a :obj:`networkx.Graph` or :obj:`networkx.DiGraph` to a 49 | :class:`torch_geometric.data.Data` instance. 50 | 51 | Args: 52 | G (networkx.Graph or networkx.DiGraph): A networkx graph. 53 | """ 54 | 55 | G = nx.convert_node_labels_to_integers(G) 56 | G = G.to_directed() if not nx.is_directed(G) else G 57 | edge_index = torch.tensor(list(G.edges)).t().contiguous() 58 | 59 | data = {} 60 | 61 | for i, (_, feat_dict) in enumerate(G.nodes(data=True)): 62 | for key, value in feat_dict.items(): 63 | data[str(key)] = [value] if i == 0 else data[str(key)] + [value] 64 | 65 | for key, item in data.items(): 66 | try: 67 | data[key] = torch.tensor(item) 68 | except ValueError: 69 | pass 70 | 71 | data['edge_index'] = edge_index.view(2, -1) 72 | data = torch_geometric.data.Data.from_dict(data) 73 | data.num_nodes = G.number_of_nodes() 74 | 75 | return data 76 | -------------------------------------------------------------------------------- /VACA_modified/datasets/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/patrickrchao/DiffusionBasedCausalModels/33fe8010a2d7700bd715563e70c5802d748e4a29/VACA_modified/datasets/__init__.py -------------------------------------------------------------------------------- /VACA_modified/datasets/__pycache__/__init__.cpython-37.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/patrickrchao/DiffusionBasedCausalModels/33fe8010a2d7700bd715563e70c5802d748e4a29/VACA_modified/datasets/__pycache__/__init__.cpython-37.pyc -------------------------------------------------------------------------------- /VACA_modified/datasets/__pycache__/__init__.cpython-39.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/patrickrchao/DiffusionBasedCausalModels/33fe8010a2d7700bd715563e70c5802d748e4a29/VACA_modified/datasets/__pycache__/__init__.cpython-39.pyc -------------------------------------------------------------------------------- /VACA_modified/datasets/__pycache__/_adjacency.cpython-37.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/patrickrchao/DiffusionBasedCausalModels/33fe8010a2d7700bd715563e70c5802d748e4a29/VACA_modified/datasets/__pycache__/_adjacency.cpython-37.pyc -------------------------------------------------------------------------------- /VACA_modified/datasets/__pycache__/_adjacency.cpython-39.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/patrickrchao/DiffusionBasedCausalModels/33fe8010a2d7700bd715563e70c5802d748e4a29/VACA_modified/datasets/__pycache__/_adjacency.cpython-39.pyc -------------------------------------------------------------------------------- /VACA_modified/datasets/__pycache__/_heterogeneous.cpython-37.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/patrickrchao/DiffusionBasedCausalModels/33fe8010a2d7700bd715563e70c5802d748e4a29/VACA_modified/datasets/__pycache__/_heterogeneous.cpython-37.pyc -------------------------------------------------------------------------------- /VACA_modified/datasets/__pycache__/_heterogeneous.cpython-39.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/patrickrchao/DiffusionBasedCausalModels/33fe8010a2d7700bd715563e70c5802d748e4a29/VACA_modified/datasets/__pycache__/_heterogeneous.cpython-39.pyc -------------------------------------------------------------------------------- /VACA_modified/datasets/__pycache__/toy.cpython-37.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/patrickrchao/DiffusionBasedCausalModels/33fe8010a2d7700bd715563e70c5802d748e4a29/VACA_modified/datasets/__pycache__/toy.cpython-37.pyc -------------------------------------------------------------------------------- /VACA_modified/datasets/__pycache__/toy.cpython-39.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/patrickrchao/DiffusionBasedCausalModels/33fe8010a2d7700bd715563e70c5802d748e4a29/VACA_modified/datasets/__pycache__/toy.cpython-39.pyc -------------------------------------------------------------------------------- /VACA_modified/datasets/__pycache__/transforms.cpython-37.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/patrickrchao/DiffusionBasedCausalModels/33fe8010a2d7700bd715563e70c5802d748e4a29/VACA_modified/datasets/__pycache__/transforms.cpython-37.pyc -------------------------------------------------------------------------------- /VACA_modified/datasets/__pycache__/transforms.cpython-39.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/patrickrchao/DiffusionBasedCausalModels/33fe8010a2d7700bd715563e70c5802d748e4a29/VACA_modified/datasets/__pycache__/transforms.cpython-39.pyc -------------------------------------------------------------------------------- /VACA_modified/datasets/_adjacency.py: -------------------------------------------------------------------------------- 1 | 2 | from torch_geometric.utils import dense_to_sparse 3 | import torch 4 | 5 | import networkx as nx 6 | class Adjacency: 7 | def __init__(self, adj): 8 | 9 | self.adj_matrix = adj 10 | 11 | self.num_nodes = adj.shape[0] 12 | self.num_edges = int(adj.sum()) 13 | 14 | self.edge_index, _ = dense_to_sparse(torch.tensor(self.adj_matrix)) 15 | self.edge_attr = torch.eye(self.num_edges, self.num_edges) 16 | 17 | 18 | # Adjacency intervention 19 | self.adj_matrix_i = None 20 | self.edge_attr_i = None 21 | self.edge_index_i = None 22 | 23 | 24 | return 25 | 26 | 27 | 28 | def set_diagonal(self): 29 | 30 | self.adj_matrix_i = torch.eye(self.num_nodes) 31 | 32 | self.edge_index_i, _ = dense_to_sparse(torch.tensor(self.adj_matrix_i)) 33 | 34 | edge_attr_i = [] 35 | for i in range(self.edge_index_i.shape[1]): 36 | for j in range(self.num_edges): 37 | if all(self.edge_index_i[:, i] == self.edge_index[:, j]): 38 | edge_attr_i.append(self.edge_attr[j]) 39 | break 40 | 41 | 42 | self.edge_attr_i = torch.stack(edge_attr_i, 0) 43 | 44 | 45 | 46 | 47 | 48 | def set_intervention(self, node_id_list, add_self_loop=True): 49 | 50 | self.adj_matrix_i = self.adj_matrix.copy() 51 | for node_id in node_id_list: 52 | self.adj_matrix_i[:, node_id] = 0.0 53 | if add_self_loop: self.adj_matrix_i[node_id, node_id] = 1.0 54 | 55 | self.edge_index_i, _ = dense_to_sparse(torch.tensor(self.adj_matrix_i)) 56 | edge_attr_i = [] 57 | for i in range(self.edge_index_i.shape[1]): 58 | for j in range(self.num_edges): 59 | if all(self.edge_index_i[:, i] == self.edge_index[:, j]): 60 | edge_attr_i.append(self.edge_attr[j]) 61 | break 62 | 63 | self.edge_attr_i = torch.stack(edge_attr_i, 0) 64 | 65 | def clean_intervention(self): 66 | # Adjacency intervention 67 | self.adj_matrix_i = None 68 | self.edge_attr_i = None 69 | self.edge_index_i = None 70 | 71 | -------------------------------------------------------------------------------- /VACA_modified/datasets/chain.py: -------------------------------------------------------------------------------- 1 | from datasets.toy import ToySCM 2 | from utils.constants import Cte 3 | from utils.distributions import * 4 | 5 | 6 | class ChainSCM(ToySCM): 7 | 8 | def __init__(self, root_dir, 9 | split: str = 'train', 10 | num_samples: int = 5000, 11 | equations_type=Cte.LINEAR, 12 | likelihood_names: str = 'd_d_cb', 13 | transform=None, 14 | **kwargs 15 | ): 16 | 17 | if equations_type == Cte.LINEAR: 18 | structural_eq = { 19 | 'x1': lambda u1: u1, 20 | 'x2': lambda u2, x1: -x1 + u2, 21 | 'x3': lambda u3, x2: 0.25 * x2 + u3, 22 | } 23 | noises_distr = { 24 | 'x1': MixtureOfGaussians(probs=[0.5, 0.5], means=[-2, 1.5], vars=[1.5, 1]), 25 | 'x2': Normal(0, 1), 26 | 'x3': Normal(0, 1), 27 | } 28 | elif equations_type == Cte.NONLINEAR: 29 | structural_eq = { 30 | 'x1': lambda u1: u1, 31 | 'x2': lambda u2, x1: -x1 + u2, 32 | 'x3': lambda u3, x2: 0.25 * x2 + u3, 33 | } 34 | noises_distr = { 35 | 'x1': MixtureOfGaussians(probs=[0.5, 0.5], means=[-2, 1.5], vars=[1.5, 1]), 36 | 'x2': Normal(0, 1), 37 | 'x3': Normal(0, 1), 38 | } 39 | elif equations_type == Cte.NONADDITIVE: 40 | structural_eq = { 41 | 'x1': lambda u1: u1, 42 | 'x2': lambda u2, x1: -x1 + u2, 43 | 'x3': lambda u3, x2: 0.25 * x2 + u3, 44 | } 45 | noises_distr = { 46 | 'x1': MixtureOfGaussians(probs=[0.5, 0.5], means=[-2, 1.5], vars=[1.5, 1]), 47 | 'x2': Normal(0, 1), 48 | 'x3': Normal(0, 1), 49 | } 50 | else: 51 | raise NotImplementedError 52 | 53 | super().__init__(root_dir=root_dir, 54 | name=Cte.CHAIN, 55 | eq_type=equations_type, 56 | nodes_to_intervene=['x1', 'x2'], 57 | structural_eq=structural_eq, 58 | noises_distr=noises_distr, 59 | adj_edges={'x1': ['x2'], 60 | 'x2': ['x3'], 61 | 'x3': []}, 62 | split=split, 63 | num_samples=num_samples, 64 | likelihood_names=likelihood_names, 65 | transform=transform, 66 | **kwargs, 67 | ) 68 | -------------------------------------------------------------------------------- /VACA_modified/datasets/collider.py: -------------------------------------------------------------------------------- 1 | from datasets.toy import ToySCM 2 | from utils.constants import Cte 3 | from utils.distributions import * 4 | 5 | 6 | class ColliderSCM(ToySCM): 7 | 8 | def __init__(self, root_dir, 9 | split: str = 'train', 10 | num_samples: int = 5000, 11 | equations_type=Cte.LINEAR, 12 | likelihood_names: str = 'd_d_cb', 13 | transform=None, 14 | **kwargs 15 | ): 16 | 17 | if equations_type == Cte.LINEAR: 18 | structural_eq = { 19 | 'x1': lambda u1: u1, 20 | 'x2': lambda u2: u2, 21 | 'x3': lambda u3, x1, x2: 0.05 * x1 + 0.25 * x2 + u3, 22 | } 23 | 24 | noises_distr = { 25 | 'x1': MixtureOfGaussians(probs=[0.5, 0.5], means=[-2, 1.5], vars=[1.5, 1]), 26 | 'x2': Normal(0, 1), 27 | 'x3': Normal(0, 1), 28 | } 29 | 30 | elif equations_type == Cte.NONLINEAR: 31 | structural_eq = { 32 | 'x1': lambda u1: u1, 33 | 'x2': lambda u2: u2, 34 | 'x3': lambda u3, x1, x2: 0.05 * x1 + 0.25 * x2 ** 2 + u3, 35 | } 36 | 37 | noises_distr = { 38 | 'x1': MixtureOfGaussians(probs=[0.5, 0.5], means=[-2, 1.5], vars=[1.5, 1]), 39 | 'x2': Normal(0, 0.1), 40 | 'x3': Normal(0, 1), 41 | } 42 | 43 | elif equations_type == Cte.NONADDITIVE: 44 | structural_eq = { 45 | 'x1': lambda u1: u1, 46 | 'x2': lambda u2: u2, 47 | 'x3': lambda u3, x1, x2: -1 + 0.1 * np.sign(u3) * (x1 ** 2 + x2 ** 2) * u3, 48 | } 49 | 50 | noises_distr = { 51 | 'x1': MixtureOfGaussians(probs=[0.5, 0.5], means=[-2.5, 2.5], vars=[1, 1]), 52 | 'x2': Normal(0, 0.25), 53 | 'x3': Normal(0, 0.25 ** 2), 54 | } 55 | else: 56 | raise NotImplementedError 57 | 58 | super().__init__(root_dir=root_dir, 59 | name=Cte.COLLIDER, 60 | eq_type=equations_type, 61 | nodes_to_intervene=['x1', 'x2'], 62 | structural_eq=structural_eq, 63 | noises_distr=noises_distr, 64 | adj_edges={'x1': ['x3'], 65 | 'x2': ['x3'], 66 | 'x3': []}, 67 | split=split, 68 | num_samples=num_samples, 69 | likelihood_names=likelihood_names, 70 | transform=transform, 71 | **kwargs 72 | ) 73 | -------------------------------------------------------------------------------- /VACA_modified/datasets/german.py: -------------------------------------------------------------------------------- 1 | import os 2 | 3 | from datasets._heterogeneous import HeterogeneousSCM 4 | from utils.distributions import * 5 | 6 | 7 | class GermanSCM(HeterogeneousSCM): 8 | 9 | def __init__(self, root_dir, 10 | split: str = 'train', 11 | num_samples_tr: int = 800, 12 | lambda_: float = 0.05, 13 | transform=None, 14 | ): 15 | assert split in ['train', 'valid', 'test'] 16 | 17 | self.name = 'german' 18 | self.split = split 19 | self.num_samples_tr = num_samples_tr 20 | self.X_train = np.load(os.path.join(root_dir, 'german_data', 'train_1_X.npy')) 21 | self.X_valid = np.load(os.path.join(root_dir, 'german_data', 'test_valid_1_X.npy')) 22 | 23 | self.Y = None 24 | super().__init__(root_dir=root_dir, 25 | transform=transform, 26 | nodes_to_intervene=['age'], 27 | structural_eq=None, 28 | noises_distr=None, 29 | nodes_list=['sex', 'age', 'R', 'S'], 30 | adj_edges={'sex': ['R', 'S'], # A#excluding 16 31 | 'age': ['R', 'S'], # Age 32 | 'R': [], 33 | 'S': [], 34 | }, 35 | lambda_=lambda_, 36 | ) 37 | 38 | @property 39 | def likelihoods(self): 40 | likelihoods_tmp = [] 41 | # lik_node_sex = [self._get_lik('b',dim=1,normalize='dim')] 42 | # likelihoods_tmp.append(lik_node_sex) 43 | lik_node_sex = [self._get_lik('d', dim=1, normalize=None)] 44 | likelihoods_tmp.append(lik_node_sex) 45 | 46 | lik_node_age = [self._get_lik('d', dim=1, normalize='dim')] 47 | likelihoods_tmp.append(lik_node_age) 48 | 49 | lik_node_R = [self._get_lik('d', dim=2, normalize='dim')] 50 | likelihoods_tmp.append(lik_node_R) 51 | 52 | lik_node_S = [self._get_lik('c', dim=3, normalize='dim'), 53 | self._get_lik('c', dim=5, normalize='dim'), 54 | self._get_lik('c', dim=4, normalize='dim')] 55 | likelihoods_tmp.append(lik_node_S) 56 | 57 | return likelihoods_tmp 58 | 59 | def _create_data(self): 60 | X = np.concatenate([self.X_train, self.X_valid], axis=0) 61 | if self.split == 'train': 62 | self.X = X[:self.num_samples_tr] 63 | elif self.split == 'valid': 64 | num_samples = (X.shape[0] - self.num_samples_tr) // 2 65 | self.X = X[self.num_samples_tr:(self.num_samples_tr + num_samples)] 66 | elif self.split == 'test': 67 | num_samples = (X.shape[0] - self.num_samples_tr) // 2 68 | self.X = X[-num_samples:] 69 | self.Y = self.X[:, -1].copy() 70 | self.X = self.X[:, :-1] 71 | self.U = np.zeros([self.X.shape[0], 1]) 72 | 73 | def node_is_image(self): 74 | return [False, False, False, False] 75 | 76 | def is_toy(self): 77 | return False 78 | 79 | def get_attributes_dict(self): 80 | # TODO: Makes this more general 81 | unfair_attributes = list(range(2, 16)) # R, S 82 | fair_attributes = [1] # Age 83 | sensitive_attributes = [0] # Sex 84 | return {'unfair_attributes': unfair_attributes, 85 | 'fair_attributes': fair_attributes, 86 | 'sens_attributes': sensitive_attributes} 87 | -------------------------------------------------------------------------------- /VACA_modified/datasets/loan.py: -------------------------------------------------------------------------------- 1 | from datasets.toy import ToySCM 2 | from utils.constants import Cte 3 | from utils.distributions import * 4 | 5 | 6 | class LoanSCM(ToySCM): 7 | 8 | def __init__(self, root_dir, 9 | split: str = 'train', 10 | num_samples: int = 5000, 11 | equations_type=Cte.LINEAR, 12 | likelihood_names: str = 'd_d_cb', 13 | transform=None, 14 | **kwargs 15 | ): 16 | """ 17 | Args: 18 | root_dir: path to data directory 19 | train: whether to load the training subset (``True``, ``'train-*'`` files) or the test 20 | subset (``False``, ``'t10k-*'`` files) 21 | columns: list of morphometrics to load; by default (``None``) loads the image index and 22 | all available metrics: area, length, thickness, slant, width, and height 23 | """ 24 | 25 | if equations_type == Cte.LINEAR: 26 | e_0 = -1 27 | e_G = 0.5 28 | e_A = 1 29 | 30 | l_0 = 1 31 | l_A = .01 32 | l_G = 1 33 | 34 | d_0 = -1 35 | d_A = .1 36 | d_G = 2 37 | d_L = 1 38 | 39 | i_0 = -4 40 | i_A = .1 41 | i_G = 2 42 | # i_E = 10 43 | i_GE = 1 44 | 45 | s_0 = -4 46 | s_I = 1.5 47 | 48 | structural_eq = { 49 | # Gender 50 | 'x1': lambda u1,: u1, 51 | # Age 52 | 'x2': lambda u2,: -35 + u2, 53 | # Education 54 | 'x3': lambda u3, x1, x2: -0.5 + ( 55 | 1 + np.exp(-(e_0 + e_G * x1 + e_A * (1 + np.exp(- .1 * (x2))) ** (-1) + u3))) ** (-1), 56 | # Loan amount 57 | 'x4': lambda u4, x1, x2: l_0 + l_A * (x2 - 5) * (5 - x2) + l_G * x1 + u4, 58 | # Loan duration 59 | 'x5': lambda u5, x1, x2, x4: d_0 + d_A * x2 + d_G * x1 + d_L * x4 + u5, 60 | # Income 61 | 'x6': lambda u6, x1, x2, x3: i_0 + i_A * (x2 + 35) + i_G * x1 + i_GE * x1 * x3 + u6, 62 | # Savings 63 | 'x7': lambda u7, x6: s_0 + s_I * (x6 > 0) * x6 + u7, 64 | } 65 | 66 | noises_distr = { 67 | # Gender 68 | 'x1': Bernoulli(0.5), 69 | # Age 70 | 'x2': Gamma(10, 3.5), 71 | # Education 72 | 'x3': Normal(0, 0.5 ** 2), 73 | # Loan amount 74 | 'x4': Normal(0, 2 ** 2), 75 | # Loan duration 76 | 'x5': Normal(0, 3 ** 2), 77 | # Income 78 | 'x6': Normal(0, 2 ** 2), 79 | # Savings 80 | 'x7': Normal(0, 5 ** 2), 81 | } 82 | 83 | 84 | else: 85 | raise NotImplementedError 86 | 87 | super().__init__(root_dir=root_dir, 88 | name=Cte.LOAN, 89 | eq_type=equations_type, 90 | nodes_to_intervene=['x1', 'x2', 'x4', 'x6'], 91 | structural_eq=structural_eq, 92 | noises_distr=noises_distr, 93 | adj_edges={'x1': ['x3', 'x4', 'x5', 'x6'], 94 | 'x2': ['x3', 'x4', 'x5', 'x6'], 95 | 'x3': ['x6'], 96 | 'x4': ['x5'], 97 | 'x5': [], 98 | 'x6': ['x7'], 99 | 'x7': []}, 100 | split=split, 101 | num_samples=num_samples, 102 | likelihood_names=likelihood_names, 103 | transform=transform, 104 | **kwargs 105 | ) 106 | -------------------------------------------------------------------------------- /VACA_modified/datasets/mgraph.py: -------------------------------------------------------------------------------- 1 | from datasets.toy import ToySCM 2 | from utils.constants import Cte 3 | from utils.distributions import * 4 | 5 | 6 | class MGraphSCM(ToySCM): 7 | 8 | def __init__(self, root_dir, 9 | split: str = 'train', 10 | num_samples: int = 5000, 11 | equations_type=Cte.LINEAR, 12 | likelihood_names: str = 'd_d_d_d_d', 13 | transform=None, 14 | **kwargs 15 | ): 16 | """ 17 | Args: 18 | root_dir: path to data directory 19 | train: whether to load the training subset (``True``, ``'train-*'`` files) or the test 20 | subset (``False``, ``'t10k-*'`` files) 21 | columns: list of morphometrics to load; by default (``None``) loads the image index and 22 | all available metrics: area, length, thickness, slant, width, and height 23 | """ 24 | 25 | if equations_type == Cte.LINEAR: 26 | structural_eq = { 27 | 'x1': lambda u1: u1, 28 | 'x2': lambda u2: u2, 29 | 'x3': lambda u3, x1: x1 + u3, 30 | 'x4': lambda u4, x1, x2: -x2 + 0.5 * x1 + u4, 31 | 'x5': lambda u5, x2: -1.5 * x2 + u5, 32 | } 33 | 34 | noises_distr = { 35 | 'x1': Normal(0, 1), 36 | 'x2': Normal(0, 1), 37 | 'x3': Normal(0, 1), 38 | 'x4': Normal(0, 1), 39 | 'x5': Normal(0, 1), 40 | } 41 | 42 | 43 | elif equations_type == Cte.NONLINEAR: 44 | structural_eq = { 45 | 'x1': lambda u1: u1, 46 | 'x2': lambda u2: u2, 47 | 'x3': lambda u3, x1: x1 + 0.5 * x1 ** 2 + u3, 48 | 'x4': lambda u4, x1, x2: -x2 + 0.5 * x1 ** 2 + u4, 49 | 'x5': lambda u5, x2: -1.5 * x2 ** 2 + u5, 50 | } 51 | 52 | noises_distr = { 53 | 'x1': Normal(0, 1), 54 | 'x2': Normal(0, 1), 55 | 'x3': Normal(0, 1), 56 | 'x4': Normal(0, 1), 57 | 'x5': Normal(0, 1), 58 | } 59 | 60 | elif equations_type == Cte.NONADDITIVE: 61 | structural_eq = { 62 | 'x1': lambda u1: u1, 63 | 'x2': lambda u2: u2, 64 | 'x3': lambda u3, x1: (x1) * u3, 65 | 'x4': lambda u4, x1, x2: (-x2 + 0.5 * x1 ** 2) * u4, 66 | 'x5': lambda u5, x2: (-1.5 * x2 ** 2) * u5, 67 | } 68 | 69 | noises_distr = { 70 | 'x1': Normal(0, 1), # MixtureOfGaussians(probs=[0.5, 0.5], means=[-2.5, 2.5], vars=[1, 1]), 71 | 'x2': Normal(0, 1), 72 | 'x3': Normal(0, 1), 73 | 'x4': Normal(0, 1), 74 | 'x5': Normal(0, 1), 75 | } 76 | 77 | else: 78 | raise NotImplementedError 79 | 80 | super().__init__(root_dir=root_dir, 81 | name=Cte.MGRAPH, 82 | eq_type=equations_type, 83 | nodes_to_intervene=['x1', 'x2'], 84 | structural_eq=structural_eq, 85 | noises_distr=noises_distr, 86 | adj_edges={'x1': ['x3', 'x4'], 87 | 'x2': ['x4', 'x5'], 88 | 'x3': [], 89 | 'x4': [], 90 | 'x5': []}, 91 | split=split, 92 | num_samples=num_samples, 93 | likelihood_names=likelihood_names, 94 | transform=transform, 95 | **kwargs 96 | ) 97 | -------------------------------------------------------------------------------- /VACA_modified/datasets/toy.py: -------------------------------------------------------------------------------- 1 | from datasets._heterogeneous import HeterogeneousSCM 2 | 3 | flatten = lambda t: [item for sublist in t for item in sublist] 4 | 5 | from utils.distributions import * 6 | from utils.constants import Cte 7 | import os 8 | from utils.args_parser import mkdir 9 | 10 | 11 | class ToySCM(HeterogeneousSCM): 12 | 13 | def __init__(self, root_dir, 14 | name: str = 'chain', 15 | eq_type: str = Cte.LINEAR, 16 | nodes_to_intervene: list = None, 17 | structural_eq: dict = None, 18 | noises_distr: dict = None, 19 | adj_edges: dict = None, 20 | split: str = 'train', 21 | num_samples: int = 5000, 22 | likelihood_names: str = 'd_d_cb', 23 | lambda_: float = 0.05, 24 | transform=None, 25 | nodes_list=None, 26 | ): 27 | assert split in ['train', 'valid', 'test'] 28 | 29 | self.split = split 30 | self.name = name 31 | self._num_samples = num_samples 32 | self.eq_type = eq_type 33 | num_nodes = len(structural_eq) 34 | 35 | likelihood_names = likelihood_names.split('_') 36 | if len(likelihood_names) == 1: 37 | likelihood_names = [likelihood_names[0], ] * num_nodes 38 | self.likelihood_names = likelihood_names 39 | assert num_nodes == len(noises_distr) 40 | assert num_nodes == len(adj_edges) 41 | assert num_nodes == len(self.likelihood_names) 42 | 43 | if nodes_list is None: 44 | nodes_list = [f'x{i + 1}' for i in range(num_nodes)] 45 | 46 | assert set(structural_eq.keys()) == set(noises_distr.keys()), 'Keys for the SE and Noise distribution should be the same' 47 | #for key_eq, key_n in zip(structural_eq.keys(), noises_distr.keys()): 48 | # assert key_eq == key_n, 'Keys for the SE and Noise distribution should be the same' 49 | super().__init__(root_dir=root_dir, 50 | transform=transform, 51 | nodes_to_intervene=nodes_to_intervene, 52 | nodes_list=nodes_list, 53 | adj_edges=adj_edges, 54 | structural_eq=structural_eq, 55 | noises_distr=noises_distr, 56 | lambda_=lambda_ 57 | ) 58 | 59 | @property 60 | def likelihoods(self): 61 | likelihoods_tmp = [] 62 | 63 | for i, lik_name in enumerate(self.likelihood_names): 64 | likelihoods_tmp.append([self._get_lik(lik_name, 65 | dim=1, 66 | normalize='dim')]) 67 | 68 | return likelihoods_tmp 69 | 70 | @property 71 | def std_list(self): 72 | return [-1, -0.5, 0, 0.5, 1] 73 | 74 | def _create_data(self, new_data): 75 | X = np.zeros([self._num_samples, self.num_dimensions]) 76 | U = np.zeros([self._num_samples, self.num_nodes]) 77 | 78 | folder = mkdir(os.path.join(self.root_dir, f'{self.name}_{self.eq_type}')) 79 | 80 | X_file = os.path.join(folder, f'{self.split}_{self._num_samples}_X.npy') 81 | U_file = os.path.join(folder, f'{self.split}_{self._num_samples}_U.npy') 82 | if (not new_data) and os.path.exists(X_file) and os.path.exists(U_file): 83 | X = np.load(X_file) 84 | U = np.load(U_file) 85 | np.save(X_file, X) 86 | np.save(U_file, U) 87 | else: 88 | for i in range(self._num_samples): 89 | x, u = self.sample() 90 | X[i, :] = x 91 | U[i, :] = u 92 | 93 | 94 | self.X = X.astype(np.float32) 95 | self.U = U.astype(np.float32) 96 | 97 | def node_is_image(self): 98 | return [False for _ in self.num_nodes] 99 | 100 | 101 | def create_toy_dataset(root_dir, 102 | name: str = 'chain', 103 | eq_type: str = Cte.LINEAR, 104 | nodes_to_intervene: list = None, 105 | structural_eq: dict = None, 106 | noises_distr: dict = None, 107 | adj_edges: dict = None, 108 | split: str = 'train', 109 | num_samples: int = 5000, 110 | likelihood_names: str = 'd_d_cb', 111 | lambda_: float = 0.05, 112 | transform=None, 113 | nodes_list = None,): 114 | 115 | return ToySCM(root_dir, 116 | name, 117 | eq_type, 118 | nodes_to_intervene, 119 | structural_eq, 120 | noises_distr, 121 | adj_edges, 122 | split, 123 | num_samples, 124 | likelihood_names, 125 | lambda_=lambda_, 126 | transform=transform, 127 | nodes_list=nodes_list,) 128 | -------------------------------------------------------------------------------- /VACA_modified/datasets/transforms.py: -------------------------------------------------------------------------------- 1 | import torch 2 | 3 | 4 | class ToTensor: 5 | """Convert ndarrays in sample to Tensors.""" 6 | 7 | def __call__(self, sample): 8 | return torch.tensor(sample) 9 | 10 | def __repr__(self): 11 | return self.__class__.__name__ + '()' 12 | 13 | 14 | class ToOneHot: 15 | 16 | def __init__(self, n_dims=10): 17 | self.n_dims = n_dims 18 | 19 | def __call__(self, target): 20 | y_onehot = torch.FloatTensor(self.n_dims) 21 | y_onehot.zero_() 22 | y_onehot[target] = 1 23 | return y_onehot 24 | 25 | def __repr__(self): 26 | return self.__class__.__name__ + '()' 27 | -------------------------------------------------------------------------------- /VACA_modified/datasets/triangle.py: -------------------------------------------------------------------------------- 1 | from datasets.toy import ToySCM 2 | from utils.constants import Cte 3 | from utils.distributions import * 4 | 5 | 6 | class TriangleSCM(ToySCM): 7 | 8 | def __init__(self, root_dir, 9 | split: str = 'train', 10 | num_samples: int = 5000, 11 | equations_type=Cte.LINEAR, 12 | likelihood_names: str = 'd_d_d', 13 | transform=None, 14 | **kwargs 15 | ): 16 | 17 | if equations_type == Cte.LINEAR: 18 | structural_eq = { 19 | 'x1': lambda u1: u1, 20 | 'x2': lambda u2, x1: -x1 + u2, 21 | 'x3': lambda u3, x1, x2: x1 + 0.25 * x2 + u3, 22 | } 23 | 24 | noises_distr = { 25 | 'x1': MixtureOfGaussians(probs=[0.5, 0.5], means=[-2, 1.5], vars=[1.5, 1]), 26 | 'x2': Normal(0, 1), 27 | 'x3': Normal(0, 1), 28 | } 29 | 30 | 31 | elif equations_type == Cte.NONLINEAR: 32 | structural_eq = { 33 | 'x1': lambda u1: u1, 34 | 'x2': lambda u2, x1: -1 + 3 / (1 + np.exp(-2 * x1)) + u2, 35 | 'x3': lambda u3, x1, x2: x1 + 0.25 * x2 ** 2 + u3, 36 | } 37 | 38 | noises_distr = { 39 | 'x1': MixtureOfGaussians(probs=[0.5, 0.5], means=[-2, 1.5], vars=[1.5, 1]), 40 | 'x2': Normal(0, 0.1), 41 | 'x3': Normal(0, 1), 42 | } 43 | 44 | elif equations_type == Cte.NONADDITIVE: 45 | structural_eq = { 46 | 'x1': lambda u1: u1, 47 | 'x2': lambda u2, x1: 0.25 * np.sign(u2) * x1 ** 2 * (1 + u2 ** 2), 48 | 'x3': lambda u3, x1, x2: -1 + 0.1 * np.sign(u3) * (x1 ** 2 + x2 ** 2) + u3, 49 | } 50 | 51 | noises_distr = { 52 | 'x1': MixtureOfGaussians(probs=[0.5, 0.5], means=[-2.5, 2.5], vars=[1, 1]), 53 | 'x2': Normal(0, 0.25), 54 | 'x3': Normal(0, 0.25 ** 2), 55 | } 56 | 57 | else: 58 | raise NotImplementedError 59 | 60 | super().__init__(root_dir=root_dir, 61 | name=Cte.TRIANGLE, 62 | eq_type=equations_type, 63 | nodes_to_intervene=['x1', 'x2'], 64 | structural_eq=structural_eq, 65 | noises_distr=noises_distr, 66 | adj_edges={'x1': ['x2', 'x3'], 67 | 'x2': ['x3'], 68 | 'x3': []}, 69 | split=split, 70 | num_samples=num_samples, 71 | likelihood_names=likelihood_names, 72 | transform=transform, 73 | **kwargs 74 | ) 75 | -------------------------------------------------------------------------------- /VACA_modified/datasets/utils.py: -------------------------------------------------------------------------------- 1 | import networkx as nx 2 | import numpy as np 3 | import pandas as pd 4 | import torch 5 | import torch_geometric 6 | 7 | 8 | def normalize_adj(adj, how='sym'): 9 | if how == 'sym': 10 | deg = adj.sum(1) 11 | deg_inv = 1 / np.sqrt(deg) 12 | deg_inv[deg_inv == float('inf')] = 0 13 | D_inv = np.diag(deg_inv) 14 | return D_inv @ adj @ D_inv 15 | elif how == 'row': # Normalize by children 16 | deg = adj.sum(1) 17 | deg_inv = 1 / deg 18 | deg_inv[deg_inv == float('inf')] = 0 19 | D_inv = np.diag(deg_inv) 20 | return D_inv @ adj 21 | elif how == 'col': # Normalize by parents 22 | deg = adj.sum(0) 23 | deg_inv = 1 / deg 24 | deg_inv[deg_inv == float('inf')] = 0 25 | D_inv = np.diag(deg_inv) 26 | return adj @ D_inv 27 | 28 | 29 | def convert_nodes_to_df(G, node_id_col='node_id'): 30 | data = [] 31 | for node_id, attr_dict in G.nodes(True): 32 | my_dict = {node_id_col: node_id} 33 | my_dict.update(attr_dict) 34 | data.append(my_dict) 35 | return pd.DataFrame(data) 36 | 37 | 38 | def convert_edges_to_df(G): 39 | data = [] 40 | for node_src, node_dst in G.edges: 41 | my_dict = {'node_src': node_src, 'node_dst': node_dst} 42 | my_dict.update(G.edges[(node_src, node_dst)]) 43 | data.append(my_dict) 44 | return pd.DataFrame(data) 45 | 46 | 47 | def from_networkx(G): 48 | r"""Converts a :obj:`networkx.Graph` or :obj:`networkx.DiGraph` to a 49 | :class:`torch_geometric.data.Data` instance. 50 | 51 | Args: 52 | G (networkx.Graph or networkx.DiGraph): A networkx graph. 53 | """ 54 | 55 | G = nx.convert_node_labels_to_integers(G) 56 | G = G.to_directed() if not nx.is_directed(G) else G 57 | edge_index = torch.tensor(list(G.edges)).t().contiguous() 58 | 59 | data = {} 60 | 61 | for i, (_, feat_dict) in enumerate(G.nodes(data=True)): 62 | for key, value in feat_dict.items(): 63 | data[str(key)] = [value] if i == 0 else data[str(key)] + [value] 64 | 65 | for key, item in data.items(): 66 | try: 67 | data[key] = torch.tensor(item) 68 | except ValueError: 69 | pass 70 | 71 | data['edge_index'] = edge_index.view(2, -1) 72 | data = torch_geometric.data.Data.from_dict(data) 73 | data.num_nodes = G.number_of_nodes() 74 | 75 | return data 76 | -------------------------------------------------------------------------------- /VACA_modified/environment.yml: -------------------------------------------------------------------------------- 1 | name: vaca 2 | channels: 3 | - defaults 4 | dependencies: 5 | - pip=21.2.4 6 | - python=3.9.0 7 | - pip: 8 | - matplotlib==3.4.3 9 | - networkx==2.6.3 10 | - numpy==1.21.2 11 | - pandas==1.3.4 12 | - pytorch-lightning==1.4.9 13 | - scikit-learn==1.0 14 | - scipy==1.7.1 15 | - seaborn==0.11.2 16 | - tensorboard==2.7.0 17 | - torch==1.9.1 18 | - torch-cluster==1.5.9 19 | - torch-geometric==2.0.1 20 | - torch-scatter==2.0.8 21 | - torch-sparse==0.6.12 22 | - torch-spline-conv==1.2.1 23 | - torchaudio==0.9.1 24 | - torchmetrics==0.5.1 25 | - torchvision==0.10.1 26 | - tqdm==4.62.3 27 | -------------------------------------------------------------------------------- /VACA_modified/models/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/patrickrchao/DiffusionBasedCausalModels/33fe8010a2d7700bd715563e70c5802d748e4a29/VACA_modified/models/.DS_Store -------------------------------------------------------------------------------- /VACA_modified/models/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/patrickrchao/DiffusionBasedCausalModels/33fe8010a2d7700bd715563e70c5802d748e4a29/VACA_modified/models/__init__.py -------------------------------------------------------------------------------- /VACA_modified/models/__pycache__/__init__.cpython-37.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/patrickrchao/DiffusionBasedCausalModels/33fe8010a2d7700bd715563e70c5802d748e4a29/VACA_modified/models/__pycache__/__init__.cpython-37.pyc -------------------------------------------------------------------------------- /VACA_modified/models/__pycache__/__init__.cpython-39.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/patrickrchao/DiffusionBasedCausalModels/33fe8010a2d7700bd715563e70c5802d748e4a29/VACA_modified/models/__pycache__/__init__.cpython-39.pyc -------------------------------------------------------------------------------- /VACA_modified/models/__pycache__/_density_estimators.cpython-37.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/patrickrchao/DiffusionBasedCausalModels/33fe8010a2d7700bd715563e70c5802d748e4a29/VACA_modified/models/__pycache__/_density_estimators.cpython-37.pyc -------------------------------------------------------------------------------- /VACA_modified/models/__pycache__/_density_estimators.cpython-39.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/patrickrchao/DiffusionBasedCausalModels/33fe8010a2d7700bd715563e70c5802d748e4a29/VACA_modified/models/__pycache__/_density_estimators.cpython-39.pyc -------------------------------------------------------------------------------- /VACA_modified/models/__pycache__/_evaluator.cpython-37.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/patrickrchao/DiffusionBasedCausalModels/33fe8010a2d7700bd715563e70c5802d748e4a29/VACA_modified/models/__pycache__/_evaluator.cpython-37.pyc -------------------------------------------------------------------------------- /VACA_modified/models/__pycache__/_evaluator.cpython-39.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/patrickrchao/DiffusionBasedCausalModels/33fe8010a2d7700bd715563e70c5802d748e4a29/VACA_modified/models/__pycache__/_evaluator.cpython-39.pyc -------------------------------------------------------------------------------- /VACA_modified/models/_density_estimators.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | import torch 3 | 4 | 5 | def ELBO(log_w): 6 | return torch.mean(log_w, dim=-1) 7 | 8 | 9 | def IWAE(log_w, trick=False): 10 | if not trick: 11 | return torch.logsumexp(log_w, dim=-1) - np.log(log_w.shape[-1]), {} 12 | # notice that using the trick is required for computing the gradient wrt the IWAE measure, but doesn't return a 13 | # lower bound of the log evidence. Therefore use only for computing the gradient. For the estimate, 14 | # use trick=False 15 | 16 | log_w_max = log_w - torch.max(log_w, dim=-1)[0].view(-1, 1) # w_k/max(w) 17 | # normalized_w_k = w_k/(sum_k w_k) 18 | normalized_w = torch.exp(log_w_max - torch.logsumexp(log_w_max, dim=-1, keepdim=True)).detach().clone() 19 | info = {} # {'normalized_w': normalized_w} 20 | return torch.mul(normalized_w, log_w).sum(-1), info 21 | 22 | 23 | def IWAE_dreg(log_w, zs): 24 | # print('IWAE_dreg!') 25 | with torch.no_grad(): 26 | normalized_w = torch.exp(log_w - torch.logsumexp(log_w, dim=-1, keepdim=True)) 27 | if zs.requires_grad: 28 | # print('requires grad!') 29 | zs.register_hook(lambda grad: normalized_w.unsqueeze(-1) * grad) 30 | info = {} # {'normalized_w': normalized_w} 31 | return torch.mul(normalized_w, log_w).sum(-1), info 32 | -------------------------------------------------------------------------------- /VACA_modified/models/_utils.py: -------------------------------------------------------------------------------- 1 | import torch.nn as nn 2 | 3 | 4 | def init_weights_orthogonal(m): 5 | print(m) 6 | if type(m) == nn.Linear: 7 | nn.init.orthogonal_(m.weight) 8 | nn.init.zeros_(m.bias) 9 | 10 | 11 | def init_weights_kaiming_normal(m): 12 | # weight initialization 13 | if isinstance(m, nn.Conv2d): 14 | nn.init.kaiming_normal_(m.weight, mode='fan_out') 15 | if m.bias is not None: 16 | nn.init.zeros_(m.bias) 17 | if isinstance(m, nn.ConvTranspose2d): 18 | nn.init.kaiming_normal_(m.weight, mode='fan_out') 19 | if m.bias is not None: 20 | nn.init.zeros_(m.bias) 21 | elif isinstance(m, (nn.BatchNorm2d, nn.GroupNorm)): 22 | nn.init.ones_(m.weight) 23 | nn.init.zeros_(m.bias) 24 | elif isinstance(m, nn.Linear): 25 | nn.init.normal_(m.weight, 0, 0.01) 26 | nn.init.zeros_(m.bias) 27 | 28 | 29 | def init_weights_xavier(m): 30 | # weight initialization 31 | if isinstance(m, nn.Conv2d): 32 | nn.init.xavier_uniform_(m.weight) 33 | if m.bias is not None: 34 | nn.init.xavier_uniform_(m.bias) 35 | if isinstance(m, nn.ConvTranspose2d): 36 | nn.init.xavier_uniform_(m.weight) 37 | if m.bias is not None: 38 | nn.init.zeros_(m.bias) 39 | elif isinstance(m, (nn.BatchNorm2d, nn.GroupNorm)): 40 | nn.init.xavier_uniform_(m.weight) 41 | nn.init.xavier_uniform_(m.bias) 42 | elif isinstance(m, nn.Linear): 43 | nn.init.xavier_uniform_(m.weight) 44 | -------------------------------------------------------------------------------- /VACA_modified/models/carefl/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/patrickrchao/DiffusionBasedCausalModels/33fe8010a2d7700bd715563e70c5802d748e4a29/VACA_modified/models/carefl/.DS_Store -------------------------------------------------------------------------------- /VACA_modified/models/carefl/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/patrickrchao/DiffusionBasedCausalModels/33fe8010a2d7700bd715563e70c5802d748e4a29/VACA_modified/models/carefl/__init__.py -------------------------------------------------------------------------------- /VACA_modified/models/carefl/__pycache__/__init__.cpython-37.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/patrickrchao/DiffusionBasedCausalModels/33fe8010a2d7700bd715563e70c5802d748e4a29/VACA_modified/models/carefl/__pycache__/__init__.cpython-37.pyc -------------------------------------------------------------------------------- /VACA_modified/models/carefl/__pycache__/__init__.cpython-39.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/patrickrchao/DiffusionBasedCausalModels/33fe8010a2d7700bd715563e70c5802d748e4a29/VACA_modified/models/carefl/__pycache__/__init__.cpython-39.pyc -------------------------------------------------------------------------------- /VACA_modified/models/carefl/__pycache__/carefl.cpython-37.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/patrickrchao/DiffusionBasedCausalModels/33fe8010a2d7700bd715563e70c5802d748e4a29/VACA_modified/models/carefl/__pycache__/carefl.cpython-37.pyc -------------------------------------------------------------------------------- /VACA_modified/models/carefl/__pycache__/carefl.cpython-39.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/patrickrchao/DiffusionBasedCausalModels/33fe8010a2d7700bd715563e70c5802d748e4a29/VACA_modified/models/carefl/__pycache__/carefl.cpython-39.pyc -------------------------------------------------------------------------------- /VACA_modified/models/carefl/nflib/__init__.py: -------------------------------------------------------------------------------- 1 | from .flows import MAF, IAF, AffineCL, AffineConstantFlow 2 | from .flows import NormalizingFlow, NormalizingFlowModel, ActNorm, ARMLP 3 | from .nets import MLP4, MLP1layer 4 | from .spline_flows import NSF_CL, NSF_AR 5 | -------------------------------------------------------------------------------- /VACA_modified/models/carefl/nflib/__pycache__/__init__.cpython-37.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/patrickrchao/DiffusionBasedCausalModels/33fe8010a2d7700bd715563e70c5802d748e4a29/VACA_modified/models/carefl/nflib/__pycache__/__init__.cpython-37.pyc -------------------------------------------------------------------------------- /VACA_modified/models/carefl/nflib/__pycache__/__init__.cpython-39.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/patrickrchao/DiffusionBasedCausalModels/33fe8010a2d7700bd715563e70c5802d748e4a29/VACA_modified/models/carefl/nflib/__pycache__/__init__.cpython-39.pyc -------------------------------------------------------------------------------- /VACA_modified/models/carefl/nflib/__pycache__/flows.cpython-37.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/patrickrchao/DiffusionBasedCausalModels/33fe8010a2d7700bd715563e70c5802d748e4a29/VACA_modified/models/carefl/nflib/__pycache__/flows.cpython-37.pyc -------------------------------------------------------------------------------- /VACA_modified/models/carefl/nflib/__pycache__/flows.cpython-39.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/patrickrchao/DiffusionBasedCausalModels/33fe8010a2d7700bd715563e70c5802d748e4a29/VACA_modified/models/carefl/nflib/__pycache__/flows.cpython-39.pyc -------------------------------------------------------------------------------- /VACA_modified/models/carefl/nflib/__pycache__/nets.cpython-37.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/patrickrchao/DiffusionBasedCausalModels/33fe8010a2d7700bd715563e70c5802d748e4a29/VACA_modified/models/carefl/nflib/__pycache__/nets.cpython-37.pyc -------------------------------------------------------------------------------- /VACA_modified/models/carefl/nflib/__pycache__/nets.cpython-39.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/patrickrchao/DiffusionBasedCausalModels/33fe8010a2d7700bd715563e70c5802d748e4a29/VACA_modified/models/carefl/nflib/__pycache__/nets.cpython-39.pyc -------------------------------------------------------------------------------- /VACA_modified/models/carefl/nflib/__pycache__/spline_flows.cpython-37.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/patrickrchao/DiffusionBasedCausalModels/33fe8010a2d7700bd715563e70c5802d748e4a29/VACA_modified/models/carefl/nflib/__pycache__/spline_flows.cpython-37.pyc -------------------------------------------------------------------------------- /VACA_modified/models/carefl/nflib/__pycache__/spline_flows.cpython-39.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/patrickrchao/DiffusionBasedCausalModels/33fe8010a2d7700bd715563e70c5802d748e4a29/VACA_modified/models/carefl/nflib/__pycache__/spline_flows.cpython-39.pyc -------------------------------------------------------------------------------- /VACA_modified/models/carefl/nflib/nets.py: -------------------------------------------------------------------------------- 1 | """ 2 | Various helper network modules 3 | """ 4 | 5 | import torch 6 | from torch import nn 7 | 8 | 9 | class LeafParam(nn.Module): 10 | 11 | def __init__(self, n): 12 | super().__init__() 13 | self.p = nn.Parameter(torch.zeros(1, n)) 14 | 15 | def forward(self, x): 16 | return self.p.expand(x.size(0), self.p.size(1)) 17 | 18 | 19 | class PositionalEncoder(nn.Module): 20 | """ 21 | Each dimension of the input gets expanded out with sins/coses 22 | to "carve" out the space. Useful in low-dimensional cases with 23 | tightly "curled up" data. 24 | """ 25 | 26 | def __init__(self, freqs=(.5, 1, 2, 4, 8)): 27 | super().__init__() 28 | self.freqs = freqs 29 | 30 | def forward(self, x): 31 | sines = [torch.sin(x * f) for f in self.freqs] 32 | coses = [torch.cos(x * f) for f in self.freqs] 33 | out = torch.cat(sines + coses, dim=1) 34 | return out 35 | 36 | 37 | class MLP1layer(nn.Module): 38 | """ a simple 4-layer MLP """ 39 | 40 | def __init__(self, nin, nout, nh): 41 | super().__init__() 42 | self.net = nn.Sequential( 43 | nn.Linear(nin, nh), 44 | nn.LeakyReLU(0.2), 45 | nn.Linear(nh, nout), 46 | ) 47 | 48 | def forward(self, x): 49 | return self.net(x) 50 | 51 | 52 | class MLP4(nn.Module): 53 | """ a simple 4-layer MLP """ 54 | 55 | def __init__(self, nin, nout, nh): 56 | super().__init__() 57 | self.net = nn.Sequential( 58 | nn.Linear(nin, nh), 59 | nn.LeakyReLU(0.2), 60 | nn.Linear(nh, nh), 61 | nn.LeakyReLU(0.2), 62 | nn.Linear(nh, nh), 63 | nn.LeakyReLU(0.2), 64 | nn.Linear(nh, nout), 65 | ) 66 | 67 | def forward(self, x): 68 | return self.net(x) 69 | 70 | 71 | class PosEncMLP(nn.Module): 72 | """ 73 | Position Encoded MLP, where the first layer performs position encoding. 74 | Each dimension of the input gets transformed to len(freqs)*2 dimensions 75 | using a fixed transformation of sin/cos of given frequencies. 76 | """ 77 | 78 | def __init__(self, nin, nout, nh, freqs=(.5, 1, 2, 4, 8)): 79 | super().__init__() 80 | self.net = nn.Sequential( 81 | PositionalEncoder(freqs), 82 | MLP4(nin * len(freqs) * 2, nout, nh), 83 | ) 84 | 85 | def forward(self, x): 86 | return self.net(x) 87 | -------------------------------------------------------------------------------- /VACA_modified/models/multicvae/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/patrickrchao/DiffusionBasedCausalModels/33fe8010a2d7700bd715563e70c5802d748e4a29/VACA_modified/models/multicvae/__init__.py -------------------------------------------------------------------------------- /VACA_modified/models/multicvae/cvae_module.py: -------------------------------------------------------------------------------- 1 | import torch 2 | import torch.nn as nn 3 | 4 | from utils.constants import Cte 5 | from utils.likelihoods import get_likelihood 6 | 7 | from modules.dense import MLPModule 8 | 9 | 10 | class CVAEModule(nn.Module): 11 | ''' 12 | Conditional VAE 13 | ''' 14 | 15 | def __init__(self, x_dim, 16 | h_dim_list_dec, 17 | h_dim_list_enc, 18 | z_dim, 19 | cond_dim=0, 20 | act_name=Cte.RELU, 21 | drop_rate=0.0, 22 | likelihood_x=None, 23 | distr_z='normal'): 24 | super(CVAEModule, self).__init__() 25 | 26 | likelihood_z = get_likelihood(distr_z, z_dim) 27 | 28 | c_list = [x_dim + cond_dim] 29 | c_list.extend(h_dim_list_enc) 30 | c_list.append(likelihood_z.params_size) 31 | 32 | self.encoder_module = MLPModule(h_dim_list=c_list, 33 | activ_name=act_name, 34 | bn=False, 35 | drop_rate=drop_rate) 36 | 37 | c_list = [z_dim + cond_dim] 38 | c_list.extend(h_dim_list_dec) 39 | c_list.append(likelihood_x.params_size) 40 | # Instantiate Decoder module 41 | 42 | self.decoder_module = MLPModule(h_dim_list=c_list, 43 | activ_name=act_name, 44 | bn=False, 45 | drop_rate=drop_rate) 46 | 47 | self.z_dim = z_dim 48 | 49 | self.likelihood_z = likelihood_z 50 | self.likelihood_x = likelihood_x 51 | 52 | self.distr_z = distr_z 53 | 54 | def encoder_params(self): 55 | return self.encoder_module.parameters() 56 | 57 | def decoder_params(self): 58 | return self.decoder_module.parameters() 59 | 60 | def set_z_prior_distr(self, device): 61 | if self.distr_z == Cte.CONTINOUS_BERN: # Continous Bernoulli 62 | self.z_prior_distr = torch.distributions.ContinuousBernoulli( 63 | probs=0.5 * torch.ones(self.hparams.latent_dim).to(device)) 64 | elif self.distr_z == Cte.EXPONENTIAL: # Exponential 65 | self.z_prior_distr = torch.distributions.Exponential( 66 | rate=0.2 * torch.ones(self.hparams.latent_dim).to(device)) 67 | elif self.distr_z == Cte.BETA: # Beta 68 | self.z_prior_distr = torch.distributions.Beta( 69 | concentration0=torch.ones(self.hparams.latent_dim).to(device), 70 | concentration1=torch.ones(self.hparams.latent_dim).to(device)) 71 | elif self.distr_z == Cte.GAUSSIAN: 72 | self.z_prior_distr = torch.distributions.Normal(torch.zeros(self.z_dim).to(device), 73 | torch.ones(self.z_dim).to(device)) 74 | else: 75 | raise NotImplementedError 76 | 77 | def encoder(self, X, cond_data=None, return_mean=False, **kwargs): 78 | if cond_data is not None: 79 | X = torch.cat([X, cond_data], dim=1) 80 | logits = self.encoder_module(X) 81 | if return_mean: 82 | mean, qz_x = self.likelihood_z(logits, return_mean=True) 83 | return mean, qz_x 84 | else: 85 | qz_x = self.likelihood_z(logits) 86 | return qz_x 87 | 88 | def decoder(self, Z, cond_data=None, return_type=None, **kwargs): 89 | if cond_data is not None: 90 | Z = torch.cat([Z, cond_data], dim=1) 91 | logits = self.decoder_module(Z, **kwargs) 92 | if return_type == 'mean': 93 | mean, px_z = self.likelihood_x(logits, return_mean=True) 94 | return mean, px_z 95 | elif return_type == 'sample': 96 | mean, px_z = self.likelihood_x(logits, return_mean=True) 97 | return px_z.sample(), px_z 98 | else: 99 | px_z = self.likelihood_x(logits) 100 | return px_z 101 | 102 | def compute_log_w(self, X, K, cond_data=None, mask=None): 103 | """ 104 | IWAE log(1\K \sum_k w_k) w_k = p(x, z_i)/ q(z_i | x) 105 | log_wi = log p(x, z_i) - log q(z_i | x) 106 | Args: 107 | X: 108 | K: 109 | cond_data: 110 | mask: 111 | 112 | Returns: 113 | 114 | """ 115 | 116 | x_input = X.clone() 117 | 118 | if mask is not None: 119 | x_input[~mask] = 0.0 120 | 121 | log_w = [] 122 | for k in range(K): 123 | qz_x = self.encoder(x_input, cond_data=cond_data) 124 | z = qz_x.rsample() 125 | px_z_k = self.decoder(z, cond_data=cond_data) 126 | 127 | log_prob_qz_x = qz_x.log_prob(z).sum(-1) # Summing over dim(z) 128 | log_prob_pz = self.z_prior_distr.log_prob(z).sum(-1) 129 | log_prob_px_z = px_z_k.log_prob(X).sum(-1) 130 | 131 | log_w_k = log_prob_px_z + log_prob_pz - log_prob_qz_x 132 | 133 | if mask is not None: 134 | log_w.append(log_w_k[mask]) 135 | else: 136 | log_w.append(log_w_k) 137 | 138 | log_w = torch.stack(log_w, dim=0) 139 | 140 | # [K, N] 141 | return log_w.T 142 | 143 | def forward(self, X, estimator, cond_data=None, beta=1.0): 144 | 145 | if estimator == 'elbo': 146 | 147 | qz_x = self.encoder(X, cond_data=cond_data) 148 | z = qz_x.rsample() 149 | 150 | px_z = self.decoder(z, cond_data=cond_data) 151 | log_prob_x = px_z.log_prob(X).flatten(1).sum(1).mean() 152 | kl_z = torch.distributions.kl.kl_divergence(qz_x, self.z_prior_distr).flatten(1).sum(1).mean() 153 | 154 | elbo = log_prob_x - beta * kl_z 155 | 156 | data = {'log_prob_x': log_prob_x, 157 | 'kl_z': kl_z} 158 | 159 | return elbo, data 160 | else: 161 | raise NotImplementedError 162 | 163 | @torch.no_grad() 164 | def reconstruct(self, X, cond_data=None): 165 | z_mean, qz_x = self.encoder(X, cond_data=cond_data, return_mean=True) 166 | 167 | x_hat, _ = self.decoder(z_mean, cond_data=cond_data, return_type='mean') 168 | 169 | return z_mean, x_hat 170 | -------------------------------------------------------------------------------- /VACA_modified/models/vaca/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/patrickrchao/DiffusionBasedCausalModels/33fe8010a2d7700bd715563e70c5802d748e4a29/VACA_modified/models/vaca/.DS_Store -------------------------------------------------------------------------------- /VACA_modified/models/vaca/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/patrickrchao/DiffusionBasedCausalModels/33fe8010a2d7700bd715563e70c5802d748e4a29/VACA_modified/models/vaca/__init__.py -------------------------------------------------------------------------------- /VACA_modified/models/vaca/__pycache__/__init__.cpython-37.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/patrickrchao/DiffusionBasedCausalModels/33fe8010a2d7700bd715563e70c5802d748e4a29/VACA_modified/models/vaca/__pycache__/__init__.cpython-37.pyc -------------------------------------------------------------------------------- /VACA_modified/models/vaca/__pycache__/__init__.cpython-39.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/patrickrchao/DiffusionBasedCausalModels/33fe8010a2d7700bd715563e70c5802d748e4a29/VACA_modified/models/vaca/__pycache__/__init__.cpython-39.pyc -------------------------------------------------------------------------------- /VACA_modified/models/vaca/__pycache__/vaca.cpython-37.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/patrickrchao/DiffusionBasedCausalModels/33fe8010a2d7700bd715563e70c5802d748e4a29/VACA_modified/models/vaca/__pycache__/vaca.cpython-37.pyc -------------------------------------------------------------------------------- /VACA_modified/models/vaca/__pycache__/vaca.cpython-39.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/patrickrchao/DiffusionBasedCausalModels/33fe8010a2d7700bd715563e70c5802d748e4a29/VACA_modified/models/vaca/__pycache__/vaca.cpython-39.pyc -------------------------------------------------------------------------------- /VACA_modified/models/vaca/__pycache__/vaca_module.cpython-37.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/patrickrchao/DiffusionBasedCausalModels/33fe8010a2d7700bd715563e70c5802d748e4a29/VACA_modified/models/vaca/__pycache__/vaca_module.cpython-37.pyc -------------------------------------------------------------------------------- /VACA_modified/models/vaca/__pycache__/vaca_module.cpython-39.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/patrickrchao/DiffusionBasedCausalModels/33fe8010a2d7700bd715563e70c5802d748e4a29/VACA_modified/models/vaca/__pycache__/vaca_module.cpython-39.pyc -------------------------------------------------------------------------------- /VACA_modified/models/vaca/vaca_piwae.py: -------------------------------------------------------------------------------- 1 | 2 | from utils.dropout import dropout_adj, dropout_adj_parents 3 | from models.vaca.vaca import VACA 4 | from utils.optimizers import get_optimizer, get_scheduler 5 | 6 | 7 | class VACA_PIWAE(VACA): 8 | """ 9 | VACA trained with PIWAE 10 | """ 11 | 12 | 13 | def __init__(self, *args, **kwargs): 14 | super(VACA_PIWAE, self).__init__(*args, **kwargs) 15 | self.save_hyperparameters() 16 | 17 | self.estimator_inference = self.estimator.split('_')[0] 18 | self.estimator_gener = self.estimator.split('_')[1] 19 | 20 | self.estimator = self.estimator.split('_')[0] 21 | 22 | def set_optim_params(self, optim_params, sched_params): 23 | self.optim_params = optim_params 24 | self.sched_params = sched_params 25 | 26 | def configure_optimizers(self): 27 | tmp_params = self.optim_params['params'].copy() 28 | # tmp_params['lr'] = tmp_params['lr'] * 0.5 29 | 30 | optim_infer = get_optimizer(self.optim_params['name'])(self.model.encoder_params(), 31 | **self.optim_params['params']) 32 | 33 | optim_gener = get_optimizer(self.optim_params['name'])(self.model.decoder_params(), 34 | **tmp_params) 35 | 36 | if isinstance(self.sched_params, dict): 37 | sched_infer = get_scheduler(self.sched_params['name'])(optim_infer, **self.sched_params['params']) 38 | sched_gener = get_scheduler(self.sched_params['name'])(optim_gener, **self.sched_params['params']) 39 | sched = [sched_infer, sched_gener] 40 | else: 41 | sched = [] 42 | return [optim_infer, optim_gener], sched 43 | 44 | 45 | 46 | def training_step(self, batch, batch_idx, optimizer_idx): 47 | 48 | if self.dropout_adj > 0.0 and self.current_epoch >= self.dropout_adj_T: 49 | batch = batch.clone() 50 | batch.edge_index, batch.edge_attr = dropout_adj(batch.edge_index, batch.edge_attr, 51 | p=self.dropout_adj, keep_self_loops= self.keep_self_loops) 52 | 53 | 54 | if self.dropout_adj_pa > 0.0 and self.current_epoch >= self.dropout_adj_T: 55 | batch = batch.clone() 56 | batch.edge_index, batch.edge_attr = dropout_adj_parents(batch.edge_index, batch.edge_attr, 57 | p=self.dropout_adj_pa, prob_keep_self=self.dropout_adj_pa_prob_keep_self) 58 | 59 | 60 | if optimizer_idx == 0: # Inference 61 | objective, data = self.model(batch, 62 | estimator=self.estimator_inference, 63 | beta=self.beta*self.get_beta_annealing_factor(self.current_epoch)) 64 | 65 | self.log('train_objective_inference', objective.item(), prog_bar=True) 66 | for key, value in data.items(): 67 | self.log(f'train_{key}_inference', value.item(), prog_bar=True) 68 | else: # Generator 69 | objective, data = self.model(batch, estimator=self.estimator_gener) 70 | 71 | self.log('train_objective_generator', objective.item(), prog_bar=True) 72 | for key, value in data.items(): 73 | self.log(f'train_{key}_generator', value.item(), prog_bar=True) 74 | 75 | return -objective 76 | -------------------------------------------------------------------------------- /VACA_modified/modules/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/patrickrchao/DiffusionBasedCausalModels/33fe8010a2d7700bd715563e70c5802d748e4a29/VACA_modified/modules/.DS_Store -------------------------------------------------------------------------------- /VACA_modified/modules/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/patrickrchao/DiffusionBasedCausalModels/33fe8010a2d7700bd715563e70c5802d748e4a29/VACA_modified/modules/__init__.py -------------------------------------------------------------------------------- /VACA_modified/modules/__pycache__/__init__.cpython-37.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/patrickrchao/DiffusionBasedCausalModels/33fe8010a2d7700bd715563e70c5802d748e4a29/VACA_modified/modules/__pycache__/__init__.cpython-37.pyc -------------------------------------------------------------------------------- /VACA_modified/modules/__pycache__/__init__.cpython-39.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/patrickrchao/DiffusionBasedCausalModels/33fe8010a2d7700bd715563e70c5802d748e4a29/VACA_modified/modules/__pycache__/__init__.cpython-39.pyc -------------------------------------------------------------------------------- /VACA_modified/modules/__pycache__/disjoint_gnn.cpython-37.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/patrickrchao/DiffusionBasedCausalModels/33fe8010a2d7700bd715563e70c5802d748e4a29/VACA_modified/modules/__pycache__/disjoint_gnn.cpython-37.pyc -------------------------------------------------------------------------------- /VACA_modified/modules/__pycache__/disjoint_gnn.cpython-39.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/patrickrchao/DiffusionBasedCausalModels/33fe8010a2d7700bd715563e70c5802d748e4a29/VACA_modified/modules/__pycache__/disjoint_gnn.cpython-39.pyc -------------------------------------------------------------------------------- /VACA_modified/modules/blocks/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/patrickrchao/DiffusionBasedCausalModels/33fe8010a2d7700bd715563e70c5802d748e4a29/VACA_modified/modules/blocks/.DS_Store -------------------------------------------------------------------------------- /VACA_modified/modules/blocks/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/patrickrchao/DiffusionBasedCausalModels/33fe8010a2d7700bd715563e70c5802d748e4a29/VACA_modified/modules/blocks/__init__.py -------------------------------------------------------------------------------- /VACA_modified/modules/blocks/__pycache__/__init__.cpython-37.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/patrickrchao/DiffusionBasedCausalModels/33fe8010a2d7700bd715563e70c5802d748e4a29/VACA_modified/modules/blocks/__pycache__/__init__.cpython-37.pyc -------------------------------------------------------------------------------- /VACA_modified/modules/blocks/__pycache__/__init__.cpython-39.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/patrickrchao/DiffusionBasedCausalModels/33fe8010a2d7700bd715563e70c5802d748e4a29/VACA_modified/modules/blocks/__pycache__/__init__.cpython-39.pyc -------------------------------------------------------------------------------- /VACA_modified/modules/blocks/__pycache__/disjoint_dense.cpython-37.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/patrickrchao/DiffusionBasedCausalModels/33fe8010a2d7700bd715563e70c5802d748e4a29/VACA_modified/modules/blocks/__pycache__/disjoint_dense.cpython-37.pyc -------------------------------------------------------------------------------- /VACA_modified/modules/blocks/__pycache__/disjoint_dense.cpython-39.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/patrickrchao/DiffusionBasedCausalModels/33fe8010a2d7700bd715563e70c5802d748e4a29/VACA_modified/modules/blocks/__pycache__/disjoint_dense.cpython-39.pyc -------------------------------------------------------------------------------- /VACA_modified/modules/blocks/__pycache__/disjoint_graph_conv.cpython-37.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/patrickrchao/DiffusionBasedCausalModels/33fe8010a2d7700bd715563e70c5802d748e4a29/VACA_modified/modules/blocks/__pycache__/disjoint_graph_conv.cpython-37.pyc -------------------------------------------------------------------------------- /VACA_modified/modules/blocks/__pycache__/disjoint_graph_conv.cpython-39.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/patrickrchao/DiffusionBasedCausalModels/33fe8010a2d7700bd715563e70c5802d748e4a29/VACA_modified/modules/blocks/__pycache__/disjoint_graph_conv.cpython-39.pyc -------------------------------------------------------------------------------- /VACA_modified/modules/blocks/dense.py: -------------------------------------------------------------------------------- 1 | import torch.nn as nn 2 | 3 | from utils.activations import get_activation 4 | 5 | 6 | def basic_dense_block(in_dim, out_dim, activation_name, drop_rate=0.0, bn=True, **kwargs): 7 | act = get_activation(activation_name) 8 | dense = nn.Linear(in_dim, out_dim) 9 | drop_layer = nn.Dropout(drop_rate) if drop_rate > 0 else nn.Identity() 10 | bn_layer = nn.BatchNorm1d(out_dim) if bn else nn.Identity() 11 | return nn.Sequential(dense, bn_layer, act, drop_layer) 12 | -------------------------------------------------------------------------------- /VACA_modified/modules/blocks/disjoint_dense.py: -------------------------------------------------------------------------------- 1 | import torch 2 | import torch.nn as nn 3 | 4 | 5 | class DisjointDense(nn.Module): 6 | 7 | def __init__(self, in_dimension: int, 8 | out_dimension: int, 9 | num_disjoint: int): 10 | super(DisjointDense, self).__init__() 11 | 12 | self.weights = nn.Linear(num_disjoint, in_dimension * out_dimension, bias=False) 13 | 14 | self.bias = nn.Linear(num_disjoint, out_dimension, bias=False) 15 | 16 | self.in_dimension = in_dimension 17 | self.out_dimension = out_dimension 18 | 19 | def reset_parameters(self): 20 | self.weights.reset_parameters() 21 | self.bias.reset_parameters() 22 | 23 | def forward(self, x, one_hot_selector): 24 | w = self.weights(one_hot_selector).view(-1, self.in_dimension, self.out_dimension) # [N, in, out] 25 | h = torch.einsum('bij,bi->bj', w, x) # [N, out] 26 | 27 | bias = self.bias(one_hot_selector) 28 | 29 | return h + bias 30 | -------------------------------------------------------------------------------- /VACA_modified/modules/blocks/disjoint_graph_conv.py: -------------------------------------------------------------------------------- 1 | from typing import Optional 2 | 3 | import torch 4 | import torch.nn as nn 5 | from torch import Tensor 6 | from torch_geometric.nn.conv import MessagePassing 7 | from torch_geometric.typing import Adj 8 | 9 | from modules.blocks.disjoint_dense import DisjointDense 10 | from utils.activations import get_activation 11 | from utils.constants import Cte 12 | 13 | 14 | class DisjointGConv(MessagePassing): 15 | """ 16 | Disjoint Graph convolution 17 | """ 18 | 19 | def __init__(self, m_channels: list, 20 | edge_dim: int, 21 | aggr: Optional[str] = 'add', 22 | act_name: Optional[str] = 'relu', 23 | drop_rate: Optional[float] = 0.0, 24 | use_i_in_message_ij: Optional[bool] = False, 25 | **kwargs): 26 | """ 27 | 28 | Args: 29 | m_channels: 30 | edge_dim: 31 | one hot encoding of the index of the edge in the graph. 32 | I.e., edge_dim = # edges in our graph including self loops. 33 | aggr: 34 | act_name: 35 | drop_rate: 36 | use_i_in_message_ij: 37 | **kwargs: 38 | """ 39 | 40 | super(DisjointGConv, self).__init__(aggr=aggr, 41 | node_dim=0, **kwargs) 42 | 43 | assert len(m_channels) >= 2 44 | self.m_net_list = nn.ModuleList() 45 | self.activs = nn.ModuleList() 46 | self.dropouts = nn.ModuleList() 47 | self.use_i_in_message_ij = use_i_in_message_ij 48 | self.m_layers = len(m_channels) - 1 49 | if self.use_i_in_message_ij: m_channels[0] = m_channels[0] * 2 50 | 51 | for i, (in_ch, out_ch) in enumerate(zip(m_channels[:-1], m_channels[1:])): 52 | m_net = DisjointDense(in_dimension=in_ch, out_dimension=out_ch, num_disjoint=edge_dim) 53 | self.m_net_list.append(m_net) 54 | act = get_activation(act_name if (i + 1) < len(m_channels[:-1]) else Cte.IDENTITY) 55 | self.activs.append(act) 56 | 57 | dropout = nn.Dropout(drop_rate if (i + 1) < len(m_channels[:-1]) else 0.0) 58 | self.dropouts.append(dropout) 59 | 60 | self.edge_dim = edge_dim 61 | 62 | self.reset_parameters() 63 | 64 | def reset_parameters(self): 65 | for m_net in self.m_net_list: 66 | m_net.reset_parameters() 67 | 68 | def forward(self, x: Tensor, 69 | edge_index: Adj, 70 | edge_attr: Tensor) -> Tensor: 71 | """ 72 | 73 | Args: 74 | x: 75 | edge_index: 76 | edge_index = [] 77 | edge_index.append([0,1]) 78 | edge_index.append([2,2]) 79 | edge_attr: 80 | 81 | Returns: 82 | 83 | """ 84 | 85 | out = self.propagate(edge_index, x=x, edge_attr=edge_attr, size=None) 86 | return out 87 | 88 | def message(self, x_i: Tensor, 89 | x_j: Tensor, 90 | edge_attr: Tensor) -> Tensor: 91 | """ 92 | 93 | Args: 94 | x_i: 95 | are N nodes being updated 96 | x_j: 97 | is a neighbor of node x_i, could be itself if we have self-loops 98 | edge_attr: 99 | dimension self.edge_dim. In our case one-hot encoding 100 | 101 | Returns: 102 | 103 | """ 104 | 105 | if self.use_i_in_message_ij: 106 | x = torch.cat([x_i, x_j], dim=1) 107 | else: 108 | x = x_j 109 | 110 | for i, (m_net, act, dout) in enumerate(zip(self.m_net_list, self.activs, self.dropouts)): 111 | h = act(m_net(x, one_hot_selector=edge_attr)) 112 | x = dout(h) 113 | return x 114 | -------------------------------------------------------------------------------- /VACA_modified/modules/blocks/pna/__init__.py: -------------------------------------------------------------------------------- 1 | from .pna import PNAConv, PNAConvSimple -------------------------------------------------------------------------------- /VACA_modified/modules/blocks/pna/aggregators.py: -------------------------------------------------------------------------------- 1 | from typing import Optional 2 | 3 | import torch 4 | from torch import Tensor 5 | from torch_scatter import scatter 6 | 7 | 8 | # Implemented with the help of Matthias Fey, author of PyTorch Geometric 9 | # For an example see https://github.com/rusty1s/pytorch_geometric/blob/master/examples/pna.py 10 | 11 | def aggregate_sum(src: Tensor, index: Tensor, dim_size: Optional[int]): 12 | return scatter(src, index, 0, None, dim_size, reduce='sum') 13 | 14 | 15 | def aggregate_mean(src: Tensor, index: Tensor, dim_size: Optional[int]): 16 | return scatter(src, index, 0, None, dim_size, reduce='mean') 17 | 18 | 19 | def aggregate_mul(src: Tensor, index: Tensor, dim_size: Optional[int]): 20 | return scatter(src, index, 0, None, dim_size, reduce='mul') 21 | 22 | 23 | def aggregate_min(src: Tensor, index: Tensor, dim_size: Optional[int]): 24 | return scatter(src, index, 0, None, dim_size, reduce='min') 25 | 26 | 27 | def aggregate_max(src: Tensor, index: Tensor, dim_size: Optional[int]): 28 | return scatter(src, index, 0, None, dim_size, reduce='max') 29 | 30 | 31 | def aggregate_var(src, index, dim_size): 32 | mean = aggregate_mean(src, index, dim_size) 33 | mean_squares = aggregate_mean(src * src, index, dim_size) 34 | return mean_squares - mean * mean 35 | 36 | 37 | def aggregate_std(src, index, dim_size): 38 | return torch.sqrt(torch.relu(aggregate_var(src, index, dim_size)) + 1e-5) 39 | 40 | 41 | AGGREGATORS = { 42 | 'sum': aggregate_sum, 43 | 'mean': aggregate_mean, 44 | 'min': aggregate_min, 45 | 'max': aggregate_max, 46 | 'var': aggregate_var, 47 | 'std': aggregate_std, 48 | } 49 | -------------------------------------------------------------------------------- /VACA_modified/modules/blocks/pna/disjoint_pna.py: -------------------------------------------------------------------------------- 1 | from typing import Optional, List, Dict 2 | 3 | import torch 4 | import torch.nn as nn 5 | from torch import Tensor 6 | from torch_geometric.nn.conv import MessagePassing 7 | from torch_geometric.typing import Adj 8 | from torch_geometric.utils import degree 9 | 10 | from modules.blocks.disjoint_dense import DisjointDense 11 | from utils.activations import get_activation 12 | from .aggregators import AGGREGATORS 13 | from .scalers import SCALERS 14 | 15 | 16 | class DisjointPNAConv(MessagePassing): 17 | def __init__(self, m_channels: list, 18 | edge_dim: int, 19 | num_nodes: int, 20 | aggregators: List[str], 21 | scalers: List[str], 22 | deg: Tensor, 23 | act_name: Optional[str] = 'relu', 24 | drop_rate: Optional[float] = 0.0, 25 | **kwargs): 26 | ''' 27 | edge_dim is a one hot encoding of the index of the edge in the graph. 28 | I.e. edge_dim = # edges in our graph including self loops. 29 | ''' 30 | super(DisjointPNAConv, self).__init__(aggr=None, 31 | node_dim=0, **kwargs) 32 | 33 | assert len(m_channels) >= 2 34 | 35 | self.aggregators = [AGGREGATORS[aggr] for aggr in aggregators] 36 | self.scalers = [SCALERS[scale] for scale in scalers] 37 | 38 | self.m_net_list = nn.ModuleList() 39 | self.activs = nn.ModuleList() 40 | self.dropouts = nn.ModuleList() 41 | 42 | self.m_layers = len(m_channels) - 1 43 | m_channels[0] = m_channels[0] * 2 44 | 45 | self.avg_deg: Dict[str, float] = { 46 | 'lin': deg.mean().item(), 47 | 'log': (deg + 1).log().mean().item(), 48 | 'exp': deg.exp().mean().item(), 49 | } 50 | 51 | for i, (in_ch, out_ch) in enumerate(zip(m_channels[:-1], m_channels[1:])): 52 | m_net = DisjointDense(in_dimension=in_ch, out_dimension=out_ch, num_disjoint=edge_dim) 53 | self.m_net_list.append(m_net) 54 | act = get_activation(act_name) 55 | self.activs.append(act) 56 | 57 | dropout = nn.Dropout(drop_rate) 58 | self.dropouts.append(dropout) 59 | 60 | self.edge_dim = edge_dim 61 | 62 | self.in_update_net = (len(aggregators) * len(scalers)) * m_channels[-1] + m_channels[0] // 2 63 | 64 | self.update_net = DisjointDense(in_dimension=self.in_update_net, out_dimension=m_channels[-1], 65 | num_disjoint=num_nodes) 66 | 67 | self.reset_parameters() 68 | 69 | def reset_parameters(self): 70 | for m_net in self.m_net_list: 71 | m_net.reset_parameters() 72 | 73 | self.update_net.reset_parameters() 74 | 75 | def forward(self, x: Tensor, edge_index: Adj, 76 | edge_attr: Tensor, node_ids: Tensor) -> Tensor: 77 | ''' 78 | edge_index = [] 79 | edge_index.append([0,1]) 80 | edge_index.append([2,2]) 81 | ''' 82 | out = self.propagate(edge_index, x=x, edge_attr=edge_attr, size=None) 83 | 84 | out = torch.cat([x, out], dim=-1) 85 | 86 | return self.update_net(out, one_hot_selector=node_ids) 87 | 88 | def message(self, x_i: Tensor, x_j: Tensor, edge_attr: Tensor) -> Tensor: 89 | ''' 90 | edge_attr: dimension self.edge_dim. In our case one-hot encoding 91 | x_i are N nodes being updated 92 | x_j is a neighbor of node x_i, could be itself if we have self-loops 93 | ''' 94 | 95 | x = torch.cat([x_i, x_j], dim=1) 96 | 97 | for i, (m_net, act, dout) in enumerate(zip(self.m_net_list, self.activs, self.dropouts)): 98 | h = act(m_net(x, one_hot_selector=edge_attr)) 99 | x = dout(h) 100 | return x 101 | 102 | def aggregate(self, inputs: Tensor, index: Tensor, 103 | dim_size: Optional[int] = None) -> Tensor: 104 | outs = [aggr(inputs, index, dim_size) for aggr in self.aggregators] 105 | out = torch.cat(outs, dim=-1) 106 | 107 | deg = degree(index, dim_size, dtype=inputs.dtype).view(-1, 1) 108 | 109 | outs = [scaler(out, deg, self.avg_deg) for scaler in self.scalers] 110 | 111 | return torch.cat(outs, dim=-1) 112 | -------------------------------------------------------------------------------- /VACA_modified/modules/blocks/pna/scalers.py: -------------------------------------------------------------------------------- 1 | from typing import Dict 2 | 3 | import torch 4 | from torch import Tensor 5 | 6 | 7 | # Implemented with the help of Matthias Fey, author of PyTorch Geometric 8 | # For an example see https://github.com/rusty1s/pytorch_geometric/blob/master/examples/pna.py 9 | 10 | def scale_identity(src: Tensor, deg: Tensor, avg_deg: Dict[str, float]): 11 | return src 12 | 13 | 14 | def scale_amplification(src: Tensor, deg: Tensor, avg_deg: Dict[str, float]): 15 | return src * (torch.log(deg + 1) / avg_deg['log']) 16 | 17 | 18 | def scale_attenuation(src: Tensor, deg: Tensor, avg_deg: Dict[str, float]): 19 | scale = avg_deg['log'] / torch.log(deg + 1) 20 | scale[deg == 0] = 1 21 | return src * scale 22 | 23 | 24 | def scale_linear(src: Tensor, deg: Tensor, avg_deg: Dict[str, float]): 25 | return src * (deg / avg_deg['lin']) 26 | 27 | 28 | def scale_inverse_linear(src: Tensor, deg: Tensor, avg_deg: Dict[str, float]): 29 | scale = avg_deg['lin'] / deg 30 | scale[deg == 0] = 1 31 | return src * scale 32 | 33 | 34 | SCALERS = { 35 | 'identity': scale_identity, 36 | 'amplification': scale_amplification, 37 | 'attenuation': scale_attenuation, 38 | 'linear': scale_linear, 39 | 'inverse_linear': scale_inverse_linear 40 | } 41 | -------------------------------------------------------------------------------- /VACA_modified/modules/dense.py: -------------------------------------------------------------------------------- 1 | from typing import List 2 | 3 | import torch.nn as nn 4 | 5 | from utils.constants import Cte 6 | from .blocks.dense import basic_dense_block 7 | 8 | 9 | class MLPModule(nn.Module): 10 | 11 | def __init__(self, h_dim_list: List[int], 12 | activ_name: str = Cte.RELU, 13 | bn: bool = True, 14 | drop_rate: float = 0.0): 15 | super(MLPModule, self).__init__() 16 | assert isinstance(h_dim_list, list) 17 | assert len(h_dim_list) > 1 18 | n_layers = len(h_dim_list) - 1 19 | 20 | layers = [] 21 | for i, (h_in, h_out) in enumerate(zip(h_dim_list[:-1], h_dim_list[1:])): 22 | if (i + 1) < n_layers: 23 | layers.append(basic_dense_block(h_in, h_out, activ_name, drop_rate=drop_rate, bn=bn)) 24 | else: 25 | layers.append(basic_dense_block(h_in, h_out, activation_name=Cte.IDENTITY, 26 | drop_rate=drop_rate, bn=bn)) 27 | 28 | self.mlp = nn.Sequential(*layers) 29 | 30 | self.dims = None 31 | 32 | def set_output_dims(self, dims): 33 | self.dims = dims 34 | 35 | def forward(self, x): 36 | batch_size = x.size(0) 37 | x = x.view(batch_size, -1) 38 | logits = self.mlp(x) 39 | if self.dims is not None: 40 | logits = logits.view(logits.size(0), *self.dims) 41 | 42 | return logits 43 | -------------------------------------------------------------------------------- /VACA_modified/modules/disjoint_gnn.py: -------------------------------------------------------------------------------- 1 | from typing import List 2 | 3 | import torch.nn as nn 4 | 5 | from modules.blocks.disjoint_dense import DisjointDense 6 | from modules.blocks.disjoint_graph_conv import DisjointGConv 7 | from utils.activations import get_activation 8 | from utils.constants import Cte 9 | 10 | 11 | class DisjointGNN(nn.Module): 12 | ''' 13 | Disjoint parameters for each edge connection 14 | ''' 15 | 16 | def __init__(self, c_list: List[int], # [1,2,2] 17 | m_layers: int, # Number of layers per message 18 | edge_dim: int, 19 | num_nodes: int = None, 20 | drop_rate: float = 0.1, 21 | residual: int = 0, 22 | act_name: str = Cte.RELU, 23 | use_i_in_message_ij: bool = True, 24 | aggr: str = 'add'): 25 | 26 | super().__init__() 27 | assert (len(c_list) - 1) % m_layers == 0 28 | # 29 | self.residual = residual 30 | self.convs = nn.ModuleList() 31 | self.activs = nn.ModuleList() 32 | self.dropouts = nn.ModuleList() 33 | if residual: 34 | assert num_nodes is not None 35 | self.residuals = nn.ModuleList() 36 | else: 37 | self.residuals = None 38 | self.num_steps_mp = (len(c_list) - 1) // m_layers # Number of steps of message passing 39 | 40 | for i in range(self.num_steps_mp): 41 | m_channels = c_list[(m_layers * i):(m_layers * (i + 1) + 1)] 42 | net = DisjointGConv(m_channels=m_channels.copy(), 43 | edge_dim=edge_dim, 44 | aggr=aggr, 45 | act_name=act_name, 46 | drop_rate=drop_rate, 47 | use_i_in_message_ij=use_i_in_message_ij) 48 | 49 | self.convs.append(net) 50 | act = get_activation(act_name if (i + 1) < self.num_steps_mp else Cte.IDENTITY) 51 | self.activs.append(act) 52 | 53 | dropout = nn.Dropout(drop_rate if (i + 1) < self.num_steps_mp else 0.0) 54 | self.dropouts.append(dropout) 55 | if self.residual: 56 | fc = DisjointDense(in_dimension=m_channels[0], out_dimension=m_channels[-1], num_disjoint=num_nodes) 57 | self.residuals.append(fc) 58 | self.drop_rate = drop_rate 59 | 60 | self.act_name = act_name 61 | 62 | def forward(self, x, edge_index, edge_attr=None, **kwargs): 63 | """ 64 | Inputs: 65 | x - Input features per node 66 | edge_index - List of vertex index pairs representing the edges in the graph (PyTorch geometric notation) 67 | """ 68 | node_ids = kwargs['node_ids'] 69 | for i, (conv, act, dout) in enumerate(zip(self.convs, self.activs, self.dropouts)): 70 | h = act(conv(x, edge_index, edge_attr)) 71 | if self.residual: 72 | 73 | x = h + self.residuals[i](x, node_ids) 74 | x = dout(x) 75 | else: 76 | x = dout(h) 77 | 78 | return x 79 | -------------------------------------------------------------------------------- /VACA_modified/modules/disjoint_pna.py: -------------------------------------------------------------------------------- 1 | from typing import List 2 | 3 | import torch.nn as nn 4 | from torch import Tensor 5 | 6 | from modules.blocks.disjoint_dense import DisjointDense 7 | from modules.blocks.pna.disjoint_pna import DisjointPNAConv 8 | from utils.activations import get_activation 9 | from utils.constants import Cte 10 | 11 | """ 12 | Code build from https://github.com/lukecavabarrett/pna 13 | """ 14 | 15 | 16 | class DisjointPNA(nn.Module): 17 | """ 18 | Disjoint parameters for each edge connection 19 | """ 20 | 21 | def __init__(self, c_list: List[int], # [1,2,2] 22 | m_layers: int, # Number of layers per message 23 | edge_dim: int, 24 | deg: Tensor, 25 | num_nodes: int = None, 26 | aggregators: List[str] = None, 27 | scalers: List[str] = None, 28 | drop_rate: float = 0.1, 29 | residual: int = 0, 30 | act_name: str = Cte.RELU, 31 | ): 32 | 33 | super().__init__() 34 | assert (len(c_list) - 1) % m_layers == 0 35 | 36 | if aggregators is None: 37 | aggregators = ['sum', 'min', 'max', 'std'] 38 | if scalers is None: 39 | scalers = ['identity'] 40 | 41 | # 42 | self.residual = residual 43 | self.convs = nn.ModuleList() 44 | self.activs = nn.ModuleList() 45 | self.dropouts = nn.ModuleList() 46 | if residual: 47 | assert num_nodes is not None 48 | self.residuals = nn.ModuleList() 49 | else: 50 | self.residuals = None 51 | self.num_steps_mp = (len(c_list) - 1) // m_layers # Number of steps of message passing 52 | 53 | for i in range(self.num_steps_mp): 54 | m_channels = c_list[(m_layers * i):(m_layers * (i + 1) + 1)] 55 | net = DisjointPNAConv(m_channels=m_channels.copy(), 56 | edge_dim=edge_dim, 57 | num_nodes=num_nodes, 58 | aggregators=aggregators, 59 | scalers=scalers, 60 | deg=deg, 61 | act_name=act_name, 62 | drop_rate=drop_rate) 63 | 64 | self.convs.append(net) 65 | act = get_activation(act_name if (i + 1) < self.num_steps_mp else Cte.IDENTITY) 66 | self.activs.append(act) 67 | 68 | dropout = nn.Dropout(drop_rate if (i + 1) < self.num_steps_mp else 0.0) 69 | self.dropouts.append(dropout) 70 | if self.residual: 71 | fc = DisjointDense(in_dimension=m_channels[0], out_dimension=m_channels[-1], num_disjoint=num_nodes) 72 | self.residuals.append(fc) 73 | self.drop_rate = drop_rate 74 | 75 | self.act_name = act_name 76 | 77 | def forward(self, x, edge_index, edge_attr=None, **kwargs): 78 | """ 79 | 80 | Args: 81 | x: Input features per node 82 | edge_index: List of vertex index pairs representing the edges in the graph (PyTorch geometric notation) 83 | edge_attr: 84 | **kwargs: 85 | 86 | Returns: 87 | 88 | """ 89 | node_ids = kwargs['node_ids'] 90 | for i, (conv, act, dout) in enumerate(zip(self.convs, self.activs, self.dropouts)): 91 | h = act(conv(x, edge_index, edge_attr, node_ids)) 92 | if self.residual: 93 | 94 | x = h + self.residuals[i](x, node_ids) 95 | x = dout(x) 96 | else: 97 | x = dout(h) 98 | 99 | return x 100 | -------------------------------------------------------------------------------- /VACA_modified/modules/pna.py: -------------------------------------------------------------------------------- 1 | import torch.nn as nn 2 | 3 | from modules.blocks.pna import PNAConv 4 | from utils.activations import get_activation 5 | from utils.constants import Cte 6 | 7 | """ 8 | Code build from https://github.com/lukecavabarrett/pna 9 | """ 10 | 11 | 12 | class PNAModule(nn.Module): 13 | """ 14 | Principal Neighborhood aggregation (PNA) 15 | 16 | Output activation is Identity 17 | """ 18 | 19 | def __init__(self, c_list, deg, 20 | edge_dim=None, 21 | drop_rate=0.1, 22 | act_name=Cte.RELU, 23 | aggregators=None, 24 | scalers=None, 25 | residual=False): 26 | """ 27 | 28 | Args: 29 | c_list: 30 | deg: In-degree histogram over training data 31 | edge_dim: 32 | drop_rate: 33 | act_name: 34 | aggregators: 35 | scalers: 36 | residual: 37 | """ 38 | 39 | super().__init__() 40 | if aggregators is None: 41 | aggregators = ['sum', 'min', 'max', 'std'] 42 | if scalers is None: 43 | scalers = ['identity', 'amplification', 'attenuation'] 44 | 45 | self.convs = nn.ModuleList() 46 | self.activs = nn.ModuleList() 47 | self.dropouts = nn.ModuleList() 48 | if residual: 49 | self.residuals = nn.ModuleList() 50 | else: 51 | self.residuals = None 52 | 53 | self.residual = residual 54 | 55 | for i, (in_ch, out_ch) in enumerate(zip(c_list[:-1], c_list[1:])): 56 | conv = PNAConv(in_channels=in_ch, out_channels=out_ch, aggregators=aggregators, 57 | edge_dim=edge_dim, 58 | scalers=scalers, deg=deg, post_layers=1) 59 | 60 | self.convs.append(conv) 61 | act = get_activation(act_name if (i + 1) < len(c_list[:-1]) else Cte.IDENTITY) 62 | self.activs.append(act) 63 | 64 | dropout = nn.Dropout(drop_rate) 65 | self.dropouts.append(dropout) 66 | if self.residual: 67 | fc = nn.Linear(in_features=in_ch, out_features=out_ch) 68 | self.residuals.append(fc) 69 | 70 | self.drop_rate = drop_rate 71 | self.act_name = act_name 72 | 73 | def forward(self, x, edge_index, edge_attr=None, **kwargs): 74 | """ 75 | 76 | Args: 77 | x: Input features per node 78 | edge_index: List of vertex index pairs representing the edges in the graph (PyTorch geometric notation) 79 | edge_attr: 80 | **kwargs: 81 | 82 | Returns: 83 | 84 | """ 85 | 86 | for i, (conv, act, dout) in enumerate(zip(self.convs, self.activs, self.dropouts)): 87 | h = act(conv(x, edge_index, edge_attr)) 88 | if self.residual: 89 | x = h + self.residuals[i](x) 90 | x = dout(x) 91 | else: 92 | x = dout(h) 93 | return x 94 | -------------------------------------------------------------------------------- /VACA_modified/tests/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/patrickrchao/DiffusionBasedCausalModels/33fe8010a2d7700bd715563e70c5802d748e4a29/VACA_modified/tests/__init__.py -------------------------------------------------------------------------------- /VACA_modified/tests/test_custom_dataset.py: -------------------------------------------------------------------------------- 1 | from datasets.toy import create_toy_dataset 2 | from utils.distributions import * 3 | dataset = create_toy_dataset(root_dir='./my_custom_datasets', 4 | name='2graph', 5 | eq_type='linear', 6 | nodes_to_intervene=['x1'], 7 | structural_eq={'x1': lambda u1: u1, 8 | 'x2': lambda u2, x1: u2 + x1}, 9 | noises_distr={'x1': Normal(0,1), 10 | 'x2': Normal(0,1)}, 11 | adj_edges={'x1': ['x2'], 12 | 'x2': []}, 13 | split='train', 14 | num_samples=5000, 15 | likelihood_names='d_d', 16 | lambda_=0.05) 17 | -------------------------------------------------------------------------------- /VACA_modified/tests/test_datasets.py: -------------------------------------------------------------------------------- 1 | 2 | import os 3 | import numpy as np 4 | from utils._errors import IsHeterogeneousError 5 | def print_dataset_details(dataset): 6 | print('') 7 | print(f"DATASET {dataset.name}") 8 | dataset.prepare_data() 9 | print(f"\ndataset.num_nodes") 10 | print(dataset.num_nodes) 11 | print(f"\ndataset.node_dim") 12 | try: 13 | print(dataset.node_dim) 14 | except IsHeterogeneousError: 15 | print('Dataset is heterogeneous! You will get an error if you use dataset.node_dim') 16 | 17 | print(f"\ndataset.num_parameters") 18 | print(dataset.num_parameters) 19 | 20 | print(f"\ndataset.dim_of_x_in_x0") 21 | print(dataset.dim_of_x_in_x0) 22 | 23 | print(f"\ndataset._get_x0_dim_of_node_name({dataset.nodes_list[0]})") 24 | 25 | print(dataset._get_x0_dim_of_node_name(dataset.nodes_list[0])) 26 | 27 | 28 | node0_name = dataset.nodes_list[0] 29 | 30 | print(f"\nIntervention {node0_name}=0") 31 | x_I = {node0_name: 0} 32 | dataset.set_intervention(x_I=x_I) 33 | batch = dataset.__getitem__(0) 34 | x = batch.x.reshape(1, -1) 35 | print(f"data.x[0]={x[0]}") 36 | x_i = batch.x_i.reshape(1, -1) 37 | print(f"data.x_i[0]={x_i[0]}") 38 | 39 | x_sample_intervention = dataset.sample_intervention(x_I=x_I, 40 | n_samples=2) 41 | 42 | if dataset.has_ground_truth: 43 | print(f"\nGenerating 2 interventional samples {node0_name}=0") 44 | with np.printoptions(precision=2, suppress=True): 45 | print(f"x_sample_intervention=\n{x_sample_intervention}") 46 | 47 | print(f"\nGetting 2 counterfactual samples {node0_name}=0") 48 | batch_1 = dataset.__getitem__(1) 49 | x = np.concatenate([batch.x.reshape(1, -1), 50 | batch_1.x.reshape(1, -1)]) 51 | u = np.concatenate([batch.u.reshape(1, -1), 52 | batch_1.u.reshape(1, -1)]) 53 | x_cf = dataset.get_counterfactual(x_factual=x, 54 | u_factual=u, 55 | x_I=x_I) 56 | with np.printoptions(precision=2, suppress=True): 57 | for i in range(2): 58 | print(f"x[{i}]={x[i]}") 59 | print(f"x_cf[{i}]={x_cf[i]}") 60 | 61 | 62 | root_dir = os.path.join('.', '_data') 63 | 64 | # %% Collider 65 | from datasets.collider import ColliderSCM 66 | from utils.constants import Cte 67 | 68 | dataset = ColliderSCM(root_dir=root_dir, 69 | split = 'train', 70 | num_samples = 5000, 71 | equations_type=Cte.LINEAR, 72 | likelihood_names='d_d_d', 73 | transform=None) 74 | 75 | print_dataset_details(dataset) 76 | 77 | # %% M-graph 78 | from datasets.mgraph import MGraphSCM 79 | 80 | dataset = MGraphSCM(root_dir=root_dir, 81 | split = 'train', 82 | num_samples = 5000, 83 | equations_type=Cte.LINEAR, 84 | likelihood_names='d_d_d_d_d', 85 | transform=None) 86 | 87 | print_dataset_details(dataset) 88 | 89 | 90 | # %% Triangle 91 | from datasets.triangle import TriangleSCM 92 | 93 | dataset = TriangleSCM(root_dir=root_dir, 94 | split = 'train', 95 | num_samples = 5000, 96 | equations_type=Cte.LINEAR, 97 | likelihood_names='d_d_d', 98 | transform=None) 99 | 100 | print_dataset_details(dataset) 101 | 102 | # %% Chain 103 | from datasets.chain import ChainSCM 104 | dataset = ChainSCM(root_dir=root_dir, 105 | split = 'train', 106 | num_samples = 5000, 107 | equations_type=Cte.LINEAR, 108 | likelihood_names='d_d_d', 109 | transform=None) 110 | 111 | print_dataset_details(dataset) 112 | 113 | # %% Loan 114 | from datasets.loan import LoanSCM 115 | from utils.constants import Cte 116 | 117 | dataset = LoanSCM(root_dir=root_dir, 118 | split = 'train', 119 | num_samples = 5000, 120 | equations_type=Cte.LINEAR, 121 | likelihood_names='b_d_d_d_d_d_d', 122 | transform=None) 123 | 124 | print_dataset_details(dataset) 125 | 126 | 127 | # %% German 128 | from datasets.german import GermanSCM 129 | from utils.constants import Cte 130 | 131 | dataset = GermanSCM(root_dir=root_dir, 132 | split = 'train', 133 | num_samples_tr = 800, 134 | lambda_ = 0.05, 135 | transform=None) 136 | 137 | print_dataset_details(dataset) 138 | 139 | 140 | # %% Adult 141 | 142 | from datasets.adult import AdultSCM 143 | from utils.constants import Cte 144 | dataset = AdultSCM(root_dir=root_dir, 145 | split = 'train', 146 | num_samples = 5000, 147 | equations_type=Cte.LINEAR, 148 | likelihood_names = 'c_d_c_b_d_d_c_c_c_c_d', 149 | transform=None) 150 | 151 | print_dataset_details(dataset) 152 | -------------------------------------------------------------------------------- /VACA_modified/tests/test_models.py: -------------------------------------------------------------------------------- 1 | # %% 2 | from data_modules.het_scm import HeterogeneousSCMDataModule 3 | 4 | data_module = HeterogeneousSCMDataModule(data_dir="../Data", 5 | dataset_name='triangle', 6 | num_workers=0, 7 | num_samples_tr=5000, 8 | batch_size=256, 9 | likelihood_names='d_d_d', 10 | normalize='lik', 11 | lambda_=0.05, 12 | equations_type='linear') 13 | 14 | data_module.prepare_data() 15 | print(data_module.edge_dimension) 16 | 17 | data_loader = data_module.train_dataloader() 18 | scaler = data_module.scaler 19 | batch = next(iter(data_loader)) 20 | 21 | # %% 22 | from utils.constants import Cte 23 | model_list = [] 24 | from models.carefl.carefl import CAREFL 25 | 26 | model = CAREFL(node_per_dimension_list=data_module.train_dataset.node_per_dimension_list, 27 | distr_z='laplace', 28 | flow_net_class='mlp', 29 | flow_architecture='spline', 30 | n_layers=1, 31 | n_hidden=1, 32 | parity=False, 33 | scaler=data_module.scaler, 34 | init=None) 35 | 36 | 37 | model_list.append(model) 38 | 39 | from models.multicvae.multicvae import MCVAE 40 | 41 | model = MCVAE(h_dim_list_dec=[8,8], 42 | h_dim_list_enc=[8], 43 | z_dim=4, 44 | drop_rate=0.0, 45 | act_name=Cte.RELU, 46 | likelihood_x=data_module.likelihood_list, 47 | distr_z='normal', 48 | num_epochs_per_nodes=50, 49 | topological_node_dims=data_module.train_dataset.get_node_columns_in_X(), 50 | topological_parents=data_module.topological_parents, 51 | scaler=data_module.scaler) 52 | 53 | 54 | model_list.append(model) 55 | 56 | from models.vaca.vaca import VACA 57 | 58 | model = VACA(h_dim_list_dec=[8,8], 59 | h_dim_list_enc=[8], 60 | z_dim=4, 61 | m_layers = 1, 62 | deg = data_module.get_deg(indegree=True), 63 | edge_dim= data_module.edge_dimension, 64 | num_nodes=data_module.num_nodes, 65 | beta = 1.0, 66 | annealing_beta = False, 67 | residual = 0, # Only PNA architecture 68 | drop_rate = 0.0, 69 | dropout_adj_rate = 0.0, 70 | dropout_adj_pa_rate = 0.0, 71 | dropout_adj_pa_prob_keep_self = 0.0, 72 | keep_self_loops = True, 73 | dropout_adj_T = 0, # Epoch to start the dropout_adj_T 74 | act_name = Cte.RELU, 75 | likelihood_x = data_module.likelihood_list, 76 | distr_z = 'normal', 77 | architecture = 'pna', # PNA, DGNN, DPNA 78 | estimator = 'elbo', 79 | K=1, # Only for IWAE estimator 80 | scaler = data_module.scaler, 81 | init = None, 82 | is_heterogeneous=data_module.is_heterogeneous) 83 | 84 | 85 | model_list.append(model) 86 | 87 | 88 | 89 | 90 | # %% 91 | 92 | from models._evaluator import MyEvaluator 93 | 94 | x_I={data_module.train_dataset.nodes_list[0]: 2} 95 | 96 | for model in model_list[1:]: 97 | print(f"\n\nMODEL: {model._get_name()}\n\n") 98 | evaluator = MyEvaluator(model=model, 99 | intervention_list=data_module.train_dataset.get_intervention_list(), 100 | scaler=data_module.scaler 101 | ) 102 | 103 | model.set_my_evaluator(evaluator=evaluator) 104 | 105 | if model._get_name() != 'CAREFL': 106 | model.set_random_train_sampler(data_module.get_random_train_sampler()) 107 | 108 | output = model.get_objective_metrics(data_loader=data_loader, name='test') 109 | 110 | 111 | z, x, x_real = model.get_observational_distr(data_loader) 112 | 113 | print(f"z: {z.shape}") 114 | print(f"x: {x.shape}") 115 | print(f"x_real: {x_real.shape}") 116 | 117 | 118 | 119 | z, x, x_real = model.get_reconstruction_distr(data_loader) 120 | 121 | 122 | x_gener_dict_out, x_real_dict_out = model.get_interventional_distr(data_loader, 123 | x_I=x_I, 124 | use_aggregated_posterior=False) 125 | 126 | x_cf_gener_dict, x_cf_real_dict, x_factual_dict = model.get_counterfactual_distr(data_loader, 127 | x_I=x_I, 128 | is_noise=False) 129 | 130 | 131 | 132 | output = model.evaluate(data_loader, name='test', save_dir='.') 133 | -------------------------------------------------------------------------------- /VACA_modified/tests/test_probabilistic_model.py: -------------------------------------------------------------------------------- 1 | 2 | # %% 3 | import torch 4 | from utils.probabilistic_model import HeterogeneousDistribution 5 | from utils.likelihoods import get_likelihood 6 | import utils.likelihoods as ul 7 | likelihoods=[ul.NormalLikelihood(1), ul.CategoricalLikelihood(3)] 8 | 9 | 10 | params_list = [lik.params_size for lik in likelihoods] 11 | logits = torch.randn([16, sum(params_list)])*10 12 | 13 | 14 | p = HeterogeneousDistribution(likelihoods=likelihoods, 15 | norm_categorical=False, 16 | norm_by_dim=False) 17 | 18 | 19 | 20 | # %% 21 | p.set_logits(logits) 22 | 23 | print(p.mean[:2]) 24 | 25 | 26 | # %% 27 | 28 | x = p.sample() 29 | 30 | # %% 31 | samples = [] 32 | 33 | logits_list = torch.split(logits, split_size_or_sections=params_list, dim=1) 34 | for logits_i, lik_i in zip(logits_list, likelihoods): 35 | distr_i = lik_i(logits_i) 36 | sample_i = distr_i.sample() 37 | if lik_i.name == 'cat': 38 | y_onehot = torch.FloatTensor(logits_i.shape) 39 | # In your for loop 40 | y_onehot.zero_() 41 | y_onehot.scatter_(1, sample_i.view(-1, 1), 1) 42 | sample_i = y_onehot 43 | samples.append(sample_i) 44 | 45 | sample = torch.cat(samples, dim=1) 46 | 47 | p.log_prob(sample) 48 | 49 | 50 | # %% 51 | import torch 52 | from utils.probabilistic_model import ProbabilisticModelSCM 53 | likelihoods = [] 54 | likelihoods.append([ul.NormalLikelihood(2)]) # First node 55 | likelihoods.append([ul.CategoricalLikelihood(3), ul.CategoricalLikelihood(2)]) # Second node 56 | likelihoods.append([ul.NormalLikelihood(2)]) # Third node 57 | 58 | likelihood = ProbabilisticModelSCM(likelihoods=likelihoods, 59 | embedding_size=8, 60 | act_name='relu', 61 | drop_rate=0.0, 62 | norm_categorical=False, 63 | norm_by_dim=False) 64 | 65 | 66 | 67 | num_graphs = 3 68 | logits = torch.randn([num_graphs*likelihood.num_nodes, likelihood.embedding_size]) 69 | 70 | 71 | px = likelihood(logits, return_mean=False) 72 | 73 | 74 | # %% Test distributions 75 | from utils.distributions import * 76 | Normal(0,1).sample() 77 | Normal(0,1).sample(10) 78 | Normal(0,1).pdf(0) 79 | Normal(0,1).visualize() 80 | 81 | MixtureOfGaussians([0.5, 0.5], [-2, +2], [1, 1]).sample() 82 | MixtureOfGaussians([0.5, 0.5], [-2, +2], [1, 1]).sample(10) 83 | MixtureOfGaussians([0.5, 0.5], [-2, +2], [1, 1]).pdf(0) 84 | MixtureOfGaussians([0.5, 0.5], [-2, +2], [1, 1]).pdf(+2) 85 | MixtureOfGaussians([0.5, 0.5], [-2, +2], [1, 1]).pdf(-2) 86 | MixtureOfGaussians([0.5, 0.5], [-2, +2], [1, 1]).visualize() -------------------------------------------------------------------------------- /VACA_modified/utils/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/patrickrchao/DiffusionBasedCausalModels/33fe8010a2d7700bd715563e70c5802d748e4a29/VACA_modified/utils/.DS_Store -------------------------------------------------------------------------------- /VACA_modified/utils/.ipynb_checkpoints/constants-checkpoint.py: -------------------------------------------------------------------------------- 1 | class Cte: 2 | # equation-types 3 | LINEAR = "linear" 4 | NONLINEAR = 'non-linear' 5 | NONADDITIVE = 'non-additive' 6 | 7 | # Datasets 8 | SENS = 'sex' # sensitive attribute for CF fairness 9 | 10 | TRIANGLE = 'triangle' # a.k.a. Connected fork 11 | COLLIDER = 'collider' 12 | LOAN = 'loan' 13 | ADULT = 'adult' 14 | MGRAPH = 'mgraph' 15 | CHAIN = 'chain' 16 | GERMAN = 'german' 17 | 18 | DATASET_LIST = [COLLIDER, 19 | TRIANGLE, 20 | LOAN, 21 | MGRAPH, 22 | CHAIN, 23 | ADULT, 24 | GERMAN] 25 | DATASET_LIST_TOY = [COLLIDER, 26 | TRIANGLE, 27 | LOAN, 28 | MGRAPH, 29 | CHAIN, 30 | ADULT] 31 | # Models 32 | VACA = 'vaca' 33 | VACA_PIWAE = 'vaca_piwae' 34 | MCVAE = 'mcvae' 35 | CAREFL = 'carefl' 36 | 37 | # Optimizers 38 | ADAM = 'adam' 39 | RADAM = 'radam' 40 | ADAGRAD = 'adag' 41 | ADADELTA = 'adad' 42 | RMS = 'rms' 43 | ASGD = 'asgd' 44 | 45 | # Scheduler 46 | STEP_LR = 'step_lr' 47 | EXP_LR = 'exp_lr' 48 | 49 | # Activation 50 | TAHN = 'tahn' 51 | RELU = 'relu' 52 | RELU6 = 'relu6' 53 | SOFTPLUS = 'softplus' 54 | RRELU = 'rrelu' 55 | LRELU = 'lrelu' 56 | ELU = 'elu' 57 | SELU = 'selu' 58 | SIGMOID = 'sigmoid' 59 | GLU = 'glu' 60 | IDENTITY = 'identity' 61 | 62 | # Distribution 63 | BETA = 'beta' 64 | CONTINOUS_BERN = 'cb' 65 | BERNOULLI = 'ber' 66 | GAUSSIAN = 'normal' 67 | CATEGORICAL = 'cat' 68 | EXPONENTIAL = 'exp' 69 | DELTA = 'delta' 70 | -------------------------------------------------------------------------------- /VACA_modified/utils/.ipynb_checkpoints/distributions-checkpoint.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | from matplotlib import pyplot as plt 3 | from scipy.stats import bernoulli 4 | from scipy.stats import norm # norm for univariate; use multivariate_normal otherwise 5 | 6 | 7 | # univariate distributions 8 | class BaseDistribution(object): 9 | def __init__(self): 10 | pass 11 | 12 | def sample(self, size=1): 13 | raise NotImplementedError 14 | 15 | def pdf(self): 16 | raise NotImplementedError 17 | 18 | def visualize(self): 19 | plt.hist(self.sample(500), 50, facecolor='green', alpha=0.75) 20 | plt.ylabel('Count') 21 | plt.title(fr'Histogram of {self.name}') 22 | plt.grid(True) 23 | plt.show() 24 | 25 | 26 | class Normal(BaseDistribution): 27 | def __init__(self, mean, var): 28 | assert isinstance(mean, int) or isinstance(mean, float), 'Expected `mean` to be an int or float.' 29 | assert isinstance(var, int) or isinstance(var, float), 'Expected `var` to be an int or float.' 30 | self.mean = mean 31 | self.var = var 32 | self.name = f'Normal\t mean={self.mean}, var={self.var}' 33 | 34 | def sample(self, size=1): 35 | tmp = [np.random.normal(self.mean, np.sqrt(self.var)) for _ in range(size)] 36 | return tmp[0] if size == 1 else tmp 37 | 38 | def pdf(self, value): 39 | return norm(self.mean, self.var).pdf(value) 40 | 41 | 42 | class MixtureOfGaussians(BaseDistribution): 43 | 44 | def __init__(self, probs, means, vars): 45 | assert sum(probs) == 1, 'Mixture probabilities must sum to 1.' 46 | assert len(probs) == len(means) == len(vars), 'Length mismatch.' 47 | self.probs = probs 48 | self.means = means 49 | self.vars = vars 50 | self.name = f'MoG\t probs={self.probs}, means={self.means}, vars={self.vars}' 51 | 52 | def sample(self, size=1): 53 | tmp = [ 54 | np.random.normal(self.means[mixture_idx], np.sqrt(self.vars[mixture_idx])) 55 | for mixture_idx in np.random.choice(len(self.probs), size=size, p=self.probs) 56 | ] 57 | return tmp[0] if size == 1 else tmp 58 | 59 | def pdf(self, value): 60 | return np.sum([ 61 | prob * norm(mean, var).pdf(value) 62 | for (prob, mean, var) in zip(self.probs, self.means, self.vars) 63 | ]) 64 | 65 | 66 | class Bernoulli(BaseDistribution): 67 | 68 | def __init__(self, prob): 69 | assert isinstance(prob, int) or isinstance(prob, float), 'Expected `prob` to be an int or float.' 70 | assert prob >= 0 and prob <= 1 71 | 72 | self.prob = prob 73 | self.name = f'Bernoulli\t prob={self.prob}' 74 | 75 | def sample(self, size=1): 76 | tmp = bernoulli.rvs(self.prob, size=size) 77 | return tmp[0] if size == 1 else list(tmp) 78 | 79 | def pdf(self, value): 80 | raise Exception(f'not supported yet; code should not come here.') 81 | 82 | 83 | class Poisson(BaseDistribution): 84 | 85 | def __init__(self, p_lambda): 86 | assert isinstance(p_lambda, int) or isinstance(p_lambda, float), 'Expected `p_lambda` to be an int or float.' 87 | assert p_lambda > 0 88 | self.p_lambda = p_lambda 89 | self.name = f'Poisson\t prob={self.p_lambda}' 90 | 91 | def sample(self, size=1): 92 | tmp = np.random.poisson(self.p_lambda, size) 93 | return tmp[0] if size == 1 else list(tmp) 94 | 95 | def pdf(self, value): 96 | raise Exception(f'not supported yet; code should not come here.') 97 | 98 | 99 | class Gamma(BaseDistribution): 100 | 101 | def __init__(self, shape, scale): 102 | assert isinstance(shape, int) or isinstance(shape, float), 'Expected `shape` to be an int or float.' 103 | assert isinstance(scale, int) or isinstance(scale, float), 'Expected `scale` to be an int or float.' 104 | assert shape > 0 105 | assert scale > 0 106 | self.shape = shape 107 | self.scale = scale 108 | self.name = f'Gamma\t shape={self.shape}, scale={self.scale}' 109 | 110 | def sample(self, size=1): 111 | tmp = np.random.gamma(self.shape, self.scale, size) 112 | return tmp[0] if size == 1 else list(tmp) 113 | 114 | def pdf(self, value): 115 | raise Exception(f'not supported yet; code should not come here.') 116 | 117 | 118 | class Categorical(BaseDistribution): 119 | 120 | def __init__(self, probs): 121 | assert isinstance(probs, list), 'Expected `probs` to be a list.' 122 | assert np.sum(probs) == 1 123 | assert np.sum(probs == 0) == 0 124 | self.probs = probs 125 | self.num_categories = len(probs) 126 | self.name = f'Categorical\t probs={self.probs}' 127 | 128 | def sample(self, size=1): 129 | tmp = np.random.choice(self.num_categories, size=size, p=self.probs) 130 | return tmp[0] if size == 1 else list(tmp) 131 | 132 | def pdf(self, value): 133 | raise Exception(f'not supported yet; code should not come here.') 134 | 135 | 136 | class Laplace(BaseDistribution): 137 | 138 | def __init__(self, loc, scale): 139 | assert isinstance(loc, int) or isinstance(loc, float), 'Expected `shape` to be an int or float.' 140 | assert isinstance(scale, int) or isinstance(scale, float), 'Expected `scale` to be an int or float.' 141 | assert loc >= 0 142 | assert scale > 0 143 | self.loc = loc 144 | self.scale = scale 145 | self.name = f'Laplace\t loc={self.loc}, scale={self.scale}' 146 | 147 | def sample(self, size=1): 148 | tmp = np.random.laplace(self.loc, self.scale, size) 149 | return tmp[0] if size == 1 else list(tmp) 150 | 151 | def pdf(self, value): 152 | raise Exception(f'not supported yet; code should not come here.') 153 | -------------------------------------------------------------------------------- /VACA_modified/utils/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/patrickrchao/DiffusionBasedCausalModels/33fe8010a2d7700bd715563e70c5802d748e4a29/VACA_modified/utils/__init__.py -------------------------------------------------------------------------------- /VACA_modified/utils/__pycache__/__init__.cpython-37.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/patrickrchao/DiffusionBasedCausalModels/33fe8010a2d7700bd715563e70c5802d748e4a29/VACA_modified/utils/__pycache__/__init__.cpython-37.pyc -------------------------------------------------------------------------------- /VACA_modified/utils/__pycache__/__init__.cpython-39.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/patrickrchao/DiffusionBasedCausalModels/33fe8010a2d7700bd715563e70c5802d748e4a29/VACA_modified/utils/__pycache__/__init__.cpython-39.pyc -------------------------------------------------------------------------------- /VACA_modified/utils/__pycache__/_errors.cpython-37.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/patrickrchao/DiffusionBasedCausalModels/33fe8010a2d7700bd715563e70c5802d748e4a29/VACA_modified/utils/__pycache__/_errors.cpython-37.pyc -------------------------------------------------------------------------------- /VACA_modified/utils/__pycache__/_errors.cpython-39.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/patrickrchao/DiffusionBasedCausalModels/33fe8010a2d7700bd715563e70c5802d748e4a29/VACA_modified/utils/__pycache__/_errors.cpython-39.pyc -------------------------------------------------------------------------------- /VACA_modified/utils/__pycache__/activations.cpython-37.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/patrickrchao/DiffusionBasedCausalModels/33fe8010a2d7700bd715563e70c5802d748e4a29/VACA_modified/utils/__pycache__/activations.cpython-37.pyc -------------------------------------------------------------------------------- /VACA_modified/utils/__pycache__/activations.cpython-39.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/patrickrchao/DiffusionBasedCausalModels/33fe8010a2d7700bd715563e70c5802d748e4a29/VACA_modified/utils/__pycache__/activations.cpython-39.pyc -------------------------------------------------------------------------------- /VACA_modified/utils/__pycache__/args_parser.cpython-37.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/patrickrchao/DiffusionBasedCausalModels/33fe8010a2d7700bd715563e70c5802d748e4a29/VACA_modified/utils/__pycache__/args_parser.cpython-37.pyc -------------------------------------------------------------------------------- /VACA_modified/utils/__pycache__/args_parser.cpython-39.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/patrickrchao/DiffusionBasedCausalModels/33fe8010a2d7700bd715563e70c5802d748e4a29/VACA_modified/utils/__pycache__/args_parser.cpython-39.pyc -------------------------------------------------------------------------------- /VACA_modified/utils/__pycache__/constants.cpython-37.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/patrickrchao/DiffusionBasedCausalModels/33fe8010a2d7700bd715563e70c5802d748e4a29/VACA_modified/utils/__pycache__/constants.cpython-37.pyc -------------------------------------------------------------------------------- /VACA_modified/utils/__pycache__/constants.cpython-39.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/patrickrchao/DiffusionBasedCausalModels/33fe8010a2d7700bd715563e70c5802d748e4a29/VACA_modified/utils/__pycache__/constants.cpython-39.pyc -------------------------------------------------------------------------------- /VACA_modified/utils/__pycache__/distributions.cpython-37.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/patrickrchao/DiffusionBasedCausalModels/33fe8010a2d7700bd715563e70c5802d748e4a29/VACA_modified/utils/__pycache__/distributions.cpython-37.pyc -------------------------------------------------------------------------------- /VACA_modified/utils/__pycache__/distributions.cpython-39.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/patrickrchao/DiffusionBasedCausalModels/33fe8010a2d7700bd715563e70c5802d748e4a29/VACA_modified/utils/__pycache__/distributions.cpython-39.pyc -------------------------------------------------------------------------------- /VACA_modified/utils/__pycache__/dropout.cpython-37.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/patrickrchao/DiffusionBasedCausalModels/33fe8010a2d7700bd715563e70c5802d748e4a29/VACA_modified/utils/__pycache__/dropout.cpython-37.pyc -------------------------------------------------------------------------------- /VACA_modified/utils/__pycache__/dropout.cpython-39.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/patrickrchao/DiffusionBasedCausalModels/33fe8010a2d7700bd715563e70c5802d748e4a29/VACA_modified/utils/__pycache__/dropout.cpython-39.pyc -------------------------------------------------------------------------------- /VACA_modified/utils/__pycache__/likelihoods.cpython-37.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/patrickrchao/DiffusionBasedCausalModels/33fe8010a2d7700bd715563e70c5802d748e4a29/VACA_modified/utils/__pycache__/likelihoods.cpython-37.pyc -------------------------------------------------------------------------------- /VACA_modified/utils/__pycache__/likelihoods.cpython-39.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/patrickrchao/DiffusionBasedCausalModels/33fe8010a2d7700bd715563e70c5802d748e4a29/VACA_modified/utils/__pycache__/likelihoods.cpython-39.pyc -------------------------------------------------------------------------------- /VACA_modified/utils/__pycache__/optimizers.cpython-37.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/patrickrchao/DiffusionBasedCausalModels/33fe8010a2d7700bd715563e70c5802d748e4a29/VACA_modified/utils/__pycache__/optimizers.cpython-37.pyc -------------------------------------------------------------------------------- /VACA_modified/utils/__pycache__/optimizers.cpython-39.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/patrickrchao/DiffusionBasedCausalModels/33fe8010a2d7700bd715563e70c5802d748e4a29/VACA_modified/utils/__pycache__/optimizers.cpython-39.pyc -------------------------------------------------------------------------------- /VACA_modified/utils/_errors.py: -------------------------------------------------------------------------------- 1 | class IsHeterogeneousError(Exception): 2 | """ 3 | Exception raised when accesing a variable not 4 | implemented for heterogeneous SCMs 5 | """ 6 | 7 | def __init__(self, ): 8 | self.message = 'The SCM is Heterogeneous' 9 | super().__init__(self.message) 10 | -------------------------------------------------------------------------------- /VACA_modified/utils/activations.py: -------------------------------------------------------------------------------- 1 | import torch.nn as nn 2 | 3 | from utils.constants import Cte 4 | 5 | 6 | def get_activation(name): 7 | if name == Cte.TAHN: 8 | return nn.Tanh() 9 | elif name == Cte.RELU: 10 | return nn.ReLU() 11 | elif name == Cte.RELU6: 12 | return nn.ReLU6() 13 | elif name == Cte.SOFTPLUS: 14 | return nn.Softplus() 15 | elif name == Cte.RRELU: 16 | return nn.RReLU() 17 | elif name == Cte.LRELU: 18 | return nn.LeakyReLU(negative_slope=0.05) 19 | elif name == Cte.ELU: 20 | return nn.ELU() 21 | elif name == Cte.SELU: 22 | return nn.SELU() 23 | elif name == Cte.GLU: 24 | return nn.GLU() 25 | elif name == Cte.SIGMOID: 26 | return nn.Sigmoid() 27 | elif name == Cte.IDENTITY: 28 | return nn.Identity() 29 | else: 30 | raise NotImplementedError 31 | -------------------------------------------------------------------------------- /VACA_modified/utils/args_parser.py: -------------------------------------------------------------------------------- 1 | import argparse 2 | import ast 3 | import os 4 | import pickle 5 | 6 | import yaml 7 | 8 | 9 | def list_intersection(l1, l2): 10 | out = list(set(l1) & set(l2)) 11 | if len(out) > 0: 12 | my_type = type(out[0]) 13 | assert all(isinstance(x, my_type) for x in out) 14 | return out 15 | 16 | 17 | def list_union(l1, l2): 18 | out = list(set(l1) | set(l2)) 19 | if len(out) > 0: 20 | my_type = type(out[0]) 21 | assert all(isinstance(x, my_type) for x in out) 22 | return out 23 | 24 | 25 | def list_substract(l, l_substact): 26 | out = list(set(l) - set(l_substact)) 27 | if len(out) > 0: 28 | my_type = type(out[0]) 29 | assert all(isinstance(x, my_type) for x in out) 30 | return out 31 | 32 | 33 | def to_str(elem): 34 | if isinstance(elem, list): 35 | return '_'.join([str(s) for s in elem]) 36 | else: 37 | return str(elem) 38 | 39 | 40 | class StoreDictKeyPair(argparse.Action): 41 | def __call__(self, parser, namespace, values, option_string=None): 42 | my_dict = {} 43 | for kv in values.split("+"): 44 | k, v = kv.split("=") 45 | if isinstance(v, str): 46 | if k not in ['missing_perc', 'features_s', 'features_e', 'features_l']: 47 | try: 48 | v = ast.literal_eval(v) 49 | except: 50 | pass 51 | else: 52 | try: 53 | vi = ast.literal_eval(v) 54 | except: 55 | vi = 2 56 | pass 57 | v = vi if vi < 1.0 else v 58 | 59 | my_dict[k] = v 60 | setattr(namespace, self.dest, my_dict) 61 | 62 | 63 | def mkdir(path): 64 | os.makedirs(path, exist_ok=True) 65 | return path 66 | 67 | 68 | def newest(path, include_last=False): 69 | if not os.path.exists(path): 70 | return None 71 | files = os.listdir(path) 72 | if len(files) == 0: 73 | return None 74 | paths = [] 75 | for basename in files: 76 | if 'last.ckpt' not in basename: 77 | paths.append(os.path.join(path, basename)) 78 | else: 79 | if include_last: 80 | paths.append(os.path.join(path, basename)) 81 | 82 | return max(paths, key=os.path.getctime) 83 | 84 | 85 | def save_yaml(yaml_object, file_path): 86 | with open(file_path, 'w') as yaml_file: 87 | yaml.dump(yaml_object, yaml_file, default_flow_style=False) 88 | 89 | print(f'Saving yaml: {file_path}') 90 | return 91 | 92 | 93 | def save_obj(filename_no_extension, obj, ext='.pkl'): 94 | with open(filename_no_extension + ext, 'wb') as f: 95 | pickle.dump(obj, f, pickle.HIGHEST_PROTOCOL) 96 | 97 | 98 | def load_obj(filename): 99 | with open(filename, 'rb') as f: 100 | return pickle.load(f) 101 | 102 | 103 | def parse_args(yaml_file): 104 | with open(yaml_file, 'r') as stream: 105 | try: 106 | cfg = yaml.safe_load(stream) 107 | except yaml.YAMLError as exc: 108 | print(exc) 109 | 110 | return cfg 111 | 112 | 113 | def flatten_cfg(cfg): 114 | cfg_flat = {} 115 | for key, value in cfg.items(): 116 | if not isinstance(value, dict): 117 | cfg_flat[key] = value 118 | else: 119 | for key2, value2 in value.items(): 120 | if not isinstance(value2, dict): 121 | cfg_flat[f'{key}_{key2}'] = value2 122 | else: 123 | for key3, value3 in value2.items(): 124 | cfg_flat[f'{key}_{key2}_{key3}'] = value3 125 | 126 | return cfg_flat 127 | 128 | 129 | def get_experiment_folder(cfg): 130 | dataset_params = '_'.join([f"{to_str(v)}" for k, v in cfg['dataset']['params2'].items()]) 131 | 132 | model_params = '_'.join([f"{to_str(v)}" for k, v in cfg['model']['params'].items()]) 133 | optim_params = '_'.join([f"{to_str(v)}" for k, v in cfg['optimizer']['params'].items()]) 134 | if isinstance(cfg['scheduler'], dict): 135 | sched_params = '_'.join([f"{to_str(v)}" for k, v in cfg['scheduler']['params'].items()]) 136 | optim_params = f"{optim_params}_{cfg['scheduler']['name']}_{sched_params}" 137 | 138 | return os.path.join(f"{cfg['dataset']['name']}_{dataset_params}", 139 | cfg['model']['name'], 140 | model_params, cfg['optimizer']['name'], 141 | optim_params) 142 | -------------------------------------------------------------------------------- /VACA_modified/utils/constants.py: -------------------------------------------------------------------------------- 1 | class Cte: 2 | # equation-types 3 | LINEAR = "linear" 4 | NONLINEAR = 'non-linear' 5 | NONADDITIVE = 'non-additive' 6 | 7 | # Datasets 8 | SENS = 'sex' # sensitive attribute for CF fairness 9 | 10 | TRIANGLE = 'triangle' # a.k.a. Connected fork 11 | COLLIDER = 'collider' 12 | LOAN = 'loan' 13 | ADULT = 'adult' 14 | MGRAPH = 'mgraph' 15 | CHAIN = 'chain' 16 | GERMAN = 'german' 17 | 18 | DATASET_LIST = [COLLIDER, 19 | TRIANGLE, 20 | LOAN, 21 | MGRAPH, 22 | CHAIN, 23 | ADULT, 24 | GERMAN] 25 | DATASET_LIST_TOY = [COLLIDER, 26 | TRIANGLE, 27 | LOAN, 28 | MGRAPH, 29 | CHAIN, 30 | ADULT] 31 | # Models 32 | VACA = 'vaca' 33 | VACA_PIWAE = 'vaca_piwae' 34 | MCVAE = 'mcvae' 35 | CAREFL = 'carefl' 36 | 37 | # Optimizers 38 | ADAM = 'adam' 39 | RADAM = 'radam' 40 | ADAGRAD = 'adag' 41 | ADADELTA = 'adad' 42 | RMS = 'rms' 43 | ASGD = 'asgd' 44 | 45 | # Scheduler 46 | STEP_LR = 'step_lr' 47 | EXP_LR = 'exp_lr' 48 | 49 | # Activation 50 | TAHN = 'tahn' 51 | RELU = 'relu' 52 | RELU6 = 'relu6' 53 | SOFTPLUS = 'softplus' 54 | RRELU = 'rrelu' 55 | LRELU = 'lrelu' 56 | ELU = 'elu' 57 | SELU = 'selu' 58 | SIGMOID = 'sigmoid' 59 | GLU = 'glu' 60 | IDENTITY = 'identity' 61 | 62 | # Distribution 63 | BETA = 'beta' 64 | CONTINOUS_BERN = 'cb' 65 | BERNOULLI = 'ber' 66 | GAUSSIAN = 'normal' 67 | CATEGORICAL = 'cat' 68 | EXPONENTIAL = 'exp' 69 | DELTA = 'delta' 70 | -------------------------------------------------------------------------------- /VACA_modified/utils/distributions.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | from matplotlib import pyplot as plt 3 | from scipy.stats import bernoulli 4 | from scipy.stats import norm # norm for univariate; use multivariate_normal otherwise 5 | 6 | 7 | # univariate distributions 8 | class BaseDistribution(object): 9 | def __init__(self): 10 | pass 11 | 12 | def sample(self, size=1): 13 | raise NotImplementedError 14 | 15 | def pdf(self): 16 | raise NotImplementedError 17 | 18 | def visualize(self): 19 | plt.hist(self.sample(500), 50, facecolor='green', alpha=0.75) 20 | plt.ylabel('Count') 21 | plt.title(fr'Histogram of {self.name}') 22 | plt.grid(True) 23 | plt.show() 24 | 25 | 26 | class Normal(BaseDistribution): 27 | def __init__(self, mean, var): 28 | assert isinstance(mean, int) or isinstance(mean, float), 'Expected `mean` to be an int or float.' 29 | assert isinstance(var, int) or isinstance(var, float), 'Expected `var` to be an int or float.' 30 | self.mean = mean 31 | self.var = var 32 | self.name = f'Normal\t mean={self.mean}, var={self.var}' 33 | 34 | def sample(self, size=1): 35 | tmp = [np.random.normal(self.mean, np.sqrt(self.var)) for _ in range(size)] 36 | return tmp[0] if size == 1 else tmp 37 | 38 | def pdf(self, value): 39 | return norm(self.mean, self.var).pdf(value) 40 | 41 | 42 | class MixtureOfGaussians(BaseDistribution): 43 | 44 | def __init__(self, probs, means, vars): 45 | assert sum(probs) == 1, 'Mixture probabilities must sum to 1.' 46 | assert len(probs) == len(means) == len(vars), 'Length mismatch.' 47 | self.probs = probs 48 | self.means = means 49 | self.vars = vars 50 | self.name = f'MoG\t probs={self.probs}, means={self.means}, vars={self.vars}' 51 | 52 | def sample(self, size=1): 53 | tmp = [ 54 | np.random.normal(self.means[mixture_idx], np.sqrt(self.vars[mixture_idx])) 55 | for mixture_idx in np.random.choice(len(self.probs), size=size, p=self.probs) 56 | ] 57 | return tmp[0] if size == 1 else tmp 58 | 59 | def pdf(self, value): 60 | return np.sum([ 61 | prob * norm(mean, var).pdf(value) 62 | for (prob, mean, var) in zip(self.probs, self.means, self.vars) 63 | ]) 64 | 65 | 66 | class Bernoulli(BaseDistribution): 67 | 68 | def __init__(self, prob): 69 | assert isinstance(prob, int) or isinstance(prob, float), 'Expected `prob` to be an int or float.' 70 | assert prob >= 0 and prob <= 1 71 | 72 | self.prob = prob 73 | self.name = f'Bernoulli\t prob={self.prob}' 74 | 75 | def sample(self, size=1): 76 | tmp = bernoulli.rvs(self.prob, size=size) 77 | return tmp[0] if size == 1 else list(tmp) 78 | 79 | def pdf(self, value): 80 | raise Exception(f'not supported yet; code should not come here.') 81 | 82 | 83 | class Poisson(BaseDistribution): 84 | 85 | def __init__(self, p_lambda): 86 | assert isinstance(p_lambda, int) or isinstance(p_lambda, float), 'Expected `p_lambda` to be an int or float.' 87 | assert p_lambda > 0 88 | self.p_lambda = p_lambda 89 | self.name = f'Poisson\t prob={self.p_lambda}' 90 | 91 | def sample(self, size=1): 92 | tmp = np.random.poisson(self.p_lambda, size) 93 | return tmp[0] if size == 1 else list(tmp) 94 | 95 | def pdf(self, value): 96 | raise Exception(f'not supported yet; code should not come here.') 97 | 98 | 99 | class Gamma(BaseDistribution): 100 | 101 | def __init__(self, shape, scale): 102 | assert isinstance(shape, int) or isinstance(shape, float), 'Expected `shape` to be an int or float.' 103 | assert isinstance(scale, int) or isinstance(scale, float), 'Expected `scale` to be an int or float.' 104 | assert shape > 0 105 | assert scale > 0 106 | self.shape = shape 107 | self.scale = scale 108 | self.name = f'Gamma\t shape={self.shape}, scale={self.scale}' 109 | 110 | def sample(self, size=1): 111 | tmp = np.random.gamma(self.shape, self.scale, size) 112 | return tmp[0] if size == 1 else list(tmp) 113 | 114 | def pdf(self, value): 115 | raise Exception(f'not supported yet; code should not come here.') 116 | 117 | 118 | class Categorical(BaseDistribution): 119 | 120 | def __init__(self, probs): 121 | assert isinstance(probs, list), 'Expected `probs` to be a list.' 122 | assert np.sum(probs) == 1 123 | assert np.sum(probs == 0) == 0 124 | self.probs = probs 125 | self.num_categories = len(probs) 126 | self.name = f'Categorical\t probs={self.probs}' 127 | 128 | def sample(self, size=1): 129 | tmp = np.random.choice(self.num_categories, size=size, p=self.probs) 130 | return tmp[0] if size == 1 else list(tmp) 131 | 132 | def pdf(self, value): 133 | raise Exception(f'not supported yet; code should not come here.') 134 | 135 | 136 | class Laplace(BaseDistribution): 137 | 138 | def __init__(self, loc, scale): 139 | assert isinstance(loc, int) or isinstance(loc, float), 'Expected `shape` to be an int or float.' 140 | assert isinstance(scale, int) or isinstance(scale, float), 'Expected `scale` to be an int or float.' 141 | assert loc >= 0 142 | assert scale > 0 143 | self.loc = loc 144 | self.scale = scale 145 | self.name = f'Laplace\t loc={self.loc}, scale={self.scale}' 146 | 147 | def sample(self, size=1): 148 | tmp = np.random.laplace(self.loc, self.scale, size) 149 | return tmp[0] if size == 1 else list(tmp) 150 | 151 | def pdf(self, value): 152 | raise Exception(f'not supported yet; code should not come here.') 153 | 154 | 155 | class Empirical(BaseDistribution): 156 | 157 | def __init__(self, data): 158 | if len(data.shape) >1 : 159 | data = data.flatten() 160 | self.data = data 161 | 162 | self.name = f'Empirical' 163 | 164 | def sample(self, size=1): 165 | return np.random.choice(self.data, size,replace=True) 166 | 167 | def pdf(self, value): 168 | raise Exception(f'not supported yet; code should not come here.') 169 | -------------------------------------------------------------------------------- /VACA_modified/utils/dropout.py: -------------------------------------------------------------------------------- 1 | from typing import Optional 2 | 3 | import numpy as np 4 | import torch 5 | from torch_sparse import coalesce 6 | 7 | 8 | def maybe_num_nodes(index: torch.Tensor, 9 | num_nodes: Optional[int] = None) -> int: 10 | return int(index.max()) + 1 if num_nodes is None else num_nodes 11 | 12 | 13 | def filter_adj(row, col, edge_attr, mask): 14 | return row[mask], col[mask], None if edge_attr is None else edge_attr[mask] 15 | 16 | 17 | def dropout_adj(edge_index, edge_attr=None, p=0.5, force_undirected=False, 18 | num_nodes=None, training=True, keep_self_loops=True): 19 | r"""Randomly drops edges from the adjacency matrix 20 | :obj:`(edge_index, edge_attr)` with probability :obj:`p` using samples from 21 | a Bernoulli distribution. 22 | 23 | Args: 24 | edge_index (LongTensor): The edge indices. 25 | edge_attr (Tensor, optional): Edge weights or multi-dimensional 26 | edge features. (default: :obj:`None`) 27 | p (float, optional): Dropout probability. (default: :obj:`0.5`) 28 | force_undirected (bool, optional): If set to :obj:`True`, will either 29 | drop or keep both edges of an undirected edge. 30 | (default: :obj:`False`) 31 | num_nodes (int, optional): The number of nodes, *i.e.* 32 | :obj:`max_val + 1` of :attr:`edge_index`. (default: :obj:`None`) 33 | training (bool, optional): If set to :obj:`False`, this operation is a 34 | no-op. (default: :obj:`True`) 35 | """ 36 | 37 | if p < 0. or p > 1.: 38 | raise ValueError('Dropout probability has to be between 0 and 1, ' 39 | 'but got {}'.format(p)) 40 | 41 | if not training: 42 | return edge_index, edge_attr 43 | 44 | N = maybe_num_nodes(edge_index, num_nodes) 45 | row, col = edge_index 46 | 47 | if force_undirected: 48 | row, col, edge_attr = filter_adj(row, col, edge_attr, row < col) 49 | 50 | mask = edge_index.new_full((row.size(0),), 1 - p, dtype=torch.float) 51 | mask = torch.bernoulli(mask).to(torch.bool) 52 | if keep_self_loops: 53 | mask[row == col] = True 54 | 55 | row, col, edge_attr = filter_adj(row, col, edge_attr, mask) 56 | 57 | if force_undirected: 58 | edge_index = torch.stack( 59 | [torch.cat([row, col], dim=0), 60 | torch.cat([col, row], dim=0)], dim=0) 61 | if edge_attr is not None: 62 | edge_attr = torch.cat([edge_attr, edge_attr], dim=0) 63 | edge_index, edge_attr = coalesce(edge_index, edge_attr, N, N) 64 | else: 65 | edge_index = torch.stack([row, col], dim=0) 66 | 67 | return edge_index, edge_attr 68 | 69 | 70 | def dropout_adj_parents(edge_index, edge_attr=None, p=0.5, prob_keep_self=0.0, force_undirected=False, training=True): 71 | r"""Randomly drops edges from the adjacency matrix 72 | :obj:`(edge_index, edge_attr)` with probability :obj:`p` using samples from 73 | a Bernoulli distribution. 74 | 75 | Args: 76 | edge_index (LongTensor): The edge indices. 77 | edge_attr (Tensor, optional): Edge weights or multi-dimensional 78 | edge features. (default: :obj:`None`) 79 | p (float, optional): Dropout probability. (default: :obj:`0.5`) 80 | prob_keep_self (float, optional): probability of doing dropout out on the self loops. (default: :obj:`0.0`) 81 | force_undirected (bool, optional): If set to :obj:`True`, will either 82 | drop or keep both edges of an undirected edge. 83 | (default: :obj:`False`) 84 | num_nodes (int, optional): The number of nodes, *i.e.* 85 | :obj:`max_val + 1` of :attr:`edge_index`. (default: :obj:`None`) 86 | training (bool, optional): If set to :obj:`False`, this operation is a 87 | no-op. (default: :obj:`True`) 88 | """ 89 | 90 | if p < 0. or p > 1.: 91 | raise ValueError('Dropout probability has to be between 0 and 1, ' 92 | 'but got {}'.format(p)) 93 | 94 | if not training: 95 | return edge_index, edge_attr 96 | 97 | N = maybe_num_nodes(edge_index, num_nodes=None) 98 | row, col = edge_index 99 | 100 | if force_undirected: 101 | row, col, edge_attr = filter_adj(row, col, edge_attr, row < col) 102 | 103 | nodes = torch.tensor(list(range(N))) 104 | mask_nodes = edge_index.new_full((N,), 1 - p, dtype=torch.float) 105 | mask_nodes = torch.bernoulli(mask_nodes).to(torch.bool) 106 | 107 | nodes_to_be_dropped = nodes[~mask_nodes] 108 | 109 | mask = edge_index.new_full((row.size(0),), 1., dtype=torch.float).to(torch.bool) 110 | 111 | if np.random.uniform() > prob_keep_self: # Dropout parents 112 | dst_nodes = edge_index[1, :] 113 | for node_id_to_drop in nodes_to_be_dropped: 114 | mask[dst_nodes == node_id_to_drop] = False 115 | 116 | mask[row == col] = True 117 | else: # Dropout self loops 118 | src_nodes = edge_index[0, :] 119 | dst_nodes = edge_index[1, :] 120 | for node_id_to_drop in nodes_to_be_dropped: 121 | cond = (dst_nodes == node_id_to_drop) & (src_nodes == node_id_to_drop) 122 | mask[cond] = False 123 | 124 | row, col, edge_attr = filter_adj(row, col, edge_attr, mask) 125 | 126 | if force_undirected: 127 | edge_index = torch.stack( 128 | [torch.cat([row, col], dim=0), 129 | torch.cat([col, row], dim=0)], dim=0) 130 | if edge_attr is not None: 131 | edge_attr = torch.cat([edge_attr, edge_attr], dim=0) 132 | edge_index, edge_attr = coalesce(edge_index, edge_attr, N, N) 133 | else: 134 | edge_index = torch.stack([row, col], dim=0) 135 | 136 | return edge_index, edge_attr 137 | -------------------------------------------------------------------------------- /VACA_modified/utils/metrics/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/patrickrchao/DiffusionBasedCausalModels/33fe8010a2d7700bd715563e70c5802d748e4a29/VACA_modified/utils/metrics/__init__.py -------------------------------------------------------------------------------- /VACA_modified/utils/metrics/__pycache__/__init__.cpython-37.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/patrickrchao/DiffusionBasedCausalModels/33fe8010a2d7700bd715563e70c5802d748e4a29/VACA_modified/utils/metrics/__pycache__/__init__.cpython-37.pyc -------------------------------------------------------------------------------- /VACA_modified/utils/metrics/__pycache__/__init__.cpython-39.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/patrickrchao/DiffusionBasedCausalModels/33fe8010a2d7700bd715563e70c5802d748e4a29/VACA_modified/utils/metrics/__pycache__/__init__.cpython-39.pyc -------------------------------------------------------------------------------- /VACA_modified/utils/metrics/__pycache__/mmd.cpython-37.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/patrickrchao/DiffusionBasedCausalModels/33fe8010a2d7700bd715563e70c5802d748e4a29/VACA_modified/utils/metrics/__pycache__/mmd.cpython-37.pyc -------------------------------------------------------------------------------- /VACA_modified/utils/metrics/__pycache__/mmd.cpython-39.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/patrickrchao/DiffusionBasedCausalModels/33fe8010a2d7700bd715563e70c5802d748e4a29/VACA_modified/utils/metrics/__pycache__/mmd.cpython-39.pyc -------------------------------------------------------------------------------- /VACA_modified/utils/metrics/__pycache__/time.cpython-37.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/patrickrchao/DiffusionBasedCausalModels/33fe8010a2d7700bd715563e70c5802d748e4a29/VACA_modified/utils/metrics/__pycache__/time.cpython-37.pyc -------------------------------------------------------------------------------- /VACA_modified/utils/metrics/__pycache__/time.cpython-39.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/patrickrchao/DiffusionBasedCausalModels/33fe8010a2d7700bd715563e70c5802d748e4a29/VACA_modified/utils/metrics/__pycache__/time.cpython-39.pyc -------------------------------------------------------------------------------- /VACA_modified/utils/metrics/metrics.py: -------------------------------------------------------------------------------- 1 | import torch 2 | from torch.distributions import Normal 3 | 4 | 5 | def accuracy(preds, targets, one_hot_preds=True, dim=-1): 6 | if one_hot_preds: 7 | preds = preds.argmax(dim=dim) 8 | 9 | return (preds == targets).sum().float() / len(targets) 10 | 11 | 12 | def kl_divergence_normal(mu1, log_std1, mu2=None, log_std2=None): 13 | q = Normal(mu1, torch.exp(log_std1)) 14 | if mu2 == None or log_std2 == None: 15 | mu2 = torch.zeros_like(mu1) 16 | log_std2 = torch.zeros_like(log_std1) 17 | p = Normal(mu2, torch.exp(log_std2)) 18 | 19 | return torch.distributions.kl_divergence(q, p) 20 | -------------------------------------------------------------------------------- /VACA_modified/utils/metrics/mmd.py: -------------------------------------------------------------------------------- 1 | import random 2 | 3 | import torch 4 | import torch.nn as nn 5 | 6 | 7 | def guassian_kernel(source, target, kernel_mul=2.0, kernel_num=5, fix_sigma=None): 8 | n_samples = int(source.size()[0]) + int(target.size()[0]) 9 | 10 | total = torch.cat([source, target], dim=0) 11 | 12 | total0 = total.unsqueeze(0).expand(int(total.size(0)), int(total.size(0)), int(total.size(1))) 13 | total1 = total.unsqueeze(1).expand(int(total.size(0)), int(total.size(0)), int(total.size(1))) 14 | L2_distance = ((total0 - total1) ** 2).sum(2) 15 | if fix_sigma: 16 | bandwidth = fix_sigma 17 | else: 18 | bandwidth = torch.sum(L2_distance.data) / (n_samples ** 2 - n_samples) 19 | bandwidth /= kernel_mul ** (kernel_num // 2) # 20 | bandwidth_list = [bandwidth * (kernel_mul ** i) for i in range(kernel_num)] 21 | kernel_val = [torch.exp(-L2_distance / bandwidth_temp) for bandwidth_temp in bandwidth_list] 22 | return sum(kernel_val) 23 | 24 | 25 | class MMDLoss(nn.Module): 26 | def __init__(self, kernel_mul=2.0, kernel_num=5, num_samples=None): 27 | super(MMDLoss, self).__init__() 28 | self.kernel_num = kernel_num 29 | self.kernel_mul = kernel_mul 30 | self.fix_sigma = None 31 | self.num_samples = num_samples 32 | return 33 | 34 | def forward(self, source, target): 35 | batch_size = int(source.size()[0]) 36 | if self.num_samples is not None: 37 | if batch_size > self.num_samples: 38 | idx = random.choices(list(range(batch_size)), k=self.num_samples) 39 | source = source[idx] 40 | target = target[idx] 41 | batch_size = int(source.size()[0]) 42 | if len(source.size()) > 2: 43 | source = source.view(batch_size, -1) 44 | if len(target.size()) > 2: 45 | target = target.view(batch_size, -1) 46 | kernels = guassian_kernel(source, target, kernel_mul=self.kernel_mul, kernel_num=self.kernel_num, 47 | fix_sigma=self.fix_sigma) 48 | XX = kernels[:batch_size, :batch_size] 49 | YY = kernels[batch_size:, batch_size:] 50 | XY = kernels[:batch_size, batch_size:] 51 | YX = kernels[batch_size:, :batch_size] 52 | loss = torch.mean(XX + YY - XY - YX) 53 | return loss 54 | -------------------------------------------------------------------------------- /VACA_modified/utils/metrics/time.py: -------------------------------------------------------------------------------- 1 | import time 2 | 3 | 4 | class Timer: 5 | 6 | def __init__(self): 7 | self.timer_dict = {} 8 | self.stop_dict = {} 9 | 10 | def tic(self, name): 11 | self.timer_dict[name] = time.time() 12 | 13 | def toc(self, name): 14 | assert name in self.timer_dict 15 | elapsed = time.time() - self.timer_dict[name] 16 | del self.timer_dict[name] 17 | return elapsed 18 | 19 | def stop(self, name): 20 | self.stop_dict[name] = time.time() 21 | 22 | def resume(self, name): 23 | if name not in self.timer_dict: 24 | del self.stop_dict[name] 25 | return 26 | elapsed = time.time() - self.stop_dict[name] 27 | self.timer_dict[name] = self.timer_dict[name] + elapsed 28 | del self.stop_dict[name] 29 | -------------------------------------------------------------------------------- /VACA_modified/utils/optimizers.py: -------------------------------------------------------------------------------- 1 | import torch 2 | 3 | from utils.constants import Cte 4 | 5 | 6 | def get_optimizer(name): 7 | if name == Cte.ADAM: 8 | return torch.optim.Adam 9 | elif name == Cte.ADAGRAD: 10 | return torch.optim.Adagrad 11 | elif name == Cte.ADADELTA: 12 | return torch.optim.Adadelta 13 | elif name == Cte.RMS: 14 | return torch.optim.RMSprop 15 | elif name == Cte.ASGD: 16 | return torch.optim.ASGD 17 | else: 18 | raise NotImplementedError 19 | 20 | 21 | def get_scheduler(name): 22 | if name == Cte.STEP_LR: 23 | return torch.optim.lr_scheduler.StepLR 24 | elif name == Cte.EXP_LR: 25 | return torch.optim.lr_scheduler.ExponentialLR 26 | else: 27 | raise NotImplementedError 28 | -------------------------------------------------------------------------------- /VACA_modified/utils/tools.py: -------------------------------------------------------------------------------- 1 | import os 2 | import sys 3 | 4 | 5 | # Disable 6 | def blockPrint(): 7 | sys.stdout = open(os.devnull, 'w') 8 | 9 | 10 | # Restore 11 | def enablePrint(): 12 | sys.stdout = sys.__stdout__ 13 | -------------------------------------------------------------------------------- /environment.yml: -------------------------------------------------------------------------------- 1 | name: dcm 2 | channels: 3 | - defaults 4 | dependencies: 5 | - pip 6 | - python=3.9.0 7 | - pip: 8 | - dowhy==0.8 9 | - matplotlib==3.5.2 10 | - networkx==2.6.3 11 | - numpy==1.21.2 12 | - pandas==1.3.5 13 | - pytorch-lightning==1.4.9 14 | - scikit-learn==1.0 15 | - scipy==1.7.1 16 | - seaborn==0.11.2 17 | - torch==1.10.0 18 | #- torch-cluster==1.5.9 19 | #- torch-geometric==2.2.0 20 | #- torch-scatter==2.0.9 21 | #- torch-sparse==0.6.13 22 | #- torch-spline-conv==1.2.1 23 | #- torchvision==0.11.1 24 | - tqdm==4.62.3 -------------------------------------------------------------------------------- /experiments/MMD.py: -------------------------------------------------------------------------------- 1 | from sklearn.metrics import euclidean_distances 2 | from sklearn.metrics.pairwise import rbf_kernel 3 | import numpy as np 4 | 5 | def mmd(source, generated, prec = None): 6 | X = source 7 | Y = generated 8 | if prec is None: 9 | prec = _median_precision(X,Y) 10 | 11 | XX = rbf_kernel(X, X, prec).mean() 12 | YY = rbf_kernel(Y, Y, prec).mean() 13 | XY = rbf_kernel(X, Y, prec).mean() 14 | 15 | dist = XX + YY - 2 * XY 16 | return dist 17 | 18 | # _median_bw code modified from 19 | # https://github.com/py-why/dowhy/blob/ead8d47102f0ac6db51d84432874c331fb84f3cb/dowhy/gcm/independence_test/kernel_operation.py#L127 20 | # MIT License 21 | 22 | # Copyright (c) Microsoft Corporation. All rights reserved. 23 | 24 | # Permission is hereby granted, free of charge, to any person obtaining a copy 25 | # of this software and associated documentation files (the "Software"), to deal 26 | # in the Software without restriction, including without limitation the rights 27 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 28 | # copies of the Software, and to permit persons to whom the Software is 29 | # furnished to do so, subject to the following conditions: 30 | 31 | # The above copyright notice and this permission notice shall be included in all 32 | # copies or substantial portions of the Software. 33 | 34 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 35 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 36 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 37 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 38 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 39 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 40 | # SOFTWARE 41 | 42 | def _median_precision(X, Y) -> float: 43 | tmp = euclidean_distances(X, Y, squared=True) 44 | tmp = tmp - np.tril(tmp, -1) 45 | tmp = tmp.reshape(-1, 1) 46 | return 1 / np.median(tmp[tmp > 0]) 47 | 48 | # https://github.com/psanch21/VACA/blob/a14b9b93726647907c07c44860f7c6bc85b31f88/utils/metrics/mmd.py 49 | 50 | # MIT License 51 | 52 | # Copyright (c) 2021 53 | 54 | # Permission is hereby granted, free of charge, to any person obtaining a copy 55 | # of this software and associated documentation files (the "Software"), to deal 56 | # in the Software without restriction, including without limitation the rights 57 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 58 | # copies of the Software, and to permit persons to whom the Software is 59 | # furnished to do so, subject to the following conditions: 60 | 61 | # The above copyright notice and this permission notice shall be included in all 62 | # copies or substantial portions of the Software. 63 | 64 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 65 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 66 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 67 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 68 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 69 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 70 | # SOFTWARE. 71 | import torch 72 | def mmd_vaca(source, generated, kernel_mul = 2.0, kernel_num = 5): 73 | batch_size = int(source.size()[0]) 74 | kernels = gaussian_kernel(source, generated, kernel_mul, kernel_num) 75 | XX = kernels[:batch_size, :batch_size] 76 | YY = kernels[batch_size:, batch_size:] 77 | XY = kernels[:batch_size, batch_size:] 78 | YX = kernels[batch_size:, :batch_size] 79 | loss = torch.mean(XX + YY - XY - YX) 80 | return loss 81 | 82 | def gaussian_kernel(source, generated, kernel_mul, kernel_num): 83 | n_samples = int(source.size()[0]) + int(generated.size()[0]) 84 | 85 | total = torch.cat([source, generated], dim=0) 86 | 87 | total0 = total.unsqueeze(0).expand(int(total.size(0)), int(total.size(0)), int(total.size(1))) 88 | total1 = total.unsqueeze(1).expand(int(total.size(0)), int(total.size(0)), int(total.size(1))) 89 | L2_distance = ((total0 - total1) ** 2).sum(2) 90 | 91 | bandwidth = torch.sum(L2_distance.data) / (n_samples ** 2 - n_samples) 92 | bandwidth /= kernel_mul ** (kernel_num // 2) # 93 | bandwidth_list = [bandwidth * (kernel_mul ** i) for i in range(kernel_num)] 94 | kernel_val = [torch.exp(-L2_distance / bandwidth_temp) for bandwidth_temp in bandwidth_list] 95 | return sum(kernel_val) -------------------------------------------------------------------------------- /experiments/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/patrickrchao/DiffusionBasedCausalModels/33fe8010a2d7700bd715563e70c5802d748e4a29/experiments/__init__.py -------------------------------------------------------------------------------- /experiments/exp_helper.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | import os 3 | from experiments.structural_equations import get_graph 4 | import pandas as pd 5 | import networkx as nx 6 | 7 | def get_weight_matrices(graph, equations_type, scm_type): 8 | weights = {} 9 | # Offset is 1 in nonadditive because we add an extra dimension for noise 10 | offset = int (equations_type == "nonadditive") 11 | if scm_type == "random" or scm_type =="ladder": 12 | for node in graph: 13 | node_int = int(node[1:]) 14 | super_node = int(np.ceil(node_int/3)) 15 | in_deg = graph.in_degree(node) 16 | if in_deg > 0: 17 | if str(super_node)+"_1" not in weights: 18 | weights[str(super_node)+"_1"] = np.random.uniform(low=-1, high=1, size=(16 ,in_deg + offset)) 19 | weights[str(super_node)+"_2"] = np.random.uniform(low=-1, high= 1, size=(3, 16)) 20 | elif scm_type == "sachs": 21 | for node in graph: 22 | in_deg = graph.in_degree(node) 23 | if in_deg > 0: 24 | if node+"_1" not in weights: 25 | weights[node+"_1"] = np.random.uniform(low=-1, high=1, size=(16 ,in_deg + offset)) 26 | weights[node+"_2"] = np.random.uniform(low=-1, high= 1, size=(1, 16)) 27 | else: 28 | raise ValueError("SCM type not recognized") 29 | return weights 30 | 31 | def summarize_results(summary_metrics, scaling = 1): 32 | metrics = ["Obs_MMD", "Int_MMD", "CF_MSE"] 33 | sorted_cols = sorted(summary_metrics.columns) 34 | for metric in metrics: 35 | for col in sorted_cols: 36 | if metric in col: 37 | vals = np.array(summary_metrics[col]) 38 | print(f"{col.replace('_',' '):<30} {np.mean(vals)*scaling:>9.4f} \u00B1{np.std(vals)*scaling:>8.4f}") 39 | print("") 40 | 41 | def get_folder(scm_type, equations_type, query_type): 42 | folder_path = f"experiments/generated_values/{query_type}/{scm_type}_{equations_type}" 43 | if not os.path.exists(folder_path): 44 | os.makedirs(folder_path) 45 | return folder_path 46 | 47 | def get_file_name(seed, n, num_epochs, int_var=None, int_num=None): 48 | name = f"{seed}_{n}_{num_epochs}" 49 | if int_var is not None and int_num is not None: 50 | name = f"{name}_{int_var}_{int_num}.csv" 51 | else: 52 | name = f"{name}.csv" 53 | return name 54 | 55 | def initialize_summary_metrics(scm_type, model_names,num_initializations): 56 | all_int_var = None 57 | if scm_type is not "random": 58 | graph = get_graph(scm_type) 59 | if scm_type == "ladder": 60 | all_int_var = sorted(["x01","x04"]) 61 | else: 62 | all_int_var = sorted([node for node in graph if len(nx.descendants(graph,node)) > 0]) 63 | 64 | summary_names = [model + "_Obs_MMD" for model in model_names ] 65 | # If using a random scm, then the column names are inconsistent 66 | if scm_type is not "random": 67 | summary_names.extend([model + "_" + metric + "_" + int_var for metric in ["Int_MMD", "CF_MSE"] for model in model_names for int_var in all_int_var]) 68 | else: 69 | summary_names.extend([model + "_" + metric + "_" + str(ind) for metric in ["Int_MMD", "CF_MSE"] for model in model_names for ind in range(1,4)]) 70 | 71 | summary_metrics = pd.DataFrame(np.zeros((num_initializations, len(summary_names))), columns=summary_names) 72 | return summary_metrics, all_int_var 73 | 74 | def reindex_columns(column_order, dfs): 75 | if isinstance(dfs, pd.DataFrame): 76 | dfs = [dfs] 77 | result = [df.reindex(columns = column_order) for df in dfs] 78 | if len(result) == 1: 79 | return result[0] 80 | else: 81 | return result 82 | -------------------------------------------------------------------------------- /experiments/generated_values/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/patrickrchao/DiffusionBasedCausalModels/33fe8010a2d7700bd715563e70c5802d748e4a29/experiments/generated_values/.DS_Store -------------------------------------------------------------------------------- /experiments/generated_values/counterfactual/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/patrickrchao/DiffusionBasedCausalModels/33fe8010a2d7700bd715563e70c5802d748e4a29/experiments/generated_values/counterfactual/.DS_Store -------------------------------------------------------------------------------- /experiments/generated_values/interventional/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/patrickrchao/DiffusionBasedCausalModels/33fe8010a2d7700bd715563e70c5802d748e4a29/experiments/generated_values/interventional/.DS_Store -------------------------------------------------------------------------------- /experiments/generated_values/observational/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/patrickrchao/DiffusionBasedCausalModels/33fe8010a2d7700bd715563e70c5802d748e4a29/experiments/generated_values/observational/.DS_Store -------------------------------------------------------------------------------- /experiments/images/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/patrickrchao/DiffusionBasedCausalModels/33fe8010a2d7700bd715563e70c5802d748e4a29/experiments/images/.DS_Store -------------------------------------------------------------------------------- /experiments/images/Nonadditive_5000_500.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/patrickrchao/DiffusionBasedCausalModels/33fe8010a2d7700bd715563e70c5802d748e4a29/experiments/images/Nonadditive_5000_500.pdf -------------------------------------------------------------------------------- /experiments/images/Nonlinear_5000_500.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/patrickrchao/DiffusionBasedCausalModels/33fe8010a2d7700bd715563e70c5802d748e4a29/experiments/images/Nonlinear_5000_500.pdf -------------------------------------------------------------------------------- /experiments/images/ladder_random_Nonadditive_5000_500.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/patrickrchao/DiffusionBasedCausalModels/33fe8010a2d7700bd715563e70c5802d748e4a29/experiments/images/ladder_random_Nonadditive_5000_500.jpg -------------------------------------------------------------------------------- /experiments/images/ladder_random_Nonadditive_5000_500.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/patrickrchao/DiffusionBasedCausalModels/33fe8010a2d7700bd715563e70c5802d748e4a29/experiments/images/ladder_random_Nonadditive_5000_500.pdf -------------------------------------------------------------------------------- /experiments/images/ladder_random_Nonlinear_5000_500.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/patrickrchao/DiffusionBasedCausalModels/33fe8010a2d7700bd715563e70c5802d748e4a29/experiments/images/ladder_random_Nonlinear_5000_500.jpg -------------------------------------------------------------------------------- /experiments/images/ladder_random_Nonlinear_5000_500.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/patrickrchao/DiffusionBasedCausalModels/33fe8010a2d7700bd715563e70c5802d748e4a29/experiments/images/ladder_random_Nonlinear_5000_500.pdf -------------------------------------------------------------------------------- /experiments/metric_summaries/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/patrickrchao/DiffusionBasedCausalModels/33fe8010a2d7700bd715563e70c5802d748e4a29/experiments/metric_summaries/.DS_Store -------------------------------------------------------------------------------- /model/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/patrickrchao/DiffusionBasedCausalModels/33fe8010a2d7700bd715563e70c5802d748e4a29/model/__init__.py --------------------------------------------------------------------------------