LiamM32 / Eurovision_Condorcet

A program to count votes for the Eurovision Song Contest using a redesigned voting system.
1 stars 2 forks source link

Implicit conversion from float to int #6

Closed julien-boudry closed 1 year ago

julien-boudry commented 1 year ago

When I try to run your project, I was curious about this error:

Deprecated: Implicit conversion from float 0.3333333333333333 to int loses precision in /Users/julien/eur/src/Method/EurovisionSchulze.php on line 30

Having a warning is not a good sign (and can also slower things).

But behind this warning, I found that:

0.2^2;
DEPRECATED  Implicit conversion from float 0.2 to int loses precision.

^ is a bitwise operator in PHP, NOT an arithmetic operator. https://www.php.net/manual/en/language.operators.bitwise.php

The one you looking for seems to be the ** operator. https://www.php.net/manual/en/language.operators.arithmetic.php

Probably the confusion comes from.

LiamM32 commented 1 year ago

Thank you. I wish I noticed this before I made my most recent commits. I don't know how to integrate these changes in a way that shows your contribution in the history.

julien-boudry commented 1 year ago

Don't worry about that ;)

LiamM32 commented 1 year ago

Well, I just made the commits. I've been having a good experience with PHP, but I find this a very bizarre design choice.

LiamM32 commented 1 year ago

I must have done something wrong. It now ends in a 26-way tie.

julien-boudry commented 1 year ago

Well, I just made the commits. I've been having a good experience with PHP, but I find this a very bizarre design choice.

** is a relatively new operator is PHP. Before, It's was only the pow() function (like other language).

