text:
# Quarter II on SBG

We’re at the twilight of the second quarter in my calculus class. Standards Based Grading has become normal. The most exciting thing about SBG is seeing students who are traditionally unsuccessful turn that around. They can get it, but they realize they have to conquer their weaknesses. The Giant Specter that Haunts All Calculus Teachers is the deficiencies we see with our kids’ algebra abilities.)

Students will use the quotient rule to get something like $\frac{(x+1)(2\cos(x))-(2\sin(x))(1)}{(x+1)^2}$. And then, then, *shudder*: $\frac{2\cos(x)-2\sin(x)}{x+1}$ because, you know Mr. Shah, you can just cancel the $x+1$.

Students simply can’t get by without fixing their algebra deficiencies. But they have lots of opportunities to fix them. It’s really hard to unlearn bad algebra, but many are doing it.

The flip side is possibly the MOST FRUSTRATING THING ABOUT SBG. Yes, all caps means I’m yelling. It’s the students who just sit there, don’t reassess, and *hope* everything will turn around. I encourage (of course I encourage!), but I am only going to go so far. They’re not freshman. They’re seniors. They know what they’re doing. They’re making choices. It saddens me that students who have been given the opportunity to learn would rather languish.

It simply highlights my biggest pet peeve. I really really dislike it when I am confronted with a student with SO MUCH POTENTIAL and SO MUCH ABILITY but they flounder because they don’t want to work. They don’t want to put in the effort it takes. It angers me because they don’t realize that THEY ARE SO LUCKY TO HAVE THE OPPORTUNITY TO LEARN. They’re squandering opportunities and closing doors and that saddens me.

However, those students are few and far between.

In terms of the number of kids reassessing this quarter:

Week 1: 6
Week 2:  8
Week 3:  11
Week 4:  15
Week 5:  15
Week 6:  17

TOTAL:  72 reassessments

Making the change this quarter that students can only reassess on Fridays (and they have to send me their form email demonstrating they’ve remediated by Tuesday at 5pm) has been amazing, in terms of my own self-preservation. I generally spend (closer to the end of the quarter) an extra 4 hours/week writing, grading, and recording the reassessments. It feels worth it, so I do it.

I was hoping that the number of reassessments would decrease in the 2nd quarter, once students began to recognize what was required of them to do well on the assessments. That isn’t the case (Quarter 1 had 70 assessments). I have two conjectures about this:

1. The material is harder, and way more algebra heavy, so students are struggling more.
2. Students have started to view the reassessments as their safety net (or their crutch, depending on the student), so they aren’t adequately preparing for assessments

I also wonder how senioritis will affect things in the 3rd and 4th quarters.

## 19 comments

1. >because, you know Mr. Shah, you can just cancel the x+1.

In an attempt to help students with this mistake I’ve banished the word “cancel” from my classroom, and now I insist on talking about “factoring fractions.” This has led to a lot of “OOOOOHHHHHHH”s from my Alg II students, and I’m going to teach my freshmen how to deal with fractions in this way instead of talking about when you can cancel.

2. I agree with this post and recognize similar issues/benefits in my classes.

3. Chris says:

> The Giant Specter that Haunts All Calculus Teachers is the deficiencies we see with our kids’ algebra abilities.

I don’t teach calculus, but I run into the same problem with my (college-level) students all the time. Such as the time that they were all doing a proof in pairs on the board and EVERY SINGLE PAIR immediately “simplified” the subexpression n-(k-1) to n-k-1. When I went around pointing out that these were not the same, several people did not believe me until I forced them to plug in numbers.

I’m sure everybody here has a story like this.

Over time though, my view has shifted from “they can’t do algebra” to “they can’t do symbolic manipulation” to “they can’t recognize when/where to apply known abstractions, and even when they do, they frequently apply them incorrectly”. For example, they know the associative/commutative/distributive laws (although perhaps not by name), but don’t recognize the opportunity to apply these laws when that opportunity is buried inside a subexpression or when the context is lacking explicit instructions to use this or that law.

I can’t claim that this shift in view has magically fixed the problem, but I do feel like it helps me recognize when various kinds of struggles, both mathematical and non-mathematical, are really instances of this same underlying phenomenon.

4. I was the kid in highschool with high skill / talent / potential levels but low motivation. I spent my evenings reading novels and learning to program my computer from the internet (which was more interesting than my calculus class and WAY more interesting than my history class) and spent class playing tetris on my calculator (which I was learning to program). Then, in college, I did the same thing (though I did get a degree in math). It wasn’t until I started teaching precalc that I learned to love algebra and to really get to KNOW things like the law of cosines, various factor theorems, etc. Now, I’m really glad to be so well read and to have such refined computer skills and to be a good writer. Admittedly, conversations about history these days can be downright embarrassing for me.

All this to say that I wouldn’t worry too much about those smart kids that aren’t choosing to do your class to their best ability. They may be squandering opportunities, but they may not be – they may just not be ready to be interested in your class. I think it’s ok for them to choose something else, even though they’re lucky enough to go to your school.

5. Sam, you said, “They’re not freshman. They’re seniors. They know what they’re doing. They’re making choices. It saddens me that students who have been given the opportunity to learn would rather languish.”

I agree that the SBG philosophy gives students more opportunities to re-assess and in turn demonstrate understanding of concepts when compared with the traditional system. It also levels the playing field a bit between students who “get it” early and those who “get it” later. For these two (among others) reasons, I can’t imagine teaching/assessing/grading in any other way. You recognized there’s a difference between seniors and freshmen and I couldn’t agree more. I wonder if re-assessments should be “forced” rather than optional for some grade levels (not necessarily seniors). At what point do we release 100% of the responsibility to the student? The intent of this comment is not to criticize what you’re doing, Sam, but rather to stir up a conversation amongst the SBGers here in the edublogosphere. How much of the re-assessment should be left up to the student? If a sixth grade teacher tells me he/she leaves it all up to the student, I think I’d question that practice a bit. What are your thoughts? It may be the topic for a future post.

1. Jim Doherty says:

Along these lines…

One of my grad school professors had that attitude (probably still has it!) that all papers not only should be rewritten, but they MUST be rewritten. To move this process along, every first draft was returned without a grade. Instead, he simply marked next to each line or passage where he thought there was an issue that needed to be addressed. It was up to me to decide whether I needed to strengthen an argument, fix a grammar problem, move a sentence, etc. I never thought more deeply about my writing than I did that semester. How applicable is that idea to our discipline?

6. Mike says:

I really want my students to take the second attempt in class but the majority do not. Will limiting re-assessments make the second attempt more important? I really don’t want to limit total re-assessments though? I am limiting them to 3 per week to make it more manageable for me, but not a total for the quarter.

7. Keep fighting the good fight, Sam. You are doing exactly what you need to do to reach students half way. I’m proud of you for jumping into this!

I’ll bite on Townsley’s prompt. SBG is about clarity in grading and reporting. SBG is about not punishing students with arbitrary timelines. The reassessment component is what makes most teachers shy away from SBG, and I think we’ve all put too much emphasis on it. I’ve taken almost all of the reassessment control from my students (GIANT classes, ahem, Matt), and things are still working fantastically. I just have to write better quizzes and quizzing schedules. (for Math, that is)

