forked from wjakob/nori
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathttest.cpp
210 lines (178 loc) · 8.23 KB
/
ttest.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
/*
This file is part of Nori, a simple educational ray tracer
Copyright (c) 2015 by Wenzel Jakob
*/
#include <nori/scene.h>
#include <nori/bsdf.h>
#include <nori/camera.h>
#include <nori/integrator.h>
#include <nori/sampler.h>
#include <hypothesis.h>
#include <pcg32.h>
/*
* =======================================================================
* WARNING WARNING WARNING WARNING WARNING WARNING
* =======================================================================
* Remember to put on SAFETY GOGGLES before looking at this file. You
* are most certainly not expected to read or understand any of it.
* =======================================================================
*/
NORI_NAMESPACE_BEGIN
/**
* Student's t-test for the equality of means
*
* This test analyzes whether the expected value of a random variable matches a
* certain known value. When there is significant statistical "evidence"
* against this hypothesis, the test fails.
*
* This is useful in checking whether a Monte Carlo method method converges
* against the right value. Because statistical tests are able to handle the
* inherent noise of these methods, they can be used to construct statistical
* test suites not unlike the traditional unit tests used in software engineering.
*
* This implementation can be used to test two things:
*
* 1. that the illumination scattered by a BRDF model under uniform illumination
* into a certain direction matches a given value (modulo noise).
*
* 2. that the average radiance received by a camera within some scene
* matches a given value (modulo noise).
*/
class StudentsTTest : public NoriObject {
public:
StudentsTTest(const PropertyList &propList) {
/* The null hypothesis will be rejected when the associated
p-value is below the significance level specified here. */
m_significanceLevel = propList.getFloat("significanceLevel", 0.01f);
/* This parameter specifies a list of incidence angles that will be tested */
std::vector<std::string> angles = tokenize(propList.getString("angles", ""));
for (auto angle : angles)
m_angles.push_back(toFloat(angle));
/* This parameter specifies a list of reference values, one for each angle */
std::vector<std::string> references = tokenize(propList.getString("references", ""));
for (auto angle : references)
m_references.push_back(toFloat(angle));
/* Number of BSDF samples that should be generated (default: 100K) */
m_sampleCount = propList.getInteger("sampleCount", 100000);
}
virtual ~StudentsTTest() {
for (auto bsdf : m_bsdfs)
delete bsdf;
for (auto scene : m_scenes)
delete scene;
}
void addChild(NoriObject *obj) {
switch (obj->getClassType()) {
case EBSDF:
m_bsdfs.push_back(static_cast<BSDF *>(obj));
break;
case EScene:
m_scenes.push_back(static_cast<Scene *>(obj));
break;
default:
throw NoriException("StudentsTTest::addChild(<%s>) is not supported!",
classTypeName(obj->getClassType()));
}
}
/// Invoke a series of t-tests on the provided input
void activate() {
int total = 0, passed = 0;
pcg32 random;
if (!m_bsdfs.empty()) {
if (m_references.size() * m_bsdfs.size() != m_angles.size())
throw NoriException("Specified a different number of angles and reference values!");
if (!m_scenes.empty())
throw NoriException("Cannot test BSDFs and scenes at the same time!");
/* Test each registered BSDF */
int ctr = 0;
for (auto bsdf : m_bsdfs) {
for (size_t i=0; i<m_references.size(); ++i) {
float angle = m_angles[i], reference = m_references[ctr++];
cout << "------------------------------------------------------" << endl;
cout << "Testing (angle=" << angle << "): " << bsdf->toString() << endl;
++total;
BSDFQueryRecord bRec(sphericalDirection(degToRad(angle), 0));
cout << "Drawing " << m_sampleCount << " samples .. " << endl;
double mean=0, variance = 0;
for (int k=0; k<m_sampleCount; ++k) {
Point2f sample(random.nextFloat(), random.nextFloat());
double result = (double) bsdf->sample(bRec, sample).getLuminance();
/* Numerically robust online variance estimation using an
algorithm proposed by Donald Knuth (TAOCP vol.2, 3rd ed., p.232) */
double delta = result - mean;
mean += delta / (double) (k+1);
variance += delta * (result - mean);
}
variance /= m_sampleCount - 1;
std::pair<bool, std::string>
result = hypothesis::students_t_test(mean, variance, reference,
m_sampleCount, m_significanceLevel, (int) m_references.size());
if (result.first)
++passed;
cout << result.second << endl;
}
}
} else {
if (m_references.size() != m_scenes.size())
throw NoriException("Specified a different number of scenes and reference values!");
Sampler *sampler = static_cast<Sampler *>(
NoriObjectFactory::createInstance("independent", PropertyList()));
int ctr = 0;
for (auto scene : m_scenes) {
const Integrator *integrator = scene->getIntegrator();
const Camera *camera = scene->getCamera();
float reference = m_references[ctr++];
cout << "------------------------------------------------------" << endl;
cout << "Testing scene: " << scene->toString() << endl;
++total;
cout << "Generating " << m_sampleCount << " paths.. " << endl;
double mean = 0, variance = 0;
for (int k=0; k<m_sampleCount; ++k) {
/* Sample a ray from the camera */
Ray3f ray;
Point2f pixelSample = (sampler->next2D().array()
* camera->getOutputSize().cast<float>().array()).matrix();
Color3f value = camera->sampleRay(ray, pixelSample, sampler->next2D());
/* Compute the incident radiance */
value *= integrator->Li(scene, sampler, ray);
/* Numerically robust online variance estimation using an
algorithm proposed by Donald Knuth (TAOCP vol.2, 3rd ed., p.232) */
double result = (double) value.getLuminance();
double delta = result - mean;
mean += delta / (double) (k+1);
variance += delta * (result - mean);
}
variance /= m_sampleCount - 1;
std::pair<bool, std::string>
result = hypothesis::students_t_test(mean, variance, reference,
m_sampleCount, m_significanceLevel, (int) m_references.size());
if (result.first)
++passed;
cout << result.second << endl;
}
}
cout << "Passed " << passed << "/" << total << " tests." << endl;
if (passed < total)
throw std::runtime_error("Some tests failed :(");
}
std::string toString() const {
return tfm::format(
"StudentsTTest[\n"
" significanceLevel = %f,\n"
" sampleCount= %i\n"
"]",
m_significanceLevel,
m_sampleCount
);
}
EClassType getClassType() const { return ETest; }
private:
std::vector<BSDF *> m_bsdfs;
std::vector<Scene *> m_scenes;
std::vector<float> m_angles;
std::vector<float> m_references;
float m_significanceLevel;
int m_sampleCount;
};
NORI_REGISTER_CLASS(StudentsTTest, "ttest");
NORI_NAMESPACE_END