Python uses exactly the same operators than PHP here, for both ^ and **. C++ and Rust use ^ in the same way that PHP does (but doesn't seem to have an exponent operator ?) But the new Julia language use ^ for exponent. PHP is relatively conservative here.

julien-boudry commented 1 year ago

I must have done something wrong. It now ends in a 26-way tie.

Made this test: _(here we doing echo and vardump, but we could use a real interactive debugger)

        var_dump($nationalMargins);
        var_dump(array_sum($nationalMargins));

        return array_sum($nationalMargins);

The array_sum is always equal to float(NAN)

The national margin looks like that:

array(38) {
  ["ALB"]=>
  float(305.31773749766074)
  ["ARM"]=>
  float(0)
  ["AUS"]=>
  float(0)
  ["AUT"]=>
  float(568.4815241989093)
  ["AZE"]=>
  float(NAN)
  ["BEL"]=>
  float(637.6405308542155)
  ["CHE"]=>
  float(NAN)
  ["CYP"]=>
  float(190.57487934296086)
  ["CZE"]=>
  float(876.8844445555251)
  ["DEU"]=>
  float(NAN)
  ["DNK"]=>
  float(NAN)
  ["ESP"]=>
  float(2051.6521502635132)
  ["EST"]=>
  float(NAN)
  ["FIN"]=>
  float(NAN)
  ["FRA"]=>
  float(2405.1677042702613)
  ["GBR"]=>
  float(NAN)
  ["GEO"]=>
  float(492.3124763845493)
  ["GRC"]=>
  float(606.183009567419)
  ["HRV"]=>
  float(0)
  ["IRL"]=>
  float(410.1073504344644)
  ["ISL"]=>
  float(NAN)
  ["ISR"]=>
  float(687.7629335222447)
  ["ITA"]=>
  float(1580.9659444864128)
  ["LTU"]=>
  float(NAN)
  ["LVA"]=>
  float(235.19067669207067)
  ["MDA"]=>
  float(282.8879052617626)
  ["MLT"]=>
  float(0)
  ["NLD"]=>
  float(NAN)
  ["NOR"]=>
  float(NAN)
  ["POL"]=>
  float(NAN)
  ["PRT"]=>
  float(NAN)
  ["ROU"]=>
  float(NAN)
  ["SMR"]=>
  float(NAN)
  ["SRB"]=>
  float(0)
  ["SVN"]=>
  float(NAN)
  ["SWE"]=>
  float(NAN)
  ["UKR"]=>
  float(NAN)
  ["WLD"]=>
  float(10914.278247252607)
}

You must find out why NAN is from your formula. And it looks spicy! If needed (better is to use it only if no choice), Condorcet includes brick/maths as a dependency.

julien-boudry commented 1 year ago

As I understand it, using a fractional exponent is a strange thing. PHP accepts it, but the results can be strange, with a lot of Nan. ( https://www.php.net/manual/fr/function.is-nan.php#62504 )

I try same computation in other languages:

Python:

(-15 * 1897426) ** (1/3)
>>> (152.6588687488304+264.41291689896286j)

Julia

julia> (-15 * 1897426) ^ (1/3)
ERROR: DomainError with -2.846139e7:
Exponentiation yielding a complex result requires a complex argument.
Replace x^y with (x+0im)^y, Complex(x)^y, or similar.
Stacktrace:
 [1] throw_exp_domainerror(x::Float64)
   @ Base.Math ./math.jl:37
 [2] ^(x::Float64, y::Float64)
   @ Base.Math ./math.jl:1123
 [3] ^(x::Int64, y::Float64)
   @ Base ./promotion.jl:444
 [4] top-level scope
   @ REPL[2]:1

Javascript (hard to say it: but same as PHP)

(-15 * 1897426) ** (1/3)
>>> NaN 

Rust

use num::pow;

fn main() {
    let x = pow(-15 * 1897426, 1/3);
    println!("x = {}", x);
}

>>> x = 1
// Found that from doc: Note that 0⁰ (pow(0, 0)) returns 1. Mathematically this is undefined.

Ruby

(-15 * 1897426) ** (1/3)
=> 1

Perl:

print ( (-15 * 1897426) ** (1/3) );
=> NaN

C:

#include <stdio.h>
#include <math.h>

int main()
{
    double r;

    r = pow(-15 * 1897426, 1/3);

    printf("%.2lf", r);
}

=> 1

Also, brick/math library doesn't accept float as an exponent, only int, so it raises an error if you try to exponent by 1/3.

So should be a math problem more than a code problem.

LiamM32 commented 1 year ago

Here's a test I tried.

<?php
echo("\n5^(1/2)=". 5**(1/2));
echo("\n8^(2/3)=". 8**(2/3));
echo("\n-8^(2/3)=". (-8)**(2/3));
echo("\n-8^(1/3)=". (-8)**(1/3));

Output:

5^(1/2)=2.2360679774998
8^(2/3)=4
-8^(2/3)=NAN
-8^(1/3)=NAN

It seems like it just doesn't like doing exponentiation on negative numbers.

LiamM32 commented 1 year ago

Here's two lines that I might commit in EurovisionSchulze.php. It solves the problem of PHP not liking exponentiation of negative numbers.

$filteredMargin = $this->filteredPairwise[$country][$iCountry]['win'][$jCountry]-$this->filteredPairwise[$country][$jCountry]['win'][$iCountry];
            $nationalMargins[$country] = ($filteredMargin<=>0)*(abs($filteredMargin) * $contest->populations[$country] )**(1/3);

But here are the end results of some-votes.cvotes using this line: "FIN > ISR = SWE > UKR > NOR > ITA > ARM > ALB > HRV > BEL = FRA > ESP = MDA > CZE > POL > CHE = EST > AUS = CYP > AUT = LTU > SVN > PRT > SRB > GBR > DEU" I find it hard to believe that there would be any ties if the program is working properly. I can see significant margins between the countries that are supposedly tied.

julien-boudry commented 1 year ago

Not a PHP problem, as shown above, all the languages have problems. Rust, Ruby, and C give an absurd result because math is 'undefined', but probably faster for CPU. Python gives incomplete results (that I don't understand). Julia tells us to use complex maths types instead. Perl, Javascript, and PHP give NaN.

Yes, I agree that ties are unlikely. Maybe could you compare this result with the original Schulze method. Then do math step by steps. For that, you can also remove the getStats method for your call. The original will give you the strongest paths $result->getStats();.

LiamM32 commented 1 year ago

I tried an experiment with all countries populations set to 5000. I also got it to output Schulze Margins at the end. Here are the results from some-votes.cvotes:

Results from the first method:
string(153) "FIN > SWE > ISR > NOR > ITA > UKR > BEL = HRV > CZE > ARM > FRA > POL > MDA > EST > CHE > CYP > ALB > AUS = LTU > SVN > AUT = ESP > PRT > SRB > GBR > DEU"

Results from the second method:
string(153) "FIN > SWE > ISR > NOR > ITA > UKR > BEL = HRV > CZE > ARM > FRA > POL > MDA > EST > CHE > CYP > ALB > AUS = LTU > SVN > AUT = ESP > PRT > SRB > GBR > DEU"

Results from the regular Schulze Method:
string(153) "FIN > SWE > ISR > NOR > ITA > UKR > HRV > BEL > ARM > MDA > CZE > ALB > CHE = FRA > CYP > EST > POL > LTU > AUS > SVN > ESP > AUT > PRT > SRB > GBR > DEU"

So there was only one tie in Schulze Margin.

Edit: There was a mistake in the code. The 'second method' is just a repeat of 'Eurovision Schulze'.