|
| 1 | +// -*- C++ -*- |
| 2 | +/** |
| 3 | + * @brief RotatedCopula |
| 4 | + * |
| 5 | + * Copyright 2005-2025 Airbus-EDF-IMACS-ONERA-Phimeca |
| 6 | + * |
| 7 | + * This library is free software: you can redistribute it and/or modify |
| 8 | + * it under the terms of the GNU Lesser General Public License as published by |
| 9 | + * the Free Software Foundation, either version 3 of the License, or |
| 10 | + * (at your option) any later version. |
| 11 | + * |
| 12 | + * This library is distributed in the hope that it will be useful, |
| 13 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| 14 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| 15 | + * GNU Lesser General Public License for more details. |
| 16 | + * |
| 17 | + * You should have received a copy of the GNU Lesser General Public License |
| 18 | + * along with this library. If not, see <http://www.gnu.org/licenses/>. |
| 19 | + * |
| 20 | + */ |
| 21 | +#include "otvine/RotatedCopula.hxx" |
| 22 | +#include <openturns/PersistentObjectFactory.hxx> |
| 23 | + |
| 24 | +#include <vinecopulib.hpp> |
| 25 | + |
| 26 | +using namespace OT; |
| 27 | + |
| 28 | +namespace OTVINE |
| 29 | +{ |
| 30 | + |
| 31 | +CLASSNAMEINIT(RotatedCopula); |
| 32 | + |
| 33 | +static Factory<RotatedCopula> Factory_RotatedCopula; |
| 34 | + |
| 35 | + |
| 36 | +/* Default constructor */ |
| 37 | +RotatedCopula::RotatedCopula() |
| 38 | + : DistributionImplementation() |
| 39 | +{ |
| 40 | + isCopula_ = true; |
| 41 | + setName("RotatedCopula"); |
| 42 | +} |
| 43 | + |
| 44 | +RotatedCopula::RotatedCopula(const OT::Distribution & copula, const Rotation rotation) |
| 45 | + : DistributionImplementation() |
| 46 | + , copula_(copula) |
| 47 | + , rotation_(rotation) |
| 48 | +{ |
| 49 | + isCopula_ = true; |
| 50 | + setName("RotatedCopula"); |
| 51 | + setDimension(copula.getDimension()); |
| 52 | + setDescription(copula.getDescription()); |
| 53 | + computeRange(); |
| 54 | + if (!copula.isCopula()) |
| 55 | + throw InvalidArgumentException(HERE) << "RotatedCopula distribution is not a copula"; |
| 56 | + if (copula.getDimension() != 2) |
| 57 | + throw NotYetImplementedException(HERE) << "RotatedCopula only supports dimension 2"; |
| 58 | +} |
| 59 | + |
| 60 | +/* Virtual constructor method */ |
| 61 | +RotatedCopula * RotatedCopula::clone() const |
| 62 | +{ |
| 63 | + return new RotatedCopula(*this); |
| 64 | +} |
| 65 | + |
| 66 | +Point RotatedCopula::rotate(const Point & x, const Bool inverse) const |
| 67 | +{ |
| 68 | + if (x.getDimension() != 2) |
| 69 | + throw NotYetImplementedException(HERE) << "RotatedCopula only supports dimension 2"; |
| 70 | + const Scalar u1 = x[0]; |
| 71 | + const Scalar u2 = x[1]; |
| 72 | + Rotation rotation = static_cast<Rotation>(rotation_); |
| 73 | + if (inverse) |
| 74 | + { |
| 75 | + switch (rotation) |
| 76 | + { |
| 77 | + case (ROTATION_90): |
| 78 | + rotation = ROTATION_270; |
| 79 | + break; |
| 80 | + case (ROTATION_270): |
| 81 | + rotation = ROTATION_90; |
| 82 | + break; |
| 83 | + default: |
| 84 | + break; |
| 85 | + } |
| 86 | + } |
| 87 | + |
| 88 | + switch (rotation) |
| 89 | + { |
| 90 | + case (ROTATION_0): |
| 91 | + return {u1, u2}; |
| 92 | + case (ROTATION_90): |
| 93 | + return {u2, 1.0 - u1}; |
| 94 | + case (ROTATION_180): |
| 95 | + return {1.0 - u2, 1.0 - u1}; |
| 96 | + case (ROTATION_270): |
| 97 | + return {1.0 - u2, u1}; |
| 98 | + default: |
| 99 | + throw NotYetImplementedException(HERE) << "RotatedCopula invalid rotation"; |
| 100 | + } |
| 101 | +} |
| 102 | + |
| 103 | +/* Get the PDF of the distribution */ |
| 104 | +Scalar RotatedCopula::computePDF(const Point & point) const |
| 105 | +{ |
| 106 | + return copula_.computePDF(rotate(point)); |
| 107 | +} |
| 108 | + |
| 109 | +/* Get the CDF of the distribution */ |
| 110 | +Scalar RotatedCopula::computeCDF(const Point & point) const |
| 111 | +{ |
| 112 | + // https://quantitative-thinking.com/copula_theory/cop_modifications/clockwise_rot.html |
| 113 | + const Scalar cdf0 = copula_.computeCDF(rotate(point)); |
| 114 | + switch (rotation_) |
| 115 | + { |
| 116 | + case (ROTATION_0): |
| 117 | + return cdf0; |
| 118 | + case (ROTATION_90): |
| 119 | + return copula_.computeCDF(Point({point[1], 1.0})) - cdf0; |
| 120 | + case (ROTATION_180): |
| 121 | + return 1.0 - copula_.computeCDF(Point({1.0, 1.0 - point[1]})) - copula_.computeCDF(Point({1.0 - point[0], 1.0})) + cdf0; |
| 122 | + case (ROTATION_270): |
| 123 | + return copula_.computeCDF(Point({1.0, point[0]})) - cdf0; |
| 124 | + default: |
| 125 | + throw NotYetImplementedException(HERE) << "RotatedCopula invalid rotation"; |
| 126 | + } |
| 127 | +} |
| 128 | + |
| 129 | +/* Get one realization of the distribution */ |
| 130 | +Point RotatedCopula::getRealization() const |
| 131 | +{ |
| 132 | + const Point x = copula_.getRealization(); |
| 133 | + return rotate(x, true); |
| 134 | +} |
| 135 | + |
| 136 | +/* Parameters value accessor */ |
| 137 | +Point RotatedCopula::getParameter() const |
| 138 | +{ |
| 139 | + return copula_.getParameter(); |
| 140 | +} |
| 141 | + |
| 142 | +void RotatedCopula::setParameter(const Point & parameter) |
| 143 | +{ |
| 144 | + copula_.setParameter(parameter); |
| 145 | +} |
| 146 | + |
| 147 | +/* Parameters description accessor */ |
| 148 | +Description RotatedCopula::getParameterDescription() const |
| 149 | +{ |
| 150 | + return copula_.getParameterDescription(); |
| 151 | +} |
| 152 | + |
| 153 | +/* Comparison operator */ |
| 154 | +Bool RotatedCopula::operator ==(const RotatedCopula & other) const |
| 155 | +{ |
| 156 | + if (this == &other) return true; |
| 157 | + return (copula_ == other.copula_) && (rotation_ == other.rotation_); |
| 158 | +} |
| 159 | + |
| 160 | +Bool RotatedCopula::equals(const DistributionImplementation & other) const |
| 161 | +{ |
| 162 | + const RotatedCopula* p_other = dynamic_cast<const RotatedCopula*>(&other); |
| 163 | + return p_other && (*this == *p_other); |
| 164 | +} |
| 165 | + |
| 166 | +/* String converter */ |
| 167 | +String RotatedCopula::__repr__() const |
| 168 | +{ |
| 169 | + OSS oss; |
| 170 | + oss << "class=" << RotatedCopula::GetClassName() << " copula=" << copula_ << " rotation=" << rotation_ * 90; |
| 171 | + return oss; |
| 172 | +} |
| 173 | + |
| 174 | +String RotatedCopula::__str__(const String &) const |
| 175 | +{ |
| 176 | + OSS oss; |
| 177 | + oss << "RotatedCopula(copula=" << copula_.__str__() << ", rotation=" << rotation_ * 90 << ")"; |
| 178 | + return oss; |
| 179 | +} |
| 180 | + |
| 181 | +Bool RotatedCopula::isContinuous() const |
| 182 | +{ |
| 183 | + return copula_.isContinuous(); |
| 184 | +} |
| 185 | + |
| 186 | +/* Method save() stores the object through the StorageManager */ |
| 187 | +void RotatedCopula::save(Advocate & adv) const |
| 188 | +{ |
| 189 | + DistributionImplementation::save(adv); |
| 190 | + adv.saveAttribute("copula_", copula_); |
| 191 | + adv.saveAttribute("rotation_", rotation_); |
| 192 | +} |
| 193 | + |
| 194 | +/* Method load() reloads the object from the StorageManager */ |
| 195 | +void RotatedCopula::load(Advocate & adv) |
| 196 | +{ |
| 197 | + DistributionImplementation::load(adv); |
| 198 | + adv.loadAttribute("copula_", copula_); |
| 199 | + adv.loadAttribute("rotation_", rotation_); |
| 200 | + computeRange(); |
| 201 | +} |
| 202 | + |
| 203 | + |
| 204 | +} /* namespace OTVINE */ |
0 commit comments