liquid_feedback_core

changeset 305:a839e7efde9f v2.0.12

Removed unwanted (and broken) tie-breaking by approval rate
author jbe
date Fri Oct 05 11:41:51 2012 +0200 (2012-10-05)
parents e818f83e133b
children d8b455e3a9be
files core.sql update/core-update.v2.0.11-v2.0.12.sql
line diff
     1.1 --- a/core.sql	Wed Jun 20 21:22:02 2012 +0200
     1.2 +++ b/core.sql	Fri Oct 05 11:41:51 2012 +0200
     1.3 @@ -7,7 +7,7 @@
     1.4  BEGIN;
     1.5  
     1.6  CREATE VIEW "liquid_feedback_version" AS
     1.7 -  SELECT * FROM (VALUES ('2.0.11', 2, 0, 11))
     1.8 +  SELECT * FROM (VALUES ('2.0.12', 2, 0, 12))
     1.9    AS "subquery"("string", "major", "minor", "revision");
    1.10  
    1.11  
    1.12 @@ -2796,37 +2796,6 @@
    1.13  
    1.14  
    1.15  
    1.16 -------------------------------
    1.17 --- Comparison by vote count --
    1.18 -------------------------------
    1.19 -
    1.20 -CREATE FUNCTION "vote_ratio"
    1.21 -  ( "positive_votes_p" "initiative"."positive_votes"%TYPE,
    1.22 -    "negative_votes_p" "initiative"."negative_votes"%TYPE )
    1.23 -  RETURNS FLOAT8
    1.24 -  LANGUAGE 'plpgsql' STABLE AS $$
    1.25 -    BEGIN
    1.26 -      IF "positive_votes_p" > 0 AND "negative_votes_p" > 0 THEN
    1.27 -        RETURN
    1.28 -          "positive_votes_p"::FLOAT8 /
    1.29 -          ("positive_votes_p" + "negative_votes_p")::FLOAT8;
    1.30 -      ELSIF "positive_votes_p" > 0 THEN
    1.31 -        RETURN "positive_votes_p";
    1.32 -      ELSIF "negative_votes_p" > 0 THEN
    1.33 -        RETURN 1 - "negative_votes_p";
    1.34 -      ELSE
    1.35 -        RETURN 0.5;
    1.36 -      END IF;
    1.37 -    END;
    1.38 -  $$;
    1.39 -
    1.40 -COMMENT ON FUNCTION "vote_ratio"
    1.41 -  ( "initiative"."positive_votes"%TYPE,
    1.42 -    "initiative"."negative_votes"%TYPE )
    1.43 -  IS 'Returns a number, which can be used for comparison of initiatives based on count of approvals and disapprovals. Greater numbers indicate a better result. This function is NOT injective.';
    1.44 -
    1.45 -
    1.46 -
    1.47  ------------------------------------------------
    1.48  -- Locking for snapshots and voting procedure --
    1.49  ------------------------------------------------
    1.50 @@ -4054,7 +4023,6 @@
    1.51            WHERE "issue_id" = "issue_id_p" AND "eligible"
    1.52            ORDER BY
    1.53              "schulze_rank",
    1.54 -            "vote_ratio"("positive_votes", "negative_votes"),
    1.55              "id"
    1.56            LIMIT 1
    1.57          ) AS "subquery"
    1.58 @@ -4069,7 +4037,6 @@
    1.59            "winner" DESC,
    1.60            "eligible" DESC,
    1.61            "schulze_rank",
    1.62 -          "vote_ratio"("positive_votes", "negative_votes"),
    1.63            "id"
    1.64        LOOP
    1.65          UPDATE "initiative" SET "rank" = "rank_v"
     2.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     2.2 +++ b/update/core-update.v2.0.11-v2.0.12.sql	Fri Oct 05 11:41:51 2012 +0200
     2.3 @@ -0,0 +1,309 @@
     2.4 +BEGIN;
     2.5 +
     2.6 +CREATE OR REPLACE VIEW "liquid_feedback_version" AS
     2.7 +  SELECT * FROM (VALUES ('2.0.12', 2, 0, 12))
     2.8 +  AS "subquery"("string", "major", "minor", "revision");
     2.9 +
    2.10 +CREATE OR REPLACE FUNCTION "calculate_ranks"("issue_id_p" "issue"."id"%TYPE)
    2.11 +  RETURNS VOID
    2.12 +  LANGUAGE 'plpgsql' VOLATILE AS $$
    2.13 +    DECLARE
    2.14 +      "issue_row"         "issue"%ROWTYPE;
    2.15 +      "policy_row"        "policy"%ROWTYPE;
    2.16 +      "dimension_v"       INTEGER;
    2.17 +      "vote_matrix"       INT4[][];  -- absolute votes
    2.18 +      "matrix"            INT8[][];  -- defeat strength / best paths
    2.19 +      "i"                 INTEGER;
    2.20 +      "j"                 INTEGER;
    2.21 +      "k"                 INTEGER;
    2.22 +      "battle_row"        "battle"%ROWTYPE;
    2.23 +      "rank_ary"          INT4[];
    2.24 +      "rank_v"            INT4;
    2.25 +      "done_v"            INTEGER;
    2.26 +      "winners_ary"       INTEGER[];
    2.27 +      "initiative_id_v"   "initiative"."id"%TYPE;
    2.28 +    BEGIN
    2.29 +      SELECT * INTO "issue_row"
    2.30 +        FROM "issue" WHERE "id" = "issue_id_p"
    2.31 +        FOR UPDATE;
    2.32 +      SELECT * INTO "policy_row"
    2.33 +        FROM "policy" WHERE "id" = "issue_row"."policy_id";
    2.34 +      SELECT count(1) INTO "dimension_v"
    2.35 +        FROM "battle_participant" WHERE "issue_id" = "issue_id_p";
    2.36 +      -- Create "vote_matrix" with absolute number of votes in pairwise
    2.37 +      -- comparison:
    2.38 +      "vote_matrix" := array_fill(NULL::INT4, ARRAY["dimension_v", "dimension_v"]);
    2.39 +      "i" := 1;
    2.40 +      "j" := 2;
    2.41 +      FOR "battle_row" IN
    2.42 +        SELECT * FROM "battle" WHERE "issue_id" = "issue_id_p"
    2.43 +        ORDER BY
    2.44 +        "winning_initiative_id" NULLS LAST,
    2.45 +        "losing_initiative_id" NULLS LAST
    2.46 +      LOOP
    2.47 +        "vote_matrix"["i"]["j"] := "battle_row"."count";
    2.48 +        IF "j" = "dimension_v" THEN
    2.49 +          "i" := "i" + 1;
    2.50 +          "j" := 1;
    2.51 +        ELSE
    2.52 +          "j" := "j" + 1;
    2.53 +          IF "j" = "i" THEN
    2.54 +            "j" := "j" + 1;
    2.55 +          END IF;
    2.56 +        END IF;
    2.57 +      END LOOP;
    2.58 +      IF "i" != "dimension_v" OR "j" != "dimension_v" + 1 THEN
    2.59 +        RAISE EXCEPTION 'Wrong battle count (should not happen)';
    2.60 +      END IF;
    2.61 +      -- Store defeat strengths in "matrix" using "defeat_strength"
    2.62 +      -- function:
    2.63 +      "matrix" := array_fill(NULL::INT8, ARRAY["dimension_v", "dimension_v"]);
    2.64 +      "i" := 1;
    2.65 +      LOOP
    2.66 +        "j" := 1;
    2.67 +        LOOP
    2.68 +          IF "i" != "j" THEN
    2.69 +            "matrix"["i"]["j"] := "defeat_strength"(
    2.70 +              "vote_matrix"["i"]["j"],
    2.71 +              "vote_matrix"["j"]["i"]
    2.72 +            );
    2.73 +          END IF;
    2.74 +          EXIT WHEN "j" = "dimension_v";
    2.75 +          "j" := "j" + 1;
    2.76 +        END LOOP;
    2.77 +        EXIT WHEN "i" = "dimension_v";
    2.78 +        "i" := "i" + 1;
    2.79 +      END LOOP;
    2.80 +      -- Find best paths:
    2.81 +      "i" := 1;
    2.82 +      LOOP
    2.83 +        "j" := 1;
    2.84 +        LOOP
    2.85 +          IF "i" != "j" THEN
    2.86 +            "k" := 1;
    2.87 +            LOOP
    2.88 +              IF "i" != "k" AND "j" != "k" THEN
    2.89 +                IF "matrix"["j"]["i"] < "matrix"["i"]["k"] THEN
    2.90 +                  IF "matrix"["j"]["i"] > "matrix"["j"]["k"] THEN
    2.91 +                    "matrix"["j"]["k"] := "matrix"["j"]["i"];
    2.92 +                  END IF;
    2.93 +                ELSE
    2.94 +                  IF "matrix"["i"]["k"] > "matrix"["j"]["k"] THEN
    2.95 +                    "matrix"["j"]["k"] := "matrix"["i"]["k"];
    2.96 +                  END IF;
    2.97 +                END IF;
    2.98 +              END IF;
    2.99 +              EXIT WHEN "k" = "dimension_v";
   2.100 +              "k" := "k" + 1;
   2.101 +            END LOOP;
   2.102 +          END IF;
   2.103 +          EXIT WHEN "j" = "dimension_v";
   2.104 +          "j" := "j" + 1;
   2.105 +        END LOOP;
   2.106 +        EXIT WHEN "i" = "dimension_v";
   2.107 +        "i" := "i" + 1;
   2.108 +      END LOOP;
   2.109 +      -- Determine order of winners:
   2.110 +      "rank_ary" := array_fill(NULL::INT4, ARRAY["dimension_v"]);
   2.111 +      "rank_v" := 1;
   2.112 +      "done_v" := 0;
   2.113 +      LOOP
   2.114 +        "winners_ary" := '{}';
   2.115 +        "i" := 1;
   2.116 +        LOOP
   2.117 +          IF "rank_ary"["i"] ISNULL THEN
   2.118 +            "j" := 1;
   2.119 +            LOOP
   2.120 +              IF
   2.121 +                "i" != "j" AND
   2.122 +                "rank_ary"["j"] ISNULL AND
   2.123 +                "matrix"["j"]["i"] > "matrix"["i"]["j"]
   2.124 +              THEN
   2.125 +                -- someone else is better
   2.126 +                EXIT;
   2.127 +              END IF;
   2.128 +              IF "j" = "dimension_v" THEN
   2.129 +                -- noone is better
   2.130 +                "winners_ary" := "winners_ary" || "i";
   2.131 +                EXIT;
   2.132 +              END IF;
   2.133 +              "j" := "j" + 1;
   2.134 +            END LOOP;
   2.135 +          END IF;
   2.136 +          EXIT WHEN "i" = "dimension_v";
   2.137 +          "i" := "i" + 1;
   2.138 +        END LOOP;
   2.139 +        "i" := 1;
   2.140 +        LOOP
   2.141 +          "rank_ary"["winners_ary"["i"]] := "rank_v";
   2.142 +          "done_v" := "done_v" + 1;
   2.143 +          EXIT WHEN "i" = array_upper("winners_ary", 1);
   2.144 +          "i" := "i" + 1;
   2.145 +        END LOOP;
   2.146 +        EXIT WHEN "done_v" = "dimension_v";
   2.147 +        "rank_v" := "rank_v" + 1;
   2.148 +      END LOOP;
   2.149 +      -- write preliminary results:
   2.150 +      "i" := 1;
   2.151 +      FOR "initiative_id_v" IN
   2.152 +        SELECT "id" FROM "initiative"
   2.153 +        WHERE "issue_id" = "issue_id_p" AND "admitted"
   2.154 +        ORDER BY "id"
   2.155 +      LOOP
   2.156 +        UPDATE "initiative" SET
   2.157 +          "direct_majority" =
   2.158 +            CASE WHEN "policy_row"."direct_majority_strict" THEN
   2.159 +              "positive_votes" * "policy_row"."direct_majority_den" >
   2.160 +              "policy_row"."direct_majority_num" * ("positive_votes"+"negative_votes")
   2.161 +            ELSE
   2.162 +              "positive_votes" * "policy_row"."direct_majority_den" >=
   2.163 +              "policy_row"."direct_majority_num" * ("positive_votes"+"negative_votes")
   2.164 +            END
   2.165 +            AND "positive_votes" >= "policy_row"."direct_majority_positive"
   2.166 +            AND "issue_row"."voter_count"-"negative_votes" >=
   2.167 +                "policy_row"."direct_majority_non_negative",
   2.168 +            "indirect_majority" =
   2.169 +            CASE WHEN "policy_row"."indirect_majority_strict" THEN
   2.170 +              "positive_votes" * "policy_row"."indirect_majority_den" >
   2.171 +              "policy_row"."indirect_majority_num" * ("positive_votes"+"negative_votes")
   2.172 +            ELSE
   2.173 +              "positive_votes" * "policy_row"."indirect_majority_den" >=
   2.174 +              "policy_row"."indirect_majority_num" * ("positive_votes"+"negative_votes")
   2.175 +            END
   2.176 +            AND "positive_votes" >= "policy_row"."indirect_majority_positive"
   2.177 +            AND "issue_row"."voter_count"-"negative_votes" >=
   2.178 +                "policy_row"."indirect_majority_non_negative",
   2.179 +          "schulze_rank"           = "rank_ary"["i"],
   2.180 +          "better_than_status_quo" = "rank_ary"["i"] < "rank_ary"["dimension_v"],
   2.181 +          "worse_than_status_quo"  = "rank_ary"["i"] > "rank_ary"["dimension_v"],
   2.182 +          "multistage_majority"    = "rank_ary"["i"] >= "rank_ary"["dimension_v"],
   2.183 +          "reverse_beat_path"      = "matrix"["dimension_v"]["i"] >= 0,
   2.184 +          "eligible"               = FALSE,
   2.185 +          "winner"                 = FALSE,
   2.186 +          "rank"                   = NULL  -- NOTE: in cases of manual reset of issue state
   2.187 +          WHERE "id" = "initiative_id_v";
   2.188 +        "i" := "i" + 1;
   2.189 +      END LOOP;
   2.190 +      IF "i" != "dimension_v" THEN
   2.191 +        RAISE EXCEPTION 'Wrong winner count (should not happen)';
   2.192 +      END IF;
   2.193 +      -- take indirect majorities into account:
   2.194 +      LOOP
   2.195 +        UPDATE "initiative" SET "indirect_majority" = TRUE
   2.196 +          FROM (
   2.197 +            SELECT "new_initiative"."id" AS "initiative_id"
   2.198 +            FROM "initiative" "old_initiative"
   2.199 +            JOIN "initiative" "new_initiative"
   2.200 +              ON "new_initiative"."issue_id" = "issue_id_p"
   2.201 +              AND "new_initiative"."indirect_majority" = FALSE
   2.202 +            JOIN "battle" "battle_win"
   2.203 +              ON "battle_win"."issue_id" = "issue_id_p"
   2.204 +              AND "battle_win"."winning_initiative_id" = "new_initiative"."id"
   2.205 +              AND "battle_win"."losing_initiative_id" = "old_initiative"."id"
   2.206 +            JOIN "battle" "battle_lose"
   2.207 +              ON "battle_lose"."issue_id" = "issue_id_p"
   2.208 +              AND "battle_lose"."losing_initiative_id" = "new_initiative"."id"
   2.209 +              AND "battle_lose"."winning_initiative_id" = "old_initiative"."id"
   2.210 +            WHERE "old_initiative"."issue_id" = "issue_id_p"
   2.211 +            AND "old_initiative"."indirect_majority" = TRUE
   2.212 +            AND CASE WHEN "policy_row"."indirect_majority_strict" THEN
   2.213 +              "battle_win"."count" * "policy_row"."indirect_majority_den" >
   2.214 +              "policy_row"."indirect_majority_num" *
   2.215 +              ("battle_win"."count"+"battle_lose"."count")
   2.216 +            ELSE
   2.217 +              "battle_win"."count" * "policy_row"."indirect_majority_den" >=
   2.218 +              "policy_row"."indirect_majority_num" *
   2.219 +              ("battle_win"."count"+"battle_lose"."count")
   2.220 +            END
   2.221 +            AND "battle_win"."count" >= "policy_row"."indirect_majority_positive"
   2.222 +            AND "issue_row"."voter_count"-"battle_lose"."count" >=
   2.223 +                "policy_row"."indirect_majority_non_negative"
   2.224 +          ) AS "subquery"
   2.225 +          WHERE "id" = "subquery"."initiative_id";
   2.226 +        EXIT WHEN NOT FOUND;
   2.227 +      END LOOP;
   2.228 +      -- set "multistage_majority" for remaining matching initiatives:
   2.229 +      UPDATE "initiative" SET "multistage_majority" = TRUE
   2.230 +        FROM (
   2.231 +          SELECT "losing_initiative"."id" AS "initiative_id"
   2.232 +          FROM "initiative" "losing_initiative"
   2.233 +          JOIN "initiative" "winning_initiative"
   2.234 +            ON "winning_initiative"."issue_id" = "issue_id_p"
   2.235 +            AND "winning_initiative"."admitted"
   2.236 +          JOIN "battle" "battle_win"
   2.237 +            ON "battle_win"."issue_id" = "issue_id_p"
   2.238 +            AND "battle_win"."winning_initiative_id" = "winning_initiative"."id"
   2.239 +            AND "battle_win"."losing_initiative_id" = "losing_initiative"."id"
   2.240 +          JOIN "battle" "battle_lose"
   2.241 +            ON "battle_lose"."issue_id" = "issue_id_p"
   2.242 +            AND "battle_lose"."losing_initiative_id" = "winning_initiative"."id"
   2.243 +            AND "battle_lose"."winning_initiative_id" = "losing_initiative"."id"
   2.244 +          WHERE "losing_initiative"."issue_id" = "issue_id_p"
   2.245 +          AND "losing_initiative"."admitted"
   2.246 +          AND "winning_initiative"."schulze_rank" <
   2.247 +              "losing_initiative"."schulze_rank"
   2.248 +          AND "battle_win"."count" > "battle_lose"."count"
   2.249 +          AND (
   2.250 +            "battle_win"."count" > "winning_initiative"."positive_votes" OR
   2.251 +            "battle_lose"."count" < "losing_initiative"."negative_votes" )
   2.252 +        ) AS "subquery"
   2.253 +        WHERE "id" = "subquery"."initiative_id";
   2.254 +      -- mark eligible initiatives:
   2.255 +      UPDATE "initiative" SET "eligible" = TRUE
   2.256 +        WHERE "issue_id" = "issue_id_p"
   2.257 +        AND "initiative"."direct_majority"
   2.258 +        AND "initiative"."indirect_majority"
   2.259 +        AND "initiative"."better_than_status_quo"
   2.260 +        AND (
   2.261 +          "policy_row"."no_multistage_majority" = FALSE OR
   2.262 +          "initiative"."multistage_majority" = FALSE )
   2.263 +        AND (
   2.264 +          "policy_row"."no_reverse_beat_path" = FALSE OR
   2.265 +          "initiative"."reverse_beat_path" = FALSE );
   2.266 +      -- mark final winner:
   2.267 +      UPDATE "initiative" SET "winner" = TRUE
   2.268 +        FROM (
   2.269 +          SELECT "id" AS "initiative_id"
   2.270 +          FROM "initiative"
   2.271 +          WHERE "issue_id" = "issue_id_p" AND "eligible"
   2.272 +          ORDER BY
   2.273 +            "schulze_rank",
   2.274 +            "id"
   2.275 +          LIMIT 1
   2.276 +        ) AS "subquery"
   2.277 +        WHERE "id" = "subquery"."initiative_id";
   2.278 +      -- write (final) ranks:
   2.279 +      "rank_v" := 1;
   2.280 +      FOR "initiative_id_v" IN
   2.281 +        SELECT "id"
   2.282 +        FROM "initiative"
   2.283 +        WHERE "issue_id" = "issue_id_p" AND "admitted"
   2.284 +        ORDER BY
   2.285 +          "winner" DESC,
   2.286 +          "eligible" DESC,
   2.287 +          "schulze_rank",
   2.288 +          "id"
   2.289 +      LOOP
   2.290 +        UPDATE "initiative" SET "rank" = "rank_v"
   2.291 +          WHERE "id" = "initiative_id_v";
   2.292 +        "rank_v" := "rank_v" + 1;
   2.293 +      END LOOP;
   2.294 +      -- set schulze rank of status quo and mark issue as finished:
   2.295 +      UPDATE "issue" SET
   2.296 +        "status_quo_schulze_rank" = "rank_ary"["dimension_v"],
   2.297 +        "state" =
   2.298 +          CASE WHEN EXISTS (
   2.299 +            SELECT NULL FROM "initiative"
   2.300 +            WHERE "issue_id" = "issue_id_p" AND "winner"
   2.301 +          ) THEN
   2.302 +            'finished_with_winner'::"issue_state"
   2.303 +          ELSE
   2.304 +            'finished_without_winner'::"issue_state"
   2.305 +          END,
   2.306 +        "ranks_available" = TRUE
   2.307 +        WHERE "id" = "issue_id_p";
   2.308 +      RETURN;
   2.309 +    END;
   2.310 +  $$;
   2.311 +
   2.312 +COMMIT;

Impressum / About Us