I guess that the following index should do the trick:

    CREATE INDEX ON a (id) WHERE num1 IS NOT NULL AND num2 IS NOT NULL;

    VACUUM (ANALYZE) a;

The `ANALYZE` might improve the horrible estimate, and even without that an index-only scan should make the fast plan more attractive.

A less elegant solution is to force the hand of the optimizer:

    SELECT count(*)
    FROM a
    CROSS JOIN LATERAL (SELECT FROM b
                        WHERE a.id = b.a_id)
    WHERE a.num1 IS NOT NULL
      AND a.num2 IS NOT NULL;