Skip to content

Commit d23962c

Browse files
committed
Add RotatedCopula
1 parent 0889453 commit d23962c

File tree

10 files changed

+368
-2
lines changed

10 files changed

+368
-2
lines changed

lib/src/CMakeLists.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,11 @@
22

33
ot_add_current_dir_to_include_dirs ()
44

5+
ot_add_source_file (RotatedCopula.cxx)
56
ot_add_source_file (VineCopulaFactory.cxx)
67
ot_add_source_file (VineCopula.cxx)
78

9+
ot_install_header_file (RotatedCopula.hxx)
810
ot_install_header_file (VineCopulaFactory.hxx)
911
ot_install_header_file (VineCopula.hxx)
1012

lib/src/RotatedCopula.cxx

Lines changed: 204 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,204 @@
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 */

lib/src/otvine/RotatedCopula.hxx

Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
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+
#ifndef OTVINE_ROTATEDCOPULA_HXX
22+
#define OTVINE_ROTATEDCOPULA_HXX
23+
24+
#include <openturns/DistributionImplementation.hxx>
25+
#include <openturns/Distribution.hxx>
26+
27+
#include "otvine/otvineprivate.hxx"
28+
29+
namespace OTVINE
30+
{
31+
32+
/**
33+
* @class RotatedCopula
34+
*
35+
* RotatedCopula is some vinecopulafactory type to illustrate how to add some classes in OpenTURNS
36+
*/
37+
class OTVINE_API RotatedCopula
38+
: public OT::DistributionImplementation
39+
{
40+
CLASSNAME
41+
42+
public:
43+
/** Default constructor */
44+
RotatedCopula();
45+
46+
enum Rotation {ROTATION_0, ROTATION_90, ROTATION_180, ROTATION_270};
47+
48+
explicit RotatedCopula(const OT::Distribution & copula, const Rotation = ROTATION_90);
49+
50+
/** Virtual constructor method */
51+
RotatedCopula * clone() const override;
52+
53+
OT::Bool operator ==(const RotatedCopula & other) const;
54+
protected:
55+
OT::Bool equals(const DistributionImplementation & other) const override;
56+
public:
57+
58+
/** String converter */
59+
OT::String __repr__() const override;
60+
OT::String __str__(const OT::String & offset = "") const override;
61+
62+
/** Get one realization of the distribution */
63+
OT::Point getRealization() const override;
64+
65+
/** Get the PDF of the distribution */
66+
using OT::DistributionImplementation::computePDF;
67+
OT::Scalar computePDF(const OT::Point & point) const override;
68+
69+
/** Get the CDF of the distribution */
70+
using OT::DistributionImplementation::computeCDF;
71+
OT::Scalar computeCDF(const OT::Point & point) const override;
72+
73+
/** Parameters value accessors */
74+
void setParameter(const OT::Point & parameter) override;
75+
OT::Point getParameter() const override;
76+
77+
/** Parameters description accessor */
78+
OT::Description getParameterDescription() const override;
79+
80+
/** Check if the distribution is continuous */
81+
OT::Bool isContinuous() const override;
82+
83+
/** Method save() stores the object through the StorageManager */
84+
void save(OT::Advocate & adv) const override;
85+
86+
/** Method load() reloads the object from the StorageManager */
87+
void load(OT::Advocate & adv) override;
88+
89+
private:
90+
OT::Point rotate(const OT::Point & x, const OT::Bool inverse = false) const;
91+
92+
OT::Distribution copula_;
93+
OT::UnsignedInteger rotation_ = ROTATION_0;
94+
95+
}; /* class RotatedCopula */
96+
97+
} /* namespace OTVINE */
98+
99+
#endif /* OTVINE_ROTATEDCOPULA_HXX */

python/doc/user_manual/user_manual.rst

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,3 +8,5 @@ API Reference
88
:template: class.rst_t
99

1010
VineCopulaFactory
11+
VineCopula
12+
RotatedCopula

python/src/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@ endmacro ()
4747

4848

4949
ot_add_python_module( ${PACKAGE_NAME} ${PACKAGE_NAME}_module.i
50+
RotatedCopula.i RotatedCopula_doc.i
5051
VineCopula.i VineCopula_doc.i
5152
VineCopulaFactory.i VineCopulaFactory_doc.i
5253
)

python/src/RotatedCopula.i

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
// SWIG file RotatedCopula.i
2+
3+
%{
4+
#include "otvine/RotatedCopula.hxx"
5+
%}
6+
7+
%include RotatedCopula_doc.i
8+
9+
%copyctor OTVINE::RotatedCopula;
10+
11+
%include otvine/RotatedCopula.hxx

python/src/RotatedCopula_doc.i

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
%feature("docstring") OTVINE::RotatedCopula
2+
"Rotated copula.
3+
4+
Parameters
5+
----------
6+
copula : :py:class:`openturns.Distribution`
7+
A copula
8+
rotation : int
9+
The rotation index:
10+
11+
- otvine.RotatedCopula.ROTATION_0
12+
- otvine.RotatedCopula.ROTATION_90
13+
- otvine.RotatedCopula.ROTATION_180
14+
- otvine.RotatedCopula.ROTATION_270
15+
16+
Examples
17+
--------
18+
>>> import otvine
19+
>>> import openturns as ot
20+
>>> copula = ot.GumbelCopula()
21+
>>> rotated = otvine.RotatedCopula(copula, otvine.ROTATION_180)
22+
"

python/src/otvine_module.i

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,6 @@
1818

1919
// The new classes
2020
%include otvine/otvineprivate.hxx
21+
%include RotatedCopula.i
2122
%include VineCopula.i
2223
%include VineCopulaFactory.i
23-
24-

python/test/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ macro (ot_pyinstallcheck_test TESTNAME)
3737
endmacro ()
3838

3939

40+
ot_pyinstallcheck_test (RotatedCopula_std IGNOREOUT)
4041
ot_pyinstallcheck_test (VineCopulaFactory_std IGNOREOUT)
4142
ot_pyinstallcheck_test (docstring IGNOREOUT)
4243

0 commit comments

Comments
 (0)