Good luck, man. Don’t get caught up in systems and rules. Just remember that this is better than the penal colony we were all warden of before.

8. I once told my students (not as sharp as yours, perhaps…) who absolutely were not giving me even the minimum effort to pass my class what my best friend had told me about poverty in the Philippines (where he had spent all of his life up till 22): Many kids in the Philippines live in shacks with no roof and they have to dig through trash for food. And yet they still go to school everyday. Why?? Because education is deeply valued.

American kids don’t know how good they have it, so they just throw away learning opportunities. It’s truly such a shame. In El Salvador, many kids don’t even get to go to school. They have to sell Chiclets and cigarettes on the street with their parents all day/night, just to make enough money to buy food.

9. Oops – correction: shacks with no walls. :) They have roofs.

10. Jim Doherty says:

Sam and all –
I am more and more intrigued as I read about your adventures into SBG. I want to convince my administrators to free up some PD funds to send me (and hopefully some others at my school) to a nearby conference discussing SBG. My question is this – Is there a concise reading I can find to present to my colleagues and administrators that really summarizes SBG ideas and benefits neatly?

I thank you all in advance!

Jim D

11. Elizabeth says:

I believe there are seasons of hard work and seasons of slacking off in all students’ lives–even among the most motivated. You definitely seem to be experiencing a fallow period with your Calculus students, and I can understand your feelings of discouragement and frustration. Still, I think it may be premature to catapult all the way to sadness because they are, in fact, still adolescents. Plus it’s February. Nobody is at their best in February. I haven’t finished analyzing all the data — because there’ve sure been a lot of Februaries since the institution of the current calendar! — but I’m pretty sure about this.

Also, as a teacher, parent, or mentor, there always comes a point when you have to just allow a student to experience what happens when they stop acting impeccably in their own best interests. Many of these students, especially the ones who make it to Calculus, have never experienced failure or even a significant setback. But that is part of their development process too.

I find that again and again, I have to step back, let go, and simply say, “Wow, well, this is going to be interesting for me. I’ve never had a super-motivated student like you simply give up so early in the year. I’ll be curious to see how it all turns out. Good luck with this!”

After all, if we are REALLY trying to get these students to take ownership of their own learning, we can’t just restrict our perspective to the positive parts of their experience. This too is part of SBG. It is also about respecting the adult-style boundaries students are demanding. By their senior years, kids need to be accountable to themselves for their own performance, and we actively do them a disservice by taking on more of the responsibility for their success than is appropriate.

Some people need more time to experience the consequences of their own slacking off. I know that once *I* got to college and realized how seriously I had shortchanged myself by slacking off that last semester in high school, it put my choices into sharp relief for me. And at that point, I made major changes to my study habits that took me to a dramatically higher level of performance.

Fortunately, I — like your students — had been given a terrific foundation that had just gone slightly dormant. So it was a lot easier for me to kick back into high gear than it was for those students who had always gotten along on the sheer easiness of classes.

So be of good cheer, ye SBG teachers of 12th grade slackers. You have planted the seeds and given them everything they need for success. Now it’s up to them to decide whether (or when) to pick up where you’ve left off.

Elizabeth (aka @cheesemonkeysf on Twitter)

1. I agree with your statement, except for one thing: although it is the student who needs to take ownership of their learning (and I encourage them and show them ways to do so), I as their teacher get stuck with the aftermath of students who are underperforming (by parents, by higher ups, etc.). Logic about personal responsibility may go a long way in convincing *me* that I’m doing right by these students, but it doesn’t always convince those other adults who are involved with looking out for students who are underperforming.

Which is all to say that I’ve found the “sink or swim” mentality (even when I’m throwing life preservers and rope and sending helicopters) of SBG to demand nuances that I did not anticipate before I started it.

Sam

1. Elizabeth says:

I can definitely understand what you are saying and I too go pretty far (life preservers, ropes, helicopters, trained rescue dolphins) for the students who are underperforming.

In my comment, I was thinking primarily of the peculiar case of those high-achieving seniors who have actually made it to Calculus and are showing symptoms of acute senioritis. For myself, that is the moment at which my attitude shifts to the above.

Also I would characterize this posture less as an insistence on “personal responsibility” and more as an implementation of Rudolf Dreikurs’ work on using “natural and logical consequences” with adolescents who are taking on more and more autonomy at this pivotal moment in their lives.

When I have a 9th, 10th, or 11th grader fluffing off and squandering their educational opportunity, it’s a whole different situation, and my interventions are more direct and incessant.

Elizabeth

12. from a recent convert…

