liquid_feedback_core

diff update/core-update.v2.2.6-v3.0.1.sql @ 420:044a2b65c707

Merged from v2.2.6: Marked "no_reverse_beat_path" and "no_multistage_majority" as experimental; Disabled "no_reverse_beat_path" by default
author jbe
date Sat Apr 05 23:04:43 2014 +0200 (2014-04-05)
parents update/core-update.v2.2.5-v3.0.0.sql@399dc1a86398
children 34cc98defa8b
line diff
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/update/core-update.v2.2.6-v3.0.1.sql	Sat Apr 05 23:04:43 2014 +0200
     1.3 @@ -0,0 +1,339 @@
     1.4 +BEGIN;
     1.5 +
     1.6 +CREATE OR REPLACE VIEW "liquid_feedback_version" AS
     1.7 +  SELECT * FROM (VALUES ('3.0.1', 3, 0, 1))
     1.8 +  AS "subquery"("string", "major", "minor", "revision");
     1.9 +
    1.10 +CREATE TABLE "issue_order_in_admission_state" (
    1.11 +        "id"                    INT8            PRIMARY KEY, --REFERENCES "issue" ("id") ON DELETE CASCADE ON UPDATE CASCADE,
    1.12 +        "order_in_area"         INT4,
    1.13 +        "order_in_unit"         INT4 );
    1.14 +
    1.15 +COMMENT ON TABLE "issue_order_in_admission_state" IS 'Ordering information for issues that are not stored in the "issue" table to avoid locking of multiple issues at once; Filled/updated by "lf_update_issue_order"';
    1.16 +
    1.17 +COMMENT ON COLUMN "issue_order_in_admission_state"."id"            IS 'References "issue" ("id") but has no referential integrity trigger associated, due to performance/locking issues';
    1.18 +COMMENT ON COLUMN "issue_order_in_admission_state"."order_in_area" IS 'Order of issues in admission state within a single area; NULL values sort last';
    1.19 +COMMENT ON COLUMN "issue_order_in_admission_state"."order_in_unit" IS 'Order of issues in admission state within all areas of a unit; NULL values sort last';
    1.20 +
    1.21 +CREATE VIEW "issue_supporter_in_admission_state" AS
    1.22 +  SELECT DISTINCT
    1.23 +    "area"."unit_id",
    1.24 +    "issue"."area_id",
    1.25 +    "issue"."id" AS "issue_id",
    1.26 +    "supporter"."member_id",
    1.27 +    "direct_interest_snapshot"."weight"
    1.28 +  FROM "issue"
    1.29 +  JOIN "area" ON "area"."id" = "issue"."area_id"
    1.30 +  JOIN "supporter" ON "supporter"."issue_id" = "issue"."id"
    1.31 +  JOIN "direct_interest_snapshot"
    1.32 +    ON  "direct_interest_snapshot"."issue_id" = "issue"."id"
    1.33 +    AND "direct_interest_snapshot"."event" = "issue"."latest_snapshot_event"
    1.34 +    AND "direct_interest_snapshot"."member_id" = "supporter"."member_id"
    1.35 +  WHERE "issue"."state" = 'admission'::"issue_state";
    1.36 +
    1.37 +COMMENT ON VIEW "issue_supporter_in_admission_state" IS 'Helper view for "lf_update_issue_order" to allow a (proportional) ordering of issues within an area';
    1.38 +
    1.39 +COMMENT ON COLUMN "initiative"."schulze_rank"           IS 'Schulze-Ranking';
    1.40 +COMMENT ON COLUMN "initiative"."better_than_status_quo" IS 'TRUE, if initiative has a schulze-ranking better than the status quo';
    1.41 +COMMENT ON COLUMN "initiative"."worse_than_status_quo"  IS 'TRUE, if initiative has a schulze-ranking worse than the status quo (DEPRECATED, since schulze-ranking is unique per issue; use "better_than_status_quo"=FALSE)';
    1.42 +COMMENT ON COLUMN "initiative"."winner"                 IS 'Winner is the "eligible" initiative with best "schulze_rank"';
    1.43 +
    1.44 +CREATE OR REPLACE FUNCTION "calculate_ranks"("issue_id_p" "issue"."id"%TYPE)
    1.45 +  RETURNS VOID
    1.46 +  LANGUAGE 'plpgsql' VOLATILE AS $$
    1.47 +    DECLARE
    1.48 +      "issue_row"         "issue"%ROWTYPE;
    1.49 +      "policy_row"        "policy"%ROWTYPE;
    1.50 +      "dimension_v"       INTEGER;
    1.51 +      "vote_matrix"       INT4[][];  -- absolute votes
    1.52 +      "matrix"            INT8[][];  -- defeat strength / best paths
    1.53 +      "i"                 INTEGER;
    1.54 +      "j"                 INTEGER;
    1.55 +      "k"                 INTEGER;
    1.56 +      "battle_row"        "battle"%ROWTYPE;
    1.57 +      "rank_ary"          INT4[];
    1.58 +      "rank_v"            INT4;
    1.59 +      "initiative_id_v"   "initiative"."id"%TYPE;
    1.60 +    BEGIN
    1.61 +      PERFORM "require_transaction_isolation"();
    1.62 +      SELECT * INTO "issue_row"
    1.63 +        FROM "issue" WHERE "id" = "issue_id_p";
    1.64 +      SELECT * INTO "policy_row"
    1.65 +        FROM "policy" WHERE "id" = "issue_row"."policy_id";
    1.66 +      SELECT count(1) INTO "dimension_v"
    1.67 +        FROM "battle_participant" WHERE "issue_id" = "issue_id_p";
    1.68 +      -- Create "vote_matrix" with absolute number of votes in pairwise
    1.69 +      -- comparison:
    1.70 +      "vote_matrix" := array_fill(NULL::INT4, ARRAY["dimension_v", "dimension_v"]);
    1.71 +      "i" := 1;
    1.72 +      "j" := 2;
    1.73 +      FOR "battle_row" IN
    1.74 +        SELECT * FROM "battle" WHERE "issue_id" = "issue_id_p"
    1.75 +        ORDER BY
    1.76 +        "winning_initiative_id" NULLS FIRST,
    1.77 +        "losing_initiative_id" NULLS FIRST
    1.78 +      LOOP
    1.79 +        "vote_matrix"["i"]["j"] := "battle_row"."count";
    1.80 +        IF "j" = "dimension_v" THEN
    1.81 +          "i" := "i" + 1;
    1.82 +          "j" := 1;
    1.83 +        ELSE
    1.84 +          "j" := "j" + 1;
    1.85 +          IF "j" = "i" THEN
    1.86 +            "j" := "j" + 1;
    1.87 +          END IF;
    1.88 +        END IF;
    1.89 +      END LOOP;
    1.90 +      IF "i" != "dimension_v" OR "j" != "dimension_v" + 1 THEN
    1.91 +        RAISE EXCEPTION 'Wrong battle count (should not happen)';
    1.92 +      END IF;
    1.93 +      -- Store defeat strengths in "matrix" using "defeat_strength"
    1.94 +      -- function:
    1.95 +      "matrix" := array_fill(NULL::INT8, ARRAY["dimension_v", "dimension_v"]);
    1.96 +      "i" := 1;
    1.97 +      LOOP
    1.98 +        "j" := 1;
    1.99 +        LOOP
   1.100 +          IF "i" != "j" THEN
   1.101 +            "matrix"["i"]["j"] := "defeat_strength"(
   1.102 +              "vote_matrix"["i"]["j"],
   1.103 +              "vote_matrix"["j"]["i"]
   1.104 +            );
   1.105 +          END IF;
   1.106 +          EXIT WHEN "j" = "dimension_v";
   1.107 +          "j" := "j" + 1;
   1.108 +        END LOOP;
   1.109 +        EXIT WHEN "i" = "dimension_v";
   1.110 +        "i" := "i" + 1;
   1.111 +      END LOOP;
   1.112 +      -- Find best paths:
   1.113 +      "i" := 1;
   1.114 +      LOOP
   1.115 +        "j" := 1;
   1.116 +        LOOP
   1.117 +          IF "i" != "j" THEN
   1.118 +            "k" := 1;
   1.119 +            LOOP
   1.120 +              IF "i" != "k" AND "j" != "k" THEN
   1.121 +                IF "matrix"["j"]["i"] < "matrix"["i"]["k"] THEN
   1.122 +                  IF "matrix"["j"]["i"] > "matrix"["j"]["k"] THEN
   1.123 +                    "matrix"["j"]["k"] := "matrix"["j"]["i"];
   1.124 +                  END IF;
   1.125 +                ELSE
   1.126 +                  IF "matrix"["i"]["k"] > "matrix"["j"]["k"] THEN
   1.127 +                    "matrix"["j"]["k"] := "matrix"["i"]["k"];
   1.128 +                  END IF;
   1.129 +                END IF;
   1.130 +              END IF;
   1.131 +              EXIT WHEN "k" = "dimension_v";
   1.132 +              "k" := "k" + 1;
   1.133 +            END LOOP;
   1.134 +          END IF;
   1.135 +          EXIT WHEN "j" = "dimension_v";
   1.136 +          "j" := "j" + 1;
   1.137 +        END LOOP;
   1.138 +        EXIT WHEN "i" = "dimension_v";
   1.139 +        "i" := "i" + 1;
   1.140 +      END LOOP;
   1.141 +      -- Determine order of winners:
   1.142 +      "rank_ary" := array_fill(NULL::INT4, ARRAY["dimension_v"]);
   1.143 +      "rank_v" := 1;
   1.144 +      LOOP
   1.145 +        "i" := 1;
   1.146 +        LOOP
   1.147 +          IF "rank_ary"["i"] ISNULL THEN
   1.148 +            "j" := 1;
   1.149 +            LOOP
   1.150 +              IF
   1.151 +                "i" != "j" AND
   1.152 +                "rank_ary"["j"] ISNULL AND
   1.153 +                ( "matrix"["j"]["i"] > "matrix"["i"]["j"] OR
   1.154 +                  -- tie-breaking by "id"
   1.155 +                  ( "matrix"["j"]["i"] = "matrix"["i"]["j"] AND
   1.156 +                    "j" < "i" ) )
   1.157 +              THEN
   1.158 +                -- someone else is better
   1.159 +                EXIT;
   1.160 +              END IF;
   1.161 +              "j" := "j" + 1;
   1.162 +              IF "j" = "dimension_v" + 1 THEN
   1.163 +                -- noone is better
   1.164 +                "rank_ary"["i"] := "rank_v";
   1.165 +                EXIT;
   1.166 +              END IF;
   1.167 +            END LOOP;
   1.168 +            EXIT WHEN "j" = "dimension_v" + 1;
   1.169 +          END IF;
   1.170 +          "i" := "i" + 1;
   1.171 +          IF "i" > "dimension_v" THEN
   1.172 +            RAISE EXCEPTION 'Schulze ranking does not compute (should not happen)';
   1.173 +          END IF;
   1.174 +        END LOOP;
   1.175 +        EXIT WHEN "rank_v" = "dimension_v";
   1.176 +        "rank_v" := "rank_v" + 1;
   1.177 +      END LOOP;
   1.178 +      -- write preliminary results:
   1.179 +      "i" := 2;  -- omit status quo with "i" = 1
   1.180 +      FOR "initiative_id_v" IN
   1.181 +        SELECT "id" FROM "initiative"
   1.182 +        WHERE "issue_id" = "issue_id_p" AND "admitted"
   1.183 +        ORDER BY "id"
   1.184 +      LOOP
   1.185 +        UPDATE "initiative" SET
   1.186 +          "direct_majority" =
   1.187 +            CASE WHEN "policy_row"."direct_majority_strict" THEN
   1.188 +              "positive_votes" * "policy_row"."direct_majority_den" >
   1.189 +              "policy_row"."direct_majority_num" * ("positive_votes"+"negative_votes")
   1.190 +            ELSE
   1.191 +              "positive_votes" * "policy_row"."direct_majority_den" >=
   1.192 +              "policy_row"."direct_majority_num" * ("positive_votes"+"negative_votes")
   1.193 +            END
   1.194 +            AND "positive_votes" >= "policy_row"."direct_majority_positive"
   1.195 +            AND "issue_row"."voter_count"-"negative_votes" >=
   1.196 +                "policy_row"."direct_majority_non_negative",
   1.197 +            "indirect_majority" =
   1.198 +            CASE WHEN "policy_row"."indirect_majority_strict" THEN
   1.199 +              "positive_votes" * "policy_row"."indirect_majority_den" >
   1.200 +              "policy_row"."indirect_majority_num" * ("positive_votes"+"negative_votes")
   1.201 +            ELSE
   1.202 +              "positive_votes" * "policy_row"."indirect_majority_den" >=
   1.203 +              "policy_row"."indirect_majority_num" * ("positive_votes"+"negative_votes")
   1.204 +            END
   1.205 +            AND "positive_votes" >= "policy_row"."indirect_majority_positive"
   1.206 +            AND "issue_row"."voter_count"-"negative_votes" >=
   1.207 +                "policy_row"."indirect_majority_non_negative",
   1.208 +          "schulze_rank"           = "rank_ary"["i"],
   1.209 +          "better_than_status_quo" = "rank_ary"["i"] < "rank_ary"[1],
   1.210 +          "worse_than_status_quo"  = "rank_ary"["i"] > "rank_ary"[1],
   1.211 +          "multistage_majority"    = "rank_ary"["i"] >= "rank_ary"[1],
   1.212 +          "reverse_beat_path"      = "matrix"[1]["i"] >= 0,
   1.213 +          "eligible"               = FALSE,
   1.214 +          "winner"                 = FALSE,
   1.215 +          "rank"                   = NULL  -- NOTE: in cases of manual reset of issue state
   1.216 +          WHERE "id" = "initiative_id_v";
   1.217 +        "i" := "i" + 1;
   1.218 +      END LOOP;
   1.219 +      IF "i" != "dimension_v" + 1 THEN
   1.220 +        RAISE EXCEPTION 'Wrong winner count (should not happen)';
   1.221 +      END IF;
   1.222 +      -- take indirect majorities into account:
   1.223 +      LOOP
   1.224 +        UPDATE "initiative" SET "indirect_majority" = TRUE
   1.225 +          FROM (
   1.226 +            SELECT "new_initiative"."id" AS "initiative_id"
   1.227 +            FROM "initiative" "old_initiative"
   1.228 +            JOIN "initiative" "new_initiative"
   1.229 +              ON "new_initiative"."issue_id" = "issue_id_p"
   1.230 +              AND "new_initiative"."indirect_majority" = FALSE
   1.231 +            JOIN "battle" "battle_win"
   1.232 +              ON "battle_win"."issue_id" = "issue_id_p"
   1.233 +              AND "battle_win"."winning_initiative_id" = "new_initiative"."id"
   1.234 +              AND "battle_win"."losing_initiative_id" = "old_initiative"."id"
   1.235 +            JOIN "battle" "battle_lose"
   1.236 +              ON "battle_lose"."issue_id" = "issue_id_p"
   1.237 +              AND "battle_lose"."losing_initiative_id" = "new_initiative"."id"
   1.238 +              AND "battle_lose"."winning_initiative_id" = "old_initiative"."id"
   1.239 +            WHERE "old_initiative"."issue_id" = "issue_id_p"
   1.240 +            AND "old_initiative"."indirect_majority" = TRUE
   1.241 +            AND CASE WHEN "policy_row"."indirect_majority_strict" THEN
   1.242 +              "battle_win"."count" * "policy_row"."indirect_majority_den" >
   1.243 +              "policy_row"."indirect_majority_num" *
   1.244 +              ("battle_win"."count"+"battle_lose"."count")
   1.245 +            ELSE
   1.246 +              "battle_win"."count" * "policy_row"."indirect_majority_den" >=
   1.247 +              "policy_row"."indirect_majority_num" *
   1.248 +              ("battle_win"."count"+"battle_lose"."count")
   1.249 +            END
   1.250 +            AND "battle_win"."count" >= "policy_row"."indirect_majority_positive"
   1.251 +            AND "issue_row"."voter_count"-"battle_lose"."count" >=
   1.252 +                "policy_row"."indirect_majority_non_negative"
   1.253 +          ) AS "subquery"
   1.254 +          WHERE "id" = "subquery"."initiative_id";
   1.255 +        EXIT WHEN NOT FOUND;
   1.256 +      END LOOP;
   1.257 +      -- set "multistage_majority" for remaining matching initiatives:
   1.258 +      UPDATE "initiative" SET "multistage_majority" = TRUE
   1.259 +        FROM (
   1.260 +          SELECT "losing_initiative"."id" AS "initiative_id"
   1.261 +          FROM "initiative" "losing_initiative"
   1.262 +          JOIN "initiative" "winning_initiative"
   1.263 +            ON "winning_initiative"."issue_id" = "issue_id_p"
   1.264 +            AND "winning_initiative"."admitted"
   1.265 +          JOIN "battle" "battle_win"
   1.266 +            ON "battle_win"."issue_id" = "issue_id_p"
   1.267 +            AND "battle_win"."winning_initiative_id" = "winning_initiative"."id"
   1.268 +            AND "battle_win"."losing_initiative_id" = "losing_initiative"."id"
   1.269 +          JOIN "battle" "battle_lose"
   1.270 +            ON "battle_lose"."issue_id" = "issue_id_p"
   1.271 +            AND "battle_lose"."losing_initiative_id" = "winning_initiative"."id"
   1.272 +            AND "battle_lose"."winning_initiative_id" = "losing_initiative"."id"
   1.273 +          WHERE "losing_initiative"."issue_id" = "issue_id_p"
   1.274 +          AND "losing_initiative"."admitted"
   1.275 +          AND "winning_initiative"."schulze_rank" <
   1.276 +              "losing_initiative"."schulze_rank"
   1.277 +          AND "battle_win"."count" > "battle_lose"."count"
   1.278 +          AND (
   1.279 +            "battle_win"."count" > "winning_initiative"."positive_votes" OR
   1.280 +            "battle_lose"."count" < "losing_initiative"."negative_votes" )
   1.281 +        ) AS "subquery"
   1.282 +        WHERE "id" = "subquery"."initiative_id";
   1.283 +      -- mark eligible initiatives:
   1.284 +      UPDATE "initiative" SET "eligible" = TRUE
   1.285 +        WHERE "issue_id" = "issue_id_p"
   1.286 +        AND "initiative"."direct_majority"
   1.287 +        AND "initiative"."indirect_majority"
   1.288 +        AND "initiative"."better_than_status_quo"
   1.289 +        AND (
   1.290 +          "policy_row"."no_multistage_majority" = FALSE OR
   1.291 +          "initiative"."multistage_majority" = FALSE )
   1.292 +        AND (
   1.293 +          "policy_row"."no_reverse_beat_path" = FALSE OR
   1.294 +          "initiative"."reverse_beat_path" = FALSE );
   1.295 +      -- mark final winner:
   1.296 +      UPDATE "initiative" SET "winner" = TRUE
   1.297 +        FROM (
   1.298 +          SELECT "id" AS "initiative_id"
   1.299 +          FROM "initiative"
   1.300 +          WHERE "issue_id" = "issue_id_p" AND "eligible"
   1.301 +          ORDER BY
   1.302 +            "schulze_rank",
   1.303 +            "id"
   1.304 +          LIMIT 1
   1.305 +        ) AS "subquery"
   1.306 +        WHERE "id" = "subquery"."initiative_id";
   1.307 +      -- write (final) ranks:
   1.308 +      "rank_v" := 1;
   1.309 +      FOR "initiative_id_v" IN
   1.310 +        SELECT "id"
   1.311 +        FROM "initiative"
   1.312 +        WHERE "issue_id" = "issue_id_p" AND "admitted"
   1.313 +        ORDER BY
   1.314 +          "winner" DESC,
   1.315 +          "eligible" DESC,
   1.316 +          "schulze_rank",
   1.317 +          "id"
   1.318 +      LOOP
   1.319 +        UPDATE "initiative" SET "rank" = "rank_v"
   1.320 +          WHERE "id" = "initiative_id_v";
   1.321 +        "rank_v" := "rank_v" + 1;
   1.322 +      END LOOP;
   1.323 +      -- set schulze rank of status quo and mark issue as finished:
   1.324 +      UPDATE "issue" SET
   1.325 +        "status_quo_schulze_rank" = "rank_ary"[1],
   1.326 +        "state" =
   1.327 +          CASE WHEN EXISTS (
   1.328 +            SELECT NULL FROM "initiative"
   1.329 +            WHERE "issue_id" = "issue_id_p" AND "winner"
   1.330 +          ) THEN
   1.331 +            'finished_with_winner'::"issue_state"
   1.332 +          ELSE
   1.333 +            'finished_without_winner'::"issue_state"
   1.334 +          END,
   1.335 +        "closed" = "phase_finished",
   1.336 +        "phase_finished" = NULL
   1.337 +        WHERE "id" = "issue_id_p";
   1.338 +      RETURN;
   1.339 +    END;
   1.340 +  $$;
   1.341 +
   1.342 +COMMIT;

Impressum / About Us