Solver.cxx
Go to the documentation of this file.
1 #include "Solver.h"
2 
3 #include <algorithm>
4 #include <cstdlib>
5 #include <set>
6 #include <string>
7 #include <iostream>
8 
9 template<class T> T sqr(T x){return x*x;}
10 
11 // ---------------------------------------------------------------------------
13  : fChannel(chan), fCharge(q), fPred(0)
14 {
15 }
16 
17 // ---------------------------------------------------------------------------
18 Neighbour::Neighbour(SpaceCharge* sc, double coupling)
19  : fSC(sc), fCoupling(coupling)
20 {
21 }
22 
23 // ---------------------------------------------------------------------------
24 SpaceCharge::SpaceCharge(double x, double y, double z,
25  CollectionWireHit* cwire,
26  InductionWireHit* wire1, InductionWireHit* wire2)
27  : fX(x), fY(y), fZ(z),
28  fCWire(cwire), fWire1(wire1), fWire2(wire2),
29  fPred(0),
30  fNeiPotential(0)
31 {
32 }
33 
34 // ---------------------------------------------------------------------------
35 void SpaceCharge::AddCharge(double dq)
36 {
37  fPred += dq;
38 
39  for(Neighbour& nei: fNeighbours)
40  nei.fSC->fNeiPotential += dq * nei.fCoupling;
41 
42  if(fWire1) fWire1->fPred += dq;
43  if(fWire2) fWire2->fPred += dq;
44 }
45 
46 // ---------------------------------------------------------------------------
48  const std::vector<SpaceCharge*>& cross)
49  : fChannel(chan), fCharge(q), fCrossings(cross)
50 {
51  if(q < 0){
52  std::cout << "Trying to construct collection wire with negative charge " << q << " this should never happen." << std::endl;
53  abort();
54  }
55 
56  const double p = q/cross.size();
57 
58  for(SpaceCharge* sc: cross) sc->AddCharge(p);
59 }
60 
61 // ---------------------------------------------------------------------------
63 {
64  // Each SpaceCharge can only be in one collection wire, so we own them. But
65  // not SpaceCharge does not clean up its induction wires since they're
66  // shared.
67  for(SpaceCharge* sc: fCrossings) delete sc;
68 }
69 
70 // ---------------------------------------------------------------------------
71 double Metric(double q, double p)
72 {
73  return sqr(q-p);
74 }
75 
76 // ---------------------------------------------------------------------------
78 {
79  return sqr(q-p);
80 }
81 
82 // ---------------------------------------------------------------------------
83 double Metric(const std::vector<SpaceCharge*>& scs, double alpha)
84 {
85  double ret = 0;
86 
87  std::set<InductionWireHit*> iwires;
88  for(const SpaceCharge* sc: scs){
89  if(sc->fWire1) iwires.insert(sc->fWire1);
90  if(sc->fWire2) iwires.insert(sc->fWire2);
91 
92  if(alpha != 0){
93  ret -= alpha*sqr(sc->fPred);
94  // "Double-counting" of the two ends of the connection is
95  // intentional. Otherwise we'd have a half in the line above.
96  ret -= alpha * sc->fPred * sc->fNeiPotential;
97  }
98  }
99 
100  for(const InductionWireHit* iwire: iwires){
101  ret += Metric(iwire->fCharge, iwire->fPred);
102  }
103 
104  return ret;
105 }
106 
107 // ---------------------------------------------------------------------------
108 double Metric(const std::vector<CollectionWireHit*>& cwires, double alpha)
109 {
110  std::vector<SpaceCharge*> scs;
111  for(CollectionWireHit* cwire: cwires)
112  scs.insert(scs.end(), cwire->fCrossings.begin(), cwire->fCrossings.end());
113  return Metric(scs, alpha);
114 }
115 
116 // ---------------------------------------------------------------------------
117 QuadExpr Metric(const SpaceCharge* sci, const SpaceCharge* scj, double alpha)
118 {
119  QuadExpr ret = 0;
120 
121  // How much charge moves from scj to sci
122  QuadExpr x = QuadExpr::X();
123 
124  if(alpha != 0){
125  const double scip = sci->fPred;
126  const double scjp = scj->fPred;
127 
128  // Self energy. SpaceCharges are never the same object
129  ret -= alpha*sqr(scip + x);
130  ret -= alpha*sqr(scjp - x);
131 
132  // Interaction. We're only seeing one end of the double-ended connection
133  // here, so multiply by two.
134  ret -= 2 * alpha * (scip + x) * sci->fNeiPotential;
135  ret -= 2 * alpha * (scjp - x) * scj->fNeiPotential;
136 
137  // This miscounts if i and j are neighbours of each other
138  for(const Neighbour& n: sci->fNeighbours){
139  if(n.fSC == scj){
140  // If we detect that case, remove the erroneous terms
141  ret += 2 * alpha * (scip + x) * scjp * n.fCoupling;
142  ret += 2 * alpha * (scjp - x) * scip * n.fCoupling;
143 
144  // And replace with the correct interaction terms
145  ret -= 2 * alpha * (scip + x) * (scjp - x) * n.fCoupling;
146  break;
147  }
148  }
149  }
150 
151 
152  const InductionWireHit* iwire1 = sci->fWire1;
153  const InductionWireHit* jwire1 = scj->fWire1;
154 
155  const double qi1 = iwire1 ? iwire1->fCharge : 0;
156  const double pi1 = iwire1 ? iwire1->fPred : 0;
157 
158  const double qj1 = jwire1 ? jwire1->fCharge : 0;
159  const double pj1 = jwire1 ? jwire1->fPred : 0;
160 
161  if(iwire1 == jwire1){
162  // Same wire means movement of charge cancels itself out
163  if(iwire1) ret += Metric(qi1, pi1);
164  }
165  else{
166  if(iwire1) ret += Metric(qi1, pi1 + x);
167  if(jwire1) ret += Metric(qj1, pj1 - x);
168  }
169 
170  const InductionWireHit* iwire2 = sci->fWire2;
171  const InductionWireHit* jwire2 = scj->fWire2;
172 
173  const double qi2 = iwire2 ? iwire2->fCharge : 0;
174  const double pi2 = iwire2 ? iwire2->fPred : 0;
175 
176  const double qj2 = jwire2 ? jwire2->fCharge : 0;
177  const double pj2 = jwire2 ? jwire2->fPred : 0;
178 
179  if(iwire2 == jwire2){
180  if(iwire2) ret += Metric(qi2, pi2);
181  }
182  else{
183  if(iwire2) ret += Metric(qi2, pi2 + x);
184  if(jwire2) ret += Metric(qj2, pj2 - x);
185  }
186 
187  return ret;
188 }
189 
190 // ---------------------------------------------------------------------------
191 QuadExpr Metric(const SpaceCharge* sc, double alpha)
192 {
193  QuadExpr ret = 0;
194 
195  // How much charge is added to sc
196  QuadExpr x = QuadExpr::X();
197 
198  if(alpha != 0){
199  const double scp = sc->fPred;
200 
201  // Self energy
202  ret -= alpha*sqr(scp + x);
203 
204  // Interaction. We're only seeing one end of the double-ended connection
205  // here, so multiply by two.
206  ret -= 2 * alpha * (scp + x) * sc->fNeiPotential;
207  }
208 
209  // Prediction of the induction wires
210  ret += Metric(sc->fWire1->fCharge, sc->fWire1->fPred + x);
211  ret += Metric(sc->fWire2->fCharge, sc->fWire2->fPred + x);
212 
213  return ret;
214 }
215 
216 // ---------------------------------------------------------------------------
218  SpaceCharge* sci, SpaceCharge* scj,
219  double alpha)
220 {
221  const QuadExpr chisq = Metric(sci, scj, alpha);
222  const double chisq0 = chisq.Eval(0);
223 
224  // Find the minimum of a quadratic expression
225  double x = -chisq.Linear()/(2*chisq.Quadratic());
226 
227  // Don't allow either SpaceCharge to go negative
228  const double xmin = -sci->fPred;
229  const double xmax = scj->fPred;
230 
231  // Clamp to allowed range
232  x = std::min(xmax, x);
233  x = std::max(xmin, x);
234 
235  const double chisq_new = chisq.Eval(x);
236 
237  // Should try these too, because the function might be convex not concave, so
238  // d/dx=0 gives the max not the min, and the true min is at one extreme of
239  // the range.
240  const double chisq_p = chisq.Eval(xmax);
241  const double chisq_n = chisq.Eval(xmin);
242 
243  if(std::min(std::min(chisq_p, chisq_n), chisq_new) > chisq0+1){
244  std::cout << "Solution at " << x << " is worse than current state! Scan from " << xmin << " to " << xmax << std::endl;
245  for(double x = xmin; x < xmax; x += .01*(xmax-xmin)){
246  std::cout << x << " " << chisq.Eval(x) << std::endl;
247  }
248 
249  std::cout << "Soln, original, up edge, low edge:" << std::endl;
250  std::cout << chisq_new << " " << chisq0 << " " << chisq_p << " " << chisq_n << std::endl;
251  abort();
252  }
253 
254  if(std::min(chisq_n, chisq_p) < chisq_new){
255  if(chisq_n < chisq_p) return xmin;
256  return xmax;
257  }
258 
259  return x;
260 }
261 
262 // ---------------------------------------------------------------------------
263 void Iterate(CollectionWireHit* cwire, double alpha)
264 {
265  // Consider all pairs of crossings
266  const unsigned int N = cwire->fCrossings.size();
267 
268  for(unsigned int i = 0; i+1 < N; ++i){
269  SpaceCharge* sci = cwire->fCrossings[i];
270 
271  for(unsigned int j = i+1; j < N; ++j){
272  SpaceCharge* scj = cwire->fCrossings[j];
273 
274  const double x = SolvePair(cwire, sci, scj, alpha);
275 
276  if(x == 0) continue;
277 
278  // Actually make the update
279  sci->AddCharge(+x);
280  scj->AddCharge(-x);
281  } // end for j
282  } // end for i
283 }
284 
285 // ---------------------------------------------------------------------------
286 void Iterate(SpaceCharge* sc, double alpha)
287 {
288  const QuadExpr chisq = Metric(sc, alpha);
289 
290  // Find the minimum of a quadratic expression
291  double x = -chisq.Linear()/(2*chisq.Quadratic());
292 
293  // Don't allow the SpaceCharge to go negative
294  const double xmin = -sc->fPred;
295 
296  // Clamp to allowed range
297  x = std::max(xmin, x);
298 
299  const double chisq_new = chisq.Eval(x);
300 
301  // Should try here too, because the function might be convex not concave, so
302  // d/dx=0 gives the max not the min, and the true min is at one extreme of
303  // the range.
304  const double chisq_n = chisq.Eval(xmin);
305 
306  if(chisq_n < chisq_new)
307  sc->AddCharge(xmin);
308  else
309  sc->AddCharge(x);
310 }
311 
312 // ---------------------------------------------------------------------------
313 void Iterate(const std::vector<CollectionWireHit*>& cwires,
314  const std::vector<SpaceCharge*>& orphanSCs,
315  double alpha)
316 {
317  // Visiting in a "random" order helps prevent local artefacts that are slow
318  // to break up.
319  unsigned int cwireIdx = 0;
320  if (!cwires.empty()){
321  do{
322  Iterate(cwires[cwireIdx], alpha);
323 
324  const unsigned int prime = 1299827;
325  cwireIdx = (cwireIdx+prime)%cwires.size();
326  } while(cwireIdx != 0);
327  }
328 
329  for(SpaceCharge* sc: orphanSCs) Iterate(sc, alpha);
330 }
void AddCharge(double dq)
Definition: Solver.cxx:35
double fCharge
Definition: Solver.h:25
double fPred
Definition: Solver.h:27
double Linear() const
Definition: QuadExpr.h:16
double fCoupling
Definition: Solver.h:37
std::vector< Neighbour > fNeighbours
Definition: Solver.h:56
SpaceCharge(double x, double y, double z, CollectionWireHit *cwire, InductionWireHit *wire1, InductionWireHit *wire2)
Definition: Solver.cxx:24
double fNeiPotential
Neighbour-induced potential.
Definition: Solver.h:59
void Iterate(CollectionWireHit *cwire, double alpha)
Definition: Solver.cxx:263
std::vector< SpaceCharge * > fCrossings
Definition: Solver.h:73
double SolvePair(CollectionWireHit *cwire, SpaceCharge *sci, SpaceCharge *scj, double alpha)
Definition: Solver.cxx:217
InductionWireHit * fWire2
Definition: Solver.h:54
static QuadExpr X()
Definition: QuadExpr.cxx:8
std::void_t< T > n
double alpha
Definition: doAna.cpp:15
p
Definition: test.py:223
InductionWireHit * fWire1
Definition: Solver.h:54
double Metric(double q, double p)
Definition: Solver.cxx:71
Neighbour(SpaceCharge *sc, double coupling)
Definition: Solver.cxx:18
static int max(int a, int b)
InductionWireHit(int chan, double q)
Definition: Solver.cxx:12
T sqr(T x)
Definition: Solver.cxx:9
T min(sqlite3 *const db, std::string const &table_name, std::string const &column_name)
Definition: statistics.h:55
double Eval(double x) const
Definition: QuadExpr.cxx:69
list x
Definition: train.py:276
Vector cross(Vector const &a, Vector const &b)
Return cross product of two vectors.
CollectionWireHit(int chan, double q, const std::vector< SpaceCharge * > &cross)
Definition: Solver.cxx:47
double fPred
Definition: Solver.h:58
QTextStream & endl(QTextStream &s)
double Quadratic() const
Definition: QuadExpr.h:15
SpaceCharge * fSC
Definition: Solver.h:36