I just started doing SBG (right after Thanksgiving in my Geometry classes and about 1 month ago in my Algebra classes. I have done very little reassessing because a lot of my students do not really care. I’m thinking it will pick up after conferences and the end of the trimester. Any student with a C or below is getting a phone call this week though.

I am also quite discouraged by their prerequisite knowledge and using this type of assessment is making their deficiencies very clear. This “canceling out” phenonema Sam mentioned starts in middle school, they also like to cross multiply willy nilly.

I am wondering what to do with my advanced 8th graders (taking Geometry) who cannot calculate slope or write the equation of a line or solve an equation with fractions. They are doing fine with concepts that are strictly Geometry (as long as they don’t contain fractions)!

Because they are doing fine with most of the Geometry concepts, their grades are not too bad, so they are not motivated to fix it. I know that these are the students who won’t know their algebra in their high school classes and that is frustrating because I want to fix it but I don’t know how.

Any ideas?

1. @Anita: What I generally do with such a student is to have them come to my desk after the lesson and call the parent with the student present. I then put the student on the line to explain to their parent why I’m calling. You’d be amazed what a difference that often makes. Especially when I tell them afterward that I will be calling their parent again if they stop doing homework again. (Your mileage may vary, depending on the student.)

I also subscribe to currency theory, which I recently blogged about how I use it in my classroom:

http://challenge-of-teaching-math.blogspot.com/2011/02/whats-your-currency.html

The bottom line for me is that my honors students work hard because they already get something out of it: good grades, getting into college, they enjoy the subject, etc. For a lot of the students in my regular classes, they need to be “paid” in their own currency to get them to do what I want.

Paul Hawking
Blog:
The Challenge of Teaching Math
Latest post:
http://challenge-of-teaching-math.blogspot.com/2011/02/pulled-from-comments-feeds-2-19-2001.html

13. How about if students have to demonstrate readiness prior to writing a quiz? Would this help negate the situation where students don’t take the first test seriously because they know they can remediate later? So perhaps they would have to show completion and some level of mastery prior to writing the initial quiz/test.


text:
For each radar data type, you will find one archive per year and per zone, each one sliced in periods of 10 or 11 days (each month is separated in 3 files).

Each .npz file contains 3 arrays :

• data : an array containing maps of radar data values.

• dates : an array containing a datetime object and indicating the date of each data map.

• miss_dates : an array containing a datetime object for each missing radar map over the period.

The time step between two radar scans is 5 min. The radar starts scanning at 00h every day and ends at 23h55. So each file of 11 days should contain a maximum of 3168 rainfall maps (minus the missing dates).

The radar's spatial resolution is 0.01° and the projection system used is EPSG:4326.

You will also find a radar_coords_ZONE.npz file containing 2 arrays lats and lons, indicating the coordinates (latitude and longitude) of the center of each radar pixel.

## 2. Cumulated Rainfall

### 2.1 Description

The rainfall data is organized as specified here.

The data array contains a rainfall map for each time step. For each pixel, the value represents the cumulated rainfall in hundreth of millimeters ($10^{-2} mm$). If the value is missing then the value is -1.

### 2.2 Files organization

The files are stored with 4 compression levels :

• two per year (.tar.gz and .tar)
• two per month (.tar.gz and .tar).

For each month, the data are stored per period of 10 or 11 days (each month is separated in 3 files) :

• part 1 : from day 1 to day 9
• part 2 : from day 10 to day 20
• part 3 : from day 21 to day 30 or 31 according to the month

Example

Here is the path for the NW zone, year 2016, January, first part of month (from day 1 to day 9) :
NW_rainfall_2016.tar/NW_rainfall_2016/rainfall-NW-2016-01.tar.gz/
rainfall-NW-2016-01.tar/rainfall-NW-2016-01/rainfall_NW_2016_01.1.npz

## 3. Rain Radar Quality Code

### 3.1 Description

For each pixel of the rainfall product is associated a quality code with values ranging between 0 (very bad) and 100 (perfect), the values are in percents. If the value is missing, then the value is 255.

The rain radar quality code data is organized as specified here. The original data was to heavy to be manipulated so the quality code data are divided into 2 files :

• rainfall_mean_quality_DATE.npz contains the quality code's average over a day and for each pixel.

• rainfall_diff_quality_DATE.npz contains the difference between the original value and the average over the day for each pixel.

Info

Remark about the average quality code computation : if there is no value for a whole day on 1 pixel, it will be indicated as a missing value in the mean file, else the mean computation is done over the non missing values for 1 day and 1 pixel.

### 3.2 Files organization

The files are stored in the same way as the cumulated Rainfall (4 compression levels). At the month level, the files are split into two categories : mean and diff (cf part Description above). For example, for the NW zone, year 2016, January, there are two files :

• rainfall-mean-quality-code-NW-2016-01.tar.gz
• rainfall-diff-quality-code-NW-2016-01.tar.gz

Example

Here are the paths for the NW zone, year 2016, January, first part of month (from day 1 to day 9) :

• mean data :
NW_rainfall_quality_code_2016.tar.gz/
NW_rainfall_quality_code_2016.tar/
NW_rainfall_quality_code_2016/
rainfall-mean-quality-code-NW-2016-01.tar.gz/
rainfall-mean-quality-code-NW-2016-01.tar/
rainfall-mean-quality-code-NW-2016-01/
rainfall_mean_quality-code_NW_2016_01.1.npz
• diff data :
NW_rainfall_quality_code_2016.tar.gz/
NW_rainfall_quality_code_2016.tar/
NW_rainfall_quality_code_2016/
rainfall-diff-quality-code-NW-2016-01.tar.gz/
rainfall-diff-quality-code-NW-2016-01.tar/
rainfall-diff-quality-code-NW-2016-01/
rainfall_diff_quality-code_NW_2016_01.1.npz

## 4. Reflectivity

The reflectivity is a measure of the efficiency of a radar target in intercepting and returning radio energy. Each radar emits a signal which is partly reflected by the precipitation and returned to the radar's receiver. The returned signal's power is then compared to a reference power density at a distance of 1 meter from the radar's antenna before computing the reflectivity.

The reflectivity factor Z is measured in decibels (dBZ), which can be approximated to a rainfall rate R, expressed in millimeter per hour (mm/h), using the Marshall-Palmer relation:

Info

The 200 and 1.6 coefficients depend on the type of rain and are specific to mainland France.

In 2018, METEO FRANCE introduced a new reflectivity product, with additional data (rainfall probability, height measures,...), which definitively replaced the old product in November 2018. Consequently, during the first part of 2018, the two products overlap and both data are available in the dataset.

### 4.1 Old Product

#### 4.1.1 Description

Warning

The old product data range from 01/01/2016 to 30/10/18 included.

The rain radar data are organized as specified here.

Each pixel of the data array contains a reflectivity value expressed in dBZ, computed using the lower bound of reflecivity levels as follows :

Level Reflectivity Range Data value
0 $Z < 8$ dBZ 0
1 $8 < Z < 16$ dBZ 8
2 $16 < Z < 20$ dBZ 16
N $17 + N < Z < 18 + N$ dBZ $17 + N$
53 $70 \leq Z$ dBZ 70
255 No value or missing data 255

#### 4.1.2 Files organization

The files are stored in the same way as the cumulated Rainfall (4 compression levels).

Example

Here is the path for the NW zone, year 2016, January, first part of month (from day 1 to day 9):
NW_reflectivity_old_product_2016.tar.gz/
NW_reflectivity_old_product_2016.tar/
NW_reflectivity_old_product_2016/
reflectivity-old-NW-2016-01.tar.gz/
reflectivity-old-NW-2016-01.tar/
reflectivity-old-NW-2016-01/
reflectivity_old_NW_2016_01.1.npz

### 4.2 New Product

#### 4.2.1 Description

Warning

The new product data range from 01/02/2018 to 31/12/18 included.

The new reflectivity product introduced the following changes :

• a new data processing algorithm

• the reflectivity minimum detection threshold reduced from 8 to -9 dBZ

• the reflectivity values with a step of 0.5 dBZ

• 2 additionnal products : rain probability and selected measure height

The rain radar data are organized as specified here, with 2 additionnals arrays prob and height, and contains the following data :

• data : reflectivity values (in 1/10 dBZ)

• prob : rain probability (in %)

• height : selected measure height (in m)

The 'non detection data' value is -100 and the 'missing data' value is -200.

#### 4.2.2 Files organization

The files are stored in the same way as the cumulated Rainfall (4 compression levels).

Example

Here is the path for the NW zone, year 2018, March, first part of month (from day 1 to day 9) :

format: plain


text:
# Transforming the Binomial Theorem into $\sum_{k=1}^n k3^k {n \choose k}$

By setting $x$ equal to the appropriate values in the binomial expansion (or one of its derivatives, etc.), evaluate

$$\sum_{k=1}^n k3^k {n \choose k}$$

Basically this problem is tasking me with taking the Binomial Theorem:

$$(1+x)^n = \sum_{k=0}^n {n \choose k} x^k$$

And performing manipulations until it reaches the given form, in which case a value of $x$ can be determined.

By taking the derivative, we have that:

$$n(1+x)^{n-1} = \sum_{k=0}^n {n \choose k} kx^{k-1}$$

By changing the bounds:

$$n(1+x)^{n-1} = \sum_{k=1}^n {n \choose k-1} (k-1)x^{(k-1)-1}$$

$$n(1+x)^{n-1} = \sum_{k=1}^n {n \choose k-1} (k-1)\frac{x^k}{x^2}$$

This could also become:

$$x^2n(1+x)^{n-1} = \sum_{k=1}^n {n \choose k-1} (k-1)x^k$$

Which is pretty close to the desired form, but I'm not really sure what to do with the $k-1$ term inside ${n \choose k-1}$. I can further simplify by doing:

$$x^2n(1+x)^{n-1} = \sum_{k=1}^n {n \choose k-1} kx^k - x^k$$

And I'm not sure if this is a valid simplification, given the Sigma:

$$x^2n(1+x)^{n-1} + x^k = \sum_{k=1}^n {n \choose k-1} kx^k$$

How can I achieve this desired form? Thanks so much! I appreciate the help!

• I think multiply by $x$ the both sides of $$n(1+x)^{n-1} = \sum_{k=0}^n {n \choose k} kx^{k-1}$$ would work. – Cave Johnson Sep 20 '16 at 5:34
• A combinatorial way to get this through is by using $k\binom nk = n \binom{n-1}{k-1}$. – Sungjin Kim Sep 20 '16 at 5:58

Evaluating the expression at $x=3$ gives the result.
Consider $$\sum_{k=1}^n k {n \choose k}x^k=x\sum_{k=1}^n k {n \choose k}x^{k-1}=x \left(\sum_{k=1}^n {n \choose k}x^{k} \right)'=x\left((1+x)^n-1\right))'=nx(1+x)^{n-1}$$


text:
Opened 2 years ago

Closed 19 months ago

# Fix moebius_transform, midpoint and perpendicular_bisector

Reported by: Owned by: gh-kliem major sage-9.4 geometry hyperbolic geodesic Samuel Lelièvre, Michael Orlitzky, Travis Scrimshaw, Greg Laun, Javier Honrubia González Travis Scrimshaw Samuel Lelièvre N/A 234604b 234604b9faddb714d10b588325f258a0241304e2 #29962

We fix several related issues in hyperbolic geometry:

• moebius_transform wrong in the orientation-reversing (negative determinant) case
• midpoint and perpendicular_bisector gave different results for (a, b) and (b, a)

Fuzzing geometry/hyperbolic_space/hyperbolic_geodesic.py doctests uncovered these issues. See #29935, #29962, #29963.

UHP = HyperbolicPlane().UHP()
sage: g = UHP.random_geodesic()   # Good one
sage: g.start(), g.end()
(Point in UHP -9.26728445125634 + 7.15068223909426*I,
Point in UHP -8.57515638592863 + 0.982992065975858*I)
sage: g = UHP.random_geodesic()   # Bad one
sage: g.start(), g.end()
(Point in UHP -4.92591958004554 + 7.81075974592370*I,
Point in UHP -8.44416583993011 + 3.87025183089797*I)

### comment:1 Changed 2 years ago by gh-kliem

Authors: → Jonathan Kliem → public/29936 → 5283dc49042f5bfd45e2cfa92c567e6843b22f6d

The first thing is to use the flag # abs tol to obtain more meaningful doctest failures at random states. I get the following (not all during the same run):

File "src/sage/geometry/hyperbolic_space/hyperbolic_geodesic.py", line 912, in sage.geometry.hyperbolic_space.hyperbolic_geodesic.HyperbolicGeodesic.perpendicular_bisector
Failed example:
abs(h.intersection(g)[0].coordinates() - g.midpoint().coordinates())  # abs tol 1e-9
Expected:
0
Got:
0.309903204800636
Tolerance exceeded:
0 vs 0.309903204800636, tolerance 4e-1 > 1e-9
**********************************************************************
File "src/sage/geometry/hyperbolic_space/hyperbolic_geodesic.py", line 1376, in sage.geometry.hyperbolic_space.hyperbolic_geodesic.HyperbolicGeodesicUHP.perpendicular_bisector
Failed example:
abs(c(g.intersection(h)[0]) - c(g.midpoint()))  # abs tol 1e-9
Expected:
0
Got:
9.74965482957673
Tolerance exceeded:
0 vs 9.74965482957673, tolerance 1e1 > 1e-9

**********************************************************************
File "src/sage/geometry/hyperbolic_space/hyperbolic_geodesic.py", line 1914, in sage.geometry.hyperbolic_space.hyperbolic_geodesic.HyperbolicGeodesicUHP._crossratio_matrix
Failed example:
abs(moebius_transform(A, p1))  # abs tol 1e-9
Expected:
0
Got:
1.82859072578637
Tolerance exceeded:
0 vs 1.82859072578637, tolerance 2e0 > 1e-9
**********************************************************************
File "src/sage/geometry/hyperbolic_space/hyperbolic_geodesic.py", line 1916, in sage.geometry.hyperbolic_space.hyperbolic_geodesic.HyperbolicGeodesicUHP._crossratio_matrix
Failed example:
abs(moebius_transform(A, p2) - 1)  # abs tol 1e-9
Expected:
0
Got:
0.619923876802936
Tolerance exceeded:
0 vs 0.619923876802936, tolerance 7e-1 > 1e-9

This tells me that the preciseness that is claimed with the doctests is far from valid.

New commits:

 23ed583 fix doctest in hyperbolic_space/hyperbolic_point 5283dc4 use abs tol flag

### comment:2 Changed 2 years ago by gh-kliem

I know very little about numerics. From what I remember it makes sense to consider the relative error as well:

sage: from sage.geometry.hyperbolic_space.hyperbolic_isometry import moebius_transform
sage: from sage.geometry.hyperbolic_space.hyperbolic_geodesic import HyperbolicGeodesicUHP
sage: UHP = HyperbolicPlane().UHP()
sage: def foo():
....:     (p1, p2, p3) = [UHP.random_point().coordinates() for k in range(3)]
....:     A = HyperbolicGeodesicUHP._crossratio_matrix(p1, p2, p3)
....:     a = abs(moebius_transform(A, p1))
....:     b = abs((moebius_transform(A, p2) - 1))
....:     sum_of_norms = A.norm() + p1.norm() + p2.norm() + p3.norm()
....:     return min(a, a/sum_of_norms), min(b, b/sum_of_norms)
....:
sage: max(foo()[1] for _ in range(1000))
0.157952578757514
sage: max(foo()[0] for _ in range(1000))
0.0615941849084595

The situation is far worse with the other two tests, which are essentially the same, if I understand correctly:

sage: def foo():
....:     g = HyperbolicPlane().PD().random_geodesic()
....:     h = g.perpendicular_bisector()
....:     error = abs(h.intersection(g)[0].coordinates() - g.midpoint().coordinates())
....:     sum_of_norms = h.intersection(g)[0].coordinates().norm() + g.midpoint().coordinates().norm()
....:     return min(error, error/sum_of_norms)
....:
sage: max(foo() for _ in range(10))
0.908342722333419

### comment:3 Changed 2 years ago by git

Commit: 5283dc49042f5bfd45e2cfa92c567e6843b22f6d → 7b244c03bb763bde8a3b95fa2ce5c129562c3260

Branch pushed to git repo; I updated commit sha1. New commits:

 7b244c0 modify doctests to the extend that they hold with fuzz

### comment:4 Changed 2 years ago by gh-kliem

Description: modified (diff) hyperbolic geodesic hyperbolic point added new → needs_review Fix precicion errors in geometry → Fix wrong precicion claims in geometry doctests

I tested 10 times and not a single test failed. I hope that is good enough.

### comment:5 Changed 2 years ago by Samuel Lelièvre

Cc: Samuel Lelièvre added modified (diff) Fix wrong precicion claims in geometry doctests → Fix wrong precision claims in geometry doctests

### comment:6 follow-up:  9 Changed 2 years ago by Michael Orlitzky

Cc: Michael Orlitzky Travis Scrimshaw Greg Laun Javier Honrubia González added

I've added a few people to CC who may know how these tests are supposed to work. Those errors look pretty large to me.

For something less substantial: I personally always try to limit my use of abs tol to the TESTS block (as opposed to EXAMPLES). The parsing of that comment is an internal detail of our doctest framework, and users who see it in the reference manual generally won't know what it means. Even those users who do understand it have to do a mental calculation when running the example to see if their output matches what is expected. Maybe you agree?

### comment:7 Changed 2 years ago by gh-kliem

Dependencies: → #29962

### comment:8 Changed 2 years ago by gh-kliem

Branch: public/29936 → public/29936-reb 7b244c03bb763bde8a3b95fa2ce5c129562c3260 → 8292b04158fcc2f3e8fed87670026cbdfb418744

Theoretically, there is not need to base this on top of #29962. But basically #29962 discovers those problems. So the doctest works fine until you test it on top of #29962 with a different random seed than 0.

Last 10 new commits:

 da1c6be start from a "random" random seed for doctesting b7b836d make random seed reproducible eedbe5e document random_seed 998b1b9 default random seed 0 for now 1d7b00e dash instead of underscore for command line options b31e2d5 Merge branch 'public/29962' of git://trac.sagemath.org/sage into public/29962-reb 2f30dd9 small fixes b62f781 doctests do not start from a random seed by default yet 1d99129 fix merge conflict 8292b04 Merge branch 'public/29962-reb2' of git://trac.sagemath.org/sage into public/29936-reb

### comment:9 in reply to:  6 ; follow-up:  10 Changed 2 years ago by gh-kliem

I've added a few people to CC who may know how these tests are supposed to work. Those errors look pretty large to me.

I agree. That's why I opened #29939 to fix this.

For something less substantial: I personally always try to limit my use of abs tol to the TESTS block (as opposed to EXAMPLES). The parsing of that comment is an internal detail of our doctest framework, and users who see it in the reference manual generally won't know what it means. Even those users who do understand it have to do a mental calculation when running the example to see if their output matches what is expected. Maybe you agree?

I agree. I moved that stuff to TESTS. Especially with the modified doctests, the tests are really hard to read. But I would argue that for some random stuff you cannot expect a good absolute error, but only a good relative error (and the minimum is needed, because you don't want to divide by zero).

### comment:10 in reply to:  9 ; follow-ups:  11  12 Changed 2 years ago by Travis Scrimshaw

I've added a few people to CC who may know how these tests are supposed to work. Those errors look pretty large to me.

I agree. That's why I opened #29939 to fix this.

It looks like you are comparing using relative tolerance, which if the values are close to 0, could cause large variations. Since you are taking things to be more random, it is quite possible you could divide by something close to 0. So I am not sure this is a bug as instead it could a problem that you have introduced by changing the doctests.

For something less substantial: I personally always try to limit my use of abs tol to the TESTS block (as opposed to EXAMPLES). The parsing of that comment is an internal detail of our doctest framework, and users who see it in the reference manual generally won't know what it means. Even those users who do understand it have to do a mental calculation when running the example to see if their output matches what is expected. Maybe you agree?

I agree. I moved that stuff to TESTS. Especially with the modified doctests, the tests are really hard to read.

I disagree. The problem is numerics and working with inexact fields. You cannot get around this. I don't exactly understand what your modified doctests are doing now other than they are comparing something relatively rather than absolutely.

But I would argue that for some random stuff you cannot expect a good absolute error, but only a good relative error (and the minimum is needed, because you don't want to divide by zero).

You have two competing issues: large values want relative error, small values want absolute error. You have too much randomness now to deal with.

### comment:11 in reply to:  10 Changed 2 years ago by Michael Orlitzky

It looks like you are comparing using relative tolerance, which if the values are close to 0, could cause large variations. Since you are taking things to be more random, it is quite possible you could divide by something close to 0. So I am not sure this is a bug as instead it could a problem that you have introduced by changing the doctests.

The original tests also fail if you introduce any randomness at all. I'm mainly concerned about the errors that Jonathan noticed right away, e.g.

File "src/sage/geometry/hyperbolic_space/hyperbolic_geodesic.py", line 1914, in sage.geometry.hyperbolic_space.hyperbolic_geodesic.HyperbolicGeodesicUHP._crossratio_matrix
Failed example:
abs(moebius_transform(A, p1))  # abs tol 1e-9
Expected:
0
Got:
1.82859072578637
Tolerance exceeded:
0 vs 1.82859072578637, tolerance 2e0 > 1e-9

If you have a method that's supposed to return zero but instead returns two, something other than numerical noise might be to blame. Can that test be re-run with a failing random seed, to see what a failing example looks like?

### comment:12 in reply to:  10 Changed 2 years ago by gh-kliem

I've added a few people to CC who may know how these tests are supposed to work. Those errors look pretty large to me.

I agree. That's why I opened #29939 to fix this.

It looks like you are comparing using relative tolerance, which if the values are close to 0, could cause large variations. Since you are taking things to be more random, it is quite possible you could divide by something close to 0. So I am not sure this is a bug as instead it could a problem that you have introduced by changing the doctests.

For something less substantial: I personally always try to limit my use of abs tol to the TESTS block (as opposed to EXAMPLES). The parsing of that comment is an internal detail of our doctest framework, and users who see it in the reference manual generally won't know what it means. Even those users who do understand it have to do a mental calculation when running the example to see if their output matches what is expected. Maybe you agree?

I agree. I moved that stuff to TESTS. Especially with the modified doctests, the tests are really hard to read.

I disagree. The problem is numerics and working with inexact fields. You cannot get around this. I don't exactly understand what your modified doctests are doing now other than they are comparing something relatively rather than absolutely.

But I would argue that for some random stuff you cannot expect a good absolute error, but only a good relative error (and the minimum is needed, because you don't want to divide by zero).

You have two competing issues: large values want relative error, small values want absolute error. You have too much randomness now to deal with.

I'm taking the minimum of the absolute and the relative error, because I want to be fair. This makes it much better. This is the original test:

sage: from sage.geometry.hyperbolic_space.hyperbolic_isometry import moebius_transform
....: from sage.geometry.hyperbolic_space.hyperbolic_geodesic import HyperbolicGeodesicUHP
....: UHP = HyperbolicPlane().UHP()
....: def foo():
....:     (p1, p2, p3) = [UHP.random_point().coordinates() for k in range(3)]
....:     A = HyperbolicGeodesicUHP._crossratio_matrix(p1, p2, p3)
....:     a = abs(moebius_transform(A, p1))
....:     b = abs((moebius_transform(A, p2) - 1))
....:     return (a,b)
....:
sage: max(foo()[1] for _ in range(1000))
19.6483996640312
sage: max(foo()[0] for _ in range(1000))
76.0110826766808

Here are examples that perform bad. I hope you see, why I chose to change the doctest to minimum of absolute and relative norm:

sage: (p1, p2, p3) = (-2.29920829511711 + 5.52937627459424*I, -2.37141010564321 + 5.57310680132109*I, 8.28221981185745 + 2.99165901130902*I)
sage: A = HyperbolicGeodesicUHP._crossratio_matrix(p1, p2, p3)
sage: a = abs(moebius_transform(A, p1)); a
105.706157025922
sage: b = abs((moebius_transform(A, p2) - 1)); b
105.113014884487
sage: sum_of_norms = A.norm() + p1.norm() + p2.norm() + p3.norm(); sum_of_norms
216.645504358830

### comment:13 Changed 2 years ago by gh-kliem

Given those values, I think the original doctests are unacceptable. We know those exactness claims are wrong. Even if it succeeds with random seed 0, we shouldn't leave it as it is.

### comment:14 follow-up:  20 Changed 2 years ago by Travis Scrimshaw

So I agree with changing the test in hyperbolic_point.py: The equality test is perhaps a bit too narrow and needs to be expanded to allow for some fuzziness (up to some precision set globally).

For the issue in the geodesic, the mathematics is correct:

sage: var('a0,a1,a2,b0,b1,b2')
(a0, a1, a2, b0, b1, b2)
sage: p0 = a0+b0*I
sage: p1 = a1+b1*I
sage: p2 = a2+b2*I
sage: p0 = a0+b0*I
sage: p1 = a1+b1*I
sage: p2 = a2+b2*I
sage: A = HyperbolicGeodesicUHP._crossratio_matrix(p0, p1, p2)
sage: moebius_transform(A, p0)
0
sage: moebius_transform(A, p1).factor()
1
sage: moebius_transform(A, p2)
+Infinity

So I think the issue comes from this in the moebius_transform function:

        if a * d - b * c < 0:
w = z.conjugate()  # Reverses orientation
else:
w = z

At least, I only seem to notice it when the determinant of A has a negative real value. So if I remove the conjugate(), then the issue appears to be fixed.

The problem is not the doctests, which are correct and meaningful (testing the three points are indeed mapped to 0, 1, oo in the UHP model). There is an honest bug and blaming the doctests means you have actually been trying to cover up a bug.

Last edited 2 years ago by Travis Scrimshaw (previous) (diff)

### comment:16 follow-up:  18 Changed 2 years ago by Travis Scrimshaw

Note that removing the conjugate() makes the test RP*gP == gP in HyperbolicGeodesic.reflection_involution() fail.

I still don't know what is going on with the perpendicular bisector.

### comment:17 Changed 2 years ago by Travis Scrimshaw

With the bisector, sometimes it is not intersecting the geodesic (but the completed versions do intersect perpendicularly). So that is why the test is failing, but I don't know why it is doing this.

### comment:18 in reply to:  16 Changed 2 years ago by gh-kliem

Note that removing the conjugate() makes the test RP*gP == gP in HyperbolicGeodesic.reflection_involution() fail.

I still don't know what is going on with the perpendicular bisector.

You're right. It is a sign issue and not a precision issue:

....: from sage.geometry.hyperbolic_space.hyperbolic_geodesic import HyperbolicGeodesicUHP
....: UHP = HyperbolicPlane().UHP()
....: def foo():
....:     (p1, p2, p3) = [UHP.random_point().coordinates() for k in range(3)]
....:     A = HyperbolicGeodesicUHP._crossratio_matrix(p1, p2, p3)
....:     a1 = abs(moebius_transform(A, p1))
....:     a2 = abs(moebius_transform(A, p1.conjugate()))
....:     a = min(a1, a2)
....:     b1 = abs((moebius_transform(A, p2) - 1))
....:     b2 = abs((moebius_transform(A, p2.conjugate()) - 1))
....:     b = min(b1, b2)
....:     sum_of_norms = A.norm() + p1.norm() + p2.norm() + p3.norm()
....:     return min(a, a/sum_of_norms), min(b, b/sum_of_norms)
sage: max(foo()[1] for _ in range(1000))
3.32270874593069e-17
sage: max(foo()[0] for _ in range(1000))
0.000000000000000

If you return just a, b (the absolute errors), you get:

sage: max(foo()[1] for _ in range(1000))
2.79547014692442e-15
sage: max(foo()[0] for _ in range(1000))
0.000000000000000

I guess the absolute error is well again, because The points are uniformly distributed over the rectangle [-10, 10] \times [0, 10i].

Version 0, edited 2 years ago by gh-kliem (next)

### comment:19 Changed 2 years ago by gh-kliem

So the original doctests are fine up to a sign issue.

### comment:20 in reply to:  14 Changed 2 years ago by gh-kliem

So I agree with changing the test in hyperbolic_point.py: The equality test is perhaps a bit too narrow and needs to be expanded to allow for some fuzziness (up to some precision set globally).

For the issue in the geodesic, the mathematics is correct:

sage: var('a0,a1,a2,b0,b1,b2')
(a0, a1, a2, b0, b1, b2)
sage: p0 = a0+b0*I
sage: p1 = a1+b1*I
sage: p2 = a2+b2*I
sage: p0 = a0+b0*I
sage: p1 = a1+b1*I
sage: p2 = a2+b2*I
sage: A = HyperbolicGeodesicUHP._crossratio_matrix(p0, p1, p2)
sage: moebius_transform(A, p0)
0
sage: moebius_transform(A, p1).factor()
1
sage: moebius_transform(A, p2)
+Infinity

So I think the issue comes from this in the moebius_transform function:

        if a * d - b * c < 0:
w = z.conjugate()  # Reverses orientation
else:
w = z

At least, I only seem to notice it when the determinant of A has a negative real value. So if I remove the conjugate(), then the issue appears to be fixed.

The problem is not the doctests, which are correct and meaningful (testing the three points are indeed mapped to 0, 1, oo in the UHP model). There is an honest bug and blaming the doctests means you have actually been trying to cover up a bug.

I opened #29939 to fix this problem. But if we can fix this here that is fine with me to. If it would have been a precision problem, I don't think I would have been able to fix this. So this is why I opened a separate ticket.

Anyway, I'm glad you figured out the actual problem.

### comment:21 Changed 2 years ago by Travis Scrimshaw

Branch: public/29936-reb → public/geometry/fix_hyperbolic_plane-29936 8292b04158fcc2f3e8fed87670026cbdfb418744 → 2028478e2e1ffd9294b7a5b0e96626479ce7f546 needs_review → needs_work

So the issue with the perp bisector comes from this:

sage: g = UHP.random_geodesic()   # Good one
sage: g.start(), g.end()
(Point in UHP -9.26728445125634 + 7.15068223909426*I,
Point in UHP -8.57515638592863 + 0.982992065975858*I)
sage: g = UHP.random_geodesic()   # Bad one
sage: g.start(), g.end()
(Point in UHP -4.92591958004554 + 7.81075974592370*I,
Point in UHP -8.44416583993011 + 3.87025183089797*I)

In particular, the completed geodesics always have the endpoints in order. I am not sure if this is the best fix, but it does fix that issue.

However, I don't yet understand enough of the mathematics to fix the issue with the crossratio matrix.

New commits:

 2028478 Fixing perp bisector when coordinates out of order.

### comment:22 Changed 2 years ago by gh-kliem

Authors: Jonathan Kliem modified (diff) point removed Fix wrong precision claims in geometry doctests → Sign error in hyperbolic geodesic

### comment:23 Changed 2 years ago by Samuel Lelièvre

I would rewrite the test along these lines:

+        Check the result is independent of the order::
+
+            sage: def works_both_ways(a, b):
+            ....:     UHP = HyperbolicPlane().UHP()
+            ....:     g = UHP.get_geodesic(a, b)
+            ....:     h = UHP.get_geodesic(b, a)
+            ....:     p = g.perpendicular_bisector()
+            ....:     q = h.perpendicular_bisector()
+            ....:     c = lambda x: x.coordinates()
+            ....:     assert bool(c(g.intersection(p)[0]) - c(g.midpoint()) < 1e-9)
+            ....:     assert bool(c(h.intersection(q)[0]) - c(g.midpoint()) < 1e-9)
+            sage: works_both_ways(1 + I, 2 + 0.5*I)
+            sage: works_both_ways(2 + I, 2 + 0.5*I)

### comment:24 Changed 2 years ago by Travis Scrimshaw

That is a better way to do the test. I will change that next chance I get (although it might not be until the end of the week).

### comment:25 Changed 2 years ago by Samuel Lelièvre

Authors: → Travis Scrimshaw → Samuel Lelièvre

Some more suggestions.

Add trac ticket number to test:

+        Check the result is independent of the order (:trac:29936)::

Rephrase comment?

-        # Since the complete geodesic p1 -> p2 always returns p1 < p2,
-        #   we need to swap start and end when that happens
+        # Completed geodesics always have endpoints in order,
+        # so we swap start and end if needed.

Define S immediately after the swap, since that's where .complete() happens.

+        S = self.complete()._to_std_geod(start)
d = self._model._dist_points(start, self._end.coordinates()) / 2
-        S = self.complete()._to_std_geod(start)

Avoid detour to the symbolic ring.

-        s2 = sqrt(2) * 0.5
+        s2 = sqrt(0.5)

### comment:26 Changed 2 years ago by git

Branch pushed to git repo; I updated commit sha1. New commits:

 ad8102e Addressing reviewer comments.

### comment:27 Changed 2 years ago by Travis Scrimshaw

I managed to find a bit of time today. Here are the changes you requested although I used a different version of the comment.

Now to figure out how to fix the issue in comment:14 as some parts of the code that use moebius_transformation seem to use that conjugation...

### comment:28 Changed 2 years ago by Matthias Köppe

Milestone: sage-9.2 → sage-9.3

### comment:29 Changed 22 months ago by Matthias Köppe

Milestone: sage-9.3 → sage-9.4

Setting new milestone based on a cursory review of ticket status, priority, and last modification date.

### comment:30 Changed 22 months ago by Samuel Lelièvre

The patchbot's pyflakes plugin complains that moebius_transform is undefined.

### comment:31 Changed 20 months ago by git

Branch pushed to git repo; I updated commit sha1. This was a forced push. New commits:

 3e6410b Fixing perp bisector when coordinates out of order. d0ee202 Addressing reviewer comments. 3ead571 Moebius transformations should not do conjugation.

### comment:32 Changed 20 months ago by Travis Scrimshaw

Status: needs_work → needs_review

Here is a rebased branch that fixes the other problem. It is indeed related with the moebius_tranformation. That is really meant for isomorphisms of CP1 with maps in PGL(2, C), so we should not do complex conjugation. Moreover, we cannot expect the determinant to be a real number unless we choose a good representative, but it is more numerically stable to not do more divisions than necessary. The orientation preserving maps of H2 in the UHP model are for PGL(2, R), so there is a realness assumption of the determinant that we can apply, which means we can compare with 0. Because of this, I moved the orientation reversing code directly into the model computations. This should now fix all of the issues noted above.

### comment:33 Changed 20 months ago by Travis Scrimshaw

We also need the detour to the symbolic ring because sometimes a computation could be wanted in the symbolic ring and it makes checking for valid isometries much more stable.

### comment:34 Changed 20 months ago by Samuel Lelièvre

I missed one thing: to check a complex number is small, use its norm:

             sage: def works_both_ways(a, b):
....:     UHP = HyperbolicPlane().UHP()
....:     g = UHP.get_geodesic(a, b)
....:     h = UHP.get_geodesic(b, a)
....:     p = g.perpendicular_bisector()
....:     q = h.perpendicular_bisector()
....:     c = lambda x: x.coordinates()
-            ....:     assert bool(c(g.intersection(p)[0]) - c(g.midpoint()) < 1e-9)
-            ....:     assert bool(c(h.intersection(q)[0]) - c(g.midpoint()) < 1e-9)
+            ....:     error_ab = norm(c(g.intersection(p)[0]) - c(g.midpoint()))
+            ....:     error_ba = norm(c(h.intersection(q)[0]) - c(h.midpoint()))
+            ....:     assert(bool(error_ab < 1e-9) and bool(error_ba < 1e-9))

Indeed, c(g.intersection(p)[0]) - c(g.midpoint()) is a complex number, and:

sage: I < 1e-9
True

Last edited 20 months ago by Samuel Lelièvre (previous) (diff)

### comment:35 Changed 20 months ago by git

Branch pushed to git repo; I updated commit sha1. New commits:

 8f00631 Better check for trac #29936.

### comment:36 Changed 20 months ago by Travis Scrimshaw

Status: needs_review → needs_work

Good point, which uncovered that there is still an error in the second test...

### comment:37 Changed 20 months ago by Travis Scrimshaw

There is an issue with the midpoints:

sage: UHP = HyperbolicPlane().UHP()
sage: g = UHP.get_geodesic(2+I,2+0.5*I)
sage: h = UHP.get_geodesic(2+0.5*I,2+I)
sage: g.midpoint()
Point in UHP 2.00000000000000 + 1.41421356237309*I
sage: h.midpoint()
Point in UHP 2.00000000000000 + 0.707106781186547*I

### comment:38 Changed 20 months ago by git

Commit: 8f00631626fa636d1979842a1e0b6761f1e2d399 → 767a045b913ac1858d8785f2f78a3aedcc0718fe

Branch pushed to git repo; I updated commit sha1. New commits:

 767a045 Fixing issue with UHP.midpoint() being order dependent.

### comment:39 Changed 20 months ago by Travis Scrimshaw

Status: needs_work → needs_review

This now fixes the issue with midpoint(), which was the same issue as in the perp bisector.

### comment:40 Changed 20 months ago by Samuel Lelièvre

We test a bisector intersects a segment at its midpoint up to rounding error.

Testing that in terms of relative hyperbolic distances would be cleaner.

Proposed replacement for the works_both_ways test:

-            sage: def works_both_ways(a, b):
-            ....:     UHP = HyperbolicPlane().UHP()
-            ....:     g = UHP.get_geodesic(a, b)
-            ....:     h = UHP.get_geodesic(b, a)
-            ....:     p = g.perpendicular_bisector()
-            ....:     q = h.perpendicular_bisector()
-            ....:     c = lambda x: x.coordinates()
-            ....:     error_ab = norm(c(g.intersection(p)[0]) - c(g.midpoint()))
-            ....:     error_ba = norm(c(h.intersection(q)[0]) - c(h.midpoint()))
-            ....:     assert(bool(error_ab < 1e-9) and bool(error_ba < 1e-9)), (error_ab, error_ba)
-            sage: works_both_ways(1 + I, 2 + 0.5*I)
-            sage: works_both_ways(2 + I, 2 + 0.5*I)
+            sage: def bisector_gets_midpoint(a, b):
+            ....:     UHP = HyperbolicPlane().UHP()
+            ....:     g = UHP.get_geodesic(a, b)
+            ....:     p = g.perpendicular_bisector()
+            ....:     x, m = g.intersection(p)[0]), g.midpoint()
+            ....:     return bool(x.dist(m) < 1e-9 * a.dist(b))
+            sage: a, b, c = 1 + I, 2 + I, 2 + 0.5*I
+            sage: pairs = [(a, b), (b, a), (a, c), (c, a), (b, c), (c, b)]
+            sage: all(bisector_gets_midpoint(x, y) for x, y in pairs)

This also seems more readable to me. Sorry for the slow iterations.

### comment:41 Changed 20 months ago by git

Commit: 767a045b913ac1858d8785f2f78a3aedcc0718fe → bd2d756c73c0f6d2f9677dfec9338752fcaeb6c8

Branch pushed to git repo; I updated commit sha1. New commits:

 bd2d756 Doctest to check the distance in the hyperbolic metric.

### comment:42 follow-up:  44 Changed 20 months ago by Travis Scrimshaw

I agree that that version is more readable. I tweaked the input points to have floating point numbers as otherwise the code wants to do a really horrible (but exact) symbolic computation. I also made the distance check absolute instead of relative since they should be the same point.

### comment:43 Changed 20 months ago by Travis Scrimshaw

The gap between iterations is no problem. Thank you for taking the time to review this.

### comment:44 in reply to:  42 Changed 20 months ago by Samuel Lelièvre

made the distance check absolute instead of relative since they should be the same point.

For short segments (say of length 1e-12) we might prefer relative, but the current doctest uses segments of length on the order of one anyway, so I don't mind.

-            sage: a, b, c = 1.0 + I, 2.0 + I, 2.0 + 0.5*I
-            sage: pairs = [(a, b), (b, a), (a, c), (c, a), (b, c), (c, b)]
-            sage: all(bisector_gets_midpoint(x, y) for x, y in pairs)
+            sage: c, d, e = CC(1, 1), CC(2, 1), CC(2, 0.5)
+            sage: pairs = [(c, d), (d, c), (c, e), (e, c), (d, e), (e, d)]
+            sage: all(bisector_gets_midpoint(a, b) for a, b in pairs)

In hyperbolic_isometry.py, pyflakes complains: 'sage.rings.all.RR' imported but unused.

### comment:45 Changed 20 months ago by Samuel Lelièvre

Description: modified (diff) Sign error in hyperbolic geodesic → Fix moebius_transform, midpoint and perpendicular_bisector

### comment:46 Changed 20 months ago by Travis Scrimshaw

I will make that change tomorrow.

Ah. right, I forgot to take care of that pyflakes issue...

### comment:47 Changed 20 months ago by git

Branch pushed to git repo; I updated commit sha1. New commits:

 234604b Last details of #29936.

Fixed.

Green patchbot.

### comment:50 Changed 19 months ago by Samuel Lelièvre

Status: needs_review → positive_review

Good to see these issues solved.

### comment:51 Changed 19 months ago by Travis Scrimshaw

Thank you for the review.

At some point, when I have more free time, I want to implement higher dimensional hyperbolic space. Unfortunately other directly-applicable-to-my-research-code (and paper writing) takes priority...

### comment:52 Changed 19 months ago by Volker Braun

Branch: public/geometry/fix_hyperbolic_plane-29936 → 234604b9faddb714d10b588325f258a0241304e2 → fixed positive_review → closed
Note: See TracTickets for help on using tickets.


text:
Integrate Nuclino with OneDrive

Who can use this feature?

🔐 All team members can configure this integration.

⭐ Available on all plans.

If you use OneDrive, you can easily link to your files from Nuclino, keeping all your content synced and organized. After enabling the OneDrive integration, copying a link to a file stored in your OneDrive and pasting it inline will automatically fetch the title of the file.

To enable the OneDrive integration

This integration needs to be enabled by each user individually by following these steps:

1. Open your Team settings by opening the main menu in the top left corner of the app, clicking on your team name, and then choosing Team settings.

2. Go to the Apps & Integrations section and click on the toggle next to the OneDrive entry.

Note: If you'd like to paste the original file URL as is, without fetching the file title, use the Undo shortcut (Ctrl/Cmd + Z) after pasting the link.


text:
Calculates the mixing parameter $$\eta_j$$ from the scales of the Gaussian/Lorentzian components.

getVoigtParam(scale_G, scale_L)

## Arguments

scale_G Vector of standard deviations $$\sigma_j$$ of the Gaussian components. Vector of scale parameters $$\phi_j$$ of the Lorentzian components.

## Value

The Voigt mixing weights for each peak, between 0 (Gaussian) and 1 (Lorentzian).

## Details

First, calculate a polynomial average of the scale parameters according to the approximation of Thompson et al. (1987): $$f_{G,L} = (\sigma_j^5 + 2.69\sigma_j^4\phi_j + 2.42\sigma_j^3\phi_j^2 + 4.47\sigma_j^2\phi_j^3 + 0.07\sigma_j\phi_j^4 + \phi_j^5)^{1/5}$$

Then the Voigt mixing parameter $$\eta_j$$ is defined as: $$\eta_j = 1.36\frac{\phi_j}{f_{G,L}} - 0.47(\frac{\phi_j}{f_{G,L}})^2 + 0.11(\frac{\phi_j}{f_{G,L}})^3$$

## References

Thompson, Cox & Hastings (1987) "Rietveld refinement of Debye--Scherrer synchrotron X-ray data from $$Al_2 O_3$$," J. Appl. Crystallogr. 20(2): 79--83, DOI: 10.1107/S0021889887087090